ชุมชนโปรแกรมเมอร์กำลังคึกคักกับการอย่างเรื่องความท้าทายของ static linking ในการพัฒนา C และ C++ ซึ่งถูกจุดประกายโดยบทความที่ก่อให้เกิดการถกเถียงที่อ้างว่าไฟล์ archive .a มีข้อบกพร่องโดยพื้นฐาน ในขณะที่บทความต้นฉบับโต้แย้งให้มีรูปแบบไฟล์ใหม่ทั้งหมด นักพัฒนาที่มีประสบการณ์ได้ก้าวออกมาพร้อมกับวิธีแก้ไขเชิงปฏิบัติที่ใช้งานได้ในปัจจุบัน
Compiler Flags ที่ซ่อนอยู่อาจแก้ปัญหาส่วนใหญ่ได้
นักพัฒนาหลายคนรู้สึกประหลาดใจที่ได้เรียนรู้เกี่ยวกับ GCC flags ที่มีอยู่แล้วซึ่งสามารถแก้ไขปัญหา static linking ทั่วไป flags -ffunction-sections -fdata-sections
ร่วมกับ -Wl,--gc-sections
สามารถกำจัดโค้ดที่ไม่ได้ใช้จาก static libraries โดยไม่ต้องใช้ Link Time Optimization (LTO) ตัวเลือกเหล่านี้เป็นที่รู้จักกันดีในการพัฒนา embedded firmware ซึ่งถือว่าเป็นสิ่งที่จำเป็นเกือบบังคับ แต่ยังคงไม่เป็นที่รู้จักสำหรับนักพัฒนาทั่วไป
การอภิปรายของชุมชนเผยให้เห็นช่องว่างของความรู้ แม้ว่า flags เหล่านี้จะมีผลการค้นหา 750,000 ครั้งบน GitHub และเป็นค่าเริ่มต้นในระบบ build บางตัวเช่น Bazel แต่นักพัฒนา C/C++ หลายคนยังคงไม่ทราบเกี่ยวกับสิ่งเหล่านี้ สิ่งนี้บ่งชี้ว่าปัญหาไม่ได้อยู่ที่ static linking เอง แต่อยู่ที่การศึกษาและค่าเริ่มต้นของเครื่องมือ
แฟล็ก GCC หลักสำหรับการลิงก์แบบสแตติก
แฟล็ก | วัตถุประสงค์ |
---|---|
-ffunction-sections |
วางแต่ละฟังก์ชันในส่วนของตัวเอง |
-fdata-sections |
วางแต่ละรายการข้อมูลในส่วนของตัวเอง |
-Wl,--gc-sections |
ลบส่วนที่ไม่ได้ใช้ระหว่างการลิงก์ |
-flto |
เปิดใช้งาน Link Time Optimization เพื่อกำจัดโค้ดที่ตายแล้ว |
ความขัดแย้งของ Symbol มีวิธีแก้ไขมาตรฐาน
การถกเถียงยังเน้นย้ำว่าปัญหาที่รับรู้หลายอย่างกับ static libraries เกิดจากการออกแบบไลบรารีที่ไม่ดีมากกว่าข้อบกพร่องพื้นฐานในรูปแบบ .a ผู้เขียน C library ที่มีประสบการณ์ปฏิบัติตามแนวทางที่กำหนดไว้: ทำให้ฟังก์ชันภายในทั้งหมดเป็น static เพิ่มคำนำหน้าชื่อไลบรารีที่ไม่ซ้ำกันให้กับ exported symbols ทั้งหมด และหลีกเลี่ยงการเริ่มต้นโมดูลอัตโนมัติโดยใช้ฟังก์ชัน init ที่ชัดเจนแทน
ขั้นตอนที่ 1: ทำให้ globals/functions ทั้งหมดเป็น static เว้นแต่จะเป็นสำหรับ export ขั้นตอนที่ 2: ให้คำนำหน้า exported symbols และ public header definitions ทั้งหมด เช่น 'mylibname_' เพราะ linkage มี global namespace
แนวทางเหล่านี้แม้ว่าจะต้องใช้ความระมัดระวัง แต่สามารถป้องกันความขัดแย้งของ symbol ที่รบกวน static libraries ที่ออกแบบไม่ดีได้อย่างมีประสิทธิภาพ
แนวทางปฏิบัติที่ดีสำหรับ Static Library
- ทำให้ฟังก์ชันภายในและตัวแปรส่วนกลางทั้งหมดเป็น
static
- เพิ่มคำนำหน้าให้กับสัญลักษณ์ที่ส่งออกทั้งหมดด้วยชื่อไลบรารีที่ไม่ซ้ำกัน
- หลีกเลี่ยงการเริ่มต้นโมดูลแบบอัตโนมัติ
- ใช้ฟังก์ชันเริ่มต้น/ทำความสะอาดแบบชัดเจนแทน
- พิจารณาไลบรารีแบบออบเจกต์เดียวเพื่อการควบคุมสัญลักษณ์ที่ดีกว่า
วิธีแก้ไขเชิงสร้างสรรค์เกิดขึ้น
นักพัฒนาบางคนได้สร้างวิธีแก้ไขที่เป็นนวัตกรรมเพื่อเชื่อมช่องว่างระหว่าง static และ dynamic libraries เครื่องมือเช่น armerge
สามารถรวม object files หลายไฟล์ใน static library เข้าเป็น merged object เดียว ให้การควบคุม symbol visibility ที่ดีขึ้นในขณะที่รักษาความเข้ากันได้กับ toolchains ที่มีอยู่
คนอื่นๆ ได้พัฒนาสคริปต์เพื่อสร้าง static libraries ที่มีปัญหาใหม่ โดยแยกและจัดระเบียบ object files ใหม่เพื่อกำจัดความขัดแย้ง แม้ว่าวิธีการเหล่านี้จะต้องใช้งานด้วยตนเอง แต่ก็แสดงให้เห็นว่าเทคโนโลยีพื้นฐานมีความยืดหยุ่นมากกว่าที่ปรากฏในตอนแรก
การถกเถียงเกี่ยวกับรูปแบบยังคงดำเนินต่อไป
การอภิปรายยังได้สัมผัสกับคำถามที่ลึกซึ้งยิ่งขึ้นเกี่ยวกับเหตุผลที่อุตสาหกรรมรักษารูปแบบแยกต่างหากสำหรับ static (.a) และ dynamic (.so) libraries นักพัฒนาบางคนโต้แย้งว่า shared objects มีข้อมูลทั้งหมดที่จำเป็นสำหรับ static linking อยู่แล้ว ทำให้ความแตกต่างเป็นเรื่องทางประวัติศาสตร์เป็นส่วนใหญ่
นักพัฒนา Windows ชี้ให้เห็นว่า MSVC toolchain สร้างไฟล์แยกต่างหากสามไฟล์สำหรับแต่ละไลบรารี: เวอร์ชัน static, dynamic library และ import library สำหรับ linking กับเวอร์ชัน dynamic ความซับซ้อนนี้บ่งชี้ว่าปัญหาขยายไปเกินกว่าแค่ไฟล์ .a แบบ Unix
ฉันทามติของชุมชนดูเหมือนจะเป็นว่าแม้ว่า static linking จะมีความท้าทายที่แท้จริง แต่วิธีแก้ไขอยู่ที่เครื่องมือและการศึกษาที่ดีขึ้นมากกว่าการละทิ้งเทคโนโลยีที่ใช้งานได้มาหลายทศวรรษ การถกเถียงได้เน้นย้ำให้เห็นว่าความรู้เชิงปฏิบัติมากมายที่มีอยู่ในชุมชนไม่ได้แบ่งปันกันอย่างกว้างขวางในหมู่นักพัฒนาทุกคน
อ้างอิง: The .a File Is a Relic: Why Static Archives Were a Bad Idea All Along