2012-06-15 14 views
13

Problema:Cargando compartido bibliotecas que dependen de otras bibliotecas compartidas

Estoy construyendo aplicación Android en Eclipse que utiliza compartida lib libgstreamer-0.10.so(GStreamer-Android NDK libs Bundle compilados para la plataforma android-8). Hice la nueva carpeta libs/armeabi en la carpeta raíz del proyecto y la puse allí. Además, he puesto todas las otras librerías que venían con él (158 de ellas) en la misma carpeta. Si pongo esto en mi principal código de actividad:

static{ 
    System.loadLibrary("gstreamer-0.10"); 
} 

y construir/instalar/ejecutar mi aplicación en Android-8 emulador, se lanza este error:

06-15 21:54:00.835: E/AndroidRuntime(402): Caused by: java.lang.UnsatisfiedLinkError: Cannot load library: link_image[1962]: 33 could not load needed library 'libglib-2.0.so' for 'libgstreamer-0.10.so' (load_library[1104]: Library 'libglib-2.0.so' not found) 

Ahora, libglib-2.0.so es de la misma carpeta como libgstreamer-0.10.so, y ¿por qué no está cargado? Me sale que el enlazador intenta cargarlo desde /system/lib y libglib-2.0.so simplemente no está allí, pero ¿por qué no lo está cargando desde la ubicación donde está libgstreamer-0.10.so?

así que fui a descubrir qué libs libgstreamer-0.10.so depende de con este comando:

arm-linux-androideabi-readelf -d libgstreamer-0.10.so 

Resultados:

Dynamic section at offset 0x118b64 contains 29 entries: 
    Tag  Type       Name/Value 
0x00000001 (NEEDED)      Shared library: [libglib-2.0.so] 
0x00000001 (NEEDED)      Shared library: [libgobject-2.0.so] 
0x00000001 (NEEDED)      Shared library: [libgthread-2.0.so] 
0x00000001 (NEEDED)      Shared library: [libgmodule-2.0.so] 
0x00000001 (NEEDED)      Shared library: [libdl.so] 
0x00000001 (NEEDED)      Shared library: [libm.so] 
0x00000001 (NEEDED)      Shared library: [libstdc++.so] 
0x00000001 (NEEDED)      Shared library: [libc.so] 
0x0000000e (SONAME)      Library soname: [libgstreamer-0.10.so] 
0x00000010 (SYMBOLIC)     0x0 

Primera de cuatro libglib-2.0.so, libgobject-2.0.so, libgthread-2.0.so, libgmodule-2.0.so están ubicados en la misma carpeta libgstreamer-0.10.so se encuentra en (/data/data/com.marko.gstreamer_test/lib) en el dispositivo.

solución lógica:

tanto, he intentado cargar estos cuatro librerías antes me carga libgstreamer-0.10.so y, funcionó:

static{ 
    System.loadLibrary("glib-2.0"); 
    System.loadLibrary("gthread-2.0"); 
    System.loadLibrary("gobject-2.0"); 
    System.loadLibrary("gmodule-2.0"); 
    System.loadLibrary("gstreamer-0.10"); 
} 

Mis preguntas son:

  1. ¿Puedo decirle al vinculador que cargue libs también desde la ubicación de la aplicación? Como agregar ruta a alguna variable de entorno o algo ... similar a PATH en Linux.

  2. ¿Mi solución tiene algunos efectos secundarios negativos? Quiero decir, el enlazador también haría esto antes de cargar el libgstreamer-0.10.so. Pero, ¿esto creará algún problema?

  3. ¿Puedo instalar mis libs en la carpeta /system/lib en un dispositivo sin estela?

+4

Ese truco es en realidad la solución que recomiendan los propios desarrolladores de Android: https://groups.google.com/forum/?fromgroups#!topic/android-ndk/F7DnfSQt8qs Con decisiones técnicas como esa, no es de extrañar que Android sea tan calesa. –

+0

¿Cómo determinaste qué bibliotecas cargar explícitamente? –

+0

@dpk 'arm-linux-androideabi-readelf -d libgstreamer-0.10.so' proporciona la lista de dependencias. Algunos de ellos ya están cargados (libc, etc.), pero algunos deben cargarse explícitamente. – chrisvarnz

Respuesta

26

Según https://groups.google.com/forum/?fromgroups#!msg/android-ndk/J3lzK4X--bM/4YaijymZy_AJ

Yes, and this is the documented behaviour: you must load libraries in reverse dependency order explicitely. [...] It is a limitation of the system.

In a nutshell, the dynamic linker doesn't know anything about your application (e.g. where its libraries live), it only knows about the LD_LIBRARY_PATH value that was set when the process was created. When you start an Android application, you really fork the Zygote process, you don't create a new one, so the library search path is the initial one and doesn't include your app's /data/data//lib/ directory, where your native libraries live. This means that dlopen("libfoo.so") will not work, because only /system/lib/libfoo.so will be searched.

When you call System.loadLibrary("foo") from Java, the VM framework knows the application's directory, so it can translate "foo" into "/data/data//lib/libfoo.so", then call dlopen() with this full path, which will work.

It libfoo.so references "libbar.so", then the dynamic linker will not be able to find the latter.

Add to this that even if you update LD_LIBRARY_PATH from native code, the dynamic linker will not see the new value. For various low-level reasons, the dynamic linker contains its own copy of the program's environment as it was when the process was created (not forked). And there is simply no way to update it from native code. This is by design, and changing this would have drastic security constraints. For the record, this is also how the Linux dynamic linker works, this forces any program that needs a custom library search path to use a wrapper script to launch its executable (e.g. Firefox, Chrome and many others).

He escrito el autor preguntando dónde esto está documentado.

Tor Lillqvist va a proporcionar una solución alternativa: https://groups.google.com/d/msg/android-ndk/J3lzK4X--bM/n2zUancIFUEJ

To be more verbose, what that lo_dlopen() function does is:

  • Searches where the shared object in question is. It searches a set of directories passed to it by the Java code. The Java code looks at LD_LIBRARY_PATH and adds the app's lib directory to that.
  • Opens the found shared object file and reads the ELF structures in it . Not all, but just enough to find out what shared objects it needs (the DT_NEEDED ones as displayed by arm-linux-androideabi-readelf -d). It calls itself recursively on the needed shared objects.
  • Only after that, i.e. after making sure that all needed other shared objects have been loaded, it calls the real dlopen() on the found full pathname to the shared object.

Usted puede encontrar su código en http://cgit.freedesktop.org/libreoffice/core/tree/sal/android/lo-bootstrap.c?id=5510127e89d6971a219ce3664e4631d6c6dda2b1

ACTUALIZACIÓN: De acuerdo con http://code.google.com/p/android/issues/detail?id=34416 este código se integró en Android a partir de diciembre de 2012. ¡Hurra! Las dependencias se cargan automáticamente para dispositivos con nivel de API 18 y superior. Si está soportando niveles API más antiguos que eso, aún necesita enumerar las dependencias.

3
  1. no estoy seguro de que puede hacer por las aplicaciones Java. Para las aplicaciones de línea de comandos nativas, puede hacerlo configurando la variable de entorno LD_LIBRARY_PATH antes de indicar la aplicación.

  2. Esta es la solución correcta. En algún lugar de los documentos NDK, se menciona que debe cargar todas las bibliotecas dependientes de esta manera.

  3. No, no puedes hacer eso.

Cuestiones relacionadas