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/include/llvm/Object/ELFObjectFile.h b/llvm/include/llvm/Object/ELFObjectFile.h --- a/llvm/include/llvm/Object/ELFObjectFile.h +++ b/llvm/include/llvm/Object/ELFObjectFile.h @@ -1019,7 +1019,9 @@ template elf_symbol_iterator ELFObjectFile::dynamic_symbol_begin() const { - DataRefImpl Sym = toDRI(DotDynSymSec, 0); + DataRefImpl Sym = + toDRI(DotDynSymSec, + DotDynSymSec && DotDynSymSec->sh_size >= sizeof(Elf_Sym) ? 1 : 0); return symbol_iterator(SymbolRef(Sym, this)); } diff --git a/llvm/test/tools/llvm-objdump/ELF/dynamic-symbol.test b/llvm/test/tools/llvm-objdump/ELF/dynamic-symbol.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-objdump/ELF/dynamic-symbol.test @@ -0,0 +1,106 @@ +## Test that llvm-objdump can dump dynamic symbols. +# RUN: yaml2obj --docnum=1 %s -o %t1 +# RUN: llvm-objdump --dynamic-syms %t1 | FileCheck %s --match-full-lines --check-prefix=DYNSYM +# RUN: llvm-objdump -T %t1 | FileCheck %s --match-full-lines --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 + +--- !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 | FileCheck %s --match-full-lines --check-prefix=NODYNSYM +# RUN: llvm-objdump -T %t2 | FileCheck %s --match-full-lines --check-prefix=NODYNSYM + +# NODYNSYM: DYNAMIC SYMBOL TABLE: + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_X86_64 +Sections: + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_WRITE ] + + +## Test dumping ELF files with logically empty .dynsym section. (only have a 0-index NULL symbol) +# RUN: llvm-objdump --dynamic-syms %t1 | FileCheck %s --match-full-lines --check-prefix=LOGICAL_EMPTY +# RUN: llvm-objdump -T %t1 | FileCheck %s --match-full-lines --check-prefix=LOGICAL_EMPTY + +# LOGICAL_EMPTY: DYNAMIC SYMBOL TABLE: + +--- !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: + +## Test dumping ELF files with truely empty .dynsym section. (size of .dynsym section is 0) +# RUN: llvm-objdump --dynamic-syms %t1 | FileCheck %s --match-full-lines --check-prefix=TRUELY_EMPTY +# RUN: llvm-objdump -T %t1 | FileCheck %s --match-full-lines --check-prefix=TRUELY_EMPTY + +# TRUELY_EMPTY: DYNAMIC SYMBOL TABLE: + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_X86_64 +Sections: + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_WRITE ] + - Name: .dynsym + Type: SHT_DYNSYM + Content: "" 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,37 @@ } void printSymbolTable(const ObjectFile *O, StringRef ArchiveName, - StringRef ArchitectureName) { - outs() << "SYMBOL TABLE:\n"; + StringRef ArchitectureName, bool DumpDynamic) { + const StringRef FileName = O->getFileName(); - if (const COFFObjectFile *Coff = dyn_cast(O)) { - printCOFFSymbolTable(Coff); - return; - } + auto I = O->symbol_begin(); + auto E = O->symbol_end(); - const StringRef FileName = O->getFileName(); 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"; + + if (const COFFObjectFile *Coff = dyn_cast(O)) { + printCOFFSymbolTable(Coff); + return; + } + } else { + outs() << "DYNAMIC SYMBOL TABLE:\n"; + + if (!O->isELF()) { + reportWarning( + "This operation is only currently supported for ELF object files.\n", + 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 +1929,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 +1968,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 +2248,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 +2390,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 ||