menghubungkan statis hanya beberapa perpustakaan

108

Bagaimana cara saya menautkan secara statis beberapa pustaka tertentu ke biner saya saat menautkan dengan GCC?

gcc ... -static ...mencoba untuk secara statis menautkan semua pustaka yang ditautkan, tetapi saya belum mendapatkan versi statis dari beberapa di antaranya (misalnya: libX11).

peoro
sumber

Jawaban:

112

gcc -lsome_dynamic_lib code.c some_static_lib.a

Šimon Tóth
sumber
5
Pustaka tautan setelah file objek - terutama pustaka statis. Dalam versi kuno dan modern dari lingkungan tautan (saya tidak yakin dengan status quo untuk versi yang cukup lama pada November 2010), daftar pustaka statis sebelum code.cfile menjamin bahwa simbol di dalamnya akan diabaikan kecuali kebetulan ada sebuah main()fungsi dalam salah satu file perpustakaan objek.
Jonathan Leffler
44
Coule, tolong jelaskan tentang cara kerjanya? Jawaban kode saja tidak berguna untuk pemula.
jb.
8
@jb secara default, gcc menautkan secara dinamis. Saat Anda menggunakan -lsome_dynamic_lib, ini akan ditautkan secara dinamis seperti yang diharapkan. Namun, jika gcc diberikan pustaka statis secara eksplisit, ia akan selalu mencoba menautkannya secara statis. Namun demikian, ada beberapa detail rumit tentang urutan penyelesaian simbol; Saya tidak begitu yakin bagaimana cara kerjanya. Saya telah belajar bahwa, jika ragu, cobalah mengatur ulang urutan bendera perpustakaan :-)
bchurchill
4
ada masalah lincense jika Anda menautkan secara statis misalnya pustaka GPL
HiB
1
@HiB GPL menerapkan cara yang sama untuk tautan statis dan dinamis
osvein
50

Anda juga bisa menggunakan ldopsi-Bdynamic

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

Semua perpustakaan setelahnya (termasuk yang ditautkan oleh gcc secara otomatis) akan ditautkan secara dinamis.

Dmitry Yudakov
sumber
19
-Wl, -Bdynamic membutuhkan GNU ld, jadi solusi ini tidak bekerja pada sistem di mana gcc menggunakan sistem ld (misalnya Mac OS X).
poin
33
gcc objectfiles -o program -Wl,-Bstatic -ls1 -ls2 -Wl,-Bdynamic -ld1 -ld2

Anda juga dapat menggunakan: -static-libgcc -static-libstdc++flags untuk pustaka gcc

perlu diingat bahwa jika libs1.sodan libs1.akeduanya ada, penaut akan memilih libs1.sosebelum -Wl,-Bstaticatau sesudah -Wl,-Bdynamic. Jangan lupa untuk lulus -L/libs1-library-location/sebelum menelepon -ls1.

wgodoy.dll
sumber
1
Setidaknya, solusi ini berfungsi untuk tautan statis terhadap libgomp!
Jérôme
Ini berfungsi dengan baik untuk saya, saat menggunakan -staticsuatu tempat di perintah gagal (saya berasumsi itu mencoba untuk menghubungkan lebih banyak hal secara statis daripada hanya perpustakaan yang saya inginkan).
nh2
4
NB. Urutan -Wl,-Bstaticdan -Wl,-Bdynamicpenting.
Pavel Vlasov
27

Dari halaman manual ld(ini tidak bekerja dengan gcc), mengacu pada --staticopsi:

Anda dapat menggunakan opsi ini beberapa kali pada baris perintah: ini mempengaruhi pencarian perpustakaan untuk opsi -l yang mengikutinya.

Salah satu solusinya adalah meletakkan dependensi dinamis Anda sebelum --staticopsi pada baris perintah.

Kemungkinan lain adalah untuk tidak menggunakan --static, tetapi sebaliknya memberikan nama file / jalur lengkap dari file objek statis (yaitu tidak menggunakan opsi -l) untuk menghubungkan secara statis ke perpustakaan tertentu. Contoh:

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

