『hello, world』 如何运行

来源:互联网 发布:自助设计软件 编辑:程序博客网 时间:2024/06/14 19:20
 15 May 2014  http://blog.cyeam.com/computer%20systems:%20a%20programmer's%20perpective/2014/05/15/gcc/

以经典的“hello, world”为例,分析编译的各个阶段。

/* $begin hello */#include <stdio.h>int main() {    printf("hello, world\n");}/* $end hello */

IMG-THUMBNAIL

  • 预处理阶段。将include要引入的文件载入并替换掉include。将.c文件转换为.i文件。使用gcc命令加上参数-E

      `gcc -E hello.c -o hello.i`

    预处理结束后,生成了845行的hello.i文件。

  • 编译阶段。将.i文件编译成.s文件。

      `gcc -S hello.i -o hello.s`

    将其翻译成汇编语言。

          .file   "hello.c"      .section    .rodata  .LC0:      .string "hello, world"      .text      .globl  main      .type   main, @function  main:  .LFB0:      .cfi_startproc      pushq   %rbp      .cfi_def_cfa_offset 16      .cfi_offset 6, -16      movq    %rsp, %rbp      .cfi_def_cfa_register 6      movl    $.LC0, %edi      call    puts      popq    %rbp      .cfi_def_cfa 7, 8      ret      .cfi_endproc  .LFE0:.LC0:      .size   main, .-main      .ident  "GCC: (Ubuntu/Linaro 4.8.1-10ubuntu9) 4.8.1"      .section    .note.GNU-stack,"",@progbits
    • .section .rodata后面定义了一个只读字符串常量.LC0,用来储存hello, world
    • .text后面开始是代码区,.globl main定义了主函数入口。
    • .cfi_startproc用在每个函数的开始,用于初始化一些内部数据结构。
    • rbp寄存器是ebp寄存器64位扩展,ebp寄存器扩展基址指针寄存器(extended base pointer)  其内存放一个指针,该指针指向系统栈最上面一个栈帧的底部。

      以 %r 开头的表示 64-bit 寄存器;以 %e 开头的是 32-bit 寄存器。

      pushq,64位,所以是q。保存rbp,以便使用rbp作为栈指针。

    • .cfi_endproc在函数结束的时候使用与.cfi_startproc相配套使用。
    • movq %rsp, %rbp保存栈frame,现在有些编译器开发者致力于优化函数调用,优化这个frame就是其中一项。
    • movl $.LC0, %edi把字符串”hello, world”的地址放入edi。
    • call puts打印并且在后面自动追加换行符\n
  • 汇编阶段。将上一步生成的汇编代码通过汇编器编译成目标文件.o

    gcc -c hello.s -o hello.o

  • 链接阶段。将上一步得到的目标文件与printf.o链接合并到可执行文件hello中。编译完成。

    gcc hello.o -o hello


0 0
原创粉丝点击