在 x86-64 处理器上,什么软件可见的处理器状态需要进入 jmp_buf?

What software-visible processor state needs to go in a jmp_buf on an x86-64 processor?

如前所述,当调用 setjmp(jmp_buf env) 时,x86-64 处理器上的 jmp_buf 中需要进入什么软件可见处理器状态?什么处理器状态没有?

我已经阅读了很多关于 setjmplongjmp 的文章,但找不到我的问题的明确答案。我知道它依赖于实现,但我想知道 x86_64 架构。

来自following implementation 似乎在 x86-64 机器上,所有被调用者保存的寄存器(%r12-%r15%rbp%rbx)以及堆栈指针、程序计数器和所有保存的寄存器都需要保存当前环境的参数。不过我不太确定,希望有人能为我澄清一下。

由于 jmp_buf 是唯一可用于在 longjmp 上恢复处理器状态的地方,它通常是恢复机器完整状态所需的一切setjmp被调用。

这显然在很大程度上取决于处理器和编译器(它究竟使用 CPU 的功能来存储程序状态):

  • 在一个理想的纯堆栈机器上,除了堆栈之外别无他处保存 CPU 状态信息,那将只是堆栈指针。除了非常古老或纯粹的学术实现之外,这样的机器很少存在。但是,您可以在像 x86 这样的现代机器上编写编译器,它只使用堆栈来存储此类信息。对于这样一个假设的编译器,只保存堆栈指针就足以恢复程序状态。
  • 在更常见、更实用的机器上,这可能是堆栈指针和用于存储程序状态的全套寄存器。
  • 在某些 CPUs 上,在其他地方存储程序状态信息,例如在零页中,以及利用此类 CPU 功能的编译器,jmp_buff 将还需要存储此零页的副本(某些 65xx CPUs 或 ATmel AVR MCU 及其编译器可能会使用此功能)

For example, which x86-64 registers need to be saved? What about condition flags? For example, I think the floating point registers do not need to be saved because they don't contribute to the state of the program.

那是因为调用约定。 setjmp 是一个函数调用,可以 return 多次(第一次实际调用它,稍后子函数调用 longjmp),但它仍然是一个函数调用。与任何函数调用一样,编译器假定所有被调用破坏的寄存器都已被破坏,因此 longjmp 不需要恢复它们。

所以是的,它们不是函数调用边界上 "program state" 的一部分,因为编译器生成的 asm 肯定不会在其中保留任何值。

您正在查看 x86-64 系统 V ABI 的 glibc 实现,其中 所有 向量/x87 寄存器都被调用破坏,因此不必保存.

在 Windows x86-64 调用约定中,xmm6-15 是调用保留的(只是低 128 位,而不是 y/zmm6-15 的高位部分),并且必须是一部分jmp_buf.

即这里相关的不是 CPU 架构,而是软件调用约定。


除了调用保留寄存器之外,一个关键的事情是只有 longjmp 到父函数保存的 jmp_buf 是合法的,而不是从调用 [ 的函数之后的任意函数=10=] 编辑了 return。

如果 setjmp 必须支持它,它必须保存整个堆栈帧,或者实际上(为了函数能够 return,并且父级能够 return, 等等) 整个堆栈一直到顶部。这显然是疯狂的,因此很清楚为什么 longjmp 具有只能跳转到父/(伟大的)祖父函数的限制,所以它只需要恢复堆栈指针以指向仍然存在的堆栈构建并恢复自 setjmp.

以来该函数中可能已被修改的任何局部变量

(在架构/调用约定的 C/C++ 实现中,使用不同于普通调用堆栈的东西,关于跳转目标函数能够 return 的类似论点仍然适用。)