Исключить динамические зависимости из команды сборки? - PullRequest
0 голосов
/ 14 января 2019

Предположим, у нас есть библиотека libutils.so:

.
ldd libutils.so
...
libdependency.so
...

Предположим далее, что нам нужно создать приложение:

g++ appliation.cpp -lutils -o application

Можем ли мы опустить -ldependency в приведенной выше команде или написать:

g++ appliation.cpp -lutils -ldependency -o application

1 Ответ

0 голосов
/ 14 января 2019

Можем ли мы опустить -ldependency в приведенной выше команде

Если вы управляете связью самого libutils.so, да, вы можете. Иллюстрация:

main.c

extern void foo(void);

int main(void)
{
    foo();
    return 0;
}

foo.c

extern void bar(void);

void foo(void)
{
    bar();
}

bar.c

#include <stdio.h>

void bar(void)
{
    puts(__func__);
}

Мы создадим программу, которая зависит от libfoo.so, которая зависит от libbar.so. Сделайте объектные файлы:

$ gcc -Wall -c -fPIC foo.c bar.c
gcc -Wall -c main.c

Теперь ссылка libbar.so Без излишеств:

$ gcc -shared -o libbar.so bar.o

Следующая ссылка libfoo.so вот так:

$ gcc -shared -o libfoo.so foo.o -L. -lbar -Wl,-rpath=$(pwd)

Эффект -rpath linker option составляет:

-rpath = каталог

Добавить каталог в путь поиска библиотеки времени выполнения. Это используется при связывании исполняемого файла ELF с общими объектами. Все аргументы -rpath объединяются и передаются компоновщику среды выполнения, который использует их для поиска общих объектов во время выполнения. Опция -rpath также используется при поиске общих объектов, которые необходимы для общих объектов, явно включенных в ссылку; см. описание опции -rpath-link. Если -rpath не используется при связывании исполняемого файла ELF, содержимое переменной среды LD_RUN_PATH будет использоваться, если оно определено.

Результат таков:

$ objdump -x -j .dynamic libfoo.so | egrep '(RUNPATH|NEEDED)'
  NEEDED               libbar.so
  RUNPATH              /home/imk/develop/so/scrap

libfoo.so имеет запись NEEDED, вписанную в раздел .dynamic, в которой говорится, что библиотека имеет зависимость времени выполнения от libbar.so. Точно так же имеет запись RUNPATH о том, что зависимости во время выполнения можно искать в /home/imk/develop/so/scrap Это просто pwd, где я сделал связь: это не должно быть так долго, пока это действительно так каталог, в котором libbar.so можно найти, когда его ищет компоновщик или загрузчик.

Эта информация может быть прочитана компоновщиком, когда libbar.so связан с чем-то другим, и загрузчиком во время выполнения. Наконец, я могу связать prog так:

$ gcc -o prog main.o -L. -lfoo -Wl,-rpath=$(pwd)

Мне не нужно упоминать -lbar, потому что libfoo.so сам предоставляет компоновщику информация, от которой libfoo.so зависит libbar.so, и где ее искать.

Так как я также передал -rpath=$(pwd) в связи prog, мы видим, что прога предоставит эту информацию

$ objdump -x -j .dynamic prog | egrep '(RUNPATH|NEEDED)'
  NEEDED               libfoo.so
  NEEDED               libc.so.6
  RUNPATH              /home/imk/develop/so/scrap

загрузчику времени выполнения: prog необходимо libfoo.so, и его можно искать в /home/imk/develop/so/scrap. Когда загрузчик найдет libfoo.so и загрузит его, он будет узнайте из этого, что:

  NEEDED               libbar.so
  RUNPATH              /home/imk/develop/so/scrap

и в свою очередь найдет и загрузит libbar.so, что позволит ему разрешить все символы, упомянутые в процессе строительства. Следовательно, prog может быть запущено немедленно:

$ ./prog
bar

У меня не было , чтобы передать -rpath=$(pwd) в связи prog. Но если бы я не сделал:

$ gcc -o prog main.o -L. -lfoo
$ ./prog
./prog: error while loading shared libraries: libfoo.so: cannot open shared object file: No such file or directory

загрузчик не будет знать, где найти libfoo.so. См:

$ ldd prog
    linux-vdso.so.1 (0x00007ffffcc35000)
    libfoo.so => not found
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f4d1aff9000)
    /lib64/ld-linux-x86-64.so.2 (0x00007f4d1b5ec000)

И тогда мне придется прибегнуть к:

