Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Symbol visibility not working as expected

Tags:

c

linker

clang

I have a sample program like this:

#include <stdio.h>

#if 1
#define FOR_EXPORT __attribute__ ((visibility("hidden")))
#else
#define FOR_EXPORT
#endif

FOR_EXPORT void mylocalfunction1(void)
{
    printf("function1\n");
}

void mylocalfunction2(void)
{
    printf("function2\n");
}

void mylocalfunction3(void)
{
    printf("function3\n");
}

void printMessage(void)
{
    printf("Running the function exported from the shared library\n");
}

And compile it using

gcc -shared -fPIC -fvisibility=hidden -o libdefaultvisibility.so defaultvisibility.c

Now after compilation I do:

$ nm libdefaultvisibility.so
nm libdefaultvisibility.so 
0000000000000eb0 t _mylocalfunction1
0000000000000ed0 t _mylocalfunction2
0000000000000ef0 t _mylocalfunction3
0000000000000f10 t _printMessage
                 U _printf
                 U dyld_stub_binder

Which means as far as I can tell that despite -fvisibility=hidden all symbols get exported. The book I was following claimed that only the function marked with FOR_EXPORT should be exported.

I looked oup several other resources, but for the simple test I'm doing -fvisibility=hidden should be sufficient.

My clang version:

$ clang -v
clang -v
Apple LLVM version 7.3.0 (clang-703.0.31)
Target: x86_64-apple-darwin15.0.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
like image 642
Max Avatar asked Jun 21 '16 01:06

Max


2 Answers

You're misunderstanding the output of nm. Scroll through man nm and you'll you read that the t flag means the symbol is a local (static) symbol in the text section. The linker can't see it. If it were global (external) the flag would be T. So all four of your functions are local.

Contrast:

$ clang -shared -fPIC -fvisibility=hidden -o libdefaultvisibility.so defaultvisibility.c
$ nm libdefaultvisibility.so | grep ' t '
0000000000000570 t deregister_tm_clones
0000000000000600 t __do_global_dtors_aux
0000000000200e08 t __do_global_dtors_aux_fini_array_entry
0000000000000640 t frame_dummy
0000000000200e00 t __frame_dummy_init_array_entry
0000000000000670 t mylocalfunction1
0000000000000690 t mylocalfunction2
00000000000006b0 t mylocalfunction3
00000000000006d0 t printMessage
00000000000005b0 t register_tm_clones   

with dropping the -fvisibility=hidden:

$ clang -shared -fPIC -o libdefaultvisibility.so defaultvisibility.c
$ nm libdefaultvisibility.so | grep ' t '
0000000000000600 t deregister_tm_clones
0000000000000690 t __do_global_dtors_aux
0000000000200e08 t __do_global_dtors_aux_fini_array_entry
00000000000006d0 t frame_dummy
0000000000200e00 t __frame_dummy_init_array_entry
0000000000000700 t mylocalfunction1
0000000000000640 t register_tm_clones
$ nm libdefaultvisibility.so | grep ' T '
0000000000000780 T _fini
00000000000005b0 T _init
0000000000000720 T mylocalfunction2
0000000000000740 T mylocalfunction3
0000000000000760 T printMessage

Then only the explicitly hidden mylocalfunction1 remains local, and the other three are now global.

You should not expect that a symbol marked with __attribute__ ((visibility("hidden"))) will be exported by a shared library in any circumstances. The attribute means precisely that it will not be, whether it is applied explicitly to a symbol, as in this case, or acquired by default in the presence of the linker option -fvisibility=hidden.

If you want to export just that one function in the example by means of a visibility attribution you would have:

#define FOR_EXPORT __attribute__ ((visibility("default")))

Then:

$ clang -shared -fPIC -fvisibility=hidden -o libdefaultvisibility.so defaultvisibility.c
$ nm libdefaultvisibility.so | grep ' T '
0000000000000720 T _fini
0000000000000550 T _init
00000000000006a0 T mylocalfunction1

It is global, because the explicit attribition overrides the commandline option, and all your other functions are local. Perhaps confusingly, default visibility is always public.

And you could accomplish this without resorting to visibility attributions - which are not portable - simply declaring all the functions that you don't want to export as static. Then the compiler would not expose them to the linker in the first place:

foo.c

#include <stdio.h>

void mylocalfunction1(void)
{
    printf("function1\n");
}

static void mylocalfunction2(void)
{
    printf("function2\n");
}

static void mylocalfunction3(void)
{
    printf("function3\n");
}

static void printMessage(void)
{
    printf("Running the function exported from the shared library\n");
}

With which you get again:-

$ clang -shared -fPIC -o libfoo.so foo.c
$ nm libfoo.so | grep ' T '
00000000000006c0 T _fini
0000000000000550 T _init
00000000000006a0 T mylocalfunction1

Although the distinction does not make itself felt in your example you should understand that while a local/static symbol is not seen by the linker and (therefore) is unavailable for dynamic linkage, a global/external symbol may or may not be available for dynamic linkage. visibility controls the availability of global symbols for dynamic linkage, only.

like image 143
Mike Kinghan Avatar answered Oct 14 '22 04:10

Mike Kinghan


According to GCC Wiki on Visibility, you should:

Use nm -C -D on the outputted DSO [Dynamic Shared Object] to compare before and after to see the difference it makes.

As stated on nm manual:

-D will display the dynamic symbols rather than the normal symbols

If I compile your code exactly as you did I get the following objects:

$ nm -C -D libdefaultvisibility.so
nm -C -D libdefaultvisibility.so
0000000000200a68 B __bss_start
                 w __cxa_finalize
0000000000200a68 D _edata
0000000000200a70 B _end
00000000000006c8 T _fini
                 w __gmon_start__
0000000000000518 T _init
                 w _ITM_deregisterTMCloneTable
                 w _ITM_registerTMCloneTable
                 w _Jv_RegisterClasses
                 U puts

And if I compile it without the -fvisibility=hidden option I get the objects:

$ nm -C -D libdefaultvisibility.so
nm -C -D libdefaultvisibility.so
0000000000200ae8 B __bss_start
                 w __cxa_finalize
0000000000200ae8 D _edata
0000000000200af0 B _end
0000000000000748 T _fini
                 w __gmon_start__
00000000000005a0 T _init
                 w _ITM_deregisterTMCloneTable
                 w _ITM_registerTMCloneTable
                 w _Jv_RegisterClasses
0000000000000712 T mylocalfunction2
0000000000000724 T mylocalfunction3
0000000000000736 T printMessage
                 U puts
like image 24
learning_frog Avatar answered Oct 14 '22 04:10

learning_frog