blob: 296cd698304d0a162e41c035d97735edbfb698fb [file] [log] [blame]
//===-- MachODump.cpp - Object file dumping utility for llvm --------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file implements the MachO-specific dumper for llvm-objdump.
//
//===----------------------------------------------------------------------===//
#include "MachODump.h"
#include "ObjdumpOptID.h"
#include "llvm-objdump.h"
#include "llvm-c/Disassembler.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/BinaryFormat/MachO.h"
#include "llvm/Config/config.h"
#include "llvm/DebugInfo/DIContext.h"
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
#include "llvm/Demangle/Demangle.h"
#include "llvm/MC/MCAsmInfo.h"
#include "llvm/MC/MCContext.h"
#include "llvm/MC/MCDisassembler/MCDisassembler.h"
#include "llvm/MC/MCInst.h"
#include "llvm/MC/MCInstPrinter.h"
#include "llvm/MC/MCInstrDesc.h"
#include "llvm/MC/MCInstrInfo.h"
#include "llvm/MC/MCRegisterInfo.h"
#include "llvm/MC/MCSubtargetInfo.h"
#include "llvm/MC/MCTargetOptions.h"
#include "llvm/MC/TargetRegistry.h"
#include "llvm/Object/MachO.h"
#include "llvm/Object/MachOUniversal.h"
#include "llvm/Option/ArgList.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/FormattedStream.h"
#include "llvm/Support/GraphWriter.h"
#include "llvm/Support/LEB128.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/ToolOutputFile.h"
#include "llvm/Support/WithColor.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/TargetParser/Triple.h"
#include <algorithm>
#include <cstring>
#include <system_error>
#ifdef LLVM_HAVE_LIBXAR
extern "C" {
#include <xar/xar.h>
}
#endif
using namespace llvm;
using namespace llvm::object;
using namespace llvm::objdump;
bool objdump::FirstPrivateHeader;
bool objdump::ExportsTrie;
bool objdump::Rebase;
bool objdump::Rpaths;
bool objdump::Bind;
bool objdump::LazyBind;
bool objdump::WeakBind;
static bool UseDbg;
static std::string DSYMFile;
bool objdump::FullLeadingAddr;
bool objdump::LeadingHeaders;
bool objdump::UniversalHeaders;
static bool ArchiveMemberOffsets;
bool objdump::IndirectSymbols;
bool objdump::DataInCode;
FunctionStartsMode objdump::FunctionStartsType =
objdump::FunctionStartsMode::None;
bool objdump::LinkOptHints;
bool objdump::InfoPlist;
bool objdump::ChainedFixups;
bool objdump::DyldInfo;
bool objdump::DylibsUsed;
bool objdump::DylibId;
bool objdump::Verbose;
bool objdump::ObjcMetaData;
std::string objdump::DisSymName;
bool objdump::SymbolicOperands;
static std::vector<std::string> ArchFlags;
static bool ArchAll = false;
static std::string ThumbTripleName;
static StringRef ordinalName(const object::MachOObjectFile *, int);
void objdump::parseMachOOptions(const llvm::opt::InputArgList &InputArgs) {
FirstPrivateHeader = InputArgs.hasArg(OBJDUMP_private_header);
ExportsTrie = InputArgs.hasArg(OBJDUMP_exports_trie);
Rebase = InputArgs.hasArg(OBJDUMP_rebase);
Rpaths = InputArgs.hasArg(OBJDUMP_rpaths);
Bind = InputArgs.hasArg(OBJDUMP_bind);
LazyBind = InputArgs.hasArg(OBJDUMP_lazy_bind);
WeakBind = InputArgs.hasArg(OBJDUMP_weak_bind);
UseDbg = InputArgs.hasArg(OBJDUMP_g);
DSYMFile = InputArgs.getLastArgValue(OBJDUMP_dsym_EQ).str();
FullLeadingAddr = InputArgs.hasArg(OBJDUMP_full_leading_addr);
LeadingHeaders = !InputArgs.hasArg(OBJDUMP_no_leading_headers);
UniversalHeaders = InputArgs.hasArg(OBJDUMP_universal_headers);
ArchiveMemberOffsets = InputArgs.hasArg(OBJDUMP_archive_member_offsets);
IndirectSymbols = InputArgs.hasArg(OBJDUMP_indirect_symbols);
DataInCode = InputArgs.hasArg(OBJDUMP_data_in_code);
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);
DyldInfo = InputArgs.hasArg(OBJDUMP_dyld_info);
DylibsUsed = InputArgs.hasArg(OBJDUMP_dylibs_used);
DylibId = InputArgs.hasArg(OBJDUMP_dylib_id);
Verbose = !InputArgs.hasArg(OBJDUMP_non_verbose);
ObjcMetaData = InputArgs.hasArg(OBJDUMP_objc_meta_data);
DisSymName = InputArgs.getLastArgValue(OBJDUMP_dis_symname).str();
SymbolicOperands = !InputArgs.hasArg(OBJDUMP_no_symbolic_operands);
ArchFlags = InputArgs.getAllArgValues(OBJDUMP_arch_EQ);
}
static const Target *GetTarget(const MachOObjectFile *MachOObj,
const char **McpuDefault,
const Target **ThumbTarget) {
// Figure out the target triple.
Triple TT(TripleName);
if (TripleName.empty()) {
TT = MachOObj->getArchTriple(McpuDefault);
TripleName = TT.str();
}
if (TT.getArch() == Triple::arm) {
// We've inferred a 32-bit ARM target from the object file. All MachO CPUs
// that support ARM are also capable of Thumb mode.
Triple ThumbTriple = TT;
std::string ThumbName = (Twine("thumb") + TT.getArchName().substr(3)).str();
ThumbTriple.setArchName(ThumbName);
ThumbTripleName = ThumbTriple.str();
}
// Get the target specific parser.
std::string Error;
const Target *TheTarget = TargetRegistry::lookupTarget(TripleName, Error);
if (TheTarget && ThumbTripleName.empty())
return TheTarget;
*ThumbTarget = TargetRegistry::lookupTarget(ThumbTripleName, Error);
if (*ThumbTarget)
return TheTarget;
WithColor::error(errs(), "llvm-objdump") << "unable to get target for '";
if (!TheTarget)
errs() << TripleName;
else
errs() << ThumbTripleName;
errs() << "', see --version and --triple.\n";
return nullptr;
}
namespace {
struct SymbolSorter {
bool operator()(const SymbolRef &A, const SymbolRef &B) {
Expected<SymbolRef::Type> ATypeOrErr = A.getType();
if (!ATypeOrErr)
reportError(ATypeOrErr.takeError(), A.getObject()->getFileName());
SymbolRef::Type AType = *ATypeOrErr;
Expected<SymbolRef::Type> BTypeOrErr = B.getType();
if (!BTypeOrErr)
reportError(BTypeOrErr.takeError(), B.getObject()->getFileName());
SymbolRef::Type BType = *BTypeOrErr;
uint64_t AAddr =
(AType != SymbolRef::ST_Function) ? 0 : cantFail(A.getValue());
uint64_t BAddr =
(BType != SymbolRef::ST_Function) ? 0 : cantFail(B.getValue());
return AAddr < BAddr;
}
};
class MachODumper : public Dumper {
const object::MachOObjectFile &Obj;
public:
MachODumper(const object::MachOObjectFile &O) : Dumper(O), Obj(O) {}
void printPrivateHeaders() override;
};
} // namespace
std::unique_ptr<Dumper>
objdump::createMachODumper(const object::MachOObjectFile &Obj) {
return std::make_unique<MachODumper>(Obj);
}
// Types for the storted data in code table that is built before disassembly
// and the predicate function to sort them.
typedef std::pair<uint64_t, DiceRef> DiceTableEntry;
typedef std::vector<DiceTableEntry> DiceTable;
typedef DiceTable::iterator dice_table_iterator;
#ifdef LLVM_HAVE_LIBXAR
namespace {
struct ScopedXarFile {
xar_t xar;
ScopedXarFile(const char *filename, int32_t flags) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
xar = xar_open(filename, flags);
#pragma clang diagnostic pop
}
~ScopedXarFile() {
if (xar)
xar_close(xar);
}
ScopedXarFile(const ScopedXarFile &) = delete;
ScopedXarFile &operator=(const ScopedXarFile &) = delete;
operator xar_t() { return xar; }
};
struct ScopedXarIter {
xar_iter_t iter;
ScopedXarIter() : iter(xar_iter_new()) {}
~ScopedXarIter() {
if (iter)
xar_iter_free(iter);
}
ScopedXarIter(const ScopedXarIter &) = delete;
ScopedXarIter &operator=(const ScopedXarIter &) = delete;
operator xar_iter_t() { return iter; }
};
} // namespace
#endif // defined(LLVM_HAVE_LIBXAR)
// This is used to search for a data in code table entry for the PC being
// disassembled. The j parameter has the PC in j.first. A single data in code
// table entry can cover many bytes for each of its Kind's. So if the offset,
// aka the i.first value, of the data in code table entry plus its Length
// covers the PC being searched for this will return true. If not it will
// return false.
static bool compareDiceTableEntries(const DiceTableEntry &i,
const DiceTableEntry &j) {
uint16_t Length;
i.second.getLength(Length);
return j.first >= i.first && j.first < i.first + Length;
}
static uint64_t DumpDataInCode(const uint8_t *bytes, uint64_t Length,
unsigned short Kind) {
uint32_t Value, Size = 1;
switch (Kind) {
default:
case MachO::DICE_KIND_DATA:
if (Length >= 4) {
if (ShowRawInsn)
dumpBytes(ArrayRef(bytes, 4), outs());
Value = bytes[3] << 24 | bytes[2] << 16 | bytes[1] << 8 | bytes[0];
outs() << "\t.long " << Value;
Size = 4;
} else if (Length >= 2) {
if (ShowRawInsn)
dumpBytes(ArrayRef(bytes, 2), outs());
Value = bytes[1] << 8 | bytes[0];
outs() << "\t.short " << Value;
Size = 2;
} else {
if (ShowRawInsn)
dumpBytes(ArrayRef(bytes, 2), outs());
Value = bytes[0];
outs() << "\t.byte " << Value;
Size = 1;
}
if (Kind == MachO::DICE_KIND_DATA)
outs() << "\t@ KIND_DATA\n";
else
outs() << "\t@ data in code kind = " << Kind << "\n";
break;
case MachO::DICE_KIND_JUMP_TABLE8:
if (ShowRawInsn)
dumpBytes(ArrayRef(bytes, 1), outs());
Value = bytes[0];
outs() << "\t.byte " << format("%3u", Value) << "\t@ KIND_JUMP_TABLE8\n";
Size = 1;
break;
case MachO::DICE_KIND_JUMP_TABLE16:
if (ShowRawInsn)
dumpBytes(ArrayRef(bytes, 2), outs());
Value = bytes[1] << 8 | bytes[0];
outs() << "\t.short " << format("%5u", Value & 0xffff)
<< "\t@ KIND_JUMP_TABLE16\n";
Size = 2;
break;
case MachO::DICE_KIND_JUMP_TABLE32:
case MachO::DICE_KIND_ABS_JUMP_TABLE32:
if (ShowRawInsn)
dumpBytes(ArrayRef(bytes, 4), outs());
Value = bytes[3] << 24 | bytes[2] << 16 | bytes[1] << 8 | bytes[0];
outs() << "\t.long " << Value;
if (Kind == MachO::DICE_KIND_JUMP_TABLE32)
outs() << "\t@ KIND_JUMP_TABLE32\n";
else
outs() << "\t@ KIND_ABS_JUMP_TABLE32\n";
Size = 4;
break;
}
return Size;
}
static void getSectionsAndSymbols(MachOObjectFile *MachOObj,
std::vector<SectionRef> &Sections,
std::vector<SymbolRef> &Symbols,
SmallVectorImpl<uint64_t> &FoundFns,
uint64_t &BaseSegmentAddress) {
const StringRef FileName = MachOObj->getFileName();
for (const SymbolRef &Symbol : MachOObj->symbols()) {
StringRef SymName = unwrapOrError(Symbol.getName(), FileName);
if (!SymName.startswith("ltmp"))
Symbols.push_back(Symbol);
}
append_range(Sections, MachOObj->sections());
bool BaseSegmentAddressSet = false;
for (const auto &Command : MachOObj->load_commands()) {
if (Command.C.cmd == MachO::LC_FUNCTION_STARTS) {
// We found a function starts segment, parse the addresses for later
// consumption.
MachO::linkedit_data_command LLC =
MachOObj->getLinkeditDataLoadCommand(Command);
MachOObj->ReadULEB128s(LLC.dataoff, FoundFns);
} else if (Command.C.cmd == MachO::LC_SEGMENT) {
MachO::segment_command SLC = MachOObj->getSegmentLoadCommand(Command);
StringRef SegName = SLC.segname;
if (!BaseSegmentAddressSet && SegName != "__PAGEZERO") {
BaseSegmentAddressSet = true;
BaseSegmentAddress = SLC.vmaddr;
}
} else if (Command.C.cmd == MachO::LC_SEGMENT_64) {
MachO::segment_command_64 SLC = MachOObj->getSegment64LoadCommand(Command);
StringRef SegName = SLC.segname;
if (!BaseSegmentAddressSet && SegName != "__PAGEZERO") {
BaseSegmentAddressSet = true;
BaseSegmentAddress = SLC.vmaddr;
}
}
}
}
static bool DumpAndSkipDataInCode(uint64_t PC, const uint8_t *bytes,
DiceTable &Dices, uint64_t &InstSize) {
// Check the data in code table here to see if this is data not an
// instruction to be disassembled.
DiceTable Dice;
Dice.push_back(std::make_pair(PC, DiceRef()));
dice_table_iterator DTI =
std::search(Dices.begin(), Dices.end(), Dice.begin(), Dice.end(),
compareDiceTableEntries);
if (DTI != Dices.end()) {
uint16_t Length;
DTI->second.getLength(Length);
uint16_t Kind;
DTI->second.getKind(Kind);
InstSize = DumpDataInCode(bytes, Length, Kind);
if ((Kind == MachO::DICE_KIND_JUMP_TABLE8) &&
(PC == (DTI->first + Length - 1)) && (Length & 1))
InstSize++;
return true;
}
return false;
}
static void printRelocationTargetName(const MachOObjectFile *O,
const MachO::any_relocation_info &RE,
raw_string_ostream &Fmt) {
// Target of a scattered relocation is an address. In the interest of
// generating pretty output, scan through the symbol table looking for a
// symbol that aligns with that address. If we find one, print it.
// Otherwise, we just print the hex address of the target.
const StringRef FileName = O->getFileName();
if (O->isRelocationScattered(RE)) {
uint32_t Val = O->getPlainRelocationSymbolNum(RE);
for (const SymbolRef &Symbol : O->symbols()) {
uint64_t Addr = unwrapOrError(Symbol.getAddress(), FileName);
if (Addr != Val)
continue;
Fmt << unwrapOrError(Symbol.getName(), FileName);
return;
}
// If we couldn't find a symbol that this relocation refers to, try
// to find a section beginning instead.
for (const SectionRef &Section : ToolSectionFilter(*O)) {
uint64_t Addr = Section.getAddress();
if (Addr != Val)
continue;
StringRef NameOrErr = unwrapOrError(Section.getName(), O->getFileName());
Fmt << NameOrErr;
return;
}
Fmt << format("0x%x", Val);
return;
}
StringRef S;
bool isExtern = O->getPlainRelocationExternal(RE);
uint64_t Val = O->getPlainRelocationSymbolNum(RE);
if (O->getAnyRelocationType(RE) == MachO::ARM64_RELOC_ADDEND &&
(O->getArch() == Triple::aarch64 || O->getArch() == Triple::aarch64_be)) {
Fmt << format("0x%0" PRIx64, Val);
return;
}
if (isExtern) {
symbol_iterator SI = O->symbol_begin();
std::advance(SI, Val);
S = unwrapOrError(SI->getName(), FileName);
} else {
section_iterator SI = O->section_begin();
// Adjust for the fact that sections are 1-indexed.
if (Val == 0) {
Fmt << "0 (?,?)";
return;
}
uint32_t I = Val - 1;
while (I != 0 && SI != O->section_end()) {
--I;
std::advance(SI, 1);
}
if (SI == O->section_end()) {
Fmt << Val << " (?,?)";
} else {
if (Expected<StringRef> NameOrErr = SI->getName())
S = *NameOrErr;
else
consumeError(NameOrErr.takeError());
}
}
Fmt << S;
}
Error objdump::getMachORelocationValueString(const MachOObjectFile *Obj,
const RelocationRef &RelRef,
SmallVectorImpl<char> &Result) {
DataRefImpl Rel = RelRef.getRawDataRefImpl();
MachO::any_relocation_info RE = Obj->getRelocation(Rel);
unsigned Arch = Obj->getArch();
std::string FmtBuf;
raw_string_ostream Fmt(FmtBuf);
unsigned Type = Obj->getAnyRelocationType(RE);
bool IsPCRel = Obj->getAnyRelocationPCRel(RE);
// Determine any addends that should be displayed with the relocation.
// These require decoding the relocation type, which is triple-specific.
// X86_64 has entirely custom relocation types.
if (Arch == Triple::x86_64) {
switch (Type) {
case MachO::X86_64_RELOC_GOT_LOAD:
case MachO::X86_64_RELOC_GOT: {
printRelocationTargetName(Obj, RE, Fmt);
Fmt << "@GOT";
if (IsPCRel)
Fmt << "PCREL";
break;
}
case MachO::X86_64_RELOC_SUBTRACTOR: {
DataRefImpl RelNext = Rel;
Obj->moveRelocationNext(RelNext);
MachO::any_relocation_info RENext = Obj->getRelocation(RelNext);
// X86_64_RELOC_SUBTRACTOR must be followed by a relocation of type
// X86_64_RELOC_UNSIGNED.
// NOTE: Scattered relocations don't exist on x86_64.
unsigned RType = Obj->getAnyRelocationType(RENext);
if (RType != MachO::X86_64_RELOC_UNSIGNED)
reportError(Obj->getFileName(), "Expected X86_64_RELOC_UNSIGNED after "
"X86_64_RELOC_SUBTRACTOR.");
// The X86_64_RELOC_UNSIGNED contains the minuend symbol;
// X86_64_RELOC_SUBTRACTOR contains the subtrahend.
printRelocationTargetName(Obj, RENext, Fmt);
Fmt << "-";
printRelocationTargetName(Obj, RE, Fmt);
break;
}
case MachO::X86_64_RELOC_TLV:
printRelocationTargetName(Obj, RE, Fmt);
Fmt << "@TLV";
if (IsPCRel)
Fmt << "P";
break;
case MachO::X86_64_RELOC_SIGNED_1:
printRelocationTargetName(Obj, RE, Fmt);
Fmt << "-1";
break;
case MachO::X86_64_RELOC_SIGNED_2:
printRelocationTargetName(Obj, RE, Fmt);
Fmt << "-2";
break;
case MachO::X86_64_RELOC_SIGNED_4:
printRelocationTargetName(Obj, RE, Fmt);
Fmt << "-4";
break;
default:
printRelocationTargetName(Obj, RE, Fmt);
break;
}
// X86 and ARM share some relocation types in common.
} else if (Arch == Triple::x86 || Arch == Triple::arm ||
Arch == Triple::ppc) {
// Generic relocation types...
switch (Type) {
case MachO::GENERIC_RELOC_PAIR: // prints no info
return Error::success();
case MachO::GENERIC_RELOC_SECTDIFF: {
DataRefImpl RelNext = Rel;
Obj->moveRelocationNext(RelNext);
MachO::any_relocation_info RENext = Obj->getRelocation(RelNext);
// X86 sect diff's must be followed by a relocation of type
// GENERIC_RELOC_PAIR.
unsigned RType = Obj->getAnyRelocationType(RENext);
if (RType != MachO::GENERIC_RELOC_PAIR)
reportError(Obj->getFileName(), "Expected GENERIC_RELOC_PAIR after "
"GENERIC_RELOC_SECTDIFF.");
printRelocationTargetName(Obj, RE, Fmt);
Fmt << "-";
printRelocationTargetName(Obj, RENext, Fmt);
break;
}
}
if (Arch == Triple::x86 || Arch == Triple::ppc) {
switch (Type) {
case MachO::GENERIC_RELOC_LOCAL_SECTDIFF: {
DataRefImpl RelNext = Rel;
Obj->moveRelocationNext(RelNext);
MachO::any_relocation_info RENext = Obj->getRelocation(RelNext);
// X86 sect diff's must be followed by a relocation of type
// GENERIC_RELOC_PAIR.
unsigned RType = Obj->getAnyRelocationType(RENext);
if (RType != MachO::GENERIC_RELOC_PAIR)
reportError(Obj->getFileName(), "Expected GENERIC_RELOC_PAIR after "
"GENERIC_RELOC_LOCAL_SECTDIFF.");
printRelocationTargetName(Obj, RE, Fmt);
Fmt << "-";
printRelocationTargetName(Obj, RENext, Fmt);
break;
}
case MachO::GENERIC_RELOC_TLV: {
printRelocationTargetName(Obj, RE, Fmt);
Fmt << "@TLV";
if (IsPCRel)
Fmt << "P";
break;
}
default:
printRelocationTargetName(Obj, RE, Fmt);
}
} else { // ARM-specific relocations
switch (Type) {
case MachO::ARM_RELOC_HALF:
case MachO::ARM_RELOC_HALF_SECTDIFF: {
// Half relocations steal a bit from the length field to encode
// whether this is an upper16 or a lower16 relocation.
bool isUpper = (Obj->getAnyRelocationLength(RE) & 0x1) == 1;
if (isUpper)
Fmt << ":upper16:(";
else
Fmt << ":lower16:(";
printRelocationTargetName(Obj, RE, Fmt);
DataRefImpl RelNext = Rel;
Obj->moveRelocationNext(RelNext);
MachO::any_relocation_info RENext = Obj->getRelocation(RelNext);
// ARM half relocs must be followed by a relocation of type
// ARM_RELOC_PAIR.
unsigned RType = Obj->getAnyRelocationType(RENext);
if (RType != MachO::ARM_RELOC_PAIR)
reportError(Obj->getFileName(), "Expected ARM_RELOC_PAIR after "
"ARM_RELOC_HALF");
// NOTE: The half of the target virtual address is stashed in the
// address field of the secondary relocation, but we can't reverse
// engineer the constant offset from it without decoding the movw/movt
// instruction to find the other half in its immediate field.
// ARM_RELOC_HALF_SECTDIFF encodes the second section in the
// symbol/section pointer of the follow-on relocation.
if (Type == MachO::ARM_RELOC_HALF_SECTDIFF) {
Fmt << "-";
printRelocationTargetName(Obj, RENext, Fmt);
}
Fmt << ")";
break;
}
default: {
printRelocationTargetName(Obj, RE, Fmt);
}
}
}
} else
printRelocationTargetName(Obj, RE, Fmt);
Fmt.flush();
Result.append(FmtBuf.begin(), FmtBuf.end());
return Error::success();
}
static void PrintIndirectSymbolTable(MachOObjectFile *O, bool verbose,
uint32_t n, uint32_t count,
uint32_t stride, uint64_t addr) {
MachO::dysymtab_command Dysymtab = O->getDysymtabLoadCommand();
uint32_t nindirectsyms = Dysymtab.nindirectsyms;
if (n > nindirectsyms)
outs() << " (entries start past the end of the indirect symbol "
"table) (reserved1 field greater than the table size)";
else if (n + count > nindirectsyms)
outs() << " (entries extends past the end of the indirect symbol "
"table)";
outs() << "\n";
uint32_t cputype = O->getHeader().cputype;
if (cputype & MachO::CPU_ARCH_ABI64)
outs() << "address index";
else
outs() << "address index";
if (verbose)
outs() << " name\n";
else
outs() << "\n";
for (uint32_t j = 0; j < count && n + j < nindirectsyms; j++) {
if (cputype & MachO::CPU_ARCH_ABI64)
outs() << format("0x%016" PRIx64, addr + j * stride) << " ";
else
outs() << format("0x%08" PRIx32, (uint32_t)addr + j * stride) << " ";
MachO::dysymtab_command Dysymtab = O->getDysymtabLoadCommand();
uint32_t indirect_symbol = O->getIndirectSymbolTableEntry(Dysymtab, n + j);
if (indirect_symbol == MachO::INDIRECT_SYMBOL_LOCAL) {
outs() << "LOCAL\n";
continue;
}
if (indirect_symbol ==
(MachO::INDIRECT_SYMBOL_LOCAL | MachO::INDIRECT_SYMBOL_ABS)) {
outs() << "LOCAL ABSOLUTE\n";
continue;
}
if (indirect_symbol == MachO::INDIRECT_SYMBOL_ABS) {
outs() << "ABSOLUTE\n";
continue;
}
outs() << format("%5u ", indirect_symbol);
if (verbose) {
MachO::symtab_command Symtab = O->getSymtabLoadCommand();
if (indirect_symbol < Symtab.nsyms) {
symbol_iterator Sym = O->getSymbolByIndex(indirect_symbol);
SymbolRef Symbol = *Sym;
outs() << unwrapOrError(Symbol.getName(), O->getFileName());
} else {
outs() << "?";
}
}
outs() << "\n";
}
}
static void PrintIndirectSymbols(MachOObjectFile *O, bool verbose) {
for (const auto &Load : O->load_commands()) {
if (Load.C.cmd == MachO::LC_SEGMENT_64) {
MachO::segment_command_64 Seg = O->getSegment64LoadCommand(Load);
for (unsigned J = 0; J < Seg.nsects; ++J) {
MachO::section_64 Sec = O->getSection64(Load, J);
uint32_t section_type = Sec.flags & MachO::SECTION_TYPE;
if (section_type == MachO::S_NON_LAZY_SYMBOL_POINTERS ||
section_type == MachO::S_LAZY_SYMBOL_POINTERS ||
section_type == MachO::S_LAZY_DYLIB_SYMBOL_POINTERS ||
section_type == MachO::S_THREAD_LOCAL_VARIABLE_POINTERS ||
section_type == MachO::S_SYMBOL_STUBS) {
uint32_t stride;
if (section_type == MachO::S_SYMBOL_STUBS)
stride = Sec.reserved2;
else
stride = 8;
if (stride == 0) {
outs() << "Can't print indirect symbols for (" << Sec.segname << ","
<< Sec.sectname << ") "
<< "(size of stubs in reserved2 field is zero)\n";
continue;
}
uint32_t count = Sec.size / stride;
outs() << "Indirect symbols for (" << Sec.segname << ","
<< Sec.sectname << ") " << count << " entries";
uint32_t n = Sec.reserved1;
PrintIndirectSymbolTable(O, verbose, n, count, stride, Sec.addr);
}
}
} else if (Load.C.cmd == MachO::LC_SEGMENT) {
MachO::segment_command Seg = O->getSegmentLoadCommand(Load);
for (unsigned J = 0; J < Seg.nsects; ++J) {
MachO::section Sec = O->getSection(Load, J);
uint32_t section_type = Sec.flags & MachO::SECTION_TYPE;
if (section_type == MachO::S_NON_LAZY_SYMBOL_POINTERS ||
section_type == MachO::S_LAZY_SYMBOL_POINTERS ||
section_type == MachO::S_LAZY_DYLIB_SYMBOL_POINTERS ||
section_type == MachO::S_THREAD_LOCAL_VARIABLE_POINTERS ||
section_type == MachO::S_SYMBOL_STUBS) {
uint32_t stride;
if (section_type == MachO::S_SYMBOL_STUBS)
stride = Sec.reserved2;
else
stride = 4;
if (stride == 0) {
outs() << "Can't print indirect symbols for (" << Sec.segname << ","
<< Sec.sectname << ") "
<< "(size of stubs in reserved2 field is zero)\n";
continue;
}
uint32_t count = Sec.size / stride;
outs() << "Indirect symbols for (" << Sec.segname << ","
<< Sec.sectname << ") " << count << " entries";
uint32_t n = Sec.reserved1;
PrintIndirectSymbolTable(O, verbose, n, count, stride, Sec.addr);
}
}
}
}
}
static void PrintRType(const uint64_t cputype, const unsigned r_type) {
static char const *generic_r_types[] = {
"VANILLA ", "PAIR ", "SECTDIF ", "PBLAPTR ", "LOCSDIF ", "TLV ",
" 6 (?) ", " 7 (?) ", " 8 (?) ", " 9 (?) ", " 10 (?) ", " 11 (?) ",
" 12 (?) ", " 13 (?) ", " 14 (?) ", " 15 (?) "
};
static char const *x86_64_r_types[] = {
"UNSIGND ", "SIGNED ", "BRANCH ", "GOT_LD ", "GOT ", "SUB ",
"SIGNED1 ", "SIGNED2 ", "SIGNED4 ", "TLV ", " 10 (?) ", " 11 (?) ",
" 12 (?) ", " 13 (?) ", " 14 (?) ", " 15 (?) "
};
static char const *arm_r_types[] = {
"VANILLA ", "PAIR ", "SECTDIFF", "LOCSDIF ", "PBLAPTR ",
"BR24 ", "T_BR22 ", "T_BR32 ", "HALF ", "HALFDIF ",
" 10 (?) ", " 11 (?) ", " 12 (?) ", " 13 (?) ", " 14 (?) ", " 15 (?) "
};
static char const *arm64_r_types[] = {
"UNSIGND ", "SUB ", "BR26 ", "PAGE21 ", "PAGOF12 ",
"GOTLDP ", "GOTLDPOF", "PTRTGOT ", "TLVLDP ", "TLVLDPOF",
"ADDEND ", " 11 (?) ", " 12 (?) ", " 13 (?) ", " 14 (?) ", " 15 (?) "
};
if (r_type > 0xf){
outs() << format("%-7u", r_type) << " ";
return;
}
switch (cputype) {
case MachO::CPU_TYPE_I386:
outs() << generic_r_types[r_type];
break;
case MachO::CPU_TYPE_X86_64:
outs() << x86_64_r_types[r_type];
break;
case MachO::CPU_TYPE_ARM:
outs() << arm_r_types[r_type];
break;
case MachO::CPU_TYPE_ARM64:
case MachO::CPU_TYPE_ARM64_32:
outs() << arm64_r_types[r_type];
break;
default:
outs() << format("%-7u ", r_type);
}
}
static void PrintRLength(const uint64_t cputype, const unsigned r_type,
const unsigned r_length, const bool previous_arm_half){
if (cputype == MachO::CPU_TYPE_ARM &&
(r_type == MachO::ARM_RELOC_HALF ||
r_type == MachO::ARM_RELOC_HALF_SECTDIFF || previous_arm_half == true)) {
if ((r_length & 0x1) == 0)
outs() << "lo/";
else
outs() << "hi/";
if ((r_length & 0x1) == 0)
outs() << "arm ";
else
outs() << "thm ";
} else {
switch (r_length) {
case 0:
outs() << "byte ";
break;
case 1:
outs() << "word ";
break;
case 2:
outs() << "long ";
break;
case 3:
if (cputype == MachO::CPU_TYPE_X86_64)
outs() << "quad ";
else
outs() << format("?(%2d) ", r_length);
break;
default:
outs() << format("?(%2d) ", r_length);
}
}
}
static void PrintRelocationEntries(const MachOObjectFile *O,
const relocation_iterator Begin,
const relocation_iterator End,
const uint64_t cputype,
const bool verbose) {
const MachO::symtab_command Symtab = O->getSymtabLoadCommand();
bool previous_arm_half = false;
bool previous_sectdiff = false;
uint32_t sectdiff_r_type = 0;
for (relocation_iterator Reloc = Begin; Reloc != End; ++Reloc) {
const DataRefImpl Rel = Reloc->getRawDataRefImpl();
const MachO::any_relocation_info RE = O->getRelocation(Rel);
const unsigned r_type = O->getAnyRelocationType(RE);
const bool r_scattered = O->isRelocationScattered(RE);
const unsigned r_pcrel = O->getAnyRelocationPCRel(RE);
const unsigned r_length = O->getAnyRelocationLength(RE);
const unsigned r_address = O->getAnyRelocationAddress(RE);
const bool r_extern = (r_scattered ? false :
O->getPlainRelocationExternal(RE));
const uint32_t r_value = (r_scattered ?
O->getScatteredRelocationValue(RE) : 0);
const unsigned r_symbolnum = (r_scattered ? 0 :
O->getPlainRelocationSymbolNum(RE));
if (r_scattered && cputype != MachO::CPU_TYPE_X86_64) {
if (verbose) {
// scattered: address
if ((cputype == MachO::CPU_TYPE_I386 &&
r_type == MachO::GENERIC_RELOC_PAIR) ||
(cputype == MachO::CPU_TYPE_ARM && r_type == MachO::ARM_RELOC_PAIR))
outs() << " ";
else
outs() << format("%08x ", (unsigned int)r_address);
// scattered: pcrel
if (r_pcrel)
outs() << "True ";
else
outs() << "False ";
// scattered: length
PrintRLength(cputype, r_type, r_length, previous_arm_half);
// scattered: extern & type
outs() << "n/a ";
PrintRType(cputype, r_type);
// scattered: scattered & value
outs() << format("True 0x%08x", (unsigned int)r_value);
if (previous_sectdiff == false) {
if ((cputype == MachO::CPU_TYPE_ARM &&
r_type == MachO::ARM_RELOC_PAIR))
outs() << format(" half = 0x%04x ", (unsigned int)r_address);
} else if (cputype == MachO::CPU_TYPE_ARM &&
sectdiff_r_type == MachO::ARM_RELOC_HALF_SECTDIFF)
outs() << format(" other_half = 0x%04x ", (unsigned int)r_address);
if ((cputype == MachO::CPU_TYPE_I386 &&
(r_type == MachO::GENERIC_RELOC_SECTDIFF ||
r_type == MachO::GENERIC_RELOC_LOCAL_SECTDIFF)) ||
(cputype == MachO::CPU_TYPE_ARM &&
(sectdiff_r_type == MachO::ARM_RELOC_SECTDIFF ||
sectdiff_r_type == MachO::ARM_RELOC_LOCAL_SECTDIFF ||
sectdiff_r_type == MachO::ARM_RELOC_HALF_SECTDIFF))) {
previous_sectdiff = true;
sectdiff_r_type = r_type;
} else {
previous_sectdiff = false;
sectdiff_r_type = 0;
}
if (cputype == MachO::CPU_TYPE_ARM &&
(r_type == MachO::ARM_RELOC_HALF ||
r_type == MachO::ARM_RELOC_HALF_SECTDIFF))
previous_arm_half = true;
else
previous_arm_half = false;
outs() << "\n";
}
else {
// scattered: address pcrel length extern type scattered value
outs() << format("%08x %1d %-2d n/a %-7d 1 0x%08x\n",
(unsigned int)r_address, r_pcrel, r_length, r_type,
(unsigned int)r_value);
}
}
else {
if (verbose) {
// plain: address
if (cputype == MachO::CPU_TYPE_ARM && r_type == MachO::ARM_RELOC_PAIR)
outs() << " ";
else
outs() << format("%08x ", (unsigned int)r_address);
// plain: pcrel
if (r_pcrel)
outs() << "True ";
else
outs() << "False ";
// plain: length
PrintRLength(cputype, r_type, r_length, previous_arm_half);
if (r_extern) {
// plain: extern & type & scattered
outs() << "True ";
PrintRType(cputype, r_type);
outs() << "False ";
// plain: symbolnum/value
if (r_symbolnum > Symtab.nsyms)
outs() << format("?(%d)\n", r_symbolnum);
else {
SymbolRef Symbol = *O->getSymbolByIndex(r_symbolnum);
Expected<StringRef> SymNameNext = Symbol.getName();
const char *name = nullptr;
if (SymNameNext)
name = SymNameNext->data();
if (name == nullptr)
outs() << format("?(%d)\n", r_symbolnum);
else
outs() << name << "\n";
}
}
else {
// plain: extern & type & scattered
outs() << "False ";
PrintRType(cputype, r_type);
outs() << "False ";
// plain: symbolnum/value
if (cputype == MachO::CPU_TYPE_ARM && r_type == MachO::ARM_RELOC_PAIR)
outs() << format("other_half = 0x%04x\n", (unsigned int)r_address);
else if ((cputype == MachO::CPU_TYPE_ARM64 ||
cputype == MachO::CPU_TYPE_ARM64_32) &&
r_type == MachO::ARM64_RELOC_ADDEND)
outs() << format("addend = 0x%06x\n", (unsigned int)r_symbolnum);
else {
outs() << format("%d ", r_symbolnum);
if (r_symbolnum == MachO::R_ABS)
outs() << "R_ABS\n";
else {
// in this case, r_symbolnum is actually a 1-based section number
uint32_t nsects = O->section_end()->getRawDataRefImpl().d.a;
if (r_symbolnum > 0 && r_symbolnum <= nsects) {
object::DataRefImpl DRI;
DRI.d.a = r_symbolnum-1;
StringRef SegName = O->getSectionFinalSegmentName(DRI);
if (Expected<StringRef> NameOrErr = O->getSectionName(DRI))
outs() << "(" << SegName << "," << *NameOrErr << ")\n";
else
outs() << "(?,?)\n";
}
else {
outs() << "(?,?)\n";
}
}
}
}
if (cputype == MachO::CPU_TYPE_ARM &&
(r_type == MachO::ARM_RELOC_HALF ||
r_type == MachO::ARM_RELOC_HALF_SECTDIFF))
previous_arm_half = true;
else
previous_arm_half = false;
}
else {
// plain: address pcrel length extern type scattered symbolnum/section
outs() << format("%08x %1d %-2d %1d %-7d 0 %d\n",
(unsigned int)r_address, r_pcrel, r_length, r_extern,
r_type, r_symbolnum);
}
}
}
}
static void PrintRelocations(const MachOObjectFile *O, const bool verbose) {
const uint64_t cputype = O->getHeader().cputype;
const MachO::dysymtab_command Dysymtab = O->getDysymtabLoadCommand();
if (Dysymtab.nextrel != 0) {
outs() << "External relocation information " << Dysymtab.nextrel
<< " entries";
outs() << "\naddress pcrel length extern type scattered "
"symbolnum/value\n";
PrintRelocationEntries(O, O->extrel_begin(), O->extrel_end(), cputype,
verbose);
}
if (Dysymtab.nlocrel != 0) {
outs() << format("Local relocation information %u entries",
Dysymtab.nlocrel);
outs() << "\naddress pcrel length extern type scattered "
"symbolnum/value\n";
PrintRelocationEntries(O, O->locrel_begin(), O->locrel_end(), cputype,
verbose);
}
for (const auto &Load : O->load_commands()) {
if (Load.C.cmd == MachO::LC_SEGMENT_64) {
const MachO::segment_command_64 Seg = O->getSegment64LoadCommand(Load);
for (unsigned J = 0; J < Seg.nsects; ++J) {
const MachO::section_64 Sec = O->getSection64(Load, J);
if (Sec.nreloc != 0) {
DataRefImpl DRI;
DRI.d.a = J;
const StringRef SegName = O->getSectionFinalSegmentName(DRI);
if (Expected<StringRef> NameOrErr = O->getSectionName(DRI))
outs() << "Relocation information (" << SegName << "," << *NameOrErr
<< format(") %u entries", Sec.nreloc);
else
outs() << "Relocation information (" << SegName << ",?) "
<< format("%u entries", Sec.nreloc);
outs() << "\naddress pcrel length extern type scattered "
"symbolnum/value\n";
PrintRelocationEntries(O, O->section_rel_begin(DRI),
O->section_rel_end(DRI), cputype, verbose);
}
}
} else if (Load.C.cmd == MachO::LC_SEGMENT) {
const MachO::segment_command Seg = O->getSegmentLoadCommand(Load);
for (unsigned J = 0; J < Seg.nsects; ++J) {
const MachO::section Sec = O->getSection(Load, J);
if (Sec.nreloc != 0) {
DataRefImpl DRI;
DRI.d.a = J;
const StringRef SegName = O->getSectionFinalSegmentName(DRI);
if (Expected<StringRef> NameOrErr = O->getSectionName(DRI))
outs() << "Relocation information (" << SegName << "," << *NameOrErr
<< format(") %u entries", Sec.nreloc);
else
outs() << "Relocation information (" << SegName << ",?) "
<< format("%u entries", Sec.nreloc);
outs() << "\naddress pcrel length extern type scattered "
"symbolnum/value\n";
PrintRelocationEntries(O, O->section_rel_begin(DRI),
O->section_rel_end(DRI), cputype, verbose);
}
}
}
}
}
static void PrintFunctionStarts(MachOObjectFile *O) {
uint64_t BaseSegmentAddress = 0;
for (const MachOObjectFile::LoadCommandInfo &Command : O->load_commands()) {
if (Command.C.cmd == MachO::LC_SEGMENT) {
MachO::segment_command SLC = O->getSegmentLoadCommand(Command);
if (StringRef(SLC.segname) == "__TEXT") {
BaseSegmentAddress = SLC.vmaddr;
break;
}
} else if (Command.C.cmd == MachO::LC_SEGMENT_64) {
MachO::segment_command_64 SLC = O->getSegment64LoadCommand(Command);
if (StringRef(SLC.segname) == "__TEXT") {
BaseSegmentAddress = SLC.vmaddr;
break;
}
}
}
SmallVector<uint64_t, 8> FunctionStarts;
for (const MachOObjectFile::LoadCommandInfo &LC : O->load_commands()) {
if (LC.C.cmd == MachO::LC_FUNCTION_STARTS) {
MachO::linkedit_data_command FunctionStartsLC =
O->getLinkeditDataLoadCommand(LC);
O->ReadULEB128s(FunctionStartsLC.dataoff, FunctionStarts);
break;
}
}
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 (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";
}
}
}
static void PrintDataInCodeTable(MachOObjectFile *O, bool verbose) {
MachO::linkedit_data_command DIC = O->getDataInCodeLoadCommand();
uint32_t nentries = DIC.datasize / sizeof(struct MachO::data_in_code_entry);
outs() << "Data in code table (" << nentries << " entries)\n";
outs() << "offset length kind\n";
for (dice_iterator DI = O->begin_dices(), DE = O->end_dices(); DI != DE;
++DI) {
uint32_t Offset;
DI->getOffset(Offset);
outs() << format("0x%08" PRIx32, Offset) << " ";
uint16_t Length;
DI->getLength(Length);
outs() << format("%6u", Length) << " ";
uint16_t Kind;
DI->getKind(Kind);
if (verbose) {
switch (Kind) {
case MachO::DICE_KIND_DATA:
outs() << "DATA";
break;
case MachO::DICE_KIND_JUMP_TABLE8:
outs() << "JUMP_TABLE8";
break;
case MachO::DICE_KIND_JUMP_TABLE16:
outs() << "JUMP_TABLE16";
break;
case MachO::DICE_KIND_JUMP_TABLE32:
outs() << "JUMP_TABLE32";
break;
case MachO::DICE_KIND_ABS_JUMP_TABLE32:
outs() << "ABS_JUMP_TABLE32";
break;
default:
outs() << format("0x%04" PRIx32, Kind);
break;
}
} else
outs() << format("0x%04" PRIx32, Kind);
outs() << "\n";
}
}
static void PrintLinkOptHints(MachOObjectFile *O) {
MachO::linkedit_data_command LohLC = O->getLinkOptHintsLoadCommand();
const char *loh = O->getData().substr(LohLC.dataoff, 1).data();
uint32_t nloh = LohLC.datasize;
outs() << "Linker optimiztion hints (" << nloh << " total bytes)\n";
for (uint32_t i = 0; i < nloh;) {
unsigned n;
uint64_t identifier = decodeULEB128((const uint8_t *)(loh + i), &n);
i += n;
outs() << " identifier " << identifier << " ";
if (i >= nloh)
return;
switch (identifier) {
case 1:
outs() << "AdrpAdrp\n";
break;
case 2:
outs() << "AdrpLdr\n";
break;
case 3:
outs() << "AdrpAddLdr\n";
break;
case 4:
outs() << "AdrpLdrGotLdr\n";
break;
case 5:
outs() << "AdrpAddStr\n";
break;
case 6:
outs() << "AdrpLdrGotStr\n";
break;
case 7:
outs() << "AdrpAdd\n";
break;
case 8:
outs() << "AdrpLdrGot\n";
break;
default:
outs() << "Unknown identifier value\n";
break;
}
uint64_t narguments = decodeULEB128((const uint8_t *)(loh + i), &n);
i += n;
outs() << " narguments " << narguments << "\n";
if (i >= nloh)
return;
for (uint32_t j = 0; j < narguments; j++) {
uint64_t value = decodeULEB128((const uint8_t *)(loh + i), &n);
i += n;
outs() << "\tvalue " << format("0x%" PRIx64, value) << "\n";
if (i >= nloh)
return;
}
}
}
static SmallVector<std::string> GetSegmentNames(object::MachOObjectFile *O) {
SmallVector<std::string> Ret;
for (const MachOObjectFile::LoadCommandInfo &Command : O->load_commands()) {
if (Command.C.cmd == MachO::LC_SEGMENT) {
MachO::segment_command SLC = O->getSegmentLoadCommand(Command);
Ret.push_back(SLC.segname);
} else if (Command.C.cmd == MachO::LC_SEGMENT_64) {
MachO::segment_command_64 SLC = O->getSegment64LoadCommand(Command);
Ret.push_back(SLC.segname);
}
}
return Ret;
}
static void
PrintChainedFixupsHeader(const MachO::dyld_chained_fixups_header &H) {
outs() << "chained fixups header (LC_DYLD_CHAINED_FIXUPS)\n";
outs() << " fixups_version = " << H.fixups_version << '\n';
outs() << " starts_offset = " << H.starts_offset << '\n';
outs() << " imports_offset = " << H.imports_offset << '\n';
outs() << " symbols_offset = " << H.symbols_offset << '\n';
outs() << " imports_count = " << H.imports_count << '\n';
outs() << " imports_format = " << H.imports_format;
switch (H.imports_format) {
case llvm::MachO::DYLD_CHAINED_IMPORT:
outs() << " (DYLD_CHAINED_IMPORT)";
break;
case llvm::MachO::DYLD_CHAINED_IMPORT_ADDEND:
outs() << " (DYLD_CHAINED_IMPORT_ADDEND)";
break;
case llvm::MachO::DYLD_CHAINED_IMPORT_ADDEND64:
outs() << " (DYLD_CHAINED_IMPORT_ADDEND64)";
break;
}
outs() << '\n';
outs() << " symbols_format = " << H.symbols_format;
if (H.symbols_format == llvm::MachO::DYLD_CHAINED_SYMBOL_ZLIB)
outs() << " (zlib compressed)";
outs() << '\n';
}
static constexpr std::array<StringRef, 13> PointerFormats{
"DYLD_CHAINED_PTR_ARM64E",
"DYLD_CHAINED_PTR_64",
"DYLD_CHAINED_PTR_32",
"DYLD_CHAINED_PTR_32_CACHE",
"DYLD_CHAINED_PTR_32_FIRMWARE",
"DYLD_CHAINED_PTR_64_OFFSET",
"DYLD_CHAINED_PTR_ARM64E_KERNEL",
"DYLD_CHAINED_PTR_64_KERNEL_CACHE",
"DYLD_CHAINED_PTR_ARM64E_USERLAND",
"DYLD_CHAINED_PTR_ARM64E_FIRMWARE",
"DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE",
"DYLD_CHAINED_PTR_ARM64E_USERLAND24",
};
static void PrintChainedFixupsSegment(const ChainedFixupsSegment &Segment,
StringRef SegName) {
outs() << "chained starts in segment " << Segment.SegIdx << " (" << SegName
<< ")\n";
outs() << " size = " << Segment.Header.size << '\n';
outs() << " page_size = " << format("0x%0" PRIx16, Segment.Header.page_size)
<< '\n';
outs() << " pointer_format = " << Segment.Header.pointer_format;
if ((Segment.Header.pointer_format - 1) <
MachO::DYLD_CHAINED_PTR_ARM64E_USERLAND24)
outs() << " (" << PointerFormats[Segment.Header.pointer_format - 1] << ")";
outs() << '\n';
outs() << " segment_offset = "
<< format("0x%0" PRIx64, Segment.Header.segment_offset) << '\n';
outs() << " max_valid_pointer = " << Segment.Header.max_valid_pointer
<< '\n';
outs() << " page_count = " << Segment.Header.page_count << '\n';
for (auto [Index, PageStart] : enumerate(Segment.PageStarts)) {
outs() << " page_start[" << Index << "] = " << PageStart;
// FIXME: Support DYLD_CHAINED_PTR_START_MULTI (32-bit only)
if (PageStart == MachO::DYLD_CHAINED_PTR_START_NONE)
outs() << " (DYLD_CHAINED_PTR_START_NONE)";
outs() << '\n';
}
}
static void PrintChainedFixupTarget(ChainedFixupTarget &Target, size_t Idx,
int Format, MachOObjectFile *O) {
if (Format == MachO::DYLD_CHAINED_IMPORT)
outs() << "dyld chained import";
else if (Format == MachO::DYLD_CHAINED_IMPORT_ADDEND)
outs() << "dyld chained import addend";
else if (Format == MachO::DYLD_CHAINED_IMPORT_ADDEND64)
outs() << "dyld chained import addend64";
// FIXME: otool prints the encoded value as well.
outs() << '[' << Idx << "]\n";
outs() << " lib_ordinal = " << Target.libOrdinal() << " ("
<< ordinalName(O, Target.libOrdinal()) << ")\n";
outs() << " weak_import = " << Target.weakImport() << '\n';
outs() << " name_offset = " << Target.nameOffset() << " ("
<< Target.symbolName() << ")\n";
if (Format != MachO::DYLD_CHAINED_IMPORT)
outs() << " addend = " << (int64_t)Target.addend() << '\n';
}
static void PrintChainedFixups(MachOObjectFile *O) {
// MachOObjectFile::getChainedFixupsHeader() reads LC_DYLD_CHAINED_FIXUPS.
// FIXME: Support chained fixups in __TEXT,__chain_starts section too.
auto ChainedFixupHeader =
unwrapOrError(O->getChainedFixupsHeader(), O->getFileName());
if (!ChainedFixupHeader)
return;
PrintChainedFixupsHeader(*ChainedFixupHeader);
auto [SegCount, Segments] =
unwrapOrError(O->getChainedFixupsSegments(), O->getFileName());
auto SegNames = GetSegmentNames(O);
size_t StartsIdx = 0;
outs() << "chained starts in image\n";
outs() << " seg_count = " << SegCount << '\n';
for (size_t I = 0; I < SegCount; ++I) {
uint64_t SegOffset = 0;
if (StartsIdx < Segments.size() && I == Segments[StartsIdx].SegIdx) {
SegOffset = Segments[StartsIdx].Offset;
++StartsIdx;
}
outs() << " seg_offset[" << I << "] = " << SegOffset << " ("
<< SegNames[I] << ")\n";
}
for (const ChainedFixupsSegment &S : Segments)
PrintChainedFixupsSegment(S, SegNames[S.SegIdx]);
auto FixupTargets =
unwrapOrError(O->getDyldChainedFixupTargets(), O->getFileName());
uint32_t ImportsFormat = ChainedFixupHeader->imports_format;
for (auto [Idx, Target] : enumerate(FixupTargets))
PrintChainedFixupTarget(Target, Idx, ImportsFormat, O);
}
static void PrintDyldInfo(MachOObjectFile *O) {
Error Err = Error::success();
size_t SegmentWidth = strlen("segment");
size_t SectionWidth = strlen("section");
size_t AddressWidth = strlen("address");
size_t AddendWidth = strlen("addend");
size_t DylibWidth = strlen("dylib");
const size_t PointerWidth = 2 + O->getBytesInAddress() * 2;
auto HexLength = [](uint64_t Num) {
return Num ? (size_t)divideCeil(Log2_64(Num), 4) : 1;
};
for (const object::MachOChainedFixupEntry &Entry : O->fixupTable(Err)) {
SegmentWidth = std::max(SegmentWidth, Entry.segmentName().size());
SectionWidth = std::max(SectionWidth, Entry.sectionName().size());
AddressWidth = std::max(AddressWidth, HexLength(Entry.address()) + 2);
if (Entry.isBind()) {
AddendWidth = std::max(AddendWidth, HexLength(Entry.addend()) + 2);
DylibWidth = std::max(DylibWidth, Entry.symbolName().size());
}
}
// Errors will be handled when printing the table.
if (Err)
consumeError(std::move(Err));
outs() << "dyld information:\n";
outs() << left_justify("segment", SegmentWidth) << ' '
<< left_justify("section", SectionWidth) << ' '
<< left_justify("address", AddressWidth) << ' '
<< left_justify("pointer", PointerWidth) << " type "
<< left_justify("addend", AddendWidth) << ' '
<< left_justify("dylib", DylibWidth) << " symbol/vm address\n";
for (const object::MachOChainedFixupEntry &Entry : O->fixupTable(Err)) {
outs() << left_justify(Entry.segmentName(), SegmentWidth) << ' '
<< left_justify(Entry.sectionName(), SectionWidth) << ' ' << "0x"
<< left_justify(utohexstr(Entry.address()), AddressWidth - 2) << ' '
<< format_hex(Entry.rawValue(), PointerWidth, true) << ' ';
if (Entry.isBind()) {
outs() << "bind "
<< "0x" << left_justify(utohexstr(Entry.addend()), AddendWidth - 2)
<< ' ' << left_justify(ordinalName(O, Entry.ordinal()), DylibWidth)
<< ' ' << Entry.symbolName();
if (Entry.flags() & MachO::BIND_SYMBOL_FLAGS_WEAK_IMPORT)
outs() << " (weak import)";
outs() << '\n';
} else {
assert(Entry.isRebase());
outs() << "rebase";
outs().indent(AddendWidth + DylibWidth + 2);
outs() << format("0x%" PRIX64, Entry.pointerValue()) << '\n';
}
}
if (Err)
reportError(std::move(Err), O->getFileName());
// TODO: Print opcode-based fixups if the object uses those.
}
static void PrintDylibs(MachOObjectFile *O, bool JustId) {
unsigned Index = 0;
for (const auto &Load : O->load_commands()) {
if ((JustId && Load.C.cmd == MachO::LC_ID_DYLIB) ||
(!JustId && (Load.C.cmd == MachO::LC_ID_DYLIB ||
Load.C.cmd == MachO::LC_LOAD_DYLIB ||
Load.C.cmd == MachO::LC_LOAD_WEAK_DYLIB ||
Load.C.cmd == MachO::LC_REEXPORT_DYLIB ||
Load.C.cmd == MachO::LC_LAZY_LOAD_DYLIB ||
Load.C.cmd == MachO::LC_LOAD_UPWARD_DYLIB))) {
MachO::dylib_command dl = O->getDylibIDLoadCommand(Load);
if (dl.dylib.name < dl.cmdsize) {
const char *p = (const char *)(Load.Ptr) + dl.dylib.name;
if (JustId)
outs() << p << "\n";
else {
outs() << "\t" << p;
outs() << " (compatibility version "
<< ((dl.dylib.compatibility_version >> 16) & 0xffff) << "."
<< ((dl.dylib.compatibility_version >> 8) & 0xff) << "."
<< (dl.dylib.compatibility_version & 0xff) << ",";
outs() << " current version "
<< ((dl.dylib.current_version >> 16) & 0xffff) << "."
<< ((dl.dylib.current_version >> 8) & 0xff) << "."
<< (dl.dylib.current_version & 0xff);
if (Load.C.cmd == MachO::LC_LOAD_WEAK_DYLIB)
outs() << ", weak";
if (Load.C.cmd == MachO::LC_REEXPORT_DYLIB)
outs() << ", reexport";
if (Load.C.cmd == MachO::LC_LOAD_UPWARD_DYLIB)
outs() << ", upward";
if (Load.C.cmd == MachO::LC_LAZY_LOAD_DYLIB)
outs() << ", lazy";
outs() << ")\n";
}
} else {
outs() << "\tBad offset (" << dl.dylib.name << ") for name of ";
if (Load.C.cmd == MachO::LC_ID_DYLIB)
outs() << "LC_ID_DYLIB ";
else if (Load.C.cmd == MachO::LC_LOAD_DYLIB)
outs() << "LC_LOAD_DYLIB ";
else if (Load.C.cmd == MachO::LC_LOAD_WEAK_DYLIB)
outs() << "LC_LOAD_WEAK_DYLIB ";
else if (Load.C.cmd == MachO::LC_LAZY_LOAD_DYLIB)
outs() << "LC_LAZY_LOAD_DYLIB ";
else if (Load.C.cmd == MachO::LC_REEXPORT_DYLIB)
outs() << "LC_REEXPORT_DYLIB ";
else if (Load.C.cmd == MachO::LC_LOAD_UPWARD_DYLIB)
outs() << "LC_LOAD_UPWARD_DYLIB ";
else
outs() << "LC_??? ";
outs() << "command " << Index++ << "\n";
}
}
}
}
static void printRpaths(MachOObjectFile *O) {
for (const auto &Command : O->load_commands()) {
if (Command.C.cmd == MachO::LC_RPATH) {
auto Rpath = O->getRpathCommand(Command);
const char *P = (const char *)(Command.Ptr) + Rpath.path;
outs() << P << "\n";
}
}
}
typedef DenseMap<uint64_t, StringRef> SymbolAddressMap;
static void CreateSymbolAddressMap(MachOObjectFile *O,
SymbolAddressMap *AddrMap) {
// Create a map of symbol addresses to symbol names.
const StringRef FileName = O->getFileName();
for (const SymbolRef &Symbol : O->symbols()) {
SymbolRef::Type ST = unwrapOrError(Symbol.getType(), FileName);
if (ST == SymbolRef::ST_Function || ST == SymbolRef::ST_Data ||
ST == SymbolRef::ST_Other) {
uint64_t Address = cantFail(Symbol.getValue());
StringRef SymName = unwrapOrError(Symbol.getName(), FileName);
if (!SymName.startswith(".objc"))
(*AddrMap)[Address] = SymName;
}
}
}
// GuessSymbolName is passed the address of what might be a symbol and a
// pointer to the SymbolAddressMap. It returns the name of a symbol
// with that address or nullptr if no symbol is found with that address.
static const char *GuessSymbolName(uint64_t value, SymbolAddressMap *AddrMap) {
const char *SymbolName = nullptr;
// A DenseMap can't lookup up some values.
if (value != 0xffffffffffffffffULL && value != 0xfffffffffffffffeULL) {
StringRef name = AddrMap->lookup(value);
if (!name.empty())
SymbolName = name.data();
}
return SymbolName;
}
static void DumpCstringChar(const char c) {
char p[2];
p[0] = c;
p[1] = '\0';
outs().write_escaped(p);
}
static void DumpCstringSection(MachOObjectFile *O, const char *sect,
uint32_t sect_size, uint64_t sect_addr,
bool print_addresses) {
for (uint32_t i = 0; i < sect_size; i++) {
if (print_addresses) {
if (O->is64Bit())
outs() << format("%016" PRIx64, sect_addr + i) << " ";
else
outs() << format("%08" PRIx64, sect_addr + i) << " ";
}
for (; i < sect_size && sect[i] != '\0'; i++)
DumpCstringChar(sect[i]);
if (i < sect_size && sect[i] == '\0')
outs() << "\n";
}
}
static void DumpLiteral4(uint32_t l, float f) {
outs() << format("0x%08" PRIx32, l);
if ((l & 0x7f800000) != 0x7f800000)
outs() << format(" (%.16e)\n", f);
else {
if (l == 0x7f800000)
outs() << " (+Infinity)\n";
else if (l == 0xff800000)
outs() << " (-Infinity)\n";
else if ((l & 0x00400000) == 0x00400000)
outs() << " (non-signaling Not-a-Number)\n";
else
outs() << " (signaling Not-a-Number)\n";
}
}
static void DumpLiteral4Section(MachOObjectFile *O, const char *sect,
uint32_t sect_size, uint64_t sect_addr,
bool print_addresses) {
for (uint32_t i = 0; i < sect_size; i += sizeof(float)) {
if (print_addresses) {
if (O->is64Bit())
outs() << format("%016" PRIx64, sect_addr + i) << " ";
else
outs() << format("%08" PRIx64, sect_addr + i) << " ";
}
float f;
memcpy(&f, sect + i, sizeof(float));
if (O->isLittleEndian() != sys::IsLittleEndianHost)
sys::swapByteOrder(f);
uint32_t l;
memcpy(&l, sect + i, sizeof(uint32_t));
if (O->isLittleEndian() != sys::IsLittleEndianHost)
sys::swapByteOrder(l);
DumpLiteral4(l, f);
}
}
static void DumpLiteral8(MachOObjectFile *O, uint32_t l0, uint32_t l1,
double d) {
outs() << format("0x%08" PRIx32, l0) << " " << format("0x%08" PRIx32, l1);
uint32_t Hi, Lo;
Hi = (O->isLittleEndian()) ? l1 : l0;
Lo = (O->isLittleEndian()) ? l0 : l1;
// Hi is the high word, so this is equivalent to if(isfinite(d))
if ((Hi & 0x7ff00000) != 0x7ff00000)
outs() << format(" (%.16e)\n", d);
else {
if (Hi == 0x7ff00000 && Lo == 0)
outs() << " (+Infinity)\n";
else if (Hi == 0xfff00000 && Lo == 0)
outs() << " (-Infinity)\n";
else if ((Hi & 0x00080000) == 0x00080000)
outs() << " (non-signaling Not-a-Number)\n";
else
outs() << " (signaling Not-a-Number)\n";
}
}
static void DumpLiteral8Section(MachOObjectFile *O, const char *sect,
uint32_t sect_size, uint64_t sect_addr,
bool print_addresses) {
for (uint32_t i = 0; i < sect_size; i += sizeof(double)) {
if (print_addresses) {
if (O->is64Bit())
outs() << format("%016" PRIx64, sect_addr + i) << " ";
else
outs() << format("%08" PRIx64, sect_addr + i) << " ";
}
double d;
memcpy(&d, sect + i, sizeof(double));
if (O->isLittleEndian() != sys::IsLittleEndianHost)
sys::swapByteOrder(d);
uint32_t l0, l1;
memcpy(&l0, sect + i, sizeof(uint32_t));
memcpy(&l1, sect + i + sizeof(uint32_t), sizeof(uint32_t));
if (O->isLittleEndian() != sys::IsLittleEndianHost) {
sys::swapByteOrder(l0);
sys::swapByteOrder(l1);
}
DumpLiteral8(O, l0, l1, d);
}
}
static void DumpLiteral16(uint32_t l0, uint32_t l1, uint32_t l2, uint32_t l3) {
outs() << format("0x%08" PRIx32, l0) << " ";
outs() << format("0x%08" PRIx32, l1) << " ";
outs() << format("0x%08" PRIx32, l2) << " ";
outs() << format("0x%08" PRIx32, l3) << "\n";
}
static void DumpLiteral16Section(MachOObjectFile *O, const char *sect,
uint32_t sect_size, uint64_t sect_addr,
bool print_addresses) {
for (uint32_t i = 0; i < sect_size; i += 16) {
if (print_addresses) {
if (O->is64Bit())
outs() << format("%016" PRIx64, sect_addr + i) << " ";
else
outs() << format("%08" PRIx64, sect_addr + i) << " ";
}
uint32_t l0, l1, l2, l3;
memcpy(&l0, sect + i, sizeof(uint32_t));
memcpy(&l1, sect + i + sizeof(uint32_t), sizeof(uint32_t));
memcpy(&l2, sect + i + 2 * sizeof(uint32_t), sizeof(uint32_t));
memcpy(&l3, sect + i + 3 * sizeof(uint32_t), sizeof(uint32_t));
if (O->isLittleEndian() != sys::IsLittleEndianHost) {
sys::swapByteOrder(l0);
sys::swapByteOrder(l1);
sys::swapByteOrder(l2);
sys::swapByteOrder(l3);
}
DumpLiteral16(l0, l1, l2, l3);
}
}
static void DumpLiteralPointerSection(MachOObjectFile *O,
const SectionRef &Section,
const char *sect, uint32_t sect_size,
uint64_t sect_addr,
bool print_addresses) {
// Collect the literal sections in this Mach-O file.
std::vector<SectionRef> LiteralSections;
for (const SectionRef &Section : O->sections()) {
DataRefImpl Ref = Section.getRawDataRefImpl();
uint32_t section_type;
if (O->is64Bit()) {
const MachO::section_64 Sec = O->getSection64(Ref);
section_type = Sec.flags & MachO::SECTION_TYPE;
} else {
const MachO::section Sec = O->getSection(Ref);
section_type = Sec.flags & MachO::SECTION_TYPE;
}
if (section_type == MachO::S_CSTRING_LITERALS ||
section_type == MachO::S_4BYTE_LITERALS ||
section_type == MachO::S_8BYTE_LITERALS ||
section_type == MachO::S_16BYTE_LITERALS)
LiteralSections.push_back(Section);
}
// Set the size of the literal pointer.
uint32_t lp_size = O->is64Bit() ? 8 : 4;
// Collect the external relocation symbols for the literal pointers.
std::vector<std::pair<uint64_t, SymbolRef>> Relocs;
for (const RelocationRef &Reloc : Section.relocations()) {
DataRefImpl Rel;
MachO::any_relocation_info RE;
bool isExtern = false;
Rel = Reloc.getRawDataRefImpl();
RE = O->getRelocation(Rel);
isExtern = O->getPlainRelocationExternal(RE);
if (isExtern) {
uint64_t RelocOffset = Reloc.getOffset();
symbol_iterator RelocSym = Reloc.getSymbol();
Relocs.push_back(std::make_pair(RelocOffset, *RelocSym));
}
}
array_pod_sort(Relocs.begin(), Relocs.end());
// Dump each literal pointer.
for (uint32_t i = 0; i < sect_size; i += lp_size) {
if (print_addresses) {
if (O->is64Bit())
outs() << format("%016" PRIx64, sect_addr + i) << " ";
else
outs() << format("%08" PRIx64, sect_addr + i) << " ";
}
uint64_t lp;
if (O->is64Bit()) {
memcpy(&lp, sect + i, sizeof(uint64_t));
if (O->isLittleEndian() != sys::IsLittleEndianHost)
sys::swapByteOrder(lp);
} else {
uint32_t li;
memcpy(&li, sect + i, sizeof(uint32_t));
if (O->isLittleEndian() != sys::IsLittleEndianHost)
sys::swapByteOrder(li);
lp = li;
}
// First look for an external relocation entry for this literal pointer.
auto Reloc = find_if(Relocs, [&](const std::pair<uint64_t, SymbolRef> &P) {
return P.first == i;
});
if (Reloc != Relocs.end()) {
symbol_iterator RelocSym = Reloc->second;
StringRef SymName = unwrapOrError(RelocSym->getName(), O->getFileName());
outs() << "external relocation entry for symbol:" << SymName << "\n";
continue;
}
// For local references see what the section the literal pointer points to.
auto Sect = find_if(LiteralSections, [&](const SectionRef &R) {
return lp >= R.getAddress() && lp < R.getAddress() + R.getSize();
});
if (Sect == LiteralSections.end()) {
outs() << format("0x%" PRIx64, lp) << " (not in a literal section)\n";
continue;
}
uint64_t SectAddress = Sect->getAddress();
uint64_t SectSize = Sect->getSize();
StringRef SectName;
Expected<StringRef> SectNameOrErr = Sect->getName();
if (SectNameOrErr)
SectName = *SectNameOrErr;
else
consumeError(SectNameOrErr.takeError());
DataRefImpl Ref = Sect->getRawDataRefImpl();
StringRef SegmentName = O->getSectionFinalSegmentName(Ref);
outs() << SegmentName << ":" << SectName << ":";
uint32_t section_type;
if (O->is64Bit()) {
const MachO::section_64 Sec = O->getSection64(Ref);
section_type = Sec.flags & MachO::SECTION_TYPE;
} else {
const MachO::section Sec = O->getSection(Ref);
section_type = Sec.flags & MachO::SECTION_TYPE;
}
StringRef BytesStr = unwrapOrError(Sect->getContents(), O->getFileName());
const char *Contents = reinterpret_cast<const char *>(BytesStr.data());
switch (section_type) {
case MachO::S_CSTRING_LITERALS:
for (uint64_t i = lp - SectAddress; i < SectSize && Contents[i] != '\0';
i++) {
DumpCstringChar(Contents[i]);
}
outs() << "\n";
break;
case MachO::S_4BYTE_LITERALS:
float f;
memcpy(&f, Contents + (lp - SectAddress), sizeof(float));
uint32_t l;
memcpy(&l, Contents + (lp - SectAddress), sizeof(uint32_t));
if (O->isLittleEndian() != sys::IsLittleEndianHost) {
sys::swapByteOrder(f);
sys::swapByteOrder(l);
}
DumpLiteral4(l, f);
break;
case MachO::S_8BYTE_LITERALS: {
double d;
memcpy(&d, Contents + (lp - SectAddress), sizeof(double));
uint32_t l0, l1;
memcpy(&l0, Contents + (lp - SectAddress), sizeof(uint32_t));
memcpy(&l1, Contents + (lp - SectAddress) + sizeof(uint32_t),
sizeof(uint32_t));
if (O->isLittleEndian() != sys::IsLittleEndianHost) {
sys::swapByteOrder(f);
sys::swapByteOrder(l0);
sys::swapByteOrder(l1);
}
DumpLiteral8(O, l0, l1, d);
break;
}
case MachO::S_16BYTE_LITERALS: {
uint32_t l0, l1, l2, l3;
memcpy(&l0, Contents + (lp - SectAddress), sizeof(uint32_t));
memcpy(&l1, Contents + (lp - SectAddress) + sizeof(uint32_t),
sizeof(uint32_t));
memcpy(&l2, Contents + (lp - SectAddress) + 2 * sizeof(uint32_t),
sizeof(uint32_t));
memcpy(&l3, Contents + (lp - SectAddress) + 3 * sizeof(uint32_t),
sizeof(uint32_t));
if (O->isLittleEndian() != sys::IsLittleEndianHost) {
sys::swapByteOrder(l0);
sys::swapByteOrder(l1);
sys::swapByteOrder(l2);
sys::swapByteOrder(l3);
}
DumpLiteral16(l0, l1, l2, l3);
break;
}
}
}
}
static void DumpInitTermPointerSection(MachOObjectFile *O,
const SectionRef &Section,
const char *sect,
uint32_t sect_size, uint64_t sect_addr,
SymbolAddressMap *AddrMap,
bool verbose) {
uint32_t stride;
stride = (O->is64Bit()) ? sizeof(uint64_t) : sizeof(uint32_t);
// Collect the external relocation symbols for the pointers.
std::vector<std::pair<uint64_t, SymbolRef>> Relocs;
for (const RelocationRef &Reloc : Section.relocations()) {
DataRefImpl Rel;
MachO::any_relocation_info RE;
bool isExtern = false;
Rel = Reloc.getRawDataRefImpl();
RE = O->getRelocation(Rel);
isExtern = O->getPlainRelocationExternal(RE);
if (isExtern) {
uint64_t RelocOffset = Reloc.getOffset();
symbol_iterator RelocSym = Reloc.getSymbol();
Relocs.push_back(std::make_pair(RelocOffset, *RelocSym));
}
}
array_pod_sort(Relocs.begin(), Relocs.end());
for (uint32_t i = 0; i < sect_size; i += stride) {
const char *SymbolName = nullptr;
uint64_t p;
if (O->is64Bit()) {
outs() << format("0x%016" PRIx64, sect_addr + i * stride) << " ";
uint64_t pointer_value;
memcpy(&pointer_value, sect + i, stride);
if (O->isLittleEndian() != sys::IsLittleEndianHost)
sys::swapByteOrder(pointer_value);
outs() << format("0x%016" PRIx64, pointer_value);
p = pointer_value;
} else {
outs() << format("0x%08" PRIx64, sect_addr + i * stride) << " ";
uint32_t pointer_value;
memcpy(&pointer_value, sect + i, stride);
if (O->isLittleEndian() != sys::IsLittleEndianHost)
sys::swapByteOrder(pointer_value);
outs() << format("0x%08" PRIx32, pointer_value);
p = pointer_value;
}
if (verbose) {
// First look for an external relocation entry for this pointer.
auto Reloc = find_if(Relocs, [&](const std::pair<uint64_t, SymbolRef> &P) {
return P.first == i;
});
if (Reloc != Relocs.end()) {
symbol_iterator RelocSym = Reloc->second;
outs() << " " << unwrapOrError(RelocSym->getName(), O->getFileName());
} else {
SymbolName = GuessSymbolName(p, AddrMap);
if (SymbolName)
outs() << " " << SymbolName;
}
}
outs() << "\n";
}
}
static void DumpRawSectionContents(MachOObjectFile *O, const char *sect,
uint32_t size, uint64_t addr) {
uint32_t cputype = O->getHeader().cputype;
if (cputype == MachO::CPU_TYPE_I386 || cputype == MachO::CPU_TYPE_X86_64) {
uint32_t j;
for (uint32_t i = 0; i < size; i += j, addr += j) {
if (O->is64Bit())
outs() << format("%016" PRIx64, addr) << "\t";
else
outs() << format("%08" PRIx64, addr) << "\t";
for (j = 0; j < 16 && i + j < size; j++) {
uint8_t byte_word = *(sect + i + j);
outs() << format("%02" PRIx32, (uint32_t)byte_word) << " ";
}
outs() << "\n";
}
} else {
uint32_t j;
for (uint32_t i = 0; i < size; i += j, addr += j) {
if (O->is64Bit())
outs() << format("%016" PRIx64, addr) << "\t";
else
outs() << format("%08" PRIx64, addr) << "\t";
for (j = 0; j < 4 * sizeof(int32_t) && i + j < size;
j += sizeof(int32_t)) {
if (i + j + sizeof(int32_t) <= size) {
uint32_t long_word;
memcpy(&long_word, sect + i + j, sizeof(int32_t));
if (O->isLittleEndian() != sys::IsLittleEndianHost)
sys::swapByteOrder(long_word);
outs() << format("%08" PRIx32, long_word) << " ";
} else {
for (uint32_t k = 0; i + j + k < size; k++) {
uint8_t byte_word = *(sect + i + j + k);
outs() << format("%02" PRIx32, (uint32_t)byte_word) << " ";
}
}
}
outs() << "\n";
}
}
}
static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF,
StringRef DisSegName, StringRef DisSectName);
static void DumpProtocolSection(MachOObjectFile *O, const char *sect,
uint32_t size, uint32_t addr);
#ifdef LLVM_HAVE_LIBXAR
static void DumpBitcodeSection(MachOObjectFile *O, const char *sect,
uint32_t size, bool verbose,
bool PrintXarHeader, bool PrintXarFileHeaders,
std::string XarMemberName);
#endif // defined(LLVM_HAVE_LIBXAR)
static void DumpSectionContents(StringRef Filename, MachOObjectFile *O,
bool verbose) {
SymbolAddressMap AddrMap;
if (verbose)
CreateSymbolAddressMap(O, &AddrMap);
for (unsigned i = 0; i < FilterSections.size(); ++i) {
StringRef DumpSection = FilterSections[i];
std::pair<StringRef, StringRef> DumpSegSectName;
DumpSegSectName = DumpSection.split(',');
StringRef DumpSegName, DumpSectName;
if (!DumpSegSectName.second.empty()) {
DumpSegName = DumpSegSectName.first;
DumpSectName = DumpSegSectName.second;
} else {
DumpSegName = "";
DumpSectName = DumpSegSectName.first;
}
for (const SectionRef &Section : O->sections()) {
StringRef SectName;
Expected<StringRef> SecNameOrErr = Section.getName();
if (SecNameOrErr)
SectName = *SecNameOrErr;
else
consumeError(SecNameOrErr.takeError());
if (!DumpSection.empty())
FoundSectionSet.insert(DumpSection);
DataRefImpl Ref = Section.getRawDataRefImpl();
StringRef SegName = O->getSectionFinalSegmentName(Ref);
if ((DumpSegName.empty() || SegName == DumpSegName) &&
(SectName == DumpSectName)) {
uint32_t section_flags;
if (O->is64Bit()) {
const MachO::section_64 Sec = O->getSection64(Ref);
section_flags = Sec.flags;
} else {
const MachO::section Sec = O->getSection(Ref);
section_flags = Sec.flags;
}
uint32_t section_type = section_flags & MachO::SECTION_TYPE;
StringRef BytesStr =
unwrapOrError(Section.getContents(), O->getFileName());
const char *sect = reinterpret_cast<const char *>(BytesStr.data());
uint32_t sect_size = BytesStr.size();
uint64_t sect_addr = Section.getAddress();
if (LeadingHeaders)
outs() << "Contents of (" << SegName << "," << SectName
<< ") section\n";
if (verbose) {
if ((section_flags & MachO::S_ATTR_PURE_INSTRUCTIONS) ||
(section_flags & MachO::S_ATTR_SOME_INSTRUCTIONS)) {
DisassembleMachO(Filename, O, SegName, SectName);
continue;
}
if (SegName == "__TEXT" && SectName == "__info_plist") {
outs() << sect;
continue;
}
if (SegName == "__OBJC" && SectName == "__protocol") {
DumpProtocolSection(O, sect, sect_size, sect_addr);
continue;
}
#ifdef LLVM_HAVE_LIBXAR
if (SegName == "__LLVM" && SectName == "__bundle") {
DumpBitcodeSection(O, sect, sect_size, verbose, SymbolicOperands,
ArchiveHeaders, "");
continue;
}
#endif // defined(LLVM_HAVE_LIBXAR)
switch (section_type) {
case MachO::S_REGULAR:
DumpRawSectionContents(O, sect, sect_size, sect_addr);
break;
case MachO::S_ZEROFILL:
outs() << "zerofill section and has no contents in the file\n";
break;
case MachO::S_CSTRING_LITERALS:
DumpCstringSection(O, sect, sect_size, sect_addr, LeadingAddr);
break;
case MachO::S_4BYTE_LITERALS:
DumpLiteral4Section(O, sect, sect_size, sect_addr, LeadingAddr);
break;
case MachO::S_8BYTE_LITERALS:
DumpLiteral8Section(O, sect, sect_size, sect_addr, LeadingAddr);
break;
case MachO::S_16BYTE_LITERALS:
DumpLiteral16Section(O, sect, sect_size, sect_addr, LeadingAddr);
break;
case MachO::S_LITERAL_POINTERS:
DumpLiteralPointerSection(O, Section, sect, sect_size, sect_addr,
LeadingAddr);
break;
case MachO::S_MOD_INIT_FUNC_POINTERS:
case MachO::S_MOD_TERM_FUNC_POINTERS:
DumpInitTermPointerSection(O, Section, sect, sect_size, sect_addr,
&AddrMap, verbose);
break;
default:
outs() << "Unknown section type ("
<< format("0x%08" PRIx32, section_type) << ")\n";
DumpRawSectionContents(O, sect, sect_size, sect_addr);
break;
}
} else {
if (section_type == MachO::S_ZEROFILL)
outs() << "zerofill section and has no contents in the file\n";
else
DumpRawSectionContents(O, sect, sect_size, sect_addr);
}
}
}
}
}
static void DumpInfoPlistSectionContents(StringRef Filename,
MachOObjectFile *O) {
for (const SectionRef &Section : O->sections()) {
StringRef SectName;
Expected<StringRef> SecNameOrErr = Section.getName();
if (SecNameOrErr)
SectName = *SecNameOrErr;
else
consumeError(SecNameOrErr.takeError());
DataRefImpl Ref = Section.getRawDataRefImpl();
StringRef SegName = O->getSectionFinalSegmentName(Ref);
if (SegName == "__TEXT" && SectName == "__info_plist") {
if (LeadingHeaders)
outs() << "Contents of (" << SegName << "," << SectName << ") section\n";
StringRef BytesStr =
unwrapOrError(Section.getContents(), O->getFileName());
const char *sect = reinterpret_cast<const char *>(BytesStr.data());
outs() << format("%.*s", BytesStr.size(), sect) << "\n";
return;
}
}
}
// checkMachOAndArchFlags() checks to see if the ObjectFile is a Mach-O file
// and if it is and there is a list of architecture flags is specified then
// check to make sure this Mach-O file is one of those architectures or all
// architectures were specified. If not then an error is generated and this
// routine returns false. Else it returns true.
static bool checkMachOAndArchFlags(ObjectFile *O, StringRef Filename) {
auto *MachO = dyn_cast<MachOObjectFile>(O);
if (!MachO || ArchAll || ArchFlags.empty())
return true;
MachO::mach_header H;
MachO::mach_header_64 H_64;
Triple T;
const char *McpuDefault, *ArchFlag;
if (MachO->is64Bit()) {
H_64 = MachO->MachOObjectFile::getHeader64();
T = MachOObjectFile::getArchTriple(H_64.cputype, H_64.cpusubtype,
&McpuDefault, &ArchFlag);
} else {
H = MachO->MachOObjectFile::getHeader();
T = MachOObjectFile::getArchTriple(H.cputype, H.cpusubtype,
&McpuDefault, &ArchFlag);
}
const std::string ArchFlagName(ArchFlag);
if (!llvm::is_contained(ArchFlags, ArchFlagName)) {
WithColor::error(errs(), "llvm-objdump")
<< Filename << ": no architecture specified.\n";
return false;
}
return true;
}
static void printObjcMetaData(MachOObjectFile *O, bool verbose);
// ProcessMachO() is passed a single opened Mach-O file, which may be an
// archive member and or in a slice of a universal file. It prints the
// the file name and header info and then processes it according to the
// command line options.
static void ProcessMachO(StringRef Name, MachOObjectFile *MachOOF,
StringRef ArchiveMemberName = StringRef(),
StringRef ArchitectureName = StringRef()) {
std::unique_ptr<Dumper> D = createMachODumper(*MachOOF);
// If we are doing some processing here on the Mach-O file print the header
// info. And don't print it otherwise like in the case of printing the
// UniversalHeaders or ArchiveHeaders.
if (Disassemble || Relocations || PrivateHeaders || ExportsTrie || Rebase ||
Bind || SymbolTable || LazyBind || WeakBind || IndirectSymbols ||
DataInCode || FunctionStartsType != FunctionStartsMode::None ||
LinkOptHints || ChainedFixups || DyldInfo || DylibsUsed || DylibId ||
Rpaths || ObjcMetaData || (!FilterSections.empty())) {
if (LeadingHeaders) {
outs() << Name;
if (!ArchiveMemberName.empty())
outs() << '(' << ArchiveMemberName << ')';
if (!ArchitectureName.empty())
outs() << " (architecture " << ArchitectureName << ")";
outs() << ":\n";
}
}
// To use the report_error() form with an ArchiveName and FileName set
// these up based on what is passed for Name and ArchiveMemberName.
StringRef ArchiveName;
StringRef FileName;
if (!ArchiveMemberName.empty()) {
ArchiveName = Name;
FileName = ArchiveMemberName;
} else {
ArchiveName = StringRef();
FileName = Name;
}
// If we need the symbol table to do the operation then check it here to
// produce a good error message as to where the Mach-O file comes from in
// the error message.
if (Disassemble || IndirectSymbols || !FilterSections.empty() || UnwindInfo)
if (Error Err = MachOOF->checkSymbolTable())
reportError(std::move(Err), FileName, ArchiveName, ArchitectureName);
if (DisassembleAll) {
for (const SectionRef &Section : MachOOF->sections()) {
StringRef SectName;
if (Expected<StringRef> NameOrErr = Section.getName())
SectName = *NameOrErr;
else
consumeError(NameOrErr.takeError());
if (SectName.equals("__text")) {
DataRefImpl Ref = Section.getRawDataRefImpl();
StringRef SegName = MachOOF->getSectionFinalSegmentName(Ref);
DisassembleMachO(FileName, MachOOF, SegName, SectName);
}
}
}
else if (Disassemble) {
if (MachOOF->getHeader().filetype == MachO::MH_KEXT_BUNDLE &&
MachOOF->getHeader().cputype == MachO::CPU_TYPE_ARM64)
DisassembleMachO(FileName, MachOOF, "__TEXT_EXEC", "__text");
else
DisassembleMachO(FileName, MachOOF, "__TEXT", "__text");
}
if (IndirectSymbols)
PrintIndirectSymbols(MachOOF, Verbose);
if (DataInCode)
PrintDataInCodeTable(MachOOF, Verbose);
if (FunctionStartsType != FunctionStartsMode::None)
PrintFunctionStarts(MachOOF);
if (LinkOptHints)
PrintLinkOptHints(MachOOF);
if (Relocations)
PrintRelocations(MachOOF, Verbose);
if (SectionHeaders)
printSectionHeaders(*MachOOF);
if (SectionContents)
printSectionContents(MachOOF);
if (!FilterSections.empty())
DumpSectionContents(FileName, MachOOF, Verbose);
if (InfoPlist)
DumpInfoPlistSectionContents(FileName, MachOOF);
if (DyldInfo)
PrintDyldInfo(MachOOF);
if (ChainedFixups)
PrintChainedFixups(MachOOF);
if (DylibsUsed)
PrintDylibs(MachOOF, false);
if (DylibId)
PrintDylibs(MachOOF, true);
if (SymbolTable)
D->printSymbolTable(ArchiveName, ArchitectureName);
if (UnwindInfo)
printMachOUnwindInfo(MachOOF);
if (PrivateHeaders) {
printMachOFileHeader(MachOOF);
printMachOLoadCommands(MachOOF);
}
if (FirstPrivateHeader)
printMachOFileHeader(MachOOF);
if (ObjcMetaData)
printObjcMetaData(MachOOF, Verbose);
if (ExportsTrie)
printExportsTrie(MachOOF);
if (Rebase)
printRebaseTable(MachOOF);
if (Rpaths)
printRpaths(MachOOF);
if (Bind)
printBindTable(MachOOF);
if (LazyBind)
printLazyBindTable(MachOOF);
if (WeakBind)
printWeakBindTable(MachOOF);
if (DwarfDumpType != DIDT_Null) {
std::unique_ptr<DIContext> DICtx = DWARFContext::create(*MachOOF);
// Dump the complete DWARF structure.
DIDumpOptions DumpOpts;
DumpOpts.DumpType = DwarfDumpType;
DICtx->dump(outs(), DumpOpts);
}
}
// printUnknownCPUType() helps print_fat_headers for unknown CPU's.
static void printUnknownCPUType(uint32_t cputype, uint32_t cpusubtype) {
outs() << " cputype (" << cputype << ")\n";
outs() << " cpusubtype (" << cpusubtype << ")\n";
}
// printCPUType() helps print_fat_headers by printing the cputype and
// pusubtype (symbolically for the one's it knows about).
static void printCPUType(uint32_t cputype, uint32_t cpusubtype) {
switch (cputype) {
case MachO::CPU_TYPE_I386:
switch (cpusubtype) {
case MachO::CPU_SUBTYPE_I386_ALL:
outs() << " cputype CPU_TYPE_I386\n";
outs() << " cpusubtype CPU_SUBTYPE_I386_ALL\n";
break;
default:
printUnknownCPUType(cputype, cpusubtype);
break;
}
break;
case MachO::CPU_TYPE_X86_64:
switch (cpusubtype) {
case MachO::CPU_SUBTYPE_X86_64_ALL:
outs() << " cputype CPU_TYPE_X86_64\n";
outs() << " cpusubtype CPU_SUBTYPE_X86_64_ALL\n";
break;
case MachO::CPU_SUBTYPE_X86_64_H:
outs() << " cputype CPU_TYPE_X86_64\n";
outs() << " cpusubtype CPU_SUBTYPE_X86_64_H\n";
break;
default:
printUnknownCPUType(cputype, cpusubtype);
break;
}
break;
case MachO::CPU_TYPE_ARM:
switch (cpusubtype) {
case MachO::CPU_SUBTYPE_ARM_ALL:
outs() << " cputype CPU_TYPE_ARM\n";
outs() << " cpusubtype CPU_SUBTYPE_ARM_ALL\n";
break;
case MachO::CPU_SUBTYPE_ARM_V4T:
outs() << " cputype CPU_TYPE_ARM\n";
outs() << " cpusubtype CPU_SUBTYPE_ARM_V4T\n";
break;
case MachO::CPU_SUBTYPE_ARM_V5TEJ:
outs() << " cputype CPU_TYPE_ARM\n";
outs() << " cpusubtype CPU_SUBTYPE_ARM_V5TEJ\n";
break;
case MachO::CPU_SUBTYPE_ARM_XSCALE:
outs() << " cputype CPU_TYPE_ARM\n";
outs() << " cpusubtype CPU_SUBTYPE_ARM_XSCALE\n";
break;
case MachO::CPU_SUBTYPE_ARM_V6:
outs() << " cputype CPU_TYPE_ARM\n";
outs() << " cpusubtype CPU_SUBTYPE_ARM_V6\n";
break;
case MachO::CPU_SUBTYPE_ARM_V6M:
outs() << " cputype CPU_TYPE_ARM\n";
outs() << " cpusubtype CPU_SUBTYPE_ARM_V6M\n";
break;
case MachO::CPU_SUBTYPE_ARM_V7:
outs() << " cputype CPU_TYPE_ARM\n";
outs() << " cpusubtype CPU_SUBTYPE_ARM_V7\n";
break;
case MachO::CPU_SUBTYPE_ARM_V7EM:
outs() << " cputype CPU_TYPE_ARM\n";
outs() << " cpusubtype CPU_SUBTYPE_ARM_V7EM\n";
break;
case MachO::CPU_SUBTYPE_ARM_V7K:
outs() << " cputype CPU_TYPE_ARM\n";
outs() << " cpusubtype CPU_SUBTYPE_ARM_V7K\n";
break;
case MachO::CPU_SUBTYPE_ARM_V7M:
outs() << " cputype CPU_TYPE_ARM\n";
outs() << " cpusubtype CPU_SUBTYPE_ARM_V7M\n";
break;
case MachO::CPU_SUBTYPE_ARM_V7S:
outs() << " cputype CPU_TYPE_ARM\n";
outs() << " cpusubtype CPU_SUBTYPE_ARM_V7S\n";
break;
default:
printUnknownCPUType(cputype, cpusubtype);
break;
}
break;
case MachO::CPU_TYPE_ARM64:
switch (cpusubtype & ~MachO::CPU_SUBTYPE_MASK) {
case MachO::CPU_SUBTYPE_ARM64_ALL:
outs() << " cputype CPU_TYPE_ARM64\n";
outs() << " cpusubtype CPU_SUBTYPE_ARM64_ALL\n";
break;
case MachO::CPU_SUBTYPE_ARM64_V8:
outs() << " cputype CPU_TYPE_ARM64\n";
outs() << " cpusubtype CPU_SUBTYPE_ARM64_V8\n";
break;
case MachO::CPU_SUBTYPE_ARM64E:
outs() << " cputype CPU_TYPE_ARM64\n";
outs() << " cpusubtype CPU_SUBTYPE_ARM64E\n";
break;
default:
printUnknownCPUType(cputype, cpusubtype);
break;
}
break;
case MachO::CPU_TYPE_ARM64_32:
switch (cpusubtype & ~MachO::CPU_SUBTYPE_MASK) {
case MachO::CPU_SUBTYPE_ARM64_32_V8:
outs() << " cputype CPU_TYPE_ARM64_32\n";
outs() << " cpusubtype CPU_SUBTYPE_ARM64_32_V8\n";
break;
default:
printUnknownCPUType(cputype, cpusubtype);
break;
}
break;
default:
printUnknownCPUType(cputype, cpusubtype);
break;
}
}
static void printMachOUniversalHeaders(const object::MachOUniversalBinary *UB,
bool verbose) {
outs() << "Fat headers\n";
if (verbose) {
if (UB->getMagic() == MachO::FAT_MAGIC)
outs() << "fat_magic FAT_MAGIC\n";
else // UB->getMagic() == MachO::FAT_MAGIC_64
outs() << "fat_magic FAT_MAGIC_64\n";
} else
outs() << "fat_magic " << format("0x%" PRIx32, MachO::FAT_MAGIC) << "\n";
uint32_t nfat_arch = UB->getNumberOfObjects();
StringRef Buf = UB->getData();
uint64_t size = Buf.size();
uint64_t big_size = sizeof(struct MachO::fat_header) +
nfat_arch * sizeof(struct MachO::fat_arch);
outs() << "nfat_arch " << UB->getNumberOfObjects();
if (nfat_arch == 0)
outs() << " (malformed, contains zero architecture types)\n";
else if (big_size > size)
outs() << " (malformed, architectures past end of file)\n";
else
outs() << "\n";
for (uint32_t i = 0; i < nfat_arch; ++i) {
MachOUniversalBinary::ObjectForArch OFA(UB, i);
uint32_t cputype = OFA.getCPUType();
uint32_t cpusubtype = OFA.getCPUSubType();
outs() << "architecture ";
for (uint32_t j = 0; i != 0 && j <= i - 1; j++) {
MachOUniversalBinary::ObjectForArch other_OFA(UB, j);
uint32_t other_cputype = other_OFA.getCPUType();
uint32_t other_cpusubtype = other_OFA.getCPUSubType();
if (cputype != 0 && cpusubtype != 0 && cputype == other_cputype &&
(cpusubtype & ~MachO::CPU_SUBTYPE_MASK) ==
(other_cpusubtype & ~MachO::CPU_SUBTYPE_MASK)) {
outs() << "(illegal duplicate architecture) ";
break;
}
}
if (verbose) {
outs() << OFA.getArchFlagName() << "\n";
printCPUType(cputype, cpusubtype & ~MachO::CPU_SUBTYPE_MASK);
} else {
outs() << i << "\n";
outs() << " cputype " << cputype << "\n";
outs() << " cpusubtype " << (cpusubtype & ~MachO::CPU_SUBTYPE_MASK)
<< "\n";
}
if (verbose &&
(cpusubtype & MachO::CPU_SUBTYPE_MASK) == MachO::CPU_SUBTYPE_LIB64)
outs() << " capabilities CPU_SUBTYPE_LIB64\n";
else
outs() << " capabilities "
<< format("0x%" PRIx32,
(cpusubtype & MachO::CPU_SUBTYPE_MASK) >> 24) << "\n";
outs() << " offset " << OFA.getOffset();
if (OFA.getOffset() > size)
outs() << " (past end of file)";
if (OFA.getOffset() % (1ull << OFA.getAlign()) != 0)
outs() << " (not aligned on it's alignment (2^" << OFA.getAlign() << ")";
outs() << "\n";
outs() << " size " << OFA.getSize();
big_size = OFA.getOffset() + OFA.getSize();
if (big_size > size)