Mach-O (Mach object) is a file format for executables, object code, shared libraries, dynamically loaded code, and core dumps. It was developed to replace the a.out format.
Mach-O is used by some systems based on the Mach kernel. NeXTSTEP, macOS, and iOS are examples of systems that use this format for native executables, libraries and object code.
File layout Each Mach-O file is made up of one Mach-O header, followed by a series of load commands, followed by one or more segments, each of which contains between 0 and 255 sections. Mach-O uses the REL relocation format to handle references to symbols. When looking up symbols, Mach-O uses a two-level namespace that encodes each symbol into an 'object/symbol name' pair that is then linearly searched for, first by the object and then the symbol name.
The basic structure—a list of variable-length "load commands" that reference pages of data elsewhere in the file—was also used in the executable file format for Accent. The Accent file format was, in turn, based on an idea from Spice Lisp.
All multi-byte values in all data structures are written in the byte order of the host for which the code was produced.
Header
+ Mach-O file header Offset Bytes Description 0 4 Magic number 4 4 CPU type 8 4 CPU subtype 12 4 File type 16 4 Number of load commands 20 4 Size of load commands 24 4 Flags 28 4 Reserved (64-bit only)
The magic number for 32-bit code is while the magic number for 64-bit architectures is .
The reserved value is only present in 64-bit Mach-O files. It is reserved for future use or extension of the 64-bit header.
The CPU type indicates the instruction set architecture for the code. If the file is for the 64-bit version of the instruction set architecture, the CPU type value has the bit set. If the file is for the 64-bit version of the instruction set architecture but with 32-bit pointers, the CPU type value has the bit set.
The CPU type values are as follows:
+ CPU type Value CPU type VAX ROMP NS32032 NS32332 MC680x0 x86 MIPS NS32352 HP-PA ARM MC88000 SPARC i860 (big-endian) i860 (little-endian) or maybe DEC Alpha RS/6000 PowerPC / MC98000 RISC-V
Each CPU type has a set of CPU subtype values, indicating a particular model of that CPU type for which the code is intended. Newer models of a CPU type may support instructions, or other features, not supported by older CPU models, so that code compiled or written for a newer model might contain instructions that are illegal instructions on an older model, causing that code to trap or otherwise fail to operate correctly when run on an older model. Code intended for an older model will run on newer models without problems.
If the CPU type is ARM then the subtypes are as follows:
+ CPU subtype ARM Value CPU version All ARM processors Optimized for ARM-A500 ARCH or newer. Optimized for ARM-A500 or newer. Optimized for ARM-A440 or newer. Optimized for ARM-M4 or newer. Optimized for ARM-V4T or newer. Optimized for ARM-V6 or newer. Optimized for ARM-V5TEJ or newer. Optimized for ARM-XSCALE or newer. Optimized for ARM-V7 or newer. Optimized for ARM-V7F (Cortex A9) or newer. Optimized for ARM-V7S (Swift) or newer. Optimized for ARM-V7K (Kirkwood40) or newer. Optimized for ARM-V8 or newer. Optimized for ARM-V6M or newer. Optimized for ARM-V7M or newer. Optimized for ARM-V7EM or newer.
If the CPU type is x86 then the subtypes are as follows:
+ CPU subtype x86 Value CPU version All x86 processors. Optimized for 486 or newer. Optimized for 486SX or newer. Optimized for Pentium M5 or newer. Optimized for Celeron or newer. Optimized for Celeron Mobile. Optimized for Pentium 3 or newer. Optimized for Pentium 3-M or newer. Optimized for Pentium 3-XEON or newer. Optimized for Pentium-4 or newer. Optimized for Itanium or newer. Optimized for Itanium-2 or newer. Optimized for XEON or newer. Optimized for XEON-MP or newer.
After the subtype value is the file type value.
+ File type Value Description Relocatable object file. Demand paged executable file. Fixed VM shared library file. Core file. Preloaded executable file. Dynamically bound shared library file. Dynamic link editor. Dynamically bound bundle file. Shared library stub for static linking only, no section contents. Companion file with only debug sections. x86_64 kexts. a file composed of other Mach-Os to be run in the same userspace sharing a single linkedit.
After the file type value is the number of load commands and the total number of bytes the load commands are after the Mach-O header, then a 32-bit flag with the following possible settings.
+ Flag settings Flag in left shift Flag in binary Description The object file has no undefined references. The object file is the output of an incremental link against a base file and can't be link edited again. The object file is input for the dynamic linker and can't be statically link edited again. The object file's undefined references are bound by the dynamic linker when loaded. The file has its dynamic undefined references prebound. The file has its read-only and read-write segments split. The shared library init routine is to be run lazily via catching memory faults to its writeable segments (obsolete). The image is using two-level name space bindings. The executable is forcing all images to use flat name space bindings. This umbrella guarantees no multiple definitions of symbols in its sub-images so the two-level namespace hints can always be used. Do not have dyld notify the prebinding agent about this executable. The binary is not prebound but can have its prebinding redone. only used when MH_PREBOUND is not set. Indicates that this binary binds to all two-level namespace modules of its dependent libraries. Safe to divide up the sections into sub-sections via symbols for dead code stripping. The binary has been canonicalized via the un-prebind operation. The final linked image contains external weak symbols. The final linked image uses weak symbols. When this bit is set, all stacks in the task will be given stack execution privilege. When this bit is set, the binary declares it is safe for use in processes with uid zero. When this bit is set, the binary declares it is safe for use in processes when UGID is true. When this bit is set on a dylib, the static linker does not need to examine dependent dylibs to see if any are re-exported. When this bit is set, the OS will load the main executable at a random address. Only for use on dylibs. When linking against a dylib that has this bit set, the static linker will automatically not create a load command to the dylib if no symbols are being referenced from the dylib. Contains a section of type S_THREAD_LOCAL_VARIABLES. When this bit is set, the OS will run the main executable with a non-executable heap even on platforms (e.g. i386) that don't require it. The code was linked for use in an application. The external symbols listed in the nlist symbol table do not include all the symbols listed in the dyld info. Allow LC_MIN_VERSION_MACOS and LC_BUILD_VERSION load commands with the platforms macOS, macCatalyst, iOSSimulator, tvOSSimulator and watchOSSimulator. Only for use on dylibs. When this bit is set, the dylib is part of the dyld shared cache, rather than loose in the filesystem. The digits marked with "x" have no use, and are reserved for future use.
Multiple binary digits can be set to one in the flags to identify any information or settings that apply to the binary.
Now the load commands are read as one has reached the end of the Mach-O header.
Multi-architecture binaries Multiple Mach-O files can be combined in a multi-architecture binary. This allows a single binary file to contain code to support multiple instruction set architectures, for example for different generations and types of Apple devices, including different processor architectures such as ARM64 and x86-64.
All fields in the universal header are big-endian.
The universal header is in the following form:
+ Mach-O universal header Offset Bytes Description 0 4 Magic number 4 4 Number of binaries
The magic number in a multi-architecture binary is in big-endian byte order, so the first four bytes of the header will always be , in that order.
The number of binaries is the number of entries that follow the header.
The header is followed by a sequence of entries in the following form:
+ Mach-O universal file entries Offset Bytes Description 0 4 CPU type 4 4 CPU subtype 8 4 File offset 12 4 Size 16 4 Section alignment (power of 2)
The sequence of entries is followed by a sequence of Mach-O images. Each entry refers to a Mach-O image.
The CPU type and subtype for an entry must be the same as the CPU type and subtype for the Mach-O image to which the entry refers.
The file offset and size are the offset in the file of the beginning of the Mach-O image, and the size of the Mach-O image, to which the entry refers.
The section alignment is the logarithm, base 2, of the byte alignment in the file required for the Mach-O image to which the entry refers; for example, a value of 14 means that the image must be aligned on a 214-byte boundary, i.e. a 16384-byte boundary. This is required by tools that modify the multi-architecture binary, in order for them to keep the image properly aligned.
Load commands
The load commands are read immediately after the Mach-O header.
The Mach-O header specifies how many load commands exist after the Mach-O header and the size in bytes to where the load commands end. The size of load commands is used as a redundancy check.
When the last load command is read and the number of bytes for the load commands do not match, or if we go outside the number of bytes for load commands before reaching the last load command, then the file may be corrupted.
Each load command is a sequence of entries in the following form:
+ Load command Offset Bytes Description 0 4 Command type 4 4 Command size
The load command type identifies what the parameters are in the load command. If a load command starts with bit set, the load command is necessary in order to be able to load or run the binary. This allows older Mach-O loaders to skip commands not understood by the loader that are not mandatory for loading the application.
Segment load command Mach-O binaries that use load command type use the 32-bit version of the segment load command, while is used to specify the 64-bit version of the segment load command.,
The segment load command varies whether the Mach-O header is 32-bit or 64-bit. This is because 64-bit processor architecture uses 64-bit addresses while 32-bit architectures use 32-bit addresses.
All virtual RAM addresses are added to a base address to keep applications spaced apart. Each section in a segment load command has a relocation list offset that specifies the offsets in the section that must be adjusted based on the application's base address. The relocations are unnecessary if the application can be placed at its defined RAM address locations such as a base address of zero. + Load command (Segment load32/64) Offset (32-bit) Bytes (32-bit) Offset (64-bit) Bytes (64-bit) Description 0 4 0 4 (Command type 32-bit) (Command type 64-bit) 4 4 4 4 Command size 8 16 8 16 Segment name 24 4 24 8 Address 28 4 32 8 Address size 32 4 40 8 File offset 36 4 48 8 Size (bytes from file offset) 40 4 56 4 Maximum virtual memory protections 44 4 60 4 Initial virtual memory protections 48 4 64 4 Number of sections 52 4 68 4 Flag32
A segment name cannot be larger than 16 text characters in bytes. The unused characters are in value.
The segment command contains the address to write the section in virtual address space plus the application's base address. The number of bytes to write to the address location (address size).
After the address information is the file offset the segment data is located in the Mach-O binary, and the number of bytes to read from the file.