blob: 0923f6197902fbf5710024c8170c598dc4fabe2b [file]
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license 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/misc.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
#if HAVE_ALLOCA_H
#include <alloca.h>
#endif // HAVE_ALLOCA_H
#ifdef __APPLE__
#include <mach-o/dyld.h>
#include <mach-o/loader.h>
#endif // __APPLE__
#include <sys/types.h>
#include <sys/mman.h>
#if defined(__LINUX__) && !defined(__ANDROID__)
#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;
size_t g_cbszCoreCLRPath = MAX_LONGPATH * sizeof(char);
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 = InternalStrdup(lpLibFileName);
if (!lpstr)
{
ERROR("InternalStrdup failure!\n");
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
goto Done;
}
FILEDosToUnixPathA(lpstr);
hModule = LOADLoadLibrary(lpstr, TRUE);
/* let LOADLoadLibrary call SetLastError */
Done:
if (lpstr != nullptr)
{
InternalFree(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);
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;
}
HMODULE
PALAPI
GetModuleHandleW(
IN OPTIONAL LPCWSTR lpModuleName)
{
if (lpModuleName)
{
// UNIXTODO: Implement this!
ASSERT("Needs Implementation!!!");
return nullptr;
}
return (HMODULE)&exe_module;
}
BOOL
PALAPI
GetModuleHandleExW(
IN DWORD dwFlags,
IN OPTIONAL LPCWSTR lpModuleName,
OUT HMODULE *phModule)
{
*phModule = NULL;
return FALSE;
}
/*
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_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 = getenv("PAL_ForcePEMapFailure");
if (envVar && strlen(envVar) > 0)
{
TRACE("Forcing failure of PE file map, and retry\n");
PAL_LOADUnloadPEFile(loadedBase); // unload it
loadedBase = MAPMapPEFile(hFile); // load it again
}
}
#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
InternalFree(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)
{
InternalFree(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 */
InternalFree(module->lib_name);
InternalFree(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;
{
TRACE("Calling DllMain (%p) for module %S\n",
param.module->pDllMain,
param.module->lib_name ? param.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;
param.ret = param.module->pDllMain(param.module->hinstance, param.dwReason, param.lpReserved);
}
}
#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();
if (dwLastError == ERROR_INSUFFICIENT_BUFFER)
{
ERROR("wideLibraryPath converted to a multibyte string is longer than MAX_LONGPATH (%d)!\n", MAX_LONGPATH);
}
else
{
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)
{
printf("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");
InternalFree(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 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 thought 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.
if (strcmp(shortAsciiName, LIBC_NAME_WITHOUT_EXTENSION) == 0)
{
#if defined(__APPLE__)
shortAsciiName = "libc.dylib";
#elif defined(__FreeBSD__)
shortAsciiName = FREEBSD_LIBC;
#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;
}
/*++
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)
{
g_szCoreCLRPath = (char*) InternalMalloc(g_cbszCoreCLRPath);
if (g_szCoreCLRPath == nullptr)
{
ERROR("LOADGetPalLibrary: InternalMalloc failed!");
goto exit;
}
}
if (strcpy_s(g_szCoreCLRPath, g_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);
}