Skip to content

Instantly share code, notes, and snippets.

@samhenrigold
Last active July 15, 2026 23:23
Show Gist options
  • Select an option

  • Save samhenrigold/54fe2513cbcd8735b9313ee45bca12cf to your computer and use it in GitHub Desktop.

Select an option

Save samhenrigold/54fe2513cbcd8735b9313ee45bca12cf to your computer and use it in GitHub Desktop.
Smallest possible Mac app
window: win.s
clang -arch x86_64 -c -o win.o win.s
segedit win.o -extract __TEXT __text $@
chmod +x $@
@rm -f win.o
@ls -l $@
clean:
rm -f window win.o
// Look ma, no linker! The whole file (mach header included)
// is laid out below and carved out of the object with segedit.
// It has no imports and no relocations, so everything ld would add
// (__LINKEDIT, symtab, chained fixups) is just dead weight we skip.
.intel_syntax noprefix
.section __TEXT,__text
Lhdr:
.long 0xFEEDFACF // MH_MAGIC_64
.long 0x01000007 // CPU_TYPE_X86_64
.long 3 // CPU_SUBTYPE_X86_64_ALL
.long 2 // MH_EXECUTE
.long 5 // ncmds
.long Lcmds_end - Lcmds // sizeofcmds
.long 4 // MH_DYLDLINK
.long 0
Lcmds:
Lpz: // __PAGEZERO, or the kernel won't run us
.long 0x19 // LC_SEGMENT_64
.long Lpz_end - Lpz
.ascii "__PAGEZERO"
.space 6
.quad 0 // vmaddr
.quad 0x100000000 // vmsize
.quad 0, 0 // fileoff, filesize
.long 0, 0, 0, 0 // maxprot, initprot, nsects, flags
Lpz_end:
Ltext: // __TEXT covers the whole file, r-x
.long 0x19
.long Ltext_end - Ltext
.ascii "__TEXT"
.space 10
.quad 0x100000000 // vmaddr
.quad 0x1000 // vmsize
.quad 0 // fileoff
.long Lfile_end - Lhdr // filesize (split so it stays a foldable long)
.long 0
.long 5, 5, 0, 0 // maxprot, initprot, nsects, flags
Ltext_end:
Ldyld:
.long 0x0E // LC_LOAD_DYLINKER
.long Ldyld_end - Ldyld
.long 12
.asciz "/usr/lib/dyld"
.balign 8, 0
Ldyld_end:
Llib: // never called; just drags AppKit in. shorter
.long 0x0C // LC_LOAD_DYLIB path = fewer bytes than AppKit's own
.long Llib_end - Llib
.long 24
.long 0
.long 0x10000, 0x10000
.asciz "/usr/lib/libcryptex.dylib"
.balign 8, 0
Llib_end:
Lmain:
.long 0x80000028 // LC_MAIN
.long Lmain_end - Lmain
.long Lcode - Lhdr // entryoff
.long 0
.quad 0 // stacksize
Lmain_end:
Lcmds_end:
Lcode:
pop r15 // return addr into dyld (also aligns the stack)
lea rbp, [rip+Ls_mid] // strings are reached as [rbp+n] to keep the leas short
and r15, -4096
Lscan:
sub r15, 4096 // dyld's header is some pages back; walk to the magic
cmp dword ptr [r15], 0xFEEDFACF
jne Lscan
mov r12, r15
lea r13, [rbp+0] // "_dyld_all_image_infos"
call Llookup
mov rbx, [rax+8] // infoArray. first image exporting _dlsym is libdyld;
Lfind: // ours (image 0) has no trie and re-exports are skipped
mov r12, [rbx]
lea r13, [rbp+22] // "_dlsym"
call Llookup
test rax, rax
jnz Lfound
add rbx, 24
jmp Lfind
Lfound:
xchg rax, r15 // dlsym
push -2 // RTLD_DEFAULT
pop rdi
lea rsi, [rbp+29] // "objc_msgSend"
call r15
xchg rax, r12
push -2
pop rdi
lea rsi, [rbp+42] // "sel_getUid"
call r15
xchg rax, r13
push -2
pop rdi
lea rsi, [rbp+53] // "OBJC_CLASS_$_NSWindow"
call r15
xchg rax, r14
lea rdi, [rbp+88] // [[NSWindow new] orderFront:nil]
call r13
xchg rax, rsi
mov rdi, r14
call r12
xchg rax, r14
lea rdi, [rbp+92] // "orderFront:"
call r13
xchg rax, rsi
mov rdi, r14
xor edx, edx
call r12
push -2 // CFRunLoopRun()
pop rdi
lea rsi, [rbp+75] // "CFRunLoopRun"
call r15
call rax
// Llookup(r12 = mach header, r13 = symbol) -> rax, or 0 if not exported here.
// Preserves rbx, rbp, r15.
Llookup:
mov ecx, [r12+16] // ncmds
lea rdi, [r12+32]
xor r9d, r9d
mov r8, [rdi+24] // __TEXT vmaddr (segment 0 on every dylib and on dyld)
Llc:
mov eax, [rdi]
cmp al, 0x19 // LC_SEGMENT_64
jne Lnotseg
mov r10, [rdi+24] // trailing segment wins -> __LINKEDIT vmaddr/fileoff
mov r11, [rdi+40]
jmp Lnextlc
Lnotseg:
cmp al, 0x33 // LC_DYLD_EXPORTS_TRIE
jne Lnextlc
mov r9d, [rdi+8]
Lnextlc:
mov eax, [rdi+4]
add rdi, rax
dec ecx
jnz Llc
test r9, r9 // no trie -> not this one
jz Lfail
lea rsi, [r10+r9] // trie = mh + (le_vmaddr - text_vmaddr) + dataoff - le_fileoff
add rsi, r12
sub rsi, r8
sub rsi, r11
mov r10, rsi
Lwalk:
call Luleb // terminal size
cmp byte ptr [r13], 0
jne Lskipterm
test edx, edx
jz Lskipterm
call Luleb // flags
test dl, 8 // re-export carries no address of its own
jnz Lfail
call Luleb
lea rax, [rdx+r12]
ret
Lskipterm:
add rsi, rdx
lodsb
mov r14b, al // child count
test al, al
jz Lfail
Lchild:
mov rdi, r13
Lecmp:
lodsb // match this edge against the rest of the symbol
test al, al
jz Lematch
cmp al, [rdi]
jne Leskip
inc rdi
jmp Lecmp
Leskip:
lodsb // wrong edge, skip to its terminating NUL
test al, al
jnz Leskip
call Luleb
dec r14b
jnz Lchild
Lfail:
xor eax, eax
ret
Lematch:
mov r13, rdi
call Luleb // descend
lea rsi, [r10+rdx]
jmp Lwalk
Luleb: // read uleb128 at rsi into rdx, advance rsi
xor edx, edx
xor ecx, ecx
Lu1:
lodsb
mov r11d, eax
and eax, 0x7F
shl rax, cl
or rdx, rax
add cl, 7
test r11b, r11b
js Lu1
ret
// The [rbp+n] above are hand-picked disp8 offsets (literals, so the leas assemble to
// 4 bytes instead of 7). str verifies each string still lands where the code expects.
.macro str off, text
.if . - Ls_mid != \off
.error "string block moved; fix the [rbp+n] offsets"
.endif
.asciz "\text"
.endm
Ls_mid:
str 0, "_dyld_all_image_infos"
str 22, "_dlsym"
str 29, "objc_msgSend"
str 42, "sel_getUid"
str 53, "OBJC_CLASS_$_NSWindow"
str 75, "CFRunLoopRun"
str 88, "new"
str 92, "orderFront:"
Lfile_end:
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment