| 1 | //===--- DwarfCFIEHPrinter.h - DWARF-based Unwind Information Printer -----===// |
| 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 | #ifndef LLVM_TOOLS_LLVM_READOBJ_DWARFCFIEHPRINTER_H |
| 10 | #define LLVM_TOOLS_LLVM_READOBJ_DWARFCFIEHPRINTER_H |
| 11 | |
| 12 | #include "llvm-readobj.h" |
| 13 | #include "llvm/ADT/STLExtras.h" |
| 14 | #include "llvm/BinaryFormat/Dwarf.h" |
| 15 | #include "llvm/DebugInfo/DWARF/DWARFCFIPrinter.h" |
| 16 | #include "llvm/DebugInfo/DWARF/DWARFContext.h" |
| 17 | #include "llvm/DebugInfo/DWARF/DWARFDataExtractor.h" |
| 18 | #include "llvm/DebugInfo/DWARF/DWARFDebugFrame.h" |
| 19 | #include "llvm/Object/ELF.h" |
| 20 | #include "llvm/Object/ELFObjectFile.h" |
| 21 | #include "llvm/Object/ELFTypes.h" |
| 22 | #include "llvm/Support/Casting.h" |
| 23 | #include "llvm/Support/Debug.h" |
| 24 | #include "llvm/Support/Endian.h" |
| 25 | #include "llvm/Support/Format.h" |
| 26 | #include "llvm/Support/ScopedPrinter.h" |
| 27 | #include "llvm/Support/type_traits.h" |
| 28 | |
| 29 | namespace llvm { |
| 30 | namespace DwarfCFIEH { |
| 31 | |
| 32 | template <typename ELFT> class PrinterContext { |
| 33 | using Elf_Shdr = typename ELFT::Shdr; |
| 34 | using Elf_Phdr = typename ELFT::Phdr; |
| 35 | |
| 36 | ScopedPrinter &W; |
| 37 | const object::ELFObjectFile<ELFT> &ObjF; |
| 38 | |
| 39 | void printEHFrameHdr(const Elf_Phdr *EHFramePHdr) const; |
| 40 | void printEHFrame(const Elf_Shdr *EHFrameShdr) const; |
| 41 | |
| 42 | public: |
| 43 | PrinterContext(ScopedPrinter &W, const object::ELFObjectFile<ELFT> &ObjF) |
| 44 | : W(W), ObjF(ObjF) {} |
| 45 | |
| 46 | void printUnwindInformation() const; |
| 47 | }; |
| 48 | |
| 49 | template <class ELFT> |
| 50 | static const typename ELFT::Shdr * |
| 51 | findSectionByAddress(const object::ELFObjectFile<ELFT> &ObjF, uint64_t Addr) { |
| 52 | Expected<typename ELFT::ShdrRange> SectionsOrErr = |
| 53 | ObjF.getELFFile().sections(); |
| 54 | if (!SectionsOrErr) |
| 55 | reportError(SectionsOrErr.takeError(), ObjF.getFileName()); |
| 56 | |
| 57 | for (const typename ELFT::Shdr &Shdr : *SectionsOrErr) |
| 58 | if (Shdr.sh_addr == Addr) |
| 59 | return &Shdr; |
| 60 | return nullptr; |
| 61 | } |
| 62 | |
| 63 | template <typename ELFT> |
| 64 | void PrinterContext<ELFT>::printUnwindInformation() const { |
| 65 | const object::ELFFile<ELFT> &Obj = ObjF.getELFFile(); |
| 66 | |
| 67 | Expected<typename ELFT::PhdrRange> PhdrsOrErr = Obj.program_headers(); |
| 68 | if (!PhdrsOrErr) |
| 69 | reportError(PhdrsOrErr.takeError(), ObjF.getFileName()); |
| 70 | |
| 71 | for (const Elf_Phdr &Phdr : *PhdrsOrErr) { |
| 72 | if (Phdr.p_type != ELF::PT_GNU_EH_FRAME) |
| 73 | continue; |
| 74 | |
| 75 | if (Phdr.p_memsz != Phdr.p_filesz) |
| 76 | reportError(object::createError( |
| 77 | Err: "p_memsz does not match p_filesz for GNU_EH_FRAME" ), |
| 78 | ObjF.getFileName()); |
| 79 | printEHFrameHdr(EHFramePHdr: &Phdr); |
| 80 | break; |
| 81 | } |
| 82 | |
| 83 | Expected<typename ELFT::ShdrRange> SectionsOrErr = Obj.sections(); |
| 84 | if (!SectionsOrErr) |
| 85 | reportError(SectionsOrErr.takeError(), ObjF.getFileName()); |
| 86 | |
| 87 | for (const Elf_Shdr &Shdr : *SectionsOrErr) { |
| 88 | Expected<StringRef> NameOrErr = Obj.getSectionName(Shdr); |
| 89 | if (!NameOrErr) |
| 90 | reportError(NameOrErr.takeError(), ObjF.getFileName()); |
| 91 | if (*NameOrErr == ".eh_frame" ) |
| 92 | printEHFrame(EHFrameShdr: &Shdr); |
| 93 | } |
| 94 | } |
| 95 | |
| 96 | template <typename ELFT> |
| 97 | void PrinterContext<ELFT>::printEHFrameHdr(const Elf_Phdr *EHFramePHdr) const { |
| 98 | DictScope L(W, "EHFrameHeader" ); |
| 99 | uint64_t EHFrameHdrAddress = EHFramePHdr->p_vaddr; |
| 100 | W.startLine() << format(Fmt: "Address: 0x%" PRIx64 "\n" , Vals: EHFrameHdrAddress); |
| 101 | W.startLine() << format(Fmt: "Offset: 0x%" PRIx64 "\n" , Vals: (uint64_t)EHFramePHdr->p_offset); |
| 102 | W.startLine() << format(Fmt: "Size: 0x%" PRIx64 "\n" , Vals: (uint64_t)EHFramePHdr->p_memsz); |
| 103 | |
| 104 | const object::ELFFile<ELFT> &Obj = ObjF.getELFFile(); |
| 105 | if (const Elf_Shdr *EHFrameHdr = |
| 106 | findSectionByAddress(ObjF, EHFramePHdr->p_vaddr)) { |
| 107 | Expected<StringRef> NameOrErr = Obj.getSectionName(*EHFrameHdr); |
| 108 | if (!NameOrErr) |
| 109 | reportError(NameOrErr.takeError(), ObjF.getFileName()); |
| 110 | W.printString(Label: "Corresponding Section" , Value: *NameOrErr); |
| 111 | } |
| 112 | |
| 113 | Expected<ArrayRef<uint8_t>> Content = Obj.getSegmentContents(*EHFramePHdr); |
| 114 | if (!Content) |
| 115 | reportError(Content.takeError(), ObjF.getFileName()); |
| 116 | |
| 117 | DataExtractor DE(*Content, ELFT::Endianness == llvm::endianness::little, |
| 118 | ELFT::Is64Bits ? 8 : 4); |
| 119 | |
| 120 | DictScope D(W, "Header" ); |
| 121 | uint64_t Offset = 0; |
| 122 | |
| 123 | auto Version = DE.getU8(offset_ptr: &Offset); |
| 124 | W.printNumber(Label: "version" , Value: Version); |
| 125 | if (Version != 1) |
| 126 | reportError( |
| 127 | object::createError(Err: "only version 1 of .eh_frame_hdr is supported" ), |
| 128 | ObjF.getFileName()); |
| 129 | |
| 130 | uint64_t EHFramePtrEnc = DE.getU8(offset_ptr: &Offset); |
| 131 | W.startLine() << format(Fmt: "eh_frame_ptr_enc: 0x%" PRIx64 "\n" , Vals: EHFramePtrEnc); |
| 132 | if (EHFramePtrEnc != (dwarf::DW_EH_PE_pcrel | dwarf::DW_EH_PE_sdata4)) |
| 133 | reportError(object::createError(Err: "unexpected encoding eh_frame_ptr_enc" ), |
| 134 | ObjF.getFileName()); |
| 135 | |
| 136 | uint64_t FDECountEnc = DE.getU8(offset_ptr: &Offset); |
| 137 | W.startLine() << format(Fmt: "fde_count_enc: 0x%" PRIx64 "\n" , Vals: FDECountEnc); |
| 138 | if (FDECountEnc != dwarf::DW_EH_PE_udata4) |
| 139 | reportError(object::createError(Err: "unexpected encoding fde_count_enc" ), |
| 140 | ObjF.getFileName()); |
| 141 | |
| 142 | uint64_t TableEnc = DE.getU8(offset_ptr: &Offset); |
| 143 | W.startLine() << format(Fmt: "table_enc: 0x%" PRIx64 "\n" , Vals: TableEnc); |
| 144 | if (TableEnc != (dwarf::DW_EH_PE_datarel | dwarf::DW_EH_PE_sdata4)) |
| 145 | reportError(object::createError(Err: "unexpected encoding table_enc" ), |
| 146 | ObjF.getFileName()); |
| 147 | |
| 148 | auto EHFramePtr = DE.getSigned(offset_ptr: &Offset, size: 4) + EHFrameHdrAddress + 4; |
| 149 | W.startLine() << format(Fmt: "eh_frame_ptr: 0x%" PRIx64 "\n" , Vals: EHFramePtr); |
| 150 | |
| 151 | auto FDECount = DE.getUnsigned(offset_ptr: &Offset, byte_size: 4); |
| 152 | W.printNumber(Label: "fde_count" , Value: FDECount); |
| 153 | |
| 154 | unsigned NumEntries = 0; |
| 155 | uint64_t PrevPC = 0; |
| 156 | while (Offset + 8 <= EHFramePHdr->p_memsz && NumEntries < FDECount) { |
| 157 | DictScope D(W, std::string("entry " ) + std::to_string(val: NumEntries)); |
| 158 | |
| 159 | auto InitialPC = DE.getSigned(offset_ptr: &Offset, size: 4) + EHFrameHdrAddress; |
| 160 | W.startLine() << format(Fmt: "initial_location: 0x%" PRIx64 "\n" , Vals: InitialPC); |
| 161 | auto Address = DE.getSigned(offset_ptr: &Offset, size: 4) + EHFrameHdrAddress; |
| 162 | W.startLine() << format(Fmt: "address: 0x%" PRIx64 "\n" , Vals: Address); |
| 163 | |
| 164 | if (InitialPC < PrevPC) |
| 165 | reportError(object::createError(Err: "initial_location is out of order" ), |
| 166 | ObjF.getFileName()); |
| 167 | |
| 168 | PrevPC = InitialPC; |
| 169 | ++NumEntries; |
| 170 | } |
| 171 | } |
| 172 | |
| 173 | template <typename ELFT> |
| 174 | void PrinterContext<ELFT>::printEHFrame(const Elf_Shdr *EHFrameShdr) const { |
| 175 | uint64_t Address = EHFrameShdr->sh_addr; |
| 176 | uint64_t ShOffset = EHFrameShdr->sh_offset; |
| 177 | W.startLine() << format(Fmt: ".eh_frame section at offset 0x%" PRIx64 |
| 178 | " address 0x%" PRIx64 ":\n" , |
| 179 | Vals: ShOffset, Vals: Address); |
| 180 | W.indent(); |
| 181 | |
| 182 | Expected<ArrayRef<uint8_t>> DataOrErr = |
| 183 | ObjF.getELFFile().getSectionContents(*EHFrameShdr); |
| 184 | if (!DataOrErr) |
| 185 | reportError(DataOrErr.takeError(), ObjF.getFileName()); |
| 186 | |
| 187 | // Construct DWARFDataExtractor to handle relocations ("PC Begin" fields). |
| 188 | std::unique_ptr<DWARFContext> DICtx = DWARFContext::create( |
| 189 | ObjF, DWARFContext::ProcessDebugRelocations::Process, nullptr); |
| 190 | DWARFDataExtractor DE( |
| 191 | DICtx->getDWARFObj(), DICtx->getDWARFObj().getEHFrameSection(), |
| 192 | ELFT::Endianness == llvm::endianness::little, ELFT::Is64Bits ? 8 : 4); |
| 193 | DWARFDebugFrame EHFrame(Triple::ArchType(ObjF.getArch()), /*IsEH=*/true, |
| 194 | /*EHFrameAddress=*/Address); |
| 195 | if (Error E = EHFrame.parse(Data: DE)) |
| 196 | reportError(std::move(E), ObjF.getFileName()); |
| 197 | |
| 198 | for (const dwarf::FrameEntry &Entry : EHFrame) { |
| 199 | std::optional<uint64_t> InitialLocation; |
| 200 | if (const dwarf::CIE *CIE = dyn_cast<dwarf::CIE>(Val: &Entry)) { |
| 201 | W.startLine() << format(Fmt: "[0x%" PRIx64 "] CIE length=%" PRIu64 "\n" , |
| 202 | Vals: Address + CIE->getOffset(), Vals: CIE->getLength()); |
| 203 | W.indent(); |
| 204 | |
| 205 | W.printNumber(Label: "version" , Value: CIE->getVersion()); |
| 206 | W.printString(Label: "augmentation" , Value: CIE->getAugmentationString()); |
| 207 | W.printNumber(Label: "code_alignment_factor" , Value: CIE->getCodeAlignmentFactor()); |
| 208 | W.printNumber(Label: "data_alignment_factor" , Value: CIE->getDataAlignmentFactor()); |
| 209 | W.printNumber(Label: "return_address_register" , Value: CIE->getReturnAddressRegister()); |
| 210 | } else { |
| 211 | const dwarf::FDE *FDE = cast<dwarf::FDE>(Val: &Entry); |
| 212 | W.startLine() << format(Fmt: "[0x%" PRIx64 "] FDE length=%" PRIu64 |
| 213 | " cie=[0x%" PRIx64 "]\n" , |
| 214 | Vals: Address + FDE->getOffset(), Vals: FDE->getLength(), |
| 215 | Vals: Address + FDE->getLinkedCIE()->getOffset()); |
| 216 | W.indent(); |
| 217 | |
| 218 | InitialLocation = FDE->getInitialLocation(); |
| 219 | W.startLine() << format(Fmt: "initial_location: 0x%" PRIx64 "\n" , |
| 220 | Vals: *InitialLocation); |
| 221 | W.startLine() << format( |
| 222 | Fmt: "address_range: 0x%" PRIx64 " (end : 0x%" PRIx64 ")\n" , |
| 223 | Vals: FDE->getAddressRange(), |
| 224 | Vals: FDE->getInitialLocation() + FDE->getAddressRange()); |
| 225 | } |
| 226 | |
| 227 | W.getOStream() << "\n" ; |
| 228 | W.startLine() << "Program:\n" ; |
| 229 | W.indent(); |
| 230 | auto DumpOpts = DIDumpOptions(); |
| 231 | DumpOpts.IsEH = true; |
| 232 | printCFIProgram(P: Entry.cfis(), OS&: W.getOStream(), DumpOpts, IndentLevel: W.getIndentLevel(), |
| 233 | Address: InitialLocation); |
| 234 | W.unindent(); |
| 235 | W.unindent(); |
| 236 | W.getOStream() << "\n" ; |
| 237 | } |
| 238 | |
| 239 | W.unindent(); |
| 240 | } |
| 241 | } // namespace DwarfCFIEH |
| 242 | } // namespace llvm |
| 243 | |
| 244 | #endif |
| 245 | |