การลิงก์แบบคงที่เฉพาะบางไลบรารี


109

ฉันจะลิงก์เฉพาะไลบรารีเฉพาะบางส่วนกับไบนารีของฉันได้อย่างไรเมื่อเชื่อมโยงกับ GCC

gcc ... -static ...พยายามเชื่อมโยงไลบรารีที่เชื่อมโยงทั้งหมดแบบคงที่แต่ฉันไม่มีเวอร์ชันคงที่ของบางไลบรารี (เช่น libX11)


คำตอบ:


112

gcc -lsome_dynamic_lib code.c some_static_lib.a


5
เชื่อมโยงไลบรารีหลังไฟล์อ็อบเจ็กต์ - โดยเฉพาะไลบรารีแบบคงที่ ในสภาพแวดล้อมลิงก์เวอร์ชันเก่าและใหม่ (ฉันไม่แน่ใจในสถานะที่เป็นอยู่ของเวอร์ชันเก่าพอประมาณ ณ เดือนพฤศจิกายน 2010) การแสดงรายการไลบรารีแบบคงที่ก่อนที่code.cไฟล์จะรับประกันว่าสัญลักษณ์ในนั้นจะถูกละเว้นเว้นแต่จะเกิดขึ้นmain()ฟังก์ชั่นในหนึ่งในไฟล์วัตถุห้องสมุด
Jonathan Leffler

45
กรุณาอธิบายรายละเอียดเกี่ยวกับวิธีการทำงานนี้? คำตอบสำหรับรหัสเท่านั้นไม่ได้เป็นประโยชน์สำหรับผู้เริ่มต้น
jb.

8
@jb โดยค่าเริ่มต้นลิงก์ gcc แบบไดนามิก เมื่อคุณใช้ -lsome_dynamic_lib ระบบจะเชื่อมโยงแบบไดนามิกตามที่คาดไว้ แต่เมื่อ gcc ได้รับไลบรารีแบบคงที่อย่างชัดเจนมันจะพยายามเชื่อมโยงแบบคงที่เสมอ อย่างไรก็ตามมีรายละเอียดที่ยุ่งยากเกี่ยวกับลำดับการแก้ไขสัญลักษณ์ ฉันไม่ค่อยแน่ใจว่ามันทำงานอย่างไร ฉันได้เรียนรู้ว่าหากมีข้อสงสัยให้ลองจัดลำดับแฟล็กห้องสมุดใหม่ :-)
bchurchill

4
มีปัญหาเรื่องความลื่นหากคุณเชื่อมโยงแบบคงที่เช่นไลบรารี GPL
HiB

1
@HiB GPL ใช้วิธีเดียวกันกับการเชื่อมโยงแบบคงที่และแบบไดนามิก
osvein

51

คุณยังสามารถใช้ldตัวเลือก-Bdynamic

gcc <objectfiles> -static -lstatic1 -lstatic2 -Wl,-Bdynamic -ldynamic1 -ldynamic2

ไลบรารีทั้งหมดหลังจากนั้น (รวมถึงระบบที่เชื่อมโยงโดย gcc โดยอัตโนมัติ) จะเชื่อมโยงแบบไดนามิก


19
-Wl, -Bdynamic ต้องใช้ GNU ld ดังนั้นโซลูชันนี้จึงใช้ไม่ได้กับระบบที่ gcc ใช้ system ld (เช่น Mac OS X)
จุด

34
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.


1
อย่างน้อยวิธีนี้ใช้ได้กับการเชื่อมโยงแบบคงที่กับ libgomp!
Jérôme

สิ่งนี้ใช้ได้ดีสำหรับฉันในขณะที่ใช้-staticที่ไหนสักแห่งในคำสั่งล้มเหลว (ฉันคิดว่ามันพยายามเชื่อมโยงสิ่งต่างๆแบบคงที่มากกว่าไลบรารีที่ฉันต้องการเท่านั้น)
nh2

4
NB. ลำดับ-Wl,-Bstaticและ-Wl,-Bdynamicมีความสำคัญ
Pavel Vlasov

27

จาก 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ไฟล์จะถูกเชื่อมโยงแบบไดนามิกเสมอแม้ว่าจะระบุด้วยชื่อไฟล์ / เส้นทางแบบเต็มก็ตาม


Relationshop ระหว่าง libX11.a และเอาต์พุตของldd a.outอะไร
Raffi Khatchadourian

1
ฉันเห็น lddส่งออกไลบรารีที่ใช้ร่วมกันที่ต้องการและ libX11 ไม่ปรากฏในรายการนั้น
Raffi Khatchadourian

สิ่งนี้ไม่ชัดเจน คุณพูดว่า 'ตัวเลือกนี้' และ 'ตัวเลือกนั้น' ตัวเลือกอะไร
Octopus

19

