王涵宇 原创作品转载请注明出处 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000
计算机是什么?
我们所说的计算机,总体上概括基本上都在冯-诺依曼的框架之内,那么只能存储01的电路版,芯片是怎样实现复杂的功能呢?
实际上0和1能做许多事情,冯诺依曼的体系结构正是把这种朦胧的"材料"分化清晰,它为代码,数据,指令划分了明确的界限,由此有了计算机的初步印象:
cpu的ip寄存器通过总线指向内存的代码段,从里面取指令,内存保存指令和数据,cpu负责执行。
第一个实验:反汇编简单c代码并做分析
0~
知识储备:
最基本常用指令
movl %eax,%edx 把eax的值赋值给edx
movl $0x123,%edx 把绝对数值放到寄存器里
movl 0x123,%edx edx=*(int32_t*)0x123;把这个地址里放的值放到寄存器中
movl (%ebx),%edx edx=*(int32_t*)ebx
movl 4(%ebx),%edx edx=*(int32_t*)(ebx+4)
操作堆栈指令(:后面是方便理解的步骤,实际上程序员不能这样操作)
pushl %eax :subl $4,%esp
movl %eax,(%esp)
popl %eax :movl (%esp),%eax
addl $4,%esp
call 0x12345 :pishl %eip //保存当时的eip
movl $0x12345,%eip
ret :pop %eip
leave :movl %ebp,%esp
popl %ebp
1~写一段c代码,保存为main.c,主要目的是为了展现栈的嵌套
int g(int x)
{
return x + 2;
}
int f(int x)
{
return g(x);
}
int main(void)
{
return f(5) + 2;
}
2~.在实验楼的linux客户端输入gcc -S -o main.s main.c -m32,来反汇编
3~.打开main.s
4~.做分析
删掉main.s的冗余代码,主要代码是这样的
g:
pushl %ebp
movl %esp, %ebp
movl 8(%ebp), %eax
addl $2, %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 $5, (%esp)
call f
addl $2, %eax
leave
ret
%ebp,%esp理解成指针指向运行栈的位置,每一个栈在运行之前通过push %ebp,movl %ebp,%esp两个操作开辟新空间,保存旧的空间
(%esp)理解成指针指向的内存单元(4字节),用笔在纸上划一个栈,跟着一步一步走,也就不难理解栈的巧妙之处了。
如下...(图很丑,理解大意..)
经过画图,这段代码在栈上执行了开辟栈和回收栈,把结果9放到eax中
栈的关键在于ebp和esp,esp用于开辟新的栈空间,而ebp用来保存每次执行完之后上一次栈的栈顶,push和pop正是完成此操作的关键
call和ret则是针对eip的“保存”,保证了eip恢复当前到执行指令的下一条(比如执行完f之后回到main)
leave还原栈上一次的样子
5~.小结
通过实验复习了栈的嵌套调用(上学期病毒课学过一点),计算机运算的奥秘就在于寄存器(快速运算),栈(能够保存现场)
可以说没有栈就不会有c程序等高级语言,我们现在还在操作寄存器而编程...