Понимание того, как динамическая компоновка работает в UNIX
Рассмотрим следующую ситуацию:
- программа с именем
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
показывает, что это не так.
Кажется странным, что нужно перекомпилировать (переконфигурировать) каждый двоичный, который зависит от некоторой библиотеки каждый раз, когда изменения в библиотеке меняются. Какие решения у меня есть для предотвращения этого?
Ответы
Ответ 1
Проблема возникает, если вы не связали libfoo.so
с libbar
. Когда вы компилируете исполняемый файл, по умолчанию компоновщик не позволит вам оставить ссылки undefined. Однако, когда вы компилируете общую библиотеку, она будет - и она ожидает, что они будут удовлетворены во время соединения. Это значит, что 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
.
Ответ 2
В режиме динамической компоновки 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 program
Программа ldd
...
libfoo.so = > не найден
Обновить/etc/ld.so.cache
sudo ldconfig/home/tobias/projects/stubs/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 program
...
libfoo.so = > /home/tobias/projects/stubs/so/libfoo.so(0x00007f49236c7000)
libbar.so = > не найден
Это показывает, что динамический компоновщик все еще находит libfoo.so, но не libbar.so
Снова обновите /etc/ld.so.cache и перепроверьте.
sudo ldconfig/home/tobias/projects/stubs/so/
ldd libfoo.so
...
libbar.so = > /home/tobias/projects/stubs/so/libbar.so(0x00007f935e0bd000)
ldd program
...
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 - это своего рода повторение.
Странно или нет, что линкер должен знать зависимости библиотек, которые он связывает.
Есть много других способов реализовать это, но это было выбрано.
Ответ 3
Вы не указали системную информацию, используете ли вы glibc? Если да, то каков результат этой команды:
LD_DEBUG = программа для файлов
Также проверьте "Как писать общие (ELF) библиотеки" (pdf) (используете ли вы glibc или нет)
Ответ 4
Ваша программа не должна связываться с 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
.
Ответ 5
Это не должно быть так, если только что-то о символе bar_func не изменилось. Используйте команду "nm", чтобы получить дамп символов в вашей программе, а общий объект - увидеть, есть ли несоответствие и почему.