[This is the second article of a two part series. Part I can be found here.]
In this installment, we continue our look into optimizing the loading of DLLs using the Rebase.exe tool from the Microsoft Platform SDK.
As I mentioned in the first issue in this series, its a simple matter to determine the size of a DLL using the DEPENDS.EXE utility. Using this information, you can then specify a series of base addresses and sizes for your DLLs using the Project properties dialog. However, this can be a real pain if there are many DLLs in an existing application. It gets worse if, down the road, you want to quickly rebase the DLLs because their sizes changed.
Consider a series of DLL's named apple.dll, orange.dll, and grape.dll. To ensure that the base addresses are correct (that is, the DLLs don't overlap when loaded into memory), you'd have to constantly check their sizes during development and adjust the Project properties for each. Yes, you could specify arbitrarily large offsets between base addresses, which would likely reduce the probability the situation would occur, but when it does (and it will), you'd have to adjust the DLLs one by one via Project properties.
There must be a better way.
Fortunately, there is. The Project properties /BASE switch takes not only a specific memory address, but also an alternative format that provides for loading the address from a file based on a key-an alphanumeric string that is used to load the address from the specified text file. This key can be any value you wish, but traditionally it is the name of the DLL.
In the previous example with our 3 DLL's, here is how that file might look:
Figure 1: Listing of rebase addresses.
The format is straightforward and what you might expect a key value followed by the base address followed by the size of the DLL. The Visual Studio linker will parse this file and locate the specified key automatically if the file is specified within the Project properties dialog properly. Here is how that would look:
Figure 2: apple DLL project properties showing rebase_addresses.txt file.
Pretty slick. Now, rebasing the set of DLLs is a simple matter of adjusting the values in the single file and relinking all of the DLLs (assuming each DLLs project was updated to specify that the base address should come from this text file using a unique key for that DLL).
That cut out a lot of work adjusting projects by hand. But we can do better still.
What would really be nice is a tool that would automatically calculate the sizes and base addresses for a set of DLLs and then adjust them on the fly to the new values. Before you fire up Visual Studio.NET and begin writing a tool to do this, it turns out that the Microsoft SDK comes with a utility that does this already-REBASE.EXE.
Running REBASE.EXE from a command shell shows the following output:
Figure 3: Default output of REBASE.EXE utility.
The set of switches and options on this utility is a bit daunting, but Ill point the ones that will allow us to rebase a set of DLLs automatically. First, we need to perform a top-down rebase (-d switch)this forces DLLs to load from the beginning of available memory rather than the end, which is better because the dynamic heap is used from the bottom-up. If we loaded our DLLs from the bottom, we would likely collide with memory already in use by a memory allocation in some block of code. This would cause the Windows loader to relocate the DLL and would remove the benefit of rebasing.
Next, we specify an initial address (-b switch) of 0x60000000 to ensure the REBASE tool loads our DLLs from that point, which follows Microsoft guidelines on DLL placement (covered in Part 1 of this series).
Finally, we specify the set of DLLs we want rebased using a wildcard (*.dll). The utility will locate and rebase all files with the .dll extension. As it loads, each DLL picks up where it left off with the previous DLL so that the entire set does not overlap.
The complete command line would look like this:
REBASE.EXE -d -b 0x60000000 *.dll
Once the tool completes, all of the DLLs found in the specified path are rebased. You can load each one with DEPENDS.EXE to verify that the base address is correct and that none overlap. Very handy.
The best place to use the REBASE tool is inside of a make file, batch file, or other build script at the end of your build process. Run the tool against all of your DLLs (including third party ones) as your final step. This will ensure that the base addresses are always up to date and it makes it easier to keep up with changes in DLL load size.
Mark M. Baker is the Chief of Research & Development at BNA Software located
in Washington, D.C.
Do you have a Windows development question? Send it to [email protected].