14

问题:

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

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.so与 位于同一文件夹中libgstreamer-0.10.so,为什么没有加载?我得到那个链接器试图从它加载它/system/liblibglib-2.0.so但它不在那里,但为什么它不从它所在的位置加载它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. 我可以将我的库安装到无根设备上的/system/lib文件夹吗?

4

2 回答 2

27

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

是的,这是记录在案的行为:您必须明确地以反向依赖顺序加载库。[...] 这是系统的限制。

简而言之,动态链接器对您的应用程序一无所知(例如,它的库所在的位置),它只知道创建进程时设置的 LD_LIBRARY_PATH 值。当你启动一个 Android 应用程序时,你实际上是 fork Zygote 进程,你没有创建一个新的,所以库搜索路径是初始路径并且不包括你的应用程序的 /data/data//lib/ 目录,其中您的本机库已上线。这意味着 dlopen("libfoo.so") 将不起作用,因为只会搜索 /system/lib/libfoo.so。

当你从 Java 调用 System.loadLibrary("foo") 时,VM 框架知道应用程序的目录,因此它可以将 "foo" 翻译成 "/data/data//lib/libfoo.so",然后调用 dlopen()这条完整的路径,它将起作用。

它 libfoo.so 引用“libbar.so”,那么动态链接器将无法找到后者。

此外,即使您从本机代码更新 LD_LIBRARY_PATH,动态链接器也不会看到新值。由于各种低级原因,动态链接器包含它自己的程序环境副本,就像创建进程时(不是分叉)一样。而且根本没有办法从本机代码更新它。这是设计使然,改变这一点会产生严重的安全限制。作为记录,这也是 Linux 动态链接器的工作方式,这会强制任何需要自定义库搜索路径的程序使用包装脚本来启动其可执行文件(例如 Firefox、Chrome 和许多其他程序)。

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

Tor Lillqvist 继续提供解决方法:https ://groups.google.com/d/msg/android-ndk/J3lzK4X--bM/n2zUancIFUEJ

更详细地说,lo_dlopen() 函数的作用是:

  • 搜索有问题的共享对象在哪里。它搜索一组由 Java 代码传递给它的目录。Java 代码查看 LD_LIBRARY_PATH 并将应用程序的 lib 目录添加到其中。
  • 打开找到的共享对象文件并读取其中的 ELF 结构。不是全部,但足以找出它需要哪些共享对象(由 arm-linux-androideabi-readelf -d 显示的 DT_NEEDED 对象)。它在需要的共享对象上递归调用自己。
  • 只有在那之后,即在确保所有需要的其他共享对象都已加载之后,它才会在找到的共享对象的完整路径名上调用真正的 dlopen()。

你可以在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 级别,您仍然需要列出依赖项。

于 2012-06-18T14:59:10.090 回答
3
  1. 我不确定你可以为 Java 应用程序做些什么。对于本机命令行应用程序,您可以通过在声明应用程序之前设置 LD_LIBRARY_PATH 环境变量来做到这一点。

  2. 这是正确的解决方案。在 NDK 文档中的某处提到您需要以这种方式加载所有依赖库。

  3. 不,你不能那样做。

于 2012-06-15T23:09:04.670 回答