449 lines · cpp
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-exception6//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 15using namespace llvm;16using namespace llvm::object;17using namespace llvm::Win64EH;18 19const EnumEntry<unsigned> UnwindFlags[] = {20 { "ExceptionHandler", UNW_ExceptionHandler },21 { "TerminateHandler", UNW_TerminateHandler },22 { "ChainInfo" , UNW_ChainInfo }23};24 25const 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 44static uint64_t getOffsetOfLSDA(const UnwindInfo& UI) {45 return static_cast<const char*>(UI.getLanguageSpecificData())46 - reinterpret_cast<const char*>(&UI);47}48 49static 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.56static 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.74static 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.97static 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 117static 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 131static 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 internal136 // 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 162static 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, pointing172 // 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 else183 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 else204 OS << format("(0x%" PRIX64 ")", Offset);205 return OS.str();206}207 208static 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 230static const object::coff_section *231getSectionContaining(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 242namespace llvm {243namespace Win64EH {244void 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 in258// the unwind codes array, this function requires that the correct number of259// slots is provided.260void 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 * 8275 : 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 else286 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 335void 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 380void 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 414void 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 else420 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