[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
This commit is contained in:
Keith Smiley
2022-02-04 18:07:12 -08:00
parent c2d209476c
commit bc99fd95e0
7 changed files with 97 additions and 23 deletions

View File

@@ -356,9 +356,12 @@ MACH-O ONLY OPTIONS AND COMMANDS
Display exported symbols.
.. option:: --function-starts
.. option:: --function-starts [=<addrs|names|both>]
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

View File

@@ -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:

View File

@@ -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<FunctionStartsMode>(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<uint64_t, StringRef> SymbolNames;
if (FunctionStartsType == FunctionStartsMode::Names ||
FunctionStartsType == FunctionStartsMode::Both) {
for (SymbolRef Sym : O->symbols()) {
if (Expected<uint64_t> Addr = Sym.getAddress()) {
if (Expected<StringRef> 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<uint32_t>(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<uint32_t>(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);

View File

@@ -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;

View File

@@ -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<grp_mach_o>;
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<grp_mach_o>;
def : Flag<["--"], "function-starts">, Alias<function_starts_EQ>,
AliasArgs<["addrs"]>, Group<grp_mach_o>;
def link_opt_hints : Flag<["--"], "link-opt-hints">,
HelpText<"Print the linker optimization hints for "
"Mach-O objects (requires --macho)">,

View File

@@ -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;
}

View File

@@ -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<T> EO, Ts &&... Args) {
reportError(EO.takeError(), std::forward<Ts>(Args)...);
}
void invalidArgValue(const opt::Arg *A);
std::string getFileNameForError(const object::Archive::Child &C,
unsigned Index);
SymbolInfoTy createSymbolInfo(const object::ObjectFile &Obj,