首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在AndroidL.Error上运行本机库:只支持与位置无关的可执行文件(PIE)

在AndroidL.Error上运行本机库:只支持与位置无关的可执行文件(PIE)
EN

Stack Overflow用户
提问于 2014-07-18 06:54:35
回答 3查看 88.7K关注 0票数 55

当我在Android (Nexus 5)上运行本机代码时,我会得到错误。

错误:只支持与位置无关的可执行文件(PIE)。

同样的代码在我的三星Galaxy S3 (Android4.3)上被正确执行。

这是我的Application.mk

代码语言:javascript
复制
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时(当我阅读这里时,PIE支持出现在Jelly ( appeared 16)中),同样的可执行文件在Android上工作得很好。

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

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2014-07-22 06:18:51

我构建了两个可执行文件:一个使用APP_PLATFORM := android-9,另一个使用APP_PLATFORM := android-16。要在Java中运行本机代码,我需要这样做:

代码语言:javascript
复制
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
EN

Stack Overflow用户

发布于 2014-10-17 10:17:32

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

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

NDK10c及更高版本:

确保默认情况下禁用饼图,因为手动启用它比禁用它容易。除非您的APP_PLATFORM是>=16,否则PIE默认不会启用。确保您的APP_PLATFORM没有设置(默认为Androd-3,或android-14,自NDK 15以来),低于android-16,或者设置APP_PIE := false

然后,下面的Android.mk创建一个饼和一个非饼二进制(),但是有一个警告(见下文)

代码语言:javascript
复制
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和任何库,这可能令人沮丧,而且很难跟踪。您可以做的是将主可执行文件编译成一个静态库,然后只从这个静态库构建可执行文件。静态库不需要饼饼。

代码语言:javascript
复制
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:

NDK10b默认启用饼,并且不允许禁用它,除非使用可怕的黑客。真的,更新到10c就行了。我把我以前的答案留在这里作参考,但我不会推荐给任何人。

代码语言:javascript
复制
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)
票数 51
EN

Stack Overflow用户

发布于 2014-12-29 17:45:23

Chromium项目发布了一个包装器,允许PIE二进制文件在JB发布前运行。请注意,为了使此工作,饼可执行文件需要一些额外的标志:

代码语言:javascript
复制
CFLAGS += -fvisibility=default -fPIE
LDFLAGS += -rdynamic -fPIE -pie

在我的例子中,我为3种体系结构发送了~2MB二进制文件,并且不想为继续支持ICS而向APK添加6MB的未压缩数据。run_pie非常小(6-7kB),所以它符合要求。

run_pie不应该使用饼标志构建,也不应该在Android 5.0+上执行(当然,非饼类二进制文件是被禁止的)。不幸的是,它不能静态构建,因为它需要与-ldl链接,而且NDK只提供了该库的共享版本。

Java端看起来可能如下所示:

代码语言:javascript
复制
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是一个饼形可执行文件,并生活在应用程序的私有文件目录中。

还请参阅:前面对本主题这里这里的讨论。

编辑JFDee:在我的示例中,在使用饼式可执行文件运行run_pie时,我一直得到错误"dlopen() failed:无法加载库“。我必须显式地将LD_LIBRARY_PATH设置为可执行文件所在的目录,即当前路径。

在这种情况下,"run_pie“调用的修改示例代码行如下所示:

代码语言:javascript
复制
...
    command = "LD_LIBRARY_PATH=. " + dir + "/run_pie " + command;
...
票数 14
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/24818902

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档