测试环境:X86下Ubuntu12.04 +gcc4.6.3 引言:任何一种编程语言都会提供相应的机制对数据和过程进行抽象,同时还需要为数据的存储提供内存访问模型,以满足图灵完备性。说到编程语言就不可不提编译器,编译器以生成机器代码的形式向程序员提供了两种抽象模型:一是定义了指令格式行为及寄存器状态的ISA;二是虚拟地址空间,虽然这涉及到物理内存、内存控制器以及操作系统的软件层。 对c语言的过程调用,可大致分为三个阶段:调用前主调函数准备阶段,被调函数执行阶段以及控制返回阶段。 1、调用前准备 (1):实参入栈,最后一个参数最先入栈,第一个参数最后入栈(要视具体平台)注意:在参数个数比较少的情况下,编译器会将参数拷贝到寄存器中从而提高速度,当然这里还存在寄存器保存的问题,这些对于高级语言程序员来说是透明的,此时esp寄存器指向第一个参数的低地址位置 (2):返回地址入栈,call指令的下一条指令的地址。push作为入栈指令其实是分为两条指令完成的:R[%esp]<---R[%esp] - 4 ;M[R[%esp]]<---value 2、被调函数执行 (1):程序控制流程的跳转,控制从主调函数到被调函数的转移 (2):保存caller的栈帧指针 pushl %ebp (3):创建被调函数栈帧 movl %esp,%ebp,此时ebp和esp指向相同的位置,即保存的caller的栈帧的低地址 (4):如果需要可以为局部变量分配空间,通过subl $16, %esp的方式,分配16个字节的局部空间 (5):执行具体计算,直到计算完成 (6):销毁局部变量,通过addl $16, %esp,当然也可以一步到位movl %ebp,%esp (7):恢复caller栈帧指针 popl %ebp 3、控制返回 ret指令其实执行了:popl %eip,将下一条指令恢复,如果函数有返回值则保存在eax中 注意: 1、一些特殊寄存器的用途,eip指令指针,esp堆栈指针,eax函数的返回值,ebx存基址,ecx循环时候使用。 2、在被调函数中,ebp+8是第一个参数,ebp+12第二个参数,以此类推,如下图 |
|