lib-symbol-visibility module allows precise control of the
symbols exported by a shared library. This is useful because
LD_PRELOAD.) Whereas a call to a function for which the compiler can assume that it is in the same shared library is just a direct “call” instructions. Similarly for variables: A reference to a global variable fetches a pointer in the so-called GOT (global offset table); this is a pointer to the variable’s memory. So the code to access it is two memory load instructions. Whereas for a variable which is known to reside in the same shared library, it is just a direct memory access: one memory load instruction.
There are traditionally three ways to specify the exported symbols of a shared library.
The upside of this approach is flexibility: it allows the same code to be used in different libraries with different export lists. The downsides are: 1. it’s a lot of maintenance overhead when the symbol list is platform dependent, 2. it doesn’t work well with C++, due to name mangling.
The drawbacks of this approach are: Symbols are still exported from the library by default. It’s a lot of maintenance work to mark every non- exported variable and function. But usually the exported API is quite small, compared to the internal API of the library. And it’s the wrong paradigm: It doesn’t force thinking when introducing new exported API.
This is perfect: It burdens the maintainer only for exported API, not for library-internal API. And it keeps the annotations in the source code.
GNU libtool’s -export-symbols option implements the first approach.
declared.sh from Gnulib can help to produce the list of
This gnulib module implements the third approach. For this it relies on GNU GCC 4.0 or newer, namely on its ‘-fvisibility=hidden’ command-line option and the “visibility” attribute. (The “visibility” attribute was already supported in GCC 3.4, but without the command line option, introduced in GCC 4.0, the third approach could not be used.)
More explanations on this subject can be found in https://gcc.gnu.org/wiki/Visibility, which contains more details on the GCC features and additional advice for C++ libraries, and in Ulrich Drepper’s paper https://www.akkadia.org/drepper/dsohowto.pdf, which also explains other tricks for reducing the startup time impact of shared libraries.
The gnulib autoconf macro
gl_VISIBILITY tests for GCC 4.0 or newer.
It defines a Makefile variable
‘-fvisibility=hidden’ or nothing. It also defines as a C macro and
as a substituted variable: @HAVE_VISIBILITY@. Its value is 1 when symbol
visibility control is supported, and 0 otherwise.
As of 2022, symbol visibility control is supported on
It is not supported on
To use this module in a library, say libfoo, you will do these steps:
@CFLAG_VISIBILITY@or (in a Makefile.am)
$(CFLAG_VISIBILITY)to the CFLAGS for the compilation of the sources that make up the library.
#if HAVE_VISIBILITY && BUILDING_LIBFOO #define LIBFOO_DLL_EXPORTED __attribute__((__visibility__("default"))) #else #define LIBFOO_DLL_EXPORTED #endif
This macro should be enabled in all public header files of your library.
Note that the precise control of the exported symbols will not work with other compilers than GCC >= 4.0, and will not work on systems where the assembler or linker lack the support of “hidden” visibility. Therefore, it’s good if, in order to reduce the risk of collisions with symbols in other libraries, you continue to use a prefix specific to your library for all non-static variables and functions and for all C++ classes in your library.
Note about other compilers: MSVC support can be added easily, by extending the definition of the macro mentioned above, to something like this:
#if HAVE_VISIBILITY && BUILDING_LIBFOO #define LIBFOO_DLL_EXPORTED __attribute__((__visibility__("default"))) #elif (defined _WIN32 && !defined __CYGWIN__) && BUILDING_SHARED && BUILDING_LIBFOO #define LIBFOO_DLL_EXPORTED __declspec(dllexport) #elif (defined _WIN32 && !defined __CYGWIN__) && BUILDING_SHARED #define LIBFOO_DLL_EXPORTED __declspec(dllimport) #else #define LIBFOO_DLL_EXPORTED #endif
BUILDING_SHARED is a C macro that you have to define. It
ought to evaluate to 1 in a build configured with ‘--enable-shared’,
or to 0 in a build configured with ‘--disable-shared’.
You may use the following ‘configure.ac’ snippet:
if test "$enable_shared" = yes; then building_shared=1 else building_shared=0 fi AC_DEFINE_UNQUOTED([BUILDING_SHARED], [$building_shared], [Define when --enable-shared is used.])