1. 程式人生 > 其它 >Gcc 編譯過程

Gcc 編譯過程

gcc編譯

gcc hello.c -o hello

hello.c 預處理——>hello.i 編譯——>hello.s 彙編——>hello.o 連結——>hello

示例

hello.c

#include<stdio.h>

#define TEST_STRING "hello world!"

int main()
{
    printf("%s\n", TEST_STRING);
    return 0;
}

預處理

gcc -E hello.c -o hello.i

預編譯過程主要處理那些原始碼檔案中以“#”開始的預編譯指令,比如“#include”、“#define”等。

主要處理規則有:

  1. 將所有的“#define”刪除,並且展開所有的巨集定義。
  2. 處理所有的條件預編譯指令,比如:“#if”、“#ifdef”、“#elif”、“#else”、“#endif”
  3. 處理“include”預編譯指令,將被包含的檔案插入到該預編譯指令的位置,該過程是遞迴進行的。
  4. 刪除所有的註釋“//”和“/**/”。
  5. 新增行號和檔名標識,以便於編譯時編譯器產生除錯用的行號資訊及用於編譯時產出錯誤或警告時能夠顯示行號。
  6. 保留所有的#pragma編譯器指令,因為編譯器需要使用它們。

hello.i 檔案末尾內容如下,標頭檔案已展開,並將 TEST_STRING 替換為 hello world

/**
 * bala bala 省略部分
 */
# 940 "/usr/include/stdio.h" 3 4

# 9 "hello.c" 2

int main()
{
    printf("%s\n", "hello world!");
    return 0;
}

編譯

編譯過程就是把預處理完的檔案進行一系列詞法分析、語法分析、語義分析及優化後生成相應的彙編程式碼檔案。

gcc -S hello.i -o hello.s

編譯生成彙編程式碼 hello.s

	.file	"hello.c"
	.section	.rodata
.LC0:
	.string	"hello world!"
	.text
	.globl	main
	.type	main, @function
main:
.LFB0:
	.cfi_startproc
	pushl	%ebp
	.cfi_def_cfa_offset 8
	.cfi_offset 5, -8
	movl	%esp, %ebp
	.cfi_def_cfa_register 5
	andl	$-16, %esp
	subl	$16, %esp
	movl	$.LC0, (%esp)
	call	puts
	movl	$0, %eax
	leave
	.cfi_restore 5
	.cfi_def_cfa 4, 4
	ret
	.cfi_endproc
.LFE0:
	.size	main, .-main
	.ident	"GCC: (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3"
	.section	.note.GNU-stack,"",@progbits

彙編

彙編器是將彙編程式碼轉變成機器可以執行的指令,每一個彙編語句幾乎都對應一條機器指令。

彙編後輸出 目標檔案,還不可執行!

gcc -c hello.s -o hello.o

連結

連結的過程主要包括:地址和空間分配、符號決議和重定位

主要工作是把一些指令對其它符號地址的引用加以修正,把各個模組之間的相互引用的部分處理好。
比如 A 模組用到 B 模組的某個函式,但是在編譯 A 模組時,並不知道 B 模組中的函式的地址,所以需要連結器來處理。

符號決議:

確保所有目標檔案中的符號引用都有唯一的定義

重定位:

連結時,確定變數或函式的地址後,把指令的目標地址修改成確定後的地址,這個過程就是重定位

gcc hello.o -o hello