Hi, gcc-4.0 now provides support for controlling the set of symbols exported from a shared library in a reasonable, maintainable way. I have added a gnulib module to this effect. With documentation, this time :-)
Bruno ============================= modules/visibility ============================= Description: Control of symbols exported by shared libraries. Files: m4/visibility.m4 Depends-on: configure.ac: gl_VISIBILITY Makefile.am: Include: License: LGPL Maintainer: Bruno Haible ============================== m4/visibility.m4 ============================== # visibility.m4 serial 1 (gettext-0.15) dnl Copyright (C) 2005 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. dnl From Bruno Haible. dnl Tests whether the compiler supports the command-line option dnl -fvisibility=hidden and the function and variable attributes dnl __attribute__((__visibility__("hidden"))) and dnl __attribute__((__visibility__("default"))). dnl Does *not* test for __visibility__("protected") - which has tricky dnl semantics (see the 'vismain' test in glibc) and does not exist e.g. on dnl MacOS X. dnl Does *not* test for __visibility__("internal") - which has processor dnl dependent semantics. dnl Does *not* test for #pragma GCC visibility push(hidden) - which is dnl "really only recommended for legacy code". dnl Set the variable CFLAG_VISIBILITY. dnl Defines and sets the variable HAVE_VISIBILITY. AC_DEFUN([gl_VISIBILITY], [ AC_REQUIRE([AC_PROG_CC]) CFLAG_VISIBILITY= HAVE_VISIBILITY=0 if test -n "$GCC"; then AC_MSG_CHECKING([for simple visibility declarations]) AC_CACHE_VAL(gl_cv_cc_visibility, [ gl_save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -fvisibility=hidden" AC_TRY_COMPILE( [extern __attribute__((__visibility__("hidden"))) int hiddenvar; extern __attribute__((__visibility__("default"))) int exportedvar; extern __attribute__((__visibility__("hidden"))) int hiddenfunc (void); extern __attribute__((__visibility__("default"))) int exportedfunc (void);], [], gl_cv_cc_visibility=yes, gl_cv_cc_visibility=no) CFLAGS="$gl_save_CFLAGS"]) AC_MSG_RESULT([$gl_cv_cc_visibility]) if test $gl_cv_cc_visibility = yes; then CFLAG_VISIBILITY="-fvisibility=hidden" HAVE_VISIBILITY=1 fi fi AC_SUBST([CFLAG_VISIBILITY]) AC_SUBST([HAVE_VISIBILITY]) AC_DEFINE_UNQUOTED([HAVE_VISIBILITY], [$HAVE_VISIBILITY], [Define to 1 or 0, depending whether the compiler supports simple visibility declarations.]) ]) ============================= doc/visibility.texi ============================= @c Documentation of gnulib module 'visibility'. This module allows precise control of the symbols exported by a shared library. This is useful because @itemize @bullet @item It prevents abuse of undocumented APIs of your library. Symbols that are not exported from the library cannot be used. This eliminates the problem that when the maintainer of the library changes internals of the library, maintainers of other projects cry "breakage". Instead, these maintainers are forced to negotiate the desired API from the maintainer of the library. @item It reduces the risk of symbol collision between your library and other libraries. For example, the symbol @samp{readline} is defined in several libraries, most of which don't have the same semantics and the same calling convention as the GNU readline library. @item It reduces the startup time of programs linked to the library. This is because the dynamic loader has less symbols to process. @item It allows the compiler to generate better code. Within a shared library, a call to a function that is a global symbol costs a "call" instruction to a code location in the so-called PLT (procedure linkage table) which contains a "jump" instruction to the actual function's code. (This is needed so that the function can be overridden, for example by a function with the same name in the executable or in a shared library interposed with @code{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 pointer 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. @end itemize There are traditionally three ways to specify the exported symbols of a shared library. @itemize @bullet @item The programmer specifies the list of symbols to be exported when the shared library is created. Usually a command-line option is passed to the linker, with the name of a file containing the symbols. 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. @item The programmer specifies a "hidden" attribute for every variable and function that shall not be exported. 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. @item The programmer specifies a "hidden" attribute for all files that make up the shared library, and an "exported" attribute for those symbols in these files that shall be exported. 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. @end itemize This gnulib module implements the third approach. For this it relies on GNU GCC 4.0 or newer, namely on its @samp{-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 @url{http://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 @url{http://people.redhat.com/drepper/dsohowto.pdf} - which also explains other tricks for reducing the startup time impact of shared libraries. The gnulib autoconf macro @code{gl_VISIBILITY} tests for GCC 4.0 or newer. It defines a Makefile variable @code{@@CFLAG_VISIBILITY@@} containing @samp{-fvisibility=hidden} or nothing. It also defines as a C macro and as a Makefile variable: @@HAVE_VISIBILITY@@. Its value is 1 when symbol visibility control is supported, and 0 otherwise. To use this module in a library, say libfoo, you will do these steps: @enumerate @item Add @code{@@CFLAG_VISIBILITY@@} or (in a Makefile.am) @code{$(CFLAG_VISIBILITY)} to the CFLAGS for the compilation of the sources that make up the library. @item Add a C macro definition, say @samp{-DBUILDING_LIBFOO}, to the CPPFLAGS for the compilation of the sources that make up the library. @item Define a macro specific to your library like this. @smallexample #if @@HAVE_VISIBILITY@@ && BUILDING_LIBFOO #define LIBFOO_DLL_EXPORTED __attribute__((__visibility__("default"))) #else #define LIBFOO_DLL_EXPORTED #endif @end smallexample This macro should be enabled in all public header files of your library. @item Annotate all variable, function and class declarations in all public header files of your library with @samp{LIBFOO_DLL_EXPORTED}. This annotation can occur at different locations: between the @samp{extern} and the type or return type, or just before the entity being declared, or after the entire declarator. My preference is to put it right after @samp{extern}, so that the declarations in the header files remain halfway readable. @end enumerate 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: @smallexample #if @@HAVE_VISIBILITY@@ && BUILDING_LIBFOO #define LIBFOO_DLL_EXPORTED __attribute__((__visibility__("default"))) #elif defined _MSC_VER && BUILDING_LIBFOO #define LIBFOO_DLL_EXPORTED __declspec(dllexport) #elif defined _MSC_VER #define LIBFOO_DLL_EXPORTED __declspec(dllimport) #else #define LIBFOO_DLL_EXPORTED #endif @end smallexample =============================================================================== _______________________________________________ bug-gnulib mailing list bug-gnulib@gnu.org http://lists.gnu.org/mailman/listinfo/bug-gnulib