18

Say I have one executable: app.exe

I use 2 different 3rd party DLLs in this executable: foo.dll bar.dll and the Application must link implicitly to these DLLs, that is I cannot use ::LoadLibrary to load them.

(Note: It's not that I cannot call LoadLibrary, but these DLLs require static linking (C++ DLLs with __declspec(dllexport)), so me calling LoadLibrarydoesn't make any sense because the excutable loader has already called it.)

These two DLLs do not have any dependencies on each other, that is, their load order is undefined as far as I can tell (and should be irrelevant). (Dependencies of both are basically only on the standard windows dlls (kernel32, msvcrt, etc.)

I now have the problem that I wish to control the load order of these DLLs, that is I wish that foo.dll is always loaded (DLL_PROCESS_ATTACH) before bar.dll.

Is it somehow possible to tell the Windows DLL Loader to load one DLL before another?

Edit: To check the DLL load order of an executable, one can use the DUMPBIN.exe utility: (Just launch the Visual Studio Command Prompt)

Edit: As per this answer / this blog entry, the NT Loader does walk the import section sequentially. (Which will result in independent DLLs being loaded in the order they appear in the import section.)

C:\path\to\program> dumpbin /IMPORTS app.exe | grep -i \.dll
  MSVCR80D.dll
  KERNEL32.dll
  OLEAUT32.dll
  MSVCP80D.dll
  foo.dll
  bar.DLL

This output means that MSVCR80D.dll (and its dependecies[a]) will be loaded first and that bar.DLL will be loaded last. Unload will happen in reverse order.

What I haven't found out yet is how to influence this load order ...


(Notes)

[a] : This means of course that e.g. kernel32.dll will be loaded first, because msvcr80d.dll will depend on kernel32.dll.


As per some requests, I'm adding a rationale for this: (But please, I'm still interested in this generally. I know how to work around the MFC problem.)

The Microsoft MFC DLL in it's debug version has memory leak detection built in. (As far as I can tell, it's the same mechanism used by _CrtSetDbgFlag and related tools.)

The MFC debug DLL will dump all unfreed memory when it is unloaded. Now, if you have a second DLL in your process, that is independent of MFC, and this second DLL deallocates memory on DLL_PROCESS_DETACH, the MFC reporting mechanism will report false memory leaks, if the MFC DLL is unloaded before the other dll.

If one could make sure that the debug MFC DLL is loaded first / unloaded last of all independent DLLs, then all other DLLs would already have cleaned up after themselves and MFC wouldn't report false leaks.

hft
  • 1,245
  • 10
  • 29
