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 const EnumEntry<unsigned> UnwindFlags[] = { 20 { "ExceptionHandler", UNW_ExceptionHandler }, 21 { "TerminateHandler", UNW_TerminateHandler }, 22 { "ChainInfo" , UNW_ChainInfo } 23 }; 24 25 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 case UOP_Epilog: 69 return "EPILOG"; 70 } 71 } 72 73 // Returns the name of a referenced register. 74 static StringRef getUnwindRegisterName(uint8_t Reg) { 75 switch (Reg) { 76 default: llvm_unreachable("Invalid register"); 77 case 0: return "RAX"; 78 case 1: return "RCX"; 79 case 2: return "RDX"; 80 case 3: return "RBX"; 81 case 4: return "RSP"; 82 case 5: return "RBP"; 83 case 6: return "RSI"; 84 case 7: return "RDI"; 85 case 8: return "R8"; 86 case 9: return "R9"; 87 case 10: return "R10"; 88 case 11: return "R11"; 89 case 12: return "R12"; 90 case 13: return "R13"; 91 case 14: return "R14"; 92 case 15: return "R15"; 93 } 94 } 95 96 // Calculates the number of array slots required for the unwind code. 97 static unsigned getNumUsedSlots(const UnwindCode &UnwindCode) { 98 switch (UnwindCode.getUnwindOp()) { 99 default: llvm_unreachable("Invalid unwind code"); 100 case UOP_PushNonVol: 101 case UOP_AllocSmall: 102 case UOP_SetFPReg: 103 case UOP_PushMachFrame: 104 case UOP_Epilog: 105 return 1; 106 case UOP_SaveNonVol: 107 case UOP_SaveXMM128: 108 return 2; 109 case UOP_SaveNonVolBig: 110 case UOP_SaveXMM128Big: 111 return 3; 112 case UOP_AllocLarge: 113 return (UnwindCode.getOpInfo() == 0) ? 2 : 3; 114 } 115 } 116 117 static std::error_code getSymbol(const COFFObjectFile &COFF, uint64_t VA, 118 object::SymbolRef &Sym) { 119 for (const auto &Symbol : COFF.symbols()) { 120 Expected<uint64_t> Address = Symbol.getAddress(); 121 if (!Address) 122 return errorToErrorCode(Address.takeError()); 123 if (*Address == VA) { 124 Sym = Symbol; 125 return std::error_code(); 126 } 127 } 128 return inconvertibleErrorCode(); 129 } 130 131 static object::SymbolRef getPreferredSymbol(const COFFObjectFile &COFF, 132 object::SymbolRef Sym, 133 uint32_t &SymbolOffset, 134 bool IsRangeEnd) { 135 // The symbol resolved by ResolveSymbol can be any internal 136 // nondescriptive symbol; try to resolve a more descriptive one. 137 COFFSymbolRef CoffSym = COFF.getCOFFSymbol(Sym); 138 if (CoffSym.getStorageClass() != COFF::IMAGE_SYM_CLASS_LABEL && 139 CoffSym.getSectionDefinition() == nullptr) 140 return Sym; 141 for (const auto &S : COFF.symbols()) { 142 COFFSymbolRef CS = COFF.getCOFFSymbol(S); 143 if (CS.getSectionNumber() == CoffSym.getSectionNumber() && 144 CS.getValue() <= CoffSym.getValue() + SymbolOffset && 145 CS.getStorageClass() != COFF::IMAGE_SYM_CLASS_LABEL && 146 CS.getSectionDefinition() == nullptr) { 147 uint32_t Offset = CoffSym.getValue() + SymbolOffset - CS.getValue(); 148 // For the end of a range, don't pick a symbol with a zero offset; 149 // prefer a symbol with a small positive offset. 150 if (Offset <= SymbolOffset && (!IsRangeEnd || Offset > 0)) { 151 SymbolOffset = Offset; 152 Sym = S; 153 CoffSym = CS; 154 if (CS.isExternal() && SymbolOffset == 0) 155 return Sym; 156 } 157 } 158 } 159 return Sym; 160 } 161 162 static std::string formatSymbol(const Dumper::Context &Ctx, 163 const coff_section *Section, uint64_t Offset, 164 uint32_t Displacement, 165 bool IsRangeEnd = false) { 166 std::string Buffer; 167 raw_string_ostream OS(Buffer); 168 169 SymbolRef Symbol; 170 if (!Ctx.ResolveSymbol(Section, Offset, Symbol, Ctx.UserData)) { 171 // We found a relocation at the given offset in the section, pointing 172 // at a symbol. 173 174 // Try to resolve label/section symbols into function names. 175 Symbol = getPreferredSymbol(Ctx.COFF, Symbol, Displacement, IsRangeEnd); 176 177 Expected<StringRef> Name = Symbol.getName(); 178 if (Name) { 179 OS << *Name; 180 if (Displacement > 0) 181 OS << format(" +0x%X (0x%" PRIX64 ")", Displacement, Offset); 182 else 183 OS << format(" (0x%" PRIX64 ")", Offset); 184 return OS.str(); 185 } else { 186 // TODO: Actually report errors helpfully. 187 consumeError(Name.takeError()); 188 } 189 } else if (!getSymbol(Ctx.COFF, Ctx.COFF.getImageBase() + Displacement, 190 Symbol)) { 191 Expected<StringRef> Name = Symbol.getName(); 192 if (Name) { 193 OS << *Name; 194 OS << format(" (0x%" PRIX64 ")", Ctx.COFF.getImageBase() + Displacement); 195 return OS.str(); 196 } else { 197 consumeError(Name.takeError()); 198 } 199 } 200 201 if (Displacement > 0) 202 OS << format("(0x%" PRIX64 ")", Ctx.COFF.getImageBase() + Displacement); 203 else 204 OS << format("(0x%" PRIX64 ")", Offset); 205 return OS.str(); 206 } 207 208 static std::error_code resolveRelocation(const Dumper::Context &Ctx, 209 const coff_section *Section, 210 uint64_t Offset, 211 const coff_section *&ResolvedSection, 212 uint64_t &ResolvedAddress) { 213 SymbolRef Symbol; 214 if (std::error_code EC = 215 Ctx.ResolveSymbol(Section, Offset, Symbol, Ctx.UserData)) 216 return EC; 217 218 Expected<uint64_t> ResolvedAddressOrErr = Symbol.getAddress(); 219 if (!ResolvedAddressOrErr) 220 return errorToErrorCode(ResolvedAddressOrErr.takeError()); 221 ResolvedAddress = *ResolvedAddressOrErr; 222 223 Expected<section_iterator> SI = Symbol.getSection(); 224 if (!SI) 225 return errorToErrorCode(SI.takeError()); 226 ResolvedSection = Ctx.COFF.getCOFFSection(**SI); 227 return std::error_code(); 228 } 229 230 static const object::coff_section * 231 getSectionContaining(const COFFObjectFile &COFF, uint64_t VA) { 232 for (const auto &Section : COFF.sections()) { 233 uint64_t Address = Section.getAddress(); 234 uint64_t Size = Section.getSize(); 235 236 if (VA >= Address && (VA - Address) <= Size) 237 return COFF.getCOFFSection(Section); 238 } 239 return nullptr; 240 } 241 242 namespace llvm { 243 namespace Win64EH { 244 void Dumper::printRuntimeFunctionEntry(const Context &Ctx, 245 const coff_section *Section, 246 uint64_t Offset, 247 const RuntimeFunction &RF) { 248 SW.printString("StartAddress", 249 formatSymbol(Ctx, Section, Offset + 0, RF.StartAddress)); 250 SW.printString("EndAddress", 251 formatSymbol(Ctx, Section, Offset + 4, RF.EndAddress, 252 /*IsRangeEnd=*/true)); 253 SW.printString("UnwindInfoAddress", 254 formatSymbol(Ctx, Section, Offset + 8, RF.UnwindInfoOffset)); 255 } 256 257 // Prints one unwind code. Because an unwind code can occupy up to 3 slots in 258 // the unwind codes array, this function requires that the correct number of 259 // slots is provided. 260 void Dumper::printUnwindCode(const UnwindInfo &UI, ArrayRef<UnwindCode> UC, 261 bool &SeenFirstEpilog) { 262 assert(UC.size() >= getNumUsedSlots(UC[0])); 263 264 SW.startLine() << format("0x%02X: ", unsigned(UC[0].u.CodeOffset)) 265 << getUnwindCodeTypeName(UC[0].getUnwindOp()); 266 267 switch (UC[0].getUnwindOp()) { 268 case UOP_PushNonVol: 269 OS << " reg=" << getUnwindRegisterName(UC[0].getOpInfo()); 270 break; 271 272 case UOP_AllocLarge: 273 OS << " size=" 274 << ((UC[0].getOpInfo() == 0) ? UC[1].FrameOffset * 8 275 : getLargeSlotValue(UC)); 276 break; 277 278 case UOP_AllocSmall: 279 OS << " size=" << (UC[0].getOpInfo() + 1) * 8; 280 break; 281 282 case UOP_SetFPReg: 283 if (UI.getFrameRegister() == 0) 284 OS << " reg=<invalid>"; 285 else 286 OS << " reg=" << getUnwindRegisterName(UI.getFrameRegister()) 287 << format(", offset=0x%X", UI.getFrameOffset() * 16); 288 break; 289 290 case UOP_SaveNonVol: 291 OS << " reg=" << getUnwindRegisterName(UC[0].getOpInfo()) 292 << format(", offset=0x%X", UC[1].FrameOffset * 8); 293 break; 294 295 case UOP_SaveNonVolBig: 296 OS << " reg=" << getUnwindRegisterName(UC[0].getOpInfo()) 297 << format(", offset=0x%X", getLargeSlotValue(UC)); 298 break; 299 300 case UOP_SaveXMM128: 301 OS << " reg=XMM" << static_cast<uint32_t>(UC[0].getOpInfo()) 302 << format(", offset=0x%X", UC[1].FrameOffset * 16); 303 break; 304 305 case UOP_SaveXMM128Big: 306 OS << " reg=XMM" << static_cast<uint32_t>(UC[0].getOpInfo()) 307 << format(", offset=0x%X", getLargeSlotValue(UC)); 308 break; 309 310 case UOP_PushMachFrame: 311 OS << " errcode=" << (UC[0].getOpInfo() == 0 ? "no" : "yes"); 312 break; 313 314 case UOP_Epilog: 315 if (SeenFirstEpilog) { 316 uint32_t Offset = UC[0].getEpilogOffset(); 317 if (Offset == 0) { 318 OS << " padding"; 319 } else { 320 OS << " offset=" << format("0x%X", Offset); 321 } 322 } else { 323 SeenFirstEpilog = true; 324 bool AtEnd = (UC[0].getOpInfo() & 0x1) != 0; 325 uint32_t Length = UC[0].u.CodeOffset; 326 OS << " atend=" << (AtEnd ? "yes" : "no") 327 << ", length=" << format("0x%X", Length); 328 } 329 break; 330 } 331 332 OS << "\n"; 333 } 334 335 void Dumper::printUnwindInfo(const Context &Ctx, const coff_section *Section, 336 off_t Offset, const UnwindInfo &UI) { 337 DictScope UIS(SW, "UnwindInfo"); 338 SW.printNumber("Version", UI.getVersion()); 339 SW.printFlags("Flags", UI.getFlags(), ArrayRef(UnwindFlags)); 340 SW.printNumber("PrologSize", UI.PrologSize); 341 if (UI.getFrameRegister()) { 342 SW.printEnum("FrameRegister", UI.getFrameRegister(), 343 ArrayRef(UnwindOpInfo)); 344 SW.printHex("FrameOffset", UI.getFrameOffset()); 345 } else { 346 SW.printString("FrameRegister", StringRef("-")); 347 SW.printString("FrameOffset", StringRef("-")); 348 } 349 350 SW.printNumber("UnwindCodeCount", UI.NumCodes); 351 { 352 ListScope UCS(SW, "UnwindCodes"); 353 ArrayRef<UnwindCode> UC(&UI.UnwindCodes[0], UI.NumCodes); 354 bool SeenFirstEpilog = false; 355 for (const UnwindCode *UCI = UC.begin(), *UCE = UC.end(); UCI < UCE; ++UCI) { 356 unsigned UsedSlots = getNumUsedSlots(*UCI); 357 if (UsedSlots > UC.size()) { 358 errs() << "corrupt unwind data"; 359 return; 360 } 361 362 printUnwindCode(UI, ArrayRef(UCI, UCE), SeenFirstEpilog); 363 UCI = UCI + UsedSlots - 1; 364 } 365 } 366 367 uint64_t LSDAOffset = Offset + getOffsetOfLSDA(UI); 368 if (UI.getFlags() & (UNW_ExceptionHandler | UNW_TerminateHandler)) { 369 SW.printString("Handler", 370 formatSymbol(Ctx, Section, LSDAOffset, 371 UI.getLanguageSpecificHandlerOffset())); 372 } else if (UI.getFlags() & UNW_ChainInfo) { 373 if (const RuntimeFunction *Chained = UI.getChainedFunctionEntry()) { 374 DictScope CS(SW, "Chained"); 375 printRuntimeFunctionEntry(Ctx, Section, LSDAOffset, *Chained); 376 } 377 } 378 } 379 380 void Dumper::printRuntimeFunction(const Context &Ctx, 381 const coff_section *Section, 382 uint64_t SectionOffset, 383 const RuntimeFunction &RF) { 384 DictScope RFS(SW, "RuntimeFunction"); 385 printRuntimeFunctionEntry(Ctx, Section, SectionOffset, RF); 386 387 const coff_section *XData = nullptr; 388 uint64_t Offset; 389 resolveRelocation(Ctx, Section, SectionOffset + 8, XData, Offset); 390 Offset = Offset + RF.UnwindInfoOffset; 391 392 if (!XData) { 393 uint64_t Address = Ctx.COFF.getImageBase() + RF.UnwindInfoOffset; 394 XData = getSectionContaining(Ctx.COFF, Address); 395 if (!XData) 396 return; 397 Offset = RF.UnwindInfoOffset - XData->VirtualAddress; 398 } 399 400 ArrayRef<uint8_t> Contents; 401 if (Error E = Ctx.COFF.getSectionContents(XData, Contents)) 402 reportError(std::move(E), Ctx.COFF.getFileName()); 403 404 if (Contents.empty()) 405 return; 406 407 if (Offset > Contents.size()) 408 return; 409 410 const auto UI = reinterpret_cast<const UnwindInfo*>(Contents.data() + Offset); 411 printUnwindInfo(Ctx, XData, Offset, *UI); 412 } 413 414 void Dumper::printData(const Context &Ctx) { 415 for (const auto &Section : Ctx.COFF.sections()) { 416 StringRef Name; 417 if (Expected<StringRef> NameOrErr = Section.getName()) 418 Name = *NameOrErr; 419 else 420 consumeError(NameOrErr.takeError()); 421 422 if (Name != ".pdata" && !Name.starts_with(".pdata$")) 423 continue; 424 425 const coff_section *PData = Ctx.COFF.getCOFFSection(Section); 426 ArrayRef<uint8_t> Contents; 427 428 if (Error E = Ctx.COFF.getSectionContents(PData, Contents)) 429 reportError(std::move(E), Ctx.COFF.getFileName()); 430 if (Contents.empty()) 431 continue; 432 433 const RuntimeFunction *Entries = 434 reinterpret_cast<const RuntimeFunction *>(Contents.data()); 435 const size_t Count = Contents.size() / sizeof(RuntimeFunction); 436 ArrayRef<RuntimeFunction> RuntimeFunctions(Entries, Count); 437 438 size_t Index = 0; 439 for (const auto &RF : RuntimeFunctions) { 440 printRuntimeFunction(Ctx, Ctx.COFF.getCOFFSection(Section), 441 Index * sizeof(RuntimeFunction), RF); 442 ++Index; 443 } 444 } 445 } 446 } 447 } 448 449