使用信号处理修改程序计数器跳过除零错误时遇内存访问报错求助
问题分析与解决
1. 内存访问错误的根源
你遇到的runtime error: load of address ... insufficient space for an object of type 'int',核心原因是指针类型不匹配导致的越界访问:
&signalno是int*类型,(&signalno)+39等价于(char*)&signalno + 39*sizeof(int)(也就是你说的156字节,假设int为4字节),地址是对的,但该位置存储的是程序计数器(PC),其大小为系统指针长度(32位系统4字节,64位系统8字节)。- 若在64位系统下,用
int*去读8字节的PC数据,会试图从目标地址开始读取4字节,后续内存访问会超出合法对象边界,或不符合int的对齐要求,触发错误。
2. 修复内存访问错误
将指针转换为指针大小的标准整数类型(uintptr_t,需包含<stdint.h>),避免类型不匹配问题:
#include <stdint.h> #include <inttypes.h> void signal_handle(int signalno) { // 用char*做精确字节偏移,再转为uintptr_t*访问指针大小的数据 uintptr_t *pc_ptr = (uintptr_t*)((char*)&signalno + 156); printf("p1: %p, p2: %p, v: %" PRIxPTR "\n", &signalno, pc_ptr, *pc_ptr); printf("I was slain!\n"); // 修改PC,跳过除零指令(需根据实际指令长度调整偏移量) *pc_ptr += 1; }
- 用
char*做偏移确保是精确的156字节,不受int大小影响; uintptr_t是专门存储指针的整数类型,匹配系统指针长度,避免访问越界。
3. 更可靠的PC修改方式
直接依赖栈偏移访问PC是不稳定的,会受编译器、系统栈布局影响。标准做法是利用信号处理函数的上下文参数修改寄存器:
信号处理函数可声明为以下形式,通过ucontext_t直接获取寄存器上下文并修改PC:
#include <ucontext.h> void signal_handle(int signalno, siginfo_t *info, ucontext_t *uc) { printf("I was slain!\n"); // x86_64系统修改RIP,32位系统修改EIP uc->uc_mcontext.gregs[REG_RIP] += 1; // 跳过除零指令,需根据实际指令长度调整 }
注册信号时需指定SA_SIGINFO标志,才能传递额外参数:
struct sigaction sa; sa.sa_sigaction = signal_handle; sa.sa_flags = SA_SIGINFO; sigemptyset(&sa.sa_mask); sigaction(SIGFPE, &sa, NULL);
这种方法不依赖栈布局,兼容性和稳定性更强。
4. 确认指令长度
需验证除零指令的实际长度,避免偏移量错误导致程序崩溃:
- 用
gdb调试,在z=x/y;处打断点,执行disassemble查看编译后的指令:
找到除零对应的指令(如break main:10 // 假设z=x/y在第10行 run disassembledivl 0x10(%rbp)),查看其长度,调整PC的偏移量,确保跳转到printf("I live again...")的第一条指令。
内容的提问来源于stack exchange,提问作者WubbaLubbaDubbDubb
相关产品推荐
相关产品推荐

