ฉันจะลิงก์เฉพาะไลบรารีเฉพาะบางส่วนกับไบนารีของฉันได้อย่างไรเมื่อเชื่อมโยงกับ GCC
gcc ... -static ...
พยายามเชื่อมโยงไลบรารีที่เชื่อมโยงทั้งหมดแบบคงที่แต่ฉันไม่มีเวอร์ชันคงที่ของบางไลบรารี (เช่น libX11)
ฉันจะลิงก์เฉพาะไลบรารีเฉพาะบางส่วนกับไบนารีของฉันได้อย่างไรเมื่อเชื่อมโยงกับ GCC
gcc ... -static ...
พยายามเชื่อมโยงไลบรารีที่เชื่อมโยงทั้งหมดแบบคงที่แต่ฉันไม่มีเวอร์ชันคงที่ของบางไลบรารี (เช่น libX11)
คำตอบ:
gcc -lsome_dynamic_lib code.c some_static_lib.a
code.c
ไฟล์จะรับประกันว่าสัญลักษณ์ในนั้นจะถูกละเว้นเว้นแต่จะเกิดขึ้นmain()
ฟังก์ชั่นในหนึ่งในไฟล์วัตถุห้องสมุด
คุณยังสามารถใช้ld
ตัวเลือก-Bdynamic
gcc <objectfiles> -static -lstatic1 -lstatic2 -Wl,-Bdynamic -ldynamic1 -ldynamic2
ไลบรารีทั้งหมดหลังจากนั้น (รวมถึงระบบที่เชื่อมโยงโดย gcc โดยอัตโนมัติ) จะเชื่อมโยงแบบไดนามิก
gcc objectfiles -o program -Wl,-Bstatic -ls1 -ls2 -Wl,-Bdynamic -ld1 -ld2
คุณยังสามารถใช้: -static-libgcc -static-libstdc++
แฟล็กสำหรับไลบรารี gcc
เก็บไว้ในใจว่าถ้าlibs1.so
และlibs1.a
ทั้งสองมีอยู่ลิงเกอร์จะรับlibs1.so
ถ้าเป็นก่อนหรือหลัง-Wl,-Bstatic
-Wl,-Bdynamic
อย่าลืมผ่าน-L/libs1-library-location/
ก่อนโทร-ls1
.
-static
ที่ไหนสักแห่งในคำสั่งล้มเหลว (ฉันคิดว่ามันพยายามเชื่อมโยงสิ่งต่างๆแบบคงที่มากกว่าไลบรารีที่ฉันต้องการเท่านั้น)
-Wl,-Bstatic
และ-Wl,-Bdynamic
มีความสำคัญ
จาก manpage ของld
(สิ่งนี้ใช้ไม่ได้กับ gcc) อ้างถึง--static
ตัวเลือก:
คุณสามารถใช้ตัวเลือกนี้ได้หลายครั้งในบรรทัดคำสั่งซึ่งจะส่งผลต่อการค้นหาไลบรารีสำหรับอ็อพชัน -l ที่ตามมา
วิธีแก้ปัญหาอย่างหนึ่งคือใส่การอ้างอิงแบบไดนามิกของคุณก่อน--static
ตัวเลือกบนบรรทัดคำสั่ง
ความเป็นไปได้อีกประการหนึ่งคือการไม่ใช้--static
แต่ให้ชื่อไฟล์ / พา ธ แบบเต็มของไฟล์อ็อบเจ็กต์สแตติกแทน (เช่นไม่ใช้อ็อพชัน -l) สำหรับการลิงก์แบบสแตติกในไลบรารีเฉพาะ ตัวอย่าง:
# echo "int main() {}" > test.cpp
# c++ test.cpp /usr/lib/libX11.a
# ldd a.out
linux-vdso.so.1 => (0x00007fff385cc000)
libstdc++.so.6 => /usr/lib/libstdc++.so.6 (0x00007f9a5b233000)
libm.so.6 => /lib/libm.so.6 (0x00007f9a5afb0000)
libgcc_s.so.1 => /lib/libgcc_s.so.1 (0x00007f9a5ad99000)
libc.so.6 => /lib/libc.so.6 (0x00007f9a5aa46000)
/lib64/ld-linux-x86-64.so.2 (0x00007f9a5b53f000)
ดังที่คุณเห็นในตัวอย่างlibX11
ไม่ได้อยู่ในรายการของไลบรารีที่เชื่อมโยงแบบไดนามิกเนื่องจากมีการเชื่อมโยงแบบคงที่
ระวัง: .so
ไฟล์จะถูกเชื่อมโยงแบบไดนามิกเสมอแม้ว่าจะระบุด้วยชื่อไฟล์ / เส้นทางแบบเต็มก็ตาม
ldd a.out
อะไร
ldd
ส่งออกไลบรารีที่ใช้ร่วมกันที่ต้องการและ libX11 ไม่ปรากฏในรายการนั้น
ปัญหาตามที่ฉันเข้าใจมีดังนี้ คุณมีไลบรารีหลายไลบรารีบางแบบคงที่บางแบบไดนามิกและบางไลบรารีทั้งแบบคงที่และไดนามิก พฤติกรรมเริ่มต้นของgccคือการลิงก์ "ไดนามิกส่วนใหญ่" นั่นคือgccเชื่อมโยงไปยังไลบรารีแบบไดนามิกเมื่อเป็นไปได้ แต่จะกลับไปที่ไลบรารีแบบคงที่ เมื่อคุณใช้อ็อพชัน -staticเพื่อgccลักษณะการทำงานคือลิงก์เฉพาะไลบรารีแบบคงที่และออกโดยมีข้อผิดพลาดหากไม่พบไลบรารีแบบคงที่แม้ว่าจะมีไดนามิกไลบรารีที่เหมาะสมก็ตาม
อีกทางเลือกหนึ่งซึ่งฉันมีหลายครั้งที่ต้องการให้gccมีคือสิ่งที่ฉันเรียกว่า- เกือบคงที่และโดยพื้นฐานแล้วตรงกันข้ามกับ-dynamic (ค่าเริ่มต้น) - เกือบคงที่หากมีอยู่ชอบที่จะเชื่อมโยงกับไลบรารีแบบคงที่ แต่จะถอยกลับไปที่ไลบรารีแบบไดนามิก
ไม่มีตัวเลือกนี้ แต่สามารถจำลองได้ด้วยอัลกอริทึมต่อไปนี้:
การสร้างคำสั่งลิงค์ที่มีออกมารวมทั้ง-static
ทำซ้ำบนตัวเลือกลิงก์แบบไดนามิก
สะสมพา ธ ไลบรารีนั่นคืออ็อพชันเหล่านั้นของฟอร์ม-L <lib_dir>ในตัวแปร<lib_path>
สำหรับแต่ละอ็อพชันไดนามิกลิงก์เช่นฟอร์ม-l <lib_name>ให้รันคำสั่งgcc <lib_path> -print-file-name = lib <lib_name> .aและจับเอาต์พุต
หากคำสั่งพิมพ์สิ่งอื่นนอกเหนือจากสิ่งที่คุณส่งผ่านคำสั่งนั้นจะเป็นเส้นทางแบบเต็มไปยังไลบรารีแบบคงที่ แทนที่อ็อพชันไดนามิกไลบรารีด้วยพา ธ เต็มไปยังไลบรารีสแตติก
ล้างและทำซ้ำจนกว่าคุณจะประมวลผลบรรทัดคำสั่งลิงก์ทั้งหมด สคริปต์ยังสามารถใช้รายการชื่อไลบรารีเพื่อแยกออกจากการลิงก์แบบคงที่
สคริปต์ทุบตีต่อไปนี้ดูเหมือนจะทำเคล็ดลับ:
#!/bin/bash
if [ $# -eq 0 ]; then
echo "Usage: $0 [--exclude <lib_name>]. . . <link_command>"
fi
exclude=()
lib_path=()
while [ $# -ne 0 ]; do
case "$1" in
-L*)
if [ "$1" == -L ]; then
shift
LPATH="-L$1"
else
LPATH="$1"
fi
lib_path+=("$LPATH")
echo -n "\"$LPATH\" "
;;
-l*)
NAME="$(echo $1 | sed 's/-l\(.*\)/\1/')"
if echo "${exclude[@]}" | grep " $NAME " >/dev/null; then
echo -n "$1 "
else
LIB="$(gcc $lib_path -print-file-name=lib"$NAME".a)"
if [ "$LIB" == lib"$NAME".a ]; then
echo -n "$1 "
else
echo -n "\"$LIB\" "
fi
fi
;;
--exclude)
shift
exclude+=(" $1 ")
;;
*) echo -n "$1 "
esac
shift
done
echo
ตัวอย่างเช่น:
mostlyStatic gcc -o test test.c -ldl -lpthread
ในระบบของฉันส่งคืน:
gcc -o test test.c "/usr/lib/gcc/x86_64-linux-gnu/4.7/../../../x86_64-linux-gnu/libdl.a" "/usr/lib/gcc/x86_64-linux-gnu/4.7/../../../x86_64-linux-gnu/libpthread.a"
หรือด้วยการยกเว้น:
mostlyStatic --exclude dl gcc -o test test.c -ldl -lpthread
จากนั้นฉันจะได้รับ:
gcc -o test test.c -ldl "/usr/lib/gcc/x86_64-linux-gnu/4.7/../../../x86_64-linux-gnu/libpthread.a"
นอกจากนี้ยังมี-l:libstatic1.a
(ลบ l โคลอน) ตัวเลือก -l ใน gcc ซึ่งสามารถใช้เพื่อเชื่อมโยงไลบรารีแบบคงที่ (ขอบคุณข้อมูลhttps://stackoverflow.com/a/20728782 ) เป็นเอกสารหรือไม่? ไม่อยู่ในเอกสารอย่างเป็นทางการของ gcc (ซึ่งไม่แน่นอนสำหรับ libs ที่แชร์ด้วย): https://gcc.gnu.org/onlinedocs/gcc/Link-Options.html
-llibrary -l library
ค้นหาไลบรารีชื่อไลบรารีเมื่อทำการเชื่อมโยง (ทางเลือกที่สองที่มีไลบรารีเป็นอาร์กิวเมนต์แยกต่างหากสำหรับการปฏิบัติตาม POSIX เท่านั้นและไม่แนะนำ) ... ข้อแตกต่างเพียงอย่างเดียวระหว่างการใช้อ็อพชัน -l และการระบุชื่อไฟล์คือ -l ล้อมรอบไลบรารีที่มี 'lib' และ ".a" และค้นหาหลายไดเรกทอรี
binutils ld doc อธิบาย -lname
ตัวเลือกที่จะดำเนินการค้นหาlibname.so
แล้วสำหรับlibname.a
การเพิ่มคำนำหน้า lib และ.so
(ถ้าเปิดใช้งานในขณะนี้) หรือ.a
ต่อท้าย แต่-l:name
ตัวเลือกจะค้นหาเฉพาะชื่อที่ระบุเท่านั้น:
https://sourceware.org/binutils/docs/ld/Options.html
-l namespec --library=namespec
เพิ่มไฟล์เก็บถาวรหรืออ็อบเจ็กต์ที่ระบุโดย
namespec
รายการไฟล์ที่จะลิงก์ ตัวเลือกนี้สามารถใช้ได้กี่ครั้งก็ได้ ถ้าnamespec
เป็นในรูปแบบ:filename
, LD จะค้นหาเส้นทางห้องสมุดสำหรับไฟล์ที่เรียกว่ามิฉะนั้นมันจะค้นหาเส้นทางห้องสมุดสำหรับไฟล์ที่เรียกว่าfilename
libnamespec.a
บนระบบที่รองรับไลบรารีแบบแบ่งใช้ ld อาจค้นหาไฟล์อื่นที่ไม่ใช่
libnamespec.a
. โดยเฉพาะเกี่ยวกับเอลฟ์และ SunOS ระบบ LD จะค้นหาไดเรกทอรีสำหรับห้องสมุดที่เรียกว่า ก่อนที่จะค้นหาหนึ่งที่เรียกว่าlibnamespec.so
libnamespec.a
(ตามแบบแผน.so
ส่วนขยายระบุไลบรารีที่ใช้ร่วมกัน) โปรดทราบว่าพฤติกรรมนี้ใช้ไม่ได้กับ:filename
ซึ่งระบุไฟล์ที่เรียกว่าfilename
.ตัวเชื่อมโยงจะค้นหาที่เก็บถาวรเพียงครั้งเดียว ณ ตำแหน่งที่ระบุไว้ในบรรทัดคำสั่ง หากไฟล์เก็บถาวรกำหนดสัญลักษณ์ที่ไม่ได้กำหนดไว้ในอ็อบเจ็กต์บางอย่างซึ่งปรากฏก่อนไฟล์เก็บถาวรในบรรทัดคำสั่งตัวเชื่อมโยงจะรวมไฟล์ที่เหมาะสมจากไฟล์เก็บถาวร อย่างไรก็ตามสัญลักษณ์ที่ไม่ได้กำหนดในวัตถุที่ปรากฏในบรรทัดคำสั่งในภายหลังจะไม่ทำให้ตัวเชื่อมโยงค้นหาที่เก็บถาวรอีกครั้ง
ดู
-(
ตัวเลือกสำหรับวิธีบังคับให้ลิงก์ค้นหาที่เก็บถาวรหลายครั้งคุณสามารถแสดงรายการที่เก็บถาวรเดียวกันได้หลายครั้งในบรรทัดคำสั่ง
การค้นหาที่เก็บถาวรประเภทนี้เป็นมาตรฐานสำหรับ Unix linkers อย่างไรก็ตามหากคุณกำลังใช้ ld บน AIX โปรดสังเกตว่ามันแตกต่างจากลักษณะการทำงานของตัวเชื่อมโยง AIX
ตัวแปร-l:namespec
นี้ได้รับการบันทึกไว้ตั้งแต่เวอร์ชัน 2.18 ของ binutils (2007): https://sourceware.org/binutils/docs-2.18/ld/Options.html
รถตัก (ตัวเชื่อม) บางตัวมีสวิตช์สำหรับเปิดและปิดการโหลดแบบไดนามิก หาก GCC ทำงานบนระบบดังกล่าว (Solaris - และอื่น ๆ ) คุณสามารถใช้ตัวเลือกที่เกี่ยวข้องได้
หากคุณทราบว่าคุณต้องการเชื่อมโยงไลบรารีใดแบบสแตติกคุณสามารถระบุไฟล์ไลบรารีแบบคงที่ในบรรทัดลิงก์โดยใช้เส้นทางแบบเต็ม
ในการเชื่อมโยงไลบรารีไดนามิกและสแตติกภายในหนึ่งบรรทัดคุณต้องใส่สแตติก libs หลังไดนามิก libs และอ็อบเจ็กต์ไฟล์ดังนี้:
gcc -lssl main.o -lFooLib -o main
มิฉะนั้นมันจะไม่ทำงาน. ฉันต้องใช้เวลาพอสมควรในการคิดออก