1//===- Win32/DynamicLibrary.cpp - Win32 DL Implementation -------*- C++ -*-===// 2// 3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4// See https://llvm.org/LICENSE.txt for license information. 5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6// 7//===----------------------------------------------------------------------===// 8// 9// This file provides the Win32 specific implementation of DynamicLibrary. 10// 11//===----------------------------------------------------------------------===// 12 13#include "llvm/Support/ConvertUTF.h" 14#include "llvm/Support/Windows/WindowsSupport.h" 15#include "llvm/Support/raw_ostream.h" 16 17#include <psapi.h> 18 19//===----------------------------------------------------------------------===// 20//=== WARNING: Implementation here must contain only Win32 specific code 21//=== and must not be UNIX code. 22//===----------------------------------------------------------------------===// 23 24DynamicLibrary::HandleSet::~HandleSet() { 25 for (void *Handle : llvm::reverse(Handles)) 26 FreeLibrary(HMODULE(Handle)); 27 28 // 'Process' should not be released on Windows. 29 assert((!Process || Process == this) && "Bad Handle"); 30 // llvm_shutdown called, Return to default 31 DynamicLibrary::SearchOrder = DynamicLibrary::SO_Linker; 32} 33 34void *DynamicLibrary::HandleSet::DLOpen(const char *File, std::string *Err) { 35 // Create the instance and return it to be the *Process* handle 36 // simillar to dlopen(NULL, RTLD_LAZY|RTLD_GLOBAL) 37 if (!File) 38 return &getGlobals().OpenedHandles; 39 40 SmallVector<wchar_t, MAX_PATH> FileUnicode; 41 if (std::error_code ec = windows::UTF8ToUTF16(File, FileUnicode)) { 42 SetLastError(ec.value()); 43 MakeErrMsg(Err, std::string(File) + ": Can't convert to UTF-16"); 44 return &DynamicLibrary::Invalid; 45 } 46 47 HMODULE Handle = LoadLibraryW(FileUnicode.data()); 48 if (Handle == NULL) { 49 MakeErrMsg(Err, std::string(File) + ": Can't open"); 50 return &DynamicLibrary::Invalid; 51 } 52 53 return reinterpret_cast<void *>(Handle); 54} 55 56static DynamicLibrary::HandleSet *IsOpenedHandlesInstance(void *Handle) { 57 DynamicLibrary::HandleSet &Inst = getGlobals().OpenedHandles; 58 return Handle == &Inst ? &Inst : nullptr; 59} 60 61void DynamicLibrary::HandleSet::DLClose(void *Handle) { 62 if (HandleSet *HS = IsOpenedHandlesInstance(Handle)) 63 HS->Process = nullptr; // Just drop the *Process* handle. 64 else 65 FreeLibrary((HMODULE)Handle); 66} 67 68static bool GetProcessModules(HANDLE H, DWORD &Bytes, HMODULE *Data = nullptr) { 69 // EnumProcessModules will fail on Windows 64 while some versions of 70 // MingW-32 don't have EnumProcessModulesEx. 71 if ( 72#ifdef _WIN64 73 !EnumProcessModulesEx(H, Data, Bytes, &Bytes, LIST_MODULES_64BIT) 74#else 75 !EnumProcessModules(H, Data, Bytes, &Bytes) 76#endif 77 ) { 78 std::string Err; 79 if (MakeErrMsg(&Err, "EnumProcessModules failure")) 80 llvm::errs() << Err << "\n"; 81 return false; 82 } 83 return true; 84} 85 86void *DynamicLibrary::HandleSet::DLSym(void *Handle, const char *Symbol) { 87 HandleSet *HS = IsOpenedHandlesInstance(Handle); 88 if (!HS) 89 return (void *)uintptr_t(GetProcAddress((HMODULE)Handle, Symbol)); 90 91 // Could have done a dlclose on the *Process* handle 92 if (!HS->Process) 93 return nullptr; 94 95 // Trials indicate EnumProcessModulesEx is consistantly faster than using 96 // EnumerateLoadedModules64 or CreateToolhelp32Snapshot. 97 // 98 // | Handles | DbgHelp.dll | CreateSnapshot | EnumProcessModulesEx 99 // |=========|=============|======================================== 100 // | 37 | 0.0000585 * | 0.0003031 | 0.0000152 101 // | 1020 | 0.0026310 * | 0.0121598 | 0.0002683 102 // | 2084 | 0.0149418 * | 0.0369936 | 0.0005610 103 // 104 // * Not including the load time of Dbghelp.dll (~.005 sec) 105 // 106 // There's still a case to somehow cache the result of EnumProcessModulesEx 107 // across invocations, but the complication of doing that properly... 108 // Possibly using LdrRegisterDllNotification to invalidate the cache? 109 110 DWORD Bytes = 0; 111 HMODULE Self = HMODULE(GetCurrentProcess()); 112 if (!GetProcessModules(Self, Bytes)) 113 return nullptr; 114 115 // Get the most recent list in case any modules added/removed between calls 116 // to EnumProcessModulesEx that gets the amount of, then copies the HMODULES. 117 // MSDN is pretty clear that if the module list changes during the call to 118 // EnumProcessModulesEx the results should not be used. 119 std::vector<HMODULE> Handles; 120 do { 121 assert(Bytes && ((Bytes % sizeof(HMODULE)) == 0) && 122 "Should have at least one module and be aligned"); 123 Handles.resize(Bytes / sizeof(HMODULE)); 124 if (!GetProcessModules(Self, Bytes, Handles.data())) 125 return nullptr; 126 } while (Bytes != (Handles.size() * sizeof(HMODULE))); 127 128 // Try EXE first, mirroring what dlsym(dlopen(NULL)) does. 129 if (FARPROC Ptr = GetProcAddress(HMODULE(Handles.front()), Symbol)) 130 return (void *)uintptr_t(Ptr); 131 132 if (Handles.size() > 1) { 133 // This is different behaviour than what Posix dlsym(dlopen(NULL)) does. 134 // Doing that here is causing real problems for the JIT where msvc.dll 135 // and ucrt.dll can define the same symbols. The runtime linker will choose 136 // symbols from ucrt.dll first, but iterating NOT in reverse here would 137 // mean that the msvc.dll versions would be returned. 138 139 for (auto I = Handles.rbegin(), E = Handles.rend() - 1; I != E; ++I) { 140 if (FARPROC Ptr = GetProcAddress(HMODULE(*I), Symbol)) 141 return (void *)uintptr_t(Ptr); 142 } 143 } 144 return nullptr; 145} 146 147// Stack probing routines are in the support library (e.g. libgcc), but we don't 148// have dynamic linking on windows. Provide a hook. 149#define EXPLICIT_SYMBOL(SYM) \ 150 extern "C" { \ 151 extern void *SYM; \ 152 } 153#define EXPLICIT_SYMBOL2(SYMFROM, SYMTO) EXPLICIT_SYMBOL(SYMTO) 154 155#ifdef _M_IX86 156// Win32 on x86 implements certain single-precision math functions as macros. 157// These functions are not exported by the DLL, but will still be needed 158// for symbol-resolution by the JIT loader. Therefore, this Support libray 159// provides helper functions with the same implementation. 160 161#define INLINE_DEF_SYMBOL1(TYP, SYM) \ 162 extern "C" TYP inline_##SYM(TYP _X) { return SYM(_X); } 163#define INLINE_DEF_SYMBOL2(TYP, SYM) \ 164 extern "C" TYP inline_##SYM(TYP _X, TYP _Y) { return SYM(_X, _Y); } 165#endif 166 167#include "explicit_symbols.inc" 168 169#undef EXPLICIT_SYMBOL 170#undef EXPLICIT_SYMBOL2 171#undef INLINE_DEF_SYMBOL1 172#undef INLINE_DEF_SYMBOL2 173 174static void *DoSearch(const char *SymbolName) { 175 176#define EXPLICIT_SYMBOL(SYM) \ 177 if (!strcmp(SymbolName, #SYM)) \ 178 return (void *)&SYM; 179#define EXPLICIT_SYMBOL2(SYMFROM, SYMTO) \ 180 if (!strcmp(SymbolName, #SYMFROM)) \ 181 return (void *)&SYMTO; 182 183#ifdef _M_IX86 184#define INLINE_DEF_SYMBOL1(TYP, SYM) \ 185 if (!strcmp(SymbolName, #SYM)) \ 186 return (void *)&inline_##SYM; 187#define INLINE_DEF_SYMBOL2(TYP, SYM) INLINE_DEF_SYMBOL1(TYP, SYM) 188#endif 189 190 { 191#include "explicit_symbols.inc" 192 } 193 194#undef EXPLICIT_SYMBOL 195#undef EXPLICIT_SYMBOL2 196#undef INLINE_DEF_SYMBOL1 197#undef INLINE_DEF_SYMBOL2 198 199 return nullptr; 200} 201