DLL loading not initializing C ++ static classes

I have a dll that loads at runtime. The DLL relies on a static variable for internal operations (this is std :: map), this variable is defined in the DLL.

When I call the first function from the DLL after loading, I get SegFault from the DLL, the card was never initialized. From everything that I read from DLL Loading, static and global data initialization should occur before DLLMain is called.

To test static initialization, I added a static structure that displays a message and even throws a breakpoint for good measure.

static struct a { a(void) { puts("Constructing\n"); } }statica; 

There was no message or there was no break before calling the DLLMain or function.

Here is my download code:

  dll = LoadLibrary("NetSim"); //Error Handling ChangeReliability = reinterpret_cast<NetSim::ChangeReliability> (GetProcAddress(dll, "ChangeReliability")); ChangeReliability(100); 

I checked that the dll version is correct, recompiled the entire project several times, but it makes no difference. I am fresh from ideas.

+6
c ++ static dll winapi
source share
5 answers

When you linked your DLL, was the / ENTRY switch specified? If so, it will override the linker default, that is, DllMainCRTStartup. As a result, _CRT_INIT will not be called and, in turn, global initializers will not be called, which will lead to uninitialized global (static) data.

If you specify / ENTRY for your own entry point, you need to call _CRT_INIT during the process attach and process detach.

If you did not specify / ENTRY, the linker should use a CRT entry point that calls _CRT_INIT when connecting / disconnecting a process before calling it in your DllMain.

+8
source share

I would like to point out that complex static objects in the DLL should be avoided.

Remember that static intializers in a DLL are called from DllMain, and therefore all restrictions on DllMain code apply to the constructors and destructors of static objects. In particular, std :: map can allocate dynamic memory during construction, which can lead to unpredictable results if the C ++ runtime is not yet initialized (in case you use a dynamically linked runtime).

There is a good article about writing DllMain: Recommendations for creating DLLs .

I would suggest changing the static map object to a static pointer (which can be safely initialized with zero) and either add a separate DLL-exported function for initialization, or use lazy initialization (that is, check the pointer before accessing it and create an object if it is null).

+8
source share

In fact, most likely you are making the wrong assumption:

Loading, static and global data initialization must occur before calling DLLMain.

See clause 4 of effective C ++:

Initialization order non-local static objects defined in different translation units undefined

The reason is that it is not possible to guarantee the β€œcorrect” initialization order, so the C ++ standard just refuses it, and just leave it undefined.

So, if your DllMain is in a different file than the code where the static variable is declared, the behavior is undefined, and you have very good chances of getting a DllMain call before the initialization is done.

The solution is quite simple and described in the same element of Effective C ++ (by the way, I highly recommend that you read this book!) And requires the declaration of a static variable inside the function, which simply returns it.

+1
source share

Although I'm not sure why the initialization failed, one way would be to explicitly create and initialize the variables in your DllMain. According to Microsoft's best practices recommendation, you should avoid using the CRT allocation function in DllMain, so instead I use the Windows heap allocation functions using my own allocator (note: unverified code, but should be more or less correct):

 template<typename T> struct HeapAllocator { typedef T value_type, *pointer, &reference; typedef const T *const_pointer, &const_reference; typedef size_t size_type; typedef ptrdiff_t difference_type; HeapAllocator() throw() { } HeapAllocator(const HeapAllocator&) throw() { } typedef<typename U> HeapAllocator(const HeapAllocator<U>&) throw() { } pointer address(reference x) const { return &x; } const_pointer address(const_reference x) const { return &x; } pointer allocate(size_type n, HeapAllocator<void>::const_pointer hint = 0) { LPVOID rv = HeapAlloc(GetProcessHeap(), 0, n * sizeof(value_type)); if (!rv) throw std::bad_alloc(); return (pointer)rv; } void deallocate(pointer p, size_type n) { HeapFree(GetProcessHeap(), 0, (LPVOID)p); } size_type max_size() const throw() { // Make a wild guess... return (2 * 1024 * 1024 * 1024) / sizeof(value_type); } void construct(pointer p, const_reference val) { new ((void*)p) T(val); } void destroy(pointer p) { p->~T(); } }; std::map<foo, HeapAllocator> *myMap; BOOL WINAPI DllMain(HANDLE hInst, ULONG ul_reason, LPVOID lpReserved) { switch(ul_reason) { case DLL_PROCESS_ATTACH: myMap = (std::map<foo, HeapAllocator> *)HeapAlloc(GetProcessHeap(), 0, sizeof(*myMap)); if (!myMap) return FALSE; // failed DLL init new ((void*)myMap) std::map<foo, HeapAllocator>; break; case DLL_PROCESS_DETACH: myMap->~map(); HeapFree(GetProcessHeap(), 0, (LPVOID)myMap); break; } return TRUE; } 
0
source share

The "classic" simple execution in a single mode will work:

 std::map<Key,Value>& GetMap() { static std::map<Key,Value> the Map; return theMap; } 

Of course, you should not call it from DllMain.

0
source share

All Articles