web-dev-qa-db-ja.com

Android Lでネイティブライブラリを実行しています。エラー:位置独立実行可能ファイル(PIE)のみがサポートされています

Android L(Nexus 5)でネイティブコードを実行すると、エラーが発生します。

エラー:位置独立実行可能ファイル(PIE)のみがサポートされています。

同じコードがSamsung Galaxy S3(Android 4.3)で正しく実行されます。

これが私のApplication.mkです

APP_PROJECT_PATH := $(call my-dir)/..
APP_ABI := armeabi
NDK_TOOLCHAIN_VERSION := 4.7
APP_PLATFORM := Android-9
APP_GNUSTL_FORCE_CPP_FEATURES := exceptions rtti

ただし、APP_PLATFORM := Android-9APP_PLATFORM := Android-16に置き換えると( ここ を読んで、PIEサポートがJelly Been(APIレベル16)に現れた)、同じ実行可能ファイルがAndroidL。

APP_PLATFORM := Android-9を使用してネイティブコードをコンパイルし、Android Lで実行する方法はありますか?

51
Maksim Dmitriev

2つの実行可能ファイルを作成しました。1つはAPP_PLATFORM := Android-9で、もう1つはAPP_PLATFORM := Android-16でした。 Javaでネイティブコードを実行するには、これが必要です。

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KitKat_WATCH) {
    // Run the file which was created using APP_PLATFORM := Android-16
} else {
    // Run the file which was created using APP_PLATFORM := Android-9
}
7
Maksim Dmitriev

Android 4.1+のみをサポートして生活できる場合は、APP_PLATFORM := Android-16を設定するだけで準備完了です。舞台裏でAPP_PIE := trueを設定します。バイナリは古いSDKでセグメンテーション違反になります。

より低いSDKレベルもサポートする必要がある場合は、2つのバイナリを作成する必要があります。私が見た他のいくつかの答えは、異なるAPP_PLATFORMを持つ2つの別々のソースツリーを維持することを推奨していますが、あなたはそれをする必要はありません。単一のAndroid.mk出力でPIEバイナリと非PIEバイナリの両方を作成できます。

NDK 10c以降:

手動で有効にする方が無効にするよりも簡単なので、PIEがデフォルトで無効になっていることを確認してください。 APP_PLATFORMが16以上でなければ、PIEはデフォルトで有効になりません。 APP_PLATFORMが設定されていない(Android-3またはNDK 15以降はAndroid-14に設定されている)か、Android-16よりも低いか、APP_PIE := falseに設定されていることを確認してください。

次のAndroid.mkは、PIEと非PIEバイナリを作成しますただし、注意事項があります(以下を参照)

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

# Enable PIE manually. Will get reset on $(CLEAR_VARS). This
# is what enabling PIE translates to behind the scenes.
LOCAL_CFLAGS += -fPIE
LOCAL_LDFLAGS += -fPIE -pie

LOCAL_MODULE := mymod

LOCAL_SRC_FILES := \
    mymod.c

include $(BUILD_EXECUTABLE)

include $(CLEAR_VARS)

LOCAL_MODULE := mymod-nopie

LOCAL_SRC_FILES := \
    mymod.c

include $(BUILD_EXECUTABLE)

次に、コード内の正しいバイナリを呼び出すための何らかのロジックを追加する必要があります。

残念ながら、これは、実行可能モジュールを2回コンパイルする必要があることを意味し、時間がかかる場合があります。また、LOCAL_SRC_FILESとライブラリを2回指定する必要がありますが、これはフラストレーションがたまり、追跡が難しい場合があります。できることは、メインの実行可能ファイルを静的ライブラリとしてコンパイルし、その静的ライブラリ以外から実行可能ファイルをビルドすることです。静的ライブラリにはPIEは必要ありません。

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := mymod-common

LOCAL_SRC_FILES := \
  mymod.c

include $(BUILD_STATIC_LIBRARY)

include $(CLEAR_VARS)

# Enable PIE manually. Will get reset on $(CLEAR_VARS). This
# is what enabling PIE translates to behind the scenes.
LOCAL_CFLAGS += -fPIE
LOCAL_LDFLAGS += -fPIE -pie

LOCAL_MODULE := mymod

LOCAL_STATIC_LIBRARIES := mymod-common

include $(BUILD_EXECUTABLE)

include $(CLEAR_VARS)

LOCAL_MODULE := mymod-nopie

LOCAL_STATIC_LIBRARIES := mymod-common

include $(BUILD_EXECUTABLE)

一定量の定型文がまだ必要ですが、これは非常にうまくいくようです。

NDK 10b:

NDK 10bはデフォルトでPIEを有効にし、ひどいハッキングを除いて無効にすることはできません。本当に、10cにアップデートするだけです。参考のために古い回答をここに残していますが、誰にもお勧めしません。

LOCAL_PATH := $(call my-dir)

# Forcefully disable PIE globally. This makes it possible to
# build some binaries without PIE by adding the necessary flags
# manually. These will not get reset by $(CLEAR_VARS). PIE is
# force-enabled on NDK 10b so we'll need this even if APP_PIE
# is set to false.
TARGET_PIE := false
NDK_APP_PIE := false

include $(CLEAR_VARS)

# Enable PIE manually. Will get reset on $(CLEAR_VARS). This
# is what enabling PIE translates to behind the scenes.
LOCAL_CFLAGS += -fPIE
LOCAL_LDFLAGS += -fPIE -pie

LOCAL_MODULE := mymod

LOCAL_SRC_FILES := \
    mymod.c

include $(BUILD_EXECUTABLE)

include $(CLEAR_VARS)

LOCAL_MODULE := mymod-nopie

LOCAL_SRC_FILES := \
    mymod.c

include $(BUILD_EXECUTABLE)
48
Simo Kinnunen

Chromiumプロジェクトは、PIBバイナリをJB以前のAndroidリリースで実行できる wrapper をリリースしました。 PIE実行可能ファイルには、これを機能させるためにいくつかの追加フラグが必要であることに注意してください。

CFLAGS += -fvisibility=default -fPIE
LDFLAGS += -rdynamic -fPIE -pie

私の場合、3つのアーキテクチャで2 MBのバイナリを出荷していましたが、ICSのサポートを続けるためだけに6 MBの非圧縮データをAPKに追加したくありませんでした。 run_pieは非常に小さい(6-7kB)ため、法案に適合します。

run_pieは、PIEフラグを使用してnot構築する必要があり、notAndroid 5.0+で実行されます(もちろん、非PIEバイナリは禁止されているため)。残念ながら、-ldlとリンクする必要があり、NDKはそのライブラリの共有バージョンのみを提供するため、静的にビルドすることはできません。

Java側は次のようになります。

String dir = mContext.getFilesDir().getPath();
String command = dir + "/busybox netstat";
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
    command = dir + "/run_pie " + command;
}

ここで、busyboxはPIE実行可能ファイルであり、アプリのプライベートファイルディレクトリにあります。

参照:このトピックの以前の説明 here および here

JFDeeの編集:私の場合、run_pieをPIE実行可能ファイルで実行すると、「dlopen()failed:Cannot load library」というエラーが表示され続けました。 LD_LIBRARY_PATHを、実行可能ファイルが存在するディレクトリ、つまり現在のパスに明示的に設定する必要がありました。

その場合、「run_pie」コールの修正されたサンプルコード行は次のようになります。

...
    command = "LD_LIBRARY_PATH=. " + dir + "/run_pie " + command;
...
14
Kevin Cernekee