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