2012-06-15 38 views
13

Vấn đề:tải chia sẻ libs mà phụ thuộc vào libs chia sẻ khác

Tôi đang xây dựng ứng dụng Android trong Eclipse trong đó sử dụng chia sẻ lib libgstreamer-0.10.so(GStreamer-android libs NDK Bundle biên soạn cho nền tảng android-8). Tôi đã tạo thư mục mới libs/armeabi trong thư mục gốc của dự án và đặt nó ở đó. Ngoài ra, tôi đã đặt tất cả các libs khác đi kèm với nó (158 trong số họ) trong cùng một thư mục. Nếu tôi đặt điều này trong mã hoạt động chính của tôi:

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

Và xây dựng/cài đặt/chạy ứng dụng của tôi trên Android-8 giả lập, nó throws lỗi này:

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) 

Bây giờ, libglib-2.0.so là trong cùng một thư mục là libgstreamer-0.10.so và tại sao nó không được tải? Tôi nhận được rằng mối liên kết cố gắng để tải nó từ /system/liblibglib-2.0.so chỉ là không có, nhưng tại sao nó không tải nó từ vị trí nơi libgstreamer-0.10.so là?

Vì vậy, tôi đã đi để khám phá libs libgstreamer-0.10.so phụ thuộc vào bằng lệnh này:

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

Kết quả:

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 

Đầu Bốn libglib-2.0.so, libgobject-2.0.so, libgthread-2.0.so, libgmodule-2.0.so đều nằm trong cùng một thư mục libgstreamer-0.10.so tọa lạc tại (/data/data/com.marko.gstreamer_test/lib) trên thiết bị.

giải pháp hợp lý:

Vì vậy, tôi đã cố gắng để tải bốn libs trước khi tôi tải libgstreamer-0.10.so và nó làm việc:

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"); 
} 

Câu hỏi của tôi là:

  1. Tôi bằng cách nào đó có thể yêu cầu trình liên kết tải libs cũng từ vị trí ứng dụng không? Giống như thêm đường dẫn đến một số biến môi trường hoặc một cái gì đó ... tương tự như PATH trên Linux.

  2. Dung dịch của tôi có một số tác dụng phụ không? Ý tôi là, trình liên kết cũng sẽ làm điều này trước khi tải libgstreamer-0.10.so. Nhưng điều này sẽ làm cho bất kỳ vấn đề?

  3. Tôi có thể cài đặt libs của mình vào thư mục /system/lib trên thiết bị chưa được hủy kích thước không?

+4

Đó thực sự là giải pháp mà nhà phát triển Android tự giới thiệu: https://groups.google.com/forum/?fromgroups#!topic/android-ndk/F7DnfSQt8qs Với các quyết định kỹ thuật như vậy, không có gì ngạc nhiên khi Android lỗi. –

+0

Bạn đã xác định thư viện nào để tải một cách rõ ràng? –

+0

@dpk 'arm-linux-androideabi-readelf -d libgstreamer-0.10.so' cung cấp danh sách phụ thuộc. Một số trong số đó đã được tải (libc, vv), nhưng một số bạn cần tải một cách rõ ràng. – chrisvarnz

Trả lời

26

Theo 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).

Tôi đã gửi email cho tác giả hỏi nơi này được ghi chép lại.

Tor Lillqvist tiếp tục cung cấp một 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.

Bạn có thể tìm mã của mình tại http://cgit.freedesktop.org/libreoffice/core/tree/sal/android/lo-bootstrap.c?id=5510127e89d6971a219ce3664e4631d6c6dda2b1

CẬP NHẬT: Theo http://code.google.com/p/android/issues/detail?id=34416 mã này đã được tích hợp vào Android tính đến tháng 12 năm 2012. Yay! Phụ thuộc được tải tự động cho các thiết bị có cấp API 18 trở lên. Nếu bạn đang hỗ trợ các cấp API cũ hơn bạn vẫn cần phải liệt kê các phụ thuộc.

3
  1. Tôi không chắc chắn bạn có thể làm cho các ứng dụng Java. Đối với các ứng dụng dòng lệnh gốc, bạn có thể thực hiện điều đó bằng cách đặt biến môi trường LD_LIBRARY_PATH trước khi nêu ứng dụng.

  2. Đây là giải pháp đúng. Một nơi nào đó trong tài liệu NDK này được đề cập rằng bạn cần phải tải tất cả các thư viện phụ thuộc theo cách này.

  3. Không, bạn không thể làm điều đó.