最近好像对关于函数调用过程中,产生的函数帧在stack中的格式很敢兴趣,在今天研究了一下午的汇编代码以后我觉得应该有点小小的了解了。
首先贴一段简单的代码,用来产生汇编代码,然后再开始分析这个桟帧的分析。
#include <stdio.h>
int global_val = 3;
void display(int a, int b)
{
int i = 0;
int j = 0;
a = b;
}
void int_max(int a, int b)
{
int max_ = 0;
display(a,b);
printf("hello.world!\n");
}
这个代码是没有什么意思的,不过只是为了让我能看见调用的过程。
int_max:
1.pushl %ebp
2.movl %esp, %ebp
3.subl $40, %esp
4.movl $0, -12(%ebp)
5.movl 12(%ebp), %eax
6.movl %eax, 4(%esp)
7.movl 8(%ebp), %eax
8.movl %eax, (%esp)
9.call display
10.movl $.LC0, (%esp)
11.call puts
这段是int_max函数的汇编程序,那我对这个汇编进行了小小的分析,就我现在所知道的是,在函数调用的过程中
1.先将函数的参数进行压stack
2.保存现场的一些东西(寄存器的值),将返回地址压入stack中。
3.保存调用该函数的ebp
4.函数的局部变量之类的
在分析之前,我先要说明两个寄存器的作用:
1.esp 这个寄存器是桟顶指针吧,我是这样叫的。这个指针是指向当前函数记录的最顶端的位置。而且不论在什么时候都是值指在最上面的。
2.ebp 这个寄存器存放的是帧底指针,也就是说是指向的是当前函数的帧的最下方的位置,而不是指向stack的底部。(其实我对这个是保持一种怀疑态度,到最后的时候我在分析为什么我怀疑这个问题)
这两个指针必须要搞清楚的。不然在解析过程中很难理解这个问题。
现在开始分析汇编代码,
哈不过还是有些问题没有解决,比如ebp这个指针的指向位置很不能理解,他被定位在返回地址上面,我觉得他应该从参数那个开始就是一帧了,还有就是返回地址在ebp前面,可是寄存器的值在ebp后面,这个也是问题,不知道能怎么样????1.这两行代码在任何一个函数中都有的,意思是先将前面一个函数帧的帧底压入stack,然后将当前的桟顶指针赋值给ebp,也就是现在两个指针都指向桟顶。1.pushl %ebp 2.movl %esp, %ebp
3.subl $40, %esp 4.movl $0, -12(%ebp) 5.movl 12(%ebp), %eax 6.movl %eax, 4(%esp) 7.movl 8(%ebp), %eax 8.movl %eax, (%esp)
2.说明一下,stack的底部是高地址,所以stack向下进行伸展的。所以减才是压桟的过程。esp 减去0x40,0x40也就是表示保留了64BYTE的空间,可能你会奇怪明明只有一个局部变量,为什么要那么大的空间。其实不然因为在进行函数调用的时候不但要将返回地址放入stack中,而且还要将一些寄存器的值放入桟中,以便后面进行恢复。不过在代码中没有表示出来。而且关于这个的具体位置和大小也不怎么清楚。
4号代码,将0放到(epb - 12)的位置,而5号的代码是将(将参数b)放到eax中,6号是将b存入esp+4这个地方,而7,8号就是将a放到esp的位置。其实这一部可以看成为下了函数的参数进行压桟。
如果从整体来看是这样的一个过程,在调用函数的时,编译器会先将函数的参数进行压桟,而且这个压桟的过程是一个倒序的过程(不过好像这是和特定的编译有关系的),然后调用下面的指令
这条指针是用来调用函数的指令,call这个指令好像不是想象中的那么简单,他要做的事情挺多的,好像是保存寄存器,将返回地址进行压桟等,后面的过程就是我们分析的那样了。9.call display