DLL Global Object Constructor String Error

Hello,

This one has really got my head spinning. I cannot figure it out.

I have a c++ console application that loads a dll.
In that dll there is a global variable that is a user defined object.
Upon the dll being loaded the dll calls the object constructor to instantiate the global variable (as expected).
In the constructor I try to use a const string which I have declared in an included file, but I get an error.
The error I recieve is a SIGSEGV, which to me usually means I am referencing an array past its bounds.

I have recreated this in the following simplified files. There are two projects 1) quickhook which calls the dll quickdll.dll, and 2) quickdll which is the dll loaded in quickhook.

quickhook's main.cpp

#include <iostream>
#include <windows.h>

using namespace std;

int main()
{
 
  const string QUICKDLL_DLL = "E:\\C++\\codeblocks\\Practice\\quickdll\\bin\\Debug\\quickdll.dll";  

  if (!(hinstDLL = LoadLibrary((LPCTSTR)QUICKDLL_DLL.c_str()))) {
    DWORD dwErr1 = ::GetLastError();
    cout << "Loading Library Failed:" << dwErr1 << endl;
  }
	
	//OTHER CODE	
	
    return 0;
}

quickdll's main.cpp

#include "main.h"
#include <string>
#include "myclass.h"

using namespace std;

MyClass myclass;

quickdll's myclass.h

#ifndef MYCLASS_H_INCLUDED
#define MYCLASS_H_INCLUDED

#include <string>

using namespace std;

class MyClass {
  public:
    string str;
    MyClass();
};

#endif // MYCLASS_H_INCLUDED

quickdll's myclass.cpp

#ifndef MYCLASS_CPP_INCLUDED
#define MYCLASS_CPP_INCLUDED

#include "myclass.h"
#include <string>
#include "const.h"

using namespace std;

MyClass::MyClass() {
  this->str = USER_SN;
}

#endif // MYCLASS_CPP_INCLUDED

quickdll's const.h

#ifndef CONST_H_INCLUDED
#define CONST_H_INCLUDED

#include <string>

using namespace std;

//User Constant
const string USER_SN = "MEMEME";

#endif // CONST_H_INCLUDED

The following is the files used

Here is the backtrace from,
#0 712CAD96 std::string::assign() (E:\C++\codeblocks\Practice\quickdll\bin\Debug\quickdll.dll:??)
#1 712CCD45 std::string::operator=() (E:\C++\codeblocks\Practice\quickdll\bin\Debug\quickdll.dll:??)
#2 712C1291 MyClass::MyClass(this=0x712d0030) (E:/C++/codeblocks/Practice/quickdll/myclass.cpp:12)
#3 712C1421 __static_initialization_and_destruction_0(__initialize_p=1, __priority=65535) (E:/C++/codeblocks/Practice/quickdll/main.cpp:9)
#4 712C1458 global constructors keyed to myclass() (E:/C++/codeblocks/Practice/quickdll/main.cpp:10)
#5 712C605F __do_global_ctors() (E:\C++\codeblocks\Practice\quickdll\bin\Debug\quickdll.dll:??)
#6 712C10E1 DllMainCRTStartup@12() (E:\C++\codeblocks\Practice\quickdll\bin\Debug\quickdll.dll:??)
#7 7C90118A ntdll!LdrSetAppCompatDllRedirectionCallback() (C:\WINDOWS\system32\ntdll.dll:??)
#8 712C0000 ??() (??:??)
#9 00000001 ??() (??:??)
#10 00000000 ??() (??:??)


When I debug this I get the sigsegv with the above back trace.

What is going on here? This one has me losing my hair... What "lesson" from programming 101 did I miss?

Thanks,

Dani AI

Generated

Short diagnosis and why it crashed
This is the classic static-initialization-order problem. The global MyClass instance is being constructed in one translation unit before the global std::string that MyClass’s constructor reads has been constructed in another translation unit. When MyClass does the assignment it ends up calling std::string::assign on an object whose internals aren’t set up yet, producing the SIGSEGV you saw. That also explains ’s observation that a plain int worked (trivial/constant initialization) while a std::string did not.

Practical fixes (safe and portable)

  • Prefer "construct-on-first-use" for globals that depend on nontrivial objects. Example pattern:
const std::string& user_sn() {
    static const std::string s("MEMEME");
    return s;
}

Then use user_sn() inside MyClass so the string is initialized on first use (well-defined order). In modern C++ this is thread-safe; in older toolchains it still avoids the cross-TU ordering issue.

  • Another robust approach is explicit initialization: remove nontrivial globals and call an init function after LoadLibrary (or from main) that constructs what you need, or pass the value into MyClass’s constructor from a place that you control.

Notes, cautions and extra pointers

  • As mentioned, compiler-specific tweaks (init_priority, attributes) can reorder initialization, but they’re nonportable and fragile—avoid them unless you need a short-term hack.
  • In DLL code be careful with global constructors and DllMain: avoid doing complex initialization inside DllMain and prefer explicit initialization after the DLL is loaded (as hinted).
  • If you need a single global instance visible across TUs, declare it extern in the header and define it in one source file; otherwise your header-level const may produce per-TU copies.

Quick troubleshooting
Add simple logging inside each constructor/initializer (or break on the constructors) to confirm the run-time order. That will quickly show which object is being built first.

Recommended Answers

All 5 Replies

what compiler are you using? I used VC++ 2008 Express and could not duplicate your problem. The only problem I had with your code is that hinstDLL was undefined. I even added cout statement in MyClass constructor -- it displayed ok too.

The dll file you did not post was this:

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
					 )
{
	switch (ul_reason_for_call)
	{
	case DLL_PROCESS_ATTACH:
	case DLL_THREAD_ATTACH:
	case DLL_THREAD_DETACH:
	case DLL_PROCESS_DETACH:
		break;
	}
	return TRUE;
}

Hello,

I am using code::blocks with mingw32 gcc 3.4.5

I do not need a dll entry point to duplicate the error.

Also, I have replicated this with non-dll implementation.

#include <iostream>
#include "myclass.h"

using namespace std;

  MyClass myclass;

int main()
{
    cout << "Hello world!" << endl;
    return 0;
}

Could this have to do with the "order" of instantiation. For instance, the string object has not been instantiated before the global MyClass object gets instantiated? I tried naming the MyClass ZyClass, b/c 'z' comes after 's', but this did not do anything. I am guessing my compiler loads up the MyClass stuff first b/c it is in the project root, and loads up the string stuff afterwards because it is not.
I am thinking this way because:
1) I do not experience this issue with built in c++ types such as 'int'.

const int USER_SN = 3;

2) I do not experience this issue with another user defined object

const MyClass2 USER_SN;

where,

class MyClass2 {
  public:
    int a;
    MyClass2();
};

MyClass2::MyClass2() {
  this->a = 34;
}

Honestly, I haven't a clue...

I have CodeBlocks but it won't compile anything -- produces this error:
mingw32-g++.exe: installation problem, cannot exec `cc1plus': No such file or directory

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.