2014-03-07 74 views
3

我想隱藏與最後一個用戶無關的符號名稱,並在共享庫或靜態庫中僅顯示API。我有一個簡單的代碼那樣:隱藏庫中的符號名稱

int f_b1(){ 
return 21 ; 
} 

int f_b3(){ 
return f_b1() ; 
} 

我申請的所有方法表示here如使用__attribute__ ((visibility ("hidden")))static數據,但沒有得到圓滿的結果。我的操作系統是Ubuntu和x86_64 GNU/Linux處理器。我們在使用gcc編譯時使用特殊選項嗎?我使用nm命令列出庫的模塊和函數。在我上面的例子中,我只想使可見的f_b3函數。當我使用attribute hidden宏編譯器不會給出任何錯誤,但函數仍然存在於由nm命令輸出的列表中。

回答

17

visibility("hidden")屬性不抑制一個符號從 一個目標文件,並不能防止通過nm被提取的符號。它只是 指示動態鏈接器該符號不能從外部調用 包含它的共享庫。

考慮一個源文件file.c包含您的功能。例如:

int f_b1(){ 
return 21 ; 
} 

int f_b3(){ 
return f_b1() ; 
} 

編譯文件:

gcc -c -o file.o file.c 

運行nm file.o列出的符號。輸出:

0000000000000000 T f_b1 
000000000000000b T f_b3 

現在運行objdump -t file.o約符號更全面的信息。輸出:

file.o:  file format elf64-x86-64 

SYMBOL TABLE: 
0000000000000000 l df *ABS* 0000000000000000 file.c 
0000000000000000 l d .text 0000000000000000 .text 
0000000000000000 l d .data 0000000000000000 .data 
0000000000000000 l d .bss 0000000000000000 .bss 
0000000000000000 l d .note.GNU-stack 0000000000000000 .note.GNU-stack 
0000000000000000 l d .eh_frame 0000000000000000 .eh_frame 
0000000000000000 l d .comment 0000000000000000 .comment 
0000000000000000 g  F .text 000000000000000b f_b1 
000000000000000b g  F .text 000000000000000b f_b3 

這裏我們看到f_b1f_b3是全球性的(G)功能(F)在.text 部分。

現在修改該文件是這樣的:

__attribute__((visibility ("hidden"))) int f_b1(void){ 
return 21 ; 
} 

__attribute__((visibility ("hidden"))) int f_b3(void){ 
return f_b1() ; 
} 

運行objdump再次:

file.o:  file format elf64-x86-64 

SYMBOL TABLE: 
0000000000000000 l df *ABS* 0000000000000000 file.c 
0000000000000000 l d .text 0000000000000000 .text 
0000000000000000 l d .data 0000000000000000 .data 
0000000000000000 l d .bss 0000000000000000 .bss 
0000000000000000 l d .note.GNU-stack 0000000000000000 .note.GNU-stack 
0000000000000000 l d .eh_frame 0000000000000000 .eh_frame 
0000000000000000 l d .comment 0000000000000000 .comment 
0000000000000000 g  F .text 000000000000000b .hidden f_b1 
000000000000000b g  F .text 000000000000000b .hidden f_b3 

輸出是一樣的,不同之處在於符號f_b1f_b3現在被標記 .hidden。它們仍然具有外部(全局)鏈接,並且可以從包含它們的庫中的其他模塊靜態調用,例如 示例,但可能不會從該庫外部以dymamically方式調用 。

所以,如果你想從動態鏈接隱瞞f_b1f_b3共享 庫,你可以使用visibility ("hidden")如圖所示。

如果你想在一個靜態 庫隱瞞f_b1f_b3靜態聯動,您不能使用visibility屬性做到這一點的。

在靜態庫的情況下,你可以「隱藏」一個符號,只給它 內部而不是外部鏈接。要做到這一點的方法是在 標準static關鍵字前添加。但內部聯動意味着符號爲 ,只能在其自己的彙編單位內可見:不能從 其他模塊引用。它根本不可用於鏈接器。

修改file.c再次,像這樣:

static int f_b1(void){ 
return 21 ; 
} 

static int f_b3(void){ 
return f_b1() ; 
} 

,並再次運行objump

file.o:  file format elf64-x86-64 

SYMBOL TABLE: 
0000000000000000 l df *ABS* 0000000000000000 file.c 
0000000000000000 l d .text 0000000000000000 .text 
0000000000000000 l d .data 0000000000000000 .data 
0000000000000000 l d .bss 0000000000000000 .bss 
0000000000000000 l  F .text 000000000000000b f_b1 
000000000000000b l  F .text 000000000000000b f_b3 
0000000000000000 l d .note.GNU-stack 0000000000000000 .note.GNU-stack 
0000000000000000 l d .eh_frame 0000000000000000 .eh_frame 
0000000000000000 l d .comment 0000000000000000 .comment 

你看到f_b1f_b3仍報告爲.text 部分功能,但現在分類當地(l),不是全球。這是內部聯繫。 運行nm file.o和輸出是:

0000000000000000 t f_b1 
000000000000000b t f_b3 

這是相同的原始文件,但不是「T」標誌 我們現在有「T」標誌。兩個標誌都表示符號位於.text部分, ,但'T'表示它是全局的,'t'表示它是本地的。

顯然,你想要nm報告此文件是沒有符號的所有。 您現在應該明白nm file.o會報告一個符號,如果它存在於 file.o中,但它的存在與靜態或動態鏈接是否可見 無關。

