Відповіді:
(Дивіться історію цієї відповіді, щоб отримати більш детальний текст, але зараз я думаю, що читачеві легше бачити реальні командні рядки).
Загальні файли, якими користуються всі команди нижче
$ 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
Лінк шукає зліва направо і відзначає нерозв’язані символи. Якщо бібліотека розв’язує символ, для вирішення цього символу потрібні файли об'єктів цієї бібліотеки (в цьому випадку не входить libb.a).
Залежності статичних бібліотек одна від одної працюють однаково - спочатку повинна бути бібліотека, якій потрібні символи, потім бібліотека, яка розв’язує символ.
Якщо статична бібліотека залежить від іншої бібліотеки, але інша бібліотека знову залежить від колишньої бібліотеки, виникає цикл. Ви можете вирішити це шляхом додавання циклічно залежних бібліотек до -(
та -)
, наприклад, як -( -la -lb -)
(можливо, вам знадобиться уникнути паролів, таких як -\(
і -\)
). Потім лінкер кілька разів шукає ті вкладені вкладки, щоб забезпечити усунення залежностей від велосипедного руху. Крім того , ви можете вказати в бібліотеках кілька разів, так що кожен друг перед другом: -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
Тут те саме - бібліотеки повинні слідувати об’єктним файлам програми. Різниця тут порівняно зі статичними бібліотеками полягає в тому, що вам не потрібно дбати про залежності бібліотек одна від одної, оскільки динамічні бібліотеки самі розбирають свої залежності .
У деяких останніх дистрибутивах, очевидно, за замовчуванням використовується --as-needed
прапор лінкера, який примушує, що файли об'єктів програми надходять до динамічних бібліотек. Якщо цей прапор буде передано, лінкер не посилатиметься на бібліотеки, які не потрібні виконуваному файлу (і він виявляє це зліва направо). Мій останній дистрибутив archlinux не використовує цей прапор за замовчуванням, тому він не видав помилку за недотримання правильного порядку.
Неправильно опускати залежність b.so
проти d.so
при створенні першої. a
Тоді вам потрібно буде вказати бібліотеку при посиланні , але a
це дійсно не потребує самого цілого числа b
, тому його не слід робити, щоб дбати про b
власні залежності.
Ось приклад наслідків, якщо ви пропустите вказати залежності 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"
Якщо ви зараз вивчите, які залежності мають бінарні файли, то зауважте, що саме бінарне також залежить від того libd
, libb
як воно повинно. Бінарний файл потрібно буде повторно пов’язати, якщо libb
пізніше це залежить від іншої бібліотеки, якщо ви зробите це так. І якщо хтось ще завантажує libb
користування dlopen
під час виконання (подумайте про завантаження плагінів динамічно), дзвінок також буде невдалим. Тож "right"
справді має бути wrong
також.
lorder
+ tsort
. Але іноді порядку немає, якщо у вас є циклічні посилання. Тоді вам просто доведеться переглядати список бібліотек, поки все не буде вирішено.
Лінкер GNU ld - це так званий розумний лінкер. Він буде відслідковувати функції, використовувані попередніми статичними бібліотеками, постійно викидаючи ті функції, які не використовуються зі своїх таблиць пошуку. Результат полягає в тому, що якщо ви зв’яжете статичну бібліотеку занадто рано, то функції цієї бібліотеки більше не доступні статичним бібліотекам пізніше у рядку посилань.
Типовий лінкер UNIX працює зліва направо, тому поставте всі залежні бібліотеки зліва та ті, які задовольняють ці залежності праворуч від лінії зв'язку. Ви можете виявити, що деякі бібліотеки залежать від інших, тоді як інші бібліотеки залежать від них. Тут це ускладнюється. Якщо мова йде про кругові посилання, виправте свій код!
Ось приклад, щоб зрозуміти, як все працює з GCC, коли задіяні статичні бібліотеки. Тож припустимо, що у нас є такий сценарій:
myprog.o
- містить main()
функцію, залежно відlibmysqlclient
libmysqlclient
- статичні, для прикладу (ви б, звичайно, віддавали перевагу спільній бібліотеці, оскільки libmysqlclient
величезна кількість); в /usr/local/lib
; і залежать від речей відlibz
libz
(динамічний)Як ми це пов’язуємо? (Примітка: приклади компіляції на Cygwin за допомогою 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
Якщо ви додасте -Wl,--start-group
до прапорців посилання, не важливо, в якому порядку вони перебувають, чи є кругові залежності.
На Qt це означає додавання:
QMAKE_LFLAGS += -Wl,--start-group
Економить навантажень часу, що заплутується, і, схоже, це не сповільнить багато зв'язків (що займає набагато менше часу, ніж компіляція).
Ви можете використовувати опцію -Xlinker.
g++ -o foobar -Xlinker -start-group -Xlinker libA.a -Xlinker libB.a -Xlinker libC.a -Xlinker -end-group
дорівнює АЛМОСТ
g++ -o foobar -Xlinker -start-group -Xlinker libC.a -Xlinker libB.a -Xlinker libA.a -Xlinker -end-group
Обережно!
Швидкий підказок, який підключив мене: якщо ви посилаєтеся на посилання як "gcc" або "g ++", то використання "--start-group" і "--end-group" не передасть ці параметри до Linker - і він не позначить помилку. Він просто провалить посилання з невизначеними символами, якщо ви неправильно замовили бібліотеку.
Вам потрібно записати їх як "-Wl, - start-group" тощо, щоб сказати GCC передати аргумент до лінкера.
Порядок посилання, безумовно, має значення, принаймні, на деяких платформах. Я бачив збої в додатках, пов’язаних з бібліотеками в неправильному порядку (де неправильний означає A, пов’язаний перед B, але B залежить від A).
Я бачив це багато, деякі з наших модулів посилаються на понад 100 бібліотек нашого коду, плюс система та сторонні бібліотеки.
Залежно від різних лінкерів HP / Intel / GCC / SUN / SGI / IBM / тощо, ви можете отримати невирішені функції / змінні тощо, на деяких платформах вам доведеться перераховувати бібліотеки двічі.
Здебільшого ми використовуємо структуровану ієрархію бібліотек, ядра, платформи, різних шарів абстракції, але для деяких систем вам все одно доводиться грати з порядком у команді посилання.
Після того, як ви натрапите на документ рішення, його наступному розробнику не доведеться працювати над ним знову.
Мій старий викладач говорив: " висока згуртованість і низька зв'язок ", це все ще актуально сьогодні.
gcc
змінений на більш-суворе поводження (відносно) в останній час .