或者更常见的情况是,条件码中没有任何有用的数据,只有整数寄存器中的整数值。大多数指令都写EFLAGS,但大多数时候你从来没有读过这些结果。你通常使用add,imul等作为整数结果,忽略标志结果。 有趣的事实:64-bit OS X system calls set CF on error, otherwise they clear CF。在EFLAGS中没有常见的32位或64位函数调用约定返回任何内容;它们只是被删除。(对于Linux系统调用,EFLAGS / RFLAGS被保留。系统调用通常不删除任何寄存器,除了返回值,部分原因是这样可以避免将内核信息泄漏回用户空间。)
3条答案
按热度按时间nx7onnlm1#
状态寄存器在函数调用中不会被保留。如果状态寄存器中有重要的东西,它需要复制到其他地方(通常使用SETcc),但调用约定并不要求调用函数这样做,就像它不要求调用函数保存和恢复AX等,如果它们中没有重要的东西。
vx6bjr1n2#
回答你的第二个问题:
还有,压入栈的返回地址值是否就是指令寄存器的值?
是的,这是
call
内部执行期间的当前rip
(32/16位模式下的eip/ip
)值(因为rip
指向下一条指令)。ret
指令将弹出堆栈顶部的任何值,并将其设置为rip
,从而更改下一条指令的代码执行流(从ret
之后的下一个指令到堆栈中的地址/值)。因此,堆栈中的值成为ip
寄存器的内容,在ret
完成之后。ret
就像(不存在)pop ip
,但它有自己的助记符,使它在人阅读时更好地在源代码中脱颖而出,而且它有完全不同的操作码,所以晶体管中的硬件实现完全是特定于它的(这在现代x86上是有意义的,其中ret
实现使用了许多额外的技巧来获得更好的性能,但我有点好奇为什么8086不会将其编码为pop ip
,就像pop
的另一个寄存器一样,即使在当时,也可能在某些细节上有些特殊)。llycmphe3#
海湾合作委员会呼叫公约
gcc在其目标平台上使用标准调用约定。听起来像是在描述Linux上使用的i386 System V调用约定/ ABI,和/或某些Windows调用约定。(其中一些以不同的方式传递参数,但对可以被清除的寄存器进行相同的选择。)
你使用的是16位寄存器名,但gcc几乎不支持16位x86。它基本上生成32位代码,然后用
.code16gcc
组装它,所以大多数指令都有操作数大小和/或地址大小前缀。(包括call
和ret
,所以它们推送和弹出一个4字节的返回地址,不像普通的.code16
。)调用者保存AX、CX和DX寄存器的值
不,调用者只在其中有任何数据需要在
call
上保存时才这样做。正常情况下,调用者会让这些值消失。“调用者保存”与“被调用者保存”是不好的术语,因为它意味着所有寄存器 * 实际上 * 都保存在某个地方。更容易理解的是,IMO
*call-clobbered:EAX、ECX、EDX和条件代码(EFLAGS的一部分),所有xmm代码
*调用保留:EBX、ESI、EDI、EBP、ESP。
DF在调用和返回时必须为0,因此字符串指令向上。(DF,方向标志,是EFLAGS中的另一个位。)x87堆栈在
call
和ret
上必须为空,除了返回FP值的函数(在这种情况下,st0
具有返回值,x87堆栈的其余部分为空)。Call-clobbered意味着在
call
之后,调用者必须假设寄存器保存垃圾,无论被调用者是否实际使用了该寄存器。* 如果 * 该寄存器中有调用者稍后需要的任何内容,则必须将其移动到其他地方。但如果没有,则完全可以让值死亡。例如,编译类似rv = foo(a + b + c)
的东西,调用者会在寄存器中计算a+b+c
。但是如果在函数调用之后它不需要这个值,那么它就没有必要保存它。调用保留意味着调用者可以假设寄存器值没有更改,无论被调用者只是避免接触该寄存器,还是被调用者保存/恢复它。(或者对于ESP,被调用者通常会使用
add esp, 28
或类似的文件恢复它,以反转它对push
和sub
所做的任何更改。)被调用方如何设法返回保留调用方值的调用寄存器并不重要,这就是为什么“被调用者保存”也不是最清楚的术语:它暗示被调用者显式地保存它们。但是,状态寄存器呢?谁保存它?
没有人保存它,除非在非常罕见的情况下。如果需要,调用者 * 可以 * 保存它,但通常情况下,重做比较更容易和更便宜(
popf
很慢,并且首先保存EFLAGS的pushf
不是免费的)。或者更常见的情况是,条件码中没有任何有用的数据,只有整数寄存器中的整数值。大多数指令都写EFLAGS,但大多数时候你从来没有读过这些结果。你通常使用
add
,imul
等作为整数结果,忽略标志结果。有趣的事实:64-bit OS X system calls set CF on error, otherwise they clear CF。在EFLAGS中没有常见的32位或64位函数调用约定返回任何内容;它们只是被删除。(对于Linux系统调用,EFLAGS / RFLAGS被保留。系统调用通常不删除任何寄存器,除了返回值,部分原因是这样可以避免将内核信息泄漏回用户空间。)