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