这是网易云课堂linux内核分析课程的实验报告
实验的内容是通过编写一段简单的c程序,并分析其汇编代码,以了解计算机是如何运行程序。
程序hello.c源代码:
1 #include <stdio.h>
2
3 int bar(int a)
4 {
5 return a;
6 }
7
8 int foo(a)
9 {
10 return bar(a) + 1;
11 }
12
13 int main()
14 {
15 return foo(10) + 1;
16 }
在bash下输入如下指令,获得源代码的汇编hello.s:
gcc -S -o hello.s hello.c -m32
由于本次试验只需要用到源代码对应的汇编指令,所以可以把hello.s中以.
开头的行删除。在vim下可以使用:
:%s/\s*\..*//g #将以"."开头的行换为空行
:%g/^$/d #删除空行
操作后得到以下代码:
1 bar:
2 pushl %ebp
3 movl %esp, %ebp
4 movl 8(%ebp), %eax
5 popl %ebp
6 ret
7 foo:
8 pushl %ebp
9 movl %esp, %ebp
10 subl $4, %esp
11 movl 8(%ebp), %eax
12 movl %eax, (%esp)
13 call bar
14 addl $1, %eax
15 leave
16 ret
17 main:
18 pushl %ebp
19 movl %esp, %ebp
20 subl $4, %esp
21 movl $10, (%esp)
22 call foo
23 addl $1, %eax
24 leave
25 ret
下面对汇编代码进行分析,从main函数开始:
18、19行的pushl
和movl
指令相当于enter
指令,用于保存前一个栈的信息,同时为main函数开辟一个空栈。pushl
将前一个栈的基地址保存,movl
将ebp
赋值为前一个栈的栈顶,同时也是main栈的基地址。esp
作为main栈的栈顶。完成了上面两部,main栈就算建成了。接下来开始执行源代码里的东西了。
20、21行。subl
为函数的栈开了空间,用来存放foo需要的参数。movl
将参数放在该空间。然后执行call
指令,跳到foo函数中,也就是eip
要变成foo的地址。注意call
指令要把当前eip
压栈(pushl %eip
)。然后把目光转到foo函数中,也就是第7行。和main函数开头一样,需要保存上一个栈的信息,同时为自己开创一个栈。pushl
和movl
指令做了这件事。接着放置bar需要的参数在自己的栈中(就是刚刚在mian函数时放的那个参数)。完成后执行call
指令跳到bar函数。
bar函数终于不再调用别的函数,而是获取foo给的参数(第4行,这时候ebp就是foo栈的栈顶)。将参数赋值给eax
后进行结束函数的工作。第5行的popl
指令将esp
加4,变为前一个栈(foo)的栈顶;ebp
获得出栈的内容,也就是前一个栈的基址。foo的栈完成恢复。最后ret
指令跳回foo函数中(相当于popl %eip
),bar函数就正式结束了。回到foo函数后,根据源代码要给返回值加1(14行),紧接着要结束foo。和结束bar函数过程一样,结束foo函数就是执行leave
然后回跳到main中。main函数为返回值加1(23行),以同样的流程退出main函数。程序结束。
注意1:函数的栈在内存中是从高地址向低地址增长的,所以pushl
后esp
要减4,而popl
后esp
要加4
注意2:leave
指令相当于movl %esp %ebp
,popl %ebp
。
由于bar的代码没有使用到变量,所以在创建好函数的栈后,esp
和ebp
是相等的,不需要leave指令。函数foo和main中创建了变量,并存放在栈中,修改了esp,因此退出函数时要还原esp的。所以在退出函数的时候要使用leave
。
注意3:call
指令压栈的内容在调用函数的栈中。毕竟call
指令执行在开辟新的函数栈指令之前。
理解:
程序就是由一条条汇编代码组成。这些汇编代码执行了运算,使用了内存空间。c语言中的函数,就是汇编中将当前寄存器保存到内存中,然后转跳到另外一处执行,执行完成后跳回原来地方,并恢复寄存器内容。而c语言屏蔽了这一过程,提供了抽象,我们只需要专注于函数要实现什么功能,不需要关注如何实现函数。
过程截图
xxtsmooc
原创作品转载请注明出处
《Linux内核分析》MOOC课程
http://mooc.study.163.com/course/USTC-1000029000
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。