ProblemWithVerifyELFAndRPATH: различия между версиями

Материал из ALT Linux Wiki
Нет описания правки
(не показано 8 промежуточных версий 4 участников)
Строка 1: Строка 1:
[[Category:Devel]]
[[Category:Devel]]
{{MovedFromFreesourceInfo|AltLinux/Sisyphus/devel/ProblemWithVerifyELFAndRPATH}}
{{Category navigation|title=Сборка пакетов|category=Сборка пакетов|sortkey={{SUBPAGENAME}}}}


== Проблемы с ELF ==
== Проблемы с ELF ==


=== RPATH ===
=== RPATH ===
При сборке [http://freshmeat.net/projects/wmnetload/ wmnetload] я столкнулся со следующей ошибкой:
При сборке {{pkg|wmnetload}} я столкнулся со следующей ошибкой:


<pre>verify-elf: ./usr/bin/wmnetload: RPATH entry contains ":": :/usr/local/lib</pre>
<pre>verify-elf: ./usr/bin/wmnetload: RPATH entry contains ":": :/usr/local/lib</pre>
Строка 11: Строка 11:
Было два возможных выхода из сложившейся ситуации, даже три:
Было два возможных выхода из сложившейся ситуации, даже три:


1) ослабить проверку, добавив в спек <tt>%set_verify_elf_method textrel=relaxed</tt>
# ослабить проверку, добавив в спек <tt>%set_verify_elf_method textrel=relaxed</tt>
2) докопаться до истинной причины и решить проблему в корне
# докопаться до истинной причины и решить проблему в корне
3) не собирать wmnetload :-)
# не собирать wmnetload :-)


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


==== Немного теории ====
==== Немного теории ====


Перед тем как начать что-то делать не плохо бы узнать что такое <tt>RPATH</tt> и почему <tt>/usr/local/lib</tt>, присутствующий в нем, является ошибкой.
Перед тем как начать что-то делать, неплохо бы узнать, что такое <tt>RPATH</tt> и почему {{path|/usr/local/lib}}, присутствующий в нем, является ошибкой.


<tt>RPATH</tt> -- это свойство некоего ELF-объекта (программы или библиотеки) хранить в себе дополнительные пути поиска разделяемых (shared) библиотек. Т.е. программа будет искать so'шку сначала там, куда указывает RPATH, а затем в <tt>/lib</tt>, <tt>/usr/lib</tt> и других системных путях, которые указаны в <tt>/etc/ld.so.conf</tt>. Значение переменный RPATH "зашито" в самой програме и вы можете увидеть его вызвав <tt>readelf -a program</tt>. Среди множества появившейся информации ищите упоминание про RPATH. Маленькая тонкость: в бинарниках со стандартным значением RPATH вы не найдете упоминания о нем, так как по умолчанию значение у переменной пустое. Для примера приведу часть вывода readelf для все того же wmnetload, скомпилированного с <tt>/usr/local/lib</tt> в RPATH:
<tt>RPATH</tt> это свойство некоего ELF-объекта (программы или библиотеки) хранить в себе дополнительные пути поиска разделяемых библиотек (shared libraries). То есть, программа будет искать so’шку сначала там, куда указывает RPATH, а затем в {{path|/lib}}, {{path|/usr/lib}} и других системных путях, которые указаны в {{path|/etc/ld.so.conf}}. Значение переменной RPATH «зашито» в самой программе и вы можете увидеть его, вызвав {{cmd|readelf -a program}}. Среди множества появившейся информации ищите упоминание про RPATH. Маленькая тонкость: в бинарниках со стандартным значением RPATH вы не найдете упоминания о нем, так как по умолчанию значение у переменной пустое. Для примера приведу часть вывода readelf для все того же wmnetload, скомпилированного с {{path|/usr/local/lib}} в RPATH:


<pre>[c0der@mycomp ~/wm/bin]$ readelf -a wmnetload | grep RPATH
<pre>[c0der@mycomp ~/wm/bin]$ readelf -a wmnetload | grep PATH
  0x0000000f (RPATH)                      Library rpath: [ :/usr/local/lib]</pre>
  0x0000000f (RPATH)                      Library rpath: [ :/usr/local/lib]</pre>
Обратите внимание, что существует RPATH, а существует RUNPATH. Для удобства и то, и то обычно называют «RPATH».


В случае, если RPATH не содержит дополнительных путей, мы ничего не увидим, т.е. вывод программы будет пустым.
В случае, если RPATH не содержит дополнительных путей, мы ничего не увидим, т.е. вывод программы будет пустым.


