From a8a34c5382a81950ca6586de672c80a9179738e5 Mon Sep 17 00:00:00 2001 From: Frederic Riss Date: Mon, 31 Aug 2015 00:29:09 +0000 Subject: [PATCH] [dsymutil] Implement -symtab/-s option. This option dumps the STAB entries that define the debug map(s) stored in the input binaries, and then exits. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@246403 91177308-0d34-0410-b5e6-96231b3b80d8 --- test/tools/dsymutil/dump-symtab.test | 44 +++++++ tools/dsymutil/MachODebugMapParser.cpp | 175 +++++++++++++++++++++++++ tools/dsymutil/dsymutil.cpp | 14 ++ tools/dsymutil/dsymutil.h | 4 + 4 files changed, 237 insertions(+) create mode 100644 test/tools/dsymutil/dump-symtab.test diff --git a/test/tools/dsymutil/dump-symtab.test b/test/tools/dsymutil/dump-symtab.test new file mode 100644 index 00000000000..b83ac7f7ad1 --- /dev/null +++ b/test/tools/dsymutil/dump-symtab.test @@ -0,0 +1,44 @@ +RUN: llvm-dsymutil -s %p/Inputs/fat-test.dylib | FileCheck -check-prefix=ALL -check-prefix=I386 %s +RUN: llvm-dsymutil -arch i386 -s %p/Inputs/fat-test.dylib | FileCheck -check-prefix=I386 -check-prefix=ONE %s + + +ALL: ---------------------------------------------------------------------- +ALL-NEXT: Symbol table for: '{{.*}}fat-test.dylib' (x86_64) +ALL-NEXT: ---------------------------------------------------------------------- +ALL-NEXT: Index n_strx n_type n_sect n_desc n_value +ALL-NEXT: ======== -------- ------------------ ------ ------ ---------------- +ALL-NEXT: [ 0] 00000002 64 (N_SO ) 00 0000 0000000000000000 '/Inputs/' +ALL-NEXT: [ 1] 0000000b 64 (N_SO ) 00 0000 0000000000000000 'fat-test.c' +ALL-NEXT: [ 2] 00000016 66 (N_OSO ) 03 0001 0000000055b1d0b9 '/Inputs/fat-test.o' +ALL-NEXT: [ 3] 00000029 20 (N_GSYM ) 00 0000 0000000000000000 '_x86_64_var' +ALL-NEXT: [ 4] 00000001 64 (N_SO ) 01 0000 0000000000000000 +ALL-NEXT: [ 5] 00000035 0f ( SECT EXT) 02 0000 0000000000001000 '_x86_64_var' +ALL-NEXT: [ 6] 00000041 01 ( UNDF EXT) 00 0100 0000000000000000 'dyld_stub_binder' + +I386: ---------------------------------------------------------------------- +I386-NEXT: Symbol table for: '{{.*}}fat-test.dylib' (i386) +I386-NEXT: ---------------------------------------------------------------------- +I386-NEXT: Index n_strx n_type n_sect n_desc n_value +I386-NEXT: ======== -------- ------------------ ------ ------ ---------------- +I386-NEXT: [ 0] 00000002 64 (N_SO ) 00 0000 0000000000000000 '/Inputs/' +I386-NEXT: [ 1] 0000000b 64 (N_SO ) 00 0000 0000000000000000 'fat-test.c' +I386-NEXT: [ 2] 00000016 66 (N_OSO ) 03 0001 0000000055b1d0b9 '/Inputs/fat-test.o' +I386-NEXT: [ 3] 00000029 20 (N_GSYM ) 00 0000 0000000000000000 '_i386_var' +I386-NEXT: [ 4] 00000001 64 (N_SO ) 01 0000 0000000000000000 +I386-NEXT: [ 5] 00000033 0f ( SECT EXT) 02 0000 0000000000001000 '_i386_var' +I386-NEXT: [ 6] 0000003d 01 ( UNDF EXT) 00 0100 0000000000000000 'dyld_stub_binder' + +ONE-NOT: Symbol table + +ALL: ---------------------------------------------------------------------- +ALL-NEXT: Symbol table for: '{{.*}}fat-test.dylib' (x86_64h) +ALL-NEXT: ---------------------------------------------------------------------- +ALL-NEXT: Index n_strx n_type n_sect n_desc n_value +ALL-NEXT: ======== -------- ------------------ ------ ------ ---------------- +ALL-NEXT: [ 0] 00000002 64 (N_SO ) 00 0000 0000000000000000 '/Inputs/' +ALL-NEXT: [ 1] 0000000b 64 (N_SO ) 00 0000 0000000000000000 'fat-test.c' +ALL-NEXT: [ 2] 00000016 66 (N_OSO ) 08 0001 0000000055b1d0b9 '/Inputs/fat-test.o' +ALL-NEXT: [ 3] 00000029 20 (N_GSYM ) 00 0000 0000000000000000 '_x86_64h_var' +ALL-NEXT: [ 4] 00000001 64 (N_SO ) 01 0000 0000000000000000 +ALL-NEXT: [ 5] 00000036 0f ( SECT EXT) 02 0000 0000000000001000 '_x86_64h_var' +ALL-NEXT: [ 6] 00000043 01 ( UNDF EXT) 00 0100 0000000000000000 'dyld_stub_binder' \ No newline at end of file diff --git a/tools/dsymutil/MachODebugMapParser.cpp b/tools/dsymutil/MachODebugMapParser.cpp index 8d17d527dfc..107bff7c5b8 100644 --- a/tools/dsymutil/MachODebugMapParser.cpp +++ b/tools/dsymutil/MachODebugMapParser.cpp @@ -34,6 +34,9 @@ public: /// or isn't of a supported type. ErrorOr>> parse(); + /// Walk the symbol table and dump it. + bool dumpStab(); + private: std::string BinaryPath; SmallVector Archs; @@ -74,6 +77,22 @@ private: handleStabSymbolTableEntry(STE.n_strx, STE.n_type, STE.n_sect, STE.n_desc, STE.n_value); } + + /// Dump the symbol table output header. + void dumpSymTabHeader(raw_ostream &OS, StringRef Arch); + + /// Dump the contents of nlist entries. + void dumpSymTabEntry(raw_ostream &OS, uint64_t Index, uint32_t StringIndex, + uint8_t Type, uint8_t SectionIndex, uint16_t Flags, + uint64_t Value); + + template + void dumpSymTabEntry(raw_ostream &OS, uint64_t Index, const STEType &STE) { + dumpSymTabEntry(OS, Index, STE.n_strx, STE.n_type, STE.n_sect, STE.n_desc, + STE.n_value); + } + void dumpOneBinaryStab(const MachOObjectFile &MainBinary, + StringRef BinaryPath); }; static void Warning(const Twine &Msg) { errs() << "warning: " + Msg + "\n"; } @@ -116,6 +135,12 @@ void MachODebugMapParser::switchToNewDebugMapObject(StringRef Filename, loadCurrentObjectFileSymbols(*ErrOrAchObj); } +static std::string getArchName(const object::MachOObjectFile &Obj) { + Triple ThumbTriple; + Triple T = Obj.getArch(nullptr, &ThumbTriple); + return T.getArchName(); +} + std::unique_ptr MachODebugMapParser::parseOneBinary(const MachOObjectFile &MainBinary, StringRef BinaryPath) { @@ -135,6 +160,133 @@ MachODebugMapParser::parseOneBinary(const MachOObjectFile &MainBinary, return std::move(Result); } +// Table that maps Darwin's Mach-O stab constants to strings to allow printing. +// llvm-nm has very similar code, the strings used here are however slightly +// different and part of the interface of dsymutil (some project's build-systems +// parse the ouptut of dsymutil -s), thus they shouldn't be changed. +struct DarwinStabName { + uint8_t NType; + const char *Name; +}; + +static const struct DarwinStabName DarwinStabNames[] = { + {MachO::N_GSYM, "N_GSYM"}, {MachO::N_FNAME, "N_FNAME"}, + {MachO::N_FUN, "N_FUN"}, {MachO::N_STSYM, "N_STSYM"}, + {MachO::N_LCSYM, "N_LCSYM"}, {MachO::N_BNSYM, "N_BNSYM"}, + {MachO::N_PC, "N_PC"}, {MachO::N_AST, "N_AST"}, + {MachO::N_OPT, "N_OPT"}, {MachO::N_RSYM, "N_RSYM"}, + {MachO::N_SLINE, "N_SLINE"}, {MachO::N_ENSYM, "N_ENSYM"}, + {MachO::N_SSYM, "N_SSYM"}, {MachO::N_SO, "N_SO"}, + {MachO::N_OSO, "N_OSO"}, {MachO::N_LSYM, "N_LSYM"}, + {MachO::N_BINCL, "N_BINCL"}, {MachO::N_SOL, "N_SOL"}, + {MachO::N_PARAMS, "N_PARAM"}, {MachO::N_VERSION, "N_VERS"}, + {MachO::N_OLEVEL, "N_OLEV"}, {MachO::N_PSYM, "N_PSYM"}, + {MachO::N_EINCL, "N_EINCL"}, {MachO::N_ENTRY, "N_ENTRY"}, + {MachO::N_LBRAC, "N_LBRAC"}, {MachO::N_EXCL, "N_EXCL"}, + {MachO::N_RBRAC, "N_RBRAC"}, {MachO::N_BCOMM, "N_BCOMM"}, + {MachO::N_ECOMM, "N_ECOMM"}, {MachO::N_ECOML, "N_ECOML"}, + {MachO::N_LENG, "N_LENG"}, {0, 0}}; + +static const char *getDarwinStabString(uint8_t NType) { + for (unsigned i = 0; DarwinStabNames[i].Name; i++) { + if (DarwinStabNames[i].NType == NType) + return DarwinStabNames[i].Name; + } + return 0; +} + +void MachODebugMapParser::dumpSymTabHeader(raw_ostream &OS, StringRef Arch) { + OS << "-----------------------------------" + "-----------------------------------\n"; + OS << "Symbol table for: '" << BinaryPath << "' (" << Arch.data() << ")\n"; + OS << "-----------------------------------" + "-----------------------------------\n"; + OS << "Index n_strx n_type n_sect n_desc n_value\n"; + OS << "======== -------- ------------------ ------ ------ ----------------\n"; +} + +void MachODebugMapParser::dumpSymTabEntry(raw_ostream &OS, uint64_t Index, + uint32_t StringIndex, uint8_t Type, + uint8_t SectionIndex, uint16_t Flags, + uint64_t Value) { + + // Index + OS << '[' << format_decimal(Index, 6) << "] " + // n_strx + << format_hex_no_prefix(StringIndex, 8) << ' ' + // n_type... + << format_hex_no_prefix(Type, 2) << " ("; + + if (Type & MachO::N_STAB) + OS << left_justify(getDarwinStabString(Type), 13); + else { + if (Type & MachO::N_PEXT) + OS << "PEXT "; + else + OS << " "; + switch (Type & MachO::N_TYPE) { + case MachO::N_UNDF: // 0x0 undefined, n_sect == NO_SECT + OS << "UNDF"; + break; + case MachO::N_ABS: // 0x2 absolute, n_sect == NO_SECT + OS << "ABS "; + break; + case MachO::N_SECT: // 0xe defined in section number n_sect + OS << "SECT"; + break; + case MachO::N_PBUD: // 0xc prebound undefined (defined in a dylib) + OS << "PBUD"; + break; + case MachO::N_INDR: // 0xa indirect + OS << "INDR"; + break; + default: + OS << format_hex_no_prefix(Type, 2) << " "; + break; + } + if (Type & MachO::N_EXT) + OS << " EXT"; + else + OS << " "; + } + + OS << ") " + // n_sect + << format_hex_no_prefix(SectionIndex, 2) << " " + // n_desc + << format_hex_no_prefix(Flags, 4) << " " + // n_value + << format_hex_no_prefix(Value, 16); + + const char *Name = &MainBinaryStrings.data()[StringIndex]; + if (Name && Name[0]) + OS << " '" << Name << "'"; + + OS << "\n"; +} + +void MachODebugMapParser::dumpOneBinaryStab(const MachOObjectFile &MainBinary, + StringRef BinaryPath) { + loadMainBinarySymbols(MainBinary); + MainBinaryStrings = MainBinary.getStringTableData(); + raw_ostream &OS(llvm::outs()); + + StringRef ArchName = getArchName(MainBinary); + dumpSymTabHeader(OS, ArchName); + uint64_t Idx = 0; + for (const SymbolRef &Symbol : MainBinary.symbols()) { + const DataRefImpl &DRI = Symbol.getRawDataRefImpl(); + if (MainBinary.is64Bit()) + dumpSymTabEntry(OS, Idx, MainBinary.getSymbol64TableEntry(DRI)); + else + dumpSymTabEntry(OS, Idx, MainBinary.getSymbolTableEntry(DRI)); + Idx++; + } + + OS << "\n\n"; + resetParserState(); +} + static bool shouldLinkArch(SmallVectorImpl &Archs, StringRef Arch) { if (Archs.empty() || std::find(Archs.begin(), Archs.end(), "all") != Archs.end() || @@ -148,6 +300,23 @@ static bool shouldLinkArch(SmallVectorImpl &Archs, StringRef Arch) { return std::find(Archs.begin(), Archs.end(), Arch) != Archs.end(); } +bool MachODebugMapParser::dumpStab() { + auto MainBinOrError = + MainBinaryHolder.GetFilesAs(BinaryPath); + if (auto Error = MainBinOrError.getError()) { + llvm::errs() << "Cannot get '" << BinaryPath + << "' as MachO file: " << Error.message() << "\n"; + return false; + } + + Triple T; + for (const auto *Binary : *MainBinOrError) + if (shouldLinkArch(Archs, Binary->getArch(nullptr, &T).getArchName())) + dumpOneBinaryStab(*Binary, BinaryPath); + + return true; +} + /// This main parsing routine tries to open the main binary and if /// successful iterates over the STAB entries. The real parsing is /// done in handleStabSymbolTableEntry. @@ -297,5 +466,11 @@ parseDebugMap(StringRef InputFile, ArrayRef Archs, return DebugMap::parseYAMLDebugMap(InputFile, PrependPath, Verbose); } } + +bool dumpStab(StringRef InputFile, ArrayRef Archs, + StringRef PrependPath) { + MachODebugMapParser Parser(InputFile, Archs, PrependPath, false); + return Parser.dumpStab(); +} } } diff --git a/tools/dsymutil/dsymutil.cpp b/tools/dsymutil/dsymutil.cpp index 9e112ba0727..e3e37a36262 100644 --- a/tools/dsymutil/dsymutil.cpp +++ b/tools/dsymutil/dsymutil.cpp @@ -48,6 +48,13 @@ static opt OsoPrependPath( desc("Specify a directory to prepend to the paths of object files."), value_desc("path"), cat(DsymCategory)); +static opt DumpStab( + "symtab", + desc("Dumps the symbol table found in executable or object file(s) and\n" + "exits."), + init(false), cat(DsymCategory)); +static alias DumpStabA("s", desc("Alias for --symtab"), aliasopt(DumpStab)); + static opt FlatOut("flat", desc("Produce a flat dSYM file (not a bundle)."), init(false), cat(DsymCategory)); @@ -274,6 +281,13 @@ int main(int argc, char **argv) { } for (auto &InputFile : InputFiles) { + // Dump the symbol table for each input file and requested arch + if (DumpStab) { + if (!dumpStab(InputFile, ArchFlags, OsoPrependPath)) + exitDsymutil(1); + continue; + } + auto DebugMapPtrsOrErr = parseDebugMap(InputFile, ArchFlags, OsoPrependPath, Verbose, InputIsYAMLDebugMap); diff --git a/tools/dsymutil/dsymutil.h b/tools/dsymutil/dsymutil.h index 6a94063f282..e8787453829 100644 --- a/tools/dsymutil/dsymutil.h +++ b/tools/dsymutil/dsymutil.h @@ -39,6 +39,10 @@ llvm::ErrorOr>> parseDebugMap(StringRef InputFile, ArrayRef Archs, StringRef PrependPath, bool Verbose, bool InputIsYAML); +/// \brief Dump the symbol table +bool dumpStab(StringRef InputFile, ArrayRef Archs, + StringRef PrependPath = ""); + /// \brief Link the Dwarf debuginfo as directed by the passed DebugMap /// \p DM into a DwarfFile named \p OutputFilename. /// \returns false if the link failed. -- 2.34.1