1. 程式人生 > >程式的編譯,裝載與連結過程

程式的編譯,裝載與連結過程

程式編譯四個過程:

  • 預處理(Prepressing):原始碼和相關的標頭檔案被預編譯器cpp預編譯為一個 .i 檔案(#define ,#include,#if,刪除註釋行)
  • 編譯(Compilation):將預處理之後的檔案進行一系列詞法分析,語法分析,語義分析及優化後生產相應的彙編程式碼檔案
  • 彙編(Assembly):將彙編程式碼轉化為機器可以執行的機器程式碼,例如使用gcc命令從C原始碼檔案開始,經過預編譯,編譯和彙編直接輸出目標檔案(Object File)(還沒有經過連結的過程)
  • 連結(Linking): 目標檔案和庫一起連結形成最中的可執行檔案
    • 經過掃描,語法分析,語義分析,原始碼優化,程式碼生成和目的碼優化,編譯器忙活了這麼多個步驟以後,原始碼終於可以被編譯成了目的碼。但是這個目的碼有一個問題:index和array的地址還沒有確定。
    • 如果我們要把目的碼使用匯編器編譯成能夠執行的指令,那麼index和array的地址應該從哪裡得到呢還有and so on?事實上,定義其他模組的全域性變數和函式在最終執行時的絕對地址都是要在最終連結的時候才能確定。
    • 所以現代編譯器可以將一個原始碼檔案編譯成一個未連結的目標檔案,然後由連結器最終確定這些目標檔案連結起來形成可執行檔案。

連結器為目標檔案分配地址和空間

1.第一步:空間與地址分配(分析這兩個步驟中連結器的工作過程,在第一步的掃描和空間分配階段,連結器按照前面介紹的空間分配方法進行分配,這時輸入檔案中各個段在連結後的虛擬地址就已經確定,比如.text段的起始位置和.data的起始位置) 2.第二步:符號解析與重定位

重定位:我們在程式模組main.C中使用另外一個模組func.c中的函式foo()我們在每一處呼叫foo的時候都必須確切知道foo這個函式的地址,所以它暫時把這些呼叫foo的指令的目標地址擱置,等待最後連結的時候由連結器去將這些指令的目標地址修正,則填入正確的foo函式地址。當func.c模組重新編譯,foo函式位置地址有可能改變,那麼我們在main.c中所有使用到foo的地址的指令將要全部重新調整。 ‘’’ ‘’’

程序的建立 1.建立一個獨立的虛擬地址空間(虛擬空間由一組頁對映函式將虛擬空間的各個頁對映至相應的物理空間) 2.讀取可執行檔案頭,並且建立虛擬空間與可執行檔案的對映關係 3.將CPU的指令暫存器設定成可執行檔案(映像檔案)的入口地址,啟動執行 頁錯誤