1、將c原始檔編譯成可執行檔案的過程:
1)編譯器驅動程式(compiler driver)執行預處理器,將c原始檔翻譯成乙個中間檔案 .i
2)編譯器驅動程式(compiler driver)執行編譯器,將.i 檔案翻譯成乙個組合語言檔案.s
3)編譯器驅動程式(compiler driver)執行彙編器,將.s檔案翻譯成乙個可重定位目標檔案.o
4)編譯器驅動程式(compiler driver)執行聯結器,將多個.o檔案以及一些必要的系統目標檔案組合成乙個可執行目標檔案。
2、符號(symbol)在匯程式設計序中代表乙個位址,可以用在彙編指令中,匯程式設計序經過彙編器的處理之後,所有的符號都被替換成它所代表的位址值。在 c 語言中我們通過變數名訪問乙個變數,其實就是讀寫某個位址的記憶體單元,我們通過函式名呼叫乙個函式,其實就是跳轉到該函式第一條指令所在的位址,所以變數名和函式名都是符號,本質上是代表記憶體位址的。
如果乙個符號沒有用.globl 宣告,就表示這個符號不會被鏈結器用到,只在當前匯程式設計序的內部使用。
匯程式設計序中_start符號定義後面的第一條指令的位址作為這個符號所代表的位址。
3、可重定位目標檔案的構成
1).text **段
2).rodata 唯讀資料段
3).data段 :已初始化的全域性變數和靜態變數
4)comman : 未初始化的全域性變數
5).bss : 未初始化的靜態變數,或者初始化為0的全域性變數或靜態變數。 comman裡的符號是弱符號,編譯器無法確定其符號定義是哪乙個,而.bss裡的符號的符號定義是唯一的。因為初始化為0的全域性變數是強符號,而靜態變數是當前模組私有的,它們的符號定義是確定的。
6)符號表。 包含程式定義和引用的符號的資訊。這些符號包括函式、全域性變數以及靜態變數。在當前模組定義的函式和全域性變數在符號表中的符號型別是全域性,在當前模組中引用的符號在符號表中的符號型別是外部,在當前模組中的靜態變數和靜態函式在符號表中的符號型別是區域性。符號表用於鏈結器進行符號解析(決議)。符號解析是在鏈結過程中把每乙個符號引用與乙個確定的符號定義關聯起來。符號表中符號的儲存空間是靜態分配的,即它們的虛擬記憶體位址是在編譯鏈結後確定的,其虛擬記憶體位址是固定不變的。 而位於**段中的變數,在函式呼叫時自動在棧上分配儲存空間,其記憶體位址與ebp暫存器有關。ebp暫存器儲存著指向棧幀底部的指標。
7).rel.text: 記錄當前目標模組引用的外部函式的資訊。編譯器不能確定引用的外部函式的虛擬記憶體位址,編譯器將其位址設定為空,並將引用的外部函式記錄在.rel.text段中。在鏈結階段,鏈結器會重定位這些引用符號的虛擬記憶體位址。
8).rel.data:記錄當前目標模組引用的外部變數的資訊。
4、鏈結器解析多重定義的全域性符號
函式(這裡指的是函式定義,因為不可能有重複定義的函式)和已初始化的全域性變數是強符號,未初始化的全域性變數是弱符號。
根據強弱符號的定義, linux 鏈結器使用下面的規則來處理多重定義的符號名
* 規則 1: 不允許有多個同名的強符號。
* 規則 2: 如果有乙個強符號和多個弱符號同名,那麼選擇強符號。
* 規則 3: 如果有多個弱符號同名,那麼從這些弱符號中任意選擇乙個。
C語言編輯鏈結
庫函式 library files 庫函式就是函式的倉庫,它們都經過編譯,重用性不錯。通常,庫函式相互合作,來完成特定的任務。比如操控螢幕的庫函式 cursers和ncursers庫函式 資料庫讀取庫函式 dbm庫函式 等。系統呼叫的標準庫函式一般位於 lib以及 usr lib。c編譯器 精確點說...
C語言筆記之標頭檔案與鏈結(一)
雖然一直在用 include命令包含標頭檔案,但其實一致不太明白標頭檔案的原理。今天就研究了一下。首先,在大型專案中,僅僅乙個原始檔是不夠的,巨大的 量需要分別放在幾個檔案中,當然分開存放的更主要的目的是便於模組化。我們把 按照不同的功能或作用分隔存放在不同的檔案中,那麼當其中乙個功能有改動時,只需...
C語言筆記 模組化編譯鏈結 MinGW
在我們入門c c 語言時,編寫的大部分c語言程式都只包含乙個原始檔,沒有將 分散到多個模組中,這裡的模組指的時不同的原始檔,每個檔案稱為乙個可編譯單元,可以分別編譯。在c語言中,我們可以將乙個.c檔案稱為乙個模組 module 所謂模組化開發,是指乙個程式包含了多個原始檔 c 檔案 以及標頭檔案 h...