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
28namespace llvm {
29namespace DwarfCFIEH {
30
31template <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
41public:
42 PrinterContext(ScopedPrinter &W, const object::ELFObjectFile<ELFT> &ObjF)
43 : W(W), ObjF(ObjF) {}
44
45 void printUnwindInformation() const;
46};
47
48template <class ELFT>
49static const typename ELFT::Shdr *
50findSectionByAddress(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
62template <typename ELFT>
63void 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
95template <typename ELFT>
96void 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
172template <typename ELFT>
173void 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