1 //===- Win64EHDumper.cpp - Win64 EH Printer ---------------------*- C++ -*-===//
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 #include "Win64EHDumper.h"
10 #include "llvm-readobj.h"
11 #include "llvm/Object/COFF.h"
12 #include "llvm/Support/ErrorHandling.h"
13 #include "llvm/Support/Format.h"
14 
15 using namespace llvm;
16 using namespace llvm::object;
17 using namespace llvm::Win64EH;
18 
19 static const EnumEntry<unsigned> UnwindFlags[] = {
20   { "ExceptionHandler", UNW_ExceptionHandler },
21   { "TerminateHandler", UNW_TerminateHandler },
22   { "ChainInfo"       , UNW_ChainInfo        }
23 };
24 
25 static const EnumEntry<unsigned> UnwindOpInfo[] = {
26   { "RAX",  0 },
27   { "RCX",  1 },
28   { "RDX",  2 },
29   { "RBX",  3 },
30   { "RSP",  4 },
31   { "RBP",  5 },
32   { "RSI",  6 },
33   { "RDI",  7 },
34   { "R8",   8 },
35   { "R9",   9 },
36   { "R10", 10 },
37   { "R11", 11 },
38   { "R12", 12 },
39   { "R13", 13 },
40   { "R14", 14 },
41   { "R15", 15 }
42 };
43 
44 static uint64_t getOffsetOfLSDA(const UnwindInfo& UI) {
45   return static_cast<const char*>(UI.getLanguageSpecificData())
46          - reinterpret_cast<const char*>(&UI);
47 }
48 
49 static uint32_t getLargeSlotValue(ArrayRef<UnwindCode> UC) {
50   if (UC.size() < 3)
51     return 0;
52   return UC[1].FrameOffset + (static_cast<uint32_t>(UC[2].FrameOffset) << 16);
53 }
54 
55 // Returns the name of the unwind code.
56 static StringRef getUnwindCodeTypeName(uint8_t Code) {
57   switch (Code) {
58   default: llvm_unreachable("Invalid unwind code");
59   case UOP_PushNonVol: return "PUSH_NONVOL";
60   case UOP_AllocLarge: return "ALLOC_LARGE";
61   case UOP_AllocSmall: return "ALLOC_SMALL";
62   case UOP_SetFPReg: return "SET_FPREG";
63   case UOP_SaveNonVol: return "SAVE_NONVOL";
64   case UOP_SaveNonVolBig: return "SAVE_NONVOL_FAR";
65   case UOP_SaveXMM128: return "SAVE_XMM128";
66   case UOP_SaveXMM128Big: return "SAVE_XMM128_FAR";
67   case UOP_PushMachFrame: return "PUSH_MACHFRAME";
68   }
69 }
70 
71 // Returns the name of a referenced register.
72 static StringRef getUnwindRegisterName(uint8_t Reg) {
73   switch (Reg) {
74   default: llvm_unreachable("Invalid register");
75   case 0: return "RAX";
76   case 1: return "RCX";
77   case 2: return "RDX";
78   case 3: return "RBX";
79   case 4: return "RSP";
80   case 5: return "RBP";
81   case 6: return "RSI";
82   case 7: return "RDI";
83   case 8: return "R8";
84   case 9: return "R9";
85   case 10: return "R10";
86   case 11: return "R11";
87   case 12: return "R12";
88   case 13: return "R13";
89   case 14: return "R14";
90   case 15: return "R15";
91   }
92 }
93 
94 // Calculates the number of array slots required for the unwind code.
95 static unsigned getNumUsedSlots(const UnwindCode &UnwindCode) {
96   switch (UnwindCode.getUnwindOp()) {
97   default: llvm_unreachable("Invalid unwind code");
98   case UOP_PushNonVol:
99   case UOP_AllocSmall:
100   case UOP_SetFPReg:
101   case UOP_PushMachFrame:
102     return 1;
103   case UOP_SaveNonVol:
104   case UOP_SaveXMM128:
105     return 2;
106   case UOP_SaveNonVolBig:
107   case UOP_SaveXMM128Big:
108     return 3;
109   case UOP_AllocLarge:
110     return (UnwindCode.getOpInfo() == 0) ? 2 : 3;
111   }
112 }
113 
114 static std::string formatSymbol(const Dumper::Context &Ctx,
115                                 const coff_section *Section, uint64_t Offset,
116                                 uint32_t Displacement) {
117   std::string Buffer;
118   raw_string_ostream OS(Buffer);
119 
120   SymbolRef Symbol;
121   if (!Ctx.ResolveSymbol(Section, Offset, Symbol, Ctx.UserData)) {
122     Expected<StringRef> Name = Symbol.getName();
123     if (Name) {
124       OS << *Name;
125       if (Displacement > 0)
126         OS << format(" +0x%X (0x%" PRIX64 ")", Displacement, Offset);
127       else
128         OS << format(" (0x%" PRIX64 ")", Offset);
129       return OS.str();
130     } else {
131       // TODO: Actually report errors helpfully.
132       consumeError(Name.takeError());
133     }
134   }
135 
136   OS << format(" (0x%" PRIX64 ")", Offset);
137   return OS.str();
138 }
139 
140 static std::error_code resolveRelocation(const Dumper::Context &Ctx,
141                                          const coff_section *Section,
142                                          uint64_t Offset,
143                                          const coff_section *&ResolvedSection,
144                                          uint64_t &ResolvedAddress) {
145   SymbolRef Symbol;
146   if (std::error_code EC =
147           Ctx.ResolveSymbol(Section, Offset, Symbol, Ctx.UserData))
148     return EC;
149 
150   Expected<uint64_t> ResolvedAddressOrErr = Symbol.getAddress();
151   if (!ResolvedAddressOrErr)
152     return errorToErrorCode(ResolvedAddressOrErr.takeError());
153   ResolvedAddress = *ResolvedAddressOrErr;
154 
155   Expected<section_iterator> SI = Symbol.getSection();
156   if (!SI)
157     return errorToErrorCode(SI.takeError());
158   ResolvedSection = Ctx.COFF.getCOFFSection(**SI);
159   return std::error_code();
160 }
161 
162 namespace llvm {
163 namespace Win64EH {
164 void Dumper::printRuntimeFunctionEntry(const Context &Ctx,
165                                        const coff_section *Section,
166                                        uint64_t Offset,
167                                        const RuntimeFunction &RF) {
168   SW.printString("StartAddress",
169                  formatSymbol(Ctx, Section, Offset + 0, RF.StartAddress));
170   SW.printString("EndAddress",
171                  formatSymbol(Ctx, Section, Offset + 4, RF.EndAddress));
172   SW.printString("UnwindInfoAddress",
173                  formatSymbol(Ctx, Section, Offset + 8, RF.UnwindInfoOffset));
174 }
175 
176 // Prints one unwind code. Because an unwind code can occupy up to 3 slots in
177 // the unwind codes array, this function requires that the correct number of
178 // slots is provided.
179 void Dumper::printUnwindCode(const UnwindInfo& UI, ArrayRef<UnwindCode> UC) {
180   assert(UC.size() >= getNumUsedSlots(UC[0]));
181 
182   SW.startLine() << format("0x%02X: ", unsigned(UC[0].u.CodeOffset))
183                  << getUnwindCodeTypeName(UC[0].getUnwindOp());
184 
185   switch (UC[0].getUnwindOp()) {
186   case UOP_PushNonVol:
187     OS << " reg=" << getUnwindRegisterName(UC[0].getOpInfo());
188     break;
189 
190   case UOP_AllocLarge:
191     OS << " size="
192        << ((UC[0].getOpInfo() == 0) ? UC[1].FrameOffset * 8
193                                     : getLargeSlotValue(UC));
194     break;
195 
196   case UOP_AllocSmall:
197     OS << " size=" << (UC[0].getOpInfo() + 1) * 8;
198     break;
199 
200   case UOP_SetFPReg:
201     if (UI.getFrameRegister() == 0)
202       OS << " reg=<invalid>";
203     else
204       OS << " reg=" << getUnwindRegisterName(UI.getFrameRegister())
205          << format(", offset=0x%X", UI.getFrameOffset() * 16);
206     break;
207 
208   case UOP_SaveNonVol:
209     OS << " reg=" << getUnwindRegisterName(UC[0].getOpInfo())
210        << format(", offset=0x%X", UC[1].FrameOffset * 8);
211     break;
212 
213   case UOP_SaveNonVolBig:
214     OS << " reg=" << getUnwindRegisterName(UC[0].getOpInfo())
215        << format(", offset=0x%X", getLargeSlotValue(UC));
216     break;
217 
218   case UOP_SaveXMM128:
219     OS << " reg=XMM" << static_cast<uint32_t>(UC[0].getOpInfo())
220        << format(", offset=0x%X", UC[1].FrameOffset * 16);
221     break;
222 
223   case UOP_SaveXMM128Big:
224     OS << " reg=XMM" << static_cast<uint32_t>(UC[0].getOpInfo())
225        << format(", offset=0x%X", getLargeSlotValue(UC));
226     break;
227 
228   case UOP_PushMachFrame:
229     OS << " errcode=" << (UC[0].getOpInfo() == 0 ? "no" : "yes");
230     break;
231   }
232 
233   OS << "\n";
234 }
235 
236 void Dumper::printUnwindInfo(const Context &Ctx, const coff_section *Section,
237                              off_t Offset, const UnwindInfo &UI) {
238   DictScope UIS(SW, "UnwindInfo");
239   SW.printNumber("Version", UI.getVersion());
240   SW.printFlags("Flags", UI.getFlags(), makeArrayRef(UnwindFlags));
241   SW.printNumber("PrologSize", UI.PrologSize);
242   if (UI.getFrameRegister()) {
243     SW.printEnum("FrameRegister", UI.getFrameRegister(),
244                  makeArrayRef(UnwindOpInfo));
245     SW.printHex("FrameOffset", UI.getFrameOffset());
246   } else {
247     SW.printString("FrameRegister", StringRef("-"));
248     SW.printString("FrameOffset", StringRef("-"));
249   }
250 
251   SW.printNumber("UnwindCodeCount", UI.NumCodes);
252   {
253     ListScope UCS(SW, "UnwindCodes");
254     ArrayRef<UnwindCode> UC(&UI.UnwindCodes[0], UI.NumCodes);
255     for (const UnwindCode *UCI = UC.begin(), *UCE = UC.end(); UCI < UCE; ++UCI) {
256       unsigned UsedSlots = getNumUsedSlots(*UCI);
257       if (UsedSlots > UC.size()) {
258         errs() << "corrupt unwind data";
259         return;
260       }
261 
262       printUnwindCode(UI, makeArrayRef(UCI, UCE));
263       UCI = UCI + UsedSlots - 1;
264     }
265   }
266 
267   uint64_t LSDAOffset = Offset + getOffsetOfLSDA(UI);
268   if (UI.getFlags() & (UNW_ExceptionHandler | UNW_TerminateHandler)) {
269     SW.printString("Handler",
270                    formatSymbol(Ctx, Section, LSDAOffset,
271                                 UI.getLanguageSpecificHandlerOffset()));
272   } else if (UI.getFlags() & UNW_ChainInfo) {
273     if (const RuntimeFunction *Chained = UI.getChainedFunctionEntry()) {
274       DictScope CS(SW, "Chained");
275       printRuntimeFunctionEntry(Ctx, Section, LSDAOffset, *Chained);
276     }
277   }
278 }
279 
280 void Dumper::printRuntimeFunction(const Context &Ctx,
281                                   const coff_section *Section,
282                                   uint64_t SectionOffset,
283                                   const RuntimeFunction &RF) {
284   DictScope RFS(SW, "RuntimeFunction");
285   printRuntimeFunctionEntry(Ctx, Section, SectionOffset, RF);
286 
287   const coff_section *XData;
288   uint64_t Offset;
289   resolveRelocation(Ctx, Section, SectionOffset + 8, XData, Offset);
290 
291   ArrayRef<uint8_t> Contents;
292   error(Ctx.COFF.getSectionContents(XData, Contents));
293   if (Contents.empty())
294     return;
295 
296   Offset = Offset + RF.UnwindInfoOffset;
297   if (Offset > Contents.size())
298     return;
299 
300   const auto UI = reinterpret_cast<const UnwindInfo*>(Contents.data() + Offset);
301   printUnwindInfo(Ctx, XData, Offset, *UI);
302 }
303 
304 void Dumper::printData(const Context &Ctx) {
305   for (const auto &Section : Ctx.COFF.sections()) {
306     StringRef Name;
307     Section.getName(Name);
308 
309     if (Name != ".pdata" && !Name.startswith(".pdata$"))
310       continue;
311 
312     const coff_section *PData = Ctx.COFF.getCOFFSection(Section);
313     ArrayRef<uint8_t> Contents;
314     error(Ctx.COFF.getSectionContents(PData, Contents));
315     if (Contents.empty())
316       continue;
317 
318     const RuntimeFunction *Entries =
319       reinterpret_cast<const RuntimeFunction *>(Contents.data());
320     const size_t Count = Contents.size() / sizeof(RuntimeFunction);
321     ArrayRef<RuntimeFunction> RuntimeFunctions(Entries, Count);
322 
323     size_t Index = 0;
324     for (const auto &RF : RuntimeFunctions) {
325       printRuntimeFunction(Ctx, Ctx.COFF.getCOFFSection(Section),
326                            Index * sizeof(RuntimeFunction), RF);
327       ++Index;
328     }
329   }
330 }
331 }
332 }
333 
334