我想隐藏与上一个用户无关的符号名称,并在我的共享或静态库中仅显示可见的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
命令输出的列表中。
答案 0 :(得分:25)
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_b1
中看到f_b3
和.text
是全局(g)函数(F)
部分。
现在像这样修改文件:
__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_b1
和f_b3
.hidden
。它们仍然具有外部(全局)链接,可以静态调用
例如,来自包含它们的库中的其他模块,但可以
不要从图书馆以外的地方调用。
因此,如果您想隐藏共享中动态链接的f_b1
和f_b3
如图所示,您可以使用visibility ("hidden")
。
如果您想在静态中隐藏静态链接中的f_b1
和f_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_b1
和f_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_b1
和f_b3
已消失,nm file.o
根本没有报告。为什么?
因为static
告诉编译器只能调用这些符号
从它正在编译的文件中,优化决定那里
没有必要提到他们;所以编译器将它们排除在外
目标代码。但是如果它们对于链接器来说已经不可见了,没有
优化,然后我们无法优化它们。
结论:nm
是否可以提取符号无关紧要。如果
符号是本地/内部的,它无法静态或动态链接。
如果符号已标记为.hidden
,则无法动态链接。您
可以使用visibility("hidden")
标记符号.hidden
。使用标准
<{1}}关键字,用于制作符号本地/内部。
答案 1 :(得分:4)
我意识到这已经是一个老线程了。但是,我想在隐藏符号本地的意义上分享一些关于静态链接的事实,从而防止这些符号来自对象文件或静态库中的(全局)静态链接。这并不意味着在符号表中使它们不可见。
Mike Kingham的回答非常有用,但未完成以下细节:
如果要在静态中隐藏静态链接中的f_b1和f_b3 库,您根本无法使用visibility属性来执行此操作。
让我展示隐藏的符号当然可以通过使用file.c
中的简单代码示例并在ypsu中应用Symbol hiding in static libraries built with Xcode/gcc的答案来实现。
在第一步,让我们重现objdump输出,隐藏属性在f_b1
和f_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获得的中间结果完全相同。现在让我们使用隐藏属性local来制作符号。这是通过使用objcopy
中的binutils
完成的,如下所示:
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_b1
和f_b3
在符号表中仍然可见,但它们是本地的。因此,函数f_b1
和f_b3
隐藏在静态链接中!
我还想添加一个关于声明静态函数的注释,并且可以完全从符号表中删除它们。首先,可以使用objcopy
确定性地执行删除操作,而不是依赖于编译器优化。
objcopy --strip-unneeded file.o
静态函数f_b1
和f_b2
不再位于file.o
的符号表中。
其次,使用声明函数static让它们从符号表中消失只能在单个源文件C项目中使用。一旦C项目由许多组件和文件组成,这只能通过将所有C-source和-header文件合并到一个单独的源文件中来完成,并且声明所有内部接口(函数)是静态的,显然除了全局(顶部)界面。如果无法做到这一点,可以回退到ypsu最初描述的方法(可能还有许多其他方法 - 请参阅Restricting symbols in a Linux static library)。
答案 2 :(得分:1)
实际上,在ELF结构中有2个符号表:“ symtab”和“ dynsym”。 在我的自定义库中,我总是剥离 all 符号,因为正确链接不需要它们-即“ symtab”(由“ nm”实用程序打印)可以为空,因为链接器实际上正在使用“ dynsym”表。 这样可以将库大小减小10%至20%(通常)
仅从“ symtab”中删除具有“ hidden”属性的功能,但仍可以在“ dynsym”表中看到它们。
您可以使用以下方法进行验证:
readelf --syms --dyn-syms <your dso here>
“ dynsym”表始终包含链接器所需的所有条目,包括f.e。 STD ::函数,标记为“ UND”(未定义->由链接程序解析)
致谢。
答案 3 :(得分:0)
请注意,对于MacOS / iOS,链接器有一些额外的选项来控制符号可见性;
-[un|re]exported_symbols_list
-[un]exported_symbol
有关详细信息,请查看ld64文档或查看here。