| //===-- 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; |
| } |
| }; |
| } // namespace |
| |
| // 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()) { |
| // 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) |
| printSymbolTable(*MachOOF, 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) |
| outs() << " (past end of file)"; |
| outs() << "\n"; |
| outs() << " align 2^" << OFA.getAlign() << " (" << (1 << OFA.getAlign()) |
| << ")\n"; |
| } |
| } |
| |
| static void printArchiveChild(StringRef Filename, const Archive::Child &C, |
| size_t ChildIndex, bool verbose, |
| bool print_offset, |
| StringRef ArchitectureName = StringRef()) { |
| if (print_offset) |
| outs() << C.getChildOffset() << |