从汇编代码理解计算机是如何工作的
删了。。溜了溜了
1945年冯·诺依曼首先提出了“存储程序计算机”的概念,即完成一条指令需要3个步骤:从内存取指令、指令译码和CPU执行指令,然后继续取下一条指令、指令译码、CPU执行……
为了深入理解计算机工作的原理,我们先通过编写一个简单的C程序:
int g(int x) { return x + 6; } int f(int x) { return g(x); } int main(void) { return f(5) + 7; }
这个简单的C程序实际上就是算出5+6+7=18。
我们对这个C程序进行汇编处理,gcc -S -o main.s main.c -m32,得到原始汇编代码;然后对其进行简化(前缀带.的均删除),得到:
1 g: 2 pushl %ebp 3 movl %esp, %ebp 4 movl 8(%ebp), %eax 5 addl $6, %eax 6 popl %ebp 7 ret 8 f: 9 pushl %ebp 10 movl %esp, %ebp 11 subl $4, %esp 12 movl 8(%ebp), %eax 13 movl %eax, (%esp) 14 call g 15 leave 16 ret 17 main: 18 pushl %ebp 19 movl %esp, %ebp 20 subl $4, %esp 21 movl $5, (%esp) 22 call f 23 addl $7, %eax 24 leave 25 ret
笔者此前只接触过DOS/Windows下的汇编语言,这些汇编代码都是Intel风格的。但Linux操作系统采用的是AT&T格式,两者在语法格式上有着很大的不同。
(参考https://www.ibm.com/developerworks/cn/linux/l-assembly/)
学习了 AT&T 汇编风格后,我们开始对汇编代码一步一步分析。为此笔者做了一个动态过程图,有助于理解每一个步骤程序的变化。
程序是从main函数开始执行,我们假设此时寄存器ebp、esp均指向内存地址为4000的位置(即堆栈为空)。25行汇编代码的执行过程如下动图所示:

几个指令的解析:
-
pushl %eax 等价于 → subl $4, %esp movl %eax,(%esp)
-
popl %eax 等价于 → movl (%esp),%eax addl $4,%esp
-
call 0x12345 等价于 → pushl %eip(*) movl $0x12345,%eip
-
ret 等价于 → popl %eip(*)
注:由于eip寄存器不能直接操作,所以对eip的操作都是伪指令,伪指令用(*)表示;
-
enter 等价于 → pushl %ebp movl %esp,%ebp
-
leave 等价于 → movl %ebp,%esp popl %ebp

浙公网安备 33010602011771号