xref: /freebsd/contrib/llvm-project/llvm/lib/Support/Windows/DynamicLibrary.inc (revision be092bcde96bdcfde9013d60e442cca023bfbd1b)
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 
24 DynamicLibrary::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 
34 void *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 
56 static DynamicLibrary::HandleSet *IsOpenedHandlesInstance(void *Handle) {
57   DynamicLibrary::HandleSet &Inst = getGlobals().OpenedHandles;
58   return Handle == &Inst ? &Inst : nullptr;
59 }
60 
61 void 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 
68 static 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 
86 void *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 
174 static 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