版权声明:陈诚
原创作品转载请注明出处
《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000
=================================================================
这是linux系统分析课程的第一个实验,比较简单的一个实验,要能看懂AT&T汇编指令并且能够理解程序运行时内存栈上动态变化的过程。
1.进入实验楼的环境,按照实验要求进行。先用vi编辑器打开一个空白文件命名为main.c,然后复制粘贴给出的c代码。
2.敲入命令:gcc -S -o main.s main.c -m32,将c代码反汇编成汇编代码,然后去除所有以圆点(.)开头的行,结果如下图所示。
3.接下来结合图示详细分析这段汇编代码在栈上是如何动态运行的
--------------------------------------------
g:
pushl % ebp
movl %esp, % ebp
movl 8(% ebp), % eax
addl $3, % 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 $8, (%esp)
call f
addl $1, % eax
leave
ret
-----------------------------------------
程序从main函数开始执行
pushl % ebp
movl %esp, % ebp
这两句在汇编中只要是函数开始一般都会有,与宏指令Enter功能相同,栈中情况如下图:
subl $4, %esp // 将esp寄存器中的值减4,即在栈上分配一个存储空间
movl $8, (%esp) // 将立即数8放入esp寄存器指向的内存单元中,即在调用函数前将函数需要的参数压栈
这两句执行后,栈中情况如下图:
call f // 调用f函数,执行这句前先将下条语句的地址压栈(addl $1, % eax 这条语句的地址)
--------------------------------------
下面进入f函数:
f:
pushl % ebp
movl %esp, % ebp
这两句和main函数开始的功能一样,栈中情况如下图:
subl $4, %esp
movl 8(% ebp), % eax // 将ebp值加8后放入eax寄存器中
movl % eax, (%esp)
这三句执行后,栈中情况如下图:
call g // 下面调用g函数
---------------------------
下面进入g函数:
g:
pushl % ebp
movl %esp, % ebp
上面两句不解释了,执行后栈中情况如下图
movl 8(% ebp), % eax
addl $3, % eax
这两句执行完成后,栈中情况如下图:
popl % ebp
ret
这两句执行完成后,栈中情况如下图:
---------------------------
下面返回f函数继续执行:
leave // 相当于 movl % ebp, %esp 和 popl % ebp这两句的功能
ret // 将栈中的值弹出到eip
执行后栈中情况如下图:
--------------------------------------
返回main函数继续执行
addl $1, % eax // eax寄存器值加一,此时eax寄存器中保存的是返回的结果,此时eax=12
leave // 相当于 movl % ebp, %esp 和popl % ebp这两句的功能
ret // 将栈中的值弹出到eip
----------------------------------
总结:
关于计算机是如何工作的。这个问题比较大,不同的角度回答,答案都不一样。
从cpu的角度看,就是输入、运算、输出(可能也没有输出)。从程序角度看,当程序运行时,需要在内存中的栈上分配一段空间,伴随着程序的运行,esp,ebp,eip等各个寄存器发生变化。
cpu将代码段中的语句,经过取指、翻译、执行后的结果,可以输出I/O、写回数据段等。接着再由eip自加一生成下一条指令的地址,继续执行。关于程序运行时栈中的变化情况,博客正文中已结合图示演示。