控制共用庫的符號可見度(一) 符號可見度簡介

來源:互聯網
上載者:User

什麼是符號和符號可見度

符號是談及對象檔案、連結等內容時的基本術語之一。實際上,在 C/C++ 語言中,符號是很多使用者定義的變數、函數名稱以 及一些名稱空間、類/結構/名稱等的對應實體。例如,當我們定義非靜態全域變數或非靜態函數時,C/C++ 編譯器就會在對象文 件中產生符號,這些符號對於連結器(linker)確定不同模組(對象檔案、動態共用程式庫、可執行檔)是否會共用相同的資料或 代碼很有用。

儘管變數和函數都可能會在模組之間共用,但是對象檔案之間的變數共用更為常見。例如,程式員可能會在 a.c 中聲明一個 變數:

extern int shared_var;

卻在 b.c 中定義該變數:

int shared_var;

這樣,兩個 shared_var 符號會出現在已編譯的對象 a.o 和 b.o 中,最後在連結器解析之後,a.o 中的符號會共用 b.o 的 地址。但是,人們很少讓變數在共用庫和可執行檔之間共用。對於此類別模組,通常只會讓函數對其他模組可見。有時,我們將 此類函數稱之為 API,因為我們覺得該模組是為其他模組提供調用的介面。我們也把這種符號稱為匯出的 (exported),因為它對 其他模組可見。注意,此可見度只在動態連結時有效,因為共用庫通常在程式運行時被載入為記憶體映像的一部分。因此,符號可 見性 (symbol visibility) 是所有全域符號的一個用於動態連結的屬性。

為什麼需要控制符號可見度

在不同的平台上,XL C/C++ 編譯器可能會選擇匯出或者不匯出模組中的所有符號。例如,在 IBM PowerLinux 平台上建立 Executable and Linking Format (ELF) 共用庫時,預設情況下,所有的符號都會匯出。在 POWER 平台上的 AIX 系統中建立 XCOFF 庫時,當前 XL C/C++ 編譯器在沒有工具的協助下可能會選擇不匯出任何符號。還有其他方式允許程式員逐個地決定符號 可見度(這是本系列下一部分要介紹的內容)。但是,一般不建議匯出模組中的所有符號。程式員可以根據需要匯出符號。這不 僅對庫的安全有益,也對動態連結時間有益。

程式員選擇匯出所有符號時,存在很高的風險,連結時可能會出現符號衝突,尤其是當模組是由不同的開發人員開發的時。因 為符號是低層級的概念,所以它不涉及到範圍。只要有人連結一個跟您的庫具有相同符號名稱的庫,當進行連結器解析時,該 庫就可能會意外地覆蓋您自己的符號(但願會給出一些警告或錯誤資訊)。大多數情況下,此類符號從來不會被從庫設計者的角 度去使用。因此,為符號建立有限制、有含義(經過深思熟慮)的名稱,對於避免此類問題有很大協助。

對於 C++ 編程,現在越來越注重效能了。然而,由於對其他庫的依賴性以及使用特定的 C++ 特性(比如模板),編譯器/鏈 接器趨向於會使用和產生大量的符號。因此,匯出所有符號會減慢程式速度,並耗用大量記憶體。匯出有限數量的符號可以縮短動 態共用庫的載入和連結時間。此外,也支援編譯器角度的最佳化,這意味著會產生更有效代碼。

以上關於匯出所有符號的缺點解釋了為什麼一定要定義符號可見度。在本文中,我們將提供一些解決方案來控制動態共用對象 (DSO) 中的符號。使用者可以使用不同的方式解決相同的問題,我們將提議特定平台應該首選哪種解決方式。

控制符號可見度的方式

在後面的討論中,我們將用到下面的 C++ 程式碼片段:
清單 1. a.C

int myintvar = 5;

int func0 () {
 return ++myintvar;
}

int func1 (int i) {
 return func0() * i;
}

在 a.C 中,我們定義了一個變數 myintvar,以及兩個函數 func0 和 func1。預設情況下,在 AIX 平台上建立共用庫時,編 譯器和連結器以及 CreateExportList 工具會讓所有三個符號都可見。我們可以利用 dump 二進位工具從 Loader Symbol Table Information 檢查這一情況:

$ xlC -qpic a.C -qmkshrobj -o libtest.a
$ dump -Tv libtest.a

               ***Loader Symbol Table Information***
[Index]      Value      Scn     IMEX Sclass   Type           IMPid Name

[0]     0x20000280    .data      EXP     RW SECdef        [noIMid] myintvar
[1]     0x20000284    .data      EXP     DS SECdef        [noIMid] func0__Fv
[2]     0x20000290    .data      EXP     DS SECdef        [noIMid] func1__Fi

這裡,“EXP”表示符號是匯出的。函數名稱 func0 和 func1 被 C++ 重整規則(mangling rule)進行了重整( 但是,不難猜出名稱的意思)。dump 工具的 -T 選項顯示 Loader Symbol Table Information,動態連結器將用到此資訊。在本 例中,a.C 中的所有符號都被匯出。但是從庫編寫者的角度,本例中我們可能只想匯出 func1。全域符號 myintvar 和函數 func0 被認為只保持/改變內部狀態,或者說只在局部使用。因此,對於庫編寫者來說,讓它們不可見至關重要。

我們至少有三種方式可以達此目的。包括:使用 static 關鍵字,定義 GNU visibility 屬性,以及使用匯出列表。每種方式 都有各自不同的功用和缺點。下面就來看看這些方式。
1. 使用 static 關鍵字

C/C++ 中的 static 可能是一個最常用的關鍵字,因為它可以為變數指定範圍和儲存。對於範圍,可以說成它為檔案中的 符號禁用了外部連結。這意味著,帶有關鍵字 static 的符號永遠不會是可連結的,因為編譯器不為連結器留下關於此符號的任 何資訊。這是一種語言層級的控制,是最簡單的一種隱藏符號的方式。

我們來給上面的例子添加 static 關鍵字吧:
清單 2. b.C

static int myintvar = 5;

static int func0 () {
 return ++myintvar;
}

int func1 (int i) {
 return func0() * i;
}

產生共用庫並再次查看 Loader Symbol Table Information,可以看到預期的效果:

$ xlC -qpic a.C -qmkshrobj -o libtest.a
$ dump -Tv libtest.a

               ***Loader Symbol Table Information***
[Index]      Value      Scn     IMEX Sclass   Type           IMPid Name

[0]     0x20000284    .data      EXP     DS SECdef        [noIMid] func1__Fi

相關文章

E-Commerce Solutions

Leverage the same tools powering the Alibaba Ecosystem

Learn more >

Apsara Conference 2019

The Rise of Data Intelligence, September 25th - 27th, Hangzhou, China

Learn more >

Alibaba Cloud Free Trial

Learn and experience the power of Alibaba Cloud with a free trial worth $300-1200 USD

Learn more >

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。