/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ │vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ ╞══════════════════════════════════════════════════════════════════════════════╡ │ Copyright 2020 Justine Alexandra Roberts Tunney │ │ │ │ This program is free software; you can redistribute it and/or modify │ │ it under the terms of the GNU General Public License as published by │ │ the Free Software Foundation; version 2 of the License. │ │ │ │ This program is distributed in the hope that it will be useful, but │ │ WITHOUT ANY WARRANTY; without even the implied warranty of │ │ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ │ General Public License for more details. │ │ │ │ You should have received a copy of the GNU General Public License │ │ along with this program; if not, write to the Free Software │ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╠──────────────────────────────────────────────────────────────────────────────╣ │░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│ │░░░░░░░█▀█░█▀█░▀█▀░█░█░█▀█░█░░░█░░░█░█░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│ │░░░░░░░█▀█░█░▄░░█░░█░█░█▀█░█░░░█░░░▀█▀░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│ │░░░░░░░▀░▀░▀▀▀░░▀░░▀▀▀░▀░▀░▀▀▀░▀▀▀░░▀░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│ │░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│ │░░░░░░░█▀█░█▀█░█▀█░▀█▀░█▀█░█▀█░█░░░█▀▀░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│ │░░░░░░░█▀▀░█ █░██▀░░█░░█▀█░█▀█░█░░░█▀▀░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│ │░░░░░░░▀░░░▀▀▀░▀░▀░░▀░░▀░▀░▀▀▀░▀▀▀░▀▀▀░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│ │░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│ │░░░░░░░█▀▀░█░█░█▀▀░█▀█░█░█░▀█▀░█▀█░█▀█░█░░█▀▀░░░░░░░░░░░░░░░░░░░░░░░░▄▄░░░▐█░░│ │░░░░░░░█▀▀░▄▀▄░█▀▀░█░▄░█░█░░█░░█▀█░█▀█░█░░█▀▀░░░░░░░░░░░░▄▄▄░░░▄██▄░░█▀░░░█░▄░│ │░░░░░░░▀▀▀░▀░▀░▀▀▀░▀▀▀░▀▀▀░░▀░░▀░▀░▀▀▀░▀▀░▀▀▀░░░░░░░░░░▄██▀█▌░██▄▄░░▐█▀▄░▐█▀░░│ │░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▐█▀▀▌░░░▄▀▌░▌░█░▌░░▌░▌░░│ ╠──────────────────────────────────────────────────────▌▀▄─▐──▀▄─▐▄─▐▄▐▄─▐▄─▐▄─│ │ αcτµαlly pδrταblε εxεcµταblε § program header │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "ape/config.h" #include "ape/lib/apm.h" #include "ape/lib/pc.h" #include "ape/macros.h" #include "ape/notice.inc" #include "ape/relocations.h" #include "libc/elf/def.h" #include "libc/macho.h" #include "libc/nexgen32e/uart.h" #include "libc/nexgen32e/vidya.h" #include "libc/nt/pedef.h" #include "libc/sysv/consts/prot.h" .source "NOTICE" .source "ape/ape.S" .source "ape/ape.lds" .section .text,"ax",@progbits .align __SIZEOF_POINTER__ .previous .section .rodata,"a",@progbits .align __SIZEOF_POINTER__ __ro: .endobj __ro,globl,hidden # ←for gdb readibility .previous .section .data,"aw",@progbits .align __SIZEOF_POINTER__ .previous .section .bss,"aw",@nobits .align __SIZEOF_POINTER__ .previous .section .rodata.str1.1,"aMS",@progbits cstr: .endobj cstr,globl,hidden # ←for gdb readibility .previous .section .sort.rodata.real.str1.1,"aMS",@progbits rlstr: .endobj rlstr,globl,hidden # ←for gdb readibility .previous .section .head,"ax",@progbits /* ████████ ████████ ███████████ ██░░░░▒▒██ ██░░░░▒▒██ ████░░░░░░░░░▒▒████ ██░░░░░░██ ██░░░░▒▒██ ██░░░░▒▒███████░░░░▒▒██ ██░░░░░░▒▒██ ░██░░░░░░▒▒██ ██░░▒▒██ ██░░▒▒██ ██░░▒▒░░░░██ ░██░░░░░░▒▒██ ██░░▒▒██ ████████ ██░░▒▒▒▒░░▒▒██ ██▓░░░░▒▒░░▒▒██ ██░░▒▒▒▒███████ ██░░▒▒██░░░░██ ██▓░░░░██░░▒▒██ ████░░░░░░░▒▒████ ██░░▒▒████░░▒▒██░░░░░████░░▒▒██ ███████░░░░▒▒██ ██░░▒▒████░░░░██░░░░░████░░▒▒██ ██████ ██░░░░▒▒██ ██░░▒▒██ ██░░▒▒░░▒██ ██░░▒▒██ ██░░░░██ ██░░▒▒██ ██░░▒▒██ ██░░░░░░▒██ ██░░▒▒██ ██░░░░█████████░░░░▒▒██ ██░░▒▒██ ▓▓▒▒░░▒▒▒▓▓ ██░░▒▒██ ▓▓▒▒░░▓▓▓▓▓▓▓▓▓░░░░▓▓▓▓ ██░░▒▒██ ██░░██▓ ██░░▒▒██ ██░░░░░░░░░░░░░▒▒██ ████████████████████▓ ██████████████ ███████████████ ██▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓█████▓▓░░▓▓░░▓▓░░████░░░░░░░░░░░░░░░████ ██▓▓▓▓▓▓▓▓▓▓▓▓▓▓█████▓▓░░▓▓░░▓▓░░████░░░░░░░░░░░░░░░░░░░░░██ ██▓▓▓▓▓▓▓▓▓▓▓▓▓▓██▓▓▒░░▓▓░░▓▓░░▓▓██░░░░░░░░░░░░░░░░░░░░░░░░░██ ██▓▓▓▓▓▓██████████░░▒▓▓▓▓██████▓▓██░░░░░░███████████▓▓░░░░▓▓██ ██▓▓▓▓▓▓████████░░▓▓▓▓▓██▓▓▓▓██████░░░░████░░▒▓▓██ ██▓▓▓▓▓▓██ ██▓▓▓▓▓▓████████▓▓░░▒████▓▓▓▓██████░░░░██▓▓▓▓▒░░██ ██████ ██▓▓▓▓▓▓██ ██░░▓▓▓▓▓██▓▓▓▓██ ██▓▓░░████░░▒▓▓██████ ██▓▓▓▓▓▓██ ██▓▓░░▒████▓▓▓▓██ ██▓▓██▓▓▓▓▒░░██░░░░████ ██▓▓▓▓▓▓██ ██░░▓▓▓▓▓██▓▓▓▓██ ██████░░▒▓▓██░░░░░░░░██ ██▓▓▓▓▓▓██ ██▓▓░░▒████▓▓▓▓██ ██▓▓▓▓▒░░██▓▓░░░░░░░░██ ██▓▓▓▓▓▓██ ██░░▓▓▓████▓▓▓▓██ ████████░░▒▓▓████▓▓░░░░░░██ ██▓▓▓▓▓▓██ ██░░▒██▓▓▓▓████ ██░░░░░░██▓▓███ ██░░░░░░██ ██▓▓▓▓▓▓█████████████▓▓▓▓██████████▓▓░░░░░░█████████░░░░░░░░██ ██▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓████▓▓░░██▓▓░░░░░░░░░░░░░░░░░░░░░▓▓██ ██▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓████▓▓░░▓▓██▓▓▓▓░░░░░░░░░░░░░░░░░▓▓▓▓██ ██▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓████▓▓░░▓▓░░▓▓██▓▓▓▓▓▓░░░░░░░░░░░▓▓▓▓▓▓██ ██▓▓█████████████████████▓▓██▓▓██▓▓████▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓██ ███████████████████████████▓▓██▓▓██▓▓████▓▓▓▓▓▓▓▓▓▓▓▓▓████ ██████████████████▓ ██████████████ █████████████ ╔──────────────────────────────────────────────────────────────────────────────╗ │ αcτµαlly pδrταblε εxεcµταblε § the old technology │ ╚─────────────────────────────────────────────────────────────────────────────*/ / MZ Literally Executable Header / / This is the beginning of the program file and it can serve as an / entrypoint too. It shouldn't matter if the program is running on / Linux, Windows, etc. Please note if the underlying machine isn't / a machine, this header may need to morph itself to say the magic / words, e.g. ⌂ELF, which also works fine as a generic entrypoint. / / @see www.delorie.com/djgpp/doc/exe/ / @noreturn ape.mz: .ascii "MZ" # Mark 'Zibo' Joseph Zbikowski jno 2f # MZ: bytes on last page jo 2f # MZ: 512-byte pages in file .ascii "='" # MZ: reloc table entry count .ascii "\n\0" # MZ: data segment file offset / 16 .short 0x1000 # MZ: lowers upper bound load / 16 .short 0xf800 # MZ: roll greed on bss .short 0 # MZ: lower bound on stack segment .short 0 # MZ: initialize stack pointer .short 0 # MZ: ∑bₙ checksum don't bother .short 0x0100 # MZ: initial ip value .short 0x0800 # MZ: increases cs load lower bound .short 0x0040 # MZ: reloc table offset .short 0 # MZ: overlay number .org 0x24 # MZ: bytes reserved for you .ascii "JT" # MZ: OEM identifier .short 0 # MZ: OEM information .org 0x40-4 # MZ: bytes reserved for you .long RVA(ape.pe) # PE: the new technology .endobj ape.mz,globl,hidden / Disk Operating System Stub / @noreturn .org 0x40 # mz/elf header length stub: mov $0x40,%dl # *literally* dos jmp 1f # good bios skips here 1: jmp pc nop # system five bootpoint .org 0x48,0x90 # ⌂ELF → JNLE 47 jmp 3f 2: push %rdx # don't move or shell script breaks xor %edx,%edx # Z in MZ ate BIOS drive letter :( 3: .byte 0xbd,0,0 # mov $0x????0000,%[e]bp jmp pc jmp ape.hop # already in userspace .endfn stub / Mitigate incidental quotation marks. .real ape.hop:pop %rdx push %r10 # MZ → pop %r10 w/ NexGen32e .weak __imp_GetStartupInfoW ezlea __imp_GetStartupInfoW,ax test %rax,%rax jz 0f .weak KernelBase.GetStartupInfo test %rax,%rax / TODO(jart) / cmpq $RVA(KernelBase.GetStartupInfo),(%rax) jz 0f jmp WinMain 0: .weak _start jmp _start .endfn ape.hop .previous /*─────────────────────────────────────────────────────────────────────────────╗ │ αcτµαlly pδrταblε εxεcµταblε § ibm personal computer │ ╚──────────────────────────────────────────────────────────────────────────────┘ IBM designed BIOS to run programs by handing over the computer to a program as soon as its first sector is loaded. That gives us control over user-facing latency, even though the next step will generally be asking the BIOS to load more. The process is trivial enough that this entrypoint can support handoffs from alternative program-loaders e.g. Grub and MS-DOS so long as they either load our full program, or implement the PC BIOS disk service API. Since so many different implementations of these APIs have been built the last forty years these routines also canonicalize the cpu and program state, as it is written in the System V ABI. */ / Initializes program and jumps to long mode loader. / / @param dl drive number (use 0x40 to skip bios disk load) / @mode real / @noreturn .code16 pc: cld mov $REAL_STACK_FRAME>>4,%di # we need a stack xor %cx,%cx rlstack %di,%cx movpp %cs,%ax # memcpy() [relocate this page] call 1f 1: pop %si sub $RVA(1b),%si mov $4,%cl 1: shr %si loop 1b mov $512,%cx mov $IMAGE_BASE_REAL>>4,%di mov %si,%ds mov %di,%es xor %si,%si xor %di,%di rep movsb %ds:(%si),%es:(%di) ljmp $0,$REAL(1f) # longjmp() 1: mov $-512,%cx # memcpy() [relocate this frame] rep movsb %ds:(%si),%es:(%di) xor %bp,%bp mov %bp,%ds # %ds and %cs are now zero call 1f # setup frame pointers 1: push %bp mov %sp,%bp mov $XLM_SIZE,%cx # memset() [clear real bss] mov $XLM_BASE_REAL>>4,%ax mov %ax,%es xor %ax,%ax xor %di,%di rep stosb %al,%es:(%di) cmp $0x40,%dl # statfs() [disk geometry] je 6f call dsknfo mov $IMAGE_BASE_REAL>>4,%ax mov %ax,%es mov $v_ape_realsectors,%di # total sectors to read xor %dh,%dh # current head state xor %cx,%cx # current cylinder state 3: mov %di,%ax call pcread mov %es,%si 4: add $512>>4,%si dec %di jz 6f dec %ax jnz 4b mov %si,%es jmp 3b 6: mov %cx,XLM(LOADSTATE)+0 mov %dx,XLM(LOADSTATE)+2 ljmp $0,$REAL(realmodeloader) .endfn pc,globl,hidden / Be gentler on Unix line buffer impls. .byte 0x0a / Determines disk geometry. / / We use imperial measurements for storage systems so the software / can have an understanding of physical locality, which deeply / impacts the latency of operations. / / - 160KB: 1 head × 40 cylinders × 8 sectors × 512 = 163,840 / - 180KB: 1 head × 40 cylinders × 9 sectors × 512 = 184,320 / - 320KB: 2 heads × 40 cylinders × 8 sectors × 512 = 327,680 / - 360KB: 2 heads × 40 cylinders × 9 sectors × 512 = 368,640 / - 720KB: 2 heads × 80 cylinders × 9 sectors × 512 = 737,280 / - 1.2MB: 2 heads × 80 cylinders × 15 sectors × 512 = 1,228,800 / - 1.44MB: 2 heads × 80 cylinders × 18 sectors × 512 = 1,474,560 / / Terminology / / - Cylinder / Tracks should mean the same thing / - Heads / Sides / Spindles should mean the same thing / / Disk Base Table / / 0: specify byte 1, step-rate time, head unload time / 1: specify byte 2, head load time, DMA mode / 2: timer ticks to wait before disk motor shutoff / 3: bytes per sector code / 0: 128 bytes 2: 512 bytes / 1: 256 bytes 3: 1024 bytes / 4: sectors per track (last sector number) / 5: inter-block gap length/gap between sectors / 6: data length, if sector length not specified / 7: gap length between sectors for format / 8: fill byte for formatted sectors / 9: head settle time in milliseconds / 10: motor startup time in eighths of a second / / @param dl drive number / @return dl = pc_drive (corrected if clobbered by header) / pc_drive / pc_drive_type / pc_drive_heads / pc_drive_last_cylinder / pc_drive_last_sector / @clob ax, cx, dx, di, si, es, flags / @since IBM Personal Computer XT dsknfo: push %bx 1: push %dx mov $0x08,%ah # get disk params int $0x13 jc 9f mov %cl,%bh and $0b00111111,%bh and $0b11000000,%cl rol %cl rol %cl xchg %cl,%ch push %ds # disk base table in es:di movpp %es,%ds xor %si,%si mov %si,%es mov $XLM(DRIVE_BASE_TABLE),%si xchg %si,%di movsw #→ headunloadtime, headloadtime movsw #→ shutofftime, bytespersector movsw #→ sectorspertrack, sectorgap movsw #→ datalength, formatgap movsw #→ formatfill, settletime movsb #→ startuptime pop %ds xchg %bx,%ax stosw #→ pc_drive_type, pc_drive_last_sector xchg %cx,%ax stosw #→ pc_drive_last_cylinder xchg %dx,%ax stosw #→ pc_drives_attached, pc_drive_last_head pop %ax stosb #→ pc_drive xchg %ax,%dx pop %bx ret 9: pop %dx 8: xor $0x80,%dl # try cycling drive a/c xor %ax,%ax # reset disk int $0x13 jc 8b jmp 1b .endfn dsknfo / Reads disk sectors via BIOS. / / Each call to this function performs a single read. It has a / special ABI, where its parameters are preserved, and rolled / forward accordingly across calls. BIOS requirements on read / parameter validity are abstracted. If the requested size is / too large for one read, then it's tuned down appropriately. / / Please note that dskinfo() must be called beforehand. / / @param ax number sectors to read / @param es destination memory address >> 4 / @param cx cylinder number / @param dh head number / @param dl drive number / @return number of sectors actually read pcread: push %bx xor %bx,%bx mov XLM(DRIVE_LAST_SECTOR),%bl cmp %ax,%bx # ax = min(ax, pertrack) ja 1f mov %bx,%ax 1: push %cx # how many sectors until 64k push %ax # boundary reads can't cross mov $0x1000,%bx # canonicalizing the segment mov %es,%ax # register is not sufficient sub %ax,%bx and $0x0fff,%bx mov $5,%cx 0: shr %bx loop 0b pop %ax pop %cx test %bx,%bx # %es 64k-aligned edge case jz 1f cmp %ax,%bx # ax = min(ax, remain64k) ja 1f mov %bx,%ax 1: push %ax # setup int-13-2() params push %cx xchg %cl,%ch ror %cl ror %cl or $1,%cl # one cylinder at a time xor %bx,%bx # es:bx is destination addr mov $0x02,%ah # read disk sectors ordinal int $0x13 pop %cx pop %bx jc 9f mov $0,%ah cmp %ax,%bx jl 9f inc %cx cmp XLM(DRIVE_LAST_CYLINDER),%cx jle 2f xor %cx,%cx inc %dh 2: pop %bx ret 9: push %ax xor %ax,%ax # try disk reset on error int $0x13 pop %ax jmp 1b .endfn pcread /*───────────────────────────────────────────────────────────────────────────│─╗ │ αcτµαlly pδrταblε εxεcµταblε § partition table ─╬─│┼ ╚────────────────────────────────────────────────────────────────────────────│*/ / Partition Table. .Lape.mbrpad: .org 0x1b4 .endobj .Lape.mbrpad ape_disk: .stub .Lape.diskid,quad .org 0x1be,0x00 .macro .partn x .stub .Lape.part\x\().status,byte # 0=absent / 0x80=present .stub .Lape.part\x\().first.head,byte # in low 6 bits .stub .Lape.part\x\().first.cylinder,byte .stub .Lape.part\x\().first.sector,byte .stub .Lape.part\x\().filesystem,byte .stub .Lape.part\x\().last.head,byte .stub .Lape.part\x\().last.cylinder,byte .stub .Lape.part\x\().last.sector,byte .stub .Lape.part\x\().lba,long # c₀*Cₙ + h₀*Hₙ + s₀*Sₙ .stub .Lape.part\x\().sector.count,long # sectors are 512 bytes .endm .partn 1 .partn 2 .partn 3 .partn 4 .org 0x1fe .short BOOTSIG .endobj ape_disk /* ▄▄▄ ▄▄▄ ▀▓▓▒▄ ▄▓▒▒░ ▀▓▒▒▒▄ ▄▓▓▓▒▀ ▄▄▄▄ ▒▓▒▒░▒▄ ▄▓▓▓▒▓ ▄▄▓██▓▓▓▓▒▒▒▒▓▓▄▄▓▓▒▒▒░░▒ ▓▓▓▓▒▒▒▄▄ ░▒█▓▓▓▓▓▓▓▓▓▒▒▒▒▒▒▒▒▒▒▒▓▒░░▒░ ██▓▓▓▒▒░░▒▒▒▒▓▓▓▓▓▓▒▓▒░▒▒░▀▒▒▒▒░▀░▒▒▒░▒ ▓▓▓▓▓▓▓▒▒▒▒▒▒▓▓▒▓▓▒▒▒░▒▒░░ ░▒▒░ ░▒▒▒▒ ▀▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒░░▒░░ ░▒▒ ░ ▀▒▒ ▀▓▓█▓▓▓▓▓▓▓▓▓▓▒▒░░▒▒░░ ░░░▓░ ▓░░░▒ ▀▀█▓███▓▓▓▓▓▒▒░░░▒░░ ░█▓░█▓░█▓▓▄▒░ ░▓██▓▓▓▓▓▒▒░░░▒░░ ░████▓▒▓█▓▀░▀▄ ░▓██▓▓▓▓▓▒▒▒░░░▒░░ ▒██▓▒▒▒▒▒▒░░░▒ ████▓▓▓▓▓▒▒▒▒▒▒▒▒▒░░▒▓▓▒░░░░▒░░░▒░ ░░░░░ ░▓███▓▓▓▓▓▒▒░░░░░░░▒▒▒▒▒▒▒▒▒▒▒░░░ ░░░░░ ░ ▓███▓▓▓▓▓▒▓▒▒▒▒░░░░░░░░░▒▓▒▒░▀ ░░░ ░░░░░ ▀▒██▓▓▓▓▒▒▒▓▓▓▓▒▒▒▒▒▒▒▓▀▀░ ░░░░░░░░░ ░ ▓▓▓▓▓▓▓▒▓▒▒▒▒▓▓▓▒▀░ ░░░░░▄░░░ ░░░ ░░░░░░ ▓▓▓▒▒▒▒▒▒▒▒▒▒▒▓ █▓▒░░▒░░░░ ░░░░░░░░ ▄▓▓▓▒▒▒▒▒░░░░░░░▒▄▄▄░▒▓▓▒▒░▀░ ░▓█▓▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓▒░░░▒ besiyata ▓▓█▓▓▒▓▓▓▒▒▒░░░░░░▒▓▓▓▓▒▒▒▒▒░ dishmaya ▓▓█▓▓▓▓▓▓▒▒▒░░░░░░░▒▓▓▒▀▀▀ ▓▓██▓▓▓▓▓▓▓▒▒▒▒▒▒▒▒▒▒▀ █▓▓█▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▀ ▒▓▓▓▓▀░░▒▓▓▓▓▓▓▓▓▒▒░░▒ ▄▓▓▀░░░▄▓▓▓▓▒▒▒▒▒░░░░▄░ ▄███▄▄▓▓▓▓▓▓▓▒▒▒▒▒░░▒▒░ ▄▓▓▓█▓█▓▓███▓▓▓▓▓▓▓▓▓▓▓░ ▄░▓▓▓▓▓▓▀▒▓▓▓▒▒▓▒░░░▒▓▒░░░▓ ▄▄▄░▒▓▓▓▓▓▓░▀▀ ▓▓▒░▓▒▒▒▒▒▒▒▒▒▒▄░░▀▀░░ ▄▄▄▄ ▄▄▄▒▒▓▓█▓▓▓▓▓▀▀▀▀▀ ▓▓▓▓▓▒▒▒▒▒▒▒▒▒▒▒▒▒▀░░▀░░▒▒▒░░░ ░░░░░ ▄▓▓▓▒▀▀ ▓▒▓▓▓▓▓▒▒▒▒▒▒▒▒▓░░░ ▒▒▒░░░░░░░░▒ █▓▓▒ ▄▄▄ ▀▓▒▓▒▒▒▓▓▓▓▓▓▒▒▒░░░░░░░░░▒▒░░░░░░░ ▀▓▓▓▓▒▄▄▒▒▒▒▒▒▄▄ ▀▀▀▀░░▒▒▒▒░░░░░░ ▀▀▀▓▓▓▓▒▒▒▒▒▓▓▄▄ ╔────────────────────────────────────────────────────────────────────────────│─╗ │ αcτµαlly pδrταblε εxεcµταblε § bell system five ─╬─│┼ ╚────────────────────────────────────────────────────────────────────────────│─╝ the bourne executable & linkable format */ apesh: .ascii "'\n#'\"\n" # sixth edition shebang .ascii "o=\"$(command -v \"$0\")\"\n" .ascii "if [ -d /Applications ]; then\n" .ascii "dd if=\"$o\"" .ascii " of=\"$o\"" .ascii " bs=8" .ascii " skip=\"" .shstub .Lape.macho.dd.skip,2 .ascii "\" count=\"" .shstub .Lape.macho.dd.count,2 .ascii "\" conv=notrunc 2>/dev/null\n" .ascii "elif exec 7<> \"$o\"; then\n" .ascii "printf '" .ascii "\\177ELF" # 0x0: ⌂ELF .ascii "\\2" # 4: long mode .ascii "\\1" # 5: little endian .ascii "\\1" # 6: elf v1.o .ascii "\\011" # 7: FreeBSD .ascii "\\0" # 8: os/abi ver. .ascii "\\0\\0\\0" # 9: padding 3/7 .ascii "\\0\\0\\0\\0" # padding 4/7 .ascii "\\2\\0" # 10: εxεcµταblε .ascii "\\076\\0" # 12: NexGen32e .ascii "\\1\\0\\0\\0" # 14: elf v1.o .shstub .Lape.elf.entry,8 # 18: e_entry .shstub .Lape.elf.phoff,8 # 20: e_phoff .shstub .Lape.elf.shoff,8 # 28: e_shoff .ascii "\\0\\0\\0\\0" # 30: e_flags .ascii "\\100\\0" # 34: e_ehsize .ascii "\\070\\0" # 36: e_phentsize .shstub .Lape.elf.phnum,2 # 38: e_phnum .ascii "\\0\\0" # 3a: e_shentsize .shstub .Lape.elf.shnum,2 # 3c: e_shnum .shstub .Lape.elf.shstrndx,2 # 3e: e_shstrndx .ascii "' >&7\n" .ascii "exec 7<&-\n" .ascii "fi\n" .ascii "exec \"$0\" \"$@\"\n" # etxtbsy tail recursion .ascii "R=$?\n" # architecture optimistic .ascii "\n" .ascii "if [ $R -eq 126 ] && [ \"$(uname -m)\" != x86_64 ]; then\n" .ascii "if Q=\"$(command -v qemu-x86_64)\"; then\n" .ascii "exec \"$Q\" \"$0\" \"$@\"\n" .ascii "else\n" .ascii "echo error: need qemu-x86_64 >&2\n" .ascii "fi\n" .ascii "elif [ $R -eq 127 ]; then\n" # means argv[0] was wrong .ascii " exec \"$o\" \"$@\"\n" # so do a path resolution .ascii "fi\n" .ascii "exit $R\n" .endobj apesh .section .elf.phdrs,"a",@progbits .align __SIZEOF_POINTER__ .type ape.phdrs,@object .globl ape.phdrs ape.phdrs: .long PT_LOAD # text segment .long PF_R|PF_X .stub .Lape.rom.offset,quad .stub .Lape.rom.vaddr,quad .stub .Lape.rom.paddr,quad .stub .Lape.rom.filesz,quad .stub .Lape.rom.memsz,quad .stub .Lape.rom.align,quad .align __SIZEOF_POINTER__ .long PT_LOAD # data segment .long PF_R|PF_W .stub .Lape.ram.offset,quad .stub .Lape.ram.vaddr,quad .stub .Lape.ram.paddr,quad .stub .Lape.ram.filesz,quad .stub .Lape.ram.memsz,quad .stub .Lape.ram.align,quad / Linux ignores mprotect() and returns 0 without this lool / It has nothing to do with the stack, which is still exec .align __SIZEOF_POINTER__ .long PT_GNU_STACK # p_type .long PF_R|PF_W # p_flags .quad 0 # p_offset .quad 0 # p_vaddr .quad 0 # p_paddr .quad 0 # p_filesz .quad 0 # p_memsz .quad 16 # p_align .align __SIZEOF_POINTER__ .long PT_NOTE # openbsd note .long PF_R .stub .Lape.note.offset,quad .stub .Lape.note.vaddr,quad .stub .Lape.note.paddr,quad .stub .Lape.note.filesz,quad .stub .Lape.note.memsz,quad .stub .Lape.note.align,quad .previous .section .note.openbsd.ident,"a",@progbits .Lopenbsd.ident: .long 8 .long 4 .long 0x1 .asciz "OpenBSD" .long 0 .size .Lopenbsd.ident,.-.Lopenbsd.ident .type .Lopenbsd.ident,@object .previous /* ▄▄███▄ ▄▄████████▄ ▄█████████████▄ ▄▄███▓▓▓▓▓▓▓▓▓▓▓███▄ ▄▄█████▓▓▓█████████▓▓▓██▄ ▄▄████████▓▓▓███████▓▓▓▓▓████▄ ▄█████░░░████▓▓█████▓▓▓▓█████████▄ ▄▄█████████░░░███▓▓█▓▓▓▓▒███████▓▓▒███▄ ▄██████████████░░░██▓▓▓▓███████████▓▓█████▄ ██████████████████░░░██▓▓▓█████████▓▓▓███████▄ ███░░░░░░█████████▓░░███▓▓▓▓▓▓▓▓▓▓▓█████▒▒▒██▄ █░███░░░██░░░░░░░░░██░░██████████████▒▒▒▒██████▄ ███████░░░█████████░░░░░░█████████▒▒▒▒▒██████████▄ █████ ██░░░███████████████████▒▒▒▒▒██░▒▒██████████▄ ██████ ██░░░██████████████░███▒████████▒▒██████████▄ ████████ ███░░█████████████░░████████████▒▒███████████ █████████ ███░░███████████░░██████████████▒▒███████████ ▄██████████ ██████████████ ░░███████████████▒▒███████████ ████████████ ███░░░░░█████░░█████████████████▒▒██████ █ █████████████ ██████░░░░░░░▒█████████████████████ ████▀ █████████████ ██████████░░░░░░░░░███████████ ████████ █████████████ ████████░░███████░░░██████ ▓██████████ █████████████ ██████░░░████████████ █████████████ ╔────────────────────────────────────────────────────────────────────────────│─╗ │ αcτµαlly pδrταblε εxεcµταblε § nexstep carnegie melon mach object format ─╬─│┼ ╚────────────────────────────────────────────────────────────────────────────│─╝ @note hey xnu before we get upx'd email feedback jtunney@gmail.com @see OS X ABI Mach-O File Format Reference, Apple Inc. 2009-02-04 @see System V Application Binary Interface NexGen32e Architecture Processor Supplement, Version 1.0, December 5th, 2018 */ .section .macho,"a",@progbits .align __SIZEOF_POINTER__ ape.macho: .long 0xFEEDFACE+1 .long MAC_CPU_NEXGEN32E .long MAC_CPU_NEXGEN32E_ALL .long MAC_EXECUTE .long 5 # number of load commands .long 60f-10f # size of all load commands .long MAC_NOUNDEFS # flags .long 0 # reserved 10: .long MAC_LC_SEGMENT_64 .long 20f-10b # unmaps first page dir .ascin "__PAGEZERO",16 # consistent with linux .quad 0,0x200000,0,0 # which forbids mem <2m .long 0,0,0,0 20: .long MAC_LC_SEGMENT_64 .long 30f-20b .ascin "__TEXT",16 .stub .Lape.rom.vaddr,quad .stub .Lape.rom.memsz,quad .stub .Lape.rom.offset,quad .stub .Lape.rom.filesz,quad .long PROT_EXEC|PROT_READ|PROT_WRITE # maxprot .long PROT_EXEC|PROT_READ # initprot .long 1 # segment section count .long 0 # flags 210: .ascin "__text",16 # section name (.text) .ascin "__TEXT",16 .stub .Lape.text.vaddr,quad .stub .Lape.text.memsz,quad .stub .Lape.text.offset,long .long 12 # align 2**12 = 4096 .long 0 # reloc table offset .long 0 # relocation count .long MAC_S_ATTR_SOME_INSTRUCTIONS # section type & attributes .long 0,0,0 # reserved 30: .long MAC_LC_SEGMENT_64 .long 40f-30b .ascin "__DATA",16 .stub .Lape.ram.vaddr,quad .stub .Lape.ram.memsz,quad .stub .Lape.ram.offset,quad .stub .Lape.ram.filesz,quad .long PROT_EXEC|PROT_READ|PROT_WRITE # maxprot .long PROT_READ|PROT_WRITE # initprot .long 2 # segment section count .long 0 # flags 310: .ascin "__data",16 # section name (.data) .ascin "__DATA",16 .stub .Lape.data.vaddr,quad .stub .Lape.data.memsz,quad .stub .Lape.data.offset,long .long 12 # align 2**12 = 4096 .long 0 # reloc table offset .long 0 # relocation count .long 0 # section type & attributes .long 0,0,0 # reserved 320: .ascin "__bss",16 # section name (.bss) .ascin "__DATA",16 .stub .Lape.bss.vaddr,quad # virtual address .stub .Lape.bss.memsz,quad # memory size .long 0 # file offset .long 12 # align 2**12 = 4096 .long 0 # reloc table offset .long 0 # relocation count .long MAC_S_ZEROFILL # section type & attributes .long 0,0,0 # reserved 40: .long MAC_LC_UUID .long 50f-40b .stub uuid1_,quad .stub uuid2_,quad 50: .long MAC_LC_UNIXTHREAD .long 60f-50b # cmdsize .long MAC_THREAD_NEXGEN32E # flavaflav .long (520f-510f)/4 # count 510: .quad 0 # rax .quad IMAGE_BASE_VIRTUAL # rbx .quad 0 # rcx .quad 0 # rdx .quad 0 # rdi .quad 0 # rsi .quad 0 # rbp .quad 0 # rsp .quad 0 # r8 .quad 0 # r9 .quad 0 # r10 .quad 0 # r11 .quad 0 # r12 .quad 0 # r13 .quad 0 # r14 .quad 0 # r15 .quad _start_xnu # rip .quad 0 # rflags .quad 0 # cs .quad 0 # fs .quad 0 # gs 520: 60: .endobj ape.macho,globl,hidden .previous /* .macho */ /* ░░░░ ▒▒▒░░░▒▒▒▒▒▒▒▓▓▓░ ▒▒▒▒░░░▒▒▒▒▒▒▓▓▓▓▓▓░ ▒▒▒▒░░░▒▒▒▒▒▒▒▓▓▓▓▓▓ ▒▓░ ▒▒▒░░░░▒▒▒▒▒▒▓▓▓▓▓▓ ▓▓▓▓▓▓▒ ▒▒▒▓▓█ ▒▒▒▒░░░▒▒▒▒▒▒▒▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▒▒▒▒▒▒▒▒▒▒▓▓▓ ░▒▒▒░░░░▒▒▒▒▒▒▓▓▓▓▓▓ █▓▓▓▓▓▓▓▒▒▒▒▒▒▒▒▒▒▓▓█ ▒▒▒▒░░░▒▒▒▒▒▒▒▓▓▓▓▓░ ▓▓▓▓▓▓▓▓▒▒▒▒▒▒▒▒▒▒▓▓▓ ▒▒▒▒░░░▒▒▒▒▒▒▒▓▓▓▓▓▓ ▒▓▓▓▓▓▓▓▓▒▒▒▒▒▒▒▒▒▒▓▓▒ ▒▒▒▒▓▓ ▓▒▒▓▓▓▓ ▓▓▓▓▓▓▓▓▒▒▒▒▒▒▒▒▒▒▓▓█ ▒▓ ▓▓▓▓▓▓▓▓▓▒▒▒▒▒▒▒▒▒▒▓▓ ░░░░░░░░░░░▒▒▒▒ ▓▓▓▓▓▓▓▓▒▒▒▒▒▒▒▒▒▒▓▓█ ▒▒░░░░░░░░░░▒▒▒▒▒▓▓▓ ▓▓▓▓▓▒▒▒▒▒▒▒▒▒▒▓▓▓ ░▒░░░░░░░░░░░▒▒▒▒▒▓▓ ▓░ ░▓███▓ ▒▒░░░░░░░░░░▒▒▒▒▒▓▓░ ▒▓▓▓▒▒▒ ░▒▒▒▓ ████████████ ▒▒░░░░░░░░░░░▒▒▒▒▒▓▓ ▒▓▓▓▓▒▒▒▒▒▒▒▒░░░▒▒▒▒▒░ ░███ ▒░░░░░░░░░░░▒▒▒▒▒▓▓ ▓▓▓▓▒▒▒▒▒▒▒▒░░░░▒▒▒▒▓ ███ ▒▒░░░░░░░░░░▒▒▒▒▒▒▓▓ ▒▓▓▓▒▒▒▒▒▒▒▒░░░░▒▒▒▒▒ ▓██ ▒░░░░░░░░░░░▒▒▒▒▒▓▓ ▓▓▓▓▒▒▒▒▒▒▒▒░░░▒▒▒▒▒▓ ▓██ ▒▒░░░▒▒▒░░░▒▒░▒▒▒▓▓▒ ▒▓▓▓▒▒▒▒▒▒▒▒░░░░▒▒▒▒▒ ███ ░▒▓ ░▓▓▓▓▒▒▒▒▒▒▒▒░░░░▒▒▒▒▓ ▓██ ╔─────────────────────────────────────────────────────────────────▀▀▀────────│─╗ │ αcτµαlly pδrταblε εxεcµταblε § the new technology ─╬─│┼ ╚────────────────────────────────────────────────────────────────────────────│─╝ The Portable Executable Format @see https://docs.microsoft.com/en-us/windows/desktop/debug/pe-format @see "The Portable Executable File Format from Top to Bottom", Randy Kath, Microsoft Developer Network Technology Group. */ / ┌14:Uniprocessor Machine ┌─────────────────────────┐ / │┌13:DLL │ PE File Characteristics │ / ││┌12:System ├─────────────────────────┤ / │││┌11:If Net Run From Swap │ r │ reserved │ / ││││┌10:If Removable Run From Swap │ d │ deprecated │ / │││││┌9:Debug Stripped │ D │ deprecated with │ / ││││││┌8:32bit Machine │ │ extreme prejudice │ / │││││││ ┌5:Large Address Aware └───┴─────────────────────┘ / │││││││ │ ┌1:Executable / │││││││ │ │┌0:Relocs Stripped / d│││││││dr│Ddd││ .LPEEXE = 0b0000001000100011 / ┌15:TERMINAL_SERVER_AWARE ┌─────────────────────────┐ / │┌14:GUARD_CF │ PE DLL Characteristics │ / ││┌13:WDM_DRIVER ├─────────────────────────┤ / │││┌12:APPCONTAINER │ r │ reserved │ / ││││┌11:NO_BIND └───┴─────────────────────┘ / │││││┌10:NO_SEH / ││││││┌9:NO_ISOLATION / │││││││┌8:NX_COMPAT / ││││││││┌7:FORCE_INTEGRITY / │││││││││┌6:DYNAMIC_BASE / ││││││││││┌5:HIGH_ENTROPY_VA / │││││││││││rrrrr .LDLLSTD = 0b0000000100100000 .LDLLPIE = 0b0000000001000000 .LDLLEXE = .LDLLSTD / ┌31:Writeable ┌─────────────────────────┐ / │┌30:Readable │ PE Section Flags │ / ││┌29:Executable ├─────────────────────────┤ / │││┌28:Shareable │ o │ for object files │ / ││││┌27:Unpageable │ r │ reserved │ / │││││┌26:Uncacheable └───┴─────────────────────┘ / ││││││┌25:Discardable / │││││││┌24:Contains Extended Relocations / ││││││││ ┌15:Contains Global Pointer (GP) Relative Data / ││││││││ │ ┌7:Contains Uninitialized Data / ││││││││ │ │┌6:Contains Initialized Data / ││││││││ o │ ││┌5:Contains Code / ││││││││┌┴─┐rrrr│ ooror│││rorrr .LPETEXT = 0b01110000000000000000000001100000 .LPEDATA = 0b11000000000000000000000011000000 .LPEIMPS = 0b11000000000000000000000001000000 .section .pe.header,"a",@progbits .align __SIZEOF_POINTER__ ape.pe: .ascin "PE",4 .short kNtImageFileMachineNexgen32e .stub .Lape.pe.shnum,short # NumberOfSections .long 0x5c64126b # TimeDateStamp .long 0 # PointerToSymbolTable .long 0 # NumberOfSymbols .stub .Lape.pe.optsz,short # SizeOfOptionalHeader .short .LPEEXE # Characteristics .short kNtPe64bit # Optional Header Magic .byte 14 # MajorLinkerVersion .byte 15 # MinorLinkerVersion .long 0 # SizeOfCode .long 0 # SizeOfInitializedData .long 0 # SizeOfUninitializedData .long RVA(WinMain) # EntryPoint .long 0 # BaseOfCode .quad IMAGE_BASE_VIRTUAL # ImageBase .long 4096 # SectionAlignment .long 4096 # FileAlignment .short 6 # MajorOperatingSystemVersion .short 0 # MinorOperatingSystemVersion .short 0 # MajorImageVersion .short 0 # MinorImageVersion .short 6 # MajorSubsystemVersion .short 0 # MinorSubsystemVersion .long 0 # Win32VersionValue .long RVA(_end) # SizeOfImage .long RVA(_ehead) # SizeOfHeaders .long 0 # Checksum .short v_ntsubsystem # Subsystem: 0=Neutral,2=GUI,3=Console .short .LDLLEXE # DllCharacteristics .quad 0x0000000000100000 # StackReserve .quad 0x0000000000030000 # StackCommit (64kb [goog] + arg + env) .quad 0x0000000000080000 # HeapReserve .quad 0x0000000000001000 # HeapCommit (we make our own heap) .long 0x00000000 # LoaderFlags .long 16 # NumberOfDirectoryEntries .long 0,0 # ExportsDirectory .long RVA(idata.idt) # ImportsDirectory .stub .Lidata.idtsize,long # ImportsDirectorySize .long 0,0 # ResourcesDirectory .long 0,0 # ExceptionsDirectory .long 0,0 # SecurityDirectory .long 0,0 # BaseRelocationTable .long 0,0 # DebugDirectory .long 0,0 # DescriptionString .long 0,0 # MachineSpecific .long 0,0 # ThreadLocalStorage .long 0,0 # LoadConfigurationDirectory .long 0,0 # BoundImportDirectory .long RVA(idata.iat) # ImportAddressDirectory .stub .Lidata.iatsize,long # ImportAddressDirectorySize .long 0,0 # DelayImportDescriptor .long 0,0 # ComPlusRuntimeHeader .long 0,0 # Reserved .endobj ape.pe,globl .previous .section .pe.sections,"a",@progbits .ascin ".text",8 # Section Name .stub .Lape.text.memsz,long # Virtual Size or Physical Address .stub .Lape.text.rva,long # Relative Virtual Address .stub .Lape.text.filesz,long # Physical Size .stub .Lape.text.offset,long # Physical Offset .long 0x00000000 # Relocation Table Offset .long 0x00000000 # Line Number Table Offset .short 0x0000 # Relocation Count .short 0x0000 # Line Number Count .long .LPETEXT # Flags .previous .section .pe.sections,"a",@progbits .ascin ".data",8 # Section Name .stub .Lape.ram.memsz,long # Virtual Size or Physical Address .stub .Lape.ram.rva,long # Relative Virtual Address .stub .Lape.ram.filesz,long # Physical Size .stub .Lape.ram.offset,long # Physical Offset .long 0x00000000 # Relocation Table Offset .long 0x00000000 # Line Number Table Offset .short 0x0000 # Relocation Count .short 0x0000 # Line Number Count .long .LPEDATA # Flags .previous .section .idata.ro.idt.1,"a",@progbits .type idata.idtend,@object .type idata.idt,@object .globl idata.idt,idata.idtend .hidden idata.idt,idata.idtend idata.idt: .previous/* ... decentralized content ... */.section .idata.ro.idt.3,"a",@progbits .long 0,0,0,0,0 idata.idtend: .previous .section .piro.data.sort.iat.1,"aw",@progbits .type idata.iatend,@object .type idata.iat,@object .globl idata.iat,idata.iatend .hidden idata.iat,idata.iatend idata.iat: .previous/* ... decentralized content ... */.section .piro.data.sort.iat.3,"aw",@progbits idata.iatend: .previous /*─────────────────────────────────────────────────────────────────────────────╗ │ αcτµαlly pδrταblε εxεcµταblε § early-stage read-only data │ ╚──────────────────────────────────────────────────────────────────────────────╝ better code/data separation (.head is rwx[real] rx[long]) */ / NUL-Terminated Strings. ape.str: .Lstr.ape: .byte 0xe0,0x63,0xe7,0xe6,0xe0,0x6c,0x6c,0x79 #αcτµαlly .byte 0x20,0x70,0xeb,0x72,0xe7,0xe0,0x62,0x6c # pδrταbl .byte 0xee,0x20,0xee,0x78,0xee,0x63,0xe6,0xe7 #ε εxεcµτ .byte 0xe0,0x62,0x6c,0xee,0x0d,0x0a,0x00 #αblε. .endobj .Lstr.ape .Lstr.error: .asciz "error: " .endobj .Lstr.error .Lstr.crlf: .asciz "\r\n" .endobj .Lstr.crlf .Lstr.cpuid: .asciz "cpuid" .endobj .Lstr.cpuid .Lstr.oldskool: .asciz "oldskool" .endobj .Lstr.oldskool .Lstr.dsknfo: .asciz "dsknfo" .endobj .Lstr.dsknfo .Lstr.e820: .asciz "e820" .endobj .Lstr.e820 .Lstr.memory: .asciz "nomem" .endobj .Lstr.memory .Lstr.long: .asciz "nolong" .endobj .Lstr.long .Lstr.hello: .asciz "hello\n" .endobj .Lstr.hello .endobj ape.str / Serial Line Configuration (8250 UART 16550) / If it's hacked, it'll at least get hacked very slowly. sconf: .short 1843200/*hz*/ / 16/*wut*/ / 9600/*baud*/ / / ┌interrupt trigger level {1,4,8,14} / │ ┌enable 64 byte fifo (UART 16750+) / │ │ ┌select dma mode / │ │ │┌clear transmit fifo / │ │ ││┌clear receive fifo / ├┐│ │││┌enable fifos .byte 0b00000000 / / ┌dlab: flips configuration mode state / │┌enable break signal / ││ ┌parity {none,odd,even,high,low} / ││ │ ┌extra stop bit / ││ │ │┌data word length (bits+5) / ││┌┴┐│├┐ .byte 0b01000011 .endobj sconf,global,hidden / Global Descriptor Table / / @note address portion only concern legacy modes .align 8 gdt: .short 2f-1f # table byte length .long REAL(1f),0 # table address .align 8 1: / ┌G:granularity (1 → limit *= 0x1000) / │┌D/B:default operation size (0 = 16|64bit, 1 = 32-bit) / ││┌L:long mode / │││┌AVL:this bit is thine (1<<52) / ││││ ┌P:present / ││││ │┌DPL:privilege / ││││ ││ ┌─────────data/code(1) / ││││ ││ │┌────data(0)──────code(1) / ││││ ││ ││┌───conforming───expand-down / ││││ ││ │││┌──writeable────readable / ││││ ││ ││││┌─accessed─────accessed / ││││ ││ │││││ / ││││ ┌──││─│││││───────────────────────────────┐ / ┌───││││─│──││─│││││───────────┐ │ / ┌───┴──┐││││┌┴─┐│├┐│││││┌──────────┴───────────┐┌──────┴───────┐ / │ ││││││ ││││││││││ base address││ segment limit│ / │ ││││││ ││││││││││ 32 bits││ 20 bits│ / │ ││││││ ││││││││││ ││ │ / 6666555555555544444444443333333333222222222211111111110000000000 / 3210987654321098765432109876543210987654321098765432109876543210 / │ ││││││ ││││││││││ ││ │ .quad 0b0000000000000000000000000000000000000000000000000000000000000000 # 0 .quad 0b0000000000001111100110100000000000000000000000001111111111111111 # 8 .quad 0b0000000000001111100100100000000000000000000000001111111111111111 #16 .quad 0b0000000011001111100110100000000000000000000000001111111111111111 #24 .quad 0b0000000011001111100100100000000000000000000000001111111111111111 #32 .quad 0b0000000010101111100110110000000000000000000000001111111111111111 #40 .quad 0b0000000010101111100100110000000000000000000000001111111111111111 #48 2: .endobj gdt,global,hidden /*─────────────────────────────────────────────────────────────────────────────╗ │ αcτµαlly pδrταblε εxεcµταblε § multiboot stub │ ╚──────────────────────────────────────────────────────────────────────────────╝ boot modernized for the nineties */ #define GRUB_MAGIC 0x1BADB002 #define GRUB_EAX 0x2BADB002 #define GRUB_AOUT (1 << 16) #define GRUB_CHECKSUM(FLAGS) (-(GRUB_MAGIC + (FLAGS)) & 0xffffffff) / Grub Header. .align 4 ape.grub: .long GRUB_MAGIC # Magic .long GRUB_AOUT # Flags .long GRUB_CHECKSUM(GRUB_AOUT) # Checksum .long RVA(ape.grub) # HeaderPhysicalAddress .long IMAGE_BASE_PHYSICAL # TextPhysicalAddress .long PHYSICAL(_edata) # LoadEndPhysicalAddress .long PHYSICAL(_end) # BssEndPhysicalAddress .long RVA(ape.grub.entry) # EntryPhysicalAddress .endobj ape.grub,globl,hidden / Grub Entrypoint. / Takes CPU out of legacy mode and jumps to normal entrypoint. / @noreturn .align 4 ape.grub.entry: .code32 cmp $GRUB_EAX,%eax jne triplf push $0 popf mov $0x40,%dl mov %cr0,%eax and $~CR0_PE,%eax mov %eax,%cr0 ljmpw $0,$REAL(pc) .code16 .endfn ape.grub.entry /*─────────────────────────────────────────────────────────────────────────────╗ │ αcτµαlly pδrταblε εxεcµταblε § real mode │ ╚──────────────────────────────────────────────────────────────────────────────╝ the default mode of operation on modern cpus */ nop nop nop nop realmodeloader: push %bp mov %sp,%bp call rlinit call sinit4 mov $VIDYA_MODE,%di mov %es,XLM(VIDEO_POSITION_FAR_POINTER) mov %ax,XLM(VIDEO_POSITION_FAR_POINTER)+2 call vinit mov $REAL(.Lstr.ape),%di call rvputs .optfn _start16 call _start16 call longmodeloader .endfn realmodeloader,globl,hidden .section .sort.text.real.init.1,"ax",@progbits .type rrinit,@function rlinit: push %bp mov %sp,%bp .previous/* ... decentralized function ... */.section .sort.text.real.init.3,"ax",@progbits pop %bp ret .previous / Initializes present PC serial lines. sinit4: push %bp mov %sp,%bp movw $4,%cx movw $kBiosDataAreaXlm+COM1,%si 0: lodsb mov %al,%dl lodsb mov %al,%dh test %dx,%dx jz 1f push %cx push %si mov %dx,%di movw $REAL(sconf),%si call sinit pop %si pop %cx 1: loop 0b pop %bp ret .endfn sinit4,global,hidden / Initializes Serial Line Communications 8250 UART 16550A / / @param word di tty port / @param char (*{es:,e,r}si)[4] register initial values / @mode long,legacy,real / @see www.lammertbies.nl/comm/info/serial-uart.html sinit: push %bp mov %sp,%bp mov %di,%dx test %dx,%dx jz 2f push %dx push %si xorw %cx,%cx mov $UART_LCR,%cl add %cx,%dx lodsb %ds:(%si),%al pop %si or $UART_DLAB,%al out %al,%dx pop %dx 1: lodsb %ds:(%si),%al out %al,%dx add $1,%dx sub $1,%cx jns 1b 2: pop %bp ret .endfn sinit,global,hidden / Abnormally exits program. / / @param di message / @mode real / @noreturn rldie: push %bp mov %sp,%bp call rlpute call rloff .endfn rldie,globl,hidden / Shuts down machine. / / @mode real / @noreturn rloff: push %bp mov %sp,%bp mov $kBiosDataAreaXlm+COM1,%di mov $4,%si call sflush call apmoff .endfn rloff,globl,hidden / Prints error message. / / @param di message / @mode real rlpute: push %bp mov %sp,%bp mov kBiosDataAreaXlm+METAL_STDERR(%bx),%si test %si,%si jnz 1f mov kBiosDataAreaXlm+METAL_STDOUT,%si 1: xor %ax,%ax push %ax push %si mov $REAL(.Lstr.crlf),%ax push %ax push %si push %di push %si mov $REAL(.Lstr.error),%ax push %ax 1: pop %di test %di,%di jz 2f pop %si call rlput2 jmp 1b 2: pop %bp ret .endfn rlpute,globl,hidden / Prints string to both video and serial. / / @param di NUL-terminated string / @param si serial port / @mode real rlput2: push %bp mov %sp,%bp push %di push %si call rvputs pop %si pop %di test %si,%si jz 1f call sputs 1: pop %bp ret .endfn rlput2,globl,hidden / Video put string. / / @param di is the string / @mode real rvputs: push %bp mov %sp,%bp mov %di,%si les XLM(VIDEO_POSITION_FAR_POINTER),%di call vputs mov %es,XLM(VIDEO_POSITION_FAR_POINTER) mov %ax,XLM(VIDEO_POSITION_FAR_POINTER)+2 pop %bp ret .endfn rvputs,globl,hidden / Writes string to serial line. / / @param di NUL-terminated string / @param si serial port / @mode long,legacy,real sputs: push %bp mov %sp,%bp push %bx mov %di,%bx 1: xchg %bx,%si lodsb xchg %bx,%si test %al,%al jz 2f mov %ax,%di push %si rlcall sputc pop %si jmp 1b 2: pop %bx pop %bp ret .endfn sputs,globl / Waits for serial lines to become idle. / / @param di short array of serial ports (0 means not present) / @param si number of items in array / @mode long,legacy,real sflush: mov %si,%cx mov %di,%si xor %dx,%dx 0: lodsb mov %al,%dl lodsb mov %al,%dh test %ax,%ax jz 2f add $UART_LSR,%dx mov $UART_TTYIDL,%ah 1: in %dx,%al and %ah,%al rep nop # todo(jart): interrupts are better jz 1b loop 0b 2: ret .endfn sflush,globl / Transmits byte over serial line. / / This is both blocking and asynchronous. / / @param di character to send / @param si serial port / @mode long,legacy,real / @see ttytxr sputc: push %ax push %cx push %dx mov %si,%dx add $UART_LSR,%dx mov $UART_TTYTXR,%ah 1: in %dx,%al and %ah,%al jnz 2f rep nop # todo(jart): interrupts are better jmp 1b 2: mov %di,%ax mov %si,%dx out %al,%dx pop %dx pop %cx pop %ax ret .endfn sputc,globl / Asks BIOS to initialize MDA/CGA display w/o cursor/blinking. / / @param dil video mode / @return es:ax start of video page / @mode real vinit: push %bp mov %sp,%bp push %bx bbmov VIDYA_ADDR_CGA>>4,%ax,%ah,%al mov %ax,%es mov %di,%ax cmp $VIDYA_MODE_CGA,%al je 1f xor %ch,%ch # aka movesdi VIDYA_ADDR_MDA 1: xor %di,%di bbmov VIDYA_SET_MODE,%ax,%ah,%al int $VIDYA_SERVICE bbmov VIDYA_SET_CURSOR,,%ah,%al bbmov VIDYA_SET_CURSOR_NONE,,%ch,%cl # since we don't support it int $VIDYA_SERVICE bbmov VIDYA_SET_BLINKING,,%ah,%al # ← cargo culting (ega?) bbmov VIDYA_SET_BLINKING_NONE,,%bh,%bl int $VIDYA_SERVICE xor %ax,%ax pop %bx pop %bp ret .endfn vinit,globl / Prints byte to display w/ teletype emulation. / / @param es:di screen position / @param sil byte / @return es:ax new screen position / @mode long,legacy,real vputc: push %bp mov %sp,%bp sub $16,%sp # goal is to turn sil into a buffer xchg %si,%ax # modrm sooo different in real mode mov %di,%cx mov %sp,%si mov %sp,%di stosb mov %cx,%di pushpop 1,%dx jmp 23f / Prints string to display w/ teletype emulation. / / @param es:di screen position / @param si NUL-terminated string / @return es:ax new screen position / @mode long,legacy,real vputs: push %si # inlined strlen 1: lodsb test %al,%al jnz 1b mov %si,%dx pop %si sub %si,%dx / fallthrough / Prints data to display w/ teletype emulation. / / @param es:di screen position / @param si data address / @param dx data size in bytes / @return es:ax new screen position / @mode long,legacy,real vtput: push %bp mov %sp,%bp 23: push %bx mov %dx,%cx mov %di,%dx bband VIDYA_REWIND,%dh,%dl bbadd VIDYA_SIZE,%dh,%dl bbmov VIDYA_COLUMNS*2,%bx,%bh,%bl 0: cmp %di,%dx je 6f ja 3f lodsb # todo: utf8 → cp437 cmp $'\n,%al je 4f cmp $'\r,%al je 5f 1: stosb mov $VIDYA_ATTR_NORMAL,%al # todo: ansi color stosb 2: loop 0b 3: mov %di,%ax pop %bx pop %bp ret 4: add %bx,%di # line feed jmp 2b 5: mov %di,%ax # carriage return push %dx xor %dx,%dx idiv %bx # todo: division is deprecated sub %dx,%di pop %dx jmp 2b 6: push %ax push %cx push %dx push %si mov %bx,%si rlcall vscroll mov %ax,%di pop %si pop %dx pop %cx pop %ax jmp 2b .endfn vtput,globl .endfn vputs,globl .endfn vputc,globl / Scrolls up content in display page. / / @param es:di cursor address (bytes after aren't moved) / @param si byte difference, e.g. VIDYA_COLUMNS*2 / @return es:ax new cursor address (which is es:di-si) / @mode long,legacy,real vscroll:not %dx mov %di,%cx bband VIDYA_REWIND,%ch,%cl xchg %cx,%di # di is now page addr, i.e. top-left sub %si,%cx # cx is now future cursor address push %cx sub %di,%cx # cx is now memcpy size mov %di,%si add %cx,%si mov $0,%ax movpp %ds,%es rep movsb pop %ax ret .endfn vscroll,globl / Shuts down personal computer. / / @mode real / @noreturn apmoff: push %bp mov %sp,%bp mov $0x5300,%ax # apm installation check xor %bx,%bx # for the apm bios itself int $APM_SERVICE jc 1f cmp $'P<<8|'M,%bx # did apm bios service interrupt? jne 1f mov $0x5301,%ax # real mode interface connect xor %bx,%bx # to apm bios device int $APM_SERVICE # ignore errors e.g. already connected xor %bx,%bx xor %cx,%cx mov $0x5307,%ax # set power state mov $1,%bl # for all devices within my dominion mov $3,%cl # to off int $APM_SERVICE 1: call panic .endfn apmoff,globl /* █ █▒ █ █ █▓▄ █ █ ▒▓ ░█░ ░▒▓▓███░ █▒ ▒▓ ▒▓███▓▒░ ░▓ █ ▓░ ▄██░ █ ▓▓ ▓██░ ▓█░▓█ ▒▓ ▒█ ░█ █ ░▓ █ █ █░ █ ░▓ ▀▒ █░ █ █▒ ▀▒░ ▓▓ ▄▄▄▓████▓▓▒░ █ ▄▄▄▄▓▓▓█▓▓▓▓▒░ ▒█ ▄▓▓▀ ▒ ░█ ▄▓▀ ░███▒ ▓▀ ░░ ▀▓▄▄▄▒▒ █ █ ░░█▒ ▒█ ▒▓ █ ▀█ ▄▄▄▄▄▄▄▄▄▄ ▀▀░▓██▓ █ ▀■▄▄▄■▀ ▀▀█▄ █ ▀▀█▄ █ ▀█▄ █ █▌ █ █▌ █ █▌ █ █▌ █ █▌ █ █▌ █ █▌ █ █▌ █ █▌ █ █▌ █ █▌ █ █▌ █ █▌ █ █▌ █ █▌ █ █▌ █ █▌ █ █▌ █ █▌ █ █▌ █ █▌ █ █▌ █ ╔──────────────────────────────────────────────────────────────────────────▀─│─╗ │ αcτµαlly pδrταblε εxεcµταblε § long mode loader ─╬─│┼ ╚────────────────────────────────────────────────────────────────────────────│─╝ long mode is long */ longmodeloader: push %bp mov %sp,%bp call lcheck call a20 mov $XLM(E820),%di mov $XLM_E820_SIZE,%si call e820 jc 9f call unreal call hiload call golong 9: mov $REAL(.Lstr.e820),%ax call rldie .endfn longmodeloader,globl,hidden / Long Mode Hardware Check lcheck: push %bp mov %sp,%bp pushfw # check for i8086 / i8088 / i80186 pop %ax and $0b111<<12,%ax # see intel manual volume 1 §19.1.2 jnz 9f pushfl # now check for later model of 80486 pop %eax # test ability to *change* cpuid bit mov %eax,%ecx mov $1<<21,%ebx xor %ebx,%eax push %eax popfl pushfl pop %eax cmp %eax,%ecx je 12f and %ebx,%eax # puts cpuid bit in the on position push %eax popfl mov $0x80000000,%edi # get amd ext cpuid thingy length mov %edi,%eax inc %edi cpuid # clobbers eax, ebx, ecx, and edx cmp %edi,%eax jl 10f mov %edi,%eax cpuid mov $1<<29/*LM*/,%edi # check for nexgen32e support and %edi,%edx cmp %edi,%edx jne 10f xor %ax,%ax 1: pop %bp ret 9: mov $REAL(.Lstr.oldskool),%ax jmp 20f 10: mov $REAL(.Lstr.long),%ax jmp 20f 12: mov $REAL(.Lstr.cpuid),%ax jmp 20f 20: call rldie .endfn lcheck / Gets memory map from BIOS. / / @param di paragraph aligned buffer / @param si bytes in buffer to fill / @return number of bytes written or CF on error / @mode real e820: push %bp mov %sp,%bp pushl $'S<<24|'M<<16|'A<<8|'P # magic @ -4(%bp) push %bx shr $4,%di mov %di,%es xor %edi,%edi # es:di is destination buffer xor %ebx,%ebx # ebx is an api state tracker 1: mov $0xE820,%eax # magic mov $8+8+4+4,%ecx # sizeof(struct SmapEntry) mov -4(%bp),%edx # magic int $0x15 # ax,bx,cx,dx,di → ax,bx,cx jc 9f # cf = unsupported or abuse cmp -4(%bp),%eax # more magic means success jne 9f test %cx,%cx # discard empty results jz 5f cmp $8+8+4+1,%cx # discard if ignore flag jb 4f testb $1/*ignore*/,8+8+4/*SmapEntry::__acpi3*/(%di) jnz 5f 4: add $8+8+4+4,%di # keep entry 5: test %ebx,%ebx # last entry? jz 7f cmp %si,%di # out of buf? jb 1b 7: mov %di,%ax 8: pop %bx leave ret 9: stc jmp 8b .endfn e820,globl,hidden / Unreal Mode. / Makes 4gb of real memory accessible via %fs segment. unreal: push %bp mov %sp,%bp cli lgdt REAL(gdt) mov %cr0,%eax or $CR0_PE,%al mov %eax,%cr0 jmp 1f 1: mov $GDT_LEGACY_DATA,%cx mov %cx,%fs and $~CR0_PE,%al mov %eax,%cr0 ljmpl $0,$REAL(1f) 1: sti pop %bp ret .endfn unreal / Loads remainder of executable off disk. hiload: push %bp mov %sp,%bp push %bx mov $IMAGE_BASE_REAL,%esi # relocate, again mov $IMAGE_BASE_PHYSICAL,%ebx mov $v_ape_realsectors,%ecx shl $9,%ecx or $-4,%edx 0: add $4,%edx cmp %edx,%ecx je 1f mov %fs:(%esi,%edx),%eax mov %eax,%fs:(%ebx,%edx) jmp 0b 1: lea (%ebx,%edx),%ebx mov $v_ape_highsectors,%di # then copy rest off disk mov $REAL_SCRATCH_AREA>>4,%ax # to real memory buffer mov %ax,%es mov XLM(LOADSTATE)+0,%cx mov XLM(LOADSTATE)+2,%dx 0: test %di,%di jz 9f mov %di,%ax push %bx xor %bx,%bx call pcread pop %bx sub %ax,%di push %cx mov %ax,%cx # copy real buffer to high shl $9,%cx # no way bios loaded >64k xor %si,%si 1: mov %es:(%si),%eax mov %eax,%fs:(%ebx) add $4,%ebx add $4,%si sub $4,%cx jnz 1b pop %cx jmp 0b 9: pop %bx pop %bp ret .endfn hiload / Asks keyboard to grant system 65,519 more bytes of memory. / / Yup. / / @assume realmode && df=0 / @clob ax,di,si,es,flags / @mode real / @see wiki.osdev.org/A20_Line a20: cli push %ds xor %ax,%ax mov %ax,%es dec %ax mov %ax,%ds mov $0x0500,%di mov $0x0510,%si mov %es:(%di),%al push %ax mov %ds:(%si),%al push %ax movb $0x00,%es:(%di) movb $0xff,%ds:(%si) cmpb $0xff,%es:(%di) pop %ax mov %al,%ds:(%si) pop %ax mov %al,%es:(%di) pop %ds jne 3f mov $1,%ax call 1f mov $0xad,%al out %al,$0x64 call 1f mov $0xd0,%al out %al,$0x64 call 2f in $0x60,%al push %ax call 1f mov $0xd1,%al out %al,$0x64 call 1f pop %ax or $2,%al out %al,$0x60 call 1f mov $0xae,%al out %al,$0x64 call 1f jmp a20 1: in $0x64,%al test $2,%al jnz 1b ret 2: in $0x64,%al test $1,%al jz 2b ret 3: sti 5: ret .endfn a20,globl,hidden # obj since waste of objdump space / Initializes long mode paging. / / Modern computers access memory via four levels of indirection: / / register char (*(*(*(*ram)[512])[512])[512])[4096] asm(cr3) / / Your page tables grow down in memory, starting from the real / stack segment base. This function only defines enough tables / to get us started. #define TIP REAL_STACK_FRAME pinit: push %bp mov %sp,%bp push %ds mov $(TIP-0x4000)>>4,%ax mov %ax,%ds movl $TIP-0x2000+PAGE_V+PAGE_RW,%ds:0x3000 # PML4T→PDPT movl $TIP-0x3000+PAGE_V+PAGE_RW,%ds:0x2000 # PDPT→PDT movl $TIP-0x4000+PAGE_V+PAGE_RW,%ds:0x1000 # PDT→PD mov $0x100000/0x1000,%cx # PD→512kb mov $PAGE_V+PAGE_RW,%eax xor %si,%si 0: mov %eax,%ds:(%si) add $0x1000,%eax add $8,%si loop 0b pop %ds movl $TIP-0x4000,XLM(PAGE_TABLE_STACK_POINTER) # STACK→XLM mov $TIP-0x1000,%eax # PML4T→CR3 mov %eax,%cr3 pop %bp ret .endfn pinit,globl,hidden / Switch from Real Mode → Long Mode / / @see Intel Manual V3A §4.1.2 golong: cli lidt XLM(BADIDT) call pinit mov %cr4,%eax or $CR4_PAE|CR4_PGE|CR4_OSFXSR,%eax mov %eax,%cr4 movl $EFER,%ecx rdmsr or $EFER_LME|EFER_SCE,%eax wrmsr lgdt REAL(gdt) mov %cr0,%eax or $CR0_PE|CR0_PG|CR0_MP,%eax and $~CR0_EM,%eax mov %eax,%cr0 ljmp $GDT_LONG_CODE,$REAL(1f) .code64 1: mov $GDT_LONG_DATA,%eax mov %ax,%ds mov %ax,%fs mov %ax,%gs xor %edx,%edx jmp long .endfn golong / Long mode is long. / / @noreturn long: .frame0 xor %edi,%edi call pageunmap mov $e820map_xlm,%edi call smapsort mov $e820map_xlm,%edi mov $g_pml4t,%esi mov $g_ptsp_xlm,%edx call flattenhighmemory mov $REAL(.Lstr.hello),%edi mov kBiosDataAreaXlm+METAL_STDOUT,%esi call sputs mov $kBiosDataAreaXlm+METAL_STDOUT,%edi mov $4,%esi call sflush jmp triplf lea triplf(%rip),%rdx call _start jmp triplf .endfn long / Avoid linker script variables appearing as code in objdump. .macro .ldsvar name:req .type \name,@object .weak \name .endm .ldsvar _end .ldsvar _etext .ldsvar v_ape_realsectors .ldsvar v_ape_highsectors .ldsvar idata.ro .ldsvar ape.pad.rodata .ldsvar ape.piro .ldsvar ape.piro.end .type .Lape.macho.end,@object .type .Lape.note,@object .type .Lape.note.end,@object .type .Lape.note.vaddr,@object .type .Lape.pe.sections,@object .type .Lape.pe.sections_end,@object .type .Lape.text.nops,@object .type __test_end,@object .section .commentprologue,"a",@progbits .type kLegalNotices,@object .hidden kLegalNotices kLegalNotices:/* ... decentralized content ... */.previous .section .commentepilogue,"a",@progbits .byte 0 .previous .section .ape.pad.head,"a",@progbits .type ape.pad.head,@object .hidden ape.pad.head ape.pad.head: .previous .section .ape.pad.text,"a",@progbits .type ape.pad.text,@object .hidden ape.pad.text ape.pad.text: .previous .section .ape.pad.privileged,"a",@progbits .type ape.pad.privileged,@object .hidden ape.pad.privileged ape.pad.privileged: .previous .section .ape.pad.test,"a",@progbits .type ape.pad.test,@object .hidden ape.pad.test ape.pad.test: .previous .section .ape.pad.test,"a",@progbits .type ape.pad.test,@object .hidden ape.pad.test ape.pad.test: .previous .section .ape.pad.rodata,"a",@progbits .type ape.pad.rodata,@object .hidden ape.pad.rodata ape.pad.rodata: .previous .section .ape.pad.data,"a",@progbits .type ape.pad.data,@object .hidden ape.pad.data ape.pad.data: .previous .section .idata.ro,"a",@progbits .type idata.ro,@object .hidden idata.ro idata.ro: .previous .section .dataprologue,"aw",@progbits .type __data_start,@object .globl __data_start .hidden __data_start __data_start: .previous .type __piro_start,@object .hidden __piro_start .end