Linux下从原文件到可执行文件的过程
二、流程图
现在我们分别在看一下这四个过程分别做了什么事情。
三、预编译
预编译阶段主要是做一些预处理,比如宏的替换,头文件的包含处理,去除源码中的空格和注释,预编译条件的判断等操作,下面经过一段测试代码来说明这些情况:
#include "main.h"
#include <stdio.h>
//#include "main.h"
#define N 5
#define DEBUG 0
#define NUM 2
int main()
{
#ifdef DEBUG
printf("define debug\n"); //print debug
#else
printf("do not debug\n"); //do not debug
#endif
int a[N] = {0};
#if NUM == 1
printf("NUM is 1\n");
a = 9;
#elif NUM == 2
printf("NUM is 2\n");
#else
printf("default NUM\n");
#endif
return 0;
}
可以看到上述代码有函数,语句,宏定义,有预编译条件语句,有注释,有头文件包含等。
我们执行下面语句将源文件预编译后的文件输出:
gcc -E main.c > main.i
输出后,截取一部分main.i文件的内容如下:
1 "main.c"
2 # 1 "<命令行>"
3 # 1 "main.c"
4
5 # 1 "main.h" 1
6
7
8
9 int a = 1;
10 int b = 2;
11 int c = 3;
12 # 3 "main.c" 2
13 # 1 "/usr/include/stdio.h" 1 3 4
14 # 27 "/usr/include/stdio.h" 3 4
15 # 1 "/usr/include/features.h" 1 3 4
..........................
56 typedef unsigned char __u_char;
57 typedef unsigned short int __u_short;
58 typedef unsigned int __u_int;
59 typedef unsigned long int __u_long;
60
61
62 typedef signed char __int8_t;
63 typedef unsigned char __uint8_t;
64 typedef signed short int __int16_t;
65 typedef unsigned short int __uint16_t;
66 typedef signed int __int32_t;
67 typedef unsigned int __uint32_t;
68
69
70
71
72 __extension__ typedef signed long long int __int64_t;
73 __extension__ typedef unsigned long long int __uint64_t;
...........................
...........................
# 943 "/usr/include/stdio.h" 3 4
856
857 # 4 "main.c" 2
858
859
860
861
862
863
864 int main()
865 {
866
867
868 printf("define debug\n");
869
870
871
872
873 int a[5] = {0};
874
875
876
877
878 printf("NUM is 2\n");
879
880
881
882 return 0;
883 }
与原始的main.c文件相比较,可以看到下面的几点区别:
1.宏的展开,将N替换为5;
2.预定义条件的判断,可以看到只输出条件判断正确的语句;
3.去掉了源代码中的注释;
4.头文件的展开,可以看到大串的代码都是头文件stdio.h的文件,还有自定义头文件main.h的展开。
四.编译
编译阶段会对经过预处理后文件进行语法,词法的检查,将main.i文件翻译成文本文件main.s文件,该文件中都是一系列的汇编语言组成的指令,可以用如下命令执行生成main.s文件
gcc -S main.i > main.s
执行完成后,可以看到main.s文件中为汇编指令,截取如下:
1 .file "main.c"
2 .globl a
3 .data
4 .align 4
5 .type a, @object
6 .size a, 4
7 a:
8 .long 1
9 .globl b
10 .align 4
11 .type b, @object
12 .size b, 4
13 b:
14 .long 2
15 .globl c
16 .align 4
17 .type c, @object
18 .size c, 4
19 c:
20 .long 3
21 .section .rodata
22 .LC0:
23 .string "define debug"
24 .LC1:
25 .string "NUM is 2"
26 .text
27 .globl main
28 .type main, @function
29 main:
30 .LFB0:
31 .cfi_startproc
32 pushl %ebp
五.汇编
汇编阶段将是汇编器将上一阶段中的汇编代码翻译成机器语言指令,把这些指令打包成可重定位目标程序格式,并将结果保存在main.o文件中,该文件是一堆二进制代码,,他的编码方式是机器语言指令而不是字符,所以打开后看到的是乱码。
可以通过下面命令生成main.o文件
gcc -c main.c -o main.o
六.链接
链接阶段就需要链接器进行链接操作,链接阶段主要是将所有需要的.o文件以及对应的静态库或者动态库链接后生成可执行文件,可以加载到内存中,由系统执行。
这里有动态库和静态库只说,他们具体具体的区别与联系可以看下一篇博文;
在Linux环境下,静态库是以.a结束的文件,静态库在链接的时候将库里面的代码直接拷贝到可执行程序中。
在Linux环境下,动态库是.so结尾的文件,动态库在链接的时候只是创建一些符号表,而在运行的时候才将代码装入到内存中,映到运行时相应进程的需地址空间。
七.总结
以上为从源代码到可执行程序中间经历的步骤,因为在一次面试中有问道相应的知识,所以在这里座椅个总结,以备日后查看。
推荐阅读
-
Linux下从原文件到可执行文件的过程
-
linux系统从入侵到提权的详细过程(图文)
-
Linux下安装Mysql多实例作为数据备份服务器实现多主到一从多实例的备份
-
Linux下安装Mysql多实例作为数据备份服务器实现多主到一从多实例的备份
-
使用Opencv对图片处理的各种原理-从图片的储存形式到各种处理过程(必看)(下)
-
从写项目到部署linux服务器全过程-linux下Nginx的安装
-
Linux下安装MySQL多实例作为数据备份服务器实现多主到一从多实例的备份
-
从linux下获取的文件目录字符串列表,转换成树形结构展示到前端
-
Linux下安装MySQL多实例作为数据备份服务器实现多主到一从多实例的备份
-
linux系统从入侵到提权的详细过程(图文)