libtool
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [RFC] w32 and Libtool.


From: Peter Rosin
Subject: Re: [RFC] w32 and Libtool.
Date: Thu, 14 Oct 2010 12:31:05 +0200
User-agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.9) Gecko/20100915 Thunderbird/3.1.4

Den 2010-10-13 20:50 skrev Ralf Wildenhues:
> Hi Peter,
> 
> * Peter Rosin wrote on Wed, Oct 13, 2010 at 08:19:27PM CEST:
>> Can you spot any errors?
> 
> See below.  I've only checked for things obvious to me; I hope somebody
> else verifies the w32 semantics and details.  ;-)
> 
> Thanks for writing this!
> 
>> (I have not actually tested the code samples. Yet)
> 
> Thanks in advance!  :-)

Should have done that before the OP.  Oh well.  Here's a 3rd attempt
with actual tested and working code.  The lessons I relearned during
testing should also have improved the descriptions in the text.



Windows DLLs.
-------------

This topic describes a couple of ways to portably create Windows Dynamic
Link Libraries (DLLs).  Libtool knows how to create DLLs using GNU tools
and using Microsoft tools.

A typical library has a "hidden" implementation with an interface
described in a header file.  On just about every system, the interface
could be something like this:

Example foo.h:

#ifndef FOO_H
#define FOO_H

int one (void);
int two (void);
extern int three;

#endif /* FOO_H */

And the implementation could be something like this:

Example foo.c:

#include "foo.h"

int one (void)
{
  return 1;
}

int two (void)
{
  return three - one ();
}

int three = 3;


When using contemporary GNU tools to create the Windows DLL, the above
code will work there too, thanks to its auto-import/auto-export
features.  But that is not the case when using older GNU tools or perhaps
more interesting when using proprietary tools.  In those cases the code
will need additional decorations on the interface symbols with
__declspec(dllimport) and __declspec(dllexport) depending on if the
library is built or if it's consumed and how it's built and consumed.
However, it should be noted that it would have worked also with
Microsoft tools, if only the variable three hadn't been there, due to
the fact the Microsoft tools will automatically import functions (but
sadly not variables) and Libtool will automatically export non-static
symbols as described next.

With Microsoft tools, Libtool will dig through the object files that
make up the library, looking for non-static symbols to automatically
export.  I.e., Libtool with Microsoft tools is trying to mimic the auto-
export feature of the contemporary GNU tools.  It should be noted that
the GNU auto-export feature is turned off when an explicit
__declspec(dllexport) is seen.  The GNU tools do this to not make more
symbols visible for projects that have already taken the trouble to
decorate symbols.  There is no similar way to limit which symbols are
visible in the code when Libtool is using Microsoft tools.  In order to
limit symbol visibility in that case you need to use one of the options
-export-symbols or -export-symbols-regex.

No matching help with auto-import is provided by Libtool, which is why
variables must be decorated to import them from a DLL for everything but
contemporary GNU tools.  As stated above, functions are automatically
imported by both contemporary GNU tools and Microsoft tools, but for
other proprietary tools the auto-import status of functions is unknown.

When the objects that form the library are built, there are generally
two copies built for each object.  One copy is used when linking the DLL
and one copy is used for the static library.  On Windows systems, a pair
of defines are commonly used to discriminate how the interface symbols
should be decorated.  The first define is -DDLL_EXPORT which is
automatically provided by Libtool when Libtool builds the copy of the
object that is destined for the DLL.  The second define is -DLIBFOO_BUILD
(or similar) which is often added by the package providing the library
and is used when building the library, but not when consuming the
library.

However, the matching double compile is not performed when consuming
libraries.  It is therefore not possible to reliably distinguish if the
consumer is importing from a DLL or if it is going to use a static
library.

With contemporary GNU tools, auto-import often saves the day, but see
the GNU ld documentation and its --enable-auto-import option for some
corner cases when it does not.

With Microsoft tools you typically get away with always compiling the
code such that variables are expected to be imported from a DLL and
functions are expected to be found in a static library.  The tools will
then automatically import the function from a DLL if that is where they
are found.  If the variables are not imported from a DLL as expected, but
are found in a static library that is otherwise pulled in by some
function, the linker will issue a warning (LNK4217) that a locally
defined symbol is imported, but it still works.  In other words, this
scheme will not work to only consume variables from a library.  There is
also a price connected to this liberal use of imports in that an extra
indirection is introduced when you are consuming the static version of
the library.  That extra indirection is always present when the DLL is
consumed, but it is not needed when consuming the static library.

For older GNU tools and other proprietary tools there is no generic way
to make it possible to consume either of the DLL or the static library
without user intervention, the tools need to be told what is intended.
One common assumption is that if a DLL is being built (DLL_EXPORT is
defined) then that DLL is going to consume any dependent libraries as
DLLs.  If that assumption is made everywhere, it is possible to select
how an end-user application is consuming libraries by adding a single
flag -DDLL_EXPORT when a DLL build is required.  This is of course an all
or nothing deal, either everything as DLLs or everything as static
libraries.

To sum up the above, the header file of the foo library needs to be
changed into something like this:

Modified foo.h:

#ifndef FOO_H
#define FOO_H

#if defined _WIN32 && !defined __GNUC__
# ifdef LIBFOO_BUILD
#  ifdef DLL_EXPORT
#   define LIBFOO_SCOPE            __declspec (dllexport)
#   define LIBFOO_SCOPE_VAR extern __declspec (dllexport)
#  endif
# elif defined _MSC_VER
#  define LIBFOO_SCOPE
#  define LIBFOO_SCOPE_VAR  extern __declspec (dllimport)
# elif defined DLL_EXPORT
#  define LIBFOO_SCOPE             __declspec (dllimport)
#  define LIBFOO_SCOPE_VAR  extern __declspec (dllimport)
# endif
#endif
#ifndef LIBFOO_SCOPE
# define LIBFOO_SCOPE
# define LIBFOO_SCOPE_VAR extern
#endif

LIBFOO_SCOPE     int one (void);
LIBFOO_SCOPE     int two (void);
LIBFOO_SCOPE_VAR int three;

#endif /* FOO_H */

When the targets are limited to contemporary GNU tools and Microsoft
tools, the above can be simplified to the following:

Simplified foo.h:

#ifndef FOO_H
#define FOO_H

#if defined _WIN32 && !defined __GNUC__ && !defined LIBFOO_BUILD
# define LIBFOO_SCOPE_VAR extern __declspec (dllimport)
#else
# define LIBFOO_SCOPE_VAR extern
#endif

int one (void);
int two (void);
LIBFOO_SCOPE_VAR int three;

#endif /* FOO_H */

This last simplified version can of course only work when Libtool is
used to build the DLL, as no symbols would be exported otherwise (i.e.,
when using Microsoft tools).

It should be noted that there are various projects that attempt to relax
these requirements by various low level tricks, but they are not
discussed here.  Examples are FlexDLL(1) and edll(2).

(1) http://alain.frisch.fr/flexdll.html
(2) http://edll.sourceforge.net/



reply via email to

[Prev in Thread] Current Thread [Next in Thread]