【问题标题】:What software-visible processor state needs to go in a jmp_buf on an x86-64 processor?x86-64 处理器上的 jmp_buf 需要什么样的软件可见处理器状态?
【发布时间】:2023-03-12 08:04:01
【问题描述】:

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

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

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

【问题讨论】:

  • 如果 glibc 实现这么说,是什么让你认为它不正确?
  • 我只是不确定我对代码的理解,因为我对汇编和 C 比较陌生。
  • 你是在问 glibc 是这样做的,还是 glibc 所做的是否正确,还是别的什么?
  • 我一般问的是在 x86-64 架构上的 jmp_buf 中需要什么样的处理器状态。不是 glibc 所做的或它是否正确。例如,需要保存哪些 x86-64 寄存器?条件标志呢?例如,我认为浮点寄存器不需要保存,因为它们对程序的状态没有贡献。
  • 问什么不会被保存可能更容易。不会保存所有调用方保存的寄存器。调用setjmp 就像任何其他函数一样。调用者必须在调用setjmp 之前保留调用者寄存器(易失性)。在 x86-64(和 x86)上,这些数据通常通过将它们推送或存储到非易失性寄存器来保存到堆栈中。因为这个数据已经保存了,jmp_buf 需要自己关心保存所有非易失性(被调用者保存)寄存器。

标签: c assembly x86 x86-64 setjmp


【解决方案1】:

例如,需要保存哪些 x86-64 寄存器?条件标志呢?例如,我认为浮点寄存器不需要保存,因为它们对程序的状态没有贡献。

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

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

您正在查看 glibc 对 x86-64 System V ABI 的实现,其中 all 向量/x87 寄存器被调用破坏,因此不必保存。

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

即与此处相关的不是 CPU 架构,而是软件调用约定。


除了保留调用的寄存器之外,一个关键的事情是,只有在调用setjmp 的函数返回后,longjmp 才能被父函数保存的jmp_buf 合法,而不是来自任何任意函数。

如果setjmp 必须支持这一点,它就必须保存整个堆栈帧,或者实际上(让函数能够返回,并且该父级能够返回等)整个堆栈全部通往顶峰的路。这显然很疯狂,因此很清楚为什么longjmp 具有只能跳转到父/(大)祖父函数的限制,因此它只需将堆栈指针恢复为指向仍然存在的堆栈帧和恢复自 setjmp 以来该函数中可能已修改的任何局部变量。

(在架构上的 C/C++ 实现/使用普通调用堆栈以外的调用约定时,关于跳转目标函数能够返回的类似论点仍然适用。)

【讨论】:

    【解决方案2】:

    由于jmp_buf 是唯一可用于在longjmp 上恢复处理器状态的位置,因此它通常是恢复机器完整状态所需的一切,就像调用setjmp 时一样。

    这显然在很大程度上取决于处理器和编译器(它究竟是如何利用 CPU 的特性来存储程序状态的):

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

    【讨论】:

    • 谢谢!这使它更清楚。但是在实际机器上,比如 x86-64 架构,我们真的需要保存完整的寄存器集吗?还有浮点寄存器、标志状态等?
    • 你没有看第二段,尤其是括号里的部分。
    • 我做到了,但我希望对于具体的 x86-64 GNU C 编译器或类似的编译器有更具体的答案。只是为了了解 jmp_buf 中可能重要的内容以及程序状态/环境可能包含的内容。
    猜你喜欢
    • 1970-01-01
    • 2011-09-04
    • 1970-01-01
    • 1970-01-01
    • 2017-05-30
    • 2021-10-25
    • 2010-10-11
    • 2012-03-21
    相关资源
    最近更新 更多