From bc99fd95e090a406a16fe431047d8b7d59af7dc0 Mon Sep 17 00:00:00 2001 From: Keith Smiley Date: Fri, 4 Feb 2022 18:07:12 -0800 Subject: [PATCH] [llvm-objdump/mac] Add new function starts print mode This updates the `--function-starts` argument to now accept 3 different modes, `addrs` for just printing the addresses of the function starts (previous behavior), `names` for just printing the names of the function starts, and `both` to print them both side by side. In general if you're debugging function starts issues it's useful to see the symbol name alongside the address. This also mirrors Apple's `dyldinfo -function_starts` command which prints both. Differential Revision: https://reviews.llvm.org/D119050 --- llvm/docs/CommandGuide/llvm-objdump.rst | 7 ++- .../tools/llvm-objdump/MachO/function-starts.test | 27 ++++++++++- llvm/tools/llvm-objdump/MachODump.cpp | 56 ++++++++++++++++++---- llvm/tools/llvm-objdump/MachODump.h | 4 +- llvm/tools/llvm-objdump/ObjdumpOpts.td | 10 ++-- llvm/tools/llvm-objdump/llvm-objdump.cpp | 10 ++-- llvm/tools/llvm-objdump/llvm-objdump.h | 6 +++ 7 files changed, 97 insertions(+), 23 deletions(-) diff --git a/llvm/docs/CommandGuide/llvm-objdump.rst b/llvm/docs/CommandGuide/llvm-objdump.rst index 7879ac1..65a26bb 100644 --- a/llvm/docs/CommandGuide/llvm-objdump.rst +++ b/llvm/docs/CommandGuide/llvm-objdump.rst @@ -356,9 +356,12 @@ MACH-O ONLY OPTIONS AND COMMANDS Display exported symbols. -.. option:: --function-starts +.. option:: --function-starts [=] - Print the function starts table for Mach-O objects. + Print the function starts table for Mach-O objects. Either ``addrs`` + (default) to print only the addresses of functions, ``names`` to print only + the names of the functions (when available), or ``both`` to print the + names beside the addresses. .. option:: -g diff --git a/llvm/test/tools/llvm-objdump/MachO/function-starts.test b/llvm/test/tools/llvm-objdump/MachO/function-starts.test index a9cab10..8c01317 100644 --- a/llvm/test/tools/llvm-objdump/MachO/function-starts.test +++ b/llvm/test/tools/llvm-objdump/MachO/function-starts.test @@ -1,7 +1,30 @@ ## This test verifies that llvm-objdump correctly prints function starts data. -RUN: llvm-objdump --macho --function-starts %p/Inputs/hello.exe.macho-i386 | FileCheck %s --check-prefix=32-BIT +RUN: llvm-objdump --macho --function-starts %p/Inputs/hello.exe.macho-i386 | FileCheck %s --check-prefix=32-BIT --implicit-check-not=_main +RUN: llvm-objdump --macho --function-starts=addrs %p/Inputs/hello.exe.macho-i386 | FileCheck %s --check-prefix=32-BIT --implicit-check-not=_main 32-BIT: 00001f40 -RUN: llvm-objdump --macho --function-starts %p/Inputs/hello.exe.macho-x86_64 | FileCheck %s --check-prefix=64-BIT +RUN: llvm-objdump --macho --function-starts=names %p/Inputs/hello.exe.macho-i386 | FileCheck %s --check-prefix=32-BIT-NAMES +32-BIT-NAMES: {{^}}_main + +RUN: llvm-objdump --macho --function-starts=both %p/Inputs/hello.exe.macho-i386 | FileCheck %s --check-prefix=32-BIT-BOTH +32-BIT-BOTH: 00001f40 _main + +RUN: llvm-objdump --macho --function-starts %p/Inputs/hello.exe.macho-x86_64 | FileCheck %s --check-prefix=64-BIT --implicit-check-not=_main +RUN: llvm-objdump --macho --function-starts=addrs %p/Inputs/hello.exe.macho-x86_64 | FileCheck %s --check-prefix=64-BIT --implicit-check-not=_main 64-BIT: 0000000100000f30 + +RUN: llvm-objdump --macho --function-starts=names %p/Inputs/hello.exe.macho-x86_64 | FileCheck %s --check-prefix=64-BIT-NAMES +64-BIT-NAMES: {{^}}_main + +RUN: llvm-objdump --macho --function-starts=both %p/Inputs/hello.exe.macho-x86_64 | FileCheck %s --check-prefix=64-BIT-BOTH +64-BIT-BOTH: 0000000100000f30 _main + +RUN: llvm-strip %p/Inputs/hello.exe.macho-x86_64 -o %t.stripped +RUN: llvm-objdump --macho --function-starts=both %t.stripped | FileCheck %s --check-prefix=BOTH-STRIPPED +BOTH-STRIPPED: 0000000100000f30 ? + +RUN: llvm-strip %p/Inputs/hello.exe.macho-x86_64 -o %t.stripped +RUN: llvm-objdump --macho --function-starts=names %t.stripped | FileCheck %s --check-prefix=NAMES-STRIPPED +NAMES-STRIPPED: function-starts.test.tmp.stripped: +NAMES-STRIPPED-EMPTY: diff --git a/llvm/tools/llvm-objdump/MachODump.cpp b/llvm/tools/llvm-objdump/MachODump.cpp index 4637b09..3164159 100644 --- a/llvm/tools/llvm-objdump/MachODump.cpp +++ b/llvm/tools/llvm-objdump/MachODump.cpp @@ -78,7 +78,8 @@ bool objdump::UniversalHeaders; static bool ArchiveMemberOffsets; bool objdump::IndirectSymbols; bool objdump::DataInCode; -bool objdump::FunctionStarts; +FunctionStartsMode objdump::FunctionStartsType = + objdump::FunctionStartsMode::None; bool objdump::LinkOptHints; bool objdump::InfoPlist; bool objdump::ChainedFixups; @@ -112,7 +113,15 @@ void objdump::parseMachOOptions(const llvm::opt::InputArgList &InputArgs) { ArchiveMemberOffsets = InputArgs.hasArg(OBJDUMP_archive_member_offsets); IndirectSymbols = InputArgs.hasArg(OBJDUMP_indirect_symbols); DataInCode = InputArgs.hasArg(OBJDUMP_data_in_code); - FunctionStarts = InputArgs.hasArg(OBJDUMP_function_starts); + if (const opt::Arg *A = InputArgs.getLastArg(OBJDUMP_function_starts_EQ)) { + FunctionStartsType = StringSwitch(A->getValue()) + .Case("addrs", FunctionStartsMode::Addrs) + .Case("names", FunctionStartsMode::Names) + .Case("both", FunctionStartsMode::Both) + .Default(FunctionStartsMode::None); + if (FunctionStartsType == FunctionStartsMode::None) + invalidArgValue(A); + } LinkOptHints = InputArgs.hasArg(OBJDUMP_link_opt_hints); InfoPlist = InputArgs.hasArg(OBJDUMP_info_plist); ChainedFixups = InputArgs.hasArg(OBJDUMP_chained_fixups); @@ -1080,12 +1089,39 @@ static void PrintFunctionStarts(MachOObjectFile *O) { } } + DenseMap SymbolNames; + if (FunctionStartsType == FunctionStartsMode::Names || + FunctionStartsType == FunctionStartsMode::Both) { + for (SymbolRef Sym : O->symbols()) { + if (Expected Addr = Sym.getAddress()) { + if (Expected Name = Sym.getName()) { + SymbolNames[*Addr] = *Name; + } + } + } + } + for (uint64_t S : FunctionStarts) { uint64_t Addr = BaseSegmentAddress + S; - if (O->is64Bit()) - outs() << format("%016" PRIx64, Addr) << "\n"; - else - outs() << format("%08" PRIx32, static_cast(Addr)) << "\n"; + if (FunctionStartsType == FunctionStartsMode::Names) { + auto It = SymbolNames.find(Addr); + if (It != SymbolNames.end()) + outs() << It->second << "\n"; + } else { + if (O->is64Bit()) + outs() << format("%016" PRIx64, Addr); + else + outs() << format("%08" PRIx32, static_cast(Addr)); + + if (FunctionStartsType == FunctionStartsMode::Both) { + auto It = SymbolNames.find(Addr); + if (It != SymbolNames.end()) + outs() << " " << It->second; + else + outs() << " ?"; + } + outs() << "\n"; + } } } @@ -2111,9 +2147,9 @@ static void ProcessMachO(StringRef Name, MachOObjectFile *MachOOF, // UniversalHeaders or ArchiveHeaders. if (Disassemble || Relocations || PrivateHeaders || ExportsTrie || Rebase || Bind || SymbolTable || LazyBind || WeakBind || IndirectSymbols || - DataInCode || FunctionStarts || LinkOptHints || ChainedFixups || - DyldInfo || DylibsUsed || DylibId || Rpaths || ObjcMetaData || - (!FilterSections.empty())) { + DataInCode || FunctionStartsType != FunctionStartsMode::None || + LinkOptHints || ChainedFixups || DyldInfo || DylibsUsed || DylibId || + Rpaths || ObjcMetaData || (!FilterSections.empty())) { if (LeadingHeaders) { outs() << Name; if (!ArchiveMemberName.empty()) @@ -2168,7 +2204,7 @@ static void ProcessMachO(StringRef Name, MachOObjectFile *MachOOF, PrintIndirectSymbols(MachOOF, Verbose); if (DataInCode) PrintDataInCodeTable(MachOOF, Verbose); - if (FunctionStarts) + if (FunctionStartsType != FunctionStartsMode::None) PrintFunctionStarts(MachOOF); if (LinkOptHints) PrintLinkOptHints(MachOOF); diff --git a/llvm/tools/llvm-objdump/MachODump.h b/llvm/tools/llvm-objdump/MachODump.h index 2e2c2b8..d9d3a70 100644 --- a/llvm/tools/llvm-objdump/MachODump.h +++ b/llvm/tools/llvm-objdump/MachODump.h @@ -34,6 +34,8 @@ namespace objdump { void parseMachOOptions(const llvm::opt::InputArgList &InputArgs); +enum class FunctionStartsMode { Addrs, Names, Both, None }; + // MachO specific options extern bool Bind; extern bool DataInCode; @@ -45,7 +47,7 @@ extern bool DylibsUsed; extern bool ExportsTrie; extern bool FirstPrivateHeader; extern bool FullLeadingAddr; -extern bool FunctionStarts; +extern FunctionStartsMode FunctionStartsType; extern bool IndirectSymbols; extern bool InfoPlist; extern bool LazyBind; diff --git a/llvm/tools/llvm-objdump/ObjdumpOpts.td b/llvm/tools/llvm-objdump/ObjdumpOpts.td index 46eefa1..205bb94 100644 --- a/llvm/tools/llvm-objdump/ObjdumpOpts.td +++ b/llvm/tools/llvm-objdump/ObjdumpOpts.td @@ -305,11 +305,15 @@ def data_in_code : Flag<["--"], "data-in-code">, HelpText<"Print the data in code table for Mach-O objects (requires --macho)">, Group; -def function_starts : Flag<["--"], "function-starts">, - HelpText<"Print the function starts table for " - "Mach-O objects (requires --macho)">, +def function_starts_EQ : Joined<["--"], "function-starts=">, + HelpText<"Print the function starts table for Mach-O objects. " + "Options: addrs (default), names, both (requires --macho)">, + Values<"addrs,names,both">, Group; +def : Flag<["--"], "function-starts">, Alias, + AliasArgs<["addrs"]>, Group; + def link_opt_hints : Flag<["--"], "link-opt-hints">, HelpText<"Print the linker optimization hints for " "Mach-O objects (requires --macho)">, diff --git a/llvm/tools/llvm-objdump/llvm-objdump.cpp b/llvm/tools/llvm-objdump/llvm-objdump.cpp index aa3c053..efa64ed 100644 --- a/llvm/tools/llvm-objdump/llvm-objdump.cpp +++ b/llvm/tools/llvm-objdump/llvm-objdump.cpp @@ -2928,7 +2928,7 @@ static object::BuildID parseBuildIDArg(const opt::Arg *A) { return object::BuildID(BuildID.begin(), BuildID.end()); } -static void invalidArgValue(const opt::Arg *A) { +void objdump::invalidArgValue(const opt::Arg *A) { reportCmdLineError("'" + StringRef(A->getValue()) + "' is not a valid value for '" + A->getSpelling() + "'"); } @@ -3217,10 +3217,10 @@ int main(int argc, char **argv) { !DynamicSymbolTable && !UnwindInfo && !FaultMapSection && !Offloading && !(MachOOpt && (Bind || DataInCode || ChainedFixups || DyldInfo || DylibId || - DylibsUsed || ExportsTrie || FirstPrivateHeader || FunctionStarts || - IndirectSymbols || InfoPlist || LazyBind || LinkOptHints || - ObjcMetaData || Rebase || Rpaths || UniversalHeaders || WeakBind || - !FilterSections.empty()))) { + DylibsUsed || ExportsTrie || FirstPrivateHeader || + FunctionStartsType != FunctionStartsMode::None || IndirectSymbols || + InfoPlist || LazyBind || LinkOptHints || ObjcMetaData || Rebase || + Rpaths || UniversalHeaders || WeakBind || !FilterSections.empty()))) { T->printHelp(ToolName); return 2; } diff --git a/llvm/tools/llvm-objdump/llvm-objdump.h b/llvm/tools/llvm-objdump/llvm-objdump.h index c64c042d..efb4451 100644 --- a/llvm/tools/llvm-objdump/llvm-objdump.h +++ b/llvm/tools/llvm-objdump/llvm-objdump.h @@ -20,6 +20,10 @@ namespace llvm { class StringRef; class Twine; +namespace opt { +class Arg; +} // namespace opt + namespace object { class RelocationRef; struct VersionEntry; @@ -146,6 +150,8 @@ T unwrapOrError(Expected EO, Ts &&... Args) { reportError(EO.takeError(), std::forward(Args)...); } +void invalidArgValue(const opt::Arg *A); + std::string getFileNameForError(const object::Archive::Child &C, unsigned Index); SymbolInfoTy createSymbolInfo(const object::ObjectFile &Obj, -- 2.7.4