【问题标题】:Running a native library on Android L. error: only position independent executables (PIE) are supported在 Android L. 上运行本机库错误:仅支持与位置无关的可执行文件 (PIE)
【发布时间】:2014-09-09 05:33:28
【问题描述】:

当我在 Android L (Nexus 5) 上运行本机代码时,出现错误。

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

相同的代码在我的三星 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 时,Jelly Been(API 级别 16)中出现了 PIE 支持),相同的可执行文件在 Android L 上运行良好。

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

【问题讨论】:

    标签: android android-ndk


    【解决方案1】:

    如果您只能支持 Android 4.1+,只需设置 APP_PLATFORM := android-16 即可。在幕后设置APP_PIE := true。您的二进制文件将在较旧的 SDK 上出现段错误。

    如果您还需要支持较低级别的 SDK,则需要创建两个二进制文件。我见过的其他一些答案建议使用不同的 APP_PLATFORM 维护两个单独的源代码树,但您不需要这样做。可以使单个 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)
    

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

    不幸的是,这意味着您必须编译可执行模块两次,这可能会很慢。您还需要指定 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,并且不允许您禁用它,除非有可怕的 hack。真的,只需更新到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)
    

    【讨论】:

    • 你能解释一下make文件吗?很难理解
    • 您的意思是您不熟悉 Android.mk 文件,还是说您习惯使用 Android.mk 文件但是这特别难以理解?
    • 其实我的 Android.mk 文件已经有一些命令了......我只是不知道我是否必须在 make 文件的开头或 make 文件的末尾添加它..我只是担心我添加的地方......它会影响。
    • 如果你使用我推荐的版本,你应该把你现有的设置放在 mymod-common 部分(它们应该按原样工作)。除非您正在做一些特别的事情,否则您不需要在 BUILD_STATIC_LIBRARY 行之后触摸任何内容(更改模块名称除外)。
    • @SimoKinnunen:我在尝试在控制台(终端 IDE)中执行命令时遇到了同样的 PIE 错误。不知道为什么...你知道我是如何解决这个问题的吗?在 Galaxy Tab Pro 8.4 中使用 CY 12.0 (Android 5.0.2)。
    【解决方案2】:

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

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

    在我的例子中,我为 3 种架构运送了大约 2MB 的二进制文件,并且不想为了继续支持 ICS 向 APK 添加 6MB 的未压缩数据。 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:在我的例子中,当我使用我的 PIE 可执行文件运行 run_pie 时,我不断收到错误“dlopen() failed: Cannot load library”。我必须将 LD_LIBRARY_PATH 显式设置为可执行文件所在的目录,即当前路径。

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

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

    【讨论】:

      【解决方案3】:

      我构建了两个可执行文件:一个带有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
      }
      

      【讨论】:

      • 你能解释一下为什么会这样吗? android-16 是解决这个问题的方法吗?
      • 您在 Nexus 5 上验证了吗?
      • 我有这个错误 /home/hfi/Downloads/android-ndk-r10d/platforms/android-16/arch-arm/usr/lib/crtbegin_dynamic.o:crtbrand.c:function _start :错误:未定义对'main'collect2的引用:错误:ld返回1退出状态库.mak:106:目标'libavutil/libavutil-54.so'的配方失败制作:*** [libavutil/libavutil-54.so ] 错误 1
      猜你喜欢
      • 2015-08-17
      • 1970-01-01
      • 2015-04-19
      • 2014-11-11
      • 1970-01-01
      • 1970-01-01
      • 2015-01-17
      • 2015-08-10
      • 1970-01-01
      相关资源
      最近更新 更多