了解-clang编译过程

2019-11-26  本文已影响0人  ibingewin

clang编译过程

clang是一个 CC++Objective-C的编译器, 包含了预处理语法解析代码生成优化汇编链接阶段, 尽管clang是高度集成的, 但是理解编译的各个阶段, 仍然很有必要.
过程:

预处理 -> 语法解析 -> 代码生成&优化 -> 汇编 -> 链接
.c -> AST -> .s -> .o -> .out

编译过程

驱动

clang可执行文件实际上是一个小的驱动程序, 控制其他工具(如编译器、汇编器和链接器)的总体执行. 通常你不需要直接和驱动程序交互, 就可以使用clang来运行其他工具.

预处理

这个阶段会对输入的源文件进行标记化处理、宏扩展、#include扩展和其他预处理器指令的处理. 对C输出.i, 对C++输出 .ii, 对 OC 输出 .mi, 对Objective-C++ 输出 .mii, 分别对应如下:

输入: .c.cpp.m.mm
输出: .i.ii.mi.mii

语法分析和语义分析

这个阶段会解析输入文件, 将预处理标记转换为解析树. 一旦以解析树的形式出现, 它也会用语义分析来计算表达式的类型, 确认代码格式是否正确.
该阶段负责生成大多数的编译警告错误, 输出为 AST(抽象语法树, Abstract Syntax Tree)

输出: AST

代码生成和优化

这个阶段会将AST转换为底层的中间代码(称为 LLVM IR), 再最终转换为机器码.该阶段负责优化生成的代码, 并处理特定目标的代码生成. 输出通常称为 .s 文件或者 汇编文件.

输出: .s汇编文件

汇编

这个阶段将编译器的输出转换为目标文件, 输出为 .o 文件 或者 object 文件

输出: .oobject文件

链接

这个阶段会将多个目标文件合并为一个可执行文件或动态库.输出为 a.out 或者 .so 文件

输出: .out.so

示例

下面用一个简单的hello world来演示一下:

第1步: 创建源码文件hello.c如下:

//
//  hello.c
//

#include <stdio.h>

#define ADD(x,y) (x+y)
#define ADD10(x, y) ADD(x+10, y)


int main() {
#if A
    printf("hello world A"); // A
#else
    printf("hello world B"); // B
#endif
    printf("result:%d", ADD10(1, 2));
}


第2步: 对其进行预编译, 得到.i输出文件, 使用命令:

$ clang -E hello.c -o hello.i

-E 选项为进行预编译 (更多的编译选项可以在查看这里)
hello.i 文件内容如下:

# 1 "hello.c"
# 1 "<built-in>" 1
# 1 "<built-in>" 3
# 362 "<built-in>" 3
# 1 "<command line>" 1
# 1 "<built-in>" 2
# 1 "hello.c" 2
.
.
.
# 10 "hello.c" 2



int main() {



    printf("hello world B");

    printf("result:%d", (1 +10 +2));
}

从中可以看到预处理做的一些工作

第3步:编译,得到.s文件, 使用命令

$ clang -S hello.i -o hello.s

-S 选项为进行汇编之前的所有阶段(代码生成优化指定目标的代码生成), 产生一个汇编文件 (更多的编译选项可以在查看这里)
hello.s文件内容如下:

    .section    __TEXT,__text,regular,pure_instructions
    .build_version macos, 10, 15    sdk_version 10, 15
    .globl  _main                   ## -- Begin function main
    .p2align    4, 0x90
_main:                                  ## @main
    .cfi_startproc
## %bb.0:
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset %rbp, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register %rbp
    subq    $16, %rsp
    leaq    L_.str(%rip), %rdi
    movb    $0, %al
    callq   _printf
    leaq    L_.str.1(%rip), %rdi
    movl    $13, %esi
    movl    %eax, -4(%rbp)          ## 4-byte Spill
    movb    $0, %al
    callq   _printf
    xorl    %esi, %esi
    movl    %eax, -8(%rbp)          ## 4-byte Spill
    movl    %esi, %eax
    addq    $16, %rsp
    popq    %rbp
    retq
    .cfi_endproc
                                        ## -- End function
    .section    __TEXT,__cstring,cstring_literals
L_.str:                                 ## @.str
    .asciz  "hello world B"

L_.str.1:                               ## @.str.1
    .asciz  "result:%d"


.subsections_via_symbols

第4步: 汇编,得到.o文件, 使用命令

$ clang -c hello.s -o hello.o

-c 选项为执行汇编及之前的所有阶段, 生成机器码0101, 产生一个目标文件 (更多的编译选项可以在查看这里)
hello.o文件内容如下:

cffa edfe 0700 0001 0300 0000 0100 0000
0400 0000 0802 0000 0020 0000 0000 0000
1900 0000 8801 0000 0000 0000 0000 0000
0000 0000 0000 0000 0000 0000 0000 0000
b800 0000 0000 0000 2802 0000 0000 0000
.
.
.
68ff ffff ffff ffff 3900 0000 0000 0000
0041 0e10 8602 430d 0600 0000 0000 0000
2800 0000 0100 002d 1900 0000 0200 0015
1200 0000 0100 002d 0b00 0000 0200 0015
0000 0000 0100 0006 0100 0000 0f01 0000
0000 0000 0000 0000 0700 0000 0100 0000
0000 0000 0000 0000 005f 6d61 696e 005f
7072 696e 7466 0000 
上一篇下一篇

猜你喜欢

热点阅读