ปัญหาตามที่ฉันเข้าใจมีดังนี้ คุณมีไลบรารีหลายไลบรารีบางแบบคงที่บางแบบไดนามิกและบางไลบรารีทั้งแบบคงที่และไดนามิก พฤติกรรมเริ่มต้นของgccคือการลิงก์ "ไดนามิกส่วนใหญ่" นั่นคือgccเชื่อมโยงไปยังไลบรารีแบบไดนามิกเมื่อเป็นไปได้ แต่จะกลับไปที่ไลบรารีแบบคงที่ เมื่อคุณใช้อ็อพชัน -staticเพื่อgccลักษณะการทำงานคือลิงก์เฉพาะไลบรารีแบบคงที่และออกโดยมีข้อผิดพลาดหากไม่พบไลบรารีแบบคงที่แม้ว่าจะมีไดนามิกไลบรารีที่เหมาะสมก็ตาม

อีกทางเลือกหนึ่งซึ่งฉันมีหลายครั้งที่ต้องการให้gccมีคือสิ่งที่ฉันเรียกว่า- เกือบคงที่และโดยพื้นฐานแล้วตรงกันข้ามกับ-dynamic (ค่าเริ่มต้น) - เกือบคงที่หากมีอยู่ชอบที่จะเชื่อมโยงกับไลบรารีแบบคงที่ แต่จะถอยกลับไปที่ไลบรารีแบบไดนามิก

ไม่มีตัวเลือกนี้ แต่สามารถจำลองได้ด้วยอัลกอริทึมต่อไปนี้:

  1. การสร้างคำสั่งลิงค์ที่มีออกมารวมทั้ง-static

  2. ทำซ้ำบนตัวเลือกลิงก์แบบไดนามิก

  3. สะสมพา ธ ไลบรารีนั่นคืออ็อพชันเหล่านั้นของฟอร์ม-L <lib_dir>ในตัวแปร<lib_path>

  4. สำหรับแต่ละอ็อพชันไดนามิกลิงก์เช่นฟอร์ม-l <lib_name>ให้รันคำสั่งgcc <lib_path> -print-file-name = lib <lib_name> .aและจับเอาต์พุต

  5. หากคำสั่งพิมพ์สิ่งอื่นนอกเหนือจากสิ่งที่คุณส่งผ่านคำสั่งนั้นจะเป็นเส้นทางแบบเต็มไปยังไลบรารีแบบคงที่ แทนที่อ็อพชันไดนามิกไลบรารีด้วยพา ธ เต็มไปยังไลบรารีสแตติก

ล้างและทำซ้ำจนกว่าคุณจะประมวลผลบรรทัดคำสั่งลิงก์ทั้งหมด สคริปต์ยังสามารถใช้รายการชื่อไลบรารีเพื่อแยกออกจากการลิงก์แบบคงที่

สคริปต์ทุบตีต่อไปนี้ดูเหมือนจะทำเคล็ดลับ:

#!/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"

7

นอกจากนี้ยังมี-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 จะค้นหาเส้นทางห้องสมุดสำหรับไฟล์ที่เรียกว่ามิฉะนั้นมันจะค้นหาเส้นทางห้องสมุดสำหรับไฟล์ที่เรียกว่าfilenamelibnamespec.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


ดูเหมือนว่าตัวเลือกนี้จะใช้งานได้เมื่อทุกอย่างล้มเหลว เราเพิ่งสะดุดกับกรณีที่เราต้องการสแตติกลิงค์ libjsoncpp.a เนื่องจากเครื่องสร้างของเราจะสร้างไบนารีที่เชื่อมโยงกับ libjsocpp.so.0 ในขณะที่ระบบปฏิบัติการเป้าหมายให้เฉพาะ libjsoncpp.so.1 จนกว่าเราจะสามารถล้างความแตกต่างนี้ได้นี่เป็นทางออกเดียวที่ให้ผลลัพธ์ที่เหมาะสมในกรณีของเรา
Tomasz W

4

รถตัก (ตัวเชื่อม) บางตัวมีสวิตช์สำหรับเปิดและปิดการโหลดแบบไดนามิก หาก GCC ทำงานบนระบบดังกล่าว (Solaris - และอื่น ๆ ) คุณสามารถใช้ตัวเลือกที่เกี่ยวข้องได้

หากคุณทราบว่าคุณต้องการเชื่อมโยงไลบรารีใดแบบสแตติกคุณสามารถระบุไฟล์ไลบรารีแบบคงที่ในบรรทัดลิงก์โดยใช้เส้นทางแบบเต็ม


6
แม้ว่าคำตอบนี้จะได้รับการยอมรับ แต่ก็ไม่ได้ช่วยแก้ปัญหาอย่างเต็มที่ ดังที่ @peoro อธิบายถึงปัญหาที่เขาพยายามแก้ไขคือเขาไม่มีไลบรารีเวอร์ชันคงที่ทั้งหมดซึ่งหมายความว่าเขาต้องการเชื่อมโยงห้องสมุดให้มากที่สุดแบบคงที่ ดูคำตอบของฉัน
jcoffland

2

ในการเชื่อมโยงไลบรารีไดนามิกและสแตติกภายในหนึ่งบรรทัดคุณต้องใส่สแตติก libs หลังไดนามิก libs และอ็อบเจ็กต์ไฟล์ดังนี้:

gcc -lssl main.o -lFooLib -o main

มิฉะนั้นมันจะไม่ทำงาน. ฉันต้องใช้เวลาพอสมควรในการคิดออก

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.