自定义ELF段调用mprotect()触发段错误的原因及解决尝试问询
自定义ELF段调用mprotect触发段错误的原因与解决方法
看到一篇关于获取自定义ELF段起始和结束地址的内容后,我尝试对存储在自定义ELF段中的函数调用mprotect(),但运行时触发了段错误——即使没有调用目标函数也会出错。用GDB调试后,回溯信息指向程序退出阶段的_fini函数;且发现将权限改为PROT_EXEC时无此问题,推测是自定义段被设置为无执行权限时,fini函数需要执行该受保护内存区域导致错误。
测试代码
#include <stdio.h> #include <unistd.h> #include <sys/mman.h> #define PAGESIZE 4096 // 自定义ELF段需要页对齐才能使用mprotect void foo() __attribute__((aligned(PAGESIZE))) __attribute__ ((section ("custom_sec"))); extern struct fun_info *__start_custom_sec; extern struct fun_info *__stop_custom_sec; int main() { // Sanity检查 printf("Custom section range: %p to %p\n", (void *)&__start_custom_sec, (void *)&__stop_custom_sec); if(mprotect(&__start_custom_sec, getpagesize(), PROT_READ | PROT_WRITE) == -1) { perror("mprotect()"); return 1; } } void foo() { printf("Foo\n"); }
编译运行命令
gcc stackoverflow.c -o so ./so
GDB回溯信息
>>> bt #0 0x0000555555555014 in _fini () #1 0x00007ffff7de8d88 in _dl_fini () at dl-fini.c:240 #2 0x00007ffff7a6f940 in __run_exit_handlers (status=0, listp=0x7ffff7dd35d8 <__exit_funcs>, run_list_atexit=run_list_atexit@entry=true, run_dtors=run_dtors@entry=true) at exit.c:83 #3 0x00007ffff7a6f99a in __GI_exit (status=<optimized out>) at exit.c:105 #4 0x00007ffff7a5a2e8 in __libc_start_main (main=0x5555555547e0 <main>, argc=1, argv=0x7fffffffe518, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffe508) at ../csu/libc-start.c:325 #5 0x00005555555546da in _start ()
我的初衷是在大型程序中,将多个函数放入自定义段并统一用mprotect设置权限,但目前的实现会触发段错误,需要明确故障原因。
更新:解决方案
在提示下,我通过编写链接脚本解决了问题:
链接脚本so_linker.ld
SECTIONS { .custom_sec BLOCK(4096) : ALIGN(4096) { _start_custom_sec = .; *(.custom_sec) _end_custom_sec = .; } } INSERT AFTER .rodata;
修改后的测试代码
#include <stdio.h> #include <unistd.h> #include <sys/mman.h> #define PAGESIZE 4096 void foo() __attribute__ ((section (".custom_sec"))); const void * _start_custom_sec; const void * _end_custom_sec; int main() { // Sanity检查 printf("Custom section range: %p to %p\t Foo: %p\n", (void *)&_start_custom_sec, (void *)&_end_custom_sec, (void *)&foo); if(mprotect(&foo, getpagesize(), PROT_READ | PROT_WRITE) == -1) { perror("mprotect()"); return 1; } } void foo() { printf("Foo\n"); }
编译命令
gcc -T so_linker.ld stackoverflow.c -o so
段布局验证
通过readelf --sections W so查看段布局,确认.custom_sec已被移至独立的页对齐区域:
[11] .init PROGBITS 0000000000000620 000620 000017 00 AX 0 0 4 [12] .plt PROGBITS 0000000000000640 000640 000060 10 AX 0 0 16 [13] .plt.got PROGBITS 00000000000006a0 0006a0 000008 00 AX 0 0 8 [14] .text PROGBITS 00000000000006b0 0006b0 000212 00 AX 0 0 16 [15] .fini PROGBITS 00000000000008c4 0008c4 000009 00 AX 0 0 4 [16] .rodata PROGBITS 00000000000008d0 0008d0 000044 00 A 0 0 8 [17] .custom_sec PROGBITS 0000000000001000 001000 000013 00 AX 0 0 4096
关键修改点
- 自定义段名需要以
.开头(如custom_sec改为.custom_sec),才能被链接脚本正确识别并重新布局 - 通过链接脚本将
.custom_sec设置为页对齐的独立区域,避免与其他需要执行权限的段(如.fini)共享页面
内容的提问来源于stack exchange,提问作者Jay
相关产品推荐
相关产品推荐

