Почему ld need-rpath-link при связывании исполняемого файла с so, который нуждается в другом so?

мне просто любопытно. Я создал общий объект:

gcc -o liba.so -fPIC -shared liba.c

и еще один общий объект, который связывается с первым:

gcc -o libb.so -fPIC -shared libb.c liba.so

теперь при создании исполняемого файла, который ссылается на libb.so, мне нужно будет указать-rpath-link на ld, чтобы он мог найти liba.so, когда обнаружил, что libb.so зависит от этого:

gcc -o test -Wl,-rpath-link,./ test.c libb.so

в противном случае LD будет жаловаться.

почему это, что ld должен быть в состоянии найти liba.so когда связывание test? Потому что мне не кажется, что ld делает что-то еще, кроме подтверждения 'ы. Например, бег readelf --dynamic ./test только списки libb.so по мере необходимости, поэтому я думаю, что динамический компоновщик должен обнаружить libb.so -> liba.so зависимость сама по себе, и сделать это собственный поиск liba.so.

я на платформе x86-64 GNU / Linux, и main () - routine в test вызывает функцию в libb.so, что в свою очередь вызывает функцию в liba.so.

4 ответов


почему это, что ld должен быть в состоянии найти liba.so при связывании test? Потому что мне не кажется, что ld делает что-то еще, кроме подтверждения liba.so's существование. Например, бег readelf --dynamic ./test только списки libb.so по мере необходимости, поэтому я думаю, что динамический компоновщик должен обнаружить libb.so -> liba.so зависимость сама по себе, и сделать это собственный поиск liba.so.

Ну, если я правильно понимаю процесс связывания,ld на самом деле не нужно найти еще libb.so. Он может просто игнорировать все неразрешенные ссылки в test надеясь, что динамический компоновщик разрешит их при загрузке libb.so во время выполнения. Но если ... --66-->ld таким образом, многие ошибки "неопределенной ссылки" не будут обнаружены во время ссылки, вместо этого они будут найдены при попытке загрузить test в runtime. Так что ld просто делает дополнительную проверку, что все символы не найдены в test себя можно действительно найти в shared библиотеки, которые test зависит от. Так что если test программа имеет ошибку" неопределенная ссылка " (некоторая переменная или функция не найдена в и ни libb.so), это становится очевидным во время ссылки, а не только во время выполнения. Таким образом, такое поведение является лишь дополнительной проверкой здравомыслия.

но ld идет еще дальше. При ссылке test, ld также проверяет, что все неразрешенные ссылки в libb.so находятся в общих библиотеках, которые libb.so зависит (в нашем случае libb.so зависит от liba.so, поэтому он требует liba.so быть расположенным на времени соединения). Ну, вообще-то ld уже сделал эту проверку, когда он связывал libb.so. Почему он делает эту проверку во второй раз... Может быть, разработчики ld нашел эту двойную проверку полезной для обнаружения сломанных зависимостей при попытке связать вашу программу с устаревшей библиотекой, которая может быть загружена во времена, когда она была связана, но теперь она не может загружаться, потому что библиотеки, от которых он зависит, обновляются (например,liba.so позже был переработан, и часть функции была удалена из него).

UPD

просто сделал несколько экспериментов. Кажется, мое предположение "на самом деле ld уже сделал эту проверку, когда он связывал libb.so" - это неправильно.

допустим liba.c имеет следующее содержание:

int liba_func(int i)
{
    return i + 1;
}

и libb.c имеет далее:

int liba_func(int i);
int liba_nonexistent_func(int i);

int libb_func(int i)
{
    return liba_func(i + 1) + liba_nonexistent_func(i + 2);
}

и test.c

#include <stdio.h>

int libb_func(int i);

int main(int argc, char *argv[])
{
    fprintf(stdout, "%d\n", libb_func(argc));
    return 0;
}

при связывании libb.so:

gcc -o libb.so -fPIC -shared libb.c liba.so

linker не генерирует никаких сообщений об ошибках, которые liba_nonexistent_func невозможно решить, вместо этого он просто молча генерирует сломанную общую библиотеку libb.so. Поведение такое же, как и при создании статической библиотеки (libb.a) С ar который также не разрешает символы сгенерированной библиотеки.

но когда вы пытаетесь связать test:

gcc -o test -Wl,-rpath-link=./ test.c libb.so

вы получили ошибку:

