libtool: Windows DLLs

1 
1 15.3.8 Windows DLLs
1 -------------------
1 
1 This topic describes a couple of ways to portably create Windows Dynamic
1 Link Libraries (DLLs).  Libtool knows how to create DLLs using GNU tools
1 and using Microsoft tools.
1 
1    A typical library has a "hidden" implementation with an interface
1 described in a header file.  On just about every system, the interface
1 could be something like this:
1 
1    Example 'foo.h':
1 
1      #ifndef FOO_H
1      #define FOO_H
1 
1      int one (void);
1      int two (void);
1      extern int three;
1 
1      #endif /* FOO_H */
1 
1 And the implementation could be something like this:
1 
1    Example 'foo.c':
1 
1      #include "foo.h"
1 
1      int one (void)
1      {
1        return 1;
1      }
1 
1      int two (void)
1      {
1        return three - one ();
1      }
1 
1      int three = 3;
1 
1    When using contemporary GNU tools to create the Windows DLL, the
1 above code will work there too, thanks to its auto-import/auto-export
1 features.  But that is not the case when using older GNU tools or
1 perhaps more interestingly when using proprietary tools.  In those cases
1 the code will need additional decorations on the interface symbols with
1 '__declspec(dllimport)' and '__declspec(dllexport)' depending on whether
1 the library is built or it's consumed and how it's built and consumed.
1 However, it should be noted that it would have worked also with
1 Microsoft tools, if only the variable 'three' hadn't been there, due to
1 the fact the Microsoft tools will automatically import functions (but
1 sadly not variables) and Libtool will automatically export non-static
1 symbols as described next.
1 
1    With Microsoft tools, Libtool digs through the object files that make
1 up the library, looking for non-static symbols to automatically export.
1 I.e., Libtool with Microsoft tools tries to mimic the auto-export
1 feature of contemporary GNU tools.  It should be noted that the GNU
1 auto-export feature is turned off when an explicit
1 '__declspec(dllexport)' is seen.  The GNU tools do this to not make more
1 symbols visible for projects that have already taken the trouble to
1 decorate symbols.  There is no similar way to limit what symbols are
1 visible in the code when Libtool is using Microsoft tools.  In order to
1 limit symbol visibility in that case you need to use one of the options
1 '-export-symbols' or '-export-symbols-regex'.
1 
1    No matching help with auto-import is provided by Libtool, which is
1 why variables must be decorated to import them from a DLL for everything
1 but contemporary GNU tools.  As stated above, functions are
1 automatically imported by both contemporary GNU tools and Microsoft
1 tools, but for other proprietary tools the auto-import status of
1 functions is unknown.
1 
1    When the objects that form the library are built, there are generally
1 two copies built for each object.  One copy is used when linking the DLL
1 and one copy is used for the static library.  On Windows systems, a pair
1 of defines are commonly used to discriminate how the interface symbols
1 should be decorated.  The first define is '-DDLL_EXPORT', which is
1 automatically provided by Libtool when 'libtool' builds the copy of the
1 object that is destined for the DLL. The second define is
1 '-DLIBFOO_BUILD' (or similar), which is often added by the package
1 providing the library and is used when building the library, but not
1 when consuming the library.
1 
1    However, the matching double compile is not performed when consuming
1 libraries.  It is therefore not possible to reliably distinguish if the
1 consumer is importing from a DLL or if it is going to use a static
1 library.
1 
1    With contemporary GNU tools, auto-import often saves the day, but see
1 the GNU ld documentation and its '--enable-auto-import' option for some
11 corner cases when it does not (⇒'--enable-auto-import'
 (ld)Options.).
1 
1    With Microsoft tools you typically get away with always compiling the
1 code such that variables are expected to be imported from a DLL and
1 functions are expected to be found in a static library.  The tools will
1 then automatically import the function from a DLL if that is where they
1 are found.  If the variables are not imported from a DLL as expected,
1 but are found in a static library that is otherwise pulled in by some
1 function, the linker will issue a warning (LNK4217) that a locally
1 defined symbol is imported, but it still works.  In other words, this
1 scheme will not work to only consume variables from a library.  There is
1 also a price connected to this liberal use of imports in that an extra
1 indirection is introduced when you are consuming the static version of
1 the library.  That extra indirection is unavoidable when the DLL is
1 consumed, but it is not needed when consuming the static library.
1 
1    For older GNU tools and other proprietary tools there is no generic
1 way to make it possible to consume either of the DLL or the static
1 library without user intervention, the tools need to be told what is
1 intended.  One common assumption is that if a DLL is being built
1 ('DLL_EXPORT' is defined) then that DLL is going to consume any
1 dependent libraries as DLLs.  If that assumption is made everywhere, it
1 is possible to select how an end-user application is consuming libraries
1 by adding a single flag '-DDLL_EXPORT' when a DLL build is required.
1 This is of course an all or nothing deal, either everything as DLLs or
1 everything as static libraries.
1 
1    To sum up the above, the header file of the foo library needs to be
1 changed into something like this:
1 
1    Modified 'foo.h':
1 
1      #ifndef FOO_H
1      #define FOO_H
1 
1      #if defined _WIN32 && !defined __GNUC__
1      # ifdef LIBFOO_BUILD
1      #  ifdef DLL_EXPORT
1      #   define LIBFOO_SCOPE            __declspec (dllexport)
1      #   define LIBFOO_SCOPE_VAR extern __declspec (dllexport)
1      #  endif
1      # elif defined _MSC_VER
1      #  define LIBFOO_SCOPE
1      #  define LIBFOO_SCOPE_VAR  extern __declspec (dllimport)
1      # elif defined DLL_EXPORT
1      #  define LIBFOO_SCOPE             __declspec (dllimport)
1      #  define LIBFOO_SCOPE_VAR  extern __declspec (dllimport)
1      # endif
1      #endif
1      #ifndef LIBFOO_SCOPE
1      # define LIBFOO_SCOPE
1      # define LIBFOO_SCOPE_VAR extern
1      #endif
1 
1      LIBFOO_SCOPE     int one (void);
1      LIBFOO_SCOPE     int two (void);
1      LIBFOO_SCOPE_VAR int three;
1 
1      #endif /* FOO_H */
1 
1    When the targets are limited to contemporary GNU tools and Microsoft
1 tools, the above can be simplified to the following:
1 
1    Simplified 'foo.h':
1 
1      #ifndef FOO_H
1      #define FOO_H
1 
1      #if defined _WIN32 && !defined __GNUC__ && !defined LIBFOO_BUILD
1      # define LIBFOO_SCOPE_VAR extern __declspec (dllimport)
1      #else
1      # define LIBFOO_SCOPE_VAR extern
1      #endif
1 
1      int one (void);
1      int two (void);
1      LIBFOO_SCOPE_VAR int three;
1 
1      #endif /* FOO_H */
1 
1    This last simplified version can of course only work when Libtool is
1 used to build the DLL, as no symbols would be exported otherwise (i.e.,
1 when using Microsoft tools).
1 
1    It should be noted that there are various projects that attempt to
1 relax these requirements by various low level tricks, but they are not
1 discussed here.  Examples are FlexDLL
1 (http://alain.frisch.fr/flexdll.html) and edll
1 (http://edll.sourceforge.net/).
1