Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Loading DLL not initializing static C++ classes

I have a DLL that is being loaded at run-time. The DLL relies on a static variable for internal workings (it is a std::map), this variable is defined within the DLL.

When I call the first function from the DLL after loading, I get a SegFault from the DLL, the map was never initialized. From everything I have read from DLL Loading, static and global data initialization should happen before even the call to DLLMain.

To test static initialization I added a static struct that prints out a message, and even threw in a breakpoint for good measure.

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

There was no message, or break before DLLMain or the function is called.

Here is my loading code:

  dll = LoadLibrary("NetSim");
  //Error Handling

  ChangeReliability   = reinterpret_cast<NetSim::ChangeReliability>
                        (GetProcAddress(dll, "ChangeReliability"));


ChangeReliability(100);

I have verified that the dll version is the correct one, rebuilt the entire project multiple times, but no difference. I am fresh out of ideas.

like image 325
Ramon Zarazua B. Avatar asked Feb 25 '11 07:02

Ramon Zarazua B.


3 Answers

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

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

If you are not specifying /ENTRY, the linker should be using the CRT's entry point which calls _CRT_INIT on process attach/detach before calling into your DllMain.

like image 74
Peter Huene Avatar answered Oct 01 '22 05:10

Peter Huene


I'd like to point out that complex static objects in DLLs should be avoided.

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

There is a good article on writing DllMain: Best Practices for Creating DLLs.

I would suggest changing your static map object to a static pointer (which can be safely zero-initialized), and either adding a separate DLL-exported function for initialization, or using lazy initialization (i.e. check the pointer before accessing it and create the object if it's null).

like image 45
atzz Avatar answered Oct 01 '22 05:10

atzz


Actually, chances are you are making a wrong assumption:

Loading, static and global data initialization should happen before even the call to DLLMain.

See item 4 of Effective C++:

The order of initialization of non-local static objects defined in different translation units is undefined

The reason is that ensuring a "correct" initialization order is impossible, and therefore the C++ standard simply gives up on it, and just leave that as 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 DllMain called before the initialization is actually done.

Solution is quite simple, and outlined in the same item of Effective C++ (btw: I strongly recommend you reading that book!), and requires declaring the static variable inside a function, that simply returns it.

like image 29
rob Avatar answered Oct 01 '22 05:10

rob