代码改变世界

透过汇编代码来理解计算机的工作

2015-03-08 13:34  zfunction  阅读(105)  评论(0)    收藏  举报

发表本博客的目的是要完成《Linux内核分析》课程布置的实验,同时也为自己的学习做一个梳理和总结。如有疏漏和错误,敬请指正!

实验环境为实验楼64位Linux虚拟机,实验代码为一个简单的C程序生成的32位汇编代码。C源代码(main.c)如下:

 1 int g(int x)
 2 {
 3   return x + 2;
 4 }
 5 
 6 int f(int x)
 7 {
 8   return g(x);
 9 }
10 
11 int main(void)
12 {
13   return f(3) + 5;
14 }

通过命令:

gcc –S –o main.s main.c -m32

获得32位汇编代码(main.s):

 1     .file    "main.c"
 2     .text
 3     .globl    g
 4     .type    g, @function
 5 g:
 6 .LFB0:
 7     .cfi_startproc
 8     pushl    %ebp
 9     .cfi_def_cfa_offset 8
10     .cfi_offset 5, -8
11     movl    %esp, %ebp
12     .cfi_def_cfa_register 5
13     movl    8(%ebp), %eax
14     addl    $3, %eax
15     popl    %ebp
16     .cfi_restore 5
17     .cfi_def_cfa 4, 4
18     ret
19     .cfi_endproc
20 .LFE0:
21     .size    g, .-g
22     .globl    f
23     .type    f, @function
24 f:
25 .LFB1:
26     .cfi_startproc
27     pushl    %ebp
28     .cfi_def_cfa_offset 8
29     .cfi_offset 5, -8
30     movl    %esp, %ebp
31     .cfi_def_cfa_register 5
32     subl    $4, %esp
33     movl    8(%ebp), %eax
34     movl    %eax, (%esp)
35     call    g
36     leave
37     .cfi_restore 5
38     .cfi_def_cfa 4, 4
39     ret
40     .cfi_endproc
41 .LFE1:
42     .size    f, .-f
43     .globl    main
44     .type    main, @function
45 main:
46 .LFB2:
47     .cfi_startproc
48     pushl    %ebp
49     .cfi_def_cfa_offset 8
50     .cfi_offset 5, -8
51     movl    %esp, %ebp
52     .cfi_def_cfa_register 5
53     subl    $4, %esp
54     movl    $8, (%esp)
55     call    f
56     addl    $1, %eax
57     leave
58     .cfi_restore 5
59     .cfi_def_cfa 4, 4
60     ret
61     .cfi_endproc
62 .LFE2:
63     .size    main, .-main
64     .ident    "GCC: (Ubuntu 4.8.2-19ubuntu1) 4.8.2"
65     .section    .note.GNU-stack,"",@progbits

可以发现main.c生成的main.s汇编代码多而杂,但是凡是以"."开头的行都是一些辅助消息,暂且将其看作是注释好了。我们可以将其都删掉,于是有简化后的代码(main.s):

 1 g:
 2     pushl    %ebp
 3     movl    %esp, %ebp
 4     movl    8(%ebp), %eax
 5     addl    $2, %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    $3, (%esp)
22     call    f
23     addl    $5, %eax
24     leave
25     ret

现在对此汇编代码进行具体分析,可见每个C函数生成的汇编代码都维护有一个自己的堆栈,而堆栈之间也随着函数的调用而相互嵌套的。

g:
  pushl %ebp       #接下来的两行指令建立被调函数的堆栈
  movl %esp, %ebp   # 
  movl 8(%ebp), %eax  # 接下来的两条指令实现3+2的加法运算
  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      # #1
  movl $3, (%esp)
  call f
  addl $5, %eax
  leave
ret

吴厚洲 + 原创作品转载请注明出处 + 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000