从汇编代码理解计算机是如何工作的

删了。。溜了溜了

 

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行汇编代码的执行过程如下动图所示:


 


 

几个指令的解析:

  1. pushl  %eax         等价于 →        subl  $4, %esp        movl  %eax,(%esp)  

  2. popl  %eax          等价于 →        movl (%esp),%eax     addl  $4,%esp

  3. call  0x12345       等价于 →        pushl %eip(*)        movl  $0x12345,%eip

  4. ret         等价于 →                     popl  %eip(*)

    注:由于eip寄存器不能直接操作,所以对eip的操作都是伪指令,伪指令用(*)表示;

  5. enter     等价于 →         pushl %ebp     movl %esp,%ebp

  6. leave      等价于 →        movl %ebp,%esp      popl %ebp

 

posted @ 2017-02-27 13:18  漩涡一惊  阅读(578)  评论(0)    收藏  举报