[llvm-objdump] Teach `llvm-objdump` dump dynamic symbols.
authorvgxbj <higuoxing@gmail.com>
Sun, 5 Apr 2020 01:58:53 +0000 (09:58 +0800)
committervgxbj <higuoxing@gmail.com>
Sun, 5 Apr 2020 02:46:59 +0000 (10:46 +0800)
Summary:
This patch is to teach `llvm-objdump` dump dynamic symbols (`-T` and `--dynamic-syms`). Currently, this patch is not fully compatible with `gnu-objdump`, but I would like to continue working on this in next few patches. It has two issues.

1. Some symbols shouldn't be marked as global(g). (`-t/--syms` has same issue as well) (Fixed by D75659)
2. `gnu-objdump` can dump version information and *dynamically* insert before symbol name field.

`objdump -T a.out` gives:

```
DYNAMIC SYMBOL TABLE:
0000000000000000  w   D  *UND*  0000000000000000              _ITM_deregisterTMCloneTable
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 printf
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 __libc_start_main
0000000000000000  w   D  *UND*  0000000000000000              __gmon_start__
0000000000000000  w   D  *UND*  0000000000000000              _ITM_registerTMCloneTable
0000000000000000  w   DF *UND*  0000000000000000  GLIBC_2.2.5 __cxa_finalize
```

`llvm-objdump -T a.out` gives:

```
DYNAMIC SYMBOL TABLE:
0000000000000000  w   D  *UND*  0000000000000000 _ITM_deregisterTMCloneTable
0000000000000000 g    DF *UND*  0000000000000000 printf
0000000000000000 g    DF *UND*  0000000000000000 __libc_start_main
0000000000000000  w   D  *UND*  0000000000000000 __gmon_start__
0000000000000000  w   D  *UND*  0000000000000000 _ITM_registerTMCloneTable
0000000000000000  w   DF *UND*  0000000000000000 __cxa_finalize
```

Reviewers: jhenderson, grimar, MaskRay, espindola

Reviewed By: jhenderson, grimar

Subscribers: emaste, rupprecht, llvm-commits

Tags: #llvm

Differential Revision: https://reviews.llvm.org/D75756

llvm/docs/CommandGuide/llvm-objdump.rst
llvm/test/tools/llvm-objdump/X86/elf-dynamic-symbols.test [new file with mode: 0644]
llvm/test/tools/llvm-objdump/unimplemented-features.test [new file with mode: 0644]
llvm/tools/llvm-objdump/llvm-objdump.cpp
llvm/tools/llvm-objdump/llvm-objdump.h

index 04c7649..c236d2d 100644 (file)
@@ -85,6 +85,10 @@ combined with other commands:
 
   Display the symbol table.
 
+.. option:: -T, --dynamic-syms
+
+  Display the contents of the dynamic symbol table.
+
 .. option:: -u, --unwind-info
 
   Display the unwind info of the input(s).
