Mercurial > hg > CbC > CbC_llvm
view lib/ExecutionEngine/JITLink/EHFrameSupport.cpp @ 148:63bd29f05246
merged
author | Shinji KONO <kono@ie.u-ryukyu.ac.jp> |
---|---|
date | Wed, 14 Aug 2019 19:46:37 +0900 |
parents | c2174574ed3a |
children |
line wrap: on
line source
//===-------- JITLink_EHFrameSupport.cpp - JITLink eh-frame utils ---------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "EHFrameSupportImpl.h" #include "llvm/BinaryFormat/Dwarf.h" #include "llvm/Support/DynamicLibrary.h" #define DEBUG_TYPE "jitlink" namespace llvm { namespace jitlink { EHFrameParser::EHFrameParser(AtomGraph &G, Section &EHFrameSection, StringRef EHFrameContent, JITTargetAddress EHFrameAddress, Edge::Kind FDEToCIERelocKind, Edge::Kind FDEToTargetRelocKind) : G(G), EHFrameSection(EHFrameSection), EHFrameContent(EHFrameContent), EHFrameAddress(EHFrameAddress), EHFrameReader(EHFrameContent, G.getEndianness()), FDEToCIERelocKind(FDEToCIERelocKind), FDEToTargetRelocKind(FDEToTargetRelocKind) {} Error EHFrameParser::atomize() { while (!EHFrameReader.empty()) { size_t RecordOffset = EHFrameReader.getOffset(); LLVM_DEBUG({ dbgs() << "Processing eh-frame record at " << format("0x%016" PRIx64, EHFrameAddress + RecordOffset) << " (offset " << RecordOffset << ")\n"; }); size_t CIELength = 0; uint32_t CIELengthField; if (auto Err = EHFrameReader.readInteger(CIELengthField)) return Err; // Process CIE length/extended-length fields to build the atom. // // The value of these fields describe the length of the *rest* of the CIE // (not including data up to the end of the field itself) so we have to // bump CIELength to include the data up to the end of the field: 4 bytes // for Length, or 12 bytes (4 bytes + 8 bytes) for ExtendedLength. if (CIELengthField == 0) // Length 0 means end of __eh_frame section. break; // If the regular length field's value is 0xffffffff, use extended length. if (CIELengthField == 0xffffffff) { uint64_t CIEExtendedLengthField; if (auto Err = EHFrameReader.readInteger(CIEExtendedLengthField)) return Err; if (CIEExtendedLengthField > EHFrameReader.bytesRemaining()) return make_error<JITLinkError>("CIE record extends past the end of " "the __eh_frame section"); if (CIEExtendedLengthField + 12 > std::numeric_limits<size_t>::max()) return make_error<JITLinkError>("CIE record too large to process"); CIELength = CIEExtendedLengthField + 12; } else { if (CIELengthField > EHFrameReader.bytesRemaining()) return make_error<JITLinkError>("CIE record extends past the end of " "the __eh_frame section"); CIELength = CIELengthField + 4; } LLVM_DEBUG(dbgs() << " length: " << CIELength << "\n"); // Add an atom for this record. CurRecordAtom = &G.addAnonymousAtom( EHFrameSection, EHFrameAddress + RecordOffset, G.getPointerSize()); CurRecordAtom->setContent(EHFrameContent.substr(RecordOffset, CIELength)); // Read the CIE Pointer. size_t CIEPointerAddress = EHFrameAddress + EHFrameReader.getOffset(); uint32_t CIEPointer; if (auto Err = EHFrameReader.readInteger(CIEPointer)) return Err; // Based on the CIE pointer value, parse this as a CIE or FDE record. if (CIEPointer == 0) { if (auto Err = processCIE()) return Err; } else { if (auto Err = processFDE(CIEPointerAddress, CIEPointer)) return Err; } EHFrameReader.setOffset(RecordOffset + CIELength); } return Error::success(); } Expected<EHFrameParser::AugmentationInfo> EHFrameParser::parseAugmentationString() { AugmentationInfo AugInfo; uint8_t NextChar; uint8_t *NextField = &AugInfo.Fields[0]; if (auto Err = EHFrameReader.readInteger(NextChar)) return std::move(Err); while (NextChar != 0) { switch (NextChar) { case 'z': AugInfo.AugmentationDataPresent = true; break; case 'e': if (auto Err = EHFrameReader.readInteger(NextChar)) return std::move(Err); if (NextChar != 'h') return make_error<JITLinkError>("Unrecognized substring e" + Twine(NextChar) + " in augmentation string"); AugInfo.EHDataFieldPresent = true; break; case 'L': case 'P': case 'R': *NextField++ = NextChar; break; default: return make_error<JITLinkError>("Unrecognized character " + Twine(NextChar) + " in augmentation string"); } if (auto Err = EHFrameReader.readInteger(NextChar)) return std::move(Err); } return std::move(AugInfo); } Expected<JITTargetAddress> EHFrameParser::readAbsolutePointer() { static_assert(sizeof(JITTargetAddress) == sizeof(uint64_t), "Result must be able to hold a uint64_t"); JITTargetAddress Addr; if (G.getPointerSize() == 8) { if (auto Err = EHFrameReader.readInteger(Addr)) return std::move(Err); } else if (G.getPointerSize() == 4) { uint32_t Addr32; if (auto Err = EHFrameReader.readInteger(Addr32)) return std::move(Err); Addr = Addr32; } else llvm_unreachable("Pointer size is not 32-bit or 64-bit"); return Addr; } Error EHFrameParser::processCIE() { // Use the dwarf namespace for convenient access to pointer encoding // constants. using namespace dwarf; LLVM_DEBUG(dbgs() << " Record is CIE\n"); CIEInformation CIEInfo(*CurRecordAtom); uint8_t Version = 0; if (auto Err = EHFrameReader.readInteger(Version)) return Err; if (Version != 0x01) return make_error<JITLinkError>("Bad CIE version " + Twine(Version) + " (should be 0x01) in eh-frame"); auto AugInfo = parseAugmentationString(); if (!AugInfo) return AugInfo.takeError(); // Skip the EH Data field if present. if (AugInfo->EHDataFieldPresent) if (auto Err = EHFrameReader.skip(G.getPointerSize())) return Err; // Read and sanity check the code alignment factor. { uint64_t CodeAlignmentFactor = 0; if (auto Err = EHFrameReader.readULEB128(CodeAlignmentFactor)) return Err; if (CodeAlignmentFactor != 1) return make_error<JITLinkError>("Unsupported CIE code alignment factor " + Twine(CodeAlignmentFactor) + " (expected 1)"); } // Read and sanity check the data alignment factor. { int64_t DataAlignmentFactor = 0; if (auto Err = EHFrameReader.readSLEB128(DataAlignmentFactor)) return Err; if (DataAlignmentFactor != -8) return make_error<JITLinkError>("Unsupported CIE data alignment factor " + Twine(DataAlignmentFactor) + " (expected -8)"); } // Skip the return address register field. if (auto Err = EHFrameReader.skip(1)) return Err; uint64_t AugmentationDataLength = 0; if (auto Err = EHFrameReader.readULEB128(AugmentationDataLength)) return Err; uint32_t AugmentationDataStartOffset = EHFrameReader.getOffset(); uint8_t *NextField = &AugInfo->Fields[0]; while (uint8_t Field = *NextField++) { switch (Field) { case 'L': { CIEInfo.FDEsHaveLSDAField = true; uint8_t LSDAPointerEncoding; if (auto Err = EHFrameReader.readInteger(LSDAPointerEncoding)) return Err; if (LSDAPointerEncoding != (DW_EH_PE_pcrel | DW_EH_PE_absptr)) return make_error<JITLinkError>( "Unsupported LSDA pointer encoding " + formatv("{0:x2}", LSDAPointerEncoding) + " in CIE at " + formatv("{0:x16}", CurRecordAtom->getAddress())); break; } case 'P': { uint8_t PersonalityPointerEncoding = 0; if (auto Err = EHFrameReader.readInteger(PersonalityPointerEncoding)) return Err; if (PersonalityPointerEncoding != (DW_EH_PE_indirect | DW_EH_PE_pcrel | DW_EH_PE_sdata4)) return make_error<JITLinkError>( "Unspported personality pointer " "encoding " + formatv("{0:x2}", PersonalityPointerEncoding) + " in CIE at " + formatv("{0:x16}", CurRecordAtom->getAddress())); uint32_t PersonalityPointerAddress; if (auto Err = EHFrameReader.readInteger(PersonalityPointerAddress)) return Err; break; } case 'R': { uint8_t FDEPointerEncoding; if (auto Err = EHFrameReader.readInteger(FDEPointerEncoding)) return Err; if (FDEPointerEncoding != (DW_EH_PE_pcrel | DW_EH_PE_absptr)) return make_error<JITLinkError>( "Unsupported FDE address pointer " "encoding " + formatv("{0:x2}", FDEPointerEncoding) + " in CIE at " + formatv("{0:x16}", CurRecordAtom->getAddress())); break; } default: llvm_unreachable("Invalid augmentation string field"); } } if (EHFrameReader.getOffset() - AugmentationDataStartOffset > AugmentationDataLength) return make_error<JITLinkError>("Read past the end of the augmentation " "data while parsing fields"); assert(!CIEInfos.count(CurRecordAtom->getAddress()) && "Multiple CIEs recorded at the same address?"); CIEInfos[CurRecordAtom->getAddress()] = std::move(CIEInfo); return Error::success(); } Error EHFrameParser::processFDE(JITTargetAddress CIEPointerAddress, uint32_t CIEPointer) { LLVM_DEBUG(dbgs() << " Record is FDE\n"); LLVM_DEBUG({ dbgs() << " CIE pointer: " << format("0x%016" PRIx64, CIEPointerAddress - CIEPointer) << "\n"; }); auto CIEInfoItr = CIEInfos.find(CIEPointerAddress - CIEPointer); if (CIEInfoItr == CIEInfos.end()) return make_error<JITLinkError>( "FDE at " + formatv("{0:x16}", CurRecordAtom->getAddress()) + " points to non-existant CIE at " + formatv("{0:x16}", CIEPointerAddress - CIEPointer)); auto &CIEInfo = CIEInfoItr->second; // The CIEPointer looks good. Add a relocation. CurRecordAtom->addEdge(FDEToCIERelocKind, CIEPointerAddress - CurRecordAtom->getAddress(), *CIEInfo.CIEAtom, 0); // Read and sanity check the PC-start pointer and size. JITTargetAddress PCBeginAddress = EHFrameAddress + EHFrameReader.getOffset(); auto PCBeginDelta = readAbsolutePointer(); if (!PCBeginDelta) return PCBeginDelta.takeError(); JITTargetAddress PCBegin = PCBeginAddress + *PCBeginDelta; LLVM_DEBUG({ dbgs() << " PC begin: " << format("0x%016" PRIx64, PCBegin) << "\n"; }); auto *TargetAtom = G.getAtomByAddress(PCBegin); if (!TargetAtom) return make_error<JITLinkError>("FDE PC-begin " + formatv("{0:x16}", PCBegin) + " does not point at atom"); if (TargetAtom->getAddress() != PCBegin) return make_error<JITLinkError>( "FDE PC-begin " + formatv("{0:x16}", PCBegin) + " does not point to start of atom at " + formatv("{0:x16}", TargetAtom->getAddress())); LLVM_DEBUG(dbgs() << " FDE target: " << *TargetAtom << "\n"); // The PC-start pointer and size look good. Add relocations. CurRecordAtom->addEdge(FDEToTargetRelocKind, PCBeginAddress - CurRecordAtom->getAddress(), *TargetAtom, 0); // Add a keep-alive relocation from the function to the FDE to ensure it is // not dead stripped. TargetAtom->addEdge(Edge::KeepAlive, 0, *CurRecordAtom, 0); // Skip over the PC range size field. if (auto Err = EHFrameReader.skip(G.getPointerSize())) return Err; if (CIEInfo.FDEsHaveLSDAField) { uint64_t AugmentationDataSize; if (auto Err = EHFrameReader.readULEB128(AugmentationDataSize)) return Err; if (AugmentationDataSize != G.getPointerSize()) return make_error<JITLinkError>( "Unexpected FDE augmentation data size (expected " + Twine(G.getPointerSize()) + ", got " + Twine(AugmentationDataSize) + ") for FDE at " + formatv("{0:x16}", CurRecordAtom->getAddress())); JITTargetAddress LSDAAddress = EHFrameAddress + EHFrameReader.getOffset(); auto LSDADelta = readAbsolutePointer(); if (!LSDADelta) return LSDADelta.takeError(); JITTargetAddress LSDA = LSDAAddress + *LSDADelta; auto *LSDAAtom = G.getAtomByAddress(LSDA); if (!LSDAAtom) return make_error<JITLinkError>("FDE LSDA " + formatv("{0:x16}", LSDA) + " does not point at atom"); if (LSDAAtom->getAddress() != LSDA) return make_error<JITLinkError>( "FDE LSDA " + formatv("{0:x16}", LSDA) + " does not point to start of atom at " + formatv("{0:x16}", LSDAAtom->getAddress())); LLVM_DEBUG(dbgs() << " FDE LSDA: " << *LSDAAtom << "\n"); // LSDA looks good. Add relocations. CurRecordAtom->addEdge(FDEToTargetRelocKind, LSDAAddress - CurRecordAtom->getAddress(), *LSDAAtom, 0); } return Error::success(); } Error addEHFrame(AtomGraph &G, Section &EHFrameSection, StringRef EHFrameContent, JITTargetAddress EHFrameAddress, Edge::Kind FDEToCIERelocKind, Edge::Kind FDEToTargetRelocKind) { return EHFrameParser(G, EHFrameSection, EHFrameContent, EHFrameAddress, FDEToCIERelocKind, FDEToTargetRelocKind) .atomize(); } // Determine whether we can register EH tables. #if (defined(__GNUC__) && !defined(__ARM_EABI__) && !defined(__ia64__) && \ !(defined(_AIX) && defined(__ibmxl__)) && !defined(__SEH__) && \ !defined(__USING_SJLJ_EXCEPTIONS__)) #define HAVE_EHTABLE_SUPPORT 1 #else #define HAVE_EHTABLE_SUPPORT 0 #endif #if HAVE_EHTABLE_SUPPORT 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<JITLinkError>("could not register eh-frame: " "__register_frame function not found"); } 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<JITLinkError>("could not deregister eh-frame: " "__deregister_frame function not found"); } #endif #ifdef __APPLE__ template <typename HandleFDEFn> Error walkAppleEHFrameSection(const char *const SectionStart, HandleFDEFn HandleFDE) { const char *CurCFIRecord = SectionStart; uint64_t Size = *reinterpret_cast<const uint32_t *>(CurCFIRecord); while (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); if (Offset != 0) if (auto Err = HandleFDE(CurCFIRecord)) return Err; 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"; }); CurCFIRecord += Size; Size = *reinterpret_cast<const uint32_t *>(CurCFIRecord); } return Error::success(); } #endif // __APPLE__ Error registerEHFrameSection(const void *EHFrameSectionAddr) { #ifdef __APPLE__ // On Darwin __register_frame has to be called for each FDE entry. return walkAppleEHFrameSection(static_cast<const char *>(EHFrameSectionAddr), registerFrameWrapper); #else // On Linux __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) { #ifdef __APPLE__ return walkAppleEHFrameSection(static_cast<const char *>(EHFrameSectionAddr), deregisterFrameWrapper); #else return deregisterFrameWrapper(EHFrameSectionAddr); #endif } EHFrameRegistrar::~EHFrameRegistrar() {} InProcessEHFrameRegistrar &InProcessEHFrameRegistrar::getInstance() { static InProcessEHFrameRegistrar Instance; return Instance; } InProcessEHFrameRegistrar::InProcessEHFrameRegistrar() {} AtomGraphPassFunction createEHFrameRecorderPass(const Triple &TT, StoreFrameAddressFunction StoreFrameAddress) { const char *EHFrameSectionName = nullptr; if (TT.getObjectFormat() == Triple::MachO) EHFrameSectionName = "__eh_frame"; else EHFrameSectionName = ".eh_frame"; auto RecordEHFrame = [EHFrameSectionName, StoreFrameAddress](AtomGraph &G) -> Error { // Search for a non-empty eh-frame and record the address of the first atom // in it. JITTargetAddress Addr = 0; if (auto *S = G.findSectionByName(EHFrameSectionName)) Addr = S->getRange().getStart(); StoreFrameAddress(Addr); return Error::success(); }; return RecordEHFrame; } } // end namespace jitlink } // end namespace llvm