| 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 "jit_dispatch.h" |
| 21 | #include "wrapper_function_utils.h" |
| 22 | |
| 23 | #include <array> |
| 24 | #include <list> |
| 25 | #include <map> |
| 26 | #include <mutex> |
| 27 | #include <sstream> |
| 28 | #include <string_view> |
| 29 | #include <vector> |
| 30 | |
| 31 | #define DEBUG_TYPE "coff_platform" |
| 32 | |
| 33 | using namespace orc_rt; |
| 34 | |
| 35 | namespace orc_rt { |
| 36 | |
| 37 | using COFFJITDylibDepInfo = std::vector<ExecutorAddr>; |
| 38 | using COFFJITDylibDepInfoMap = |
| 39 | std::unordered_map<ExecutorAddr, COFFJITDylibDepInfo>; |
| 40 | |
| 41 | using SPSCOFFObjectSectionsMap = |
| 42 | SPSSequence<SPSTuple<SPSString, SPSExecutorAddrRange>>; |
| 43 | |
| 44 | using SPSCOFFJITDylibDepInfo = SPSSequence<SPSExecutorAddr>; |
| 45 | |
| 46 | using SPSCOFFJITDylibDepInfoMap = |
| 47 | SPSSequence<SPSTuple<SPSExecutorAddr, SPSCOFFJITDylibDepInfo>>; |
| 48 | |
| 49 | } // namespace orc_rt |
| 50 | |
| 51 | ORC_RT_JIT_DISPATCH_TAG(__orc_rt_coff_symbol_lookup_tag) |
| 52 | ORC_RT_JIT_DISPATCH_TAG(__orc_rt_coff_push_initializers_tag) |
| 53 | |
| 54 | namespace { |
| 55 | class COFFPlatformRuntimeState { |
| 56 | private: |
| 57 | // Ctor/dtor section. |
| 58 | // Manage lists of *tor functions sorted by the last character of subsection |
| 59 | // name. |
| 60 | struct XtorSection { |
| 61 | void Register(char SubsectionChar, span<void (*)(void)> Xtors) { |
| 62 | Subsections[SubsectionChar - 'A'].push_back(x: Xtors); |
| 63 | SubsectionsNew[SubsectionChar - 'A'].push_back(x: Xtors); |
| 64 | } |
| 65 | |
| 66 | void RegisterNoRun(char SubsectionChar, span<void (*)(void)> Xtors) { |
| 67 | Subsections[SubsectionChar - 'A'].push_back(x: Xtors); |
| 68 | } |
| 69 | |
| 70 | void Reset() { SubsectionsNew = Subsections; } |
| 71 | |
| 72 | void RunAllNewAndFlush(); |
| 73 | |
| 74 | private: |
| 75 | std::array<std::vector<span<void (*)(void)>>, 26> Subsections; |
| 76 | std::array<std::vector<span<void (*)(void)>>, 26> SubsectionsNew; |
| 77 | }; |
| 78 | |
| 79 | struct JITDylibState { |
| 80 | std::string Name; |
| 81 | void * = nullptr; |
| 82 | size_t LinkedAgainstRefCount = 0; |
| 83 | size_t DlRefCount = 0; |
| 84 | std::vector<JITDylibState *> Deps; |
| 85 | std::vector<void (*)(void)> AtExits; |
| 86 | XtorSection CInitSection; // XIA~XIZ |
| 87 | XtorSection CXXInitSection; // XCA~XCZ |
| 88 | XtorSection CPreTermSection; // XPA~XPZ |
| 89 | XtorSection CTermSection; // XTA~XTZ |
| 90 | |
| 91 | bool referenced() const { |
| 92 | return LinkedAgainstRefCount != 0 || DlRefCount != 0; |
| 93 | } |
| 94 | }; |
| 95 | |
| 96 | public: |
| 97 | static void initialize(); |
| 98 | static COFFPlatformRuntimeState &get(); |
| 99 | static bool isInitialized() { return CPS; } |
| 100 | static void destroy(); |
| 101 | |
| 102 | COFFPlatformRuntimeState() = default; |
| 103 | |
| 104 | // Delete copy and move constructors. |
| 105 | COFFPlatformRuntimeState(const COFFPlatformRuntimeState &) = delete; |
| 106 | COFFPlatformRuntimeState & |
| 107 | operator=(const COFFPlatformRuntimeState &) = delete; |
| 108 | COFFPlatformRuntimeState(COFFPlatformRuntimeState &&) = delete; |
| 109 | COFFPlatformRuntimeState &operator=(COFFPlatformRuntimeState &&) = delete; |
| 110 | |
| 111 | const char *dlerror(); |
| 112 | void *dlopen(std::string_view Name, int Mode); |
| 113 | int dlclose(void *); |
| 114 | void *dlsym(void *, std::string_view Symbol); |
| 115 | |
| 116 | Error registerJITDylib(std::string Name, void *); |
| 117 | Error deregisterJITDylib(void *); |
| 118 | |
| 119 | Error registerAtExit(ExecutorAddr , void (*AtExit)(void)); |
| 120 | |
| 121 | Error registerObjectSections( |
| 122 | ExecutorAddr , |
| 123 | std::vector<std::pair<std::string_view, ExecutorAddrRange>> Secs, |
| 124 | bool RunInitializers); |
| 125 | Error deregisterObjectSections( |
| 126 | ExecutorAddr , |
| 127 | std::vector<std::pair<std::string_view, ExecutorAddrRange>> Secs); |
| 128 | |
| 129 | void *findJITDylibBaseByPC(uint64_t PC); |
| 130 | |
| 131 | private: |
| 132 | Error registerBlockRange(ExecutorAddr , ExecutorAddrRange Range); |
| 133 | Error deregisterBlockRange(ExecutorAddr , ExecutorAddrRange Range); |
| 134 | |
| 135 | Error registerSEHFrames(ExecutorAddr , |
| 136 | ExecutorAddrRange SEHFrameRange); |
| 137 | Error deregisterSEHFrames(ExecutorAddr , |
| 138 | ExecutorAddrRange SEHFrameRange); |
| 139 | |
| 140 | Expected<void *> dlopenImpl(std::string_view Path, int Mode); |
| 141 | Error dlopenFull(JITDylibState &JDS); |
| 142 | Error dlopenInitialize(JITDylibState &JDS, COFFJITDylibDepInfoMap &DepInfo); |
| 143 | |
| 144 | Error dlcloseImpl(void *DSOHandle); |
| 145 | Error dlcloseDeinitialize(JITDylibState &JDS); |
| 146 | |
| 147 | JITDylibState *getJITDylibStateByHeader(void *DSOHandle); |
| 148 | JITDylibState *getJITDylibStateByName(std::string_view Path); |
| 149 | Expected<ExecutorAddr> lookupSymbolInJITDylib(void *DSOHandle, |
| 150 | std::string_view Symbol); |
| 151 | |
| 152 | static COFFPlatformRuntimeState *CPS; |
| 153 | |
| 154 | std::recursive_mutex JDStatesMutex; |
| 155 | std::map<void *, JITDylibState> JDStates; |
| 156 | struct BlockRange { |
| 157 | void *; |
| 158 | size_t Size; |
| 159 | }; |
| 160 | std::map<void *, BlockRange> BlockRanges; |
| 161 | std::unordered_map<std::string_view, void *> ; |
| 162 | std::string DLFcnError; |
| 163 | }; |
| 164 | |
| 165 | } // namespace |
| 166 | |
| 167 | COFFPlatformRuntimeState *COFFPlatformRuntimeState::CPS = nullptr; |
| 168 | |
| 169 | COFFPlatformRuntimeState::JITDylibState * |
| 170 | COFFPlatformRuntimeState::(void *) { |
| 171 | auto I = JDStates.find(x: Header); |
| 172 | if (I == JDStates.end()) |
| 173 | return nullptr; |
| 174 | return &I->second; |
| 175 | } |
| 176 | |
| 177 | COFFPlatformRuntimeState::JITDylibState * |
| 178 | COFFPlatformRuntimeState::getJITDylibStateByName(std::string_view Name) { |
| 179 | // FIXME: Avoid creating string copy here. |
| 180 | auto I = JDNameToHeader.find(x: std::string(Name.data(), Name.size())); |
| 181 | if (I == JDNameToHeader.end()) |
| 182 | return nullptr; |
| 183 | void *H = I->second; |
| 184 | auto J = JDStates.find(x: H); |
| 185 | assert(J != JDStates.end() && |
| 186 | "JITDylib has name map entry but no header map entry" ); |
| 187 | return &J->second; |
| 188 | } |
| 189 | |
| 190 | Error COFFPlatformRuntimeState::registerJITDylib(std::string Name, |
| 191 | void *) { |
| 192 | ORC_RT_DEBUG({ |
| 193 | printdbg("Registering JITDylib %s: Header = %p\n" , Name.c_str(), Header); |
| 194 | }); |
| 195 | std::lock_guard<std::recursive_mutex> Lock(JDStatesMutex); |
| 196 | if (JDStates.count(x: Header)) { |
| 197 | std::ostringstream ErrStream; |
| 198 | ErrStream << "Duplicate JITDylib registration for header " << Header |
| 199 | << " (name = " << Name << ")" ; |
| 200 | return make_error<StringError>(Args: ErrStream.str()); |
| 201 | } |
| 202 | if (JDNameToHeader.count(x: Name)) { |
| 203 | std::ostringstream ErrStream; |
| 204 | ErrStream << "Duplicate JITDylib registration for header " << Header |
| 205 | << " (header = " << Header << ")" ; |
| 206 | return make_error<StringError>(Args: ErrStream.str()); |
| 207 | } |
| 208 | |
| 209 | auto &JDS = JDStates[Header]; |
| 210 | JDS.Name = std::move(t&: Name); |
| 211 | JDS.Header = Header; |
| 212 | JDNameToHeader[JDS.Name] = Header; |
| 213 | return Error::success(); |
| 214 | } |
| 215 | |
| 216 | Error COFFPlatformRuntimeState::deregisterJITDylib(void *) { |
| 217 | std::lock_guard<std::recursive_mutex> Lock(JDStatesMutex); |
| 218 | auto I = JDStates.find(x: Header); |
| 219 | if (I == JDStates.end()) { |
| 220 | std::ostringstream ErrStream; |
| 221 | ErrStream << "Attempted to deregister unrecognized header " << Header; |
| 222 | return make_error<StringError>(Args: ErrStream.str()); |
| 223 | } |
| 224 | |
| 225 | // Remove std::string construction once we can use C++20. |
| 226 | auto J = JDNameToHeader.find( |
| 227 | x: std::string(I->second.Name.data(), I->second.Name.size())); |
| 228 | assert(J != JDNameToHeader.end() && |
| 229 | "Missing JDNameToHeader entry for JITDylib" ); |
| 230 | |
| 231 | ORC_RT_DEBUG({ |
| 232 | printdbg("Deregistering JITDylib %s: Header = %p\n" , I->second.Name.c_str(), |
| 233 | Header); |
| 234 | }); |
| 235 | |
| 236 | JDNameToHeader.erase(position: J); |
| 237 | JDStates.erase(position: I); |
| 238 | return Error::success(); |
| 239 | } |
| 240 | |
| 241 | void COFFPlatformRuntimeState::XtorSection::RunAllNewAndFlush() { |
| 242 | for (auto &Subsection : SubsectionsNew) { |
| 243 | for (auto &XtorGroup : Subsection) |
| 244 | for (auto &Xtor : XtorGroup) |
| 245 | if (Xtor) |
| 246 | Xtor(); |
| 247 | Subsection.clear(); |
| 248 | } |
| 249 | } |
| 250 | |
| 251 | const char *COFFPlatformRuntimeState::dlerror() { return DLFcnError.c_str(); } |
| 252 | |
| 253 | void *COFFPlatformRuntimeState::dlopen(std::string_view Path, int Mode) { |
| 254 | ORC_RT_DEBUG({ |
| 255 | std::string S(Path.data(), Path.size()); |
| 256 | printdbg("COFFPlatform::dlopen(\"%s\")\n" , S.c_str()); |
| 257 | }); |
| 258 | std::lock_guard<std::recursive_mutex> Lock(JDStatesMutex); |
| 259 | if (auto H = dlopenImpl(Path, Mode)) |
| 260 | return *H; |
| 261 | else { |
| 262 | // FIXME: Make dlerror thread safe. |
| 263 | DLFcnError = toString(Err: H.takeError()); |
| 264 | return nullptr; |
| 265 | } |
| 266 | } |
| 267 | |
| 268 | int COFFPlatformRuntimeState::dlclose(void *DSOHandle) { |
| 269 | ORC_RT_DEBUG({ |
| 270 | auto *JDS = getJITDylibStateByHeader(DSOHandle); |
| 271 | std::string DylibName; |
| 272 | if (JDS) { |
| 273 | std::string S; |
| 274 | printdbg("COFFPlatform::dlclose(%p) (%s)\n" , DSOHandle, S.c_str()); |
| 275 | } else |
| 276 | printdbg("COFFPlatform::dlclose(%p) (%s)\n" , DSOHandle, "invalid handle" ); |
| 277 | }); |
| 278 | std::lock_guard<std::recursive_mutex> Lock(JDStatesMutex); |
| 279 | if (auto Err = dlcloseImpl(DSOHandle)) { |
| 280 | // FIXME: Make dlerror thread safe. |
| 281 | DLFcnError = toString(Err: std::move(t&: Err)); |
| 282 | return -1; |
| 283 | } |
| 284 | return 0; |
| 285 | } |
| 286 | |
| 287 | void *COFFPlatformRuntimeState::dlsym(void *, std::string_view Symbol) { |
| 288 | auto Addr = lookupSymbolInJITDylib(DSOHandle: Header, Symbol); |
| 289 | if (!Addr) { |
| 290 | return 0; |
| 291 | } |
| 292 | |
| 293 | return Addr->toPtr<void *>(); |
| 294 | } |
| 295 | |
| 296 | Expected<void *> COFFPlatformRuntimeState::dlopenImpl(std::string_view Path, |
| 297 | int Mode) { |
| 298 | // Try to find JITDylib state by name. |
| 299 | auto *JDS = getJITDylibStateByName(Name: Path); |
| 300 | |
| 301 | if (!JDS) |
| 302 | return make_error<StringError>(Args: "No registered JTIDylib for path " + |
| 303 | std::string(Path.data(), Path.size())); |
| 304 | |
| 305 | if (auto Err = dlopenFull(JDS&: *JDS)) |
| 306 | return std::move(t&: Err); |
| 307 | |
| 308 | // Bump the ref-count on this dylib. |
| 309 | ++JDS->DlRefCount; |
| 310 | |
| 311 | // Return the header address. |
| 312 | return JDS->Header; |
| 313 | } |
| 314 | |
| 315 | Error COFFPlatformRuntimeState::dlopenFull(JITDylibState &JDS) { |
| 316 | // Call back to the JIT to push the initializers. |
| 317 | Expected<COFFJITDylibDepInfoMap> DepInfoMap((COFFJITDylibDepInfoMap())); |
| 318 | if (auto Err = WrapperFunction<SPSExpected<SPSCOFFJITDylibDepInfoMap>( |
| 319 | SPSExecutorAddr)>:: |
| 320 | call(Dispatch: JITDispatch(&__orc_rt_coff_push_initializers_tag), Result&: DepInfoMap, |
| 321 | Args: ExecutorAddr::fromPtr(Ptr: JDS.Header))) |
| 322 | return Err; |
| 323 | if (!DepInfoMap) |
| 324 | return DepInfoMap.takeError(); |
| 325 | |
| 326 | if (auto Err = dlopenInitialize(JDS, DepInfo&: *DepInfoMap)) |
| 327 | return Err; |
| 328 | |
| 329 | if (!DepInfoMap->empty()) { |
| 330 | ORC_RT_DEBUG({ |
| 331 | printdbg("Unrecognized dep-info key headers in dlopen of %s\n" , |
| 332 | JDS.Name.c_str()); |
| 333 | }); |
| 334 | std::ostringstream ErrStream; |
| 335 | ErrStream << "Encountered unrecognized dep-info key headers " |
| 336 | "while processing dlopen of " |
| 337 | << JDS.Name; |
| 338 | return make_error<StringError>(Args: ErrStream.str()); |
| 339 | } |
| 340 | |
| 341 | return Error::success(); |
| 342 | } |
| 343 | |
| 344 | Error COFFPlatformRuntimeState::dlopenInitialize( |
| 345 | JITDylibState &JDS, COFFJITDylibDepInfoMap &DepInfo) { |
| 346 | ORC_RT_DEBUG({ |
| 347 | printdbg("COFFPlatformRuntimeState::dlopenInitialize(\"%s\")\n" , |
| 348 | JDS.Name.c_str()); |
| 349 | }); |
| 350 | |
| 351 | // Skip visited dependency. |
| 352 | auto I = DepInfo.find(x: ExecutorAddr::fromPtr(Ptr: JDS.Header)); |
| 353 | if (I == DepInfo.end()) |
| 354 | return Error::success(); |
| 355 | |
| 356 | auto DI = std::move(t&: I->second); |
| 357 | DepInfo.erase(position: I); |
| 358 | |
| 359 | // Run initializers of dependencies in proper order by depth-first traversal |
| 360 | // of dependency graph. |
| 361 | std::vector<JITDylibState *> OldDeps; |
| 362 | std::swap(x&: JDS.Deps, y&: OldDeps); |
| 363 | JDS.Deps.reserve(n: DI.size()); |
| 364 | for (auto : DI) { |
| 365 | auto *DepJDS = getJITDylibStateByHeader(Header: DepHeaderAddr.toPtr<void *>()); |
| 366 | if (!DepJDS) { |
| 367 | std::ostringstream ErrStream; |
| 368 | ErrStream << "Encountered unrecognized dep header " |
| 369 | << DepHeaderAddr.toPtr<void *>() << " while initializing " |
| 370 | << JDS.Name; |
| 371 | return make_error<StringError>(Args: ErrStream.str()); |
| 372 | } |
| 373 | ++DepJDS->LinkedAgainstRefCount; |
| 374 | if (auto Err = dlopenInitialize(JDS&: *DepJDS, DepInfo)) |
| 375 | return Err; |
| 376 | } |
| 377 | |
| 378 | // Run static initializers. |
| 379 | JDS.CInitSection.RunAllNewAndFlush(); |
| 380 | JDS.CXXInitSection.RunAllNewAndFlush(); |
| 381 | |
| 382 | // Decrement old deps. |
| 383 | for (auto *DepJDS : OldDeps) { |
| 384 | --DepJDS->LinkedAgainstRefCount; |
| 385 | if (!DepJDS->referenced()) |
| 386 | if (auto Err = dlcloseDeinitialize(JDS&: *DepJDS)) |
| 387 | return Err; |
| 388 | } |
| 389 | |
| 390 | return Error::success(); |
| 391 | } |
| 392 | |
| 393 | Error COFFPlatformRuntimeState::dlcloseImpl(void *DSOHandle) { |
| 394 | // Try to find JITDylib state by header. |
| 395 | auto *JDS = getJITDylibStateByHeader(Header: DSOHandle); |
| 396 | |
| 397 | if (!JDS) { |
| 398 | std::ostringstream ErrStream; |
| 399 | ErrStream << "No registered JITDylib for " << DSOHandle; |
| 400 | return make_error<StringError>(Args: ErrStream.str()); |
| 401 | } |
| 402 | |
| 403 | // Bump the ref-count. |
| 404 | --JDS->DlRefCount; |
| 405 | |
| 406 | if (!JDS->referenced()) |
| 407 | return dlcloseDeinitialize(JDS&: *JDS); |
| 408 | |
| 409 | return Error::success(); |
| 410 | } |
| 411 | |
| 412 | Error COFFPlatformRuntimeState::dlcloseDeinitialize(JITDylibState &JDS) { |
| 413 | ORC_RT_DEBUG({ |
| 414 | printdbg("COFFPlatformRuntimeState::dlcloseDeinitialize(\"%s\")\n" , |
| 415 | JDS.Name.c_str()); |
| 416 | }); |
| 417 | |
| 418 | // Run atexits |
| 419 | for (auto AtExit : JDS.AtExits) |
| 420 | AtExit(); |
| 421 | JDS.AtExits.clear(); |
| 422 | |
| 423 | // Run static terminators. |
| 424 | JDS.CPreTermSection.RunAllNewAndFlush(); |
| 425 | JDS.CTermSection.RunAllNewAndFlush(); |
| 426 | |
| 427 | // Queue all xtors as new again. |
| 428 | JDS.CInitSection.Reset(); |
| 429 | JDS.CXXInitSection.Reset(); |
| 430 | JDS.CPreTermSection.Reset(); |
| 431 | JDS.CTermSection.Reset(); |
| 432 | |
| 433 | // Deinitialize any dependencies. |
| 434 | for (auto *DepJDS : JDS.Deps) { |
| 435 | --DepJDS->LinkedAgainstRefCount; |
| 436 | if (!DepJDS->referenced()) |
| 437 | if (auto Err = dlcloseDeinitialize(JDS&: *DepJDS)) |
| 438 | return Err; |
| 439 | } |
| 440 | |
| 441 | return Error::success(); |
| 442 | } |
| 443 | |
| 444 | Expected<ExecutorAddr> |
| 445 | COFFPlatformRuntimeState::lookupSymbolInJITDylib(void *, |
| 446 | std::string_view Sym) { |
| 447 | Expected<ExecutorAddr> Result((ExecutorAddr())); |
| 448 | if (auto Err = WrapperFunction<SPSExpected<SPSExecutorAddr>( |
| 449 | SPSExecutorAddr, |
| 450 | SPSString)>::call(Dispatch: JITDispatch(&__orc_rt_coff_symbol_lookup_tag), |
| 451 | Result, Args: ExecutorAddr::fromPtr(Ptr: header), 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_WrapperFunctionResult |
| 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_WrapperFunctionResult |
| 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_WrapperFunctionResult |
| 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_WrapperFunctionResult |
| 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_WrapperFunctionResult |
| 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_WrapperFunctionResult |
| 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 | |