我目前正在玩,检查堆栈帧,试图了解它是如何工作的。在阅读了一些总是解释一般结构的文章之后:
local vars <---SP
低地址
老BP
<---BP
ret addr args 高地址
我有一个示例程序,它调用一个带有三个参数的函数,并有两个缓冲区作为局部变量:
#include <stdio.h>
void function(int a, int b, int c);
int main()
{
function(1, 2, 3);
return 0;
}
void function(int a, int b, int c)
{
char buffer1[5];
char buffer2[10];
}
我看了看程序的汇编代码,很惊讶在调用函数时没有找到我所期望的。我期待的东西是:
# The arguments are pushed onto the stack:
push 3
push 2
push 1
call function # Pushes ret address onto stack and changes IP to function
...
# In function:
# Push old base pointer onto stack and set current base pointer to point to it
push rbp
mov rbp, rsp
# Reserve space for stack frame etc....
因此,在执行函数时,框架的结构将类似于:
buffers <--- SP low address
old BP <--- BP
ret Addr
1
2
3 high address
但相反,会发生以下情况:
函数调用:
mov edx, 3
mov esi, 2
mov edi, 1
call function
既然我们可以直接压栈,为什么还要在这里使用寄存器???在我们调用的实际函数中:
.cfi_startproc
push rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
mov rbp, rsp
.cfi_def_cfa_register 6
sub rsp, 48
mov DWORD PTR [rbp-36], edi
mov DWORD PTR [rbp-40], esi
mov DWORD PTR [rbp-44], edx
mov rax, QWORD PTR fs:40
mov QWORD PTR [rbp-8], rax
xor eax, eax
mov rax, QWORD PTR [rbp-8]
xor rax, QWORD PTR fs:40
je .L3
call __stack_chk_fail
据我所见,为堆栈帧保留了 48 个字节,对吗?然后,使用函数调用中的寄存器,将函数的参数复制到堆栈的末尾。所以它看起来像这样:
3 <--- SP
2
1
??
??
old BP <--- BP
return Address
??
我假设缓冲区介于 args 和 old 之间BP
。但是我真的不确定确切的位置...因为它们总共只有 15 个字节,而保留了 48 个字节...那里不会有一堆未使用的空间吗?有人可以帮我概述这里发生的事情吗?这是依赖于处理器的东西吗?我正在使用英特尔 i7。
干杯,砖