diff --git a/llvm/test/tools/llvm-objdump/X86/elf-dynamic-symbols.test b/llvm/test/tools/llvm-objdump/X86/elf-dynamic-symbols.test
new file mode 100644 (file)
index 0000000..580ac85
--- /dev/null
@@ -0,0 +1,107 @@
+## Test that llvm-objdump can dump dynamic symbols.
+# RUN: yaml2obj --docnum=1 %s -o %t1
+# RUN: llvm-objdump --dynamic-syms %t1 | \
+# RUN:   FileCheck %s --match-full-lines --strict-whitespace --check-prefix=DYNSYM
+# RUN: llvm-objdump -T %t1 | \
+# RUN:   FileCheck %s --match-full-lines --strict-whitespace --check-prefix=DYNSYM
+
+#       DYNSYM:DYNAMIC SYMBOL TABLE:
+#  DYNSYM-NEXT:0000000000000000 l    DO .data  0000000000000000 localsym
+#  DYNSYM-NEXT:0000000000000000 g    DO .data  0000000000000000 globalsym
+#  DYNSYM-NEXT:0000000000000000 u    DO .data  0000000000000000 uniqueglobalsym
+#  DYNSYM-NEXT:0000000000000000  w   DO .data  0000000000000000 weaksym
+#  DYNSYM-NEXT:0000000000000000 g    Df .data  0000000000000000 filesym
+#  DYNSYM-NEXT:0000000000000000 g    DF .data  0000000000000000 funcsym
+# DYNSYM-EMPTY:
+
+--- !ELF
+FileHeader:
+  Class:   ELFCLASS64
+  Data:    ELFDATA2LSB
+  Type:    ET_DYN
+  Machine: EM_X86_64
+Sections:
+  - Name:  .data
+    Type:  SHT_PROGBITS
+    Flags: [ SHF_ALLOC, SHF_WRITE ]
+DynamicSymbols:
+  - Name:    localsym
+    Type:    STT_OBJECT
+    Section: .data
+    Binding: STB_LOCAL
+  - Name:    globalsym
+    Type:    STT_OBJECT
+    Section: .data
+    Binding: STB_GLOBAL
+  - Name:    uniqueglobalsym
+    Type:    STT_OBJECT
+    Section: .data
+    Binding: STB_GNU_UNIQUE
+  - Name:    weaksym
+    Type:    STT_OBJECT
+    Section: .data
+    Binding: STB_WEAK
+  - Name:    filesym
+    Type:    STT_FILE
+    Section: .data
+    Binding: STB_GLOBAL
+  - Name:    funcsym
+    Type:    STT_FUNC
+    Section: .data
+    Binding: STB_GLOBAL
+
+## Test dumping ELF files with no .dynsym section.
+# RUN: yaml2obj --docnum=2 %s -o %t2
+# RUN: llvm-objdump --dynamic-syms %t2 | \
+# RUN:   FileCheck %s --match-full-lines --strict-whitespace --check-prefix=NODYNSYM
+# RUN: llvm-objdump -T %t2 | \
+# RUN:   FileCheck %s --match-full-lines --strict-whitespace --check-prefix=NODYNSYM
+
+#       NODYNSYM:DYNAMIC SYMBOL TABLE:
+# NODYNSYM-EMPTY:
+
+--- !ELF
+FileHeader:
+  Class:   ELFCLASS64
+  Data:    ELFDATA2LSB
+  Type:    ET_DYN
+  Machine: EM_X86_64
+
+## Test dumping ELF files with logically empty .dynsym section (only has a 0-index NULL symbol).
+# RUN: yaml2obj --docnum=3 %s -o %t3
+# RUN: llvm-objdump --dynamic-syms %t3 | \
+# RUN:   FileCheck %s --match-full-lines --strict-whitespace --check-prefix=ONLY-NULL
+# RUN: llvm-objdump -T %t3 | \
+# RUN:   FileCheck %s --match-full-lines --strict-whitespace --check-prefix=ONLY-NULL
+
+#       ONLY-NULL:DYNAMIC SYMBOL TABLE:
+# ONLY-NULL-EMPTY:
+
+--- !ELF
+FileHeader:
+  Class:   ELFCLASS64
+  Data:    ELFDATA2LSB
+  Type:    ET_DYN
+  Machine: EM_X86_64
+DynamicSymbols: []
+
+## Test dumping ELF files with truly empty .dynsym section (size of .dynsym section is 0).
+# RUN: yaml2obj --docnum=4 %s -o %t4
+# RUN: llvm-objdump --dynamic-syms %t4 | \
+# RUN:   FileCheck %s --match-full-lines --strict-whitespace --check-prefix=EMPTY
+# RUN: llvm-objdump -T %t4 | \
+# RUN:   FileCheck %s --match-full-lines --strict-whitespace --check-prefix=EMPTY
+
+#       EMPTY:DYNAMIC SYMBOL TABLE:
+# EMPTY-EMPTY:
+
+--- !ELF
+FileHeader:
+  Class:   ELFCLASS64
+  Data:    ELFDATA2LSB
+  Type:    ET_DYN
+  Machine: EM_X86_64
+Sections:
+  - Name: .dynsym
+    Type: SHT_DYNSYM
+    Size: 0
diff --git a/llvm/test/tools/llvm-objdump/unimplemented-features.test b/llvm/test/tools/llvm-objdump/unimplemented-features.test
new file mode 100644 (file)
index 0000000..87366e1
--- /dev/null
@@ -0,0 +1,31 @@
+## Test dumping dynamic symbols reports a warning if the operation is unsupported for the file format.
+# RUN: yaml2obj %s --docnum=1 -o %t.macho.o
+# RUN: llvm-objdump --dynamic-syms %t.macho.o 2>&1 | \
+# RUN:   FileCheck %s -DFILE=%t.macho.o
+# RUN: yaml2obj %s --docnum=2 -o %t.coff.o
+# RUN: llvm-objdump --dynamic-syms %t.coff.o 2>&1 | \
+# RUN:   FileCheck %s -DFILE=%t.coff.o
+# RUN: llvm-objdump --dynamic-syms %p/XCOFF/Inputs/xcoff-section-headers.o 2>&1 | \
+# RUN:   FileCheck %s -DFILE=%p/XCOFF/Inputs/xcoff-section-headers.o
+
+#       CHECK:DYNAMIC SYMBOL TABLE:
+#  CHECK-NEXT:{{.*}}llvm-objdump: warning: '[[FILE]]': this operation is not currently supported for this file format
+# CHECK-EMPTY:
+
+--- !mach-o
+FileHeader:      
+  magic:      0xFEEDFACF
+  cputype:    0x01000007
+  cpusubtype: 0x80000003
+  filetype:   0x00000002
+  ncmds:      0
+  sizeofcmds: 0
+  flags:      0x00218085
+  reserved:   0x00000000
+
+--- !COFF
+header:
+  Machine:         IMAGE_FILE_MACHINE_AMD64
+  Characteristics: []
+sections: []
+symbols:  []
index 6f28415..69967db 100644 (file)
@@ -321,6 +321,15 @@ static cl::alias SymbolTableShort("t", cl::desc("Alias for --syms"),
                                   cl::NotHidden, cl::Grouping,
                                   cl::aliasopt(SymbolTable));
 
