说我们有一个函数:
int exchange(int*xp, int y)
{
x = *xp;
*xp = y;
return x;
}
因此,我正在阅读的书解释说xp存储在相对于地址寄存器%ebp的偏移量8和12处。我不明白的是为什么将它们存储为任何类型的单元8和12,以及:在这种情况下,偏移量是多少?最后,当寄存器分别接受以1 2和4字节为单位的移动时,8和12如何适合?
汇编代码:
xp at %ebp+8, y at%ebp+12
1 movl 8(%ebp), %edx (Get xp By copying to %eax below, x becomes the return value)
2 movl (%edx), %eax (Get x at xp)
3 movl 12(%ebp), %ecx (Get y)
4 movl %ecx, (%edx) (Store y at xp)
我认为答案是: 因此,在检查注册表时,通常会看到类似注册表%rdi的值,其值为0x1004,这是一个地址,而0x1004在地址中,其值为0xAA。
当然,这是一个假设示例,与书中列出的注册表不符。每个注册表都是16-32位,前四位可用于自由存储整数。是否将其偏移8使其类似于0x1000 + 8?同样,我不完全确定在将新单元存储到空白空间时此方案中的偏移量是什么。
答案 0 :(得分:2)
由于使用C declaration时调用堆栈的结构。
首先,呼叫者将push
的4字节y
,然后是4字节的xp
(此顺序很重要,因此C可以支持Variadic Functions),然后call
会隐式地push
返回地址,该地址也是4字节(这是一个32位程序)。
函数要做的第一件事是push
的状态ebp
,稍后需要恢复该状态,以便调用者可以继续正常工作,然后复制{{1}的当前状态}(堆栈指针)指向esp
。总而言之:
ebp
这也称为function prologue。
完成所有这些操作后,您终于可以实际运行编写的代码了,在此阶段,堆栈是这样的:
push %ebp
movl %esp, %ebp
函数完成后,会将%ebp- ? = address of your local variables (which in this example you don't have)
%ebp+ 0 = address of the saved state of previous ebp
%ebp+ 4 = ret address
%ebp+ 8 = address where is stored the value of xp
%ebp+12 = address where is stored the value of y
%ebp+16 = out of bonds, this memory space belongs to the caller
设置回esp
,然后将ebp
设为原始的pop
和ebp
,将其包装起来。
ret
movl %ebp, %esp
pop %ebp
ret
基本上是ret
来自堆栈并pop
指向它的指针的快捷方式。
编辑:用于AT&T装配的固定参数顺序
答案 1 :(得分:1)
查看汇编器中的正常函数条目:
push ebp
mov ebp, esp
sub esp, <size of local variables>
因此,ebp+4
保留了ebp
的先前值。在旧的ebp之前是寄信人地址ebp+8
。在此之前,函数的参数是相反的,因此第一个参数在ebp+12
,第二个参数在ebp+8
。