Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
638 views
in Technique[技术] by (71.8m points)

gcc - Hiding symbol names in library

I want to hide symbol names which are not relevant to the last user and make visible only APIs in my shared or static library. I have a simple code like that:

int f_b1(){
return 21 ;
}

int f_b3(){
return f_b1() ;
}

I applied the all methods stated here such as using __attribute__ ((visibility ("hidden"))) and static data but got no successful result. My operating system is Ubuntu and x86_64 GNU/Linux processor. Do we use special options while compiling with gcc? I am listing modules and function of libraries with nm command. In my example above I only want to make visible f_b3 function. When I use attribute hidden macro compiler does not give any error but the function still exists in list outputted by nm command.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The visibility("hidden") attribute does not suppress a symbol from an object file and cannot prevent a symbol being extracted by nm. It just instructs the dynamic linker that the symbol cannot be called from outside a shared library that contains it.

Consider a source file file.c containing your example functions:

int f_b1(){
return 21 ;
}

int f_b3(){
return f_b1() ;
}

Compile the file:

gcc -c -o file.o file.c

Run nm file.o to list the symbols. Output:

0000000000000000 T f_b1
000000000000000b T f_b3

Now run objdump -t file.o for fuller information about the symbols. Output:

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

Here we see that f_b1 and f_b3 are global (g) functions (F) in the .text section.

Now modify the file like this:

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

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

Run objdump again:

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

The output is the same, except that the symbols f_b1 and f_b3 are now marked .hidden. They still have external (global) linkage and could be statically called, for example, from other modules within a library that contains them, but could not be dymamically called from outside that library.

So, if you want to conceal f_b1 and f_b3 from dynamic linkage in a shared library, you can use visibility ("hidden") as shown.

If you want to conceal f_b1 and f_b3 from static linkage in a static library, you cannot use the visibility attribute to do that at all.

In the case of a static library, you can "hide" a symbol only be giving it internal instead of external linkage. The way to do that is by prefixing the standard static keyword. But internal linkage means that the symbol is visible only within its own compilation unit: it can't be referenced from other modules. It is not available to the linker at all.

Modify file.c again, like this:

static int f_b1(void){
return 21 ;
}

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

And run objump again:

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

You see that f_b1 and f_b3 are still reported as functions in the .text section, but are now classified local (l), not global. That is internal linkage. Run nm file.o and the output is:

0000000000000000 t f_b1
000000000000000b t f_b3

That is the same as for the original file, except that instead of 'T' flags we now have 't' flags. Both flags mean that the symbol is in the .text section, but 'T' means it is global and 't' means it is local.

Apparently, what you would like nm to report for this file is no symbols at all. You should now understand that nm file.o will report a symbol if it exists in file.o, but its existence has got nothing to do with whether it is visible for static or dynamic linkage.

To make the function symbols disappear, compile file.c yet again (still with the static keyword), this time with optimisation enabled:

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

Now, objdump reports:

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 and f_b3 are gone, and nm file.o reports nothing at all. Why? Because static tells the compiler that these symbols can only be called from within the file it is compiling, and optimisation decides that there is no need to refer to them; so the compiler eliminates them from the object code. But if they weren't already invisible to linker, without optimisation, then we couldn't optimise them away.

Bottom line: It doesn't matter whether nm can extract a symbol. If the symbol is local/internal, it can't be linked, either statically or dynamically. If the symbol is marked .hidden then it can't be dynamically linked. You can use visibility("hidden") to mark a symbol .hidden. Use the standard static keyword to make a symbol local/internal.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...