You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用信号处理修改程序计数器跳过除零错误时遇内存访问报错求助

问题分析与解决

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
    disassemble
    
    找到除零对应的指令(如divl 0x10(%rbp)),查看其长度,调整PC的偏移量,确保跳转到printf("I live again...")的第一条指令。

内容的提问来源于stack exchange,提问作者WubbaLubbaDubbDubb

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 21:33:20