編譯連結過程

aron1992發表於2019-04-04

編譯連結過程

一個完整的編譯連結過程包含了以下步驟:

  • 預編譯
  • 編譯
  • 彙編
  • 連結

預編譯

預編譯的處理規則如下

  • 刪除#define,並且展開所有的巨集定義
  • 處理條件預編譯指令,#if/#ifdef/#elif/#else/#endif
  • 遞迴處理#include
  • 刪除註釋 // /註釋/
  • 新增行好和檔案標識,以便編譯時產生的錯誤或警告時能顯示行號
  • 保留#pragma編譯指令,編譯階段需要使用到
gcc -E hello.c -o hello.i
複製程式碼

編譯

編譯把預編譯生成的檔案進行一些列的詞法語法分析生成彙編程式碼

gcc -S hello.i -o hello.s
# 或者
gcc -S hello.c -o hello.s
複製程式碼

彙編

彙編是將彙編程式碼轉換成機器可以執行的指令(稱為目標檔案)

as hello.s -o hello.o
# 或者
gcc -c hello.c -o hello.o
gcc -c hello.s -o hello.o
複製程式碼

連結

把目標檔案連結最終生成可執行檔案

 /Users/aron/softwares/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ld" -demangle -lto_library /Users/aron/softwares/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/libLTO.dylib -no_deduplicate -dynamic -arch x86_64 -macosx_version_min 10.13.0 -o a.out /var/folders/nw/w87gck5x1mb57_rh9_g1cyd80000gn/T/hello-316635.o -lSystem /Users/aron/softwares/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/9.0.0/lib/darwin/libclang_rt.osx.a

 # 去掉路徑的簡化版本

 ld -demangle -lto_library libLTO.dylib -no_deduplicate -dynamic -arch x86_64 -macosx_version_min 10.13.0 -o a.out hello.o -lSystem libclang_rt.osx.a
複製程式碼

其中編譯過程是複雜度最高的本部分,包含了

  • 詞法分析
  • 語法分析
  • 語義分析
  • 中間語言生成

編譯過程展開

詞法分析

原始碼輸入到掃描器,分割為一系列的記號(Token),現成的工具lex
Yacc 與 Lex 快速入門 www.ibm.com/developerwo…

# lex-code-snipet.c 檔案內容
# arrar[index] = (index + 4) * (2 + 6)

lex -t lex-code-snipet.c > lex-code-result.yyc
複製程式碼

語法分析

把語法分析的結果轉換為表示式樹
現成的工具yacc
Yacc 與 Lex 快速入門 www.ibm.com/developerwo…

語法分析
語法分析

語義分析

分析語法樹的表示式是否合法 靜態語義分析:

  • 浮點型的表示式賦值給整形表示式,隱含了轉換過程
  • 浮點型的表示式賦值給指標,語義分析會檢測出型別不匹配編譯器將會報錯 
    語義分析
    語義分析

中間語言生成

原始碼級別會有一個優化的過程(Source Code optimizer),優化後的語法樹如下 

中間語言生成
中間語言生成

原始碼優化器往往將整個語法樹轉換成中間程式碼,他是語法樹的順序標識,和目的碼非常接近,與機器的執行環境無關,不包含資料的尺寸、變數的地址和暫存器名稱,常見的中間程式碼是三地址碼(Three-Address Code)

中間程式碼是的編譯器可以被分為編譯前端和編譯後端

  • 編譯前端生成和目標機器無關的中間程式碼
  • 編譯後端把中間程式碼轉換為目標機器的程式碼

模組拼裝-靜態連結

連結的主要內容就是把各個模組之間相互引用的部分都處理好,使得各個模組之間能夠正確的銜接,連結過程包含:

  • 地址空間分配
  • 符號決議
  • 重定位

模組拼裝-靜態連結
模組拼裝-靜態連結

連結過程解析:

連結過程解析
連結過程解析

  • main.o 依賴 func.o foo函式
  • main.o 和 func.o 分開獨立編譯
  • main.o 預留func.o foo符號的地址
  • 拼接main.o 和 func.o 是修正main.o 中預留的 foo符號的地址,這個過程稱為重定位(Relocation)

這個地址修正的過程稱為重定位(Relocation),每個要被修正的地方叫重定位入口(Relocation Entry)

相關文章