Почему RPATH не должен содержать какие-либо пути отличные от стандартных? Потому что все библиотеки должны располагаться в стандартных и отведенных для этого местах, а именно в /usr/lib. "Потому что должны быть веские причины держать разделяемую библиотеку не в /usr/lib и тем более веские держать их в разных местах, ни одно из которых не является /usr/lib" (с) lioka
Почему RPATH не должен содержать стандартные пути? Потому что ld-linux(1) их там найдет и без RPATH. Почему стоит избегать RPATH? Потому что все библиотеки должны располагаться в стандартных и отведенных для этого местах, а именно в {{path|/usr/lib}}. "Потому что должны быть веские причины держать разделяемую библиотеку не в {{path|/usr/lib}} и тем более веские — держать их в разных местах, ни одно из которых не является {{path|/usr/lib}}" (с) lioka. А когда библиотека лежит в нестандартном месте, большинство программ ее не найдет, а RPM-пакет будет содержать в себе ненужные Provides, вводя другие пакеты в заблуждение.


==== Поиск источника проблемы ====
==== Поиск источника проблемы ====


Начал я с того, что по'grep'ал исходники на предмет этого самого RPATH. Все указывало на файл <tt>configure.in</tt>, в котором я обнаружил следующие строки:
Начал я с того, что по'grep'ал исходники на предмет этого самого RPATH. Все указывало на файл {{path|configure.in}}, в котором я обнаружил следующие строки:


<pre>dnl
<pre>dnl
Строка 46: Строка 48:
...</pre>
...</pre>


Как видим, RPATH включает в себя <tt>$LIBRARY_RPATH</tt>. Тогда ищем где и как определяется <tt>$LIBRARY_RPATH</tt>. Наша цель узнать как в ней оказывается <tt>/usr/local/lib</tt> и удалить этот путь.
Как видим, RPATH включает в себя <tt>$LIBRARY_RPATH</tt>. Тогда ищем, где и как определяется <tt>$LIBRARY_RPATH</tt>. Наша цель узнать как в ней оказывается {{path|/usr/local/lib}} и удалить этот путь.


<pre>LIBRARY_RPATH=`echo "$LIBRARY_SEARCH_PATH" | sed 's/[ *]-L[ *]/:/g'`</pre>
<pre>LIBRARY_RPATH=`echo "$LIBRARY_SEARCH_PATH" | sed 's/[ *]-L[ *]/:/g'`</pre>


Теперь ищем где же определяется <tt>$LIBRARY_SEARCH_PATH</tt>:
Теперь ищем, где же определяется <tt>$LIBRARY_SEARCH_PATH</tt>:


<pre>LIBRARY_SEARCH_PATH="$lib_search_path -L/usr/local/lib"</pre>
<pre>LIBRARY_SEARCH_PATH="$lib_search_path -L/usr/local/lib"</pre>


Вот то что мы искали! Вот откуда "растут ноги" у нашей проблемы. Остаётся лишь удалить этот путь из RPATH.
Вот то, что мы искали! Вот откуда «растут ноги» у нашей проблемы. Остаётся лишь удалить этот путь из RPATH.


==== Решение проблемы ====
==== Решение проблемы ====


Как мы выяснили выше для ликвидации проблемы нужно удалить путь /usr/local/lib из $LIBRARY_SEARCH_PATH. Я сделал это так:
Как мы выяснили выше, для ликвидации проблемы нужно удалить путь {{path|/usr/local/lib}} из <tt>$LIBRARY_SEARCH_PATH</tt>. Я сделал это так:
 
<pre>sed -i 's|LIBRARY_SEARCH_PATH="$lib_search_path -L/usr/local/lib"|LIBRARY_SEARCH_PATH="$lib_search_path"|' configure.in</pre>


<pre>%__sed -i 's|LIBRARY_SEARCH_PATH="$lib_search_path -L/usr/local/lib"|LIBRARY_SEARCH_PATH="$lib_search_path"|' configure.in</pre>
Т.е. просто заменил строку sed'ом. Этот код следует вызывать в секции <tt>%pre</tt>, т.е. до вызова <tt>%configure</tt>. Также следует не забыть пересоздать configure-скрипт, вызвав autoconf (или даже <tt>%autoreconf</tt>).


Т.е. просто заменил строку sed'ом. Этот код следует вызывать в секции <tt>%pre</tt>, т.е. до вызова <tt>%configure</tt>. Также следует не забыть
==== Дополнение ====
пересоздать configure-скрипт, вызвав autoconf (<tt>%__autoconf</tt>).
В некоторых случаях у configure есть опция --enable-rpath / --disable-rpath и достаточно в спеке указать %configure --disable-rpath


