chỉ liên kết tĩnh một số thư viện


108

Làm cách nào để chỉ liên kết tĩnh một số thư viện cụ thể với tệp nhị phân của tôi khi liên kết với GCC?

gcc ... -static ...cố gắng liên kết tĩnh tất cả các thư viện được liên kết, nhưng tôi không có phiên bản tĩnh của một số thư viện (ví dụ: libX11).


Câu trả lời:


112

gcc -lsome_dynamic_lib code.c some_static_lib.a


5
Liên kết các thư viện sau các tệp đối tượng - đặc biệt là các thư viện tĩnh. Trong các phiên bản cổ và hiện đại của môi trường liên kết (tôi không chắc về hiện trạng của các phiên bản cũ ở mức khiêm tốn kể từ tháng 11 năm 2010), liệt kê thư viện tĩnh trước khi code.ctệp đảm bảo rằng các ký hiệu trong đó sẽ bị bỏ qua trừ khi có một main()hàm trong một trong các tệp đối tượng thư viện.
Jonathan Leffler

44
Coule bạn vui lòng giải thích về cách thức hoạt động Câu trả lời chỉ có mã không hữu ích cho người mới bắt đầu.
jb.

8
@jb theo mặc định, liên kết gcc động. Khi bạn sử dụng -lsome_dynamic_lib, nó sẽ được liên kết động như mong đợi. Tuy nhiên, khi gcc được đưa ra một thư viện tĩnh rõ ràng, nó sẽ luôn cố gắng liên kết nó một cách tĩnh. Tuy nhiên, có một số chi tiết phức tạp về thứ tự các biểu tượng được giải quyết; Tôi không chắc nó hoạt động như thế nào. Tôi đã học được rằng, khi nghi ngờ, hãy thử sắp xếp lại thứ tự của cờ thư viện :-)
bchurchill

4
có một vấn đề lincense nếu bạn liên kết tĩnh, ví dụ như thư viện GPL
HiB 12/10/16

1
@HiB GPL áp dụng cách tương tự cho liên kết tĩnh và động
osvein

50

Bạn cũng có thể sử dụng ldtùy chọn-Bdynamic

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

Tất cả các thư viện sau nó (bao gồm cả các thư viện được liên kết bởi gcc tự động) sẽ được liên kết động.


19
-Wl, -Bdynamic yêu cầu GNU ld, vì vậy giải pháp này không hoạt động trên các hệ thống mà gcc sử dụng hệ thống ld (ví dụ: Mac OS X).
pts

33
gcc objectfiles -o program -Wl,-Bstatic -ls1 -ls2 -Wl,-Bdynamic -ld1 -ld2

bạn cũng có thể sử dụng: -static-libgcc -static-libstdc++flags cho thư viện gcc

hãy nhớ rằng nếu libs1.solibs1.acả hai đều tồn tại, trình liên kết sẽ chọn libs1.sonó trước -Wl,-Bstatichay sau -Wl,-Bdynamic. Đừng quên vượt qua -L/libs1-library-location/trước khi gọi -ls1.


1
Ít nhất, giải pháp này hoạt động để chống lại liên kết tĩnh chống lại libgomp!
Jérôme

Điều này hoạt động tốt đối với tôi, trong khi sử dụng -staticở đâu đó trong lệnh không thành công (tôi cho rằng nó cố gắng liên kết tĩnh nhiều thứ hơn là chỉ các thư viện tôi muốn).
nh2

4
NB. Thứ tự của -Wl,-Bstatic-Wl,-Bdynamiclà quan trọng.
Pavel Vlasov

27

Từ trang của ld(điều này không hoạt động với gcc), tham chiếu đến --statictùy chọn:

Bạn có thể sử dụng tùy chọn này nhiều lần trên dòng lệnh: nó ảnh hưởng đến việc tìm kiếm thư viện cho các tùy chọn -l theo sau nó.

Một giải pháp là đặt các phụ thuộc động của bạn trước --statictùy chọn trên dòng lệnh.

Một khả năng khác là không sử dụng --staticmà thay vào đó cung cấp tên tệp / đường dẫn đầy đủ của tệp đối tượng tĩnh (tức là không sử dụng tùy chọn -l) để liên kết tĩnh trong một thư viện cụ thể. Thí dụ:

# 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)

Như bạn có thể thấy trong ví dụ, libX11không có trong danh sách các thư viện được liên kết động, vì nó được liên kết tĩnh.

