參考《程式設計師的自我修養》
1.源**為什麼需要編譯鏈結
編譯類的語言(c 或者c++)寫出的源**機器是看不懂的,機器看懂的是可執行的**。而源**轉化成可執行**需要經過編譯和鏈結。
2.虛擬位址空間布局
生成完乙個可執行檔案a.out之後,我們需要把指令和資料從磁碟載入到虛擬位址空間中去。我們先看一下可執行檔案都載入到虛擬位址空間中哪些地方。
3.編譯鏈結步驟
看完虛擬記憶體布局之後,我們看一下編譯鏈結步驟
1)預編譯 2)編譯 3)彙編 4)鏈結
該圖**《程式設計師的自我修養》
我們看一下這個圖來講一下每個步驟都做了哪些事情
預編譯test.cpp檔案和相關標頭檔案預編譯成.i檔案
1)將所有#define刪除 ,並展開巨集定義
2)處理所有的預編譯指令 比如#if
3)處理#include 預編譯指令,將包含的標頭檔案插入到預編譯指令的位置
4)刪除注釋/* //
5)新增行號和檔案標識
6)保留#pragma編譯器指令
簡單來說就是展開需要展開的東西,刪除一些不要的東西。不檢查**
編譯1)詞法分析
2)語法分析
3)語義分析
4)**優化
5)彙總當前檔案的所有符號
彙編1).s檔案中有很多彙編指令,根據特定平台(windows linux)把它們轉化成特定的機器碼。
2)構建.obj格式
鏈結1)合併所有obj檔案的段,並調整段偏移和段長度, 合併符號表
2)符號解析完成後,分配到虛擬位址
3)鏈結核心 符號的重定位
看完概述之後,我們具體看一下這幾個問題
產生了obj組成格式是什麼
鏈結都做了什麼,可執行檔案為什麼能執行,從**執行
4.obj檔案格式
這個是.obj檔案格式
elf header 這個裡面儲存了整個檔案的基本屬性,版本,程式入口位址等,檔案頭大小,這樣也能往下偏移多少
主要儲存了乙個關鍵段表。段表儲存了段名,長度,檔案的偏移。這裡就包括了.bss段。要知道elf檔案中沒有單獨儲存.bss段
資訊是儲存在段表中的。.bss少了個資料,其實是gdata3,它是乙個弱符號,不知道會不會被其他目標檔案強符號代替。所以它是放在.comment段裡的。.symtab是符號表,裡面存放了符號。符號也是鏈結不可缺少的東西。
5.鏈結
符號:我們將函式統稱為符號,符號表就是把所有符號進行分類,函式名和變數名就是符號名。符號表記錄了目標檔案所用到的符號,每個符號都有乙個對應的符號值。對於變數和函式來說,就是它們的位址。
我們先看一段**
產生的main.o檔案中的,兩個外部引用的符號。*und* gdata 10 和 * und*sum 因為是分離編譯,當前檔案找不到。
函式和資料在編譯階段都是不分配位址的。資料位址是0位址,函式位址是-4位址
鏈結步驟
相似段合併
這個就是把所有目標檔案相同屬性(可讀,可寫,可讀可寫之類的)的段合併在一起,因為obj按4個位元組對齊,可執行檔案是按頁面4kb對齊。
在段表裡重新調整下,每個段的起始位址和段的偏移量。合併符號表。
符號解析
鏈結器在掃瞄完所有的輸入目標檔案後,所有這些未定義的符號都應該能夠在全域性符號表中找到,否則鏈結器報符號未定義錯誤。
給符號分配位址
重定位
程式執行
./a.out
1)建立虛擬位址空間到物理記憶體的對映建立頁目錄和頁表
2)載入**段和資料段
3)把可執行檔案的入口位址寫到cpu的pc暫存器裡面
編譯和鏈結
一般來說,無論是c c 首先要把原始檔編譯成中間 檔案,在windows下也就是 obj 檔案,unix下是 o 檔案,即 object file,這個動作叫做編譯 compile 然後再把大量的object file合成執行檔案,這個動作叫作鏈結 link 編譯時,編譯器需要的是語法的正確,函式與...
編譯和鏈結
在多道程式的實現中,要想使原始檔生成可執行檔案通常需要兩個步驟編譯和鏈結,其中編譯是指將原始檔編譯為中間 檔案,在linux中為 o檔案,其實質就是由c或c 等高階語言生成組合語言。生成可執行檔案,我們以編譯c c 為例,在windows中編譯生成的為.obj檔案,在linux unix中生成.o檔...
編譯和鏈結
平時,我們口頭上並不嚴格區分 編譯 compile 與 鏈結 link 這兩個專業術語。例如我們總是說 把那個 c檔案編譯成可執行檔案 寫成命令就是 gcc example.c 這個命令馬上給我們產生乙個 a.out 如果程式沒有錯誤的話 實際上,整個工作至少要分成四個階段,分別由不同的程式完成 第...