Подтвердить что ты не робот

Загрузка общих библиотек, которые зависят от других общих библиотек

Проблема:

Я создаю приложение Android в Eclipse, которое использует общий lib libgstreamer-0.10.so (GStreamer-android NDK Bundle libs, скомпилированный для платформы android-8). Я создал новую папку 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?

Итак, я решил узнать, от каких libs 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");
}

Мои вопросы:

  • Могу ли я каким-то образом сказать компоновщику загрузить libs также из местоположения приложения? Как добавить путь к какой-либо переменной среды или что-то... похожее на PATH на Linux.

  • Есть ли у моего решения какие-то плохие побочные эффекты? Я имею в виду, что компоновщик сделает это и до загрузки libgstreamer-0.10.so. Но будут ли возникать какие-либо проблемы?

  • Могу ли я установить свои файлы libs в папку /system/lib на непереработанном устройстве?

4b9b3361

Ответ 1

Согласно https://groups.google.com/forum/?fromgroups#!msg/android-ndk/J3lzK4X--bM/4YaijymZy_AJ

Да, и это документированное поведение: вы должны явно загружать библиотеки в обратную зависимость. [...] Это ограничение системы.

В двух словах динамический компоновщик ничего не знает о вашем приложении (например, где живут его библиотеки), он знает только о значении LD_LIBRARY_PATH, которое было установлено при создании процесса. Когда вы запускаете приложение для Android, вы действительно разворачиваете процесс Zygote, вы не создаете новый, поэтому путь поиска библиотеки является исходным и не включает в себя каталог приложений/данных/данных//lib/, где ваши родные библиотеки живут. Это означает, что dlopen ( "libfoo.so" ) не будет работать, потому что будет выполняться поиск только /system/lib/libfoo.so.

Когда вы вызываете System.loadLibrary( "foo" ) из Java, среда VM знает каталог приложения, поэтому он может переводить "foo" в "/data/data//lib/libfoo.so", а затем вызвать dlopen() с этим полным путем, который будет работать.

Это libfoo.so ссылается на "libbar.so", тогда динамический компоновщик не сможет найти последний.

Добавьте к этому, что даже если вы обновите LD_LIBRARY_PATH из собственного кода, динамический компоновщик не увидит новое значение. Для разных низкоуровневых причин динамический компоновщик содержит свою собственную копию программной среды, как это было при создании процесса (не разветвлено). И просто нет возможности обновить его из собственного кода. Это по дизайну, и изменение этого будет иметь серьезные ограничения безопасности. Для записи это также то, как работает динамический компоновщик Linux, это заставляет любую программу, которая нуждается в пути поиска пользовательской библиотеки, использовать оболочку script для запуска своего исполняемого файла (например, Firefox, Chrome и многие другие).

Я отправил по электронной почте автора, спрашивая, где это задокументировано.

Tor Lillqvist продолжает обходное решение: https://groups.google.com/d/msg/android-ndk/J3lzK4X--bM/n2zUancIFUEJ

Чтобы быть более подробным, что делает эта функция lo_dlopen():

  • Ищет, где находится общий объект. Он ищет набор каталогов, переданных ему кодом Java. Код Java выглядит на LD_LIBRARY_PATH и добавляет к нему каталог приложений lib.
  • Открывает найденный файл общих объектов и читает в нем структуры ELF. Не все, но достаточно, чтобы узнать, какие общие объекты ему нужны (DT_NEEDED, как показано в файле arm-linux-androideabi-readelf -d). Он называет себя рекурсивно на необходимых общих объектах.
  • Только после этого, то есть после проверки того, что все необходимые другие общие объекты были загружены, он вызывает реальный dlopen() для найденного полного пути к общему объекту.

Вы можете найти его код в 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 года. Yay! Зависимости загружаются автоматически для устройств с уровнем API 18 и выше. Если вы поддерживаете более старые уровни API, чем это, вам все равно нужно перечислять зависимости.

Ответ 2

  • Я не уверен, что вы можете сделать для приложений Java. Для собственных приложений командной строки вы можете это сделать, установив переменную среды LD_LIBRARY_PATH перед выражением приложения.

  • Это правильное решение. Где-то в документах NDK упоминается, что вам необходимо загрузить все зависимые библиотеки.

  • Нет, вы не можете этого сделать.