คำตอบ:
(ดูประวัติของคำตอบนี้เพื่อให้ได้ข้อความที่ละเอียดยิ่งขึ้น แต่ตอนนี้ฉันคิดว่ามันง่ายกว่าที่ผู้อ่านจะเห็นบรรทัดคำสั่งจริง)
ไฟล์ทั่วไปที่แชร์โดยคำสั่งด้านล่างทั้งหมด
$ cat a.cpp
extern int a;
int main() {
return a;
}
$ cat b.cpp
extern int b;
int a = b;
$ cat d.cpp
int b;
$ g++ -c b.cpp -o b.o
$ ar cr libb.a b.o
$ g++ -c d.cpp -o d.o
$ ar cr libd.a d.o
$ g++ -L. -ld -lb a.cpp # wrong order
$ g++ -L. -lb -ld a.cpp # wrong order
$ g++ a.cpp -L. -ld -lb # wrong order
$ g++ a.cpp -L. -lb -ld # right order
ตัวเชื่อมโยงค้นหาจากซ้ายไปขวาและบันทึกสัญลักษณ์ที่ไม่ได้แก้ไขขณะที่มันไป หากไลบรารีแก้ไขสัญลักษณ์ใช้ไฟล์อ็อบเจ็กต์ของไลบรารีนั้นเพื่อแก้ไขสัญลักษณ์ (bo จาก libb.a ในกรณีนี้)
การพึ่งพาของไลบรารีแบบสแตติกเทียบกับงานอื่น ๆ เหมือนกัน - ไลบรารีที่ต้องการสัญลักษณ์ต้องเป็นอันดับแรกจากนั้นไลบรารีที่แก้ไขสัญลักษณ์
หากห้องสมุดคงขึ้นอยู่กับห้องสมุดอื่น แต่ห้องสมุดอื่นอีกครั้งขึ้นอยู่กับห้องสมุดเดิมมีรอบ คุณสามารถแก้ไขปัญหานี้ได้ด้วยการใส่ไลบรารี่ที่ขึ้นกับวัฏจักรโดย-(
และ-)
เช่น-( -la -lb -)
(คุณอาจต้องหลีกเลี่ยง parens เช่น-\(
และ-\)
) ตัวเชื่อมโยงจะค้นหา lib ที่ปิดล้อมเหล่านั้นหลายครั้งเพื่อให้แน่ใจว่าการอ้างอิงการวนรอบได้รับการแก้ไขแล้ว หรือคุณสามารถระบุไลบรารีหลาย ๆ ครั้ง-la -lb -la
ได้
$ export LD_LIBRARY_PATH=. # not needed if libs go to /usr/lib etc
$ g++ -fpic -shared d.cpp -o libd.so
$ g++ -fpic -shared b.cpp -L. -ld -o libb.so # specifies its dependency!
$ g++ -L. -lb a.cpp # wrong order (works on some distributions)
$ g++ -Wl,--as-needed -L. -lb a.cpp # wrong order
$ g++ -Wl,--as-needed a.cpp -L. -lb # right order
มันเหมือนกันที่นี่ - ห้องสมุดจะต้องติดตามไฟล์อ็อบเจ็กต์ของโปรแกรม ความแตกต่างที่นี่เมื่อเทียบกับห้องสมุดแบบคงที่คือการที่คุณไม่จำเป็นต้องดูแลเกี่ยวกับการอ้างอิงของห้องสมุดกับแต่ละอื่น ๆ เพราะห้องสมุดแบบไดนามิกสังคายนาการพึ่งพาตัวเองของพวกเขา
เห็นได้ชัดว่าการกระจายเมื่อเร็ว ๆ นี้บางค่าเริ่มต้นคือการใช้การ--as-needed
ตั้งค่าตัวเชื่อมโยงซึ่งบังคับใช้ว่าไฟล์วัตถุของโปรแกรมมาก่อนไลบรารีแบบไดนามิก หากการตั้งค่าสถานะนั้นถูกส่งตัวเชื่อมโยงจะไม่เชื่อมโยงไปยังไลบรารีที่ไม่ได้ใช้งานจริงโดยโปรแกรมเรียกใช้งาน (และจะตรวจจับสิ่งนี้จากซ้ายไปขวา) การกระจาย Archlinux ล่าสุดของฉันไม่ได้ใช้แฟล็กนี้ตามค่าเริ่มต้นดังนั้นจึงไม่มีข้อผิดพลาดในการไม่ทำตามคำสั่งที่ถูกต้อง
มันไม่ถูกต้องที่จะละเว้นการพึ่งพาของb.so
กับd.so
เมื่อมีการสร้างในอดีต คุณจะต้องระบุไลบรารีเมื่อทำการเชื่อมโยงa
แต่a
ไม่ต้องการจำนวนเต็มb
จริง ๆ ดังนั้นจึงไม่ควรสนใจการb
พึ่งพาของตัวเอง
นี่คือตัวอย่างของความหมายหากคุณไม่ได้ระบุการพึ่งพาสำหรับ libb.so
$ export LD_LIBRARY_PATH=. # not needed if libs go to /usr/lib etc
$ g++ -fpic -shared d.cpp -o libd.so
$ g++ -fpic -shared b.cpp -o libb.so # wrong (but links)
$ g++ -L. -lb a.cpp # wrong, as above
$ g++ -Wl,--as-needed -L. -lb a.cpp # wrong, as above
$ g++ a.cpp -L. -lb # wrong, missing libd.so
$ g++ a.cpp -L. -ld -lb # wrong order (works on some distributions)
$ g++ -Wl,--as-needed a.cpp -L. -ld -lb # wrong order (like static libs)
$ g++ -Wl,--as-needed a.cpp -L. -lb -ld # "right"
ถ้าตอนนี้คุณดูว่าการพึ่งพาไบนารีมีอะไรบ้างคุณสังเกตว่าไบนารีนั้นขึ้นอยู่กับlibd
ว่าไม่ใช่แค่libb
เท่าที่ควร ไบนารีจะต้องมีการเชื่อมโยงlibb
ใหม่ในภายหลังหากขึ้นอยู่กับห้องสมุดอื่นถ้าคุณทำเช่นนี้ และถ้ามีคนอื่นโหลดlibb
โดยใช้dlopen
ขณะทำงาน (คิดว่าการโหลดปลั๊กอินแบบไดนามิก) การโทรจะล้มเหลวเช่นกัน ดังนั้น"right"
สิ่งที่ควรจะเป็นwrong
เช่นกัน
lorder
+ tsort
ทำ แต่บางครั้งก็ไม่มีคำสั่งถ้าคุณมีการอ้างอิงครบวงจร จากนั้นคุณเพียงต้องวนไปตามรายการไลบรารีจนกว่าทุกอย่างจะได้รับการแก้ไข
ตัวลิงก์ GNU ld เป็นตัวเชื่อมโยงอัจฉริยะที่เรียกว่า มันจะติดตามฟังก์ชั่นที่ใช้โดยนำหน้าสแตติกไลบรารีอย่างถาวรเพื่อสลับฟังก์ชั่นเหล่านั้นที่ไม่ได้ใช้จากตารางการค้นหา ผลลัพธ์คือถ้าคุณเชื่อมโยงไลบรารีแบบคงที่เร็วเกินไปฟังก์ชันในไลบรารีนั้นจะไม่พร้อมใช้งานกับไลบรารีแบบคงที่อีกต่อไปในบรรทัดลิงก์
ตัวเชื่อมโยง UNIX ทั่วไปทำงานจากซ้ายไปขวาดังนั้นให้วางไลบรารีที่ต้องพึ่งพาทั้งหมดของคุณทางด้านซ้ายและอันที่ตอบสนองการพึ่งพาเหล่านั้นทางด้านขวาของลิงค์ลิงค์ คุณอาจพบว่าห้องสมุดบางแห่งขึ้นอยู่กับคนอื่นในขณะที่ห้องสมุดอื่น ๆ ขึ้นอยู่กับพวกเขา นี่คือสิ่งที่มันซับซ้อน เมื่อพูดถึงการอ้างอิงแบบวงกลมแก้ไขรหัสของคุณ!
นี่คือตัวอย่างเพื่อให้ชัดเจนว่าสิ่งต่าง ๆ ทำงานกับ GCC อย่างไรเมื่อมีการเข้าใช้ห้องสมุดแบบคงที่ ดังนั้นสมมติว่าเรามีสถานการณ์ต่อไปนี้:
myprog.o
- main()
ฟังก์ชั่นที่มีขึ้นอยู่กับlibmysqlclient
libmysqlclient
- คงที่เพื่อประโยชน์ของตัวอย่าง (คุณต้องการห้องสมุดสาธารณะที่libmysqlclient
มีขนาดใหญ่) ใน/usr/local/lib
; และขึ้นอยู่กับเนื้อหาจากlibz
libz
(แบบไดนามิก)เราจะเชื่อมโยงสิ่งนี้ได้อย่างไร (หมายเหตุ: ตัวอย่างจากการคอมไพล์บน Cygwin โดยใช้ gcc 4.3.4)
gcc -L/usr/local/lib -lmysqlclient myprog.o
# undefined reference to `_mysql_init'
# myprog depends on libmysqlclient
# so myprog has to come earlier on the command line
gcc myprog.o -L/usr/local/lib -lmysqlclient
# undefined reference to `_uncompress'
# we have to link with libz, too
gcc myprog.o -lz -L/usr/local/lib -lmysqlclient
# undefined reference to `_uncompress'
# libz is needed by libmysqlclient
# so it has to appear *after* it on the command line
gcc myprog.o -L/usr/local/lib -lmysqlclient -lz
# this works
ถ้าคุณเพิ่ม-Wl,--start-group
ในตัวเชื่อมโยงการตั้งค่าสถานะมันไม่สนใจลำดับใดที่อยู่ในหรือถ้ามีการอ้างอิงแบบวงกลม
ใน Qt หมายถึงการเพิ่ม:
QMAKE_LFLAGS += -Wl,--start-group
ประหยัดเวลาที่ยุ่งวุ่นวายและดูเหมือนจะไม่ทำให้การเชื่อมโยงช้าลงเท่าไหร่ (ซึ่งใช้เวลาน้อยกว่าการคอมไพล์อยู่ดี)
อีกทางเลือกหนึ่งคือการระบุรายการของห้องสมุดสองครั้ง:
gcc prog.o libA.a libB.a libA.a libB.a -o prog.x
การทำเช่นนี้คุณไม่ต้องกังวลกับลำดับที่ถูกต้องเนื่องจากการอ้างอิงจะได้รับการแก้ไขในบล็อกที่สอง
คุณสามารถใช้ตัวเลือก -Xlinker
g++ -o foobar -Xlinker -start-group -Xlinker libA.a -Xlinker libB.a -Xlinker libC.a -Xlinker -end-group
เกือบเท่ากับ
g++ -o foobar -Xlinker -start-group -Xlinker libC.a -Xlinker libB.a -Xlinker libA.a -Xlinker -end-group
ระวัง!
เคล็ดลับสั้น ๆ ที่ทำให้ฉันสะดุด: หากคุณเรียกใช้ตัวเชื่อมโยงเป็น "gcc" หรือ "g ++" ให้ใช้ "- เริ่มต้นกลุ่ม" และ "- สิ้นสุดกลุ่ม" จะไม่ผ่านตัวเลือกเหล่านั้นผ่านไปยัง ตัวเชื่อมโยง - และจะไม่มีการตั้งค่าสถานะข้อผิดพลาด มันจะล้มเหลวในการเชื่อมโยงกับสัญลักษณ์ที่ไม่ได้กำหนดถ้าคุณมีคำสั่งห้องสมุดผิด
คุณต้องเขียนเป็น "-Wl, - start-group" ฯลฯ เพื่อบอก GCC ให้ส่งอาร์กิวเมนต์ไปยังลิงเกอร์
ลำดับการเชื่อมโยงมีความสำคัญอย่างน้อยในบางแพลตฟอร์ม ฉันได้เห็นข้อขัดข้องสำหรับแอปพลิเคชันที่เชื่อมโยงกับไลบรารีในลำดับที่ไม่ถูกต้อง (ที่ผิดหมายถึง A ที่เชื่อมโยงก่อน B แต่ B ขึ้นอยู่กับ A)
ฉันได้เห็นสิ่งนี้มากมายโมดูลของเราบางอันเชื่อมโยงกับเกิน 100 ไลบรารี่ของรหัสของเรารวมถึงระบบ & libs บุคคลที่สาม
ขึ้นอยู่กับ linkers ที่แตกต่างกัน HP / Intel / GCC / SUN / SGI / IBM / ฯลฯ คุณสามารถรับฟังก์ชั่น / ตัวแปรที่ไม่ได้รับการแก้ไข ฯลฯ ในบางแพลตฟอร์มคุณต้องแสดงรายการห้องสมุดสองครั้ง
ส่วนใหญ่เราใช้ลำดับชั้นที่มีโครงสร้างของไลบรารีแกนแพลตฟอร์มเลเยอร์สิ่งที่เป็นนามธรรม แต่สำหรับบางระบบคุณยังคงต้องเล่นตามลำดับในคำสั่งลิงก์
เมื่อคุณเข้าสู่เอกสารโซลูชันแล้วนักพัฒนาซอฟต์แวร์รายต่อไปก็ไม่จำเป็นต้องทำมันอีก
อาจารย์เก่าของฉันเคยพูดว่า "การติดต่อกันสูงและการมีเพศสัมพันธ์ต่ำ " มันยังคงเป็นจริงในวันนี้
gcc
เปลี่ยนเป็นพฤติกรรมที่เข้มงวดมากขึ้นเมื่อเร็ว ๆ นี้