assembly 调用函数时保存状态寄存器(EFLAGS)

fsi0uk1n  于 11个月前  发布在  其他
关注(0)|答案(3)|浏览(130)

据我所知,当我调用一个函数时,基于GCC调用约定,会发生以下情况:
调用者保存AX、CX和DX寄存器的值。参数和返回地址被压入堆栈。此外,被调用者必须保存SI、DI、BX和BP寄存器的值。
但是,状态寄存器呢?谁保存它?
还有,压入栈的返回地址值是否就是指令寄存器的值?

nx7onnlm

nx7onnlm1#

状态寄存器在函数调用中不会被保留。如果状态寄存器中有重要的东西,它需要复制到其他地方(通常使用SETcc),但调用约定并不要求调用函数这样做,就像它不要求调用函数保存和恢复AX等,如果它们中没有重要的东西。

vx6bjr1n

vx6bjr1n2#

回答你的第二个问题:
还有,压入栈的返回地址值是否就是指令寄存器的值?
是的,这是call内部执行期间的当前rip(32/16位模式下的eip/ip)值(因为rip指向下一条指令)。
ret指令将弹出堆栈顶部的任何值,并将其设置为rip,从而更改下一条指令的代码执行流(从ret之后的下一个指令到堆栈中的地址/值)。因此,堆栈中的值成为ip寄存器的内容,在ret完成之后。ret就像(不存在)pop ip,但它有自己的助记符,使它在人阅读时更好地在源代码中脱颖而出,而且它有完全不同的操作码,所以晶体管中的硬件实现完全是特定于它的(这在现代x86上是有意义的,其中ret实现使用了许多额外的技巧来获得更好的性能,但我有点好奇为什么8086不会将其编码为pop ip,就像pop的另一个寄存器一样,即使在当时,也可能在某些细节上有些特殊)。

llycmphe

llycmphe3#

海湾合作委员会呼叫公约
gcc在其目标平台上使用标准调用约定。听起来像是在描述Linux上使用的i386 System V调用约定/ ABI,和/或某些Windows调用约定。(其中一些以不同的方式传递参数,但对可以被清除的寄存器进行相同的选择。)
你使用的是16位寄存器名,但gcc几乎不支持16位x86。它基本上生成32位代码,然后用.code16gcc组装它,所以大多数指令都有操作数大小和/或地址大小前缀。(包括callret,所以它们推送和弹出一个4字节的返回地址,不像普通的.code16。)
调用者保存AX、CX和DX寄存器的值
不,调用者只在其中有任何数据需要在call上保存时才这样做。正常情况下,调用者会让这些值消失。“调用者保存”与“被调用者保存”是不好的术语,因为它意味着所有寄存器 * 实际上 * 都保存在某个地方。
更容易理解的是,IMO

*call-clobbered:EAX、ECX、EDX和条件代码(EFLAGS的一部分),所有xmm代码
*调用保留:EBX、ESI、EDI、EBP、ESP。

DF在调用和返回时必须为0,因此字符串指令向上。(DF,方向标志,是EFLAGS中的另一个位。)x87堆栈在callret上必须为空,除了返回FP值的函数(在这种情况下,st0具有返回值,x87堆栈的其余部分为空)。
Call-clobbered意味着在call之后,调用者必须假设寄存器保存垃圾,无论被调用者是否实际使用了该寄存器。* 如果 * 该寄存器中有调用者稍后需要的任何内容,则必须将其移动到其他地方。但如果没有,则完全可以让值死亡。例如,编译类似rv = foo(a + b + c)的东西,调用者会在寄存器中计算a+b+c。但是如果在函数调用之后它不需要这个值,那么它就没有必要保存它。
调用保留意味着调用者可以假设寄存器值没有更改,无论被调用者只是避免接触该寄存器,还是被调用者保存/恢复它。(或者对于ESP,被调用者通常会使用add esp, 28或类似的文件恢复它,以反转它对pushsub所做的任何更改。)被调用方如何设法返回保留调用方值的调用寄存器并不重要,这就是为什么“被调用者保存”也不是最清楚的术语:它暗示被调用者显式地保存它们。
但是,状态寄存器呢?谁保存它?

没有人保存它,除非在非常罕见的情况下。如果需要,调用者 * 可以 * 保存它,但通常情况下,重做比较更容易和更便宜(popf很慢,并且首先保存EFLAGS的pushf不是免费的)。

或者更常见的情况是,条件码中没有任何有用的数据,只有整数寄存器中的整数值。大多数指令都写EFLAGS,但大多数时候你从来没有读过这些结果。你通常使用addimul等作为整数结果,忽略标志结果。
有趣的事实:64-bit OS X system calls set CF on error, otherwise they clear CF。在EFLAGS中没有常见的32位或64位函数调用约定返回任何内容;它们只是被删除。(对于Linux系统调用,EFLAGS / RFLAGS被保留。系统调用通常不删除任何寄存器,除了返回值,部分原因是这样可以避免将内核信息泄漏回用户空间。)

相关问题