mach_o: format specification

KS implementation details

License: MIT
Minimal Kaitai Struct required: 0.9

References

This page hosts a formal specification of mach_o using Kaitai Struct. This specification can be automatically translated into a variety of programming languages to get a parsing library.

Block diagram

Format specification in Kaitai Struct YAML

meta:
  id: mach_o
  xref:
    justsolve: Mach-O
    pronom:
      - fmt/692 # Mach-O 32bit
      - fmt/693 # Mach-O 64bit
    wikidata: Q2627217
  license: MIT
  ks-version: 0.9
  imports:
    - /serialization/asn1/asn1_der
  endian: le
doc-ref:
  - https://www.stonedcoder.org/~kd/lib/MachORuntime.pdf
  - https://opensource.apple.com/source/python_modules/python_modules-43/Modules/macholib-1.5.1/macholib-1.5.1.tar.gz
  - https://github.com/comex/cs/blob/07a88f9/macho_cs.py
  - https://opensource.apple.com/source/Security/Security-55471/libsecurity_codesigning/requirements.grammar.auto.html
  - https://github.com/apple/darwin-xnu/blob/xnu-2782.40.9/bsd/sys/codesign.h
  - https://opensource.apple.com/source/dyld/dyld-852/src/ImageLoaderMachO.cpp.auto.html
  - https://opensource.apple.com/source/dyld/dyld-852/src/ImageLoaderMachOCompressed.cpp.auto.html
seq:
  - id: magic
    type: u4be
    enum: magic_type
  - id: header
    type: mach_header
  - id: load_commands
    type: load_command
    repeat: expr
    repeat-expr: header.ncmds
enums:
  magic_type:
    # Note that for multiarch (a.k.a. fat) Mach-O files, which are the primary
    # kind you find on macOS today, you should instead use mach_o_fat.ksy, which
    # parses the fat header and embeds mach_o.ksy to parse each arch.
    0xFEEDFACE: macho_be_x86 # MH_MAGIC:    mach-o, big-endian,    x86
    0xCEFAEDFE: macho_le_x86 # MH_CIGAM:    mach-o, little-endian, x86
    0xFEEDFACF: macho_be_x64 # MH_MAGIC_64: mach-o, big-endian,    x64
    0xCFFAEDFE: macho_le_x64 # MH_CIGAM_64: mach-o, little-endian, x64
  cpu_type:
    0xffffffff: any
    1:          vax
    2:          romp
    4:          ns32032
    5:          ns32332
    7:          i386
    8:          mips
    9:          ns32532
    11:         hppa
    12:         arm
    13:         mc88000
    14:         sparc
    15:         i860
    16:         i860_little
    17:         rs6000
    18:         powerpc
    0x1000000:  abi64     # flag
    0x1000007:  x86_64    # abi64 | i386
    0x1000012:  powerpc64 # abi64 | powerpc
    0x100000c:  arm64     # abi64 | arm
  file_type:
    # https://opensource.apple.com/source/xnu/xnu-1456.1.26/EXTERNAL_HEADERS/mach-o/loader.h
    0x1: object      # relocatable object file
    0x2: execute     # demand paged executable file
    0x3: fvmlib      # fixed VM shared library file
    0x4: core        # core file
    0x5: preload     # preloaded executable file
    0x6: dylib       # dynamically bound shared library
    0x7: dylinker    # dynamic link editor
    0x8: bundle      # dynamically bound bundle file
    0x9: dylib_stub  # shared library stub for static linking only, no section contents
    0xa: dsym        # companion file with only debug sections
    0xb: kext_bundle # x86_64 kexts
  load_command_type:
    # https://opensource.apple.com/source/xnu/xnu-1456.1.26/EXTERNAL_HEADERS/mach-o/loader.h
    0x80000000: req_dyld
    0x1       : segment        # segment of this file to be mapped
    0x2       : symtab         # link-edit stab symbol table info
    0x3       : symseg         # link-edit gdb symbol table info (obsolete)
    0x4       : thread         # thread
    0x5       : unix_thread    # unix thread (includes a stack)
    0x6       : load_fvm_lib   # load a specified fixed VM shared library
    0x7       : id_fvm_lib     # fixed VM shared library identification
    0x8       : ident          # object identification info (obsolete)
    0x9       : fvm_file       # fixed VM file inclusion (internal use)
    0xa       : prepage        # prepage command (internal use)
    0xb       : dysymtab       # dynamic link-edit symbol table info
    0xc       : load_dylib     # load a dynamically linked shared library
    0xd       : id_dylib       # dynamically linked shared lib ident
    0xe       : load_dylinker  # load a dynamic linker
    0xf       : id_dylinker    # dynamic linker identification
    0x10      : prebound_dylib # modules prebound for a dynamically
    # linked shared library
    0x11      : routines           # image routines
    0x12      : sub_framework      # sub framework
    0x13      : sub_umbrella       # sub umbrella
    0x14      : sub_client         # sub client
    0x15      : sub_library        # sub library
    0x16      : twolevel_hints     # two-level namespace lookup hints
    0x17      : prebind_cksum      # prebind checksum
    0x80000018: load_weak_dylib    # load a dynamically linked shared library that is allowed to be missing (all symbols are weak imported)
    0x19      : segment_64         # 64-bit segment of this file to be mapped
    0x1a      : routines_64        # 64-bit image routines
    0x1b      : uuid               # the uuid
    0x8000001c: rpath              # runpath additions
    0x1d      : code_signature     # local of code signature
    0x1e      : segment_split_info # local of info to split segments
    0x8000001f: reexport_dylib     # load and re-export dylib
    0x20      : lazy_load_dylib    # delay load of dylib until first use
    0x21      : encryption_info    # encrypted segment information
    0x22      : dyld_info          # compressed dyld information
    0x80000022: dyld_info_only     # compressed dyld information only
    0x80000023: load_upward_dylib
    0x24      : version_min_macosx
    0x25      : version_min_iphoneos
    0x26      : function_starts
    0x27      : dyld_environment
    0x80000028: main
    0x29      : data_in_code
    0x2A      : source_version
    0x2B      : dylib_code_sign_drs
    0x2C      : encryption_info_64
    0x2D      : linker_option
    0x2E      : linker_optimization_hint
    0x2F      : version_min_tvos
    0x30      : version_min_watchos
    0x32      : build_version # LC_BUILD_VERSION