+cl::opt<bool> DynamicSymbolTable(
+    "dynamic-syms",
+    cl::desc("Display the contents of the dynamic symbol table"),
+    cl::cat(ObjdumpCat));
+static cl::alias DynamicSymbolTableShort("T",
+                                         cl::desc("Alias for --dynamic-syms"),
+                                         cl::NotHidden, cl::Grouping,
+                                         cl::aliasopt(DynamicSymbolTable));
+
 cl::opt<std::string> TripleName("triple",
                                 cl::desc("Target triple to disassemble for, "
                                          "see -version for available targets"),
@@ -1857,143 +1866,167 @@ void printSectionContents(const ObjectFile *Obj) {
 }
 
 void printSymbolTable(const ObjectFile *O, StringRef ArchiveName,
-                      StringRef ArchitectureName) {
-  outs() << "SYMBOL TABLE:\n";
-
-  if (const COFFObjectFile *Coff = dyn_cast<const COFFObjectFile>(O)) {
-    printCOFFSymbolTable(Coff);
+                      StringRef ArchitectureName, bool DumpDynamic) {
+  if (O->isCOFF() && !DumpDynamic) {
+    outs() << "SYMBOL TABLE:\n";
+    printCOFFSymbolTable(cast<const COFFObjectFile>(O));
     return;
   }
 
   const StringRef FileName = O->getFileName();
-  const MachOObjectFile *MachO = dyn_cast<const MachOObjectFile>(O);
-  for (auto I = O->symbol_begin(), E = O->symbol_end(); I != E; ++I) {
-    const SymbolRef &Symbol = *I;
-    uint64_t Address = unwrapOrError(Symbol.getAddress(), FileName, ArchiveName,
-                                     ArchitectureName);
-    if ((Address < StartAddress) || (Address > StopAddress))
-      continue;
-    SymbolRef::Type Type = unwrapOrError(Symbol.getType(), FileName,
-                                         ArchiveName, ArchitectureName);
-    uint32_t Flags = Symbol.getFlags();
 
-    // Don't ask a Mach-O STAB symbol for its section unless you know that 
-    // STAB symbol's section field refers to a valid section index. Otherwise
-    // the symbol may error trying to load a section that does not exist.
-    bool isSTAB = false;
-    if (MachO) {
-      DataRefImpl SymDRI = Symbol.getRawDataRefImpl();
-      uint8_t NType = (MachO->is64Bit() ?
-                       MachO->getSymbol64TableEntry(SymDRI).n_type:
-                       MachO->getSymbolTableEntry(SymDRI).n_type);
-      if (NType & MachO::N_STAB)
-        isSTAB = true;
-    }
-    section_iterator Section = isSTAB ? O->section_end() :
-                               unwrapOrError(Symbol.getSection(), FileName,
-                                             ArchiveName, ArchitectureName);
+  if (!DumpDynamic) {
+    outs() << "SYMBOL TABLE:\n";
+    for (auto I = O->symbol_begin(); I != O->symbol_end(); ++I)
+      printSymbol(O, *I, FileName, ArchiveName, ArchitectureName, DumpDynamic);
+    return;
+  }
 
-    StringRef Name;
-    if (Type == SymbolRef::ST_Debug && Section != O->section_end()) {
-      if (Expected<StringRef> NameOrErr = Section->getName())
-        Name = *NameOrErr;
-      else
-        consumeError(NameOrErr.takeError());
+  outs() << "DYNAMIC SYMBOL TABLE:\n";
+  if (!O->isELF()) {
+    reportWarning(
+        "this operation is not currently supported for this file format",
+        FileName);
+    return;
+  }
 
-    } else {
-      Name = unwrapOrError(Symbol.getName(), FileName, ArchiveName,
-                           ArchitectureName);
-    }
+  const ELFObjectFileBase *ELF = cast<const ELFObjectFileBase>(O);
+  for (auto I = ELF->getDynamicSymbolIterators().begin();
+       I != ELF->getDynamicSymbolIterators().end(); ++I)
+    printSymbol(O, *I, FileName, ArchiveName, ArchitectureName, DumpDynamic);
+}
 
-    bool Global = Flags & SymbolRef::SF_Global;
-    bool Weak = Flags & SymbolRef::SF_Weak;
-    bool Absolute = Flags & SymbolRef::SF_Absolute;
-    bool Common = Flags & SymbolRef::SF_Common;
-    bool Hidden = Flags & SymbolRef::SF_Hidden;
-
-    char GlobLoc = ' ';
-    if ((Section != O->section_end() || Absolute) && !Weak)
-      GlobLoc = Global ? 'g' : 'l';
-    char IFunc = ' ';
-    if (isa<ELFObjectFileBase>(O)) {
-      if (ELFSymbolRef(*I).getELFType() == ELF::STT_GNU_IFUNC)
-        IFunc = 'i';
-      if (ELFSymbolRef(*I).getBinding() == ELF::STB_GNU_UNIQUE)
-        GlobLoc = 'u';
-    }
-    char Debug = (Type == SymbolRef::ST_Debug || Type == SymbolRef::ST_File)
-                 ? 'd' : ' ';
-    char FileFunc = ' ';
-    if (Type == SymbolRef::ST_File)
-      FileFunc = 'f';
-    else if (Type == SymbolRef::ST_Function)
-      FileFunc = 'F';
-    else if (Type == SymbolRef::ST_Data)
-      FileFunc = 'O';
-
-    const char *Fmt = O->getBytesInAddress() > 4 ? "%016" PRIx64 :
-                                                   "%08" PRIx64;
-
-    outs() << format(Fmt, Address) << " "
-           << GlobLoc // Local -> 'l', Global -> 'g', Neither -> ' '
-           << (Weak ? 'w' : ' ') // Weak?
-           << ' ' // Constructor. Not supported yet.
-           << ' ' // Warning. Not supported yet.
-           << IFunc
-           << Debug // Debugging (d) or dynamic (D) symbol.
-           << FileFunc // Name of function (F), file (f) or object (O).
-           << ' ';
-    if (Absolute) {
-      outs() << "*ABS*";
-    } else if (Common) {
-      outs() << "*COM*";
-    } else if (Section == O->section_end()) {
-      outs() << "*UND*";
-    } else {
-      if (const MachOObjectFile *MachO =
-          dyn_cast<const MachOObjectFile>(O)) {
-        DataRefImpl DR = Section->getRawDataRefImpl();
-        StringRef SegmentName = MachO->getSectionFinalSegmentName(DR);
-        outs() << SegmentName << ",";
-      }
-      StringRef SectionName =
-          unwrapOrError(Section->getName(), O->getFileName());
-      outs() << SectionName;
-    }
+void printSymbol(const ObjectFile *O, const SymbolRef &Symbol,
+                 StringRef FileName, StringRef ArchiveName,
+                 StringRef ArchitectureName, bool DumpDynamic) {
+  const MachOObjectFile *MachO = dyn_cast<const MachOObjectFile>(O);
+  uint64_t Address = unwrapOrError(Symbol.getAddress(), FileName, ArchiveName,
+                                   ArchitectureName);
+  if ((Address < StartAddress) || (Address > StopAddress))
+    return;
+  SymbolRef::Type Type =
+      unwrapOrError(Symbol.getType(), FileName, ArchiveName, ArchitectureName);
+  uint32_t Flags = Symbol.getFlags();
+
+  // Don't ask a Mach-O STAB symbol for its section unless you know that
+  // STAB symbol's section field refers to a valid section index. Otherwise
+  // the symbol may error trying to load a section that does not exist.
+  bool IsSTAB = false;
+  if (MachO) {
+    DataRefImpl SymDRI = Symbol.getRawDataRefImpl();
+    uint8_t NType =
+        (MachO->is64Bit() ? MachO->getSymbol64TableEntry(SymDRI).n_type
+                          : MachO->getSymbolTableEntry(SymDRI).n_type);
+    if (NType & MachO::N_STAB)
+      IsSTAB = true;
+  }
+  section_iterator Section = IsSTAB
+                                 ? O->section_end()
+                                 : unwrapOrError(Symbol.getSection(), FileName,
+                                                 ArchiveName, ArchitectureName);
+
+  StringRef Name;
+  if (Type == SymbolRef::ST_Debug && Section != O->section_end()) {
+    if (Expected<StringRef> NameOrErr = Section->getName())
+      Name = *NameOrErr;
+    else
+      consumeError(NameOrErr.takeError());
 
-    if (Common || isa<ELFObjectFileBase>(O)) {
-      uint64_t Val =
-          Common ? Symbol.getAlignment() : ELFSymbolRef(Symbol).getSize();
-      outs() << '\t' << format(Fmt, Val);
+  } else {
+    Name = unwrapOrError(Symbol.getName(), FileName, ArchiveName,
+                         ArchitectureName);
+  }
+
+  bool Global = Flags & SymbolRef::SF_Global;
+  bool Weak = Flags & SymbolRef::SF_Weak;
+  bool Absolute = Flags & SymbolRef::SF_Absolute;
+  bool Common = Flags & SymbolRef::SF_Common;
+  bool Hidden = Flags & SymbolRef::SF_Hidden;
+
+  char GlobLoc = ' ';
+  if ((Section != O->section_end() || Absolute) && !Weak)
+    GlobLoc = Global ? 'g' : 'l';
+  char IFunc = ' ';
+  if (isa<ELFObjectFileBase>(O)) {
+    if (ELFSymbolRef(Symbol).getELFType() == ELF::STT_GNU_IFUNC)
+      IFunc = 'i';
+    if (ELFSymbolRef(Symbol).getBinding() == ELF::STB_GNU_UNIQUE)
+      GlobLoc = 'u';
+  }
+
+  char Debug = ' ';
+  if (DumpDynamic)
+    Debug = 'D';
+  else if (Type == SymbolRef::ST_Debug || Type == SymbolRef::ST_File)
+    Debug = 'd';
+
+  char FileFunc = ' ';
+  if (Type == SymbolRef::ST_File)
+    FileFunc = 'f';
+  else if (Type == SymbolRef::ST_Function)
+    FileFunc = 'F';
+  else if (Type == SymbolRef::ST_Data)
+    FileFunc = 'O';
+
+  const char *Fmt = O->getBytesInAddress() > 4 ? "%016" PRIx64 : "%08" PRIx64;
+
+  outs() << format(Fmt, Address) << " "
+         << GlobLoc            // Local -> 'l', Global -> 'g', Neither -> ' '
+         << (Weak ? 'w' : ' ') // Weak?
+         << ' '                // Constructor. Not supported yet.
+         << ' '                // Warning. Not supported yet.
+         << IFunc              // Indirect reference to another symbol.
+         << Debug              // Debugging (d) or dynamic (D) symbol.
+         << FileFunc           // Name of function (F), file (f) or object (O).
+         << ' ';
+  if (Absolute) {
+    outs() << "*ABS*";
+  } else if (Common) {
+    outs() << "*COM*";
+  } else if (Section == O->section_end()) {
+    outs() << "*UND*";
+  } else {
+    if (const MachOObjectFile *MachO = dyn_cast<const MachOObjectFile>(O)) {
+      DataRefImpl DR = Section->getRawDataRefImpl();
+      StringRef SegmentName = MachO->getSectionFinalSegmentName(DR);
+      outs() << SegmentName << ",";
     }
+    StringRef SectionName = unwrapOrError(Section->getName(), FileName);
+    outs() << SectionName;
+  }
 
-    if (isa<ELFObjectFileBase>(O)) {
-      uint8_t Other = ELFSymbolRef(Symbol).getOther();
-      switch (Other) {
-      case ELF::STV_DEFAULT:
-        break;
-      case ELF::STV_INTERNAL:
-        outs() << " .internal";
-        break;
-      case ELF::STV_HIDDEN:
-        outs() << " .hidden";
-        break;
-      case ELF::STV_PROTECTED:
-        outs() << " .protected";
-        break;
-      default:
-        outs() << format(" 0x%02x", Other);
-        break;
-      }
-    } else if (Hidden) {
+  if (Common || isa<ELFObjectFileBase>(O)) {
+    uint64_t Val =
+        Common ? Symbol.getAlignment() : ELFSymbolRef(Symbol).getSize();
+    outs() << '\t' << format(Fmt, Val);
+  }
+
+  if (isa<ELFObjectFileBase>(O)) {
+    uint8_t Other = ELFSymbolRef(Symbol).getOther();
+    switch (Other) {
+    case ELF::STV_DEFAULT:
+      break;
+    case ELF::STV_INTERNAL:
+      outs() << " .internal";
+      break;
+    case ELF::STV_HIDDEN:
       outs() << " .hidden";
+      break;
+    case ELF::STV_PROTECTED:
+      outs() << " .protected";
+      break;
+    default:
+      outs() << format(" 0x%02x", Other);
+      break;
     }
-
-    if (Demangle)
-      outs() << ' ' << demangle(std::string(Name)) << '\n';
-    else
-      outs() << ' ' << Name << '\n';
+  } else if (Hidden) {
+    outs() << " .hidden";
   }
+
+  if (Demangle)
+    outs() << ' ' << demangle(std::string(Name)) << '\n';
+  else
+    outs() << ' ' << Name << '\n';
 }
 
 static void printUnwindInfo(const ObjectFile *O) {
@@ -2237,6 +2270,9 @@ static void dumpObject(ObjectFile *O, const Archive *A = nullptr,
     printSectionHeaders(O);
   if (SymbolTable)
     printSymbolTable(O, ArchiveName);
+  if (DynamicSymbolTable)
+    printSymbolTable(O, ArchiveName, /*ArchitectureName=*/"",
+                     /*DumpDynamic=*/true);
   if (DwarfDumpType != DIDT_Null) {
     std::unique_ptr<DIContext> DICtx = DWARFContext::create(*O);
     // Dump the complete DWARF structure.
@@ -2378,7 +2414,7 @@ int main(int argc, char **argv) {
   if (!ArchiveHeaders && !Disassemble && DwarfDumpType == DIDT_Null &&
       !DynamicRelocations && !FileHeaders && !PrivateHeaders && !RawClangAST &&
       !Relocations && !SectionHeaders && !SectionContents && !SymbolTable &&
-      !UnwindInfo && !FaultMapSection &&
+      !DynamicSymbolTable && !UnwindInfo && !FaultMapSection &&
       !(MachOOpt &&
         (Bind || DataInCode || DylibId || DylibsUsed || ExportsTrie ||
          FirstPrivateHeader || IndirectSymbols || InfoPlist || LazyBind ||
index f834f11..7863689 100644 (file)
@@ -133,7 +133,11 @@ void printDynamicRelocations(const object::ObjectFile *O);
 void printSectionHeaders(const object::ObjectFile *O);
 void printSectionContents(const object::ObjectFile *O);
 void printSymbolTable(const object::ObjectFile *O, StringRef ArchiveName,
-                      StringRef ArchitectureName = StringRef());
+                      StringRef ArchitectureName = StringRef(),
+                      bool DumpDynamic = false);
+void printSymbol(const object::ObjectFile *O, const object::SymbolRef &Symbol,
+                 StringRef FileName, StringRef ArchiveName,
+                 StringRef ArchitectureName, bool DumpDynamic);
 LLVM_ATTRIBUTE_NORETURN void reportError(StringRef File, Twine Message);
 LLVM_ATTRIBUTE_NORETURN void reportError(Error E, StringRef FileName,
                                          StringRef ArchiveName = "",