This article introduces the DLL explicit link process and the module base address relocation and module binding technology.
The first method to map a DLL to a process address space is to directly reference the functions or variables contained in the DLL in the source code. After the DLL program is run, it is implicitly loaded by the loader, this method is called implicit link.
The second method is to explicitly load the required DLL and explicitly link the desired symbol by calling the API when the program is running. In other words, when a program is running, one of the threads can explicitly call the DLL to the process address space and obtain the virtual address of a function in the DLL in the process address space, then call this function. This method is called an explicit link.
Note: When explicitly loading a DLL, the Lib file of the dll is not required and the exe file does not contain the import table of the dll.
To load the DLL module, follow these steps:
A thread can call LoadLibrary to map a DLL to a process address space.
HMODULE LoadLibrary (PCTSTR pszDLLPathName );
This function locates the DLL to be loaded and maps the DLL to the address space of the calling process. The virtual address of the DLL in the calling process is returned. That is, the module handle. If the DLL cannot be loaded into the process address space, the return value is NULL.
Another function similar to it
HMODULE LoadLibraryEx (PCTSTR pszDLLPathName, HANDLE hFile, DWORD dwFlags );
You can also load the DLL into the process address space. For details, refer to MSDN.
If the program no longer needs the DLL after it is loaded, call FreeLibrary to detach the DLL from the process address space:
BOOL FreeLibrary (HMODULE hInstDll );
You can also call FreeLibraryEx to uninstall a DLL.
The following functions not only detach a DLL from the process address space, but also exit the calling thread:
VOID FreeLibraryAndExitThread (HMODULE hInstDll, DWORD dwExitCode)
{
FreeLibrary (HMODULE hInstDll );
ExitThread (dwExitCode );
}
When you first see it, you may think it is more than enough. Consider the following situation:
We call a DLL. The code in this DLL creates a thread. After this thread completes the work, we can call FreeLibrary and ExitThread to uninstall the DLL from the process address space and terminate ourselves. Because the thread is created by the DLL, the Code executed by the thread is also in the DLL. When the thread calls FreeLibrary to uninstall the DLL, the code to be executed in the future is no longer in the process address space. Attempting to execute non-existent code may result in access violations and Process Termination.
If the thread calls FreeLibraryAndExitThread, this function is in Kernel32.dll. After the FreeLibraryAndExitThread function calls FreeLibrary to uninstall the DLL where the thread function is located, its DLL Kernel32.dll is still in the process address space, the FreeLibraryAndExitThread function continues to call ExitThread. Subsequent code still exists and will not cause access violations.
Each DLL has a count in the process. LoadLibrary (Ex) increases its count. FreeLibrary (Ex) and FreeLibraryAndExitThread decrease its count. For example, when the program calls LoadLibrary for loading a DLL for the first time, the system maps the DLL to the process address space and adds the use count of the DLL to the process address space. If the thread calls LoadLibrary (Ex) again later, the system will not map the DLL to the process address space again, but will only increase the usage count of this DLL. To cancel the DLL ing from the process address space, the thread must call FreeLibrary (Ex) twice. The first time is to reduce the usage count of this DLL to 1, and the second time to 0. When the system finds that the usage count of a DLL is 0, the DLL is detached from the process address space. In this case, if the thread tries to explicitly call the functions in the DLL, access violations will occur.
The system will maintain a count for the DLL in each process. Calling LoadLibrary in this process only increases the usage count of the DLL in this process. If A thread in process A executes LoadLibrary ("Mydll. dll "); a thread of process B also calls LoadLibrary (" Mydll. dll "); then the DLL will be mapped to the two process spaces A and B, and the count of process A and process B is 1.
Call FreeLibrary ("Mydll. dll"), which only reduces the usage count of the DLL in this process.
HMODULE GetModuleHandle (PCTSTR pszModuleName );
This function can be used to check whether a DLL is mapped to the process address space. If the returned value is NULL, the DLL is not loaded.
If pszModuleName is NULL, the function returns the handle to the executable file of the application.
Explicit link export symbol
After a DLL is explicitly loaded, the thread can call the following function to obtain the address of the symbol to be referenced.
FARPROC GetProcAddr (HMODULE hInstDll, PCSTR pszSymbolName );
HInstDll identifies the DLL handle of the exported symbol. It is the handle returned by LoadLibrary (Ex) or GetModuleHandle.
PszSymbolName is used to identify the export symbol.
PszSymbolName can be in two forms:
First, use a symbolic name to specify the address of the symbol we want to obtain.
For example, FARPROC pfn = GetProcAddress (hInstDll, "MyProc ");
It is a string ending with 0. Note that this string is of the ANSI Type. Because the compiler and the linker always store the name of the symbol in the export segment of the DLL in the form of an ANSI string.
Type 2: Use the serial number to specify the address of the symbol we want.
For example, FARPROC pfn = GetProcAddress (hInstDll, MAKERESOURCE (2 ));
This method assumes that the serial number of an export symbol in a DLL is 2. Microsoft strongly opposes serial numbers.
Using Serial numbers is slower than using strings, because the system needs to compare the symbols identified by a string. In the second method, GetProcAddress returns a non-NULL value even if the sequence number does not match any export function. In fact, this address is invalid. Access to this address may cause access violations.
Note: before using the function pointer returned by GetProcAddress to call a function, you need to convert it to a type that matches the function signature.
For example:
Typedef void (CALLBACK * PFN_DUM_MOUDLE) (MODULE hModule );
It is the same as the void DynamicDumpModule (HMODULE hModule) function.
Example of dynamically calling a DLL export function:
<Span style = "font-size: 18px;"> PFN_DUMPMODULE pfnDumpModule = (PFN_DUMPMODULE) GetProcAddress (hDll, "DumpModule ");
If (pfnDumpModule! = NULL)
{
PfnDumpModule (hDll );
}
;/Span>
DLL entry point function
A dll can have an entry point function. The system will call this function at different times. These calls are notification-oriented and are usually used by DLL to perform initialization and cleanup tasks related to processes or threads.
If you do not need to perform these operations, you do not need to implement this function in the source code.
If you need the DLL to receive these notifications, you should implement the function in the following format.
<Span style = "font-size: 18px;"> Bool WINAPI DllMain (HINSTANCE hInsDll, DWORD fdwReason, PVOID fImpLoad)
{
Swith (fdwReason)
{
Case DLL_PROCESS_ATTACH:
// If the DLL is mapped to the process address space, run the code here.
Break;
Case DLL_THREAD_ATTACH:
// Run the command when the thread is created.
Break;
Case DLL_THREAD_DETACH:
// Execute when the thread stops running.
Break;
Case DLL_PROCESS_DETACH:
// Execute the DLL when it is uninstalled.
Break;
}
}
</Span>
HInstDll is the handle of the DLL instance. It is the virtual address mapped to the process address space by the DLL file. This parameter is usually stored in a global variable. This can be used in other DLL export functions.
If the DLL is implicitly loaded, fImpLoad is a non-zero value, and explicitly, fImportLoad is 0.
FdwReason indicates the reason for the system to call the entry point function. It is a parameter of the switch statement. It can be the preceding four values. It indicates four situations respectively. In the future, we will detail each situation.
Note: DLL uses DllMain to initialize itself. During DllMain execution, other DLL may not be initialized yet. This means that we should avoid calling the function exported from other DLL in DllMain.
DLL_PROCESS_ATTACH notification www.2cto.com
When the system maps a DLL to the process address space for the first time, it calls the DllMain function and passes DLL_PROCESS_ATTACH to fdwReason. Note: DllMain is called only when this DLL is called to the process address space for the first time. If you call LoadLibrary (Ex) again later, the OS simply increments the usage count of the DLL in this process and does not call DllMain again.
When the DLL is processing DLL_PROCESS_ATTACH, it should execute process-related initialization as needed. For example, if a DLL contains some functions and you need to use your own heap, you can perform some heap initialization during process loading.
When DLL_PROCESS_ATTACH is processed, the returned value of DllMain indicates whether the DLL Initialization is successful. If Initialization is successful, return TRUE; otherwise, return false.
Next let's take a look at the time of DllMain's call:
When creating a new process, the system allocates address space for the process and maps the exe executable file and the required DLL to the process address space. Then create the main thread and use the main thread to call the DllMain function of each DLL, and input the DLL_PROCESS_ATTACH. After all the mapped DLL files have completed processing the notification, the system will let the main process execute the startup code of the C/C ++ Runtime Library of the executable module. Then execute the entry point function (_ tmain or _ tWinMain) of the executable module ). If the DllMain of any DLL returns false, the initialization fails. The system clears all file images from the address space and displays the error information to the user.
Explicit DLL loading process:
The process calls LoadLibrary (Ex). This function locates the DLL and maps the DLL to the process address space. Then, the thread that calls LoadLibrary (Ex) will call the DllMain function and pass in the DLL_PROCESS_ATTACH. After the DLL DllMain function completes the notification processing, the system will let LoadLibrary return. In this way, the thread can continue execution.
Note: DllMain is called when the process calls LoadLibrary (Ex. It is returned to the LoadLibrary (Ex) function.
Not complete .....
See the fourth part of the fifth edition of windows core programming. The above is just a personal summary. If you have any questions, please do not give me any further advice!
From ithzhang's column