简体   繁体   English

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

[英]Loading shared libs that depend on other shared libs

Problem: 问题:

I am building Android app in Eclipse which uses shared lib libgstreamer-0.10.so (GStreamer-android NDK Bundle libs compiled for android-8 platform) . 我在Eclipse中构建Android应用程序,它使用共享库libgstreamer-0.10.so (为Android-8平台编译的GStreamer-android NDK Bundle库) I made new folder libs/armeabi in project root folder and put it there. 我在项目根文件夹中libs/armeabi了新文件夹libs/armeabi并将其放在那里。 Also, I have put all other libs that came with it (158 of them) in the same folder. 此外,我已将所有其他随附的库(其中158个)放在同一个文件夹中。 If I put this in my main activity code: 如果我把它放在我的主要活动代码中:

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

And build/install/run my app on Android-8 emulator, it throws this error: 在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)

Now, libglib-2.0.so is in the same folder as libgstreamer-0.10.so , and why is it not loaded? 现在, libglib-2.0.so是在同一文件夹中libgstreamer-0.10.so ,以及它为什么不装? I get that linker tries to load it from /system/lib and libglib-2.0.so just is not there, but why is it not loading it from the location where libgstreamer-0.10.so is? 我得到链接器尝试从/system/liblibglib-2.0.so加载它只是不存在,但为什么不从libgstreamer-0.10.so的位置加载它?

So I went to discover which libs libgstreamer-0.10.so depends on with this command: 所以我去发现libgstreamer-0.10.so依赖于这个命令的libs:

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

Results: 结果:

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

First four libglib-2.0.so, libgobject-2.0.so, libgthread-2.0.so, libgmodule-2.0.so are all located in the same folder libgstreamer-0.10.so is located in ( /data/data/com.marko.gstreamer_test/lib ) on the device. 前四个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 )在设备上。

Logical solution: 逻辑解决方案:

So, I tried to load these four libs before I load libgstreamer-0.10.so and, it worked: 所以,在我加载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");
}

My questions are: 我的问题是:

  1. Can I somehow tell the linker to load libs also from the app location? 我可以以某种方式告诉链接器也从应用程序位置加载库吗? Like add path to some environment variable or something... similar to PATH on Linux. 就像添加一些环境变量的路径一样......类似于Linux上的PATH。

  2. Does my solution have some bad side-effects? 我的解决方案是否有一些不良副作用? I mean, linker would do this also before it loads the libgstreamer-0.10.so . 我的意思是,链接器在加载libgstreamer-0.10.so之前也会这样做 But will this make any problems? 但这会有什么问题吗?

  3. Can I install my libs to /system/lib folder on unrooted device? 我可以在无根设备上将我的lib安装到/ system / lib文件夹吗?

According to https://groups.google.com/forum/?fromgroups#!msg/android-ndk/J3lzK4X--bM/4YaijymZy_AJ 根据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 (eg where its libraries live), it only knows about the LD_LIBRARY_PATH value that was set when the process was created. 简而言之,动态链接器不了解您的应用程序(例如它的库所在的位置),它只知道创建进程时设置的LD_LIBRARY_PATH值。 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. 当你启动一个Android应用程序时,你真的分叉了Zygote进程,你没有创建一个新的,所以库搜索路径是初始的,不包括你的应用程序的/ data / data // lib /目录,其中你的本地图书馆。 This means that dlopen("libfoo.so") will not work, because only /system/lib/libfoo.so will be searched. 这意味着dlopen(“libfoo.so”)将无法工作,因为只会搜索/system/lib/libfoo.so。

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. 当您从Java调用System.loadLibrary(“foo”)时,VM框架知道应用程序的目录,因此它可以将“foo”转换为“/data/data//lib/libfoo.so”,然后调用dlopen()这条完整的道路,将起作用。

It libfoo.so references "libbar.so", then the dynamic linker will not be able to find the latter. 它libfoo.so引用“libbar.so”,然后动态链接器将无法找到后者。

