原创作品转载请注明出处
《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000
使用如下的C程序
int
g(
int
x)
{
return
x + 3;
}
int
f(
int
x)
{
return
g(x);
}
int
main(
void
)
{
return
f(8) + 1;
}
使用
gcc –S –o main.s main.c -m32
命令编译成汇编代码
实验截图如下:
代码精简后如下所示:
g:
pushl %ebp
movl %esp, %ebp
movl 8(%ebp), %eax
addl $5, %eax
popl %ebp
ret
f:
pushl %ebp
movl %esp, %ebp
subl $4, %esp
movl 8(%ebp), %eax
movl %eax, (%esp)
call g
leave
ret
main:
pushl %ebp
movl %esp, %ebp
subl $4, %esp
movl $170, (%esp)
call f
addl $6, %eax
leave
ret
内存堆栈是向下增长的,这里为了方便描述,假定堆栈底的地址编号是0,堆栈向下每增加一个位置,编号加1(实际在32位机器中是增加4)。eax 寄存器用于存储函数的返回值,eip是指令寄存器,ebp、esp分别指向当前堆栈的底部和顶部。
内存堆栈图(下面的图片是用AutoCAD画的,画得较烂,大家将就着看下吧~~)如下所示:
(不好意思,下面的内容会比较长,主要是图片占的面积太大了;本想每行多放几张图片,但在编辑时,图片和文字会显示排版的错位,无语,不知该如何使用这个博客的编辑器才不会错位,所以,我就采用每行只放一张图片的方法来避免排版错位问题了,敬请谅解~~)
首先从main函数开始执行:
初始状态为:
pushl %ebp:
movl %esp, %ebp:
subl $4, %esp:
movl $170, (%esp):
call f(此时eip指向f函数):
pushl %ebp :
movl %esp, %ebp:
subl $4, %esp:
movl 8(%ebp), %eax:
movl %eax, (%esp):
call g(此时eip指向g函数):
pushl %ebp:
movl %esp, %ebp:
movl 8(%ebp), %eax:
addl $5, %eax:
popl %ebp:
ret(此时eip指向第15行指令):
leave:
ret(此时eip指向第23行指令):
addl $6, %eax(181是我的学号,也算是在实验中体现出个人ID以证明原创了,呵呵~~):
leave:
总结:在计算机中,CPU依次读取内存中的指令,通过eip,ebp,esp这些寄存器和内存之间的相互作用,来实现各种函数功能,完成计算机的各种工作。