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