| //===-- MachODump.cpp - Object file dumping utility for llvm --------------===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // This file implements the MachO-specific dumper for llvm-objdump. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "llvm-objdump.h" |
| #include "llvm-c/Disassembler.h" |
| #include "llvm/ADT/STLExtras.h" |
| #include "llvm/ADT/StringExtras.h" |
| #include "llvm/ADT/Triple.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/Object/MachO.h" |
| #include "llvm/Object/MachOUniversal.h" |
| #include "llvm/Support/Casting.h" |
| #include "llvm/Support/CommandLine.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/TargetRegistry.h" |
| #include "llvm/Support/TargetSelect.h" |
| #include "llvm/Support/ToolOutputFile.h" |
| #include "llvm/Support/raw_ostream.h" |
| #include <algorithm> |
| #include <cstring> |
| #include <system_error> |
| |
| #ifdef HAVE_LIBXAR |
| extern "C" { |
| #include <xar/xar.h> |
| } |
| #endif |
| |
| using namespace llvm; |
| using namespace object; |
| |
| static cl::opt<bool> |
| UseDbg("g", |
| cl::desc("Print line information from debug info if available")); |
| |
| static cl::opt<std::string> DSYMFile("dsym", |
| cl::desc("Use .dSYM file for debug info")); |
| |
| static cl::opt<bool> FullLeadingAddr("full-leading-addr", |
| cl::desc("Print full leading address")); |
| |
| static cl::opt<bool> NoLeadingHeaders("no-leading-headers", |
| cl::desc("Print no leading headers")); |
| |
| cl::opt<bool> llvm::UniversalHeaders("universal-headers", |
| cl::desc("Print Mach-O universal headers " |
| "(requires -macho)")); |
| |
| cl::opt<bool> |
| llvm::ArchiveHeaders("archive-headers", |
| cl::desc("Print archive headers for Mach-O archives " |
| "(requires -macho)")); |
| |
| cl::opt<bool> |
| ArchiveMemberOffsets("archive-member-offsets", |
| cl::desc("Print the offset to each archive member for " |
| "Mach-O archives (requires -macho and " |
| "-archive-headers)")); |
| |
| cl::opt<bool> |
| llvm::IndirectSymbols("indirect-symbols", |
| cl::desc("Print indirect symbol table for Mach-O " |
| "objects (requires -macho)")); |
| |
| cl::opt<bool> |
| llvm::DataInCode("data-in-code", |
| cl::desc("Print the data in code table for Mach-O objects " |
| "(requires -macho)")); |
| |
| cl::opt<bool> |
| llvm::LinkOptHints("link-opt-hints", |
| cl::desc("Print the linker optimization hints for " |
| "Mach-O objects (requires -macho)")); |
| |
| cl::opt<bool> |
| llvm::InfoPlist("info-plist", |
| cl::desc("Print the info plist section as strings for " |
| "Mach-O objects (requires -macho)")); |
| |
| cl::opt<bool> |
| llvm::DylibsUsed("dylibs-used", |
| cl::desc("Print the shared libraries used for linked " |
| "Mach-O files (requires -macho)")); |
| |
| cl::opt<bool> |
| llvm::DylibId("dylib-id", |
| cl::desc("Print the shared library's id for the dylib Mach-O " |
| "file (requires -macho)")); |
| |
| cl::opt<bool> |
| llvm::NonVerbose("non-verbose", |
| cl::desc("Print the info for Mach-O objects in " |
| "non-verbose or numeric form (requires -macho)")); |
| |
| cl::opt<bool> |
| llvm::ObjcMetaData("objc-meta-data", |
| cl::desc("Print the Objective-C runtime meta data for " |
| "Mach-O files (requires -macho)")); |
| |
| cl::opt<std::string> llvm::DisSymName( |
| "dis-symname", |
| cl::desc("disassemble just this symbol's instructions (requires -macho)")); |
| |
| static cl::opt<bool> NoSymbolicOperands( |
| "no-symbolic-operands", |
| cl::desc("do not symbolic operands when disassembling (requires -macho)")); |
| |
| static cl::list<std::string> |
| ArchFlags("arch", cl::desc("architecture(s) from a Mach-O file to dump"), |
| cl::ZeroOrMore); |
| |
| bool ArchAll = false; |
| |
| static std::string ThumbTripleName; |
| |
| static const Target *GetTarget(const MachOObjectFile *MachOObj, |
| const char **McpuDefault, |
| const Target **ThumbTarget) { |
| // Figure out the target triple. |
| llvm::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. |
| llvm::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; |
| |
| errs() << "llvm-objdump: error: unable to get target for '"; |
| if (!TheTarget) |
| errs() << TripleName; |
| else |
| errs() << ThumbTripleName; |
| errs() << "', see --version and --triple.\n"; |
| return nullptr; |
| } |
| |
| struct SymbolSorter { |
| bool operator()(const SymbolRef &A, const SymbolRef &B) { |
| Expected<SymbolRef::Type> ATypeOrErr = A.getType(); |
| if (!ATypeOrErr) |
| report_error(A.getObject()->getFileName(), ATypeOrErr.takeError()); |
| SymbolRef::Type AType = *ATypeOrErr; |
| Expected<SymbolRef::Type> BTypeOrErr = B.getType(); |
| if (!BTypeOrErr) |
| report_error(B.getObject()->getFileName(), BTypeOrErr.takeError()); |
| SymbolRef::Type BType = *BTypeOrErr; |
| uint64_t AAddr = (AType != SymbolRef::ST_Function) ? 0 : A.getValue(); |
| uint64_t BAddr = (BType != SymbolRef::ST_Function) ? 0 : B.getValue(); |
| return AAddr < BAddr; |
| } |
| }; |
| |
| // 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 HAVE_LIBXAR |
| namespace { |
| struct ScopedXarFile { |
| xar_t xar; |
| ScopedXarFile(const char *filename, int32_t flags) |
| : xar(xar_open(filename, flags)) {} |
| ~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(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 (!NoShowRawInsn) |
| dumpBytes(makeArrayRef(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 (!NoShowRawInsn) |
| dumpBytes(makeArrayRef(bytes, 2), outs()); |
| Value = bytes[1] << 8 | bytes[0]; |
| outs() << "\t.short " << Value; |
| Size = 2; |
| } else { |
| if (!NoShowRawInsn) |
| dumpBytes(makeArrayRef(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 (!NoShowRawInsn) |
| dumpBytes(makeArrayRef(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 (!NoShowRawInsn) |
| dumpBytes(makeArrayRef(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 (!NoShowRawInsn) |
| dumpBytes(makeArrayRef(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) { |
| for (const SymbolRef &Symbol : MachOObj->symbols()) { |
| Expected<StringRef> SymName = Symbol.getName(); |
| if (!SymName) |
| report_error(MachOObj->getFileName(), SymName.takeError()); |
| if (!SymName->startswith("ltmp")) |
| Symbols.push_back(Symbol); |
| } |
| |
| for (const SectionRef &Section : MachOObj->sections()) { |
| StringRef SectName; |
| Section.getName(SectName); |
| Sections.push_back(Section); |
| } |
| |
| 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; |
| } |
| } |
| } |
| } |
| |
| 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; |
| Expected<StringRef> SymName = Symbol.getName(); |
| if (!SymName) |
| report_error(O->getFileName(), SymName.takeError()); |
| outs() << *SymName; |
| } 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: |
| 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 == llvm::MachO::ARM_RELOC_HALF || |
| r_type == llvm::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 == llvm::MachO::GENERIC_RELOC_PAIR) || |
| (cputype == MachO::CPU_TYPE_ARM && |
| r_type == llvm::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 == llvm::MachO::ARM_RELOC_PAIR)) |
| outs() << format(" half = 0x%04x ", (unsigned int)r_address); |
| } |
| else if (cputype == MachO::CPU_TYPE_ARM && |
| sectdiff_r_type == llvm::MachO::ARM_RELOC_HALF_SECTDIFF) |
| outs() << format(" other_half = 0x%04x ", (unsigned int)r_address); |
| if ((cputype == MachO::CPU_TYPE_I386 && |
| (r_type == llvm::MachO::GENERIC_RELOC_SECTDIFF || |
| r_type == llvm::MachO::GENERIC_RELOC_LOCAL_SECTDIFF)) || |
| (cputype == MachO::CPU_TYPE_ARM && |
| (sectdiff_r_type == llvm::MachO::ARM_RELOC_SECTDIFF || |
| sectdiff_r_type == llvm::MachO::ARM_RELOC_LOCAL_SECTDIFF || |
| sectdiff_r_type == llvm::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 == llvm::MachO::ARM_RELOC_HALF || |
| r_type == llvm::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 == llvm::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 = NULL; |
| if (SymNameNext) |
| name = SymNameNext->data(); |
| if (name == NULL) |
| 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 == llvm::MachO::ARM_RELOC_PAIR) |
| outs() << format("other_half = 0x%04x\n", (unsigned int)r_address); |
| else if (cputype == MachO::CPU_TYPE_ARM64 && |
| r_type == llvm::MachO::ARM64_RELOC_ADDEND) |
| outs() << format("addend = 0x%06x\n", (unsigned int)r_symbolnum); |
| else { |
| outs() << format("%d ", r_symbolnum); |
| if (r_symbolnum == llvm::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) { |
| llvm::object::DataRefImpl DRI; |
| DRI.d.a = r_symbolnum-1; |
| StringRef SegName = O->getSectionFinalSegmentName(DRI); |
| StringRef SectName; |
| if (O->getSectionName(DRI, SectName)) |
| outs() << "(?,?)\n"; |
| else |
| outs() << "(" << SegName << "," << SectName << ")\n"; |
| } |
| else { |
| outs() << "(?,?)\n"; |
| } |
| } |
| } |
| } |
| if (cputype == MachO::CPU_TYPE_ARM && |
| (r_type == llvm::MachO::ARM_RELOC_HALF || |
| r_type == llvm::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); |
| StringRef SectName; |
| if (O->getSectionName(DRI, SectName)) |
| outs() << "Relocation information (" << SegName << ",?) " |
| << format("%u entries", Sec.nreloc); |
| else |
| outs() << "Relocation information (" << SegName << "," |
| << SectName << 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); |
| StringRef SectName; |
| if (O->getSectionName(DRI, SectName)) |
| outs() << "Relocation information (" << SegName << ",?) " |
| << format("%u entries", Sec.nreloc); |
| else |
| outs() << "Relocation information (" << SegName << "," |
| << SectName << 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 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 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) << ")\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"; |
| } |
| } |
| } |
| } |
| |
| typedef DenseMap<uint64_t, StringRef> SymbolAddressMap; |
| |
| static void CreateSymbolAddressMap(MachOObjectFile *O, |
| SymbolAddressMap *AddrMap) { |
| // Create a map of symbol addresses to symbol names. |
| for (const SymbolRef &Symbol : O->symbols()) { |
| Expected<SymbolRef::Type> STOrErr = Symbol.getType(); |
| if (!STOrErr) |
| report_error(O->getFileName(), STOrErr.takeError()); |
| SymbolRef::Type ST = *STOrErr; |
| if (ST == SymbolRef::ST_Function || ST == SymbolRef::ST_Data || |
| ST == SymbolRef::ST_Other) { |
| uint64_t Address = Symbol.getValue(); |
| Expected<StringRef> SymNameOrErr = Symbol.getName(); |
| if (!SymNameOrErr) |
| report_error(O->getFileName(), SymNameOrErr.takeError()); |
| StringRef SymName = *SymNameOrErr; |
| 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; |
| Expected<StringRef> SymName = RelocSym->getName(); |
| if (!SymName) |
| report_error(O->getFileName(), SymName.takeError()); |
| 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; |
| Sect->getName(SectName); |
| 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; |
| Sect->getContents(BytesStr); |
| 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 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); |
| for (uint32_t i = 0; i < sect_size; i += stride) { |
| const char *SymbolName = nullptr; |
| 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); |
| if (verbose) |
| SymbolName = GuessSymbolName(pointer_value, AddrMap); |
| } 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); |
| if (verbose) |
| SymbolName = GuessSymbolName(pointer_value, 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 HAVE_LIBXAR |
| static void DumpBitcodeSection(MachOObjectFile *O, const char *sect, |
| uint32_t size, bool verbose, |
| bool PrintXarHeader, bool PrintXarFileHeaders, |
| std::string XarMemberName); |
| #endif // defined(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.size()) { |
| DumpSegName = DumpSegSectName.first; |
| DumpSectName = DumpSegSectName.second; |
| } else { |
| DumpSegName = ""; |
| DumpSectName = DumpSegSectName.first; |
| } |
| for (const SectionRef &Section : O->sections()) { |
| StringRef SectName; |
| Section.getName(SectName); |
| 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; |
| Section.getContents(BytesStr); |
| const char *sect = reinterpret_cast<const char *>(BytesStr.data()); |
| uint32_t sect_size = BytesStr.size(); |
| uint64_t sect_addr = Section.getAddress(); |
| |
| 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 HAVE_LIBXAR |
| if (SegName == "__LLVM" && SectName == "__bundle") { |
| DumpBitcodeSection(O, sect, sect_size, verbose, !NoSymbolicOperands, |
| ArchiveHeaders, ""); |
| continue; |
| } |
| #endif // defined(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, !NoLeadingAddr); |
| break; |
| case MachO::S_4BYTE_LITERALS: |
| DumpLiteral4Section(O, sect, sect_size, sect_addr, !NoLeadingAddr); |
| break; |
| case MachO::S_8BYTE_LITERALS: |
| DumpLiteral8Section(O, sect, sect_size, sect_addr, !NoLeadingAddr); |
| break; |
| case MachO::S_16BYTE_LITERALS: |
| DumpLiteral16Section(O, sect, sect_size, sect_addr, !NoLeadingAddr); |
| break; |
| case MachO::S_LITERAL_POINTERS: |
| DumpLiteralPointerSection(O, Section, sect, sect_size, sect_addr, |
| !NoLeadingAddr); |
| break; |
| case MachO::S_MOD_INIT_FUNC_POINTERS: |
| case MachO::S_MOD_TERM_FUNC_POINTERS: |
| DumpInitTermPointerSection(O, 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; |
| Section.getName(SectName); |
| DataRefImpl Ref = Section.getRawDataRefImpl(); |
| StringRef SegName = O->getSectionFinalSegmentName(Ref); |
| if (SegName == "__TEXT" && SectName == "__info_plist") { |
| if (!NoLeadingHeaders) |
| outs() << "Contents of (" << SegName << "," << SectName << ") section\n"; |
| StringRef BytesStr; |
| Section.getContents(BytesStr); |
| 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 (none_of(ArchFlags, [&](const std::string &Name) { |
| return Name == ArchFlagName; |
| })) { |
| 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 || LinkOptHints || DylibsUsed || DylibId || ObjcMetaData || |
| (FilterSections.size() != 0)) { |
| if (!NoLeadingHeaders) { |
| 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.size() != 0 || |
| UnwindInfo) |
| if (Error Err = MachOOF->checkSymbolTable()) |
| report_error(ArchiveName, FileName, std::move(Err), ArchitectureName); |
| |
| 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, !NonVerbose); |
| if (DataInCode) |
| PrintDataInCodeTable(MachOOF, !NonVerbose); |
| if (LinkOptHints) |
| PrintLinkOptHints(MachOOF); |
| if (Relocations) |
| PrintRelocations(MachOOF, !NonVerbose); |
| if (SectionHeaders) |
| PrintSectionHeaders(MachOOF); |
| if (SectionContents) |
| PrintSectionContents(MachOOF); |
| if (FilterSections.size() != 0) |
| DumpSectionContents(FileName, MachOOF, !NonVerbose); |
| if (InfoPlist) |
| DumpInfoPlistSectionContents(FileName, 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, !NonVerbose); |
| if (ExportsTrie) |
| printExportsTrie(MachOOF); |
| if (Rebase) |
| printRebaseTable(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; |
| 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() % (1 << 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, |
| bool verbose, bool print_offset, |
| StringRef ArchitectureName = StringRef()) { |
| if (print_offset) |
| outs() << C.getChildOffset() << "\t"; |
| Expected<sys::fs::perms> ModeOrErr = C.getAccessMode(); |
| if (!ModeOrErr) |
| report_error(Filename, C, ModeOrErr.takeError(), ArchitectureName); |
| sys::fs::perms Mode = ModeOrErr.get(); |
| if (verbose) { |
| // FIXME: this first dash, "-", is for (Mode & S_IFMT) == S_IFREG. |
| // But there is nothing in sys::fs::perms for S_IFMT or S_IFREG. |
| outs() << "-"; |
| outs() << ((Mode & sys::fs::owner_read) ? "r" : "-"); |
| outs() << ((Mode & sys::fs::owner_write) ? "w" : "-"); |
| outs() << ((Mode & sys::fs::owner_exe) ? "x" : "-"); |
| outs() << ((Mode & sys::fs::group_read) ? "r" : "-"); |
| outs() << ((Mode & sys::fs::group_write) ? "w" : "-"); |
| outs() << ((Mode & sys::fs::group_exe) ? "x" : "-"); |
| outs() << ((Mode & sys::fs::others_read) ? "r" : "-"); |
| outs() << ((Mode & sys::fs::others_write) ? "w" : "-"); |
| outs() << ((Mode & sys::fs::others_exe) ? "x" : "-"); |
| } else { |
| outs() << format("0%o ", Mode); |
| } |
| |
| Expected<unsigned> UIDOrErr = C.getUID(); |
| if (!UIDOrErr) |
| report_error(Filename, C, UIDOrErr.takeError(), ArchitectureName); |
| unsigned UID = UIDOrErr.get(); |
| outs() << format("%3d/", UID); |
| Expected<unsigned> GIDOrErr = C.getGID(); |
| if (!GIDOrErr) |
| report_error(Filename, C, GIDOrErr.takeError(), ArchitectureName); |
| unsigned GID = GIDOrErr.get(); |
| outs() << format("%-3d ", GID); |
| Expected<uint64_t> Size = C.getRawSize(); |
| if (!Size) |
| report_error(Filename, C, Size.takeError(), ArchitectureName); |
| outs() << format("%5" PRId64, Size.get()) << " "; |
| |
| StringRef RawLastModified = C.getRawLastModified(); |
| if (verbose) { |
| unsigned Seconds; |
| if (RawLastModified.getAsInteger(10, Seconds)) |
| outs() << "(date: \"" << RawLastModified |
| << "\" contains non-decimal chars) "; |
| else { |
| // Since cime(3) returns a 26 character string of the form: |
| // "Sun Sep 16 01:03:52 1973\n\0" |
| // just print 24 characters. |
| time_t t = Seconds; |
| outs() << format("%.24s ", ctime(&t)); |
| } |
| } else { |
| outs() << RawLastModified << " "; |
| } |
| |
| if (verbose) { |
| Expected<StringRef> NameOrErr = C.getName(); |
| if (!NameOrErr) { |
| consumeError(NameOrErr.takeError()); |
| Expected<StringRef> NameOrErr = C.getRawName(); |
| if (!NameOrErr) |
| report_error(Filename, C, NameOrErr.takeError(), ArchitectureName); |
| StringRef RawName = NameOrErr.get(); |
| outs() << RawName << "\n"; |
| } else { |
| StringRef Name = NameOrErr.get(); |
| outs() << Name << "\n"; |
| } |
| } else { |
| Expected<StringRef> NameOrErr = C.getRawName(); |
| if (!NameOrErr) |
| report_error(Filename, C, NameOrErr.takeError(), ArchitectureName); |
| StringRef RawName = NameOrErr.get(); |
| outs() << RawName << "\n"; |
| } |
| } |
| |
| static void printArchiveHeaders(StringRef Filename, Archive *A, bool verbose, |
| bool print_offset, |
| StringRef ArchitectureName = StringRef()) { |
| Error Err = Error::success(); |
| ; |
| for (const auto &C : A->children(Err, false)) |
| printArchiveChild(Filename, C, verbose, print_offset, ArchitectureName); |
| |
| if (Err) |
| report_error(StringRef(), Filename, std::move(Err), ArchitectureName); |
| } |
| |
| // ParseInputMachO() parses the named Mach-O file in Filename and handles the |
| // -arch flags selecting just those slices as specified by them and also parses |
| // archive files. Then for each individual Mach-O file ProcessMachO() is |
| // called to process the file based on the command line options. |
| void llvm::ParseInputMachO(StringRef Filename) { |
| // Check for -arch all and verifiy the -arch flags are valid. |
| for (unsigned i = 0; i < ArchFlags.size(); ++i) { |
| if (ArchFlags[i] == "all") { |
| ArchAll = true; |
| } else { |
| if (!MachOObjectFile::isValidArch(ArchFlags[i])) { |
| errs() << "llvm-objdump: Unknown architecture named '" + ArchFlags[i] + |
| "'for the -arch option\n"; |
| return; |
| } |
| } |
| } |
| |
| // Attempt to open the binary. |
| Expected<OwningBinary<Binary>> BinaryOrErr = createBinary(Filename); |
| if (!BinaryOrErr) { |
| if (auto E = isNotObjectErrorInvalidFileType(BinaryOrErr.takeError())) |
| report_error(Filename, std::move(E)); |
| else |
| outs() << Filename << ": is not an object file\n"; |
| return; |
| } |
| Binary &Bin = *BinaryOrErr.get().getBinary(); |
| |
| if (Archive *A = dyn_cast<Archive>(&Bin)) { |
| outs() << "Archive : " << Filename << "\n"; |
| if (ArchiveHeaders) |
| printArchiveHeaders(Filename, A, !NonVerbose, ArchiveMemberOffsets); |
| |
| Error Err = Error::success(); |
| for (auto &C : A->children(Err)) { |
| Expected<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary(); |
| if (!ChildOrErr) { |
| if (auto E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError())) |
| report_error(Filename, C, std::move(E)); |
| continue; |
| } |
| if (MachOObjectFile *O = dyn_cast<MachOObjectFile>(&*ChildOrErr.get())) { |
| if (!checkMachOAndArchFlags(O, Filename)) |
| return; |
| ProcessMachO(Filename, O, O->getFileName()); |
| } |
| } |
| if (Err) |
| report_error(Filename, std::move(Err)); |
| return; |
| } |
| if (UniversalHeaders) { |
| if (MachOUniversalBinary *UB = dyn_cast<MachOUniversalBinary>(&Bin)) |
| printMachOUniversalHeaders(UB, !NonVerbose); |
| } |
| if (MachOUniversalBinary *UB = dyn_cast<MachOUniversalBinary>(&Bin)) { |
| // If we have a list of architecture flags specified dump only those. |
| if (!ArchAll && ArchFlags.size() != 0) { |
| // Look for a slice in the universal binary that matches each ArchFlag. |
| bool ArchFound; |
| for (unsigned i = 0; i < ArchFlags.size(); ++i) { |
| ArchFound = false; |
| for (MachOUniversalBinary::object_iterator I = UB->begin_objects(), |
| E = UB->end_objects(); |
| I != E; ++I) { |
| if (ArchFlags[i] == I->getArchFlagName()) { |
| ArchFound = true; |
| Expected<std::unique_ptr<ObjectFile>> ObjOrErr = |
| I->getAsObjectFile(); |
| std::string ArchitectureName = ""; |
| if (ArchFlags.size() > 1) |
| ArchitectureName = I->getArchFlagName(); |
| if (ObjOrErr) { |
| ObjectFile &O = *ObjOrErr.get(); |
| if (MachOObjectFile *MachOOF = dyn_cast<MachOObjectFile>(&O)) |
| ProcessMachO(Filename, MachOOF, "", ArchitectureName); |
| } else if (auto E = isNotObjectErrorInvalidFileType( |
| ObjOrErr.takeError())) { |
| report_error(Filename, StringRef(), std::move(E), |
| ArchitectureName); |
| continue; |
| } else if (Expected<std::unique_ptr<Archive>> AOrErr = |
| I->getAsArchive()) { |
| std::unique_ptr<Archive> &A = *AOrErr; |
| outs() << "Archive : " << Filename; |
| if (!ArchitectureName.empty()) |
| outs() << " (architecture " << ArchitectureName << ")"; |
| outs() << "\n"; |
| if (ArchiveHeaders) |
| printArchiveHeaders(Filename, A.get(), !NonVerbose, |
| ArchiveMemberOffsets, ArchitectureName); |
| Error Err = Error::success(); |
| for (auto &C : A->children(Err)) { |
| Expected<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary(); |
| if (!ChildOrErr) { |
| if (auto E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError())) |
| report_error(Filename, C, std::move(E), ArchitectureName); |
| continue; |
| } |
| if (MachOObjectFile *O = |
| dyn_cast<MachOObjectFile>(&*ChildOrErr.get())) |
| ProcessMachO(Filename, O, O->getFileName(), ArchitectureName); |
| } |
| if (Err) |
| report_error(Filename, std::move(Err)); |
| } else { |
| consumeError(AOrErr.takeError()); |
| error("Mach-O universal file: " + Filename + " for " + |
| "architecture " + StringRef(I->getArchFlagName()) + |
| " is not a Mach-O file or an archive file"); |
| } |
| } |
| } |
| if (!ArchFound) { |
| errs() << "llvm-objdump: file: " + Filename + " does not contain " |
| << "architecture: " + ArchFlags[i] + "\n"; |
| return; |
| } |
| } |
| return; |
| } |
| // No architecture flags were specified so if this contains a slice that |
| // matches the host architecture dump only that. |
| if (!ArchAll) { |
| for (MachOUniversalBinary::object_iterator I = UB->begin_objects(), |
| E = UB->end_objects(); |
| I != E; ++I) { |
| if (MachOObjectFile::getHostArch().getArchName() == |
| I->getArchFlagName()) { |
| Expected<std::unique_ptr<ObjectFile>> ObjOrErr = I->getAsObjectFile(); |
| std::string ArchiveName; |
| ArchiveName.clear(); |
| if (ObjOrErr) { |
| ObjectFile &O = *ObjOrErr.get(); |
| if (MachOObjectFile *MachOOF = dyn_cast<MachOObjectFile>(&O)) |
| ProcessMachO(Filename, MachOOF); |
| } else if (auto E = isNotObjectErrorInvalidFileType( |
| ObjOrErr.takeError())) { |
| report_error(Filename, std::move(E)); |
| continue; |
| } else if (Expected<std::unique_ptr<Archive>> AOrErr = |
| I->getAsArchive()) { |
| std::unique_ptr<Archive> &A = *AOrErr; |
| outs() << "Archive : " << Filename << "\n"; |
| if (ArchiveHeaders) |
| printArchiveHeaders(Filename, A.get(), !NonVerbose, |
| ArchiveMemberOffsets); |
| Error Err = Error::success(); |
| for (auto &C : A->children(Err)) { |
| Expected<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary(); |
| if (!ChildOrErr) { |
| if (auto E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError())) |
| report_error(Filename, C, std::move(E)); |
| continue; |
| } |
| if (MachOObjectFile *O = |
| dyn_cast<MachOObjectFile>(&*ChildOrErr.get())) |
| ProcessMachO(Filename, O, O->getFileName()); |
| } |
| if (Err) |
| report_error(Filename, std::move(Err)); |
| } else { |
| consumeError(AOrErr.takeError()); |
| error("Mach-O universal file: " + Filename + " for architecture " + |
| StringRef(I->getArchFlagName()) + |
| " is not a Mach-O file or an archive file"); |
| } |
| return; |
| } |
| } |
| } |
| // Either all architectures have been specified or none have been specified |
| // and this does not contain the host architecture so dump all the slices. |
| bool moreThanOneArch = UB->getNumberOfObjects() > 1; |
| for (MachOUniversalBinary::object_iterator I = UB->begin_objects(), |
| E = UB->end_objects(); |
| I != E; ++I) { |
| Expected<std::unique_ptr<ObjectFile>> ObjOrErr = I->getAsObjectFile(); |
| std::string ArchitectureName = ""; |
| if (moreThanOneArch) |
| ArchitectureName = I->getArchFlagName(); |
| if (ObjOrErr) { |
| ObjectFile &Obj = *ObjOrErr.get(); |
| if (MachOObjectFile *MachOOF = dyn_cast<MachOObjectFile>(&Obj)) |
| ProcessMachO(Filename, MachOOF, "", ArchitectureName); |
| } else if (auto E = isNotObjectErrorInvalidFileType( |
| ObjOrErr.takeError())) { |
| report_error(StringRef(), Filename, std::move(E), ArchitectureName); |
| continue; |
| } else if (Expected<std::unique_ptr<Archive>> AOrErr = |
| I->getAsArchive()) { |
| std::unique_ptr<Archive> &A = *AOrErr; |
| outs() << "Archive : " << Filename; |
| if (!ArchitectureName.empty()) |
| outs() << " (architecture " << ArchitectureName << ")"; |
| outs() << "\n"; |
| if (ArchiveHeaders) |
| printArchiveHeaders(Filename, A.get(), !NonVerbose, |
| ArchiveMemberOffsets, ArchitectureName); |
| Error Err = Error::success(); |
| for (auto &C : A->children(Err)) { |
| Expected<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary(); |
| if (!ChildOrErr) { |
| if (auto E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError())) |
| report_error(Filename, C, std::move(E), ArchitectureName); |
| continue; |
| } |
| if (MachOObjectFile *O = |
| dyn_cast<MachOObjectFile>(&*ChildOrErr.get())) { |
| if (MachOObjectFile *MachOOF = dyn_cast<MachOObjectFile>(O)) |
| ProcessMachO(Filename, MachOOF, MachOOF->getFileName(), |
| ArchitectureName); |
| } |
| } |
| if (Err) |
| report_error(Filename, std::move(Err)); |
| } else { |
| consumeError(AOrErr.takeError()); |
| error("Mach-O universal file: " + Filename + " for architecture " + |
| StringRef(I->getArchFlagName()) + |
| " is not a Mach-O file or an archive file"); |
| } |
| } |
| return; |
| } |
| if (ObjectFile *O = dyn_cast<ObjectFile>(&Bin)) { |
| if (!checkMachOAndArchFlags(O, Filename)) |
| return; |
| if (MachOObjectFile *MachOOF = dyn_cast<MachOObjectFile>(&*O)) { |
| ProcessMachO(Filename, MachOOF); |
| } else |
| errs() << "llvm-objdump: '" << Filename << "': " |
| << "Object is not a Mach-O file type.\n"; |
| return; |
| } |
| llvm_unreachable("Input object can't be invalid at this point"); |
| } |
| |
| // The block of info used by the Symbolizer call backs. |
| struct DisassembleInfo { |
| bool verbose; |
| MachOObjectFile *O; |
| SectionRef S; |
| SymbolAddressMap *AddrMap; |
| std::vector<SectionRef> *Sections; |
| const char *class_name; |
| const char *selector_name; |
| char *method; |
| char *demangled_name; |
| uint64_t adrp_addr; |
| uint32_t adrp_inst; |
| std::unique_ptr<SymbolAddressMap> bindtable; |
| uint32_t depth; |
| }; |
| |
| // SymbolizerGetOpInfo() is the operand information call back function. |
| // This is called to get the symbolic information for operand(s) of an |
| // instruction when it is being done. This routine does this from |
| // the relocation information, symbol table, etc. That block of information |
| // is a pointer to the struct DisassembleInfo that was passed when the |
| // disassembler context was created and passed to back to here when |
| // called back by the disassembler for instruction operands that could have |
| // relocation information. The address of the instruction containing operand is |
| // at the Pc parameter. The immediate value the operand has is passed in |
| // op_info->Value and is at Offset past the start of the instruction and has a |
| // byte Size of 1, 2 or 4. The symbolc information is returned in TagBuf is the |
| // LLVMOpInfo1 struct defined in the header "llvm-c/Disassembler.h" as symbol |
| // names and addends of the symbolic expression to add for the operand. The |
| // value of TagType is currently 1 (for the LLVMOpInfo1 struct). If symbolic |
| // information is returned then this function returns 1 else it returns 0. |
| static int SymbolizerGetOpInfo(void *DisInfo, uint64_t Pc, uint64_t Offset, |
| uint64_t Size, int TagType, void *TagBuf) { |
| struct DisassembleInfo *info = (struct DisassembleInfo *)DisInfo; |
| struct LLVMOpInfo1 *op_info = (struct LLVMOpInfo1 *)TagBuf; |
| uint64_t value = op_info->Value; |
| |
| // Make sure all fields returned are zero if we don't set them. |
| memset((void *)op_info, '\0', sizeof(struct LLVMOpInfo1)); |
| op_info->Value = value; |
| |
| // If the TagType is not the value 1 which it code knows about or if no |
| // verbose symbolic information is wanted then just return 0, indicating no |
| // information is being returned. |
| if (TagType != 1 || !info->verbose) |
| return 0; |
| |
| unsigned int Arch = info->O->getArch(); |
| if (Arch == Triple::x86) { |
| if (Size != 1 && Size != 2 && Size != 4 && Size != 0) |
| return 0; |
| if (info->O->getHeader().filetype != MachO::MH_OBJECT) { |
| // TODO: |
| // Search the external relocation entries of a fully linked image |
| // (if any) for an entry that matches this segment offset. |
| // uint32_t seg_offset = (Pc + Offset); |
| return 0; |
| } |
| // In MH_OBJECT filetypes search the section's relocation entries (if any) |
| // for an entry for this section offset. |
| uint32_t sect_addr = info->S.getAddress(); |
| uint32_t sect_offset = (Pc + Offset) - sect_addr; |
| bool reloc_found = false; |
| DataRefImpl Rel; |
| MachO::any_relocation_info RE; |
| bool isExtern = false; |
| SymbolRef Symbol; |
| bool r_scattered = false; |
| uint32_t r_value, pair_r_value, r_type; |
| for (const RelocationRef &Reloc : info->S.relocations()) { |
| uint64_t RelocOffset = Reloc.getOffset(); |
| if (RelocOffset == sect_offset) { |
| Rel = Reloc.getRawDataRefImpl(); |
| RE = info->O->getRelocation(Rel); |
| r_type = info->O->getAnyRelocationType(RE); |
| r_scattered = info->O->isRelocationScattered(RE); |
| if (r_scattered) { |
| r_value = info->O->getScatteredRelocationValue(RE); |
| if (r_type == MachO::GENERIC_RELOC_SECTDIFF || |
| r_type == MachO::GENERIC_RELOC_LOCAL_SECTDIFF) { |
| DataRefImpl RelNext = Rel; |
| info->O->moveRelocationNext(RelNext); |
| MachO::any_relocation_info RENext; |
| RENext = info->O->getRelocation(RelNext); |
| if (info->O->isRelocationScattered(RENext)) |
| pair_r_value = info->O->getScatteredRelocationValue(RENext); |
| else |
| return 0; |
| } |
| } else { |
| isExtern = info->O->getPlainRelocationExternal(RE); |
| if (isExtern) { |
| symbol_iterator RelocSym = Reloc.getSymbol(); |
| Symbol = *RelocSym; |
| } |
| } |
| reloc_found = true; |
| break; |
| } |
| } |
| if (reloc_found && isExtern) { |
| Expected<StringRef> SymName = Symbol.getName(); |
| if (!SymName) |
| report_error(info->O->getFileName(), SymName.takeError()); |
| const char *name = SymName->data(); |
| op_info->AddSymbol.Present = 1; |
| op_info->AddSymbol.Name = name; |
| // For i386 extern relocation entries the value in the instruction is |
| // the offset from the symbol, and value is already set in op_info->Value. |
| return 1; |
| } |
| if (reloc_found && (r_type == MachO::GENERIC_RELOC_SECTDIFF || |
| r_type == MachO::GENERIC_RELOC_LOCAL_SECTDIFF)) { |
| const char *add = GuessSymbolName(r_value, info->AddrMap); |
| const char *sub = GuessSymbolName(pair_r_value, info->AddrMap); |
| uint32_t offset = value - (r_value - pair_r_value); |
| op_info->AddSymbol.Present = 1; |
| if (add != nullptr) |
| op_info->AddSymbol.Name = add; |
| else |
| op_info->AddSymbol.Value = r_value; |
| op_info->SubtractSymbol.Present = 1; |
| if (sub != nullptr) |
| op_info->SubtractSymbol.Name = sub; |
| else |
| op_info->SubtractSymbol.Value = pair_r_value; |
| op_info->Value = offset; |
| return 1; |
| } |
| return 0; |
| } |
| if (Arch == Triple::x86_64) { |
| if (Size != 1 && Size != 2 && Size != 4 && Size != 0) |
| return 0; |
| // For non MH_OBJECT types, like MH_KEXT_BUNDLE, Search the external |
| // relocation entries of a linked image (if any) for an entry that matches |
| // this segment offset. |
| if (info->O->getHeader().filetype != MachO::MH_OBJECT) { |
| uint64_t seg_offset = Pc + Offset; |
| bool reloc_found = false; |
| DataRefImpl Rel; |
| MachO::any_relocation_info RE; |
| bool isExtern = false; |
| SymbolRef Symbol; |
| for (const RelocationRef &Reloc : info->O->external_relocations()) { |
| uint64_t RelocOffset = Reloc.getOffset(); |
| if (RelocOffset == seg_offset) { |
| Rel = Reloc.getRawDataRefImpl(); |
| RE = info->O->getRelocation(Rel); |
| // external relocation entries should always be external. |
| isExtern = info->O->getPlainRelocationExternal(RE); |
| if (isExtern) { |
| symbol_iterator RelocSym = Reloc.getSymbol(); |
| Symbol = *RelocSym; |
| } |
| reloc_found = true; |
| break; |
| } |
| } |
| if (reloc_found && isExtern) { |
| // The Value passed in will be adjusted by the Pc if the instruction |
| // adds the Pc. But for x86_64 external relocation entries the Value |
| // is the offset from the external symbol. |
| if (info->O->getAnyRelocationPCRel(RE)) |
| op_info->Value -= Pc + Offset + Size; |
| Expected<StringRef> SymName = Symbol.getName(); |
| if (!SymName) |
| report_error(info->O->getFileName(), SymName.takeError()); |
| const char *name = SymName->data(); |
| op_info->AddSymbol.Present = 1; |
| op_info->AddSymbol.Name = name; |
| return 1; |
| } |
| return 0; |
| } |
| // In MH_OBJECT filetypes search the section's relocation entries (if any) |
| // for an entry for this section offset. |
| uint64_t sect_addr = info->S.getAddress(); |
| uint64_t sect_offset = (Pc + Offset) - sect_addr; |
| bool reloc_found = false; |
| DataRefImpl Rel; |
| MachO::any_relocation_info RE; |
| bool isExtern = false; |
| SymbolRef Symbol; |
| for (const RelocationRef &Reloc : info->S.relocations()) { |
| uint64_t RelocOffset = Reloc.getOffset(); |
| if (RelocOffset == sect_offset) { |
| Rel = Reloc.getRawDataRefImpl(); |
| RE = info->O->getRelocation(Rel); |
| // NOTE: Scattered relocations don't exist on x86_64. |
| isExtern = info->O->getPlainRelocationExternal(RE); |
| if (isExtern) { |
| symbol_iterator RelocSym = Reloc.getSymbol(); |
| Symbol = *RelocSym; |
| } |
| reloc_found = true; |
| break; |
| } |
| } |
| if (reloc_found && isExtern) { |
| // The Value passed in will be adjusted by the Pc if the instruction |
| // adds the Pc. But for x86_64 external relocation entries the Value |
| // is the offset from the external symbol. |
| if (info->O->getAnyRelocationPCRel(RE)) |
| op_info->Value -= Pc + Offset + Size; |
| Expected<StringRef> SymName = Symbol.getName(); |
| if (!SymName) |
| report_error(info->O->getFileName(), SymName.takeError()); |
| const char *name = SymName->data(); |
| unsigned Type = info->O->getAnyRelocationType(RE); |
| if (Type == MachO::X86_64_RELOC_SUBTRACTOR) { |
| DataRefImpl RelNext = Rel; |
| info->O->moveRelocationNext(RelNext); |
| MachO::any_relocation_info RENext = info->O->getRelocation(RelNext); |
| unsigned TypeNext = info->O->getAnyRelocationType(RENext); |
| bool isExternNext = info->O->getPlainRelocationExternal(RENext); |
| unsigned SymbolNum = info->O->getPlainRelocationSymbolNum(RENext); |
| if (TypeNext == MachO::X86_64_RELOC_UNSIGNED && isExternNext) { |
| op_info->SubtractSymbol.Present = 1; |
| op_info->SubtractSymbol.Name = name; |
| symbol_iterator RelocSymNext = info->O->getSymbolByIndex(SymbolNum); |
| Symbol = *RelocSymNext; |
| Expected<StringRef> SymNameNext = Symbol.getName(); |
| if (!SymNameNext) |
| report_error(info->O->getFileName(), SymNameNext.takeError()); |
| name = SymNameNext->data(); |
| } |
| } |
| // TODO: add the VariantKinds to op_info->VariantKind for relocation types |
| // like: X86_64_RELOC_TLV, X86_64_RELOC_GOT_LOAD and X86_64_RELOC_GOT. |
| op_info->AddSymbol.Present = 1; |
| op_info->AddSymbol.Name = name; |
| return 1; |
| } |
| return 0; |
| } |
| if (Arch == Triple::arm) { |
| if (Offset != 0 || (Size != 4 && Size != 2)) |
| return 0; |
| if (info->O->getHeader().filetype != MachO::MH_OBJECT) { |
| // TODO: |
| // Search the external relocation entries of a fully linked image |
| // (if any) for an entry that matches this segment offset. |
| // uint32_t seg_offset = (Pc + Offset); |
| return 0; |
| } |
| // In MH_OBJECT filetypes search the section's relocation entries (if any) |
| // for an entry for this section offset. |
| uint32_t sect_addr = info->S.getAddress(); |
| uint32_t sect_offset = (Pc + Offset) - sect_addr; |
| DataRefImpl Rel; |
| MachO::any_relocation_info RE; |
| bool isExtern = false; |
| SymbolRef Symbol; |
| bool r_scattered = false; |
| uint32_t r_value, pair_r_value, r_type, r_length, other_half; |
| auto Reloc = |
| find_if(info->S.relocations(), [&](const RelocationRef &Reloc) { |
| uint64_t RelocOffset = Reloc.getOffset(); |
| return RelocOffset == sect_offset; |
| }); |
| |
| if (Reloc == info->S.relocations().end()) |
| return 0; |
| |
| Rel = Reloc->getRawDataRefImpl(); |
| RE = info->O->getRelocation(Rel); |
| r_length = info->O->getAnyRelocationLength(RE); |
| r_scattered = info->O->isRelocationScattered(RE); |
| if (r_scattered) { |
| r_value = info->O->getScatteredRelocationValue(RE); |
| r_type = info->O->getScatteredRelocationType(RE); |
| } else { |
| r_type = info->O->getAnyRelocationType(RE); |
| isExtern = info->O->getPlainRelocationExternal(RE); |
| if (isExtern) { |
| symbol_iterator RelocSym = Reloc->getSymbol(); |
| Symbol = *RelocSym; |
| } |
| } |
| if (r_type == MachO::ARM_RELOC_HALF || |
| r_type == MachO::ARM_RELOC_SECTDIFF || |
| r_type == MachO::ARM_RELOC_LOCAL_SECTDIFF || |
| r_type == MachO::ARM_RELOC_HALF_SECTDIFF) { |
| DataRefImpl RelNext = Rel; |
| info->O->moveRelocationNext(RelNext); |
| MachO::any_relocation_info RENext; |
| RENext = info->O->getRelocation(RelNext); |
| other_half = info->O->getAnyRelocationAddress(RENext) & 0xffff; |
| if (info->O->isRelocationScattered(RENext)) |
| pair_r_value = info->O->getScatteredRelocationValue(RENext); |
| } |
| |
| if (isExtern) { |
| Expected<StringRef> SymName = Symbol.getName(); |
| if (!SymName) |
| report_error(info->O->getFileName(), SymName.takeError()); |
| const char *name = SymName->data(); |
| op_info->AddSymbol.Present = 1; |
| op_info->AddSymbol.Name = name; |
| switch (r_type) { |
| case MachO::ARM_RELOC_HALF: |
| if ((r_length & 0x1) == 1) { |
| op_info |