隐藏图书馆中的符号名称

时间:2014-03-07 07:49:14

标签: gcc shared-libraries static-libraries

我想隐藏与上一个用户无关的符号名称,并在我的共享或静态库中仅显示可见的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命令输出的列表中。

4 个答案:

答案 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_b1f_b3 .hidden。它们仍然具有外部(全局)链接,可以静态调用 例如,来自包含它们的库中的其他模块,但可以 不要从图书馆以外的地方调用。

因此,如果您想隐藏共享中动态链接的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。使用标准 <{1}}关键字,用于制作符号本地/内部。

答案 1 :(得分:4)

我意识到这已经是一个老线程了。但是,我想在隐藏符号本地的意义上分享一些关于静态链接的事实,从而防止这些符号来自对象文件或静态库中的(全局)静态链接。这并不意味着在符号表中使它们不可见。

Mike Kingham的回答非常有用,但未完成以下细节:

  

如果要在静态中隐藏静态链接中的f_b1和f_b3   库,您根本无法使用visibility属性来执行此操作。

让我展示隐藏的符号当然可以通过使用file.c中的简单代码示例并在ypsu中应用Symbol hiding in static libraries built with Xcode/gcc的答案来实现。 在第一步,让我们重现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获得的中间结果完全相同。现在让我们使用隐藏属性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_b1f_b3在符号表中仍然可见,但它们是本地的。因此,函数f_b1f_b3隐藏在静态链接中!

我还想添加一个关于声明静态函数的注释,并且可以完全从符号表中删除它们。首先,可以使用objcopy确定性地执行删除操作,而不是依赖于编译器优化。

objcopy --strip-unneeded file.o

静态函数f_b1f_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