Rafael Auler | ed6b821 | 2018-03-08 00:46:53 +0000 | [diff] [blame] | 1 | //===--- DwarfCFIEHPrinter.h - DWARF-based Unwind Information Printer -----===// |
| 2 | // |
| 3 | // The LLVM Compiler Infrastructure |
| 4 | // |
| 5 | // This file is distributed under the University of Illinois Open Source |
| 6 | // License. See LICENSE.TXT for details. |
| 7 | // |
| 8 | //===----------------------------------------------------------------------===// |
| 9 | |
| 10 | #ifndef LLVM_TOOLS_LLVM_READOBJ_DWARFCFIEHPRINTER_H |
| 11 | #define LLVM_TOOLS_LLVM_READOBJ_DWARFCFIEHPRINTER_H |
| 12 | |
| 13 | #include "Error.h" |
Benjamin Kramer | d2ac73c | 2018-03-12 15:02:59 +0000 | [diff] [blame] | 14 | #include "llvm-readobj.h" |
Rafael Auler | ed6b821 | 2018-03-08 00:46:53 +0000 | [diff] [blame] | 15 | #include "llvm/ADT/STLExtras.h" |
| 16 | #include "llvm/BinaryFormat/Dwarf.h" |
| 17 | #include "llvm/Object/ELF.h" |
| 18 | #include "llvm/Object/ELFTypes.h" |
Luke Cheeseman | a0875a9 | 2018-12-18 10:37:42 +0000 | [diff] [blame] | 19 | #include "llvm/Object/ELFObjectFile.h" |
Rafael Auler | ed6b821 | 2018-03-08 00:46:53 +0000 | [diff] [blame] | 20 | #include "llvm/Support/Casting.h" |
| 21 | #include "llvm/Support/ScopedPrinter.h" |
| 22 | #include "llvm/Support/Debug.h" |
| 23 | #include "llvm/DebugInfo/DWARF/DWARFDataExtractor.h" |
| 24 | #include "llvm/DebugInfo/DWARF/DWARFDebugFrame.h" |
| 25 | #include "llvm/Support/Endian.h" |
| 26 | #include "llvm/Support/Format.h" |
| 27 | #include "llvm/Support/type_traits.h" |
| 28 | |
| 29 | namespace llvm { |
| 30 | namespace DwarfCFIEH { |
| 31 | |
| 32 | template <typename ELFT> |
| 33 | class PrinterContext { |
| 34 | ScopedPrinter &W; |
Luke Cheeseman | a0875a9 | 2018-12-18 10:37:42 +0000 | [diff] [blame] | 35 | const object::ELFObjectFile<ELFT> *ObjF; |
Rafael Auler | ed6b821 | 2018-03-08 00:46:53 +0000 | [diff] [blame] | 36 | |
| 37 | void printEHFrameHdr(uint64_t Offset, uint64_t Address, uint64_t Size) const; |
| 38 | |
| 39 | void printEHFrame(const typename ELFT::Shdr *EHFrameShdr) const; |
| 40 | |
| 41 | public: |
Luke Cheeseman | a0875a9 | 2018-12-18 10:37:42 +0000 | [diff] [blame] | 42 | PrinterContext(ScopedPrinter &W, const object::ELFObjectFile<ELFT> *ObjF) |
| 43 | : W(W), ObjF(ObjF) {} |
Rafael Auler | ed6b821 | 2018-03-08 00:46:53 +0000 | [diff] [blame] | 44 | |
| 45 | void printUnwindInformation() const; |
| 46 | }; |
| 47 | |
| 48 | template <class ELFO> |
| 49 | static const typename ELFO::Elf_Shdr *findSectionByAddress(const ELFO *Obj, |
| 50 | uint64_t Addr) { |
| 51 | auto Sections = Obj->sections(); |
| 52 | if (Error E = Sections.takeError()) |
| 53 | reportError(toString(std::move(E))); |
| 54 | |
| 55 | for (const auto &Shdr : *Sections) |
| 56 | if (Shdr.sh_addr == Addr) |
| 57 | return &Shdr; |
| 58 | return nullptr; |
| 59 | } |
| 60 | |
| 61 | template <typename ELFT> |
| 62 | void PrinterContext<ELFT>::printUnwindInformation() const { |
Luke Cheeseman | a0875a9 | 2018-12-18 10:37:42 +0000 | [diff] [blame] | 63 | const object::ELFFile<ELFT> *Obj = ObjF->getELFFile(); |
Rafael Auler | ed6b821 | 2018-03-08 00:46:53 +0000 | [diff] [blame] | 64 | const typename ELFT::Phdr *EHFramePhdr = nullptr; |
| 65 | |
| 66 | auto PHs = Obj->program_headers(); |
| 67 | if (Error E = PHs.takeError()) |
| 68 | reportError(toString(std::move(E))); |
| 69 | |
| 70 | for (const auto &Phdr : *PHs) { |
| 71 | if (Phdr.p_type == ELF::PT_GNU_EH_FRAME) { |
| 72 | EHFramePhdr = &Phdr; |
| 73 | if (Phdr.p_memsz != Phdr.p_filesz) |
| 74 | reportError("p_memsz does not match p_filesz for GNU_EH_FRAME"); |
| 75 | break; |
| 76 | } |
| 77 | } |
| 78 | |
| 79 | if (EHFramePhdr) |
| 80 | printEHFrameHdr(EHFramePhdr->p_offset, EHFramePhdr->p_vaddr, |
| 81 | EHFramePhdr->p_memsz); |
| 82 | |
| 83 | auto Sections = Obj->sections(); |
| 84 | if (Error E = Sections.takeError()) |
| 85 | reportError(toString(std::move(E))); |
| 86 | |
| 87 | for (const auto &Shdr : *Sections) { |
| 88 | auto SectionName = Obj->getSectionName(&Shdr); |
| 89 | if (Error E = SectionName.takeError()) |
| 90 | reportError(toString(std::move(E))); |
| 91 | |
| 92 | if (*SectionName == ".eh_frame") |
| 93 | printEHFrame(&Shdr); |
| 94 | } |
| 95 | } |
| 96 | |
| 97 | template <typename ELFT> |
| 98 | void PrinterContext<ELFT>::printEHFrameHdr(uint64_t EHFrameHdrOffset, |
| 99 | uint64_t EHFrameHdrAddress, |
| 100 | uint64_t EHFrameHdrSize) const { |
| 101 | ListScope L(W, "EH_FRAME Header"); |
| 102 | W.startLine() << format("Address: 0x%" PRIx64 "\n", EHFrameHdrAddress); |
| 103 | W.startLine() << format("Offset: 0x%" PRIx64 "\n", EHFrameHdrOffset); |
| 104 | W.startLine() << format("Size: 0x%" PRIx64 "\n", EHFrameHdrSize); |
| 105 | |
Luke Cheeseman | a0875a9 | 2018-12-18 10:37:42 +0000 | [diff] [blame] | 106 | const object::ELFFile<ELFT> *Obj = ObjF->getELFFile(); |
Rafael Auler | ed6b821 | 2018-03-08 00:46:53 +0000 | [diff] [blame] | 107 | const auto *EHFrameHdrShdr = findSectionByAddress(Obj, EHFrameHdrAddress); |
| 108 | if (EHFrameHdrShdr) { |
| 109 | auto SectionName = Obj->getSectionName(EHFrameHdrShdr); |
| 110 | if (Error E = SectionName.takeError()) |
| 111 | reportError(toString(std::move(E))); |
| 112 | |
| 113 | W.printString("Corresponding Section", *SectionName); |
| 114 | } |
| 115 | |
| 116 | DataExtractor DE( |
| 117 | StringRef(reinterpret_cast<const char *>(Obj->base()) + EHFrameHdrOffset, |
| 118 | EHFrameHdrSize), |
| 119 | ELFT::TargetEndianness == support::endianness::little, |
| 120 | ELFT::Is64Bits ? 8 : 4); |
| 121 | |
| 122 | DictScope D(W, "Header"); |
| 123 | uint32_t Offset = 0; |
| 124 | |
| 125 | auto Version = DE.getU8(&Offset); |
| 126 | W.printNumber("version", Version); |
| 127 | if (Version != 1) |
| 128 | reportError("only version 1 of .eh_frame_hdr is supported"); |
| 129 | |
| 130 | uint64_t EHFramePtrEnc = DE.getU8(&Offset); |
| 131 | W.startLine() << format("eh_frame_ptr_enc: 0x%" PRIx64 "\n", EHFramePtrEnc); |
| 132 | if (EHFramePtrEnc != (dwarf::DW_EH_PE_pcrel | dwarf::DW_EH_PE_sdata4)) |
| 133 | reportError("unexpected encoding eh_frame_ptr_enc"); |
| 134 | |
| 135 | uint64_t FDECountEnc = DE.getU8(&Offset); |
| 136 | W.startLine() << format("fde_count_enc: 0x%" PRIx64 "\n", FDECountEnc); |
| 137 | if (FDECountEnc != dwarf::DW_EH_PE_udata4) |
| 138 | reportError("unexpected encoding fde_count_enc"); |
| 139 | |
| 140 | uint64_t TableEnc = DE.getU8(&Offset); |
| 141 | W.startLine() << format("table_enc: 0x%" PRIx64 "\n", TableEnc); |
| 142 | if (TableEnc != (dwarf::DW_EH_PE_datarel | dwarf::DW_EH_PE_sdata4)) |
| 143 | reportError("unexpected encoding table_enc"); |
| 144 | |
| 145 | auto EHFramePtr = DE.getSigned(&Offset, 4) + EHFrameHdrAddress + 4; |
| 146 | W.startLine() << format("eh_frame_ptr: 0x%" PRIx64 "\n", EHFramePtr); |
| 147 | |
| 148 | auto FDECount = DE.getUnsigned(&Offset, 4); |
| 149 | W.printNumber("fde_count", FDECount); |
| 150 | |
| 151 | unsigned NumEntries = 0; |
| 152 | uint64_t PrevPC = 0; |
| 153 | while (Offset + 8 <= EHFrameHdrSize && NumEntries < FDECount) { |
| 154 | DictScope D(W, std::string("entry ") + std::to_string(NumEntries)); |
| 155 | |
| 156 | auto InitialPC = DE.getSigned(&Offset, 4) + EHFrameHdrAddress; |
| 157 | W.startLine() << format("initial_location: 0x%" PRIx64 "\n", InitialPC); |
| 158 | auto Address = DE.getSigned(&Offset, 4) + EHFrameHdrAddress; |
| 159 | W.startLine() << format("address: 0x%" PRIx64 "\n", Address); |
| 160 | |
| 161 | if (InitialPC < PrevPC) |
| 162 | reportError("initial_location is out of order"); |
| 163 | |
| 164 | PrevPC = InitialPC; |
| 165 | ++NumEntries; |
| 166 | } |
| 167 | } |
| 168 | |
| 169 | template <typename ELFT> |
| 170 | void PrinterContext<ELFT>::printEHFrame( |
| 171 | const typename ELFT::Shdr *EHFrameShdr) const { |
| 172 | uint64_t Address = EHFrameShdr->sh_addr; |
| 173 | uint64_t ShOffset = EHFrameShdr->sh_offset; |
| 174 | W.startLine() << format(".eh_frame section at offset 0x%" PRIx64 |
| 175 | " address 0x%" PRIx64 ":\n", |
| 176 | ShOffset, Address); |
| 177 | W.indent(); |
| 178 | |
Luke Cheeseman | a0875a9 | 2018-12-18 10:37:42 +0000 | [diff] [blame] | 179 | const object::ELFFile<ELFT> *Obj = ObjF->getELFFile(); |
Rafael Auler | ed6b821 | 2018-03-08 00:46:53 +0000 | [diff] [blame] | 180 | auto Result = Obj->getSectionContents(EHFrameShdr); |
| 181 | if (Error E = Result.takeError()) |
| 182 | reportError(toString(std::move(E))); |
| 183 | |
| 184 | auto Contents = Result.get(); |
| 185 | DWARFDataExtractor DE( |
| 186 | StringRef(reinterpret_cast<const char *>(Contents.data()), |
| 187 | Contents.size()), |
| 188 | ELFT::TargetEndianness == support::endianness::little, |
| 189 | ELFT::Is64Bits ? 8 : 4); |
Luke Cheeseman | a0875a9 | 2018-12-18 10:37:42 +0000 | [diff] [blame] | 190 | DWARFDebugFrame EHFrame(Triple::ArchType(ObjF->getArch()), /*IsEH=*/true, |
| 191 | /*EHFrameAddress=*/Address); |
Rafael Auler | ed6b821 | 2018-03-08 00:46:53 +0000 | [diff] [blame] | 192 | EHFrame.parse(DE); |
| 193 | |
| 194 | for (const auto &Entry : EHFrame) { |
| 195 | if (const auto *CIE = dyn_cast<dwarf::CIE>(&Entry)) { |
| 196 | W.startLine() << format("[0x%" PRIx64 "] CIE length=%" PRIu64 "\n", |
| 197 | Address + CIE->getOffset(), |
| 198 | CIE->getLength()); |
| 199 | W.indent(); |
| 200 | |
| 201 | W.printNumber("version", CIE->getVersion()); |
| 202 | W.printString("augmentation", CIE->getAugmentationString()); |
| 203 | W.printNumber("code_alignment_factor", CIE->getCodeAlignmentFactor()); |
| 204 | W.printNumber("data_alignment_factor", CIE->getDataAlignmentFactor()); |
| 205 | W.printNumber("return_address_register", CIE->getReturnAddressRegister()); |
| 206 | |
| 207 | W.getOStream() << "\n"; |
| 208 | W.startLine() << "Program:\n"; |
| 209 | W.indent(); |
| 210 | CIE->cfis().dump(W.getOStream(), nullptr, W.getIndentLevel()); |
| 211 | W.unindent(); |
| 212 | |
| 213 | W.unindent(); |
| 214 | W.getOStream() << "\n"; |
| 215 | |
| 216 | } else if (const auto *FDE = dyn_cast<dwarf::FDE>(&Entry)) { |
| 217 | W.startLine() << format("[0x%" PRIx64 "] FDE length=%" PRIu64 |
| 218 | " cie=[0x%" PRIx64 "]\n", |
| 219 | Address + FDE->getOffset(), |
| 220 | FDE->getLength(), |
| 221 | Address + FDE->getLinkedCIE()->getOffset()); |
| 222 | W.indent(); |
| 223 | |
| 224 | W.startLine() << format("initial_location: 0x%" PRIx64 "\n", |
| 225 | FDE->getInitialLocation()); |
| 226 | W.startLine() |
| 227 | << format("address_range: 0x%" PRIx64 " (end : 0x%" PRIx64 ")\n", |
| 228 | FDE->getAddressRange(), |
| 229 | FDE->getInitialLocation() + FDE->getAddressRange()); |
| 230 | |
| 231 | W.getOStream() << "\n"; |
| 232 | W.startLine() << "Program:\n"; |
| 233 | W.indent(); |
| 234 | FDE->cfis().dump(W.getOStream(), nullptr, W.getIndentLevel()); |
| 235 | W.unindent(); |
| 236 | |
| 237 | W.unindent(); |
| 238 | W.getOStream() << "\n"; |
| 239 | } else { |
| 240 | llvm_unreachable("unexpected DWARF frame kind"); |
| 241 | } |
| 242 | } |
| 243 | |
| 244 | W.unindent(); |
| 245 | } |
| 246 | |
| 247 | } |
| 248 | } |
| 249 | |
| 250 | #endif |