在 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
中需要进入什么软件可见处理器状态?什么处理器状态没有?
我已经阅读了很多关于 setjmp
和 longjmp
的文章,但找不到我的问题的明确答案。我知道它依赖于实现,但我想知道 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 的类似论点仍然适用。)
如前所述,当调用 setjmp(jmp_buf env)
时,x86-64 处理器上的 jmp_buf
中需要进入什么软件可见处理器状态?什么处理器状态没有?
我已经阅读了很多关于 setjmp
和 longjmp
的文章,但找不到我的问题的明确答案。我知道它依赖于实现,但我想知道 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 的类似论点仍然适用。)