【问题标题】:Get instruction pointer on segmentation fault or crash (for x86 JIT compiler project)?获取分段错误或崩溃的指令指针(对于 x86 JIT 编译器项目)?
【发布时间】:2026-02-14 05:05:01
【问题描述】:

我正在为生成 x86 代码的 JavaScript JIT 编译器实现后端。有时,由于错误,我会遇到分段错误。追溯造成它们的原因可能非常困难。因此,我一直想知道是否会有一些“简单”的方法来捕获分段错误和其他此类崩溃,并获取导致错误的指令的地址。这样,我可以将地址映射回已编译的 x86 程序集,甚至可以映射回源代码。

这需要在 Linux 上运行,但最好在任何符合 POSIX 的系统上运行。在最坏的情况下,如果我无法捕获 seg 错误并在运行的 JIT 中获取 IP,我希望能够将其捕获到外部(内核日志?),也许只是让编译器转储一个大文件将地址映射到指令,我可以用 Python 脚本或其他东西进行匹配。

感谢任何想法/建议。如果您曾经参与过自己的编译器项目,请随时分享您自己的调试技巧。

【问题讨论】:

标签: linux x86 segmentation-fault posix jit


【解决方案1】:

如果你使用sigaction,你可以定义一个接受3个参数的信号处理器:

void (*sa_sigaction)(int signum, siginfo_t *info, void *ucontext)

传递给信号处理程序的第三个参数是指向操作系统和体系结构特定数据结构的指针。在 linux 上,它是一个 ucontext_t,它在 <sys/ucontext.h> 头文件中定义。其中,uc_mcontext 是一个mcontext_t(机器上下文),对于x86,它包含gregs 中的信号发生时的所有寄存器。这样你就可以访问了

ucontext->uc_mcontext.gregs[REG_EIP]  (32 bit mode)
ucontext->uc_mcontext.gregs[REG_RIP]  (64 bit mode)

获取故障指令的指令指针。

【讨论】:

    【解决方案2】:

    使用带有 SA_SIGINFO 标志的 sigaction 和带有原型 void (*handler)(int signum, siginfo_t *info, void *data) 的信号处理程序。当调用信号处理程序时,info->si_addr 将包含发生故障的指令指针的值。

    请记住,在接收到不是使用 raise() 或 kill() 生成的 SISEGV 后,进程的状态是未定义的。如果可以,请使用

    【讨论】:

    • si_addr 是导致故障的地址,而不是导致故障的指令。另外,进程的状态不是未定义的,只是没有定义为 POSIX 规范的一部分。这取决于操作系统和架构之一。
    • 我了解到进程的状态是“未定义”。但实际上,如果我的 JIT 编译器正在运行一些客户端程序并且客户端程序出现故障,我猜我的 JIT 可能仍处于一致状态,并且至少能够在退出之前打印一些调试信息?客户端程序不应破坏主机 JIT 的状态。
    • Chris Dodd 对 si_addr 的看法是正确的:我阅读了手册页并错过了那部分。然而,仅仅因为 POSIX 说它是未定义的,并不意味着“如果你不是 POSIX,它就是安全的”。许多类 UNIX 操作系统会将收到 SIGSEGV 的进程视为后终止进程,并且可能会或可能不会将该内存分配给新运行的程序。因此,绝对挂钩 SIGSEGV 并执行任何不涉及立即终止程序的操作。