| //===--- DwarfCFIEHPrinter.h - DWARF-based Unwind Information Printer -----===// | |
| // | |
| // The LLVM Compiler Infrastructure | |
| // | |
| // This file is distributed under the University of Illinois Open Source | |
| // License. See LICENSE.TXT for details. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #ifndef LLVM_TOOLS_LLVM_READOBJ_DWARFCFIEHPRINTER_H | |
| #define LLVM_TOOLS_LLVM_READOBJ_DWARFCFIEHPRINTER_H | |
| #include "Error.h" | |
| #include "llvm-readobj.h" | |
| #include "llvm/ADT/STLExtras.h" | |
| #include "llvm/BinaryFormat/Dwarf.h" | |
| #include "llvm/Object/ELF.h" | |
| #include "llvm/Object/ELFTypes.h" | |
| #include "llvm/Support/Casting.h" | |
| #include "llvm/Support/ScopedPrinter.h" | |
| #include "llvm/Support/Debug.h" | |
| #include "llvm/DebugInfo/DWARF/DWARFDataExtractor.h" | |
| #include "llvm/DebugInfo/DWARF/DWARFDebugFrame.h" | |
| #include "llvm/Support/Endian.h" | |
| #include "llvm/Support/Format.h" | |
| #include "llvm/Support/type_traits.h" | |
| namespace llvm { | |
| namespace DwarfCFIEH { | |
| template <typename ELFT> | |
| class PrinterContext { | |
| ScopedPrinter &W; | |
| const object::ELFFile<ELFT> *Obj; | |
| void printEHFrameHdr(uint64_t Offset, uint64_t Address, uint64_t Size) const; | |
| void printEHFrame(const typename ELFT::Shdr *EHFrameShdr) const; | |
| public: | |
| PrinterContext(ScopedPrinter &W, const object::ELFFile<ELFT> *Obj) | |
| : W(W), Obj(Obj) {} | |
| void printUnwindInformation() const; | |
| }; | |
| template <class ELFO> | |
| static const typename ELFO::Elf_Shdr *findSectionByAddress(const ELFO *Obj, | |
| uint64_t Addr) { | |
| auto Sections = Obj->sections(); | |
| if (Error E = Sections.takeError()) | |
| reportError(toString(std::move(E))); | |
| for (const auto &Shdr : *Sections) | |
| if (Shdr.sh_addr == Addr) | |
| return &Shdr; | |
| return nullptr; | |
| } | |
| template <typename ELFT> | |
| void PrinterContext<ELFT>::printUnwindInformation() const { | |
| const typename ELFT::Phdr *EHFramePhdr = nullptr; | |
| auto PHs = Obj->program_headers(); | |
| if (Error E = PHs.takeError()) | |
| reportError(toString(std::move(E))); | |
| for (const auto &Phdr : *PHs) { | |
| if (Phdr.p_type == ELF::PT_GNU_EH_FRAME) { | |
| EHFramePhdr = &Phdr; | |
| if (Phdr.p_memsz != Phdr.p_filesz) | |
| reportError("p_memsz does not match p_filesz for GNU_EH_FRAME"); | |
| break; | |
| } | |
| } | |
| if (EHFramePhdr) | |
| printEHFrameHdr(EHFramePhdr->p_offset, EHFramePhdr->p_vaddr, | |
| EHFramePhdr->p_memsz); | |
| auto Sections = Obj->sections(); | |
| if (Error E = Sections.takeError()) | |
| reportError(toString(std::move(E))); | |
| for (const auto &Shdr : *Sections) { | |
| auto SectionName = Obj->getSectionName(&Shdr); | |
| if (Error E = SectionName.takeError()) | |
| reportError(toString(std::move(E))); | |
| if (*SectionName == ".eh_frame") | |
| printEHFrame(&Shdr); | |
| } | |
| } | |
| template <typename ELFT> | |
| void PrinterContext<ELFT>::printEHFrameHdr(uint64_t EHFrameHdrOffset, | |
| uint64_t EHFrameHdrAddress, | |
| uint64_t EHFrameHdrSize) const { | |
| ListScope L(W, "EH_FRAME Header"); | |
| W.startLine() << format("Address: 0x%" PRIx64 "\n", EHFrameHdrAddress); | |
| W.startLine() << format("Offset: 0x%" PRIx64 "\n", EHFrameHdrOffset); | |
| W.startLine() << format("Size: 0x%" PRIx64 "\n", EHFrameHdrSize); | |
| const auto *EHFrameHdrShdr = findSectionByAddress(Obj, EHFrameHdrAddress); | |
| if (EHFrameHdrShdr) { | |
| auto SectionName = Obj->getSectionName(EHFrameHdrShdr); | |
| if (Error E = SectionName.takeError()) | |
| reportError(toString(std::move(E))); | |
| W.printString("Corresponding Section", *SectionName); | |
| } | |
| DataExtractor DE( | |
| StringRef(reinterpret_cast<const char *>(Obj->base()) + EHFrameHdrOffset, | |
| EHFrameHdrSize), | |
| ELFT::TargetEndianness == support::endianness::little, | |
| ELFT::Is64Bits ? 8 : 4); | |
| DictScope D(W, "Header"); | |
| uint32_t Offset = 0; | |
| auto Version = DE.getU8(&Offset); | |
| W.printNumber("version", Version); | |
| if (Version != 1) | |
| reportError("only version 1 of .eh_frame_hdr is supported"); | |
| uint64_t EHFramePtrEnc = DE.getU8(&Offset); | |
| W.startLine() << format("eh_frame_ptr_enc: 0x%" PRIx64 "\n", EHFramePtrEnc); | |
| if (EHFramePtrEnc != (dwarf::DW_EH_PE_pcrel | dwarf::DW_EH_PE_sdata4)) | |
| reportError("unexpected encoding eh_frame_ptr_enc"); | |
| uint64_t FDECountEnc = DE.getU8(&Offset); | |
| W.startLine() << format("fde_count_enc: 0x%" PRIx64 "\n", FDECountEnc); | |
| if (FDECountEnc != dwarf::DW_EH_PE_udata4) | |
| reportError("unexpected encoding fde_count_enc"); | |
| uint64_t TableEnc = DE.getU8(&Offset); | |
| W.startLine() << format("table_enc: 0x%" PRIx64 "\n", TableEnc); | |
| if (TableEnc != (dwarf::DW_EH_PE_datarel | dwarf::DW_EH_PE_sdata4)) | |
| reportError("unexpected encoding table_enc"); | |
| auto EHFramePtr = DE.getSigned(&Offset, 4) + EHFrameHdrAddress + 4; | |
| W.startLine() << format("eh_frame_ptr: 0x%" PRIx64 "\n", EHFramePtr); | |
| auto FDECount = DE.getUnsigned(&Offset, 4); | |
| W.printNumber("fde_count", FDECount); | |
| unsigned NumEntries = 0; | |
| uint64_t PrevPC = 0; | |
| while (Offset + 8 <= EHFrameHdrSize && NumEntries < FDECount) { | |
| DictScope D(W, std::string("entry ") + std::to_string(NumEntries)); | |
| auto InitialPC = DE.getSigned(&Offset, 4) + EHFrameHdrAddress; | |
| W.startLine() << format("initial_location: 0x%" PRIx64 "\n", InitialPC); | |
| auto Address = DE.getSigned(&Offset, 4) + EHFrameHdrAddress; | |
| W.startLine() << format("address: 0x%" PRIx64 "\n", Address); | |
| if (InitialPC < PrevPC) | |
| reportError("initial_location is out of order"); | |
| PrevPC = InitialPC; | |
| ++NumEntries; | |
| } | |
| } | |
| template <typename ELFT> | |
| void PrinterContext<ELFT>::printEHFrame( | |
| const typename ELFT::Shdr *EHFrameShdr) const { | |
| uint64_t Address = EHFrameShdr->sh_addr; | |
| uint64_t ShOffset = EHFrameShdr->sh_offset; | |
| W.startLine() << format(".eh_frame section at offset 0x%" PRIx64 | |
| " address 0x%" PRIx64 ":\n", | |
| ShOffset, Address); | |
| W.indent(); | |
| auto Result = Obj->getSectionContents(EHFrameShdr); | |
| if (Error E = Result.takeError()) | |
| reportError(toString(std::move(E))); | |
| auto Contents = Result.get(); | |
| DWARFDataExtractor DE( | |
| StringRef(reinterpret_cast<const char *>(Contents.data()), | |
| Contents.size()), | |
| ELFT::TargetEndianness == support::endianness::little, | |
| ELFT::Is64Bits ? 8 : 4); | |
| DWARFDebugFrame EHFrame(/*IsEH=*/true, /*EHFrameAddress=*/Address); | |
| EHFrame.parse(DE); | |
| for (const auto &Entry : EHFrame) { | |
| if (const auto *CIE = dyn_cast<dwarf::CIE>(&Entry)) { | |
| W.startLine() << format("[0x%" PRIx64 "] CIE length=%" PRIu64 "\n", | |
| Address + CIE->getOffset(), | |
| CIE->getLength()); | |
| W.indent(); | |
| W.printNumber("version", CIE->getVersion()); | |
| W.printString("augmentation", CIE->getAugmentationString()); | |
| W.printNumber("code_alignment_factor", CIE->getCodeAlignmentFactor()); | |
| W.printNumber("data_alignment_factor", CIE->getDataAlignmentFactor()); | |
| W.printNumber("return_address_register", CIE->getReturnAddressRegister()); | |
| W.getOStream() << "\n"; | |
| W.startLine() << "Program:\n"; | |
| W.indent(); | |
| CIE->cfis().dump(W.getOStream(), nullptr, W.getIndentLevel()); | |
| W.unindent(); | |
| W.unindent(); | |
| W.getOStream() << "\n"; | |
| } else if (const auto *FDE = dyn_cast<dwarf::FDE>(&Entry)) { | |
| W.startLine() << format("[0x%" PRIx64 "] FDE length=%" PRIu64 | |
| " cie=[0x%" PRIx64 "]\n", | |
| Address + FDE->getOffset(), | |
| FDE->getLength(), | |
| Address + FDE->getLinkedCIE()->getOffset()); | |
| W.indent(); | |
| W.startLine() << format("initial_location: 0x%" PRIx64 "\n", | |
| FDE->getInitialLocation()); | |
| W.startLine() | |
| << format("address_range: 0x%" PRIx64 " (end : 0x%" PRIx64 ")\n", | |
| FDE->getAddressRange(), | |
| FDE->getInitialLocation() + FDE->getAddressRange()); | |
| W.getOStream() << "\n"; | |
| W.startLine() << "Program:\n"; | |
| W.indent(); | |
| FDE->cfis().dump(W.getOStream(), nullptr, W.getIndentLevel()); | |
| W.unindent(); | |
| W.unindent(); | |
| W.getOStream() << "\n"; | |
| } else { | |
| llvm_unreachable("unexpected DWARF frame kind"); | |
| } | |
| } | |
| W.unindent(); | |
| } | |
| } | |
| } | |
| #endif |