源程序
代码语言:javascript复制int g(int x)
{
return x 5;
}
int f(int x)
{
return g(x);
}
int main(void)
{
return f(7) 2;
}
编译命令
代码语言:javascript复制gcc -S -o main.s main.c -m32
- 将原程序编译成汇编指令,其中-m32是将其编译成32位下的汇编。
- 将汇编文件中用于链接过程的代码删除,得到下面汇编代码
汇编代码
代码语言:javascript复制1 g:
2 pushl �p
3 movl %esp, �p
4 movl 8(�p), �x
5 addl $5, �x
6 popl �p
7 ret
8 f:
9 pushl �p
10 movl %esp, �p
11 subl $4, %esp
12 movl 8(�p), �x
13 movl �x, (%esp)
14 call g
15 leave
16 ret
17 main:
18 pushl �p
19 movl %esp, �p
20 subl $4, %esp
21 movl $7, (%esp)
22 call f
23 addl $2, �x
24 leave
25 ret
分析汇编程序
首先从main函数开始分析,18行将ebp推入栈中,19行中将esp的值赋给ebp(左边值赋给右边),20行开辟4个字节(32位)的栈空间给后面的局部变量7。函数传递传递参数的方式有三种:堆栈方式、寄存器方式、以及通过全局变量进行隐含参数的传递。这里是利用堆栈传递参数,堆栈是一种“后进先出”的存储区,栈顶指针ESP指向堆栈中第一个可用的数据项。21行传入7,22行然后调用f函数,call指令可以看成是执行了
代码语言:javascript复制push %eip(保存23行指令地址)
movl $9 %eip
此时的堆栈情况为:
主要是保存23行的地址,并跳转到第9行执行。第9行开始又重新开辟了一段堆栈,12行取的是[ebp 8](栈地址是从高地址向低地址生长)的值,即7,然后把7赋给eax,13行把eax赋值给esp,这里实际上将7保存在当前的esp。14行调用g函数。call指令可以看成是执行了
代码语言:javascript复制pushl %eip (保存15行指令地址)
movl $2 %eip
跳转到第2行执行,开辟一段栈空间,4行取[ebp 8]的值给eax,即7,5行执行7 5=12,把结果存在了eax。 此时的堆栈情况为:
6行pop出当前的ebp,7行执行ret指令,实际上是:
代码语言:javascript复制popl %eip
movl $15 %eip
跳转到15行执行leave指令,相当于:
代码语言:javascript复制movl �p %esp
popl �p
此时f函数的栈空间已经被释放了,此时ebp指向main函数调用f函数之前的栈底,16行的ret使程序跳转到23行执行,eax保存着之前的12,此时再将eax 12=14。然后执行leave和ret指令释放main函数的栈空间。