diff --git a/llvm/docs/CommandGuide/llvm-readobj.rst b/llvm/docs/CommandGuide/llvm-readobj.rst --- a/llvm/docs/CommandGuide/llvm-readobj.rst +++ b/llvm/docs/CommandGuide/llvm-readobj.rst @@ -106,6 +106,10 @@ When used with :option:`--sections`, display symbols for each section shown. This option has no effect for GNU style output. +.. option:: --sort-symbols= + + Specify the keys to sort symbols before displaying symtab. + Valid values for sort_key are ``name`` and ``type``. .. option:: --stackmap Display contents of the stackmap section. diff --git a/llvm/test/tools/llvm-readobj/MachO/stabs-sorted.yaml b/llvm/test/tools/llvm-readobj/MachO/stabs-sorted.yaml new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-readobj/MachO/stabs-sorted.yaml @@ -0,0 +1,230 @@ +## Verify that llvm-readobj can dump files with stabs symbols in a sorted order, + +# RUN: yaml2obj --docnum=1 %s -o %t + +# RUN: not llvm-readobj --syms --sort-symbols=foo %t 2>&1 | FileCheck %s --check-prefix ERR-KEY +# RUN: not llvm-readobj --syms --sort-symbols=,, %t 2>&1 | FileCheck %s --check-prefix ERR-KEY-EMPT + +# RUN: llvm-readobj --syms --sort-symbols=type,name %t | FileCheck %s --check-prefix TYPE-NAME +# RUN: llvm-readobj --syms --sort-symbols=name,type %t | FileCheck %s --check-prefix NAME-TYPE +# RUN: llvm-readobj --syms --sort-symbols=type %t | FileCheck %s --check-prefix TYPE-ONLY + +# TYPE-NAME: Name: _a (19) +# TYPE-NAME-NEXT: Type: Section (0xE) +# TYPE-NAME: Name: _d (10) +# TYPE-NAME-NEXT: Type: Section (0xE) +# TYPE-NAME: Name: _f (7) +# TYPE-NAME-NEXT: Type: SymDebugTable (0x2E) +# TYPE-NAME: Name: _z (1) +# TYPE-NAME-NEXT: Type: SymDebugTable (0x2E) +# TYPE-NAME: Name: _c (13) +# TYPE-NAME-NEXT: Type: SymDebugTable (0x64) +# TYPE-NAME: Name: _g (4) +# TYPE-NAME-NEXT: Type: SymDebugTable (0x64) +# TYPE-NAME: Name: _b (16) +# TYPE-NAME-NEXT: Type: SymDebugTable (0x66) +# TYPE-NAME: Name: _d2 (22) +# TYPE-NAME-NEXT: Type: SymDebugTable (0x66) + +# NAME-TYPE: Name: _a (19) +# NAME-TYPE-NEXT: Type: Section (0xE) +# NAME-TYPE: Name: _b (16) +# NAME-TYPE-NEXT: Type: SymDebugTable (0x66) +# NAME-TYPE: Name: _c (13) +# NAME-TYPE-NEXT: Type: SymDebugTable (0x64) +# NAME-TYPE: Name: _d (10) +# NAME-TYPE-NEXT: Type: Section (0xE) +# NAME-TYPE: Name: _d2 (22) +# NAME-TYPE-NEXT: Type: SymDebugTable (0x66) +# NAME-TYPE: Name: _f (7) +# NAME-TYPE-NEXT: Type: SymDebugTable (0x2E) +# NAME-TYPE: Name: _g (4) +# NAME-TYPE-NEXT: Type: SymDebugTable (0x64) +# NAME-TYPE: Name: _z (1) +# NAME-TYPE-NEXT: Type: SymDebugTable (0x2E) + +# TYPE-ONLY: Name: _d (10) +# TYPE-ONLY-NEXT: Type: Section (0xE) +# TYPE-ONLY: Name: _a (19) +# TYPE-ONLY-NEXT: Type: Section (0xE) +# TYPE-ONLY: Name: _f (7) +# TYPE-ONLY-NEXT: Type: SymDebugTable (0x2E) +# TYPE-ONLY: Name: _z (1) +# TYPE-ONLY-NEXT: Type: SymDebugTable (0x2E) +# TYPE-ONLY: Name: _g (4) +# TYPE-ONLY-NEXT: Type: SymDebugTable (0x64) +# TYPE-ONLY: Name: _c (13) +# TYPE-ONLY-NEXT: Type: SymDebugTable (0x64) +# TYPE-ONLY: Name: _d2 (22) +# TYPE-ONLY-NEXT: Type: SymDebugTable (0x66) +# TYPE-ONLY: Name: _b (16) +# TYPE-ONLY-NEXT: Type: SymDebugTable (0x66) + +--- !mach-o +FileHeader: + magic: 0xFEEDFACF + cputype: 0x1000007 + cpusubtype: 0x3 + filetype: 0x1 + ncmds: 3 + sizeofcmds: 496 + flags: 0x2000 + reserved: 0x0 +LoadCommands: + - cmd: LC_SEGMENT_64 + cmdsize: 392 + segname: '' + vmaddr: 0 + vmsize: 32 + fileoff: 528 + filesize: 28 + maxprot: 7 + initprot: 7 + nsects: 4 + flags: 0 + Sections: + - sectname: __text + segname: __TEXT + addr: 0x0 + size: 9 + offset: 0x210 + align: 0 + reloff: 0x230 + nreloc: 1 + flags: 0x80000000 + reserved1: 0x0 + reserved2: 0x0 + reserved3: 0x0 + content: '000000000000000000' + relocations: + - address: 0x0 + symbolnum: 7 + pcrel: false + length: 3 + extern: true + type: 0 + scattered: false + value: 0 + - sectname: more_data + segname: __DATA + addr: 0x9 + size: 8 + offset: 0x219 + align: 0 + reloff: 0x0 + nreloc: 0 + flags: 0x0 + reserved1: 0x0 + reserved2: 0x0 + reserved3: 0x0 + content: 7B00000000000000 + - sectname: __data + segname: __DATA + addr: 0x11 + size: 11 + offset: 0x221 + align: 0 + reloff: 0x0 + nreloc: 0 + flags: 0x0 + reserved1: 0x0 + reserved2: 0x0 + reserved3: 0x0 + content: 7B00000000000000000000 + - sectname: __common + segname: __DATA + addr: 0x1C + size: 4 + offset: 0x0 + align: 2 + reloff: 0x0 + nreloc: 0 + flags: 0x1 + reserved1: 0x0 + reserved2: 0x0 + reserved3: 0x0 + - cmd: LC_SYMTAB + cmdsize: 24 + symoff: 568 + nsyms: 8 + stroff: 696 + strsize: 32 + - cmd: LC_DYSYMTAB + cmdsize: 80 + ilocalsym: 0 + nlocalsym: 7 + iextdefsym: 7 + nextdefsym: 0 + iundefsym: 7 + nundefsym: 1 + tocoff: 0 + ntoc: 0 + modtaboff: 0 + nmodtab: 0 + extrefsymoff: 0 + nextrefsyms: 0 + indirectsymoff: 0 + nindirectsyms: 0 + extreloff: 0 + nextrel: 0 + locreloff: 0 + nlocrel: 0 +LinkEditData: + NameList: + - n_strx: 4 + n_type: 0x64 + n_sect: 1 + n_desc: 0 + n_value: 0 + - n_strx: 10 + n_type: 0xE + n_sect: 1 + n_desc: 0 + n_value: 8 + - n_strx: 22 + n_type: 0x66 + n_sect: 1 + n_desc: 0 + n_value: 8 + - n_strx: 16 + n_type: 0x66 + n_sect: 2 + n_desc: 0 + n_value: 9 + - n_strx: 19 + n_type: 0xE + n_sect: 3 + n_desc: 0 + n_value: 17 + - n_strx: 13 + n_type: 0x64 + n_sect: 4 + n_desc: 0 + n_value: 28 + - n_strx: 7 + n_type: 0x2E + n_sect: 3 + n_desc: 0 + n_value: 25 + - n_strx: 1 + n_type: 0x2E + n_sect: 0 + n_desc: 0 + n_value: 0 + StringTable: + - '' + - _z + - _g + - _f + - _d + - _c + - _b + - _a + - _d2 + - '' + - '' + - '' + - '' + - '' + - '' +... \ No newline at end of file diff --git a/llvm/test/tools/llvm-readobj/sort-symbols.test b/llvm/test/tools/llvm-readobj/sort-symbols.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-readobj/sort-symbols.test @@ -0,0 +1,58 @@ +# Test that we print a warning for ELF, WASM, and COFF but still dump the contents for all. + +# RUN: yaml2obj --docnum=1 %s -o %t_macho +# RUN: yaml2obj --docnum=2 %s -o %t_elf +# RUN: yaml2obj --docnum=3 %s -o %t_wasm +# RUN: yaml2obj --docnum=4 %s -o %t_coff + +# RUN: llvm-readobj --syms --sort-symbols=type,name %t_elf %t_wasm %t_coff %t_macho 2>&1 | FileCheck %s + +# CHECK: warning: '{{.+}}_elf': --sort-symbols is not supported yet for this format +# CHECK: Format: elf64-unknown +# CHECK: warning: '{{.+}}_wasm': --sort-symbols is not supported yet for this format +# CHECK: Format: WASM +# CHECK: warning: '{{.+}}_coff': --sort-symbols is not supported yet for this format +# CHECK: Format: aixcoff-rs6000 +# CHECK-NOT: warning '{{.+}}_macho': --sort-symbols is not supported yet for this format. +# CHECK: Format: Mach-O 64-bit x86-64 + +--- !mach-o +FileHeader: + magic: 0xFEEDFACF + cputype: 0x1000007 + cpusubtype: 0x3 + filetype: 0x1 + ncmds: 0 + sizeofcmds: 0 + flags: 0x2000 + reserved: 0x0 +... +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC +Sections: + - Name: .gnu.version + Type: SHT_GNU_versym +... +--- !WASM +FileHeader: + Version: 0x00000001 +Sections: + - Type: DATA + Segments: + - SectionOffset: 6 + InitFlags: 0 + Offset: + Opcode: GLOBAL_GET + Index: 1 + Content: '64' +... +--- !XCOFF +FileHeader: + MagicNumber: 0x01DF + CreationTime: 1 + EntriesInSymbolTable: 1 + +... diff --git a/llvm/tools/llvm-readobj/MachODumper.cpp b/llvm/tools/llvm-readobj/MachODumper.cpp --- a/llvm/tools/llvm-readobj/MachODumper.cpp +++ b/llvm/tools/llvm-readobj/MachODumper.cpp @@ -13,6 +13,7 @@ #include "ObjDumper.h" #include "StackMapPrinter.h" #include "llvm-readobj.h" +#include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Object/MachO.h" @@ -39,6 +40,11 @@ void printNeededLibraries() override; + bool canCompareSymbols() const override { return true; } + bool compareSymbolsByName(object::SymbolRef LHS, + object::SymbolRef RHS) const override; + bool compareSymbolsByType(object::SymbolRef LHS, + object::SymbolRef RHS) const override; // MachO-specific. void printMachODataInCode() override; void printMachOVersionMin() override; @@ -51,10 +57,12 @@ template void printFileHeaders(const MachHeader &Header); - StringRef getSymbolName(const SymbolRef &Symbol); + StringRef getSymbolName(const SymbolRef &Symbol) const; + uint8_t getSymbolType(const SymbolRef &Symbol) const; - void printSymbols() override; - void printDynamicSymbols() override; + void printSymbols(Optional SymComp) override; + void printDynamicSymbols(Optional SymComp) override; + void printSymbol(const SymbolRef &Symbol, ScopedPrinter &W); void printSymbol(const SymbolRef &Symbol); void printRelocation(const RelocationRef &Reloc); @@ -602,7 +610,7 @@ } } -StringRef MachODumper::getSymbolName(const SymbolRef &Symbol) { +StringRef MachODumper::getSymbolName(const SymbolRef &Symbol) const { Expected SymbolNameOrErr = Symbol.getName(); if (!SymbolNameOrErr) { reportError(SymbolNameOrErr.takeError(), Obj->getFileName()); @@ -610,19 +618,42 @@ return *SymbolNameOrErr; } -void MachODumper::printSymbols() { - ListScope Group(W, "Symbols"); +uint8_t MachODumper::getSymbolType(const SymbolRef &Symbol) const { + return Obj->getSymbol64TableEntry(Symbol.getRawDataRefImpl()).n_type; +} + +bool MachODumper::compareSymbolsByName(SymbolRef LHS, SymbolRef RHS) const { + return getSymbolName(LHS).str().compare(getSymbolName(RHS).str()) < 0; +} + +bool MachODumper::compareSymbolsByType(SymbolRef LHS, SymbolRef RHS) const { + return getSymbolType(LHS) < getSymbolType(RHS); +} - for (const SymbolRef &Symbol : Obj->symbols()) { - printSymbol(Symbol); +void MachODumper::printSymbols(Optional SymComp) { + if (SymComp) { + auto SymbolRange = Obj->symbols(); + std::vector SortedSymbols(SymbolRange.begin(), + SymbolRange.end()); + llvm::stable_sort(SortedSymbols, *SymComp); + for (SymbolRef Symbol : SortedSymbols) + printSymbol(Symbol); + } else { + for (const SymbolRef &Symbol : Obj->symbols()) { + printSymbol(Symbol); + } } } -void MachODumper::printDynamicSymbols() { +void MachODumper::printDynamicSymbols(Optional SymComp) { ListScope Group(W, "DynamicSymbols"); } void MachODumper::printSymbol(const SymbolRef &Symbol) { + printSymbol(Symbol, W); +} + +void MachODumper::printSymbol(const SymbolRef &Symbol, ScopedPrinter &W) { StringRef SymbolName = getSymbolName(Symbol); MachOSymbol MOSymbol; diff --git a/llvm/tools/llvm-readobj/ObjDumper.h b/llvm/tools/llvm-readobj/ObjDumper.h --- a/llvm/tools/llvm-readobj/ObjDumper.h +++ b/llvm/tools/llvm-readobj/ObjDumper.h @@ -12,6 +12,10 @@ #include #include +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/STLFunctionalExtras.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" #include "llvm/Object/ObjectFile.h" #include "llvm/Support/CommandLine.h" @@ -25,7 +29,7 @@ class ObjectFile; class XCOFFObjectFile; class ELFObjectFileBase; -} +} // namespace object namespace codeview { class GlobalTypeTableBuilder; class MergingTypeTableBuilder; @@ -33,6 +37,33 @@ class ScopedPrinter; +// Comparator to compare symbols. +// Usage: the caller registers predicates (i.e., how to compare the symbols) by +// calling addPredicate(). The order in which predicates are registered is also +// their priority. +class SymbolComparator { +public: + using CompPredicate = + function_ref; + + // Each Obj format has a slightly different way of retrieving a symbol's info + // So we defer the predicate's impl to each format. + void addPredicate(CompPredicate Pred) { Predicates.push_back(Pred); } + + bool operator()(object::SymbolRef LHS, object::SymbolRef RHS) { + for (CompPredicate Pred : Predicates) { + if (Pred(LHS, RHS)) + return true; + if (Pred(RHS, LHS)) + return false; + } + return false; + } + +private: + SmallVector Predicates; +}; + class ObjDumper { public: ObjDumper(ScopedPrinter &Writer, StringRef ObjName); @@ -47,10 +78,14 @@ virtual void printSectionHeaders() = 0; virtual void printRelocations() = 0; virtual void printSymbols(bool PrintSymbols, bool PrintDynamicSymbols) { + printSymbols(PrintSymbols, PrintDynamicSymbols, llvm::None); + } + virtual void printSymbols(bool PrintSymbols, bool PrintDynamicSymbols, + llvm::Optional SymComp) { if (PrintSymbols) - printSymbols(); + printSymbols(SymComp); if (PrintDynamicSymbols) - printDynamicSymbols(); + printDynamicSymbols(SymComp); } virtual void printProgramHeaders(bool PrintProgramHeaders, cl::boolOrDefault PrintSectionMapping) { @@ -62,6 +97,17 @@ virtual void printUnwindInfo() = 0; + // Symbol comparison functions. + virtual bool canCompareSymbols() const { return false; } + virtual bool compareSymbolsByName(object::SymbolRef LHS, + object::SymbolRef RHS) const { + return true; + } + virtual bool compareSymbolsByType(object::SymbolRef LHS, + object::SymbolRef RHS) const { + return true; + } + // Only implemented for ELF at this time. virtual void printDependentLibs() {} virtual void printDynamicRelocations() { } @@ -132,8 +178,10 @@ ScopedPrinter &W; private: - virtual void printSymbols() {} - virtual void printDynamicSymbols() {} + virtual void printSymbols() { printSymbols(llvm::None); } + virtual void printSymbols(llvm::Optional Comp) {} + virtual void printDynamicSymbols() { printDynamicSymbols(llvm::None); } + virtual void printDynamicSymbols(llvm::Optional Comp) {} virtual void printProgramHeaders() {} virtual void printSectionMapping() {} diff --git a/llvm/tools/llvm-readobj/Opts.td b/llvm/tools/llvm-readobj/Opts.td --- a/llvm/tools/llvm-readobj/Opts.td +++ b/llvm/tools/llvm-readobj/Opts.td @@ -37,6 +37,7 @@ def section_mapping_EQ_false : FF<"section-mapping=false", "Don't display the section to segment mapping">, Flags<[HelpHidden]>; def section_relocations : FF<"section-relocations", "Display relocations for each section shown. This option has no effect for GNU style output">; def section_symbols : FF<"section-symbols", "Display symbols for each section shown. This option has no effect for GNU style output">; +defm sort_symbols : Eq<"sort-symbols", "Specify the keys to sort the symbols before displaying symtab">; def stack_sizes : FF<"stack-sizes", "Display contents of all stack sizes sections. This option has no effect for GNU style output">; def stackmap : FF<"stackmap", "Display contents of stackmap section">; defm string_dump : Eq<"string-dump", "Display the specified section(s) as a list of strings">, MetaVarName<"">; diff --git a/llvm/tools/llvm-readobj/llvm-readobj.h b/llvm/tools/llvm-readobj/llvm-readobj.h --- a/llvm/tools/llvm-readobj/llvm-readobj.h +++ b/llvm/tools/llvm-readobj/llvm-readobj.h @@ -9,10 +9,13 @@ #ifndef LLVM_TOOLS_LLVM_READOBJ_LLVM_READOBJ_H #define LLVM_TOOLS_LLVM_READOBJ_LLVM_READOBJ_H +#include "ObjDumper.h" + +#include "llvm/ADT/SmallVector.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Compiler.h" -#include "llvm/Support/ErrorOr.h" #include "llvm/Support/Error.h" +#include "llvm/Support/ErrorOr.h" #include namespace llvm { diff --git a/llvm/tools/llvm-readobj/llvm-readobj.cpp b/llvm/tools/llvm-readobj/llvm-readobj.cpp --- a/llvm/tools/llvm-readobj/llvm-readobj.cpp +++ b/llvm/tools/llvm-readobj/llvm-readobj.cpp @@ -21,6 +21,7 @@ #include "llvm-readobj.h" #include "ObjDumper.h" #include "WindowsResourceDumper.h" +#include "llvm/ADT/Optional.h" #include "llvm/DebugInfo/CodeView/GlobalTypeTableBuilder.h" #include "llvm/DebugInfo/CodeView/MergingTypeTableBuilder.h" #include "llvm/MC/TargetRegistry.h" @@ -83,6 +84,14 @@ }; enum OutputFormatTy { bsd, sysv, posix, darwin, just_symbols }; + +enum SortSymbolKeyTy { + NAME = 0, + TYPE = 1, + UNKNOWN = 100, + // TODO: add ADDRESS, SIZE as needed. +}; + } // namespace namespace opts { @@ -113,6 +122,7 @@ static bool Symbols; static bool UnwindInfo; static cl::boolOrDefault SectionMapping; +static SmallVector SortKeys; // ELF specific options. static bool DynamicTable; @@ -253,6 +263,19 @@ opts::ProgramHeaders = Args.hasArg(OPT_program_headers); opts::RawRelr = Args.hasArg(OPT_raw_relr); opts::SectionGroups = Args.hasArg(OPT_section_groups); + if (Arg *A = Args.getLastArg(OPT_sort_symbols_EQ)) { + std::string SortKeysString = A->getValue(); + for (StringRef KeyStr : llvm::split(A->getValue(), ",")) { + SortSymbolKeyTy KeyType = StringSwitch(KeyStr) + .Case("name", SortSymbolKeyTy::NAME) + .Case("type", SortSymbolKeyTy::TYPE) + .Default(SortSymbolKeyTy::UNKNOWN); + if (KeyType == SortSymbolKeyTy::UNKNOWN) + error("--sort-symbols value should be 'name' or 'type', but was '" + + Twine(KeyStr) + "'"); + opts::SortKeys.push_back(KeyType); + } + } opts::VersionInfo = Args.hasArg(OPT_version_info); // Mach-O specific options. @@ -334,11 +357,39 @@ toString(std::move(ContentErr)); ObjDumper *Dumper; + Optional SymComp; Expected> DumperOrErr = createDumper(Obj, Writer); if (!DumperOrErr) reportError(DumperOrErr.takeError(), FileStr); Dumper = (*DumperOrErr).get(); + if (!opts::SortKeys.empty()) { + if (Dumper->canCompareSymbols()) { + SymComp = SymbolComparator(); + for (SortSymbolKeyTy Key : opts::SortKeys) { + switch (Key) { + case NAME: + SymComp->addPredicate([Dumper](SymbolRef LHS, SymbolRef RHS) { + return Dumper->compareSymbolsByName(LHS, RHS); + }); + break; + case TYPE: + SymComp->addPredicate([Dumper](SymbolRef LHS, SymbolRef RHS) { + return Dumper->compareSymbolsByType(LHS, RHS); + }); + break; + case UNKNOWN: + llvm_unreachable("Unsupported sort key"); + } + } + + } else { + reportWarning(createStringError( + errc::invalid_argument, + "--sort-symbols is not supported yet for this format"), + FileStr); + } + } Dumper->printFileSummary(FileStr, Obj, opts::InputFilenames, A); if (opts::FileHeaders) @@ -374,7 +425,7 @@ if (opts::UnwindInfo) Dumper->printUnwindInfo(); if (opts::Symbols || opts::DynamicSymbols) - Dumper->printSymbols(opts::Symbols, opts::DynamicSymbols); + Dumper->printSymbols(opts::Symbols, opts::DynamicSymbols, SymComp); if (!opts::StringDump.empty()) Dumper->printSectionsAsString(Obj, opts::StringDump); if (!opts::HexDump.empty())