]> Gitweb @ Texas Instruments - Open Source Git Repositories - git.TI.com/gitweb - opencl/llvm.git/blobdiff - tools/llvm-objdump/COFFDump.cpp
For llvm-objdump, hook up existing options to work when using -macho (the Mach-O...
[opencl/llvm.git] / tools / llvm-objdump / COFFDump.cpp
index 30faecbb19d22036cb768d71ef9d1e9a4c956f5d..4a20b91b71fb3cd6a2dd2dc715d3a351360eca61 100644 (file)
@@ -10,7 +10,7 @@
 /// \file
 /// \brief This file implements the COFF-specific dumper for llvm-objdump.
 /// It outputs the Win64 EH data structures as plain text.
-/// The encoding of the unwind codes is decribed in MSDN:
+/// The encoding of the unwind codes is described in MSDN:
 /// http://msdn.microsoft.com/en-us/library/ck9asaa9.aspx
 ///
 //===----------------------------------------------------------------------===//
 #include "llvm/Object/ObjectFile.h"
 #include "llvm/Support/Format.h"
 #include "llvm/Support/SourceMgr.h"
-#include "llvm/Support/raw_ostream.h"
-#include "llvm/Support/system_error.h"
 #include "llvm/Support/Win64EH.h"
+#include "llvm/Support/raw_ostream.h"
 #include <algorithm>
 #include <cstring>
+#include <system_error>
 
 using namespace llvm;
 using namespace object;
@@ -94,7 +94,7 @@ static unsigned getNumUsedSlots(const UnwindCode &UnwindCode) {
 // slots is provided.
 static void printUnwindCode(ArrayRef<UnwindCode> UCs) {
   assert(UCs.size() >= getNumUsedSlots(UCs[0]));
-  outs() <<  format("    0x%02x: ", unsigned(UCs[0].u.CodeOffset))
+  outs() <<  format("      0x%02x: ", unsigned(UCs[0].u.CodeOffset))
          << getUnwindCodeTypeName(UCs[0].getUnwindOp());
   switch (UCs[0].getUnwindOp()) {
   case UOP_PushNonVol:
@@ -157,60 +157,66 @@ static void printAllUnwindCodes(ArrayRef<UnwindCode> UCs) {
 }
 
 // Given a symbol sym this functions returns the address and section of it.
-static error_code resolveSectionAndAddress(const COFFObjectFile *Obj,
-                                           const SymbolRef &Sym,
-                                           const coff_section *&ResolvedSection,
-                                           uint64_t &ResolvedAddr) {
-  if (error_code ec = Sym.getAddress(ResolvedAddr)) return ec;
-  section_iterator iter(Obj->begin_sections());
-  if (error_code ec = Sym.getSection(iter)) return ec;
-  ResolvedSection = Obj->getCOFFSection(iter);
+static std::error_code
+resolveSectionAndAddress(const COFFObjectFile *Obj, const SymbolRef &Sym,
+                         const coff_section *&ResolvedSection,
+                         uint64_t &ResolvedAddr) {
+  if (std::error_code EC = Sym.getAddress(ResolvedAddr))
+    return EC;
+  section_iterator iter(Obj->section_begin());
+  if (std::error_code EC = Sym.getSection(iter))
+    return EC;
+  ResolvedSection = Obj->getCOFFSection(*iter);
   return object_error::success;
 }
 
 // Given a vector of relocations for a section and an offset into this section
 // the function returns the symbol used for the relocation at the offset.
-static error_code resolveSymbol(const std::vector<RelocationRef> &Rels,
-                                uint64_t Offset, SymbolRef &Sym) {
+static std::error_code resolveSymbol(const std::vector<RelocationRef> &Rels,
+                                     uint64_t Offset, SymbolRef &Sym) {
   for (std::vector<RelocationRef>::const_iterator I = Rels.begin(),
                                                   E = Rels.end();
                                                   I != E; ++I) {
     uint64_t Ofs;
-    if (error_code ec = I->getOffset(Ofs)) return ec;
+    if (std::error_code EC = I->getOffset(Ofs))
+      return EC;
     if (Ofs == Offset) {
-      if (error_code ec = I->getSymbol(Sym)) return ec;
-      break;
+      Sym = *I->getSymbol();
+      return object_error::success;
     }
   }
-  return object_error::success;
+  return object_error::parse_failed;
 }
 
 // Given a vector of relocations for a section and an offset into this section
 // the function resolves the symbol used for the relocation at the offset and
 // returns the section content and the address inside the content pointed to
 // by the symbol.
-static error_code getSectionContents(const COFFObjectFile *Obj,
-                                     const std::vector<RelocationRef> &Rels,
-                                     uint64_t Offset,
-                                     ArrayRef<uint8_t> &Contents,
-                                     uint64_t &Addr) {
+static std::error_code
+getSectionContents(const COFFObjectFile *Obj,
+                   const std::vector<RelocationRef> &Rels, uint64_t Offset,
+                   ArrayRef<uint8_t> &Contents, uint64_t &Addr) {
   SymbolRef Sym;
-  if (error_code ec = resolveSymbol(Rels, Offset, Sym)) return ec;
+  if (std::error_code EC = resolveSymbol(Rels, Offset, Sym))
+    return EC;
   const coff_section *Section;
-  if (error_code ec = resolveSectionAndAddress(Obj, Sym, Section, Addr))
-    return ec;
-  if (error_code ec = Obj->getSectionContents(Section, Contents)) return ec;
+  if (std::error_code EC = resolveSectionAndAddress(Obj, Sym, Section, Addr))
+    return EC;
+  if (std::error_code EC = Obj->getSectionContents(Section, Contents))
+    return EC;
   return object_error::success;
 }
 
 // Given a vector of relocations for a section and an offset into this section
 // the function returns the name of the symbol used for the relocation at the
 // offset.
-static error_code resolveSymbolName(const std::vector<RelocationRef> &Rels,
-                                    uint64_t Offset, StringRef &Name) {
+static std::error_code resolveSymbolName(const std::vector<RelocationRef> &Rels,
+                                         uint64_t Offset, StringRef &Name) {
   SymbolRef Sym;
-  if (error_code ec = resolveSymbol(Rels, Offset, Sym)) return ec;
-  if (error_code ec = Sym.getName(Name)) return ec;
+  if (std::error_code EC = resolveSymbol(Rels, Offset, Sym))
+    return EC;
+  if (std::error_code EC = Sym.getName(Name))
+    return EC;
   return object_error::success;
 }
 
@@ -218,138 +224,327 @@ static void printCOFFSymbolAddress(llvm::raw_ostream &Out,
                                    const std::vector<RelocationRef> &Rels,
                                    uint64_t Offset, uint32_t Disp) {
   StringRef Sym;
-  if (error_code ec = resolveSymbolName(Rels, Offset, Sym)) {
-    error(ec);
-    return ;
+  if (!resolveSymbolName(Rels, Offset, Sym)) {
+    Out << Sym;
+    if (Disp > 0)
+      Out << format(" + 0x%04x", Disp);
+  } else {
+    Out << format("0x%04x", Disp);
   }
-  Out << Sym;
-  if (Disp > 0)
-    Out << format(" + 0x%04x", Disp);
 }
 
-void llvm::printCOFFUnwindInfo(const COFFObjectFile *Obj) {
-  const coff_file_header *Header;
-  if (error(Obj->getHeader(Header))) return;
+static void
+printSEHTable(const COFFObjectFile *Obj, uint32_t TableVA, int Count) {
+  if (Count == 0)
+    return;
 
-  if (Header->Machine != COFF::IMAGE_FILE_MACHINE_AMD64) {
-    errs() << "Unsupported image machine type "
-              "(currently only AMD64 is supported).\n";
+  const pe32_header *PE32Header;
+  if (error(Obj->getPE32Header(PE32Header)))
     return;
-  }
+  uint32_t ImageBase = PE32Header->ImageBase;
+  uintptr_t IntPtr = 0;
+  if (error(Obj->getVaPtr(TableVA, IntPtr)))
+    return;
+  const support::ulittle32_t *P = (const support::ulittle32_t *)IntPtr;
+  outs() << "SEH Table:";
+  for (int I = 0; I < Count; ++I)
+    outs() << format(" 0x%x", P[I] + ImageBase);
+  outs() << "\n\n";
+}
 
-  const coff_section *Pdata = 0;
+static void printLoadConfiguration(const COFFObjectFile *Obj) {
+  // Skip if it's not executable.
+  const pe32_header *PE32Header;
+  if (error(Obj->getPE32Header(PE32Header)))
+    return;
+  if (!PE32Header)
+    return;
 
-  error_code ec;
-  for (section_iterator SI = Obj->begin_sections(),
-                        SE = Obj->end_sections();
-                        SI != SE; SI.increment(ec)) {
-    if (error(ec)) return;
+  // Currently only x86 is supported
+  if (Obj->getMachine() != COFF::IMAGE_FILE_MACHINE_I386)
+    return;
 
-    StringRef Name;
-    if (error(SI->getName(Name))) continue;
+  const data_directory *DataDir;
+  if (error(Obj->getDataDirectory(COFF::LOAD_CONFIG_TABLE, DataDir)))
+    return;
+  uintptr_t IntPtr = 0;
+  if (DataDir->RelativeVirtualAddress == 0)
+    return;
+  if (error(Obj->getRvaPtr(DataDir->RelativeVirtualAddress, IntPtr)))
+    return;
 
-    if (Name != ".pdata") continue;
+  auto *LoadConf = reinterpret_cast<const coff_load_configuration32 *>(IntPtr);
+  outs() << "Load configuration:"
+         << "\n  Timestamp: " << LoadConf->TimeDateStamp
+         << "\n  Major Version: " << LoadConf->MajorVersion
+         << "\n  Minor Version: " << LoadConf->MinorVersion
+         << "\n  GlobalFlags Clear: " << LoadConf->GlobalFlagsClear
+         << "\n  GlobalFlags Set: " << LoadConf->GlobalFlagsSet
+         << "\n  Critical Section Default Timeout: " << LoadConf->CriticalSectionDefaultTimeout
+         << "\n  Decommit Free Block Threshold: " << LoadConf->DeCommitFreeBlockThreshold
+         << "\n  Decommit Total Free Threshold: " << LoadConf->DeCommitTotalFreeThreshold
+         << "\n  Lock Prefix Table: " << LoadConf->LockPrefixTable
+         << "\n  Maximum Allocation Size: " << LoadConf->MaximumAllocationSize
+         << "\n  Virtual Memory Threshold: " << LoadConf->VirtualMemoryThreshold
+         << "\n  Process Affinity Mask: " << LoadConf->ProcessAffinityMask
+         << "\n  Process Heap Flags: " << LoadConf->ProcessHeapFlags
+         << "\n  CSD Version: " << LoadConf->CSDVersion
+         << "\n  Security Cookie: " << LoadConf->SecurityCookie
+         << "\n  SEH Table: " << LoadConf->SEHandlerTable
+         << "\n  SEH Count: " << LoadConf->SEHandlerCount
+         << "\n\n";
+  printSEHTable(Obj, LoadConf->SEHandlerTable, LoadConf->SEHandlerCount);
+  outs() << "\n";
+}
 
-    Pdata = Obj->getCOFFSection(SI);
-    std::vector<RelocationRef> Rels;
-    for (relocation_iterator RI = SI->begin_relocations(),
-                             RE = SI->end_relocations();
-                             RI != RE; RI.increment(ec)) {
-      if (error(ec)) break;
-      Rels.push_back(*RI);
+// Prints import tables. The import table is a table containing the list of
+// DLL name and symbol names which will be linked by the loader.
+static void printImportTables(const COFFObjectFile *Obj) {
+  import_directory_iterator I = Obj->import_directory_begin();
+  import_directory_iterator E = Obj->import_directory_end();
+  if (I == E)
+    return;
+  outs() << "The Import Tables:\n";
+  for (; I != E; I = ++I) {
+    const import_directory_table_entry *Dir;
+    StringRef Name;
+    if (I->getImportTableEntry(Dir)) return;
+    if (I->getName(Name)) return;
+
+    outs() << format("  lookup %08x time %08x fwd %08x name %08x addr %08x\n\n",
+                     static_cast<uint32_t>(Dir->ImportLookupTableRVA),
+                     static_cast<uint32_t>(Dir->TimeDateStamp),
+                     static_cast<uint32_t>(Dir->ForwarderChain),
+                     static_cast<uint32_t>(Dir->NameRVA),
+                     static_cast<uint32_t>(Dir->ImportAddressTableRVA));
+    outs() << "    DLL Name: " << Name << "\n";
+    outs() << "    Hint/Ord  Name\n";
+    const import_lookup_table_entry32 *entry;
+    if (I->getImportLookupEntry(entry))
+      return;
+    for (; entry->Data; ++entry) {
+      if (entry->isOrdinal()) {
+        outs() << format("      % 6d\n", entry->getOrdinal());
+        continue;
+      }
+      uint16_t Hint;
+      StringRef Name;
+      if (Obj->getHintName(entry->getHintNameRVA(), Hint, Name))
+        return;
+      outs() << format("      % 6d  ", Hint) << Name << "\n";
     }
+    outs() << "\n";
+  }
+}
+
+// Prints export tables. The export table is a table containing the list of
+// exported symbol from the DLL.
+static void printExportTable(const COFFObjectFile *Obj) {
+  outs() << "Export Table:\n";
+  export_directory_iterator I = Obj->export_directory_begin();
+  export_directory_iterator E = Obj->export_directory_end();
+  if (I == E)
+    return;
+  StringRef DllName;
+  uint32_t OrdinalBase;
+  if (I->getDllName(DllName))
+    return;
+  if (I->getOrdinalBase(OrdinalBase))
+    return;
+  outs() << " DLL name: " << DllName << "\n";
+  outs() << " Ordinal base: " << OrdinalBase << "\n";
+  outs() << " Ordinal      RVA  Name\n";
+  for (; I != E; I = ++I) {
+    uint32_t Ordinal;
+    if (I->getOrdinal(Ordinal))
+      return;
+    uint32_t RVA;
+    if (I->getExportRVA(RVA))
+      return;
+    outs() << format("    % 4d %# 8x", Ordinal, RVA);
+
+    StringRef Name;
+    if (I->getSymbolName(Name))
+      continue;
+    if (!Name.empty())
+      outs() << "  " << Name;
+    outs() << "\n";
+  }
+}
+
+// Given the COFF object file, this function returns the relocations for .pdata
+// and the pointer to "runtime function" structs.
+static bool getPDataSection(const COFFObjectFile *Obj,
+                            std::vector<RelocationRef> &Rels,
+                            const RuntimeFunction *&RFStart, int &NumRFs) {
+  for (const SectionRef &Section : Obj->sections()) {
+    StringRef Name;
+    if (error(Section.getName(Name)))
+      continue;
+    if (Name != ".pdata")
+      continue;
+
+    const coff_section *Pdata = Obj->getCOFFSection(Section);
+    for (const RelocationRef &Reloc : Section.relocations())
+      Rels.push_back(Reloc);
 
     // Sort relocations by address.
     std::sort(Rels.begin(), Rels.end(), RelocAddressLess);
 
     ArrayRef<uint8_t> Contents;
-    if (error(Obj->getSectionContents(Pdata, Contents))) continue;
-    if (Contents.empty()) continue;
+    if (error(Obj->getSectionContents(Pdata, Contents)))
+      continue;
+    if (Contents.empty())
+      continue;
+
+    RFStart = reinterpret_cast<const RuntimeFunction *>(Contents.data());
+    NumRFs = Contents.size() / sizeof(RuntimeFunction);
+    return true;
+  }
+  return false;
+}
 
-    ArrayRef<RuntimeFunction> RFs(
-                  reinterpret_cast<const RuntimeFunction *>(Contents.data()),
-                                  Contents.size() / sizeof(RuntimeFunction));
-    for (const RuntimeFunction *I = RFs.begin(), *E = RFs.end(); I < E; ++I) {
-      const uint64_t SectionOffset = std::distance(RFs.begin(), I)
-                                     * sizeof(RuntimeFunction);
+static void printWin64EHUnwindInfo(const Win64EH::UnwindInfo *UI) {
+  // The casts to int are required in order to output the value as number.
+  // Without the casts the value would be interpreted as char data (which
+  // results in garbage output).
+  outs() << "    Version: " << static_cast<int>(UI->getVersion()) << "\n";
+  outs() << "    Flags: " << static_cast<int>(UI->getFlags());
+  if (UI->getFlags()) {
+    if (UI->getFlags() & UNW_ExceptionHandler)
+      outs() << " UNW_ExceptionHandler";
+    if (UI->getFlags() & UNW_TerminateHandler)
+      outs() << " UNW_TerminateHandler";
+    if (UI->getFlags() & UNW_ChainInfo)
+      outs() << " UNW_ChainInfo";
+  }
+  outs() << "\n";
+  outs() << "    Size of prolog: " << static_cast<int>(UI->PrologSize) << "\n";
+  outs() << "    Number of Codes: " << static_cast<int>(UI->NumCodes) << "\n";
+  // Maybe this should move to output of UOP_SetFPReg?
+  if (UI->getFrameRegister()) {
+    outs() << "    Frame register: "
+           << getUnwindRegisterName(UI->getFrameRegister()) << "\n";
+    outs() << "    Frame offset: " << 16 * UI->getFrameOffset() << "\n";
+  } else {
+    outs() << "    No frame pointer used\n";
+  }
+  if (UI->getFlags() & (UNW_ExceptionHandler | UNW_TerminateHandler)) {
+    // FIXME: Output exception handler data
+  } else if (UI->getFlags() & UNW_ChainInfo) {
+    // FIXME: Output chained unwind info
+  }
+
+  if (UI->NumCodes)
+    outs() << "    Unwind Codes:\n";
 
-      outs() << "Function Table:\n";
+  printAllUnwindCodes(ArrayRef<UnwindCode>(&UI->UnwindCodes[0], UI->NumCodes));
 
-      outs() << "  Start Address: ";
-      printCOFFSymbolAddress(outs(), Rels, SectionOffset +
+  outs() << "\n";
+  outs().flush();
+}
+
+/// Prints out the given RuntimeFunction struct for x64, assuming that Obj is
+/// pointing to an executable file.
+static void printRuntimeFunction(const COFFObjectFile *Obj,
+                                 const RuntimeFunction &RF) {
+  if (!RF.StartAddress)
+    return;
+  outs() << "Function Table:\n"
+         << format("  Start Address: 0x%04x\n",
+                   static_cast<uint32_t>(RF.StartAddress))
+         << format("  End Address: 0x%04x\n",
+                   static_cast<uint32_t>(RF.EndAddress))
+         << format("  Unwind Info Address: 0x%04x\n",
+                   static_cast<uint32_t>(RF.UnwindInfoOffset));
+  uintptr_t addr;
+  if (Obj->getRvaPtr(RF.UnwindInfoOffset, addr))
+    return;
+  printWin64EHUnwindInfo(reinterpret_cast<const Win64EH::UnwindInfo *>(addr));
+}
+
+/// Prints out the given RuntimeFunction struct for x64, assuming that Obj is
+/// pointing to an object file. Unlike executable, fields in RuntimeFunction
+/// struct are filled with zeros, but instead there are relocations pointing to
+/// them so that the linker will fill targets' RVAs to the fields at link
+/// time. This function interprets the relocations to find the data to be used
+/// in the resulting executable.
+static void printRuntimeFunctionRels(const COFFObjectFile *Obj,
+                                     const RuntimeFunction &RF,
+                                     uint64_t SectionOffset,
+                                     const std::vector<RelocationRef> &Rels) {
+  outs() << "Function Table:\n";
+  outs() << "  Start Address: ";
+  printCOFFSymbolAddress(outs(), Rels,
+                         SectionOffset +
                              /*offsetof(RuntimeFunction, StartAddress)*/ 0,
-                             I->StartAddress);
-      outs() << "\n";
+                         RF.StartAddress);
+  outs() << "\n";
 
-      outs() << "  End Address: ";
-      printCOFFSymbolAddress(outs(), Rels, SectionOffset +
+  outs() << "  End Address: ";
+  printCOFFSymbolAddress(outs(), Rels,
+                         SectionOffset +
                              /*offsetof(RuntimeFunction, EndAddress)*/ 4,
-                             I->EndAddress);
-      outs() << "\n";
+                         RF.EndAddress);
+  outs() << "\n";
 
-      outs() << "  Unwind Info Address: ";
-      printCOFFSymbolAddress(outs(), Rels, SectionOffset +
+  outs() << "  Unwind Info Address: ";
+  printCOFFSymbolAddress(outs(), Rels,
+                         SectionOffset +
                              /*offsetof(RuntimeFunction, UnwindInfoOffset)*/ 8,
-                             I->UnwindInfoOffset);
-      outs() << "\n";
-
-      ArrayRef<uint8_t> XContents;
-      uint64_t UnwindInfoOffset = 0;
-      if (error(getSectionContents(Obj, Rels, SectionOffset +
-                              /*offsetof(RuntimeFunction, UnwindInfoOffset)*/ 8,
-                                   XContents, UnwindInfoOffset))) continue;
-      if (XContents.empty()) continue;
-
-      UnwindInfoOffset += I->UnwindInfoOffset;
-      if (UnwindInfoOffset > XContents.size()) continue;
-
-      const Win64EH::UnwindInfo *UI =
-                            reinterpret_cast<const Win64EH::UnwindInfo *>
-                              (XContents.data() + UnwindInfoOffset);
-
-      // The casts to int are required in order to output the value as number.
-      // Without the casts the value would be interpreted as char data (which
-      // results in garbage output).
-      outs() << "  Version: " << static_cast<int>(UI->getVersion()) << "\n";
-      outs() << "  Flags: " << static_cast<int>(UI->getFlags());
-      if (UI->getFlags()) {
-          if (UI->getFlags() & UNW_ExceptionHandler)
-            outs() << " UNW_ExceptionHandler";
-          if (UI->getFlags() & UNW_TerminateHandler)
-            outs() << " UNW_TerminateHandler";
-          if (UI->getFlags() & UNW_ChainInfo)
-            outs() << " UNW_ChainInfo";
-      }
-      outs() << "\n";
-      outs() << "  Size of prolog: "
-             << static_cast<int>(UI->PrologSize) << "\n";
-      outs() << "  Number of Codes: "
-             << static_cast<int>(UI->NumCodes) << "\n";
-      // Maybe this should move to output of UOP_SetFPReg?
-      if (UI->getFrameRegister()) {
-        outs() << "  Frame register: "
-                << getUnwindRegisterName(UI->getFrameRegister())
-                << "\n";
-        outs() << "  Frame offset: "
-                << 16 * UI->getFrameOffset()
-                << "\n";
-      } else {
-        outs() << "  No frame pointer used\n";
-      }
-      if (UI->getFlags() & (UNW_ExceptionHandler | UNW_TerminateHandler)) {
-        // FIXME: Output exception handler data
-      } else if (UI->getFlags() & UNW_ChainInfo) {
-        // FIXME: Output chained unwind info
-      }
+                         RF.UnwindInfoOffset);
+  outs() << "\n";
 
-      if (UI->NumCodes)
-        outs() << "  Unwind Codes:\n";
+  ArrayRef<uint8_t> XContents;
+  uint64_t UnwindInfoOffset = 0;
+  if (error(getSectionContents(
+          Obj, Rels, SectionOffset +
+                         /*offsetof(RuntimeFunction, UnwindInfoOffset)*/ 8,
+          XContents, UnwindInfoOffset)))
+    return;
+  if (XContents.empty())
+    return;
 
-      printAllUnwindCodes(ArrayRef<UnwindCode>(&UI->UnwindCodes[0],
-                          UI->NumCodes));
+  UnwindInfoOffset += RF.UnwindInfoOffset;
+  if (UnwindInfoOffset > XContents.size())
+    return;
 
-      outs() << "\n\n";
-      outs().flush();
-    }
+  auto *UI = reinterpret_cast<const Win64EH::UnwindInfo *>(XContents.data() +
+                                                           UnwindInfoOffset);
+  printWin64EHUnwindInfo(UI);
+}
+
+void llvm::printCOFFUnwindInfo(const COFFObjectFile *Obj) {
+  if (Obj->getMachine() != COFF::IMAGE_FILE_MACHINE_AMD64) {
+    errs() << "Unsupported image machine type "
+              "(currently only AMD64 is supported).\n";
+    return;
   }
+
+  std::vector<RelocationRef> Rels;
+  const RuntimeFunction *RFStart;
+  int NumRFs;
+  if (!getPDataSection(Obj, Rels, RFStart, NumRFs))
+    return;
+  ArrayRef<RuntimeFunction> RFs(RFStart, NumRFs);
+
+  bool IsExecutable = Rels.empty();
+  if (IsExecutable) {
+    for (const RuntimeFunction &RF : RFs)
+      printRuntimeFunction(Obj, RF);
+    return;
+  }
+
+  for (const RuntimeFunction &RF : RFs) {
+    uint64_t SectionOffset =
+        std::distance(RFs.begin(), &RF) * sizeof(RuntimeFunction);
+    printRuntimeFunctionRels(Obj, RF, SectionOffset, Rels);
+  }
+}
+
+void llvm::printCOFFFileHeader(const object::ObjectFile *Obj) {
+  const COFFObjectFile *file = dyn_cast<const COFFObjectFile>(Obj);
+  printLoadConfiguration(file);
+  printImportTables(file);
+  printExportTable(file);
 }