¿No puede encontrar .so en el mismo directorio que el ejecutable?

45

Tengo un ejecutable que necesita vincularse libtest.sodinámicamente, así que los puse en el mismo directorio, luego:

cd path_to_dir
./binary

Pero tengo esto:

error while loading shared libraries: libtest.so: cannot open shared object file: No such file or directory

¿Cómo puede ser incapaz de encontrar libtest.socuál ya está en el mismo directorio que el ejecutable?

linuxer
fuente

Respuestas:

25

El cargador nunca comprueba el directorio actual para objetos compartidos a menos que se dirija explícitamente a través de via $LD_LIBRARY_PATH. Vea la ld.so(8)página del manual para más detalles.

Ignacio Vazquez-Abrams
fuente
echo $LD_LIBRARY_PATHestá vacío en mi máquina :(
linuxer
Por lo general lo es.
Ignacio Vazquez-Abrams
2
Especifica directorios adicionales para que el cargador busque bibliotecas.
Ignacio Vazquez-Abrams
1
Las rutas en * nix están separadas por dos puntos ( :), no un punto y coma.
Ignacio Vazquez-Abrams
3
LD_LIBRARY_PATH es generalmente una mala elección en producción. Es bueno para hacks rápidos, y cosas como ayudar a los binarios desinstalados a encontrar sus bibliotecas compartidas al ejecutar pruebas unitarias (think ./configure; make; make check). Al construir su binario, puede colocar su biblioteca en una ubicación estándar (listada en /etc/ld.so.conf) o pasar el indicador -R al enlazador para que el binario sepa dónde buscar.
automatthias
57

Si bien puede configurar LD_LIBRARY_PATH para que el enlazador dinámico sepa dónde buscar, hay mejores opciones. Puede colocar su biblioteca compartida en uno de los lugares estándar, consulte /etc/ld.so.conf(en Linux) y /usr/bin/crle(en Solaris) la lista de estos lugares

Puede pasar -R <path>al vinculador al crear su binario, que se agregará <path>a la lista de directorios escaneados para su biblioteca compartida. Aquí hay un ejemplo. Primero, mostrando el problema:

libtest.h:

void hello_world(void);

libtest.c:

#include <stdio.h>
void hello_world(void) {
  printf("Hello world, I'm a library!\n");
}

Hola C:

#include "libtest.h"
int main(int argc, char **argv) {
  hello_world();
}

Makefile (se deben usar pestañas):

all: hello
hello: libtest.so.0
%.o: %.c
        $(CC) $(CFLAGS) -fPIC -c -o $@ $<
libtest.so.0.0.1: libtest.o
        $(CC) -shared -Wl,-soname,libtest.so.0 -o libtest.so.0.0.1 libtest.o
libtest.so.0: libtest.so.0.0.1
        ln -s $< $@
clean:
        rm -f hello libtest.o hello.o libtest.so.0.0.1 libtest.so.0

Vamos a ejecutarlo:

$ make
cc  -fPIC -c -o libtest.o libtest.c
cc -shared -Wl,-soname,libtest.so.0 -o libtest.so.0.0.1 libtest.o
ln -s libtest.so.0.0.1 libtest.so.0
cc     hello.c libtest.so.0   -o hello
$ ./hello 
./hello: error while loading shared libraries: libtest.so.0: cannot open shared object file: No such file or directory

¿Como arreglarlo? Agregar -R <path>a las banderas de enlace (aquí, mediante la configuración LDFLAGS).

$ make clean
(...)
$ make LDFLAGS="-Wl,-R -Wl,/home/maciej/src/tmp"
(...)
cc   -Wl,-R -Wl,/home/maciej/src/tmp  hello.c libtest.so.0   -o hello
$ ./hello 
Hello world, I'm a library!

Mirando el binario, puede ver que necesita libtest.so.0:

$ objdump -p hello | grep NEEDED
  NEEDED               libtest.so.0
  NEEDED               libc.so.6

El binario buscará sus bibliotecas, además de los lugares estándar, en el directorio especificado:

$ objdump -p hello | grep RPATH
  RPATH                /home/maciej/src/tmp

Si desea que el binario se vea en el directorio actual, puede configurar RPATH en $ORIGIN. Esto es un poco complicado, porque debes asegurarte de que el signo de dólar no sea interpretado por make. Aquí hay una forma de hacerlo:

$ make CFLAGS="-fPIC" LDFLAGS="-Wl,-rpath '-Wl,\$\$ORIGIN'"
$ objdump -p hello | grep RPATH
  RPATH                $ORIGIN
$ ./hello 
Hello world, I'm a library!
automatismos
fuente
1
Si no se usa make, como cuando se llama manualmente g++, intente -Wl,-rpath='$ORIGIN'(tenga en cuenta las comillas simples) para evitar que se $ORIGINexpanda a una cadena vacía.
Morpork
14

Para cargar los objetos compartidos desde el mismo directorio que su ejecutable, simplemente ejecute:

$ LD_LIBRARY_PATH=. ./binary

Nota: No modificará la variable LD_LIBRARY_PATH de su sistema. El cambio solo afecta a esto, y solo a esto, a la ejecución de su programa.

Cisnes
fuente
4

Para cualquiera que todavía tenga dificultades sin una respuesta, encontré una con la siguiente sugerencia:

Puede intentar actualizar ld.so.cache usando: sudo ldconfig -v

Trabajó para mi.

Ian Frisbie
fuente
A mi también me sirvió.
Joel
3

Para cualquiera que use CMake para su compilación, puede establecer CMAKE_EXE_LINKER_FLAGSlo siguiente:

set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-rpath='$ORIGIN'")

Esto propagará correctamente los indicadores del vinculador para todos los tipos de compilación (por ejemplo, Debug, Release, etc.) para buscar primero los archivos .so en el directorio de trabajo actual.

Michael Goldshteyn
fuente
0

El vinculador dinámico decidirá dónde buscar bibliotecas. En caso de Linux, el enlazador dinámico generalmente es GNU ld.so(o una alternativa que por lo general se comportará idénticas por razones de compatibilidad.

Para citas de Wikipedia:

El vinculador dinámico de la Biblioteca GNU C busca bibliotecas compartidas en las siguientes ubicaciones:

  1. Las rutas (separadas por dos puntos) en el DT_RPATHatributo de sección dinámica del binario si está presente y el DT_RUNPATHatributo no existe.
  2. Las rutas (separadas por dos puntos) en la variable de entorno LD_LIBRARY_PATH, a menos que el ejecutable sea un setuid/ setgidbinario, en cuyo caso se ignora. LD_LIBRARY_PATHpuede anularse llamando al enlazador dinámico con la opción --library-path (por ejemplo /lib/ld-linux.so.2 --library-path $ HOME / mylibs myprogram).
  3. Las rutas (separadas por dos puntos) en el DT_RUNPATHatributo de sección dinámica del binario si está presente.
  4. Búsqueda basada en el archivo de caché ldconfig (a menudo ubicado en /etc/ld.so.cache) que contiene una lista compilada de bibliotecas candidatas encontradas previamente en la ruta de la biblioteca aumentada (establecida por /etc/ld.so.conf). Sin embargo, si el binario se vinculó con la -z nodefaultlibopción del vinculador, se omiten las bibliotecas en las rutas predeterminadas de la biblioteca.
  5. En la ruta predeterminada de confianza /lib, y luego /usr/lib. Si el binario se vinculó con la opción del vinculador -z nodefaultlib, este paso se omite.

Fuente: https://en.wikipedia.org/wiki/Rpath

Mecki
fuente