1. 程式人生 > >普通目標檔案的符號解析與重定義處理策略

普通目標檔案的符號解析與重定義處理策略

一、什麼是普通目標檔案

靜態連結器ld可以將一組可重定位目標檔案連結成一個可執行目標檔案。
其中可重定位目標檔案有三種,分別是目標檔案(.o)、靜態連結庫(.a)和動態連結庫(.so)。
本文所指的普通目標檔案特殊“目標檔案(.a)”

二、什麼是符號

1.符號是指程式碼中的變數與函式。程式碼中的關鍵字不屬於符號。
2.符號分為以下4種:
(1)可引出符號
由本模組定義,且能被本模組和其它模組引用的符號
非Static全域性函式,非static全域性變數都是可引出符號
(2)外部符號
由其它模組定義,但被本模組引用了的符號。
其它模組的可引出符號,如果被本模組使用了,就是本模組的外部符號
外部符號變數由extern修飾
(3)靜態符號
由本模組定義,且只能被本模組引用的符號
帶static的全域性變數和全域性函式都是靜態符號
(4)區域性符號
在函式內部定義的非static變數
3.舉例

extern int buf[];
static int *bufp0 = &buf[0];
int *bufp1;
void swap()
{
    int temp;
    bufp1 = &buf[1];
    temp = *bufp0;
    *bufp0 = *bufp1;
    *bufp1 = temp;
}

運用上面的內容,對這段程式碼中的符號分類

符號名符號型別
buf外部符號
bufp0靜態符號
bufp1可引出符號
swap可引出符號
temp區域性符號

三、符號解析

符號解析是指,把符號的定義和引用聯絡起來。
不同的符號型別,解析的策略不同。為了方便理解,這裡打亂次序,從最簡單的說起。
4.區域性符號
連結器對區域性符號一點都不關心,因為區域性符號是在棧裡管理的。
3.靜態符號
連結器對靜態符號的處理比較簡單,因為靜態符號的定義和引用肯定是在同一個模組中的,只要保證模組中一個符號只有一個定義就好了。
2.外部符號
編譯器發現模組中的符號沒有定義時,就會把它放到一個符號表中,連結器從其它模組中找出這些符號的定義。
1.可引出符號
可引出符號的處理比較複雜,要從所有模組中搜索這個符號的定義,還要處理重定義的問題。

四、符號重定義的處理策略

外部符號和可引出符號的解析過程中,都有可能遇到符號重定義的問題。當連結器發現符號重定義時會怎麼處理嗎?它會報錯嗎?
答案是“不一定”。有些情況下,連結器發現重定義後會提示連結錯誤,讓我們及時地發現問題。可有些情況,連結器會悄悄地選擇其中一個定義而忽略其它定義,這樣就產生了難以定義的BUG。
1.強弱符號規則
連結器使用強弱符號規則來決定怎麼處理符號重定義的問題。
(1)至多有一個強符號存在,若出現多個強符號,連結器就會報錯
(2)如果有一個強符號和多個弱符號,那麼選擇強符號
(3)如果有多個弱符號,會從弱符號中任意選擇一個,不報錯
由此可見,為了及時地發現重定義引入的問題,儘量把符號都定義成強符號。
2.什麼是強符號,什麼是弱符號

強符號(1)函式
(2)已經初始化的全域性變數
弱符號未初始化的全域性變數