Seperti yang Anda lihat di contoh, libX11tidak ada dalam daftar pustaka yang ditautkan secara dinamis, karena ditautkan secara statis.

Hati-hati: .soFile selalu ditautkan secara dinamis, bahkan ketika ditentukan dengan nama file / jalur lengkap.

ypnos
sumber
Apa hubungan antara libX11.a dan keluarannya ldd a.out?
Raffi Khatchadourian
1
Ah, begitu. lddmengeluarkan pustaka bersama yang diperlukan dan libX11 tidak muncul di daftar itu.
Raffi Khatchadourian
ini tidak jelas. Anda mengatakan 'opsi ini' dan 'opsi itu'. pilihan apa?
Octopus
19

Masalah yang saya pahami adalah sebagai berikut. Anda memiliki beberapa perpustakaan, beberapa statis, beberapa dinamis dan beberapa statis dan dinamis. Perilaku default gcc adalah menghubungkan "sebagian besar dinamis". Artinya, gcc menautkan ke pustaka dinamis jika memungkinkan, tetapi sebaliknya kembali ke pustaka statis. Saat Anda menggunakan opsi -static untuk meng - gcc , perilakunya adalah hanya menautkan pustaka statis dan keluar dengan kesalahan jika tidak ada pustaka statis yang dapat ditemukan, bahkan jika ada pustaka dinamis yang sesuai.

Pilihan lain, yang beberapa kali saya harapkan dari gcc , adalah apa yang saya sebut -mostly-static dan pada dasarnya kebalikan dari -dynamic (default). -mostly-static akan, jika ada, lebih memilih untuk menautkan ke pustaka statis tetapi akan kembali ke pustaka dinamis.

Opsi ini tidak ada tetapi dapat diemulasikan dengan algoritme berikut:

  1. Membangun baris perintah tautan tanpa menyertakan -static .

  2. Ulangi opsi tautan dinamis.

  3. Akumulasi jalur perpustakaan, yaitu opsi-opsi dalam bentuk -L <lib_dir> dalam variabel <lib_path>

  4. Untuk setiap opsi tautan dinamis, yaitu yang berformat -l <lib_name> , jalankan perintah gcc <lib_path> -print-file-name = lib <lib_name> .a dan tangkap hasilnya.

  5. Jika perintah mencetak sesuatu selain yang Anda berikan, itu akan menjadi jalur lengkap ke pustaka statis. Ganti opsi perpustakaan dinamis dengan jalur lengkap ke perpustakaan statis.

Bilas dan ulangi sampai Anda memproses seluruh baris perintah tautan. Secara opsional, skrip juga dapat mengambil daftar nama pustaka untuk dikecualikan dari penautan statis.

Skrip bash berikut tampaknya melakukan triknya:

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

Sebagai contoh:

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

saat sistem saya kembali:

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"

atau dengan pengecualian:

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

Saya kemudian mendapatkan:

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

