c++ 編譯連結執行原理及虛擬地址空間佈局
當我們寫好.c/.cpp檔案時 此時檔案還不能執行 因為他要經過以下的四步才可以執行
.c/.cpp(生成.i) 編譯(生成.s) 彙編(生成.o) 連結(生成.exe)
1.#define巨集替換 1.詞法分析 指令翻譯成二進位制 1.合併段和符號表
2.#include 遞迴展開 2.語法分析 2.符號解析
3.處理#if #endif等 3.語義分析 3.分配地址空間
4.新增行號和檔案表示 4.程式碼優化 4.符號重定位
5.刪除註釋
6.保留#pragma
此時的檔案已經成.exe的ELF格式了 然而現在檔案還是不可以執行的 因為此時檔案還在磁碟上 檔案要載入到記憶體上才可執行 於是還有這以下三步檔案才可以執行
執行.exe(./main)
1.建立虛擬地址空間到實體記憶體的對映(建立核心對映結構體)
2.載入指令和資料到記憶體中
3.程式的入口地址寫入pc(儲存下一行指令的地址)暫存器中
虛擬地址空間佈局對映到真是實體記憶體如下圖:
此時就算虛擬空地址(本身並不存在)被惡意修改 真實實體記憶體的大小是固定的 也不會影響到其他程序了
注意:資料一般都放在堆和棧 堆和棧都有隨機的偏移 是因為防止被惡意修改 堆疊只有在執行的過程才有
DMA是不過暫存器的 直接通過資料匯流排得到資料
int * p = NULL;
*P = 20;
此段程式碼錯誤 因為NULL在保留區 保留區不允許訪問 而解引用則訪問了保留區
.data段放的是已初始化且初始化不為0的資料
.bss段放的是未初始化或初始化為0的資料
為什麼兩個段保留資料?節省的是哪塊空間?
.bss為了節省空間 .bss節省的是檔案空間(其實.bss段在檔案中不存在) 只是從section header 把.bss資訊儲存了下來
符號表
l:本檔案 g:全域性
強符號:全域性已初始化的變數
弱符號:全域性未初始化的變數
強弱符號的規則:
- 兩個強符號重定義錯誤
- 一強一弱 選強符號
- 兩個弱符號 選資料較大的
.bss弱符號在彙編時 看不到本檔案以外存放的強符號 所以先放在common塊 連結完後當所有檔案都看見後 若有強符號 則用強符號地址 此時弱符號也不再common塊 在合理的位置了
引入外部符號放在und符號表中找不到 .test段對於外部變數用虛假的0地址代替真實地址針對函式用-4偏移 此時彙編完成 弱符號以及外部符號要在連結中處理
Ld連結器只關心全域性符號g,80%錯誤發生在符號解析 main函式是預設入口地址 連結完後弱符號都放入.bss段
Call:近址相對偏移位移 所呼叫的函式入口地址為下一行指令地址+call所對應的偏移 執行過程中不需要common塊 只需指令和資料段