Better Base Addresses
Assigning base addresses to components so that they do not overlap in the process's memory address space can improve both the performance and the scalability of applications by eliminating rebasing. The best-practices strategy for this differs for application developers and developers of individual components. This is because developers of individual components do not have prior knowledge of the runtime environment in which their component will ultimately be used, while application developers do.
For Application Developers
For application developers, the best strategy for assigning a base address to a new component is to follow these steps:
Identify the valid range of base addresses available.
Identify other in-process components used by the application, their base addresses, and the range of memory addresses that they take up.
Assign base addresses to new components so that there are no overlaps, taking the range of memory used by each component into consideration.
Valid Base Addresses
The range for a valid base address is (in hex notation) from &H1000000 to &H80000000. In addition, the base address must be on a multiple of 64K (&H10000). As a first step, avoid accepting Visual Basic's default of 0x11000000 when compiling a DLL. Particularly if you are creating a set of several DLLs that will be used together, specifying the same base address in each guarantees that all but one will have to be rebased when your application loads.
Inventory Used Address Ranges
Identify the in-process components that are used by the parent process, their base addresses, and their footprint in memory. The range of addresses taken up by a component (its footprint) is the size of the compiled file, rounded up to the next 64K boundary.
The program Dependency Walker (depends.exe) is a utility that ships with Visual Studio. Dependency Walker can be used to determine required information about a given component, as well as the components it depends on. Alternatively, the Microsoft linker can be used to determine the base addresses of existing in-process components. The command line link.exe -dump -headers filename.dll will display the file's COFF header. The DLL's base address can be found in the Optional Header Values section.
For Component Developers
In contrast to application developers, component developers usually have no way of knowing anything about the other components that may be used in the same application as their component. Therefore, it's not possible for component developers to go through the process described previously and explicitly set base values in a way that assures no rebasing will occur.
In these cases, an alternative approach must be used. One approach is to use a random number generator to generate base addresses for the components. This will definitely reduce the likelihood of overlap, especially compared to simply accepting the default value. Of course, the same rules about 64K boundaries and valid range apply.
Again, the valid range for base addresses is from 0x10000000 to 0x80000000 (minus the size of the DLL image) and must be specified on 64K boundaries. In reality, there is no way to predict the state of the system at DLL load time, so the best practice is to randomly determine a base address within the valid range, while explicitly avoiding the default base address, as well as base addresses used by common system DLLs.
However, there is a special case if you are creating a set of DLLs that will normally be used together. In this case, a little planning can make rebasing even less frequentat least, among your set of DLLs. When randomly generating the base address for each DLL, take into consideration the base addresses and sizes of the other DLLs in the group so that you can be certain that at least your DLLs won't interfere with each other.
Algorithms for Random Base Address Generation
The listings that follow illustrate some basic algorithms that can be used in a utility for randomly generating DLL base addresses.
For example, Listing 1 illustrates how to calculate the size of the range of addresses on a 64K boundary that will actually be used for a component of the specified size.
Listing 1: Calculating the Footprint of a DLL
'--- Calculate Space Required on 64K Boundary ---' Dim nSize as Long '-- Size of DLL Dim nSpace '-- Space Required If nSize < 65536 Then nSpace = 65536 ElseIf nSize Mod 65536 < 0.5 Then nSpace = (nSize / 65536) + 1 * 65536 ElseIf nSize Mod 65536 > 0.5 Then nSpace = (nSize / 65536) * 65536 Else nSpace = (nSize + 1 / 65536) * 65536 End If
Next, Listing 2 provides the algorithm for the actual random generation of a candidate base address.
Listing 2: Generate a Random Base Address
'--- Find a Base Address ---' Dim nCandidate as Long '-- Base address generated Randomize nCandidate = CLng(Int((32767 - 256 + 1) * Rnd + 256) * 65536)
Listing 3 illustrates the test for an overlap with the VB default base address.
Listing 3: Testing for Conflict with Default
'--- Don't overlap default ---' Dim bFound as Boolean '-- True if no conflict bFound = (nCandidate < (285212672 - nSpace)) Or (nCandidate > 285212672)
Finally, Listing 4 is the test for an overlap with the top of the available memory address range.
Listing 4: Testing for Conflict with Upper Range of Allowable Memory
'--- Not too close to end of allowable memory ---' If bFound Then bFound = bFound And (nCandidate < (2147483647 - nSpace)) End If
Additional information about this topic can be found in the MSDN Library in the topics "Rebasing Win32 DLLs: The Whole Story" and "Setting Base Addresses for In-Process Components."