Lưu ý: Một .sotệp luôn được liên kết động, ngay cả khi được chỉ định với tên tệp / đường dẫn đầy đủ.


Mối quan hệ giữa libX11.a và đầu ra của là ldd a.outgì?
Raffi Khatchadourian

1
Ah tôi thấy. lddxuất các thư viện chia sẻ được yêu cầu và libX11 không xuất hiện trong danh sách đó.
Raffi Khatchadourian

điều này không rõ ràng. bạn nói 'tùy chọn này' và 'tùy chọn đó'. tùy chọn nào?
Octopus

19

Vấn đề như tôi hiểu nó là như sau. Bạn có một số thư viện, một số tĩnh, một số động và một số cả tĩnh và động. Hành vi mặc định của gcc là liên kết "chủ yếu là động". Có nghĩa là, gcc liên kết đến các thư viện động khi có thể nhưng nếu không sẽ trở lại thư viện tĩnh. Khi bạn sử dụng tùy chọn -static để gcc , hành vi là chỉ liên kết các thư viện tĩnh và thoát với lỗi nếu không tìm thấy thư viện tĩnh nào, ngay cả khi có thư viện động thích hợp.

Một tùy chọn khác, mà tôi có vài lần ước gcc có, là những gì tôi gọi là -mostly-static và về cơ bản là đối lập với -dynamic (mặc định). -mostly-static sẽ, nếu nó tồn tại, thích liên kết với các thư viện tĩnh nhưng sẽ trở lại thư viện động.

Tùy chọn này không tồn tại nhưng nó có thể được mô phỏng bằng thuật toán sau:

  1. Xây dựng dòng lệnh liên kết với out bao gồm -static .

  2. Lặp lại các tùy chọn liên kết động.

  3. Tích lũy các đường dẫn thư viện, tức là các tùy chọn đó của biểu mẫu -L <lib_dir> trong một biến <lib_path>

  4. Đối với mỗi tùy chọn liên kết động, tức là các tùy chọn có dạng -l <lib_name> , hãy chạy lệnh gcc <lib_path> -print-file-name = lib <lib_name> .a và chụp đầu ra.

  5. Nếu lệnh in một cái gì đó khác với những gì bạn đã truyền, nó sẽ là đường dẫn đầy đủ đến thư viện tĩnh. Thay thế tùy chọn thư viện động bằng đường dẫn đầy đủ đến thư viện tĩnh.

Rửa sạch và lặp lại cho đến khi bạn đã xử lý toàn bộ dòng lệnh liên kết. Theo tùy chọn, tập lệnh cũng có thể lấy danh sách tên thư viện để loại trừ khỏi liên kết tĩnh.

Tập lệnh bash sau dường như thực hiện thủ thuật:

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

Ví dụ:

mostlyStatic gcc -o test test.c -ldl -lpthread

trên hệ thống của tôi trả về:

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"

hoặc với một loại trừ:

mostlyStatic --exclude dl gcc -o test test.c -ldl -lpthread

Sau đó tôi nhận được:

gcc -o test test.c -ldl "/usr/lib/gcc/x86_64-linux-gnu/4.7/../../../x86_64-linux-gnu/libpthread.a"

7

