隐藏图书馆中的符号名称

mus*_*vuz 9 gcc shared-libraries static-libraries

我想隐藏与最后一个用户无关的符号名称,并在我的共享库或静态库中仅显示可见的API.我有一个简单的代码:

int f_b1(){
return 21 ;
}

int f_b3(){
return f_b1() ;
}
Run Code Online (Sandbox Code Playgroud)

我应用了此处所述的所有方法,例如使用__attribute__ ((visibility ("hidden")))static数据但未获得成功结果.我的操作系统是Ubuntu和x86_64 GNU/Linux处理器.在使用gcc编译时我们是否使用特殊选项?我用nm命令列出了库的模块和功能.在我上面的例子中,我只想制作可见的f_b3功能.当我使用attribute hidden宏编译器时不会给出任何错误但该函数仍然存在于nm命令输出的列表中.

Mik*_*han 30

visibility("hidden")属性不会抑制目标文件中的符号,也不能阻止符号被提取nm.它只是指示动态链接器无法从包含它的共享库外部调用该符号.

考虑一个file.c包含示例函数的源文件:

int f_b1(){
return 21 ;
}

int f_b3(){
return f_b1() ;
}
Run Code Online (Sandbox Code Playgroud)

编译文件:

gcc -c -o file.o file.c
Run Code Online (Sandbox Code Playgroud)

运行nm file.o以列出符号.输出:

0000000000000000 T f_b1
000000000000000b T f_b3
Run Code Online (Sandbox Code Playgroud)

现在运行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
Run Code Online (Sandbox Code Playgroud)

在这里我们看到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() ;
}
Run Code Online (Sandbox Code Playgroud)

再跑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
Run Code Online (Sandbox Code Playgroud)

输出是相同的,除了符号f_b1f_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() ;
}
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

您可以看到f_b1并且f_b3仍然在该.text 部分中报告为函数,但现在分类为local(l),而不是global.这是内部联系.运行nm file.o,输出是:

0000000000000000 t f_b1
000000000000000b t f_b3
Run Code Online (Sandbox Code Playgroud)

这与原始文件相同,除了我们现在有't'标志而不是'T'标志.两个标志都表示符号在该.text部分中,但"T"表示它是全局的,"t"表示它是本地的.

显然,您要nm为此文件报告的内容根本不是符号.您现在应该理解,nm file.o如果符号存在,它将报告符号 file.o,但它的存在与静态或动态链接是否可见无关.

要使函数符号消失,file.c再次编译(仍然使用static关键字),这次启用了优化:

gcc -c -O1 -o file.o file.c
Run Code Online (Sandbox Code Playgroud)

现在,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
Run Code Online (Sandbox Code Playgroud)

f_b1并且f_b3已经消失了,nm file.o根本没有报道.为什么?因为static告诉编译器这些符号只能从它正在编译的文件中调用,而优化决定不需要引用它们; 所以编译器将它们从目标代码中删除.但是如果链接器没有看到它们,没有优化,那么我们就无法优化它们.

底线:是否nm可以提取符号无关紧要.如果符号是本地/内部的,则无法静态或动态链接.如果符号已标记.hidden,则无法动态链接.您可以使用visibility("hidden")标记符号.hidden.使用standard static关键字将符号设置为local/internal.

  • 是的:运行`strip -s file.o`.见`man strip`.请注意,删除所有符号时无法调试目标文件. (3认同)

Oke*_*Oke 7

我意识到这已经是旧线程了。但是,我想分享一些有关静态链接的事实,即将隐藏的符号设置为本地,从而防止这些符号与目标文件或静态库中的(全局)静态链接发生联系。这并不意味着使它们在符号表中不可见。

迈克·金厄姆的答案非常有用,但在以下细节上并不完整:

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

让我展示一下,通过使用中的简单代码示例,file.c在使用Xcode / gcc构建的静态库中的“ 符号隐藏”中应用ypsu的答案,可以肯定地将隐藏符号设置为局部。第一步,让我们再现objdump输出,其中的隐藏属性在和上可见。这可以通过以下命令完成,该命令在hidden属性中提供了所有功能:f_b1f_b3file.c

gcc -fvisibility=hidden -c file.c
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

这与Mike Kingham获得的中间结果完全相同。现在让我们将具有hidden属性的符号设置为局部。这是通过使用objcopyfrom 来实现的binutils

objcopy --localize-hidden --strip-unneeded file.o
Run Code Online (Sandbox Code Playgroud)

使用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
Run Code Online (Sandbox Code Playgroud)

同样,nm file.o

0000000000000000 t f_b1
000000000000000b t f_b3
Run Code Online (Sandbox Code Playgroud)

尽管f_b1f_b3在符号表中仍然可见,但是它们是本地的。因此,函数f_b1f_b3都无法通过静态链接隐藏!

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

objcopy --strip-unneeded file.o
Run Code Online (Sandbox Code Playgroud)

的静态函数f_b1f_b2不再位于的符号表中file.o

其次,使用声明函数static来使它们从符号表中消失的这种用法仅在单个源文件C项目中有效。只要一个C项目由许多组件和文件组成,就只能通过将所有C源文件和-header文件合并到一个源文件中并声明所有内部接口(函数)为静态的(全局变量除外)为静态来完成此操作。 (顶部)界面。如果无法实现,则可以使用ypsu最初描述的方法(可能还有许多其他方法-例如,参见Linux静态库中的限制符号)。