Last active
July 15, 2026 23:23
-
-
Save samhenrigold/54fe2513cbcd8735b9313ee45bca12cf to your computer and use it in GitHub Desktop.
Smallest possible Mac app
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // 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