On Windows Server 2003, my application has started taking a long time to load on fresh install. Suspecting the DLLs are not loading to their preferred address and this is taking some time (the application has over 100 DLLs, 3rd parties included) I ran the sysinternals listDLLs utility, asking it to flag every dll that has been relocated. Oddly enough, for most of the DLLs in the list I get something like this:
Base Size Path
### Relocated from base of 0x44e90000:
0x44e90000 0x39000 validation.dll
That is: they are flagged as relocated (and the load time definitely seems to support that theory) but their load address remains the preferred address.
Some third party DLLs seem to be immune from this, but as a whole this happens to ~90% of the DLLs loaded by the application.
On windows 7, it would seem the only flagged DLLs are ones that actually move, and loading time is (as expected) significantly faster.
What is causing this? How can I stop it?
Edited: Since it sounds (in theory) like the effects of ASLR, I checked and while the OS DLLs are indeed ASLR-enabled, ours are not. And even those are relocated in place, and therefore not taking up the address for any of the other DLLs.
This is very common, setting the linker's /BASE option is often overlooked and maintaining it when DLLs grow is an unpleasant maintenance task. This does not tend to repeat well between operating system versions, they'll load different DLLs ahead of yours and that can force relocation on one but not the other. Also a single relocation can cause a train of forced relocations on all subsequent DLLs.
Having this notably affect loading time is a bit remote on modern machines. The relocation itself is very fast, just a memory operation. You do pay for having the memory used by the relocated DLL getting committed. Required since the original DLL file is no longer suitable to reload code when it gets swapped out, it is now backed by the paging file. If it needs to grow to accommodate the commit size then that costs time. That's not common.
The far more common issue in loading time is the speed of the disk drive. An issue when you have a lot of DLLs, they need to be located on disk on a cold start. With 100 DLLs, that can easily cost 5 seconds. You should suspect a cold start problem when you don't see the delay when you terminate the program and start it again. That's a warm start, the DLLs are already present in the file system cache so don't have to be found again. Solving a cold start problem requires better hardware, SSDs are nice. Or the machine learning your usage pattern so SuperFetch will pre-fetch the DLLs for you before you start the program.
Anyhoo, if you do suspect a rebasing problem then you'll need to create your own memory map to find good base addresses that don't force relocation. You need a good starting point, knowing the load order and sizes of the DLLs. You get that from, say, the VS debugger. The Output window shows the load order, the Debug + Windows + Modules window shows the DLL sizes. The linker supports specifying a .txt file for the base addresses in the /BASE option, best way to do this so you don't constantly have to tinker with individual /BASE value while your code keeps growing.