//===-------- 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 { EHFrameBinaryParser::EHFrameBinaryParser(JITTargetAddress EHFrameAddress, StringRef EHFrameContent, unsigned PointerSize, support::endianness Endianness) : EHFrameAddress(EHFrameAddress), EHFrameContent(EHFrameContent), PointerSize(PointerSize), EHFrameReader(EHFrameContent, Endianness) {} Error EHFrameBinaryParser::addToGraph() { 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 RecordLength = 0; uint32_t RecordLengthField; if (auto Err = EHFrameReader.readInteger(RecordLengthField)) return Err; // Process CIE/FDE length/extended-length fields to build the blocks. // // 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 RecordLength 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 (RecordLengthField == 0) // Length 0 means end of __eh_frame section. break; // If the regular length field's value is 0xffffffff, use extended length. if (RecordLengthField == 0xffffffff) { uint64_t ExtendedLengthField; if (auto Err = EHFrameReader.readInteger(ExtendedLengthField)) return Err; if (ExtendedLengthField > EHFrameReader.bytesRemaining()) return make_error("CIE record extends past the end of " "the __eh_frame section"); if (ExtendedLengthField + 12 > std::numeric_limits::max()) return make_error("CIE record too large to process"); RecordLength = ExtendedLengthField + 12; } else { if (RecordLengthField > EHFrameReader.bytesRemaining()) return make_error("CIE record extends past the end of " "the __eh_frame section"); RecordLength = RecordLengthField + 4; } LLVM_DEBUG(dbgs() << " length: " << RecordLength << "\n"); // 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(RecordOffset, RecordLength)) return Err; } else { if (auto Err = processFDE(RecordOffset, RecordLength, CIEPointerAddress, CIEPointer)) return Err; } EHFrameReader.setOffset(RecordOffset + RecordLength); } return Error::success(); } void EHFrameBinaryParser::anchor() {} Expected EHFrameBinaryParser::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("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("Unrecognized character " + Twine(NextChar) + " in augmentation string"); } if (auto Err = EHFrameReader.readInteger(NextChar)) return std::move(Err); } return std::move(AugInfo); } Expected EHFrameBinaryParser::readAbsolutePointer() { static_assert(sizeof(JITTargetAddress) == sizeof(uint64_t), "Result must be able to hold a uint64_t"); JITTargetAddress Addr; if (PointerSize == 8) { if (auto Err = EHFrameReader.readInteger(Addr)) return std::move(Err); } else if (PointerSize == 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 EHFrameBinaryParser::processCIE(size_t RecordOffset, size_t RecordLength) { // Use the dwarf namespace for convenient access to pointer encoding // constants. using namespace dwarf; LLVM_DEBUG(dbgs() << " Record is CIE\n"); auto &CIESymbol = createCIERecord(EHFrameAddress + RecordOffset, EHFrameContent.substr(RecordOffset, RecordLength)); CIEInformation CIEInfo(CIESymbol); uint8_t Version = 0; if (auto Err = EHFrameReader.readInteger(Version)) return Err; if (Version != 0x01) return make_error("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(PointerSize)) 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("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("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( "Unsupported LSDA pointer encoding " + formatv("{0:x2}", LSDAPointerEncoding) + " in CIE at " + formatv("{0:x16}", CIESymbol.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( "Unspported personality pointer " "encoding " + formatv("{0:x2}", PersonalityPointerEncoding) + " in CIE at " + formatv("{0:x16}", CIESymbol.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( "Unsupported FDE address pointer " "encoding " + formatv("{0:x2}", FDEPointerEncoding) + " in CIE at " + formatv("{0:x16}", CIESymbol.getAddress())); break; } default: llvm_unreachable("Invalid augmentation string field"); } } if (EHFrameReader.getOffset() - AugmentationDataStartOffset > AugmentationDataLength) return make_error("Read past the end of the augmentation " "data while parsing fields"); assert(!CIEInfos.count(CIESymbol.getAddress()) && "Multiple CIEs recorded at the same address?"); CIEInfos[CIESymbol.getAddress()] = std::move(CIEInfo); return Error::success(); } Error EHFrameBinaryParser::processFDE(size_t RecordOffset, size_t RecordLength, 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( "FDE at " + formatv("{0:x16}", EHFrameAddress + RecordOffset) + " points to non-existant CIE at " + formatv("{0:x16}", CIEPointerAddress - CIEPointer)); auto &CIEInfo = CIEInfoItr->second; // 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 *TargetSymbol = getSymbolAtAddress(PCBegin); if (!TargetSymbol) return make_error("FDE PC-begin " + formatv("{0:x16}", PCBegin) + " does not point at symbol"); if (TargetSymbol->getAddress() != PCBegin) return make_error( "FDE PC-begin " + formatv("{0:x16}", PCBegin) + " does not point to start of symbol at " + formatv("{0:x16}", TargetSymbol->getAddress())); LLVM_DEBUG(dbgs() << " FDE target: " << *TargetSymbol << "\n"); // Skip over the PC range size field. if (auto Err = EHFrameReader.skip(PointerSize)) return Err; Symbol *LSDASymbol = nullptr; JITTargetAddress LSDAAddress = 0; if (CIEInfo.FDEsHaveLSDAField) { uint64_t AugmentationDataSize; if (auto Err = EHFrameReader.readULEB128(AugmentationDataSize)) return Err; if (AugmentationDataSize != PointerSize) return make_error( "Unexpected FDE augmentation data size (expected " + Twine(PointerSize) + ", got " + Twine(AugmentationDataSize) + ") for FDE at " + formatv("{0:x16}", EHFrameAddress + RecordOffset)); LSDAAddress = EHFrameAddress + EHFrameReader.getOffset(); auto LSDADelta = readAbsolutePointer(); if (!LSDADelta) return LSDADelta.takeError(); JITTargetAddress LSDA = LSDAAddress + *LSDADelta; LSDASymbol = getSymbolAtAddress(LSDA); if (!LSDASymbol) return make_error("FDE LSDA " + formatv("{0:x16}", LSDA) + " does not point at symbol"); if (LSDASymbol->getAddress() != LSDA) return make_error( "FDE LSDA " + formatv("{0:x16}", LSDA) + " does not point to start of symbol at " + formatv("{0:x16}", LSDASymbol->getAddress())); LLVM_DEBUG(dbgs() << " FDE LSDA: " << *LSDASymbol << "\n"); } JITTargetAddress RecordAddress = EHFrameAddress + RecordOffset; auto FDESymbol = createFDERecord( RecordAddress, EHFrameContent.substr(RecordOffset, RecordLength), *CIEInfo.CIESymbol, CIEPointerAddress - RecordAddress, *TargetSymbol, PCBeginAddress - RecordAddress, LSDASymbol, LSDAAddress - RecordAddress); return FDESymbol.takeError(); } // 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("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("could not deregister eh-frame: " "__deregister_frame function not found"); } #endif #ifdef __APPLE__ template Error walkAppleEHFrameSection(const char *const SectionStart, size_t SectionSize, HandleFDEFn HandleFDE) { const char *CurCFIRecord = SectionStart; const char *End = SectionStart + SectionSize; uint64_t Size = *reinterpret_cast(CurCFIRecord); while (CurCFIRecord != End && Size != 0) { const char *OffsetField = CurCFIRecord + (Size == 0xffffffff ? 12 : 4); if (Size == 0xffffffff) Size = *reinterpret_cast(CurCFIRecord + 4) + 12; else Size += 4; uint32_t Offset = *reinterpret_cast(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(CurCFIRecord); } return Error::success(); } #endif // __APPLE__ Error registerEHFrameSection(const void *EHFrameSectionAddr, size_t EHFrameSectionSize) { #ifdef __APPLE__ // On Darwin __register_frame has to be called for each FDE entry. return walkAppleEHFrameSection(static_cast(EHFrameSectionAddr), EHFrameSectionSize, 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, size_t EHFrameSectionSize) { #ifdef __APPLE__ return walkAppleEHFrameSection(static_cast(EHFrameSectionAddr), EHFrameSectionSize, deregisterFrameWrapper); #else return deregisterFrameWrapper(EHFrameSectionAddr); #endif } EHFrameRegistrar::~EHFrameRegistrar() {} InProcessEHFrameRegistrar &InProcessEHFrameRegistrar::getInstance() { static InProcessEHFrameRegistrar Instance; return Instance; } InProcessEHFrameRegistrar::InProcessEHFrameRegistrar() {} LinkGraphPassFunction createEHFrameRecorderPass(const Triple &TT, StoreFrameRangeFunction StoreRangeAddress) { const char *EHFrameSectionName = nullptr; if (TT.getObjectFormat() == Triple::MachO) EHFrameSectionName = "__eh_frame"; else EHFrameSectionName = ".eh_frame"; auto RecordEHFrame = [EHFrameSectionName, StoreFrameRange = std::move(StoreRangeAddress)](LinkGraph &G) -> Error { // Search for a non-empty eh-frame and record the address of the first // symbol in it. JITTargetAddress Addr = 0; size_t Size = 0; if (auto *S = G.findSectionByName(EHFrameSectionName)) { auto R = SectionRange(*S); Addr = R.getStart(); Size = R.getSize(); } if (Addr == 0 && Size != 0) return make_error("__eh_frame section can not have zero " "address with non-zero size"); StoreFrameRange(Addr, Size); return Error::success(); }; return RecordEHFrame; } } // end namespace jitlink } // end namespace llvm