xref: /freebsd/contrib/llvm-project/llvm/tools/llvm-readobj/DwarfCFIEHPrinter.h (revision db33c6f3ae9d1231087710068ee4ea5398aacca7)
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                       "p_memsz does not match p_filesz for GNU_EH_FRAME"),
77                   ObjF.getFileName());
78     printEHFrameHdr(&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(&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("Address: 0x%" PRIx64 "\n", EHFrameHdrAddress);
100   W.startLine() << format("Offset: 0x%" PRIx64 "\n", (uint64_t)EHFramePHdr->p_offset);
101   W.startLine() << format("Size: 0x%" PRIx64 "\n", (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("Corresponding Section", *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);
123   W.printNumber("version", Version);
124   if (Version != 1)
125     reportError(
126         object::createError("only version 1 of .eh_frame_hdr is supported"),
127         ObjF.getFileName());
128 
129   uint64_t EHFramePtrEnc = DE.getU8(&Offset);
130   W.startLine() << format("eh_frame_ptr_enc: 0x%" PRIx64 "\n", EHFramePtrEnc);
131   if (EHFramePtrEnc != (dwarf::DW_EH_PE_pcrel | dwarf::DW_EH_PE_sdata4))
132     reportError(object::createError("unexpected encoding eh_frame_ptr_enc"),
133                 ObjF.getFileName());
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(object::createError("unexpected encoding fde_count_enc"),
139                 ObjF.getFileName());
140 
141   uint64_t TableEnc = DE.getU8(&Offset);
142   W.startLine() << format("table_enc: 0x%" PRIx64 "\n", TableEnc);
143   if (TableEnc != (dwarf::DW_EH_PE_datarel | dwarf::DW_EH_PE_sdata4))
144     reportError(object::createError("unexpected encoding table_enc"),
145                 ObjF.getFileName());
146 
147   auto EHFramePtr = DE.getSigned(&Offset, 4) + EHFrameHdrAddress + 4;
148   W.startLine() << format("eh_frame_ptr: 0x%" PRIx64 "\n", EHFramePtr);
149 
150   auto FDECount = DE.getUnsigned(&Offset, 4);
151   W.printNumber("fde_count", 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(NumEntries));
157 
158     auto InitialPC = DE.getSigned(&Offset, 4) + EHFrameHdrAddress;
159     W.startLine() << format("initial_location: 0x%" PRIx64 "\n", InitialPC);
160     auto Address = DE.getSigned(&Offset, 4) + EHFrameHdrAddress;
161     W.startLine() << format("address: 0x%" PRIx64 "\n", Address);
162 
163     if (InitialPC < PrevPC)
164       reportError(object::createError("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(".eh_frame section at offset 0x%" PRIx64
177                           " address 0x%" PRIx64 ":\n",
178                           ShOffset, 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(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>(&Entry)) {
200       W.startLine() << format("[0x%" PRIx64 "] CIE length=%" PRIu64 "\n",
201                               Address + CIE->getOffset(), CIE->getLength());
202       W.indent();
203 
204       W.printNumber("version", CIE->getVersion());
205       W.printString("augmentation", CIE->getAugmentationString());
206       W.printNumber("code_alignment_factor", CIE->getCodeAlignmentFactor());
207       W.printNumber("data_alignment_factor", CIE->getDataAlignmentFactor());
208       W.printNumber("return_address_register", CIE->getReturnAddressRegister());
209     } else {
210       const dwarf::FDE *FDE = cast<dwarf::FDE>(&Entry);
211       W.startLine() << format("[0x%" PRIx64 "] FDE length=%" PRIu64
212                               " cie=[0x%" PRIx64 "]\n",
213                               Address + FDE->getOffset(), FDE->getLength(),
214                               Address + FDE->getLinkedCIE()->getOffset());
215       W.indent();
216 
217       InitialLocation = FDE->getInitialLocation();
218       W.startLine() << format("initial_location: 0x%" PRIx64 "\n",
219                               *InitialLocation);
220       W.startLine() << format(
221           "address_range: 0x%" PRIx64 " (end : 0x%" PRIx64 ")\n",
222           FDE->getAddressRange(),
223           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(W.getOStream(), DumpOpts, 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