Add to this that even if you update LD_LIBRARY_PATH from native code, the dynamic linker will not see the new value. 除此之外,即使您从本机代码更新LD_LIBRARY_PATH,动态链接器也不会看到新值。 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 (eg Firefox, Chrome and many others). 为了记录,这也是Linux动态链接器的工作方式,这迫使任何需要自定义库搜索路径的程序使用包装器脚本来启动其可执行文件(例如Firefox,Chrome和许多其他程序)。

I've emailed the author asking where this is documented. 我已经通过电子邮件向作者询问了这些文件的位置。

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

To be more verbose, what that lo_dlopen() function does is: 为了更详细,lo_dlopen()函数的作用是:

  • Searches where the shared object in question is. 搜索相关共享对象的位置。 It searches a set of directories passed to it by the Java code. 它搜索Java代码传递给它的一组目录。 The Java code looks at LD_LIBRARY_PATH and adds the app's lib directory to that. Java代码查看LD_LIBRARY_PATH并将应用程序的lib目录添加到该目录中。
  • Opens the found shared object file and reads the ELF structures in it . 打开找到的共享对象文件并读取其中的ELF结构。 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). 不是全部,而是足以找出它需要的共享对象(由arm-linux-androideabi-readelf -d显示的DT_NEEDED)。 It calls itself recursively on the needed shared objects. 它以递归方式调用所需的共享对象。
  • Only after that, ie 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. 只有在那之后,即在确保已经加载了所有需要的其他共享对象之后,它才会在找到的共享对象的完整路径名上调用真正的dlopen()。

You can find his code at http://cgit.freedesktop.org/libreoffice/core/tree/sal/android/lo-bootstrap.c?id=5510127e89d6971a219ce3664e4631d6c6dda2b1 你可以在http://cgit.freedesktop.org/libreoffice/core/tree/sal/android/lo-bootstrap.c?id=5510127e89d6971a219ce3664e4631d6c6dda2b1找到他的代码。

UPDATE : According to http://code.google.com/p/android/issues/detail?id=34416 this code was integrated into Android as of December 2012. Yay! 更新 :根据http://code.google.com/p/android/issues/detail?id=34416,此代码已于2012年12月集成到Android中。是的! Dependencies are loaded automatically for devices with API level 18 and up. API级别为18及以上的设备会自动加载依赖项。 If you are supporting older API levels than that you still need to list the dependencies. 如果您支持较旧的API级别,则仍需要列出依赖项。

  1. I'm not sure you can do for Java apps. 我不确定你能为Java应用程序做些什么。 For native command line applications you can do that by setting LD_LIBRARY_PATH environment variable before stating application. 对于本机命令行应用程序,您可以通过在声明应用程序之前设置LD_LIBRARY_PATH环境变量来实现。

  2. This is correct solution. 这是正确的解决方案。 Somewhere in NDK docs this is mentioned that you need to load all the dependent libraries this way. 在NDK文档的某处,提到您需要以这种方式加载所有依赖库。

  3. No, you can not do that. 不,你做不到。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 在运行时选择并加载备用共享库之一 - Choosing and loading one of alternative shared libs at runtime 使用Android.mk加载共享库 - Loading shared libs using Android.mk Android.mk,在输出中包含但不包括共享库的油墨 - Android.mk , inking but not including Shared Libs in output 构建 Opencv4.3 Android 共享库错误 - Building Opencv4.3 Android shared libs error Android NDK:CMAKE 如何将 IMPORTED 共享库移动到 libs 文件夹 - Android NDK: CMAKE how to move IMPORTED shared library to libs folder 如何在Android共享库中链接多个静态库 - How to link multiple static libs in android shared lib 在安装Android APK之后,共享库是否总是安装在/ system / libs中吗? - Does Shared Libraries always be installed into /system/libs after install the Android APK? 我可以在我的 apk 中为 arm abi 使用预构建的共享库(.so),而没有 NDK? - Can I use prebuilt shared libs(.so) for arm abi in my apk, with no NDK? 为什么在obj / local / armeabi中找到的共享库比在libs / armeabi中的共享库更大? - Why a shared library found in obj/local/armeabi is bigger in size than its version in libs/armeabi? 通过Java线程或NDK pthreads加载JNI库? - Loading JNI libs- via Java threads or NDK pthreads?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM