brintos

brintos / llvm-project-archived public Read only

0
0
Text · 14.3 KiB · e17a035 Raw
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