5

パッケージで配布するライブラリと動的にリンクされた多くのネイティブ実行可能ファイルを生成するAndroidアプリがあります。これらのバイナリを起動するには、LD_LIBRARY_PATH 環境変数を使用してライブラリをロードする場所を認識させますが、一部のデバイスではこれがまったく機能せず、LD_LIBRARY_PATH は正しく更新されますが、バイナリはライブラリを見つけることができません。 . これは、私の 2 つのデバイス (Galaxy Nexus と ストック ROM を搭載した Nexus 7) で正常に動作するため、再現できるものではありません。

たとえば、スポーンするなど、多くの方法を試しました。

LD_LIBRARY_PATH=/my/package/custom/libs:$LD_LIBRARY_PATH && cd /binary/directory && ./binary

と :

    String[] envp = { "LD_LIBRARY_PATH=" + libPath + ":$LD_LIBRARY_PATH" };

    Process process = Runtime.getRuntime().exec( "su", envp );

    writer = new DataOutputStream( process.getOutputStream() );
    reader = new BufferedReader( new InputStreamReader( process.getInputStream() ) );

    writer.writeBytes( "export LD_LIBRARY_PATH=" + libPath + ":$LD_LIBRARY_PATH\n" );
    writer.flush();

しかし、それらのデバイスでは何も機能していないように見えました...だから私はこれがカーネル関連の問題であると考え始めています.一部のカーネル(私のものなど)はLD_LIBRARY_PATHを使用し、他のカーネルは使用しません(単に無視するか、アプリケーションの起動時に設定された LD_LIBRARY_PATH のみを使用するため、実行時に変更する方法はありません)。

私も System.load を使用しようとしましたが、おそらくそれらのライブラリが JNI ではないため、機能しませんでした...静的にリンクされたバイナリの使用について考え始める前に試すことができるものはありますか?

4

4 に答える 4

5

ここに私が書いた簡単なラッパーがあります:

#include <android/log.h>
#include <dlfcn.h>
#include <stdio.h>
#include <string.h>

typedef int (*main_t)(int argc, char** argv);

static int help(const char* argv0)
{
    printf("%s: simple wrapper to work around LD_LIBRARY_PATH\n\n", argv0);
    printf("Args: executable, list all the libraries you need to load in dependency order, executable again, optional parameters\n");
    printf("example: %s /data/local/ttte /data/data/app/com.testwrapper/lib/ttt.so /data/local/ttte 12345\n", argv0);
    printf("Note: the executable should be built with CFLAGS=\"-fPIC -pie\", LDFLAGS=\"-rdynamic\"\n");

    return -1;
}

int main(int argc, char** argv)
{
    int rc, nlibs;
    void *dl_handle;

    if (argc < 2)
    {
        return help(argv[0]);
    }

    __android_log_print(ANDROID_LOG_DEBUG, "wrapper", "running '%s'", argv[1]);

    for (nlibs = 2; ; nlibs++)
    {
        if (nlibs >= argc)
        {
            return help(argv[0]);
        }

        __android_log_print(ANDROID_LOG_DEBUG, "wrapper", "loading '%s'", argv[nlibs]);
        dl_handle = dlopen(argv[nlibs], 0); // do not keep the handle, except for the last
        __android_log_print(ANDROID_LOG_DEBUG, "wrapper", "loaded '%s' -> %p", argv[nlibs], dl_handle);
        if (strcmp(argv[1], argv[nlibs]) == 0)
        {
            break;
        }
    }

    main_t pmain = (main_t)dlsym(dl_handle, "main");
    __android_log_print(ANDROID_LOG_DEBUG, "wrapper", "found '%s' -> %p", "main", pmain);
    rc = pmain(argc - nlibs, argv + nlibs);

//   we are exiting the process anyway, don't need to clean the handles actually

//   __android_log_print(3, "wrapper", "closing '%s'", argv[1]);
//   dlclose(dl_handle);

    return 0;
}

読みやすくするために、エラー処理、不要なクリーンアップ、および特殊なケースの処理のほとんどを削除します。

Android.mkこの実行可能ファイルの場合:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := wrapper
LOCAL_SRC_FILES := wrapper/main.c
LOCAL_LDLIBS    := -llog

include $(BUILD_EXECUTABLE)

