В чем разница между LD_LIBRARY_PATH и -L во время соединения?

У меня проблемы с LD_LIBRARY_PATH на ссылке (этот вопрос не имеет никакого отношения к времени выполнения).

Линия ссылок выглядит так, когда я запускаю make (это Linux-система с использованием g++ версии 4.1.x):

g++ a.o b.o c.o -o myapp \
 -L/long/path/to/libs/ \
 -L/another/long/path/ \
 -labc -ldef -lghi

Опции -l ссылаются на общие библиотеки (например, libabc.so), которые существуют в каталогах, указанных параметрами -l. Эти каталоги также отображаются в LD_LIBRARY_PATH. С этой конфигурацией ссылка удалась, и я могу запустить приложение.

Если я удалю каталоги из LD_LIBRARY_PATH, тогда я получу одну строку ошибки, например:

/usr/bin/ld: cannot find -labc

С другой стороны, если я удалю каталоги из списка опций -l, тогда я получаю много предупреждений, таких как:

/usr/bin/ld: warning: libabc.so, needed by /long/path/to/libs/libxyz.so,
    not found (try using -rpath or -rpath-link)

а затем еще много ошибок, например:

/long/path/to/libs/libdef.so: undefined reference to `Foo::Bar<Baz>::junk(Fred*)'

Может кто-нибудь объяснить разницу между LD_LIBRARY_PATH и -l? Я хотел бы понять этот материал в глубину, поэтому ссылки очень ценятся!

Кроме того, что мне нужно добавить к линии ссылок, чтобы избежать использования LD_LIBRARY_PATH?

EDIT: Когда каталоги отсутствовали в -l, компилятор предложил "попробовать использовать -rpath или -rpath-link". Я не думаю, что раньше я видел эти параметры в make файле. У вас есть? Не уверен, что это поможет решить проблему LD_LIBRARY_PATH.

Ответ 1

Настройки LD_LIBRARY_PATH имеют наивысший приоритет, поэтому, когда он установлен, набор каталогов, упомянутых в LD_LIBRARY_PATH, выполняется сначала даже до стандартного набора каталогов. Таким образом, в вашем случае настройка LD_LIBRARY_PATH влияет на поиск библиотеки, упомянутые в опции -l. Без LD_LIBRARY_PATH некоторые из зависимостей возможно, были решены из стандартного набора каталогов.

Хотя настройка LD_LIBRARY_PATH поможет с отладкой и опробовать новую версию библиотека, ее использование в общей установке и развертывании среды разработки считается плохим.

Также см. этот HOWTO из документации Linux для получения дополнительной информации об общих библиотеках

Ответ 2

Есть два ответа на этот вопрос: часть ответа заключается в привязке времени компиляции (i.e gcc -lfoo -L/usr/lib... которая, в свою очередь, вызывает ld) и поисках компоновщика времени выполнения.

Когда вы компилируете свою программу, компилятор проверяет синтаксис, а затем компоновщик гарантирует, что символы, необходимые для исполнения, существуют (например, переменные/методы/etc), между прочим. LD_LIBRARY_PATH, как было отмечено, имеет побочный эффект от изменения поведения gcc/ld, а также способ, которым ведет себя компоновщик времени выполнения, изменяя путь поиска.

Когда вы запускаете свою программу, компоновщик времени выполнения на самом деле извлекает разделяемые библиотеки (на диске или из памяти, если это возможно) и загружается в общие символы/код/​​и т.д. Опять же, LD_LIBRARY_PATH влияет на этот путь поиска неявно (иногда это не очень хорошо, как уже упоминалось).

Правильное исправление для этого без использования LD_LIBRARY_PATH для большинства систем Linux - это добавить путь, который содержит ваши разделяемые библиотеки, к /etc/ld.so.conf (или в некоторых дистрибутивах, создать файл в /etc/ld.so.conf.d/ с помощью пути в нем) и запустите ldconfig (/sbin/ldconfig как root), чтобы обновить кеш привязки компоновщика времени выполнения.

Пример на Debian:

[email protected]:~$ cat /etc/ld.so.conf.d/usrlocal.conf 
/usr/local/lib

Затем, когда программа выполняется, компоновщик времени выполнения будет выглядеть в этих каталогах для библиотек, с которыми был связан ваш двоичный файл.

Если вы хотите узнать, о каких библиотеках знает компоновщик времени выполнения, вы можете использовать:

[email protected]:~$ ldconfig -v 

/usr/lib:
libbfd-2.18.0.20080103.so -> libbfd-2.18.0.20080103.so
libkdb5.so.4 -> libkdb5.so.4.0
libXext.so.6 -> libXext.so.6.4.0

И, если вы хотите знать, с чем связаны бинарные библиотеки, вы можете использовать ldd как таковой, который скажет вам, в какой библиотеке будет выбран ваш компоновщик времени выполнения:

[email protected]:~$ ldd /bin/ls
linux-vdso.so.1 =>  (0x00007fffda1ff000)
librt.so.1 => /lib/librt.so.1 (0x00007f5d2149b000)
libselinux.so.1 => /lib/libselinux.so.1 (0x00007f5d2127f000)
libacl.so.1 => /lib/libacl.so.1 (0x00007f5d21077000)
libc.so.6 => /lib/libc.so.6 (0x00007f5d20d23000)

Ответ 3

LD_LIBRARY_PATH предназначен для поиска разделяемых библиотек при запуске приложения. Это побочный эффект, который влияет на вашу связь, и вы не должны полагаться на это.

Как часто нежелательный побочный эффект, LD_LIBRARY_PATH также будет искать на этапе link (ld) после каталогов указанным с помощью -L (также, если no -L flag дается).

Почему LD_LIBRARY_PATH плох

Ответ 4

Если бы я мог догадаться, я бы сказал, что компоновщик возвращается к использованию LD_LIBRARY_PATH для разрешения библиотек, что ваши прямые ссылки (например, libabc.so, libdef.so и libghi.so) динамически связаны с, Если посмотреть на страницу руководства для ld, похоже, что ссылка на .so, которая была построена с помощью -rpath, повлияет на то, как работает поиск динамически связанных символов.

Ответ 5

Проверяя man для g++, я обнаружил, что -lxxxxx параметр ищет libxxxxx.a в предоставленном пути -L, поэтому при времени связывания будет загружен только файл .a. Во время выполнения, если библиотека отсутствует, тогда будет загружена только библиотека как общий объект, поэтому .so будет загружена, а затем она будет выглядеть в LD_LIBRARY_PATH. В исполняемом файле, над которым я работаю, я вижу, что в некотором библиотечном каталоге есть версия libxxxx.a и libxxxx.so, поэтому я думаю, что это означает, что библиотека может быть связана во время связывания или связана во время выполнения в виде общий объект.

Если библиотека существует только как общий объект, то это означает, что путь каталога библиотеки должен быть LD_LIBRARY_PATH, который будет найден во время выполнения. Если библиотека существует только как архивированная так .a, то это означает, что ее нужно связать при сборке исполняемого файла, а затем -L directorypath и -lxxxxx необходимо предоставить в g++ во время компиляции.

Это мое понимание... и, по крайней мере, это соответствует вашим наблюдениям