1 | //===- coff_platform.cpp --------------------------------------------------===// |
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 contains code required to load the rest of the COFF runtime. |
10 | // |
11 | //===----------------------------------------------------------------------===// |
12 | |
13 | #define NOMINMAX |
14 | #include <windows.h> |
15 | |
16 | #include "coff_platform.h" |
17 | |
18 | #include "debug.h" |
19 | #include "error.h" |
20 | #include "wrapper_function_utils.h" |
21 | |
22 | #include <array> |
23 | #include <list> |
24 | #include <map> |
25 | #include <mutex> |
26 | #include <sstream> |
27 | #include <string_view> |
28 | #include <vector> |
29 | |
30 | #define DEBUG_TYPE "coff_platform" |
31 | |
32 | using namespace __orc_rt; |
33 | |
34 | namespace __orc_rt { |
35 | |
36 | using COFFJITDylibDepInfo = std::vector<ExecutorAddr>; |
37 | using COFFJITDylibDepInfoMap = |
38 | std::unordered_map<ExecutorAddr, COFFJITDylibDepInfo>; |
39 | |
40 | using SPSCOFFObjectSectionsMap = |
41 | SPSSequence<SPSTuple<SPSString, SPSExecutorAddrRange>>; |
42 | |
43 | using SPSCOFFJITDylibDepInfo = SPSSequence<SPSExecutorAddr>; |
44 | |
45 | using SPSCOFFJITDylibDepInfoMap = |
46 | SPSSequence<SPSTuple<SPSExecutorAddr, SPSCOFFJITDylibDepInfo>>; |
47 | |
48 | } // namespace __orc_rt |
49 | |
50 | ORC_RT_JIT_DISPATCH_TAG(__orc_rt_coff_symbol_lookup_tag) |
51 | ORC_RT_JIT_DISPATCH_TAG(__orc_rt_coff_push_initializers_tag) |
52 | |
53 | namespace { |
54 | class COFFPlatformRuntimeState { |
55 | private: |
56 | // Ctor/dtor section. |
57 | // Manage lists of *tor functions sorted by the last character of subsection |
58 | // name. |
59 | struct XtorSection { |
60 | void Register(char SubsectionChar, span<void (*)(void)> Xtors) { |
61 | Subsections[SubsectionChar - 'A'].push_back(x: Xtors); |
62 | SubsectionsNew[SubsectionChar - 'A'].push_back(x: Xtors); |
63 | } |
64 | |
65 | void RegisterNoRun(char SubsectionChar, span<void (*)(void)> Xtors) { |
66 | Subsections[SubsectionChar - 'A'].push_back(x: Xtors); |
67 | } |
68 | |
69 | void Reset() { SubsectionsNew = Subsections; } |
70 | |
71 | void RunAllNewAndFlush(); |
72 | |
73 | private: |
74 | std::array<std::vector<span<void (*)(void)>>, 26> Subsections; |
75 | std::array<std::vector<span<void (*)(void)>>, 26> SubsectionsNew; |
76 | }; |
77 | |
78 | struct JITDylibState { |
79 | std::string Name; |
80 | void * = nullptr; |
81 | size_t LinkedAgainstRefCount = 0; |
82 | size_t DlRefCount = 0; |
83 | std::vector<JITDylibState *> Deps; |
84 | std::vector<void (*)(void)> AtExits; |
85 | XtorSection CInitSection; // XIA~XIZ |
86 | XtorSection CXXInitSection; // XCA~XCZ |
87 | XtorSection CPreTermSection; // XPA~XPZ |
88 | XtorSection CTermSection; // XTA~XTZ |
89 | |
90 | bool referenced() const { |
91 | return LinkedAgainstRefCount != 0 || DlRefCount != 0; |
92 | } |
93 | }; |
94 | |
95 | public: |
96 | static void initialize(); |
97 | static COFFPlatformRuntimeState &get(); |
98 | static bool isInitialized() { return CPS; } |
99 | static void destroy(); |
100 | |
101 | COFFPlatformRuntimeState() = default; |
102 | |
103 | // Delete copy and move constructors. |
104 | COFFPlatformRuntimeState(const COFFPlatformRuntimeState &) = delete; |
105 | COFFPlatformRuntimeState & |
106 | operator=(const COFFPlatformRuntimeState &) = delete; |
107 | COFFPlatformRuntimeState(COFFPlatformRuntimeState &&) = delete; |
108 | COFFPlatformRuntimeState &operator=(COFFPlatformRuntimeState &&) = delete; |
109 | |
110 | const char *dlerror(); |
111 | void *dlopen(std::string_view Name, int Mode); |
112 | int dlclose(void *); |
113 | void *dlsym(void *, std::string_view Symbol); |
114 | |
115 | Error registerJITDylib(std::string Name, void *); |
116 | Error deregisterJITDylib(void *); |
117 | |
118 | Error registerAtExit(ExecutorAddr , void (*AtExit)(void)); |
119 | |
120 | Error registerObjectSections( |
121 | ExecutorAddr , |
122 | std::vector<std::pair<std::string_view, ExecutorAddrRange>> Secs, |
123 | bool RunInitializers); |
124 | Error deregisterObjectSections( |
125 | ExecutorAddr , |
126 | std::vector<std::pair<std::string_view, ExecutorAddrRange>> Secs); |
127 | |
128 | void *findJITDylibBaseByPC(uint64_t PC); |
129 | |
130 | private: |
131 | Error registerBlockRange(ExecutorAddr , ExecutorAddrRange Range); |
132 | Error deregisterBlockRange(ExecutorAddr , ExecutorAddrRange Range); |
133 | |
134 | Error registerSEHFrames(ExecutorAddr , |
135 | ExecutorAddrRange SEHFrameRange); |
136 | Error deregisterSEHFrames(ExecutorAddr , |
137 | ExecutorAddrRange SEHFrameRange); |
138 | |
139 | Expected<void *> dlopenImpl(std::string_view Path, int Mode); |
140 | Error dlopenFull(JITDylibState &JDS); |
141 | Error dlopenInitialize(JITDylibState &JDS, COFFJITDylibDepInfoMap &DepInfo); |
142 | |
143 | Error dlcloseImpl(void *DSOHandle); |
144 | Error dlcloseDeinitialize(JITDylibState &JDS); |
145 | |
146 | JITDylibState *getJITDylibStateByHeader(void *DSOHandle); |
147 | JITDylibState *getJITDylibStateByName(std::string_view Path); |
148 | Expected<ExecutorAddr> lookupSymbolInJITDylib(void *DSOHandle, |
149 | std::string_view Symbol); |
150 | |
151 | static COFFPlatformRuntimeState *CPS; |
152 | |
153 | std::recursive_mutex JDStatesMutex; |
154 | std::map<void *, JITDylibState> JDStates; |
155 | struct BlockRange { |
156 | void *; |
157 | size_t Size; |
158 | }; |
159 | std::map<void *, BlockRange> BlockRanges; |
160 | std::unordered_map<std::string_view, void *> ; |
161 | std::string DLFcnError; |
162 | }; |
163 | |
164 | } // namespace |
165 | |
166 | COFFPlatformRuntimeState *COFFPlatformRuntimeState::CPS = nullptr; |
167 | |
168 | COFFPlatformRuntimeState::JITDylibState * |
169 | COFFPlatformRuntimeState::(void *) { |
170 | auto I = JDStates.find(x: Header); |
171 | if (I == JDStates.end()) |
172 | return nullptr; |
173 | return &I->second; |
174 | } |
175 | |
176 | COFFPlatformRuntimeState::JITDylibState * |
177 | COFFPlatformRuntimeState::getJITDylibStateByName(std::string_view Name) { |
178 | // FIXME: Avoid creating string copy here. |
179 | auto I = JDNameToHeader.find(x: std::string(Name.data(), Name.size())); |
180 | if (I == JDNameToHeader.end()) |
181 | return nullptr; |
182 | void *H = I->second; |
183 | auto J = JDStates.find(x: H); |
184 | assert(J != JDStates.end() && |
185 | "JITDylib has name map entry but no header map entry" ); |
186 | return &J->second; |
187 | } |
188 | |
189 | Error COFFPlatformRuntimeState::registerJITDylib(std::string Name, |
190 | void *) { |
191 | ORC_RT_DEBUG({ |
192 | printdbg("Registering JITDylib %s: Header = %p\n" , Name.c_str(), Header); |
193 | }); |
194 | std::lock_guard<std::recursive_mutex> Lock(JDStatesMutex); |
195 | if (JDStates.count(x: Header)) { |
196 | std::ostringstream ErrStream; |
197 | ErrStream << "Duplicate JITDylib registration for header " << Header |
198 | << " (name = " << Name << ")" ; |
199 | return make_error<StringError>(Args: ErrStream.str()); |
200 | } |
201 | if (JDNameToHeader.count(x: Name)) { |
202 | std::ostringstream ErrStream; |
203 | ErrStream << "Duplicate JITDylib registration for header " << Header |
204 | << " (header = " << Header << ")" ; |
205 | return make_error<StringError>(Args: ErrStream.str()); |
206 | } |
207 | |
208 | auto &JDS = JDStates[Header]; |
209 | JDS.Name = std::move(t&: Name); |
210 | JDS.Header = Header; |
211 | JDNameToHeader[JDS.Name] = Header; |
212 | return Error::success(); |
213 | } |
214 | |
215 | Error COFFPlatformRuntimeState::deregisterJITDylib(void *) { |
216 | std::lock_guard<std::recursive_mutex> Lock(JDStatesMutex); |
217 | auto I = JDStates.find(x: Header); |
218 | if (I == JDStates.end()) { |
219 | std::ostringstream ErrStream; |
220 | ErrStream << "Attempted to deregister unrecognized header " << Header; |
221 | return make_error<StringError>(Args: ErrStream.str()); |
222 | } |
223 | |
224 | // Remove std::string construction once we can use C++20. |
225 | auto J = JDNameToHeader.find( |
226 | x: std::string(I->second.Name.data(), I->second.Name.size())); |
227 | assert(J != JDNameToHeader.end() && |
228 | "Missing JDNameToHeader entry for JITDylib" ); |
229 | |
230 | ORC_RT_DEBUG({ |
231 | printdbg("Deregistering JITDylib %s: Header = %p\n" , I->second.Name.c_str(), |
232 | Header); |
233 | }); |
234 | |
235 | JDNameToHeader.erase(position: J); |
236 | JDStates.erase(position: I); |
237 | return Error::success(); |
238 | } |
239 | |
240 | void COFFPlatformRuntimeState::XtorSection::RunAllNewAndFlush() { |
241 | for (auto &Subsection : SubsectionsNew) { |
242 | for (auto &XtorGroup : Subsection) |
243 | for (auto &Xtor : XtorGroup) |
244 | if (Xtor) |
245 | Xtor(); |
246 | Subsection.clear(); |
247 | } |
248 | } |
249 | |
250 | const char *COFFPlatformRuntimeState::dlerror() { return DLFcnError.c_str(); } |
251 | |
252 | void *COFFPlatformRuntimeState::dlopen(std::string_view Path, int Mode) { |
253 | ORC_RT_DEBUG({ |
254 | std::string S(Path.data(), Path.size()); |
255 | printdbg("COFFPlatform::dlopen(\"%s\")\n" , S.c_str()); |
256 | }); |
257 | std::lock_guard<std::recursive_mutex> Lock(JDStatesMutex); |
258 | if (auto H = dlopenImpl(Path, Mode)) |
259 | return *H; |
260 | else { |
261 | // FIXME: Make dlerror thread safe. |
262 | DLFcnError = toString(Err: H.takeError()); |
263 | return nullptr; |
264 | } |
265 | } |
266 | |
267 | int COFFPlatformRuntimeState::dlclose(void *DSOHandle) { |
268 | ORC_RT_DEBUG({ |
269 | auto *JDS = getJITDylibStateByHeader(DSOHandle); |
270 | std::string DylibName; |
271 | if (JDS) { |
272 | std::string S; |
273 | printdbg("COFFPlatform::dlclose(%p) (%s)\n" , DSOHandle, S.c_str()); |
274 | } else |
275 | printdbg("COFFPlatform::dlclose(%p) (%s)\n" , DSOHandle, "invalid handle" ); |
276 | }); |
277 | std::lock_guard<std::recursive_mutex> Lock(JDStatesMutex); |
278 | if (auto Err = dlcloseImpl(DSOHandle)) { |
279 | // FIXME: Make dlerror thread safe. |
280 | DLFcnError = toString(Err: std::move(t&: Err)); |
281 | return -1; |
282 | } |
283 | return 0; |
284 | } |
285 | |
286 | void *COFFPlatformRuntimeState::dlsym(void *, std::string_view Symbol) { |
287 | auto Addr = lookupSymbolInJITDylib(DSOHandle: Header, Symbol); |
288 | if (!Addr) { |
289 | return 0; |
290 | } |
291 | |
292 | return Addr->toPtr<void *>(); |
293 | } |
294 | |
295 | Expected<void *> COFFPlatformRuntimeState::dlopenImpl(std::string_view Path, |
296 | int Mode) { |
297 | // Try to find JITDylib state by name. |
298 | auto *JDS = getJITDylibStateByName(Name: Path); |
299 | |
300 | if (!JDS) |
301 | return make_error<StringError>(Args: "No registered JTIDylib for path " + |
302 | std::string(Path.data(), Path.size())); |
303 | |
304 | if (auto Err = dlopenFull(JDS&: *JDS)) |
305 | return std::move(t&: Err); |
306 | |
307 | // Bump the ref-count on this dylib. |
308 | ++JDS->DlRefCount; |
309 | |
310 | // Return the header address. |
311 | return JDS->Header; |
312 | } |
313 | |
314 | Error COFFPlatformRuntimeState::dlopenFull(JITDylibState &JDS) { |
315 | // Call back to the JIT to push the initializers. |
316 | Expected<COFFJITDylibDepInfoMap> DepInfoMap((COFFJITDylibDepInfoMap())); |
317 | if (auto Err = WrapperFunction<SPSExpected<SPSCOFFJITDylibDepInfoMap>( |
318 | SPSExecutorAddr)>::call(FnTag: &__orc_rt_coff_push_initializers_tag, |
319 | Result&: DepInfoMap, |
320 | Args: ExecutorAddr::fromPtr(Ptr: JDS.Header))) |
321 | return Err; |
322 | if (!DepInfoMap) |
323 | return DepInfoMap.takeError(); |
324 | |
325 | if (auto Err = dlopenInitialize(JDS, DepInfo&: *DepInfoMap)) |
326 | return Err; |
327 | |
328 | if (!DepInfoMap->empty()) { |
329 | ORC_RT_DEBUG({ |
330 | printdbg("Unrecognized dep-info key headers in dlopen of %s\n" , |
331 | JDS.Name.c_str()); |
332 | }); |
333 | std::ostringstream ErrStream; |
334 | ErrStream << "Encountered unrecognized dep-info key headers " |
335 | "while processing dlopen of " |
336 | << JDS.Name; |
337 | return make_error<StringError>(Args: ErrStream.str()); |
338 | } |
339 | |
340 | return Error::success(); |
341 | } |
342 | |
343 | Error COFFPlatformRuntimeState::dlopenInitialize( |
344 | JITDylibState &JDS, COFFJITDylibDepInfoMap &DepInfo) { |
345 | ORC_RT_DEBUG({ |
346 | printdbg("COFFPlatformRuntimeState::dlopenInitialize(\"%s\")\n" , |
347 | JDS.Name.c_str()); |
348 | }); |
349 | |
350 | // Skip visited dependency. |
351 | auto I = DepInfo.find(x: ExecutorAddr::fromPtr(Ptr: JDS.Header)); |
352 | if (I == DepInfo.end()) |
353 | return Error::success(); |
354 | |
355 | auto DI = std::move(t&: I->second); |
356 | DepInfo.erase(position: I); |
357 | |
358 | // Run initializers of dependencies in proper order by depth-first traversal |
359 | // of dependency graph. |
360 | std::vector<JITDylibState *> OldDeps; |
361 | std::swap(x&: JDS.Deps, y&: OldDeps); |
362 | JDS.Deps.reserve(n: DI.size()); |
363 | for (auto : DI) { |
364 | auto *DepJDS = getJITDylibStateByHeader(Header: DepHeaderAddr.toPtr<void *>()); |
365 | if (!DepJDS) { |
366 | std::ostringstream ErrStream; |
367 | ErrStream << "Encountered unrecognized dep header " |
368 | << DepHeaderAddr.toPtr<void *>() << " while initializing " |
369 | << JDS.Name; |
370 | return make_error<StringError>(Args: ErrStream.str()); |
371 | } |
372 | ++DepJDS->LinkedAgainstRefCount; |
373 | if (auto Err = dlopenInitialize(JDS&: *DepJDS, DepInfo)) |
374 | return Err; |
375 | } |
376 | |
377 | // Run static initializers. |
378 | JDS.CInitSection.RunAllNewAndFlush(); |
379 | JDS.CXXInitSection.RunAllNewAndFlush(); |
380 | |
381 | // Decrement old deps. |
382 | for (auto *DepJDS : OldDeps) { |
383 | --DepJDS->LinkedAgainstRefCount; |
384 | if (!DepJDS->referenced()) |
385 | if (auto Err = dlcloseDeinitialize(JDS&: *DepJDS)) |
386 | return Err; |
387 | } |
388 | |
389 | return Error::success(); |
390 | } |
391 | |
392 | Error COFFPlatformRuntimeState::dlcloseImpl(void *DSOHandle) { |
393 | // Try to find JITDylib state by header. |
394 | auto *JDS = getJITDylibStateByHeader(Header: DSOHandle); |
395 | |
396 | if (!JDS) { |
397 | std::ostringstream ErrStream; |
398 | ErrStream << "No registered JITDylib for " << DSOHandle; |
399 | return make_error<StringError>(Args: ErrStream.str()); |
400 | } |
401 | |
402 | // Bump the ref-count. |
403 | --JDS->DlRefCount; |
404 | |
405 | if (!JDS->referenced()) |
406 | return dlcloseDeinitialize(JDS&: *JDS); |
407 | |
408 | return Error::success(); |
409 | } |
410 | |
411 | Error COFFPlatformRuntimeState::dlcloseDeinitialize(JITDylibState &JDS) { |
412 | ORC_RT_DEBUG({ |
413 | printdbg("COFFPlatformRuntimeState::dlcloseDeinitialize(\"%s\")\n" , |
414 | JDS.Name.c_str()); |
415 | }); |
416 | |
417 | // Run atexits |
418 | for (auto AtExit : JDS.AtExits) |
419 | AtExit(); |
420 | JDS.AtExits.clear(); |
421 | |
422 | // Run static terminators. |
423 | JDS.CPreTermSection.RunAllNewAndFlush(); |
424 | JDS.CTermSection.RunAllNewAndFlush(); |
425 | |
426 | // Queue all xtors as new again. |
427 | JDS.CInitSection.Reset(); |
428 | JDS.CXXInitSection.Reset(); |
429 | JDS.CPreTermSection.Reset(); |
430 | JDS.CTermSection.Reset(); |
431 | |
432 | // Deinitialize any dependencies. |
433 | for (auto *DepJDS : JDS.Deps) { |
434 | --DepJDS->LinkedAgainstRefCount; |
435 | if (!DepJDS->referenced()) |
436 | if (auto Err = dlcloseDeinitialize(JDS&: *DepJDS)) |
437 | return Err; |
438 | } |
439 | |
440 | return Error::success(); |
441 | } |
442 | |
443 | Expected<ExecutorAddr> |
444 | COFFPlatformRuntimeState::lookupSymbolInJITDylib(void *, |
445 | std::string_view Sym) { |
446 | Expected<ExecutorAddr> Result((ExecutorAddr())); |
447 | if (auto Err = WrapperFunction<SPSExpected<SPSExecutorAddr>( |
448 | SPSExecutorAddr, SPSString)>::call(FnTag: &__orc_rt_coff_symbol_lookup_tag, |
449 | Result, |
450 | Args: ExecutorAddr::fromPtr(Ptr: header), |
451 | Args: Sym)) |
452 | return std::move(t&: Err); |
453 | return Result; |
454 | } |
455 | |
456 | Error COFFPlatformRuntimeState::registerObjectSections( |
457 | ExecutorAddr , |
458 | std::vector<std::pair<std::string_view, ExecutorAddrRange>> Secs, |
459 | bool RunInitializers) { |
460 | std::lock_guard<std::recursive_mutex> Lock(JDStatesMutex); |
461 | auto I = JDStates.find(x: HeaderAddr.toPtr<void *>()); |
462 | if (I == JDStates.end()) { |
463 | std::ostringstream ErrStream; |
464 | ErrStream << "Unrecognized header " << HeaderAddr.getValue(); |
465 | return make_error<StringError>(Args: ErrStream.str()); |
466 | } |
467 | auto &JDState = I->second; |
468 | for (auto &KV : Secs) { |
469 | if (auto Err = registerBlockRange(HeaderAddr, Range: KV.second)) |
470 | return Err; |
471 | if (KV.first.empty()) |
472 | continue; |
473 | char LastChar = KV.first.data()[KV.first.size() - 1]; |
474 | if (KV.first == ".pdata" ) { |
475 | if (auto Err = registerSEHFrames(HeaderAddr, SEHFrameRange: KV.second)) |
476 | return Err; |
477 | } else if (KV.first >= ".CRT$XIA" && KV.first <= ".CRT$XIZ" ) { |
478 | if (RunInitializers) |
479 | JDState.CInitSection.Register(SubsectionChar: LastChar, |
480 | Xtors: KV.second.toSpan<void (*)(void)>()); |
481 | else |
482 | JDState.CInitSection.RegisterNoRun(SubsectionChar: LastChar, |
483 | Xtors: KV.second.toSpan<void (*)(void)>()); |
484 | } else if (KV.first >= ".CRT$XCA" && KV.first <= ".CRT$XCZ" ) { |
485 | if (RunInitializers) |
486 | JDState.CXXInitSection.Register(SubsectionChar: LastChar, |
487 | Xtors: KV.second.toSpan<void (*)(void)>()); |
488 | else |
489 | JDState.CXXInitSection.RegisterNoRun( |
490 | SubsectionChar: LastChar, Xtors: KV.second.toSpan<void (*)(void)>()); |
491 | } else if (KV.first >= ".CRT$XPA" && KV.first <= ".CRT$XPZ" ) |
492 | JDState.CPreTermSection.Register(SubsectionChar: LastChar, |
493 | Xtors: KV.second.toSpan<void (*)(void)>()); |
494 | else if (KV.first >= ".CRT$XTA" && KV.first <= ".CRT$XTZ" ) |
495 | JDState.CTermSection.Register(SubsectionChar: LastChar, |
496 | Xtors: KV.second.toSpan<void (*)(void)>()); |
497 | } |
498 | return Error::success(); |
499 | } |
500 | |
501 | Error COFFPlatformRuntimeState::deregisterObjectSections( |
502 | ExecutorAddr , |
503 | std::vector<std::pair<std::string_view, ExecutorAddrRange>> Secs) { |
504 | std::lock_guard<std::recursive_mutex> Lock(JDStatesMutex); |
505 | auto I = JDStates.find(x: HeaderAddr.toPtr<void *>()); |
506 | if (I == JDStates.end()) { |
507 | std::ostringstream ErrStream; |
508 | ErrStream << "Attempted to deregister unrecognized header " |
509 | << HeaderAddr.getValue(); |
510 | return make_error<StringError>(Args: ErrStream.str()); |
511 | } |
512 | for (auto &KV : Secs) { |
513 | if (auto Err = deregisterBlockRange(HeaderAddr, Range: KV.second)) |
514 | return Err; |
515 | if (KV.first == ".pdata" ) |
516 | if (auto Err = deregisterSEHFrames(HeaderAddr, SEHFrameRange: KV.second)) |
517 | return Err; |
518 | } |
519 | return Error::success(); |
520 | } |
521 | |
522 | Error COFFPlatformRuntimeState::registerSEHFrames( |
523 | ExecutorAddr , ExecutorAddrRange SEHFrameRange) { |
524 | int N = (SEHFrameRange.End.getValue() - SEHFrameRange.Start.getValue()) / |
525 | sizeof(RUNTIME_FUNCTION); |
526 | auto Func = SEHFrameRange.Start.toPtr<PRUNTIME_FUNCTION>(); |
527 | if (!RtlAddFunctionTable(Func, N, |
528 | static_cast<DWORD64>(HeaderAddr.getValue()))) |
529 | return make_error<StringError>(Args: "Failed to register SEH frames" ); |
530 | return Error::success(); |
531 | } |
532 | |
533 | Error COFFPlatformRuntimeState::deregisterSEHFrames( |
534 | ExecutorAddr , ExecutorAddrRange SEHFrameRange) { |
535 | if (!RtlDeleteFunctionTable(SEHFrameRange.Start.toPtr<PRUNTIME_FUNCTION>())) |
536 | return make_error<StringError>(Args: "Failed to deregister SEH frames" ); |
537 | return Error::success(); |
538 | } |
539 | |
540 | Error COFFPlatformRuntimeState::registerBlockRange(ExecutorAddr , |
541 | ExecutorAddrRange Range) { |
542 | assert(!BlockRanges.count(Range.Start.toPtr<void *>()) && |
543 | "Block range address already registered" ); |
544 | BlockRange B = {.Header: HeaderAddr.toPtr<void *>(), .Size: Range.size()}; |
545 | BlockRanges.emplace(args: Range.Start.toPtr<void *>(), args&: B); |
546 | return Error::success(); |
547 | } |
548 | |
549 | Error COFFPlatformRuntimeState::deregisterBlockRange(ExecutorAddr , |
550 | ExecutorAddrRange Range) { |
551 | assert(BlockRanges.count(Range.Start.toPtr<void *>()) && |
552 | "Block range address not registered" ); |
553 | BlockRanges.erase(x: Range.Start.toPtr<void *>()); |
554 | return Error::success(); |
555 | } |
556 | |
557 | Error COFFPlatformRuntimeState::registerAtExit(ExecutorAddr , |
558 | void (*AtExit)(void)) { |
559 | std::lock_guard<std::recursive_mutex> Lock(JDStatesMutex); |
560 | auto I = JDStates.find(x: HeaderAddr.toPtr<void *>()); |
561 | if (I == JDStates.end()) { |
562 | std::ostringstream ErrStream; |
563 | ErrStream << "Unrecognized header " << HeaderAddr.getValue(); |
564 | return make_error<StringError>(Args: ErrStream.str()); |
565 | } |
566 | I->second.AtExits.push_back(x: AtExit); |
567 | return Error::success(); |
568 | } |
569 | |
570 | void COFFPlatformRuntimeState::initialize() { |
571 | assert(!CPS && "COFFPlatformRuntimeState should be null" ); |
572 | CPS = new COFFPlatformRuntimeState(); |
573 | } |
574 | |
575 | COFFPlatformRuntimeState &COFFPlatformRuntimeState::get() { |
576 | assert(CPS && "COFFPlatformRuntimeState not initialized" ); |
577 | return *CPS; |
578 | } |
579 | |
580 | void COFFPlatformRuntimeState::destroy() { |
581 | assert(CPS && "COFFPlatformRuntimeState not initialized" ); |
582 | delete CPS; |
583 | } |
584 | |
585 | void *COFFPlatformRuntimeState::findJITDylibBaseByPC(uint64_t PC) { |
586 | std::lock_guard<std::recursive_mutex> Lock(JDStatesMutex); |
587 | auto It = BlockRanges.upper_bound(x: reinterpret_cast<void *>(PC)); |
588 | if (It == BlockRanges.begin()) |
589 | return nullptr; |
590 | --It; |
591 | auto &Range = It->second; |
592 | if (PC >= reinterpret_cast<uint64_t>(It->first) + Range.Size) |
593 | return nullptr; |
594 | return Range.Header; |
595 | } |
596 | |
597 | ORC_RT_INTERFACE orc_rt_CWrapperFunctionResult |
598 | __orc_rt_coff_platform_bootstrap(char *ArgData, size_t ArgSize) { |
599 | COFFPlatformRuntimeState::initialize(); |
600 | return WrapperFunctionResult().release(); |
601 | } |
602 | |
603 | ORC_RT_INTERFACE orc_rt_CWrapperFunctionResult |
604 | __orc_rt_coff_platform_shutdown(char *ArgData, size_t ArgSize) { |
605 | COFFPlatformRuntimeState::destroy(); |
606 | return WrapperFunctionResult().release(); |
607 | } |
608 | |
609 | ORC_RT_INTERFACE orc_rt_CWrapperFunctionResult |
610 | __orc_rt_coff_register_jitdylib(char *ArgData, size_t ArgSize) { |
611 | return WrapperFunction<SPSError(SPSString, SPSExecutorAddr)>::handle( |
612 | ArgData, ArgSize, |
613 | Handler: [](std::string &Name, ExecutorAddr ) { |
614 | return COFFPlatformRuntimeState::get().registerJITDylib( |
615 | Name: std::move(t&: Name), Header: HeaderAddr.toPtr<void *>()); |
616 | }) |
617 | .release(); |
618 | } |
619 | |
620 | ORC_RT_INTERFACE orc_rt_CWrapperFunctionResult |
621 | __orc_rt_coff_deregister_jitdylib(char *ArgData, size_t ArgSize) { |
622 | return WrapperFunction<SPSError(SPSExecutorAddr)>::handle( |
623 | ArgData, ArgSize, |
624 | Handler: [](ExecutorAddr ) { |
625 | return COFFPlatformRuntimeState::get().deregisterJITDylib( |
626 | Header: HeaderAddr.toPtr<void *>()); |
627 | }) |
628 | .release(); |
629 | } |
630 | |
631 | ORC_RT_INTERFACE orc_rt_CWrapperFunctionResult |
632 | __orc_rt_coff_register_object_sections(char *ArgData, size_t ArgSize) { |
633 | return WrapperFunction<SPSError(SPSExecutorAddr, SPSCOFFObjectSectionsMap, |
634 | bool)>:: |
635 | handle(ArgData, ArgSize, |
636 | Handler: [](ExecutorAddr , |
637 | std::vector<std::pair<std::string_view, ExecutorAddrRange>> |
638 | &Secs, |
639 | bool RunInitializers) { |
640 | return COFFPlatformRuntimeState::get().registerObjectSections( |
641 | HeaderAddr, Secs: std::move(t&: Secs), RunInitializers); |
642 | }) |
643 | .release(); |
644 | } |
645 | |
646 | ORC_RT_INTERFACE orc_rt_CWrapperFunctionResult |
647 | __orc_rt_coff_deregister_object_sections(char *ArgData, size_t ArgSize) { |
648 | return WrapperFunction<SPSError(SPSExecutorAddr, SPSCOFFObjectSectionsMap)>:: |
649 | handle(ArgData, ArgSize, |
650 | Handler: [](ExecutorAddr , |
651 | std::vector<std::pair<std::string_view, ExecutorAddrRange>> |
652 | &Secs) { |
653 | return COFFPlatformRuntimeState::get().deregisterObjectSections( |
654 | HeaderAddr, Secs: std::move(t&: Secs)); |
655 | }) |
656 | .release(); |
657 | } |
658 | //------------------------------------------------------------------------------ |
659 | // JIT'd dlfcn alternatives. |
660 | //------------------------------------------------------------------------------ |
661 | |
662 | const char *__orc_rt_coff_jit_dlerror() { |
663 | return COFFPlatformRuntimeState::get().dlerror(); |
664 | } |
665 | |
666 | void *__orc_rt_coff_jit_dlopen(const char *path, int mode) { |
667 | return COFFPlatformRuntimeState::get().dlopen(Path: path, Mode: mode); |
668 | } |
669 | |
670 | int __orc_rt_coff_jit_dlclose(void *) { |
671 | return COFFPlatformRuntimeState::get().dlclose(DSOHandle: header); |
672 | } |
673 | |
674 | void *__orc_rt_coff_jit_dlsym(void *, const char *symbol) { |
675 | return COFFPlatformRuntimeState::get().dlsym(Header: header, Symbol: symbol); |
676 | } |
677 | |
678 | //------------------------------------------------------------------------------ |
679 | // COFF SEH exception support |
680 | //------------------------------------------------------------------------------ |
681 | |
682 | struct ThrowInfo { |
683 | uint32_t attributes; |
684 | void *data; |
685 | }; |
686 | |
687 | ORC_RT_INTERFACE void __stdcall __orc_rt_coff_cxx_throw_exception( |
688 | void *pExceptionObject, ThrowInfo *pThrowInfo) { |
689 | #ifdef __clang__ |
690 | #pragma clang diagnostic push |
691 | #pragma clang diagnostic ignored "-Wmultichar" |
692 | #endif |
693 | constexpr uint32_t EH_EXCEPTION_NUMBER = 'msc' | 0xE0000000; |
694 | #ifdef __clang__ |
695 | #pragma clang diagnostic pop |
696 | #endif |
697 | constexpr uint32_t EH_MAGIC_NUMBER1 = 0x19930520; |
698 | auto BaseAddr = COFFPlatformRuntimeState::get().findJITDylibBaseByPC( |
699 | PC: reinterpret_cast<uint64_t>(pThrowInfo)); |
700 | if (!BaseAddr) { |
701 | // This is not from JIT'd region. |
702 | // FIXME: Use the default implementation like below when alias api is |
703 | // capable. _CxxThrowException(pExceptionObject, pThrowInfo); |
704 | fprintf(stderr, format: "Throwing exception from compiled callback into JIT'd " |
705 | "exception handler not supported yet.\n" ); |
706 | abort(); |
707 | return; |
708 | } |
709 | const ULONG_PTR parameters[] = { |
710 | EH_MAGIC_NUMBER1, |
711 | reinterpret_cast<ULONG_PTR>(pExceptionObject), |
712 | reinterpret_cast<ULONG_PTR>(pThrowInfo), |
713 | reinterpret_cast<ULONG_PTR>(BaseAddr), |
714 | }; |
715 | RaiseException(EH_EXCEPTION_NUMBER, EXCEPTION_NONCONTINUABLE, |
716 | _countof(parameters), parameters); |
717 | } |
718 | |
719 | //------------------------------------------------------------------------------ |
720 | // COFF atexits |
721 | //------------------------------------------------------------------------------ |
722 | |
723 | typedef int (*OnExitFunction)(void); |
724 | typedef void (*AtExitFunction)(void); |
725 | |
726 | ORC_RT_INTERFACE OnExitFunction __orc_rt_coff_onexit(void *, |
727 | OnExitFunction Func) { |
728 | if (auto Err = COFFPlatformRuntimeState::get().registerAtExit( |
729 | HeaderAddr: ExecutorAddr::fromPtr(Ptr: Header), AtExit: (void (*)(void))Func)) { |
730 | consumeError(Err: std::move(t&: Err)); |
731 | return nullptr; |
732 | } |
733 | return Func; |
734 | } |
735 | |
736 | ORC_RT_INTERFACE int __orc_rt_coff_atexit(void *, AtExitFunction Func) { |
737 | if (auto Err = COFFPlatformRuntimeState::get().registerAtExit( |
738 | HeaderAddr: ExecutorAddr::fromPtr(Ptr: Header), AtExit: (void (*)(void))Func)) { |
739 | consumeError(Err: std::move(t&: Err)); |
740 | return -1; |
741 | } |
742 | return 0; |
743 | } |
744 | |
745 | //------------------------------------------------------------------------------ |
746 | // COFF Run Program |
747 | //------------------------------------------------------------------------------ |
748 | |
749 | ORC_RT_INTERFACE int64_t __orc_rt_coff_run_program(const char *JITDylibName, |
750 | const char *EntrySymbolName, |
751 | int argc, char *argv[]) { |
752 | using MainTy = int (*)(int, char *[]); |
753 | |
754 | void *H = |
755 | __orc_rt_coff_jit_dlopen(path: JITDylibName, mode: __orc_rt::coff::ORC_RT_RTLD_LAZY); |
756 | if (!H) { |
757 | __orc_rt_log_error(ErrMsg: __orc_rt_coff_jit_dlerror()); |
758 | return -1; |
759 | } |
760 | |
761 | auto *Main = |
762 | reinterpret_cast<MainTy>(__orc_rt_coff_jit_dlsym(header: H, symbol: EntrySymbolName)); |
763 | |
764 | if (!Main) { |
765 | __orc_rt_log_error(ErrMsg: __orc_rt_coff_jit_dlerror()); |
766 | return -1; |
767 | } |
768 | |
769 | int Result = Main(argc, argv); |
770 | |
771 | if (__orc_rt_coff_jit_dlclose(header: H) == -1) |
772 | __orc_rt_log_error(ErrMsg: __orc_rt_coff_jit_dlerror()); |
773 | |
774 | return Result; |
775 | } |
776 | |