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 | |