CPU_TYPE_ANY = -1 CPU_ARCH_ABI64 = 0x1000000 CPU_TYPE_VAX = 1 CPU_TYPE_ROMP = 2 CPU_TYPE_NS32032 = 4 CPU_TYPE_NS32332 = 5 CPU_TYPE_MC680x0 = 6 CPU_TYPE_I386 = 7 CPU_TYPE_X86_64 = CPU_TYPE_I386 or CPU_ARCH_ABI64 CPU_TYPE_MIPS = 8 CPU_TYPE_NS32532 = 9 CPU_TYPE_HPPA = 11 CPU_TYPE_ARM = 12 CPU_TYPE_MC88000 = 13 CPU_TYPE_SPARC = 14 CPU_TYPE_I860 = 15 CPU_TYPE_I860_LITTLE = 16 CPU_TYPE_RS6000 = 17 CPU_TYPE_MC98000 = 18 CPU_TYPE_POWERPC = 18 CPU_TYPE_POWERPC64 = CPU_TYPE_POWERPC or CPU_ARCH_ABI64 CPU_TYPE_VEO = 255 CPU_SUBTYPE_MASK = 0xff000000 CPU_SUBTYPE_LIB64 = 0x80000000 CPU_SUBTYPE_I386_ALL = 3 CPU_SUBTYPE_X86_64_ALL = CPU_SUBTYPE_I386_ALL CPU_SUBTYPE_386 = 3 CPU_SUBTYPE_486 = 4 CPU_SUBTYPE_486SX = 4 + 128 CPU_SUBTYPE_586 = 5 CPU_SUBTYPE_PENT = 5 + 0 shl 4 CPU_SUBTYPE_PENTPRO = 6 + 1 shl 4 CPU_SUBTYPE_PENTII_M3 = 6 + 3 shl 4 CPU_SUBTYPE_PENTII_M5 = 6 + 5 shl 4 CPU_SUBTYPE_PENTIUM_4 = 10 + 0 shl 4 MH_OBJECT = 0x1 MH_EXECUTE = 0x2 MH_FVMLIB = 0x3 MH_CORE = 0x4 MH_PRELOAD = 0x5 MH_DYLIB = 0x6 MH_DYLINKER = 0x7 MH_BUNDLE = 0x8 MH_DYLIB_STUB = 0x9 MH_DSYM = 0xA MH_KEXT_BUNDLE = 0xB MH_NOUNDEFS = 0x1 MH_INCRLINK = 0x2 MH_DYLDLINK = 0x4 MH_BINDATLOAD = 0x8 MH_PREBOUND = 0x10 MH_SPLIT_SEGS = 0x20 MH_LAZY_INIT = 0x40 MH_TWOLEVEL = 0x80 MH_FORCE_FLAT = 0x100 MH_NOMULTIDEFS = 0x200 MH_NOFIXPREBINDING = 0x400 MH_PREBINDABLE = 0x800 MH_ALLMODSBOUND = 0x1000 MH_SUBSECTIONS_VIA_SYMBOLS = 0x2000 MH_CANONICAL = 0x4000 MH_WEAK_DEFINES = 0x8000 MH_BINDS_TO_WEAK = 0x10000 MH_ALLOW_STACK_EXECUTION = 0x20000 MH_ROOT_SAFE = 0x40000 MH_SETUID_SAFE = 0x80000 MH_NO_REEXPORTED_DYLIBS = 0x100000 MH_PIE = 0x200000 MH_DEAD_STRIPPABLE_DYLIB = 0x400000 MH_HAS_TLV_DESCRIPTORS = 0x800000 MH_NO_HEAP_EXECUTION = 0x1000000 MH_APP_EXTENSION_SAFE = 0x2000000 LC_REQ_DYLD = 0x80000000 LC_SEGMENT = 0x1 LC_SYMTAB = 0x2 LC_SYMSEG = 0x3 LC_THREAD = 0x4 LC_UNIXTHREAD = 0x5 LC_LOADFVMLIB = 0x6 LC_IDFVMLIB = 0x7 LC_IDENT = 0x8 LC_FVMFILE = 0x9 LC_PREPAGE = 0xa LC_DYSYMTAB = 0xb LC_LOAD_DYLIB = 0xc LC_ID_DYLIB = 0xd LC_LOAD_DYLINKER = 0xe LC_ID_DYLINKER = 0xf LC_PREBOUND_DYLIB = 0x10 LC_ROUTINES = 0x11 LC_SUB_FRAMEWORK = 0x12 LC_SUB_UMBRELLA = 0x13 LC_SUB_CLIENT = 0x14 LC_SUB_LIBRARY = 0x15 LC_TWOLEVEL_HINTS = 0x16 LC_PREBIND_CKSUM = 0x17 LC_LOAD_WEAK_DYLIB = 0x18 LC_SEGMENT_64 = 0x19 LC_ROUTINES_64 = 0x1a LC_UUID = 0x1b LC_RPATH = 0x1c + LC_REQ_DYLD LC_CODE_SIGNATURE = 0x1d LC_SEGMENT_SPLIT_INFO = 0x1e LC_REEXPORT_DYLIB = 0x1f + LC_REQ_DYLD LC_LAZY_LOAD_DYLIB = 0x20 LC_ENCRYPTION_INFO = 0x21 LC_DYLD_INFO = 0x22 LC_DYLD_INFO_ONLY = 0x22 + LC_REQ_DYLD SG_HIGHVM = 0x1 SG_FVMLIB = 0x2 SG_NORELOC = 0x4 SECTION_TYPE = 0x000000ff SECTION_ATTRIBUTES = 0xffffff00 S_REGULAR = 0x0 S_ZEROFILL = 0x1 S_CSTRING_LITERALS = 0x2 S_4BYTE_LITERALS = 0x3 S_8BYTE_LITERALS = 0x4 S_LITERAL_POINTERS = 0x5 S_NON_LAZY_SYMBOL_POINTERS = 0x6 S_LAZY_SYMBOL_POINTERS = 0x7 S_SYMBOL_STUBS = 0x8 S_MOD_INIT_FUNC_POINTERS = 0x9 S_MOD_TERM_FUNC_POINTERS = 0x0a S_COALESCED = 0x0b S_GB_ZEROFILL = 0x0c S_INTERPOSING = 0x0d S_16BYTE_LITERALS = 0x0e S_DTRACE_DOF = 0x0f S_LAZY_DYLIB_SYMBOL_POINTERS = 0x10 S_THREAD_LOCAL_REGULAR = 0x11 S_THREAD_LOCAL_ZEROFILL = 0x12 S_THREAD_LOCAL_VARIABLES = 0x13 S_THREAD_LOCAL_VARIABLE_POINTERS = 0x14 S_THREAD_LOCAL_INIT_FUNCTION_POINTERS = 0x15 SECTION_ATTRIBUTES_USR = 0xff000000 S_ATTR_PURE_INSTRUCTIONS = 0x80000000 S_ATTR_NO_TOC = 0x40000000 S_ATTR_STRIP_STATIC_SYMS = 0x20000000 S_ATTR_NO_DEAD_STRIP = 0x10000000 S_ATTR_LIVE_SUPPORT = 0x08000000 S_ATTR_SELF_MODIFYING_CODE = 0x04000000 S_ATTR_DEBUG = 0x02000000 SECTION_ATTRIBUTES_SYS = 0x00ffff00 S_ATTR_SOME_INSTRUCTIONS = 0x00000400 S_ATTR_EXT_RELOC = 0x00000200 S_ATTR_LOC_RELOC = 0x00000100 VM_PROT_NONE = 0x00 VM_PROT_READ = 0x01 VM_PROT_WRITE = 0x02 VM_PROT_EXECUTE = 0x04 VM_PROT_DEFAULT = VM_PROT_READ or VM_PROT_WRITE VM_PROT_ALL = VM_PROT_READ or VM_PROT_WRITE or VM_PROT_EXECUTE VM_PROT_NO_CHANGE = 0x08 VM_PROT_COPY = 0x10 VM_PROT_WANTS_COPY = 0x10 x86_THREAD_STATE32 = 1 x86_FLOAT_STATE32 = 2 x86_EXCEPTION_STATE32 = 3 x86_THREAD_STATE64 = 4 x86_FLOAT_STATE64 = 5 x86_EXCEPTION_STATE64 = 6 x86_THREAD_STATE = 7 x86_FLOAT_STATE = 8 x86_EXCEPTION_STATE = 9 x86_DEBUG_STATE32 = 10 x86_DEBUG_STATE64 = 11 x86_DEBUG_STATE = 12 THREAD_STATE_NONE = 13 N_STAB = 0xe0 N_PEXT = 0x10 N_TYPE = 0x0e N_EXT = 0x01 N_UNDF = 0x0 N_ABS = 0x2 N_SECT = 0xe N_PBUD = 0xc N_INDR = 0xa NO_SECT = 0 MAX_SECT = 255 REFERENCE_TYPE = 0xf REFERENCE_FLAG_UNDEFINED_NON_LAZY = 0 REFERENCE_FLAG_UNDEFINED_LAZY = 1 REFERENCE_FLAG_DEFINED = 2 REFERENCE_FLAG_PRIVATE_DEFINED = 3 REFERENCE_FLAG_PRIVATE_UNDEFINED_NON_LAZY = 4 REFERENCE_FLAG_PRIVATE_UNDEFINED_LAZY = 5 REFERENCED_DYNAMICALLY = 0x0010 GENERIC_RELOC_VANILLA = 0 GENERIC_RELOC_PAIR = 1 GENERIC_RELOC_SECTDIFF = 2 GENERIC_RELOC_PB_LA_PTR = 3 GENERIC_RELOC_LOCAL_SECTDIFF = 4 GENERIC_RELOC_TLV = 5 X86_64_RELOC_UNSIGNED = 0 X86_64_RELOC_SIGNED = 1 X86_64_RELOC_BRANCH = 2 X86_64_RELOC_GOT_LOAD = 3 X86_64_RELOC_GOT = 4 X86_64_RELOC_SUBTRACTOR = 5 X86_64_RELOC_SIGNED_1 = 6 X86_64_RELOC_SIGNED_2 = 7 X86_64_RELOC_SIGNED_4 = 8 X86_64_RELOC_TLV = 9 ; Basic layer: command headers MachO:: namespace MachO if defined Settings.ProcessorType CPUTYPE := Settings.ProcessorType else CPUTYPE := CPU_TYPE_I386 end if if defined Settings.ProcessorSubtype CPUSUBTYPE := Settings.ProcessorSubtype else if CPUTYPE and CPU_ARCH_ABI64 CPUSUBTYPE := CPU_SUBTYPE_I386_ALL + CPU_SUBTYPE_LIB64 else CPUSUBTYPE := CPU_SUBTYPE_I386_ALL end if end if if defined Settings.FileType FILETYPE := Settings.FileType else FILETYPE := MH_EXECUTE end if if defined Settings.Flags FLAGS := Settings.Flags else if FILETYPE <> MH_OBJECT FLAGS := MH_NOUNDEFS + MH_DYLDLINK else FLAGS := 0 end if if defined Settings.SegmentAlignment SEGMENT_ALIGNMENT := Settings.SegmentAlignment else SEGMENT_ALIGNMENT := 1000h end if if defined Settings.FileAlignment FILE_ALIGNMENT := Settings.FileAlignment else FILE_ALIGNMENT := 1000h end if if CPUTYPE and CPU_ARCH_ABI64 magic dd 0xFEEDFACF else magic dd 0xFEEDFACE end if cputype dd CPUTYPE cpusubtype dd CPUSUBTYPE filetype dd FILETYPE ncmds dd NUMBER_OF_COMMANDS sizeofcmds dd SIZE_OF_COMMANDS flags dd FLAGS if CPUTYPE and CPU_ARCH_ABI64 reserved dd ? end if COMMAND_NUMBER = 0 COMMAND_POSITION = $ commands db SIZE_OF_COMMANDS dup 0 org $ macro command type if MachO.COMMAND_POSITION > 0 virtual at MachO.COMMAND_POSITION MachO.COMMAND_POSITION = 0 end if match t, type if MachO.COMMAND_NUMBER > 0 repeat 1, p:MachO.COMMAND_NUMBER MachO.load#p.SIZE := $ - MachO.load#p.cmd end repeat end if MachO.COMMAND_NUMBER = MachO.COMMAND_NUMBER + 1 MachO.COMMAND = $ repeat 1, n:MachO.COMMAND_NUMBER namespace MachO.load#n cmd dd t cmdsize dd SIZE end namespace end repeat end match end macro macro text if MachO.COMMAND_POSITION = 0 MachO.COMMAND_POSITION = $ load MachO.COMMAND_DATA:$-$$ from $$ store MachO.COMMAND_DATA:$-$$ at MachO:$$ end virtual end if end macro end namespace macro align? boundary,value:? db (boundary-1)-($+boundary-1) mod boundary dup value end macro postpone MachO.text namespace MachO if COMMAND_NUMBER > 0 repeat 1, p:COMMAND_NUMBER load#p.SIZE := COMMAND_POSITION - MachO.load#p.cmd end repeat end if NUMBER_OF_COMMANDS := COMMAND_NUMBER SIZE_OF_COMMANDS := COMMAND_POSITION - commands end namespace end postpone ; Intermediate layer: segments and sections namespace MachO if defined Settings.BaseAddress VM_ADDRESS = Settings.BaseAddress if CPUTYPE and CPU_ARCH_ABI64 MachO.command LC_SEGMENT_64 else MachO.command LC_SEGMENT end if namespace MachO.segment1 segname emit 16: '__PAGEZERO' if CPUTYPE and CPU_ARCH_ABI64 vmaddr dq 0 vmsize dq VM_ADDRESS fileoff dq 0 filesize dq 0 else vmaddr dd 0 vmsize dd VM_ADDRESS fileoff dd 0 filesize dd 0 end if maxprot dd 0 initprot dd 0 nsects dd 0 flags dd 0 end namespace SEGMENT_NUMBER = 1 else VM_ADDRESS = 0 SEGMENT_NUMBER = 0 end if GLOBAL_SECTION_NUMBER = 0 TEXT_OFFSET = $% end namespace macro MachO.segment declaration MachO.close_segment local NAME,VMADDR,VMSIZE,FILEOFF,FILESIZE,MAXPROT,INITPROT,NSECTS INITPROT = VM_PROT_NONE MAXPROT = VM_PROT_ALL match name attributes, declaration NAME = name local sequence define sequence attributes: while 1 match attribute tail, sequence match =readable?, attribute INITPROT = INITPROT or VM_PROT_READ MAXPROT = MAXPROT or VM_PROT_READ else match =notreadable?, attribute INITPROT = INITPROT and not VM_PROT_READ MAXPROT = MAXPROT and not VM_PROT_READ else match =writeable?, attribute INITPROT = INITPROT or VM_PROT_WRITE MAXPROT = MAXPROT or VM_PROT_WRITE else match =writable?, attribute INITPROT = INITPROT or VM_PROT_WRITE MAXPROT = MAXPROT or VM_PROT_WRITE else match =notwriteable?, attribute INITPROT = INITPROT and not VM_PROT_WRITE MAXPROT = MAXPROT and not VM_PROT_WRITE else match =notwritable?, attribute INITPROT = INITPROT and not VM_PROT_WRITE MAXPROT = MAXPROT and not VM_PROT_WRITE else match =executable?, attribute INITPROT = INITPROT or VM_PROT_EXECUTE MAXPROT = MAXPROT or VM_PROT_EXECUTE else match =notexecutable?, attribute INITPROT = INITPROT and not VM_PROT_EXECUTE MAXPROT = MAXPROT and not VM_PROT_EXECUTE else err 'invalid argument' end match redefine sequence tail else break end match end while else NAME = definition end match if NAME eqtype '' & lengthof NAME > 16 err 'name too long' end if VMADDR := MachO.VM_ADDRESS if $% = MachO.TEXT_OFFSET & FILESIZE > 0 & MachO.FILETYPE <> MH_OBJECT MachO.FILE_OFFSET = 0 else MachO.FILE_OFFSET = $% end if if FILESIZE FILEOFF := MachO.FILE_OFFSET else FILEOFF := 0 end if MachO.SEGMENT_NUMBER = MachO.SEGMENT_NUMBER + 1 if MachO.CPUTYPE and CPU_ARCH_ABI64 MachO.command LC_SEGMENT_64 else MachO.command LC_SEGMENT end if repeat 1, s:MachO.SEGMENT_NUMBER namespace MachO.segment#s segname emit 16: NAME if MachO.CPUTYPE and CPU_ARCH_ABI64 vmaddr dq VMADDR vmsize dq VMSIZE fileoff dq FILEOFF filesize dq FILESIZE else vmaddr dd VMADDR vmsize dd VMSIZE fileoff dd FILEOFF filesize dd FILESIZE end if maxprot dd MAXPROT initprot dd INITPROT nsects dd NSECTS flags dd 0 end namespace repeat NSECTS namespace MachO.segment#s#_section#% sectname emit 16: ? segname emit 16: ? if MachO.CPUTYPE and CPU_ARCH_ABI64 addr dq ? size dq ? else addr dd ? size dd ? end if offset dd ? align. dd ? reloff dd ? nreloc dd ? flags dd ? reserved1 dd ? reserved2 dd ? if MachO.CPUTYPE and CPU_ARCH_ABI64 dd ? end if end namespace end repeat end repeat macro MachO.close_segment MachO.close_section local OFFSET,NEXT,TOP,FILL TOP = $% NSECTS := MachO.SECTION_NUMBER if MachO.FILE_OFFSET >= $%% FILL = MachO.FILE_OFFSET else FILL = $%% end if repeat 1, s:MachO.SEGMENT_NUMBER repeat NSECTS, n:2 if % = 1 load OFFSET from MachO:MachO.segment#s#_section#%.offset else OFFSET = NEXT end if if % < %% load NEXT from MachO:MachO.segment#s#_section#n.offset else NEXT = TOP end if if OFFSET >= $%% store S_ZEROFILL at MachO:MachO.segment#s#_section#%.flags store 0 at MachO:MachO.segment#s#_section#%.size else FILL = NEXT end if end repeat end repeat FILESIZE := FILL - MachO.FILE_OFFSET FILL = FILL - $%% MachO.SECTION_BYPASS = 1 section MachO.VM_ADDRESS + FILESIZE - FILL restore MachO.SECTION_BYPASS db FILL dup 0 if FILESIZE rb (MachO.FILE_ALIGNMENT-$%) and (MachO.FILE_ALIGNMENT-1) end if MachO.VM_ADDRESS = MachO.VM_ADDRESS + TOP - MachO.FILE_OFFSET MachO.VM_ADDRESS = MachO.VM_ADDRESS + (MachO.SEGMENT_ALIGNMENT-MachO.VM_ADDRESS) and (MachO.SEGMENT_ALIGNMENT-1) VMSIZE := MachO.VM_ADDRESS - VMADDR purge MachO.close_segment end macro MachO.text org VMADDR + $% - MachO.FILE_OFFSET MachO.SECTION_NUMBER = 0 end macro macro MachO.section declaration if MachO.SEGMENT_NUMBER = 0 MachO.segment '__TEXT' readable writable executable end if MachO.close_section local SECTNAME,SEGNAME,FLAGS,ALIGN,RESERVED1,RESERVED2 FLAGS = S_REGULAR ALIGN = 0 RESERVED1 = 0 RESERVED2 = 0 local sequence match segname:sectname tail, declaration: SECTNAME = sectname SEGNAME = segname define sequence tail else match name tail, declaration: SECTNAME = name repeat 1, s:MachO.SEGMENT_NUMBER load SEGNAME from MachO:MachO.segment#s.segname end repeat define sequence tail end match while 1 match :, sequence break else match =align? boundary tail, sequence ALIGN = bsf boundary if bsf boundary <> bsr boundary err 'invalid alignment value' end if redefine sequence tail else match =flags?(value) tail, sequence FLAGS = value redefine sequence tail else match =reserved1?(value) tail, sequence RESERVED1 = value redefine sequence tail else match =reserved2?(value) tail, sequence RESERVED2 = value redefine sequence tail else err 'invalid arguments' end match end while MachO.text if ALIGN align 1 shl ALIGN end if MachO.SECTION_ALIGN = 1 shl ALIGN MachO.SECTION_NUMBER = MachO.SECTION_NUMBER + 1 MachO.GLOBAL_SECTION_NUMBER = MachO.GLOBAL_SECTION_NUMBER + 1 repeat 1, s:MachO.SEGMENT_NUMBER, t:MachO.SECTION_NUMBER if ~ defined MachO.segment#s#_section#t.sectname MachO.command namespace MachO.segment#s#_section#t sectname emit 16: ? segname emit 16: ? if MachO.CPUTYPE and CPU_ARCH_ABI64 addr dq ? size dq ? else addr dd ? size dd ? end if offset dd ? align. dd ? reloff dd ? nreloc dd ? flags dd ? reserved1 dd ? reserved2 dd ? if MachO.CPUTYPE and CPU_ARCH_ABI64 dd ? end if end namespace MachO.text end if store SECTNAME at MachO:MachO.segment#s#_section#t.sectname store SEGNAME at MachO:MachO.segment#s#_section#t.segname store $ at MachO:MachO.segment#s#_section#t.addr store $% at MachO:MachO.segment#s#_section#t.offset store FLAGS at MachO:MachO.segment#s#_section#t.flags store ALIGN at MachO:MachO.segment#s#_section#t.align store RESERVED1 at MachO:MachO.segment#s#_section#t.reserved1 store RESERVED2 at MachO:MachO.segment#s#_section#t.reserved2 end repeat end macro macro MachO.close_section MachO.text if MachO.SECTION_NUMBER local SIZE repeat 1, s:MachO.SEGMENT_NUMBER, t:MachO.SECTION_NUMBER load OFFSET from MachO:MachO.segment#s#_section#t.offset store $%-OFFSET at MachO:MachO.segment#s#_section#t.size end repeat end if end macro macro MachO.close_segment end macro postpone MachO.close_segment if MachO.FILETYPE <> MH_OBJECT & $%% < 1000h store 0:byte at $-1 ; enforce minimum file size for OS X 10.10.5 and higher end if end postpone macro segment? args& MachO.segment args end macro macro section? args& if defined MachO.SECTION_BYPASS section args else MachO.section args end if end macro macro entry? regs& iterate reg, regs match name:value, reg MachO.thread.name? := value else if MachO.CPUTYPE = CPU_TYPE_I386 MachO.thread.eip? := reg else if MachO.CPUTYPE = CPU_TYPE_X86_64 MachO.thread.rip? := reg end if end iterate MachO.command LC_UNIXTHREAD if MachO.CPUTYPE = CPU_TYPE_I386 MachO.thread.flavor dd x86_THREAD_STATE32 iterate name, eax,ebx,ecx,edx,edi,esi,ebp,esp,ss,eflags,eip,cs,ds,es,fs,gs if % = 1 MachO.thread.count dd %% end if if defined MachO.thread.name? dd MachO.thread.name? else dd ? end if end iterate else if MachO.CPUTYPE = CPU_TYPE_X86_64 MachO.thread.flavor dd x86_THREAD_STATE64 iterate name, rax,rbx,rcx,rdx,rdi,rsi,rbp,rsp,r8,r9,r10,r11,r12,r13,r14,r15,rip,rflags,cs,fs,gs if % = 1 MachO.thread.count dd %%*2 end if if defined MachO.thread.name? dq MachO.thread.name? else dq ? end if end iterate else err 'CPU not supported' end if MachO.text end macro ; Upper layer: symbol tables define macroBuilder? macro macroBuilder? declaration& macro macroBuilder?.definition esc macro declaration end macro end macro macro macroBuilder?.line? content& macro macroBuilder?.definition macroBuilder?.definition content end macro end macro macro macroBuilder?.end? macroBuilder?.definition esc end macro end macro if MachO.FILETYPE <> MH_OBJECT namespace MachO NSYMS = 0 LIB_NUMBER = 0 end namespace macro interpreter? path MachO.command LC_LOAD_DYLINKER namespace MachO.dylinker_command lc_str dd dylinker-MachO.COMMAND dylinker db path,0 if MachO.CPUTYPE and CPU_ARCH_ABI64 align 8 else align 4 end if end namespace end macro macro uses? lib& MachO.LIB_NUMBER = MachO.LIB_NUMBER + 1 MachO.command LC_LOAD_DYLIB repeat 1, l:MachO.LIB_NUMBER namespace MachO.dylib#l#_command lc_str dd dylib-MachO.COMMAND timestamp dd 2 match path (a.b.c=,x.y.z), lib current_version dd (x and 0FFFFh) shl 16 + y shl 8 + z compatibility_version dd (a and 0FFFFh) shl 16 + b shl 8 + c dylib db path,0 else current_version dd 10000h compatibility_version dd 10000h dylib db lib,0 end match if MachO.CPUTYPE and CPU_ARCH_ABI64 align 8 else align 4 end if end namespace end repeat end macro macro import? definitions& iterate , definitions MachO.NSYMS = MachO.NSYMS + 1 define MachO.nlist name name.imported := 1 name.type := N_EXT name.desc := REFERENCE_FLAG_UNDEFINED_NON_LAZY define name.str string end iterate end macro MachO.__TEXT = 0 MachO.__DATA = 0 macro segment? args& MachO.segment args if MachO.NSYMS repeat 1, s:MachO.SEGMENT_NUMBER load MachO.SEGNAME from MachO:MachO.segment#s.segname end repeat if ~MachO.__TEXT & MachO.SEGNAME = '__TEXT' MachO.__TEXT = 1 MachO.__stubs else if ~MachO.__DATA & MachO.SEGNAME = '__DATA' MachO.__DATA = 1 MachO.__nl_symbol_ptr end if end if end macro postpone if MachO.NSYMS macroBuilder MachO.__stubs macroBuilder.line section '__stubs' flags(S_SYMBOL_STUBS+S_ATTR_SOME_INSTRUCTIONS+S_ATTR_PURE_INSTRUCTIONS) reserved1(0) reserved2(MachO.JUMP_SIZE) align 16 irpv sym, MachO.nlist if sym.imported macroBuilder.line sym: jmp [sym.ptr] if % = 1 macroBuilder.line MachO.JUMP_SIZE := $ - sym end if end if end irpv macroBuilder.end macroBuilder MachO.__nl_symbol_ptr if MachO.CPUTYPE and CPU_ARCH_ABI64 macroBuilder.line section '__nl_symbol_ptr' flags(S_NON_LAZY_SYMBOL_POINTERS) reserved1(0) align 8 else macroBuilder.line section '__nl_symbol_ptr' flags(S_NON_LAZY_SYMBOL_POINTERS) reserved1(0) align 4 end if irpv sym, MachO.nlist if sym.imported if MachO.CPUTYPE and CPU_ARCH_ABI64 macroBuilder.line sym.ptr dq 0 else macroBuilder.line sym.ptr dd 0 end if end if end irpv macroBuilder.end if ~MachO.__TEXT segment '__TEXT' readable executable end if if ~MachO.__DATA segment '__DATA' readable writable end if segment '__LINKEDIT' readable MachO.SYMOFF := $% irpv sym, MachO.nlist namespace MachO.nlist#% n_strx dd sym.strx n_type db sym.type n_sect db 0 n_desc dw sym.desc if MachO.CPUTYPE and CPU_ARCH_ABI64 n_value dq sym else n_value dd sym end if end namespace end irpv MachO.INDIRECTSYMOFF := $% irpv sym, MachO.nlist if sym.imported dd %-1 end if end irpv MachO.STROFF := $% db 20h,0 irpv sym, MachO.nlist sym.strx := $% - MachO.STROFF db string sym.str, 0 end irpv MachO.STRSIZE := $% - MachO.STROFF MachO.command LC_SYMTAB namespace MachO.symtab_command symoff dd SYMOFF nsyms dd NSYMS stroff dd STROFF strsize dd STRSIZE end namespace MachO.command LC_DYSYMTAB namespace MachO.dysymtab_command ilocalsym dd 0 nlocalsym dd 0 iextdefsym dd 0 nextdefsym dd 0 iundefsym dd 0 nundefsym dd NSYMS tocoff dd 0 ntoc dd 0 modtaboff dd 0 nmodtab dd 0 extrefsymoff dd 0 nextrefsyms dd 0 indirectsymoff dd INDIRECTSYMOFF nindirectsyms dd NSYMS extreloff dd 0 nextrel dd 0 locreloff dd 0 nlocrel dd 0 end namespace end if end postpone else namespace MachO element MachO.section? element MachO.symbol? VM_ADDRESS = 0 define nlist null null.value := 0 null.type := 0 null.sect := 0 null.desc := 0 null.str := '' NSYMS = 1 NRELOC = 0 end namespace segment 0 readable writable executable macro MachO.section declaration if MachO.SECTION_NUMBER org 0 scaleof (1 metadataof $) + 0 scaleof $ end if MachO.section declaration local sym element sym : MachO.GLOBAL_SECTION_NUMBER * MachO.section + $ org sym MachO.SECTION_OFFSET = $% MachO.SECTION_REL_INDEX = MachO.NRELOC repeat 1, s:MachO.SEGMENT_NUMBER, t:MachO.SECTION_NUMBER store MachO.RELOFF+MachO.SECTION_REL_INDEX*8 at MachO:MachO.segment#s#_section#t.reloff end repeat end macro macro MachO.close_section MachO.close_section if MachO.SECTION_NUMBER repeat 1, s:MachO.SEGMENT_NUMBER, t:MachO.SECTION_NUMBER store MachO.NRELOC-MachO.SECTION_REL_INDEX at MachO:MachO.segment#s#_section#t.nreloc end repeat end if end macro macro public? declaration* local name define MachO.nlist name match value =as? namestr, declaration name = value define name.str string namestr else name = declaration define name.str `declaration end match if name relativeto 1 elementof name & 1 elementof (1 metadataof name) eq MachO.section name.value := 0 scaleof (1 metadataof name) + 0 scaleof name name.type := N_SECT + N_EXT name.sect := 1 scaleof (1 metadataof name) else name.value := name name.type := N_ABS name.sect := 0 end if name.desc := REFERENCE_FLAG_DEFINED MachO.NSYMS = MachO.NSYMS + 1 end macro macro extrn? declaration* match namestr =as? sym, declaration define MachO.nlist sym define sym.str string namestr else define MachO.nlist declaration define declaration.str `declaration end match match sym, MachO.nlist element sym : MachO.NSYMS * MachO.symbol sym.type := N_EXT sym.sect := 0 sym.desc := 0 sym.value := 0 end match MachO.NSYMS = MachO.NSYMS + 1 end macro macro dword? value local v v = value if ~ v relativeto 0 & v relativeto 1 elementof v & (1 elementof (1 metadataof v) eq MachO.section | 1 elementof (1 metadataof v) eq MachO.symbol) MachO.reloc =: $% - MachO.SECTION_OFFSET + (1 scaleof (1 metadataof v)) shl 32 + 2 shl 57 + GENERIC_RELOC_VANILLA shl 60 if 1 elementof (1 metadataof v) eq MachO.symbol MachO.reloc = MachO.reloc + 1 shl 59 end if MachO.NRELOC = MachO.NRELOC + 1 emit dword: 0 scaleof (1 metadataof v) + 0 scaleof v else if ~ v relativeto 0 & (v + $) relativeto 1 elementof (v + $) & (1 elementof (1 metadataof (v + $)) eq MachO.section | 1 elementof (1 metadataof (v + $)) eq MachO.symbol) if MachO.CPUTYPE = CPU_TYPE_X86_64 if 1 elementof (1 metadataof (v + $)) eq MachO.symbol v = v + $ + 4 else v = v + 0 scaleof (1 metadataof v) - 0 scaleof (1 metadataof $) end if MachO.reloc =: $% - MachO.SECTION_OFFSET + (1 scaleof (1 metadataof (v + $))) shl 32 + 1 shl 56 + 2 shl 57 + X86_64_RELOC_SIGNED_4 shl 60 else v = v + 0 scaleof (1 metadataof v) - 0 scaleof (1 metadataof $) MachO.reloc =: $% - MachO.SECTION_OFFSET + (1 scaleof (1 metadataof (v + $))) shl 32 + 1 shl 56 + 2 shl 57 + GENERIC_RELOC_VANILLA shl 60 end if if 1 elementof (1 metadataof (v + $)) eq MachO.symbol MachO.reloc = MachO.reloc + 1 shl 59 end if MachO.NRELOC = MachO.NRELOC + 1 emit dword: 0 scaleof v else emit dword: v end if end macro postpone if MachO.NSYMS MachO.close_section macro MachO.close_section MachO.text end macro rb (-$%) and 11b MachO.RELOFF := $% irpv rel, MachO.reloc dq rel end irpv MachO.SYMOFF := $% irpv sym, MachO.nlist namespace MachO.nlist#% n_strx dd sym.strx n_type db sym.type n_sect db sym.sect n_desc dw sym.desc if MachO.CPUTYPE and CPU_ARCH_ABI64 n_value dq sym.value else n_value dd sym.value end if end namespace end irpv MachO.STROFF := $% irpv sym, MachO.nlist sym.strx := $% - MachO.STROFF db string sym.str, 0 end irpv MachO.STRSIZE := $% - MachO.STROFF MachO.command LC_SYMTAB namespace MachO.symtab_command symoff dd SYMOFF nsyms dd NSYMS stroff dd STROFF strsize dd STRSIZE end namespace end if end postpone macro align? boundary,value:? if $ relativeto 0 | MachO.SECTION_ALIGN mod (boundary) = 0 db (boundary-1)-(0 scaleof $+boundary-1) mod boundary dup value else err 'section not aligned enough' end if end macro macro dd? definitions& iterate value,definitions match ?, value dd ? else match n =dup? ?, value dd n dup ? else match n =dup? (?), value dd n dup ? else match n =dup? v, value repeat n dword v end repeat else dword value end match end iterate end macro struc dd? definitions& label . : dword iterate value,definitions match ?, value dd ? else match n =dup? ?, value dd n dup ? else match n =dup? (?), value dd n dup ? else match n =dup? v, value repeat n dword v end repeat else dword value end match end iterate end struc macro store? disposition match data =at? area:address, disposition store data at area:address else match data =at? address, disposition local value,length match val:len, data value = val length = len else value = data length = sizeof(address) end match if elementsof value $%? = $%?-($-address) virtual at address if length = 4 dword value load value:dword from address end if end virtual restore $%? end if store value:length at address else store disposition end match end macro end if