What Happens to Global Variables Declared in a Dll

What happens to global variables declared in a DLL?

In a Windows C++ DLL, all global objects (including static members of classes) will be constructed just before the calling of the DllMain with DLL_PROCESS_ATTACH, and they will be destroyed just after the call of the DllMain with DLL_PROCESS_DETACH.

Now, you must consider three problems:

0 - Of course, global non-const objects are evil (but you already know that, so I'll avoid mentionning multithreading, locks, god-objects, etc.)

1 - The order of construction of objects or different compilation units (i.e. CPP files) is not guaranteed, so you can't hope the object A will be constructed before B if the two objects are instanciated in two different CPPs. This is important if B depends on A. The solution is to move all global objects in the same CPP file, as inside the same compilation unit, the order of instanciation of the objects will be the order of construction (and the inverse of the order of destruction)

2 - There are things that are forbidden to do in the DllMain. Those things are probably forbidden, too, in the constructors. So avoid locking something. See Raymond Chen's excellent blog on the subject:

  • Some reasons not to do anything scary in your DllMain
  • Another reason not to do anything scary in your DllMain: Inadvertent deadlock
  • Some reasons not to do anything scary in your DllMain, part 3

In this case, lazy initialization could be interesting: The classes remain in an "un-initialized" state (internal pointers are NULL, booleans are false, whatever) until you call one of their methods, at which point they'll initialize themselves. If you use those objects inside the main (or one of the main's descendant functions), you'll be ok because they will be called after execution of DllMain.

3 - Of course, if some global objects in DLL A depend on global objects in DLL B, you should be very very careful about DLL loading order, and thus dependancies. In this case, DLLs with direct or indirect circular dependancies will cause you an insane amount of headaches. The best solution is to break the circular dependancies.

P.S.: Note that in C++, constructor can throw, and you don't want an exception in the middle of a DLL loading, so be sure your global objects won't be using exception without a very, very good reason. As correctly written destructors are not authorized to throw, the DLL unloading should be ok in this case.

using global variable in dll and exe

You're violating the One Definition Rule (§ 3.2) by having that global variable definition in a header file. Instead you were correct to only declare it in a header file with extern and then have the definition in a single implementation file.

But in order to have this work with dlls you also have to declare it as exported by the exe and imported by the dlls with __declspec(dllexport) and __declspec(dllimport), using appropriate macros to choose the right __declspec depending on whether you're compiling the exe or the dlls.

What happens to global and static variables in a shared library when it is dynamically linked?

This is a pretty famous difference between Windows and Unix-like systems.

No matter what:

  • Each process has its own address space, meaning that there is never any memory being shared between processes (unless you use some inter-process communication library or extensions).
  • The One Definition Rule (ODR) still applies, meaning that you can only have one definition of the global variable visible at link-time (static or dynamic linking).

So, the key issue here is really visibility.

In all cases, static global variables (or functions) are never visible from outside a module (dll/so or executable). The C++ standard requires that these have internal linkage, meaning that they are not visible outside the translation unit (which becomes an object file) in which they are defined. So, that settles that issue.

Where it gets complicated is when you have extern global variables. Here, Windows and Unix-like systems are completely different.

In the case of Windows (.exe and .dll), the extern global variables are not part of the exported symbols. In other words, different modules are in no way aware of global variables defined in other modules. This means that you will get linker errors if you try, for example, to create an executable that is supposed to use an extern variable defined in a DLL, because this is not allowed. You would need to provide an object file (or static library) with a definition of that extern variable and link it statically with both the executable and the DLL, resulting in two distinct global variables (one belonging to the executable and one belonging to the DLL).

To actually export a global variable in Windows, you have to use a syntax similar to the function export/import syntax, i.e.:

#ifdef COMPILING_THE_DLL
#define MY_DLL_EXPORT extern "C" __declspec(dllexport)
#else
#define MY_DLL_EXPORT extern "C" __declspec(dllimport)
#endif