Ada juga -l:libstatic1.avarian (minus l titik dua) opsi -l di gcc yang dapat digunakan untuk menghubungkan pustaka statis (Terima kasih kepada https://stackoverflow.com/a/20728782 ). Apakah itu didokumentasikan? Tidak ada dalam dokumentasi resmi gcc (yang juga tidak persis untuk perpustakaan bersama): https://gcc.gnu.org/onlinedocs/gcc/Link-Options.html

-llibrary
-l library 

Cari perpustakaan bernama perpustakaan saat menautkan. (Alternatif kedua dengan pustaka sebagai argumen terpisah hanya untuk kepatuhan POSIX dan tidak disarankan.) ... Satu-satunya perbedaan antara menggunakan opsi -l dan menentukan nama file adalah bahwa -l mengelilingi pustaka dengan 'lib' dan '.a' dan mencari beberapa direktori.

Doc binutils ld menjelaskannya. The -lnamepilihan akan melakukan pencarian untuk libname.sokemudian untuk libname.amenambahkan awalan lib dan .so(jika diaktifkan pada saat ini) atau .aakhiran. Tetapi -l:nameopsi hanya akan mencari persis untuk nama yang ditentukan: https://sourceware.org/binutils/docs/ld/Options.html

-l namespec
--library=namespec

Tambahkan arsip atau file objek yang ditentukan oleh namespecke daftar file untuk ditautkan. Opsi ini dapat digunakan beberapa kali. Jika namespecdalam bentuk :filename, ld akan mencari jalur perpustakaan untuk file bernama filename, jika tidak maka akan mencari jalur perpustakaan untuk file bernama libnamespec.a.

Pada sistem yang mendukung pustaka bersama, ld juga dapat mencari file selain libnamespec.a. Secara khusus, pada sistem ELF dan SunOS, ld akan mencari direktori untuk sebuah pustaka yang dipanggil libnamespec.sosebelum mencari pustaka yang dipanggil libnamespec.a. (Menurut konvensi, .soekstensi menunjukkan pustaka bersama.) Perhatikan bahwa perilaku ini tidak berlaku untuk :filename, yang selalu menentukan file yang dipanggil filename.

Linker akan mencari arsip hanya sekali, di lokasi yang ditentukan pada baris perintah. Jika arsip menentukan simbol yang tidak ditentukan di beberapa objek yang muncul sebelum arsip pada baris perintah, penaut akan menyertakan file yang sesuai dari arsip. Namun, simbol yang tidak ditentukan dalam objek yang muncul kemudian di baris perintah tidak akan menyebabkan linker mencari arsip lagi.

Lihat -(opsi cara memaksa linker untuk mencari arsip beberapa kali.

Anda dapat membuat daftar arsip yang sama beberapa kali pada baris perintah.

Jenis pencarian arsip ini adalah standar untuk linker Unix. Namun, jika Anda menggunakan ld di AIX, perhatikan bahwa ini berbeda dari perilaku linker AIX.

Varian -l:namespecini didokumentasikan sejak versi 2.18 binutils (2007): https://sourceware.org/binutils/docs-2.18/ld/Options.html

osgx
sumber
Opsi ini tampaknya berfungsi di mana yang lainnya gagal. Kami baru saja menemukan kasus di mana kami perlu menautkan-statis libjsoncpp.a, karena mesin build kami akan menghasilkan biner yang ditautkan ke libjsocpp.so.0, sedangkan OS target hanya menyediakan libjsoncpp.so.1. Sampai kami dapat menjernihkan perbedaan ini, ini adalah satu-satunya solusi yang memberikan hasil yang tepat dalam kasus kami.
Tomasz W
4

Beberapa loader (penaut) menyediakan sakelar untuk menghidupkan dan mematikan pemuatan dinamis. Jika GCC dijalankan pada sistem seperti itu (Solaris - dan mungkin lainnya), maka Anda dapat menggunakan opsi yang relevan.

Jika Anda tahu perpustakaan mana yang ingin Anda tautkan secara statis, Anda cukup menentukan file perpustakaan statis di baris tautan - dengan jalur lengkap.

Jonathan Leffler
sumber
6
Meskipun jawaban ini diterima, namun tidak mengatasi masalah sepenuhnya. Seperti yang dijelaskan @peoro, masalah yang dia coba selesaikan adalah dia tidak memiliki versi statis dari semua perpustakaan yang menyiratkan bahwa dia ingin menautkan sebanyak mungkin perpustakaan secara statis. Lihat jawaban saya.
jcoffland
2

untuk menghubungkan pustaka dinamis dan pustaka statis dalam satu baris, Anda harus meletakkan pustaka statis setelah berkas libs dan objek dinamis, seperti ini:

gcc -lssl main.o -lFooLib -o main

jika tidak, itu tidak akan berhasil. Aku butuh waktu untuk mengetahuinya.

Vincent
sumber