=== LD_LIBRARY_PATH ===
=== LD_LIBRARY_PATH ===
При расположении библиотек в нестандартных местах и использовании нестандартной среды сборки (не autotools)
При расположении библиотек в нестандартных местах и использовании нестандартной среды сборки (не autotools) может возникнуть проблема, при которой проверка бинарных файлов не может пройти.
может возникнуть проблема, при которой проверка бинарных файлов не может пройти.
Если не проходит проверка бинарных файлов из {{path|/usr/bin}}, скорее всего в них указан неверный RPATH (или вовсе не указан).
Если не проходит проверка бинарных файлов из /usr/bin, скорее всего в них указан (или вовсе не указан) неверный RPATH.


==== Проверка при сборке пакета ====
==== Проверка при сборке пакета ====
Для проверки бинарных файлов и библиотек используется значение LD_LIBRARY_PATH, полученное примерно следующим образом:
Для проверки бинарных файлов и библиотек используется значение LD_LIBRARY_PATH, полученное примерно следующим образом:
- добавляется значение RPATH;
* добавляется значение RPATH;
- добавляется архитектурно-зависимый путь поиска библиотек (%_libdir:/%_lib);
* добавляется архитектурно-зависимый путь поиска библиотек (%_libdir:/%_lib);
- к вычисленному значению добавляется вывод от libtool-ldconfig-dump (я так понимаю он скажет какие мы нестандартные каталоги для библиотек использовали в проекте)
* к вычисленному значению добавляется вывод от {{cmd|libtool-ldconfig-dump}} (я так понимаю, он скажет, какие мы нестандартные каталоги для библиотек использовали в проекте)
- к полученному списку добавляется в начало он же, но с префиксом %buildroot для каждого элемента;
* к полученному списку добавляется в начало он же, но с префиксом <tt>%buildroot</tt> для каждого элемента;


=== Благодарности ===
=== Благодарности ===
Строка 87: Строка 90:
* [http://wiki.sisyphus.ru/devel/TextRel/libtool http://wiki.sisyphus.ru/devel/TextRel/libtool]
* [http://wiki.sisyphus.ru/devel/TextRel/libtool http://wiki.sisyphus.ru/devel/TextRel/libtool]
* [http://lists.altlinux.ru/pipermail/devel/2005-June/021705.html http://lists.altlinux.ru/pipermail/devel/2005-June/021705.html]
* [http://lists.altlinux.ru/pipermail/devel/2005-June/021705.html http://lists.altlinux.ru/pipermail/devel/2005-June/021705.html]
* http://lists.altlinux.org/pipermail/devel/2011-December/192727.html
* [http://lists.altlinux.org/pipermail/devel/2011-December/192724.html Обзор трех типов ошибок в RPATH]
*# [http://lists.altlinux.org/pipermail/devel/2011-December/192725.html Некорректные RPATH, содержащие относительные пути] (тж. [http://lists.altlinux.org/pipermail/devel/2011-December/192739.html здесь])
*# [http://lists.altlinux.org/pipermail/devel/2011-December/192726.html Некорректные RPATH, содержащие пути за пределами стандартного размещения библиотек и плагинов]
*# [http://lists.altlinux.org/pipermail/devel/2011-December/192727.html Некорректные RPATH, содержащие пути к стандартным библиотекам]<br />(NB: замена может быть применима к {{path|configure}} при отсутствии {{path|libtool}})

Версия от 19:15, 12 мая 2020


Проблемы с ELF

RPATH

При сборке wmnetload я столкнулся со следующей ошибкой:

verify-elf: ./usr/bin/wmnetload: RPATH entry contains ":": :/usr/local/lib

Было два возможных выхода из сложившейся ситуации, даже три:

  1. ослабить проверку, добавив в спек %set_verify_elf_method textrel=relaxed
  2. докопаться до истинной причины и решить проблему в корне
  3. не собирать wmnetload :-)

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

Немного теории

Перед тем как начать что-то делать, неплохо бы узнать, что такое RPATH и почему /usr/local/lib, присутствующий в нем, является ошибкой.

RPATH — это свойство некоего ELF-объекта (программы или библиотеки) хранить в себе дополнительные пути поиска разделяемых библиотек (shared libraries). То есть, программа будет искать so’шку сначала там, куда указывает RPATH, а затем в /lib, /usr/lib и других системных путях, которые указаны в /etc/ld.so.conf. Значение переменной RPATH «зашито» в самой программе и вы можете увидеть его, вызвав readelf -a program. Среди множества появившейся информации ищите упоминание про RPATH. Маленькая тонкость: в бинарниках со стандартным значением RPATH вы не найдете упоминания о нем, так как по умолчанию значение у переменной пустое. Для примера приведу часть вывода readelf для все того же wmnetload, скомпилированного с /usr/local/lib в RPATH:

[c0der@mycomp ~/wm/bin]$ readelf -a wmnetload | grep PATH
 0x0000000f (RPATH)                      Library rpath: [ :/usr/local/lib]

Обратите внимание, что существует RPATH, а существует RUNPATH. Для удобства и то, и то обычно называют «RPATH».

В случае, если RPATH не содержит дополнительных путей, мы ничего не увидим, т.е. вывод программы будет пустым.

Почему RPATH не должен содержать стандартные пути? Потому что ld-linux(1) их там найдет и без RPATH. Почему стоит избегать RPATH? Потому что все библиотеки должны располагаться в стандартных и отведенных для этого местах, а именно в /usr/lib. "Потому что должны быть веские причины держать разделяемую библиотеку не в /usr/lib и тем более веские — держать их в разных местах, ни одно из которых не является /usr/lib" (с) lioka. А когда библиотека лежит в нестандартном месте, большинство программ ее не найдет, а RPM-пакет будет содержать в себе ненужные Provides, вводя другие пакеты в заблуждение.

Поиск источника проблемы

Начал я с того, что по'grep'ал исходники на предмет этого самого RPATH. Все указывало на файл configure.in, в котором я обнаружил следующие строки:

dnl
dnl Hack in rpath -- yes, this sucks, and it even has a hidden dependency
dnl on the implementation of AC_PATH_XTRA.  F@&* you, portability.
dnl
if test "$GCC" = yes; then
	if test "ac_R_space" = no; then
		RPATH="-Wl,\"-R$LIBRARY_RPATH\""
	else
		RPATH="-Wl,\"-R $LIBRARY_RPATH\"" 
	fi
...

Как видим, RPATH включает в себя $LIBRARY_RPATH. Тогда ищем, где и как определяется $LIBRARY_RPATH. Наша цель узнать как в ней оказывается /usr/local/lib и удалить этот путь.

LIBRARY_RPATH=`echo "$LIBRARY_SEARCH_PATH" | sed 's/[ *]-L[ *]/:/g'`

Теперь ищем, где же определяется $LIBRARY_SEARCH_PATH:

LIBRARY_SEARCH_PATH="$lib_search_path -L/usr/local/lib"

Вот то, что мы искали! Вот откуда «растут ноги» у нашей проблемы. Остаётся лишь удалить этот путь из RPATH.

Решение проблемы

Как мы выяснили выше, для ликвидации проблемы нужно удалить путь /usr/local/lib из $LIBRARY_SEARCH_PATH. Я сделал это так:

sed -i 's|LIBRARY_SEARCH_PATH="$lib_search_path -L/usr/local/lib"|LIBRARY_SEARCH_PATH="$lib_search_path"|' configure.in

Т.е. просто заменил строку sed'ом. Этот код следует вызывать в секции %pre, т.е. до вызова %configure. Также следует не забыть пересоздать configure-скрипт, вызвав autoconf (или даже %autoreconf).

Дополнение

В некоторых случаях у configure есть опция --enable-rpath / --disable-rpath и достаточно в спеке указать %configure --disable-rpath

LD_LIBRARY_PATH

При расположении библиотек в нестандартных местах и использовании нестандартной среды сборки (не autotools) может возникнуть проблема, при которой проверка бинарных файлов не может пройти. Если не проходит проверка бинарных файлов из /usr/bin, скорее всего в них указан неверный RPATH (или вовсе не указан).

Проверка при сборке пакета

Для проверки бинарных файлов и библиотек используется значение LD_LIBRARY_PATH, полученное примерно следующим образом:

  • добавляется значение RPATH;
  • добавляется архитектурно-зависимый путь поиска библиотек (%_libdir:/%_lib);
  • к вычисленному значению добавляется вывод от libtool-ldconfig-dump (я так понимаю, он скажет, какие мы нестандартные каталоги для библиотек использовали в проекте)
  • к полученному списку добавляется в начало он же, но с префиксом %buildroot для каждого элемента;

Благодарности

  • Дмитрию Левину (ldv@) за то что сделал все эти проверки
  • Виталию Липатову (lav@) за то что предложил и вдохновил написать об этом
  • Сергею Большакову aka lioka (sbolshakov@) за объяснение теоретических основ

Смотри также