Подтвердить что ты не робот

Понимание того, как динамическая компоновка работает в 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 показывает, что это не так.

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

4b9b3361

Ответ 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", чтобы получить дамп символов в вашей программе, а общий объект - увидеть, есть ли несоответствие и почему.