代码之家  ›  专栏  ›  技术社区  ›  Dusan

调用函数时保存状态寄存器

  •  0
  • Dusan  · 技术社区  · 6 年前

    据我所知,当我根据GCC调用约定调用函数时,会发生以下情况:

    调用者保存AX、CX和DX寄存器的值。参数和返回地址被推送到堆栈上。此外,被调用方必须保留SI、DI、BX和BP寄存器的值。

    但是,状态寄存器呢?谁来拯救它?

    此外,推送到堆栈上的返回地址的值实际上是指令寄存器的值吗?

    3 回复  |  直到 6 年前
        1
  •  2
  •   Sneftel    6 年前

    状态寄存器不会在函数调用中保留。如果状态寄存器中有重要内容,则需要将其复制到其他位置(通常使用SETcc),但调用约定不需要 要求 调用函数可以做到这一点,就像它不需要调用函数来保存和恢复AX等。如果它们中没有什么重要的内容。

        2
  •  2
  •   Ped7g    6 年前

    回答第二个问题:

    此外,推送到堆栈上的返回地址的值实际上是指令寄存器的值吗?

    你是指 call 指示是的,这是最新的 rip ( eip/ip 在32/16位模式下)值 呼叫 内部执行(as 裂开 指向下一条指令)。

    以及 ret 指令将弹出堆栈顶部的任何值,并将其设置为 裂开 ,更改下一条指令的代码执行流(远离 ret公司 到堆栈中的地址/值)。所以堆栈中的值成为 ip 注册,之后 ret公司 已完成。这个 ret公司 就像(不存在) pop ip ,但它有自己的助记符,以便在人类阅读时更好地在源代码中脱颖而出,而且它有完全不同的操作码,因此晶体管中的硬件实现完全是针对它的(这在现代x86上很有意义 ret公司 实现使用了许多额外的技巧来提高性能,但我有点好奇为什么8086不会将其编码为 pop ip ,就像另一个 pop ,甚至在当时的某些细节上可能有些特殊)。

        3
  •  0
  •   Peter Cordes    6 年前

    GCC调用约定

    gcc在其目标平台上使用标准调用约定。听起来您在描述Linux上使用的i386 System V调用约定/ABI和/或一些Windows调用约定。(其中一些函数传递参数的方式不同,但对可以删除的寄存器进行相同的选择)。

    您使用的是16位寄存器名,但gcc几乎不支持16位x86。它基本上生成32位代码,然后用 .code16 因此,大多数指令都有操作数大小和/或地址大小前缀。

    调用者保存AX、CX和DX寄存器的值

    不,调用方只有在其中有任何数据要在整个 call . 通常情况下,调用者会让这些值消失。“caller saved”与“callee saved”是一个糟糕的术语,因为这意味着所有寄存器 事实上 在某处保存。

    IMO,更容易理解的是

    • 呼叫失败 :EAX ECX EDX和条件代码(EFLAGS的一部分),所有xmm REG
    • 保留通话 :EBX、ESI EDI、EBP、ESP。

    在调用和返回时,DF必须为0,所以字符串指令向上。(DF是EFLAGS中的另一位)。上的x87堆栈必须为空 呼叫 ret ,但返回FP值的函数除外(在这种情况下 st0 具有返回值,x87堆栈的其余部分为空)。

    Call clobbered意思是 呼叫 ,调用者必须假设寄存器包含垃圾,无论被调用者是否实际使用了寄存器。 如果 调用方稍后需要的寄存器中有任何内容,它必须将其移动到其他位置。但如果没有,让价值消失也没关系。e、 g.编译如下内容 rv = foo(a + b + c) ,调用方将计算 a+b+c 在寄存器中。但是,如果在函数调用之后它也不需要该值,那么它就不需要保留它。

    Call preserved意味着调用者可以假定寄存器值没有更改,无论被调用者是避免触摸该寄存器,还是被调用者保存/还原了该寄存器。(对于ESP,被叫方通常使用 add esp, 28 或者类似的,来逆转它所做的任何改变 push sub . 被调用者如何在保留调用的寄存器仍然保存调用者的值的情况下返回并不重要,只是它确实如此。这就是为什么“被调用者保存”也不是最清晰的术语:它意味着被调用者显式地保存它们。

    但是,状态寄存器呢?谁来拯救它?

    除了极少数情况外,没有人能挽救它 . 来电者 能够 如果需要,可以保存它,但通常只需重新进行比较就更容易、更便宜了( popf 速度很慢 pushf 首先保存EFLAG不是免费的)。

    或者更常见的情况是,条件代码中没有任何有用的数据,只有整数寄存器中的整数值。大多数指令都会写EFLAG,但大多数情况下,您从未读过这些结果。您通常使用 add , imul ,依此类推,并忽略标志结果。

    有趣的事实: 64-bit OS X system calls set CF on error, otherwise they clear CF . 没有常见的32位或64位函数调用约定在EFLAGS中返回任何内容;他们只是被击倒了。(对于Linux系统调用,保留EFLAGS/RFLAGS。系统调用通常不破坏任何寄存器(返回值除外),部分原因是这样可以避免内核信息泄漏回用户空间。)