編譯和鏈結

2021-08-21 09:16:31 字數 2717 閱讀 1326

參考《程式設計師的自我修養》

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 如果程式沒有錯誤的話 實際上,整個工作至少要分成四個階段,分別由不同的程式完成 第...