diff --git a/ape/ape.S b/ape/ape.S index 48e25aaae..acddc38a0 100644 --- a/ape/ape.S +++ b/ape/ape.S @@ -542,27 +542,35 @@ apesh: .ascii "'\n#'\"\n" # sixth edition shebang // present two choices. .ascii "o=\"$(command -v \"$0\")\"\n" // Try to use a system-wide APE loader. - .ascii "type ape-loader >/dev/null 2>&1 && " - .ascii "exec ape-loader \"$o\" \"$@\"\n" + .ascii "type ape >/dev/null 2>&1 && " + .ascii "exec ape \"$o\" \"$@\"\n" #ifdef APE_LOADER // There is no system-wide APE loader, but there is one // embedded inside the APE. So if the system is not MacOs, // extract the loader into a temp folder, and use it to // load the APE without modifying it. - .ascii "if [ ! -d /Applications ]; then\n" - .ascii "t=\"${TMPDIR:-/tmp}/ape-loader\"\n" - .ascii "[ -x \"$t\" ] || {\n" + .ascii "t=\"${TMPDIR:-/tmp}/ape\"\n" + .ascii "if [ ! -x \"$t\" ]; then\n" + .ascii "if [ ! -d /Applications ]; then\n" .ascii "dd if=\"$o\" of=\"$t.$$\" skip=\"" .shstub ape_loader_dd_skip,2 .ascii "\" count=\"" .shstub ape_loader_dd_count,2 - .ascii "\" bs=64 2>/dev/null &&\n" - .ascii "chmod 755 \"$t.$$\" &&\n" - .ascii "mv \"$t.$$\" \"$t\"\n" - .ascii "}\n" - .ascii "exec \"$t\" \"$o\" \"$@\"\n" + .ascii "\" bs=64\n" +#if SupportsXnu() && defined(APE_LOADER_MACHO) + .ascii "else\n" + .ascii "dd if=\"$o\" of=\"$t.$$\" skip=\"" + .shstub ape_loader_macho_dd_skip,2 + .ascii "\" count=\"" + .shstub ape_loader_macho_dd_count,2 + .ascii "\" bs=64\n" +#endif /* APE_LOADER_MACHO */ + .ascii "fi 2>/dev/null &&\n" + .ascii "chmod 755 \"$t.$$\" &&\n" + .ascii "mv \"$t.$$\" \"$t\"\n" .ascii "fi\n" -#endif + .ascii "exec \"$t\" \"$o\" \"$@\"\n" +#endif /* APE_LOADER */ #ifndef APE_NO_MODIFY_SELF // The default behavior is: to overwrite the header in place. // We prefer this because it's a tiny constant one time cost. @@ -656,6 +664,11 @@ apesh: .ascii "'\n#'\"\n" # sixth edition shebang .incbin APE_LOADER .previous #endif /* APE_LOADER */ +#if SupportsXnu() && defined(APE_LOADER_MACHO) + .section .ape.loader-macho,"a",@progbits + .incbin APE_LOADER_MACHO + .previous +#endif /* APE_LOADER_MACHO */ #endif /* SupportsWindows() || SupportsMetal() || SupportsXnu() */ #if SupportsSystemv() || SupportsMetal() @@ -856,7 +869,7 @@ ape_macho: .long (520f-510f)/4 # count 510: .quad 0 # rax .quad IMAGE_BASE_VIRTUAL # rbx - .quad 0 # rcx + .quad XNU # rcx .quad 0 # rdx .quad 0 # rdi .quad 0 # rsi @@ -870,7 +883,7 @@ ape_macho: .quad 0 # r13 .quad 0 # r14 .quad 0 # r15 - .quad _xnu # rip + .quad _start # rip .quad 0 # rflags .quad 0 # cs .quad 0 # fs diff --git a/ape/ape.lds b/ape/ape.lds index 4e00ad30d..f37234a7b 100644 --- a/ape/ape.lds +++ b/ape/ape.lds @@ -397,6 +397,12 @@ SECTIONS { KEEP(*(.ape.loader)) . = ALIGN(64); HIDDEN(ape_loader_end = .); +#if SupportsXnu() + HIDDEN(ape_loader_macho = .); + KEEP(*(.ape.loader-macho)) + . = ALIGN(64); + HIDDEN(ape_loader_macho_end = .); +#endif } /*END: payload */ /*BEGIN: bss memory void */ @@ -549,6 +555,11 @@ HIDDEN(ape_bss_align = PAGESIZE); SHSTUB2(ape_loader_dd_skip, RVA(ape_loader) / 64); SHSTUB2(ape_loader_dd_count, (ape_loader_end - ape_loader) / 64); +#if SupportsXnu() +SHSTUB2(ape_loader_macho_dd_skip, RVA(ape_loader_macho) / 64); +SHSTUB2(ape_loader_macho_dd_count, (ape_loader_macho_end - ape_loader_macho) / 64); +#endif + #if SupportsXnu() SHSTUB2(ape_macho_dd_skip, RVA(ape_macho) / 8); SHSTUB2(ape_macho_dd_count, (ape_macho_end - ape_macho) / 8); diff --git a/ape/ape.mk b/ape/ape.mk index 6302a2804..590075703 100644 --- a/ape/ape.mk +++ b/ape/ape.mk @@ -15,18 +15,18 @@ PKGS += APE -APE = o/$(MODE)/ape/ape.o \ +APE = o/$(MODE)/ape/ape.o \ o/$(MODE)/ape/ape.lds -APE_NO_MODIFY_SELF = \ - o/$(MODE)/ape/ape.lds \ +APE_NO_MODIFY_SELF = \ + o/$(MODE)/ape/ape.lds \ o/$(MODE)/ape/ape-no-modify-self.o -APELINK = \ - $(COMPILE) \ - -ALINK.ape \ - $(LINK) \ - $(LINKARGS) \ +APELINK = \ + $(COMPILE) \ + -ALINK.ape \ + $(LINK) \ + $(LINKARGS) \ $(OUTPUT_OPTION) APE_FILES := $(wildcard ape/*.*) @@ -38,32 +38,43 @@ APE_SRCS = $(APE_SRCS_C) $(APE_SRCS_S) APE_OBJS = $(APE_SRCS_S:%.S=o/$(MODE)/%.o) APE_CHECKS = $(APE_HDRS:%=o/%.ok) -o/$(MODE)/ape/ape.lds: \ - ape/ape.lds \ - ape/macros.internal.h \ - libc/dce.h \ +o/$(MODE)/ape/ape.lds: \ + ape/ape.lds \ + ape/macros.internal.h \ + libc/dce.h \ libc/zip.h -o/ape/idata.inc: \ - ape/idata.internal.h \ +o/ape/idata.inc: \ + ape/idata.internal.h \ ape/relocations.h -o/$(MODE)/ape/ape-no-modify-self.o: ape/ape.S o/$(MODE)/ape/loader.elf - @$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -DAPE_LOADER="\"o/$(MODE)/ape/loader.elf\"" -DAPE_NO_MODIFY_SELF $< +o/$(MODE)/ape/ape-no-modify-self.o: \ + ape/ape.S \ + o/$(MODE)/ape/ape \ + o/$(MODE)/ape/ape.macho + @$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -DAPE_LOADER="\"o/$(MODE)/ape/ape\"" -DAPE_LOADER_MACHO="\"o/$(MODE)/ape/ape.macho\"" $< o/$(MODE)/ape/loader.o: ape/loader.c - @$(COMPILE) -AOBJECTIFY.c $(CC) $(cpp.flags) -fpie -Os -ffreestanding -mno-red-zone -fno-ident -fno-gnu-unique -c $(OUTPUT_OPTION) $< + @$(COMPILE) -AOBJECTIFY.c $(CC) -DNDEBUG -iquote. -Wall -Wextra -fpie -Os -g -ffreestanding -mno-red-zone -fno-ident -fno-gnu-unique -c $(OUTPUT_OPTION) $< o/$(MODE)/ape/loader-gcc.asm: ape/loader.c - @$(COMPILE) -AOBJECTIFY.c $(CC) $(cpp.flags) -Os -ffreestanding -mno-red-zone -fno-ident -fno-gnu-unique -c -S $(OUTPUT_OPTION) $< + @$(COMPILE) -AOBJECTIFY.c $(CC) -DNDEBUG -iquote. -Wall -Wextra -fpie -Os -g -ffreestanding -mno-red-zone -fno-ident -fno-gnu-unique -c -S $(OUTPUT_OPTION) $< -o/$(MODE)/ape/loader.elf: \ - o/$(MODE)/ape/loader.o \ - o/$(MODE)/ape/loader1.o \ +o/$(MODE)/ape/ape: \ + o/$(MODE)/ape/loader.o \ + o/$(MODE)/ape/loader-elf.o \ ape/loader.lds @$(ELFLINK) -s -z max-page-size=0x10 +o/$(MODE)/ape/ape.macho: \ + o/$(MODE)/ape/loader.o \ + o/$(MODE)/ape/loader-macho.o \ + ape/loader-macho.lds + @$(ELFLINK) -s -z max-page-size=0x10 + .PHONY: o/$(MODE)/ape -o/$(MODE)/ape: $(APE) \ - $(APE_CHECKS) \ +o/$(MODE)/ape: $(APE) \ + $(APE_CHECKS) \ + o/$(MODE)/ape/ape \ + o/$(MODE)/ape/ape.macho \ o/$(MODE)/ape/ape-no-modify-self.o diff --git a/ape/apeinstall.sh b/ape/apeinstall.sh new file mode 100755 index 000000000..d5723f130 --- /dev/null +++ b/ape/apeinstall.sh @@ -0,0 +1,63 @@ +#!/bin/sh + +if ! [ x"$(uname -s)" = xLinux ]; then + echo this script is intended for linux binfmt_misc >&2 + echo freebsd/netbsd/openbsd users can use release binary >&2 + exit 1 +fi + +if [ -f o/depend ]; then + # mkdeps.com build was successfully run so assume we can build + echo >&2 + echo running: make -j8 o//ape/ape >&2 + make -j8 o//ape/ape || exit + echo done >&2 +else + # no evidence we can build, use prebuilt one + mkdir -p o//ape || exit + cp -af build/bootstrap/ape o//ape/ape +fi + +echo >&2 +echo installing o//ape/ape to /usr/bin/ape >&2 +echo sudo mv -f o//ape/ape /usr/bin/ape >&2 +sudo mv -f o//ape/ape /usr/bin/ape || exit +echo done >&2 + +if [ -e /proc/sys/fs/binfmt_misc/APE ]; then + echo >&2 + echo it looks like APE is already registered with binfmt_misc >&2 + echo please check that it is mapped to ape not /bin/sh >&2 + echo cat /proc/sys/fs/binfmt_misc/APE >&2 + cat /proc/sys/fs/binfmt_misc/APE >&2 + # TODO: we need better uninstall recommendations + # the following works fine for justine + # but might remove unrelated software? + # sudo sh -c 'echo -1 >/proc/sys/fs/binfmt_misc/status' + exit +fi + +if ! [ -e /proc/sys/fs/binfmt_misc ]; then + echo >&2 + echo loading binfmt_misc into your kernel >&2 + echo you may need to edit configs to persist across reboot >&2 + echo sudo modprobe binfmt_misc >&2 + sudo modprobe binfmt_misc || exit + echo done >&2 +fi + +if ! [ -e /proc/sys/fs/binfmt_misc/register ]; then + echo >&2 + echo mounting binfmt_misc into your kernel >&2 + echo you may need to edit configs to persist across reboot >&2 + echo sudo mount -t binfmt_misc none /proc/sys/fs/binfmt_misc >&2 + sudo mount -t binfmt_misc none /proc/sys/fs/binfmt_misc || exit + echo done >&2 +fi + +echo >&2 +echo registering APE with binfmt_misc >&2 +echo you may need to edit configs to persist across reboot >&2 +echo 'sudo sh -c "echo '"'"':APE:M::MZqFpD::/usr/bin/ape:'"'"' >/proc/sys/fs/binfmt_misc/register"' >&2 +sudo sh -c "echo ':APE:M::MZqFpD::/usr/bin/ape:' >/proc/sys/fs/binfmt_misc/register" || exit +echo done >&2 diff --git a/ape/loader1.S b/ape/loader-elf.S similarity index 71% rename from ape/loader1.S rename to ape/loader-elf.S index 3114ffdc4..9fafc4fd0 100644 --- a/ape/loader1.S +++ b/ape/loader-elf.S @@ -38,20 +38,54 @@ ehdr: .ascii "\177ELF" .long 0 # e_flags .word 64 # e_ehsize .word 56 # e_phentsize - .word 3 # e_phnum + .word 4 # e_phnum .word 0 # e_shentsize .word 0 # e_shnum .word 0 # e_shstrndx .endobj ehdr,globl -// memcpy(0x200000, loader); xor %eax,%eax; jmp 0x200000 +// Ape Loader Entrpoint +// +// This is normally called by the operating system. However it may +// be called by the Actually Portable Executables themselves, when +// re-executing a program. Just do this: +// +// memcpy(0x200000, loader) +// xor %eax,%eax +// inc %eax +// jmp 0x200000 +// +// @see APE_LOADER_ENTRY jg47h: .org 0x47 .endobj jg47h - _start: mov %rsp,%rsi - jmp loader + jmp ApeLoader .endfn _start,globl +// System Call Entrpoint +// +// This function is used by the APE loader to make system calls. +// We also pass a reference to this function to the APE binary's +// _start() function. It's needed because on OpenBSD, msyscall() +// restricts which pages can issue system calls, and it can only +// be called once. Therefore if we want to be load and re-load a +// binary multiple times without calling the system execve(), we +// need to be able to handover the SYSCALL function. We hardcode +// this to a fixed address, but that shouldn't be used, since we +// would ideally want to move it to a random page in the future. +// +// @see APE_LOADER_SYSCALL +sc50h: .org 0x50 + .endobj sc50h +__syscall_loader: + clc + syscall + jc 1f + ret +1: neg %rax + ret + .endfn __syscall_loader,globl + .align 8 phdrs: .long PT_LOAD # p_type .long PF_R|PF_X # p_flags @@ -61,6 +95,16 @@ phdrs: .long PT_LOAD # p_type .quad filesz # p_filesz .quad filesz # p_memsz .quad PAGESIZE # p_align + + .long PT_LOAD # p_type + .long PF_R|PF_W # p_flags + .quad 0 # p_offset + .quad bss # p_vaddr + .quad bss # p_paddr + .quad 0 # p_filesz + .quad bsssize # p_memsz + .quad PAGESIZE # p_align + .long PT_GNU_STACK # p_type .long PF_R|PF_W # p_flags .quad 0 # p_offset @@ -69,6 +113,7 @@ phdrs: .long PT_LOAD # p_type .quad 0 # p_filesz .quad 0 # p_memsz .quad 16 # p_align + .long PT_NOTE # p_type .long PF_R # p_flags .quad note - ehdr # p_offset diff --git a/ape/loader-macho.S b/ape/loader-macho.S new file mode 100644 index 000000000..656a05b87 --- /dev/null +++ b/ape/loader-macho.S @@ -0,0 +1,127 @@ +/*-*- 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 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/macho.internal.h" +#include "libc/sysv/consts/prot.h" +#include "libc/dce.h" +#include "libc/macros.internal.h" + +// APE Loader Executable Structure for XNU + + .align 4096 +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 + .quad macho # vaddr + .quad filesz # memsz + .quad 0 # file offset + .quad filesz # file size + .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 + .quad _start # vaddr + .quad textsz # memsz + .long textoff # offset + .long 3 # align 2**3 = 8 + .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 + .quad bss # vaddr + .quad bsssize # memsz + .quad 0 # offset + .quad 0 # file size + .long PROT_EXEC|PROT_READ|PROT_WRITE # maxprot + .long PROT_READ|PROT_WRITE # initprot + .long 1 # segment section count + .long 0 # flags +310: .ascin "__bss",16 # section name (.bss) + .ascin "__DATA",16 + .quad bss # vaddr + .quad bsssize # memsz + .long 0 # 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 + .quad 0x3fb29ee4ac6c87aa # uuid1 + .quad 0xdd2c9bb866d9eef8 # uuid2 +50: .long MAC_LC_UNIXTHREAD + .long 60f-50b # cmdsize + .long MAC_THREAD_NEXGEN32E # flavaflav + .long (520f-510f)/4 # count +510: .quad 0 # rax + .quad 0 # rbx + .quad 0 # rcx + .quad XNU # 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 # rip + .quad 0 # rflags + .quad 0 # cs + .quad 0 # fs + .quad 0 # gs +520: +60: + .endobj macho,globl + + .align 8 +_start: mov %rsp,%rsi + jmp ApeLoader + .endfn _start,globl + +__syscall_loader: + clc + syscall + jc 1f + ret +1: neg %rax + ret + .endfn __syscall_loader,globl diff --git a/ape/loader-macho.lds b/ape/loader-macho.lds new file mode 100644 index 000000000..c8f36b161 --- /dev/null +++ b/ape/loader-macho.lds @@ -0,0 +1,43 @@ +/*-*- mode: ld-script; indent-tabs-mode: nil; tab-width: 2; coding: utf-8 -*-│ +│vi: set et sts=2 tw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +ENTRY(_start) +OUTPUT_FORMAT(binary) + +SECTIONS { + . = 0x200000; + .text : { + *(.text) + *(.rodata .rodata.*) + . = ALIGN(4096); + } + filesz = . - macho; + textsz = . - _start; + .bss ALIGN(4096) : { + bss = .; + *(.bss) + . = ALIGN(4096); + } + memsz = . - macho; + /DISCARD/ : { + *(.*) + } +} + +bsssize = SIZEOF(.bss); +textoff = _start - macho; diff --git a/ape/loader.c b/ape/loader.c index c79b01757..413641fd9 100644 --- a/ape/loader.c +++ b/ape/loader.c @@ -16,28 +16,74 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/bits/bits.h" -#include "libc/calls/struct/metastat.internal.h" -#include "libc/calls/struct/stat.h" -#include "libc/elf/def.h" -#include "libc/elf/struct/ehdr.h" -#include "libc/elf/struct/phdr.h" -#include "libc/sysv/consts/prot.h" +#include "ape/loader.h" + +#define TROUBLESHOOT 0 +#define TROUBLESHOOT_OS LINUX /** - * @fileoverview APE embeddable loader for Linux and BSD, e.g. + * @fileoverview APE Loader for GNU/Systemd and FreeBSD/NetBSD/OpenBSD + * + * We recommend using the normal APE design, where binaries assimilate + * themselves once by self-modifying the first 64 bytes. If that can't + * meet your requirements then we provide an excellent alternative. * * m=tiny * make -j8 MODE=$m o/$m/ape o/$m/examples/printargs.com - * o/$m/ape/loader.elf o/$m/examples/printargs.com + * o/$m/ape/ape o/$m/examples/printargs.com + * + * This is an embeddable Actually Portable Executable interpreter. The + * `ape/ape.S` bootloader embeds this binary inside each binary that's + * linked using `$(APE_NO_MODIFY_SELF)` so it is an automated seamless + * process. the shell script at the top of the .COM files will copy it + * to `${TMPDIR:-/tmp}/ape` and call execve(). It's a zero copy + * operation in praxis since this payload uses mmap() to load the rest + * of your executable the same way the kernel does, based on ELF phdrs + * which are located in accordance with the first sh printf statement. + * + * APE executables will look for this program on the system path first + * so your APE loader may be installed to your system as follows: + * + * m=tiny + * make -j8 MODE=$m o/$m/ape/ape + * sudo cp o/$m/ape/ape /usr/bin/ape + * + * Your APE loader may be used as a shebang interpreter by doing this: + * + * #!/usr/bin/ape python.com + * # -*- python -*- + * print("hello world") + * + * However you won't need to do that, if your APE Loader is registered + * as a binfmt_misc interpreter. You can do that as follows with root: + * + * sudo cp -f o/$m/ape/ape /usr/bin + * f=/proc/sys/fs/binfmt_misc/register + * sudo sh -c "echo ':APE:M::MZqFpD::/usr/bin/ape:' >$f" + * + * If the register file doesn't exist on your Linux machine then you'd + * load it using the following commands: + * + * sudo modprobe binfmt_misc + * sudo mount -t binfmt_misc none /proc/sys/fs/binfmt_misc + * + * You should now experience a performance boost, and you can also now + * use the regular shebang form: + * + * #!/usr/bin/python.com + * # -*- python -*- + * print("hello world") * * @note this can probably be used as a binfmt_misc interpreter */ -#define LINUX 0 -#define FREEBSD 1 -#define NETBSD 2 -#define OPENBSD 3 +#define LINUX 1 +#define METAL 2 +#define WINDOWS 4 +#define XNU 8 +#define OPENBSD 16 +#define FREEBSD 32 +#define NETBSD 64 #define O_RDONLY 0 #define PROT_READ 1 @@ -49,170 +95,394 @@ #define MAP_ANONYMOUS (os == LINUX ? 32 : 4096) #define AT_EXECFN_LINUX 31 #define AT_EXECFN_NETBSD 2014 +#define ELFCLASS64 2 +#define ELFDATA2LSB 1 +#define EM_NEXGEN32E 62 +#define ET_EXEC 2 +#define PT_LOAD 1 +#define PT_DYNAMIC 2 +#define EI_CLASS 4 +#define EI_DATA 5 +#define PF_X 1 +#define PF_W 2 +#define PF_R 4 +#define X_OK 1 +#define XCR0_SSE 2 +#define XCR0_AVX 4 -#define __NR_read (os == LINUX ? 0 : 3) -#define __NR_write (os == LINUX ? 1 : 4) -#define __NR_open (os == LINUX ? 2 : 5) -#define __NR_close (os == LINUX ? 3 : 6) -#define __NR_exit (os == LINUX ? 60 : 1) -#define __NR_mmap (os == LINUX ? 9 : os == FREEBSD ? 477 : 197) -#define __NR_fstat \ - (os == LINUX ? 5 : os == FREEBSD ? 551 : os == OPENBSD ? 53 : 440) +#define Read32(S) \ + ((unsigned)(255 & (S)[3]) << 030 | (unsigned)(255 & (S)[2]) << 020 | \ + (unsigned)(255 & (S)[1]) << 010 | (unsigned)(255 & (S)[0]) << 000) -static wontreturn void Exit(int os, long rc) { - asm volatile("syscall" - : /* no outputs */ - : "a"(__NR_exit), "D"(rc) - : "memory"); - unreachable; +#define Read64(S) \ + ((unsigned long)(255 & (S)[7]) << 070 | \ + (unsigned long)(255 & (S)[6]) << 060 | \ + (unsigned long)(255 & (S)[5]) << 050 | \ + (unsigned long)(255 & (S)[4]) << 040 | \ + (unsigned long)(255 & (S)[3]) << 030 | \ + (unsigned long)(255 & (S)[2]) << 020 | \ + (unsigned long)(255 & (S)[1]) << 010 | \ + (unsigned long)(255 & (S)[0]) << 000) + +struct PathSearcher { + char os; + unsigned long namelen; + const char *name; + const char *syspath; + char path[1024]; +}; + +struct ElfEhdr { + unsigned char e_ident[16]; + unsigned short e_type; + unsigned short e_machine; + unsigned e_version; + unsigned long e_entry; + unsigned long e_phoff; + unsigned long e_shoff; + unsigned e_flags; + unsigned short e_ehsize; + unsigned short e_phentsize; + unsigned short e_phnum; + unsigned short e_shentsize; + unsigned short e_shnum; + unsigned short e_shstrndx; +}; + +struct ElfPhdr { + unsigned p_type; + unsigned p_flags; + unsigned long p_offset; + unsigned long p_vaddr; + unsigned long p_paddr; + unsigned long p_filesz; + unsigned long p_memsz; + unsigned long p_align; +}; + +static void *syscall; +static struct PathSearcher ps; +extern char __syscall_loader[]; + +static int ToLower(int c) { + return 'A' <= c && c <= 'Z' ? c + ('a' - 'A') : c; } -static void Close(int os, long fd) { +static char *MemCpy(char *d, const char *s, unsigned long n) { + unsigned long i = 0; + for (; i < n; ++i) d[i] = s[i]; + return d + n; +} + +static unsigned long StrLen(const char *s) { + unsigned long n = 0; + while (*s++) ++n; + return n; +} + +static const char *MemChr(const char *s, unsigned char c, unsigned long n) { + for (; n; --n, ++s) { + if ((*s & 255) == c) { + return s; + } + } + return 0; +} + +static char *GetEnv(char **p, const char *s) { + unsigned long i, j; + if (p) { + for (i = 0; p[i]; ++i) { + for (j = 0;; ++j) { + if (!s[j]) { + if (p[i][j] == '=') { + return p[i] + j + 1; + } + break; + } + if (s[j] != p[i][j]) { + break; + } + } + } + } + return 0; +} + +static char *Utoa(char p[21], unsigned long x) { + char t; + unsigned long i, a, b; + i = 0; + do { + p[i++] = x % 10 + '0'; + x = x / 10; + } while (x > 0); + p[i] = '\0'; + if (i) { + for (a = 0, b = i - 1; a < b; ++a, --b) { + t = p[a]; + p[a] = p[b]; + p[b] = t; + } + } + return p + i; +} + +static char *Itoa(char p[21], long x) { + if (x < 0) *p++ = '-', x = -(unsigned long)x; + return Utoa(p, x); +} + +#if TROUBLESHOOT +const char *DescribeOs(int os) { + if (os == LINUX) { + return "GNU/SYSTEMD"; + } else if (os == XNU) { + return "XNU"; + } else if (os == FREEBSD) { + return "FREEBSD"; + } else if (os == OPENBSD) { + return "OPENBSD"; + } else if (os == NETBSD) { + return "NETBSD"; + } else { + return "WUT"; + } +} +#endif + +__attribute__((__noreturn__)) static void Exit(long rc, int os) { + asm volatile("call\t*%2" + : /* no outputs */ + : "a"((os == LINUX ? 60 : 1) | (os == XNU ? 0x2000000 : 0)), + "D"(rc), "m"(syscall) + : "memory"); + __builtin_unreachable(); +} + +static void Close(long fd, int os) { long ax, di; - asm volatile("syscall" + asm volatile("call\t*%4" : "=a"(ax), "=D"(di) - : "0"(__NR_close), "1"(fd) + : "0"((os == LINUX ? 3 : 6) | (os == XNU ? 0x2000000 : 0)), + "1"(fd), "m"(syscall) : "rcx", "rdx", "rsi", "r8", "r9", "r10", "r11", "memory", "cc"); } -static long Read(int os, long fd, void *data, unsigned long size) { - bool cf; +static long Read(long fd, void *data, unsigned long size, int os) { long ax, di, si, dx; - asm volatile("clc\n\t" - "syscall" - : "=@ccc"(cf), "=a"(ax), "=D"(di), "=S"(si), "=d"(dx) - : "1"(__NR_read), "2"(fd), "3"(data), "4"(size) + asm volatile("call\t*%8" + : "=a"(ax), "=D"(di), "=S"(si), "=d"(dx) + : "0"((os == LINUX ? 0 : 3) | (os == XNU ? 0x2000000 : 0)), + "1"(fd), "2"(data), "3"(size), "m"(syscall) : "rcx", "r8", "r9", "r10", "r11", "memory"); - if (cf) ax = -ax; return ax; } -static void Write(int os, long fd, const void *data, unsigned long size) { +static void Write(long fd, const void *data, unsigned long size, int os) { long ax, di, si, dx; - asm volatile("syscall" + asm volatile("call\t*%8" : "=a"(ax), "=D"(di), "=S"(si), "=d"(dx) - : "0"(__NR_write), "1"(fd), "2"(data), "3"(size) + : "0"((os == LINUX ? 1 : 4) | (os == XNU ? 0x2000000 : 0)), + "1"(fd), "2"(data), "3"(size), "m"(syscall) : "rcx", "r8", "r9", "r10", "r11", "memory", "cc"); } -static long Fstat(int os, long fd, union metastat *st) { - long ax, di, si; - asm volatile("syscall" - : "=a"(ax), "=D"(di), "=S"(si) - : "0"(__NR_fstat), "1"(fd), "2"(st) - : "rcx", "rdx", "r8", "r9", "r10", "r11", "memory", "cc"); +static void Execve(const char *prog, char **argv, char **envp, int os) { + long ax, di, si, dx; + asm volatile("call\t*%8" + : "=a"(ax), "=D"(di), "=S"(si), "=d"(dx) + : "0"((59) | (os == XNU ? 0x2000000 : 0)), "1"(prog), "2"(argv), + "3"(envp), "m"(syscall) + : "rcx", "r8", "r9", "r10", "r11", "memory", "cc"); +} + +static long Access(const char *path, int mode, int os) { + long ax, dx, di, si; + asm volatile("call\t*%7" + : "=a"(ax), "=D"(di), "=S"(si), "=d"(dx) + : "0"((os == LINUX ? 21 : 33) | (os == XNU ? 0x2000000 : 0)), + "1"(path), "2"(mode), "m"(syscall) + : "rcx", "r8", "r9", "r10", "r11", "memory", "cc"); return ax; } -static void Msyscall(int os, long p, long n) { +static void Msyscall(long p, long n, int os) { long ax, di, si; if (os == OPENBSD) { - asm volatile("syscall" + asm volatile("call\t*%6" : "=a"(ax), "=D"(di), "=S"(si) - : "0"(37), "1"(p), "2"(n) + : "0"(37), "1"(p), "2"(n), "m"(syscall) : "rcx", "rdx", "r8", "r9", "r10", "r11", "memory", "cc"); } } -static long Open(int os, const char *path, long flags, long mode) { - bool cf; +static long Open(const char *path, long flags, long mode, int os) { long ax, di, si, dx; - asm volatile("clc\n\t" - "syscall" - : "=@ccc"(cf), "=a"(ax), "=D"(di), "=S"(si), "=d"(dx) - : "1"(__NR_open), "2"(path), "3"(flags), "4"(mode) + asm volatile("call\t*%8" + : "=a"(ax), "=D"(di), "=S"(si), "=d"(dx) + : "0"((os == LINUX ? 2 : 5) | (os == XNU ? 0x2000000 : 0)), + "1"(path), "2"(flags), "3"(mode), "m"(syscall) : "rcx", "r8", "r9", "r10", "r11", "memory"); - if (cf) ax = -ax; return ax; } -static long Mmap(int os, long addr, long size, long prot, long flags, long fd, - long off) { - bool cf; +__attribute__((__noinline__)) long Mmap(long addr, long size, long prot, + long flags, long fd, long off, int os) { long ax; register long flags_ asm("r10") = flags; register long fd_ asm("r8") = fd; register long off_ asm("r9") = off; asm volatile("push\t%%r9\n\t" - "push\t%%r9\n\t" - "clc\n\t" - "syscall\n\t" - "pop\t%%r9\n\t" + "call\t*%8\n\t" "pop\t%%r9" - : "=@ccc"(cf), "=a"(ax) - : "1"(__NR_mmap), "D"(addr), "S"(size), "d"(prot), "r"(flags_), - "r"(fd_), "r"(off_) + : "=a"(ax) + : "0"((os == LINUX ? 9 + : os == FREEBSD ? 477 + : 197) | + (os == XNU ? 0x2000000 : 0)), + "D"(addr), "S"(size), "d"(prot), "r"(flags_), "r"(fd_), + "r"(off_), "m"(syscall) : "rcx", "r11", "memory"); - if (cf) ax = -ax; return ax; } -static size_t GetFdSize(int os, int fd) { - union metastat st; - if (!Fstat(os, fd, &st)) { - if (os == LINUX) { - return st.linux.st_size; - } else if (os == FREEBSD) { - return st.freebsd.st_size; - } else if (os == OPENBSD) { - return st.openbsd.st_size; - } else { - return st.netbsd.st_size; +static void Emit(int os, const char *s) { + Write(2, s, StrLen(s), os); +} + +static void Perror(int os, const char *c, int rc, const char *s) { + char ibuf[21]; + Emit(os, "ape error: "); + Emit(os, c); + Emit(os, ": "); + Emit(os, s); + if (rc) { + Emit(os, " failed errno="); + Itoa(ibuf, -rc); + Emit(os, ibuf); + } + Emit(os, "\n"); +} + +__attribute__((__noreturn__)) static void Pexit(int os, const char *c, int rc, + const char *s) { + Perror(os, c, rc, s); + Exit(127, os); +} + +static int StrCmp(const char *l, const char *r) { + unsigned long i = 0; + while (l[i] == r[i] && r[i]) ++i; + return (l[i] & 255) - (r[i] & 255); +} + +static char EndsWithIgnoreCase(const char *p, unsigned long n, const char *s) { + unsigned long i, m; + if (n >= (m = StrLen(s))) { + for (i = n - m; i < n; ++i) { + if (ToLower(p[i]) != *s++) { + return 0; + } } + return 1; } else { return 0; } } -static size_t Length(const char *s) { - size_t n = 0; - while (*s++) ++n; - return n; +static char IsComPath(struct PathSearcher *ps) { + return EndsWithIgnoreCase(ps->name, ps->namelen, ".com") || + EndsWithIgnoreCase(ps->name, ps->namelen, ".exe") || + EndsWithIgnoreCase(ps->name, ps->namelen, ".com.dbg"); } -static void Emit(int os, const char *s) { - Write(os, 2, s, Length(s)); +static char AccessCommand(struct PathSearcher *ps, const char *suffix, + unsigned long pathlen) { + unsigned long suffixlen; + suffixlen = StrLen(suffix); + if (pathlen + 1 + ps->namelen + suffixlen + 1 > sizeof(ps->path)) return 0; + if (pathlen && ps->path[pathlen - 1] != '/') ps->path[pathlen++] = '/'; + MemCpy(ps->path + pathlen, ps->name, ps->namelen); + MemCpy(ps->path + pathlen + ps->namelen, suffix, suffixlen + 1); + return !Access(ps->path, X_OK, ps->os); } -static void Log(int os, const char *s) { -#ifndef NDEBUG - Emit(os, "ape loader error: "); - Emit(os, s); -#endif -} - -static void Spawn(int os, int fd, long *sp, char *b, struct Elf64_Ehdr *e) { - size_t i; - int prot, flags; - long code, codesize; - struct Elf64_Phdr *p; - if (e->e_ident[EI_CLASS] != ELFCLASS64) { - Log(os, "EI_CLASS != ELFCLASS64\n"); - return; +static char SearchPath(struct PathSearcher *ps, const char *suffix) { + const char *p; + unsigned long i; + for (p = ps->syspath;;) { + for (i = 0; p[i] && p[i] != ':'; ++i) { + if (i < sizeof(ps->path)) { + ps->path[i] = p[i]; + } + } + if (AccessCommand(ps, suffix, i)) { + return 1; + } else if (p[i] == ':') { + p += i + 1; + } else { + return 0; + } } - if (e->e_ident[EI_DATA] != ELFDATA2LSB) { - Log(os, "EI_CLASS != ELFCLASS64\n"); - return; +} + +static char FindCommand(struct PathSearcher *ps, const char *suffix) { + if (MemChr(ps->name, '/', ps->namelen) || + MemChr(ps->name, '\\', ps->namelen)) { + ps->path[0] = 0; + return AccessCommand(ps, suffix, 0); + } else { + if (AccessCommand(ps, suffix, 0)) return 1; + } + return SearchPath(ps, suffix); +} + +static char *Commandv(struct PathSearcher *ps, int os, const char *name, + const char *syspath) { + ps->os = os; + ps->syspath = syspath ? syspath : "/bin:/usr/local/bin:/usr/bin"; + if (!(ps->namelen = StrLen((ps->name = name)))) return 0; + if (ps->namelen + 1 > sizeof(ps->path)) return 0; + if (FindCommand(ps, "") || (!IsComPath(ps) && FindCommand(ps, ".com"))) { + return ps->path; + } else { + return 0; + } +} + +__attribute__((__noreturn__)) static void Spawn(int os, const char *exe, int fd, + long *sp, char *page, + struct ElfEhdr *e) { + long rc; + unsigned long i; + int prot, flags; + struct ElfPhdr *p; + long code, codesize; + if (e->e_type != ET_EXEC) { + Pexit(os, exe, 0, "ELF e_type != ET_EXEC"); } if (e->e_machine != EM_NEXGEN32E) { - Log(os, "e_machine != EM_NEXGEN32E\n"); - return; + Pexit(os, exe, 0, "ELF e_machine != EM_NEXGEN32E"); } - if (e->e_type != ET_EXEC) { - Log(os, "e_type != ET_EXEC\n"); - return; + if (e->e_ident[EI_CLASS] != ELFCLASS64) { + Pexit(os, exe, 0, "ELF e_ident[EI_CLASS] != ELFCLASS64"); + } + if (e->e_ident[EI_DATA] != ELFDATA2LSB) { + Pexit(os, exe, 0, "ELF e_ident[EI_DATA] != ELFDATA2LSB"); } if (e->e_phoff + e->e_phnum * sizeof(*p) > 0x1000) { - Log(os, "phnum out of bounds\n"); - return; + Pexit(os, exe, 0, "ELF phdrs need to be in first page"); } code = 0; codesize = 0; - for (p = (struct Elf64_Phdr *)(b + e->e_phoff), i = e->e_phnum; i--;) { + for (p = (struct ElfPhdr *)(page + e->e_phoff), i = e->e_phnum; i--;) { + if (p[i].p_type == PT_DYNAMIC) { + Pexit(os, exe, 0, "not a real executable"); + } if (p[i].p_type != PT_LOAD) continue; if ((p[i].p_vaddr | p[i].p_filesz | p[i].p_memsz | p[i].p_offset) & 0xfff) { - Log(os, "ape requires strict page size padding and alignment\n"); - return; + Pexit(os, exe, 0, "APE phdrs must be 4096-aligned and 4096-padded"); } prot = 0; flags = MAP_FIXED | MAP_PRIVATE; @@ -228,107 +498,189 @@ static void Spawn(int os, int fd, long *sp, char *b, struct Elf64_Ehdr *e) { codesize = p[i].p_filesz; } if (p[i].p_memsz > p[i].p_filesz) { - if (Mmap(os, p[i].p_vaddr + p[i].p_filesz, p[i].p_memsz - p[i].p_filesz, - prot, flags | MAP_ANONYMOUS, -1, 0) < 0) { - Log(os, "bss mmap failed\n"); - return; + if ((rc = Mmap(p[i].p_vaddr + p[i].p_filesz, p[i].p_memsz - p[i].p_filesz, + prot, flags | MAP_ANONYMOUS, -1, 0, os)) < 0) { + Pexit(os, exe, rc, "bss mmap()"); } } if (p[i].p_filesz) { - if (Mmap(os, p[i].p_vaddr, p[i].p_filesz, prot, flags, fd, - p[i].p_offset) < 0) { - Log(os, "image mmap failed\n"); - return; + if ((rc = Mmap(p[i].p_vaddr, p[i].p_filesz, prot, flags, fd, + p[i].p_offset, os)) < 0) { + Pexit(os, exe, rc, "image mmap()"); } } } - Close(os, fd); - Msyscall(os, code, codesize); - sp[1] = sp[0] - 1; - ++sp; - asm volatile("mov\t%2,%%rsp\n\t" - "jmpq\t*%1" + if (!code) { + Pexit(os, exe, 0, "ELF needs PT_LOAD phdr w/ PF_X"); + } + Close(fd, os); + Msyscall(code, codesize, os); +#if TROUBLESHOOT + Emit(TROUBLESHOOT_OS, "preparing to jump\n"); +#endif + register long r8 asm("r8") = syscall; + asm volatile("xor\t%%eax,%%eax\n\t" + "xor\t%%ebx,%%ebx\n\t" + "xor\t%%r9d,%%r9d\n\t" + "xor\t%%r10d,%%r10d\n\t" + "xor\t%%r11d,%%r11d\n\t" + "xor\t%%r12d,%%r12d\n\t" + "xor\t%%r13d,%%r13d\n\t" + "xor\t%%r14d,%%r14d\n\t" + "xor\t%%r15d,%%r15d\n\t" + "mov\t%%rdx,%%rsp\n\t" + "xor\t%%edx,%%edx\n\t" + "push\t%%rsi\n\t" + "xor\t%%esi,%%esi\n\t" + "xor\t%%ebp,%%ebp\n\t" + "ret" : /* no outputs */ - : "D"(os == FREEBSD ? sp : 0), "S"(e->e_entry), "d"(sp) + : "D"(os == FREEBSD ? sp : 0), "S"(e->e_entry), "d"(sp), "c"(os), + "r"(r8) : "memory"); - unreachable; + __builtin_unreachable(); } -void loader(long di, long *sp) { - size_t size; +__attribute__((__noreturn__)) void ApeLoader(long di, long *sp, char dl, + struct ApeLoader *handoff) { long rc, *auxv; - char *p, **argv; + struct ElfEhdr *ehdr; int c, i, fd, os, argc; - union { - struct Elf64_Ehdr ehdr; + char *p, *exe, *prog, **argv, **envp, *page; + static union { + struct ElfEhdr ehdr; char p[0x1000]; } u; - os = 0; + + // detect freebsd if (di) { os = FREEBSD; sp = (long *)di; + } else if (dl == XNU) { + os = XNU; + } else { + os = LINUX; } + + // extract arguments argc = *sp; argv = (char **)(sp + 1); - auxv = (long *)(argv + argc + 1); + envp = (char **)(sp + 1 + argc + 1); + auxv = (long *)(sp + 1 + argc + 1); for (;;) { if (!*auxv++) { break; } } - if (!auxv[0]) { - os = OPENBSD; + + // get syscall function pointer + if (handoff && handoff->syscall) { + syscall = handoff->syscall; + } else { + syscall = __syscall_loader; } - for (; auxv[0]; auxv += 2) { - if (!os) { - if (auxv[0] == AT_EXECFN_NETBSD) { - os = NETBSD; - if (argc > 1) { - auxv[1] = (long)argv[1]; - } - } else if (auxv[0] == AT_EXECFN_LINUX) { - if (argc > 1) { - auxv[1] = (long)argv[1]; + + if (handoff) { + // we were called by ape_execve() + // no argument parsing is needed + // no path searching is needed + exe = handoff->prog; + fd = handoff->fd; + os = handoff->os; + exe = handoff->prog; + page = handoff->page; + ehdr = (struct ElfEhdr *)handoff->page; + } else { + + // detect openbsd + if (!auxv[0]) { + os = OPENBSD; + } + + // detect netbsd + if (os == LINUX) { + for (; auxv[0]; auxv += 2) { + if (auxv[0] == AT_EXECFN_NETBSD) { + os = NETBSD; + break; } } } + + // we can load via shell, shebang, or binfmt_misc + if (argc >= 3 && !StrCmp(argv[1], "-")) { + // if the first argument is a hyphen then we give the user the + // power to change argv[0] or omit it entirely. most operating + // systems don't permit the omission of argv[0] but we do, b/c + // it's specified by ANSI X3.159-1988. + prog = (char *)sp[3]; + argc = sp[3] = sp[0] - 3; + argv = (char **)((sp += 3) + 1); + } else if (argc < 2) { + Emit(os, "usage: ape PROG [ARGV1,ARGV2,...]\n" + " ape - PROG [ARGV0,ARGV1,...]\n" + "αcτµαlly pδrταblε εxεcµταblε loader v1.o\n" + "copyright 2022 justine alexandra roberts tunney\n" + "https://justine.lol/ape.html\n"); + Exit(1, os); + } else { + prog = (char *)sp[2]; + argc = sp[1] = sp[0] - 1; + argv = (char **)((sp += 1) + 1); + } + + if (!(exe = Commandv(&ps, os, prog, GetEnv(envp, "PATH")))) { + Pexit(os, prog, 0, "not found (maybe chmod +x)"); + } else if ((fd = Open(exe, O_RDONLY, 0, os)) < 0) { + Pexit(os, exe, fd, "open"); + } else if ((rc = Read(fd, u.p, sizeof(u.p), os)) < 0) { + Pexit(os, exe, rc, "read"); + } else if (rc != sizeof(u.p) && Read32(u.p) != Read32("\177ELF")) { + Pexit(os, exe, 0, "too small"); + } + + page = u.p; + ehdr = &u.ehdr; } - if (argc < 2) { - Emit(os, "usage: loader PROG [ARGS...]\n"); - } else if ((fd = Open(os, argv[1], O_RDONLY, 0)) < 0) { - Log(os, "open failed\n"); - } else if ((rc = Read(os, fd, u.p, sizeof(u.p))) < 0) { - Log(os, "read failed\n"); - } else if (rc != sizeof(u.p)) { - Log(os, "file too small\n"); - } else if (READ32LE(u.p) == READ32LE("\177ELF")) { - Spawn(os, fd, sp, u.p, &u.ehdr); - } else { - for (p = u.p; p < u.p + sizeof(u.p); ++p) { - if (READ64LE(p) == READ64LE("printf '")) { - for (i = 0, p += 8; p + 3 < u.p + sizeof(u.p) && (c = *p++) != '\'';) { - if (c == '\\') { + +#if TROUBLESHOOT + Emit(TROUBLESHOOT_OS, "os = "); + Emit(TROUBLESHOOT_OS, DescribeOs(os)); + Emit(TROUBLESHOOT_OS, "\n"); + for (i = 0; i < argc; ++i) { + Emit(TROUBLESHOOT_OS, "argv = "); + Emit(TROUBLESHOOT_OS, argv[i]); + Emit(TROUBLESHOOT_OS, "\n"); + } +#endif + + if (Read32(page) == Read32("\177ELF") || Read32(page) == 0xFEEDFACE + 1) { + Close(fd, os); + Execve(exe, argv, envp, os); + } + + for (p = page; p < page + sizeof(u.p); ++p) { + if (Read64(p) != Read64("printf '")) continue; + for (i = 0, p += 8; p + 3 < page + sizeof(u.p) && (c = *p++) != '\'';) { + if (c == '\\') { + if ('0' <= *p && *p <= '7') { + c = *p++ - '0'; + if ('0' <= *p && *p <= '7') { + c *= 8; + c += *p++ - '0'; if ('0' <= *p && *p <= '7') { - c = *p++ - '0'; - if ('0' <= *p && *p <= '7') { - c *= 8; - c += *p++ - '0'; - if ('0' <= *p && *p <= '7') { - c *= 8; - c += *p++ - '0'; - } - } + c *= 8; + c += *p++ - '0'; } } - u.p[i++] = c; - } - if (i >= 64 && READ32LE(u.p) == READ32LE("\177ELF")) { - Spawn(os, fd, sp, u.p, &u.ehdr); - Exit(os, 127); } } + page[i++] = c; + } + if (i >= 64 && Read32(page) == Read32("\177ELF")) { + Spawn(os, exe, fd, sp, page, ehdr); } - Log(os, "could not find printf elf in first page\n"); } - Exit(os, 127); + + Pexit(os, exe, 0, "could not find printf elf in first page"); } diff --git a/ape/loader.h b/ape/loader.h new file mode 100644 index 000000000..d36d7a567 --- /dev/null +++ b/ape/loader.h @@ -0,0 +1,21 @@ +#ifndef COSMOPOLITAN_APE_LOADER_H_ +#define COSMOPOLITAN_APE_LOADER_H_ + +#define APE_LOADER_BASE 0x200000 +#define APE_LOADER_SIZE 0x200000 +#define APE_LOADER_BSS (PAGESIZE * 2) +#define APE_LOADER_STACK 0x7f0000000000 +#define APE_LOADER_ENTRY (APE_LOADER_BASE + 0x47) +#define APE_LOADER_SYSCALL (APE_LOADER_BASE + 0x50) +#define APE_BLOCK_BASE 0x7e0000000000 +#define APE_BLOCK_SIZE 0x000200000000 + +struct ApeLoader { + int fd; + int os; + char *prog; + char *page; + void *syscall; +}; + +#endif /* COSMOPOLITAN_APE_LOADER_H_ */ diff --git a/ape/loader.lds b/ape/loader.lds index 403ae5f9f..e380bdb1b 100644 --- a/ape/loader.lds +++ b/ape/loader.lds @@ -24,9 +24,18 @@ SECTIONS { .text : { *(.text) *(.rodata .rodata.*) + . = ALIGN(4096); } filesz = . - ehdr; + .bss ALIGN(4096) : { + bss = .; + *(.bss) + . = ALIGN(4096); + } + memsz = . - ehdr; /DISCARD/ : { *(.*) } } + +bsssize = SIZEOF(.bss); diff --git a/build/bootstrap/ape b/build/bootstrap/ape new file mode 100644 index 000000000..d8857f6f4 Binary files /dev/null and b/build/bootstrap/ape differ diff --git a/build/sanitycheck b/build/sanitycheck index c2da2d263..7a06eb647 100755 --- a/build/sanitycheck +++ b/build/sanitycheck @@ -37,15 +37,14 @@ ERROR DETAILS - Actually Portable Executable assumes stock Linux configuration. - Normal behavior is non-ELF files with x bit are run by /bin/sh. - Linux lets people globally define arbitrary magic interpreters. - Your computer couldve been tuned to run MZ scripts inside WINE. - So if you use binfmt_misc you need to explicitly register this. + Your system has likely been configured to use binfmt_misc and wine. + You need to run the command below which will install a /usr/bin/ape + program and then register it with binfmt_misc. See ape/loader.c for + source code and technical details. WORKAROUND - sudo sh -c "echo ':APE:M::MZqFpD::/bin/sh:' >/proc/sys/fs/binfmt_misc/register" + ape/apeinstall.sh SEE ALSO diff --git a/build/sanitycheck2 b/build/sanitycheck2 index 152c3644a..4e74e3964 100755 --- a/build/sanitycheck2 +++ b/build/sanitycheck2 @@ -1,5 +1,12 @@ -MZqFpD=123 -exit $MZqFpD +MZboop=123 +exit $MZboop + + + + + + + diff --git a/examples/examples.mk b/examples/examples.mk index 127f9e2f5..388b019b3 100644 --- a/examples/examples.mk +++ b/examples/examples.mk @@ -104,6 +104,14 @@ o/$(MODE)/examples/%.com.dbg: \ $(APE) @$(APELINK) +o/$(MODE)/examples/nomodifyself.com.dbg: \ + $(EXAMPLES_DEPS) \ + o/$(MODE)/examples/nomodifyself.o \ + o/$(MODE)/examples/examples.pkg \ + $(CRT) \ + $(APE_NO_MODIFY_SELF) + @$(APELINK) + o/$(MODE)/examples/hellolua.com.dbg: \ $(EXAMPLES_DEPS) \ o/$(MODE)/examples/hellolua.o \ diff --git a/examples/mkhello.c b/examples/mkhello.c new file mode 100644 index 000000000..c5448ae1a --- /dev/null +++ b/examples/mkhello.c @@ -0,0 +1,17 @@ +#if 0 +/*─────────────────────────────────────────────────────────────────╗ +│ To the extent possible under law, Justine Tunney has waived │ +│ all copyright and related or neighboring rights to this file, │ +│ as it is written in the following disclaimers: │ +│ • http://unlicense.org/ │ +│ • http://creativecommons.org/publicdomain/zero/1.0/ │ +╚─────────────────────────────────────────────────────────────────*/ +#endif +#include "libc/calls/calls.h" + +int main(int argc, char *argv[]) { + creat("hello.txt", 0644); + write(3, "hello\n", 6); + close(3); + return 0; +} diff --git a/examples/nomodifyself.c b/examples/nomodifyself.c new file mode 100644 index 000000000..7db1e89e6 --- /dev/null +++ b/examples/nomodifyself.c @@ -0,0 +1,36 @@ +#if 0 +/*─────────────────────────────────────────────────────────────────╗ +│ To the extent possible under law, Justine Tunney has waived │ +│ all copyright and related or neighboring rights to this file, │ +│ as it is written in the following disclaimers: │ +│ • http://unlicense.org/ │ +│ • http://creativecommons.org/publicdomain/zero/1.0/ │ +╚─────────────────────────────────────────────────────────────────*/ +#endif +#include "libc/dce.h" +#include "libc/runtime/runtime.h" +#include "libc/stdio/stdio.h" + +/** + * @fileoverview Non-Self-Modifying APE Binary Demo + * + * See examples/examples.mk for the build config, which uses the + * alternative APE runtime. + */ + +int main(int argc, char *argv[]) { + if (_base[0] == 'M' && _base[1] == 'Z') { + printf("success: %s spawned without needing to modify its " + "executable header, thanks to APE loader\n", + argv[0]); + if (!IsWindows()) { + printf(", thanks to APE loader!\n"); + } else { + printf(", because you ran it on Windows :P\n"); + } + return 0; + } else { + printf("error: %s doesn't have an MZ file header!\n", argv[0]); + return 1; + } +} diff --git a/libc/calls/mman.greg.c b/libc/calls/mman.greg.c index bc50271a4..89df1691a 100644 --- a/libc/calls/mman.greg.c +++ b/libc/calls/mman.greg.c @@ -64,7 +64,7 @@ noasan texthead uint64_t __new_page(struct mman *mm) { * Returns pointer to page table entry for page at virtual address. * Additional page tables are allocated if needed as a side-effect. */ -noasan texthead uint64_t *__get_virtual(struct mman *mm, uint64_t *t, +noasan textreal uint64_t *__get_virtual(struct mman *mm, uint64_t *t, int64_t vaddr, bool maketables) { uint64_t *e, p; unsigned char h; diff --git a/libc/calls/nanos.c b/libc/calls/nanos.c new file mode 100644 index 000000000..14728f272 --- /dev/null +++ b/libc/calls/nanos.c @@ -0,0 +1,34 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/calls.h" +#include "libc/calls/struct/timespec.h" +#include "libc/time/time.h" + +/** + * Returns nanoseconds since UNIX epoch. + */ +int128_t _nanos(int timer) { + int128_t nanos; + struct timespec ts; + clock_gettime(timer, &ts); + nanos = ts.tv_sec; + nanos *= 1000000000; + nanos += ts.tv_nsec; + return nanos; +} diff --git a/libc/calls/nanos.h b/libc/calls/nanos.h new file mode 100644 index 000000000..547557290 --- /dev/null +++ b/libc/calls/nanos.h @@ -0,0 +1,10 @@ +#ifndef COSMOPOLITAN_LIBC_CALLS_NANOS_H_ +#define COSMOPOLITAN_LIBC_CALLS_NANOS_H_ +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +int128_t _nanos(int); + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_CALLS_NANOS_H_ */ diff --git a/libc/calls/sig2.c b/libc/calls/sig2.c index 7414fd999..d94d02e18 100644 --- a/libc/calls/sig2.c +++ b/libc/calls/sig2.c @@ -21,6 +21,7 @@ #include "libc/calls/sigbits.h" #include "libc/calls/strace.internal.h" #include "libc/intrin/cmpxchg.h" +#include "libc/intrin/lockcmpxchg.h" #include "libc/intrin/spinlock.h" #include "libc/log/libfatal.internal.h" #include "libc/macros.internal.h" @@ -131,9 +132,7 @@ static privileged bool __sig_deliver(bool restartable, int sig, int si_code, // since sigaction() is @asyncsignalsafe we only restore it if the // user didn't change it during the signal handler. we also don't // need to do anything if this was a oneshot signal or nodefer. - _spinlock(&__sig_lock); - _cmpxchg(__sighandrvas + sig, (int32_t)(intptr_t)SIG_DFL, rva); - _spunlock(&__sig_lock); + _lockcmpxchg(__sighandrvas + sig, (int32_t)(intptr_t)SIG_DFL, rva); } if (!restartable) { diff --git a/libc/crt/crt.S b/libc/crt/crt.S index 83a86ab20..f30fde679 100644 --- a/libc/crt/crt.S +++ b/libc/crt/crt.S @@ -26,6 +26,8 @@ // // @param rsp is [n,argv₀..argvₙ₋₁,0,envp₀..,0,auxv₀..,0,..] // @note FreeBSD is special (see freebsd/lib/csu/amd64/...) +// @note NetBSD will only zero the call-clobbered registers +// @note ape.S and ape-loader both set RCX to XNU on Darwin // @noreturn _start: @@ -34,12 +36,16 @@ _start: test %rdi,%rdi cmovnz %rdi,%rsp jz 0f - movb $FREEBSD,__hostos(%rip) + movb $FREEBSD,%cl +0: #endif +// set operating system when already detected + mov %cl,__hostos(%rip) + // get startup timestamp as early as possible // its used by --strace flag and kprintf() %T -0: rdtsc + rdtsc ezlea kStartTsc,bx mov %eax,(%rbx) mov %edx,4(%rbx) @@ -85,14 +91,3 @@ _start: call cosmo 9: .unreachable .endfn _start,weak,hidden - -#if SupportsXnu() -// Macintosh userspace program entrypoint. -// -// @param rsp is [n,argv₀..argvₙ₋₁,0,envp₀..,0,auxv₀..,0,..] -// @note FreeBSD is special (see freebsd/lib/csu/amd64/...) -// @noreturn -_xnu: movb $XNU,__hostos(%rip) - jmp 0b - .endfn _xnu,weak,hidden -#endif diff --git a/libc/fmt/leb128.h b/libc/fmt/leb128.h index 25b92c4f9..a16f2e960 100644 --- a/libc/fmt/leb128.h +++ b/libc/fmt/leb128.h @@ -4,7 +4,7 @@ COSMOPOLITAN_C_START_ char *sleb64(char *, int64_t); -char *zleb64(char *, int64_t); +char *zleb64(char[hasatleast 10], int64_t); char *uleb64(char[hasatleast 10], uint64_t); int unzleb64(const char *, size_t, int64_t *); int unuleb64(char *, size_t, uint64_t *); diff --git a/libc/fmt/zleb64.c b/libc/fmt/zleb64.c index 1ca96cd52..48a3f5c58 100644 --- a/libc/fmt/zleb64.c +++ b/libc/fmt/zleb64.c @@ -44,7 +44,7 @@ * @return p + i * @see unzleb64() */ -char *zleb64(char *p, int64_t x) { +char *zleb64(char p[hasatleast 10], int64_t x) { int c; uint64_t u; u = x; diff --git a/libc/runtime/clone.c b/libc/runtime/clone.c index 5a7b87b37..779a12bf7 100644 --- a/libc/runtime/clone.c +++ b/libc/runtime/clone.c @@ -58,7 +58,10 @@ struct CloneArgs { uint32_t utid; int64_t tid64; }; - int lock; + union { + int lock; + void *pstack; + }; int *ctid; int *ztid; char *tls; @@ -287,12 +290,18 @@ __attribute__((__used__, __no_reorder__)) static privileged wontreturn void OpenbsdThreadMain(struct CloneArgs *wt) { wt->func(wt->arg); - // we no longer use the stack after this point + // we no longer use the stack after this point. however openbsd + // validates the rsp register too so a race condition can still + // happen if the parent tries to free the stack. we'll solve it + // by simply changing rsp back to the old value before exiting! + // although ideally there should be a better solution. + // // void __threxit(%rdi = int32_t *notdead); - asm volatile("movl\t$0,%0\n\t" // *wt->ztid = 0 + asm volatile("mov\t%3,%%rsp\n\t" + "movl\t$0,%0\n\t" // *wt->ztid = 0 "syscall" // _Exit1() : "=m"(*wt->ztid) - : "a"(302), "D"(0) + : "a"(302), "D"(0), "r"(wt->pstack) : "rcx", "r11", "memory"); unreachable; } @@ -307,6 +316,7 @@ static int CloneOpenbsd(int (*func)(void *), char *stk, size_t stksz, int flags, -alignof(struct CloneArgs)); wt->ctid = flags & CLONE_CHILD_SETTID ? ctid : &wt->tid; wt->ztid = flags & CLONE_CHILD_CLEARTID ? ctid : &wt->tid; + wt->pstack = __builtin_frame_address(0); wt->func = func; wt->arg = arg; params.tf_stack = wt; diff --git a/libc/runtime/ftracer.c b/libc/runtime/ftracer.c index 58deed8bb..ae0359265 100644 --- a/libc/runtime/ftracer.c +++ b/libc/runtime/ftracer.c @@ -47,7 +47,7 @@ static struct Ftrace { int64_t lastaddr; } g_ftrace; -static privileged int GetNestingLevelImpl(struct StackFrame *frame) { +static privileged inline int GetNestingLevelImpl(struct StackFrame *frame) { int nesting = -2; while (frame) { ++nesting; @@ -56,7 +56,7 @@ static privileged int GetNestingLevelImpl(struct StackFrame *frame) { return MAX(0, nesting); } -static privileged int GetNestingLevel(struct StackFrame *frame) { +static privileged inline int GetNestingLevel(struct StackFrame *frame) { int nesting; nesting = GetNestingLevelImpl(frame); if (nesting < g_ftrace.skew) g_ftrace.skew = nesting; diff --git a/libc/testlib/ugly.h b/libc/testlib/ugly.h index e8c58e59f..c08baee03 100644 --- a/libc/testlib/ugly.h +++ b/libc/testlib/ugly.h @@ -9,9 +9,9 @@ #define __BENCH_ARRAY(S) \ _Section(".piro.relo.sort.bench.2." #S ",\"aw\",@init_array #") -#define __TEST_PROTOTYPE(S, N, A, K) \ - void S##_##N(void); \ - const void *const S##_##N##_ptr[] A(S##_##N) = {S##_##N}; \ +#define __TEST_PROTOTYPE(S, N, A, K) \ + void S##_##N(void); \ + testfn_t S##_##N##_ptr[] A(S##_##N) = {S##_##N}; \ testonly K void S##_##N(void) #define __TEST_SECTION(NAME, CONTENT) \ diff --git a/test/libc/calls/execve_test.c b/test/libc/calls/execve_test.c index 88cbd140f..2dd8ae76e 100644 --- a/test/libc/calls/execve_test.c +++ b/test/libc/calls/execve_test.c @@ -23,13 +23,7 @@ #include "libc/testlib/testlib.h" void SetUp(void) { - if (getenv("_SUBPROCESS")) { - if (!__argv[0]) { - exit(0); - } else { - exit(7); - } - } else if (getenv("_WEIRDENV")) { + if (getenv("_WEIRDENV")) { for (char **e = environ; *e; ++e) { if (!strcmp(*e, "WEIRD")) { exit(0); @@ -39,22 +33,6 @@ void SetUp(void) { } } -TEST(execve, testWeirdAnsiC89emptyArgv) { - char *prog; - int pid, ws; - if (IsWindows()) return; - if (IsOpenbsd()) return; - prog = GetProgramExecutableName(); - ASSERT_NE(-1, (pid = fork())); - if (!pid) { - execve(prog, (char *const[]){0}, (char *const[]){"_SUBPROCESS=1", 0}); - _Exit(127); - } - ASSERT_NE(-1, wait(&ws)); - EXPECT_TRUE(WIFEXITED(ws)); - EXPECT_EQ(0, WEXITSTATUS(ws)); -} - TEST(execve, testWeirdEnvironmentVariable) { char *prog; int pid, ws; diff --git a/test/libc/str/test.mk b/test/libc/str/test.mk index 6efecbbf3..7b3ec9f89 100644 --- a/test/libc/str/test.mk +++ b/test/libc/str/test.mk @@ -3,24 +3,32 @@ PKGS += TEST_LIBC_STR -TEST_LIBC_STR_SRCS := $(wildcard test/libc/str/*.c) -TEST_LIBC_STR_SRCS_TEST = $(filter %_test.c,$(TEST_LIBC_STR_SRCS)) +TEST_LIBC_STR_FILES := $(wildcard test/libc/str/*) +TEST_LIBC_STR_SRCS_C = $(filter %.c,$(TEST_LIBC_STR_FILES)) +TEST_LIBC_STR_SRCS_CC = $(filter %.cc,$(TEST_LIBC_STR_FILES)) +TEST_LIBC_STR_SRCS = $(TEST_LIBC_STR_SRCS_C) $(TEST_LIBC_STR_SRCS_CC) +TEST_LIBC_STR_SRCS_TEST_C = $(filter %_test.c,$(TEST_LIBC_STR_FILES)) +TEST_LIBC_STR_SRCS_TEST_CC = $(filter %_test.cc,$(TEST_LIBC_STR_FILES)) TEST_LIBC_STR_OBJS = \ - $(TEST_LIBC_STR_SRCS:%.c=o/$(MODE)/%.o) + $(TEST_LIBC_STR_SRCS_C:%.c=o/$(MODE)/%.o) \ + $(TEST_LIBC_STR_SRCS_CC:%.cc=o/$(MODE)/%.o) TEST_LIBC_STR_COMS = \ - $(TEST_LIBC_STR_SRCS:%.c=o/$(MODE)/%.com) + $(TEST_LIBC_STR_SRCS_TEST_C:%.c=o/$(MODE)/%.com) \ + $(TEST_LIBC_STR_SRCS_TEST_CC:%.cc=o/$(MODE)/%.com) TEST_LIBC_STR_BINS = \ $(TEST_LIBC_STR_COMS) \ $(TEST_LIBC_STR_COMS:%=%.dbg) TEST_LIBC_STR_TESTS = \ - $(TEST_LIBC_STR_SRCS_TEST:%.c=o/$(MODE)/%.com.ok) + $(TEST_LIBC_STR_SRCS_TEST_C:%.c=o/$(MODE)/%.com.ok) \ + $(TEST_LIBC_STR_SRCS_TEST_CC:%.cc=o/$(MODE)/%.com.ok) TEST_LIBC_STR_CHECKS = \ - $(TEST_LIBC_STR_SRCS_TEST:%.c=o/$(MODE)/%.com.runs) + $(TEST_LIBC_STR_SRCS_TEST_C:%.c=o/$(MODE)/%.com.runs) \ + $(TEST_LIBC_STR_SRCS_TEST_CC:%.cc=o/$(MODE)/%.com.runs) TEST_LIBC_STR_DIRECTDEPS = \ LIBC_ALG \ @@ -43,7 +51,9 @@ TEST_LIBC_STR_DIRECTDEPS = \ LIBC_ZIPOS \ THIRD_PARTY_MBEDTLS \ THIRD_PARTY_REGEX \ - THIRD_PARTY_ZLIB + THIRD_PARTY_ZLIB \ + THIRD_PARTY_LIBCXX \ + THIRD_PARTY_SMALLZ4 TEST_LIBC_STR_DEPS := \ $(call uniq,$(foreach x,$(TEST_LIBC_STR_DIRECTDEPS),$($(x)))) diff --git a/third_party/make/config.h b/third_party/make/config.h index 48f976078..14385ed0b 100644 --- a/third_party/make/config.h +++ b/third_party/make/config.h @@ -620,7 +620,7 @@ /* #undef HAVE__SET_INVALID_PARAMETER_HANDLER */ /* Build host information. */ -#define MAKE_HOST "x86_64-pc-linux-gnu" +#define MAKE_HOST "x86_64-pc-cosmopolitan" /* Define to 1 to enable job server support in GNU make. */ /* TODO(jart): make it work */ diff --git a/third_party/make/dir.c b/third_party/make/dir.c index b8b884ba9..9a828158b 100644 --- a/third_party/make/dir.c +++ b/third_party/make/dir.c @@ -689,15 +689,9 @@ void print_dir_data_base(void) { if (dir->contents == 0) printf(_("# %s: could not be stat'd.\n"), dir->name); else if (dir->contents->dirfiles.ht_vec == 0) { -#ifdef WINDOWS32 - printf(_("# %s (key %s, mtime %I64u): could not be opened.\n"), - dir->name, dir->contents->path_key, - (unsigned long long)dir->contents->mtime); -#else /* WINDOWS32 */ printf(_("# %s (device %ld, inode %ld): could not be opened.\n"), dir->name, (long int)dir->contents->dev, (long int)dir->contents->ino); -#endif /* WINDOWS32 */ } else { unsigned int f = 0; unsigned int im = 0; @@ -715,14 +709,8 @@ void print_dir_data_base(void) { ++f; } } -#ifdef WINDOWS32 - printf(_("# %s (key %s, mtime %I64u): "), dir->name, - dir->contents->path_key, - (unsigned long long)dir->contents->mtime); -#else /* WINDOWS32 */ printf(_("# %s (device %ld, inode %ld): "), dir->name, (long)dir->contents->dev, (long)dir->contents->ino); -#endif /* WINDOWS32 */ if (f == 0) fputs(_("No"), stdout); else @@ -822,9 +810,7 @@ static struct dirent *read_dirstream(__ptr_t stream) { #ifdef _DIRENT_HAVE_D_NAMLEN d->d_namlen = len - 1; #endif -#ifdef HAVE_STRUCT_DIRENT_D_TYPE d->d_type = df->type; -#endif memcpy(d->d_name, df->name, len); return d; } diff --git a/third_party/smallz4/smallz4.hh b/third_party/smallz4/smallz4.hh index 310c89831..abf9b5683 100644 --- a/third_party/smallz4/smallz4.hh +++ b/third_party/smallz4/smallz4.hh @@ -1,5 +1,6 @@ #ifndef COSMOPOLITAN_THIRD_PARTY_SMALLZ4_SMALLZ4_H_ #define COSMOPOLITAN_THIRD_PARTY_SMALLZ4_SMALLZ4_H_ +#include "libc/bits/bits.h" #include "third_party/libcxx/vector" /** @@ -138,7 +139,7 @@ class smallz4 { /// return true, if the four bytes at *a and *b match inline static bool match4(const void* const a, const void* const b) { - return *(const uint32_t*)a == *(const uint32_t*)b; + return READ32LE(a) == READ32LE(b); } /// simple hash function, input: 32 bits, output: HashBits bits (by default: @@ -636,7 +637,7 @@ class smallz4 { } // read next four bytes - const uint32_t four = *(uint32_t*)(dataBlock + i); + const uint32_t four = READ32LE(dataBlock + i); // convert to a shorter hash const uint32_t hash = getHash32(four); @@ -674,10 +675,9 @@ class smallz4 { // check the hash chain while (true) { // read four bytes - currentFour = - *(uint32_t*)(&data[lastHashMatch - - dataZero]); // match may be found in the - // previous block, too + currentFour = READ32LE( + &data[lastHashMatch - dataZero]); // match may be found in the + // previous block, too // match chain found, first 4 bytes are identical if (currentFour == four) break;