1249259Sdim//===-- COFFDump.cpp - COFF-specific dumper ---------------------*- C++ -*-===// 2249259Sdim// 3249259Sdim// The LLVM Compiler Infrastructure 4249259Sdim// 5249259Sdim// This file is distributed under the University of Illinois Open Source 6249259Sdim// License. See LICENSE.TXT for details. 7249259Sdim// 8249259Sdim//===----------------------------------------------------------------------===// 9249259Sdim/// 10249259Sdim/// \file 11249259Sdim/// \brief This file implements the COFF-specific dumper for llvm-objdump. 12249259Sdim/// It outputs the Win64 EH data structures as plain text. 13249259Sdim/// The encoding of the unwind codes is decribed in MSDN: 14249259Sdim/// http://msdn.microsoft.com/en-us/library/ck9asaa9.aspx 15249259Sdim/// 16249259Sdim//===----------------------------------------------------------------------===// 17249259Sdim 18249259Sdim#include "llvm-objdump.h" 19249259Sdim#include "llvm/Object/COFF.h" 20249259Sdim#include "llvm/Object/ObjectFile.h" 21249259Sdim#include "llvm/Support/Format.h" 22249259Sdim#include "llvm/Support/SourceMgr.h" 23249259Sdim#include "llvm/Support/Win64EH.h" 24249259Sdim#include "llvm/Support/raw_ostream.h" 25249259Sdim#include "llvm/Support/system_error.h" 26249259Sdim#include <algorithm> 27249259Sdim#include <cstring> 28249259Sdim 29249259Sdimusing namespace llvm; 30249259Sdimusing namespace object; 31249259Sdimusing namespace llvm::Win64EH; 32249259Sdim 33249259Sdim// Returns the name of the unwind code. 34249259Sdimstatic StringRef getUnwindCodeTypeName(uint8_t Code) { 35249259Sdim switch(Code) { 36249259Sdim default: llvm_unreachable("Invalid unwind code"); 37249259Sdim case UOP_PushNonVol: return "UOP_PushNonVol"; 38249259Sdim case UOP_AllocLarge: return "UOP_AllocLarge"; 39249259Sdim case UOP_AllocSmall: return "UOP_AllocSmall"; 40249259Sdim case UOP_SetFPReg: return "UOP_SetFPReg"; 41249259Sdim case UOP_SaveNonVol: return "UOP_SaveNonVol"; 42249259Sdim case UOP_SaveNonVolBig: return "UOP_SaveNonVolBig"; 43249259Sdim case UOP_SaveXMM128: return "UOP_SaveXMM128"; 44249259Sdim case UOP_SaveXMM128Big: return "UOP_SaveXMM128Big"; 45249259Sdim case UOP_PushMachFrame: return "UOP_PushMachFrame"; 46249259Sdim } 47249259Sdim} 48249259Sdim 49249259Sdim// Returns the name of a referenced register. 50249259Sdimstatic StringRef getUnwindRegisterName(uint8_t Reg) { 51249259Sdim switch(Reg) { 52249259Sdim default: llvm_unreachable("Invalid register"); 53249259Sdim case 0: return "RAX"; 54249259Sdim case 1: return "RCX"; 55249259Sdim case 2: return "RDX"; 56249259Sdim case 3: return "RBX"; 57249259Sdim case 4: return "RSP"; 58249259Sdim case 5: return "RBP"; 59249259Sdim case 6: return "RSI"; 60249259Sdim case 7: return "RDI"; 61249259Sdim case 8: return "R8"; 62249259Sdim case 9: return "R9"; 63249259Sdim case 10: return "R10"; 64249259Sdim case 11: return "R11"; 65249259Sdim case 12: return "R12"; 66249259Sdim case 13: return "R13"; 67249259Sdim case 14: return "R14"; 68249259Sdim case 15: return "R15"; 69249259Sdim } 70249259Sdim} 71249259Sdim 72249259Sdim// Calculates the number of array slots required for the unwind code. 73249259Sdimstatic unsigned getNumUsedSlots(const UnwindCode &UnwindCode) { 74249259Sdim switch (UnwindCode.getUnwindOp()) { 75249259Sdim default: llvm_unreachable("Invalid unwind code"); 76249259Sdim case UOP_PushNonVol: 77249259Sdim case UOP_AllocSmall: 78249259Sdim case UOP_SetFPReg: 79249259Sdim case UOP_PushMachFrame: 80249259Sdim return 1; 81249259Sdim case UOP_SaveNonVol: 82249259Sdim case UOP_SaveXMM128: 83249259Sdim return 2; 84249259Sdim case UOP_SaveNonVolBig: 85249259Sdim case UOP_SaveXMM128Big: 86249259Sdim return 3; 87249259Sdim case UOP_AllocLarge: 88249259Sdim return (UnwindCode.getOpInfo() == 0) ? 2 : 3; 89249259Sdim } 90249259Sdim} 91249259Sdim 92249259Sdim// Prints one unwind code. Because an unwind code can occupy up to 3 slots in 93249259Sdim// the unwind codes array, this function requires that the correct number of 94249259Sdim// slots is provided. 95249259Sdimstatic void printUnwindCode(ArrayRef<UnwindCode> UCs) { 96249259Sdim assert(UCs.size() >= getNumUsedSlots(UCs[0])); 97249259Sdim outs() << format(" 0x%02x: ", unsigned(UCs[0].u.CodeOffset)) 98249259Sdim << getUnwindCodeTypeName(UCs[0].getUnwindOp()); 99249259Sdim switch (UCs[0].getUnwindOp()) { 100249259Sdim case UOP_PushNonVol: 101249259Sdim outs() << " " << getUnwindRegisterName(UCs[0].getOpInfo()); 102249259Sdim break; 103249259Sdim case UOP_AllocLarge: 104249259Sdim if (UCs[0].getOpInfo() == 0) { 105249259Sdim outs() << " " << UCs[1].FrameOffset; 106249259Sdim } else { 107249259Sdim outs() << " " << UCs[1].FrameOffset 108249259Sdim + (static_cast<uint32_t>(UCs[2].FrameOffset) << 16); 109249259Sdim } 110249259Sdim break; 111249259Sdim case UOP_AllocSmall: 112249259Sdim outs() << " " << ((UCs[0].getOpInfo() + 1) * 8); 113249259Sdim break; 114249259Sdim case UOP_SetFPReg: 115249259Sdim outs() << " "; 116249259Sdim break; 117249259Sdim case UOP_SaveNonVol: 118249259Sdim outs() << " " << getUnwindRegisterName(UCs[0].getOpInfo()) 119249259Sdim << format(" [0x%04x]", 8 * UCs[1].FrameOffset); 120249259Sdim break; 121249259Sdim case UOP_SaveNonVolBig: 122249259Sdim outs() << " " << getUnwindRegisterName(UCs[0].getOpInfo()) 123249259Sdim << format(" [0x%08x]", UCs[1].FrameOffset 124249259Sdim + (static_cast<uint32_t>(UCs[2].FrameOffset) << 16)); 125249259Sdim break; 126249259Sdim case UOP_SaveXMM128: 127249259Sdim outs() << " XMM" << static_cast<uint32_t>(UCs[0].getOpInfo()) 128249259Sdim << format(" [0x%04x]", 16 * UCs[1].FrameOffset); 129249259Sdim break; 130249259Sdim case UOP_SaveXMM128Big: 131249259Sdim outs() << " XMM" << UCs[0].getOpInfo() 132249259Sdim << format(" [0x%08x]", UCs[1].FrameOffset 133249259Sdim + (static_cast<uint32_t>(UCs[2].FrameOffset) << 16)); 134249259Sdim break; 135249259Sdim case UOP_PushMachFrame: 136249259Sdim outs() << " " << (UCs[0].getOpInfo() ? "w/o" : "w") 137249259Sdim << " error code"; 138249259Sdim break; 139249259Sdim } 140249259Sdim outs() << "\n"; 141249259Sdim} 142249259Sdim 143249259Sdimstatic void printAllUnwindCodes(ArrayRef<UnwindCode> UCs) { 144249259Sdim for (const UnwindCode *I = UCs.begin(), *E = UCs.end(); I < E; ) { 145249259Sdim unsigned UsedSlots = getNumUsedSlots(*I); 146249259Sdim if (UsedSlots > UCs.size()) { 147249259Sdim outs() << "Unwind data corrupted: Encountered unwind op " 148249259Sdim << getUnwindCodeTypeName((*I).getUnwindOp()) 149249259Sdim << " which requires " << UsedSlots 150249259Sdim << " slots, but only " << UCs.size() 151249259Sdim << " remaining in buffer"; 152249259Sdim return ; 153249259Sdim } 154249259Sdim printUnwindCode(ArrayRef<UnwindCode>(I, E)); 155249259Sdim I += UsedSlots; 156249259Sdim } 157249259Sdim} 158249259Sdim 159249259Sdim// Given a symbol sym this functions returns the address and section of it. 160249259Sdimstatic error_code resolveSectionAndAddress(const COFFObjectFile *Obj, 161249259Sdim const SymbolRef &Sym, 162249259Sdim const coff_section *&ResolvedSection, 163249259Sdim uint64_t &ResolvedAddr) { 164249259Sdim if (error_code ec = Sym.getAddress(ResolvedAddr)) return ec; 165249259Sdim section_iterator iter(Obj->begin_sections()); 166249259Sdim if (error_code ec = Sym.getSection(iter)) return ec; 167249259Sdim ResolvedSection = Obj->getCOFFSection(iter); 168249259Sdim return object_error::success; 169249259Sdim} 170249259Sdim 171249259Sdim// Given a vector of relocations for a section and an offset into this section 172249259Sdim// the function returns the symbol used for the relocation at the offset. 173249259Sdimstatic error_code resolveSymbol(const std::vector<RelocationRef> &Rels, 174249259Sdim uint64_t Offset, SymbolRef &Sym) { 175249259Sdim for (std::vector<RelocationRef>::const_iterator I = Rels.begin(), 176249259Sdim E = Rels.end(); 177249259Sdim I != E; ++I) { 178249259Sdim uint64_t Ofs; 179249259Sdim if (error_code ec = I->getOffset(Ofs)) return ec; 180249259Sdim if (Ofs == Offset) { 181263508Sdim Sym = *I->getSymbol(); 182249259Sdim break; 183249259Sdim } 184249259Sdim } 185249259Sdim return object_error::success; 186249259Sdim} 187249259Sdim 188249259Sdim// Given a vector of relocations for a section and an offset into this section 189249259Sdim// the function resolves the symbol used for the relocation at the offset and 190249259Sdim// returns the section content and the address inside the content pointed to 191249259Sdim// by the symbol. 192249259Sdimstatic error_code getSectionContents(const COFFObjectFile *Obj, 193249259Sdim const std::vector<RelocationRef> &Rels, 194249259Sdim uint64_t Offset, 195249259Sdim ArrayRef<uint8_t> &Contents, 196249259Sdim uint64_t &Addr) { 197249259Sdim SymbolRef Sym; 198249259Sdim if (error_code ec = resolveSymbol(Rels, Offset, Sym)) return ec; 199249259Sdim const coff_section *Section; 200249259Sdim if (error_code ec = resolveSectionAndAddress(Obj, Sym, Section, Addr)) 201249259Sdim return ec; 202249259Sdim if (error_code ec = Obj->getSectionContents(Section, Contents)) return ec; 203249259Sdim return object_error::success; 204249259Sdim} 205249259Sdim 206249259Sdim// Given a vector of relocations for a section and an offset into this section 207249259Sdim// the function returns the name of the symbol used for the relocation at the 208249259Sdim// offset. 209249259Sdimstatic error_code resolveSymbolName(const std::vector<RelocationRef> &Rels, 210249259Sdim uint64_t Offset, StringRef &Name) { 211249259Sdim SymbolRef Sym; 212249259Sdim if (error_code ec = resolveSymbol(Rels, Offset, Sym)) return ec; 213249259Sdim if (error_code ec = Sym.getName(Name)) return ec; 214249259Sdim return object_error::success; 215249259Sdim} 216249259Sdim 217249259Sdimstatic void printCOFFSymbolAddress(llvm::raw_ostream &Out, 218249259Sdim const std::vector<RelocationRef> &Rels, 219249259Sdim uint64_t Offset, uint32_t Disp) { 220249259Sdim StringRef Sym; 221249259Sdim if (error_code ec = resolveSymbolName(Rels, Offset, Sym)) { 222249259Sdim error(ec); 223249259Sdim return ; 224249259Sdim } 225249259Sdim Out << Sym; 226249259Sdim if (Disp > 0) 227249259Sdim Out << format(" + 0x%04x", Disp); 228249259Sdim} 229249259Sdim 230263508Sdim// Prints import tables. The import table is a table containing the list of 231263508Sdim// DLL name and symbol names which will be linked by the loader. 232263508Sdimstatic void printImportTables(const COFFObjectFile *Obj) { 233263508Sdim outs() << "The Import Tables:\n"; 234263508Sdim error_code ec; 235263508Sdim for (import_directory_iterator i = Obj->import_directory_begin(), 236263508Sdim e = Obj->import_directory_end(); 237263508Sdim i != e; i = i.increment(ec)) { 238263508Sdim if (ec) 239263508Sdim return; 240263508Sdim 241263508Sdim const import_directory_table_entry *Dir; 242263508Sdim StringRef Name; 243263508Sdim if (i->getImportTableEntry(Dir)) return; 244263508Sdim if (i->getName(Name)) return; 245263508Sdim 246263508Sdim outs() << format(" lookup %08x time %08x fwd %08x name %08x addr %08x\n\n", 247263508Sdim static_cast<uint32_t>(Dir->ImportLookupTableRVA), 248263508Sdim static_cast<uint32_t>(Dir->TimeDateStamp), 249263508Sdim static_cast<uint32_t>(Dir->ForwarderChain), 250263508Sdim static_cast<uint32_t>(Dir->NameRVA), 251263508Sdim static_cast<uint32_t>(Dir->ImportAddressTableRVA)); 252263508Sdim outs() << " DLL Name: " << Name << "\n"; 253263508Sdim outs() << " Hint/Ord Name\n"; 254263508Sdim const import_lookup_table_entry32 *entry; 255263508Sdim if (i->getImportLookupEntry(entry)) 256263508Sdim return; 257263508Sdim for (; entry->data; ++entry) { 258263508Sdim if (entry->isOrdinal()) { 259263508Sdim outs() << format(" % 6d\n", entry->getOrdinal()); 260263508Sdim continue; 261263508Sdim } 262263508Sdim uint16_t Hint; 263263508Sdim StringRef Name; 264263508Sdim if (Obj->getHintName(entry->getHintNameRVA(), Hint, Name)) 265263508Sdim return; 266263508Sdim outs() << format(" % 6d ", Hint) << Name << "\n"; 267263508Sdim } 268263508Sdim outs() << "\n"; 269263508Sdim } 270263508Sdim} 271263508Sdim 272249259Sdimvoid llvm::printCOFFUnwindInfo(const COFFObjectFile *Obj) { 273249259Sdim const coff_file_header *Header; 274263508Sdim if (error(Obj->getCOFFHeader(Header))) return; 275249259Sdim 276249259Sdim if (Header->Machine != COFF::IMAGE_FILE_MACHINE_AMD64) { 277249259Sdim errs() << "Unsupported image machine type " 278249259Sdim "(currently only AMD64 is supported).\n"; 279249259Sdim return; 280249259Sdim } 281249259Sdim 282249259Sdim const coff_section *Pdata = 0; 283249259Sdim 284249259Sdim error_code ec; 285249259Sdim for (section_iterator SI = Obj->begin_sections(), 286249259Sdim SE = Obj->end_sections(); 287249259Sdim SI != SE; SI.increment(ec)) { 288249259Sdim if (error(ec)) return; 289249259Sdim 290249259Sdim StringRef Name; 291249259Sdim if (error(SI->getName(Name))) continue; 292249259Sdim 293249259Sdim if (Name != ".pdata") continue; 294249259Sdim 295249259Sdim Pdata = Obj->getCOFFSection(SI); 296249259Sdim std::vector<RelocationRef> Rels; 297249259Sdim for (relocation_iterator RI = SI->begin_relocations(), 298249259Sdim RE = SI->end_relocations(); 299249259Sdim RI != RE; RI.increment(ec)) { 300249259Sdim if (error(ec)) break; 301249259Sdim Rels.push_back(*RI); 302249259Sdim } 303249259Sdim 304249259Sdim // Sort relocations by address. 305249259Sdim std::sort(Rels.begin(), Rels.end(), RelocAddressLess); 306249259Sdim 307249259Sdim ArrayRef<uint8_t> Contents; 308249259Sdim if (error(Obj->getSectionContents(Pdata, Contents))) continue; 309249259Sdim if (Contents.empty()) continue; 310249259Sdim 311249259Sdim ArrayRef<RuntimeFunction> RFs( 312249259Sdim reinterpret_cast<const RuntimeFunction *>(Contents.data()), 313249259Sdim Contents.size() / sizeof(RuntimeFunction)); 314249259Sdim for (const RuntimeFunction *I = RFs.begin(), *E = RFs.end(); I < E; ++I) { 315249259Sdim const uint64_t SectionOffset = std::distance(RFs.begin(), I) 316249259Sdim * sizeof(RuntimeFunction); 317249259Sdim 318249259Sdim outs() << "Function Table:\n"; 319249259Sdim 320249259Sdim outs() << " Start Address: "; 321249259Sdim printCOFFSymbolAddress(outs(), Rels, SectionOffset + 322249259Sdim /*offsetof(RuntimeFunction, StartAddress)*/ 0, 323249259Sdim I->StartAddress); 324249259Sdim outs() << "\n"; 325249259Sdim 326249259Sdim outs() << " End Address: "; 327249259Sdim printCOFFSymbolAddress(outs(), Rels, SectionOffset + 328249259Sdim /*offsetof(RuntimeFunction, EndAddress)*/ 4, 329249259Sdim I->EndAddress); 330249259Sdim outs() << "\n"; 331249259Sdim 332249259Sdim outs() << " Unwind Info Address: "; 333249259Sdim printCOFFSymbolAddress(outs(), Rels, SectionOffset + 334249259Sdim /*offsetof(RuntimeFunction, UnwindInfoOffset)*/ 8, 335249259Sdim I->UnwindInfoOffset); 336249259Sdim outs() << "\n"; 337249259Sdim 338249259Sdim ArrayRef<uint8_t> XContents; 339249259Sdim uint64_t UnwindInfoOffset = 0; 340249259Sdim if (error(getSectionContents(Obj, Rels, SectionOffset + 341249259Sdim /*offsetof(RuntimeFunction, UnwindInfoOffset)*/ 8, 342249259Sdim XContents, UnwindInfoOffset))) continue; 343249259Sdim if (XContents.empty()) continue; 344249259Sdim 345249259Sdim UnwindInfoOffset += I->UnwindInfoOffset; 346249259Sdim if (UnwindInfoOffset > XContents.size()) continue; 347249259Sdim 348249259Sdim const Win64EH::UnwindInfo *UI = 349249259Sdim reinterpret_cast<const Win64EH::UnwindInfo *> 350249259Sdim (XContents.data() + UnwindInfoOffset); 351249259Sdim 352249259Sdim // The casts to int are required in order to output the value as number. 353249259Sdim // Without the casts the value would be interpreted as char data (which 354249259Sdim // results in garbage output). 355249259Sdim outs() << " Version: " << static_cast<int>(UI->getVersion()) << "\n"; 356249259Sdim outs() << " Flags: " << static_cast<int>(UI->getFlags()); 357249259Sdim if (UI->getFlags()) { 358249259Sdim if (UI->getFlags() & UNW_ExceptionHandler) 359249259Sdim outs() << " UNW_ExceptionHandler"; 360249259Sdim if (UI->getFlags() & UNW_TerminateHandler) 361249259Sdim outs() << " UNW_TerminateHandler"; 362249259Sdim if (UI->getFlags() & UNW_ChainInfo) 363249259Sdim outs() << " UNW_ChainInfo"; 364249259Sdim } 365249259Sdim outs() << "\n"; 366249259Sdim outs() << " Size of prolog: " 367249259Sdim << static_cast<int>(UI->PrologSize) << "\n"; 368249259Sdim outs() << " Number of Codes: " 369249259Sdim << static_cast<int>(UI->NumCodes) << "\n"; 370249259Sdim // Maybe this should move to output of UOP_SetFPReg? 371249259Sdim if (UI->getFrameRegister()) { 372249259Sdim outs() << " Frame register: " 373249259Sdim << getUnwindRegisterName(UI->getFrameRegister()) 374249259Sdim << "\n"; 375249259Sdim outs() << " Frame offset: " 376249259Sdim << 16 * UI->getFrameOffset() 377249259Sdim << "\n"; 378249259Sdim } else { 379249259Sdim outs() << " No frame pointer used\n"; 380249259Sdim } 381249259Sdim if (UI->getFlags() & (UNW_ExceptionHandler | UNW_TerminateHandler)) { 382249259Sdim // FIXME: Output exception handler data 383249259Sdim } else if (UI->getFlags() & UNW_ChainInfo) { 384249259Sdim // FIXME: Output chained unwind info 385249259Sdim } 386249259Sdim 387249259Sdim if (UI->NumCodes) 388249259Sdim outs() << " Unwind Codes:\n"; 389249259Sdim 390249259Sdim printAllUnwindCodes(ArrayRef<UnwindCode>(&UI->UnwindCodes[0], 391249259Sdim UI->NumCodes)); 392249259Sdim 393249259Sdim outs() << "\n\n"; 394249259Sdim outs().flush(); 395249259Sdim } 396249259Sdim } 397249259Sdim} 398263508Sdim 399263508Sdimvoid llvm::printCOFFFileHeader(const object::ObjectFile *Obj) { 400263508Sdim printImportTables(dyn_cast<const COFFObjectFile>(Obj)); 401263508Sdim} 402