Понимание того, как динамическое связывание работает в UNIX - PullRequest
25 голосов
/ 05 ноября 2010

Рассмотрим следующую ситуацию:

  • программа с именем program, которая динамически зависит от libfoo.so
  • libfoo.so это не зависит ни от чего (ну, это зависит от libstdc++ и прочего, но я думаю, мы можем это опустить)

program отлично работает.

Внезапно libfoo кодирует изменения, и некоторые функции теперь используют внутренне func_bar() функцию, которая предоставляется другой библиотекой libbar.so.

libfoo.so перекомпилируется и теперь зависит от libbar.so. program остается неизменным, оно зависит только от libfoo.so.

Теперь, когда я выполняю program, он жалуется, что не может найти func_bar().

Вот мои вопросы:

  • libfoo.so интерфейс не изменился, только его реализация. Почему program должно явно связать с libbar.so?
  • Разве дерево зависимостей не рекурсивно? Я бы подумал, что поскольку libfoo.so зависит от libbar.so, libbar.so будет автоматически добавлено в список зависимостей program, без перекомпиляции . Однако ldd program показывает, что это не так.

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

Ответы [ 5 ]

17 голосов
/ 08 ноября 2010

Проблема возникает, когда вы не связали libfoo.so с libbar. Когда вы компилируете исполняемый файл, по умолчанию компоновщик не позволит вам оставить неопределенные ссылки. Однако, когда вы компилируете разделяемую библиотеку, она будет - и будет ожидать, что они будут удовлетворены во время компоновки. Это сделано для того, чтобы libfoo мог использовать функции, экспортируемые самим program - при попытке его запуска динамический компоновщик ожидает, что func_bar() будет предоставлен program. Проблема иллюстрируется так:

(foo.c является автономным)

export LD_RUN_PATH=`pwd`
gcc -Wall -shared foo.c -o libfoo.so
gcc -Wall -L. p.c -lfoo -o p

На данный момент, ./p работает правильно, как и следовало ожидать. Затем мы создаем libbar.so и модифицируем foo.c для его использования:

gcc -Wall -shared bar.c -o libbar.so
gcc -Wall -shared foo.c -o libfoo.so

В этот момент ./p дает ошибку, которую вы описываете. Если мы проверим ldd libfoo.so, мы заметим, что не имеют зависимость от libbar.so - это ошибка. Чтобы исправить ошибку, мы должны правильно связать libfoo.so:

gcc -Wall -L. -lbar -shared foo.c -o libfoo.so

В этот момент ./p снова работает правильно, а ldd libfoo.so показывает зависимость от libbar.so.

8 голосов
/ 05 ноября 2010

В Fedora динамическое связывание выполняется ld-linux.so.2. Динамический компоновщик использует /etc/ld.so.cache и /etc/ld.so.preload для поиска файлов библиотеки.

Запустите ldconfig, чтобы сообщить системе, где libfoo должна искать libbar.

ldconfig ищет в / lib, / usr / lib и любом каталоге, указанном в /etc/ld.so.conf. Вы можете проверить, какие библиотеки программа использует с ldd.

Более подробная информация доступна на страницах руководства по каждой команде.

Вот пример приложения, использующего разделяемые библиотеки.
Program.cc

#include "foo.h"
#include <iostream>

int main(int argc, char *argv[])
{
    for (int i = 0; i < argc; ++i) {
        std::cout << func_foo(argv[i]) << std::endl;
    }
}

foo.h

#ifndef FOO_H
#define FOO_H
#include <string>
std::string func_foo(std::string const &);
#endif

foo.cc

#include "foo.h"

std::string func_foo(std::string const &arg)
{
    return arg + "|" + __func__;
}

bar.h

#ifndef BAR_H
#define BAR_H
#include <string>
std::string func_bar();
#endif

bar.cc

#include "bar.h"

std::string func_bar()
{
    return __func__;
}

Сборка с использованием libfoo.so в качестве общей библиотеки.
g ++ -Wall -Wextra -fPIC -shared foo.cc -o libfoo.so
g ++ -lfoo -L./ -Wall -Wextra program.cc foo.h -o программа
Программа LDD
...
libfoo.so => ​​не найден

Обновление /etc/ld.so.cache
sudo ldconfig / home / tobias / проекты / заглушки / so /

ldd показывает, что динамический компоновщик находит libfoo.so
Программа LDD
...
libfoo.so => ​​/home/tobias/projects/stubs/so/libfoo.so (0x00007f0bb9f15000)

Добавить вызов libbar.so в libfoo.so
Новый foo.cc

#include "foo.h"
#include "bar.h"

std::string func_foo(std::string const &arg)
{
    return arg + "|" + __func__ + "|" + func_bar();
}

Сборка libbar.so и пересборка libfoo.so
g ++ -Wall -Wextra -fPIC -shared bar.cc -o libbar.so
g ++ -Wall -Wextra -fPIC -shared libbar.so foo.cc -o libfoo.so
ldd libfoo.so
...
libbar.so => ​​не найден

Программа LDD
...
libfoo.so => ​​/home/tobias/projects/stubs/so/libfoo.so (0x00007f49236c7000)
libbar.so => ​​не найден
Это показывает, что динамический компоновщик все еще находит libfoo.so, но не libbar.so
Снова обновите /etc/ld.so.cache и перепроверьте.
sudo ldconfig / home / tobias / проекты / заглушки / so /
ldd libfoo.so
...
libbar.so => ​​/home/tobias/projects/stubs/so/libbar.so (0x00007f935e0bd000)

Программа LDD
...
libfoo.so => ​​/home/tobias/projects/stubs/so/libfoo.so (0x00007f2be4f11000)
libbar.so => ​​/home/tobias/projects/stubs/so/libbar.so (0x00007f2be4d0e000)

Найдены как libfoo.so, так и libbar.so.

Обратите внимание, что этот последний шаг не влияет на прикладную программу. Если вы действительно строгий запуск ldconfig, это своего рода перекомпоновка. Странно или нет, компоновщик должен знать зависимости библиотек, на которые он ссылается. Есть много других способов реализовать это, но это было выбрано.

2 голосов
/ 05 ноября 2010

Вы не предоставили никакой системной информации, используете ли вы glibc?Если да, что вывод этой команды:

LD_DEBUG = файлы программы

Также проверьте «Как писать общие (ELF) библиотеки» (pdf)используете glibc или нет)

1 голос
/ 05 ноября 2010

Этого не должно быть, если что-то в символе bar_func не изменилось. Используйте команду «nm», чтобы получить дамп символов как в вашей программе, так и в общем объекте - посмотрите, есть ли несоответствие и почему.

1 голос
/ 05 ноября 2010

Ваша программа не должна связываться с libbar.so.

Я думаю, что проблема вызвана невозможностью указать libbar.so как зависимость libfoo.so при сборке более поздней версии. Я не уверен, какую систему сборки вы используете, но в CMake это можно сделать следующим образом:

add_library(bar SHARED bar.c)

add_library(foo SHARED foo.c)
target_link_libraries(foo bar)

add_executable(program program.c)
target_link_libraries(program foo)

Как видите, program связан только с foo (libfoo.so) и foo только с bar (libbar.so).

Или, может быть, libbar.so не может быть найден. Попробуйте указать путь к его каталогу в LD_LIBRARY_PATH переменной среды.

...