关于函数调用浅析

栈在程序运行中有重要的地位,栈保存了一个函数调用所需要的维护信息,这通常被称为栈帧活动记录。栈帧一般包括如下内容:

  • 函数的参数和返回地址。
  • 临时变量,包括函数的局部变量和编译器自动生成的临时变量
  • 保存的上下文,函数调用前后需要保持不变的寄存器

在i386中,一个函数的活动记录用ebpesp这两个寄存器划定活动范围。esp寄存器始终指向栈的顶部,也就指向了当前函数的活动记录的顶部。而ebp指向了活动记录的一个固定位置,ebp又被称为帧指针

关于函数调用浅析_第1张图片
活动记录.png

随着函数的执行,esp会不断的变化;固定不变的ebp可以用来定位函数活动记录的各个数据。

C 语言调用约定:
在C语言中,函数参数是从右到左的顺序入栈的,同时参数是在栈中传递的, EAX,ECX 和 EDX 寄存器是由调用者保存的,其余的寄存器由被调用者保存,函数的返回值存储在 EAX 寄存器中。由调用者清理栈空间。

那么下面一段函数调用是怎么完成的呢?

int sub = subtract(3, 2);      // 主调用者

int subtract(int a, int b)     // 被调用者
{
    return  a - b;
}

结合C语言的函数调用约定那么汇编代码大概如下:

主调用者:
push 2        // 压入函数参数b  从右往左入栈
push 3        // 压入函数参数a  从右往左入栈

call subtract        // 调用函数subtract
add esp + 8          // 清理栈空间

被调用者:
push ebp                // 压入ebp备份
mov ebp, esp            // 将esp赋值给ebp

mov eax, [ebp + 0x8]    // 用ebp作为基址来取得栈中参数 a
add eax, [ebp + 0xc]    // 用ebp作为基址来取得栈中参数 b
...
mov esp, ebp            // 防止中间有入栈操作,用ebp恢复esp,通用代码,这里可有可无
pop ebp                 // 恢复ebp
ret                                      
关于函数调用浅析_第2张图片
函数调用栈的变化.png

你可能感兴趣的:(关于函数调用浅析)