Mengapa urutan pustaka yang ditautkan terkadang menyebabkan kesalahan dalam GCC?
Mengapa urutan pustaka yang ditautkan terkadang menyebabkan kesalahan dalam GCC?
Jawaban:
(Lihat sejarah pada jawaban ini untuk mendapatkan teks yang lebih rumit, tapi sekarang saya pikir lebih mudah bagi pembaca untuk melihat baris perintah yang sebenarnya).
File umum dibagikan oleh semua perintah di bawah ini
$ 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
Linker mencari dari kiri ke kanan, dan mencatat simbol yang belum terselesaikan. Jika pustaka menyelesaikan simbol, dibutuhkan file objek pustaka itu untuk menyelesaikan simbol (bo dari libb.a dalam kasus ini).
Ketergantungan perpustakaan statis terhadap satu sama lain bekerja sama - perpustakaan yang membutuhkan simbol harus terlebih dahulu, kemudian perpustakaan yang menyelesaikan simbol.
Jika pustaka statis tergantung pada pustaka lain, tetapi pustaka lain lagi tergantung pada pustaka sebelumnya, ada siklus. Anda dapat menyelesaikan ini dengan melampirkan pustaka yang bergantung secara siklis oleh -(
dan -)
, seperti -( -la -lb -)
(Anda mungkin perlu keluar dari parens, seperti -\(
dan -\)
). Linker kemudian mencari lib yang tertutup itu beberapa kali untuk memastikan ketergantungan bersepeda diselesaikan. Atau, Anda dapat menentukan perpustakaan beberapa kali, sehingga masing-masing sebelum satu sama lain: -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
Ini sama di sini - perpustakaan harus mengikuti file objek program. Perbedaannya di sini dibandingkan dengan perpustakaan statis adalah bahwa Anda tidak perlu peduli tentang dependensi perpustakaan terhadap satu sama lain, karena perpustakaan dinamis memilah dependensi mereka sendiri .
Beberapa distribusi terbaru rupanya default untuk menggunakan --as-needed
flag linker, yang memberlakukan bahwa file objek program datang sebelum perpustakaan dinamis. Jika bendera itu dilewati, penghubung tidak akan menautkan ke perpustakaan yang sebenarnya tidak dibutuhkan oleh yang dapat dieksekusi (dan mendeteksi ini dari kiri ke kanan). Distribusi archlinux saya yang terbaru tidak menggunakan flag ini secara default, jadi itu tidak memberikan kesalahan karena tidak mengikuti urutan yang benar.
Tidak benar untuk menghilangkan ketergantungan b.so
terhadap d.so
saat membuat yang pertama. Anda akan diminta untuk menentukan pustaka saat menautkan a
itu, tetapi a
tidak benar-benar membutuhkan integer b
itu sendiri, jadi itu tidak harus dibuat untuk peduli tentang b
dependensi sendiri.
Berikut adalah contoh implikasinya jika Anda lupa menentukan dependensinya 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"
Jika sekarang Anda melihat dependensi apa yang dimiliki biner, Anda perhatikan biner itu sendiri juga tergantung libd
, tidak libb
seperti yang seharusnya. Biner perlu ditaut ulang jika libb
nanti tergantung pada pustaka lain, jika Anda melakukannya dengan cara ini. Dan jika orang lain memuat libb
menggunakan dlopen
saat runtime (pikirkan memuat plugin secara dinamis), panggilan akan gagal juga. Jadi "right"
harus benar - benar wrong
juga.
lorder
+ program tsort
lakukan. Tetapi kadang-kadang tidak ada pesanan, jika Anda memiliki referensi siklik. Maka Anda hanya perlu menelusuri daftar pustaka sampai semuanya terselesaikan.
GNU ld linker adalah yang disebut smart linker. Ini akan melacak fungsi yang digunakan oleh perpustakaan statis sebelumnya, secara permanen membuang fungsi-fungsi yang tidak digunakan dari tabel pencariannya. Hasilnya adalah jika Anda menautkan pustaka statis terlalu dini, maka fungsi di pustaka itu tidak lagi tersedia untuk pustaka statis nanti di baris tautan.
Tautan UNIX yang umum berfungsi dari kiri ke kanan, jadi letakkan semua pustaka Anda yang tergantung di sebelah kiri, dan pustaka yang memenuhi dependensi tersebut di sebelah kanan garis tautan. Anda mungkin menemukan bahwa beberapa perpustakaan bergantung pada yang lain sementara pada saat yang sama perpustakaan lain bergantung pada mereka. Di sinilah semakin rumit. Ketika datang ke referensi melingkar, perbaiki kode Anda!
Berikut adalah contoh untuk memperjelas bagaimana hal-hal bekerja dengan GCC ketika perpustakaan statis terlibat. Jadi mari kita asumsikan kita memiliki skenario berikut:
myprog.o
- mengandung main()
fungsi, tergantung padalibmysqlclient
libmysqlclient
- statis, untuk contoh (Anda lebih suka perpustakaan bersama, tentu saja, karena libmysqlclient
sangat besar); di /usr/local/lib
; dan tergantung pada hal-hal darilibz
libz
(dinamis)Bagaimana kami menautkan ini? (Catatan: contoh dari kompilasi di Cygwin menggunakan 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
Jika Anda menambahkan -Wl,--start-group
ke bendera tautan, tidak peduli urutannya atau apakah ada dependensi melingkar.
Pada Qt ini berarti menambahkan:
QMAKE_LFLAGS += -Wl,--start-group
Menghemat banyak waktu untuk bermain-main dan sepertinya tidak memperlambat banyak tautan (yang tentunya memakan waktu jauh lebih sedikit daripada kompilasi).
Anda dapat menggunakan opsi -Xlinker.
g++ -o foobar -Xlinker -start-group -Xlinker libA.a -Xlinker libB.a -Xlinker libC.a -Xlinker -end-group
hampir sama dengan
g++ -o foobar -Xlinker -start-group -Xlinker libC.a -Xlinker libB.a -Xlinker libA.a -Xlinker -end-group
Hati-hati!
Kiat cepat yang membuat saya tersandung: jika Anda memanggil tautan sebagai "gcc" atau "g ++", kemudian menggunakan "--start-group" dan "--end-group" tidak akan meneruskan opsi itu ke linker - juga tidak akan menandai kesalahan. Itu hanya akan gagal tautan dengan simbol yang tidak terdefinisi jika Anda salah memesan perpustakaan.
Anda harus menuliskannya sebagai "-Wl, - start-group" dll. Untuk memberi tahu GCC agar meneruskan argumen tersebut ke tautan.
Urutan tautan tentu berpengaruh, setidaknya pada beberapa platform. Saya telah melihat crash untuk aplikasi yang terhubung dengan perpustakaan dalam urutan yang salah (di mana salah berarti A terhubung sebelum B tetapi B tergantung pada A).
Saya sudah sering melihat ini, beberapa modul kami terhubung lebih dari 100 pustaka dari kode kami ditambah sistem & lib pihak ke-3.
Bergantung pada tautan yang berbeda, HP / Intel / GCC / SUN / SGI / IBM / dll. Anda bisa mendapatkan fungsi / variabel yang tidak terselesaikan, dll, pada beberapa platform Anda harus membuat daftar pustaka dua kali.
Untuk sebagian besar kami menggunakan hierarki terstruktur dari perpustakaan, inti, platform, berbagai lapisan abstraksi, tetapi untuk beberapa sistem Anda masih harus bermain dengan urutan dalam perintah tautan.
Setelah Anda menemukan dokumen solusi sehingga pengembang berikutnya tidak harus menyelesaikannya lagi.
Dosen lama saya dulu berkata, " kohesi tinggi & kopling rendah ", itu masih berlaku sampai sekarang.
gcc
berubah menjadi perilaku yang lebih ketat (relatif) baru-baru ini.