1//===-- XCOFFDumper.cpp - XCOFF dumping utility -----------------*- 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-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file implements an XCOFF specific dumper for llvm-readobj.
10//
11//===----------------------------------------------------------------------===//
12
13#include "ObjDumper.h"
14#include "llvm-readobj.h"
15#include "llvm/Object/XCOFFObjectFile.h"
16#include "llvm/Support/ScopedPrinter.h"
17
18using namespace llvm;
19using namespace object;
20
21namespace {
22
23class XCOFFDumper : public ObjDumper {
24
25public:
26  XCOFFDumper(const XCOFFObjectFile &Obj, ScopedPrinter &Writer)
27      : ObjDumper(Writer, Obj.getFileName()), Obj(Obj) {}
28
29  void printFileHeaders() override;
30  void printSectionHeaders() override;
31  void printRelocations() override;
32  void printSymbols() override;
33  void printDynamicSymbols() override;
34  void printUnwindInfo() override;
35  void printStackMap() const override;
36  void printNeededLibraries() override;
37
38private:
39  template <typename T> void printSectionHeaders(ArrayRef<T> Sections);
40  template <typename T> void printGenericSectionHeader(T &Sec) const;
41  template <typename T> void printOverflowSectionHeader(T &Sec) const;
42  void printFileAuxEnt(const XCOFFFileAuxEnt *AuxEntPtr);
43  void printCsectAuxEnt32(const XCOFFCsectAuxEnt32 *AuxEntPtr);
44  void printSectAuxEntForStat(const XCOFFSectAuxEntForStat *AuxEntPtr);
45  void printSymbol(const SymbolRef &);
46  void printRelocations(ArrayRef<XCOFFSectionHeader32> Sections);
47  const XCOFFObjectFile &Obj;
48};
49} // anonymous namespace
50
51void XCOFFDumper::printFileHeaders() {
52  DictScope DS(W, "FileHeader");
53  W.printHex("Magic", Obj.getMagic());
54  W.printNumber("NumberOfSections", Obj.getNumberOfSections());
55
56  // Negative timestamp values are reserved for future use.
57  int32_t TimeStamp = Obj.getTimeStamp();
58  if (TimeStamp > 0) {
59    // This handling of the time stamp assumes that the host system's time_t is
60    // compatible with AIX time_t. If a platform is not compatible, the lit
61    // tests will let us know.
62    time_t TimeDate = TimeStamp;
63
64    char FormattedTime[21] = {};
65    size_t BytesWritten =
66        strftime(FormattedTime, 21, "%Y-%m-%dT%H:%M:%SZ", gmtime(&TimeDate));
67    if (BytesWritten)
68      W.printHex("TimeStamp", FormattedTime, TimeStamp);
69    else
70      W.printHex("Timestamp", TimeStamp);
71  } else {
72    W.printHex("TimeStamp", TimeStamp == 0 ? "None" : "Reserved Value",
73               TimeStamp);
74  }
75
76  // The number of symbol table entries is an unsigned value in 64-bit objects
77  // and a signed value (with negative values being 'reserved') in 32-bit
78  // objects.
79  if (Obj.is64Bit()) {
80    W.printHex("SymbolTableOffset", Obj.getSymbolTableOffset64());
81    W.printNumber("SymbolTableEntries", Obj.getNumberOfSymbolTableEntries64());
82  } else {
83    W.printHex("SymbolTableOffset", Obj.getSymbolTableOffset32());
84    int32_t SymTabEntries = Obj.getRawNumberOfSymbolTableEntries32();
85    if (SymTabEntries >= 0)
86      W.printNumber("SymbolTableEntries", SymTabEntries);
87    else
88      W.printHex("SymbolTableEntries", "Reserved Value", SymTabEntries);
89  }
90
91  W.printHex("OptionalHeaderSize", Obj.getOptionalHeaderSize());
92  W.printHex("Flags", Obj.getFlags());
93
94  // TODO FIXME Add support for the auxiliary header (if any) once
95  // XCOFFObjectFile has the necessary support.
96}
97
98void XCOFFDumper::printSectionHeaders() {
99  if (Obj.is64Bit())
100    printSectionHeaders(Obj.sections64());
101  else
102    printSectionHeaders(Obj.sections32());
103}
104
105void XCOFFDumper::printRelocations() {
106  if (Obj.is64Bit())
107    llvm_unreachable("64-bit relocation output not implemented!");
108  else
109    printRelocations(Obj.sections32());
110}
111
112static const EnumEntry<XCOFF::RelocationType> RelocationTypeNameclass[] = {
113#define ECase(X)                                                               \
114  { #X, XCOFF::X }
115    ECase(R_POS),    ECase(R_RL),     ECase(R_RLA),    ECase(R_NEG),
116    ECase(R_REL),    ECase(R_TOC),    ECase(R_TRL),    ECase(R_TRLA),
117    ECase(R_GL),     ECase(R_TCL),    ECase(R_REF),    ECase(R_BA),
118    ECase(R_BR),     ECase(R_RBA),    ECase(R_RBR),    ECase(R_TLS),
119    ECase(R_TLS_IE), ECase(R_TLS_LD), ECase(R_TLS_LE), ECase(R_TLSM),
120    ECase(R_TLSML),  ECase(R_TOCU),   ECase(R_TOCL)
121#undef ECase
122};
123
124void XCOFFDumper::printRelocations(ArrayRef<XCOFFSectionHeader32> Sections) {
125  if (!opts::ExpandRelocs)
126    report_fatal_error("Unexpanded relocation output not implemented.");
127
128  ListScope LS(W, "Relocations");
129  uint16_t Index = 0;
130  for (const auto &Sec : Sections) {
131    ++Index;
132    // Only the .text, .data, .tdata, and STYP_DWARF sections have relocation.
133    if (Sec.Flags != XCOFF::STYP_TEXT && Sec.Flags != XCOFF::STYP_DATA &&
134        Sec.Flags != XCOFF::STYP_TDATA && Sec.Flags != XCOFF::STYP_DWARF)
135      continue;
136    auto Relocations = unwrapOrError(Obj.getFileName(), Obj.relocations(Sec));
137    if (Relocations.empty())
138      continue;
139
140    W.startLine() << "Section (index: " << Index << ") " << Sec.getName()
141                  << " {\n";
142    for (auto Reloc : Relocations) {
143      StringRef SymbolName = unwrapOrError(
144          Obj.getFileName(), Obj.getSymbolNameByIndex(Reloc.SymbolIndex));
145
146      DictScope RelocScope(W, "Relocation");
147      W.printHex("Virtual Address", Reloc.VirtualAddress);
148      W.printNumber("Symbol", SymbolName, Reloc.SymbolIndex);
149      W.printString("IsSigned", Reloc.isRelocationSigned() ? "Yes" : "No");
150      W.printNumber("FixupBitValue", Reloc.isFixupIndicated() ? 1 : 0);
151      W.printNumber("Length", Reloc.getRelocatedLength());
152      W.printEnum("Type", (uint8_t)Reloc.Type,
153                  makeArrayRef(RelocationTypeNameclass));
154    }
155    W.unindent();
156    W.startLine() << "}\n";
157  }
158}
159
160static const EnumEntry<XCOFF::CFileStringType> FileStringType[] = {
161#define ECase(X)                                                               \
162  { #X, XCOFF::X }
163    ECase(XFT_FN), ECase(XFT_CT), ECase(XFT_CV), ECase(XFT_CD)
164#undef ECase
165};
166
167void XCOFFDumper::printFileAuxEnt(const XCOFFFileAuxEnt *AuxEntPtr) {
168  if (Obj.is64Bit())
169    report_fatal_error(
170        "Printing for File Auxiliary Entry in 64-bit is unimplemented.");
171  StringRef FileName =
172      unwrapOrError(Obj.getFileName(), Obj.getCFileName(AuxEntPtr));
173  DictScope SymDs(W, "File Auxiliary Entry");
174  W.printNumber("Index",
175                Obj.getSymbolIndex(reinterpret_cast<uintptr_t>(AuxEntPtr)));
176  W.printString("Name", FileName);
177  W.printEnum("Type", static_cast<uint8_t>(AuxEntPtr->Type),
178              makeArrayRef(FileStringType));
179}
180
181static const EnumEntry<XCOFF::StorageMappingClass> CsectStorageMappingClass[] =
182    {
183#define ECase(X)                                                               \
184  { #X, XCOFF::X }
185        ECase(XMC_PR), ECase(XMC_RO), ECase(XMC_DB),   ECase(XMC_GL),
186        ECase(XMC_XO), ECase(XMC_SV), ECase(XMC_SV64), ECase(XMC_SV3264),
187        ECase(XMC_TI), ECase(XMC_TB), ECase(XMC_RW),   ECase(XMC_TC0),
188        ECase(XMC_TC), ECase(XMC_TD), ECase(XMC_DS),   ECase(XMC_UA),
189        ECase(XMC_BS), ECase(XMC_UC), ECase(XMC_TL),   ECase(XMC_UL),
190        ECase(XMC_TE)
191#undef ECase
192};
193
194static const EnumEntry<XCOFF::SymbolType> CsectSymbolTypeClass[] = {
195#define ECase(X)                                                               \
196  { #X, XCOFF::X }
197    ECase(XTY_ER), ECase(XTY_SD), ECase(XTY_LD), ECase(XTY_CM)
198#undef ECase
199};
200
201void XCOFFDumper::printCsectAuxEnt32(const XCOFFCsectAuxEnt32 *AuxEntPtr) {
202  assert(!Obj.is64Bit() && "32-bit interface called on 64-bit object file.");
203
204  DictScope SymDs(W, "CSECT Auxiliary Entry");
205  W.printNumber("Index",
206                Obj.getSymbolIndex(reinterpret_cast<uintptr_t>(AuxEntPtr)));
207  if (AuxEntPtr->isLabel())
208    W.printNumber("ContainingCsectSymbolIndex", AuxEntPtr->SectionOrLength);
209  else
210    W.printNumber("SectionLen", AuxEntPtr->SectionOrLength);
211  W.printHex("ParameterHashIndex", AuxEntPtr->ParameterHashIndex);
212  W.printHex("TypeChkSectNum", AuxEntPtr->TypeChkSectNum);
213  // Print out symbol alignment and type.
214  W.printNumber("SymbolAlignmentLog2", AuxEntPtr->getAlignmentLog2());
215  W.printEnum("SymbolType", AuxEntPtr->getSymbolType(),
216              makeArrayRef(CsectSymbolTypeClass));
217  W.printEnum("StorageMappingClass",
218              static_cast<uint8_t>(AuxEntPtr->StorageMappingClass),
219              makeArrayRef(CsectStorageMappingClass));
220  W.printHex("StabInfoIndex", AuxEntPtr->StabInfoIndex);
221  W.printHex("StabSectNum", AuxEntPtr->StabSectNum);
222}
223
224void XCOFFDumper::printSectAuxEntForStat(
225    const XCOFFSectAuxEntForStat *AuxEntPtr) {
226  assert(!Obj.is64Bit() && "32-bit interface called on 64-bit object file.");
227
228  DictScope SymDs(W, "Sect Auxiliary Entry For Stat");
229  W.printNumber("Index",
230                Obj.getSymbolIndex(reinterpret_cast<uintptr_t>(AuxEntPtr)));
231  W.printNumber("SectionLength", AuxEntPtr->SectionLength);
232
233  // Unlike the corresponding fields in the section header, NumberOfRelocEnt
234  // and NumberOfLineNum do not handle values greater than 65535.
235  W.printNumber("NumberOfRelocEnt", AuxEntPtr->NumberOfRelocEnt);
236  W.printNumber("NumberOfLineNum", AuxEntPtr->NumberOfLineNum);
237}
238
239static const EnumEntry<XCOFF::StorageClass> SymStorageClass[] = {
240#define ECase(X)                                                               \
241  { #X, XCOFF::X }
242    ECase(C_NULL),  ECase(C_AUTO),    ECase(C_EXT),     ECase(C_STAT),
243    ECase(C_REG),   ECase(C_EXTDEF),  ECase(C_LABEL),   ECase(C_ULABEL),
244    ECase(C_MOS),   ECase(C_ARG),     ECase(C_STRTAG),  ECase(C_MOU),
245    ECase(C_UNTAG), ECase(C_TPDEF),   ECase(C_USTATIC), ECase(C_ENTAG),
246    ECase(C_MOE),   ECase(C_REGPARM), ECase(C_FIELD),   ECase(C_BLOCK),
247    ECase(C_FCN),   ECase(C_EOS),     ECase(C_FILE),    ECase(C_LINE),
248    ECase(C_ALIAS), ECase(C_HIDDEN),  ECase(C_HIDEXT),  ECase(C_BINCL),
249    ECase(C_EINCL), ECase(C_INFO),    ECase(C_WEAKEXT), ECase(C_DWARF),
250    ECase(C_GSYM),  ECase(C_LSYM),    ECase(C_PSYM),    ECase(C_RSYM),
251    ECase(C_RPSYM), ECase(C_STSYM),   ECase(C_TCSYM),   ECase(C_BCOMM),
252    ECase(C_ECOML), ECase(C_ECOMM),   ECase(C_DECL),    ECase(C_ENTRY),
253    ECase(C_FUN),   ECase(C_BSTAT),   ECase(C_ESTAT),   ECase(C_GTLS),
254    ECase(C_STTLS), ECase(C_EFCN)
255#undef ECase
256};
257
258static StringRef GetSymbolValueName(XCOFF::StorageClass SC) {
259  switch (SC) {
260  case XCOFF::C_EXT:
261  case XCOFF::C_WEAKEXT:
262  case XCOFF::C_HIDEXT:
263  case XCOFF::C_STAT:
264    return "Value (RelocatableAddress)";
265  case XCOFF::C_FILE:
266    return "Value (SymbolTableIndex)";
267  case XCOFF::C_FCN:
268  case XCOFF::C_BLOCK:
269  case XCOFF::C_FUN:
270  case XCOFF::C_STSYM:
271  case XCOFF::C_BINCL:
272  case XCOFF::C_EINCL:
273  case XCOFF::C_INFO:
274  case XCOFF::C_BSTAT:
275  case XCOFF::C_LSYM:
276  case XCOFF::C_PSYM:
277  case XCOFF::C_RPSYM:
278  case XCOFF::C_RSYM:
279  case XCOFF::C_ECOML:
280  case XCOFF::C_DWARF:
281    assert(false && "This StorageClass for the symbol is not yet implemented.");
282    return "";
283  default:
284    return "Value";
285  }
286}
287
288static const EnumEntry<XCOFF::CFileLangId> CFileLangIdClass[] = {
289#define ECase(X)                                                               \
290  { #X, XCOFF::X }
291    ECase(TB_C), ECase(TB_CPLUSPLUS)
292#undef ECase
293};
294
295static const EnumEntry<XCOFF::CFileCpuId> CFileCpuIdClass[] = {
296#define ECase(X)                                                               \
297  { #X, XCOFF::X }
298    ECase(TCPU_PPC64), ECase(TCPU_COM), ECase(TCPU_970)
299#undef ECase
300};
301
302void XCOFFDumper::printSymbol(const SymbolRef &S) {
303  if (Obj.is64Bit())
304    report_fatal_error("64-bit support is unimplemented.");
305
306  DataRefImpl SymbolDRI = S.getRawDataRefImpl();
307  const XCOFFSymbolEntry *SymbolEntPtr = Obj.toSymbolEntry(SymbolDRI);
308
309  XCOFFSymbolRef XCOFFSymRef(SymbolDRI, &Obj);
310  uint8_t NumberOfAuxEntries = XCOFFSymRef.getNumberOfAuxEntries();
311
312  DictScope SymDs(W, "Symbol");
313
314  StringRef SymbolName =
315      unwrapOrError(Obj.getFileName(), Obj.getSymbolName(SymbolDRI));
316
317  W.printNumber("Index",
318                Obj.getSymbolIndex(reinterpret_cast<uintptr_t>(SymbolEntPtr)));
319  W.printString("Name", SymbolName);
320  W.printHex(GetSymbolValueName(SymbolEntPtr->StorageClass),
321             SymbolEntPtr->Value);
322
323  StringRef SectionName =
324      unwrapOrError(Obj.getFileName(), Obj.getSymbolSectionName(SymbolEntPtr));
325
326  W.printString("Section", SectionName);
327  if (XCOFFSymRef.getStorageClass() == XCOFF::C_FILE) {
328    W.printEnum("Source Language ID",
329                SymbolEntPtr->CFileLanguageIdAndTypeId.LanguageId,
330                makeArrayRef(CFileLangIdClass));
331    W.printEnum("CPU Version ID",
332                SymbolEntPtr->CFileLanguageIdAndTypeId.CpuTypeId,
333                makeArrayRef(CFileCpuIdClass));
334  } else
335    W.printHex("Type", SymbolEntPtr->SymbolType);
336
337  W.printEnum("StorageClass", static_cast<uint8_t>(SymbolEntPtr->StorageClass),
338              makeArrayRef(SymStorageClass));
339  W.printNumber("NumberOfAuxEntries", SymbolEntPtr->NumberOfAuxEntries);
340
341  if (NumberOfAuxEntries == 0)
342    return;
343
344  switch (XCOFFSymRef.getStorageClass()) {
345  case XCOFF::C_FILE:
346    // If the symbol is C_FILE and has auxiliary entries...
347    for (int i = 1; i <= NumberOfAuxEntries; i++) {
348      const XCOFFFileAuxEnt *FileAuxEntPtr =
349          reinterpret_cast<const XCOFFFileAuxEnt *>(SymbolEntPtr + i);
350#ifndef NDEBUG
351      Obj.checkSymbolEntryPointer(reinterpret_cast<uintptr_t>(FileAuxEntPtr));
352#endif
353      printFileAuxEnt(FileAuxEntPtr);
354    }
355    break;
356  case XCOFF::C_EXT:
357  case XCOFF::C_WEAKEXT:
358  case XCOFF::C_HIDEXT:
359    // If the symbol is for a function, and it has more than 1 auxiliary entry,
360    // then one of them must be function auxiliary entry which we do not
361    // support yet.
362    if (XCOFFSymRef.isFunction() && NumberOfAuxEntries >= 2)
363      report_fatal_error("Function auxiliary entry printing is unimplemented.");
364
365    // If there is more than 1 auxiliary entry, instead of printing out
366    // error information, print out the raw Auxiliary entry from 1st till
367    // the last - 1. The last one must be a CSECT Auxiliary Entry.
368    for (int i = 1; i < NumberOfAuxEntries; i++) {
369      W.startLine() << "!Unexpected raw auxiliary entry data:\n";
370      W.startLine() << format_bytes(
371          ArrayRef<uint8_t>(reinterpret_cast<const uint8_t *>(SymbolEntPtr + i),
372                            XCOFF::SymbolTableEntrySize));
373    }
374
375    // The symbol's last auxiliary entry is a CSECT Auxiliary Entry.
376    printCsectAuxEnt32(XCOFFSymRef.getXCOFFCsectAuxEnt32());
377    break;
378  case XCOFF::C_STAT:
379    if (NumberOfAuxEntries > 1)
380      report_fatal_error(
381          "C_STAT symbol should not have more than 1 auxiliary entry.");
382
383    const XCOFFSectAuxEntForStat *StatAuxEntPtr;
384    StatAuxEntPtr =
385        reinterpret_cast<const XCOFFSectAuxEntForStat *>(SymbolEntPtr + 1);
386#ifndef NDEBUG
387    Obj.checkSymbolEntryPointer(reinterpret_cast<uintptr_t>(StatAuxEntPtr));
388#endif
389    printSectAuxEntForStat(StatAuxEntPtr);
390    break;
391  case XCOFF::C_DWARF:
392  case XCOFF::C_BLOCK:
393  case XCOFF::C_FCN:
394    report_fatal_error("Symbol table entry printing for this storage class "
395                       "type is unimplemented.");
396    break;
397  default:
398    for (int i = 1; i <= NumberOfAuxEntries; i++) {
399      W.startLine() << "!Unexpected raw auxiliary entry data:\n";
400      W.startLine() << format_bytes(
401          ArrayRef<uint8_t>(reinterpret_cast<const uint8_t *>(SymbolEntPtr + i),
402                            XCOFF::SymbolTableEntrySize));
403    }
404    break;
405  }
406}
407
408void XCOFFDumper::printSymbols() {
409  ListScope Group(W, "Symbols");
410  for (const SymbolRef &S : Obj.symbols())
411    printSymbol(S);
412}
413
414void XCOFFDumper::printDynamicSymbols() {
415  llvm_unreachable("Unimplemented functionality for XCOFFDumper");
416}
417
418void XCOFFDumper::printUnwindInfo() {
419  llvm_unreachable("Unimplemented functionality for XCOFFDumper");
420}
421
422void XCOFFDumper::printStackMap() const {
423  llvm_unreachable("Unimplemented functionality for XCOFFDumper");
424}
425
426void XCOFFDumper::printNeededLibraries() {
427  llvm_unreachable("Unimplemented functionality for XCOFFDumper");
428}
429
430static const EnumEntry<XCOFF::SectionTypeFlags> SectionTypeFlagsNames[] = {
431#define ECase(X)                                                               \
432  { #X, XCOFF::X }
433    ECase(STYP_PAD),    ECase(STYP_DWARF), ECase(STYP_TEXT),
434    ECase(STYP_DATA),   ECase(STYP_BSS),   ECase(STYP_EXCEPT),
435    ECase(STYP_INFO),   ECase(STYP_TDATA), ECase(STYP_TBSS),
436    ECase(STYP_LOADER), ECase(STYP_DEBUG), ECase(STYP_TYPCHK),
437    ECase(STYP_OVRFLO)
438#undef ECase
439};
440
441template <typename T>
442void XCOFFDumper::printOverflowSectionHeader(T &Sec) const {
443  if (Obj.is64Bit()) {
444    reportWarning(make_error<StringError>("An 64-bit XCOFF object file may not "
445                                          "contain an overflow section header.",
446                                          object_error::parse_failed),
447                  Obj.getFileName());
448  }
449
450  W.printString("Name", Sec.getName());
451  W.printNumber("NumberOfRelocations", Sec.PhysicalAddress);
452  W.printNumber("NumberOfLineNumbers", Sec.VirtualAddress);
453  W.printHex("Size", Sec.SectionSize);
454  W.printHex("RawDataOffset", Sec.FileOffsetToRawData);
455  W.printHex("RelocationPointer", Sec.FileOffsetToRelocationInfo);
456  W.printHex("LineNumberPointer", Sec.FileOffsetToLineNumberInfo);
457  W.printNumber("IndexOfSectionOverflowed", Sec.NumberOfRelocations);
458  W.printNumber("IndexOfSectionOverflowed", Sec.NumberOfLineNumbers);
459}
460
461template <typename T>
462void XCOFFDumper::printGenericSectionHeader(T &Sec) const {
463  W.printString("Name", Sec.getName());
464  W.printHex("PhysicalAddress", Sec.PhysicalAddress);
465  W.printHex("VirtualAddress", Sec.VirtualAddress);
466  W.printHex("Size", Sec.SectionSize);
467  W.printHex("RawDataOffset", Sec.FileOffsetToRawData);
468  W.printHex("RelocationPointer", Sec.FileOffsetToRelocationInfo);
469  W.printHex("LineNumberPointer", Sec.FileOffsetToLineNumberInfo);
470  W.printNumber("NumberOfRelocations", Sec.NumberOfRelocations);
471  W.printNumber("NumberOfLineNumbers", Sec.NumberOfLineNumbers);
472}
473
474template <typename T>
475void XCOFFDumper::printSectionHeaders(ArrayRef<T> Sections) {
476  ListScope Group(W, "Sections");
477
478  uint16_t Index = 1;
479  for (const T &Sec : Sections) {
480    DictScope SecDS(W, "Section");
481
482    W.printNumber("Index", Index++);
483    uint16_t SectionType = Sec.getSectionType();
484    switch (SectionType) {
485    case XCOFF::STYP_OVRFLO:
486      printOverflowSectionHeader(Sec);
487      break;
488    case XCOFF::STYP_LOADER:
489    case XCOFF::STYP_EXCEPT:
490    case XCOFF::STYP_TYPCHK:
491      // TODO The interpretation of loader, exception and type check section
492      // headers are different from that of generic section headers. We will
493      // implement them later. We interpret them as generic section headers for
494      // now.
495    default:
496      printGenericSectionHeader(Sec);
497      break;
498    }
499    if (Sec.isReservedSectionType())
500      W.printHex("Flags", "Reserved", SectionType);
501    else
502      W.printEnum("Type", SectionType, makeArrayRef(SectionTypeFlagsNames));
503  }
504
505  if (opts::SectionRelocations)
506    report_fatal_error("Dumping section relocations is unimplemented");
507
508  if (opts::SectionSymbols)
509    report_fatal_error("Dumping symbols is unimplemented");
510
511  if (opts::SectionData)
512    report_fatal_error("Dumping section data is unimplemented");
513}
514
515namespace llvm {
516std::unique_ptr<ObjDumper>
517createXCOFFDumper(const object::XCOFFObjectFile &XObj, ScopedPrinter &Writer) {
518  return std::make_unique<XCOFFDumper>(XObj, Writer);
519}
520} // namespace llvm
521