gcc編譯鏈接原理及使用

2020-08-11 17:32:28

1、gcc 和 arm-linux-gcc的常用選項
gcc 的使用方法:
gcc 【選項】 檔名
gcc常用選項:

-v:檢視gcc 編譯器的版本,顯示gcc執行時的詳細過程

-o < file > Place the output into < file >

指定輸出檔名爲file,這個名稱不能跟原始檔名同名

-E        Preprocess only; do not compile, assemble or link

只預處理,不會編譯、彙編、鏈接

-S       Compile only; do not assemble or link

只編譯,不會彙編、鏈接

-c        Compile and assemble, but do not link

//=======================================

gcc -v: 檢視 gcc 編譯器的版本

方式1:

gcc hello.c 輸出一個 a.out,然後 ./a.out 來執行該應用程式

gcc -o hello hello.c 輸出hello ,然後 ./hello 來執行該應用程式。

方式2:

gcc -E -o hello.i hello.c

gcc -S -o hello.s hello.i

gcc -c -o hello.o hello.s

gcc -o hello hello.o

.o: object file (OBJ檔案)

小結:

1)輸入檔案的後綴名和選項共同決定gcc到底執行哪些操作。
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2)在編譯過程中,除非使用了-E、-S、-C選項(或者編譯出錯阻止了完整的編譯過程)

否則最後的步驟都是鏈接。

方式3:

gcc -c -o hello.o hello.s

gcc -o hello hello.o

gcc會對.c檔案預設進行預處理操作, -c再來指明瞭編譯、彙編,從而得到.o檔案

再通過gcc -o hello hello.o 將.o檔案進行鏈接,得到可執行應用程式。

鏈接就是將彙編生成的OBJ檔案、系統庫的OBJ檔案、庫檔案鏈接起來,

最終生成可以在特定平臺執行的可執行程式。

crt1.o、crti.o、crtbegin.o、crtend.o、crtn.o是gcc加入的系統標準啓動檔案,

對於一般應用程式,這些啓動是必需的。

-lc:鏈接libc庫檔案,其中libc庫檔案中就實現了printf等函數。

gcc -v -nostdlib -o hello hello.o 會提示因爲沒有鏈接系統標準啓動檔案和標準庫檔案,而鏈接失敗。

這個-nostdlib選項常用於裸機 /bootloader 、linux 內核等程式,因爲它們不需要啓動檔案、標準庫檔案。

一般應用程式才需要系統標準啓動檔案和標準庫檔案。、

裸機 /BootLoader、linux內核燈程式不需要啓動檔案、標準庫檔案。

動態鏈接使用動態鏈接庫進行鏈接,生成的程式在執行的時候需要載入所需的動態庫才能 纔能巡行。

動態鏈接生成的程式體積較小,但是必須依賴所需的動態庫,否則無法執行。

靜態鏈接使用靜態庫進行鏈接,生成的程式包含程式執行所需要的全部庫,可以直接執行,

不過靜態鏈接生成的程式比較大。

gcc -c -o hello.o hello.c

gcc -o hello_shared hello.o

gcc -static -o hello_static hello.o

2、舉例說明編譯鏈接過程
一個C/C++檔案要經過預處理(preprocessing)、編譯(compilation)、彙編(assembly)、和連線(linking)才能 纔能變成可執行檔案。
以下列程式爲例,追層來分析編譯過程。
hello.c:

#include <stdio.h>

#define   MAX  20 
#define   MIN  10 

#define  _DEBUG
#define   SetBit(x)  (1<<x) 

