鏈結之符號解析

2021-09-29 06:50:00 字數 1458 閱讀 8857

之前寫過一篇部落格講述了鏈結的來由:

這篇部落格則打算講講鏈結的核心工作:符號解析和重定位。看這篇部落格之前可能需要一點基礎,大家可以先看看我上面的鏈結的文章,再來看這篇。

什麼是符號解析?

符號解析分為區域性符號解析和全域性符號解析,區域性的不用說。全域性符號解析因為多個目標檔案可能會定義相同的名字的全域性符號。此時,鏈結器有兩種可能處理方法,報錯,和選出乙個定義並拋棄其它定義。

// 檔案 main1.c

int a =5;

// 強符號,已初始化p1(

)// 強符號,函式

// 檔案 main.c

int a;

// 弱符號,未初始化p2(

)// 強符號,函式

這些規則很好理解,但是對於第三個規則有乙個很隱蔽的坑值得了解一下

// 檔案 p1.c

int x;

int y;p1(

)// -----------------------------------------

// 檔案 p2.c

double x;p2(

)

我們看這個例子,這裡 p1 和 p2 中定義的變數都是弱符號,我們對 p2 中的 x 進行寫入時,會影響到 p1 中的 y。因為兩個 x 實際上引用的是同乙個位址,而 double 的位元組數是 int 的兩倍。

再結合 **全域性變數、靜態全域性變數和靜態區域性變數都存放在記憶體的靜態儲存區域,區域性變數存放在記憶體的棧區。**靜態儲存區向下生長,棧向上生長,堆向下生長

我們就易見x,會影響到y,正好x也是y的兩倍大小。

講這麼多其實落到實處就是盡量避免使用全域性變數,但是有時這是不可避免的所以也有措施:

鏈結器完成了符號解析這一步,其實就給所有引用的符號找到了相應的定義。鏈結器就知道它輸入目標模組中的**節和資料節的確切大小。現在就可以開始重定位步驟了。重定位由兩步組成:

1、重定位節和符號定義:在這一步中,鏈結器將所有相同型別的節合併為同一型別的新的節。例如,來自所有輸入模組的 .data 節被全部合併成乙個節,這個節稱為輸出的可執行目標檔案的 .data節。然後,鏈結器將執行時記憶體位址賦給新的聚合節,賦給輸入模組定義的每個節,以及賦給輸入模組定義的每個符號。當這一步完成時,程式中的每條指令和全域性變數都有唯一的執行時記憶體位址了。當然引用的位址都還沒有確定,這個工作我們交給下一步完成。

2、重定位節中的符號引用:在這一步中,鏈結器修改每個符號的引用,使得它們指向正確的執行時位址,分為重定位相對位址修改和重定位絕對位址修改。要執行這一步,鏈結器依賴於可重定位目標模組中稱為重定位條目的資料結構。值得一提,請將符號表和重定位條目區分。

typedef

struct

elf64_rela

完成這兩步後,我們就得到了乙個可執行目標檔案。

程式的鏈結 符號解析

首先,從程式到可執行程式要經過如下幾個步驟 在彙編結束之後,就已經生成了二進位制的可執行檔案。這次我們主要介紹符號解析這一步。符號解析 符號解析 將每個模組中引用的符號 符號引用 與某個目標模組的定義符號 符號定義 建立關聯。簡單理解就是比如在某處呼叫了乙個函式fun 這個fun就可以看做是其符號引...

鏈結基本一之符號

工具 readelf s 檢視檔案的符號 注意是小寫s,大寫是檢視段 例子 symbol table symtab contains 16 entries num value size type bind vis ndx name 0 0000000000000000 0 notype local ...

urllib之解析鏈結

from urllib.parse import urlparse 可以傳入三個引數urlstring,scheme 預設協議 allow fragments是忽略fragment,設定為false,則fragment部分會被忽略,它會被解析為path params或者query的一部分,而frag...