android - 加载依赖于其他共享库的共享库

标签 android linker android-ndk gstreamer

问题:

我正在 Eclipse 中构建 Android 应用程序,它使用共享库 libgstreamer-0.10.so (为 android-8 平台编译的 GStreamer-android NDK Bundle 库)。我在项目根文件夹中创建了新文件夹 libs/armeabi 并将其放在那里。此外,我已将它附带的所有其他库(其中 158 个)放在同一个文件夹中。如果我把它放在我的主要 Activity 代码中:

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

然后在 Android-8 模拟器上构建/安装/运行我的应用程序,它会抛出此错误:

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)

现在,libglib-2.0.solibgstreamer-0.10.so 在同一文件夹中,为什么没有加载?我知道链接器试图从 /system/lib 加载它,而 libglib-2.0.so 不存在,但为什么它不从 的位置加载它code>libgstreamer-0.10.so 是?

所以我去发现哪些库 libgstreamer-0.10.so 依赖于这个命令:

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

结果:

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

前四个libglib-2.0.so、libgobject-2.0.so、libgthread-2.0.so、libgmodule-2.0.so都位于同一个文件夹libgstreamer-0.10.so 位于设备上的 (/data/data/com.marko.gstreamer_test/lib) 中。

逻辑解决方案:

因此,我尝试在加载 libgstreamer-0.10.so 之前加载这四个库,并且成功了:

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

我的问题是:

  1. 我能否以某种方式告诉链接器也从应用程序位置加载库?比如将路径添加到某些环境变量或其他东西...类似于 Linux 上的 PATH。

  2. 我的解决方案是否有不良副作用?我的意思是,链接器在加载 libgstreamer-0.10.so 之前也会这样做。但这会带来什么问题吗?

  3. 我可以将我的库安装到没有开启Root的设备上的 /system/lib 文件夹吗?

最佳答案

根据 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).

我已经给作者发邮件询问这是在哪里记录的。

Tor Lillqvist 继续提供解决方法: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.

您可以在 http://cgit.freedesktop.org/libreoffice/core/tree/sal/android/lo-bootstrap.c?id=5510127e89d6971a219ce3664e4631d6c6dda2b1 找到他的代码

更新:根据http://code.google.com/p/android/issues/detail?id=34416截至 2012 年 12 月,此代码已集成到 Android 中。耶!为 API 级别 18 及更高级别的设备自动加载依赖项。如果您支持旧的 API 级别,您仍然需要列出依赖项。

关于android - 加载依赖于其他共享库的共享库,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11058898/

相关文章:

android - 如何通过单击按钮接听来电?

visual-studio - 使用 VS 将 C++ 库编译为静态库和动态 dll

android - FFmpeg 示例代码,用于从静止图像 JNI Android 创建视频文件

android - 适用于 Java (EE) 开发人员的 Eclipse 与适用于移动开发人员的 Eclipse 和 Eclipse Classic 之间有什么区别?

android - FFMpeg : Falied to combine two MPEG TS files into a Single MP4 File

c++ - 动态链接是如何工作的,它的用法以及你如何以及为什么要制作一个 dylib

C 常量上的 Cgo 链接器错误

android - 在android平台上使用ffmpeg混合两个音频文件

android - Facebook Buck : BUILD FAILED: No native platforms detected. 可能是 Android NDK 配置不正确

java - Android不创建本地文件