int main(int argc, char* argv[])
{
    printf("Hello World \n");
    printf("MAX = %d,MIN = %d,MAX + MIN = %d\n",MAX,MIN,MAX + MIN); 

#ifdef _DEBUG
    printf("SetBit(5) = %d,SetBit(6) = %d\n",SetBit(5),SetBit(6));
    printf("SetBit( SetBit(2) ) = %d\n",SetBit( SetBit(2) ));       
#endif    
    return 0;
}
```c

① 預處理:
gcc -E -o hello.i hello.c
![在這裏插入圖片描述](https://img-blog.csdnimg.cn/202008111727113.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQzNzM5NTgy,size_16,color_FFFFFF,t_70)預處理就是將要包含(include)的檔案插入原檔案中、將宏定義展開、根據條件編譯命令選擇要使用的程式碼,最後將這些程式碼輸出到一個「.i」檔案中等待進一步處理。 
② 編譯:
gcc -S -o hello.s hello.i
![這裏寫圖片描述](https://img-blog.csdnimg.cn/20200811172747370.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQzNzM5NTgy,size_16,color_FFFFFF,t_70)
編譯就是把C/C++程式碼(比如上面的」.i」檔案)「翻譯」成彙編程式碼。
③ 彙編:
gcc -c -o hello.o hello.s
![這裏寫圖片描述](https://img-blog.csdnimg.cn/20200811172812503.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQzNzM5NTgy,size_16,color_FFFFFF,t_70)
.o:object file(OBJ檔案) 這裏表現爲二進制目標檔案: 
![這裏寫圖片描述](https://img-blog.csdnimg.cn/20200811172825761.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQzNzM5NTgy,size_16,color_FFFFFF,t_70)
彙編就是將第二步輸出的彙編程式碼翻譯成符合一定格式的機器程式碼,在Linux系統上一般表現位ELF目標檔案(OBJ檔案)。
④ 鏈接:
gcc -o hello hello.o
![這裏寫圖片描述](https://img-blog.csdnimg.cn/20200811172846984.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQzNzM5NTgy,size_16,color_FFFFFF,t_70)
鏈接就是將彙編生成的OBJ檔案、系統庫的OBJ檔案、庫檔案鏈接起來,最終生成可以在特定平臺執行的可執行程式。 
總結:在編譯過程中。除非使用了」-c」,「-S」,或」-E」選項(或者編譯錯誤阻止了完整的過程),否則統一完整鏈接步驟。
譬如:gcc hello.c 和gcc -o hello hello.c都已經完成鏈接操作。 
![這裏寫圖片描述](https://img-blog.csdnimg.cn/20200811172902994.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQzNzM5NTgy,size_16,color_FFFFFF,t_70)
又如:gcc -c -o hello.o hello.c 
![這裏寫圖片描述](https://img-blog.csdnimg.cn/2020081117291691.png)


鏈接原理:
gcc -c -o hello.o hello.c 不作最後一步鏈接,得到hello.o二進制OBJ檔案
gcc -v -o hello hello.o 我們來看一樣鏈接過程是怎樣的:

crt1.o、crti.o、crtbegin.o、crtend.o、crtn.o是gcc加入的系統標準啓動檔案,對於一般應用程式,這些啓動是必需的。
-lc:鏈接libc庫檔案,其中libc庫檔案中就實現了printf等函數。
① 動態鏈接:動態鏈接使用動態鏈接庫進行鏈接,生成的程式在執行的時候需要載入所需的動態庫才能 纔能執行。  動態鏈接生成的程式體積較小,但是必須依賴所需的動態庫,否則無法執行。
預設使用動態鏈接:gcc -o hello_shared hello.o

![這裏寫圖片描述](https://img-blog.csdnimg.cn/20200811172939352.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQzNzM5NTgy,size_16,color_FFFFFF,t_70)


② 靜態鏈接:靜態鏈接使用靜態庫進行鏈接,生成的程式包含程式執行所需要的全部庫,可以直接執行,不過靜態鏈接生成的程式體積較大。
gcc -static -o hello_static hello.o

![這裏寫圖片描述](https://img-blog.csdnimg.cn/20200811172950202.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQzNzM5NTgy,size_16,color_FFFFFF,t_70)


③ -nostartfiles
不鏈接系統標準啓動檔案,而標準庫檔案仍然正常使用: 
gcc -v -nostartfiles -o hello hello.o

![這裏寫圖片描述](https://img-blog.csdnimg.cn/2020081117300191.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQzNzM5NTgy,size_16,color_FFFFFF,t_70)


④ -nostdlib(最常用)
不鏈接系統標準啓動檔案和標準庫檔案: 
gcc -v -nostdlib -o hello hello.o

![這裏寫圖片描述](https://img-blog.csdnimg.cn/20200811173014702.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQzNzM5NTgy,size_16,color_FFFFFF,t_70)

- 會提示因爲沒有鏈接系統標準啓動檔案和標準庫檔案,而鏈接失敗。 
- 這個-nostdlib選項常用於裸機/bootloader、linux內核等程式,因爲它們不需要啓動檔案、標準庫檔案。