Ngoài ra còn có biến thể -l:libstatic1.a(trừ l dấu hai chấm) của tùy chọn -l trong gcc có thể được sử dụng để liên kết thư viện tĩnh (Cảm ơn https://stackoverflow.com/a/20728782 ). Nó có được ghi lại không? Không có trong tài liệu chính thức của gcc (cũng không chính xác cho các lib được chia sẻ): https://gcc.gnu.org/onlineocs/gcc/Link-Options.html

-llibrary
-l library 

Tìm kiếm thư viện có tên thư viện khi liên kết. (Giải pháp thay thế thứ hai với thư viện làm đối số riêng chỉ dành cho tuân thủ POSIX và không được khuyến nghị.) ... Sự khác biệt duy nhất giữa việc sử dụng tùy chọn -l và chỉ định tên tệp là -l bao quanh thư viện bằng 'lib' và '.a' và tìm kiếm một số thư mục.

Tài liệu ld binutils mô tả nó. Các -lnametùy chọn sẽ làm cho việc tìm kiếm libname.sosau đó cho libname.athêm tiền tố lib và .so(nếu được kích hoạt tại thời điểm này) hoặc .ahậu tố. Nhưng -l:nametùy chọn sẽ chỉ tìm kiếm chính xác tên được chỉ định: https://sourceware.org/binutils/docs/ld/Options.html

-l namespec
--library=namespec

Thêm tệp lưu trữ hoặc tệp đối tượng được chỉ định bởi namespecdanh sách tệp để liên kết. Tùy chọn này có thể được sử dụng bất kỳ số lần nào. Nếu namespeccó dạng :filename, ld sẽ tìm kiếm đường dẫn thư viện cho tệp được gọi filename, nếu không, nó sẽ tìm kiếm đường dẫn thư viện cho tệp được gọi libnamespec.a.

Trên các hệ thống hỗ trợ thư viện được chia sẻ, ld cũng có thể tìm kiếm các tệp khác ngoài libnamespec.a. Cụ thể, trên hệ thống ELF và SunOS, ld sẽ tìm kiếm một thư mục cho một thư viện được gọi libnamespec.sotrước khi tìm kiếm một thư viện được gọi libnamespec.a. (Theo quy ước, .sophần mở rộng chỉ ra một thư viện được chia sẻ.) Lưu ý rằng hành vi này không áp dụng cho :filename, hành vi này luôn chỉ định một tệp được gọi filename.

Trình liên kết sẽ chỉ tìm kiếm một kho lưu trữ một lần, tại vị trí mà nó được chỉ định trên dòng lệnh. Nếu kho lưu trữ xác định một ký hiệu chưa được xác định trong một số đối tượng xuất hiện trước kho lưu trữ trên dòng lệnh, trình liên kết sẽ bao gồm (các) tệp thích hợp từ kho lưu trữ. Tuy nhiên, một ký hiệu không xác định trong một đối tượng xuất hiện sau đó trên dòng lệnh sẽ không khiến trình liên kết tìm kiếm lại kho lưu trữ.

Xem -(tùy chọn để biết cách buộc trình liên kết tìm kiếm các kho lưu trữ nhiều lần.

Bạn có thể liệt kê cùng một kho lưu trữ nhiều lần trên dòng lệnh.

Loại tìm kiếm kho lưu trữ này là tiêu chuẩn cho trình liên kết Unix. Tuy nhiên, nếu bạn đang sử dụng ld trên AIX, hãy lưu ý rằng nó khác với hoạt động của trình liên kết AIX.

Biến thể -l:namespecđược ghi lại từ phiên bản 2.18 của binutils (2007): https://sourceware.org/binutils/docs-2.18/ld/Options.html


Tùy chọn này dường như hoạt động khi mọi thứ khác không thành công. Chúng tôi chỉ tình cờ gặp trường hợp cần liên kết tĩnh libjsoncpp.a, vì các máy xây dựng của chúng tôi sẽ tạo ra các tệp nhị phân được liên kết với libjsocpp.so.0, trong khi target OS chỉ cung cấp libjsoncpp.so.1. Cho đến khi chúng tôi có thể làm rõ sự khác biệt này, đây là giải pháp duy nhất mang lại kết quả thích hợp trong trường hợp của chúng tôi.
Tomasz W

4

Một số trình tải (trình liên kết) cung cấp công tắc để bật và tắt tải động. Nếu GCC đang chạy trên một hệ thống như vậy (Solaris - và có thể cả những hệ thống khác), thì bạn có thể sử dụng tùy chọn liên quan.

Nếu bạn biết thư viện nào bạn muốn liên kết tĩnh, bạn có thể chỉ định tệp thư viện tĩnh trong dòng liên kết - bằng đường dẫn đầy đủ.


6
Mặc dù câu trả lời này đã được chấp nhận nhưng nó không giải quyết vấn đề một cách đầy đủ. Như @peoro đã giải thích vấn đề mà anh ấy đang cố gắng giải quyết là anh ấy không có phiên bản tĩnh của tất cả các thư viện, ngụ ý rằng anh ấy muốn liên kết càng nhiều thư viện càng tốt. Hãy xem câu trả lời của tôi.
jcoffland

2

để liên kết thư viện động và tĩnh trong một dòng, bạn phải đặt các lib tĩnh sau các lib động và tệp đối tượng, như thế này:

gcc -lssl main.o -lFooLib -o main

nếu không, nó sẽ không hoạt động. Tôi phải mất một lúc để tìm ra nó.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.