在 Android L. 上运行本机库错误:仅支持位置无关可执行文件 (PIE)
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/24818902/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Running a native library on Android L. error: only position independent executables (PIE) are supported
提问by Maksim Dmitriev
When I run native code on Android L (Nexus 5), I get the error.
当我在 Android L (Nexus 5) 上运行本机代码时,出现错误。
error: only position independent executables (PIE) are supported.
错误:仅支持位置无关可执行文件 (PIE)。
The same code is executed correctly on my Samsung Galaxy S3 (Android 4.3).
在我的三星 Galaxy S3 (Android 4.3) 上正确执行了相同的代码。
Here is my Application.mk
这是我的 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
However when I replace APP_PLATFORM := android-9
with APP_PLATFORM := android-16
(As I read here, PIE support appeared in Jelly Been (API level 16)), the same executable file works fine on Android L.
但是,当我替换APP_PLATFORM := android-9
为APP_PLATFORM := android-16
(正如我在此处阅读的那样,PIE 支持出现在 Jelly Being(API 级别 16)中)时,相同的可执行文件在 Android L 上运行良好。
Is there a way to compile native code using APP_PLATFORM := android-9
and run it on Android L?
有没有办法编译本机代码APP_PLATFORM := android-9
并在 Android L 上运行它?
采纳答案by Maksim Dmitriev
I built two executable files: one with APP_PLATFORM := android-9
and the other with APP_PLATFORM := android-16
. To run the native code in Java I need this:
我构建了两个可执行文件:一个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
}
回答by Simo Kinnunen
If you can live with only supporting Android 4.1+, just set APP_PLATFORM := android-16
and you'll be good to go. Behind the scenes it sets APP_PIE := true
. Your binary will segfault on older SDKs.
如果您只能接受仅支持 Android 4.1+ 的系统,只需设置APP_PLATFORM := android-16
即可。它在幕后设置APP_PIE := true
。您的二进制文件将在较旧的 SDK 上出现段错误。
If you also need to support lower SDK levels, you'll need to create two binaries. Some other answers I've seen have recommended maintaining two separate source trees with different APP_PLATFORMs, but you don't need to do that. It's possible to make a single Android.mk output both a PIE and a non-PIE binary.
如果您还需要支持较低的 SDK 级别,则需要创建两个二进制文件。我见过的其他一些答案建议使用不同的 APP_PLATFORM 维护两个单独的源树,但您不需要这样做。可以使单个 Android.mk 输出 PIE 和非 PIE 二进制文件。
NDK 10c and later:
NDK 10c 及更高版本:
Make sure that PIE is disabled by default since enabling it manually is easier than disabling it. PIE doesn't get enabled by default unless your APP_PLATFORM is >=16. Make sure that your APP_PLATFORM is either not set (defaulting to android-3, or android-14 since NDK 15), lower than android-16, or set APP_PIE := false
.
确保默认情况下禁用 PIE,因为手动启用它比禁用它更容易。除非您的 APP_PLATFORM >=16,否则默认情况下不会启用 PIE。确保您的 APP_PLATFORM 未设置(默认为 android-3 或自 NDK 15 以来的 android-14)、低于 android-16 或设置APP_PIE := false
.
The following Android.mk then creates a PIE and a non-PIE binary, but has a caveat (see below):
下面的 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)
You'll then have to add some sort of logic to invoke the correct binary in your code.
然后,您必须添加某种逻辑来在代码中调用正确的二进制文件。
Unfortunately, this means you'll have to compile the executable module twice, which can be slow. You also need to specify LOCAL_SRC_FILES and any libraries twice, which can be frustrating and difficult to keep track of. What you can do is to compile the main executable as a static library, and build executables from nothing but that static library. Static libraries do not require PIE.
不幸的是,这意味着您必须将可执行模块编译两次,这可能会很慢。您还需要指定 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)
This seems to work quite nicely, although a certain amount of boilerplate is still required.
这似乎工作得很好,尽管仍然需要一定数量的样板。
NDK 10b:
NDK 10b:
NDK 10b enables PIE by default and doesn't let you disable it, except with terrible hacks. Really, just update to 10c. I'm leaving my old answer here for reference but I wouldn't recommend it to anyone.
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)
回答by Kevin Cernekee
The Chromium project released a wrapperthat allows PIE binaries to run on pre-JB Android releases. Note that your PIE executable requires a few extra flags to make this work:
Chromium 项目发布了一个包装器,允许 PIE 二进制文件在 JB 之前的 Android 版本上运行。请注意,您的 PIE 可执行文件需要一些额外的标志才能使其工作:
CFLAGS += -fvisibility=default -fPIE
LDFLAGS += -rdynamic -fPIE -pie
In my case, I was shipping ~2MB binaries for 3 architectures and did not want to add 6MB of uncompressed data to the APK just to continue supporting ICS. run_pie
is extremely tiny (6-7kB) so it fit the bill.
就我而言,我为 3 个架构提供了大约 2MB 的二进制文件,并且不想仅仅为了继续支持 ICS 向 APK 添加 6MB 的未压缩数据。 run_pie
非常小(6-7kB),所以它符合要求。
run_pie
should notbe built with the PIE flags, and it should notbe executed on Android 5.0+ (because, of course, non-PIE binaries are banned). Unfortunately it cannot be built statically because it needs to be linked with -ldl
and NDK only provides a shared version of that library.
run_pie
应该不与PIE标志建成,它应该不会在Android上执行5.0以上(因为,当然,非PIE二进制文件禁止)。不幸的是,它不能静态构建,因为它需要链接,-ldl
而 NDK 只提供该库的共享版本。
The Java side could look something like:
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;
}
where busybox
is a PIE executable and lives in the app's private files directory.
哪里busybox
是 PIE 可执行文件,它位于应用程序的私有文件目录中。
See also: earlier discussions of this topic hereand here.
Edit JFDee: In my case, I kept getting the error "dlopen() failed: Cannot load library" when running run_pie with my PIE executable. I had to explicitly set LD_LIBRARY_PATH to the directory the executable resided in, i.e. the current path.
编辑 JFDee:就我而言,在使用 PIE 可执行文件运行 run_pie 时,我不断收到错误“dlopen() 失败:无法加载库”。我必须将 LD_LIBRARY_PATH 显式设置为可执行文件所在的目录,即当前路径。
In that case the amended example code line of the "run_pie" call would look like this:
在这种情况下,“run_pie”调用的修改示例代码行将如下所示:
...
command = "LD_LIBRARY_PATH=. " + dir + "/run_pie " + command;
...