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
29namespace llvm {
30namespace DwarfCFIEH {
31
32template <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
42public:
43 PrinterContext(ScopedPrinter &W, const object::ELFObjectFile<ELFT> &ObjF)
44 : W(W), ObjF(ObjF) {}
45
46 void printUnwindInformation() const;
47};
48
49template <class ELFT>
50static const typename ELFT::Shdr *
51findSectionByAddress(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
63template <typename ELFT>
64void 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
96template <typename ELFT>
97void 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
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 unsigned EHFramePtrSize = 0;
132 if (EHFramePtrEnc == (dwarf::DW_EH_PE_pcrel | dwarf::DW_EH_PE_sdata4))
133 EHFramePtrSize = 4;
134 else if (EHFramePtrEnc == (dwarf::DW_EH_PE_pcrel | dwarf::DW_EH_PE_sdata8))
135 EHFramePtrSize = 8;
136 else
137 reportError(object::createError(Err: "unexpected encoding eh_frame_ptr_enc"),
138 ObjF.getFileName());
139
140 uint64_t FDECountEnc = DE.getU8(offset_ptr: &Offset);
141 W.startLine() << format(Fmt: "fde_count_enc: 0x%" PRIx64 "\n", Vals: FDECountEnc);
142 if (FDECountEnc != dwarf::DW_EH_PE_udata4)
143 reportError(object::createError(Err: "unexpected encoding fde_count_enc"),
144 ObjF.getFileName());
145
146 uint64_t TableEnc = DE.getU8(offset_ptr: &Offset);
147 W.startLine() << format(Fmt: "table_enc: 0x%" PRIx64 "\n", Vals: TableEnc);
148 unsigned TableEntrySize = 0;
149 if (TableEnc == (dwarf::DW_EH_PE_datarel | dwarf::DW_EH_PE_sdata4))
150 TableEntrySize = 4;
151 else if (TableEnc == (dwarf::DW_EH_PE_datarel | dwarf::DW_EH_PE_sdata8))
152 TableEntrySize = 8;
153 else
154 reportError(object::createError(Err: "unexpected encoding table_enc 0x" +
155 Twine::utohexstr(Val: TableEnc)),
156 ObjF.getFileName());
157
158 auto EHFramePtr =
159 DE.getSigned(offset_ptr: &Offset, size: EHFramePtrSize) + EHFrameHdrAddress + 4;
160 W.startLine() << format(Fmt: "eh_frame_ptr: 0x%" PRIx64 "\n", Vals: EHFramePtr);
161
162 auto FDECount = DE.getUnsigned(offset_ptr: &Offset, byte_size: 4);
163 W.printNumber(Label: "fde_count", Value: FDECount);
164
165 unsigned NumEntries = 0;
166 uint64_t PrevPC = 0;
167 while (Offset + 2 * TableEntrySize <= EHFramePHdr->p_memsz &&
168 NumEntries < FDECount) {
169 DictScope D(W, std::string("entry ") + std::to_string(val: NumEntries));
170
171 auto InitialPC = DE.getSigned(offset_ptr: &Offset, size: TableEntrySize) + EHFrameHdrAddress;
172 W.startLine() << format(Fmt: "initial_location: 0x%" PRIx64 "\n", Vals: InitialPC);
173 auto Address = DE.getSigned(offset_ptr: &Offset, size: TableEntrySize) + EHFrameHdrAddress;
174 W.startLine() << format(Fmt: "address: 0x%" PRIx64 "\n", Vals: Address);
175
176 if (InitialPC < PrevPC)
177 reportError(object::createError(Err: "initial_location is out of order"),
178 ObjF.getFileName());
179
180 PrevPC = InitialPC;
181 ++NumEntries;
182 }
183}
184
185template <typename ELFT>
186void PrinterContext<ELFT>::printEHFrame(const Elf_Shdr *EHFrameShdr) const {
187 uint64_t Address = EHFrameShdr->sh_addr;
188 uint64_t ShOffset = EHFrameShdr->sh_offset;
189 W.startLine() << format(Fmt: ".eh_frame section at offset 0x%" PRIx64
190 " address 0x%" PRIx64 ":\n",
191 Vals: ShOffset, Vals: Address);
192 W.indent();
193
194 Expected<ArrayRef<uint8_t>> DataOrErr =
195 ObjF.getELFFile().getSectionContents(*EHFrameShdr);
196 if (!DataOrErr)
197 reportError(DataOrErr.takeError(), ObjF.getFileName());
198
199 // Construct DWARFDataExtractor to handle relocations ("PC Begin" fields).
200 std::unique_ptr<DWARFContext> DICtx = DWARFContext::create(
201 ObjF, DWARFContext::ProcessDebugRelocations::Process, nullptr);
202 DWARFDataExtractor DE(
203 DICtx->getDWARFObj(), DICtx->getDWARFObj().getEHFrameSection(),
204 ELFT::Endianness == llvm::endianness::little, ELFT::Is64Bits ? 8 : 4);
205 DWARFDebugFrame EHFrame(Triple::ArchType(ObjF.getArch()), /*IsEH=*/true,
206 /*EHFrameAddress=*/Address);
207 if (Error E = EHFrame.parse(Data: DE))
208 reportError(std::move(E), ObjF.getFileName());
209
210 for (const dwarf::FrameEntry &Entry : EHFrame) {
211 std::optional<uint64_t> InitialLocation;
212 if (const dwarf::CIE *CIE = dyn_cast<dwarf::CIE>(Val: &Entry)) {
213 W.startLine() << format(Fmt: "[0x%" PRIx64 "] CIE length=%" PRIu64 "\n",
214 Vals: Address + CIE->getOffset(), Vals: CIE->getLength());
215 W.indent();
216
217 W.printNumber(Label: "version", Value: CIE->getVersion());
218 W.printString(Label: "augmentation", Value: CIE->getAugmentationString());
219 W.printNumber(Label: "code_alignment_factor", Value: CIE->getCodeAlignmentFactor());
220 W.printNumber(Label: "data_alignment_factor", Value: CIE->getDataAlignmentFactor());
221 W.printNumber(Label: "return_address_register", Value: CIE->getReturnAddressRegister());
222 } else {
223 const dwarf::FDE *FDE = cast<dwarf::FDE>(Val: &Entry);
224 W.startLine() << format(Fmt: "[0x%" PRIx64 "] FDE length=%" PRIu64
225 " cie=[0x%" PRIx64 "]\n",
226 Vals: Address + FDE->getOffset(), Vals: FDE->getLength(),
227 Vals: Address + FDE->getLinkedCIE()->getOffset());
228 W.indent();
229
230 InitialLocation = FDE->getInitialLocation();
231 W.startLine() << format(Fmt: "initial_location: 0x%" PRIx64 "\n",
232 Vals: *InitialLocation);
233 W.startLine() << format(
234 Fmt: "address_range: 0x%" PRIx64 " (end : 0x%" PRIx64 ")\n",
235 Vals: FDE->getAddressRange(),
236 Vals: FDE->getInitialLocation() + FDE->getAddressRange());
237 }
238
239 W.getOStream() << "\n";
240 W.startLine() << "Program:\n";
241 W.indent();
242 auto DumpOpts = DIDumpOptions();
243 DumpOpts.IsEH = true;
244 printCFIProgram(P: Entry.cfis(), OS&: W.getOStream(), DumpOpts, IndentLevel: W.getIndentLevel(),
245 Address: InitialLocation);
246 W.unindent();
247 W.unindent();
248 W.getOStream() << "\n";
249 }
250
251 W.unindent();
252}
253} // namespace DwarfCFIEH
254} // namespace llvm
255
256#endif
257