第七章:GCC的编译过程

我们知道gcc是一个强大的编译器,很多Linux下的GNU工具都是用C语言写的,并且用gcc编译的,那么gcc的编译过程是怎样的呢?

1. 首先是源文件经过预加载变成了.i结尾的文件,可以通过-E这个参数来生成这个中间文件,这里主要是把一些include的头文件和一些宏定义,放到源文件中。
2. 从预加载的文件经过编译就会变成汇编语言的文件,这一步可以通过-S这个参数来生成这个中间文件 。
3. 从汇编语言的文件通过汇编,就会变成目标代码.o的文件,这一步可以通过-C这个参数来生成这个中间文件 。
4. 最后经过链接,生成最终的可执行文件 。

接下来我们通过一个例子来说明下:
我新建了一个文件:

/* main.c */
/* $begin main */
int sum(int *a, int n);

int array[2] = {1, 2};

int main() 
{
    int val = sum(array, 2);
    return val;
}
/* $end main */

现在我们开始尝试将这个文件在Linux环境下,利用GCC编译生成可执行文件:
我先生成.i结尾的预加载文件
gcc -E main.c -o main.i 第七章:GCC的编译过程_第1张图片

# 1 "main.c"
# 1 ""
# 1 ""
# 31 ""
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 32 "" 2
# 1 "main.c"


int sum(int *a, int n);

int array[2] = {1, 2};

int main()
{
    int val = sum(array, 2);
    return val;
}

大家会发现这个文件的内容非常多,但是最后是我们本来的代码,上面的代码都是头文件和一些宏的内容,全加载进来了 。
下面我们通过编译生成汇编文件.s

gcc -S main.i -o main.s

	.file	"main.c"
	.text
	.globl	array
	.data
	.align 8
	.type	array, @object
	.size	array, 8
array:
	.long	1
	.long	2
	.text
	.globl	main
	.type	main, @function
main:
.LFB0:
	.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	$2, %esi
	leaq	array(%rip), %rdi
	call	sum@PLT
	movl	%eax, -4(%rbp)
	movl	-4(%rbp), %eax
	leave
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE0:
	.size	main, .-main
	.ident	"GCC: (Ubuntu 7.4.0-1ubuntu1~18.04.1) 7.4.0"
	.section	.note.GNU-stack,"",@progbits

看到了我们熟悉的汇编语言
我们继续,经过汇编器,生成.o的目标文件

gcc -c main.s -o main.o
objdump main.o -D|more
第七章:GCC的编译过程_第2张图片
最后通过链接器,生成可执行文件
gcc main.o -o mian
然后执行
第七章:GCC的编译过程_第3张图片
makefile.txt:

CC = gcc
CFLAGS = -Wall -Og

PROGS = global \
	mismatch \
	prog\
	prog2c\
	prog2l\
	prog2r\
	static-local.o \
	symbols.o \

# Do nothing
all: $(PROGS)

static-local.o: static-local.c
	$(CC) $(CFLAGS) -c static-local.c

# example program 1 
prog: main.o sum.o
	$(CC) $(CFLAGS) -o prog main.o sum.o
	$(CC) $(CFLAGS) -S main.c
	objdump -dx main.o          > main-relo.d
	objdump -dx -j .data main.o > maindata-relo.d
	readelf -s main.o	    > mainsym.d	
	objdump -dx sum.o           > sum-relo.d
	objdump -dx -j .data sum.o  > sumdata-relo.d
	objdump -dx prog            > prog-exe.d
	objdump -dx -j .data prog   > progdata-exe.d

# Type mismatch example
mismatch: mismatch-main.c mismatch-variable.c
	$(CC) $(CFLAGS) -o mismatch mismatch-main.c mismatch-variable.c

# Use of extern specifier
global: global-c1.c global-c2.c global.h
	$(CC) $(CFLAGS) -o global global-c1.c global-c2.c

# Static library linked at compile time
prog2c: main2.o addvec.o multvec.o
	$(CC) $(CFLAGS) -c main2.c addvec.c multvec.c
	ar rcs libvector.a addvec.o multvec.o
	$(CC) $(CFLAGS) -static -o prog2c main2.o -L. -lvector
#       Alternative for above line
#	$(CC) $(CFLAGS) -static -o prog2c main2.o libvector.a
	objdump -d prog2c > prog2c-exe.d


# Shared library linked at load-time
prog2l: main2.o libvector.so
	$(CC) $(CFLAGS) -o prog2l main2.o ./libvector.so
	objdump -dx main2.o          > main2-relo.d
	objdump -dx prog2l > prog2l-exe.d
	objdump -xs -j .data -j .got prog2l > prog2ldata-exe.d

# Shared library linked at run time
prog2r: dll.c
	$(CC) $(CFLAGS) -rdynamic -o prog2r dll.c -ldl

libvector.so: addvec.c multvec.c	
	$(CC) $(CFLAGS) -shared -fpic -o libvector.so addvec.c multvec.c
	objdump -xd libvector.so > libvector-relo.d
	objdump -xRr -j .data -j .got -j .got.plt -j rela.dyn -j rela.plt libvector.so > libvectordata-relo.d

clean:
	rm -f $(PROGS) *.o *~ *.d *.so *.a *.s

至此,可执行文件就生成成功了,让我们来看一下执行的结果。
在这里插入图片描述

你可能感兴趣的:(第七章:GCC的编译过程)