libb.so: undefined reference to `liba_nonexistent_func'
collect2: ld returned 1 exit status

обнаружение такой ошибки было бы невозможно, если ld не сканировал рекурсивно все общие библиотеки. Поэтому кажется, что ответ на вопрос такой же, как я сказал выше: ld должен - rpath-link чтобы убедиться, что связанный исполняемый файл может быть загружен позже динамической загрузкой. Просто вменяемость проверять.

UPD2

имеет смысл проверить наличие неразрешенных ссылок как можно раньше (при связывании libb.so), но ld по каким-то причинам не делает этого. Вероятно, это позволяет создавать циклические зависимости для общих библиотек.

liba.c может иметь следующую реализацию:

int libb_func(int i);

int liba_func(int i)
{
    int (*func_ptr)(int) = libb_func;
    return i + (int)func_ptr;
}

так liba.so использует libb.so и libb.so использует liba.so (лучше никогда этого не делать). Этот успешно компилирует и работает:

$ gcc -o liba.so -fPIC -shared liba.c
$ gcc -o libb.so -fPIC -shared libb.c liba.so
$ gcc -o test test.c -Wl,-rpath=./ libb.so
$ ./test
-1217026998

хотя readelf выступает говорит, что liba.so не нужно libb.so:

$ readelf -d liba.so | grep NEEDED
 0x00000001 (NEEDED)                     Shared library: [libc.so.6]
$ readelf -d libb.so | grep NEEDED
 0x00000001 (NEEDED)                     Shared library: [liba.so]
 0x00000001 (NEEDED)                     Shared library: [libc.so.6]

если ld проверено на наличие неразрешенных символов во время связывания общей библиотеки, связывание liba.so было бы невозможно.

обратите внимание, что я использовал - rpath ключ вместо - rpath-link. Разница в том, что - rpath-link используется во время связывания только для проверки того, что все символы в конечном исполняемом файле могут быть разрешены, тогда как - rpath фактически вставляет путь, который вы указываете в качестве параметра в ELF:

$ readelf -d test | grep RPATH
 0x0000000f (RPATH)                      Library rpath: [./]

так что теперь можно запустить test если общие библиотеки (liba.so и libb.so) находятся в вашем текущем рабочем каталоге (./). Если вы просто использовали - rpath-link такой записи не будет в test ELF, и вам нужно будет добавить путь к общему библиотеки в или LD_LIBRARY_PATH переменные среды.

UPD3

на самом деле можно проверить наличие неразрешенных символов во время связывания общей библиотеки, --no-undefined опция должна использоваться для этого:

$ gcc -Wl,--no-undefined -o libb.so -fPIC -shared libb.c liba.so
/tmp/cc1D6uiS.o: In function `libb_func':
libb.c:(.text+0x2d): undefined reference to `liba_nonexistent_func'
collect2: ld returned 1 exit status

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


Вы систему, через ld.so.conf, ld.so.conf.d, и системная среда,LD_LIBRARY_PATH, etc.. обеспечивает нет в LD_RUN_PATH так ld понадобится способ размещения liba во время компиляции исполняемого файла, либо с rpath (описано выше) или путем предоставления явного пути поиска к нему.

во-вторых, что на самом деле означает" включить его в ссылку". Мне кажется что это просто означает: "подтверждаю это существование "(либа.так и есть), так как либб.таким образом, заголовки ELF не изменяются (у них уже был необходимый тег против либы.so), а заголовки exec объявляют только libb.так как НЕОБХОДИМЫЙ. Почему ld заботится о поиске liba.так что, может он просто не уйдет задача компоновщика времени выполнения?

нет, вернемся к семантике ld. Для того, чтобы произвести "хорошая ссылка", ld должны быть в состоянии найти все зависимые библиотеки. ld не может обеспечить хорошую связь иначе. Runtime-компоновщик должен найти и загрузить не только найти общие библиотеки необходимые программы. ld не может гарантировать, что произойдет, если можете найти все необходимые общие библиотеки на момент проножки связано.


Я думаю, вам нужно знать, когда использовать и . Сначала я цитирую, что man ld указано :

  1. разница между-rpath и-rpath-link заключается в том, что каталоги, указанные-rpath параметры включены в исполняемый файл и используются во время выполнения, в то время как параметр-rpath-link действует только во время соединения. Испытующий - rpath таким образом поддерживается только родными компоновщиками и кросс-компоновщиками, которые были настроено с параметром --with-sysroot.

необходимо различать компоновки и выполнения. Согласно вашему принятому ответу anton_rh, проверка неопределенных символов не включена при компиляции и связывании общих библиотек или статических библиотек, но включена при компиляции и связывании исполняемых файлов. (Однако обратите внимание, что существуют некоторые файлы, которые являются общей библиотекой, а также исполняемыми файлами, например,ld.so. Тип man ld.so для изучения этого, и я не знаю, включена ли проверка неопределенных символов при компиляции этих файлов "двойственных" видов).

так -rpath-link используется в проверке времени ссылки и -rpath используется для компоновки и выполнения, потому что rpath встроен в заголовки ELF. Но вы должны быть осторожны, что переопределяет -rpath опция во время link-time, если оба они указаны.

но все же, почему -rpath-option и ? Я думаю, что они используются для устранение "overlinking". Смотрите это лучшее понимание вторичных зависимостей Linux, решающих с примерами., просто использовать ctrl + F для перехода к содержимому, связанному с "overlinking". Вы должны сосредоточиться на том, почему" overlinking " плохо, и из-за метода, который мы принимаем, чтобы избежать "overlinking", существование ld опции -rpath-link и -rpath разумно: мы намеренно опускаем некоторые библиотеки в командах для компиляции и связывания, чтобы избежать "перекрытия", и из-за опуская, ld нужно -rpath-link или -rpath чтобы найти эти опущенные библиотеки.


вы на самом деле не говорите ld (при связывании libb против liba) здесь liba - это - только то, что это зависимость. Быстрый ldd libb.so покажет вам, что он не может найти liba.

поскольку предположительно эти библиотеки не находятся в пути поиска компоновщика, вы получите ошибку компоновщика при ссылке исполняемого файла. Имейте в виду, что когда вы связываете саму liba, функция в libb является еще нерешенными, но ldповедение по умолчанию не заботьтесь о неразрешенных символах в DSOs, пока не свяжете окончательный исполняемый файл.