14

問題:

libgstreamer-0.10.so 共有ライブラリ(GStreamer-android-8プラットフォーム用にコンパイルされたandroid NDKバンドルライブラリ)を使用するEclipseでAndroidアプリを構築しています。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

最初の4つ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)にある同じフォルダにあります。

論理的な解決策:

したがって、ロードする前にこれらの4つのライブラリをロードしようとしましたが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

2 に答える 2

27

https://groups.google.com/forum/?fromgroups#!msg/android-ndk/J3lzK4X--bM/4YaiijymZy_AJによると

はい、これは文書化された動作です。ライブラリを明示的に逆依存順序でロードする必要があります。[...] システムの制限です。

簡単に言うと、ダイナミック リンカはアプリケーションについて何も認識せず (ライブラリの場所など)、プロセスの作成時に設定された LD_LIBRARY_PATH 値のみを認識します。Android アプリケーションを開始すると、実際には Zygote プロセスがフォークされます。新しいプロセスは作成されないため、ライブラリ検索パスは最初のものであり、アプリの /data/data//lib/ ディレクトリは含まれません。あなたのネイティブ ライブラリは生きています。これは、/system/lib/libfoo.so のみが検索されるため、dlopen("libfoo.so") が機能しないことを意味します。

Java から System.loadLibrary("foo") を呼び出すと、VM フレームワークはアプリケーションのディレクトリを認識するため、"foo" を "/data/data//lib/libfoo.so" に変換し、dlopen() を次のように呼び出します。このフルパスは機能します。

libfoo.so が "libbar.so" を参照すると、動的リンカは後者を見つけることができなくなります。

これに加えて、ネイティブ コードから LD_LIBRARY_PATH を更新しても、動的リンカーは新しい値を認識しません。さまざまな低レベルの理由により、動的リンカーには、プロセスが作成されたとき (フォークされていないとき) のプログラム環境の独自のコピーが含まれています。また、ネイティブ コードから更新する方法はまったくありません。これは仕様によるものであり、これを変更すると、大幅なセキュリティ上の制約が発生します。記録として、これは Linux ダイナミック リンカの動作方法でもあります。これにより、カスタム ライブラリ検索パスを必要とするプログラムは、実行可能ファイルを起動するためにラッパー スクリプトを使用することになります (例: Firefox、Chrome など)。

これが文書化されている場所を尋ねる著者に電子メールを送りました。

Tor Lillqvist は回避策を提供しています: https://groups.google.com/d/msg/android-ndk/J3lzK4X--bM/n2zUancIFUEJ

より詳細に説明すると、その lo_dlopen() 関数が行うことは次のとおりです。

  • 問題の共有オブジェクトがどこにあるかを検索します。Java コードから渡された一連のディレクトリを検索します。Java コードは LD_LIBRARY_PATH を調べ、アプリの lib ディレクトリをそこに追加します。
  • 見つかった共有オブジェクト ファイルを開き、その中の ELF 構造を読み取ります。すべてではありませんが、必要な共有オブジェクト (arm-linux-androideabi-readelf -d で表示される DT_NEEDED オブジェクト) を見つけるには十分です。必要な共有オブジェクトで自分自身を再帰的に呼び出します。
  • その後、つまり必要な他の共有オブジェクトがすべてロードされたことを確認した後でのみ、見つかった共有オブジェクトへのフルパス名で実際の dlopen() が呼び出されます。

彼のコードはhttp://cgit.freedesktop.org/libreoffice/core/tree/sal/android/lo-bootstrap.c?id=5510127e89d6971a219ce3664e4631d6c6dda2b1にあります。

更新: http://code.google.com/p/android/issues/detail?id=34416によると、このコードは 2012 年 12 月の時点で Android に統合されました。API レベル 18 以上のデバイスでは、依存関係が自動的に読み込まれます。それよりも古い API レベルをサポートしている場合でも、依存関係をリストする必要があります。

于 2012-06-18T14:59:10.090 に答える
3
  1. Javaアプリでできるかわかりません。ネイティブコマンドラインアプリケーションの場合、アプリケーションを指定する前にLD_LIBRARY_PATH環境変数を設定することでこれを行うことができます。

  2. これは正しい解決策です。NDKドキュメントのどこかで、この方法ですべての依存ライブラリをロードする必要があると記載されています。

  3. いいえ、それはできません。

于 2012-06-15T23:09:04.670 に答える