《Linux内核分析》MOOC课程
http://mooc.study.163.com/course/USTC-1000029000
首先创建一个C程序的文件,main.c这里写图片描述
将一段C程序代码使用命令 gcc –S –o main.s main.c -m32编译成汇编代码。
我们删除点开头的辅助信息来得到汇编代码。
这里写图片描述
这样就可以清晰的看到main.c文件的汇编代码了。
根据汇编代码我们来一步一步分析程序在执行过程中堆栈的变化过程。
首先程序从main开始执行,最初的堆栈为空,esp和ebp指向同一个地址:
这里写图片描述
然后执行18:pushl %ebp, 保存旧的ebp地址,把旧的ebp地址压入堆栈,此时堆栈情况如下:
这里写图片描述
执行19: movl %esp, %ebp 将esp的值放入ebp中,执行之后堆栈的状态如下,此时ebp和esp又指向同一地址
这里写图片描述
执行20:subl $4, %esp 将esp的值减4,此时堆栈的情况如下:
这里写图片描述
执行21: movl $8, (%esp), 将立即数8放入esp所指向的地址中。此时堆栈情况如下:
这里写图片描述
执行22:call f, 将eip(23)压入栈中,然后跳转到f:处执行,此时堆栈情况如下:
这里写图片描述
执行9:pushl %ebp,将老的ebp地址压入栈中,此时堆栈情况如下:
这里写图片描述
执行10:movl %esp, %ebp,将esp的地址赋给ebp,此时堆栈情况如下:
这里写图片描述
执行11: subl $4, %esp, 将esp的值减4,此时堆栈的情况如下:
这里写图片描述
执行12:movl 8(%ebp), %eax, 将ebp + 8所存放的值,放入eax寄存器中,ebp+8存放的值为8,所以此时eax寄存器存放的值为8. 此时堆栈无变化
执行13:movl %eax, (%esp), 将eax寄存器存放的值,放入esp所执行的地址中。此时堆栈的变化如下:
这里写图片描述
执行14: call g, 将eip(15)压入堆栈,并且跳转到函数g执行,此时堆栈的变化如下:这里写图片描述
执行2: pushl %ebp, 将老的ebp压入堆栈,此时堆栈的变化如下:
这里写图片描述
执行3: movl %esp, %ebp, 将esp存放的值放入ebp中。此时堆栈的变化如下:
这里写图片描述
执行4:movl 8(%ebp), %eax, 将ebp + 8所指向的内容放入eax寄存器中,ebp + 8存放的内容为8,所以eax寄存器存放的内容为8,此时堆栈无变化。
执行5: addl $11, %eax, 将eax寄存器的内容与11相加,并且将结果放入eax寄存器中, 此时eax寄存器为19。堆栈无变化
执行6:popl %ebp,将栈顶元素弹出,放入ebp寄存器中,即将old ebp还原。此时堆栈的变化如下:
这里写图片描述
执行7:ret, 程序将执行15行代码,此时堆栈变化如下:
这里写图片描述
执行15: leave 相当于
movl %ebp, %esp
popl %ebp
将ebp寄存器的值放入esp寄存器中,并且弹出栈顶元素,存放到ebp中。此时堆栈变化如下:
这里写图片描述
执行16:ret ,程序将执行23行代码,此时堆栈变化如下:
这里写图片描述
执行23: addl $7, %eax, 将eax寄存器的值加上7,并且将结果放入eax寄存器。
执行此指令前,eax寄存器的值为19(上面已经计算得出),执行之后,eax寄存器变为26,此时堆栈无变化
执行24:leave 相当于:
movl %ebp, %esp
popl %ebp
此时堆栈还原到最初的状态,如下图所示:
这里写图片描述
最后执行25: ret 程序退出。

通过分析前面的C程序的执行过程,可以看出计算机的执行过程,就是CPU读取一条一条的指令来进行执行,指令寄存器EIP就指向下一条需要执行的指令。
而编译器将高级语言的代码编译成CPU可以理解的二进制代码。任何复杂的程序,都最终变成一条条简单的计算机指令堆叠而成。
程序执行过程中一些数据的保存,大都是通过堆栈进行的。堆栈寄存器EBP和ESP共同确定了一个堆栈的栈底和栈顶。
通过本节课程学习,初步了解了一个C程序在指令级的运行方式,理解了程序在执行过程中,堆栈的变化过程。

Logo

更多推荐