Path: blob/main/contrib/llvm-project/llvm/tools/llvm-readobj/Win64EHDumper.cpp
35231 views
//===- Win64EHDumper.cpp - Win64 EH Printer ---------------------*- C++ -*-===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "Win64EHDumper.h"9#include "llvm-readobj.h"10#include "llvm/Object/COFF.h"11#include "llvm/Support/ErrorHandling.h"12#include "llvm/Support/Format.h"1314using namespace llvm;15using namespace llvm::object;16using namespace llvm::Win64EH;1718const EnumEntry<unsigned> UnwindFlags[] = {19{ "ExceptionHandler", UNW_ExceptionHandler },20{ "TerminateHandler", UNW_TerminateHandler },21{ "ChainInfo" , UNW_ChainInfo }22};2324const EnumEntry<unsigned> UnwindOpInfo[] = {25{ "RAX", 0 },26{ "RCX", 1 },27{ "RDX", 2 },28{ "RBX", 3 },29{ "RSP", 4 },30{ "RBP", 5 },31{ "RSI", 6 },32{ "RDI", 7 },33{ "R8", 8 },34{ "R9", 9 },35{ "R10", 10 },36{ "R11", 11 },37{ "R12", 12 },38{ "R13", 13 },39{ "R14", 14 },40{ "R15", 15 }41};4243static uint64_t getOffsetOfLSDA(const UnwindInfo& UI) {44return static_cast<const char*>(UI.getLanguageSpecificData())45- reinterpret_cast<const char*>(&UI);46}4748static uint32_t getLargeSlotValue(ArrayRef<UnwindCode> UC) {49if (UC.size() < 3)50return 0;51return UC[1].FrameOffset + (static_cast<uint32_t>(UC[2].FrameOffset) << 16);52}5354// Returns the name of the unwind code.55static StringRef getUnwindCodeTypeName(uint8_t Code) {56switch (Code) {57default: llvm_unreachable("Invalid unwind code");58case UOP_PushNonVol: return "PUSH_NONVOL";59case UOP_AllocLarge: return "ALLOC_LARGE";60case UOP_AllocSmall: return "ALLOC_SMALL";61case UOP_SetFPReg: return "SET_FPREG";62case UOP_SaveNonVol: return "SAVE_NONVOL";63case UOP_SaveNonVolBig: return "SAVE_NONVOL_FAR";64case UOP_SaveXMM128: return "SAVE_XMM128";65case UOP_SaveXMM128Big: return "SAVE_XMM128_FAR";66case UOP_PushMachFrame: return "PUSH_MACHFRAME";67}68}6970// Returns the name of a referenced register.71static StringRef getUnwindRegisterName(uint8_t Reg) {72switch (Reg) {73default: llvm_unreachable("Invalid register");74case 0: return "RAX";75case 1: return "RCX";76case 2: return "RDX";77case 3: return "RBX";78case 4: return "RSP";79case 5: return "RBP";80case 6: return "RSI";81case 7: return "RDI";82case 8: return "R8";83case 9: return "R9";84case 10: return "R10";85case 11: return "R11";86case 12: return "R12";87case 13: return "R13";88case 14: return "R14";89case 15: return "R15";90}91}9293// Calculates the number of array slots required for the unwind code.94static unsigned getNumUsedSlots(const UnwindCode &UnwindCode) {95switch (UnwindCode.getUnwindOp()) {96default: llvm_unreachable("Invalid unwind code");97case UOP_PushNonVol:98case UOP_AllocSmall:99case UOP_SetFPReg:100case UOP_PushMachFrame:101return 1;102case UOP_SaveNonVol:103case UOP_SaveXMM128:104return 2;105case UOP_SaveNonVolBig:106case UOP_SaveXMM128Big:107return 3;108case UOP_AllocLarge:109return (UnwindCode.getOpInfo() == 0) ? 2 : 3;110}111}112113static std::error_code getSymbol(const COFFObjectFile &COFF, uint64_t VA,114object::SymbolRef &Sym) {115for (const auto &Symbol : COFF.symbols()) {116Expected<uint64_t> Address = Symbol.getAddress();117if (!Address)118return errorToErrorCode(Address.takeError());119if (*Address == VA) {120Sym = Symbol;121return std::error_code();122}123}124return inconvertibleErrorCode();125}126127static object::SymbolRef getPreferredSymbol(const COFFObjectFile &COFF,128object::SymbolRef Sym,129uint32_t &SymbolOffset,130bool IsRangeEnd) {131// The symbol resolved by ResolveSymbol can be any internal132// nondescriptive symbol; try to resolve a more descriptive one.133COFFSymbolRef CoffSym = COFF.getCOFFSymbol(Sym);134if (CoffSym.getStorageClass() != COFF::IMAGE_SYM_CLASS_LABEL &&135CoffSym.getSectionDefinition() == nullptr)136return Sym;137for (const auto &S : COFF.symbols()) {138COFFSymbolRef CS = COFF.getCOFFSymbol(S);139if (CS.getSectionNumber() == CoffSym.getSectionNumber() &&140CS.getValue() <= CoffSym.getValue() + SymbolOffset &&141CS.getStorageClass() != COFF::IMAGE_SYM_CLASS_LABEL &&142CS.getSectionDefinition() == nullptr) {143uint32_t Offset = CoffSym.getValue() + SymbolOffset - CS.getValue();144// For the end of a range, don't pick a symbol with a zero offset;145// prefer a symbol with a small positive offset.146if (Offset <= SymbolOffset && (!IsRangeEnd || Offset > 0)) {147SymbolOffset = Offset;148Sym = S;149CoffSym = CS;150if (CS.isExternal() && SymbolOffset == 0)151return Sym;152}153}154}155return Sym;156}157158static std::string formatSymbol(const Dumper::Context &Ctx,159const coff_section *Section, uint64_t Offset,160uint32_t Displacement,161bool IsRangeEnd = false) {162std::string Buffer;163raw_string_ostream OS(Buffer);164165SymbolRef Symbol;166if (!Ctx.ResolveSymbol(Section, Offset, Symbol, Ctx.UserData)) {167// We found a relocation at the given offset in the section, pointing168// at a symbol.169170// Try to resolve label/section symbols into function names.171Symbol = getPreferredSymbol(Ctx.COFF, Symbol, Displacement, IsRangeEnd);172173Expected<StringRef> Name = Symbol.getName();174if (Name) {175OS << *Name;176if (Displacement > 0)177OS << format(" +0x%X (0x%" PRIX64 ")", Displacement, Offset);178else179OS << format(" (0x%" PRIX64 ")", Offset);180return OS.str();181} else {182// TODO: Actually report errors helpfully.183consumeError(Name.takeError());184}185} else if (!getSymbol(Ctx.COFF, Ctx.COFF.getImageBase() + Displacement,186Symbol)) {187Expected<StringRef> Name = Symbol.getName();188if (Name) {189OS << *Name;190OS << format(" (0x%" PRIX64 ")", Ctx.COFF.getImageBase() + Displacement);191return OS.str();192} else {193consumeError(Name.takeError());194}195}196197if (Displacement > 0)198OS << format("(0x%" PRIX64 ")", Ctx.COFF.getImageBase() + Displacement);199else200OS << format("(0x%" PRIX64 ")", Offset);201return OS.str();202}203204static std::error_code resolveRelocation(const Dumper::Context &Ctx,205const coff_section *Section,206uint64_t Offset,207const coff_section *&ResolvedSection,208uint64_t &ResolvedAddress) {209SymbolRef Symbol;210if (std::error_code EC =211Ctx.ResolveSymbol(Section, Offset, Symbol, Ctx.UserData))212return EC;213214Expected<uint64_t> ResolvedAddressOrErr = Symbol.getAddress();215if (!ResolvedAddressOrErr)216return errorToErrorCode(ResolvedAddressOrErr.takeError());217ResolvedAddress = *ResolvedAddressOrErr;218219Expected<section_iterator> SI = Symbol.getSection();220if (!SI)221return errorToErrorCode(SI.takeError());222ResolvedSection = Ctx.COFF.getCOFFSection(**SI);223return std::error_code();224}225226static const object::coff_section *227getSectionContaining(const COFFObjectFile &COFF, uint64_t VA) {228for (const auto &Section : COFF.sections()) {229uint64_t Address = Section.getAddress();230uint64_t Size = Section.getSize();231232if (VA >= Address && (VA - Address) <= Size)233return COFF.getCOFFSection(Section);234}235return nullptr;236}237238namespace llvm {239namespace Win64EH {240void Dumper::printRuntimeFunctionEntry(const Context &Ctx,241const coff_section *Section,242uint64_t Offset,243const RuntimeFunction &RF) {244SW.printString("StartAddress",245formatSymbol(Ctx, Section, Offset + 0, RF.StartAddress));246SW.printString("EndAddress",247formatSymbol(Ctx, Section, Offset + 4, RF.EndAddress,248/*IsRangeEnd=*/true));249SW.printString("UnwindInfoAddress",250formatSymbol(Ctx, Section, Offset + 8, RF.UnwindInfoOffset));251}252253// Prints one unwind code. Because an unwind code can occupy up to 3 slots in254// the unwind codes array, this function requires that the correct number of255// slots is provided.256void Dumper::printUnwindCode(const UnwindInfo& UI, ArrayRef<UnwindCode> UC) {257assert(UC.size() >= getNumUsedSlots(UC[0]));258259SW.startLine() << format("0x%02X: ", unsigned(UC[0].u.CodeOffset))260<< getUnwindCodeTypeName(UC[0].getUnwindOp());261262switch (UC[0].getUnwindOp()) {263case UOP_PushNonVol:264OS << " reg=" << getUnwindRegisterName(UC[0].getOpInfo());265break;266267case UOP_AllocLarge:268OS << " size="269<< ((UC[0].getOpInfo() == 0) ? UC[1].FrameOffset * 8270: getLargeSlotValue(UC));271break;272273case UOP_AllocSmall:274OS << " size=" << (UC[0].getOpInfo() + 1) * 8;275break;276277case UOP_SetFPReg:278if (UI.getFrameRegister() == 0)279OS << " reg=<invalid>";280else281OS << " reg=" << getUnwindRegisterName(UI.getFrameRegister())282<< format(", offset=0x%X", UI.getFrameOffset() * 16);283break;284285case UOP_SaveNonVol:286OS << " reg=" << getUnwindRegisterName(UC[0].getOpInfo())287<< format(", offset=0x%X", UC[1].FrameOffset * 8);288break;289290case UOP_SaveNonVolBig:291OS << " reg=" << getUnwindRegisterName(UC[0].getOpInfo())292<< format(", offset=0x%X", getLargeSlotValue(UC));293break;294295case UOP_SaveXMM128:296OS << " reg=XMM" << static_cast<uint32_t>(UC[0].getOpInfo())297<< format(", offset=0x%X", UC[1].FrameOffset * 16);298break;299300case UOP_SaveXMM128Big:301OS << " reg=XMM" << static_cast<uint32_t>(UC[0].getOpInfo())302<< format(", offset=0x%X", getLargeSlotValue(UC));303break;304305case UOP_PushMachFrame:306OS << " errcode=" << (UC[0].getOpInfo() == 0 ? "no" : "yes");307break;308}309310OS << "\n";311}312313void Dumper::printUnwindInfo(const Context &Ctx, const coff_section *Section,314off_t Offset, const UnwindInfo &UI) {315DictScope UIS(SW, "UnwindInfo");316SW.printNumber("Version", UI.getVersion());317SW.printFlags("Flags", UI.getFlags(), ArrayRef(UnwindFlags));318SW.printNumber("PrologSize", UI.PrologSize);319if (UI.getFrameRegister()) {320SW.printEnum("FrameRegister", UI.getFrameRegister(),321ArrayRef(UnwindOpInfo));322SW.printHex("FrameOffset", UI.getFrameOffset());323} else {324SW.printString("FrameRegister", StringRef("-"));325SW.printString("FrameOffset", StringRef("-"));326}327328SW.printNumber("UnwindCodeCount", UI.NumCodes);329{330ListScope UCS(SW, "UnwindCodes");331ArrayRef<UnwindCode> UC(&UI.UnwindCodes[0], UI.NumCodes);332for (const UnwindCode *UCI = UC.begin(), *UCE = UC.end(); UCI < UCE; ++UCI) {333unsigned UsedSlots = getNumUsedSlots(*UCI);334if (UsedSlots > UC.size()) {335errs() << "corrupt unwind data";336return;337}338339printUnwindCode(UI, ArrayRef(UCI, UCE));340UCI = UCI + UsedSlots - 1;341}342}343344uint64_t LSDAOffset = Offset + getOffsetOfLSDA(UI);345if (UI.getFlags() & (UNW_ExceptionHandler | UNW_TerminateHandler)) {346SW.printString("Handler",347formatSymbol(Ctx, Section, LSDAOffset,348UI.getLanguageSpecificHandlerOffset()));349} else if (UI.getFlags() & UNW_ChainInfo) {350if (const RuntimeFunction *Chained = UI.getChainedFunctionEntry()) {351DictScope CS(SW, "Chained");352printRuntimeFunctionEntry(Ctx, Section, LSDAOffset, *Chained);353}354}355}356357void Dumper::printRuntimeFunction(const Context &Ctx,358const coff_section *Section,359uint64_t SectionOffset,360const RuntimeFunction &RF) {361DictScope RFS(SW, "RuntimeFunction");362printRuntimeFunctionEntry(Ctx, Section, SectionOffset, RF);363364const coff_section *XData = nullptr;365uint64_t Offset;366resolveRelocation(Ctx, Section, SectionOffset + 8, XData, Offset);367Offset = Offset + RF.UnwindInfoOffset;368369if (!XData) {370uint64_t Address = Ctx.COFF.getImageBase() + RF.UnwindInfoOffset;371XData = getSectionContaining(Ctx.COFF, Address);372if (!XData)373return;374Offset = RF.UnwindInfoOffset - XData->VirtualAddress;375}376377ArrayRef<uint8_t> Contents;378if (Error E = Ctx.COFF.getSectionContents(XData, Contents))379reportError(std::move(E), Ctx.COFF.getFileName());380381if (Contents.empty())382return;383384if (Offset > Contents.size())385return;386387const auto UI = reinterpret_cast<const UnwindInfo*>(Contents.data() + Offset);388printUnwindInfo(Ctx, XData, Offset, *UI);389}390391void Dumper::printData(const Context &Ctx) {392for (const auto &Section : Ctx.COFF.sections()) {393StringRef Name;394if (Expected<StringRef> NameOrErr = Section.getName())395Name = *NameOrErr;396else397consumeError(NameOrErr.takeError());398399if (Name != ".pdata" && !Name.starts_with(".pdata$"))400continue;401402const coff_section *PData = Ctx.COFF.getCOFFSection(Section);403ArrayRef<uint8_t> Contents;404405if (Error E = Ctx.COFF.getSectionContents(PData, Contents))406reportError(std::move(E), Ctx.COFF.getFileName());407if (Contents.empty())408continue;409410const RuntimeFunction *Entries =411reinterpret_cast<const RuntimeFunction *>(Contents.data());412const size_t Count = Contents.size() / sizeof(RuntimeFunction);413ArrayRef<RuntimeFunction> RuntimeFunctions(Entries, Count);414415size_t Index = 0;416for (const auto &RF : RuntimeFunctions) {417printRuntimeFunction(Ctx, Ctx.COFF.getCOFFSection(Section),418Index * sizeof(RuntimeFunction), RF);419++Index;420}421}422}423}424}425426427428