Ответы:
(Посмотрите историю этого ответа, чтобы получить более сложный текст, но теперь я думаю, что читателю легче увидеть реальные командные строки).
Общие файлы, используемые всеми командами ниже
$ 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
Компоновщик выполняет поиск слева направо и отмечает неразрешенные символы. Если библиотека разрешает символ, она принимает объектные файлы этой библиотеки для разрешения символа (в данном случае bo из 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 linker - это так называемый умный линкер. Он будет отслеживать функции, используемые предыдущими статическими библиотеками, постоянно отбрасывая те функции, которые не используются из его справочных таблиц. В результате, если вы связываете статическую библиотеку слишком рано, то функции в этой библиотеке больше не будут доступны статическим библиотекам позже в строке ссылки.
Типичный компоновщик 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» не передаст эти параметры компоновщик - и при этом он не будет отмечать ошибку. Он просто потерпит неудачу при ссылке с неопределенными символами, если вы неправильно указали порядок в библиотеке.
Вам нужно написать их как "-Wl, - start-group" и т. Д., Чтобы GCC передал аргумент компоновщику.
Порядок ссылок, безусловно, имеет значение, по крайней мере, на некоторых платформах. Я видел сбои для приложений, связанных с библиотеками в неправильном порядке (где неправильный означает A, связанный перед B, но B зависит от A).
Я видел это много, некоторые из наших модулей связывают более 100 библиотек нашего кода плюс системные и сторонние библиотеки.
В зависимости от разных компоновщиков HP / Intel / GCC / SUN / SGI / IBM / и т. Д. Вы можете получить неразрешенные функции / переменные и т. Д., На некоторых платформах вам придется дважды перечислять библиотеки.
По большей части мы используем структурированную иерархию библиотек, ядра, платформы, разных уровней абстракции, но для некоторых систем вам все равно придется играть с порядком в команде link.
Как только вы натолкнетесь на документ решения, чтобы следующему разработчику не пришлось снова его разрабатывать.
Мой старый лектор говорил: « высокая сплоченность и низкая связь », это все еще верно сегодня.
gcc
недавно изменился на более строгое (относительно).