diff options
Diffstat (limited to 'src/pal/src/loader/module.cpp')
-rw-r--r-- | src/pal/src/loader/module.cpp | 1771 |
1 files changed, 1771 insertions, 0 deletions
diff --git a/src/pal/src/loader/module.cpp b/src/pal/src/loader/module.cpp new file mode 100644 index 0000000000..a4fc4949e4 --- /dev/null +++ b/src/pal/src/loader/module.cpp @@ -0,0 +1,1771 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*++ + + + +Module Name: + + module.c + +Abstract: + + Implementation of module related functions in the Win32 API + + + +--*/ + +#include "pal/thread.hpp" +#include "pal/malloc.hpp" +#include "pal/file.hpp" +#include "pal/palinternal.h" +#include "pal/dbgmsg.h" +#include "pal/module.h" +#include "pal/cs.hpp" +#include "pal/process.h" +#include "pal/file.h" +#include "pal/utils.h" +#include "pal/init.h" +#include "pal/modulename.h" +#include "pal/environ.h" +#include "pal/virtual.h" +#include "pal/map.hpp" +#include "pal/stackstring.hpp" + +#include <sys/param.h> +#include <errno.h> +#include <string.h> +#include <limits.h> +#if NEED_DLCOMPAT +#include "dlcompat.h" +#else // NEED_DLCOMPAT +#include <dlfcn.h> +#endif // NEED_DLCOMPAT +#include <stdlib.h> + +#ifdef __APPLE__ +#include <mach-o/dyld.h> +#include <mach-o/loader.h> +#endif // __APPLE__ + +#include <sys/types.h> +#include <sys/mman.h> + +#if HAVE_GNU_LIBNAMES_H +#include <gnu/lib-names.h> +#endif + +using namespace CorUnix; + +SET_DEFAULT_DEBUG_CHANNEL(LOADER); + +// In safemath.h, Template SafeInt uses macro _ASSERTE, which need to use variable +// defdbgchan defined by SET_DEFAULT_DEBUG_CHANNEL. Therefore, the include statement +// should be placed after the SET_DEFAULT_DEBUG_CHANNEL(LOADER) +#include <safemath.h> + +/* macro definitions **********************************************************/ + +/* get the full name of a module if available, and the short name otherwise*/ +#define MODNAME(x) ((x)->lib_name) + +/* Which path should FindLibrary search? */ +#if defined(__APPLE__) +#define LIBSEARCHPATH "DYLD_LIBRARY_PATH" +#else +#define LIBSEARCHPATH "LD_LIBRARY_PATH" +#endif + +#define LIBC_NAME_WITHOUT_EXTENSION "libc" + +/* static variables ***********************************************************/ + +/* critical section that regulates access to the module list */ +CRITICAL_SECTION module_critsec; + +/* always the first, in the in-load-order list */ +MODSTRUCT exe_module; +MODSTRUCT *pal_module = nullptr; + +char * g_szCoreCLRPath = nullptr; + +int MaxWCharToAcpLength = 3; + +/* static function declarations ***********************************************/ + +template<class TChar> static bool LOADVerifyLibraryPath(const TChar *libraryPath); +static bool LOADConvertLibraryPathWideStringToMultibyteString( + LPCWSTR wideLibraryPath, + LPSTR multibyteLibraryPath, + INT *multibyteLibraryPathLengthRef); +static BOOL LOADValidateModule(MODSTRUCT *module); +static LPWSTR LOADGetModuleFileName(MODSTRUCT *module); +static MODSTRUCT *LOADAddModule(void *dl_handle, LPCSTR libraryNameOrPath); +static void *LOADLoadLibraryDirect(LPCSTR libraryNameOrPath); +static BOOL LOADFreeLibrary(MODSTRUCT *module, BOOL fCallDllMain); +static HMODULE LOADRegisterLibraryDirect(void *dl_handle, LPCSTR libraryNameOrPath, BOOL fDynamic); +static HMODULE LOADLoadLibrary(LPCSTR shortAsciiName, BOOL fDynamic); +static BOOL LOADCallDllMainSafe(MODSTRUCT *module, DWORD dwReason, LPVOID lpReserved); + +/* API function definitions ***************************************************/ + +/*++ +Function: + LoadLibraryA + +See MSDN doc. +--*/ +HMODULE +PALAPI +LoadLibraryA( + IN LPCSTR lpLibFileName) +{ + return LoadLibraryExA(lpLibFileName, nullptr, 0); +} + +/*++ +Function: + LoadLibraryW + +See MSDN doc. +--*/ +HMODULE +PALAPI +LoadLibraryW( + IN LPCWSTR lpLibFileName) +{ + return LoadLibraryExW(lpLibFileName, nullptr, 0); +} + +/*++ +Function: +LoadLibraryExA + +See MSDN doc. +--*/ +HMODULE +PALAPI +LoadLibraryExA( + IN LPCSTR lpLibFileName, + IN /*Reserved*/ HANDLE hFile, + IN DWORD dwFlags) +{ + if (dwFlags != 0) + { + // UNIXTODO: Implement this! + ASSERT("Needs Implementation!!!"); + return nullptr; + } + + LPSTR lpstr = nullptr; + HMODULE hModule = nullptr; + + PERF_ENTRY(LoadLibraryA); + ENTRY("LoadLibraryExA (lpLibFileName=%p (%s)) \n", + (lpLibFileName) ? lpLibFileName : "NULL", + (lpLibFileName) ? lpLibFileName : "NULL"); + + if (!LOADVerifyLibraryPath(lpLibFileName)) + { + goto Done; + } + + /* do the Dos/Unix conversion on our own copy of the name */ + lpstr = strdup(lpLibFileName); + if (!lpstr) + { + ERROR("strdup failure!\n"); + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + goto Done; + } + FILEDosToUnixPathA(lpstr); + + hModule = LOADLoadLibrary(lpstr, TRUE); + + /* let LOADLoadLibrary call SetLastError */ + Done: + if (lpstr != nullptr) + { + free(lpstr); + } + + LOGEXIT("LoadLibraryExA returns HMODULE %p\n", hModule); + PERF_EXIT(LoadLibraryExA); + return hModule; + +} + +/*++ +Function: +LoadLibraryExW + +See MSDN doc. +--*/ +HMODULE +PALAPI +LoadLibraryExW( + IN LPCWSTR lpLibFileName, + IN /*Reserved*/ HANDLE hFile, + IN DWORD dwFlags) +{ + if (dwFlags != 0) + { + // UNIXTODO: Implement this! + ASSERT("Needs Implementation!!!"); + return nullptr; + } + + CHAR * lpstr; + INT name_length; + PathCharString pathstr; + HMODULE hModule = nullptr; + + PERF_ENTRY(LoadLibraryExW); + ENTRY("LoadLibraryExW (lpLibFileName=%p (%S)) \n", + lpLibFileName ? lpLibFileName : W16_NULLSTRING, + lpLibFileName ? lpLibFileName : W16_NULLSTRING); + + if (!LOADVerifyLibraryPath(lpLibFileName)) + { + goto done; + } + + lpstr = pathstr.OpenStringBuffer((PAL_wcslen(lpLibFileName)+1) * MaxWCharToAcpLength); + if (nullptr == lpstr) + { + goto done; + } + if (!LOADConvertLibraryPathWideStringToMultibyteString(lpLibFileName, lpstr, &name_length)) + { + goto done; + } + + /* do the Dos/Unix conversion on our own copy of the name */ + FILEDosToUnixPathA(lpstr); + pathstr.CloseBuffer(name_length); + + /* let LOADLoadLibrary call SetLastError in case of failure */ + hModule = LOADLoadLibrary(lpstr, TRUE); + +done: + LOGEXIT("LoadLibraryExW returns HMODULE %p\n", hModule); + PERF_EXIT(LoadLibraryExW); + return hModule; +} + +/*++ +Function: + GetProcAddress + +See MSDN doc. +--*/ +FARPROC +PALAPI +GetProcAddress( + IN HMODULE hModule, + IN LPCSTR lpProcName) +{ + MODSTRUCT *module; + FARPROC ProcAddress = nullptr; + LPCSTR symbolName = lpProcName; + + PERF_ENTRY(GetProcAddress); + ENTRY("GetProcAddress (hModule=%p, lpProcName=%p (%s))\n", + hModule, lpProcName ? lpProcName : "NULL", lpProcName ? lpProcName : "NULL"); + + LockModuleList(); + + module = (MODSTRUCT *) hModule; + + /* parameter validation */ + + if ((lpProcName == nullptr) || (*lpProcName == '\0')) + { + TRACE("No function name given\n"); + SetLastError(ERROR_INVALID_PARAMETER); + goto done; + } + + if (!LOADValidateModule(module)) + { + TRACE("Invalid module handle %p\n", hModule); + SetLastError(ERROR_INVALID_HANDLE); + goto done; + } + + /* try to assert on attempt to locate symbol by ordinal */ + /* this can't be an exact test for HIWORD((DWORD)lpProcName) == 0 + because of the address range reserved for ordinals contain can + be a valid string address on non-Windows systems + */ + if ((DWORD_PTR)lpProcName < VIRTUAL_PAGE_SIZE) + { + ASSERT("Attempt to locate symbol by ordinal?!\n"); + } + + // Get the symbol's address. + + // If we're looking for a symbol inside the PAL, we try the PAL_ variant + // first because otherwise we run the risk of having the non-PAL_ + // variant preferred over the PAL's implementation. + if (pal_module && module->dl_handle == pal_module->dl_handle) + { + int iLen = 4 + strlen(lpProcName) + 1; + LPSTR lpPALProcName = (LPSTR) alloca(iLen); + + if (strcpy_s(lpPALProcName, iLen, "PAL_") != SAFECRT_SUCCESS) + { + ERROR("strcpy_s failed!\n"); + SetLastError(ERROR_INSUFFICIENT_BUFFER); + goto done; + } + + if (strcat_s(lpPALProcName, iLen, lpProcName) != SAFECRT_SUCCESS) + { + ERROR("strcat_s failed!\n"); + SetLastError(ERROR_INSUFFICIENT_BUFFER); + goto done; + } + + ProcAddress = (FARPROC) dlsym(module->dl_handle, lpPALProcName); + symbolName = lpPALProcName; + } + + // If we aren't looking inside the PAL or we didn't find a PAL_ variant + // inside the PAL, fall back to a normal search. + if (ProcAddress == nullptr) + { + ProcAddress = (FARPROC) dlsym(module->dl_handle, lpProcName); + } + + if (ProcAddress) + { + TRACE("Symbol %s found at address %p in module %p (named %S)\n", + lpProcName, ProcAddress, module, MODNAME(module)); + + /* if we don't know the module's full name yet, this is our chance to obtain it */ + if (!module->lib_name && module->dl_handle) + { + const char* libName = PAL_dladdr((LPVOID)ProcAddress); + if (libName) + { + module->lib_name = UTIL_MBToWC_Alloc(libName, -1); + if (nullptr == module->lib_name) + { + ERROR("MBToWC failure; can't save module's full name\n"); + } + else + { + TRACE("Saving full path of module %p as %s\n", + module, libName); + } + } + } + } + else + { + TRACE("Symbol %s not found in module %p (named %S), dlerror message is \"%s\"\n", + lpProcName, module, MODNAME(module), dlerror()); + SetLastError(ERROR_PROC_NOT_FOUND); + } +done: + UnlockModuleList(); + LOGEXIT("GetProcAddress returns FARPROC %p\n", ProcAddress); + PERF_EXIT(GetProcAddress); + return ProcAddress; +} + +/*++ +Function: + FreeLibrary + +See MSDN doc. +--*/ +BOOL +PALAPI +FreeLibrary( + IN OUT HMODULE hLibModule) +{ + BOOL retval = FALSE; + + PERF_ENTRY(FreeLibrary); + ENTRY("FreeLibrary (hLibModule=%p)\n", hLibModule); + + retval = LOADFreeLibrary((MODSTRUCT *)hLibModule, TRUE /* fCallDllMain */); + + LOGEXIT("FreeLibrary returns BOOL %d\n", retval); + PERF_EXIT(FreeLibrary); + return retval; +} + +/*++ +Function: + FreeLibraryAndExitThread + +See MSDN doc. + +--*/ +PALIMPORT +VOID +PALAPI +FreeLibraryAndExitThread( + IN HMODULE hLibModule, + IN DWORD dwExitCode) +{ + PERF_ENTRY(FreeLibraryAndExitThread); + ENTRY("FreeLibraryAndExitThread()\n"); + FreeLibrary(hLibModule); + ExitThread(dwExitCode); + LOGEXIT("FreeLibraryAndExitThread\n"); + PERF_EXIT(FreeLibraryAndExitThread); +} + +/*++ +Function: + GetModuleFileNameA + +See MSDN doc. + +Notes : + because of limitations in the dlopen() mechanism, this will only return the + full path name if a relative or absolute path was given to LoadLibrary, or + if the module was used in a GetProcAddress call. otherwise, this will return + the short name as given to LoadLibrary. The exception is if hModule is + NULL : in this case, the full path of the executable is always returned. +--*/ +DWORD +PALAPI +GetModuleFileNameA( + IN HMODULE hModule, + OUT LPSTR lpFileName, + IN DWORD nSize) +{ + INT name_length; + DWORD retval = 0; + LPWSTR wide_name = nullptr; + + PERF_ENTRY(GetModuleFileNameA); + ENTRY("GetModuleFileNameA (hModule=%p, lpFileName=%p, nSize=%u)\n", + hModule, lpFileName, nSize); + + LockModuleList(); + if (hModule && !LOADValidateModule((MODSTRUCT *)hModule)) + { + TRACE("Can't find name for invalid module handle %p\n", hModule); + SetLastError(ERROR_INVALID_HANDLE); + goto done; + } + wide_name = LOADGetModuleFileName((MODSTRUCT *)hModule); + + if (!wide_name) + { + ASSERT("Can't find name for valid module handle %p\n", hModule); + SetLastError(ERROR_INTERNAL_ERROR); + goto done; + } + + /* Convert module name to Ascii, place it in the supplied buffer */ + + name_length = WideCharToMultiByte(CP_ACP, 0, wide_name, -1, lpFileName, + nSize, nullptr, nullptr); + if (name_length == 0) + { + TRACE("Buffer too small to copy module's file name.\n"); + SetLastError(ERROR_INSUFFICIENT_BUFFER); + goto done; + } + + TRACE("File name of module %p is %s\n", hModule, lpFileName); + retval = name_length; +done: + UnlockModuleList(); + LOGEXIT("GetModuleFileNameA returns DWORD %d\n", retval); + PERF_EXIT(GetModuleFileNameA); + return retval; +} + +/*++ +Function: + GetModuleFileNameW + +See MSDN doc. + +Notes : + because of limitations in the dlopen() mechanism, this will only return the + full path name if a relative or absolute path was given to LoadLibrary, or + if the module was used in a GetProcAddress call. otherwise, this will return + the short name as given to LoadLibrary. The exception is if hModule is + NULL : in this case, the full path of the executable is always returned. +--*/ +DWORD +PALAPI +GetModuleFileNameW( + IN HMODULE hModule, + OUT LPWSTR lpFileName, + IN DWORD nSize) +{ + INT name_length; + DWORD retval = 0; + LPWSTR wide_name = nullptr; + + PERF_ENTRY(GetModuleFileNameW); + ENTRY("GetModuleFileNameW (hModule=%p, lpFileName=%p, nSize=%u)\n", + hModule, lpFileName, nSize); + + LockModuleList(); + + wcscpy_s(lpFileName, nSize, W("")); + + if (hModule && !LOADValidateModule((MODSTRUCT *)hModule)) + { + TRACE("Can't find name for invalid module handle %p\n", hModule); + SetLastError(ERROR_INVALID_HANDLE); + goto done; + } + wide_name = LOADGetModuleFileName((MODSTRUCT *)hModule); + + if (!wide_name) + { + TRACE("Can't find name for valid module handle %p\n", hModule); + SetLastError(ERROR_INTERNAL_ERROR); + goto done; + } + + /* Copy module name into supplied buffer */ + + name_length = lstrlenW(wide_name); + if (name_length >= (INT)nSize) + { + TRACE("Buffer too small (%u) to copy module's file name (%u).\n", nSize, name_length); + retval = (INT)nSize; + SetLastError(ERROR_INSUFFICIENT_BUFFER); + goto done; + } + + wcscpy_s(lpFileName, nSize, wide_name); + + TRACE("file name of module %p is %S\n", hModule, lpFileName); + retval = name_length; +done: + UnlockModuleList(); + LOGEXIT("GetModuleFileNameW returns DWORD %u\n", retval); + PERF_EXIT(GetModuleFileNameW); + return retval; +} + +/* +Function: + PAL_LoadLibraryDirect + + Loads a library using a system call, without registering the library with the module list. + + Returns the system handle to the loaded library, or nullptr upon failure (error is set via SetLastError()). +*/ +void * +PALAPI +PAL_LoadLibraryDirect( + IN LPCWSTR lpLibFileName) +{ + PathCharString pathstr; + CHAR * lpstr = nullptr; + INT name_length; + void *dl_handle = nullptr; + + PERF_ENTRY(LoadLibraryDirect); + ENTRY("LoadLibraryDirect (lpLibFileName=%p (%S)) \n", + lpLibFileName ? lpLibFileName : W16_NULLSTRING, + lpLibFileName ? lpLibFileName : W16_NULLSTRING); + + if (!LOADVerifyLibraryPath(lpLibFileName)) + { + goto done; + } + + lpstr = pathstr.OpenStringBuffer((PAL_wcslen(lpLibFileName)+1) * MaxWCharToAcpLength); + if (nullptr == lpstr) + { + goto done; + } + if (!LOADConvertLibraryPathWideStringToMultibyteString(lpLibFileName, lpstr, &name_length)) + { + goto done; + } + + /* do the Dos/Unix conversion on our own copy of the name */ + FILEDosToUnixPathA(lpstr); + pathstr.CloseBuffer(name_length); + + dl_handle = LOADLoadLibraryDirect(lpstr); + +done: + LOGEXIT("LoadLibraryDirect returns HMODULE %p\n", dl_handle); + PERF_EXIT(LoadLibraryDirect); + return dl_handle; +} + +/* +Function: + PAL_RegisterLibraryDirect + + Registers a system handle to a loaded library with the module list. + + Returns a PAL handle to the loaded library, or nullptr upon failure (error is set via SetLastError()). +*/ +HMODULE +PALAPI +PAL_RegisterLibraryDirect( + IN void *dl_handle, + IN LPCWSTR lpLibFileName) +{ + PathCharString pathstr; + CHAR * lpstr = nullptr; + INT name_length; + HMODULE hModule = nullptr; + + PERF_ENTRY(RegisterLibraryDirect); + ENTRY("RegisterLibraryDirect (lpLibFileName=%p (%S)) \n", + lpLibFileName ? lpLibFileName : W16_NULLSTRING, + lpLibFileName ? lpLibFileName : W16_NULLSTRING); + + if (!LOADVerifyLibraryPath(lpLibFileName)) + { + goto done; + } + + lpstr = pathstr.OpenStringBuffer((PAL_wcslen(lpLibFileName)+1) * MaxWCharToAcpLength); + if (nullptr == lpstr) + { + goto done; + } + if (!LOADConvertLibraryPathWideStringToMultibyteString(lpLibFileName, lpstr, &name_length)) + { + goto done; + } + + /* do the Dos/Unix conversion on our own copy of the name */ + FILEDosToUnixPathA(lpstr); + pathstr.CloseBuffer(name_length); + + /* let LOADRegisterLibraryDirect call SetLastError in case of failure */ + LockModuleList(); + hModule = LOADRegisterLibraryDirect((void *)dl_handle, lpstr, true /* fDynamic */); + UnlockModuleList(); + +done: + LOGEXIT("RegisterLibraryDirect returns HMODULE %p\n", hModule); + PERF_EXIT(RegisterLibraryDirect); + return hModule; +} + +/*++ +Function: + PAL_RegisterModule + + Register the module with the target module and return a module handle in + the target module's context. Doesn't call the DllMain because it is used + as part of calling DllMain in the calling module. + +--*/ +HINSTANCE +PALAPI +PAL_RegisterModule( + IN LPCSTR lpLibFileName) +{ + HINSTANCE hinstance = nullptr; + + int err = PAL_InitializeDLL(); + if (err == 0) + { + PERF_ENTRY(PAL_RegisterModule); + ENTRY("PAL_RegisterModule(%s)\n", lpLibFileName ? lpLibFileName : ""); + + LockModuleList(); + + void *dl_handle = LOADLoadLibraryDirect(lpLibFileName); + if (dl_handle) + { + // This only creates/adds the module handle and doesn't call DllMain + hinstance = LOADAddModule(dl_handle, lpLibFileName); + } + + UnlockModuleList(); + + LOGEXIT("PAL_RegisterModule returns HINSTANCE %p\n", hinstance); + PERF_EXIT(PAL_RegisterModule); + } + + return hinstance; +} + +/*++ +Function: + PAL_UnregisterModule + + Used to cleanup the module HINSTANCE from PAL_RegisterModule. +--*/ +VOID +PALAPI +PAL_UnregisterModule( + IN HINSTANCE hInstance) +{ + PERF_ENTRY(PAL_UnregisterModule); + ENTRY("PAL_UnregisterModule(hInstance=%p)\n", hInstance); + + LOADFreeLibrary((MODSTRUCT *)hInstance, FALSE /* fCallDllMain */); + + LOGEXIT("PAL_UnregisterModule returns\n"); + PERF_EXIT(PAL_UnregisterModule); +} + +/*++ + PAL_LOADLoadPEFile + + Map a PE format file into memory like Windows LoadLibrary() would do. + Doesn't apply base relocations if the function is relocated. + +Parameters: + IN hFile - file to map + +Return value: + non-NULL - the base address of the mapped image + NULL - error, with last error set. +--*/ +void * +PALAPI +PAL_LOADLoadPEFile(HANDLE hFile) +{ + ENTRY("PAL_LOADLoadPEFile (hFile=%p)\n", hFile); + + void * loadedBase = MAPMapPEFile(hFile); + +#ifdef _DEBUG + if (loadedBase != nullptr) + { + char* envVar = EnvironGetenv("PAL_ForcePEMapFailure"); + if (envVar) + { + if (strlen(envVar) > 0) + { + TRACE("Forcing failure of PE file map, and retry\n"); + PAL_LOADUnloadPEFile(loadedBase); // unload it + loadedBase = MAPMapPEFile(hFile); // load it again + } + + free(envVar); + } + } +#endif // _DEBUG + + LOGEXIT("PAL_LOADLoadPEFile returns %p\n", loadedBase); + return loadedBase; +} + +/*++ + PAL_LOADUnloadPEFile + + Unload a PE file that was loaded by PAL_LOADLoadPEFile(). + +Parameters: + IN ptr - the file pointer returned by PAL_LOADLoadPEFile() + +Return value: + TRUE - success + FALSE - failure (incorrect ptr, etc.) +--*/ +BOOL +PALAPI +PAL_LOADUnloadPEFile(void * ptr) +{ + BOOL retval = FALSE; + + ENTRY("PAL_LOADUnloadPEFile (ptr=%p)\n", ptr); + + if (nullptr == ptr) + { + ERROR( "Invalid pointer value\n" ); + } + else + { + retval = MAPUnmapPEFile(ptr); + } + + LOGEXIT("PAL_LOADUnloadPEFile returns %d\n", retval); + return retval; +} + +/*++ + PAL_GetSymbolModuleBase + + Get base address of the module containing a given symbol + +Parameters: + void *symbol - address of symbol + +Return value: + module base address +--*/ +LPCVOID +PALAPI +PAL_GetSymbolModuleBase(void *symbol) +{ + LPCVOID retval = nullptr; + + PERF_ENTRY(PAL_GetPalModuleBase); + ENTRY("PAL_GetPalModuleBase\n"); + + if (symbol == nullptr) + { + TRACE("Can't get base address. Argument symbol == nullptr\n"); + SetLastError(ERROR_INVALID_DATA); + } + else + { + Dl_info info; + if (dladdr(symbol, &info) != 0) + { + retval = info.dli_fbase; + } + else + { + TRACE("Can't get base address of the current module\n"); + SetLastError(ERROR_INVALID_DATA); + } + } + + LOGEXIT("PAL_GetPalModuleBase returns %p\n", retval); + PERF_EXIT(PAL_GetPalModuleBase); + return retval; +} + +/* Internal PAL functions *****************************************************/ + +/*++ +Function : + LOADInitializeModules + + Initialize the process-wide list of modules + +Parameters : + None + +Return value : + TRUE if initialization succeedded + FALSE otherwise + +--*/ +extern "C" +BOOL LOADInitializeModules() +{ + _ASSERTE(exe_module.prev == nullptr); + + InternalInitializeCriticalSection(&module_critsec); + + // Initialize module for main executable + TRACE("Initializing module for main executable\n"); + + exe_module.self = (HMODULE)&exe_module; + exe_module.dl_handle = dlopen(nullptr, RTLD_LAZY); + if (exe_module.dl_handle == nullptr) + { + ERROR("Executable module will be broken : dlopen(nullptr) failed dlerror message is \"%s\" \n", dlerror()); + return FALSE; + } + exe_module.lib_name = nullptr; + exe_module.refcount = -1; + exe_module.next = &exe_module; + exe_module.prev = &exe_module; + exe_module.pDllMain = nullptr; + exe_module.hinstance = nullptr; + exe_module.threadLibCalls = TRUE; + return TRUE; +} + +/*++ +Function : + LOADSetExeName + + Set the exe name path + +Parameters : + LPWSTR man exe path and name + +Return value : + TRUE if initialization succeedded + FALSE otherwise + +--*/ +extern "C" +BOOL LOADSetExeName(LPWSTR name) +{ +#if RETURNS_NEW_HANDLES_ON_REPEAT_DLOPEN + LPSTR pszExeName = nullptr; +#endif + BOOL result = FALSE; + + LockModuleList(); + + // Save the exe path in the exe module struct + free(exe_module.lib_name); + exe_module.lib_name = name; + + // For platforms where we can't trust the handle to be constant, we need to + // store the inode/device pairs for the modules we just initialized. +#if RETURNS_NEW_HANDLES_ON_REPEAT_DLOPEN + { + struct stat stat_buf; + pszExeName = UTIL_WCToMB_Alloc(name, -1); + if (nullptr == pszExeName) + { + ERROR("WCToMB failure, unable to get full name of exe\n"); + goto exit; + } + if (-1 == stat(pszExeName, &stat_buf)) + { + SetLastError(ERROR_MOD_NOT_FOUND); + goto exit; + } + TRACE("Executable has inode %d and device %d\n", stat_buf.st_ino, stat_buf.st_dev); + + exe_module.inode = stat_buf.st_ino; + exe_module.device = stat_buf.st_dev; + } +#endif + result = TRUE; + +#if RETURNS_NEW_HANDLES_ON_REPEAT_DLOPEN +exit: + if (pszExeName) + { + free(pszExeName); + } +#endif + UnlockModuleList(); + return result; +} + +/*++ +Function : + LOADCallDllMain + + Call DllMain for all modules (that have one) with the given "fwReason" + +Parameters : + DWORD dwReason : parameter to pass down to DllMain, one of DLL_PROCESS_ATTACH, DLL_PROCESS_DETACH, + DLL_THREAD_ATTACH, DLL_THREAD_DETACH + + LPVOID lpReserved : parameter to pass down to DllMain + If dwReason is DLL_PROCESS_ATTACH, lpvReserved is NULL for dynamic loads and non-NULL for static loads. + If dwReason is DLL_PROCESS_DETACH, lpvReserved is NULL if DllMain has been called by using FreeLibrary + and non-NULL if DllMain has been called during process termination. + +(no return value) + +Notes : + This is used to send DLL_THREAD_*TACH messages to modules +--*/ +extern "C" +void LOADCallDllMain(DWORD dwReason, LPVOID lpReserved) +{ + MODSTRUCT *module = nullptr; + BOOL InLoadOrder = TRUE; /* true if in load order, false for reverse */ + CPalThread *pThread; + + pThread = InternalGetCurrentThread(); + if (UserCreatedThread != pThread->GetThreadType()) + { + return; + } + + /* Validate dwReason */ + switch(dwReason) + { + case DLL_PROCESS_ATTACH: + ASSERT("got called with DLL_PROCESS_ATTACH parameter! Why?\n"); + break; + case DLL_PROCESS_DETACH: + ASSERT("got called with DLL_PROCESS_DETACH parameter! Why?\n"); + InLoadOrder = FALSE; + break; + case DLL_THREAD_ATTACH: + TRACE("Calling DllMain(DLL_THREAD_ATTACH) on all known modules.\n"); + break; + case DLL_THREAD_DETACH: + TRACE("Calling DllMain(DLL_THREAD_DETACH) on all known modules.\n"); + InLoadOrder = FALSE; + break; + default: + ASSERT("LOADCallDllMain called with unknown parameter %d!\n", dwReason); + return; + } + + LockModuleList(); + + module = &exe_module; + + do + { + if (!InLoadOrder) + module = module->prev; + + if (module->threadLibCalls) + { + if (module->pDllMain) + { + LOADCallDllMainSafe(module, dwReason, lpReserved); + } + } + + if (InLoadOrder) + module = module->next; + + } while (module != &exe_module); + + UnlockModuleList(); +} + +/*++ +Function: + LOADFreeLibrary + +Parameters: + MODSTRUCT * module - module to free + BOOL fCallDllMain - if TRUE, call the DllMain function + +Returns: + TRUE if successful + +--*/ +static BOOL LOADFreeLibrary(MODSTRUCT *module, BOOL fCallDllMain) +{ + BOOL retval = FALSE; + + LockModuleList(); + + if (terminator) + { + /* PAL shutdown is in progress - ignore FreeLibrary calls */ + retval = TRUE; + goto done; + } + + if (!LOADValidateModule(module)) + { + TRACE("Can't free invalid module %p\n", module); + SetLastError(ERROR_INVALID_HANDLE); + goto done; + } + + if (module->refcount == -1) + { + /* special module - never released */ + retval = TRUE; + goto done; + } + + module->refcount--; + TRACE("Reference count for module %p (named %S) decreases to %d\n", + module, MODNAME(module), module->refcount); + + if (module->refcount != 0) + { + retval = TRUE; + goto done; + } + + /* Releasing the last reference : call dlclose(), remove module from the + process-wide module list */ + + TRACE("Reference count for module %p (named %S) now 0; destroying module structure\n", + module, MODNAME(module)); + + /* unlink the module structure from the list */ + module->prev->next = module->next; + module->next->prev = module->prev; + + /* remove the circular reference so that LOADValidateModule will fail */ + module->self = nullptr; + + /* Call DllMain if the module contains one */ + if (fCallDllMain && module->pDllMain) + { + LOADCallDllMainSafe(module, DLL_PROCESS_DETACH, nullptr); + } + + if (module->hinstance) + { + PUNREGISTER_MODULE unregisterModule = (PUNREGISTER_MODULE)dlsym(module->dl_handle, "PAL_UnregisterModule"); + if (unregisterModule != nullptr) + { + unregisterModule(module->hinstance); + } + module->hinstance = nullptr; + } + + if (module->dl_handle && 0 != dlclose(module->dl_handle)) + { + /* report dlclose() failure, but proceed anyway. */ + WARN("dlclose() call failed! error message is \"%s\"\n", dlerror()); + } + + /* release all memory */ + free(module->lib_name); + free(module); + + retval = TRUE; + +done: + UnlockModuleList(); + return retval; +} + +/*++ +Function : + LOADCallDllMainSafe + + Exception-safe call to DllMain. + +Parameters : + MODSTRUCT *module : module whose DllMain must be called + + DWORD dwReason : parameter to pass down to DllMain, one of DLL_PROCESS_ATTACH, DLL_PROCESS_DETACH, + DLL_THREAD_ATTACH, DLL_THREAD_DETACH + + LPVOID lpvReserved : parameter to pass down to DllMain, + If dwReason is DLL_PROCESS_ATTACH, lpvReserved is NULL for dynamic loads and non-NULL for static loads. + If dwReason is DLL_PROCESS_DETACH, lpvReserved is NULL if DllMain has been called by using FreeLibrary + and non-NULL if DllMain has been called during process termination. + +Returns: + BOOL : DllMain's return value +*/ +static BOOL LOADCallDllMainSafe(MODSTRUCT *module, DWORD dwReason, LPVOID lpReserved) +{ +#if _ENABLE_DEBUG_MESSAGES_ + /* reset ENTRY nesting level back to zero while inside the callback... */ + int old_level = DBG_change_entrylevel(0); +#endif /* _ENABLE_DEBUG_MESSAGES_ */ + + struct Param + { + MODSTRUCT *module; + DWORD dwReason; + LPVOID lpReserved; + BOOL ret; + } param; + param.module = module; + param.dwReason = dwReason; + param.lpReserved = lpReserved; + param.ret = FALSE; + + PAL_TRY(Param *, pParam, ¶m) + { + TRACE("Calling DllMain (%p) for module %S\n", + pParam->module->pDllMain, + pParam->module->lib_name ? pParam->module->lib_name : W16_NULLSTRING); + + { + // This module may be foreign to our PAL, so leave our PAL. + // If it depends on us, it will re-enter. + PAL_LeaveHolder holder; + pParam->ret = pParam->module->pDllMain(pParam->module->hinstance, pParam->dwReason, pParam->lpReserved); + } + } + PAL_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + WARN("Call to DllMain (%p) got an unhandled exception; ignoring.\n", module->pDllMain); + } + PAL_ENDTRY + +#if _ENABLE_DEBUG_MESSAGES_ + /* ...and set nesting level back to what it was */ + DBG_change_entrylevel(old_level); +#endif /* _ENABLE_DEBUG_MESSAGES_ */ + + return param.ret; +} + +/*++ +Function: + DisableThreadLibraryCalls + +See MSDN doc. +--*/ +BOOL +PALAPI +DisableThreadLibraryCalls( + IN HMODULE hLibModule) +{ + BOOL ret = FALSE; + MODSTRUCT *module; + PERF_ENTRY(DisableThreadLibraryCalls); + ENTRY("DisableThreadLibraryCalls(hLibModule=%p)\n", hLibModule); + + LockModuleList(); + + if (terminator) + { + /* PAL shutdown in progress - ignore DisableThreadLibraryCalls */ + ret = TRUE; + goto done; + } + + module = (MODSTRUCT *) hLibModule; + + if (!LOADValidateModule(module)) + { + // DisableThreadLibraryCalls() does nothing when given + // an invalid module handle. This matches the Windows + // behavior, though it is counter to MSDN. + WARN("Invalid module handle %p\n", hLibModule); + ret = TRUE; + goto done; + } + + module->threadLibCalls = FALSE; + ret = TRUE; + +done: + UnlockModuleList(); + LOGEXIT("DisableThreadLibraryCalls returns BOOL %d\n", ret); + PERF_EXIT(DisableThreadLibraryCalls); + return ret; +} + +// Checks the library path for null or empty string. On error, calls SetLastError() and returns false. +template<class TChar> +static bool LOADVerifyLibraryPath(const TChar *libraryPath) +{ + if (libraryPath == nullptr) + { + ERROR("libraryPath is null\n"); + SetLastError(ERROR_MOD_NOT_FOUND); + return false; + } + if (libraryPath[0] == '\0') + { + ERROR("libraryPath is empty\n"); + SetLastError(ERROR_INVALID_PARAMETER); + return false; + } + return true; +} + +// Converts the wide char library path string into a multibyte-char string. On error, calls SetLastError() and returns false. +static bool LOADConvertLibraryPathWideStringToMultibyteString( + LPCWSTR wideLibraryPath, + LPSTR multibyteLibraryPath, + INT *multibyteLibraryPathLengthRef) +{ + _ASSERTE(multibyteLibraryPathLengthRef != nullptr); + _ASSERTE(wideLibraryPath != nullptr); + + size_t length = (PAL_wcslen(wideLibraryPath)+1) * MaxWCharToAcpLength; + *multibyteLibraryPathLengthRef = WideCharToMultiByte(CP_ACP, 0, wideLibraryPath, -1, multibyteLibraryPath, + length, nullptr, nullptr); + + if (*multibyteLibraryPathLengthRef == 0) + { + DWORD dwLastError = GetLastError(); + + ASSERT("WideCharToMultiByte failure! error is %d\n", dwLastError); + + SetLastError(ERROR_INVALID_PARAMETER); + return false; + } + return true; +} + +/*++ +Function : + LOADValidateModule + + Check whether the given MODSTRUCT pointer is valid + +Parameters : + MODSTRUCT *module : module to check + +Return value : + TRUE if module is valid, FALSE otherwise + +NOTE : + The module lock MUST be owned. + +--*/ +static BOOL LOADValidateModule(MODSTRUCT *module) +{ + MODSTRUCT *modlist_enum = &exe_module; + + /* enumerate through the list of modules to make sure the given handle is + really a module (HMODULEs are actually MODSTRUCT pointers) */ + do + { + if (module == modlist_enum) + { + /* found it; check its integrity to be on the safe side */ + if (module->self != module) + { + ERROR("Found corrupt module %p!\n",module); + return FALSE; + } + TRACE("Module %p is valid (name : %S)\n", module, MODNAME(module)); + return TRUE; + } + modlist_enum = modlist_enum->next; + } + while (modlist_enum != &exe_module); + + TRACE("Module %p is NOT valid.\n", module); + return FALSE; +} + +/*++ +Function : + LOADGetModuleFileName [internal] + + Retrieve the module's full path if it is known, the short name given to + LoadLibrary otherwise. + +Parameters : + MODSTRUCT *module : module to check + +Return value : + pointer to internal buffer with name of module (Unicode) + +Notes : + this function assumes that the module critical section is held, and that + the module has already been validated. +--*/ +static LPWSTR LOADGetModuleFileName(MODSTRUCT *module) +{ + LPWSTR module_name; + /* special case : if module is NULL, we want the name of the executable */ + if (!module) + { + module_name = exe_module.lib_name; + TRACE("Returning name of main executable\n"); + return module_name; + } + + /* return "real" name of module if it is known. we have this if LoadLibrary + was given an absolute or relative path; we can also determine it at the + first GetProcAdress call. */ + TRACE("Returning full path name of module\n"); + return module->lib_name; +} + +/* +Function: + LOADLoadLibraryDirect [internal] + + Loads a library using a system call, without registering the library with the module list. + +Parameters: + LPCSTR libraryNameOrPath: The library to load. + +Return value: + System handle to the loaded library, or nullptr upon failure (error is set via SetLastError()). +*/ +static void *LOADLoadLibraryDirect(LPCSTR libraryNameOrPath) +{ + _ASSERTE(libraryNameOrPath != nullptr); + _ASSERTE(libraryNameOrPath[0] != '\0'); + + void *dl_handle = dlopen(libraryNameOrPath, RTLD_LAZY); + if (dl_handle == nullptr) + { + WARN("dlopen() failed; dlerror says '%s'\n", dlerror()); + SetLastError(ERROR_MOD_NOT_FOUND); + } + else + { + TRACE("dlopen() found module %s\n", libraryNameOrPath); + } + + return dl_handle; +} + +/*++ +Function : + LOADAllocModule + + Allocate and initialize a new MODSTRUCT structure + +Parameters : + void *dl_handle : handle returned by dl_open, goes in MODSTRUCT::dl_handle + + char *name : name of new module. after conversion to widechar, + goes in MODSTRUCT::lib_name + +Return value: + a pointer to a new, initialized MODSTRUCT strucutre, or NULL on failure. + +Notes : + 'name' is used to initialize MODSTRUCT::lib_name. The other member is set to NULL + In case of failure (in malloc or MBToWC), this function sets LastError. +--*/ +static MODSTRUCT *LOADAllocModule(void *dl_handle, LPCSTR name) +{ + MODSTRUCT *module; + LPWSTR wide_name; + + /* no match found : try to create a new module structure */ + module = (MODSTRUCT *)InternalMalloc(sizeof(MODSTRUCT)); + if (nullptr == module) + { + ERROR("malloc() failed! errno is %d (%s)\n", errno, strerror(errno)); + return nullptr; + } + + wide_name = UTIL_MBToWC_Alloc(name, -1); + if (nullptr == wide_name) + { + ERROR("couldn't convert name to a wide-character string\n"); + free(module); + return nullptr; + } + + module->dl_handle = dl_handle; +#if NEED_DLCOMPAT + if (isdylib(module)) + { + module->refcount = -1; + } + else + { + module->refcount = 1; + } +#else // NEED_DLCOMPAT + module->refcount = 1; +#endif // NEED_DLCOMPAT + module->self = module; + module->hinstance = nullptr; + module->threadLibCalls = TRUE; + module->pDllMain = nullptr; + module->next = nullptr; + module->prev = nullptr; + + module->lib_name = wide_name; + + return module; +} + +/* +Function: + LOADAddModule [internal] + + Registers a system handle to a loaded library with the module list. + +Parameters: + void *dl_handle: System handle to the loaded library. + LPCSTR libraryNameOrPath: The library that was loaded. + +Return value: + PAL handle to the loaded library, or nullptr upon failure (error is set via SetLastError()). +*/ +static MODSTRUCT *LOADAddModule(void *dl_handle, LPCSTR libraryNameOrPath) +{ + _ASSERTE(dl_handle != nullptr); + _ASSERTE(libraryNameOrPath != nullptr); + _ASSERTE(libraryNameOrPath[0] != '\0'); + +#if !RETURNS_NEW_HANDLES_ON_REPEAT_DLOPEN + /* search module list for a match. */ + MODSTRUCT *module = &exe_module; + do + { + if (dl_handle == module->dl_handle) + { + /* found the handle. increment the refcount and return the + existing module structure */ + TRACE("Found matching module %p for module name %s\n", module, libraryNameOrPath); + + if (module->refcount != -1) + { + module->refcount++; + } + dlclose(dl_handle); + return module; + } + module = module->next; + + } while (module != &exe_module); +#endif + + TRACE("Module doesn't exist : creating %s.\n", libraryNameOrPath); + + module = LOADAllocModule(dl_handle, libraryNameOrPath); + if (nullptr == module) + { + ERROR("couldn't create new module\n"); + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + dlclose(dl_handle); + return nullptr; + } + + /* We now get the address of DllMain if the module contains one. */ + module->pDllMain = (PDLLMAIN)dlsym(module->dl_handle, "DllMain"); + + /* Add the new module on to the end of the list */ + module->prev = exe_module.prev; + module->next = &exe_module; + exe_module.prev->next = module; + exe_module.prev = module; + +#if RETURNS_NEW_HANDLES_ON_REPEAT_DLOPEN + module->inode = stat_buf.st_ino; + module->device = stat_buf.st_dev; +#endif + + return module; +} + +/* +Function: + LOADRegisterLibraryDirect [internal] + + Registers a system handle to a loaded library with the module list. + +Parameters: + void *dl_handle: System handle to the loaded library. + LPCSTR libraryNameOrPath: The library that was loaded. + BOOL fDynamic: TRUE if dynamic load through LoadLibrary, FALSE if static load through RegisterLibrary. + +Return value: + PAL handle to the loaded library, or nullptr upon failure (error is set via SetLastError()). +*/ +static HMODULE LOADRegisterLibraryDirect(void *dl_handle, LPCSTR libraryNameOrPath, BOOL fDynamic) +{ + MODSTRUCT *module = LOADAddModule(dl_handle, libraryNameOrPath); + if (module == nullptr) + { + return nullptr; + } + + /* If the module contains a DllMain, call it. */ + if (module->pDllMain) + { + TRACE("Calling DllMain (%p) for module %S\n", + module->pDllMain, + module->lib_name ? module->lib_name : W16_NULLSTRING); + + if (nullptr == module->hinstance) + { + PREGISTER_MODULE registerModule = (PREGISTER_MODULE)dlsym(module->dl_handle, "PAL_RegisterModule"); + if (registerModule != nullptr) + { + module->hinstance = registerModule(libraryNameOrPath); + } + else + { + // If the target module doesn't have the PAL_RegisterModule export, then use this PAL's + // module handle assuming that the target module is referencing this PAL's exported + // functions on said handle. + module->hinstance = (HINSTANCE)module; + } + } + + BOOL dllMainRetVal = LOADCallDllMainSafe(module, DLL_PROCESS_ATTACH, fDynamic ? nullptr : (LPVOID)-1); + + // If DlMain(DLL_PROCESS_ATTACH) returns FALSE, we must immediately unload the module + if (!dllMainRetVal) + { + ERROR("DllMain returned FALSE; unloading module.\n"); + module->pDllMain = nullptr; + FreeLibrary((HMODULE)module); + SetLastError(ERROR_DLL_INIT_FAILED); + module = nullptr; + } + } + else + { + TRACE("Module does not contain a DllMain function.\n"); + } + + return module; +} + +/*++ +Function : + LOADLoadLibrary [internal] + + implementation of LoadLibrary (for use by the A/W variants) + +Parameters : + LPSTR shortAsciiName : name of module as specified to LoadLibrary + + BOOL fDynamic : TRUE if dynamic load through LoadLibrary, FALSE if static load through RegisterLibrary + +Return value : + handle to loaded module + +--*/ +static HMODULE LOADLoadLibrary(LPCSTR shortAsciiName, BOOL fDynamic) +{ + HMODULE module = nullptr; + void *dl_handle = nullptr; + + // Check whether we have been requested to load 'libc'. If that's the case, then: + // * For Linux, use the full name of the library that is defined in <gnu/lib-names.h> by the + // LIBC_SO constant. The problem is that calling dlopen("libc.so") will fail for libc even + // though it works for other libraries. The reason is that libc.so is just linker script + // (i.e. a test file). + // As a result, we have to use the full name (i.e. lib.so.6) that is defined by LIBC_SO. + // * For macOS, use constant value absolute path "/usr/lib/libc.dylib". + // * For FreeBSD, use constant value "libc.so.7". + // * For rest of Unices, use constant value "libc.so". + if (strcmp(shortAsciiName, LIBC_NAME_WITHOUT_EXTENSION) == 0) + { +#if defined(__APPLE__) + shortAsciiName = "/usr/lib/libc.dylib"; +#elif defined(__FreeBSD__) + shortAsciiName = "libc.so.7"; +#elif defined(LIBC_SO) + shortAsciiName = LIBC_SO; +#else + shortAsciiName = "libc.so"; +#endif + } + + LockModuleList(); + + dl_handle = LOADLoadLibraryDirect(shortAsciiName); + if (dl_handle) + { + module = LOADRegisterLibraryDirect(dl_handle, shortAsciiName, fDynamic); + } + + UnlockModuleList(); + + return module; +} + +/*++ + LOADInitializeCoreCLRModule + + Run the initialization methods for CoreCLR module (the module containing this PAL). + +Parameters: + None + +Return value: + TRUE if successful + FALSE if failure +--*/ +BOOL LOADInitializeCoreCLRModule() +{ + MODSTRUCT *module = LOADGetPalLibrary(); + if (!module) + { + ERROR("Can not load the PAL module\n"); + return FALSE; + } + PDLLMAIN pRuntimeDllMain = (PDLLMAIN)dlsym(module->dl_handle, "CoreDllMain"); + if (!pRuntimeDllMain) + { + ERROR("Can not find the CoreDllMain entry point\n"); + return FALSE; + } + return pRuntimeDllMain(module->hinstance, DLL_PROCESS_ATTACH, nullptr); +} + +/*++ +Function : + LOADGetPalLibrary + + Load and initialize the PAL module. + +Parameters : + None + +Return value : + pointer to module struct + +--*/ +MODSTRUCT *LOADGetPalLibrary() +{ + if (pal_module == nullptr) + { + // Initialize the pal module (the module containing LOADGetPalLibrary). Assumes that + // the PAL is linked into the coreclr module because we use the module name containing + // this function for the coreclr path. + TRACE("Loading module for PAL library\n"); + + Dl_info info; + if (dladdr((PVOID)&LOADGetPalLibrary, &info) == 0) + { + ERROR("LOADGetPalLibrary: dladdr() failed. dlerror message is \"%s\"\n", dlerror()); + goto exit; + } + // Stash a copy of the CoreCLR installation path in a global variable. + // Make sure it's terminated with a slash. + if (g_szCoreCLRPath == nullptr) + { + size_t cbszCoreCLRPath = strlen(info.dli_fname) + 1; + g_szCoreCLRPath = (char*) InternalMalloc(cbszCoreCLRPath); + + if (g_szCoreCLRPath == nullptr) + { + ERROR("LOADGetPalLibrary: InternalMalloc failed!"); + goto exit; + } + + if (strcpy_s(g_szCoreCLRPath, cbszCoreCLRPath, info.dli_fname) != SAFECRT_SUCCESS) + { + ERROR("LOADGetPalLibrary: strcpy_s failed!"); + goto exit; + } + } + + pal_module = (MODSTRUCT *)LOADLoadLibrary(info.dli_fname, FALSE); + } + +exit: + return pal_module; +} + +/*++ +Function: + LockModuleList + +Abstract + Enter the critical section associated to the module list + +Parameter + void + +Return + void +--*/ +extern "C" +void LockModuleList() +{ + CPalThread * pThread = + (PALIsThreadDataInitialized() ? InternalGetCurrentThread() : nullptr); + + InternalEnterCriticalSection(pThread, &module_critsec); +} + +/*++ +Function: + UnlockModuleList + +Abstract + Leave the critical section associated to the module list + +Parameter + void + +Return + void +--*/ +extern "C" +void UnlockModuleList() +{ + CPalThread * pThread = + (PALIsThreadDataInitialized() ? InternalGetCurrentThread() : nullptr); + + InternalLeaveCriticalSection(pThread, &module_critsec); +} |