嵌入式_预编译、编译、汇编、链接
写下第一个hello word!! C语言程序,在集成开发环境中,只需点击编译按钮就能一键生成可执行程序,整个过程看似波澜不惊,但是其编译器却像过山车一样。从代码到可执行文件大致可分为4部分:预编译,编译,汇编,链接。
前言
话不多,上干货
一、 预编译
预编译:预编译的作用是把程序中的宏定义、头文件全部展开、处理条件编译的真假值、去掉注释等作用,最后预编译得到的是一个纯净的C文件。
在gcc中利用gcc -E xxx.c -o xxx.i得到预编译后的文件。
下面来对test.c文件进行预编译:
代码如下(示例):
#include <stdio.h>
#include <stdlib.h>
#define MAXOPTIONS 100
#define MAXVal 20
/*fun*/
int fun(void)
{
printf("fun !");
}
/*mian.c*/
int main(int argc, char **argv)
{
int value;
printf("main");
return 0;
}
得到预编译文件test.i如下(示例):
# 1 "test.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 31 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 32 "<command-line>" 2
# 1 "test.c"
# 1 "/usr/include/stdio.h" 1 3 4
# 27 "/usr/include/stdio.h" 3 4
# 1 "/usr/include/x86_64-linux-gnu/bits/libc-header-start.h" 1 3 4
# 33 "/usr/include/x86_64-linux-gnu/bits/libc-header-start.h" 3 4
# 1 "/usr/include/features.h" 1 3 4
# 424 "/usr/include/features.h" 3 4
# 1 "/usr/include/x86_64-linux-gnu/sys/cdefs.h" 1 3 4
# 427 "/usr/include/x86_64-linux-gnu/sys/cdefs.h" 3 4
# 1 "/usr/include/x86_64-linux-gnu/bits/wordsize.h" 1 3 4
# 428 "/usr/include/x86_64-linux-gnu/sys/cdefs.h" 2 3 4
# 1 "/usr/include/x86_64-linux-gnu/bits/long-double.h" 1 3 4
# 429 "/usr/include/x86_64-linux-gnu/sys/cdefs.h" 2 3 4
# 425 "/usr/include/features.h" 2 3 4
# 448 "/usr/include/features.h" 3 4
# 1 "/usr/include/x86_64-linux-gnu/gnu/stubs.h" 1 3 4
# 10 "/usr/include/x86_64-linux-gnu/gnu/stubs.h" 3 4
# 1 "/usr/include/x86_64-linux-gnu/gnu/stubs-64.h" 1 3 4
# 11 "/usr/include/x86_64-linux-gnu/gnu/stubs.h" 2 3 4
# 449 "/usr/include/features.h" 2 3 4
# 34 "/usr/include/x86_64-linux-gnu/bits/libc-header-start.h" 2 3 4
# 28 "/usr/include/stdio.h" 2 3 4
# 1 "/usr/lib/gcc/x86_64-linux-gnu/7/include/stddef.h" 1 3 4
# 216 "/usr/lib/gcc/x86_64-linux-gnu/7/include/stddef.h" 3 4
# 216 "/usr/lib/gcc/x86_64-linux-gnu/7/include/stddef.h" 3 4
typedef long unsigned int size_t;
# 34 "/usr/include/stdio.h" 2 3 4
# 1 "/usr/include/x86_64-linux-gnu/bits/types.h" 1 3 4
# 27 "/usr/include/x86_64-linux-gnu/bits/types.h" 3 4
# 1 "/usr/include/x86_64-linux-gnu/bits/wordsize.h" 1 3 4
# 28 "/usr/include/x86_64-linux-gnu/bits/types.h" 2 3 4
.
.
.
.
# 9 "test.c"
int fun(void)
{
printf("fun !");
}
int main(int argc, char **argv)
{
int value;
printf("main");
return 0;
}
二、编译
编译:分析语法语义,优化后生成相应的汇编代码文件,最复杂最核心的步骤
gcc -S test.c -o test.s 得到汇编.s文件
代码如下(示例):
.file "test.c"
.text
.section .rodata
.LC0:
.string "fun !"
.text
.globl fun
.type fun, @function
fun:
.LFB5:
.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
nop
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE5:
.size fun, .-fun
.section .rodata
.LC1:
.string "main"
.text
.globl main
.type main, @function
main:
.LFB6:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp
movl %edi, -4(%rbp)
movq %rsi, -16(%rbp)
leaq .LC1(%rip), %rdi
movl $0, %eax
call printf@PLT
movl $0, %eax
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE6:
.size main, .-main
.ident "GCC: (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0"
.section .note.GNU-stack,"",@progbits
三、汇编
汇编器是将汇编代码转变成机器可以执行的指令,几乎每一条汇编语句都对应一条机器指令。因此汇编过程非常简单。根据汇编指令和机器指令的对照表进行翻译就行了。
gcc -c test.s -o test.o得到二进制.o文件
四、链接
项目不止一个文件组成,有多个C语言模块,每个模块之间存在函数和变量的访问,那就需要知道模块和函数的地址,因此模块间这种调用关系需要用规定好的符号联系起来,所以这个模块间的拼接过程就是链接过程
在gcc中用gcc test.o -o text.out生成
或者直接gcc test.c -o text.out
总结
以上就是整个c代码到可执行程序的整个过程!!!