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