/// Mach-O format. /// /// Sources: /// - Mac OS X ABI Mach-O File Format Reference /// - https://github.com/opensource-apple/cctools/blob/master/include/mach/machine.h /// - https://github.com/opensource-apple/cctools/blob/master/include/mach-o/loader.h /// /// Authors: dd86k <dd@dax.moe> /// Copyright: © dd86k <dd@dax.moe> /// License: BSD-3-Clause-Clear module adbg.object.format.macho; // NOTE: Layout // Header // Load Commands (one or more) // Segment (zero or one) (for LC_SEGMENT) // Section (zero or more) // // Debug info in LC_DYSYMTAB import adbg.error; import adbg.object.server; import adbg.machines : AdbgMachine; import adbg.utils.bit; import core.stdc.stdlib; /// Smallest Mach-O size. // https://codegolf.stackexchange.com/a/154685 private enum MINIMUM_SIZE = 0x1000; // Due to paging private enum LIMIT_FAT_ARCH = 200; private enum LIMIT_COMMANDS = 2000; enum MACHO_MAGIC = 0xFEEDFACEu; /// Mach-O BE 32-bit magic enum MACHO_MAGIC64 = 0xFEEDFACFu; /// Mach-O BE 64-bit magic enum MACHO_CIGAM = 0xCEFAEDFEu; /// Mach-O LE 32-bit magic enum MACHO_CIGAM64 = 0xCFFAEDFEu; /// Mach-O LE 64-bit magic enum MACHO_FATMAGIC = 0xCAFEBABEu; /// Mach-O FAT BE magic enum MACHO_FATCIGAM = 0xBEBAFECAu; /// Mach-O FAT LE magic alias cpu_type_t = int; enum { MACHO_CPUTYPE_ANY = -1, /// Used as a bitmask to mark cputype as 64-bit MACHO_CPUTYPE_ABI64 = 0x100_0000, MACHO_CPUTYPE_VAX = 1, MACHO_CPUTYPE_ROMP = 2, MACHO_CPUTYPE_NS32032 = 4, MACHO_CPUTYPE_NS32332 = 5, MACHO_CPUTYPE_MC680x0 = 6, MACHO_CPUTYPE_I386 = 7, MACHO_CPUTYPE_X86_64 = MACHO_CPUTYPE_I386 | MACHO_CPUTYPE_ABI64, MACHO_CPUTYPE_MIPS = 8, MACHO_CPUTYPE_NS32532 = 9, MACHO_CPUTYPE_HPPA = 11, MACHO_CPUTYPE_ARM = 12, MACHO_CPUTYPE_ARM64 = MACHO_CPUTYPE_ARM | MACHO_CPUTYPE_ABI64, MACHO_CPUTYPE_MC88000 = 13, MACHO_CPUTYPE_SPARC = 14, MACHO_CPUTYPE_I860 = 15, // big-endian MACHO_CPUTYPE_I860_LITTLE = 16, // little-endian MACHO_CPUTYPE_RS6000 = 17, MACHO_CPUTYPE_MC98000 = 18, MACHO_CPUTYPE_POWERPC = 18, MACHO_CPUTYPE_POWERPC64 = MACHO_CPUTYPE_POWERPC | MACHO_CPUTYPE_ABI64, MACHO_CPUTYPE_VEO = 255 } // ============================= // cpu_subtype_t - CPU Subtypes, int // ============================= // VAX subtypes enum { // SUBTYPE_VAX MACHO_SUBTYPE_VAX_ALL = 0, MACHO_SUBTYPE_VAX780 = 1, MACHO_SUBTYPE_VAX785 = 2, MACHO_SUBTYPE_VAX750 = 3, MACHO_SUBTYPE_VAX730 = 4, MACHO_SUBTYPE_UVAXI = 5, MACHO_SUBTYPE_UVAXII = 6, MACHO_SUBTYPE_VAX8200 = 7, MACHO_SUBTYPE_VAX8500 = 8, MACHO_SUBTYPE_VAX8600 = 9, MACHO_SUBTYPE_VAX8650 = 10, MACHO_SUBTYPE_VAX8800 = 11, MACHO_SUBTYPE_UVAXIII = 12 } // ROMP subtypes enum { // SUBTYPE_ROMP MACHO_SUBTYPE_RT_ALL = 0, MACHO_SUBTYPE_RT_PC = 1, MACHO_SUBTYPE_RT_APC = 2, MACHO_SUBTYPE_RT_135 = 3 } // 32032/32332/32532 subtypes enum { // SUBTYPE_32032 MACHO_SUBTYPE_MMAX_ALL = 0, MACHO_SUBTYPE_MMAX_DPC = 1, /* 032 CPU */ MACHO_SUBTYPE_SQT = 2, MACHO_SUBTYPE_MMAX_APC_FPU = 3, /* 32081 FPU */ MACHO_SUBTYPE_MMAX_APC_FPA = 4, /* Weitek FPA */ MACHO_SUBTYPE_MMAX_XPC = 5, /* 532 CPU */ } private template SUBTYPE_INTEL(short f, short m) { enum SUBTYPE_INTEL = f + (m << 4); } // x86 subtypes enum { // SUBTYPE_I386 MACHO_SUBTYPE_I386_ALL = 3, MACHO_SUBTYPE_X86_64_ALL = MACHO_SUBTYPE_I386_ALL, MACHO_SUBTYPE_i386 = 3, MACHO_SUBTYPE_i486 = 4, MACHO_SUBTYPE_i486SX = 4 + 128, // "4 + 128" MACHO_SUBTYPE_i586 = 5, MACHO_SUBTYPE_PENT = SUBTYPE_INTEL!(5, 0), MACHO_SUBTYPE_PENPRO = SUBTYPE_INTEL!(6, 1), MACHO_SUBTYPE_PENTII_M3 = SUBTYPE_INTEL!(6, 3), MACHO_SUBTYPE_PENTII_M5 = SUBTYPE_INTEL!(6, 5), MACHO_SUBTYPE_PENTIUM_4 = SUBTYPE_INTEL!(10, 0), } // MIPS subty enum { // SUBTYPE_MIPS MACHO_SUBTYPE_MIPS_ALL = 0, MACHO_SUBTYPE_R2300 = 1, MACHO_SUBTYPE_R2600 = 2, MACHO_SUBTYPE_R2800 = 3, MACHO_SUBTYPE_R2800a = 4 } // 680x0 subtypes (m68k) enum { // SUBTYPE_680x0 MACHO_SUBTYPE_MC680x0_ALL = 1, MACHO_SUBTYPE_MC68030 = 1, MACHO_SUBTYPE_MC68040 = 2, MACHO_SUBTYPE_MC68030_ONLY = 3, } // HPPA subtypes enum { // SUBTYPE_HPPA MACHO_SUBTYPE_HPPA7100 = 0, MACHO_SUBTYPE_HPPA7100LC = 1, MACHO_SUBTYPE_HPPA_ALL = 0, } // Acorn subtypes enum { // SUBTYPE_ARM MACHO_SUBTYPE_ACORN_ALL = 0, MACHO_SUBTYPE_A500_ARCH = 1, MACHO_SUBTYPE_A500 = 2, MACHO_SUBTYPE_A440 = 3, MACHO_SUBTYPE_M4 = 4, MACHO_SUBTYPE_V4T = 5, MACHO_SUBTYPE_V6 = 6, MACHO_SUBTYPE_V5TEJ = 7, MACHO_SUBTYPE_XSCALE = 8, MACHO_SUBTYPE_V7 = 9, MACHO_SUBTYPE_V8 = 13, } // MC88000 subtypes enum { // SUBTYPE_MC88000 MACHO_SUBTYPE_MC88000_ALL = 0, MACHO_SUBTYPE_MMAX_JPC = 1, MACHO_SUBTYPE_MC88100 = 1, MACHO_SUBTYPE_MC88110 = 2, } // MC98000 (PowerPC) subtypes enum { // SUBTYPE_MC98000 MACHO_SUBTYPE_MC98000_ALL = 0, MACHO_SUBTYPE_MC98601 = 1, } // I860 subtypes enum { // SUBTYPE_I860 MACHO_SUBTYPE_I860_ALL = 0, MACHO_SUBTYPE_I860 = 1, } // I860_LITTLE subtypes enum { // SUBTYPE_I860_LITTLE MACHO_SUBTYPE_I860_LITTLE_ALL = 0, MACHO_SUBTYPE_I860_LITTLE = 1 } // RS6000 subtypes enum { // SUBTYPE_RS6000 MACHO_SUBTYPE_RS6000_ALL = 0, MACHO_SUBTYPE_RS6000 = 1, } // Sun4 subtypes (port done at CMU (?)) enum { // SUBTYPE_Sun4 MACHO_SUBTYPE_SUN4_ALL = 0, MACHO_SUBTYPE_SUN4_260 = 1, MACHO_SUBTYPE_SUN4_110 = 2, } // SPARC subtypes /*enum { // SUBTYPE_SPARC ALL = 0 };*/ // PowerPC subtypes enum { // SUBTYPE_PowerPC MACHO_SUBTYPE_POWERPC_ALL = 0, MACHO_SUBTYPE_POWERPC_601 = 1, MACHO_SUBTYPE_POWERPC_602 = 2, MACHO_SUBTYPE_POWERPC_603 = 3, MACHO_SUBTYPE_POWERPC_603e = 4, MACHO_SUBTYPE_POWERPC_603ev = 5, MACHO_SUBTYPE_POWERPC_604 = 6, MACHO_SUBTYPE_POWERPC_604e = 7, MACHO_SUBTYPE_POWERPC_620 = 8, MACHO_SUBTYPE_POWERPC_750 = 9, MACHO_SUBTYPE_POWERPC_7400 = 10, MACHO_SUBTYPE_POWERPC_7450 = 11, MACHO_SUBTYPE_POWERPC_970 = 100, } // VEO subtypes enum { // SUBTYPE_VEO MACHO_SUBTYPE_VEO_1 = 1, MACHO_SUBTYPE_VEO_2 = 2, MACHO_SUBTYPE_VEO_3 = 3, MACHO_SUBTYPE_VEO_4 = 4, //VEO_ALL = VEO_2, } // ======================== /// File types // ======================== alias macho_filetype_t = int; enum { MACHO_FILETYPE_UNKNOWN = 0, MACHO_FILETYPE_OBJECT = 0x1, MACHO_FILETYPE_EXECUTE = 0x2, MACHO_FILETYPE_FVMLIB = 0x3, MACHO_FILETYPE_CORE = 0x4, MACHO_FILETYPE_PRELOAD = 0x5, MACHO_FILETYPE_DYLIB = 0x6, MACHO_FILETYPE_DYLINKER = 0x7, MACHO_FILETYPE_BUNDLE = 0x8, MACHO_FILETYPE_DYLIB_STUB = 0x9, MACHO_FILETYPE_DSYM = 0xA, MACHO_FILETYPE_KEXT_BUNDLE = 0xB, } // Flags alias macho_flag_t = int; enum { MACHO_FLAG_NOUNDEFS = 0x00000001, MACHO_FLAG_INCRLINK = 0x00000002, MACHO_FLAG_DYLDLINK = 0x00000004, MACHO_FLAG_BINDATLOAD = 0x00000008, MACHO_FLAG_PREBOUND = 0x00000010, MACHO_FLAG_SPLIT_SEGS = 0x00000020, MACHO_FLAG_LAZY_INIT = 0x00000040, MACHO_FLAG_TWOLEVEL = 0x00000080, MACHO_FLAG_FORCE_FLAT = 0x00000100, MACHO_FLAG_NOMULTIDEFS = 0x00000200, MACHO_FLAG_NOFIXPREBINDING = 0x00000400, MACHO_FLAG_PREBINDABLE = 0x00000800, MACHO_FLAG_ALLMODSBOUND = 0x00001000, MACHO_FLAG_SUBSECTIONS_VIA_SYMBOLS = 0x00002000, MACHO_FLAG_CANONICAL = 0x00004000, MACHO_FLAG_WEAK_DEFINES = 0x00008000, MACHO_FLAG_BINDS_TO_WEAK = 0x00010000, MACHO_FLAG_ALLOW_STACK_EXECUTION = 0x00020000, MACHO_FLAG_ROOT_SAFE = 0x00040000, MACHO_FLAG_SETUID_SAFE = 0x00080000, MACHO_FLAG_NO_REEXPORTED_DYLIBS = 0x00100000, MACHO_FLAG_PIE = 0x00200000, MACHO_FLAG_DEAD_STRIPPABLE_DYLIB = 0x00400000, MACHO_FLAG_HAS_TLV_DESCRIPTORS = 0x00800000, MACHO_FLAG_NO_HEAP_EXECUTION = 0x01000000, MACHO_FLAG_APP_EXTENSION_SAFE = 0x02000000 } // Commands enum { MACHO_LC_REQ_DYLD = 0x80000000, /// Requires dynamic linker MACHO_LC_SEGMENT = 0x1, /// Segment of this file to be mapped MACHO_LC_SYMTAB = 0x2, /// Link-edit stab symbol table info MACHO_LC_SYMSEG = 0x3, /// Link-edit gdb symbol table info (obsolete) MACHO_LC_THREAD = 0x4, /// Thread MACHO_LC_UNIXTHREAD = 0x5, /// Unix thread (includes a stack) MACHO_LC_LOADFVMLIB = 0x6, /// Load a specified fixed VM shared library MACHO_LC_IDFVMLIB = 0x7, /// Fixed VM shared library identification MACHO_LC_IDENT = 0x8, /// Object identification info (obsolete) MACHO_LC_FVMFILE = 0x9, /// Fixed VM file inclusion (internal use) MACHO_LC_PREPAGE = 0xa, /// Prepage command (internal use) MACHO_LC_DYSYMTAB = 0xb, /// Dynamic link-edit symbol table info MACHO_LC_LOAD_DYLIB = 0xc, /// Load a dynamically linked shared library MACHO_LC_ID_DYLIB = 0xd, /// Dynamically linked shared lib ident MACHO_LC_LOAD_DYLINKER = 0xe, /// Load a dynamic linker MACHO_LC_ID_DYLINKER = 0xf, /// Dynamic linker identification MACHO_LC_PREBOUND_DYLIB = 0x10, /// Modules prebound for a dynamically linked shared library MACHO_LC_ROUTINES = 0x11, /// Image routines MACHO_LC_SUB_FRAMEWORK = 0x12, /// Sub framework MACHO_LC_SUB_UMBRELLA = 0x13, /// Sub umbrella MACHO_LC_SUB_CLIENT = 0x14, /// Sub client MACHO_LC_SUB_LIBRARY = 0x15, /// Sub library MACHO_LC_TWOLEVEL_HINTS = 0x16, /// Two-level namespace lookup hints MACHO_LC_PREBIND_CKSUM = 0x17, /// Prebind checksum MACHO_LC_SEGMENT_64 = 0x19, /// 64-bit segment of this file to be mapped MACHO_LC_ROUTINES_64 = 0x1a, /// 64-bit image routines MACHO_LC_UUID = 0x1b, /// The uuid MACHO_LC_RPATH = 0x1c | MACHO_LC_REQ_DYLD, /// Runpath additions MACHO_LC_CODE_SIGNATURE = 0x1d, /// Local of code signature MACHO_LC_SEGMENT_SPLIT_INFO = 0x1e, /// Local of info to split segments MACHO_LC_REEXPORT_DYLIB = 0x1f | MACHO_LC_REQ_DYLD, /// Load and re-export dylib MACHO_LC_LAZY_LOAD_DYLIB = 0x20, /// Delay load of dylib until first use MACHO_LC_ENCRYPTION_INFO = 0x21, /// Encrypted segment information MACHO_LC_DYLD_INFO = 0x22, /// Compressed dyld information MACHO_LC_DYLD_INFO_ONLY = 0x22 | MACHO_LC_REQ_DYLD, /// Compressed dyld information only MACHO_LC_LOAD_UPWARD_DYLIB = 0x23 | MACHO_LC_REQ_DYLD, /// Load upward dylib MACHO_LC_VERSION_MIN_MACOSX = 0x24, /// Build for MacOSX min OS version MACHO_LC_VERSION_MIN_IPHONEOS = 0x25, /// Build for iPhoneOS min OS version MACHO_LC_FUNCTION_STARTS = 0x26, /// Compressed table of function start addresses MACHO_LC_DYLD_ENVIRONMENT = 0x27, /// String for dyld to treat like environment variable MACHO_LC_MAIN = 0x28 | MACHO_LC_REQ_DYLD, /// Replacement for LC_UNIXTHREAD MACHO_LC_DATA_IN_CODE = 0x29, /// Table of non-instructions in __text MACHO_LC_SOURCE_VERSION = 0x2a, /// Source version used to build binary MACHO_LC_DYLIB_CODE_SIGN_DRS = 0x2b, /// Code signing DRs copied from linked dylibs MACHO_LC_ENCRYPTION_INFO_64 = 0x2c, /// 64-bit encrypted segment information MACHO_LC_LINKER_OPTION = 0x2D, /// linker options in MH_OBJECT files MACHO_LC_LINKER_OPTIMIZATION_HINT = 0x2e, /// Optimization hints in MH_OBJECT files MACHO_LC_VERSION_MIN_WATCHOS = 0x30, /// Build for Watch min OS version } // 64-bit version just adds a 32-bit reserved field at the end. struct macho_header_t { uint magic; /// Mach magic number identifier uint cputype; /// Cpu specifier uint subtype; /// Machine specifier uint filetype; /// Type of file uint ncmds; /// Number of load commands uint sizeofcmds; /// The size of all the load commands uint flags; /// Flags // NOTE: 64-bit header has an extra uint field, but it's reserved } struct macho_fat_header_t { uint magic; /// Magic uint nfat_arch; /// Number of architectures (structs) in binary } struct macho_fat_arch_entry_t { uint cputype; /// uint subtype; /// uint offset; /// File offset to first segment or command? uint size; /// Segments size? uint alignment; /// Page alignment? } struct macho_load_command_t { uint cmd; /// type of load command uint cmdsize; /// total size of command in bytes } // NOTE: Mach-O structure // A load command can lead to a segment command. // A segment command can contain multiple sections. alias int vm_prot_t; struct macho_segment_command_t { /* for 32-bit architectures */ uint cmd; /// LC_SEGMENT uint cmdsize; /// includes sizeof section structs char[16] segname; /// segment name uint vmaddr; /// memory address of this segment uint vmsize; /// memory size of this segment uint fileoff; /// file offset of this segment uint filesize; /// amount to map from the file vm_prot_t maxprot; /// maximum VM protection vm_prot_t initprot; /// initial VM protection uint nsects; /// number of sections in segment uint flags; /// flags } struct macho_segment_command_64_t { /* for 64-bit architectures */ uint cmd; /// LC_SEGMENT_64 uint cmdsize; /// includes sizeof section_64 structs char[16] segname; /// segment name ulong vmaddr; /// memory address of this segment ulong vmsize; /// memory size of this segment ulong fileoff; /// file offset of this segment ulong filesize; /// amount to map from the file vm_prot_t maxprot; /// maximum VM protection vm_prot_t initprot; /// initial VM protection uint nsects; /// number of sections in segment uint flags; /// flags } enum MACHO_SECTION_TYPE = 0x000000ff; /// Section flags type mask enum MACHO_SECTION_ATTR = 0xffffff00; /// Section flags attributes mask // Constants for the section attributes part of the flags field of a section // structure. /// User setable attributes enum MACHO_SECTION_ATTRIBUTES_USR = 0xff000000; /// section contains only true machine instructions enum MACHO_S_ATTR_PURE_INSTRUCTIONS = 0x80000000; /// section contains coalesced symbols that are not to be in a ranlib table of contents enum MACHO_S_ATTR_NO_TOC = 0x40000000; /// ok to strip static symbols in this section in files with the MH_DYLDLINK flag enum MACHO_S_ATTR_STRIP_STATIC_SYMS = 0x20000000; /// no dead stripping enum MACHO_S_ATTR_NO_DEAD_STRIP = 0x10000000; /// blocks are live if they reference live blocks enum MACHO_S_ATTR_LIVE_SUPPORT = 0x08000000; /// Used with i386 code stubs written on by dyld enum MACHO_S_ATTR_SELF_MODIFYING_CODE = 0x04000000; // Constants for the type of a section /// regular section enum MACHO_S_REGULAR = 0x0; /// zero fill on demand section enum MACHO_S_ZEROFILL = 0x1; /// section with only literal C strings enum MACHO_S_CSTRING_LITERALS = 0x2; /// section with only 4 byte literals enum MACHO_S_4BYTE_LITERALS = 0x3; /// section with only 8 byte literals enum MACHO_S_8BYTE_LITERALS = 0x4; /// section with only pointers to literals enum MACHO_S_LITERAL_POINTERS = 0x5; /// section with only non-lazy symbol pointers enum MACHO_S_NON_LAZY_SYMBOL_POINTERS = 0x6; /// section with only lazy symbol pointers enum MACHO_S_LAZY_SYMBOL_POINTERS = 0x7; /// section with only symbol stubs, byte size of stub in the reserved2 field enum MACHO_S_SYMBOL_STUBS = 0x8; /// section with only function pointers for initialization enum MACHO_S_MOD_INIT_FUNC_POINTERS = 0x9; /// section with only function pointers for termination enum MACHO_S_MOD_TERM_FUNC_POINTERS = 0xa; /// section contains symbols that are to be coalesced enum MACHO_S_COALESCED = 0xb; /// zero fill on demand section (that can be larger than 4 gigabytes enum MACHO_S_GB_ZEROFILL = 0xc; /// section with only pairs of function pointers for interposing enum MACHO_S_INTERPOSING = 0xd; /// section with only 16 byte literals enum MACHO_S_16BYTE_LITERALS = 0xe; /// section contains DTrace Object Format enum MACHO_S_DTRACE_DOF = 0xf; /// section with only lazy symbol pointers to lazy loaded dylibs enum MACHO_S_LAZY_DYLIB_SYMBOL_POINTERS = 0x10; /// TLS: template of initial values for TLVs enum MACHO_S_THREAD_LOCAL_REGULAR = 0x11; /// TLS: template of initial values for TLVs enum MACHO_S_THREAD_LOCAL_ZEROFILL = 0x12; /// TLS: TLV descriptors enum MACHO_S_THREAD_LOCAL_VARIABLES = 0x13; /// TLS: pointers to TLV descriptors enum MACHO_S_THREAD_LOCAL_VARIABLE_POINTERS = 0x14; /// TLS: functions to call to initialize TLV values enum MACHO_S_THREAD_LOCAL_INIT_FUNCTION_POINTERS = 0x15; struct macho_section_t { char[16] sectname; /// Section name char[16] segname; /// Name of parent segment uint addr; /// Memory address uint size; /// Size of section uint offset; /// File offset to this section uint align_; /// Section alignment, power of 2 uint reloff; /// File offset to relocation entries uint nrelocs; /// Number of relocation entries uint flags; /// Flags uint reserved1; /// uint reserved2; /// } struct macho_section64_t { char[16] sectname; /// Section name char[16] segname; /// Name of parent segment ulong addr; /// Memory address ulong size; /// Size of section uint offset; /// File offset to this section uint align_; /// Section alignment, power of 2 uint reloff; /// File offset to relocation entries uint nrelocs; /// Number of relocation entries uint flags; /// Flags uint reserved1; /// uint reserved2; /// uint reserved3; /// } private struct internal_macho_t { union { macho_header_t header; macho_fat_header_t fat_header; } union { macho_load_command_t *commands; macho_fat_arch_entry_t *fat_entries; } union { bool *r_commands; bool *r_fat_entries; } bool *r_sections; } private enum { MACHO_IS_64 = 1 << 16, MACHO_IS_FAT = 1 << 17, } int adbg_object_macho_load(adbg_object_t *o, uint magic) { o.internal = calloc(1, internal_macho_t.sizeof); if (o.internal == null) return adbg_oops(AdbgError.crt); o.format = AdbgObject.macho; // Bit messy but can be made better later size_t size = void; switch (magic) { case MACHO_MAGIC: // 32-bit LE size = macho_header_t.sizeof; break; case MACHO_MAGIC64: // 64-bit LE size = macho_header_t.sizeof; o.status |= MACHO_IS_64; break; case MACHO_CIGAM: // 32-bit BE size = macho_header_t.sizeof; o.status |= AdbgObjectInternalFlags.reversed; break; case MACHO_CIGAM64: // 64-bit BE size = macho_header_t.sizeof; o.status |= AdbgObjectInternalFlags.reversed | MACHO_IS_64; break; case MACHO_FATMAGIC: // Fat LE size = macho_fat_header_t.sizeof; o.status |= MACHO_IS_FAT; break; case MACHO_FATCIGAM: // Fat BE size = macho_fat_header_t.sizeof; o.status |= AdbgObjectInternalFlags.reversed | MACHO_IS_FAT; break; default: // Unless loader gave a new signature? return adbg_oops(AdbgError.objectMalformed); } if (adbg_object_read_at(o, 0, o.internal, size)) return adbg_errno(); version (Trace) trace("status=%#x", o.status); // If fields need to be swapped with (cast(internal_macho_t*)o.internal) if (o.status & (AdbgObjectInternalFlags.reversed | MACHO_IS_FAT)) { fat_header.nfat_arch = adbg_bswap32(fat_header.nfat_arch); } else if (o.status & AdbgObjectInternalFlags.reversed) { header.cputype = adbg_bswap32(header.cputype); header.subtype = adbg_bswap32(header.subtype); header.filetype = adbg_bswap32(header.filetype); header.ncmds = adbg_bswap32(header.ncmds); header.sizeofcmds = adbg_bswap32(header.sizeofcmds); header.flags = adbg_bswap32(header.flags); } return 0; } void adbg_object_macho_unload(adbg_object_t *o) { if (o == null) return; if (o.internal == null) return; internal_macho_t *internal = cast(internal_macho_t*)o.internal; if (internal.commands) free(internal.commands); // and fat_entries if (internal.r_commands) free(internal.r_commands); // and r_fat_entries if (internal.r_sections) free(internal.r_sections); free(o.internal); } int adbg_object_macho_is_64bit(adbg_object_t *o) { return o.status & MACHO_IS_64; } // // Fat Mach-O util functions // int adbg_object_macho_is_fat(adbg_object_t *o) { return o.status & MACHO_IS_FAT; } macho_fat_header_t* adbg_object_macho_fat_header(adbg_object_t *o) { if (o == null) { adbg_oops(AdbgError.invalidArgument); return null; } if (o.internal == null) { adbg_oops(AdbgError.uninitiated); return null; } return &(cast(internal_macho_t*)o.internal).fat_header; } macho_fat_arch_entry_t* adbg_object_macho_fat_arch(adbg_object_t *o, size_t index) { if (o == null) { adbg_oops(AdbgError.invalidArgument); return null; } if (o.internal == null) { adbg_oops(AdbgError.uninitiated); return null; } if ((o.status & MACHO_IS_FAT) == 0) { adbg_oops(AdbgError.unavailable); return null; } internal_macho_t *internal = cast(internal_macho_t*)o.internal; if (index >= internal.fat_header.nfat_arch) { adbg_oops(AdbgError.indexBounds); return null; } if (internal.fat_entries == null) with (internal) { size_t size = fat_header.nfat_arch * macho_fat_arch_entry_t.sizeof; fat_entries = cast(macho_fat_arch_entry_t*)malloc(size); if (fat_entries == null) { adbg_oops(AdbgError.crt); return null; } if (adbg_object_read_at(o, macho_fat_header_t.sizeof, fat_entries, size)) return null; if (o.status & AdbgObjectInternalFlags.reversed) { r_fat_entries = cast(bool*)malloc(fat_header.nfat_arch); if (r_fat_entries == null) { adbg_oops(AdbgError.crt); free(fat_entries); fat_entries = null; return null; } } } macho_fat_arch_entry_t* entry = internal.fat_entries + index; if (o.status & AdbgObjectInternalFlags.reversed && internal.r_fat_entries[index] == false) { entry.cputype = adbg_bswap32(entry.cputype); entry.subtype = adbg_bswap32(entry.subtype); entry.offset = adbg_bswap32(entry.offset); entry.size = adbg_bswap32(entry.size); entry.alignment = adbg_bswap32(entry.alignment); internal.r_fat_entries[index] = true; } return entry; } // // Regular Mach-O functions // macho_header_t* adbg_object_macho_header(adbg_object_t *o) { if (o == null) { adbg_oops(AdbgError.invalidArgument); return null; } if (o.internal == null) { adbg_oops(AdbgError.uninitiated); return null; } return &(cast(internal_macho_t*)o.internal).header; } // Load commands have a type and size. // Size includes type (4 bytes), size (4 bytes), and anything that follows it // until next command. macho_load_command_t* adbg_object_macho_load_command(adbg_object_t *o, size_t index) { if (o == null) { adbg_oops(AdbgError.invalidArgument); return null; } if (o.internal == null) { adbg_oops(AdbgError.uninitiated); return null; } if (o.status & MACHO_IS_FAT) { adbg_oops(AdbgError.unavailable); return null; } internal_macho_t *internal = cast(internal_macho_t*)o.internal; if (index >= internal.header.ncmds) { adbg_oops(AdbgError.indexBounds); return null; } // NOTE: Commands // Commands are not organized in a typical table (with offsets), // but each command occupies a tiny header, then data follows. // Thankfully, sizeofcmds includes the entire set of commands. if (internal.commands == null) with (internal) { commands = cast(macho_load_command_t*)malloc(header.sizeofcmds); if (commands == null) { adbg_oops(AdbgError.crt); return null; } size_t cmdoff = o.status & MACHO_IS_64 ? macho_header_t.sizeof + 4 : macho_header_t.sizeof; if (adbg_object_read_at(o, cmdoff, commands, header.sizeofcmds)) return null; if (o.status & AdbgObjectInternalFlags.reversed) { r_commands = cast(bool*)malloc(header.ncmds); if (r_commands == null) { adbg_oops(AdbgError.crt); free(commands); commands = null; return null; } } } // First load command macho_load_command_t *command = internal.commands; for (size_t i; i < index; ++i) { // up until we reach index we want if (o.status & AdbgObjectInternalFlags.reversed && internal.r_commands[i] == false) { command.cmd = adbg_bswap32(command.cmd); command.cmdsize = adbg_bswap32(command.cmdsize); internal.r_commands[i] = true; } if (adbg_bits_ptrbounds(command, macho_load_command_t.sizeof, internal.commands, internal.header.sizeofcmds)) { adbg_oops(AdbgError.offsetBounds); return null; } command = cast(macho_load_command_t*)(cast(void*)command + command.cmdsize); } return command; } void* adbg_object_macho_segment_section(adbg_object_t *o, macho_load_command_t *c, size_t index) { if (o == null || c == null) { adbg_oops(AdbgError.invalidArgument); return null; } if (o.internal == null) { adbg_oops(AdbgError.uninitiated); return null; } if (o.status & MACHO_IS_FAT) { adbg_oops(AdbgError.unavailable); return null; } //TODO: Swap fields switch (c.cmd) { case MACHO_LC_SEGMENT: macho_segment_command_t *seg = cast(macho_segment_command_t*)c; if (index > seg.nsects) { adbg_oops(AdbgError.indexBounds); return null; } macho_section_t *section = cast(macho_section_t*) (cast(void*)seg + macho_segment_command_t.sizeof) + index; if (adbg_bits_ptrbounds(section, macho_section_t.sizeof, c, c.cmdsize)) { adbg_oops(AdbgError.offsetBounds); return null; } return section; case MACHO_LC_SEGMENT_64: macho_segment_command_64_t *seg64 = cast(macho_segment_command_64_t*)c; if (index > seg64.nsects) { adbg_oops(AdbgError.indexBounds); return null; } macho_section64_t *section64 = cast(macho_section64_t*) (cast(void*)seg64 + macho_segment_command_64_t.sizeof) + index; if (adbg_bits_ptrbounds(section64, macho_section64_t.sizeof, c, c.cmdsize)) { adbg_oops(AdbgError.offsetBounds); return null; } return section64; default: adbg_oops(AdbgError.unavailable); return null; } } const(char) *adbg_object_macho_magic_string(uint signature) { switch (signature) { case MACHO_MAGIC: return "MACHO_MAGIC"; case MACHO_MAGIC64: return "MACHO_MAGIC_64"; case MACHO_CIGAM: return "MACHO_CIGAM"; case MACHO_CIGAM64: return "MACHO_CIGAM_64"; case MACHO_FATMAGIC: return "MACHO_FAT_MAGIC"; case MACHO_FATCIGAM: return "MACHO_FAT_CIGAM"; default: return null; } } const(char) *adbg_object_macho_filetype_string(uint type) { // NOTE: FAT files have no filetypes switch (type) { case MACHO_FILETYPE_OBJECT: return "Object"; case MACHO_FILETYPE_EXECUTE: return "Executable"; case MACHO_FILETYPE_FVMLIB: return "Fixed VM Library"; case MACHO_FILETYPE_CORE: return "Core"; case MACHO_FILETYPE_PRELOAD: return "Preload"; case MACHO_FILETYPE_DYLIB: return "Dynamic library"; case MACHO_FILETYPE_DYLINKER: return "Dynamic linker"; case MACHO_FILETYPE_BUNDLE: return "Bundle"; case MACHO_FILETYPE_DYLIB_STUB: return "Dynamic library stub"; case MACHO_FILETYPE_DSYM: return "Companion file (debug)"; case MACHO_FILETYPE_KEXT_BUNDLE: return "Kext bundle"; default: return null; } } AdbgMachine adbg_object_macho_machine(adbg_object_t *o) { if (o == null) { adbg_oops(AdbgError.invalidArgument); return AdbgMachine.unknown; } if (o.internal == null) { adbg_oops(AdbgError.uninitiated); return AdbgMachine.unknown; } internal_macho_t *internal = cast(internal_macho_t*)o.internal; //TODO: Better support fat Mach-Os if (o.status & MACHO_IS_FAT) { adbg_oops(AdbgError.unavailable); return AdbgMachine.unknown; } switch (internal.header.cputype) { case MACHO_CPUTYPE_VAX: return AdbgMachine.vax; case MACHO_CPUTYPE_ROMP: return AdbgMachine.romp; case MACHO_CPUTYPE_NS32032: case MACHO_CPUTYPE_NS32332: case MACHO_CPUTYPE_NS32532: return AdbgMachine.ns32k; case MACHO_CPUTYPE_I386: return AdbgMachine.i386; case MACHO_CPUTYPE_X86_64: return AdbgMachine.amd64; case MACHO_CPUTYPE_MIPS: return AdbgMachine.mips; case MACHO_CPUTYPE_HPPA: return AdbgMachine.parisc; case MACHO_CPUTYPE_MC680x0: return AdbgMachine.m68k; case MACHO_CPUTYPE_MC88000: return AdbgMachine.m88k; case MACHO_CPUTYPE_ARM: return AdbgMachine.arm; case MACHO_CPUTYPE_ARM64: return AdbgMachine.aarch64; case MACHO_CPUTYPE_SPARC: return AdbgMachine.sparc; case MACHO_CPUTYPE_I860_LITTLE: case MACHO_CPUTYPE_I860: return AdbgMachine.i860; case MACHO_CPUTYPE_RS6000: return AdbgMachine.rs6000; case MACHO_CPUTYPE_POWERPC64: return AdbgMachine.ppc64; case MACHO_CPUTYPE_POWERPC: return AdbgMachine.ppc; case MACHO_CPUTYPE_VEO: return AdbgMachine.veo; default: return AdbgMachine.unknown; } } const(char)* adbg_object_macho_cputype_string(uint type) { switch (type) { case MACHO_CPUTYPE_VAX: return "VAX"; case MACHO_CPUTYPE_ROMP: return "ROMP"; case MACHO_CPUTYPE_NS32032: return "NS32032"; case MACHO_CPUTYPE_NS32332: return "NS32332"; case MACHO_CPUTYPE_NS32532: return "NS32532"; case MACHO_CPUTYPE_I386: return "x86"; case MACHO_CPUTYPE_X86_64: return "x86-64"; case MACHO_CPUTYPE_MIPS: return "MIPS"; case MACHO_CPUTYPE_MC680x0: return "MC68000"; case MACHO_CPUTYPE_HPPA: return "HPPA"; case MACHO_CPUTYPE_ARM: return "ARM"; case MACHO_CPUTYPE_ARM64: return "ARM64"; case MACHO_CPUTYPE_MC88000: return "MC88000"; case MACHO_CPUTYPE_I860, MACHO_CPUTYPE_I860_LITTLE: return "i860"; case MACHO_CPUTYPE_RS6000: return "RS6000"; case MACHO_CPUTYPE_POWERPC64: return "PowerPC64"; case MACHO_CPUTYPE_POWERPC: return "PowerPC"; case MACHO_CPUTYPE_VEO: return "VEO"; default: return null; } } const(char)* adbg_object_macho_subtype_string(uint type, uint subtype) { switch (type) { case MACHO_CPUTYPE_VAX: switch (subtype) { case MACHO_SUBTYPE_VAX780: return "VAX780"; case MACHO_SUBTYPE_VAX785: return "VAX785"; case MACHO_SUBTYPE_VAX750: return "VAX750"; case MACHO_SUBTYPE_VAX730: return "VAX730"; case MACHO_SUBTYPE_UVAXI: return "UVAXI"; case MACHO_SUBTYPE_UVAXII: return "UVAXII"; case MACHO_SUBTYPE_VAX8200: return "VAX8200"; case MACHO_SUBTYPE_VAX8500: return "VAX8500"; case MACHO_SUBTYPE_VAX8600: return "VAX8600"; case MACHO_SUBTYPE_VAX8650: return "VAX8650"; case MACHO_SUBTYPE_VAX8800: return "VAX8800"; case MACHO_SUBTYPE_UVAXIII: return "UVAXIII"; default: return "VAX"; } case MACHO_CPUTYPE_ROMP: switch (subtype) { case MACHO_SUBTYPE_RT_PC: return "ROMP RT_PC"; case MACHO_SUBTYPE_RT_APC: return "ROMP RT_APC"; case MACHO_SUBTYPE_RT_135: return "ROMP RT_135"; default: return "ROMP"; } case MACHO_CPUTYPE_NS32032: return "NS32032"; case MACHO_CPUTYPE_NS32332: return "NS32332"; case MACHO_CPUTYPE_NS32532: return "NS32532"; case MACHO_CPUTYPE_I386: switch (subtype) { case MACHO_SUBTYPE_i386: return "i386"; case MACHO_SUBTYPE_i486: return "i486"; case MACHO_SUBTYPE_i486SX: return "i486SX"; case MACHO_SUBTYPE_PENT: return "Pentium"; case MACHO_SUBTYPE_PENPRO: return "Pentium Pro"; case MACHO_SUBTYPE_PENTII_M3: return "Pentium III (M3)"; case MACHO_SUBTYPE_PENTII_M5: return "Pentium III (M5)"; case MACHO_SUBTYPE_PENTIUM_4: return "Pentium 4"; default: return "x86"; } case MACHO_CPUTYPE_X86_64: return "x86-64"; case MACHO_CPUTYPE_MIPS: switch (subtype) { case MACHO_SUBTYPE_R2300: return "MIPS R2300"; case MACHO_SUBTYPE_R2600: return "MIPS R2600"; case MACHO_SUBTYPE_R2800: return "MIPS R2800"; case MACHO_SUBTYPE_R2800a: return "MIPS R2800a"; default: return "MIPS"; } case MACHO_CPUTYPE_MC680x0: switch (subtype) { case MACHO_SUBTYPE_MC68030: return "MC68030"; case MACHO_SUBTYPE_MC68040: return "MC68040"; case MACHO_SUBTYPE_MC68030_ONLY: return "MC68030-only"; default: return "MC68000"; } case MACHO_CPUTYPE_HPPA: switch (subtype) { case MACHO_SUBTYPE_HPPA7100LC: return "HPPA7100LC"; default: return "HPPA7100"; } case MACHO_CPUTYPE_ARM: switch (subtype) { case MACHO_SUBTYPE_A500_ARCH: return "ARM A500"; case MACHO_SUBTYPE_A500: return "ARM A500"; case MACHO_SUBTYPE_A440: return "ARM A440"; case MACHO_SUBTYPE_M4: return "ARM M4"; case MACHO_SUBTYPE_V4T: return "ARM V4T"; case MACHO_SUBTYPE_V6: return "ARM V6"; case MACHO_SUBTYPE_V5TEJ: return "ARM V5TEJ"; case MACHO_SUBTYPE_XSCALE: return "ARM XSCALE"; case MACHO_SUBTYPE_V7: return "ARM V7"; case MACHO_SUBTYPE_V8: return "ARM V8"; default: return "ARM"; } case MACHO_CPUTYPE_ARM64: return "ARM64 V8"; case MACHO_CPUTYPE_MC88000: switch (subtype) { case MACHO_SUBTYPE_MC88100: return "MC88100"; case MACHO_SUBTYPE_MC88110: return "MC88110"; default: return "MC88000"; } case MACHO_CPUTYPE_I860: return "i860"; case MACHO_CPUTYPE_I860_LITTLE: return "i860 (little-endian)"; case MACHO_CPUTYPE_RS6000: return "IBM RS6000"; case MACHO_CPUTYPE_POWERPC64: switch (subtype) { case MACHO_SUBTYPE_POWERPC_601: return "PowerPC64 601"; case MACHO_SUBTYPE_POWERPC_602: return "PowerPC64 602"; case MACHO_SUBTYPE_POWERPC_603: return "PowerPC64 603"; case MACHO_SUBTYPE_POWERPC_603e: return "PowerPC64 603e"; case MACHO_SUBTYPE_POWERPC_603ev: return "PowerPC64 603ev"; case MACHO_SUBTYPE_POWERPC_604: return "PowerPC64 604"; case MACHO_SUBTYPE_POWERPC_604e: return "PowerPC64 604e"; case MACHO_SUBTYPE_POWERPC_620: return "PowerPC64 620"; case MACHO_SUBTYPE_POWERPC_750: return "PowerPC64 750"; case MACHO_SUBTYPE_POWERPC_7400: return "PowerPC64 7400"; case MACHO_SUBTYPE_POWERPC_7450: return "PowerPC64 7450"; case MACHO_SUBTYPE_POWERPC_970: return "PowerPC64 970"; default: return "PowerPC64"; } case MACHO_CPUTYPE_POWERPC: switch (subtype) { case MACHO_SUBTYPE_POWERPC_601: return "PowerPC 601"; case MACHO_SUBTYPE_POWERPC_602: return "PowerPC 602"; case MACHO_SUBTYPE_POWERPC_603: return "PowerPC 603"; case MACHO_SUBTYPE_POWERPC_603e: return "PowerPC 603e"; case MACHO_SUBTYPE_POWERPC_603ev: return "PowerPC 603ev"; case MACHO_SUBTYPE_POWERPC_604: return "PowerPC 604"; case MACHO_SUBTYPE_POWERPC_604e: return "PowerPC 604e"; case MACHO_SUBTYPE_POWERPC_620: return "PowerPC 620"; case MACHO_SUBTYPE_POWERPC_750: return "PowerPC 750"; case MACHO_SUBTYPE_POWERPC_7400: return "PowerPC 7400"; case MACHO_SUBTYPE_POWERPC_7450: return "PowerPC 7450"; case MACHO_SUBTYPE_POWERPC_970: return "PowerPC 970"; default: return "PowerPC"; } case MACHO_CPUTYPE_VEO: return "VEO"; default: return null; } } const(char)* adbg_object_macho_command_string(uint command) { switch (command) { case MACHO_LC_REQ_DYLD: return "LC_REQ_DYLD"; case MACHO_LC_SEGMENT: return "LC_SEGMENT"; case MACHO_LC_SYMTAB: return "LC_SYMTAB"; case MACHO_LC_SYMSEG: return "LC_SYMSEG"; case MACHO_LC_THREAD: return "LC_THREAD"; case MACHO_LC_UNIXTHREAD: return "LC_UNIXTHREAD"; case MACHO_LC_LOADFVMLIB: return "LC_LOADFVMLIB"; case MACHO_LC_IDFVMLIB: return "LC_IDFVMLIB"; case MACHO_LC_IDENT: return "LC_IDENT"; case MACHO_LC_FVMFILE: return "LC_FVMFILE"; case MACHO_LC_PREPAGE: return "LC_PREPAGE"; case MACHO_LC_DYSYMTAB: return "LC_DYSYMTAB"; case MACHO_LC_LOAD_DYLIB: return "LC_LOAD_DYLIB"; case MACHO_LC_ID_DYLIB: return "LC_ID_DYLIB"; case MACHO_LC_LOAD_DYLINKER: return "LC_LOAD_DYLINKER"; case MACHO_LC_ID_DYLINKER: return "LC_ID_DYLINKER"; case MACHO_LC_PREBOUND_DYLIB: return "LC_PREBOUND_DYLIB"; case MACHO_LC_ROUTINES: return "LC_ROUTINES"; case MACHO_LC_SUB_FRAMEWORK: return "LC_SUB_FRAMEWORK"; case MACHO_LC_SUB_UMBRELLA: return "LC_SUB_UMBRELLA"; case MACHO_LC_SUB_CLIENT: return "LC_SUB_CLIENT"; case MACHO_LC_SUB_LIBRARY: return "LC_SUB_LIBRARY"; case MACHO_LC_TWOLEVEL_HINTS: return "LC_TWOLEVEL_HINTS"; case MACHO_LC_PREBIND_CKSUM: return "LC_PREBIND_CKSUM"; case MACHO_LC_SEGMENT_64: return "LC_SEGMENT_64"; case MACHO_LC_ROUTINES_64: return "LC_ROUTINES_64"; case MACHO_LC_UUID: return "LC_UUID"; case MACHO_LC_RPATH: return "LC_RPATH"; case MACHO_LC_CODE_SIGNATURE: return "LC_CODE_SIGNATURE"; case MACHO_LC_SEGMENT_SPLIT_INFO: return "LC_SEGMENT_SPLIT_INFO"; case MACHO_LC_REEXPORT_DYLIB: return "LC_REEXPORT_DYLIB"; case MACHO_LC_LAZY_LOAD_DYLIB: return "LC_LAZY_LOAD_DYLIB"; case MACHO_LC_ENCRYPTION_INFO: return "LC_ENCRYPTION_INFO"; case MACHO_LC_DYLD_INFO: return "LC_DYLD_INFO"; case MACHO_LC_DYLD_INFO_ONLY: return "LC_DYLD_INFO_ONLY"; case MACHO_LC_LOAD_UPWARD_DYLIB: return "LC_LOAD_UPWARD_DYLIB"; case MACHO_LC_VERSION_MIN_MACOSX: return "LC_VERSION_MIN_MACOSX"; case MACHO_LC_VERSION_MIN_IPHONEOS: return "LC_VERSION_MIN_IPHONEOS"; case MACHO_LC_FUNCTION_STARTS: return "LC_FUNCTION_STARTS"; case MACHO_LC_DYLD_ENVIRONMENT: return "LC_DYLD_ENVIRONMENT"; case MACHO_LC_MAIN: return "LC_MAIN"; case MACHO_LC_DATA_IN_CODE: return "LC_DATA_IN_CODE"; case MACHO_LC_SOURCE_VERSION: return "LC_SOURCE_VERSION"; case MACHO_LC_DYLIB_CODE_SIGN_DRS: return "LC_DYLIB_CODE_SIGN_DRS"; case MACHO_LC_ENCRYPTION_INFO_64: return "LC_ENCRYPTION_INFO_64"; case MACHO_LC_LINKER_OPTION: return "LC_LINKER_OPTION"; case MACHO_LC_LINKER_OPTIMIZATION_HINT: return "LC_LINKER_OPTIMIZATION_HINT"; case MACHO_LC_VERSION_MIN_WATCHOS: return "LC_VERSION_MIN_WATCHOS"; default: return null; } } const(char)* adbg_object_macho_kind_string(adbg_object_t *o) { if (o == null) { adbg_oops(AdbgError.invalidArgument); return null; } if (o.internal == null) { adbg_oops(AdbgError.uninitiated); return null; } internal_macho_t *internal = cast(internal_macho_t*)o.internal; if (o.status & MACHO_IS_FAT) return `Fat Executable`; return adbg_object_macho_filetype_string(internal.header.filetype); }