展開の世話をする必要があることに注意してください: これwrapperを APK にパッケージ化し、いくつかのローカル パス (USB ストレージや/sdcard! ではなく) に抽出し、実行可能としてマークします ( chmod 777)。

これらは、.xml ファイルを介して実行する実行可能ファイルをビルドするときに指定する必要がある追加のパラメーターですwrapper。を使用ndk-buildしてビルドすると、次のようになります。

LOCAL_C_FLAGS   += -fPIC -pie
LOCAL_LDFLAGS   += -rdynamic 

chmodこれらの実行可能ファイルはもう必要ないことに注意してください。もう 1 つのトリック:セカンダリ実行可能ファイルを共有ライブラリにビルドすると、同じラッパーが引き続き機能します。これにより、これらのバイナリを展開する手間が省けます。NDK と Android ビルドは、APK の libs/armeabi を介してアプリの lib ディレクトリに自動的に安全に配信します。

アップデート

変更された環境でProcessBuilderを使用すると、はるかに簡単な解決策があるようです: https://stackoverflow.com/a/8962189/192373

于 2012-10-17T12:47:58.200 に答える
2

これはカーネル関連の問題ではなく、LD 関連の問題です。ライブラリのロードは完全にカーネル モードではなくユーザー モードで行われるため、カーネルは関係ありません。カーネルは、ELF バイナリの DYNAMIC セクションをループし、ライブラリをロードし、LD_PRELOAD や LD_PRELOAD などの環境変数をどう処理するかを判断する ld に制御を転送することのみを担当します。

ただし、変数には固有のセキュリティ リスクが伴う可能性があります。LD_LIBRARY_PATH を使用すると、システムのデフォルト (/lib または - Android では /system/lib) の前に独自のパスを配置できます。LD_PRELOAD は、プロセスが要求したかどうかに関係なく、ライブラリをプロセスに強制的にロード (プッシュ) するため、さらに悪いことです。これにより、機密性の高いプロセスに悪意のあるコードが挿入される可能性があります。このため、これはルート Setuid プロセスでは許可されていません。ただし、root シェルを使用している場合 (つまり、root 化された電話を使用している場合)、ほとんどのデバイスで許可されます。ベンダーが LD を変更して、問題が発生する可能性があります。

回避するためにできること:

  • 最も簡単な方法: /system を読み取り/書き込みとして再マウントし (mount -o remount /system)、ライブラリを /system/lib にコピーします。ルート化された電話が必要です

  • 最も賢い: 他の (システム) ライブラリを動的に維持しながら、ライブラリをバイナリに静的にリンクします。

お役に立てれば、

TG

于 2012-10-17T04:17:14.540 に答える
1

LD_LIBRARY_PATH が無視されるとは思わない。システム環境を更新する方法は問題ありません。LD_LIBRARY_PATH は root ユーザーの下で更新されます。

一部のマシンでは解決策が機能しない場合、いくつかの原因が考えられます。

  1. Android アプリは、常に独自のユーザー ID で実行されます。root 権限では実行されません。root ユーザーのシステム変数のみを変更する場合。アプリケーションは、独自の環境で正しいライブラリ パスを見つけることができない場合があります。

  2. アプリケーションの初期化時に LD_LIBRARY_PATH を正しく更新したとしても、アプリケーションの対応するユーザー ID がそれらのライブラリを読み取るためのアクセス権を持っていることを確認する必要があります。

  3. 一部のベンダーは、セキュリティ上の理由から Android カーネルを変更します。外部ストレージなどからのサードパーティ ライブラリの読み込みを無効にする場合があります。

于 2012-10-17T03:47:33.737 に答える
1

そうです、Android では LD_LIBRARY_PATH に依存することはできません。System.load() は任意のライブラリで使用できますが、これは子プロセスを生成するときにはあまり役に立ちません。コードで dlopen を使用できますが、通常は、多くのコード行を書き直す必要があることを意味します。

面白いトリックは、実行可能ファイルを共有ライブラリに変換し、依存関係の順序でライブラリを dlopen するラッパー実行可能ファイルからロードすることです。

ただし、できるだけ多くのスポーンをインプロセス コールに変換することを強くお勧めします。

于 2012-10-15T20:44:47.453 に答える