diff --git a/llvm/docs/CommandGuide/llvm-objdump.rst b/llvm/docs/CommandGuide/llvm-objdump.rst --- a/llvm/docs/CommandGuide/llvm-objdump.rst +++ b/llvm/docs/CommandGuide/llvm-objdump.rst @@ -85,6 +85,10 @@ 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 --- /dev/null +++ b/llvm/test/tools/llvm-objdump/X86/elf-dynamic-symbols.test @@ -0,0 +1,120 @@ +## 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 g 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 + Content: "" + +## Test dumping non-ELF files will emit warning. +# RUN: llvm-objdump --dynamic-syms %p/Inputs/hello.obj.macho-x86_64 2>&1 | \ +# RUN: FileCheck %s --match-full-lines --strict-whitespace +# RUN: llvm-objdump -T %p/Inputs/hello.obj.macho-x86_64 2>&1 | \ +# RUN: FileCheck %s --match-full-lines --strict-whitespace +# RUN: llvm-objdump --dynamic-syms %p/Inputs/internal.exe.coff-x86_64 2>&1 | \ +# RUN: FileCheck %s --match-full-lines --strict-whitespace +# RUN: llvm-objdump -T %p/Inputs/internal.exe.coff-x86_64 2>&1 | \ +# RUN: FileCheck %s --match-full-lines --strict-whitespace +# CHECK:DYNAMIC SYMBOL TABLE: +# CHECK-NEXT:{{.*}}/llvm-objdump: warning:{{.*}}: this operation is only currently supported for ELF object files +# CHECK-EMPTY: diff --git a/llvm/tools/llvm-objdump/llvm-objdump.h b/llvm/tools/llvm-objdump/llvm-objdump.h --- a/llvm/tools/llvm-objdump/llvm-objdump.h +++ b/llvm/tools/llvm-objdump/llvm-objdump.h @@ -138,7 +138,8 @@ 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); LLVM_ATTRIBUTE_NORETURN void reportError(StringRef File, Twine Message); LLVM_ATTRIBUTE_NORETURN void reportError(Error E, StringRef FileName, StringRef ArchiveName = "", diff --git a/llvm/tools/llvm-objdump/llvm-objdump.cpp b/llvm/tools/llvm-objdump/llvm-objdump.cpp --- a/llvm/tools/llvm-objdump/llvm-objdump.cpp +++ b/llvm/tools/llvm-objdump/llvm-objdump.cpp @@ -318,6 +318,15 @@ cl::NotHidden, cl::Grouping, cl::aliasopt(SymbolTable)); +cl::opt 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 TripleName("triple", cl::desc("Target triple to disassemble for, " "see -version for available targets"), @@ -1843,17 +1852,36 @@ } void printSymbolTable(const ObjectFile *O, StringRef ArchiveName, - StringRef ArchitectureName) { - outs() << "SYMBOL TABLE:\n"; - - if (const COFFObjectFile *Coff = dyn_cast(O)) { - printCOFFSymbolTable(Coff); + StringRef ArchitectureName, bool DumpDynamic) { + if (O->isCOFF() && !DumpDynamic) { + outs() << "SYMBOL TABLE:\n"; + printCOFFSymbolTable(cast(O)); return; } const StringRef FileName = O->getFileName(); + auto I = O->symbol_begin(); + auto E = O->symbol_end(); const MachOObjectFile *MachO = dyn_cast(O); - for (auto I = O->symbol_begin(), E = O->symbol_end(); I != E; ++I) { + + if (!DumpDynamic) + outs() << "SYMBOL TABLE:\n"; + else { + outs() << "DYNAMIC SYMBOL TABLE:\n"; + + if (!O->isELF()) { + reportWarning( + "this operation is only currently supported for ELF object files", + FileName); + return; + } + + const ELFObjectFileBase *ELF = cast(O); + I = ELF->getDynamicSymbolIterators().begin(); + E = ELF->getDynamicSymbolIterators().end(); + } + + for (; I != E; ++I) { const SymbolRef &Symbol = *I; uint64_t Address = unwrapOrError(Symbol.getAddress(), FileName, ArchiveName, ArchitectureName); @@ -1900,8 +1928,12 @@ char GlobLoc = ' '; if ((Section != O->section_end() || Absolute) && !Weak) GlobLoc = Global ? 'g' : 'l'; - char Debug = (Type == SymbolRef::ST_Debug || Type == SymbolRef::ST_File) - ? 'd' : ' '; + 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'; @@ -1935,8 +1967,7 @@ StringRef SegmentName = MachO->getSectionFinalSegmentName(DR); outs() << SegmentName << ","; } - StringRef SectionName = - unwrapOrError(Section->getName(), O->getFileName()); + StringRef SectionName = unwrapOrError(Section->getName(), FileName); outs() << SectionName; } @@ -2216,6 +2247,9 @@ printSectionHeaders(O); if (SymbolTable) printSymbolTable(O, ArchiveName); + if (DynamicSymbolTable) + printSymbolTable(O, ArchiveName, /*ArchitectureName=*/"", + /*DumpDynamic=*/true); if (DwarfDumpType != DIDT_Null) { std::unique_ptr DICtx = DWARFContext::create(*O); // Dump the complete DWARF structure. @@ -2355,7 +2389,7 @@ 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 ||