在Android L上运行本机库错误:仅支持位置独立可执行文件(PIE)

时间:2014-07-18 06:54:36

标签: android android-ndk

当我在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-9替换APP_PLATFORM := android-16时(正如我读here,PIE支持出现在Jelly Been(API级别16)),相同的可执行文件在Android L上正常工作

有没有办法使用APP_PLATFORM := android-9编译本机代码并在Android L上运行?

3 个答案:

答案 0 :(得分:48)

如果你只能支持Android 4.1+,只需设置APP_PLATFORM := android-16就可以了。在幕后设置APP_PIE := true。您的二进制文件将在较旧的SDK上进行分段。

如果您还需要支持较低的SDK级别,则需要创建两个二进制文件。我见过的其他一些答案建议使用不同的APP_PLATFORMs维护两个独立的源树,但是您不需要这样做。可以使单个Android.mk输出PIE和非PIE二进制文件。

NDK 10c及更高版本:

确保默认情况下禁用PIE,因为手动启用PIE比禁用PIE更容易。默认情况下,PIE不会启用,除非您的APP_PLATFORM是> = 16。确保你的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)

然后,您必须添加某种逻辑来调用代码中的正确二进制文件。

不幸的是,这意味着你必须两次编译可执行模块,这可能很慢。您还需要两次指定LOCAL_SRC_FILES和任何库,这可能令人沮丧且难以跟踪。你可以做的是将主可执行文件编译为静态库,并从静态库中构建可执行文件。静态库不需要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)

答案 1 :(得分:12)

Chromium项目发布了wrapper,允许PIE二进制文件在JB之前的Android版本上运行。请注意,您的PIE可执行文件需要一些额外的标志才能使其工作:

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

在我的情况下,我为3个架构提供了大约2MB的二进制文件,并且不想向APK添加6MB的未压缩数据以继续支持ICS。 run_pie非常小(6-7kB),因此符合要求。

run_pie 应使用PIE标志构建,并且应在Android 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可执行文件,位于应用程序的私人文件目录中。

另请参阅:此主题的早期讨论herehere

编辑JFDee:在我的情况下,我一直收到错误&#34; dlopen()失败:无法加载库&#34;使用我的PIE可执行文件运行run_pie时。我必须将LD_LIBRARY_PATH显式设置为可执行文件所在的目录,即当前路径。

在这种情况下修改的示例代码行&#34; run_pie&#34;电话会是这样的:

...
    command = "LD_LIBRARY_PATH=. " + dir + "/run_pie " + command;
...

答案 2 :(得分:7)

我构建了两个可执行文件:一个使用APP_PLATFORM := android-9,另一个使用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
}