GNU Visibility Attribute
When going through the folly code base, sometimes, you see definitions like
class FOLLY_EXPORT OptionalEmptyException : public std::runtime_error {
...
FOLLY_EXPORT
is defined as
#define FOLLY_EXPORT __attribute__((__visibility__("default")))
It sets the visibility attribute of symbol OptionalEmptyException
to "default". What does it do exactly, and how it works?
Static Library
We will start with a very simply example. We have a file foo.cpp
,
int foo(int x) {
return x*x;
}
And a simple main.cpp
that calls foo
. If we compile main.cpp
and foo.cpp
and inspect the object files,
clang++ -c main.cpp foo.cpp
nm -C main.o
0000000000000000 T main
U foo(int)
nm -C foo.o
0000000000000000 T foo(int)
nm
is a utility that displays symbols from object files. -C
dismangles the symbols, so it's more readable (foo(int)
vs _Z3fooi
).
Symbol foo
is what we are interested in here. In main.o
, foo
is U (undefined). In foo.o
, foo
is T (a symbol in .text section and externally visible).
Now let's add a visibility attribute to foo
.
int __attribute__((visibility("hidden"))) foo(int x) {
return x*x;
}
clang++ foo.cpp
nm -C foo.o
0000000000000000 T foo(int)
foo
is still public. If we make a binary out of main.o and foo.o, it will run just fine. This is because foo.o
is a static library, with static linkage. foo
is a "public" symbol as we statically link foo.o
into our final binary.
Dynamic Library
So the visibility attribute doesn't affect static library and it must affect dynamic library. We first build a shared library,
clang++ -shared -fpic foo.cpp -o libfoo.so
Now foo
becomes a local symbol, hence not visible outside of the shared library itself.
nm -C libfoo.so
0000000000004020 b completed.0
w __cxa_finalize@@GLIBC_2.2.5
0000000000001020 t deregister_tm_clones
0000000000001090 t __do_global_dtors_aux
0000000000003e18 d __do_global_dtors_aux_fini_array_entry
0000000000004018 d __dso_handle
0000000000003e20 d _DYNAMIC
0000000000001100 t _fini
00000000000010e0 t frame_dummy
0000000000003e10 d __frame_dummy_init_array_entry
0000000000002050 r __FRAME_END__
0000000000004000 d _GLOBAL_OFFSET_TABLE_
w __gmon_start__
0000000000002000 r __GNU_EH_FRAME_HDR
0000000000001000 t _init
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
0000000000001050 t register_tm_clones
0000000000004020 d __TMC_END__
00000000000010f0 t foo(int) # <--- notice the lower case t
Now if we try to link the object files together, clang would complain that it cannot find foo
.
clang++ main.cpp libfoo.so
/usr/bin/ld: /tmp/main-6da6a7.o: in function `main':
main.cpp:(.text+0x15): undefined reference to `foo(int)'
clang-10: error: linker command failed with exit code 1 (use -v to see invocation)
Usually __attribute__((visibility("default")))
is used in combination with -fvisibility=hidden
linker flag. The latter would make "hidden" the default visibility for all symbols of a shared library. Back to the original example from folly,
class FOLLY_EXPORT OptionalEmptyException : public std::runtime_error {
...
Exceptions, if visible outside of the shared library itself, needs to be marked with default visibility explicitly (if -fvisibility=hidden
is set). People usually set -fvisibility
to reduce shared library size and load time.
How Dynamic Library Works
Dynamic library (a.k.a shared library) is lazily loaded at runtime. It has the benefit of sharing the dynamic library memory section across multiple processes. This means a binary that depends on a shared library is incomplete.
clang++ main.cpp libfoo.so
objdump -D a.out -t -s -M intel
This is where foo gets called.
It looks up the Global Offset Table, where it would find out that it needs to load libfoo.so to resolve symbol foo. As expected, string "libfoo.so" is in the final binary.
Summary
To summarize, __attribute__((visibility("default")))
is often used in a shared library, and expected to be compiled with -fvisibility=hidden
(though it doesn't have to). Symbols with "default" visibility will always be public outside of the shared library itself. You would often find it set on public interfaces of a shared library (including exceptions).