1. 程式人生 > >c++ 編譯連結執行原理及虛擬地址空間佈局

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:全域性

強符號:全域性已初始化的變數

弱符號:全域性未初始化的變數

強弱符號的規則:

  1. 兩個強符號重定義錯誤
  2. 一強一弱 選強符號
  3. 兩個弱符號 選資料較大的

.bss弱符號在彙編時 看不到本檔案以外存放的強符號 所以先放在common塊 連結完後當所有檔案都看見後 若有強符號 則用強符號地址 此時弱符號也不再common塊 在合理的位置了

 

引入外部符號放在und符號表中找不到 .test段對於外部變數用虛假的0地址代替真實地址針對函式用-4偏移 此時彙編完成 弱符號以及外部符號要在連結中處理

 

Ld連結器只關心全域性符號g,80%錯誤發生在符號解析 main函式是預設入口地址 連結完後弱符號都放入.bss段

Call:近址相對偏移位移 所呼叫的函式入口地址為下一行指令地址+call所對應的偏移 執行過程中不需要common塊 只需指令和資料段