一个简单c程序
分析一个简单的c程序 main.c 如下图:
用命令 gcc –S –o main.s main.c -m32编译成汇编文件。在汇编文件中有许多的虚指令并不会形成机器指令,为了使分析简单我们把大部分去掉:
得到如下图所示:
栈的介绍
APUE中指出每一个c程序,都有一个独立的地址空间,在内存中的典型布局如下:
对栈的操作和我们在数据结构中的栈的操作是类似的,ebp,esp(具体名称与cpu体系结构相关) 这两个寄存器直接与栈的操作相关。
栈地址是从高到低的方向分配的。
- 开始一个新的栈时,使ebp,esp指向栈的起始地址的下一位。eg:ebp,esp 指向0x10000
- pushl操作,esp-4(32位机器为例),此时esp的值为0xfffc,将操作数放入栈
- popl操作,将操作数出栈,esp+4
函数调用
call指令调用一个子程序,cpu会转移到去执行子程序代码,为了当子程序执行完时能够准确返回,需要把call指令的下一条指令的地址压入栈。然后为子程序建立一个新的栈,汇编指令如下:
- pushl %ebp
- movl %esp, %ebp
pushl %ebp 指令将主调程序的栈的基址压入栈,执行movl %esp,%ebp指令后 esp,ebp 都指向新的栈的起始地址的下一位。当要恢复主调函数的栈时,只要执行如下指令,就会恢复主调函数的栈:
- movl %ebp,%esp
- popl %ebp
这两句汇编可以用leave指令代替,当然也有可能直接执行 popl %ebp 就可以。
当恢复主调函数的栈时,在栈的栈顶就是即将要执行的指令的地址,通过ret指令,将指令的地址返回给指令计数器,cpu就会从call的下一条指令继续跑下去了。