爲了消失功能符號,編譯file.c再次 (仍與static關鍵字),這次與優化啓用:現在

gcc -c -O1 -o file.o file.c 

objdump報道:

file.o:  file format elf64-x86-64 

SYMBOL TABLE: 
0000000000000000 l df *ABS* 0000000000000000 file.c 
0000000000000000 l d .text 0000000000000000 .text 
0000000000000000 l d .data 0000000000000000 .data 
0000000000000000 l d .bss 0000000000000000 .bss 
0000000000000000 l d .note.GNU-stack 0000000000000000 .note.GNU-stack 
0000000000000000 l d .comment 0000000000000000 .comment 

f_b1f_b3不見了,nm file.o什麼也沒有報告。爲什麼? 因爲static告訴編譯器,這些符號只能從它正在編譯的文件中調用 ,並且優化決定了 不需要引用它們;所以編譯器從 目標代碼中消除它們。但是,如果它們不是鏈接器不可見的,而沒有優化,那麼我們不能優化它們。

底線:nm是否可以提取符號並不重要。如果 符號是本地/內部符號,則無法靜態或動態鏈接。 如果符號被標記爲.hidden那麼它不能被動態地鏈接。您 可以使用visibility("hidden")來標記符號.hidden。使用標準 static關鍵字使符號成爲本地/內部。

+0

這是很好的解釋。 –

+0

有沒有辦法隱藏對象文件名呢?我的'nm'命令輸出是這樣的。 ''b.o: 0000000000000000 T f_b2 0000000000000010 T f_b3 c.o: '''我想隱藏'b.o和c.o'的名字。謝謝.. –

+2

是的:運行'strip -s file.o'。見'男子地帶'。請注意,當您刪除所有符號時,您無法調試目標文件。 –

1

我意識到這已經是一個老話題了。然而,我想分享一些關於靜態鏈接的事實,從而使隱藏符號本地化,從而阻止這些符號從對象文件或靜態庫中進行(全局)靜態鏈接。這並不意味着讓它們在符號表中不可見。

邁克Kingham的答案是非常有用的,但是,從以下細節沒有完成:

如果你想隱瞞靜態鏈接f_b1和f_b3靜態 庫,你不能使用visibility屬性來做到這一點在所有。

讓我告訴隱藏的符號當然可以通過使用file.c簡單的代碼示例和Symbol hiding in static libraries built with Xcode/gcc應用ypsu的回答進行地方。 第一步讓我們重現帶有隱藏屬性的objdump輸出,該隱藏屬性在f_b1f_b3上可見。這可以通過下面的命令,該命令給出的所有功能在file.c隱藏屬性來完成:

gcc -fvisibility=hidden -c file.c 

objdump -t file.o輸出給出

file.o:  file format elf64-x86-64 

SYMBOL TABLE: 
0000000000000000 l df *ABS* 0000000000000000 file.c 
0000000000000000 l d .text 0000000000000000 .text 
0000000000000000 l d .data 0000000000000000 .data 
0000000000000000 l d .bss 0000000000000000 .bss 
0000000000000000 l d .note.GNU-stack 0000000000000000 .note.GNU-stack 
0000000000000000 l d .eh_frame 0000000000000000 .eh_frame 
0000000000000000 l d .comment 0000000000000000 .comment 
0000000000000000 g  F .text 000000000000000b .hidden f_b1 
000000000000000b g  F .text 0000000000000010 .hidden f_b3 

這正是由Mike Kingham爲獲得相同的中間結果。現在,讓我們使用隱藏屬性本地的符號。這是通過使用objcopybinutils完成如下:

objcopy --localize-hidden --strip-unneeded file.o 

使用objdump的,給人

file.o:  file format elf64-x86-64 

SYMBOL TABLE: 
0000000000000000 l d .text 0000000000000000 .text 
0000000000000000 l  F .text 000000000000000b .hidden f_b1 
000000000000000b l  F .text 0000000000000010 .hidden f_b3 
0000000000000000 l d .data 0000000000000000 .data 
0000000000000000 l d .bss 0000000000000000 .bss 
0000000000000000 l d .comment 0000000000000000 .comment 
0000000000000000 l d .note.GNU-stack 0000000000000000 .note.GNU-stack 
0000000000000000 l d .eh_frame 0000000000000000 .eh_frame 

同樣,nm file.o

0000000000000000 t f_b1 
000000000000000b t f_b3 

雖然f_b1f_b3仍然可見在該符號表他們是本地的。因此,功能f_b1f_b3隱藏靜態鏈接!

我還想添加一個關於聲明靜態函數的註釋,並且可以將它們從符號表中完全移除。首先,刪除可以通過使用objcopy確定性地完成,而不取決於編譯器優化。

objcopy --strip-unneeded file.o 

靜態功能f_b1f_b2都不再在file.o符號表。其次,這種使用聲明靜態函數的方式讓它們從符號表中消失,只能在單個源文件C項目中使用。只要C項目由許多組件組成,因此文件只能通過將所有C源文件和頭文件合併爲一個單獨的源文件並將所有內部接口(函數)聲明爲靜態,而全局例外(頂部)界面。如果這是不可能的,可以回退到最初由ypsu描述的方法(可能還有許多其他方法 - 例如參見Restricting symbols in a Linux static library)。

0

請注意,對於MacOS/iOS,鏈接器有一些額外的選項來控制符號可見性;

  • -[un|re]exported_symbols_list
  • -[un]exported_symbol

有關詳情,請例如ld64文檔或看看here