types:
  macho_flags:
    -webide-representation: "{this:flags}"
    params:
      - id: value
        type: u4
    instances:
      no_undefs:
        value: value & 0x1 != 0
        doc: "the object file has no undefined references"
      incr_link:
        value: value & 0x2 != 0
        doc: "the object file is the output of an incremental link against a base file and can't be link edited again"
      dyld_link:
        value: value & 0x4 != 0
        doc: "the object file is input for the dynamic linker and can't be staticly link edited again"
      bind_at_load:
        value: value & 0x8 != 0
        doc: "the object file's undefined references are bound by the dynamic linker when loaded."
      prebound:
        value: value & 0x10 != 0
        doc: "the file has its dynamic undefined references prebound."
      split_segs:
        value: value & 0x20 != 0
        doc: "the file has its read-only and read-write segments split"
      lazy_init:
        value: value & 0x40 != 0
        doc: "the shared library init routine is to be run lazily via catching memory faults to its writeable segments (obsolete)"
      two_level:
        value: value & 0x80 != 0
        doc: "the image is using two-level name space bindings"
      force_flat:
        value: value & 0x100 != 0
        doc: "the executable is forcing all images to use flat name space bindings"
      no_multi_defs:
        value: value & 0x200 != 0
        doc: "this umbrella guarantees no multiple defintions of symbols in its sub-images so the two-level namespace hints can always be used."
      no_fix_prebinding:
        value: value & 0x400 != 0
        doc: "do not have dyld notify the prebinding agent about this executable"
      prebindable:
        value: value & 0x800 != 0
        doc: "the binary is not prebound but can have its prebinding redone. only used when MH_PREBOUND is not set."
      all_mods_bound:
        value: value & 0x1000 != 0
        doc: "indicates that this binary binds to all two-level namespace modules of its dependent libraries. only used when MH_PREBINDABLE and MH_TWOLEVEL are both set."
      subsections_via_symbols:
        value: value & 0x2000 != 0
        doc: "safe to divide up the sections into sub-sections via symbols for dead code stripping"
      canonical:
        value: value & 0x4000 != 0
        doc: "the binary has been canonicalized via the unprebind operation"
      weak_defines:
        value: value & 0x8000 != 0
        doc: "the final linked image contains external weak symbols"
      binds_to_weak:
        value: value & 0x10000 != 0
        doc: "the final linked image uses weak symbols"
      allow_stack_execution:
        value: value & 0x20000 != 0
        doc: "When this bit is set, all stacks in the task will be given stack execution privilege.  Only used in MH_EXECUTE filetypes."
      root_safe:
        value: value & 0x40000 != 0
        doc: "When this bit is set, the binary declares it is safe for use in processes with uid zero"
      setuid_safe:
        value: value & 0x80000 != 0
        doc: "When this bit is set, the binary declares it is safe for use in processes when issetugid() is true"
      no_reexported_dylibs:
        value: value & 0x100000 != 0
        doc: "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"
      pie:
        value: value & 0x200000 != 0
        doc: "When this bit is set, the OS will load the main executable at a random address. Only used in MH_EXECUTE filetypes."
      dead_strippable_dylib:
        value: value & 0x400000 != 0
      has_tlv_descriptors:
        value: value & 0x800000 != 0
      no_heap_execution:
        value: value & 0x1000000 != 0
      app_extension_safe:
        value: value & 0x2000000 != 0
  mach_header:
    seq:
      - id: cputype
        type: u4
        enum: cpu_type
      - id: cpusubtype
        type: u4
      - id: filetype
        type: u4
        enum: file_type
      - id: ncmds
        type: u4
      - id: sizeofcmds
        type: u4
      - id: flags
        type: u4
      - id: reserved
        type: u4
        if: _root.magic == magic_type::macho_be_x64 or _root.magic == magic_type::macho_le_x64
    instances:
      flags_obj:
        type: macho_flags(flags)
        -webide-parse-mode: eager
  load_command:
    -webide-representation: '{type}: {body}'
    seq:
      - id: type
        type: u4
        enum: load_command_type
      - id: size
        -orig-id: cmdsize
        type: u4
      - id: body
        size: size - 8
        type:
          switch-on: type
          cases:
            'load_command_type::segment_64'              : segment_command_64
            'load_command_type::dyld_info'               : dyld_info_command
            'load_command_type::dyld_info_only'          : dyld_info_command
            'load_command_type::symtab'                  : symtab_command
            'load_command_type::dysymtab'                : dysymtab_command
            'load_command_type::load_dylinker'           : dylinker_command
            'load_command_type::id_dylinker'             : dylinker_command
            'load_command_type::dyld_environment'        : dylinker_command
            'load_command_type::uuid'                    : uuid_command
            'load_command_type::version_min_macosx'      : version_min_command
            'load_command_type::version_min_iphoneos'    : version_min_command
            'load_command_type::version_min_tvos'        : version_min_command
            'load_command_type::version_min_watchos'     : version_min_command
            'load_command_type::source_version'          : source_version_command
            'load_command_type::main'                    : entry_point_command
            'load_command_type::load_dylib'              : dylib_command
            'load_command_type::load_upward_dylib'       : dylib_command
            'load_command_type::id_dylib'                : dylib_command
            'load_command_type::load_weak_dylib'         : dylib_command
            'load_command_type::lazy_load_dylib'         : dylib_command
            'load_command_type::reexport_dylib'          : dylib_command
            'load_command_type::rpath'                   : rpath_command
            'load_command_type::function_starts'         : linkedit_data_command
            'load_command_type::data_in_code'            : linkedit_data_command
            'load_command_type::dylib_code_sign_drs'     : linkedit_data_command
            'load_command_type::linker_optimization_hint': linkedit_data_command
            'load_command_type::segment_split_info'      : linkedit_data_command
            'load_command_type::code_signature'          : code_signature_command
            'load_command_type::encryption_info_64'      : encryption_info_command
            'load_command_type::encryption_info'         : encryption_info_command
            'load_command_type::twolevel_hints'          : twolevel_hints_command
            'load_command_type::linker_option'           : linker_option_command
            'load_command_type::sub_framework'           : sub_command
            'load_command_type::sub_umbrella'            : sub_command
            'load_command_type::sub_client'              : sub_command
            'load_command_type::sub_library'             : sub_command
            'load_command_type::routines_64'             : routines_command_64
            'load_command_type::routines'                : routines_command
            'load_command_type::build_version'           : build_version_command
            'load_command_type::segment'                 : segment_command
  vm_prot:
    seq:
      - id: strip_read
        type: b1
        doc: Special marker to support execute-only protection.
        -orig-id: VM_PROT_STRIP_READ
      - id: is_mask
        doc: Indicates to use value as a mask against the actual protection bits.
        -orig-id: VM_PROT_IS_MASK
        type: b1
      - id: reserved0
        type: b1
        doc: Reserved (unused) bit.
      - id: copy
        type: b1
        doc: Used when write permission can not be obtained, to mark the entry as COW.
        -orig-id: VM_PROT_COPY
      - id: no_change
        type: b1
        doc: Used only by memory_object_lock_request to indicate no change to page locks.
        -orig-id: VM_PROT_NO_CHANGE
      - id: execute
        type: b1
        doc: Execute permission.
        -orig-id: VM_PROT_EXECUTE
      - id: write
        type: b1
        doc: Write permission.
        -orig-id: VM_PROT_WRITE
      - id: read
        type: b1
        doc: Read permission.
        -orig-id: VM_PROT_READ
      - id: reserved1
        type: b24
        doc: Reserved (unused) bits.
  uleb128:
    -webide-representation: "{value:dec}"
    seq:
      - id: b1
        type: u1
      - id: b2
        type: u1
        if: "b1 & 0x80 != 0"
      - id: b3
        type: u1
        if: "b2 & 0x80 != 0"
      - id: b4
        type: u1
        if: "b3 & 0x80 != 0"
      - id: b5
        type: u1
        if: "b4 & 0x80 != 0"
      - id: b6
        type: u1
        if: "b5 & 0x80 != 0"
      - id: b7
        type: u1
        if: "b6 & 0x80 != 0"
      - id: b8
        type: u1
        if: "b7 & 0x80 != 0"
      - id: b9
        type: u1
        if: "b8 & 0x80 != 0"
      - id: b10
        type: u1
        if: "b9 & 0x80 != 0"
    instances:
      value:
        value: >
          ((b1  % 128) <<  0) + ((b1 & 0x80 == 0) ? 0 :
          ((b2  % 128) <<  7) + ((b2 & 0x80 == 0) ? 0 :
          ((b3  % 128) << 14) + ((b3 & 0x80 == 0) ? 0 :
          ((b4  % 128) << 21) + ((b4 & 0x80 == 0) ? 0 :
          ((b5  % 128) << 28) + ((b5 & 0x80 == 0) ? 0 :
          ((b6  % 128) << 35) + ((b6 & 0x80 == 0) ? 0 :
          ((b7  % 128) << 42) + ((b7 & 0x80 == 0) ? 0 :
          ((b8  % 128) << 49) + ((b8 & 0x80 == 0) ? 0 :
          ((b9  % 128) << 56) + ((b8 & 0x80 == 0) ? 0 :
          ((b10 % 128) << 63))))))))))
        -webide-parse-mode: eager
  segment_command_64:
    -webide-representation: '{segname} ({initprot}): offs={fileoff}, size={filesize}'
    seq:
      - id: segname
        type: str
        size: 16
        pad-right: 0
        encoding: ascii
      - id: vmaddr
        type: u8
      - id: vmsize
        type: u8
      - id: fileoff
        type: u8
      - id: filesize
        type: u8
      - id: maxprot
        type: vm_prot
      - id: initprot
        type: vm_prot
      - id: nsects
        type: u4
      - id: flags
        type: u4
      - id: sections
        type: section_64
        repeat: expr
        repeat-expr: nsects
    types:
      section_64:
        -webide-representation: '{sect_name}: offs={offset}, size={size}'
        seq:
          - id: sect_name
            -orig-id: sectname
            size: 16
            type: str
            pad-right: 0
            encoding: ascii
          - id: seg_name
            -orig-id: segname
            size: 16
            type: str
            pad-right: 0
            encoding: ascii
          - id: addr
            type: u8
          - id: size
            type: u8
          - id: offset
            type: u4
          - id: align
            type: u4
          - id: reloff
            type: u4
          - id: nreloc
            type: u4
          - id: flags
            type: u4
          - id: reserved1
            type: u4
          - id: reserved2
            type: u4
          - id: reserved3
            type: u4
        instances:
          data:
            io: _root._io
            pos: offset
            size: size
            type:
              switch-on: sect_name
              cases:
                "'__cstring'":        string_list
                "'__objc_methname'":  string_list
                "'__objc_classname'": string_list
                "'__objc_methtype'":  string_list
                "'__nl_symbol_ptr'":  pointer_list
                "'__got'":            pointer_list
                "'__la_symbol_ptr'":  pointer_list
                "'__cfstring'":       cf_string_list
                "'__objc_classlist'": pointer_list
                "'__objc_nlclslist'": pointer_list
                "'__objc_protolist'": pointer_list
                "'__objc_imageinfo'": pointer_list
                "'__objc_selrefs'":   pointer_list
                "'__objc_protorefs'": pointer_list
                "'__objc_classrefs'": pointer_list
                "'__objc_superrefs'": pointer_list
                "'__eh_frame'":       eh_frame
        types:
          # https://reviews.llvm.org/D15502#b8fe88d5
          eh_frame:
            seq:
              - id: items
                type: eh_frame_item
                repeat: eos
          eh_frame_item:
            -webide-representation: '{body}'
            seq:
              - id: length
                type: u4
              - id: length64
                type: u8
                if: length == 0xffffffff
              - id: id
                type: u4
              - id: body
                size: length - 4
                if: length > 0
                type:
                  switch-on: id
                  cases:
                    0: cie
            types:
              char_chain:
                seq:
                  - id: chr
                    type: u1
                  - id: next
                    type: char_chain
                    if: chr != 0
              cie:
                -webide-representation: 'v:{version:dec} aug:{augmentation_string} code:{code_alignment_factor} data:{data_alignment_factor} returnReg:{return_address_register}'
                seq:
                  - id: version
                    type: u1
                  #- id: augmentation_string
                  #  type: strz
                  #  encoding: ascii
                  - id: aug_str
                    type: char_chain
                  #- id: eh_data
                  #  type: u8
                  #  if: "'eh' in augmentation_string"
                  - id: code_alignment_factor
                    type: uleb128
                  - id: data_alignment_factor
                    type: uleb128
                  - id: return_address_register
                    type: u1
                  - id: augmentation
                    type: augmentation_entry
                    if: 'aug_str.chr == 122'
              augmentation_entry:
                seq:
                  - id: length
                    type: uleb128
                  - id: fde_pointer_encoding
                    type: u1
                    if: _parent.aug_str.next.chr == 82
          string_list:
            seq:
              - id: strings
                type: strz
                encoding: ascii
                repeat: eos
          pointer_list:
            seq:
              - id: items
                type: u8
                repeat: eos
          cf_string:
            -webide-representation: "isa={isa}, info={info}, data={data}, length={length}"
            seq:
              - id: isa
                type: u8
              - id: info
                type: u8
              - id: data
                type: u8
              - id: length
                type: u8
          cf_string_list:
            seq:
              - id: items
                type: cf_string
                repeat: eos
  dyld_info_command:
    -webide-representation: 'rebase={rebase_off}, bind={bind_off}, weakBind={weak_bind_off}, lazyBind={lazy_bind_off}, export={export_off}'
    seq:
      - id: rebase_off
        type: u4
      - id: rebase_size
        type: u4
      - id: bind_off
        type: u4
      - id: bind_size
        type: u4
      - id: weak_bind_off
        type: u4
      - id: weak_bind_size
        type: u4
      - id: lazy_bind_off
        type: u4
      - id: lazy_bind_size
        type: u4
      - id: export_off
        type: u4
      - id: export_size
        type: u4
    instances:
      rebase:
        io: _root._io
        pos: rebase_off
        size: rebase_size
        type: rebase_data
        if: rebase_size != 0
      bind:
        io: _root._io
        pos: bind_off
        size: bind_size
        type: bind_data
        if: bind_size != 0
      weak_bind:
        io: _root._io
        pos: weak_bind_off
        size: weak_bind_size
        type: bind_data
        if: weak_bind_size != 0
      lazy_bind:
        io: _root._io
        pos: lazy_bind_off
        size: lazy_bind_size
        type: bind_data
        if: lazy_bind_size != 0
      exports:
        io: _root._io
        pos: export_off
        size: export_size
        type: export_node
        if: export_size != 0
    types:
      rebase_data:
        seq:
          - id: items
            type: rebase_item
            repeat: until
            repeat-until: _.opcode == opcode::done
        types:
          rebase_item:
            -webide-representation: "{opcode}, imm:{immediate}, uleb:{uleb}, skip:{skip}"
            seq:
              - id: opcode_and_immediate
                type: u1
              - id: uleb
                type: uleb128
                if: >
                  opcode == opcode::set_segment_and_offset_uleb or
                  opcode == opcode::add_address_uleb or
                  opcode == opcode::do_rebase_uleb_times or
                  opcode == opcode::do_rebase_add_address_uleb or
                  opcode == opcode::do_rebase_uleb_times_skipping_uleb
              - id: skip
                type: uleb128
                if: "opcode == opcode::do_rebase_uleb_times_skipping_uleb"
            instances:
              opcode:
                value: "opcode_and_immediate & 0xf0"
                enum: opcode
                -webide-parse-mode: eager
              immediate:
                value: "opcode_and_immediate & 0x0f"
                -webide-parse-mode: eager
        enums:
          opcode:
            0x00: done
            0x10: set_type_immediate
            0x20: set_segment_and_offset_uleb
            0x30: add_address_uleb
            0x40: add_address_immediate_scaled
            0x50: do_rebase_immediate_times
            0x60: do_rebase_uleb_times
            0x70: do_rebase_add_address_uleb
            0x80: do_rebase_uleb_times_skipping_uleb
      bind_item:
        -webide-representation: "{opcode}, imm:{immediate}, uleb:{uleb}, skip:{skip}, symbol:{symbol}"
        seq:
          - id: opcode_and_immediate
            type: u1
          - id: uleb
            type: uleb128
            if: >
              opcode == bind_opcode::set_dylib_ordinal_uleb or
              opcode == bind_opcode::set_append_sleb or
              opcode == bind_opcode::set_segment_and_offset_uleb or
              opcode == bind_opcode::add_address_uleb or
              opcode == bind_opcode::do_bind_add_address_uleb or
              opcode == bind_opcode::do_bind_uleb_times_skipping_uleb
          - id: skip
            type: uleb128
            if: "opcode == bind_opcode::do_bind_uleb_times_skipping_uleb"
          - id: symbol
            type: strz
            if: "opcode == bind_opcode::set_symbol_trailing_flags_immediate"
            encoding: ascii
        instances:
          opcode:
            value: "opcode_and_immediate & 0xf0"
            enum: bind_opcode
            -webide-parse-mode: eager
          immediate:
            value: "opcode_and_immediate & 0x0f"
            -webide-parse-mode: eager
      bind_data:
        seq:
          - id: items
            type: bind_item
            repeat: eos
      export_node:
        -webide-representation: "{children_count} children, term_size={terminal_size.value}"
        seq:
          - id: terminal_size
            type: uleb128
          - id: children_count
            type: u1
          - id: children
            type: child
            repeat: expr
            repeat-expr: children_count
          - id: terminal
            size: terminal_size.value
        types:
          child:
            -webide-representation: "{name}: {node_offset}"
            seq:
              - id: name
                type: strz
                encoding: ascii
              - id: node_offset
                type: uleb128
            instances:
              value:
                pos: node_offset.value
                type: export_node
    enums:
      bind_opcode:
        0x00: done
        0x10: set_dylib_ordinal_immediate
        0x20: set_dylib_ordinal_uleb
        0x30: set_dylib_special_immediate
        0x40: set_symbol_trailing_flags_immediate
        0x50: set_type_immediate
        0x60: set_append_sleb
        0x70: set_segment_and_offset_uleb
        0x80: add_address_uleb
        0x90: do_bind
        0xa0: do_bind_add_address_uleb
        0xb0: do_bind_add_address_immediate_scaled
        0xc0: do_bind_uleb_times_skipping_uleb
  symtab_command:
    -webide-representation: "symbols: {n_syms:dec}, strtab: {str_off}"
    seq:
      - id: sym_off
        -orig-id: symoff
        type: u4
      - id: n_syms
        -orig-id: nsyms
        type: u4
      - id: str_off
        -orig-id: stroff
        type: u4
      - id: str_size
        -orig-id: strsize
        type: u4
    instances:
      symbols:
        io: _root._io
        pos: sym_off
        type:
          switch-on: _root.magic
          cases:
            magic_type::macho_le_x64 : nlist_64
            magic_type::macho_be_x64 : nlist_64
            magic_type::macho_le_x86 : nlist
            magic_type::macho_be_x86 : nlist
        repeat: expr
        repeat-expr: n_syms
      strs:
        io: _root._io
        pos: str_off
        size: str_size
        type: str_table
    types:
      str_table:
        seq:
            # NOTE:  This may not always be a u4, I've also seen " " (0x20 0x00)
          - id: unknown
            type: u4
          - id: items
            type: strz
            encoding: utf-8
            repeat: until
            repeat-until: _ == ""
            eos-error: false
      nlist_64:
        -webide-representation: "un={un} type={type} sect={sect} desc={desc} value={value}"
        seq:
          - id: un
            type: u4
          - id: type
            type: u1
          - id: sect
            type: u1
          - id: desc
            type: u2
          - id: value
            type: u8
        instances:
          name:
            pos: _parent.str_off + un
            type: strz
            encoding: utf-8
            if: un != 0
      nlist:
        -webide-representation: "un={un} type={type} sect={sect} desc={desc} value={value}"
        seq:
          - id: un
            type: u4
          - id: type
            type: u1
          - id: sect
            type: u1
          - id: desc
            type: u2
          - id: value
            type: u4
        instances:
          name:
            pos: _parent.str_off + un
            type: strz
            encoding: utf-8
            if: un != 0
  dysymtab_command:
    seq:
      - id: i_local_sym
        -orig-id: ilocalsym
        type: u4
      - id: n_local_sym
        -orig-id: nlocalsym
        type: u4
      - id: i_ext_def_sym
        -orig-id: iextdefsym
        type: u4
      - id: n_ext_def_sym
        -orig-id: nextdefsym
        type: u4
      - id: i_undef_sym
        -orig-id: iundefsym
        type: u4
      - id: n_undef_sym
        -orig-id: nundefsym
        type: u4
      - id: toc_off
        -orig-id: tocoff
        type: u4
      - id: n_toc
        -orig-id: ntoc
        type: u4
      - id: mod_tab_off
        -orig-id: modtaboff
        type: u4
      - id: n_mod_tab
        -orig-id: nmodtab
        type: u4
      - id: ext_ref_sym_off
        -orig-id: extrefsymoff
        type: u4
      - id: n_ext_ref_syms
        -orig-id: nextrefsyms
        type: u4
      - id: indirect_sym_off
        -orig-id: indirectsymoff
        type: u4
      - id: n_indirect_syms
        -orig-id: nindirectsyms
        type: u4
      - id: ext_rel_off
        -orig-id: extreloff
        type: u4
      - id: n_ext_rel
        -orig-id: nextrel
        type: u4
      - id: loc_rel_off
        -orig-id: locreloff
        type: u4
      - id: n_loc_rel
        -orig-id: nlocrel
        type: u4
    instances:
      indirect_symbols:
        io: _root._io
        pos: indirect_sym_off
        type: u4
        repeat: expr
        repeat-expr: n_indirect_syms
  lc_str:
    -webide-representation: '{value}'
    seq:
      - id: length
        -orig-id: offset
        type: u4
      - id: value
        -orig-id: ptr
        type: strz
        encoding: UTF-8
  dylinker_command:
    -webide-representation: '{name}'
    seq:
      - id: name
        type: lc_str
  uuid_command:
    -webide-representation: 'uuid={uuid}'
    seq:
      - id: uuid
        size: 16
  version:
    -webide-representation: '{major:dec}.{minor:dec}'
    seq:
      - id: p1
        type: u1
      - id: minor
        type: u1
      - id: major
        type: u1
      - id: release
        type: u1
  encryption_info_command:
    seq:
      - id: cryptoff
        type: u4
      - id: cryptsize
        type: u4
      - id: cryptid
        type: u4
      - id: pad
        type: u4
        if: _root.magic == magic_type::macho_be_x64 or _root.magic == magic_type::macho_le_x64
  twolevel_hints_command:
    seq:
      - id: offset
        type: u4
      - id: num_hints
        -orig-id: nhints
        type: u4
  linker_option_command:
    seq:
      - id: num_strings
        -orig-id: count
        type: u4
      - id: strings
        type: strz
        encoding: utf-8
        repeat: expr
        repeat-expr: num_strings
  sub_command:
    seq:
      - id: name
        type: lc_str
  routines_command_64:
    seq:
      - id: init_address
        type: u8
      - id: init_module
        type: u8
      - id: reserved
        size: 48 # u8 * 6
  routines_command:
    seq:
      - id: init_address
        type: u4
      - id: init_module
        type: u4
      - id: reserved
        size: 24 # u4 * 6
  version_min_command:
    -webide-representation: 'v:{version}, r:{reserved}'
    seq:
      - id: version
        type: version
      - id: sdk
        type: version
  source_version_command:
    -webide-representation: 'v:{version:dec}'
    seq:
      - id: version
        type: u8
  entry_point_command:
    -webide-representation: 'entry_off={entry_off}, stack_size={stack_size}'
    seq:
      - id: entry_off
        -orig-id: entryoff
        type: u8
      - id: stack_size
        -orig-id: stacksize
        type: u8
  dylib_command:
    -webide-representation: '{name}'
    seq:
      - id: name_offset
        type: u4
      - id: timestamp
        type: u4
      - id: current_version
        type: u4
      - id: compatibility_version
        type: u4
      - id: name
        type: strz
        encoding: utf-8
  rpath_command:
    -webide-representation: '{path}'
    seq:
      - id: path_offset
        type: u4
      - id: path
        type: strz
        encoding: utf-8
  linkedit_data_command:
    -webide-representation: 'offs={data_off}, size={data_size}'
    seq:
      - id: data_off
        -orig-id: dataoff
        type: u4
      - id: data_size
        -orig-id: datasize
        type: u4
  code_signature_command:
    -webide-representation: 'offs={data_off}, size={data_size}'
    seq:
      - id: data_off
        type: u4
      - id: data_size
        type: u4
    instances:
      code_signature:
        io: _root._io
        pos: data_off
        size: data_size
        type: cs_blob
  cs_blob:
    seq:
      - id: magic
        type: u4be
        enum: cs_magic
      - id: length
        type: u4be
      - id: body
        size: length - 8
        type:
          switch-on: magic
          cases:
            'cs_magic::requirement'       : requirement
            'cs_magic::requirements'      : requirements
            'cs_magic::code_directory'    : code_directory
            'cs_magic::entitlements'      : entitlements
            'cs_magic::der_entitlements'  : asn1_der
            'cs_magic::blob_wrapper'      : blob_wrapper
            'cs_magic::embedded_signature': super_blob
            'cs_magic::detached_signature': super_blob
    enums:
      cs_magic:
        0xfade0c00: requirement        # CSMAGIC_REQUIREMENT
        0xfade0c01: requirements       # CSMAGIC_REQUIREMENTS
        0xfade0c02: code_directory     # CSMAGIC_CODEDIRECTORY
        0xfade7171: entitlements       # CSMAGIC_EMBEDDED_ENTITLEMENTS
        0xfade7172: der_entitlements   # CSMAGIC_EMBEDDED_DER_ENTITLEMENTS
        0xfade0b01: blob_wrapper       # CSMAGIC_BLOBWRAPPER
        0xfade0cc0: embedded_signature # CSMAGIC_EMBEDDED_SIGNATURE
        0xfade0cc1: detached_signature # CSMAGIC_DETACHED_SIGNATURE
    types:
      code_directory:
        seq:
          - id: version
            type: u4be
          - id: flags
            type: u4be
          - id: hash_offset
            type: u4be
          - id: ident_offset
            type: u4be
          - id: n_special_slots
            type: u4be
          - id: n_code_slots
            type: u4be
          - id: code_limit
            type: u4be
          - id: hash_size
            type: u1
          - id: hash_type
            type: u1
          - id: spare1
            type: u1
          - id: page_size
            type: u1
          - id: spare2
            type: u4be
          - id: scatter_offset
            type: u4be
            if: version >= 0x20100
          - id: team_id_offset
            type: u4be
            if: version >= 0x20200
        instances:
          ident:
            pos: ident_offset - 8
            type: strz
            encoding: utf-8
            -webide-parse-mode: eager
          team_id:
            pos: team_id_offset - 8
            type: strz
            encoding: utf-8
            -webide-parse-mode: eager
          hashes:
            pos: hash_offset - 8 - hash_size * n_special_slots
            size: hash_size
            repeat: expr
            repeat-expr: n_special_slots + n_code_slots
      blob_index:
        seq:
          - id: type
            type: u4be
            enum: csslot_type
          - id: offset
            type: u4be
        instances:
          blob:
            io: _parent._io
            pos: offset - 8
            size-eos: true
            type: cs_blob
        enums:
          csslot_type:
            0:       code_directory             # CSSLOT_CODEDIRECTORY
            1:       info_slot                  # CSSLOT_INFOSLOT
            2:       requirements               # CSSLOT_REQUIREMENTS
            3:       resource_dir               # CSSLOT_RESOURCEDIR
            4:       application                # CSSLOT_APPLICATION
            5:       entitlements               # CSSLOT_ENTITLEMENTS
            7:       der_entitlements           # CSSLOT_DER_ENTITLEMENTS
            0x1000:  alternate_code_directories # CSSLOT_ALTERNATE_CODEDIRECTORIES
            0x10000: signature_slot             # CSSLOT_SIGNATURESLOT
      data:
        -webide-representation: "{value}"
        seq:
          - id: length
            type: u4be
          - id: value
            size: length
          - id: padding
            size: -length % 4
      match:
        -webide-representation: "{match_op} {data.value:str}"
        seq:
          - id: match_op
            type: u4be
            enum: op
          - id: data
            type: data
            if: 'match_op != op::exists'
        enums:
          op:
            0: exists
            1: equal
            2: contains
            3: begins_with
            4: ends_with
            5: less_than
            6: greater_than
            7: less_equal
            8: greater_equal
      expr:
        -webide-representation: '{data}'
        seq:
          - id: op
            type: u4be
            enum: op_enum
          - id: data
            type:
              switch-on: op
              cases:
                #'op_enum::false'               : 'false'
                #'op_enum::true'                : 'true'
                'op_enum::ident'               : ident_expr
                #'op_enum::apple_anchor'        : 'anchor apple'
                'op_enum::anchor_hash'         : anchor_hash_expr
                'op_enum::info_key_value'      : data
                'op_enum::and_op'              : and_expr
                'op_enum::or_op'               : or_expr
                'op_enum::cd_hash'             : data
                'op_enum::not_op'              : expr
                'op_enum::info_key_field'      : info_key_field_expr
                'op_enum::cert_field'          : cert_field_expr
                'op_enum::trusted_cert'        : cert_slot_expr
                #'op_enum::trusted_certs'       : 'anchor trusted'
                'op_enum::cert_generic'        : cert_generic_expr
                'op_enum::apple_generic_anchor': apple_generic_anchor_expr
                'op_enum::entitlement_field'   : entitlement_field_expr
        enums:
          op_enum:
            0: 'false'               # unconditionally false
            1: 'true'                # unconditionally true
            2: ident                 # match canonical code [string]
            3: apple_anchor          # signed by Apple as Apple's product ("anchor apple")
            4: anchor_hash           # match anchor [cert hash]
            5: info_key_value        # *legacy* - use opInfoKeyField [key; value]
            6: and_op                # binary prefix expr AND expr [expr; expr]
            7: or_op                 # binary prefix expr OR expr
            8: cd_hash               # match hash of CodeDirectory directly
            9: not_op                # logical inverse
            10: info_key_field       # Info.plist key field [string; match suffix]
            11: cert_field           # Certificate field [cert index; field name; match suffix]
            12: trusted_cert         # require trust settings to approve one particular cert [cert index]
            13: trusted_certs        # require trust settings to approve the cert chain
            14: cert_generic         # Certificate component by OID [cert index; oid; match suffix]
            15: apple_generic_anchor # signed by Apple in any capacity ("anchor apple generic")
            16: entitlement_field    # entitlement dictionary field [string; match suffix]
          cert_slot:
            0xffffffff: anchor_cert
            0: left_cert
        types:
          ident_expr:
            -webide-representation: "identifier {identifier.value:str}"
            seq:
              - id: identifier
                type: data
          apple_generic_anchor_expr:
            -webide-representation: "anchor apple generic"
            instances:
              value:
                value: '"anchor apple generic"'
          cert_slot_expr:
            seq:
              - id: value
                type: u4be
                enum: cert_slot
          and_expr:
            -webide-representation: "({left}) AND ({right})"
            seq:
              - id: left
                type: expr
              - id: right
                type: expr
          or_expr:
            -webide-representation: "({left}) OR ({right})"
            seq:
              - id: left
                type: expr
              - id: right
                type: expr
          anchor_hash_expr:
            seq:
              - id: cert_slot
                type: u4be
                enum: cert_slot
              - id: data
                type: data
          info_key_field_expr:
            seq:
              - id: data
                type: data
              - id: match
                type: match
          entitlement_field_expr:
            seq:
              - id: data
                type: data
              - id: match
                type: match
          cert_field_expr:
            -webide-representation: "{cert_slot}[{data.value:str}] {match}"
            seq:
              - id: cert_slot
                type: u4be
                enum: cert_slot
              - id: data
                type: data
              - id: match
                type: match
          cert_generic_expr:
            -webide-representation: "{cert_slot}[{data.value:hex}] {match}"
            seq:
              - id: cert_slot
                type: u4be
                enum: cert_slot
              - id: data
                type: data
              - id: match
                type: match
      requirement:
        seq:
          - id: kind
            type: u4be
          - id: expr
            type: expr
      entitlements:
        -webide-representation: "{data:str}"
        seq:
          - id: data
            size-eos: true
      requirements_blob_index:
        seq:
          - id: type
            type: u4be
            enum: requirement_type
          - id: offset
            type: u4be
        instances:
          value:
            pos: offset - 8
            type: cs_blob
        enums:
          requirement_type:
            1: host        # kSecHostRequirementType
            2: guest       # kSecGuestRequirementType
            3: designated  # kSecDesignatedRequirementtype
            4: library     # kSecLibraryRequirementType
      requirements:
        seq:
          - id: count
            type: u4be
          - id: items
            type: requirements_blob_index
            repeat: expr
            repeat-expr: count
      blob_wrapper:
        seq:
          - id: data
            size-eos: true
      super_blob:
        seq:
          - id: count
            type: u4be
          - id: blobs
            type: blob_index
            repeat: expr
            repeat-expr: count
  build_version_command:
    seq:
      - id: platform
        type: u4
      - id: minos
        type: u4
      - id: sdk
        type: u4
      - id: ntools
        type: u4
      - id: tools
        type: build_tool_version
        repeat: expr
        repeat-expr: ntools
    types:
      build_tool_version:
        seq:
          - id: tool
            type: u4
          - id: version
            type: u4
  segment_command:
    seq:
      - id: segname
        type: str
        size: 16
        pad-right: 0
        encoding: ascii
      - id: vmaddr
        type: u4
      - id: vmsize
        type: u4
      - id: fileoff
        type: u4
      - id: filesize
        type: u4
      - id: maxprot
        type: vm_prot
      - id: initprot
        type: vm_prot
      - id: nsects
        type: u4
      - id: flags
        type: u4
      - id: sections
        type: section
        repeat: expr
        repeat-expr: nsects
    types:
      section:
        seq:
          - id: sect_name
            -orig-id: sectname
            size: 16
            type: str
            pad-right: 0
            encoding: ascii
          - id: seg_name
            -orig-id: segname
            size: 16
            type: str
            pad-right: 0
            encoding: ascii
          - id: addr
            type: u4
          - id: size
            type: u4
          - id: offset
            type: u4
          - id: align
            type: u4
          - id: reloff
            type: u4
          - id: nreloc
            type: u4
          - id: flags
            type: u4
          - id: reserved1
            type: u4
          - id: reserved2
            type: u4
        instances:
          data:
            io: _root._io
            pos: offset
            size: size