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 + + Sort symbols before displaying symtab. + .. 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,252 @@ +## Verify that llvm-readobj can dump files with stabs symbols in a sorted order, + +# RUN: yaml2obj %s -o %t +# RUN: llvm-readobj --syms --sort-symbols %t | FileCheck %s --strict-whitespace --match-full-lines + +# CHECK:File: {{.+}} +# CHECK-NEXT:Format: Mach-O {{.+}} +# CHECK-NEXT:Arch: {{.+}} +# CHECK-NEXT:AddressSize: {{.+}}bit +# CHECK-NEXT:Symbols [ +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: _a (19) +# CHECK-NEXT: Type: Section (0xE) +# CHECK-NEXT: Section: __data (0x3) +# CHECK-NEXT: RefType: UndefinedNonLazy (0x0) +# CHECK-NEXT: Flags [ (0x0) +# CHECK-NEXT: ] +# CHECK-NEXT: Value: 0x11 +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: _d (10) +# CHECK-NEXT: Type: Section (0xE) +# CHECK-NEXT: Section: __text (0x1) +# CHECK-NEXT: RefType: UndefinedNonLazy (0x0) +# CHECK-NEXT: Flags [ (0x0) +# CHECK-NEXT: ] +# CHECK-NEXT: Value: 0x8 +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: _f (7) +# CHECK-NEXT: Type: SymDebugTable (0x2E) +# CHECK-NEXT: Section: (0x3) +# CHECK-NEXT: RefType: UndefinedNonLazy (0x0) +# CHECK-NEXT: Flags [ (0x0) +# CHECK-NEXT: ] +# CHECK-NEXT: Value: 0x19 +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: _z (1) +# CHECK-NEXT: Type: SymDebugTable (0x2E) +# CHECK-NEXT: Section: (0x0) +# CHECK-NEXT: RefType: UndefinedNonLazy (0x0) +# CHECK-NEXT: Flags [ (0x0) +# CHECK-NEXT: ] +# CHECK-NEXT: Value: 0x0 +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: _c (13) +# CHECK-NEXT: Type: SymDebugTable (0x64) +# CHECK-NEXT: Section: (0x4) +# CHECK-NEXT: RefType: UndefinedNonLazy (0x0) +# CHECK-NEXT: Flags [ (0x0) +# CHECK-NEXT: ] +# CHECK-NEXT: Value: 0x1C +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: _g (4) +# CHECK-NEXT: Type: SymDebugTable (0x64) +# CHECK-NEXT: Section: (0x1) +# CHECK-NEXT: RefType: UndefinedNonLazy (0x0) +# CHECK-NEXT: Flags [ (0x0) +# CHECK-NEXT: ] +# CHECK-NEXT: Value: 0x0 +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: _b (16) +# CHECK-NEXT: Type: SymDebugTable (0x66) +# CHECK-NEXT: Section: (0x2) +# CHECK-NEXT: RefType: UndefinedNonLazy (0x0) +# CHECK-NEXT: Flags [ (0x0) +# CHECK-NEXT: ] +# CHECK-NEXT: Value: 0x9 +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: _d2 (22) +# CHECK-NEXT: Type: SymDebugTable (0x66) +# CHECK-NEXT: Section: (0x1) +# CHECK-NEXT: RefType: UndefinedNonLazy (0x0) +# CHECK-NEXT: Flags [ (0x0) +# CHECK-NEXT: ] +# CHECK-NEXT: Value: 0x8 +# CHECK-NEXT: } +# CHECK-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 @@ -17,7 +17,12 @@ #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 +#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,65 @@ } } -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() are temporary + // and we can't hold onto them after the loop. In order to sort the symbols, + // we have to print their representation to string and sort them later. + + // Tuple of + // Sort by type (local/external/undefined), then name. + std::vector> SortedSymbols; + for (SymbolRef Symbol : Obj->symbols()) { + std::string SymRep; + raw_string_ostream StringOs(SymRep); + formatted_raw_ostream FormattedOs(StringOs); + ScopedPrinter Printer(FormattedOs); + Printer.indent(W.getIndentLevel()); + printSymbol(Symbol, Printer); + + const DataRefImpl &DRI = Symbol.getRawDataRefImpl(); + SortedSymbols.emplace_back(Obj->getSymbol64TableEntry(DRI).n_type, + Symbol.getName()->str(), std::move(SymRep)); + } + + llvm::stable_sort( + SortedSymbols, + [](const std::tuple &LHS, + const std::tuple &RHS) { + uint8_t LType = std::get<0>(LHS); + uint8_t RType = std::get<0>(RHS); + + if (LType == RType) + return std::get<1>(LHS).compare(std::get<1>(RHS)) < 0; + return LType < RType; + // TODO:: Maybe use Symbol's addresses as tie-breaker if + // names are the same. + }); + + // Unindent one level because the Symbols were already padded. + W.unindent(); + for (auto &Tup : SortedSymbols) + W.startLine() << std::get<2>(Tup); + // Restore previous indentation. + W.indent(); + } else { + for (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 @@ -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">; +def sort_symbols : FF<"sort-symbols", "Sort 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.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; +static bool SortSymbols; // ELF specific options. static bool DynamicTable; @@ -253,6 +254,7 @@ opts::ProgramHeaders = Args.hasArg(OPT_program_headers); opts::RawRelr = Args.hasArg(OPT_raw_relr); opts::SectionGroups = Args.hasArg(OPT_section_groups); + opts::SortSymbols = Args.hasArg(OPT_sort_symbols); opts::VersionInfo = Args.hasArg(OPT_version_info); // Mach-O specific options. @@ -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())