mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-03-03 07:29:23 +00:00
Support Linux binfmt_misc and APE loading on Apple
The "no modify self" variant of Actually Portable Executable is now supported on all platforms. If you use `$(APE_NO_MODIFY_SELF)` then ld.bfd will embed a 4096 byte ELF binary and a 4096 byte Macho file which are installed on the fly to ${TMPDIR:-/tmp}, which enables us launch the executable, without needing to copy the whole executable To prevent it from copying a tiny executable to your temp directory you need to install the `ape` command (renamed from ape-loader), to a system path. For example: # FreeBSD / NetBSD / OpenBSD make -j8 o//ape/ape cp o//ape/ape /usr/bin/ape # Mac OS # make -j8 o//ape/ape.macho curl https://justine.lol/ape.macho >/usr/bin/ape chmod +x /usr/bin/ape On Linux you can get even more performance with the new binfmt_misc support which makes launching non-modifying APE binaries as fast as launching ELF executables. Running the following command: # Linux ape/apeinstall.sh Will copy APE loader to /usr/bin/ape and register with binfmt_misc Lastly, this change also fixes a really interesting race condition with OpenBSD thread joining.
This commit is contained in:
parent
7838edae88
commit
db0d8dd806
31 changed files with 1089 additions and 305 deletions
39
ape/ape.S
39
ape/ape.S
|
@ -542,27 +542,35 @@ apesh: .ascii "'\n#'\"\n" # sixth edition shebang
|
||||||
// present two choices.
|
// present two choices.
|
||||||
.ascii "o=\"$(command -v \"$0\")\"\n"
|
.ascii "o=\"$(command -v \"$0\")\"\n"
|
||||||
// Try to use a system-wide APE loader.
|
// Try to use a system-wide APE loader.
|
||||||
.ascii "type ape-loader >/dev/null 2>&1 && "
|
.ascii "type ape >/dev/null 2>&1 && "
|
||||||
.ascii "exec ape-loader \"$o\" \"$@\"\n"
|
.ascii "exec ape \"$o\" \"$@\"\n"
|
||||||
#ifdef APE_LOADER
|
#ifdef APE_LOADER
|
||||||
// There is no system-wide APE loader, but there is one
|
// There is no system-wide APE loader, but there is one
|
||||||
// embedded inside the APE. So if the system is not MacOs,
|
// embedded inside the APE. So if the system is not MacOs,
|
||||||
// extract the loader into a temp folder, and use it to
|
// extract the loader into a temp folder, and use it to
|
||||||
// load the APE without modifying it.
|
// load the APE without modifying it.
|
||||||
.ascii "if [ ! -d /Applications ]; then\n"
|
.ascii "t=\"${TMPDIR:-/tmp}/ape\"\n"
|
||||||
.ascii "t=\"${TMPDIR:-/tmp}/ape-loader\"\n"
|
.ascii "if [ ! -x \"$t\" ]; then\n"
|
||||||
.ascii "[ -x \"$t\" ] || {\n"
|
.ascii "if [ ! -d /Applications ]; then\n"
|
||||||
.ascii "dd if=\"$o\" of=\"$t.$$\" skip=\""
|
.ascii "dd if=\"$o\" of=\"$t.$$\" skip=\""
|
||||||
.shstub ape_loader_dd_skip,2
|
.shstub ape_loader_dd_skip,2
|
||||||
.ascii "\" count=\""
|
.ascii "\" count=\""
|
||||||
.shstub ape_loader_dd_count,2
|
.shstub ape_loader_dd_count,2
|
||||||
.ascii "\" bs=64 2>/dev/null &&\n"
|
.ascii "\" bs=64\n"
|
||||||
.ascii "chmod 755 \"$t.$$\" &&\n"
|
#if SupportsXnu() && defined(APE_LOADER_MACHO)
|
||||||
.ascii "mv \"$t.$$\" \"$t\"\n"
|
.ascii "else\n"
|
||||||
.ascii "}\n"
|
.ascii "dd if=\"$o\" of=\"$t.$$\" skip=\""
|
||||||
.ascii "exec \"$t\" \"$o\" \"$@\"\n"
|
.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"
|
.ascii "fi\n"
|
||||||
#endif
|
.ascii "exec \"$t\" \"$o\" \"$@\"\n"
|
||||||
|
#endif /* APE_LOADER */
|
||||||
#ifndef APE_NO_MODIFY_SELF
|
#ifndef APE_NO_MODIFY_SELF
|
||||||
// The default behavior is: to overwrite the header in place.
|
// The default behavior is: to overwrite the header in place.
|
||||||
// We prefer this because it's a tiny constant one time cost.
|
// 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
|
.incbin APE_LOADER
|
||||||
.previous
|
.previous
|
||||||
#endif /* APE_LOADER */
|
#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() */
|
#endif /* SupportsWindows() || SupportsMetal() || SupportsXnu() */
|
||||||
|
|
||||||
#if SupportsSystemv() || SupportsMetal()
|
#if SupportsSystemv() || SupportsMetal()
|
||||||
|
@ -856,7 +869,7 @@ ape_macho:
|
||||||
.long (520f-510f)/4 # count
|
.long (520f-510f)/4 # count
|
||||||
510: .quad 0 # rax
|
510: .quad 0 # rax
|
||||||
.quad IMAGE_BASE_VIRTUAL # rbx
|
.quad IMAGE_BASE_VIRTUAL # rbx
|
||||||
.quad 0 # rcx
|
.quad XNU # rcx
|
||||||
.quad 0 # rdx
|
.quad 0 # rdx
|
||||||
.quad 0 # rdi
|
.quad 0 # rdi
|
||||||
.quad 0 # rsi
|
.quad 0 # rsi
|
||||||
|
@ -870,7 +883,7 @@ ape_macho:
|
||||||
.quad 0 # r13
|
.quad 0 # r13
|
||||||
.quad 0 # r14
|
.quad 0 # r14
|
||||||
.quad 0 # r15
|
.quad 0 # r15
|
||||||
.quad _xnu # rip
|
.quad _start # rip
|
||||||
.quad 0 # rflags
|
.quad 0 # rflags
|
||||||
.quad 0 # cs
|
.quad 0 # cs
|
||||||
.quad 0 # fs
|
.quad 0 # fs
|
||||||
|
|
11
ape/ape.lds
11
ape/ape.lds
|
@ -397,6 +397,12 @@ SECTIONS {
|
||||||
KEEP(*(.ape.loader))
|
KEEP(*(.ape.loader))
|
||||||
. = ALIGN(64);
|
. = ALIGN(64);
|
||||||
HIDDEN(ape_loader_end = .);
|
HIDDEN(ape_loader_end = .);
|
||||||
|
#if SupportsXnu()
|
||||||
|
HIDDEN(ape_loader_macho = .);
|
||||||
|
KEEP(*(.ape.loader-macho))
|
||||||
|
. = ALIGN(64);
|
||||||
|
HIDDEN(ape_loader_macho_end = .);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
/*END: payload */
|
/*END: payload */
|
||||||
/*BEGIN: bss memory void */
|
/*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_skip, RVA(ape_loader) / 64);
|
||||||
SHSTUB2(ape_loader_dd_count, (ape_loader_end - 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()
|
#if SupportsXnu()
|
||||||
SHSTUB2(ape_macho_dd_skip, RVA(ape_macho) / 8);
|
SHSTUB2(ape_macho_dd_skip, RVA(ape_macho) / 8);
|
||||||
SHSTUB2(ape_macho_dd_count, (ape_macho_end - ape_macho) / 8);
|
SHSTUB2(ape_macho_dd_count, (ape_macho_end - ape_macho) / 8);
|
||||||
|
|
57
ape/ape.mk
57
ape/ape.mk
|
@ -15,18 +15,18 @@
|
||||||
|
|
||||||
PKGS += APE
|
PKGS += APE
|
||||||
|
|
||||||
APE = o/$(MODE)/ape/ape.o \
|
APE = o/$(MODE)/ape/ape.o \
|
||||||
o/$(MODE)/ape/ape.lds
|
o/$(MODE)/ape/ape.lds
|
||||||
|
|
||||||
APE_NO_MODIFY_SELF = \
|
APE_NO_MODIFY_SELF = \
|
||||||
o/$(MODE)/ape/ape.lds \
|
o/$(MODE)/ape/ape.lds \
|
||||||
o/$(MODE)/ape/ape-no-modify-self.o
|
o/$(MODE)/ape/ape-no-modify-self.o
|
||||||
|
|
||||||
APELINK = \
|
APELINK = \
|
||||||
$(COMPILE) \
|
$(COMPILE) \
|
||||||
-ALINK.ape \
|
-ALINK.ape \
|
||||||
$(LINK) \
|
$(LINK) \
|
||||||
$(LINKARGS) \
|
$(LINKARGS) \
|
||||||
$(OUTPUT_OPTION)
|
$(OUTPUT_OPTION)
|
||||||
|
|
||||||
APE_FILES := $(wildcard ape/*.*)
|
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_OBJS = $(APE_SRCS_S:%.S=o/$(MODE)/%.o)
|
||||||
APE_CHECKS = $(APE_HDRS:%=o/%.ok)
|
APE_CHECKS = $(APE_HDRS:%=o/%.ok)
|
||||||
|
|
||||||
o/$(MODE)/ape/ape.lds: \
|
o/$(MODE)/ape/ape.lds: \
|
||||||
ape/ape.lds \
|
ape/ape.lds \
|
||||||
ape/macros.internal.h \
|
ape/macros.internal.h \
|
||||||
libc/dce.h \
|
libc/dce.h \
|
||||||
libc/zip.h
|
libc/zip.h
|
||||||
|
|
||||||
o/ape/idata.inc: \
|
o/ape/idata.inc: \
|
||||||
ape/idata.internal.h \
|
ape/idata.internal.h \
|
||||||
ape/relocations.h
|
ape/relocations.h
|
||||||
|
|
||||||
o/$(MODE)/ape/ape-no-modify-self.o: ape/ape.S o/$(MODE)/ape/loader.elf
|
o/$(MODE)/ape/ape-no-modify-self.o: \
|
||||||
@$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -DAPE_LOADER="\"o/$(MODE)/ape/loader.elf\"" -DAPE_NO_MODIFY_SELF $<
|
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
|
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
|
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/ape: \
|
||||||
o/$(MODE)/ape/loader.o \
|
o/$(MODE)/ape/loader.o \
|
||||||
o/$(MODE)/ape/loader1.o \
|
o/$(MODE)/ape/loader-elf.o \
|
||||||
ape/loader.lds
|
ape/loader.lds
|
||||||
@$(ELFLINK) -s -z max-page-size=0x10
|
@$(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
|
.PHONY: o/$(MODE)/ape
|
||||||
o/$(MODE)/ape: $(APE) \
|
o/$(MODE)/ape: $(APE) \
|
||||||
$(APE_CHECKS) \
|
$(APE_CHECKS) \
|
||||||
|
o/$(MODE)/ape/ape \
|
||||||
|
o/$(MODE)/ape/ape.macho \
|
||||||
o/$(MODE)/ape/ape-no-modify-self.o
|
o/$(MODE)/ape/ape-no-modify-self.o
|
||||||
|
|
63
ape/apeinstall.sh
Executable file
63
ape/apeinstall.sh
Executable file
|
@ -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
|
|
@ -38,20 +38,54 @@ ehdr: .ascii "\177ELF"
|
||||||
.long 0 # e_flags
|
.long 0 # e_flags
|
||||||
.word 64 # e_ehsize
|
.word 64 # e_ehsize
|
||||||
.word 56 # e_phentsize
|
.word 56 # e_phentsize
|
||||||
.word 3 # e_phnum
|
.word 4 # e_phnum
|
||||||
.word 0 # e_shentsize
|
.word 0 # e_shentsize
|
||||||
.word 0 # e_shnum
|
.word 0 # e_shnum
|
||||||
.word 0 # e_shstrndx
|
.word 0 # e_shstrndx
|
||||||
.endobj ehdr,globl
|
.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
|
jg47h: .org 0x47
|
||||||
.endobj jg47h
|
.endobj jg47h
|
||||||
|
|
||||||
_start: mov %rsp,%rsi
|
_start: mov %rsp,%rsi
|
||||||
jmp loader
|
jmp ApeLoader
|
||||||
.endfn _start,globl
|
.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
|
.align 8
|
||||||
phdrs: .long PT_LOAD # p_type
|
phdrs: .long PT_LOAD # p_type
|
||||||
.long PF_R|PF_X # p_flags
|
.long PF_R|PF_X # p_flags
|
||||||
|
@ -61,6 +95,16 @@ phdrs: .long PT_LOAD # p_type
|
||||||
.quad filesz # p_filesz
|
.quad filesz # p_filesz
|
||||||
.quad filesz # p_memsz
|
.quad filesz # p_memsz
|
||||||
.quad PAGESIZE # p_align
|
.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 PT_GNU_STACK # p_type
|
||||||
.long PF_R|PF_W # p_flags
|
.long PF_R|PF_W # p_flags
|
||||||
.quad 0 # p_offset
|
.quad 0 # p_offset
|
||||||
|
@ -69,6 +113,7 @@ phdrs: .long PT_LOAD # p_type
|
||||||
.quad 0 # p_filesz
|
.quad 0 # p_filesz
|
||||||
.quad 0 # p_memsz
|
.quad 0 # p_memsz
|
||||||
.quad 16 # p_align
|
.quad 16 # p_align
|
||||||
|
|
||||||
.long PT_NOTE # p_type
|
.long PT_NOTE # p_type
|
||||||
.long PF_R # p_flags
|
.long PF_R # p_flags
|
||||||
.quad note - ehdr # p_offset
|
.quad note - ehdr # p_offset
|
127
ape/loader-macho.S
Normal file
127
ape/loader-macho.S
Normal file
|
@ -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
|
43
ape/loader-macho.lds
Normal file
43
ape/loader-macho.lds
Normal file
|
@ -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;
|
708
ape/loader.c
708
ape/loader.c
|
@ -16,28 +16,74 @@
|
||||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/bits/bits.h"
|
#include "ape/loader.h"
|
||||||
#include "libc/calls/struct/metastat.internal.h"
|
|
||||||
#include "libc/calls/struct/stat.h"
|
#define TROUBLESHOOT 0
|
||||||
#include "libc/elf/def.h"
|
#define TROUBLESHOOT_OS LINUX
|
||||||
#include "libc/elf/struct/ehdr.h"
|
|
||||||
#include "libc/elf/struct/phdr.h"
|
|
||||||
#include "libc/sysv/consts/prot.h"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @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
|
* m=tiny
|
||||||
* make -j8 MODE=$m o/$m/ape o/$m/examples/printargs.com
|
* 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
|
* @note this can probably be used as a binfmt_misc interpreter
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define LINUX 0
|
#define LINUX 1
|
||||||
#define FREEBSD 1
|
#define METAL 2
|
||||||
#define NETBSD 2
|
#define WINDOWS 4
|
||||||
#define OPENBSD 3
|
#define XNU 8
|
||||||
|
#define OPENBSD 16
|
||||||
|
#define FREEBSD 32
|
||||||
|
#define NETBSD 64
|
||||||
|
|
||||||
#define O_RDONLY 0
|
#define O_RDONLY 0
|
||||||
#define PROT_READ 1
|
#define PROT_READ 1
|
||||||
|
@ -49,170 +95,394 @@
|
||||||
#define MAP_ANONYMOUS (os == LINUX ? 32 : 4096)
|
#define MAP_ANONYMOUS (os == LINUX ? 32 : 4096)
|
||||||
#define AT_EXECFN_LINUX 31
|
#define AT_EXECFN_LINUX 31
|
||||||
#define AT_EXECFN_NETBSD 2014
|
#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 Read32(S) \
|
||||||
#define __NR_write (os == LINUX ? 1 : 4)
|
((unsigned)(255 & (S)[3]) << 030 | (unsigned)(255 & (S)[2]) << 020 | \
|
||||||
#define __NR_open (os == LINUX ? 2 : 5)
|
(unsigned)(255 & (S)[1]) << 010 | (unsigned)(255 & (S)[0]) << 000)
|
||||||
#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)
|
|
||||||
|
|
||||||
static wontreturn void Exit(int os, long rc) {
|
#define Read64(S) \
|
||||||
asm volatile("syscall"
|
((unsigned long)(255 & (S)[7]) << 070 | \
|
||||||
: /* no outputs */
|
(unsigned long)(255 & (S)[6]) << 060 | \
|
||||||
: "a"(__NR_exit), "D"(rc)
|
(unsigned long)(255 & (S)[5]) << 050 | \
|
||||||
: "memory");
|
(unsigned long)(255 & (S)[4]) << 040 | \
|
||||||
unreachable;
|
(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;
|
long ax, di;
|
||||||
asm volatile("syscall"
|
asm volatile("call\t*%4"
|
||||||
: "=a"(ax), "=D"(di)
|
: "=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");
|
: "rcx", "rdx", "rsi", "r8", "r9", "r10", "r11", "memory", "cc");
|
||||||
}
|
}
|
||||||
|
|
||||||
static long Read(int os, long fd, void *data, unsigned long size) {
|
static long Read(long fd, void *data, unsigned long size, int os) {
|
||||||
bool cf;
|
|
||||||
long ax, di, si, dx;
|
long ax, di, si, dx;
|
||||||
asm volatile("clc\n\t"
|
asm volatile("call\t*%8"
|
||||||
"syscall"
|
: "=a"(ax), "=D"(di), "=S"(si), "=d"(dx)
|
||||||
: "=@ccc"(cf), "=a"(ax), "=D"(di), "=S"(si), "=d"(dx)
|
: "0"((os == LINUX ? 0 : 3) | (os == XNU ? 0x2000000 : 0)),
|
||||||
: "1"(__NR_read), "2"(fd), "3"(data), "4"(size)
|
"1"(fd), "2"(data), "3"(size), "m"(syscall)
|
||||||
: "rcx", "r8", "r9", "r10", "r11", "memory");
|
: "rcx", "r8", "r9", "r10", "r11", "memory");
|
||||||
if (cf) ax = -ax;
|
|
||||||
return 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;
|
long ax, di, si, dx;
|
||||||
asm volatile("syscall"
|
asm volatile("call\t*%8"
|
||||||
: "=a"(ax), "=D"(di), "=S"(si), "=d"(dx)
|
: "=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");
|
: "rcx", "r8", "r9", "r10", "r11", "memory", "cc");
|
||||||
}
|
}
|
||||||
|
|
||||||
static long Fstat(int os, long fd, union metastat *st) {
|
static void Execve(const char *prog, char **argv, char **envp, int os) {
|
||||||
long ax, di, si;
|
long ax, di, si, dx;
|
||||||
asm volatile("syscall"
|
asm volatile("call\t*%8"
|
||||||
: "=a"(ax), "=D"(di), "=S"(si)
|
: "=a"(ax), "=D"(di), "=S"(si), "=d"(dx)
|
||||||
: "0"(__NR_fstat), "1"(fd), "2"(st)
|
: "0"((59) | (os == XNU ? 0x2000000 : 0)), "1"(prog), "2"(argv),
|
||||||
: "rcx", "rdx", "r8", "r9", "r10", "r11", "memory", "cc");
|
"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;
|
return ax;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void Msyscall(int os, long p, long n) {
|
static void Msyscall(long p, long n, int os) {
|
||||||
long ax, di, si;
|
long ax, di, si;
|
||||||
if (os == OPENBSD) {
|
if (os == OPENBSD) {
|
||||||
asm volatile("syscall"
|
asm volatile("call\t*%6"
|
||||||
: "=a"(ax), "=D"(di), "=S"(si)
|
: "=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");
|
: "rcx", "rdx", "r8", "r9", "r10", "r11", "memory", "cc");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static long Open(int os, const char *path, long flags, long mode) {
|
static long Open(const char *path, long flags, long mode, int os) {
|
||||||
bool cf;
|
|
||||||
long ax, di, si, dx;
|
long ax, di, si, dx;
|
||||||
asm volatile("clc\n\t"
|
asm volatile("call\t*%8"
|
||||||
"syscall"
|
: "=a"(ax), "=D"(di), "=S"(si), "=d"(dx)
|
||||||
: "=@ccc"(cf), "=a"(ax), "=D"(di), "=S"(si), "=d"(dx)
|
: "0"((os == LINUX ? 2 : 5) | (os == XNU ? 0x2000000 : 0)),
|
||||||
: "1"(__NR_open), "2"(path), "3"(flags), "4"(mode)
|
"1"(path), "2"(flags), "3"(mode), "m"(syscall)
|
||||||
: "rcx", "r8", "r9", "r10", "r11", "memory");
|
: "rcx", "r8", "r9", "r10", "r11", "memory");
|
||||||
if (cf) ax = -ax;
|
|
||||||
return ax;
|
return ax;
|
||||||
}
|
}
|
||||||
|
|
||||||
static long Mmap(int os, long addr, long size, long prot, long flags, long fd,
|
__attribute__((__noinline__)) long Mmap(long addr, long size, long prot,
|
||||||
long off) {
|
long flags, long fd, long off, int os) {
|
||||||
bool cf;
|
|
||||||
long ax;
|
long ax;
|
||||||
register long flags_ asm("r10") = flags;
|
register long flags_ asm("r10") = flags;
|
||||||
register long fd_ asm("r8") = fd;
|
register long fd_ asm("r8") = fd;
|
||||||
register long off_ asm("r9") = off;
|
register long off_ asm("r9") = off;
|
||||||
asm volatile("push\t%%r9\n\t"
|
asm volatile("push\t%%r9\n\t"
|
||||||
"push\t%%r9\n\t"
|
"call\t*%8\n\t"
|
||||||
"clc\n\t"
|
|
||||||
"syscall\n\t"
|
|
||||||
"pop\t%%r9\n\t"
|
|
||||||
"pop\t%%r9"
|
"pop\t%%r9"
|
||||||
: "=@ccc"(cf), "=a"(ax)
|
: "=a"(ax)
|
||||||
: "1"(__NR_mmap), "D"(addr), "S"(size), "d"(prot), "r"(flags_),
|
: "0"((os == LINUX ? 9
|
||||||
"r"(fd_), "r"(off_)
|
: 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");
|
: "rcx", "r11", "memory");
|
||||||
if (cf) ax = -ax;
|
|
||||||
return ax;
|
return ax;
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t GetFdSize(int os, int fd) {
|
static void Emit(int os, const char *s) {
|
||||||
union metastat st;
|
Write(2, s, StrLen(s), os);
|
||||||
if (!Fstat(os, fd, &st)) {
|
}
|
||||||
if (os == LINUX) {
|
|
||||||
return st.linux.st_size;
|
static void Perror(int os, const char *c, int rc, const char *s) {
|
||||||
} else if (os == FREEBSD) {
|
char ibuf[21];
|
||||||
return st.freebsd.st_size;
|
Emit(os, "ape error: ");
|
||||||
} else if (os == OPENBSD) {
|
Emit(os, c);
|
||||||
return st.openbsd.st_size;
|
Emit(os, ": ");
|
||||||
} else {
|
Emit(os, s);
|
||||||
return st.netbsd.st_size;
|
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 {
|
} else {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t Length(const char *s) {
|
static char IsComPath(struct PathSearcher *ps) {
|
||||||
size_t n = 0;
|
return EndsWithIgnoreCase(ps->name, ps->namelen, ".com") ||
|
||||||
while (*s++) ++n;
|
EndsWithIgnoreCase(ps->name, ps->namelen, ".exe") ||
|
||||||
return n;
|
EndsWithIgnoreCase(ps->name, ps->namelen, ".com.dbg");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void Emit(int os, const char *s) {
|
static char AccessCommand(struct PathSearcher *ps, const char *suffix,
|
||||||
Write(os, 2, s, Length(s));
|
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) {
|
static char SearchPath(struct PathSearcher *ps, const char *suffix) {
|
||||||
#ifndef NDEBUG
|
const char *p;
|
||||||
Emit(os, "ape loader error: ");
|
unsigned long i;
|
||||||
Emit(os, s);
|
for (p = ps->syspath;;) {
|
||||||
#endif
|
for (i = 0; p[i] && p[i] != ':'; ++i) {
|
||||||
}
|
if (i < sizeof(ps->path)) {
|
||||||
|
ps->path[i] = p[i];
|
||||||
static void Spawn(int os, int fd, long *sp, char *b, struct Elf64_Ehdr *e) {
|
}
|
||||||
size_t i;
|
}
|
||||||
int prot, flags;
|
if (AccessCommand(ps, suffix, i)) {
|
||||||
long code, codesize;
|
return 1;
|
||||||
struct Elf64_Phdr *p;
|
} else if (p[i] == ':') {
|
||||||
if (e->e_ident[EI_CLASS] != ELFCLASS64) {
|
p += i + 1;
|
||||||
Log(os, "EI_CLASS != ELFCLASS64\n");
|
} else {
|
||||||
return;
|
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) {
|
if (e->e_machine != EM_NEXGEN32E) {
|
||||||
Log(os, "e_machine != EM_NEXGEN32E\n");
|
Pexit(os, exe, 0, "ELF e_machine != EM_NEXGEN32E");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (e->e_type != ET_EXEC) {
|
if (e->e_ident[EI_CLASS] != ELFCLASS64) {
|
||||||
Log(os, "e_type != ET_EXEC\n");
|
Pexit(os, exe, 0, "ELF e_ident[EI_CLASS] != ELFCLASS64");
|
||||||
return;
|
}
|
||||||
|
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) {
|
if (e->e_phoff + e->e_phnum * sizeof(*p) > 0x1000) {
|
||||||
Log(os, "phnum out of bounds\n");
|
Pexit(os, exe, 0, "ELF phdrs need to be in first page");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
code = 0;
|
code = 0;
|
||||||
codesize = 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_type != PT_LOAD) continue;
|
||||||
if ((p[i].p_vaddr | p[i].p_filesz | p[i].p_memsz | p[i].p_offset) & 0xfff) {
|
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");
|
Pexit(os, exe, 0, "APE phdrs must be 4096-aligned and 4096-padded");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
prot = 0;
|
prot = 0;
|
||||||
flags = MAP_FIXED | MAP_PRIVATE;
|
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;
|
codesize = p[i].p_filesz;
|
||||||
}
|
}
|
||||||
if (p[i].p_memsz > 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,
|
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) < 0) {
|
prot, flags | MAP_ANONYMOUS, -1, 0, os)) < 0) {
|
||||||
Log(os, "bss mmap failed\n");
|
Pexit(os, exe, rc, "bss mmap()");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (p[i].p_filesz) {
|
if (p[i].p_filesz) {
|
||||||
if (Mmap(os, p[i].p_vaddr, p[i].p_filesz, prot, flags, fd,
|
if ((rc = Mmap(p[i].p_vaddr, p[i].p_filesz, prot, flags, fd,
|
||||||
p[i].p_offset) < 0) {
|
p[i].p_offset, os)) < 0) {
|
||||||
Log(os, "image mmap failed\n");
|
Pexit(os, exe, rc, "image mmap()");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Close(os, fd);
|
if (!code) {
|
||||||
Msyscall(os, code, codesize);
|
Pexit(os, exe, 0, "ELF needs PT_LOAD phdr w/ PF_X");
|
||||||
sp[1] = sp[0] - 1;
|
}
|
||||||
++sp;
|
Close(fd, os);
|
||||||
asm volatile("mov\t%2,%%rsp\n\t"
|
Msyscall(code, codesize, os);
|
||||||
"jmpq\t*%1"
|
#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 */
|
: /* 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");
|
: "memory");
|
||||||
unreachable;
|
__builtin_unreachable();
|
||||||
}
|
}
|
||||||
|
|
||||||
void loader(long di, long *sp) {
|
__attribute__((__noreturn__)) void ApeLoader(long di, long *sp, char dl,
|
||||||
size_t size;
|
struct ApeLoader *handoff) {
|
||||||
long rc, *auxv;
|
long rc, *auxv;
|
||||||
char *p, **argv;
|
struct ElfEhdr *ehdr;
|
||||||
int c, i, fd, os, argc;
|
int c, i, fd, os, argc;
|
||||||
union {
|
char *p, *exe, *prog, **argv, **envp, *page;
|
||||||
struct Elf64_Ehdr ehdr;
|
static union {
|
||||||
|
struct ElfEhdr ehdr;
|
||||||
char p[0x1000];
|
char p[0x1000];
|
||||||
} u;
|
} u;
|
||||||
os = 0;
|
|
||||||
|
// detect freebsd
|
||||||
if (di) {
|
if (di) {
|
||||||
os = FREEBSD;
|
os = FREEBSD;
|
||||||
sp = (long *)di;
|
sp = (long *)di;
|
||||||
|
} else if (dl == XNU) {
|
||||||
|
os = XNU;
|
||||||
|
} else {
|
||||||
|
os = LINUX;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// extract arguments
|
||||||
argc = *sp;
|
argc = *sp;
|
||||||
argv = (char **)(sp + 1);
|
argv = (char **)(sp + 1);
|
||||||
auxv = (long *)(argv + argc + 1);
|
envp = (char **)(sp + 1 + argc + 1);
|
||||||
|
auxv = (long *)(sp + 1 + argc + 1);
|
||||||
for (;;) {
|
for (;;) {
|
||||||
if (!*auxv++) {
|
if (!*auxv++) {
|
||||||
break;
|
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 (handoff) {
|
||||||
if (auxv[0] == AT_EXECFN_NETBSD) {
|
// we were called by ape_execve()
|
||||||
os = NETBSD;
|
// no argument parsing is needed
|
||||||
if (argc > 1) {
|
// no path searching is needed
|
||||||
auxv[1] = (long)argv[1];
|
exe = handoff->prog;
|
||||||
}
|
fd = handoff->fd;
|
||||||
} else if (auxv[0] == AT_EXECFN_LINUX) {
|
os = handoff->os;
|
||||||
if (argc > 1) {
|
exe = handoff->prog;
|
||||||
auxv[1] = (long)argv[1];
|
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");
|
#if TROUBLESHOOT
|
||||||
} else if ((fd = Open(os, argv[1], O_RDONLY, 0)) < 0) {
|
Emit(TROUBLESHOOT_OS, "os = ");
|
||||||
Log(os, "open failed\n");
|
Emit(TROUBLESHOOT_OS, DescribeOs(os));
|
||||||
} else if ((rc = Read(os, fd, u.p, sizeof(u.p))) < 0) {
|
Emit(TROUBLESHOOT_OS, "\n");
|
||||||
Log(os, "read failed\n");
|
for (i = 0; i < argc; ++i) {
|
||||||
} else if (rc != sizeof(u.p)) {
|
Emit(TROUBLESHOOT_OS, "argv = ");
|
||||||
Log(os, "file too small\n");
|
Emit(TROUBLESHOOT_OS, argv[i]);
|
||||||
} else if (READ32LE(u.p) == READ32LE("\177ELF")) {
|
Emit(TROUBLESHOOT_OS, "\n");
|
||||||
Spawn(os, fd, sp, u.p, &u.ehdr);
|
}
|
||||||
} else {
|
#endif
|
||||||
for (p = u.p; p < u.p + sizeof(u.p); ++p) {
|
|
||||||
if (READ64LE(p) == READ64LE("printf '")) {
|
if (Read32(page) == Read32("\177ELF") || Read32(page) == 0xFEEDFACE + 1) {
|
||||||
for (i = 0, p += 8; p + 3 < u.p + sizeof(u.p) && (c = *p++) != '\'';) {
|
Close(fd, os);
|
||||||
if (c == '\\') {
|
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') {
|
if ('0' <= *p && *p <= '7') {
|
||||||
c = *p++ - '0';
|
c *= 8;
|
||||||
if ('0' <= *p && *p <= '7') {
|
c += *p++ - '0';
|
||||||
c *= 8;
|
|
||||||
c += *p++ - '0';
|
|
||||||
if ('0' <= *p && *p <= '7') {
|
|
||||||
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");
|
||||||
}
|
}
|
||||||
|
|
21
ape/loader.h
Normal file
21
ape/loader.h
Normal file
|
@ -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_ */
|
|
@ -24,9 +24,18 @@ SECTIONS {
|
||||||
.text : {
|
.text : {
|
||||||
*(.text)
|
*(.text)
|
||||||
*(.rodata .rodata.*)
|
*(.rodata .rodata.*)
|
||||||
|
. = ALIGN(4096);
|
||||||
}
|
}
|
||||||
filesz = . - ehdr;
|
filesz = . - ehdr;
|
||||||
|
.bss ALIGN(4096) : {
|
||||||
|
bss = .;
|
||||||
|
*(.bss)
|
||||||
|
. = ALIGN(4096);
|
||||||
|
}
|
||||||
|
memsz = . - ehdr;
|
||||||
/DISCARD/ : {
|
/DISCARD/ : {
|
||||||
*(.*)
|
*(.*)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bsssize = SIZEOF(.bss);
|
||||||
|
|
BIN
build/bootstrap/ape
Normal file
BIN
build/bootstrap/ape
Normal file
Binary file not shown.
|
@ -37,15 +37,14 @@ ERROR
|
||||||
|
|
||||||
DETAILS
|
DETAILS
|
||||||
|
|
||||||
Actually Portable Executable assumes stock Linux configuration.
|
Your system has likely been configured to use binfmt_misc and wine.
|
||||||
Normal behavior is non-ELF files with x bit are run by /bin/sh.
|
You need to run the command below which will install a /usr/bin/ape
|
||||||
Linux lets people globally define arbitrary magic interpreters.
|
program and then register it with binfmt_misc. See ape/loader.c for
|
||||||
Your computer couldve been tuned to run MZ scripts inside WINE.
|
source code and technical details.
|
||||||
So if you use binfmt_misc you need to explicitly register this.
|
|
||||||
|
|
||||||
WORKAROUND
|
WORKAROUND
|
||||||
|
|
||||||
sudo sh -c "echo ':APE:M::MZqFpD::/bin/sh:' >/proc/sys/fs/binfmt_misc/register"
|
ape/apeinstall.sh
|
||||||
|
|
||||||
SEE ALSO
|
SEE ALSO
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,12 @@
|
||||||
MZqFpD=123
|
MZboop=123
|
||||||
exit $MZqFpD
|
exit $MZboop
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -104,6 +104,14 @@ o/$(MODE)/examples/%.com.dbg: \
|
||||||
$(APE)
|
$(APE)
|
||||||
@$(APELINK)
|
@$(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: \
|
o/$(MODE)/examples/hellolua.com.dbg: \
|
||||||
$(EXAMPLES_DEPS) \
|
$(EXAMPLES_DEPS) \
|
||||||
o/$(MODE)/examples/hellolua.o \
|
o/$(MODE)/examples/hellolua.o \
|
||||||
|
|
17
examples/mkhello.c
Normal file
17
examples/mkhello.c
Normal file
|
@ -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;
|
||||||
|
}
|
36
examples/nomodifyself.c
Normal file
36
examples/nomodifyself.c
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -64,7 +64,7 @@ noasan texthead uint64_t __new_page(struct mman *mm) {
|
||||||
* Returns pointer to page table entry for page at virtual address.
|
* Returns pointer to page table entry for page at virtual address.
|
||||||
* Additional page tables are allocated if needed as a side-effect.
|
* 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) {
|
int64_t vaddr, bool maketables) {
|
||||||
uint64_t *e, p;
|
uint64_t *e, p;
|
||||||
unsigned char h;
|
unsigned char h;
|
||||||
|
|
34
libc/calls/nanos.c
Normal file
34
libc/calls/nanos.c
Normal file
|
@ -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;
|
||||||
|
}
|
10
libc/calls/nanos.h
Normal file
10
libc/calls/nanos.h
Normal file
|
@ -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_ */
|
|
@ -21,6 +21,7 @@
|
||||||
#include "libc/calls/sigbits.h"
|
#include "libc/calls/sigbits.h"
|
||||||
#include "libc/calls/strace.internal.h"
|
#include "libc/calls/strace.internal.h"
|
||||||
#include "libc/intrin/cmpxchg.h"
|
#include "libc/intrin/cmpxchg.h"
|
||||||
|
#include "libc/intrin/lockcmpxchg.h"
|
||||||
#include "libc/intrin/spinlock.h"
|
#include "libc/intrin/spinlock.h"
|
||||||
#include "libc/log/libfatal.internal.h"
|
#include "libc/log/libfatal.internal.h"
|
||||||
#include "libc/macros.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
|
// since sigaction() is @asyncsignalsafe we only restore it if the
|
||||||
// user didn't change it during the signal handler. we also don't
|
// 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.
|
// need to do anything if this was a oneshot signal or nodefer.
|
||||||
_spinlock(&__sig_lock);
|
_lockcmpxchg(__sighandrvas + sig, (int32_t)(intptr_t)SIG_DFL, rva);
|
||||||
_cmpxchg(__sighandrvas + sig, (int32_t)(intptr_t)SIG_DFL, rva);
|
|
||||||
_spunlock(&__sig_lock);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!restartable) {
|
if (!restartable) {
|
||||||
|
|
|
@ -26,6 +26,8 @@
|
||||||
//
|
//
|
||||||
// @param rsp is [n,argv₀..argvₙ₋₁,0,envp₀..,0,auxv₀..,0,..]
|
// @param rsp is [n,argv₀..argvₙ₋₁,0,envp₀..,0,auxv₀..,0,..]
|
||||||
// @note FreeBSD is special (see freebsd/lib/csu/amd64/...)
|
// @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
|
// @noreturn
|
||||||
_start:
|
_start:
|
||||||
|
|
||||||
|
@ -34,12 +36,16 @@ _start:
|
||||||
test %rdi,%rdi
|
test %rdi,%rdi
|
||||||
cmovnz %rdi,%rsp
|
cmovnz %rdi,%rsp
|
||||||
jz 0f
|
jz 0f
|
||||||
movb $FREEBSD,__hostos(%rip)
|
movb $FREEBSD,%cl
|
||||||
|
0:
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// set operating system when already detected
|
||||||
|
mov %cl,__hostos(%rip)
|
||||||
|
|
||||||
// get startup timestamp as early as possible
|
// get startup timestamp as early as possible
|
||||||
// its used by --strace flag and kprintf() %T
|
// its used by --strace flag and kprintf() %T
|
||||||
0: rdtsc
|
rdtsc
|
||||||
ezlea kStartTsc,bx
|
ezlea kStartTsc,bx
|
||||||
mov %eax,(%rbx)
|
mov %eax,(%rbx)
|
||||||
mov %edx,4(%rbx)
|
mov %edx,4(%rbx)
|
||||||
|
@ -85,14 +91,3 @@ _start:
|
||||||
call cosmo
|
call cosmo
|
||||||
9: .unreachable
|
9: .unreachable
|
||||||
.endfn _start,weak,hidden
|
.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
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
COSMOPOLITAN_C_START_
|
COSMOPOLITAN_C_START_
|
||||||
|
|
||||||
char *sleb64(char *, int64_t);
|
char *sleb64(char *, int64_t);
|
||||||
char *zleb64(char *, int64_t);
|
char *zleb64(char[hasatleast 10], int64_t);
|
||||||
char *uleb64(char[hasatleast 10], uint64_t);
|
char *uleb64(char[hasatleast 10], uint64_t);
|
||||||
int unzleb64(const char *, size_t, int64_t *);
|
int unzleb64(const char *, size_t, int64_t *);
|
||||||
int unuleb64(char *, size_t, uint64_t *);
|
int unuleb64(char *, size_t, uint64_t *);
|
||||||
|
|
|
@ -44,7 +44,7 @@
|
||||||
* @return p + i
|
* @return p + i
|
||||||
* @see unzleb64()
|
* @see unzleb64()
|
||||||
*/
|
*/
|
||||||
char *zleb64(char *p, int64_t x) {
|
char *zleb64(char p[hasatleast 10], int64_t x) {
|
||||||
int c;
|
int c;
|
||||||
uint64_t u;
|
uint64_t u;
|
||||||
u = x;
|
u = x;
|
||||||
|
|
|
@ -58,7 +58,10 @@ struct CloneArgs {
|
||||||
uint32_t utid;
|
uint32_t utid;
|
||||||
int64_t tid64;
|
int64_t tid64;
|
||||||
};
|
};
|
||||||
int lock;
|
union {
|
||||||
|
int lock;
|
||||||
|
void *pstack;
|
||||||
|
};
|
||||||
int *ctid;
|
int *ctid;
|
||||||
int *ztid;
|
int *ztid;
|
||||||
char *tls;
|
char *tls;
|
||||||
|
@ -287,12 +290,18 @@ __attribute__((__used__, __no_reorder__))
|
||||||
static privileged wontreturn void
|
static privileged wontreturn void
|
||||||
OpenbsdThreadMain(struct CloneArgs *wt) {
|
OpenbsdThreadMain(struct CloneArgs *wt) {
|
||||||
wt->func(wt->arg);
|
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);
|
// 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()
|
"syscall" // _Exit1()
|
||||||
: "=m"(*wt->ztid)
|
: "=m"(*wt->ztid)
|
||||||
: "a"(302), "D"(0)
|
: "a"(302), "D"(0), "r"(wt->pstack)
|
||||||
: "rcx", "r11", "memory");
|
: "rcx", "r11", "memory");
|
||||||
unreachable;
|
unreachable;
|
||||||
}
|
}
|
||||||
|
@ -307,6 +316,7 @@ static int CloneOpenbsd(int (*func)(void *), char *stk, size_t stksz, int flags,
|
||||||
-alignof(struct CloneArgs));
|
-alignof(struct CloneArgs));
|
||||||
wt->ctid = flags & CLONE_CHILD_SETTID ? ctid : &wt->tid;
|
wt->ctid = flags & CLONE_CHILD_SETTID ? ctid : &wt->tid;
|
||||||
wt->ztid = flags & CLONE_CHILD_CLEARTID ? ctid : &wt->tid;
|
wt->ztid = flags & CLONE_CHILD_CLEARTID ? ctid : &wt->tid;
|
||||||
|
wt->pstack = __builtin_frame_address(0);
|
||||||
wt->func = func;
|
wt->func = func;
|
||||||
wt->arg = arg;
|
wt->arg = arg;
|
||||||
params.tf_stack = wt;
|
params.tf_stack = wt;
|
||||||
|
|
|
@ -47,7 +47,7 @@ static struct Ftrace {
|
||||||
int64_t lastaddr;
|
int64_t lastaddr;
|
||||||
} g_ftrace;
|
} g_ftrace;
|
||||||
|
|
||||||
static privileged int GetNestingLevelImpl(struct StackFrame *frame) {
|
static privileged inline int GetNestingLevelImpl(struct StackFrame *frame) {
|
||||||
int nesting = -2;
|
int nesting = -2;
|
||||||
while (frame) {
|
while (frame) {
|
||||||
++nesting;
|
++nesting;
|
||||||
|
@ -56,7 +56,7 @@ static privileged int GetNestingLevelImpl(struct StackFrame *frame) {
|
||||||
return MAX(0, nesting);
|
return MAX(0, nesting);
|
||||||
}
|
}
|
||||||
|
|
||||||
static privileged int GetNestingLevel(struct StackFrame *frame) {
|
static privileged inline int GetNestingLevel(struct StackFrame *frame) {
|
||||||
int nesting;
|
int nesting;
|
||||||
nesting = GetNestingLevelImpl(frame);
|
nesting = GetNestingLevelImpl(frame);
|
||||||
if (nesting < g_ftrace.skew) g_ftrace.skew = nesting;
|
if (nesting < g_ftrace.skew) g_ftrace.skew = nesting;
|
||||||
|
|
|
@ -9,9 +9,9 @@
|
||||||
#define __BENCH_ARRAY(S) \
|
#define __BENCH_ARRAY(S) \
|
||||||
_Section(".piro.relo.sort.bench.2." #S ",\"aw\",@init_array #")
|
_Section(".piro.relo.sort.bench.2." #S ",\"aw\",@init_array #")
|
||||||
|
|
||||||
#define __TEST_PROTOTYPE(S, N, A, K) \
|
#define __TEST_PROTOTYPE(S, N, A, K) \
|
||||||
void S##_##N(void); \
|
void S##_##N(void); \
|
||||||
const void *const S##_##N##_ptr[] A(S##_##N) = {S##_##N}; \
|
testfn_t S##_##N##_ptr[] A(S##_##N) = {S##_##N}; \
|
||||||
testonly K void S##_##N(void)
|
testonly K void S##_##N(void)
|
||||||
|
|
||||||
#define __TEST_SECTION(NAME, CONTENT) \
|
#define __TEST_SECTION(NAME, CONTENT) \
|
||||||
|
|
|
@ -23,13 +23,7 @@
|
||||||
#include "libc/testlib/testlib.h"
|
#include "libc/testlib/testlib.h"
|
||||||
|
|
||||||
void SetUp(void) {
|
void SetUp(void) {
|
||||||
if (getenv("_SUBPROCESS")) {
|
if (getenv("_WEIRDENV")) {
|
||||||
if (!__argv[0]) {
|
|
||||||
exit(0);
|
|
||||||
} else {
|
|
||||||
exit(7);
|
|
||||||
}
|
|
||||||
} else if (getenv("_WEIRDENV")) {
|
|
||||||
for (char **e = environ; *e; ++e) {
|
for (char **e = environ; *e; ++e) {
|
||||||
if (!strcmp(*e, "WEIRD")) {
|
if (!strcmp(*e, "WEIRD")) {
|
||||||
exit(0);
|
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) {
|
TEST(execve, testWeirdEnvironmentVariable) {
|
||||||
char *prog;
|
char *prog;
|
||||||
int pid, ws;
|
int pid, ws;
|
||||||
|
|
|
@ -3,24 +3,32 @@
|
||||||
|
|
||||||
PKGS += TEST_LIBC_STR
|
PKGS += TEST_LIBC_STR
|
||||||
|
|
||||||
TEST_LIBC_STR_SRCS := $(wildcard test/libc/str/*.c)
|
TEST_LIBC_STR_FILES := $(wildcard test/libc/str/*)
|
||||||
TEST_LIBC_STR_SRCS_TEST = $(filter %_test.c,$(TEST_LIBC_STR_SRCS))
|
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_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_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_BINS = \
|
||||||
$(TEST_LIBC_STR_COMS) \
|
$(TEST_LIBC_STR_COMS) \
|
||||||
$(TEST_LIBC_STR_COMS:%=%.dbg)
|
$(TEST_LIBC_STR_COMS:%=%.dbg)
|
||||||
|
|
||||||
TEST_LIBC_STR_TESTS = \
|
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_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 = \
|
TEST_LIBC_STR_DIRECTDEPS = \
|
||||||
LIBC_ALG \
|
LIBC_ALG \
|
||||||
|
@ -43,7 +51,9 @@ TEST_LIBC_STR_DIRECTDEPS = \
|
||||||
LIBC_ZIPOS \
|
LIBC_ZIPOS \
|
||||||
THIRD_PARTY_MBEDTLS \
|
THIRD_PARTY_MBEDTLS \
|
||||||
THIRD_PARTY_REGEX \
|
THIRD_PARTY_REGEX \
|
||||||
THIRD_PARTY_ZLIB
|
THIRD_PARTY_ZLIB \
|
||||||
|
THIRD_PARTY_LIBCXX \
|
||||||
|
THIRD_PARTY_SMALLZ4
|
||||||
|
|
||||||
TEST_LIBC_STR_DEPS := \
|
TEST_LIBC_STR_DEPS := \
|
||||||
$(call uniq,$(foreach x,$(TEST_LIBC_STR_DIRECTDEPS),$($(x))))
|
$(call uniq,$(foreach x,$(TEST_LIBC_STR_DIRECTDEPS),$($(x))))
|
||||||
|
|
2
third_party/make/config.h
vendored
2
third_party/make/config.h
vendored
|
@ -620,7 +620,7 @@
|
||||||
/* #undef HAVE__SET_INVALID_PARAMETER_HANDLER */
|
/* #undef HAVE__SET_INVALID_PARAMETER_HANDLER */
|
||||||
|
|
||||||
/* Build host information. */
|
/* 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. */
|
/* Define to 1 to enable job server support in GNU make. */
|
||||||
/* TODO(jart): make it work */
|
/* TODO(jart): make it work */
|
||||||
|
|
14
third_party/make/dir.c
vendored
14
third_party/make/dir.c
vendored
|
@ -689,15 +689,9 @@ void print_dir_data_base(void) {
|
||||||
if (dir->contents == 0)
|
if (dir->contents == 0)
|
||||||
printf(_("# %s: could not be stat'd.\n"), dir->name);
|
printf(_("# %s: could not be stat'd.\n"), dir->name);
|
||||||
else if (dir->contents->dirfiles.ht_vec == 0) {
|
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"),
|
printf(_("# %s (device %ld, inode %ld): could not be opened.\n"),
|
||||||
dir->name, (long int)dir->contents->dev,
|
dir->name, (long int)dir->contents->dev,
|
||||||
(long int)dir->contents->ino);
|
(long int)dir->contents->ino);
|
||||||
#endif /* WINDOWS32 */
|
|
||||||
} else {
|
} else {
|
||||||
unsigned int f = 0;
|
unsigned int f = 0;
|
||||||
unsigned int im = 0;
|
unsigned int im = 0;
|
||||||
|
@ -715,14 +709,8 @@ void print_dir_data_base(void) {
|
||||||
++f;
|
++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,
|
printf(_("# %s (device %ld, inode %ld): "), dir->name,
|
||||||
(long)dir->contents->dev, (long)dir->contents->ino);
|
(long)dir->contents->dev, (long)dir->contents->ino);
|
||||||
#endif /* WINDOWS32 */
|
|
||||||
if (f == 0)
|
if (f == 0)
|
||||||
fputs(_("No"), stdout);
|
fputs(_("No"), stdout);
|
||||||
else
|
else
|
||||||
|
@ -822,9 +810,7 @@ static struct dirent *read_dirstream(__ptr_t stream) {
|
||||||
#ifdef _DIRENT_HAVE_D_NAMLEN
|
#ifdef _DIRENT_HAVE_D_NAMLEN
|
||||||
d->d_namlen = len - 1;
|
d->d_namlen = len - 1;
|
||||||
#endif
|
#endif
|
||||||
#ifdef HAVE_STRUCT_DIRENT_D_TYPE
|
|
||||||
d->d_type = df->type;
|
d->d_type = df->type;
|
||||||
#endif
|
|
||||||
memcpy(d->d_name, df->name, len);
|
memcpy(d->d_name, df->name, len);
|
||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
|
|
12
third_party/smallz4/smallz4.hh
vendored
12
third_party/smallz4/smallz4.hh
vendored
|
@ -1,5 +1,6 @@
|
||||||
#ifndef COSMOPOLITAN_THIRD_PARTY_SMALLZ4_SMALLZ4_H_
|
#ifndef COSMOPOLITAN_THIRD_PARTY_SMALLZ4_SMALLZ4_H_
|
||||||
#define COSMOPOLITAN_THIRD_PARTY_SMALLZ4_SMALLZ4_H_
|
#define COSMOPOLITAN_THIRD_PARTY_SMALLZ4_SMALLZ4_H_
|
||||||
|
#include "libc/bits/bits.h"
|
||||||
#include "third_party/libcxx/vector"
|
#include "third_party/libcxx/vector"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -138,7 +139,7 @@ class smallz4 {
|
||||||
|
|
||||||
/// return true, if the four bytes at *a and *b match
|
/// return true, if the four bytes at *a and *b match
|
||||||
inline static bool match4(const void* const a, const void* const b) {
|
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:
|
/// simple hash function, input: 32 bits, output: HashBits bits (by default:
|
||||||
|
@ -636,7 +637,7 @@ class smallz4 {
|
||||||
}
|
}
|
||||||
|
|
||||||
// read next four bytes
|
// read next four bytes
|
||||||
const uint32_t four = *(uint32_t*)(dataBlock + i);
|
const uint32_t four = READ32LE(dataBlock + i);
|
||||||
// convert to a shorter hash
|
// convert to a shorter hash
|
||||||
const uint32_t hash = getHash32(four);
|
const uint32_t hash = getHash32(four);
|
||||||
|
|
||||||
|
@ -674,10 +675,9 @@ class smallz4 {
|
||||||
// check the hash chain
|
// check the hash chain
|
||||||
while (true) {
|
while (true) {
|
||||||
// read four bytes
|
// read four bytes
|
||||||
currentFour =
|
currentFour = READ32LE(
|
||||||
*(uint32_t*)(&data[lastHashMatch -
|
&data[lastHashMatch - dataZero]); // match may be found in the
|
||||||
dataZero]); // match may be found in the
|
// previous block, too
|
||||||
// previous block, too
|
|
||||||
// match chain found, first 4 bytes are identical
|
// match chain found, first 4 bytes are identical
|
||||||
if (currentFour == four) break;
|
if (currentFour == four) break;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue