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,49 @@ +## Test that llvm-objdump can dump dynamic symbols. + +# RUN: yaml2obj %s -o %t +# RUN: llvm-objdump --dynamic-syms %t | FileCheck %s +# RUN: llvm-objdump -T %t | FileCheck %s + +--- !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 + +# CHECK: DYNAMIC SYMBOL TABLE: +# CHECK-NEXT: 0000000000000000 l DO .data 0000000000000000 localsym +# CHECK-NEXT: 0000000000000000 g DO .data 0000000000000000 globalsym +# CHECK-NEXT: 0000000000000000 u DO .data 0000000000000000 uniqueglobalsym +# CHECK-NEXT: 0000000000000000 w DO .data 0000000000000000 weaksym +# CHECK-NEXT: 0000000000000000 g Df .data 0000000000000000 filesym +# CHECK-NEXT: 0000000000000000 g DF .data 0000000000000000 funcsym diff --git a/llvm/tools/llvm-objdump/ELFDump.cpp b/llvm/tools/llvm-objdump/ELFDump.cpp --- a/llvm/tools/llvm-objdump/ELFDump.cpp +++ b/llvm/tools/llvm-objdump/ELFDump.cpp @@ -340,6 +340,110 @@ } } +template +void printDynamicSymbol(const ELFObjectFile *Elf, StringRef FileName) { + auto DynamicSymbolBegin = Elf->dynamic_symbol_begin(); + auto DynamicSymbolEnd = Elf->dynamic_symbol_end(); + + if (DynamicSymbolBegin == DynamicSymbolEnd) { + return; + } + + auto DynamicSymbol = DynamicSymbolBegin; + ++DynamicSymbol; // Skip the first meaningless symbol. + + for (; DynamicSymbol != Elf->dynamic_symbol_end(); ++DynamicSymbol) { + const ELFSymbolRef &Symbol = *DynamicSymbol; + uint64_t Address = unwrapOrError(Symbol.getAddress(), FileName); + + uint8_t ELFType = Symbol.getELFType(); + uint8_t Binding = Symbol.getBinding(); + uint32_t Flags = Symbol.getFlags(); + SymbolRef::Type Type = unwrapOrError(Symbol.getType(), FileName); + + bool Weak = Binding == ELF::STB_WEAK; + bool Absolute = Flags & SymbolRef::SF_Absolute; + bool Common = Flags & SymbolRef::SF_Common; + + section_iterator Section = unwrapOrError(Symbol.getSection(), FileName); + StringRef Name = unwrapOrError(Symbol.getName(), FileName); + + char GlobalLocal = ' '; // TODO: Both global and local should be '!' + if (Binding == ELF::STB_GLOBAL) + GlobalLocal = 'g'; + else if (Binding == ELF::STB_LOCAL) + GlobalLocal = 'l'; + else if (Binding == ELF::STB_GNU_UNIQUE) + GlobalLocal = 'u'; + + char DebugDynamic = 'D'; + if (Type == SymbolRef::ST_Debug) + DebugDynamic = 'd'; + + char FileFuncObject = ' '; + if (ELFType == ELF::STT_FILE) + FileFuncObject = 'f'; + else if (ELFType == ELF::STT_FUNC) + FileFuncObject = 'F'; + else if (ELFType == ELF::STT_OBJECT) + FileFuncObject = 'O'; + + const char *Fmt = + Elf->getBytesInAddress() > 4 ? "%016" PRIx64 : "%08" PRIx64; + + outs() << format(Fmt, Address) << " " // Symbol address + << GlobalLocal // Local: 'l', Global: 'g', Neither: ' ', Both: '!' + << (Weak ? 'w' : ' ') // Weak: 'w', Strong: ' ' + << ' ' // Constructor. Not supported. + << ' ' // Warning. Not supported. + << ' ' // Indirect reference to another symbol. Not supported. + << DebugDynamic // Debugging: 'd', Dynamic: 'D', Normal Symbol: ' ' + << FileFuncObject // File: 'f', Function: 'F', Object: 'O', Normal + // Symbol: ' ' + << ' '; + + if (Absolute) { + outs() << "*ABS*"; + } else if (Common) { + outs() << "*COM"; + } else if (Section == Elf->section_end()) { + outs() << "*UND*"; + } else { + StringRef SectionName = unwrapOrError(Section->getName(), FileName); + outs() << SectionName; + } + + uint64_t Val = + Common ? Symbol.getAlignment() : ELFSymbolRef(Symbol).getSize(); + outs() << format("\t%016" PRIx64, Val); + + 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; + } + + // TODO: If this symbol has version information, we have to add version + // information field before symbol name. + if (Demangle) + outs() << ' ' << demangle(std::string(Name)) << '\n'; + else + outs() << ' ' << Name << '\n'; + } +} + void printELFFileHeader(const object::ObjectFile *Obj) { if (const auto *ELFObj = dyn_cast(Obj)) printProgramHeaders(ELFObj->getELFFile()); @@ -372,4 +476,15 @@ else if (const auto *ELFObj = dyn_cast(Obj)) printSymbolVersionInfo(ELFObj->getELFFile(), Obj->getFileName()); } + +void printELFDynamicSymbolTable(const object::ObjectFile *Obj) { + if (const auto *ELFObj = dyn_cast(Obj)) + printDynamicSymbol(ELFObj, Obj->getFileName()); + else if (const auto *ELFObj = dyn_cast(Obj)) + printDynamicSymbol(ELFObj, Obj->getFileName()); + else if (const auto *ELFObj = dyn_cast(Obj)) + printDynamicSymbol(ELFObj, Obj->getFileName()); + else if (const auto *ELFObj = dyn_cast(Obj)) + printDynamicSymbol(ELFObj, Obj->getFileName()); +} } // namespace llvm 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 @@ -121,6 +121,7 @@ void printELFFileHeader(const object::ObjectFile *O); void printELFDynamicSection(const object::ObjectFile *Obj); void printELFSymbolVersionInfo(const object::ObjectFile *Obj); +void printELFDynamicSymbolTable(const object::ObjectFile *Obj); void printCOFFFileHeader(const object::ObjectFile *O); void printCOFFSymbolTable(const object::COFFImportFile *I); void printCOFFSymbolTable(const object::COFFObjectFile *O); @@ -139,6 +140,8 @@ void printSectionContents(const object::ObjectFile *O); void printSymbolTable(const object::ObjectFile *O, StringRef ArchiveName, StringRef ArchitectureName = StringRef()); +void printDynamicSymbolTable(const object::ObjectFile *O, StringRef ArchiveName, + StringRef ArchitectureName = StringRef()); 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"), @@ -1975,6 +1984,17 @@ } } +void printDynamicSymbolTable(const ObjectFile *O, StringRef ArchiveName, + StringRef ArchitectureName) { + outs() << "DYNAMIC SYMBOL TABLE:\n"; + if (O->isELF()) + printELFDynamicSymbolTable(O); + else + WithColor::error(errs(), ToolName) + << "This operation is only currently supported " + "for ELF object files.\n"; +} + static void printUnwindInfo(const ObjectFile *O) { outs() << "Unwind info:\n\n"; @@ -2216,6 +2236,8 @@ printSectionHeaders(O); if (SymbolTable) printSymbolTable(O, ArchiveName); + if (DynamicSymbolTable) + printDynamicSymbolTable(O, ArchiveName); if (DwarfDumpType != DIDT_Null) { std::unique_ptr DICtx = DWARFContext::create(*O); // Dump the complete DWARF structure. @@ -2355,7 +2377,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 ||