c - C编译涉及哪些内部过程?

标签 c compiler-construction embedded linker

我有一组*.C文件(嵌入式相关)。

编译和链接以创建最终可执行文件时涉及哪些步骤/过程(内部信息)? (有关预处理器/编译器通常对 C 源代码执行的操作的信息/步骤。)

最终可执行文件的一般结构是什么(例如: header 后跟符号表等)?

最佳答案

以 gcc 为例,我认为要使用的选项是 -save-temps。

大致的步骤是对文件进行传递以拉取所有包含内容并基本上创建一个要解析的文件。如今,许多工具都使用在一组规则(bison、yacc、flex 等)上运行的解析器,其目标是解析 ascii,将您的程序变成一种非常广泛的汇编语言,因为缺少更好的术语。

a = a + 1;

可以变成

Load variable named a, size of blah, type unsigned foo
load immediate 1, size blah, unsigned
add
store result a

然后可以进行优化,编译器中间语言可能有一个增量函数,并确定增量优于 1 和加法的加载。最终这些优化完成了,这个中间代码通过后端到达目标指令集。这通常作为程序集输出,并被送入汇编程序,汇编程序将其转换为目标文件,并且可以进行目标特定的优化。然后目标文件被送入链接器,链接器将它们链接在一起。一个程序中的一个函数可能正在调用一个不在名为 bob 的目标文件中的函数,该目标文件没有到达 bob 的地址或偏移量,它在那里留下一个空洞供插入地址,链接器的工作是连接所有其中,决定函数 bob 在二进制文件中的位置(为其分配一个地址),然后找到所有调用 bob 的地方,当这些地方被放置在内存中时,插入允许调用 bob 所需的指令或地址,以便最终结果是一个可执行二进制文件。

llvm 已经是 gcc 的竞争对手,提供了对该过程的良好可见性。您可以将 C 代码编译为中间体。从我们的 bob 函数开始

unsigned int bob ( unsigned int a )
{
    return(a+1);
}

编译成位码

clang -c -o bob.bc -emit-llvm bob.c

将位码反汇编成人类可读的形式

llvm-dis bob.bc

结果是 bob.ll

define i32 @bob(i32 %a) nounwind {
entry:
  %a.addr = alloca i32, align 4
  store i32 %a, i32* %a.addr, align 4
  %tmp = load i32* %a.addr, align 4
  %add = add i32 %tmp, 1
  ret i32 %add
}

Unoptimize 代码喜欢经常从内存中存储和获取,并且在传递到函数时经常从堆栈中存储和获取。

除了可以轻松让您看到幕后情况之外,llvm 还不错,因为您可以在任何级别进行优化,组合对象并在整个程序级别进行优化,而 gcc 会将您限制在文件或函数级别。所以我们可以优化这个位码。

opt -std-compile-opts bob.bc -o bob_opt.bc
llvm-dis bob_opt.bc

那些额外的存储和加载都消失了,函数的内容仍然存在。

define i32 @bob(i32 %a) nounwind readnone {
entry:
  %add = add i32 %a, 1
  ret i32 %add
}

然后 llc 用于将其转换为所需目标的汇编程序

llc -march=arm bob.bc
cat bob.s
...
bob:                                    @ @bob
@ BB#0:                                 @ %entry
    str r0, [sp, #-4]!
    add r0, r0, #1
    add sp, sp, #4
    bx  lr
...
llc -march=arm bob_opt.bc
cat bob_opt.s
...
bob:                                    @ @bob
@ BB#0:                                 @ %entry
    add r0, r0, #1
    bx  lr
...

是的,那里有很多很多书。还有许多编译器等。除了 llvm 之外,Fabrice Bellard(是的 qemu 人)还有一个 super 简单的编译器,几乎可以生成一个中间文件,您可以检查 http://bellard.org/fbcc/它被埋没了,以至于几乎不为人所知,但是如果您刚刚进入编译器的内部,那么看看它会很有趣。另外还有一个众所周知的,tcc http://bellard.org/tcc/这个特别没有通过汇编程序的后端,直接生成操作码以提高速度和实时(重新)编译。

关于c - C编译涉及哪些内部过程?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3812670/

相关文章:

c - C 程序中对函数的 undefined reference

c - C 中的枚举类型警告

c - 如何在外部服务器上为嵌入式 C 安装和使用编译器?

c - C 多久/何时执行 if/else 语句中的内容?

c - 如何限制中断驱动的 UART 传输 PIC24H?

c - c - 如何在c中使用inotify查看具有多个文件更改的目录

c - Valgrind报告内存肯定丢失没有错误

java - 为什么 Eclipse 会编译它,而 javac 不会?

java - 初始化最终字段 - Java

c - 为具有多个接口(interface)的设备设置 MTU