2012-06-15 155 views
13

問題:加載共享依賴於其他共享庫庫

我建立在Eclipse的Android應用它採用LIB libgstreamer-0.10.so(編譯爲Android-8平臺的GStreamer功能的Android NDK包庫)共享。我在項目根文件夾中創建了新文件夾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

這個破解實際上是Android開發者自己推薦的解決方案:https://groups.google.com/forum/?fromgroups#!topic/android-ndk/F7DnfSQt8qs有了這樣的技術決策,難怪Android是如此越野車。 –

+0

你是如何確定明確加載哪些庫的? – 2014-04-09 14:18:13

+0

@dpk'arm-linux-androideabi-readelf -d libgstreamer-0.10.so'給出依賴關係列表。其中一些已經加載(libc等),但有些需要顯式加載。 – chrisvarnz

回答

26

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

我已經通過電子郵件發送筆者問在哪裏,這是記錄在案。

托爾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

UPDATE找到他的代碼:根據http://code.google.com/p/android/issues/detail?id=34416該代碼被集成到Android作爲2012年12月的。好極了! API級別爲18或更高的設備會自動加載依賴關係。如果您支持比以前更高的API級別,則仍然需要列出依賴關係。

3
  1. 我不知道你能爲Java應用程序做。對於本地命令行應用程序,您可以通過在聲明應用程序之前設置LD_LIBRARY_PATH環境變量來完成此操作。

  2. 這是正確的解決方案。 NDK文檔中的某處提到了您需要以這種方式加載所有相關的庫。

  3. 不,你不能那樣做。