美文网首页
程序员自我修养1:GCC构建过程

程序员自我修养1:GCC构建过程

作者: 梦工厂 | 来源:发表于2020-10-09 01:27 被阅读0次

    示例代码:hello.c

    #include <stdio.h>
    int main()
    {
        printf("Hello, World!");
        return 0;
    }
    

    1. 预处理 gcc –E hello.c –o hello.i

    预处理过程主要处理源代码中以#开始的预编译指令,将要包含(include)的文件插入原文件中、将宏定义展开、根据条件编译命令选择要使用的代码,最后将这些代码输出到一个 ".i" 文件中等待进一步处理。

    • 将所有的 "#define" 删除,并且展开所有的宏定义
    • 处理所有条件预编译指令,比如"#if"、"#ifdef"、"#elif"、"#else"、"#endif"
    • 处理"#include"预编译指令,将被包含的文件插入到该预编译指令的位置。注意,这个过程是递归进行的,也就是说被包含的文件可能还包含其他文件
    • 删除所有的注释"//"和"/* */"
    • 添加行号和文件名标识,比如 #2 "hello.c" 2,以便于编译时编译器产生调试用的行号信息及用于编译时产生编译错误或警告时能够显示行号
    • 保留所有的 #pragma 编译器指令,因为编译器需要使用它们
    ......
    extern char *ctermid (char *__s) __attribute__ ((__nothrow__ , __leaf__));
    # 914 "/usr/include/stdio.h" 3 4
    extern void flockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__));
    extern int ftrylockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)) ;
    extern void funlockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__));
    # 944 "/usr/include/stdio.h" 3 4
    # 2 "hello.c" 2
    # 3 "hello.c"
    int main()
    {
     printf("Hello, World!");
     return 0;
    }
    

    2. 编译 gcc –S hello.c –o hello.s

    汇编过程就是把预处理完的文件进行一系列词法分析、语法分析、语义分析、优化,生成相应的汇编代码。 构建的核心
    程序员自我修养2:编译过程

    cat 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
            leaq        .LC0(%rip), %rdi
            movl        $0, %eax
            call        printf@PLT
            movl        $0, %eax
            popq        %rbp
            .cfi_def_cfa 7, 8
            ret
            .cfi_endproc
    .LFE0:
            .size        main, .-main
            .ident        "GCC: (Debian 6.3.0-18+deb9u1) 6.3.0 20170516"
            .section        .note.GNU-stack,"",@progbits
    

    3. 汇编 gcc –c hello.c –o hello.o

    汇编过程是将汇编代码转变为机器指令,每个汇编语句几乎对应一条机器指令。
    过程比较简单,没有复杂语法和语义,也没指令优化,根据汇编指令和机器指令的对照表一一翻译即可。

    4. 链接 gcc hello.o –o hello

    链接过程将多个模块的目标文件和静态库一起拼接成最终的可执行文件,并自动的去修正函数和变量的地址;

    静态链接的步骤

    1. 空间和地址分配
      扫描所有的输入目标文件,获得各个段的长度、属性和位置,相似段合并。将各个目标文件的符号表收集统一到全局符号表。
      此时各个段的虚拟地址已经确定,各个符号的虚拟地址也确定了(段内偏移+段的偏移)。
    2. 符号的解析与重定位
      每个段中需要修正的外部符号都会存储在重定位表中,对某个符号重定位时,从全局符号表中查找符号的地址。

    链接之前,printf函数地址为空;

    objdump -r hello.o //重定位信息
    RELOCATION RECORDS FOR [.text]:
    OFFSET           TYPE              VALUE
    0000000000000007 R_X86_64_PC32     .rodata-0x0000000000000004
    0000000000000011 R_X86_64_PLT32    printf-0x0000000000000004
    
    objdump -d hello.o //反汇编
    0000000000000000 <main>:
       0:        55                           push   %rbp
       1:        48 89 e5                     mov    %rsp,%rbp
       4:        48 8d 3d 00 00 00 00         lea    0x0(%rip),%rdi        # b <main+0xb>
       b:        b8 00 00 00 00               mov    $0x0,%eax
      10:        e8 00 00 00 00               callq  15 <main+0x15>
      15:        b8 00 00 00 00               mov    $0x0,%eax
      1a:        5d                           pop    %rbp
      1b:        c3                           retq
    

    链接之后,确定了函数的调用地址;

    00000000000006b0 <main>:
     6b0:        55                           push   %rbp
     6b1:        48 89 e5                     mov    %rsp,%rbp
     6b4:        48 8d 3d 99 00 00 00         lea    0x99(%rip),%rdi        # 754 <_IO_stdin_used+0x4>
     6bb:        b8 00 00 00 00               mov    $0x0,%eax
     6c0:        e8 9b fe ff ff               callq  560 <printf@plt>
     6c5:        b8 00 00 00 00               mov    $0x0,%eax
     6ca:        5d                           pop    %rbp
     6cb:        c3                           retq
     6cc:        0f 1f 40 00                  nopl   0x0(%rax)
    

    相关文章

      网友评论

          本文标题:程序员自我修养1:GCC构建过程

          本文链接:https://www.haomeiwen.com/subject/uzpkpktx.html