"linux系统内核分析"实验报告1
通过汇编一个简单的C程序,分析汇编代码理解计算机是如何工作的
原创作品转载请注明出处 + 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000
现代计算机的基本体系结构都是采用冯诺依曼结构,冯诺依曼的设计思想最重要之处是"存储程序"的这个概念。计算机的工作过程,就是执行程序的过程。首先编写需要执行的程序,然后通过输入设备送到存储器保存起来,即程序存储。根据冯诺依曼的设计,计算机应能自动执行程序,而执行程序又归结为逐条执行指令。执行一条指令又可分为以下4个基本操作:
取出指令:从存储器某个地址中取出要执行的指令送到CPU内部的指令寄存器暂存。
分析指令:把保存在指令寄存器中的指令送到指令译码器,译出该指令对应的微操作。
执行指令:根据指令译码,向各个部件发出相应控制信号,完成指令规定的各种操作。
为执行下一条指令作好准备,即取出下一条指令地址。
这里是一个非常简单的c程序,源代码如下:
输入:gcc S o main.s main.c m32 来生成汇编代码
整洁一下汇编代码以后,查看汇编代码:
我在我的虚拟机中的ubuntu系统与实验楼ubuntu系统的汇编代码有点不一样
首先我们从main函数开始。(前两条语句在gdb执行时设置不了断点,但是执行函数的语句都有这2条,放到其他函数来说明):
在main函数上先设置一个断点,然后运行:
此时查看寄存器的值:
他们的值:esp和ebp都是0xbffff568,eip是0x8048409(正好是下一条要执行的指令的地址)
接下来继续执行:
把2压到栈上
此时,esp的减4了,而ebp不变,eip继续指向下一条指令
下一条要执行call指令,这里再对函数f设置一个断点,继续执行:
此时,程序跳到函数f中去了
call f
调用函数 f,其实这条指令等价于
pushl %eip
movl $f, %eip
eip的值被保存在esp-4的位置上,保存eip的目的是函数调用返回时能够继续执行call f下面的语句:
此时,esp的值为0xbffff560,ebp都是0xbffff568
跳转到函数f后,前两条语句和 main 函数相同,都是保存堆栈状态,这里详细来说明一下:
先把ebp的值保存咋esp-4的位置上
再把esp的值赋给sbp,此时esp和ebp的值都为0xbffff55c
然后继续执行,把ebp+8的内容即2这个值压栈:
此时esp继续-4
查看寄存器的值
寄存器的值:esp的值为0xbffff558,ebp的值为0xbffff55c,
接下来要跳转到函数g了,因此再对函数g设置一个断点,然后继续执行:
观察寄存器的值:
同理:寄存器eip的值继续被保存了在esp-4的位置上,以便能够返回到函数f
进入g函数老的ebp的值也被保存了,新的esp和ebp相同
此时,esp和ebp的值都是0xbffff550
接下来继续执行,把ebp+8的值给eax
查看寄存器,此时esp和ebp的值都是0xbffff550,eax的值是2
继续执行:
把3和eax的值相加结果再保存到eax中
查看寄存器
寄存器eax的值变成5了,esp和ebp的值都是0xbffff550
然后继续执行:
把esp指向的值给ebp,查看寄存器
寄存器的值:esp的值为0xbffff554,ebp的值0xbffff55c,eax还是5
然后继续执行:
指令ret相当于指令popl %eip
esp的值为0xbffff558,ebp的值0xbffff55c,eax还是5
这样又返回到函数f继续执行:
继续执行,然后查看寄存器:
Esp和ebp的值都是0xbffff55c,eax还是5
继续执行,leave,这条指令相当于下面两条指令:
movl %ebp, %esp
popl %ebp
查看寄存器
esp的值为0xbffff560,ebp的值0xbffff568,eax还是5
继续执行ret,弹出保存的eip的值,返回到main函数执行:
查看寄存器的值,esp的值为0xbffff564,ebp的值0xbffff568,eax还是5
连续执行2步,继续执行
此时eax的值变成了6,esp和ebp的值0xbffff568
通过分析对应的汇编代码和观察运行栈的变化,加深了对程序执行过程的了解,也明白了计算机的工作方式:
根据eip 指指令执行,同时eip自增;
如果执行的是跳转语句时,先把eip压栈,然后将需要跳转的目的地址赋给 eip,实现跳转;
若执行函数调用时,将 eip 压栈,同时将ebp压栈,然后将相应函数地址赋给 eip;
若为其他指令,则继续从 eip 指向的地址取指令执行。