加载依赖于其他共享库的共享库 [英] Loading shared libs that depend on other shared libs

查看:185
本文介绍了加载依赖于其他共享库的共享库的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

问题:

我建立在Eclipse的Andr​​oid应用程序它使用的lib libgstreamer-0.10.so 共享的(编译为Android-8平台的GStreamer-的Andr​​oid NDK包库)。我在做项目的根文件夹中新建文件夹库/ armeabi 键,把它放在那里。另外,我已经把与它(其中有158人)在同一个文件夹中来的所有其它库。如果我把这个在我的主要活动code:

 静态{
    的System.loadLibrary(的GStreamer-0.10);
}
 

和构建/安装/运行我在Android上,8模拟器的应用程序,它抛出这个错误:

  06-15 21:54:00.835:E / AndroidRuntime(402):java.lang.UnsatisfiedLinkError中:产生的原因无法加载库:link_image [1962] 33无法加载所需库libglib-2.0.so'为'libgstreamer-0.10.so'(load_library [1104]:图书馆libglib-2.0.so未找到)
 

现在, libglib-2.0.so 是在同一个文件夹 libgstreamer-0.10.so ,为什么难道不是装的?我得到的链接器试图加载从 /系统/ lib目录 libglib-2.0.so 只是不存在,但为什么不从位置加载它,其中 libgstreamer-0.10.so 是?

于是我去发现哪些库 libgstreamer-0.10.so 取决于使用这个命令:

  ARM-Linux的androideabi-readelf -d libgstreamer-0.10.so
 

结果:

 动态部分的抵消0x118b64包含29项:
  标签类型名称/值
 00000001(需要)共享库:[libglib-2.0.so]
 00000001(需要)共享库:[libgobject-2.0.so]
 00000001(需要)共享库:[libgthread-2.0.so]
 00000001(需要)共享库:[libgmodule-2.0.so]
 00000001(需要)共享库:[libdl.so]
 00000001(需要)共享库:[libm.so]
 00000001(需要)共享库:[的libstdc ++等等。]
 00000001(需要)共享库:[libc.so]
 0x0000000e(SONAME)库的soname:[libgstreamer-0.10.so]
 0x00000010(符号)为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 来加载这四个库和,它的工作:

 静态{
    的System.loadLibrary(油嘴滑舌-2.0);
    的System.loadLibrary(gthread-2.0);
    的System.loadLibrary(GObject的-2.0);
    的System.loadLibrary(gmodule-2.0);
    的System.loadLibrary(的GStreamer-0.10);
}
 

我的问题是:

  1. 我可以以某种方式告诉链接器加载库也从应用程序的位置?像添加路径到一些环境变量或东西...类似的道路上的Linux。

  2. 难道我的解决方案有一些不良的副作用?我的意思是,链接器会做这也是它加载的 libgstreamer-0.10.so 的面前。不过本作会什么问题?

  3. 我可以安装我的库来的 /系统/ lib目录的关于无根的设备文件夹?

解决方案

据<一href="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

  

是的,这就是记录的行为:你必须加载图书馆   明确地相反的依赖次序。   [...]   它是该系统的限制。

     

在简单地说,动态连接器不知道任何有关你的   应用程序(例如,其中它的库住),它只知道有关   在创建过程时设定LD_LIBRARY_PATH值。当你   启动一个Android应用程序,你真的叉受精卵的过程中,你不   创建一个新的,所以库搜索路径是初始1和不   包括您的应用程序/数据/数据​​// lib /目录下,你的母语   库居住。这意味着的dlopen(libfoo.so)将无法正常工作,因为   只有/system/lib/libfoo.so将被搜索到。

     

当您从Java调用的System.loadLibrary(富),虚拟机框架知道   应用程序的目录,因此它可以翻译foo的成   /data/data//lib/libfoo.so,然后调用dlopen()的与此完全   路径,这将正常工作。

     

据libfoo.so的引用libbar.so,那么动态连接器将不   能够找到后者

     

添加到这一点,即使你从本地code更新LD_LIBRARY_PATH中,   动态连接器将看不到新的价值。关于各种低层次的原因,   动态链接程序包含它自己的程序的环境中复制,因为它   是在创建进程时(不分叉)。而且根本就没有办法   从本地code更新。这是由设计,并改变这个就   有激烈的安全约束。根据记录,这是怎么也   Linux的动态链接器的工作原理,这迫使任何需要的自定义程序   使用包装脚本库搜索路径推出可执行文件(例如:   火狐,Chrome和许多其他)。

我通过电子邮件发送笔者问在哪里,这是记录在案。

Tor的Lillqvist继续提供一种解决方法:<一href="https://groups.google.com/d/msg/android-ndk/J3lzK4X--bM/n2zUancIFUEJ">https://groups.google.com/d/msg/android-ndk/J3lzK4X--bM/n2zUancIFUEJ

  

要更详细,什么lo_dlopen()函数的作用是:

     
      
  • 搜索,其中有问题的共享对象。它搜索一组传递给它由Java code目录。 Java的code着眼于LD_LIBRARY_PATH,并增加了应用程序的lib目录到。
  •   
  • 打开找到的共享对象文件和读取ELF结构吧。不是所有的,但刚好够找出共同需要的对象(在DT_NEEDED那些所显示臂的Linux androideabi-readelf -d)。它递归调用本身所需的共享对象。
  •   
  • 只有在此之后,即确保所有需要的其他共享对象已经被加载后,它会调用在发现完整路径到共享对象的真正的dlopen()。
  •   

您可以找到自己的code在<一个href="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

更新:根据<一个href="http://$c$c.google.com/p/android/issues/detail?id=34416">http://$c$c.google.com/p/android/issues/detail?id=34416这code被整合到Android作为2012年12月耶的!

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). I made new folder libs/armeabi in project root folder and put it there. Also, I have put all other libs that came with it (158 of them) in the same folder. 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:

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? 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?

So I went to discover which libs libgstreamer-0.10.so depends on with this command:

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.

Logical solution:

So, I tried to load these four libs before I load libgstreamer-0.10.so and, it worked:

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.

  2. Does my solution have some bad side-effects? I mean, linker would do this also before it loads the libgstreamer-0.10.so. But will this make any problems?

  3. Can I install my libs to /system/lib folder on unrooted device?

解决方案

According to 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).

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

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.

You can find his code at 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!

这篇关于加载依赖于其他共享库的共享库的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