linux - ld: usando-rpath, $ ORIGEN dentro de una biblioteca compartida(recursiva)
linker shared (5)
Acabo de hacer un ejemplo básico de usar la opción -rpath
de ld con $ORIGIN
here (vea la segunda respuesta para una versión funcional). Estoy tratando de crear un ejemplo donde main.run
enlaza con foo.so
, que a su vez enlaza con bar.so
, todos usando rpath
y $ORIGIN
.
La estructura de archivos en tiempo de ejecución es:
- proyecto/
- lib /
- dir /
- sub/
- bar.so
- foo.so
- correr/
- main.run (no construir)
Estoy construyendo foo.so usando:
g++ -c -o obj/foo.o src/foo.cpp -fPIC
g++ -shared -o lib/dir/foo.so obj/foo.o -Wl,-soname,foo.so -Wl,-rpath,''$ORIGIN/sub'' -Llib/dir/sub -l:bar.so
Que construye bien. ldd lib/dir/foo.so
incluso puede encontrar bar.so
Sin embargo, cuando intento vincular main.run
a foo.so
, foo.so
no puede encontrar bar.so.
Estoy construyendo main.so usando:
g++ -c -o obj/main.o src/main.cpp
g++ -o run/main.run obj/main.o -Wl,-rpath,''$ORIGIN/../lib/dir'' -Llib/dir -l:foo.so
Esto funciona bien si se usa otra versión de foo.so
que no se vincula recursivamente. (Descomente las líneas en make.sh, en el proyecto a continuación, para probar).
Sin embargo, al usar foo.so
normal, foo.so
este error al main.run
:
/ usr / bin / ld: warning: bar.so, necesario para lib / dir / foo.so, no encontrado (intente usar -rpath o -rpath-link)
Así que mis preguntas son:
- ¿Se resuelve
$ORIGIN
dentro de foo.so paraproject/lib/dir
(dondefoo.so
es) oproject/run
(dondemain.run
(el ejecutable que lo vincula) es)?
ldd parece indicar que esproject/lib/dir
, que parece ser la mejor manera (aunque intenté asumir ambas cosas). - ¿Cómo puedo hacer que se vinculen (al tiempo que se conserva la
-rpath-link
), preferiblemente sin usar-rpath-link
?
Puedes descargar el proyecto here . Es tan simple como puedo hacerlo. 4 fuentes cortas y un guión.
Después de extraer, simplemente ejecute ./make.sh
desde el project/
.
Nota: estoy usando -l:
Esto no debería cambiar nada, excepto que las bibliotecas se denominan como foo.so
lugar de libfoo.so
, y lunk con -l:foo.so
lugar de -lfoo
.
Bueno, tengo algo funcionando. Pero realmente no entiendo por qué funciona. Esto se siente como un error en mí para mí.
strace -f -o /var/tmp/strace.out -- g++ ...
para la compilación main.run. El enlazador estático en realidad está intentando abrir archivos cuyo nombre literal se vea como "$ ORIGIN / lib / dir / sub / bar.so", entre 20 y 30 cosas más. (En otras palabras, está buscando un directorio real llamado $ORIGIN
. En serio.)
También parece estar buscando la ruta -rpath-link para el nombre "lib / dir / sub / bar.so", no solo "bar.so". No tengo ni idea de por qué.
De todos modos, este es el enlace para main.run que está funcionando para mí:
g++ -o run/main.run obj/main.o -Wl,-rpath,''$ORIGIN/../lib/dir'' -Wl,-rpath-link,. -Llib/dir -l:foo.so
Es idéntico al tuyo pero con -Wl,-rpath-link,.
insertado.
[apéndice]
Ok, creo que veo lo que está pasando. Primero, el enlazador estático (GNU ld) simplemente no respeta $ ORIGIN en las bibliotecas con las que se vincula.
Segundo, el comportamiento cuando usas -lbar
contra -l:bar.so
es muy diferente.
Ejecutar readelf -a
en foo.so
En su compilación, muestra una dependencia de "lib / dir / sub / bar.so". Esta es la razón por la cual el enlace de ruta se establece en "." corrige la construcción de main.run; hace que el enlazador estático busque "." para "lib / dir / sub / bar.so", que encuentra.
Si cambia el nombre de bar.so a libbar.so, y vincula foo.so para usar -lbar
lugar de -l:bar.so
, la misma lectura muestra que foo.so ahora depende de "libbar.so" (sin componente de ruta ). Con ese foo.so, puede hacer que el enlace main.run funcione usando -Wl,-rpath-link,lib/dir/sub
, como es de esperar si supiera que el enlazador estático simplemente no respeta $ ORIGIN.
Por cierto, no veo la sintaxis de -l:bar.so
documentada en ninguna parte del manual de GNU ld. Por curiosidad, ¿cómo se te ocurrió?
Suponiendo que es una función compatible, esto se parece un poco a un error (-l: bar.so también crea una dependencia en lib / dir / sub / bar.so en lugar de solo bar.so). Puedes lidiar con este error configurando el enlace de ruta a ''.'' para main.run, o puede cambiar el nombre de las cosas de la forma habitual (libxxx.so).
Desde la página de ld-linux(8) :
$ ORIGEN y rpath
ld.so entiende la cadena $ ORIGIN (o equivalentemente $ {ORIGIN}) en una especificación rpath (DT_RPATH o DT_RUNPATH) para indicar el directorio que contiene el ejecutable de la aplicación. Por lo tanto, una aplicación ubicada en somedir / app podría compilarse con gcc -Wl, -rpath, ''$ ORIGIN /../ lib'' para que encuentre una biblioteca compartida asociada en somedir / lib sin importar dónde se encuentre en el directorio jerarquía. Esto facilita la creación de aplicaciones "llave en mano" que no necesitan instalarse en directorios especiales, sino que pueden desempaquetarse en cualquier directorio y aún así encontrar sus propias bibliotecas compartidas.
Por lo tanto, en respuesta a su primera pregunta, solo hay un valor para $ORIGIN
: project/run
.
Por lo tanto, la respuesta a su segunda pregunta debe ser usar el siguiente comando para vincular foo.so
:
g++ -shared -o lib/dir/foo.so obj/foo.o -Wl,-soname,foo.so -Wl,-rpath,''$ORIGIN/../lib/dir/sub'' -Llib/dir/sub -l:bar.so
Primero, hay problemas con la expansión de signo $ que pueden estar causando problemas. Estoy construyendo Python desde la fuente y hago esto:
export LDFLAGS=''-Wl,-rpath,/$${ORIGIN}/../lib -Wl,-rpath,/$${ORIGIN}/../usr/lib -Wl,--enable-new-dtags''
antes de ejecutar make
. Eso funciona bien y encuentra dependencias de 1er nivel. Tenga cuidado con las comillas simples y dobles al tratar este tipo de problema de expansión de macros.
En segundo lugar, si ejecuta objdump -x
en un binario o una biblioteca, puede ver el encabezado RPATH que realmente contiene. Cuando ejecuto objdump -x path/to/python |grep RPATH
me muestra esto. RPATH $ {ORIGEN} /../ lib: $ {ORIGEN} /../usr / lib`
Le sugiero que revise sus binarios para ver qué hay realmente en el encabezado RPATH. Desafortunadamente, no creo que esto solucione tu problema. Esto es lo que veo cuando ejecuto ldd path/to/python
:
libpython2.7.so.1.0 => /data1/python27/bin/../lib/libpython2.7.so.1.0 (0x00002ad369d4f000)
libpthread.so.0 => /lib/libpthread.so.0 (0x00002ad36a12f000)
libdl.so.2 => /lib/libdl.so.2 (0x00002ad36a34d000)
libutil.so.1 => /lib/libutil.so.1 (0x00002ad36a551000)
libm.so.6 => /lib/libm.so.6 (0x00002ad36a754000)
libc.so.6 => /lib/libc.so.6 (0x00002ad36a9d8000)
/lib64/ld-linux-x86-64.so.2 (0x00002ad369b2d000)
Como puede ver, la dependencia de primer nivel se maneja correctamente mediante rpath
pero las dependencias de segundo nivel, es decir, las dependencias de libpython, vuelven a las bibliotecas del sistema. Y sí, libpython tiene exactamente el mismo encabezado RPATH en su binario. Encontré su pregunta mientras rpath recursive
Google rpath recursive
para intentar resolver mi problema de hacer un paquete independiente de distro.
Agregado más tarde Los encabezados de rpath
solo cambian la PRIMERA ruta buscada para las bibliotecas Si no se encuentran allí, entonces el cargador continúa buscando en los lugares normales. ldd
solo muestra la ruta real de la biblioteca que se encontró como resultado de la búsqueda. Cuando copié estas bibliotecas al directorio rpath
, todo funcionó. Básicamente, no hay una forma ordenada de encontrar todas las dependencias y copiarlas, simplemente ldd -v path/to/python
y algunos análisis de esa salida.
Revisa mi versión modificada de tu script make . Básicamente, se debe utilizar un -rpath-link
adicional sin $ORIGIN
, ya que ld
no entiende $ORIGIN
en absoluto.
En cuanto a tus preguntas.
-
$ORIGIN
solo funciona durante el tiempo de ejecución, y se graba en cada biblioteca. Así que diferentes bibliotecas compartidas tienen diferentes$ORIGIN
. - Me temo que la mejor manera es agregar
rpath-link
, y esto no afectará su portabilidad, ya que son relativos y no existirán en el ejecutable final, como he mostrado en mi versión demake.sh
Además, este es mi propio entendimiento de todo lo relacionado con el enlace . Espero que ayude.
También he estado investigando esto, y lo mejor que puedo decir es que necesitas usar -rpath-link
para cualquier ruta que use la expansión ORIGIN. Por ejemplo:
CC -shared (other flags) -R''$ORIGIN/../lib/'' -o /buildpath/lib/libmylib1.so
CC -shared (other flags) -R''$ORIGIN/../lib/'' -lmylib1 -o /buildpath/lib/libmylib2.so
# This fails to link ''somebinary''
CC (various flags) -R''$ORIGIN/../lib/'' -lmylib2 -o /buildpath/bin/somebinary
# This works correctly
CC (various flags) -R''$ORIGIN/../lib/'' -Wl,-rpath-link,/buildpath/lib/mylib1 -lmylib2 -o /buildpath/bin/somebinary
# The text above the carets to the right is a typo: ------------------^^^^^^
# I''m fairly sure it should read like this (though it has been awhile since I wrote this):
# (...) -Wl,-rpath-link,/buildpath/lib -lmylib1 (...)
ld
no expandirá $ORIGIN
dentro de las rutas especificadas usando -rpath-link
o las rutas que recupera del RPATH de una subdependencia. En el ejemplo anterior, mylib2
depende de mylib1
; mientras se vincula a somebinary
, ld
intenta encontrar mylib1
mediante la cadena literal / sin $ORIGIN/../lib/
incrustada en libmylib2.so. ld.so
haría en tiempo de ejecución, pero no ld
.
Tampoco utilizará las rutas especificadas con -L
para encontrar la biblioteca de sub-dependencia (y | ies).