我正在为 Android 5.0 重新编译一些可执行文件,因为它要求可执行文件是
PIE
。我能够为 ARM
重新编译它,只需在配置时添加一些参数(使用独立工具链):
export CFLAGS="-I/softdev/arm-libs/include -fPIE"
export CPPLAGS="$CPPFLAGS -fPIE"
export CXXLAGS="$CXXFLAGS -fPIE"
export LDFLAGS="-L/softdev/arm-libs/lib -static -fPIE -pie"
ARM 没有错误:
configure:3406: arm-linux-androideabi-gcc -o conftest -I/softdev/arm-libs/include -fPIE -L/softdev/arm-libs/lib -static -fPIE -pie conftest.c >&5
configure:3410: $? = 0
但是我无法为
x86
做同样的事情,因为我遇到了错误:
export CFLAGS="-I/softdev/x86-libs/include -fPIE"
export CPPLAGS="$CPPFLAGS -fPIE"
export CXXLAGS="$CXXFLAGS -fPIE"
export LDFLAGS="-L/softdev/x86-libs/lib -static -fPIE -pie"
错误:
configure:3336: i686-linux-android-gcc -I/softdev/x86-libs/include -fPIE -L/softdev/x86-libs/lib -static -fPIE -pie conftest.c >&5
/softdev/x86-toolchain-gcc4.8/bin/../lib/gcc/i686-linux-android/4.8/../../../../i686-linux-android/bin/ld: fatal error: -pie and -static are incompatible
collect2: error: ld returned 1 exit status
configure:3340: $? = 1
我需要静态链接可执行文件。出了什么问题,我该如何解决?
附言。还尝试使用来自 android ndk r9d 和 r10c 的 x86 独立工具链:
./make-standalone-toolchain.sh --toolchain=x86-4.8 --arch=x86 --install-dir=/softdev/x86-toolchain-gcc4.8-r9d --ndk-dir=/softdev/android-ndk-r9d/ --system=darwin-x86_64
如 n4sm 所述,gcc-8 或更高版本支持
-static-pie
,它使用 PIE 生成静态二进制文件。请注意,这是一个选项,而不是两个。如果你尝试使用 -static -pie
它不会按照你的想法去做。
我刚刚用 te.c 中的休假做了快速测试:
int main( int argc, const char* argv[] )
{
return 0;
}
运行
arm-linux-androideabi-gcc -o conftest -static -FPIE -pie te.c
不会产生错误。然而file -k conftest
输出
conftest: ELF 32-bit LSB shared object, ARM, EABI5 version 1 (SYSV), dynamically linked (uses shared libs), not stripped
readelf -l conftest
输出
Elf文件类型为DYN(共享对象文件)
入口点 0x500
有 7 个程序头,从偏移量 52 开始
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
PHDR 0x000034 0x00000034 0x00000034 0x000e0 0x000e0 R 0x4
INTERP 0x000114 0x00000114 0x00000114 0x00013 0x00013 R 0x1
[Requesting program interpreter: /system/bin/linker]
...
PHDR 和 INTERP 标头的存在表明 -pie 在 arm 编译器中默默地覆盖了 -static。为什么这是我不知道的,但我认为这是一个错误,当 -static 和 -pie 一起使用时没有给出警告。相反,像您这样的程序员会产生一种错误的印象,即这两个选项可以一起用于 arm。
只是为了澄清这里唯一的行为差异是 x86 编译器在同时看到 --static 和 --pie 时出错,而 arm 版本在给出 --pie 时默默地忽略 --static。如果只给出其中一个,则两个编译器的行为是相同的。
现在可以直接使用 -static-pie 选项了!
例如:
#include <stdio.h>
/* /tmp/test.c */
int main(int argc, char **argv) {
printf("Hello world!\n");
}
您只需使用 -static-pie 选项:
gcc /tmp/test.c -static-pie -o /tmp/test
有了 readelf,我们得到了这个:
LOAD 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000008158 0x0000000000008158 R 0x1000
LOAD 0x0000000000009000 0x0000000000009000 0x0000000000009000
0x000000000009473d 0x000000000009473d R E 0x1000
LOAD 0x000000000009e000 0x000000000009e000 0x000000000009e000
0x00000000000284b8 0x00000000000284b8 R 0x1000
LOAD 0x00000000000c6de0 0x00000000000c7de0 0x00000000000c7de0
0x0000000000005370 0x0000000000006a80 RW 0x1000
DYNAMIC 0x00000000000c9c18 0x00000000000cac18 0x00000000000cac18
0x00000000000001b0 0x00000000000001b0 RW 0x8
NOTE 0x00000000000002e0 0x00000000000002e0 0x00000000000002e0
0x0000000000000020 0x0000000000000020 R 0x8
NOTE 0x0000000000000300 0x0000000000000300 0x0000000000000300
0x0000000000000044 0x0000000000000044 R 0x4
TLS 0x00000000000c6de0 0x00000000000c7de0 0x00000000000c7de0
0x0000000000000020 0x0000000000000060 R 0x8
GNU_PROPERTY 0x00000000000002e0 0x00000000000002e0 0x00000000000002e0
0x0000000000000020 0x0000000000000020 R 0x8
GNU_EH_FRAME 0x00000000000ba130 0x00000000000ba130 0x00000000000ba130
0x0000000000001c8c 0x0000000000001c8c R 0x4
GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 RW 0x10
GNU_RELRO 0x00000000000c6de0 0x00000000000c7de0 0x00000000000c7de0
0x0000000000003220 0x0000000000003220 R 0x1
我不知道从什么时候我们可以使用这个选项,但对我来说,我正在使用
gcc (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0
Google 的 NDK 工具包含一些关于 PIE 使用的信息。 访问 build/core/build-binary.mk,查看第 209 行。它说:
# 为超出特定 API 级别的可执行文件启用 PIE,除非“-static”
我猜,这是linux动态链接原理的极限。 由于Android解释器(/system/bin/linker)决定在静态链接文件中加载哪个地址的elf文件没有解释器,elf文件会被linux内核映射到内存中成为一个固定的地址。 这是关于此更改的讨论Google问题
如果我有任何错误,请指出:)