程序的编译链接过程

32 阅读2分钟

在标准C的任何一种实现中,存在两个不同的环境。

  1. 翻译环境:C语言代码转化成二进制的指令(在可执行程序内部)。
  2. 执行环境:执行二进制的代码。

image-20240326190621159

Add.c:

int Add(int a, int b)
{
	return a + b;
}

Sub.c:

int Sub(int a, int b)
{
	return a - b;
}

main.c:

extern int Add(int, int);
extern int Sub(int, int);

int main()
{
	int a = 10;
	int b = 5;

	printf("%d\n", Add(a, b));
	printf("%d\n", Sub(a, b));
	return 0;
}

image.png

把C语言代码翻译成可执行程序经历了两个过程:编译+链接。

  1. 编译又分为三个阶段:预处理 + 编译 + 汇编。
  2. 链接就只有一步。

预处理

gcc 源文件名 -E -o 生成的文件名

  1. 注释的删除。
  2. 头文件的展开。
  3. #define的替换。

image-20240326125042366

编译

gcc -S xxx.i或者gcc -S 源文件名

这两种方式都会生成一个.s的汇编文件。这个过程是把C语言代码翻译成了汇编代码。比如符号汇总。

image-20240326125606864

汇编:

gcc -c xxx.s或者gcc -c 源文件名:

最终都会生成一个.o的目标文件。在Windos环境下的目标文件是以.obj结尾。而在Linux环境下的目标文件是以.o结尾。目标文件中放的都是二进制指令。

这个阶段主要是把汇编代码翻译成了二进制指令。比如形成符号表。

image-20240326130917033

链接:

gcc xxx.o -o 文件名或者gcc 源文件 -o 生成的文件。

最终生成的文件也是二进制的。这个阶段主要做了:1、合并段表 2、 符号表的合并和符号表的重定位。

Linux环境下,使用gcc编译产生的目标文件xxx.o和可执行程序gcc -o 产生的文件,都是按照ELF这种文件格式存储的。image-20240326131837285

使用readelf 工具能够识别elf格式的文件。

使用readelf 文件名 -s即可查看符号表。

test.c

#include <stdio.h>

int g_val = 100;

extern int Add(int x , int y);

int main()
{
    int a = 1;
    int b = 1;
    int c = Add(a, b );
    printf("%d\n", c);
    return 0;
}

add.c

int Add(int x, int y)
{
    return x + y;
}

makefile

test:test.o add.o
	gcc -o test test.o add.o
test.o:test.c
	gcc -c test.c -o  test.o
add.o:add.c
	gcc -c add.c -o add.o

.PHONY:clean
clean:
	rm -f *.o test
image-20240326191320966

image-20240326191406845

链接之后的可执行程序的符号表

主要就是将多个目标文件中的符号表给合并在一起。

image-20240326191535871

这一块的更多知识请看<<程序员的自我修养>>!!!