Overview

When writing a shared library, it is sometimes useful to have a set of functions that get called when the library is loaded and unloaded. In Windows, this is done by implementing the DllMain function. This function is called by the loader whenever a DLL is loaded or unloaded into the address space of a process (and also when the process creates a new thread, but it is less common to handle this case). A value is passed in as an argument to the DllMain function that indicates which event is occurring: DLL load or unload.

On Linux, one must use the GCC __attribute__((constructor)) and __attribute__((destructor)) keywords (double underscores before and after) to explicitly declare functions to be called on load and unload. These keywords cause the compiler/linker to add the specified functions to the __CTOR_LIST__ and __DTOR_LIST__ (“ConstrucTOR LIST” and “DestrucTOR LIST” respectively) in the object file. Functions on the __CTOR_LIST__ are called by the loader when the library is loaded (either implicitly or by dlopen()). The main purpose for this list is to call the constructors on global objects in the library. Conversely, functions on the __DTOR_LIST__ are called when the library is unloaded (either implicitly or by dlclose()). By adding initialization and clean-up functions to this list, one can effectively replicate the DllMain functionality on Linux.

NOTE: There are many ways to “shoot yourself in the foot” with these methods (on both Windows and Linux) because certain things aren’t available to your library until loading is complete. Don’t use these methods unless you have a real need… just export an Initialize() and Destroy() function instead, and force the consuming application to call them. Please read the “Gotcha’s” section below.

External Resources:

Example: Linux

void __attribute__ ((constructor)) my_load(void);
void __attribute__ ((destructor)) my_unload(void);

// Called when the library is loaded and before dlopen() returns
void my_load(void)
{
    // Add initialization code…
}

// Called when the library is unloaded and before dlclose()
// returns
void my_unload(void)
{
    // Add clean-up code…
}

Example: Windows

// Return TRUE on success and FALSE if an error occurs. Returning
// FALSE will cause the library to be unloaded.
BOOL WINAPI DllMain
(
    HINSTANCE hinstDLL,
    DWORD fdwReason,
    LPVOID lpReserved
)
{
    switch (fdwReason)
    {
    case DLL_PROCESS_ATTACH:
        // Add initialization code…
        break;
    case DLL_PROCESS_DETACH:
        // Add clean-up code…
        break;
    }

    return (TRUE);
}

Gotcha’s

Linux

From www.dwheeler.com:

Shared libraries must not be compiled with the gcc arguments “-nostartfiles” or “-nostdlib”. If those arguments are used, the constructor/destructor routines will not be executed (unless special measures are taken).

Windows

From Microsoft’s MSDN page on DllMain:

The entry-point function should perform only simple initialization or termination tasks. It must not call the LoadLibrary or LoadLibraryEx function (or a function that calls these functions), because this may create dependency loops in the DLL load order. This can result in a DLL being used before the system has executed its initialization code. Similarly, the entry-point function must not call the FreeLibrary function (or a function that calls FreeLibrary) during process termination, because this can result in a DLL being used after the system has executed its termination code.

Because Kernel32.dll is guaranteed to be loaded in the process address space when the entry-point function is called, calling functions in Kernel32.dll does not result in the DLL being used before its initialization code has been executed. Therefore, the entry-point function can call functions in Kernel32.dll that do not load other DLLs. For example, DllMain can create synchronization objects such as critical sections and mutexes, and use TLS. Unfortunately, there is not a comprehensive list of safe functions in Kernel32.dll.

Windows 2000: Do not create a named synchronization object in DllMain because the system will then load an additional DLL. This restriction does not apply to subsequent versions of Windows.

Calling functions that require DLLs other than Kernel32.dll may result in problems that are difficult to diagnose. For example, calling User, Shell, and COM functions can cause access violation errors, because some functions load other system components. Conversely, calling functions such as these during termination can cause access violation errors because the corresponding component may already have been unloaded or uninitialized.

Because DLL notifications are serialized, entry-point functions should not attempt to communicate with other threads or processes. Deadlocks may occur as a result.

For information on best practices when writing a DLL, see http://www.microsoft.com/whdc/driver/kernel/DLL_bestprac.mspx.

If your DLL is linked with the C run-time library (CRT), the entry point provided by the CRT calls the constructors and destructors for global and static C++ objects. Therefore, these restrictions for DllMain also apply to constructors and destructors and any code that is called from them.

Tagged with:  

One Response to Implementing DllMain in a linux shared library

  1. Andreas Mohr says:

    I think a nice way to put this information to good use might be to have a simple header-based (header-only) implementation which contains cross-platform switch defines,
    makes use of __attribute__((constructor / destructor)) or DllMain() mechanisms as available, and – *and that’s the kicker* – then have these mechanisms call generic platform-agnostic .cpp-*local* functions for each of the load types (process init, thread attach, …). And then perhaps even make use of template SFINAE mechanisms to determine whether a locally provided class does contain members for specific init/uninit event types. Alternatively perhaps make use of a clever dlsym()/dladdr() to figure out whether a local library provides certain init functions. Or (perhaps best) have this header provide a macro to register a specific object of a class (also declared by the same header!), with some (not?) derived virtuals to be served by these mechanisms.

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>