(см. Историю этого ответа, чтобы получить более сложный текст, но теперь я думаю, что читателю легче увидеть реальные командные строки).
Общие файлы, используемые всеми командами ниже
$ 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
Компоновщик выполняет поиск слева направо и отмечает неразрешенные символы. Если библиотека разрешает символ, она принимает объектные файлы этой библиотеки для разрешения символа (в данном случае b.o из 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
.