学习这章首先要明白栈帧的定义:栈帧就是利用EBP(栈帧指针)寄存器访问栈内局部变量,参数,函数返回地址等的手段.
我们知道调用一个函数通过栈来传递参数,而且函数逇返回地址也保存在栈中,那么在函数中想要准确的得到传递的参数就是一个问题.因为esp总是在变化,你无法使用esp+偏移量来定位传入的参数.如果用另一个指针总是指向函数起始地址,相对于函数起始地址的偏移量就是正确的了.这样就能正确访问到与函数中的局部变量,参数,返回地址了.
书中通过下面的程序来讲解栈帧,逐句解释汇编语言的作用.
#include
long add(long a, long b)
{
long x = a, y = b;
return (x + y);
}
int main(int argc, char * argv[])
{
long a = 1, b = 2;
printf("%d\n", add(a, b));
return 0;
}
当我们执行到main()函数时,我们发现栈中多了一条数据.这是main()函数的返回值.这是因为main()是被调用的函数,在其他地方有这样一条指令 call main()函数.
call指令:在王爽的汇编语言中这样解释call指令.将当前的地址入栈后,转到标号处执行指令
这样就可以确保函数执行完毕后可以继续执行函数调用的下一条指令.
我们通过栈向函数传入参数,函数结束后,栈的位置没有回到原来的地方,所以我们要清理掉栈中的参数,使栈恢复到原来的状态.
我们通过add esp,0x数值 来平衡栈.通过栈中数据可以发现,平衡栈后,数据并没有改变只是改变了esp的值,等到下一次push,esp改变的时候新的数据才会覆盖掉原来的数据.
在函数add()传入参数时,参数的入栈顺序与C语言源码中的参数顺序恰好相反(又称为函数参数的逆向存储)
被调函数执行完毕后,函数的调用者负责清理存储在栈中的参数,这种方式被称为cdecl方式.
被调用者负责清理保存在栈中的参数,被称为stdcall方式.这些函数调用规则统称为调用约定(Calling Convention)