$ export LD_LIBRARY_PATH=.
$ ldd prog
    linux-vdso.so.1 (0x00007fff964dc000)
    libfoo.so => ./libfoo.so (0x00007fc2a7f35000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fc2a7b44000)
    libbar.so => ./libbar.so (0x00007fc2a7942000)
    /lib64/ld-linux-x86-64.so.2 (0x00007fc2a8339000)    
$ ./prog
bar

Позже

все еще немного неясно, достаточно ли присутствия libdependency.so в выводе ldd libutils.so, чтобы опустить -ldependencny во время связывания

Вам нужно задать хотя бы один и максимум два вопроса о выводе ldd utils.so: -

  1. Вывод ldd libutils.so сообщает о так называемом libdependency.so на всех ?
  2. Если «Да» для 1 , разрешает ли это так же имя в реальном файле?

Если нет для 1 , то libdutils.so не содержит информации о зависимости libdependency.so и вы должны указать -lutils -ldependency в любой дальнейшей связи.

Если Да для 1 , но Нет для 2 (т.е. ldd libutils.so сообщает libdependency.so => not found), тогда libutils.so имеет NEEDED запись для так называемого libdependency.so, но не a RUNPATH записи, с помощью которой компоновщик или Загрузчик может преобразовать это имя в любой фактический файл. В этом случае вы снова должны связать -lutils -ldependency, если связываете -lutils, чтобы компоновщик затем искал файл, который разрешает -ldependency. По крайней мере, вы должны делать это, пока ldd libutils.so все еще сообщает о libdependency.so => not found, когда вы делаете связь. Продолжайте читать ...

Если Да - 1 и Да - 2 , тогда вы можете сбросить -ldependency в дальнейшую связь , если это работать в той же среде, в которой вы запускали ldd libutils.so

Это предостережение необходимо, потому что, если ldd libutils.so разрешит libdependency.so, все, что вы знаете ldd удалось разрешить libdependency.so, используя алгоритм поиска загрузчика: -

  • Переменная окружения LD_LIBRARY_PATH (в активной оболочке), перечисляет каталог в котором найдено libdependency.so или
  • libutils.so обеспечивает RUNPATH, в котором находится libdependency.so или
  • libdependency.so находится в одном из каталогов, перечисленных в /etc/ld.so.conf (или его рекурсивном include -расширениях), или
  • libdependency.so находится в одном из доверенных поисковых каталогов загрузчика, /lib и /usr/lib

Если ldd может разрешить libdependency.so одним из этих четырех способов, то компоновщик сможет сделать это так же, , пока этот путь все еще успешен, когда вы делаете связь .

Итак, возвращаясь к моему примеру и моей связи:

$ gcc -shared -o libfoo.so foo.o -L. -lbar -Wl,-rpath=$(pwd)

После этого, спасибо -rpath=$(pwd). Я могу связать prog как:

$ gcc -o prog main.o -L. -lfoo

без упоминания -lbar, и это удается. Теперь я связываю libfoo.so вместо без -rpath:

$ gcc -shared -o libfoo.so foo.o -L. -lbar

после чего:

$ objdump -x -j .dynamic libfoo.so | egrep '(RUNPATH|NEEDED)'
  NEEDED               libbar.so

больше нет RUNPATH, а следовательно:

$ ldd libfoo.so
    linux-vdso.so.1 (0x00007ffda05e6000)
    libbar.so => not found

потому что загрузчик не может разрешить libbar.so любым другим способом.

Теперь я больше не могу связать prog без -lbar:

$ gcc -o prog main.o -L. -lfoo
/usr/bin/ld: warning: libbar.so, needed by ./libfoo.so, not found (try using -rpath or -rpath-link)
./libfoo.so: undefined reference to `bar'

Но если я сделаю:

$ export LD_LIBRARY_PATH=$(pwd)

, то:

$ ldd libfoo.so 
    linux-vdso.so.1 (0x00007ffe56d1e000)
    libbar.so => /home/imk/develop/so/scrap/libbar.so (0x00007fd2456e8000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fd2452f7000)
    /lib64/ld-linux-x86-64.so.2 (0x00007fd245aec000)

libfoo.so Зависимость libbar.so разрешается загрузчиком, используя LD_LIBRARY_PATH, и в так же по компоновщику:

$ gcc -o prog main.o -L. -lfoo; echo Done
Done

И если я очищу LD_LIBRARY_PATH снова:

$ unset LD_LIBRARY_PATH
$ gcc -o prog main.o -L. -lfoo; echo Done
/usr/bin/ld: warning: libbar.so, needed by ./libfoo.so, not found (try using -rpath or -rpath-link)
./libfoo.so: undefined reference to `bar'
collect2: error: ld returned 1 exit status
Done

возврат к сбою.

...