diff --git a/llvm/test/tools/llvm-readobj/MachO/stabs.yaml b/llvm/test/tools/llvm-readobj/MachO/stabs.yaml --- a/llvm/test/tools/llvm-readobj/MachO/stabs.yaml +++ b/llvm/test/tools/llvm-readobj/MachO/stabs.yaml @@ -53,6 +53,56 @@ # CHECK-NEXT: } # CHECK-NEXT: ] +# RUN: llvm-readobj --syms --sort-symbols %t | FileCheck %s --check-prefix=SORTED + +# SORTED: Symbols [ +# SORTED-NEXT: Symbol { +# SORTED-NEXT: Name: (1) +# SORTED-NEXT: Type: SymDebugTable (0x2E) +# SORTED-NEXT: Section: (0x1) +# SORTED-NEXT: RefType: UndefinedNonLazy (0x0) +# SORTED-NEXT: Flags [ (0x0) +# SORTED-NEXT: ] +# SORTED-NEXT: Value: 0x100000FA0 +# SORTED-NEXT:} +# SORTED-NEXT: Symbol { +# SORTED-NEXT: Name: (1) +# SORTED-NEXT: Type: SymDebugTable (0x4E) +# SORTED-NEXT: Section: (0x1) +# SORTED-NEXT: RefType: UndefinedNonLazy (0x0) +# SORTED-NEXT: Flags [ (0x0) +# SORTED-NEXT: ] +# SORTED-NEXT: Value: 0xF +# SORTED-NEXT:} +# SORTED-NEXT: Symbol { +# SORTED-NEXT: Name: /Volumes/Sandbox/ (2) +# SORTED-NEXT: Type: SymDebugTable (0x64) +# SORTED-NEXT: Section: (0x0) +# SORTED-NEXT: RefType: UndefinedNonLazy (0x0) +# SORTED-NEXT: Flags [ (0x0) +# SORTED-NEXT: ] +# SORTED-NEXT: Value: 0x0 +# SORTED-NEXT:} +# SORTED-NEXT: Symbol { +# SORTED-NEXT: Name: /Volumes/Sandbox/test.o (20) +# SORTED-NEXT: Type: SymDebugTable (0x66) +# SORTED-NEXT: Section: (0x3) +# SORTED-NEXT: RefType: ReferenceFlagUndefinedLazy (0x1) +# SORTED-NEXT: Flags [ (0x0) +# SORTED-NEXT: ] +# SORTED-NEXT: Value: 0x5F72D5E2 +# SORTED-NEXT:} +# SORTED-NEXT: Symbol { +# SORTED-NEXT: Name: _foo (44) +# SORTED-NEXT: Type: SymDebugTable (0x24) +# SORTED-NEXT: Section: (0x1) +# SORTED-NEXT: RefType: UndefinedNonLazy (0x0) +# SORTED-NEXT: Flags [ (0x0) +# SORTED-NEXT: ] +# SORTED-NEXT: Value: 0x100000FA0 +# SORTED-NEXT:} +# SORTED-NEXT:] + --- !mach-o FileHeader: magic: 0xFEEDFACF 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,11 +13,16 @@ #include "ObjDumper.h" #include "StackMapPrinter.h" #include "llvm-readobj.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Object/MachO.h" #include "llvm/Support/Casting.h" +#include "llvm/Support/FormattedStream.h" #include "llvm/Support/ScopedPrinter.h" +#include "llvm/Support/raw_ostream.h" + +#include using namespace llvm; using namespace object; @@ -49,8 +54,9 @@ template void printFileHeaders(const MachHeader &Header); - void printSymbols() override; - void printDynamicSymbols() override; + void printSymbols(bool SortSymbols) override; + void printDynamicSymbols(bool SortSymbols) override; + void printSymbol(const SymbolRef &Symbol, ScopedPrinter &W); void printSymbol(const SymbolRef &Symbol); void printRelocation(const RelocationRef &Reloc); @@ -601,19 +607,59 @@ } } -void MachODumper::printSymbols() { +void MachODumper::printSymbols(bool SortSymbols) { ListScope Group(W, "Symbols"); - for (const SymbolRef &Symbol : Obj->symbols()) { - printSymbol(Symbol); + if (SortSymbols) { + // The references returned by calling Obj->symbols() is temporary + // and we can't hold on it them after the loop. + // So in order to sort the symbols, we have to print their representation + // to string and sort them later. + + // Pair of + std::vector> SortedSymbols; + for (const SymbolRef &Symbol : Obj->symbols()) { + std::string SymRep; + raw_string_ostream StringOs(SymRep); + llvm::formatted_raw_ostream FormattedOs(StringOs); + std::unique_ptr Printer; + + if (opts::Output == opts::JSON) + Printer = std::make_unique( + FormattedOs, opts::PrettyPrint ? 2 : 0, + std::make_unique()); + else + Printer = std::make_unique(FormattedOs); + printSymbol(Symbol, *Printer); + + SortedSymbols.emplace_back(Symbol.getName()->str(), std::move(SymRep)); + } + + llvm::stable_sort(SortedSymbols, + [](std::pair LHS, + std::pair RHS) { + return LHS.first.compare(RHS.first) < 0; + // FIXME: Maybe use Symbol's addresses as tie-breaker if + // names are the same. + }); + + for (auto &pair : SortedSymbols) + W.startLine() << pair.second; + } else { + for (const SymbolRef &Symbol : Obj->symbols()) + printSymbol(Symbol); } } -void MachODumper::printDynamicSymbols() { +void MachODumper::printDynamicSymbols(bool SortSymbols) { ListScope Group(W, "DynamicSymbols"); } void MachODumper::printSymbol(const SymbolRef &Symbol) { + printSymbol(Symbol, W); +} + +void MachODumper::printSymbol(const SymbolRef &Symbol, ScopedPrinter &W) { StringRef SymbolName; Expected SymbolNameOrErr = Symbol.getName(); if (!SymbolNameOrErr) { 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 @@ -47,10 +47,14 @@ virtual void printSectionHeaders() = 0; virtual void printRelocations() = 0; virtual void printSymbols(bool PrintSymbols, bool PrintDynamicSymbols) { + printSymbols(PrintSymbols, PrintDynamicSymbols, false); + } + virtual void printSymbols(bool PrintSymbols, bool PrintDynamicSymbols, + bool SortSymbols) { if (PrintSymbols) - printSymbols(); + printSymbols(SortSymbols); if (PrintDynamicSymbols) - printDynamicSymbols(); + printDynamicSymbols(SortSymbols); } virtual void printProgramHeaders(bool PrintProgramHeaders, cl::boolOrDefault PrintSectionMapping) { @@ -132,8 +136,10 @@ ScopedPrinter &W; private: - virtual void printSymbols() {} - virtual void printDynamicSymbols() {} + virtual void printSymbols() { printSymbols(false); } + virtual void printSymbols(bool SortSymbols) {} + virtual void printDynamicSymbols() { printDynamicSymbols(false); } + virtual void printDynamicSymbols(bool SortSymbols) {} 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 @@ -43,6 +43,7 @@ def string_table : FF<"string-table", "Display the string table (only for XCOFF now)">; def symbols : FF<"symbols", "Display the symbol table. Also display the dynamic symbol table when using GNU output style for ELF">; def unwind : FF<"unwind", "Display unwind information">; +def sort_symbols : FF<"sort-symbols", "Sort symbol before outputing symtab">; // ELF specific options. def grp_elf : OptionGroup<"kind">, HelpText<"OPTIONS (ELF specific)">; 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 @@ -39,6 +39,7 @@ extern bool RawRelr; extern bool CodeViewSubsectionBytes; extern bool Demangle; +extern bool PrettyPrint; enum OutputStyleTy { LLVM, GNU, JSON, UNKNOWN }; extern OutputStyleTy Output; } // namespace opts 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 @@ -99,7 +99,7 @@ static bool FileHeaders; static bool Headers; static std::vector HexDump; -static bool PrettyPrint; +bool PrettyPrint; static bool PrintStackMap; static bool PrintStackSizes; static bool Relocations; @@ -113,6 +113,7 @@ static bool Symbols; static bool UnwindInfo; static cl::boolOrDefault SectionMapping; +static bool SortSymbols; // ELF specific options. static bool DynamicTable; @@ -254,6 +255,7 @@ opts::RawRelr = Args.hasArg(OPT_raw_relr); opts::SectionGroups = Args.hasArg(OPT_section_groups); opts::VersionInfo = Args.hasArg(OPT_version_info); + opts::SortSymbols = Args.hasArg(OPT_sort_symbols); // Mach-O specific options. opts::MachODataInCode = Args.hasArg(OPT_macho_data_in_code); @@ -374,7 +376,8 @@ if (opts::UnwindInfo) Dumper->printUnwindInfo(); if (opts::Symbols || opts::DynamicSymbols) - Dumper->printSymbols(opts::Symbols, opts::DynamicSymbols); + Dumper->printSymbols(opts::Symbols, opts::DynamicSymbols, + opts::SortSymbols); if (!opts::StringDump.empty()) Dumper->printSectionsAsString(Obj, opts::StringDump); if (!opts::HexDump.empty())