【问题标题】:What registers must be preserved by an x86 function?x86 函数必须保留哪些寄存器?
【发布时间】:2024-04-19 14:15:02
【问题描述】:

我正在用 x86 程序集编写一个可以从 c 代码调用的函数,我想知道在返回调用者之前必须恢复哪些寄存器。

目前我只恢复espebp,而返回值在eax中。

还有其他我应该关注的寄存器吗,或者我可以在其中留下任何我喜欢的东西吗?

【问题讨论】:

  • 没有一般规则——您必须查阅 C 编译器的文档以了解它的预期。
  • 我很确定 Jerry 是对的,这取决于调用约定;如果没记错的话,Windows、Posix 等有不同的“标准”约定。您使用的是哪个编译器?
  • 海合会。我发现这个搜索en.wikibooks.org/wiki/X86_Disassembly/Calling_Conventions(见退出序列),但它没有说明寄存器,只是它们应该恢复到调用者期望的状态。

标签: assembly x86 calling-convention


【解决方案1】:

使用Microsoft's 32 bit ABIcdeclstdcall 或其他调用约定)、EAXEDXECX 是临时寄存器(调用被破坏)。其他通用整数寄存器是调用保留的。

EFLAGS 中的条件代码是调用破坏的。调用/返回时需要 DF=0,因此您可以先使用 rep movsb 而无需 cld。 x87 堆栈在调用时或从不返回 FP 值的函数返回时必须为空。 (FP 返回值进入st0,x87 堆栈除此之外为空。)XMM6 和 7 保留调用,其余为调用破坏暂存寄存器。

在 Windows 之外,大多数 32 位调用约定(包括 Linux 上的 i386 System V)都同意选择 EAX、EDX 和 ECX 作为 call-clobbered,但 所有 xmm 寄存器都是调用的- 破坏。


对于Windows下的x64,只需要恢复RBXRBPRDIRSIR12R13R14和@ 987654340@。 XMM6..15 保留调用。 (并且您必须保留 32 字节的影子空间供被调用者使用,无论是否有任何不适合寄存器的参数。) xmm6..15 是调用保留的。
详情请见https://en.wikipedia.org/wiki/X86_calling_conventions#Microsoft_x64_calling_convention

其他操作系统使用x86-64 System V ABI (see figure 3.4),其中保留调用的整数寄存器是RBPRBXRSPR12R13R14R15。所有 XMM/YMM/ZMM 寄存器都被调用破坏了。

EFLAGS 和 x87 堆栈与 32 位约定中的相同:DF=0,条件标志被破坏,x87 堆栈为空。 (x86-64 约定在 XMM0 中返回 FP 值,因此 x87 堆栈寄存器总是在调用/返回时需要为空。)


有关官方调用约定文档的链接,请参阅https://*.com/tags/x86/info

【讨论】:

    【解决方案2】:
    32-bit: EBX, ESI, EDI, EBP
    64-bit Windows: RBX, RSI, RDI, RBP, R12-R15, XMM6-XMM15
    64-bit Linux,BSD,Mac: RBX, RBP, R12-R15
    

    有关详细信息,请参阅 Agner Fog 的“Software optimization resources”。 this pdf 中描述了调用约定。

    【讨论】:

    • @StephenCanon 和 EIP/RIP :)
    • 你知道,我也想指出这一点,但 ret 指令确实会为您处理指令指针。
    • +1 链接到 Agner Fog 关于优化和调用约定的资源。确实是一个非常有用的信息来源。
    【解决方案3】:

    如果您不确定收银机的情况,以下这些说明可以轻松解决问题。

    PUSHA/PUSHAD -- Push all General Registers
    POPA/POPAD -- Pop all General Registers

    这些指令按一定顺序推送和弹出通用寄存器和 SI/ESI、DI/EDI 寄存器。

    PUSHA/PUSHAD 指令的顺序如下。

    Opcode  Instruction  Clocks   Description
    
    60      PUSHA        18       Push AX, CX, DX, BX, original SP, BP, SI, and DI
    60      PUSHAD       18       Push EAX, ECX, EDX, EBX, original ESP, EBP ESI, and EDI
    

    POPA/POPAD 指令的顺序如下。 (倒序)

    Opcode   Instruction   Clocks   Description
    
    61       POPA          24       Pop DI, SI, BP, SP, BX, DX, CX, and AX
    61       POPAD         24       Pop EDI, ESI, EBP, ESP(***),EBX, EDX, ECX, and EAX
    

    *** ESP 值被丢弃而不是加载到 ESP 中。

    【讨论】:

    • POPAPOPAD 指令实际上不会弹出 (E)SP 寄存器!还请更正POPAD 上的错误:您忘记了 EBX 寄存器。此外,您错误地指出这些指令涉及 段寄存器