Могу ли я изменить 'rpath' в уже скомпилированном двоичном файле?

93

У меня есть старый исполняемый файл, который планируется отправить в кучу лома, но его еще нет. Он полагается на некоторые библиотеки, которые были удалены из моей среды, но у меня есть некоторые библиотеки-заглушки, где они работают нормально. Я хочу указать этот исполняемый файл на эти библиотеки-заглушки. Да, я мог бы установить LD_LIBRARY_PATH, но этот исполняемый файл вызывается из многих скриптов и многих пользователей, и я хотел бы исправить его в одном месте.

У меня нет источника для этого, и было бы трудно его получить. Я подумал - могу ли я отредактировать этот файл, используя редактор, поддерживающий ELF, и добавить простой PATH в rpath, чтобы он попал в новые библиотеки? Возможно ли это, или как только вы создаете двоичный файл ELF, вы фиксируете что-то в местах, и их нельзя перемещать?

Богатая Гомолка
источник
3
Оберните его в сценарий оболочки, который устанавливает LD_LIBRARY_PATH и вызывает двоичный файл. Поместите сценарий оболочки в место в PATH вызывающего.
wildplasser 07
LD_LIBRARY_PATH наследуется дочерними процессами. Возможно, вы этого не захотите.
Уилл
1
@ да, и я уже сказал, что не хочу этого делать. :)
Rich Homolka

Ответы:

79

Есть инструмент, chrpathкоторый может это сделать - вероятно, он есть в пакетах вашего дистрибутива.

TomH
источник
9
Просто примечание для пользователей Mac, install_name_toolможно сделать это с -rpathфлагом
Кевин Тонон
10
Если вы получаете сообщение об ошибке:, <binary>: no rpath or runpath tag found.вы не можете использовать его chrpathдля его замены, но patchelfв этом случае вы можете использовать :patchelf --set-rpath /path/to/libaries <binary>
phyatt
Я предпочитаю chrpath, если это возможно, поскольку, хотя он и более универсален, в patchelf есть давняя ошибка, которая резко увеличивает размер ваших библиотек / исполняемых файлов.
taranaki
@taranaki: О какой версии patchelf вы говорите?
Хагелло
1
У chrpath есть серьезное ограничение: он может заменить RPATH только на один равной или меньшей длины (справочная страница rpath версии 0.16)
hagello
162

Есть средство более универсальное, чем chrpathназывается patchelf. Первоначально он был создан для использования при создании пакетов для Nix и NixOS (система упаковки и дистрибутив GNU / Linux).

Если в двоичном файле нет rpath (здесь называется rdsamp), происходит chrpathсбой:

chrpath -r '$ORIGIN/../lib64' rdsamp 
rdsamp: no rpath or runpath tag found.

С другой стороны,

patchelf --set-rpath '$ORIGIN/../lib64' rdsamp

удается просто отлично.

user7610
источник
10
В частности, patchelfможет добавить rpath к двоичному файлу, который еще не содержит rpath - где chrpathтолько кажется, что можно изменить уже существующую запись.
maxschlepzig
4
В общем, стоит понять тонкое различие между rpathи runpath. По сути, один может отменять, LD_LIBRARY_PATHа другой - нет. Подробнее см. Blog.tremily.us/posts/rpath
Стюарт Берг,
6
Раздражает то, что оба chrpathи patchelfнеаккуратно используют свою терминологию. Например, patchelfпоказанная выше команда изменится, runpathно rpathтолько если вы также не предоставите эту --force-rpathопцию.
Стюарт Берг
10
@superbatfish Да, но обычно разница не имеет значения. Эта запись из CHANGELOG из patchelfобъясняет это: " --set-rpath, --shrink-rpathи --print-rpathтеперь предпочитают DT_RUNPATHболее DT_RPATH, что является устаревшим при обновлении, если присутствуют оба, и обновляются Если только DT_RPATH присутствует, он преобразуется в.. , DT_RUNPATHЕсли --force-rpathне указано Если ни присутствует. , DT_RUNPATHдобавляется a, если --force-rpathне указано иное, в этом случае DT_RPATHдобавляется a ". Название опции, вероятно, осталось без изменений из соображений совместимости.
user7610 09
2
Безусловно, лучший ответ, это должен быть принятый ответ!
Кеннет Хост
13

Как сказал @ user7610, правильный путь - это patchelfинструмент.

Но я чувствую, что могу дать более исчерпывающий ответ, охватывающий все команды, необходимые для этого.

Чтобы прочитать подробную статью по этому вопросу, нажмите здесь

Во-первых, многие разработчики говорят RPATH, но на самом деле имеют в виду RUNPATH. Это два разных необязательных динамических раздела, и загрузчик обрабатывает их по-разному. Вы можете узнать больше о разнице между ними в упомянутой ранее ссылке.

А пока просто помните:

  • Если RUNPATHустановлено, RPATHигнорируется
  • RPATH устарел, и его следует избегать
  • RUNPATH предпочтительнее, потому что он может быть отменен LD_LIBRARY_PATH

См. Текущий R [UN] PATH

readelf -d <path-to-elf> | egrep "RPATH|RUNPATH"

Очистить ПУТЬ R [UN]

patchelf --remove-rpath <path-to-elf>

Примечания:

  • Удаляет оба RPATHиRUNPATH

Добавить значения в R [UN] PATH

patchelf [--force-rpath] --set-rpath "<desired-rpath>" <path-to-elf>

Примечания:

  • <desired-path> это список каталогов, разделенных запятыми, например: /my/libs:/my/other/libs
  • Если вы указываете --force-rpath, устанавливает RPATH, иначе устанавливаетRUNPATH
Дэниел Тругман
источник
1
-Wl,-R,<desired-rpath> -Wl,--enable-new-dtagsнаборы DT_RUNPATH, и это то, что большинство людей должны использовать. RUNPATHможет быть отменен LD_LIBRARY_PATH, поэтому людям не следует использовать --force-rpath.
jww
@jww Я вижу, что не добавил комментарий об устаревании RPATH, поэтому добавил его только сейчас. Благодарность!
Дэниел Тругман,
Обратите внимание, что в примере <desired-path>используется двоеточие; это должна быть запятая (то есть:) /my/libs,/my/other/libs.
Алан Де Смет,
@AlanDeSmet, про запятую не знаю, но двоеточие у меня работает.
Дэниел Тругман,
RPATH может быть устаревшим, но использование RUNPATH может привести к "неожиданным" угловым случаям. См., Например, qt.io/blog/2011/10/28/rpath-and-runpath .
Роб
0

Это сработало для меня, заменив XORIGIN на $ ORIGIN.

chrpath -r '\$\ORIGIN/../lib64' httpd

Викрам Кедлая
источник