2012-06-15 2 views
13

Problem auf andere gemeinsam genutzte Bibliotheken abhängen:Laden geteilt Libs, die

Ich baue Android App in Eclipse, die lib libgstreamer-0.10.so(GStreamer-Android NDK Bundle Libs für Android-8-Plattform kompiliert) verwendet den gemeinsamen. Ich habe den neuen Ordner libs/armeabi im Projektstammordner abgelegt und dort abgelegt. Außerdem habe ich alle anderen Bibliotheken (158 davon) in den gleichen Ordner gelegt. Wenn ich dies in meiner Haupttätigkeit Code setzen:

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

Und bauen/install/betreiben meinen App auf Android-8 Emulator, wirft es diesen Fehler:

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) 

Nun libglib-2.0.so ist in den gleichen Ordner als libgstreamer-0.10.so, und warum ist es nicht geladen? Ich bekomme, dass Linker versucht, es von /system/lib laden und libglib-2.0.so ist einfach nicht da, aber warum lädt es nicht von der Stelle, wo libgstreamer-0.10.so ist?

Also ging ich zu entdecken, welche Libs libgstreamer-0.10.so mit diesem Befehl abhängt:

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

Ergebnisse:

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 

Die ersten vier libglib-2.0.so, libgobject-2.0.so, libgthread-2.0.so, libgmodule-2.0.so sind alle im selben Ordner befinden libgstreamer-0.10.so in befindet (/data/data/com.marko.gstreamer_test/lib) auf dem Gerät.

logische Lösung:

Also habe ich versucht, diese vier Libs zu laden, bevor ich libgstreamer-0.10.so laden und es funktionierte:

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"); 
} 

Meine Fragen sind:

  1. Kann ich dem Linker irgendwie sagen, dass er libs auch vom App-Standort laden soll? Wie den Pfad zu einer Umgebungsvariablen oder etwas hinzufügen ... ähnlich wie PATH unter Linux.

  2. Hat meine Lösung einige schlechte Nebenwirkungen? Ich meine, Linker würde dies auch tun, bevor es die libgstreamer-0.10.so lädt. Aber wird das irgendwelche Probleme machen?

  3. Kann ich meine libs auf /system/lib Ordner auf unrooted Gerät installieren?

+4

Dieser Hack ist eigentlich die Lösung, die die Android-Entwickler selbst empfehlen: https://groups.google.com/forum/?fromgroups#!topic/android-ndk/F7DnfSQt8qs Mit technischen Entscheidungen wie diese, kein Wunder, ist Android so Buggy. –

+0

Wie haben Sie festgestellt, welche Bibliotheken explizit geladen werden sollen? –

+0

@dpk 'arm-linux-androideabi-readelf -d libgstreamer-0.10.so' gibt die Liste der Abhängigkeiten an. Einige von ihnen sind bereits geladen (libc, etc), aber einige müssen Sie explizit laden. – chrisvarnz

Antwort

26

Nach 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).

Ich habe den Autor per E-Mail zu fragen, wo dies dokumentiert ist.

Tor Lillqvist geht auf eine Abhilfe zu schaffen: 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.

Sie können den Code auf http://cgit.freedesktop.org/libreoffice/core/tree/sal/android/lo-bootstrap.c?id=5510127e89d6971a219ce3664e4631d6c6dda2b1

UPDATE finden: Nach http://code.google.com/p/android/issues/detail?id=34416 dieser Code wurde integriert in Android ab Dezember 2012. Yay! Abhängigkeiten werden automatisch für Geräte mit API-Stufe 18 und höher geladen. Wenn Sie ältere API-Stufen unterstützen, müssen Sie die Abhängigkeiten noch auflisten.

3
  1. Ich bin nicht sicher, dass Sie für Java-Anwendungen tun. Für native Befehlszeilenanwendungen können Sie dies tun, indem Sie die Umgebungsvariable LD_LIBRARY_PATH vor dem Angeben der Anwendung festlegen.

  2. Dies ist die richtige Lösung. Irgendwo in NDK-Dokumenten wird erwähnt, dass Sie alle abhängigen Bibliotheken auf diese Weise laden müssen.

  3. Nein, das geht nicht.