23
votes

I'm new to Windows programming and I've just "lost" two hours hunting a bug which everyone seems aware of: you cannot create an object on the heap in a DLL and destroy it in another DLL (or in the main program).

I'm almost sure that on Linux/Unix this is NOT the case (if it is, please say it, but I'm pretty sure I did that thousands of times without problems...).

At this point I have a couple of questions:

1) Do statically linked DLLs use a different heap than the main program?

2) Is the statically linked DLL mapped in the same process space of the main program? (I'm quite sure the answer here is a big YES otherwise it wouldn't make sense passing pointers from a function in the main program to a function in a DLL).

I'm talking about plain/regular DLL, not COM/ATL services

EDIT: By "statically linked" I mean that I don't use LoadLibrary to load the DLL but I link with the stub library

3
It depends on the settings for each module. Generally if two modules use the dynamic CRT then they share the heap since they both have the same instance of the CRT loaded. If a module uses the static CRT then it has its own heap since it has its own instance of the CRT statically linked in.Luke
@Luke - Additionally, it is possible for different modules to use different versions of the dynamic (DLL) CRT, and therefore different heaps.Bukes
Pretty much every non-trivial DLL will have to create its own heap, if you think about it. Take the OpenAL library as an example. You can feed data to a buffer object (the lib makes its own copy of the data), set some parameters, and the library will play sound -- great, easy, perfect, no worries. Now imagine two programs load the library. Where to put the data, who owns it? In what part of physical RAM is it? Do I want "some other program" being able to see (or modify) data on my program's heap? If it lives on your main module's heap, you're kind of in trouble...Damon
@Damon Having two different heaps doesn't mean to have 2 different virtual address spaces... I think that the second heap (the one used by the DLL) is still mapped in the main process address space. In your example I think the OpenAL still uses your main process memory, so there should be no accidental sharing. Otherwise there is something I'm really missing about DLLs here :)Emiliano

3 Answers

22
votes

DLLs / exes will need to link to an implementation of C run time libraries.

In case of C Windows Runtime libraries, you have the option to specify, if you wish to link to the following:

  1. Single-threaded C Run time library (Support for single threaded libraries have been discontinued now)
  2. Multi-threaded DLL / Multi-threaded Debug DLL
  3. Static Run time libraries.
  4. Few More (You can check the link)

Each one of them will be referring to a different heap, so you are not allowed pass address obtained from heap of one runtime library to other.

Now, it depends on which C run time library the DLL which you are talking about has been linked to. Suppose let's say, the DLL which you are using has been linked to static C run time library and your application code (containing the main function) has linked to multi-threaded C Runtime DLL, then if you pass a pointer to memory allocated in the DLL to your main program and try to free it there or vice-versa, it can lead to undefined behaviour. So, the basic root cause are the C runtime libraries. Please choose them carefully.

Please find more info on the C run time libraries supported here & here

A quote from MSDN:

Caution Do not mix static and dynamic versions of the run-time libraries. Having more than one copy of the run-time libraries in a process can cause problems, because static data in one copy is not shared with the other copy. The linker prevents you from linking with both static and dynamic versions within one .exe file, but you can still end up with two (or more) copies of the run-time libraries. For example, a dynamic-link library linked with the static (non-DLL) versions of the run-time libraries can cause problems when used with an .exe file that was linked with the dynamic (DLL) version of the run-time libraries. (You should also avoid mixing the debug and non-debug versions of the libraries in one process.)

8
votes

Let’s first understand heap allocation and stack on Windows OS wrt our applications/DLLs. Traditionally, the operating system and run-time libraries come with an implementation of the heap.

  1. At the beginning of a process, the OS creates a default heap called Process heap. The Process heap is used for allocating blocks if no other heap is used.
  2. Language run times also can create separate heaps within a process. (For example, C run time creates a heap of its own.)
  3. Besides these dedicated heaps, the application program or one of the many loaded dynamic-link libraries (DLLs) may create and use separate heaps, called private heaps
  4. These heap sits on top of the operating system's Virtual Memory Manager in all virtual memory systems.
  5. Let’s discuss more about CRT and associated heaps:
    1. C/C++ Run-time (CRT) allocator: Provides malloc() and free() as well as new and delete operators.
    2. The CRT creates such an extra heap for all its allocations (the handle of this CRT heap is stored internally in the CRT library in a global variable called _crtheap) as part of its initialization.
    3. CRT creates its own private heap, which resides on top of the Windows heap.
    4. The Windows heap is a thin layer surrounding the Windows run-time allocator(NTDLL).
    5. Windows run-time allocator interacts with Virtual Memory Allocator, which reserves and commits pages used by the OS.

Your DLL and exe link to multithreaded static CRT libraries. Each DLL and exe you create has a its own heap, i.e. _crtheap. The allocations and de-allocations has to happen from respective heap. That a dynamically allocated from DLL, cannot be de-allocated from executable and vice-versa.

What you can do? Compile our code in DLL and exe’s using /MD or /MDd to use the multithread-specific and DLL-specific version of the run-time library. Hence both DLL and exe are linked to the same C run time library and hence one _crtheap. Allocations are always paired with de-allocations within a single module.

4
votes

If I have an application that compiles as an .exe and I want to use a library I can either statically link that library from a .lib file or dynamically linked that library from a .dll file.

Each linked module (ie. each .exe or .dll) will be linked to an implementation of the C or C++ run times. The run times themselves are a library that can be statically or dynamically linked to and come in different threading configurations.

By saying statically linked dlls are you describing a set up where an application .exe dynamically links to a library .dll and that library internally statically links to the runtime? I will assume that this is what you mean.

Also worth noting is that every module (.exe or .dll) has its own scope for statics i.e. a global static in an .exe will not be the same instance as a global static with the same name in a .dll.

In the general case therefore it cannot be assumed that lines of code running inside different modules are using the same implementation of the runtime, furthermore they will not be using the same instance of any static state.

Therefore certain rules need to be obeyed when dealing with objects or pointers that cross module boundaries. Allocations and deallocations must be occur in the same module for any given address. Otherwise the heaps will not match and behaviour will not be defined.

COM solves this this using reference counting, objects delete themselves when the reference count reaches zero. This is a common pattern used to solve the matched location problem.

Other problems can exist, for instance windows defines certain actions e.g. how allocation failures are handled on a per thread basis, not on a per module basis. This means that code running in module A on a thread setup by module B can also run into unexpected behaviour.