Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to keep DLL in memory after calling process exits?

Tags:

c++

dll

qt

I have a DLL that takes 5 to 10 seconds to load, which means that I have to wait that long every time I compile and run the executable that uses it. Is there a way to keep the DLL loaded in memory so that it can be immediately accessed every time I compile the corresponding executable? I'm compiling the program on QT MinGW, if that's relevant.

EDIT: No luck so far. Loading the DLL on another program seems to have no effect (the original program still loads the DLL, and takes just as long to do so). I would guess I need to load the DLL and its functions differently if they've been loaded into another program, but I don't know how to do this. Right now I'm using LoadLibrary and GetProcAddress.

like image 254
SharpHawk Avatar asked Jun 04 '12 16:06

SharpHawk


1 Answers

I am not MinGW developer, but your question is very common and not really depend on how you create the DLL. Such problems will by typically solved with the usage of three techniques:

  • choosing of unique base addresses of the DLLs
  • binding the DLLs and exe (during installation of the application)
  • usage of DLL delay loading technique
  • a little improve the loading time the call of DisableThreadLibraryCalls inside of DLL_PROCESS_ATTACH part of DllMain

The exact switches of linker or other tools which you can use depend on your development environment.

To understand the problem you should know how executable or DLL will be loaded. First of all the EXE or DLL will be mapped in the memory. Memory mapped file (section) will be created which points to the EXE/DLL. So you will have some addresses in the process which access will corresponds to the EXE/DLL file. If you link the DLL you can choose the base address. If the address is unused in the process address space then nothing will be done. If the first line of code will be used (you call some function from the DLL) then the page of memory 8K near the used address will be loaded in memory from the file. If two processes use the same DLL then the physical memory for the code will be shared between processes. Even if you hold initialized variables the page with the variables will be shared till the first changes of the variable. On modification will be made the copy of the page of memory for the process which did the modification.

After the DLL is loaded in the process some small parts of the caller (the EXE for example) have to be modified to include the actual addresses of the functions used from the DLL. The same will be done with the DLLs which use functions from another DLL.

Everything sound perfect, but if you don't set any linker options during DLL compilation (if you don't use --image-base or --enable-auto-image-base linker option) you will have all your DLLs with the same base address (default value for the linker). So the first DLL could be loaded at the address. During loading of the second DLL which are linked with the same (or some overlapped address) the relocation of the DLL will be done. During the relocation the code of DLL sill be modified and so 1) the loading of the DLL will be slowly 2) the modified copy of the code will be made in the process (which include the memory used by DLL) 3) the modified copy will be not shared between multiple instances of the DLL (even if all instances will be modified in the same way).

I recommend you first of all to use Process Explorer for example to verify which DLLs will be relocated in your application. You should choose the option "DLLs" in the "View"/"Lower Pain View" menu and select "Relocation DLLs" checkbox in "Configure Highlighting" of the "Options" menu. You can additionally customize which information about every DLL will be displayed. The more information like below you will see the more slow the program will be loaded and the more address space will be not shared between instances of your application or between different applications which use the same DLL:

enter image description here

In the above example you see that tree Lenovo DLLs TPOSDSVC.dll, HKVOLKEY.dll and TPLHMM.dll are linked with the same base address 0x10000000 and only one DLL (TPOSDSVC.dll here) will be loaded at the address. Two other DLLs have to re relocated.

I can't write a book here about the subject. I recommend you to examine your application on the relocation problem. You can either use linker options (--image-base or --enable-auto-image-base seems be what you need). You can use dumpbin.exe tool (from Visual Studio also in the free edition) to examine the PE image.

After all your DLLs will have unique base address you can use another tool bind.exe with option -u to bind the EXE and your DLLs to its dependent DLLs. It will additionally reduce the memory size and improved the starting time of the application. It will update IMAGE_DIRECTORY_ENTRY_IMPORT and IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT parts of your DLL and EXE (see the answer). Bind.exe uses BindImageEx API internally. Many Windows Installer setups use BindImage action and BindImage table to make the binding at the end of installation of EXE and DLLs.

You can consider other techniques (see here) to reduce the size of DLL and EXE.

I don't know exactly how you can use delay-load technique in MinGW, but it should be definitively possible. The Visual Studio you need to do two steps: include Delayimp.lib as additional library and use /DELAYLOAD option (see here) to specify which from the DLLs should be loaded at the first usage instead of directly. Using very helpful Tool Dependency Walker you can see that most standard Microsoft DLLs uses the technique. You can improve the start time of your application and reduce the used memory if you would use the technique too.

like image 134
Oleg Avatar answered Oct 29 '22 16:10

Oleg