diff --git a/llvm/docs/CommandGuide/llvm-objdump.rst b/llvm/docs/CommandGuide/llvm-objdump.rst --- a/llvm/docs/CommandGuide/llvm-objdump.rst +++ b/llvm/docs/CommandGuide/llvm-objdump.rst @@ -356,9 +356,12 @@ 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 --- 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.h b/llvm/tools/llvm-objdump/MachODump.h --- a/llvm/tools/llvm-objdump/MachODump.h +++ b/llvm/tools/llvm-objdump/MachODump.h @@ -34,6 +34,8 @@ 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 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/MachODump.cpp b/llvm/tools/llvm-objdump/MachODump.cpp --- a/llvm/tools/llvm-objdump/MachODump.cpp +++ b/llvm/tools/llvm-objdump/MachODump.cpp @@ -78,7 +78,8 @@ 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 @@ 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 @@ } } + 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 @@ // 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 @@ 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/ObjdumpOpts.td b/llvm/tools/llvm-objdump/ObjdumpOpts.td --- a/llvm/tools/llvm-objdump/ObjdumpOpts.td +++ b/llvm/tools/llvm-objdump/ObjdumpOpts.td @@ -305,11 +305,15 @@ 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.h b/llvm/tools/llvm-objdump/llvm-objdump.h --- a/llvm/tools/llvm-objdump/llvm-objdump.h +++ b/llvm/tools/llvm-objdump/llvm-objdump.h @@ -20,6 +20,10 @@ class StringRef; class Twine; +namespace opt { +class Arg; +} // namespace opt + namespace object { class RelocationRef; struct VersionEntry; @@ -146,6 +150,8 @@ 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, diff --git a/llvm/tools/llvm-objdump/llvm-objdump.cpp b/llvm/tools/llvm-objdump/llvm-objdump.cpp --- a/llvm/tools/llvm-objdump/llvm-objdump.cpp +++ b/llvm/tools/llvm-objdump/llvm-objdump.cpp @@ -2928,7 +2928,7 @@ 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 @@ !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; }