| //===--------- RegisterEHFrames.cpp - Register EH frame sections ----------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "llvm/ExecutionEngine/Orc/TargetProcess/RegisterEHFrames.h" | |
| #include "llvm/Config/config.h" | |
| #include "llvm/ExecutionEngine/JITSymbol.h" | |
| #include "llvm/Support/BinaryStreamReader.h" | |
| #include "llvm/Support/Compiler.h" | |
| #include "llvm/Support/Debug.h" | |
| #include "llvm/Support/DynamicLibrary.h" | |
| #include "llvm/Support/raw_ostream.h" | |
| #include "llvm/Support/FormatVariadic.h" | |
| #define DEBUG_TYPE "orc" | |
| using namespace llvm; | |
| using namespace llvm::orc; | |
| using namespace llvm::orc::shared; | |
| namespace llvm { | |
| namespace orc { | |
| #if defined(HAVE_REGISTER_FRAME) && defined(HAVE_DEREGISTER_FRAME) && \ | |
| !defined(__SEH__) && !defined(__USING_SJLJ_EXCEPTIONS__) | |
| extern "C" void __register_frame(const void *); | |
| extern "C" void __deregister_frame(const void *); | |
| Error registerFrameWrapper(const void *P) { | |
| __register_frame(P); | |
| return Error::success(); | |
| } | |
| Error deregisterFrameWrapper(const void *P) { | |
| __deregister_frame(P); | |
| return Error::success(); | |
| } | |
| #else | |
| // The building compiler does not have __(de)register_frame but | |
| // it may be found at runtime in a dynamically-loaded library. | |
| // For example, this happens when building LLVM with Visual C++ | |
| // but using the MingW runtime. | |
| static Error registerFrameWrapper(const void *P) { | |
| static void((*RegisterFrame)(const void *)) = 0; | |
| if (!RegisterFrame) | |
| *(void **)&RegisterFrame = | |
| llvm::sys::DynamicLibrary::SearchForAddressOfSymbol("__register_frame"); | |
| if (RegisterFrame) { | |
| RegisterFrame(P); | |
| return Error::success(); | |
| } | |
| return make_error<StringError>("could not register eh-frame: " | |
| "__register_frame function not found", | |
| inconvertibleErrorCode()); | |
| } | |
| static Error deregisterFrameWrapper(const void *P) { | |
| static void((*DeregisterFrame)(const void *)) = 0; | |
| if (!DeregisterFrame) | |
| *(void **)&DeregisterFrame = | |
| llvm::sys::DynamicLibrary::SearchForAddressOfSymbol( | |
| "__deregister_frame"); | |
| if (DeregisterFrame) { | |
| DeregisterFrame(P); | |
| return Error::success(); | |
| } | |
| return make_error<StringError>("could not deregister eh-frame: " | |
| "__deregister_frame function not found", | |
| inconvertibleErrorCode()); | |
| } | |
| #endif | |
| #if defined(HAVE_UNW_ADD_DYNAMIC_FDE) || defined(__APPLE__) | |
| template <typename HandleFDEFn> | |
| Error walkLibunwindEHFrameSection(const char *const SectionStart, | |
| size_t SectionSize, HandleFDEFn HandleFDE) { | |
| const char *CurCFIRecord = SectionStart; | |
| const char *End = SectionStart + SectionSize; | |
| uint64_t Size = *reinterpret_cast<const uint32_t *>(CurCFIRecord); | |
| while (CurCFIRecord != End && Size != 0) { | |
| const char *OffsetField = CurCFIRecord + (Size == 0xffffffff ? 12 : 4); | |
| if (Size == 0xffffffff) | |
| Size = *reinterpret_cast<const uint64_t *>(CurCFIRecord + 4) + 12; | |
| else | |
| Size += 4; | |
| uint32_t Offset = *reinterpret_cast<const uint32_t *>(OffsetField); | |
| LLVM_DEBUG({ | |
| dbgs() << "Registering eh-frame section:\n"; | |
| dbgs() << "Processing " << (Offset ? "FDE" : "CIE") << " @" | |
| << (void *)CurCFIRecord << ": ["; | |
| for (unsigned I = 0; I < Size; ++I) | |
| dbgs() << format(" 0x%02" PRIx8, *(CurCFIRecord + I)); | |
| dbgs() << " ]\n"; | |
| }); | |
| if (Offset != 0) | |
| if (auto Err = HandleFDE(CurCFIRecord)) | |
| return Err; | |
| CurCFIRecord += Size; | |
| Size = *reinterpret_cast<const uint32_t *>(CurCFIRecord); | |
| } | |
| return Error::success(); | |
| } | |
| #endif // HAVE_UNW_ADD_DYNAMIC_FDE || __APPLE__ | |
| Error registerEHFrameSection(const void *EHFrameSectionAddr, | |
| size_t EHFrameSectionSize) { | |
| /* libgcc and libunwind __register_frame behave differently. We use the | |
| * presence of __unw_add_dynamic_fde to detect libunwind. */ | |
| #if defined(HAVE_UNW_ADD_DYNAMIC_FDE) || defined(__APPLE__) | |
| // With libunwind, __register_frame has to be called for each FDE entry. | |
| return walkLibunwindEHFrameSection( | |
| static_cast<const char *>(EHFrameSectionAddr), EHFrameSectionSize, | |
| registerFrameWrapper); | |
| #else | |
| // With libgcc, __register_frame takes a single argument: | |
| // a pointer to the start of the .eh_frame section. | |
| // How can it find the end? Because crtendS.o is linked | |
| // in and it has an .eh_frame section with four zero chars. | |
| return registerFrameWrapper(EHFrameSectionAddr); | |
| #endif | |
| } | |
| Error deregisterEHFrameSection(const void *EHFrameSectionAddr, | |
| size_t EHFrameSectionSize) { | |
| #if defined(HAVE_UNW_ADD_DYNAMIC_FDE) || defined(__APPLE__) | |
| return walkLibunwindEHFrameSection( | |
| static_cast<const char *>(EHFrameSectionAddr), EHFrameSectionSize, | |
| deregisterFrameWrapper); | |
| #else | |
| return deregisterFrameWrapper(EHFrameSectionAddr); | |
| #endif | |
| } | |
| } // end namespace orc | |
| } // end namespace llvm | |
| static Error registerEHFrameWrapper(ExecutorAddrRange EHFrame) { | |
| return llvm::orc::registerEHFrameSection(EHFrame.Start.toPtr<const void *>(), | |
| EHFrame.size()); | |
| } | |
| static Error deregisterEHFrameWrapper(ExecutorAddrRange EHFrame) { | |
| return llvm::orc::deregisterEHFrameSection( | |
| EHFrame.Start.toPtr<const void *>(), EHFrame.size()); | |
| } | |
| extern "C" orc::shared::CWrapperFunctionResult | |
| llvm_orc_registerEHFrameSectionWrapper(const char *Data, uint64_t Size) { | |
| return WrapperFunction<SPSError(SPSExecutorAddrRange)>::handle( | |
| Data, Size, registerEHFrameWrapper) | |
| .release(); | |
| } | |
| extern "C" orc::shared::CWrapperFunctionResult | |
| llvm_orc_deregisterEHFrameSectionWrapper(const char *Data, uint64_t Size) { | |
| return WrapperFunction<SPSError(SPSExecutorAddrRange)>::handle( | |
| Data, Size, deregisterEHFrameWrapper) | |
| .release(); | |
| } |