问题:
我在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/lib
和libglib-2.0.so
加载它只是不存在,但为什么不从libgstreamer-0.10.so
的位置加载它?
所以我通过这个命令发现了哪些lib 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
所在的同一文件夹中。{/ p>
逻辑解决方案:
所以,我在加载/data/data/com.marko.gstreamer_test/lib
之前尝试加载这四个库,并且它起作用了:
libgstreamer-0.10.so
我的问题是:
我能以某种方式告诉链接器也从应用程序位置加载库吗?就像添加一些环境变量的路径一样......类似于Linux上的PATH。
我的解决方案是否有一些不良副作用?我的意思是,链接器在加载 libgstreamer-0.10.so 之前也会这样做。但这会产生任何问题吗?
我可以在无根设备上将我的lib安装到 / system / lib 文件夹吗?
答案 0 :(得分:26)
根据https://groups.google.com/forum/?fromgroups#!msg/android-ndk/J3lzK4X--bM/4YaijymZy_AJ
是的,这是记录的行为:您必须明确地以反向依赖顺序加载库。 [...]这是系统的限制。
简而言之,动态链接器对您的应用程序一无所知(例如,它的库存在),它只知道创建进程时设置的LD_LIBRARY_PATH值。当你启动一个Android应用程序时,你真的分叉了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://code.google.com/p/android/issues/detail?id=34416,此代码已于2012年12月集成到Android中。是的! API级别为18及以上的设备会自动加载依赖项。如果您支持较旧的API级别,则仍需要列出依赖项。
答案 1 :(得分:3)
我不确定你能为Java应用程序做些什么。对于本机命令行应用程序,您可以通过在声明应用程序之前设置LD_LIBRARY_PATH环境变量来执行此操作。
这是正确的解决方案。在NDK文档的某处,提到您需要以这种方式加载所有依赖库。
不,你做不到。