陈康 + 原创作品转载请注明出处 + 本文章为《Linux内核分析》(MOOC课程http://mooc.study.163.com/course/USTC-1000029000 )的实验报告,主要探讨程序如何在计算机上利用CPU、寄存器和内存来运行的。
实验过程
程序C语言代码如下:
int g(int x)
{
return x + 9;
}
int f(int x)
{
return g(x);
}
int main(void)
{
return f(8) + 11;
}
使用以下命令将main.c编译为汇编文件main.s
gcc –S –o main.s main.c -m32
编译完成后删除以“.”开头的汇编语句得到以下汇编代码:
g:
pushl %ebp
movl %esp, %ebp
movl 8(%ebp), %eax
addl $9, %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 $11, %eax
leave
ret
工作过程分析
下面根据汇编代码一步一步分析程序的执行过程,对应的堆栈和相应寄存器的变化情况。
程序从main函数开始执行
1. 初始状态 eip:18 ebp初始值设为ebp0,esp同样也为ebp0;
2. 18行执行完毕 eip:19 ebp:ebp0 esp:ebp0-4 ebp0-4地址对应的内存区域赋值为ebp0
3. 19行执行完毕 eip:20 ebp:ebp0-4 esp:ebp0-4
4. 20行执行完毕 eip:21 ebp:ebp0-4 esp:ebp0-8
5. 21行执行完毕 eip:22 ebp:ebp0-4 esp:ebp0-8 ebp0-8地址对应的内存区域赋值为8
6. 22行执行跳转 eip:9 ebp:ebp0-4 esp:ebp0-12 ebp0-12地址对应的内存区域赋值为原eip的值23
7. 9行执行完毕 eip:10 ebp:ebp0-4 esp:ebp0-16 ebp0-16地址对应的内存区域赋值为ebp0-4
8. 10行执行完毕 eip:11 ebp:ebp0-16 esp:ebp0-16
9. 11行执行完毕 eip:12 ebp:ebp0-16 esp:ebp0-20
10. 12行执行完毕 eip:13 ebp:ebp0-16 esp:ebp0-20 将eax寄存器赋值为8
11. 13行执行完毕 eip:14 ebp:ebp0-16 esp:ebp0-20 ebp0-20地址对应的内存区域赋值为8
12. 14行执行完毕 eip:2 ebp:ebp0-16 esp:ebp0-24 ebp0-24地址对应的内存区域赋值为原eip的值15
13. 2行执行完毕 eip:3 ebp:ebp0-16 esp:ebp0-28 ebp0-28地址对应的内存区域赋值为ebp0-16
14. 3行执行完毕 eip:4 ebp:ebp0-28 esp:ebp0-28
15. 4行执行完毕 eip:5 ebp:ebp0-28 esp:ebp0-28 将eax寄存器赋值为8
16. 5行执行完毕 eip:6 ebp:ebp0-28 esp:ebp0-28 将eax寄存器设为8+9=17
17. 6行执行完毕 eip:7 ebp:ebp0-16 esp:ebp0-24
18. 7行执行完毕 eip:15 ebp:ebp0-16 esp:ebp0-20
19. 15行执行完毕 eip:16 ebp:ebp0-4 esp:ebp0-12
20. 16行执行完毕 eip:23 ebp:ebp0-4 esp:ebp0-8
21. 23行执行完毕 eip:24 ebp:ebp0-4 esp:ebp0-8 将eax寄存器设为17+11=28
22. 24行执行完毕 eip:25 ebp:ebp0 esp:ebp0
23. 25行执行完毕 程序结束
程序从main函数开始执行
1. 初始状态 eip:18 ebp初始值设为ebp0,esp同样也为ebp0;
2. 18行执行完毕 eip:19 ebp:ebp0 esp:ebp0-4 ebp0-4地址对应的内存区域赋值为ebp0
3. 19行执行完毕 eip:20 ebp:ebp0-4 esp:ebp0-4
4. 20行执行完毕 eip:21 ebp:ebp0-4 esp:ebp0-8
5. 21行执行完毕 eip:22 ebp:ebp0-4 esp:ebp0-8 ebp0-8地址对应的内存区域赋值为8
6. 22行执行跳转 eip:9 ebp:ebp0-4 esp:ebp0-12 ebp0-12地址对应的内存区域赋值为原eip的值23
7. 9行执行完毕 eip:10 ebp:ebp0-4 esp:ebp0-16 ebp0-16地址对应的内存区域赋值为ebp0-4
8. 10行执行完毕 eip:11 ebp:ebp0-16 esp:ebp0-16
9. 11行执行完毕 eip:12 ebp:ebp0-16 esp:ebp0-20
10. 12行执行完毕 eip:13 ebp:ebp0-16 esp:ebp0-20 将eax寄存器赋值为8
11. 13行执行完毕 eip:14 ebp:ebp0-16 esp:ebp0-20 ebp0-20地址对应的内存区域赋值为8
12. 14行执行完毕 eip:2 ebp:ebp0-16 esp:ebp0-24 ebp0-24地址对应的内存区域赋值为原eip的值15
13. 2行执行完毕 eip:3 ebp:ebp0-16 esp:ebp0-28 ebp0-28地址对应的内存区域赋值为ebp0-16
14. 3行执行完毕 eip:4 ebp:ebp0-28 esp:ebp0-28
15. 4行执行完毕 eip:5 ebp:ebp0-28 esp:ebp0-28 将eax寄存器赋值为8
16. 5行执行完毕 eip:6 ebp:ebp0-28 esp:ebp0-28 将eax寄存器设为8+9=17
17. 6行执行完毕 eip:7 ebp:ebp0-16 esp:ebp0-24
18. 7行执行完毕 eip:15 ebp:ebp0-16 esp:ebp0-20
19. 15行执行完毕 eip:16 ebp:ebp0-4 esp:ebp0-12
20. 16行执行完毕 eip:23 ebp:ebp0-4 esp:ebp0-8
21. 23行执行完毕 eip:24 ebp:ebp0-4 esp:ebp0-8 将eax寄存器设为17+11=28
22. 24行执行完毕 eip:25 ebp:ebp0 esp:ebp0
23. 25行执行完毕 程序结束
总结
存储程序计算机工作模型是进行分析的最基础的逻辑结构,是进行运行分析的基础。本例中分析的主要内容是函数调用堆栈的变化,包括:
- 函数调用和返回时堆栈的变化
- 如何使用堆栈进行函数参数传递
- 局部变量如何存储和释放
高级语言涉及大量的函数调用,因此进行运行分析时,堆栈是主要分析对象。