1代碼
點擊查看代碼
**sum.cpp**
int gdata=https://www.cnblogs.com/erichome/p/10;
int sum(int a,int b){
return a+b;
}
**main.cpp**
extern int gdata;
int sum(int , int );
int data=20;
int main(){
int a =gdata;
int b=data;
int ret=sum(a,b);
return 0;
}
1:編譯


需要關注的幾個點
1: .o 檔案的格式組成是什么樣子?
2: .exe 檔案的組成格式是什么樣子?
3: "所有.o檔案段的合并 符號表合并后,進行符號決議"
4: "符號的重定位(重定向)"
5: "符號表的輸出"=> "符號"
6: 符號什么時候分配虛擬地址?
預編譯
以#開頭的命令
除#pragma lib -> 鏈接階段處理
除#pragma link -> 鏈接階段處理
編譯
語法分析,語意分析,代碼優化 gcc g++
編譯后生成相應平臺的 匯編代碼 X86 和 AT&T
鏈接
鏈接所有 .o檔案和 靜態庫檔案




.o 檔案 主要是由以下組成
elf 檔案頭
.text
.data
.bss
.symbal
.section table
....
符號表中,在自己檔案中定義的,那就是符號定義,如果是參考外部的就 是 "UND"符號參考
符號表中的符號 都沒有分配地址,如下圖,所以編譯程序中,符號是不分配虛擬地址的,是在鏈接的時候分配


經過了上面的 預編譯-》編譯-》匯編 各個階段后 下面開始進入了 鏈接階段
main.o 檔案 sum.o 檔案
上面我們看到 .o 檔案是由各個段組成的,所以進入鏈接階段的時候
第一步 將各個.o 檔案 的各個段合并
main.o 檔案的 .text段 與 sum.o 檔案的 .text 段合并
main.o 檔案的 .data段 與 sum.o 檔案的 .data 段合并
main.o 檔案的 .bss段 與 sum.o 檔案的 .bss 段合并
main.o 檔案的 符號表 與 sum.o 檔案的符號表 段合并
第二步 非常重要的一點是 在main.o檔案的符號表與sum.o檔案的符號表段合并的時候,需要進行符號決議,
什么是"符號決議"?
所有對符號的參考,都要找到該符號定義的地方 “符號的參考” 即符號為 UND形式, 要找到該符號定義的地方即要找到該符號是在.text 段中定義還是在.data段中定義,
例如:聯結器發現main.o檔案的sum函式和gdata是UND形式的,那么聯結器會去其他檔案中找到sum和gdata的定義,如果沒找到,那么聯結器報錯"符號未定義",
如果聯結器找到了多個,那么聯結器也會報錯 “符號重定義”,所以在整個工程中,全域的名字是不能重名的,否則會產生沖突.
符號決議成功以后 就開始回給所有的符號分配地址
第三步 "符號重定向"
在符號決議成功以后并且給所有的符號分配地址后,需要繼續做 "符號重定向"
在我們指令編譯匯編生成.O檔案的時候,生成的指令中的符號的地址都是用0 代理,如下圖

現在我們需要將給符號分配好的地址 將指令中的這些0地址重新修正
現在我看下鏈接后的情況
符號表情況

指令情況

所以現在我們知道 “符號是在什么時候分配地址”, 在鏈接第一階段 符號決議成功后

可執行檔案 a.out 和 .O檔案的組成方式很像,但是還是有一點區別

在a.out 可執行檔案中 增加了 “program headers” , a.out 檔案中不是 所有的內容都會加載到記憶體中的,這個
"program headers"中指定了需要加載哪些到記憶體中

上圖中的 有兩個load 就是需要加載到記憶體中的.
下面我們再看看可執行程式加載到記憶體程序

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/514046.html
標籤:C++
