$ gcc — g — shared — o libx1.so modx1.o — Wl, — rpath,/home/mtk/pdir/d2 \
— L/home/mtk/pdir/d2 — lx2
Наконец, мы можем создать в каталоге pdir главную программу. Поскольку она использует библиотеку libx1.so, которая находится в нестандартном месте, опять задействуем параметр компоновщика — rpath:
$ cd /home/mtk/pdir
$ gcc — g — Wall — o prog prog.c — Wl, — rpath,/home/mtk/pdir/d1 \
— L/home/mtk/pdir/d1 — lx1
Обратите внимание: при компоновке главной программы не потребовалось упоминать библиотеку libx2.so. Компоновщик способен сам проанализировать rpath в файле libx1.so и найти libx2.so; это позволяет удовлетворить требование, согласно которому все символы должны быть найдены на этапе статической компоновки.
Для просмотра списков rpath в файлах prog и libx1.so можно воспользоваться следующими командами:
$ objdump — p prog | grep PATH
RPATH /home/mtk/pdir/d1
$ objdump — p d1/libx1.so | grep PATH
RPATH /home/mtk/pdir/d2
Списки rpath также можно просматривать, анализируя вывод команды readelf — dynamic (или аналогичной ей readelf — d), используя утилиту grep.
Команда ldd позволяет просмотреть список всех динамических зависимостей программы prog:
$ ldd prog
libx1.so => /home/mtk/pdir/d1/libx1.so (0x40017000)
libc.so.6 => /lib/tls/libc.so.6 (0x40024000)
libx2.so => /home/mtk/pdir/d2/libx2.so (0x4014c000)
/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)
Исходная спецификация формата ELF допускала встраивание в исполняемый файл только одного вида списка rpath или разделяемой библиотеки. Это относится к метке DT_RPATH в ELF-файле. Однако в последующих версиях данного формата эта метка считается устаревшей, а для представления списков rpath теперь используется запись DT_RUNPATH. Разница между двумя указанными списками заключается в их приоритете относительно переменной среды LD_LIBRARY_PATH, когда динамический компоновщик ищет разделяемые библиотеки во время выполнения программы: DT_RPATH имеет больший приоритет, а DT_RUNPATH — меньший (см. раздел 41.11).
По умолчанию компоновщик создает список rpath в виде метки DT_RPATH. Чтобы задействовать вместо этого запись DT_RUNPATH, следует дополнительно указать параметр — enable — new — dtags (включить новые динамические метки). Если пересобрать нашу программу с использованием этого параметра и проанализировать итоговый исполняемый файл с помощью утилиты objdump, можно увидеть следующее:
$ gcc — g — Wall — o prog prog.c — Wl, — enable-new-dtags \
— Wl, — rpath,/home/mtk/pdir/d1 — L/home/mtk/pdir/d1 — lx1
$ objdump — p prog | grep PATH
RPATH /home/mtk/pdir/d1
RUNPATH /home/mtk/pdir/d1
Как видите, исполняемый файл содержит обе метки — DT_RPATH и DT_RUNPATH. Компоновщик дублирует список rpath для поддержки старых динамических компоновщиков, которые могут быть несовместимы с меткой DT_RUNPATH (поддержка ее была добавлена в glibc версии 2.2). Динамические компоновщики, совместимые с записью DT_RUNPATH, игнорируют DT_RPATH (см. раздел 41.11).
Представьте, что вам нужно распространять приложение, которое применяет собственные разделяемые библиотеки; при этом вы не хотите заставлять пользователя устанавливать их в один из стандартных каталогов. Вместо этого пользователь должен иметь возможность распаковать приложение в любом месте по своему усмотрению и сразу же его запустить. Проблема в том, что приложение не может определить местоположение своих разделяемых библиотек самостоятельно; мы должны попросить пользователя задать переменную LD_LIBRARY_PATH или предоставить небольшой установочный сценарий, который будет определять соответствующие каталоги. Но ни один из указанных вариантов нам не подходит.
Для решения данной проблемы динамический компоновщик позволяет указать в параметре — rpath специальную строку, $ORIGIN (или ${ORIGIN}), которую он умеет анализировать. Он интерпретирует ее как «каталог, содержащий приложение». Это значит, что мы, к примеру, можем собрать программу с помощью следующей команды:
$ gcc — Wl, — rpath,'$ORIGIN'/lib …