From 659635d90ba86841af96fed82c1226ae32e9a2cc Mon Sep 17 00:00:00 2001 From: Bruno Haible Date: Mon, 25 Jul 2005 12:07:43 +0000 Subject: [PATCH] New gnulib module 'visibility'. --- ChangeLog | 5 ++ MODULES.html.sh | 1 + doc/ChangeLog | 4 ++ doc/visibility.texi | 146 ++++++++++++++++++++++++++++++++++++++++++++++++++++ m4/ChangeLog | 4 ++ m4/visibility.m4 | 52 +++++++++++++++++++ modules/visibility | 21 ++++++++ 7 files changed, 233 insertions(+) create mode 100644 doc/visibility.texi create mode 100644 m4/visibility.m4 create mode 100644 modules/visibility diff --git a/ChangeLog b/ChangeLog index 40245ce21..b3a16d27c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2005-07-24 Bruno Haible + + * modules/visibility: New file. + * MODULES.html.sh (Misc): Add visibility. + 2005-07-22 Bruno Haible * modules/alloca-opt (Makefile.am): Remove explicit dependency on diff --git a/MODULES.html.sh b/MODULES.html.sh index 4e88998aa..f786b3cfa 100755 --- a/MODULES.html.sh +++ b/MODULES.html.sh @@ -2004,6 +2004,7 @@ func_all_modules () func_module readutmp func_module sig2str func_module sysexits + func_module visibility func_end_table } diff --git a/doc/ChangeLog b/doc/ChangeLog index e4b32e36e..6b63c70f5 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,3 +1,7 @@ +2005-07-24 Bruno Haible + + * visibility.texi: New file. + 2005-07-16 Simon Josefsson * gnulib.texi (Library version handling): Add ELF symbol collision diff --git a/doc/visibility.texi b/doc/visibility.texi new file mode 100644 index 000000000..23f07566a --- /dev/null +++ b/doc/visibility.texi @@ -0,0 +1,146 @@ +@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 diff --git a/m4/ChangeLog b/m4/ChangeLog index cce430ee9..3dfeec002 100644 --- a/m4/ChangeLog +++ b/m4/ChangeLog @@ -1,3 +1,7 @@ +2005-07-24 Bruno Haible + + * visibility.m4: New file. + 2005-07-18 Bruno Haible * lock.m4: New file, from GNU gettext. diff --git a/m4/visibility.m4 b/m4/visibility.m4 new file mode 100644 index 000000000..2ff6330aa --- /dev/null +++ b/m4/visibility.m4 @@ -0,0 +1,52 @@ +# 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.]) +]) diff --git a/modules/visibility b/modules/visibility new file mode 100644 index 000000000..4c0dc9130 --- /dev/null +++ b/modules/visibility @@ -0,0 +1,21 @@ +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 + -- 2.11.0