Martin Ba
  • 37,187
  • 33
  • 183
  • 337
  • 1
    Your edit is incorrect. Of course `kernel32.dll` is not going to be loaded after a lowly CRT DLL (`msvcr*.dll`) :P You can use [ProcMon](http://technet.microsoft.com/en-us/sysinternals/bb896645) or WinDbg to find out. – kizzx2 Jun 16 '11 at 13:00
  • @kizzx2 - **(and its dependecies)**. I'll explicitly add that bit about kernel.dll to make sure everyone gets it. – Martin Ba Jun 16 '11 at 13:19
  • 2
    @Martin Dumpbin does not list its imports in load order. I don't know where you got that idea from. – David Heffernan Jun 16 '11 at 13:29
  • @Martin You asserted that it dumpbin lists in load order. Where's your evidence to back that up? – David Heffernan Jun 16 '11 at 15:47
  • Actually the reference of kernel32.dll is artificially handled early together with ntdll.dll because every single Win32 process relies on kernel32.dll for subsystem support. – 0xC0000022L Jun 16 '11 at 16:02
  • 2
    @David: According to the [MS blog linked to](http://blogs.msdn.com/b/mgrier/archive/2005/06/18/430409.aspx) in the other questions answer, the Loader does in fact walk the import table in sequential order. – Martin Ba Jun 17 '11 at 10:13
  • Why would the _reference to_ kernel32.dll need artificial handling? The normal procedure to handle references starts by checking if the DLL is already loaded (which kernel32 will be). – MSalters Jun 17 '11 at 10:21
  • @Martin Walk order and load order are different due to dependencies – David Heffernan Jun 17 '11 at 10:22
  • 2
    @David: How often do I need to repeat the word *independent*? – Martin Ba Jun 17 '11 at 10:49
  • 3
    @Martin The question is much better now that you have given some context and explained why it is that you want to influence the load order. I think your big problem is that you don't appreciate that we like to be motivated by such rationale. Even in your edit you are very grudgingly giving the reasons behind the question. Don't be like that. Tell us why you are asking the question. The more information you give, the more motivated will be your potential answerers and you will likely get better answers. Now that your question is clear, I deleted my answer, for example. – David Heffernan Jun 17 '11 at 11:01

4 Answers4

7

What I haven't found out yet is how to influence this load order ...

I have no clue why I hadn't tried this, but it seems the import section order of the resulting module does depend on the order in which the lib files are provided to the linker.

Configuration Properties -> Linker -> Additional Dependencies ...

The lib files listed here first are also first in the import section, meaning the loader will import these in order (modulo dependencies).

So, to answer that part: Just provide the lib files in the correct order to the linker.

Note: I have tried that on VS2005 and it appears to work. I don't know whether that is documented somewhere or if it changed in newer versions of VC++.


Update: While it worked back then, today I hit the case that the load order was not to be influenced by the linker command line order of the lib files. (Still) No clue why. (Still VS2005)

I have however managed to make it work by adding the problematic DLLs to the list of delay loaded DLLs (like in Macke's answer).


Community
  • 1
  • 1
Martin Ba
  • 37,187
  • 33
  • 183
  • 337
6

Here's an idea: How about marking them as "Delay Loaded dlls" in the linker options of app.exe?

Delay-loading will allow you to link "statically" (i.e. without LoadLibrary() et.al) but will not load the DLL and do the linking until it's actually needed.

If that is an option, then (assuming you can wait so long, i.e. do not access foo/bar dll functions before main()), you could, in main(), access a function (just fetch a function ptr or something) in foo.dll first, which would load it and bind all "statically" linked functions?

(Maybe LoadLibrary() triggers the same link-when-needed procedure. Not sure. It would look cleaner in your code though.)

Macke
  • 24,812
  • 7
  • 82
  • 118
  • 1
    Ehrm, guess what the delay-load helper does internally? Yep, uses `LoadLibrary` bad advice. Of course the OP has not explained why LoadLibrary must not be used. Also, delay-loading is a tad bit more expensive, because it uses SEH to catch attempts to call a function whose pointer has not been resolved. However, this can be countered by invoking library functions at the beginning of the program. Side-note: one can override the default delay-load helper and write a custom one. – 0xC0000022L Jun 16 '11 at 12:42
  • Using delay load would result in a call to LoadLibrary from DLLMain and that would dead lock on the loader lock – David Heffernan Jun 16 '11 at 12:52
  • Good idea. This does help with the load order. I'll have to check if it's working with my specific setup. -- Although I rather consider the delay load approach a workaround than a real solution :-) – Martin Ba Jun 16 '11 at 13:22
  • 2
    @STATUS_ACCESS_DENIED: I assumed the OP want's to avoid `LoadLibrary()` because using `GetProcAddress()` to retrieve every function is incredibly cumbersome for most applications, so one prefers the import library instead. Using LoadLibrary just to load the delay-imported dll (as opposed to call a dummy function that returns 42) should be ok. – Macke Jun 16 '11 at 13:51
  • @David: No one said anything about initiating a library-load from `DLLMain()`. I'm talking about the proper `main()` or `WinMain()` – Macke Jun 16 '11 at 13:56
  • @Martin: Yeah, it's a workaround idea. If you can write a script to edit the exe file post-build like Ben suggests, that's probably better. – Macke Jun 16 '11 at 13:58
  • @Macke The question only makes sense if there is a dependency in one of the DLLMain routines. Otherwise why care which order the DLLs are loaded in? – David Heffernan Jun 16 '11 at 14:02
  • @David: There is a "dependency" in the sense that something needs to be done in foo.dll before bar.dll is loaded, but it does not necessary have to be LoadLibrary-related. (It could be that one foo.dll want's to know about all threads in the application, and bar.dll creates some when loaded.) That ordering issue can be resolved outside DLLMain(). Maybe the OP can give more info to avoid us guessing here. – Macke Jun 16 '11 at 14:05
  • @Macke: I agree with David, though. Under normal circumstances such code would end up in `DLLMain()`. Making assumptions, too now. As I said, I think the OP should clarify. – 0xC0000022L Jun 16 '11 at 14:13
4

Just add foo.dll to the import table of bar.dll, the OS loader will handle the rest.

You should be able to do this without the source code for bar.dll, not sure if the editbin tool has such an option, but this is a fairly trivial edit to the PE file.

You might instead be able to use the registry setting that preloads DLLs, but I wouldn't do that, you don't want foo.dll getting loaded into other processes that don't need it.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • 2
    @David: We're told that `foo.dll` has no dependency on `bar.dll`, so there is no circular reference. – Ben Voigt Jun 16 '11 at 13:47
  • @Ben Yes I know that. The OP's question is not so interesting to me. What I'm interested in is if you know how the DLL loader resolves circular references. In this case, even if OP can get one DLL loaded before the other, it's plain wrong to require such a dependency in `DLLMain`. – David Heffernan Jun 16 '11 at 13:52
  • @David: No, I don't know how the loader deals with circular references. A sensible approach would be to memory-map both modules and then call the entry-points in an unspecified order, but I don't think that's guaranteed. If you have circular dependencies, you had better not do anything in DllMain of any DLL in the cycle. – Ben Voigt Jun 16 '11 at 13:56
  • 1
    @David: Interesting question actually, though quite theoretical. Gonna peek into NTDLL tonight and see what I can dig up. My guess would be that the defining factor is the order of the modules starting at the EXE, then for each DLL its own imports and so on. So more or less arbitrary as Ben said. – 0xC0000022L Jun 16 '11 at 15:26
  • `editbin` doesn't seem to have that option. I wonder how one could regard changing the PE file table be fairly trivial (if there's no existing tool to do just that) :P It's not like this is the thing most people deal with daily. – kizzx2 Jun 18 '11 at 16:16
  • @kizzx2: "editbin doesn't" is very different from "no existing tool". There are plenty of tools which can be found if one looks beyond the official Microsoft SDK. Try http://www.woodmann.com/collaborative/tools/index.php/Category:Import_Editors for a list – Ben Voigt Mar 13 '15 at 19:43
2

If you don't link the import library (foo.lib & bar.lib), then the loader will not automatically load the DLLs upon startup and you can call LoadLibrary() whenever you want.

Ferruccio
  • 98,941
  • 38
  • 226
  • 299
  • Delay-loading is equally convenient ;) – 0xC0000022L Jun 16 '11 at 15:23
  • 1
    Actually, when it's applicable, I would say that delay-loading is much more convenient. But sometimes, you don't know until run-time what DLLs you are going to need to load or where they are going to be located. – Ferruccio Jun 16 '11 at 15:45
  • fair point, but even those cases can be dynamically handled in the delay-load helper. Just override the default one and you can customize as much as you wish :) ... including code to locate the proper DLL ... – 0xC0000022L Jun 16 '11 at 16:04
  • I didn't realize you could do that. Nice! – Ferruccio Jun 16 '11 at 16:12
  • 1
    @0xC0000022L: My company simply renames the desired DLL to a specific name, and the specific named is delay-loaded :/ – Mooing Duck Mar 13 '15 at 17:19