MY_DLL_EXPORT int my_global;

When you do that, the global variable is added to the list of exported symbols and can be linked like all the other functions.

In the case of Unix-like environments (like Linux), the dynamic libraries, called "shared objects" with extension .so export all extern global variables (or functions). In this case, if you do load-time linking from anywhere to a shared object file, then the global variables are shared, i.e., linked together as one. Basically, Unix-like systems are designed to make it so that there is virtually no difference between linking with a static or a dynamic library. Again, ODR applies across the board: an extern global variable will be shared across modules, meaning that it should have only one definition across all the modules loaded.

Finally, in both cases, for Windows or Unix-like systems, you can do run-time linking of the dynamic library, i.e., using either LoadLibrary() / GetProcAddress() / FreeLibrary() or dlopen() / dlsym() / dlclose(). In that case, you have to manually get a pointer to each of the symbols you wish to use, and that includes the global variables you wish to use. For global variables, you can use GetProcAddress() or dlsym() just the same as you do for functions, provided that the global variables are part of the exported symbol list (by the rules of the previous paragraphs).

And of course, as a necessary final note: global variables should be avoided. And I believe that the text you quoted (about things being "unclear") is referring exactly to the platform-specific differences that I just explained (dynamic libraries are not really defined by the C++ standard, this is platform-specific territory, meaning it is much less reliable / portable).

Visual C++: Is global variables initialization order deterministic during DLL loading?

I'm aware of the fact that C++ standard doesn't specify the order of
global variables initialization.
To be precise, it does when the global variables are within a single translation unit:

Ordered dynamic initialization, which applies to all other non-local
variables: within a single translation unit, initialization of these
variables is always sequenced in exact order their definitions
appear in the source code.

Your DLL code above, where you have two different global variables within two different translation units, will result in two different .OBJ files before link. Then, when the .OBJ files are linked together to form a .DLL, the C++ "pre-main" runtime code will be attached to the .DLL. When the .DLL is bound to the address space of the process by process-launch or by LoadLibrary, this stub code will have access to a table within your DLL, which, before invocation of DllMain, it will iterate-through, invoking link-time-synthesized static non-member functions in the table, each non-member function having the job of running a class member constructor in the table for the corresponding global object. Naturally, during removal of DLL from your process address space, either by process-exit or by FreeLibrary, the non-member functions will be similarly invoked, but in reverse (LIFO) order.

Given that this table is "baked" into the .DLL by LINK.EXE, the order of construction for global variables within the DLL, whether they are from same translation unit, or different, will be predetermined. It will not be predictable before link-time, as you noted, but whatever it becomes after link-time, that is what it will remain for the life of the .DLL because only LINK.EXE has the ability to construct that global variable constructor table, and once it is constructed, it is constructed.

If anyone is wondering which comes first: the construction of global variables, or the programmer-supplied DllMain, it is the former. The C++ run-time code is what invokes the programmer-supplied DllMain, as can be seen in the link provided by @Algirdas Preidžius.

Is global variable in a shared library / dll, shared across process

By default, no, global variables are not shared across processes.

However, you can use a data segment (data_seg) in order to share global variables across processes. You can find more information on MSDN in the article titled "How do I share data in my DLL with an application or with other DLLs?"

Where are global variables of a DLL stored in memory?

VB6 uses thread local storage for module-level variables, not data segements. What this means is that public (global) variables in a module can have different values per different threads. Which is not what a C/C++ developer is used to.

Shared global variable with DLL not working

add export modifier to class definition and export the whole class.

In dll and lib build define QUERY_DECLSPEC=export , in exe build as import :

class QUERY_DECLSPEC Marina
{
public:
static Marina* get_marina();

protected:
static Marina* marina_instance;
};

Using global variables in a C++ DLL

Well, you labelled it static, so that's what happens. Instead, label it extern in headers and define it in one TU.

And don't modify stdafx; it's not yours. Use your own shared headers.



Related Topics



Leave a reply



Submit