【Linux】編譯鏈接原理

通常的開發環境都是集成開發環境(IDE):編譯鏈接一步完成(構建) gcc main.c

 

被隱藏的過程:

預處理(預編譯)

主要處理以#開頭的預編譯指令

  1. 將所有的“#define”刪除,並展開所有的宏定義
  2. 處理所有條件預編譯指令,如“#if”、“#ifdef”、“#elif”、“#else”、“#endif”
  3. 處理“#include”預編譯指令,將被包含的文件插入到該預編譯指令的位置。注意,這個過程是遞歸進行的(被包含的文件可能還包含其他文件)
  4. 刪除所有註釋“//”和“/**/”
  5. 添加行號和文件名標識,比如#2“main.c”2,以便於編譯時編譯器產生調試用的行號信息及用於編譯時產生編譯錯誤或警告是能夠顯示行號
  6. 保留所有的“#pragma”編譯器指令。因爲編譯器需要他們

生成“.i”文件(不包含宏定義)

 

命令:gcc -E main.c -o main.i

          

注意:若無法判斷宏定義是否正確或頭文件包含是否正確。查看預編譯生成的“.i”文件。

 

編譯

把預處理完的文件進行一系列詞法分析語法分析語義分析及優化後生產相應的彙編代碼文件

  1. 掃描。掃描器詞法分析。 有限狀態機 字符序列分割 記號 標識符、符號表 字面量、文字表

          記號:關鍵詞、標識符、字面量(數字、字符串)、特殊符號(+,-)

          程序lex可以實現詞法分析。改變詞法規則

  1. 語法分析。產生語法樹(以表達式爲節點的樹),採用上下文無關語法的分析手段

工具yacc可以實現語法分析。改變語法規則

  1. 語義分析。分析靜態語義(編譯期可以確定的語義)。語法樹的表達式被標識類型,隱式轉換插入相應的轉換節點。

申明和類型的匹配,類型的轉換

  1. 源代碼優化。生成中間語言。將語法樹轉換成中間代碼。

   類型:三碼地址,P-代碼(P-Code)

  1. 目標代碼生成。將中間代碼轉換爲目標機器代碼
  2. 目標代碼和優化。選擇適合的尋址方式、使用位移來替換乘法運算(基址比例變址尋址的lea指令,後mov指令完成賦值操作)、刪除多餘的指令

 

生成“.s”文件

命令:gcc -S main.i -o main.s

 

注意:實際上gcc這個命令只是後臺程序的包裝,他會根據不同的參數去調用預編譯編譯程序cll、彙編器as、鏈接器ld

 

彙編

彙編代碼轉變機器可以執行的指令。每一條彙編語言幾乎都對應一條機器指令。(根據彙編指令和機器指令的對照表一一翻譯)

 

命令:gcc -c main.s -o mian.o

生成“.o”文件(目標文件)

 

鏈接 (靜態鏈接)

把每個源代碼模塊獨立編譯,然後按須要將他們“組裝”起來。即把一些指令對其他符號地址的引用加以修正。

  1. 地址和空間分配
  2. 符號決議
  3. 重定位。地址修正的過程。每個要被修正的地方叫一個重定位入口。

 

命令:gcc main.o -o main

目標文件和庫(常見庫:運行時庫)鏈接生成可執行文件.out

 

補充:
鏈接過程的必要性:連接之前的過程都是以單個源文件爲單元進行操作,但是項目往往是由多文件合成,單個文件如何去訪問其他文件,這就由鏈接來完成。

鏈接過程解決了前幾步驟的遺留問題。1、弱符號(注意,弱符號只存在於C語言中),2、符號表,外部符號,3、指令段、虛假的偏移和地址。

鏈接完成的功能:1、合併段和符號表,2、符號解析(*UND*區域),3、分配地址和空間,4、符號的重定位

連接完成的文件並不是可直接運行的,缺乏CPU的環境,還需要經過運行階段配置好環境纔可以直接運行。

運行階段:

1、建立虛擬地址空間和物理內存的映射關係。(配置PCB)

2、j加載指令和數據。

3、入口地址寫入下一行指令寄存器。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章