From eb81493e95c9f0e7feecd699758caa56b7d3514e Mon Sep 17 00:00:00 2001 From: Fangrui Song Date: Mon, 11 Sep 2023 11:08:58 -0700 Subject: [PATCH] [llvm-readelf] Add --extra-sym-info (#65580) GNU readelf introduced --extra-sym-info/-X to display the section name for --syms (https://sourceware.org/PR30684). Port the feature, which is currently llvm-readelf only. For STO_AARCH64_VARIANT_PCS/STO_RISCV_VARIANT_PCS, the Ndx and Name columns may not be aligned. --- llvm/docs/CommandGuide/llvm-readelf.rst | 4 + llvm/docs/ReleaseNotes.rst | 2 + .../ELF/aarch64-symbols-stother.test | 9 ++ .../llvm-readobj/ELF/section-symbols.test | 18 +++ llvm/test/tools/llvm-readobj/ELF/symbols.test | 71 ++++++++++-- .../tools/llvm-readobj/ELF/symtab-shndx.test | 12 ++ llvm/tools/llvm-readobj/COFFDumper.cpp | 4 +- llvm/tools/llvm-readobj/ELFDumper.cpp | 109 ++++++++++++------ llvm/tools/llvm-readobj/MachODumper.cpp | 6 +- llvm/tools/llvm-readobj/ObjDumper.h | 10 +- llvm/tools/llvm-readobj/Opts.td | 2 + llvm/tools/llvm-readobj/WasmDumper.cpp | 4 +- llvm/tools/llvm-readobj/XCOFFDumper.cpp | 4 +- llvm/tools/llvm-readobj/llvm-readobj.cpp | 5 +- 14 files changed, 199 insertions(+), 61 deletions(-) diff --git a/llvm/docs/CommandGuide/llvm-readelf.rst b/llvm/docs/CommandGuide/llvm-readelf.rst index 2ad217cec7ac..6ee4a5dfb159 100644 --- a/llvm/docs/CommandGuide/llvm-readelf.rst +++ b/llvm/docs/CommandGuide/llvm-readelf.rst @@ -77,6 +77,10 @@ OPTIONS ``GNU`` (the default) output mimics the equivalent GNU :program:`readelf` output. ``JSON`` is JSON formatted output intended for machine consumption. +.. option:: --extra-sym-info + + Display extra information (section name) when showing symbols. + .. option:: --section-groups, -g Display section groups. diff --git a/llvm/docs/ReleaseNotes.rst b/llvm/docs/ReleaseNotes.rst index 226ee606d17e..533c93e62e5e 100644 --- a/llvm/docs/ReleaseNotes.rst +++ b/llvm/docs/ReleaseNotes.rst @@ -166,6 +166,8 @@ Changes to the LLVM tools * llvm-symbolizer now treats invalid input as an address for which source information is not found. +* llvm-readelf now supports ``--extra-sym-info`` (``-X``) to display extra + information (section name) when showing symbols. Changes to LLDB --------------------------------- diff --git a/llvm/test/tools/llvm-readobj/ELF/aarch64-symbols-stother.test b/llvm/test/tools/llvm-readobj/ELF/aarch64-symbols-stother.test index a223c519c819..f84d773aecbb 100644 --- a/llvm/test/tools/llvm-readobj/ELF/aarch64-symbols-stother.test +++ b/llvm/test/tools/llvm-readobj/ELF/aarch64-symbols-stother.test @@ -4,6 +4,7 @@ # RUN: llvm-readobj --symbols %t.o | FileCheck %s --check-prefix=LLVM # RUN: llvm-readobj --symbols %t.o --elf-output-style=JSON --pretty-print | FileCheck %s --check-prefix=JSON # RUN: llvm-readelf --symbols %t.o | FileCheck %s --check-prefix=GNU +# RUN: llvm-readelf --symbols --extra-sym-info %t.o | FileCheck %s --match-full-lines --strict-whitespace --check-prefix=GNUX # LLVM: Name: foo1 # LLVM: Other [ (0x80) @@ -29,6 +30,14 @@ # GNU-NEXT: 3: 0000000000000000 0 NOTYPE LOCAL PROTECTED [VARIANT_PCS] UND foo3 # GNU-NEXT: 4: 0000000000000000 0 NOTYPE LOCAL PROTECTED UND foo4 +# GNUX:Symbol table '.symtab' contains 5 entries: +# GNUX-NEXT: Num: Value Size Type Bind Vis+Other Ndx(SecName) Name [+ Version Info] +# GNUX-NEXT: 0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND +# GNUX-NEXT: 1: 0000000000000000 0 NOTYPE LOCAL DEFAULT [VARIANT_PCS] UND foo1 +# GNUX-NEXT: 2: 0000000000000000 0 NOTYPE LOCAL DEFAULT [VARIANT_PCS | 40] UND foo2 +# GNUX-NEXT: 3: 0000000000000000 0 NOTYPE LOCAL PROTECTED [VARIANT_PCS] UND foo3 +# GNUX-NEXT: 4: 0000000000000000 0 NOTYPE LOCAL PROTECTED UND foo4 + # JSON: "Name": "foo1", # JSON: "Other": { # JSON-NEXT: "Value": 128, diff --git a/llvm/test/tools/llvm-readobj/ELF/section-symbols.test b/llvm/test/tools/llvm-readobj/ELF/section-symbols.test index 353c2143de48..287fa5194dd7 100644 --- a/llvm/test/tools/llvm-readobj/ELF/section-symbols.test +++ b/llvm/test/tools/llvm-readobj/ELF/section-symbols.test @@ -7,6 +7,8 @@ # RUN: FileCheck %s -DFILE=%t1 --check-prefix=LLVM1 --implicit-check-not="warning:" # RUN: llvm-readelf %t1 --symbols --relocations 2>&1 | \ # RUN: FileCheck %s -DFILE=%t1 --check-prefix=GNU1 --implicit-check-not="warning:" +# RUN: llvm-readelf %t1 --symbols -X 2>&1 | \ +# RUN: FileCheck %s -DFILE=%t1 --check-prefix=GNUX1 --implicit-check-not="warning:" # LLVM1: Relocations [ # LLVM1-NEXT: Section (5) .rela.foo { @@ -62,6 +64,22 @@ # GNU1-NEXT: warning: '[[FILE]]': a section [index 3] has an invalid sh_name (0xffff) offset which goes past the end of the section name string table # GNU1-NEXT: 9: {{.*}} SECTION {{.*}} 3
+# GNUX1: Symbol table '.symtab' contains 10 entries: +# GNUX1-NEXT: Num: {{.*}} Type {{.*}} Ndx(SecName) Name +# GNUX1-NEXT: 0: {{.*}} NOTYPE {{.*}} UND {{$}} +# GNUX1-NEXT: 1: {{.*}} SECTION {{.*}} 1 (.foo) .foo +# GNUX1-NEXT: 2: {{.*}} SECTION {{.*}} 1 (.foo) symbol1 +# GNUX1-NEXT: warning: '[[FILE]]': invalid section index: 67 +# GNUX1-NEXT: 3: {{.*}} SECTION {{.*}} 67
+# GNUX1-NEXT: 4: {{.*}} SECTION {{.*}} 67 symbol2 +# GNUX1-NEXT: 5: {{.*}} SECTION {{.*}} 2 (.bar) .bar +# GNUX1-NEXT: 6: {{.*}} SECTION {{.*}} 2 (.bar) symbol3 +# GNUX1-NEXT: warning: '[[FILE]]': invalid section index: 66 +# GNUX1-NEXT: 7: {{.*}} SECTION {{.*}} 66
+# GNUX1-NEXT: 8: {{.*}} SECTION {{.*}} 66 symbol4 +# GNUX1-NEXT: warning: '[[FILE]]': a section [index 3] has an invalid sh_name (0xffff) offset which goes past the end of the section name string table +# GNUX1-NEXT: 9: {{.*}} SECTION {{.*}} 3
+ --- !ELF FileHeader: Class: ELFCLASS32 diff --git a/llvm/test/tools/llvm-readobj/ELF/symbols.test b/llvm/test/tools/llvm-readobj/ELF/symbols.test index e924f0b67f4b..bb4e0753e7b7 100644 --- a/llvm/test/tools/llvm-readobj/ELF/symbols.test +++ b/llvm/test/tools/llvm-readobj/ELF/symbols.test @@ -13,13 +13,18 @@ # RUN: yaml2obj %s -DBITS=64 -DTYPE=ET_REL -o %t64 # RUN: llvm-readobj --symbols %t64 | FileCheck %s --match-full-lines --strict-whitespace --check-prefix=SYMBOLS-LLVM # RUN: llvm-readelf --symbols %t64 | FileCheck %s --match-full-lines --strict-whitespace --check-prefix=SYMBOLS-GNU64 +# RUN: llvm-readelf -s --extra-sym-info %t64 | FileCheck %s --match-full-lines --strict-whitespace --check-prefix=SYMBOLS-GNU64X # RUN: yaml2obj %s -DBITS=32 -DTYPE=ET_REL -o %t32 # RUN: llvm-readobj --symbols %t32 | FileCheck %s --match-full-lines --strict-whitespace --check-prefix=SYMBOLS-LLVM # RUN: llvm-readelf --symbols %t32 | FileCheck %s --match-full-lines --strict-whitespace --check-prefix=SYMBOLS-GNU32 +# RUN: llvm-readelf -s -X %t32 | FileCheck %s --match-full-lines --strict-whitespace --check-prefix=SYMBOLS-GNU32X ## b) Check dynamic objects. # RUN: yaml2obj %s -DBITS=64 -DTYPE=ET_DYN -o %t64.so -# RUN: llvm-readobj --symbols %t64.so | FileCheck %s --match-full-lines --strict-whitespace --check-prefix=SYMBOLS-LLVM +# RUN: llvm-readobj --symbols %t64.so > %t64llvm.txt +# RUN: FileCheck --input-file=%t64llvm.txt %s --match-full-lines --strict-whitespace --check-prefix=SYMBOLS-LLVM # RUN: llvm-readelf --symbols %t64.so | FileCheck %s --match-full-lines --strict-whitespace --check-prefix=SYMBOLS-GNU64 +## --extra-sym-info is a no-op for llvm-readobj. +# RUN: llvm-readobj --symbols --extra-sym-info %t64.so | diff %t64llvm.txt - # RUN: yaml2obj %s -DBITS=32 -DTYPE=ET_DYN -o %t32.so # RUN: llvm-readobj --symbols %t32.so | FileCheck %s --match-full-lines --strict-whitespace --check-prefix=SYMBOLS-LLVM # RUN: llvm-readelf --symbols %t32.so | FileCheck %s --match-full-lines --strict-whitespace --check-prefix=SYMBOLS-GNU32 @@ -41,7 +46,7 @@ # SYMBOLS-LLVM-NEXT: Binding: Local (0x0) # SYMBOLS-LLVM-NEXT: Type: None (0x0) # SYMBOLS-LLVM-NEXT: Other: 0 -# SYMBOLS-LLVM-NEXT: Section: Undefined (0x0) +# SYMBOLS-LLVM-NEXT: Section: .text (0x1) # SYMBOLS-LLVM-NEXT: } # SYMBOLS-LLVM-NEXT: Symbol { # SYMBOLS-LLVM-NEXT: Name: bar (1) @@ -52,19 +57,44 @@ # SYMBOLS-LLVM-NEXT: Other: 0 # SYMBOLS-LLVM-NEXT: Section: Undefined (0x0) # SYMBOLS-LLVM-NEXT: } +# SYMBOLS-LLVM-NEXT: Symbol { +# SYMBOLS-LLVM-NEXT: Name: data (9) +# SYMBOLS-LLVM-NEXT: Value: 0x3 +# SYMBOLS-LLVM-NEXT: Size: 0 +# SYMBOLS-LLVM-NEXT: Binding: Global (0x1) +# SYMBOLS-LLVM-NEXT: Type: None (0x0) +# SYMBOLS-LLVM-NEXT: Other: 0 +# SYMBOLS-LLVM-NEXT: Section: .data (0x2) +# SYMBOLS-LLVM-NEXT: } # SYMBOLS-LLVM-NEXT:] -# SYMBOLS-GNU64:Symbol table '.symtab' contains 3 entries: +# SYMBOLS-GNU64:Symbol table '.symtab' contains 4 entries: # SYMBOLS-GNU64-NEXT: Num: Value Size Type Bind Vis Ndx Name # SYMBOLS-GNU64-NEXT: 0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND -# SYMBOLS-GNU64-NEXT: 1: 0000000000000001 0 NOTYPE LOCAL DEFAULT UND foo +# SYMBOLS-GNU64-NEXT: 1: 0000000000000001 0 NOTYPE LOCAL DEFAULT 1 foo # SYMBOLS-GNU64-NEXT: 2: 0000000000000002 0 NOTYPE LOCAL DEFAULT UND bar +# SYMBOLS-GNU64-NEXT: 3: 0000000000000003 0 NOTYPE GLOBAL DEFAULT 2 data -# SYMBOLS-GNU32:Symbol table '.symtab' contains 3 entries: +# SYMBOLS-GNU64X:Symbol table '.symtab' contains 4 entries: +#SYMBOLS-GNU64X-NEXT: Num: Value Size Type Bind Vis+Other Ndx(SecName) Name [+ Version Info] +#SYMBOLS-GNU64X-NEXT: 0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND +#SYMBOLS-GNU64X-NEXT: 1: 0000000000000001 0 NOTYPE LOCAL DEFAULT 1 (.text) foo +#SYMBOLS-GNU64X-NEXT: 2: 0000000000000002 0 NOTYPE LOCAL DEFAULT UND bar +#SYMBOLS-GNU64X-NEXT: 3: 0000000000000003 0 NOTYPE GLOBAL DEFAULT 2 (.data) data + +# SYMBOLS-GNU32:Symbol table '.symtab' contains 4 entries: # SYMBOLS-GNU32-NEXT: Num: Value Size Type Bind Vis Ndx Name # SYMBOLS-GNU32-NEXT: 0: 00000000 0 NOTYPE LOCAL DEFAULT UND -# SYMBOLS-GNU32-NEXT: 1: 00000001 0 NOTYPE LOCAL DEFAULT UND foo +# SYMBOLS-GNU32-NEXT: 1: 00000001 0 NOTYPE LOCAL DEFAULT 1 foo # SYMBOLS-GNU32-NEXT: 2: 00000002 0 NOTYPE LOCAL DEFAULT UND bar +# SYMBOLS-GNU32-NEXT: 3: 00000003 0 NOTYPE GLOBAL DEFAULT 2 data + +# SYMBOLS-GNU32X:Symbol table '.symtab' contains 4 entries: +#SYMBOLS-GNU32X-NEXT: Num: Value Size Type Bind Vis+Other Ndx(SecName) Name [+ Version Info] +#SYMBOLS-GNU32X-NEXT: 0: 00000000 0 NOTYPE LOCAL DEFAULT UND +#SYMBOLS-GNU32X-NEXT: 1: 00000001 0 NOTYPE LOCAL DEFAULT 1 (.text) foo +#SYMBOLS-GNU32X-NEXT: 2: 00000002 0 NOTYPE LOCAL DEFAULT UND bar +#SYMBOLS-GNU32X-NEXT: 3: 00000003 0 NOTYPE GLOBAL DEFAULT 2 (.data) data ## Case 2: Check flag aliases produce identical output # RUN: llvm-readobj --symbols %t64 > %t.symbols @@ -100,10 +130,10 @@ ## Case 6: Test that the Num index starts from zero at every new symbol table. # RUN: llvm-readelf --symbols %t64 %t64 | FileCheck %s --check-prefix=NUM-INDEX -# NUM-INDEX: Symbol table '.symtab' contains 3 entries: +# NUM-INDEX: Symbol table '.symtab' contains 4 entries: # NUM-INDEX-NEXT: Num: {{.*}} # NUM-INDEX-NEXT: 0: {{.*}} -# NUM-INDEX: Symbol table '.symtab' contains 3 entries: +# NUM-INDEX: Symbol table '.symtab' contains 4 entries: # NUM-INDEX-NEXT: Num: {{.*}} # NUM-INDEX-NEXT: 0: {{.*}} @@ -112,11 +142,21 @@ FileHeader: Class: ELFCLASS[[BITS]] Data: ELFDATA2LSB Type: [[TYPE]] +Sections: + - Name: .text + Type: SHT_PROGBITS + - Name: .data + Type: SHT_PROGBITS Symbols: - - Name: foo - Value: 0x1 - - Name: bar - Value: 0x2 + - Name: foo + Value: 0x1 + Section: .text + - Name: bar + Value: 0x2 + - Name: data + Value: 0x3 + Binding: STB_GLOBAL + Section: .data DynamicSymbols: - Name: zed @@ -232,6 +272,13 @@ Symbols: ## Test a symbol defined relative to a section with an invalid name. # RUN: yaml2obj --docnum=3 %s -o %t64.err4 # RUN: llvm-readelf -s %t64.err4 2>&1 | FileCheck /dev/null --implicit-check-not=warning: +# RUN: llvm-readelf -s -X %t64.err4 2>&1 | \ +# RUN: FileCheck %s -DFILE=%t64.err4 --check-prefix=INVALID-SECNAME-GNU --implicit-check-not=warning: + +# INVALID-SECNAME-GNU: Num: Value Size Type Bind Vis+Other Ndx(SecName) Name [+ Version Info] +# INVALID-SECNAME-GNU-NEXT: 0: 00000000 0 NOTYPE LOCAL DEFAULT UND +# INVALID-SECNAME-GNU-NEXT: warning: '[[FILE]]': a section [index 1] has an invalid sh_name (0xffff) offset which goes past the end of the section name string table +# INVALID-SECNAME-GNU-NEXT: 1: 00000000 0 NOTYPE LOCAL DEFAULT 1 foo --- !ELF FileHeader: diff --git a/llvm/test/tools/llvm-readobj/ELF/symtab-shndx.test b/llvm/test/tools/llvm-readobj/ELF/symtab-shndx.test index 493222adc302..b63204837184 100644 --- a/llvm/test/tools/llvm-readobj/ELF/symtab-shndx.test +++ b/llvm/test/tools/llvm-readobj/ELF/symtab-shndx.test @@ -7,6 +7,7 @@ # RUN: yaml2obj --docnum=1 %s -o %t1 # RUN: llvm-readelf --symbols --dyn-syms %t1 2>&1 | FileCheck %s --check-prefix=GNU +# RUN: llvm-readelf --symbols --dyn-syms --extra-sym-info %t1 2>&1 | FileCheck %s --check-prefix=GNUX # RUN: llvm-readobj --symbols --dyn-syms %t1 2>&1 | FileCheck %s --check-prefix=LLVM # GNU: Symbol table '.dynsym' contains 3 entries: @@ -20,6 +21,17 @@ # GNU-NEXT: 1: 00000000 0 NOTYPE LOCAL DEFAULT 2 sym1 # GNU-NEXT: 2: 00000000 0 NOTYPE LOCAL DEFAULT 1 sym2 +# GNUX: Symbol table '.dynsym' contains 3 entries: +# GNUX-NEXT: Num: Value Size Type Bind Vis+Other Ndx(SecName) Name [+ Version Info] +# GNUX-NEXT: 0: 00000000 0 NOTYPE LOCAL DEFAULT UND +# GNUX-NEXT: 1: 00000000 0 NOTYPE LOCAL DEFAULT 3 (.section3) dynsym1 +# GNUX-NEXT: 2: 00000000 0 NOTYPE LOCAL DEFAULT 2 (.section2) dynsym2 +# GNUX: Symbol table '.symtab' contains 3 entries: +# GNUX-NEXT: Num: Value Size Type Bind Vis+Other Ndx(SecName) Name [+ Version Info] +# GNUX-NEXT: 0: 00000000 0 NOTYPE LOCAL DEFAULT UND +# GNUX-NEXT: 1: 00000000 0 NOTYPE LOCAL DEFAULT 2 (.section2) sym1 +# GNUX-NEXT: 2: 00000000 0 NOTYPE LOCAL DEFAULT 1 (.section1) sym2 + # LLVM: Symbols [ # LLVM-NEXT: Symbol { # LLVM-NEXT: Name: (0) diff --git a/llvm/tools/llvm-readobj/COFFDumper.cpp b/llvm/tools/llvm-readobj/COFFDumper.cpp index 2c8b6e00a145..6e32a7eeb9d7 100644 --- a/llvm/tools/llvm-readobj/COFFDumper.cpp +++ b/llvm/tools/llvm-readobj/COFFDumper.cpp @@ -110,7 +110,7 @@ public: private: StringRef getSymbolName(uint32_t Index); - void printSymbols() override; + void printSymbols(bool ExtraSymInfo) override; void printDynamicSymbols() override; void printSymbol(const SymbolRef &Sym); void printRelocation(const SectionRef &Section, const RelocationRef &Reloc, @@ -1609,7 +1609,7 @@ void COFFDumper::printRelocation(const SectionRef &Section, } } -void COFFDumper::printSymbols() { +void COFFDumper::printSymbols(bool /*ExtraSymInfo*/) { ListScope Group(W, "Symbols"); for (const SymbolRef &Symbol : Obj->symbols()) diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp index 4872d0557662..e3387d02bc8f 100644 --- a/llvm/tools/llvm-readobj/ELFDumper.cpp +++ b/llvm/tools/llvm-readobj/ELFDumper.cpp @@ -298,11 +298,13 @@ protected: llvm::function_ref RelrFn); virtual void printSymtabMessage(const Elf_Shdr *Symtab, size_t Offset, - bool NonVisibilityBitsUsed) const {}; + bool NonVisibilityBitsUsed, + bool ExtraSymInfo) const {}; virtual void printSymbol(const Elf_Sym &Symbol, unsigned SymIndex, DataRegion ShndxTable, std::optional StrTable, bool IsDynamic, - bool NonVisibilityBitsUsed) const = 0; + bool NonVisibilityBitsUsed, + bool ExtraSymInfo) const = 0; virtual void printMipsABIFlags() = 0; virtual void printMipsGOT(const MipsGOTParser &Parser) = 0; @@ -406,7 +408,7 @@ protected: std::string getStaticSymbolName(uint32_t Index) const; StringRef getDynamicString(uint64_t Value) const; - void printSymbolsHelper(bool IsDynamic) const; + void printSymbolsHelper(bool IsDynamic, bool ExtraSymInfo) const; std::string getDynamicEntry(uint64_t Type, uint64_t Value) const; Expected> getRelocationTarget(const Relocation &R, @@ -508,7 +510,8 @@ ELFDumper::getVersionTable(const Elf_Shdr &Sec, ArrayRef *SymTab, } template -void ELFDumper::printSymbolsHelper(bool IsDynamic) const { +void ELFDumper::printSymbolsHelper(bool IsDynamic, + bool ExtraSymInfo) const { std::optional StrTable; size_t Entries = 0; Elf_Sym_Range Syms(nullptr, nullptr); @@ -549,10 +552,10 @@ void ELFDumper::printSymbolsHelper(bool IsDynamic) const { this->getElfObject().getELFFile().end()) : DataRegion(this->getShndxTable(SymtabSec)); - printSymtabMessage(SymtabSec, Entries, NonVisibilityBitsUsed); + printSymtabMessage(SymtabSec, Entries, NonVisibilityBitsUsed, ExtraSymInfo); for (const Elf_Sym &Sym : Syms) printSymbol(Sym, &Sym - Syms.begin(), ShndxTable, StrTable, IsDynamic, - NonVisibilityBitsUsed); + NonVisibilityBitsUsed, ExtraSymInfo); } template class GNUELFDumper : public ELFDumper { @@ -574,14 +577,16 @@ public: void printGroupSections() override; void printRelocations() override; void printSectionHeaders() override; - void printSymbols(bool PrintSymbols, bool PrintDynamicSymbols) override; + void printSymbols(bool PrintSymbols, bool PrintDynamicSymbols, + bool ExtraSymInfo) override; void printHashSymbols() override; void printSectionDetails() override; void printDependentLibs() override; void printDynamicTable() override; void printDynamicRelocations() override; void printSymtabMessage(const Elf_Shdr *Symtab, size_t Offset, - bool NonVisibilityBitsUsed) const override; + bool NonVisibilityBitsUsed, + bool ExtraSymInfo) const override; void printProgramHeaders(bool PrintProgramHeaders, cl::boolOrDefault PrintSectionMapping) override; void printVersionSymbolSection(const Elf_Shdr *Sec) override; @@ -656,12 +661,14 @@ private: void printSymbol(const Elf_Sym &Symbol, unsigned SymIndex, DataRegion ShndxTable, std::optional StrTable, bool IsDynamic, - bool NonVisibilityBitsUsed) const override; + bool NonVisibilityBitsUsed, + bool ExtraSymInfo) const override; void printDynamicRelocHeader(unsigned Type, StringRef Name, const DynRegionInfo &Reg) override; std::string getSymbolSectionNdx(const Elf_Sym &Symbol, unsigned SymIndex, - DataRegion ShndxTable) const; + DataRegion ShndxTable, + bool ExtraSymInfo = false) const; void printProgramHeaders() override; void printSectionMapping() override; void printGNUVersionSectionProlog(const typename ELFT::Shdr &Sec, @@ -686,7 +693,8 @@ public: void printGroupSections() override; void printRelocations() override; void printSectionHeaders() override; - void printSymbols(bool PrintSymbols, bool PrintDynamicSymbols) override; + void printSymbols(bool PrintSymbols, bool PrintDynamicSymbols, + bool ExtraSymInfo) override; void printDependentLibs() override; void printDynamicTable() override; void printDynamicRelocations() override; @@ -719,7 +727,8 @@ private: void printSymbol(const Elf_Sym &Symbol, unsigned SymIndex, DataRegion ShndxTable, std::optional StrTable, bool IsDynamic, - bool /*NonVisibilityBitsUsed*/) const override; + bool /*NonVisibilityBitsUsed*/, + bool /*ExtraSymInfo*/) const override; void printProgramHeaders() override; void printSectionMapping() override {} void printStackSizeEntry(uint64_t Size, @@ -3998,7 +4007,8 @@ template void GNUELFDumper::printSectionHeaders() { template void GNUELFDumper::printSymtabMessage(const Elf_Shdr *Symtab, size_t Entries, - bool NonVisibilityBitsUsed) const { + bool NonVisibilityBitsUsed, + bool ExtraSymInfo) const { StringRef Name; if (Symtab) Name = this->getPrintableSectionName(*Symtab); @@ -4008,21 +4018,27 @@ void GNUELFDumper::printSymtabMessage(const Elf_Shdr *Symtab, OS << "\nSymbol table for image"; OS << " contains " << Entries << " entries:\n"; - if (ELFT::Is64Bits) + if (ELFT::Is64Bits) { OS << " Num: Value Size Type Bind Vis"; - else + if (ExtraSymInfo) + OS << "+Other"; + } else { OS << " Num: Value Size Type Bind Vis"; + if (ExtraSymInfo) + OS << "+Other"; + } - if (NonVisibilityBitsUsed) - OS << " "; - OS << " Ndx Name\n"; + OS.PadToColumn((ELFT::Is64Bits ? 56 : 48) + (NonVisibilityBitsUsed ? 13 : 0)); + if (ExtraSymInfo) + OS << "Ndx(SecName) Name [+ Version Info]\n"; + else + OS << "Ndx Name\n"; } template -std::string -GNUELFDumper::getSymbolSectionNdx(const Elf_Sym &Symbol, - unsigned SymIndex, - DataRegion ShndxTable) const { +std::string GNUELFDumper::getSymbolSectionNdx( + const Elf_Sym &Symbol, unsigned SymIndex, DataRegion ShndxTable, + bool ExtraSymInfo) const { unsigned SectionIndex = Symbol.st_shndx; switch (SectionIndex) { case ELF::SHN_UNDEF: @@ -4041,7 +4057,8 @@ GNUELFDumper::getSymbolSectionNdx(const Elf_Sym &Symbol, this->reportUniqueWarning(IndexOrErr.takeError()); return "RSV[0xffff]"; } - return to_string(format_decimal(*IndexOrErr, 3)); + SectionIndex = *IndexOrErr; + break; } default: // Find if: @@ -4058,17 +4075,31 @@ GNUELFDumper::getSymbolSectionNdx(const Elf_Sym &Symbol, SectionIndex <= ELF::SHN_HIRESERVE) return std::string("RSV[0x") + to_string(format_hex_no_prefix(SectionIndex, 4)) + "]"; - // A normal section with an index - return to_string(format_decimal(SectionIndex, 3)); + break; } + + std::string Extra; + if (ExtraSymInfo) { + auto Sec = this->Obj.getSection(SectionIndex); + if (!Sec) { + this->reportUniqueWarning(Sec.takeError()); + } else { + auto SecName = this->Obj.getSectionName(**Sec); + if (!SecName) + this->reportUniqueWarning(SecName.takeError()); + else + Extra = Twine(" (" + *SecName + ")").str(); + } + } + return to_string(format_decimal(SectionIndex, 3)) + Extra; } template void GNUELFDumper::printSymbol(const Elf_Sym &Symbol, unsigned SymIndex, DataRegion ShndxTable, std::optional StrTable, - bool IsDynamic, - bool NonVisibilityBitsUsed) const { + bool IsDynamic, bool NonVisibilityBitsUsed, + bool ExtraSymInfo) const { unsigned Bias = ELFT::Is64Bits ? 8 : 0; Field Fields[8] = {0, 8, 17 + Bias, 23 + Bias, 31 + Bias, 38 + Bias, 48 + Bias, 51 + Bias}; @@ -4115,8 +4146,10 @@ void GNUELFDumper::printSymbol(const Elf_Sym &Symbol, unsigned SymIndex, } Fields[6].Column += NonVisibilityBitsUsed ? 13 : 0; - Fields[6].Str = getSymbolSectionNdx(Symbol, SymIndex, ShndxTable); + Fields[6].Str = + getSymbolSectionNdx(Symbol, SymIndex, ShndxTable, ExtraSymInfo); + Fields[7].Column += ExtraSymInfo ? 10 : 0; Fields[7].Str = this->getFullSymbolName(Symbol, SymIndex, ShndxTable, StrTable, IsDynamic); for (const Field &Entry : Fields) @@ -4162,13 +4195,14 @@ void GNUELFDumper::printHashedSymbol(const Elf_Sym *Symbol, template void GNUELFDumper::printSymbols(bool PrintSymbols, - bool PrintDynamicSymbols) { + bool PrintDynamicSymbols, + bool ExtraSymInfo) { if (!PrintSymbols && !PrintDynamicSymbols) return; // GNU readelf prints both the .dynsym and .symtab with --symbols. - this->printSymbolsHelper(true); + this->printSymbolsHelper(true, ExtraSymInfo); if (PrintSymbols) - this->printSymbolsHelper(false); + this->printSymbolsHelper(false, ExtraSymInfo); } template @@ -7011,7 +7045,8 @@ template void LLVMELFDumper::printSectionHeaders() { this->Obj.getSection(Sym, this->DotSymtabSec, ShndxTable)); if (SymSec == &Sec) printSymbol(Sym, &Sym - &Symbols[0], ShndxTable, StrTable, false, - false); + /*NonVisibilityBitsUsed=*/false, + /*ExtraSymInfo=*/false); } } } @@ -7098,7 +7133,8 @@ void LLVMELFDumper::printSymbol(const Elf_Sym &Symbol, unsigned SymIndex, DataRegion ShndxTable, std::optional StrTable, bool IsDynamic, - bool /*NonVisibilityBitsUsed*/) const { + bool /*NonVisibilityBitsUsed*/, + bool /*ExtraSymInfo*/) const { std::string FullSymbolName = this->getFullSymbolName( Symbol, SymIndex, ShndxTable, StrTable, IsDynamic); unsigned char SymbolType = Symbol.getType(); @@ -7122,14 +7158,15 @@ void LLVMELFDumper::printSymbol(const Elf_Sym &Symbol, unsigned SymIndex, template void LLVMELFDumper::printSymbols(bool PrintSymbols, - bool PrintDynamicSymbols) { + bool PrintDynamicSymbols, + bool ExtraSymInfo) { if (PrintSymbols) { ListScope Group(W, "Symbols"); - this->printSymbolsHelper(false); + this->printSymbolsHelper(false, ExtraSymInfo); } if (PrintDynamicSymbols) { ListScope Group(W, "DynamicSymbols"); - this->printSymbolsHelper(true); + this->printSymbolsHelper(true, ExtraSymInfo); } } diff --git a/llvm/tools/llvm-readobj/MachODumper.cpp b/llvm/tools/llvm-readobj/MachODumper.cpp index 5b385019486c..ce5ba46695d8 100644 --- a/llvm/tools/llvm-readobj/MachODumper.cpp +++ b/llvm/tools/llvm-readobj/MachODumper.cpp @@ -59,7 +59,7 @@ private: StringRef getSymbolName(const SymbolRef &Symbol) const; uint8_t getSymbolType(const SymbolRef &Symbol) const; - void printSymbols() override; + void printSymbols(bool ExtraSymInfo) override; void printSymbols(std::optional SymComp) override; void printDynamicSymbols() override; void printDynamicSymbols(std::optional SymComp) override; @@ -632,7 +632,9 @@ bool MachODumper::compareSymbolsByType(SymbolRef LHS, SymbolRef RHS) const { return getSymbolType(LHS) < getSymbolType(RHS); } -void MachODumper::printSymbols() { printSymbols(std::nullopt); } +void MachODumper::printSymbols(bool /*ExtraSymInfo*/) { + printSymbols(std::nullopt); +} void MachODumper::printSymbols(std::optional SymComp) { ListScope Group(W, "Symbols"); diff --git a/llvm/tools/llvm-readobj/ObjDumper.h b/llvm/tools/llvm-readobj/ObjDumper.h index 921792f886d0..a44fa42b85c9 100644 --- a/llvm/tools/llvm-readobj/ObjDumper.h +++ b/llvm/tools/llvm-readobj/ObjDumper.h @@ -77,13 +77,15 @@ public: virtual void printFileHeaders() = 0; virtual void printSectionHeaders() = 0; virtual void printRelocations() = 0; - virtual void printSymbols(bool PrintSymbols, bool PrintDynamicSymbols) { + virtual void printSymbols(bool PrintSymbols, bool PrintDynamicSymbols, + bool ExtraSymInfo) { if (PrintSymbols) - printSymbols(); + printSymbols(ExtraSymInfo); if (PrintDynamicSymbols) printDynamicSymbols(); } virtual void printSymbols(bool PrintSymbols, bool PrintDynamicSymbols, + bool ExtraSymInfo, std::optional SymComp) { if (SymComp) { if (PrintSymbols) @@ -91,7 +93,7 @@ public: if (PrintDynamicSymbols) printDynamicSymbols(SymComp); } else { - printSymbols(PrintSymbols, PrintDynamicSymbols); + printSymbols(PrintSymbols, PrintDynamicSymbols, ExtraSymInfo); } } virtual void printProgramHeaders(bool PrintProgramHeaders, @@ -187,7 +189,7 @@ protected: ScopedPrinter &W; private: - virtual void printSymbols() {} + virtual void printSymbols(bool ExtraSymInfo) {} virtual void printSymbols(std::optional Comp) {} virtual void printDynamicSymbols() {} virtual void printDynamicSymbols(std::optional Comp) {} diff --git a/llvm/tools/llvm-readobj/Opts.td b/llvm/tools/llvm-readobj/Opts.td index fec0adb5e6a6..e2d93c6ec229 100644 --- a/llvm/tools/llvm-readobj/Opts.td +++ b/llvm/tools/llvm-readobj/Opts.td @@ -25,6 +25,7 @@ def dependent_libraries : FF<"dependent-libraries", "Display the dependent libra def dyn_relocations : FF<"dyn-relocations", "Display the dynamic relocation entries in the file">; def dyn_syms : FF<"dyn-syms", "Display the dynamic symbol table">; def expand_relocs : FF<"expand-relocs", "Expand each shown relocation to multiple lines">; +def extra_sym_info : FF<"extra-sym-info", "Display extra information when showing symbols">; def file_header : FF<"file-header", "Display file header">; def headers : FF<"headers", "Equivalent to setting: --file-header, --program-headers, --section-headers">; defm hex_dump : Eq<"hex-dump", "Display the specified section(s) as hexadecimal bytes">, MetaVarName<"">; @@ -135,5 +136,6 @@ def : F<"S", "Alias for --section-headers">, Alias; def : F<"s", "Alias for --symbols">, Alias; def : F<"t", "Alias for --section-details">, Alias; def : F<"u", "Alias for --unwind">, Alias; +def : F<"X", "Alias for --extra-sym-info">, Alias, Group; def : F<"V", "Alias for --version-info">, Alias, Group; def : JoinedOrSeparate<["-"], "x">, Alias, HelpText<"Alias for --hex-dump">, MetaVarName<"">; diff --git a/llvm/tools/llvm-readobj/WasmDumper.cpp b/llvm/tools/llvm-readobj/WasmDumper.cpp index e6f0ac7ba762..5d7639b0b70f 100644 --- a/llvm/tools/llvm-readobj/WasmDumper.cpp +++ b/llvm/tools/llvm-readobj/WasmDumper.cpp @@ -70,7 +70,7 @@ protected: void printRelocation(const SectionRef &Section, const RelocationRef &Reloc); private: - void printSymbols() override; + void printSymbols(bool ExtraSymInfo) override; void printDynamicSymbols() override { llvm_unreachable("unimplemented"); } const WasmObjectFile *Obj; @@ -144,7 +144,7 @@ void WasmDumper::printRelocations() { } } -void WasmDumper::printSymbols() { +void WasmDumper::printSymbols(bool /*ExtraSymInfo*/) { ListScope Group(W, "Symbols"); for (const SymbolRef &Symbol : Obj->symbols()) diff --git a/llvm/tools/llvm-readobj/XCOFFDumper.cpp b/llvm/tools/llvm-readobj/XCOFFDumper.cpp index 74ebcc4ec7d8..8ebd670d5d56 100644 --- a/llvm/tools/llvm-readobj/XCOFFDumper.cpp +++ b/llvm/tools/llvm-readobj/XCOFFDumper.cpp @@ -33,7 +33,7 @@ public: void printAuxiliaryHeader() override; void printSectionHeaders() override; void printRelocations() override; - void printSymbols() override; + void printSymbols(bool ExtraSymInfo) override; void printDynamicSymbols() override; void printUnwindInfo() override; void printStackMap() const override; @@ -903,7 +903,7 @@ void XCOFFDumper::printSymbol(const SymbolRef &S) { } } -void XCOFFDumper::printSymbols() { +void XCOFFDumper::printSymbols(bool /*ExtraSymInfo*/) { ListScope Group(W, "Symbols"); for (const SymbolRef &S : Obj.symbols()) printSymbol(S); diff --git a/llvm/tools/llvm-readobj/llvm-readobj.cpp b/llvm/tools/llvm-readobj/llvm-readobj.cpp index 8a10df4beb13..ca633ceff908 100644 --- a/llvm/tools/llvm-readobj/llvm-readobj.cpp +++ b/llvm/tools/llvm-readobj/llvm-readobj.cpp @@ -102,6 +102,7 @@ bool Demangle; static bool DependentLibraries; static bool DynRelocs; static bool DynamicSymbols; +static bool ExtraSymInfo; static bool FileHeaders; static bool Headers; static std::vector HexDump; @@ -217,6 +218,7 @@ static void parseOptions(const opt::InputArgList &Args) { opts::DynRelocs = Args.hasArg(OPT_dyn_relocations); opts::DynamicSymbols = Args.hasArg(OPT_dyn_syms); opts::ExpandRelocs = Args.hasArg(OPT_expand_relocs); + opts::ExtraSymInfo = Args.hasArg(OPT_extra_sym_info); opts::FileHeaders = Args.hasArg(OPT_file_header); opts::Headers = Args.hasArg(OPT_headers); opts::HexDump = Args.getAllArgValues(OPT_hex_dump_EQ); @@ -435,7 +437,8 @@ static void dumpObject(ObjectFile &Obj, ScopedPrinter &Writer, if (opts::UnwindInfo) Dumper->printUnwindInfo(); if (opts::Symbols || opts::DynamicSymbols) - Dumper->printSymbols(opts::Symbols, opts::DynamicSymbols, SymComp); + Dumper->printSymbols(opts::Symbols, opts::DynamicSymbols, + opts::ExtraSymInfo, SymComp); if (!opts::StringDump.empty()) Dumper->printSectionsAsString(Obj, opts::StringDump); if (!opts::HexDump.empty())