如何用GCC编译各段具有不同PIE偏移的PIE ELF程序?
实现多段独立随机偏移的PIE程序(x86_64 Linux)
要让程序中不同段(如.my_data和.text/.bss)每次运行时的相对偏移随机,需要打破默认PIE程序的全局基址共享机制,让每个可加载段(PT_LOAD)被动态链接器独立随机化加载。以下是具体实现步骤:
1. 修改链接脚本(l.lds)
显式定义所有段的内存区域,确保.my_data和其他段分属不同的MEMORY区域,且链接器为每个段生成独立的PT_LOAD条目:
ENTRY(start) MEMORY { main_region (rwxai) : ORIGIN = 0x300000, LENGTH = 0x100000 /* 3M - 4M */ my_data_region (rwxai) : ORIGIN = 0x500000, LENGTH = 0x100000 /* 5M - 6M */ } SECTIONS { /* 处理代码段 */ .text : { *(.text) *(.text.*) } >main_region /* 处理只读数据段 */ .rodata : { *(.rodata) *(.rodata.*) } >main_region /* 处理已初始化数据段 */ .data : { *(.data) *(.data.*) } >main_region /* 处理未初始化数据段 */ .bss : { *(.bss) *(.bss.*) *(COMMON) } >main_region /* 自定义数据段,放到独立内存区域 */ .my_data : { *(.my_data) } >my_data_region =0x9090 }
2. 更新编译/链接选项(Makefile)
添加-fPIC确保代码完全位置无关,链接时启用-z separate-loadable-segments让链接器生成独立PT_LOAD段:
CFLAGS = -fPIE -fPIC ASFLAGS = -fPIE a: a.o l.lds ld -pie -z separate-loadable-segments --dynamic-linker=/lib64/ld-linux-x86-64.so.2 -T l.lds -o $@ a.o clean: rm -f *.o a
3. 启用内核段级ASLR
Linux默认的ASLR级别(kernel.randomize_va_space=2)支持段级独立随机化,执行以下命令启用:
sudo sysctl kernel.randomize_va_space=2
该设置会让动态链接器为每个PT_LOAD段计算独立的随机加载地址,而非共享全局基址。
原理说明
- 默认PIE程序的所有PT_LOAD段共享同一个基址偏移,段间相对偏移固定;
-z separate-loadable-segments让链接器为每个段生成独立的PT_LOAD条目。 - 内核ASLR级别2会对每个PT_LOAD段单独应用随机偏移,实现段间相对地址的随机化。
-fPIE/-fPIC确保编译器生成位置无关代码,全局变量(如my_array)通过GOT表访问,动态链接器加载时会重定位GOT条目到实际随机地址。
验证效果
编译后运行程序,多次打印&my_array、&start、&normal_array的地址,会观察到各段的相对偏移每次运行都不同,符合预期效果。
内容的提问来源于stack exchange,提问作者Eric Stdlib
相关产品推荐
相关产品推荐

