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                      "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
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("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::TargetEndianness == 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
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(".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(DICtx->getDWARFObj(),
190                        DICtx->getDWARFObj().getEHFrameSection(),
191                        ELFT::TargetEndianness == llvm::endianness::little,
192                        ELFT::Is64Bits ? 8 : 4);
193  DWARFDebugFrame EHFrame(Triple::ArchType(ObjF.getArch()), /*IsEH=*/true,
194                          /*EHFrameAddress=*/Address);
195  if (Error E = EHFrame.parse(DE))
196    reportError(std::move(E), ObjF.getFileName());
197
198  for (const dwarf::FrameEntry &Entry : EHFrame) {
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      W.startLine() << format("initial_location: 0x%" PRIx64 "\n",
218                              FDE->getInitialLocation());
219      W.startLine() << format(
220          "address_range: 0x%" PRIx64 " (end : 0x%" PRIx64 ")\n",
221          FDE->getAddressRange(),
222          FDE->getInitialLocation() + FDE->getAddressRange());
223    }
224
225    W.getOStream() << "\n";
226    W.startLine() << "Program:\n";
227    W.indent();
228    auto DumpOpts = DIDumpOptions();
229    DumpOpts.IsEH = true;
230    Entry.cfis().dump(W.getOStream(), DumpOpts, W.getIndentLevel());
231    W.unindent();
232    W.unindent();
233    W.getOStream() << "\n";
234  }
235
236  W.unindent();
237}
238} // namespace DwarfCFIEH
239} // namespace llvm
240
241#endif
242