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,342 @@ +## Verify that llvm-readobj can dump files with stabs symbols in a sorted order, + +# RUN: yaml2obj %s -o %t + +# RUN: not llvm-readobj --syms --sort-symbols=foo %t 2>&1 | FileCheck %s --check-prefix ERR-KEY +# RUN: llvm-readobj --syms --sort-symbols=,, %t 2>&1 | FileCheck %s --check-prefix WARN-KEY + +# RUN: llvm-readobj --syms --sort-symbols=type,name %t | FileCheck %s --strict-whitespace --match-full-lines --check-prefix TYPE-NAME +# RUN: llvm-readobj --syms --sort-symbols=name,type %t | FileCheck %s --strict-whitespace --match-full-lines --check-prefix NAME-TYPE + +# ERR-KEY: error: --sort-symbols value should be 'name' or 'type', but was 'foo' + +# WARN-KEY: warning: ignored empty sort key specified in --sort-symbols=',,' at position 0 +# WARN-KEY-NEXT: warning: ignored empty sort key specified in --sort-symbols=',,' at position 1 +# WARN-KEY-NEXT: warning: ignored empty sort key specified in --sort-symbols=',,' at position 2 +# WARN-KEY: File: {{.+}} + +# TYPE-NAME:File: {{.+}} +# TYPE-NAME-NEXT:Format: Mach-O {{.+}} +# TYPE-NAME-NEXT:Arch: {{.+}} +# TYPE-NAME-NEXT:AddressSize: {{.+}}bit +# TYPE-NAME-NEXT:Symbols [ +# TYPE-NAME-NEXT: Symbol { +# TYPE-NAME-NEXT: Name: _a (19) +# TYPE-NAME-NEXT: Type: Section (0xE) +# TYPE-NAME-NEXT: Section: __data (0x3) +# TYPE-NAME-NEXT: RefType: UndefinedNonLazy (0x0) +# TYPE-NAME-NEXT: Flags [ (0x0) +# TYPE-NAME-NEXT: ] +# TYPE-NAME-NEXT: Value: 0x11 +# TYPE-NAME-NEXT: } +# TYPE-NAME-NEXT: Symbol { +# TYPE-NAME-NEXT: Name: _d (10) +# TYPE-NAME-NEXT: Type: Section (0xE) +# TYPE-NAME-NEXT: Section: __text (0x1) +# TYPE-NAME-NEXT: RefType: UndefinedNonLazy (0x0) +# TYPE-NAME-NEXT: Flags [ (0x0) +# TYPE-NAME-NEXT: ] +# TYPE-NAME-NEXT: Value: 0x8 +# TYPE-NAME-NEXT: } +# TYPE-NAME-NEXT: Symbol { +# TYPE-NAME-NEXT: Name: _f (7) +# TYPE-NAME-NEXT: Type: SymDebugTable (0x2E) +# TYPE-NAME-NEXT: Section: (0x3) +# TYPE-NAME-NEXT: RefType: UndefinedNonLazy (0x0) +# TYPE-NAME-NEXT: Flags [ (0x0) +# TYPE-NAME-NEXT: ] +# TYPE-NAME-NEXT: Value: 0x19 +# TYPE-NAME-NEXT: } +# TYPE-NAME-NEXT: Symbol { +# TYPE-NAME-NEXT: Name: _z (1) +# TYPE-NAME-NEXT: Type: SymDebugTable (0x2E) +# TYPE-NAME-NEXT: Section: (0x0) +# TYPE-NAME-NEXT: RefType: UndefinedNonLazy (0x0) +# TYPE-NAME-NEXT: Flags [ (0x0) +# TYPE-NAME-NEXT: ] +# TYPE-NAME-NEXT: Value: 0x0 +# TYPE-NAME-NEXT: } +# TYPE-NAME-NEXT: Symbol { +# TYPE-NAME-NEXT: Name: _c (13) +# TYPE-NAME-NEXT: Type: SymDebugTable (0x64) +# TYPE-NAME-NEXT: Section: (0x4) +# TYPE-NAME-NEXT: RefType: UndefinedNonLazy (0x0) +# TYPE-NAME-NEXT: Flags [ (0x0) +# TYPE-NAME-NEXT: ] +# TYPE-NAME-NEXT: Value: 0x1C +# TYPE-NAME-NEXT: } +# TYPE-NAME-NEXT: Symbol { +# TYPE-NAME-NEXT: Name: _g (4) +# TYPE-NAME-NEXT: Type: SymDebugTable (0x64) +# TYPE-NAME-NEXT: Section: (0x1) +# TYPE-NAME-NEXT: RefType: UndefinedNonLazy (0x0) +# TYPE-NAME-NEXT: Flags [ (0x0) +# TYPE-NAME-NEXT: ] +# TYPE-NAME-NEXT: Value: 0x0 +# TYPE-NAME-NEXT: } +# TYPE-NAME-NEXT: Symbol { +# TYPE-NAME-NEXT: Name: _b (16) +# TYPE-NAME-NEXT: Type: SymDebugTable (0x66) +# TYPE-NAME-NEXT: Section: (0x2) +# TYPE-NAME-NEXT: RefType: UndefinedNonLazy (0x0) +# TYPE-NAME-NEXT: Flags [ (0x0) +# TYPE-NAME-NEXT: ] +# TYPE-NAME-NEXT: Value: 0x9 +# TYPE-NAME-NEXT: } +# TYPE-NAME-NEXT: Symbol { +# TYPE-NAME-NEXT: Name: _d2 (22) +# TYPE-NAME-NEXT: Type: SymDebugTable (0x66) +# TYPE-NAME-NEXT: Section: (0x1) +# TYPE-NAME-NEXT: RefType: UndefinedNonLazy (0x0) +# TYPE-NAME-NEXT: Flags [ (0x0) +# TYPE-NAME-NEXT: ] +# TYPE-NAME-NEXT: Value: 0x8 +# TYPE-NAME-NEXT: } +# TYPE-NAME-NEXT:] + +# NAME-TYPE:File: {{.+}} +# NAME-TYPE-NEXT:Format: Mach-O 64-bit x86-64 +# NAME-TYPE-NEXT:Arch: x86_64 +# NAME-TYPE-NEXT:AddressSize: 64bit +# NAME-TYPE-NEXT:Symbols [ +# NAME-TYPE-NEXT: Symbol { +# NAME-TYPE-NEXT: Name: _a (19) +# NAME-TYPE-NEXT: Type: Section (0xE) +# NAME-TYPE-NEXT: Section: __data (0x3) +# NAME-TYPE-NEXT: RefType: UndefinedNonLazy (0x0) +# NAME-TYPE-NEXT: Flags [ (0x0) +# NAME-TYPE-NEXT: ] +# NAME-TYPE-NEXT: Value: 0x11 +# NAME-TYPE-NEXT: } +# NAME-TYPE-NEXT: Symbol { +# NAME-TYPE-NEXT: Name: _b (16) +# NAME-TYPE-NEXT: Type: SymDebugTable (0x66) +# NAME-TYPE-NEXT: Section: (0x2) +# NAME-TYPE-NEXT: RefType: UndefinedNonLazy (0x0) +# NAME-TYPE-NEXT: Flags [ (0x0) +# NAME-TYPE-NEXT: ] +# NAME-TYPE-NEXT: Value: 0x9 +# NAME-TYPE-NEXT: } +# NAME-TYPE-NEXT: Symbol { +# NAME-TYPE-NEXT: Name: _c (13) +# NAME-TYPE-NEXT: Type: SymDebugTable (0x64) +# NAME-TYPE-NEXT: Section: (0x4) +# NAME-TYPE-NEXT: RefType: UndefinedNonLazy (0x0) +# NAME-TYPE-NEXT: Flags [ (0x0) +# NAME-TYPE-NEXT: ] +# NAME-TYPE-NEXT: Value: 0x1C +# NAME-TYPE-NEXT: } +# NAME-TYPE-NEXT: Symbol { +# NAME-TYPE-NEXT: Name: _d (10) +# NAME-TYPE-NEXT: Type: Section (0xE) +# NAME-TYPE-NEXT: Section: __text (0x1) +# NAME-TYPE-NEXT: RefType: UndefinedNonLazy (0x0) +# NAME-TYPE-NEXT: Flags [ (0x0) +# NAME-TYPE-NEXT: ] +# NAME-TYPE-NEXT: Value: 0x8 +# NAME-TYPE-NEXT: } +# NAME-TYPE-NEXT: Symbol { +# NAME-TYPE-NEXT: Name: _d2 (22) +# NAME-TYPE-NEXT: Type: SymDebugTable (0x66) +# NAME-TYPE-NEXT: Section: (0x1) +# NAME-TYPE-NEXT: RefType: UndefinedNonLazy (0x0) +# NAME-TYPE-NEXT: Flags [ (0x0) +# NAME-TYPE-NEXT: ] +# NAME-TYPE-NEXT: Value: 0x8 +# NAME-TYPE-NEXT: } +# NAME-TYPE-NEXT: Symbol { +# NAME-TYPE-NEXT: Name: _f (7) +# NAME-TYPE-NEXT: Type: SymDebugTable (0x2E) +# NAME-TYPE-NEXT: Section: (0x3) +# NAME-TYPE-NEXT: RefType: UndefinedNonLazy (0x0) +# NAME-TYPE-NEXT: Flags [ (0x0) +# NAME-TYPE-NEXT: ] +# NAME-TYPE-NEXT: Value: 0x19 +# NAME-TYPE-NEXT: } +# NAME-TYPE-NEXT: Symbol { +# NAME-TYPE-NEXT: Name: _g (4) +# NAME-TYPE-NEXT: Type: SymDebugTable (0x64) +# NAME-TYPE-NEXT: Section: (0x1) +# NAME-TYPE-NEXT: RefType: UndefinedNonLazy (0x0) +# NAME-TYPE-NEXT: Flags [ (0x0) +# NAME-TYPE-NEXT: ] +# NAME-TYPE-NEXT: Value: 0x0 +# NAME-TYPE-NEXT: } +# NAME-TYPE-NEXT: Symbol { +# NAME-TYPE-NEXT: Name: _z (1) +# NAME-TYPE-NEXT: Type: SymDebugTable (0x2E) +# NAME-TYPE-NEXT: Section: (0x0) +# NAME-TYPE-NEXT: RefType: UndefinedNonLazy (0x0) +# NAME-TYPE-NEXT: Flags [ (0x0) +# NAME-TYPE-NEXT: ] +# NAME-TYPE-NEXT: Value: 0x0 +# NAME-TYPE-NEXT: } +# NAME-TYPE-NEXT:] +--- !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 + - '' + - '' + - '' + - '' + - '' + - '' +... 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 @@ -52,9 +52,13 @@ void printFileHeaders(const MachHeader &Header); StringRef getSymbolName(const SymbolRef &Symbol); + uint8_t getSymbolType(const SymbolRef &Symbol); - void printSymbols() override; - void printDynamicSymbols() override; + void printSymbols( + const llvm::SmallVector *SortKeys) override; + void printDynamicSymbols( + const llvm::SmallVector *SortKeys) override; + void printSymbol(const SymbolRef &Symbol, ScopedPrinter &W); void printSymbol(const SymbolRef &Symbol); void printRelocation(const RelocationRef &Reloc); @@ -610,19 +614,57 @@ return *SymbolNameOrErr; } -void MachODumper::printSymbols() { - ListScope Group(W, "Symbols"); +uint8_t MachODumper::getSymbolType(const SymbolRef &Symbol) { + return Obj->getSymbol64TableEntry(Symbol.getRawDataRefImpl()).n_type; +} + +void MachODumper::printSymbols( + const llvm::SmallVector *SortKeys) { + + if (SortKeys && !SortKeys->empty()) { + auto SymbolRange = Obj->symbols(); + std::vector SortedSymbols(SymbolRange.begin(), + SymbolRange.end()); + + SymbolComparator SymComp; + for (opts::SortSymbolKeyTy Key : *SortKeys) { + switch (Key) { + case opts::NAME: + SymComp.addPredicate([this](SymbolRef LHS, SymbolRef RHS) -> bool { + return this->getSymbolName(LHS).str().compare( + this->getSymbolName(RHS).str()) < 0; + }); + break; + case opts::TYPE: + SymComp.addPredicate([this](SymbolRef LHS, SymbolRef RHS) -> bool { + return this->getSymbolType(LHS) < this->getSymbolType(RHS); + }); + break; + default: + assert(false && "unsupported sort key"); + } + } + stable_sort(SortedSymbols, SymComp); - for (const SymbolRef &Symbol : Obj->symbols()) { - printSymbol(Symbol); + for (SymbolRef Symbol : SortedSymbols) + printSymbol(Symbol); + } else { + for (const SymbolRef &Symbol : Obj->symbols()) { + printSymbol(Symbol); + } } } -void MachODumper::printDynamicSymbols() { +void MachODumper::printDynamicSymbols( + const llvm::SmallVector *SortKeys) { 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,12 +12,24 @@ #include #include +#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" #include +namespace opts { +enum SortSymbolKeyTy { + NAME = 0, + TYPE = 1, + UNSPEC = 100, + // TODO: add ADDRESS, SIZE as needed. +}; +} // namespace opts + namespace llvm { namespace object { class Archive; @@ -33,6 +45,33 @@ class ScopedPrinter; +// Comparator to compare symbols. +// Usage: caller register predicates (ie., how to compare the symbols) by +// calling addPredicate(). The order in which predicates are registered is also +// their priority. +final 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: + llvm::SmallVector Predicates; +}; + class ObjDumper { public: ObjDumper(ScopedPrinter &Writer, StringRef ObjName); @@ -47,10 +86,15 @@ virtual void printSectionHeaders() = 0; virtual void printRelocations() = 0; virtual void printSymbols(bool PrintSymbols, bool PrintDynamicSymbols) { + printSymbols(PrintSymbols, PrintDynamicSymbols, nullptr); + } + virtual void + printSymbols(bool PrintSymbols, bool PrintDynamicSymbols, + const llvm::SmallVector *SortKeys) { if (PrintSymbols) - printSymbols(); + printSymbols(SortKeys); if (PrintDynamicSymbols) - printDynamicSymbols(); + printDynamicSymbols(SortKeys); } virtual void printProgramHeaders(bool PrintProgramHeaders, cl::boolOrDefault PrintSectionMapping) { @@ -132,8 +176,12 @@ ScopedPrinter &W; private: - virtual void printSymbols() {} - virtual void printDynamicSymbols() {} + virtual void printSymbols() { printSymbols(nullptr); } + virtual void + printSymbols(const llvm::SmallVector *SortKeys) {} + virtual void printDynamicSymbols() { printDynamicSymbols(nullptr); } + virtual void printDynamicSymbols( + const llvm::SmallVector *SortKeys) {} 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 { @@ -41,6 +44,7 @@ extern bool Demangle; enum OutputStyleTy { LLVM, GNU, JSON, UNKNOWN }; extern OutputStyleTy Output; +extern llvm::SmallVector SortKeys; } // namespace opts #define LLVM_READOBJ_ENUM_ENT(ns, enum) \ 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 @@ -113,6 +113,7 @@ static bool Symbols; static bool UnwindInfo; static cl::boolOrDefault SectionMapping; +llvm::SmallVector SortKeys; // ELF specific options. static bool DynamicTable; @@ -169,6 +170,11 @@ exit(1); } +static void warn(Twine Msg) { + fouts().flush(); + WithColor::warning(errs(), ToolName) << Msg << "\n"; +} + [[noreturn]] void reportError(Error Err, StringRef Input) { assert(Err); if (Input == "-") @@ -253,6 +259,28 @@ 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(); + int pos = 0; + for (llvm::StringRef KeyStr : llvm::split(A->getValue(), ",")) { + if (!KeyStr.empty()) { + opts::SortSymbolKeyTy KeyType = + StringSwitch(KeyStr) + .Case("name", opts::SortSymbolKeyTy::NAME) + .Case("type", opts::SortSymbolKeyTy::TYPE) + .Default(opts::SortSymbolKeyTy::UNSPEC); + if (KeyType == opts::SortSymbolKeyTy::UNSPEC) + error("--sort-symbols value should be 'name' or 'type', but was '" + + Twine(KeyStr) + "'"); + else + opts::SortKeys.push_back(KeyType); + } else { + warn("ignored empty sort key specified in --sort-symbols='" + + SortKeysString + "' at position " + Twine(pos)); + } + ++pos; + } + } opts::VersionInfo = Args.hasArg(OPT_version_info); // Mach-O specific options. @@ -374,7 +402,7 @@ if (opts::UnwindInfo) Dumper->printUnwindInfo(); if (opts::Symbols || opts::DynamicSymbols) - Dumper->printSymbols(opts::Symbols, opts::DynamicSymbols); + Dumper->printSymbols(opts::Symbols, opts::DynamicSymbols, &opts::SortKeys); if (!opts::StringDump.empty()) Dumper->printSectionsAsString(Obj, opts::StringDump); if (!opts::HexDump.empty())