Write tests for new APE loader and fix bugs

- Add FreeBSD-specific mmap() flags
- Reduce size of the APE loader from 8kb to 4kb
- Work towards fixing the Makefile build on WSL
- Automate testing of APE no-modify-self behaviors
- Make the ape.S shell script code cleaner and tinier
- Improve the APE sanity check to test behavior better
- Fixed issue with ShowCrashReports() sigaltstack() on BSDs
- Delete symbols for S_MODE magnums which wasted compile time

If you checked out yesterday's APE commit, please run:

    rm -f /usr/bin/ape o/tmp/ape /tmp/ape "${TMPDIR:-/tmp}/ape"

Because this change fixes certain aspects of the new ABI. We don't have
automated migrations for APE loader versions yet. Thanks! You can also
download prebuilt binaries here:

- https://justine.lol/ape.elf    (Linux/FreeBSD/NetBSD/OpenBSD)
- https://justine.lol/ape.macho  (Apple)

Install the appropriate one as `/usr/bin/ape`.
This commit is contained in:
Justine Tunney 2022-05-22 04:51:02 -07:00
parent 056dc5f554
commit 4e9662cbc7
75 changed files with 759 additions and 443 deletions

109
ape/ape.S
View file

@ -550,25 +550,25 @@ apesh: .ascii "'\n#'\"\n" # sixth edition shebang
// extract the loader into a temp folder, and use it to
// load the APE without modifying it.
.ascii "t=\"${TMPDIR:-/tmp}/ape\"\n"
.ascii "if [ ! -x \"$t\" ]; then\n"
.ascii "if [ ! -d /Applications ]; then\n"
.ascii "[ -x \"$t\" ] || {\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\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"
.ascii "\" bs=64 2>/dev/null\n"
#if SupportsXnu()
.ascii "[ -d /Applications ] && "
.ascii "dd if=\"$t.$$\""
.ascii " of=\"$t.$$\""
.ascii " skip=6"
.ascii " count=10"
.ascii " bs=64"
.ascii " conv=notrunc"
.ascii " 2>/dev/null\n"
#endif /* SupportsXnu() */
.ascii "chmod 755 \"$t.$$\"\n"
.ascii "mv -f \"$t.$$\" \"$t\"\n"
.ascii "}\n"
.ascii "exec \"$t\" \"$o\" \"$@\"\n"
#endif /* APE_LOADER */
#ifndef APE_NO_MODIFY_SELF
@ -589,26 +589,14 @@ apesh: .ascii "'\n#'\"\n" # sixth edition shebang
// then permission clashes can happen between system users,
// since only root is able to set the sticky bit, which can
// be addressed simply by overriding the TMPDIR environment
.ascii "o=\"${TMPDIR:-/tmp}/$0\"\n"
.ascii "if [ ! -e \"$o\" ]; then\n"
.ascii "d=\"$o\"\n"
.ascii "o=\"$o.$$\"\n"
.ascii "mkdir -p \"${o%/*}\" 2>/dev/null\n"
.ascii "cp -f \"$0\" \"$o\" || exit 120\n"
.ascii "t=\"${TMPDIR:-/tmp}/$0\"\n"
.ascii "[ -e \"$t\" ] || {\n"
.ascii "mkdir -p \"${t%/*}\" 2>/dev/null\n"
.ascii "cp -f \"$o\" \"$t.$$\" &&\n"
.ascii "mv -f \"$t.$$\" \"$t\" || exit 120\n"
.ascii "o=\"$t\"\n"
#endif /* APE_NO_MODIFY_SELF */
#if SupportsXnu()
.ascii "if [ -d /Applications ]; then\n"
.ascii "dd if=\"$o\""
.ascii " of=\"$o\""
.ascii " bs=8"
.ascii " skip=\""
.shstub ape_macho_dd_skip,2
.ascii "\" count=\""
.shstub ape_macho_dd_count,2
.ascii "\" conv=notrunc 2>/dev/null\n"
.ascii "el"
#endif /* XNU */
.ascii "if exec 7<> \"$o\"; then\n"
.ascii "exec 7<> \"$o\" || exit 121\n"
.ascii "printf '"
.ascii "\\177ELF" # 0x0: ELF
.ascii "\\2" # 4: long mode
@ -633,27 +621,30 @@ apesh: .ascii "'\n#'\"\n" # sixth edition shebang
.shstub ape_elf_shstrndx,2 # 3e: e_shstrndx
.ascii "' >&7\n"
.ascii "exec 7<&-\n"
.ascii "else\n"
.ascii "exit 121\n"
.ascii "fi\n"
#if SupportsXnu()
.ascii "[ -d /Applications ] && "
.ascii "dd if=\"$o\""
.ascii " of=\"$o\""
.ascii " bs=8"
.ascii " skip=\""
.shstub ape_macho_dd_skip,2
.ascii "\" count=\""
.shstub ape_macho_dd_count,2
.ascii "\" conv=notrunc 2>/dev/null\n"
#endif /* XNU */
#ifndef APE_NO_MODIFY_SELF
.ascii "exec \"$0\" \"$@\"\n" # optimistic execution
.ascii "exec \"$0\" \"$@\"\n" # try to preserve argv[0]
#else
.ascii "mv -f \"$o\" \"$d\" 2>/dev/null\n"
.ascii "o=\"$d\"\n"
.ascii "fi\n"
.ascii "}\n"
.ascii "o=\"$t\"\n"
.ascii "exec \"$o\" \"$@\"\n"
#endif /* APE_NO_MODIFY_SELF */
.ascii "R=$?\n"
.ascii "\n"
.ascii "if [ $R -eq 126 ] && [ \"$(uname -m)\" != x86_64 ]; then\n"
.ascii "if Q=\"$(command -v qemu-x86_64)\"; then\n"
.ascii "if [ \"$(uname -m)\" != x86_64 ]; then\n"
.ascii "Q=\"$(command -v qemu-x86_64)\" &&\n"
.ascii "exec \"$Q\" \"$o\" \"$@\"\n"
.ascii "else\n"
.ascii "echo error: need qemu-x86_64 >&2\n"
.ascii "fi\n"
#ifndef APE_NO_MODIFY_SELF
.ascii "elif [ $R -eq 127 ]; then\n" # means argv[0] was wrong
.ascii "else\n" # means argv[0] was wrong
.ascii " exec \"$o\" \"$@\"\n" # so do a path resolution
#endif /* APE_NO_MODIFY_SELF */
.ascii "fi\n"
@ -661,14 +652,16 @@ apesh: .ascii "'\n#'\"\n" # sixth edition shebang
.endobj apesh
#ifdef APE_LOADER
.section .ape.loader,"a",@progbits
.align 64
ape_loader:
.incbin APE_LOADER
.endobj ape_loader,globl
.align 64
ape_loader_end:
nop
.endobj ape_loader_end,globl
.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()
@ -1521,12 +1514,9 @@ kernel: movabs $ape_stack_vaddr,%rsp
.byte 0x0f,0x1f,0207 # nop rdi binbase
.long (IMAGE_BASE_VIRTUAL-IMAGE_BASE_REAL)/512
#endif
.weak __hostos
ezlea __hostos,ax
test %rax,%rax
jz 1f
movb $METAL,(%rax)
1: push $0
push $METAL # sets __hostos in crt.S
pop %rcx
push $0
mov %rsp,%rbp
mov .Lenv0(%rip),%rax
mov %rax,(%rbp) # envp[0][0]
@ -1542,7 +1532,6 @@ kernel: movabs $ape_stack_vaddr,%rsp
push $1 # argc
xor %ebp,%ebp
xor %eax,%eax
xor %ecx,%ecx
xor %edx,%edx
xor %edi,%edi
xor %esi,%esi

View file

@ -243,10 +243,12 @@ SECTIONS {
/* Code that needs to be addressable in Real Mode */
*(.text.real)
KEEP(*(SORT_BY_NAME(.sort.text.real.*)))
/* Code we want earlier in the binary w/o modifications */
KEEP(*(.ape.loader))
HIDDEN(_ereal = .);
. += 1;
/*END: realmode addressability guarantee */
/*BEGIN: morphable code */
. += 1;
/* Normal Code */
*(.start)
@ -283,6 +285,7 @@ SECTIONS {
/* Privileged code invulnerable to magic */
KEEP(*(.ape.pad.privileged));
. += . > 0 ? 1 : 0;
/*END: morphable code */
HIDDEN(__privileged_start = .);
. += . > 0 ? 1 : 0;
*(.privileged)
@ -390,21 +393,6 @@ SECTIONS {
} :Ram
/*END: file content that's loaded by o/s */
/*BEGIN: payload (for now, only the APE loader) */
.payload ALIGN(64) : {
/* Loader */
HIDDEN(ape_loader = .);
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 */
.zip . : {
@ -552,13 +540,14 @@ HIDDEN(ape_bss_filesz = 0);
HIDDEN(ape_bss_memsz = SIZEOF(.bss));
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
/* we roundup here because xnu wants the file load segments page-aligned */
/* but we don't want to add the nop padding to the ape program, so we'll */
/* let ape.S dd read past the end of the file into the wrapping binaries */
SHSTUB2(ape_loader_dd_skip, DEFINED(ape_loader) ? RVA(ape_loader) / 64 : 0);
SHSTUB2(ape_loader_dd_count,
DEFINED(ape_loader_end)
? ROUNDUP(ape_loader_end - ape_loader, PAGESIZE) / 64
: 0);
#if SupportsXnu()
SHSTUB2(ape_macho_dd_skip, RVA(ape_macho) / 8);

View file

@ -22,6 +22,10 @@ APE_NO_MODIFY_SELF = \
o/$(MODE)/ape/ape.lds \
o/$(MODE)/ape/ape-no-modify-self.o
APE_COPY_SELF = \
o/$(MODE)/ape/ape.lds \
o/$(MODE)/ape/ape-copy-self.o
APELINK = \
$(COMPILE) \
-ALINK.ape \
@ -29,6 +33,22 @@ APELINK = \
$(LINKARGS) \
$(OUTPUT_OPTION)
APE_LOADER_FLAGS = \
-DNDEBUG \
-iquote. \
-Wall \
-Wextra \
-fpie \
-Os \
-ffreestanding \
-mgeneral-regs-only \
-mno-red-zone \
-fno-ident \
-fno-gnu-unique \
-c \
$(OUTPUT_OPTION) \
$<
APE_FILES := $(wildcard ape/*.*)
APE_HDRS = $(filter %.h,$(APE_FILES))
APE_INCS = $(filter %.inc,$(APE_FILES))
@ -50,31 +70,55 @@ o/ape/idata.inc: \
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/ape.elf
@$(COMPILE) \
-AOBJECTIFY.S \
$(OBJECTIFY.S) \
$(OUTPUT_OPTION) \
-DAPE_NO_MODIFY_SELF \
-DAPE_LOADER="\"o/$(MODE)/ape/ape.elf\"" $<
o/$(MODE)/ape/ape-copy-self.o: \
ape/ape.S
@$(COMPILE) \
-AOBJECTIFY.S \
$(OBJECTIFY.S) \
$(OUTPUT_OPTION) \
-DAPE_NO_MODIFY_SELF $<
o/$(MODE)/ape/loader.o: ape/loader.c
@$(COMPILE) -AOBJECTIFY.c $(CC) -DNDEBUG -iquote. -Wall -Wextra -fpie -Os -g -ffreestanding -mno-red-zone -fno-ident -fno-gnu-unique -c $(OUTPUT_OPTION) $<
@$(COMPILE) -AOBJECTIFY.c $(CC) -DSUPPORT_VECTOR=0b01111001 -g $(APE_LOADER_FLAGS)
o/$(MODE)/ape/loader-gcc.asm: ape/loader.c
@$(COMPILE) -AOBJECTIFY.c $(CC) -DNDEBUG -iquote. -Wall -Wextra -fpie -Os -g -ffreestanding -mno-red-zone -fno-ident -fno-gnu-unique -c -S $(OUTPUT_OPTION) $<
@$(COMPILE) -AOBJECTIFY.c $(CC) -DSUPPORT_VECTOR=0b01111001 -S -g0 $(APE_LOADER_FLAGS)
o/$(MODE)/ape/loader-clang.asm: ape/loader.c
@$(COMPILE) -AOBJECTIFY.c $(CLANG) -DSUPPORT_VECTOR=0b01111001 -S -g0 $(APE_LOADER_FLAGS)
o/$(MODE)/ape/ape: \
o/$(MODE)/ape/loader-xnu.o: ape/loader.c
@$(COMPILE) -AOBJECTIFY.c $(CC) -DSUPPORT_VECTOR=0b00001000 -g $(APE_LOADER_FLAGS)
o/$(MODE)/ape/loader-xnu-gcc.asm: ape/loader.c
@$(COMPILE) -AOBJECTIFY.c $(CC) -DSUPPORT_VECTOR=0b00001000 -S -g0 $(APE_LOADER_FLAGS)
o/$(MODE)/ape/loader-xnu-clang.asm: ape/loader.c
@$(COMPILE) -AOBJECTIFY.c $(CLANG) -DSUPPORT_VECTOR=0b00001000 -S -g0 $(APE_LOADER_FLAGS)
o/$(MODE)/ape/ape.elf: o/$(MODE)/ape/ape.elf.dbg
o/$(MODE)/ape/ape.macho: o/$(MODE)/ape/ape.macho.dbg
o/$(MODE)/ape/ape.elf.dbg: \
o/$(MODE)/ape/loader.o \
o/$(MODE)/ape/loader-elf.o \
ape/loader.lds
@$(ELFLINK) -s -z max-page-size=0x10
@$(ELFLINK) -z max-page-size=0x10
o/$(MODE)/ape/ape.macho: \
o/$(MODE)/ape/loader.o \
o/$(MODE)/ape/ape.macho.dbg: \
o/$(MODE)/ape/loader-xnu.o \
o/$(MODE)/ape/loader-macho.o \
ape/loader-macho.lds
@$(ELFLINK) -s -z max-page-size=0x10
@$(ELFLINK) -z max-page-size=0x10
.PHONY: o/$(MODE)/ape
o/$(MODE)/ape: $(APE) \
$(APE_CHECKS) \
o/$(MODE)/ape/ape \
o/$(MODE)/ape/ape.elf \
o/$(MODE)/ape/ape.macho \
o/$(MODE)/ape/ape-copy-self.o \
o/$(MODE)/ape/ape-no-modify-self.o

View file

@ -9,19 +9,19 @@ 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 running: make -j8 o//ape/ape.elf >&2
make -j8 o//ape/ape.elf || 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
cp -af build/bootstrap/ape.elf o//ape/ape.elf
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 installing o//ape/ape.elf to /usr/bin/ape >&2
echo sudo mv -f o//ape/ape.elf /usr/bin/ape >&2
sudo mv -f o//ape/ape.elf /usr/bin/ape || exit
echo done >&2
if [ -e /proc/sys/fs/binfmt_misc/APE ]; then

View file

@ -17,6 +17,9 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/elf/def.h"
#include "libc/sysv/consts/prot.h"
#include "libc/macho.internal.h"
#include "libc/dce.h"
#include "libc/macros.internal.h"
// APE Loader Executable Structure
@ -44,48 +47,6 @@ ehdr: .ascii "\177ELF"
.word 0 # e_shstrndx
.endobj ehdr,globl
// 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 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
@ -94,7 +55,7 @@ phdrs: .long PT_LOAD # p_type
.quad ehdr # p_paddr
.quad filesz # p_filesz
.quad filesz # p_memsz
.quad PAGESIZE # p_align
.quad 64 # p_align
.long PT_LOAD # p_type
.long PF_R|PF_W # p_flags
@ -103,7 +64,7 @@ phdrs: .long PT_LOAD # p_type
.quad bss # p_paddr
.quad 0 # p_filesz
.quad bsssize # p_memsz
.quad PAGESIZE # p_align
.quad 64 # p_align
.long PT_GNU_STACK # p_type
.long PF_R|PF_W # p_flags
@ -138,3 +99,140 @@ note: .long 2f-1f
3: .long 901000000
4: .endobj note
notesize = . - note
.align 64,0 # for ape.S dd
.org 0x180 # for ape.S dd
// APE Loader XNU Header
//
// This header is dd'd backwards by the APE shell script when
// running on Mac OS X.
//
// @see ape/ape.S
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 ehdr # vaddr
.quad 4096 # 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 6 # align 2**6 = 64
.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
.align 64,0 # for ape.S dd
.org 0x400 # for ape.S dd
// 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)
// lea handoff(%rip),%rcx
// lea argblock(%rip),%rsp
// jmp 0x200400
//
// @see APE_LOADER_ENTRY
// @see ape/loader.h
_start: mov %rsp,%rsi
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.
__syscall_loader:
clc
syscall
jc 1f
ret
1: neg %rax
ret
.endfn __syscall_loader,globl

View file

@ -41,7 +41,7 @@ macho: .long 0xFEEDFACE+1
.long 30f-20b
.ascin "__TEXT",16
.quad macho # vaddr
.quad filesz # memsz
.quad 4096 # memsz
.quad 0 # file offset
.quad filesz # file size
.long PROT_EXEC|PROT_READ|PROT_WRITE # maxprot
@ -53,7 +53,7 @@ macho: .long 0xFEEDFACE+1
.quad _start # vaddr
.quad textsz # memsz
.long textoff # offset
.long 3 # align 2**3 = 8
.long 6 # align 2**3 = 64
.long 0 # reloc table offset
.long 0 # relocation count
.long MAC_S_ATTR_SOME_INSTRUCTIONS # section type & attributes
@ -112,7 +112,7 @@ macho: .long 0xFEEDFACE+1
60:
.endobj macho,globl
.align 8
.align 64
_start: mov %rsp,%rsi
jmp ApeLoader
.endfn _start,globl

View file

@ -17,7 +17,6 @@
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
ENTRY(_start)
OUTPUT_FORMAT(binary)
SECTIONS {
. = 0x200000;

View file

@ -22,7 +22,7 @@
#define TROUBLESHOOT_OS LINUX
/**
* @fileoverview APE Loader for GNU/Systemd and FreeBSD/NetBSD/OpenBSD
* @fileoverview APE Loader for GNU/Systemd/XNU/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
@ -30,7 +30,7 @@
*
* m=tiny
* make -j8 MODE=$m o/$m/ape o/$m/examples/printargs.com
* o/$m/ape/ape o/$m/examples/printargs.com
* o/$m/ape/ape.elf 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
@ -45,8 +45,12 @@
* 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
* make -j8 MODE=$m o/$m/ape/ape.elf
* sudo cp o/$m/ape/ape.elf /usr/bin/ape
*
* For Mac OS X systems you should install the `ape.macho` executable:
*
* sudo cp o/$m/ape/ape.macho /usr/bin/ape
*
* Your APE loader may be used as a shebang interpreter by doing this:
*
@ -57,7 +61,7 @@
* 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
* sudo cp -f o/$m/ape/ape.elf /usr/bin
* f=/proc/sys/fs/binfmt_misc/register
* sudo sh -c "echo ':APE:M::MZqFpD::/usr/bin/ape:' >$f"
*
@ -78,13 +82,23 @@
*/
#define LINUX 1
#define METAL 2
#define WINDOWS 4
#define XNU 8
#define OPENBSD 16
#define FREEBSD 32
#define NETBSD 64
#define SupportsLinux() (SUPPORT_VECTOR & LINUX)
#define SupportsXnu() (SUPPORT_VECTOR & XNU)
#define SupportsFreebsd() (SUPPORT_VECTOR & FREEBSD)
#define SupportsOpenbsd() (SUPPORT_VECTOR & OPENBSD)
#define SupportsNetbsd() (SUPPORT_VECTOR & NETBSD)
#define IsLinux() (SupportsLinux() && os == LINUX)
#define IsXnu() (SupportsXnu() && os == XNU)
#define IsFreebsd() (SupportsFreebsd() && os == FREEBSD)
#define IsOpenbsd() (SupportsOpenbsd() && os == OPENBSD)
#define IsNetbsd() (SupportsNetbsd() && os == NETBSD)
#define O_RDONLY 0
#define PROT_READ 1
#define PROT_WRITE 2
@ -92,7 +106,7 @@
#define MAP_SHARED 1
#define MAP_PRIVATE 2
#define MAP_FIXED 16
#define MAP_ANONYMOUS (os == LINUX ? 32 : 4096)
#define MAP_ANONYMOUS (IsLinux() ? 32 : 4096)
#define AT_EXECFN_LINUX 31
#define AT_EXECFN_NETBSD 2014
#define ELFCLASS64 2
@ -235,15 +249,15 @@ static char *Itoa(char p[21], long x) {
#if TROUBLESHOOT
const char *DescribeOs(int os) {
if (os == LINUX) {
if (IsLinux()) {
return "GNU/SYSTEMD";
} else if (os == XNU) {
} else if (IsXnu()) {
return "XNU";
} else if (os == FREEBSD) {
} else if (IsFreebsd()) {
return "FREEBSD";
} else if (os == OPENBSD) {
} else if (IsOpenbsd()) {
return "OPENBSD";
} else if (os == NETBSD) {
} else if (IsNetbsd()) {
return "NETBSD";
} else {
return "WUT";
@ -251,40 +265,42 @@ const char *DescribeOs(int os) {
}
#endif
__attribute__((__noreturn__)) static void Exit(long rc, int os) {
__attribute__((__noreturn__)) static void Exit(int rc, int os) {
asm volatile("call\t*%2"
: /* no outputs */
: "a"((os == LINUX ? 60 : 1) | (os == XNU ? 0x2000000 : 0)),
"D"(rc), "m"(syscall)
: "a"((IsLinux() ? 60 : 1) | (IsXnu() ? 0x2000000 : 0)), "D"(rc),
"rm"(syscall)
: "memory");
__builtin_unreachable();
}
static void Close(long fd, int os) {
long ax, di;
static void Close(int fd, int os) {
int ax, di;
asm volatile("call\t*%4"
: "=a"(ax), "=D"(di)
: "0"((os == LINUX ? 3 : 6) | (os == XNU ? 0x2000000 : 0)),
"1"(fd), "m"(syscall)
: "0"((IsLinux() ? 3 : 6) | (IsXnu() ? 0x2000000 : 0)), "1"(fd),
"rm"(syscall)
: "rcx", "rdx", "rsi", "r8", "r9", "r10", "r11", "memory", "cc");
}
static long Read(long fd, void *data, unsigned long size, int os) {
long ax, di, si, dx;
static int Read(int fd, void *data, int size, int os) {
long si;
int ax, di, dx;
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)
: "0"((IsLinux() ? 0 : 3) | (IsXnu() ? 0x2000000 : 0)), "1"(fd),
"2"(data), "3"(size), "rm"(syscall)
: "rcx", "r8", "r9", "r10", "r11", "memory");
return ax;
}
static void Write(long fd, const void *data, unsigned long size, int os) {
long ax, di, si, dx;
static void Write(int fd, const void *data, int size, int os) {
long si;
int ax, di, dx;
asm volatile("call\t*%8"
: "=a"(ax), "=D"(di), "=S"(si), "=d"(dx)
: "0"((os == LINUX ? 1 : 4) | (os == XNU ? 0x2000000 : 0)),
"1"(fd), "2"(data), "3"(size), "m"(syscall)
: "0"((IsLinux() ? 1 : 4) | (IsXnu() ? 0x2000000 : 0)), "1"(fd),
"2"(data), "3"(size), "rm"(syscall)
: "rcx", "r8", "r9", "r10", "r11", "memory", "cc");
}
@ -292,58 +308,68 @@ 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)
: "0"(59 | (IsXnu() ? 0x2000000 : 0)), "1"(prog), "2"(argv),
"3"(envp), "rm"(syscall)
: "rcx", "r8", "r9", "r10", "r11", "memory", "cc");
}
static long Access(const char *path, int mode, int os) {
long ax, dx, di, si;
static int Access(const char *path, int mode, int os) {
int ax, si;
long dx, di;
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)
: "0"((IsLinux() ? 21 : 33) | (IsXnu() ? 0x2000000 : 0)),
"1"(path), "2"(mode), "rm"(syscall)
: "rcx", "r8", "r9", "r10", "r11", "memory", "cc");
return ax;
}
static void Msyscall(long p, long n, int os) {
long ax, di, si;
if (os == OPENBSD) {
static int Msyscall(long p, long n, int os) {
int ax;
long di, si;
if (!IsOpenbsd()) {
return 0;
} else {
asm volatile("call\t*%6"
: "=a"(ax), "=D"(di), "=S"(si)
: "0"(37), "1"(p), "2"(n), "m"(syscall)
: "0"(37), "1"(p), "2"(n), "rm"(syscall)
: "rcx", "rdx", "r8", "r9", "r10", "r11", "memory", "cc");
return ax;
}
}
static long Open(const char *path, long flags, long mode, int os) {
long ax, di, si, dx;
static int Open(const char *path, int flags, int mode, int os) {
long di;
int ax, dx, si;
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");
: "0"((IsLinux() ? 2 : 5) | (IsXnu() ? 0x2000000 : 0)),
"1"(path), "2"(flags), "3"(mode), "rm"(syscall)
: "rcx", "r8", "r9", "r10", "r11", "memory", "cc");
return ax;
}
__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;
__attribute__((__noinline__)) static long Mmap(long addr, long size, int prot,
int flags, int fd, long off,
int os) {
long ax, di, si, dx;
register int flags_ asm("r10") = flags;
register int fd_ asm("r8") = fd;
register long off_ asm("r9") = off;
asm volatile("push\t%%r9\n\t"
"call\t*%8\n\t"
"push\t%%r9\n\t"
"call\t*%7\n\t"
"pop\t%%r9\n\t"
"pop\t%%r9"
: "=a"(ax)
: "0"((os == LINUX ? 9
: os == FREEBSD ? 477
: "=a"(ax), "=D"(di), "=S"(si), "=d"(dx), "+r"(flags_),
"+r"(fd_), "+r"(off_)
: "rm"(syscall),
"0"((IsLinux() ? 9
: IsFreebsd() ? 477
: 197) |
(os == XNU ? 0x2000000 : 0)),
"D"(addr), "S"(size), "d"(prot), "r"(flags_), "r"(fd_),
"r"(off_), "m"(syscall)
: "rcx", "r11", "memory");
(IsXnu() ? 0x2000000 : 0)),
"1"(addr), "2"(size), "3"(prot)
: "rcx", "r11", "memory", "cc");
return ax;
}
@ -514,20 +540,33 @@ __attribute__((__noreturn__)) static void Spawn(int os, const char *exe, int fd,
Pexit(os, exe, 0, "ELF needs PT_LOAD phdr w/ PF_X");
}
Close(fd, os);
Msyscall(code, codesize, os);
// authorize only the loaded program to issue system calls. if this
// fails, then we pass a link to our syscall function to the program
// since it probably means a userspace program executed this loader
// and passed us a custom syscall function earlier.
if (Msyscall(code, codesize, os) != -1) {
syscall = 0;
}
#if TROUBLESHOOT
Emit(TROUBLESHOOT_OS, "preparing to jump\n");
#endif
register long r8 asm("r8") = syscall;
// we clear all the general registers we can to have some wiggle room
// to extend the behavior of this loader in the future. we don't need
// to clear the xmm registers since the ape loader should be compiled
// with the -mgeneral-regs-only flag.
register void *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"
"xor\t%%ebx,%%ebx\n\t" // netbsd dosen't clear this
"xor\t%%r12d,%%r12d\n\t" // netbsd dosen't clear this
"xor\t%%r13d,%%r13d\n\t" // netbsd dosen't clear this
"xor\t%%r14d,%%r14d\n\t" // netbsd dosen't clear this
"xor\t%%r15d,%%r15d\n\t" // netbsd dosen't clear this
"mov\t%%rdx,%%rsp\n\t"
"xor\t%%edx,%%edx\n\t"
"push\t%%rsi\n\t"
@ -535,7 +574,7 @@ __attribute__((__noreturn__)) static void Spawn(int os, const char *exe, int fd,
"xor\t%%ebp,%%ebp\n\t"
"ret"
: /* no outputs */
: "D"(os == FREEBSD ? sp : 0), "S"(e->e_entry), "d"(sp), "c"(os),
: "D"(IsFreebsd() ? sp : 0), "S"(e->e_entry), "d"(sp), "c"(os),
"r"(r8)
: "memory");
__builtin_unreachable();
@ -543,7 +582,8 @@ __attribute__((__noreturn__)) static void Spawn(int os, const char *exe, int fd,
__attribute__((__noreturn__)) void ApeLoader(long di, long *sp, char dl,
struct ApeLoader *handoff) {
long rc, *auxv;
int rc;
long *auxv;
struct ElfEhdr *ehdr;
int c, i, fd, os, argc;
char *p, *exe, *prog, **argv, **envp, *page;
@ -553,13 +593,15 @@ __attribute__((__noreturn__)) void ApeLoader(long di, long *sp, char dl,
} u;
// detect freebsd
if (di) {
if (handoff) {
os = handoff->os;
} else if (SupportsFreebsd() && di) {
os = FREEBSD;
sp = (long *)di;
} else if (dl == XNU) {
} else if (SupportsXnu() && dl == XNU) {
os = XNU;
} else {
os = LINUX;
os = 0;
}
// extract arguments
@ -586,19 +628,18 @@ __attribute__((__noreturn__)) void ApeLoader(long di, long *sp, char dl,
// 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]) {
if (SupportsOpenbsd() && !os && !auxv[0]) {
os = OPENBSD;
}
// detect netbsd
if (os == LINUX) {
if (SupportsNetbsd() && !os) {
for (; auxv[0]; auxv += 2) {
if (auxv[0] == AT_EXECFN_NETBSD) {
os = NETBSD;
@ -607,6 +648,11 @@ __attribute__((__noreturn__)) void ApeLoader(long di, long *sp, char dl,
}
}
// default operating system
if (!os) {
os = LINUX;
}
// 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
@ -654,11 +700,13 @@ __attribute__((__noreturn__)) void ApeLoader(long di, long *sp, char dl,
}
#endif
if (Read32(page) == Read32("\177ELF") || Read32(page) == 0xFEEDFACE + 1) {
if ((IsXnu() && Read32(page) == 0xFEEDFACE + 1) ||
(!IsXnu() && Read32(page) == Read32("\177ELF"))) {
Close(fd, os);
Execve(exe, argv, envp, os);
}
// TODO(jart): Parse Mach-O for old APE binary support on XNU.
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++) != '\'';) {

View file

@ -3,10 +3,9 @@
#define APE_LOADER_BASE 0x200000
#define APE_LOADER_SIZE 0x200000
#define APE_LOADER_ENTRY 0x200400
#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

View file

@ -17,16 +17,16 @@
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
ENTRY(_start)
OUTPUT_FORMAT(binary)
SECTIONS {
. = 0x200000;
.text : {
*(.text)
*(.rodata .rodata.*)
. = ALIGN(4096);
. = ALIGN(64);
}
filesz = . - ehdr;
textsz = . - _start;
.bss ALIGN(4096) : {
bss = .;
*(.bss)
@ -39,3 +39,4 @@ SECTIONS {
}
bsssize = SIZEOF(.bss);
textoff = _start - ehdr;

Binary file not shown.

BIN
build/bootstrap/ape.elf Executable file

Binary file not shown.

BIN
build/bootstrap/apetest.com Executable file

Binary file not shown.

View file

@ -27,28 +27,38 @@ EOF
exit 1
fi
build/sanitycheck2
if [ $? -ne 123 ]; then
if [ ! -f /proc/sys/fs/binfmt_misc/status ]; then
exit 0
fi
STATUS="$(build/bootstrap/apetest.com)"
if [ x"$STATUS" != xsuccess ]; then
cat <<'EOF' >&2
ERROR
Thompson Shell Backwards Compatibility Issue Detected
APE Execution error.
DETAILS
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.
Your system has probably been configured to use binfmt_misc. You need
to run the command below to install /usr/bin/ape and register it with
binfmt_misc. See ape/loader.c for source code, and technical details.
WORKAROUND
ape/apeinstall.sh
NOTES
If it still doesn't work, possibly due to an overly aggressive WINE
or WSL registration, or possibly due to the ordering of definitions,
then one troubleshooting step is to just unregister everything using
sudo sh -c 'echo -1 >/proc/sys/fs/binfmt_misc/status' and try again.
SEE ALSO
https://justine.storage.googleapis.com/ape.html
https://justine.lol/ape.html
EOF
kill $1

View file

@ -1,16 +0,0 @@
MZboop=123
exit $MZboop

View file

@ -34,12 +34,22 @@
char program_executable_name[PATH_MAX];
static inline char *StrCat(char buf[PATH_MAX], const char *a, const char *b) {
char *p, *e;
p = buf;
e = buf + PATH_MAX;
while (*a && p < e) *p++ = *a++;
while (*b && p < e) *p++ = *b++;
return buf;
}
static inline void GetProgramExecutableNameImpl(char *p, char *e) {
char *q;
ssize_t rc;
size_t i, n;
union {
int cmd[4];
char path[PATH_MAX];
char16_t path16[PATH_MAX];
} u;
@ -61,7 +71,11 @@ static inline void GetProgramExecutableNameImpl(char *p, char *e) {
return;
}
if (__argc && (q = __argv[0]) && !sys_faccessat(AT_FDCWD, q, F_OK, 0)) {
// if argv[0] exists then turn it into an absolute path. we also try
// adding a .com suffix since the ape auto-appends it when resolving
if (__argc && (((q = __argv[0]) && !sys_faccessat(AT_FDCWD, q, F_OK, 0)) ||
((q = StrCat(u.path, __argv[0], ".com")) &&
!sys_faccessat(AT_FDCWD, q, F_OK, 0)))) {
if (*q != '/') {
if (q[0] == '.' && q[1] == '/') {
q += 2;
@ -78,12 +92,12 @@ static inline void GetProgramExecutableNameImpl(char *p, char *e) {
return;
}
// if argv[0] doesn't exist, then fallback to interpreter name
if ((rc = sys_readlinkat(AT_FDCWD, "/proc/self/exe", p, e - p - 1)) > 0 ||
(rc = sys_readlinkat(AT_FDCWD, "/proc/curproc/file", p, e - p - 1)) > 0) {
p[rc] = 0;
return;
}
if (IsFreebsd() || IsNetbsd()) {
u.cmd[0] = CTL_KERN;
u.cmd[1] = KERN_PROC;
@ -101,7 +115,7 @@ static inline void GetProgramExecutableNameImpl(char *p, char *e) {
}
/**
* Returns absolute path of executable.
* Returns absolute path of program.
*/
char *GetProgramExecutableName(void) {
int e;

View file

@ -58,7 +58,7 @@ int __ensurefds_unlocked(int fd) {
if (!(p2 = weaken(malloc)(n2 * sizeof(*p1)))) return -1;
__cxa_atexit(FreeOldFdsArray, p1, 0);
memcpy(p2, p1, n1 * sizeof(*p1));
bzero(p2 + n1, (p2 + n2) - (p2 + n1));
bzero(p2 + n1, (n2 - n1) * sizeof(*p1));
g_fds.p = p2;
g_fds.n = n2;
return fd;

View file

@ -16,10 +16,15 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/bits/weaken.h"
#include "libc/calls/internal.h"
#include "libc/calls/sig.internal.h"
#include "libc/calls/strace.internal.h"
#include "libc/errno.h"
#include "libc/nt/errors.h"
#include "libc/nt/runtime.h"
#include "libc/runtime/internal.h"
#include "libc/runtime/runtime.h"
#include "libc/sysv/consts/sicode.h"
#include "libc/sysv/consts/sig.h"
#include "libc/sysv/errfuns.h"
@ -39,8 +44,14 @@ static textwindows ssize_t sys_write_nt_impl(int fd, void *data, size_t size,
// return edquot(); /* handled by consts.sh */
case kNtErrorBrokenPipe: // broken pipe
case kNtErrorNoData: // closing named pipe
__sig_raise(SIGPIPE, SI_KERNEL); //
return epipe(); //
if (weaken(__sig_raise)) {
weaken(__sig_raise)(SIGPIPE, SI_KERNEL);
return epipe();
} else {
STRACE("broken pipe");
__restorewintty();
_Exit(128 + EPIPE);
}
case kNtErrorAccessDenied: // write doesn't return EACCESS
return ebadf(); //
default:

View file

@ -27,7 +27,7 @@
.long \systemv - kDos2Errno
.endm
.section .rodata
.section .rodata,"a",@progbits
.underrun
kDos2Errno:
// .e kNtErrorInvalidFunction,ENOSYS # in consts.sh

View file

@ -106,8 +106,10 @@ void ShowCrashReports(void) {
bzero(&ss, sizeof(ss));
ss.ss_flags = 0;
ss.ss_size = SIGSTKSZ;
// FreeBSD sigaltstack() will EFAULT if we use MAP_STACK here
// OpenBSD sigaltstack() auto-applies MAP_STACK to the memory
if ((ss.ss_sp = mmap(0, GetStackSize(), PROT_READ | PROT_WRITE,
MAP_STACK | MAP_ANONYMOUS, -1, 0))) {
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0))) {
if (!sigaltstack(&ss, &g_oldsigaltstack)) {
__cxa_atexit(FreeSigAltStack, ss.ss_sp, 0);
} else {

View file

@ -51,6 +51,9 @@ char *GetInterpreterExecutableName(char *p, size_t n) {
if (n < 2) {
errno = ENAMETOOLONG;
} else if (IsWindows() || IsXnu()) {
// TODO(jart): Does XNU guarantee argv[0] is legit?
// Otherwise we should return NULL.
// What about OpenBSD?
if (strlen(GetProgramExecutableName()) < n) {
strcpy(p, GetProgramExecutableName());
return p;

View file

@ -96,7 +96,13 @@ static struct SymbolTable *GetSymbolTableFromZip(struct Zipos *zipos) {
* @note This code can't depend on dlmalloc()
*/
static struct SymbolTable *GetSymbolTableFromElf(void) {
return OpenSymbolTable(FindDebugBinary());
int e;
const char *s;
if ((s = FindDebugBinary())) {
return OpenSymbolTable(s);
} else {
return 0;
}
}
/**
@ -117,7 +123,7 @@ static struct SymbolTable *GetSymbolTableFromElf(void) {
* Function tracing is disabled throughout the duration of this call.
* Backtraces and other core runtime functionality depend on this.
*
* @return symbol table, or NULL w/ errno on first call
* @return symbol table, or NULL if not found
*/
struct SymbolTable *GetSymbolTable(void) {
struct Zipos *z;

View file

@ -323,7 +323,7 @@ static noasan inline void *Mmap(void *addr, size_t size, int prot, int flags,
clashes = OverlapsImageSpace(p, size) || OverlapsExistingMapping(p, size);
if ((flags & MAP_FIXED_NOREPLACE) && clashes) {
if ((flags & MAP_FIXED_NOREPLACE) == MAP_FIXED_NOREPLACE && clashes) {
STRACE("noreplace overlaps existing");
return VIP(eexist());
}
@ -464,14 +464,21 @@ static noasan inline void *Mmap(void *addr, size_t size, int prot, int flags,
* compile-time checks to ensure some char[8192] vars will not
* create an undetectable overflow into another thread's stack
* Your `flags` may optionally bitwise or any of the following:
* - `MAP_FIXED` in which case `addr` becomes more than a hint
* - `MAP_FIXED_NOREPLACE` to protect existing maps (Linux-only)
* - `MAP_ANONYMOUS` in which case `fd == -1` should be the case
* - `MAP_FIXED` in which case `addr` becomes more than a hint
* - `MAP_FIXED_NOREPLACE` to protect existing mappings; this is
* always polyfilled by mmap() which tracks its own memory and
* removed before passing to the kernel, in order to support
* old versions; if you believe mappings exist which only the
* kernel knows, then this flag may be passed to sys_mmap() on
* Linux 4.17+ and FreeBSD (where it has multiple bits)
* - `MAP_CONCEAL` is FreeBSD/NetBSD/OpenBSD-only
* - `MAP_NORESERVE` is Linux/XNU/NetBSD-only
* - `MAP_LOCKED` is Linux-only
* - `MAP_POPULATE` is Linux-only
* - `MAP_POPULATE` is Linux/FreeBSD-only
* - `MAP_NONBLOCK` is Linux-only
* - `MAP_NOSYNC` is FreeBSD-only
* - `MAP_INHERIT` is NetBSD-only
* - `MAP_LOCKED` is Linux-only
* @param fd is an open()'d file descriptor, whose contents shall be
* made available w/ automatic reading at the chosen address and
* must be -1 if MAP_ANONYMOUS is specified

View file

@ -224,22 +224,25 @@ syscon mmap MAP_PRIVATE 2 2 2 2 2 2 # forced consensus & faked nt
syscon mmap MAP_STACK 6 6 6 6 6 6 # our definition
syscon mmap MAP_TYPE 15 15 15 15 15 15 # mask for type of mapping
syscon mmap MAP_FIXED 0x00000010 0x00000010 0x00000010 0x00000010 0x00000010 0x00000010 # unix consensus; openbsd appears to forbid; faked nt
syscon mmap MAP_FIXED_NOREPLACE 0x08000000 0x08000000 0x08000000 0x08000000 0x08000000 0x08000000 # handled and defined by cosmo runtime; 0x100000 on linux 4.7+
syscon mmap MAP_FIXED_NOREPLACE 0x08000000 0x00004010 0x08000000 0x08000000 0x08000000 0x08000000 # handled and defined by cosmo runtime; 0x100000 on linux 4.7+; MAP_FIXED|MAP_EXCL on FreeBSD
syscon mmap MAP_ANONYMOUS 0x00000020 0x00001000 0x00001000 0x00001000 0x00001000 0x00000020 # bsd consensus; faked nt
syscon mmap MAP_GROWSDOWN 0x00000100 0 0 0 0 0 # use MAP_STACK; abstracted by MAP_STACK; may be passed to __sys_mmap() for low-level Linux fiddling
syscon mmap MAP_CONCEAL 0 0 0x00020000 0x00008000 0x00008000 0 # omit from core dumps; MAP_NOCORE on FreeBSD
syscon mmap MAP_LOCKED 0x00002000 0 0 0 0 0
syscon mmap MAP_NORESERVE 0x00004000 0x00000040 0 0 0x00000040 0 # Linux calls it "reserve"; NT calls it "commit"? which is default?
syscon mmap MAP_POPULATE 0x00008000 0 0 0 0 0 # can avoid madvise(MADV_WILLNEED) on private file mapping
syscon mmap MAP_POPULATE 0x00008000 0 0x00040000 0 0 0 # MAP_PREFAULT_READ on FreeBSD; can avoid madvise(MADV_WILLNEED) on private file mapping
syscon mmap MAP_NONBLOCK 0x00010000 0 0 0 0 0
syscon mmap MAP_HUGETLB 0x00040000 0 0 0 0 0x80000000 # kNtSecLargePages
syscon mmap MAP_INHERIT -1 -1 -1 -1 0x00000080 -1 # make it inherit across execve()
syscon mmap MAP_HASSEMAPHORE 0 0x00000200 0x00000200 0 0x00000200 0 # does it matter on x86?
syscon mmap MAP_NOSYNC 0 0 0x00000800 0 0 0 # flush to physical media only when necessary rather than gratuitously; be sure to use write() rather than ftruncate() with this!
syscon mmap MAP_CONCEAL 0 0 0x00020000 0x00008000 0x00008000 0 # omit from core dumps; MAP_NOCORE on FreeBSD
syscon mmap MAP_HUGE_MASK 63 0 0 0 0 0
syscon mmap MAP_HUGE_SHIFT 26 0 0 0 0 0
syscon compat MAP_NOCORE 0 0 0x0020000 0x8000 0x8000 0 # use MAP_CONCEAL
syscon compat MAP_ANON 0x20 0x1000 0x0001000 0x1000 0x1000 0x20 # bsd consensus; faked nt
syscon compat MAP_EXECUTABLE 0x1000 0 0 0 0 0 # ignored
syscon compat MAP_DENYWRITE 0x0800 0 0 0 0 0
syscon compat MAP_32BIT 0x40 0 0x080000 0 0 0 # iffy
syscon compat MAP_NOCORE 0 0 0x00020000 0x00008000 0x00008000 0 # use MAP_CONCEAL
syscon compat MAP_ANON 0x00000020 0x00001000 0x00001000 0x00001000 0x00001000 0x00000020 # bsd consensus; faked nt
syscon compat MAP_EXECUTABLE 0x00001000 0 0 0 0 0 # ignored
syscon compat MAP_DENYWRITE 0x00000800 0 0 0 0 0
syscon compat MAP_32BIT 0x00000040 0 0x00080000 0 0 0 # iffy
# madvise() flags
#
@ -337,36 +340,6 @@ syscon waitid WEXITED 4 4 0x10 0 32 0
syscon waitid WSTOPPED 2 8 2 0 2 0
syscon waitid WNOWAIT 0x01000000 0x20 8 0 0x10000 0
# stat::st_mode constants
#
# group name GNU/Systemd XNU's Not UNIX! FreeBSD OpenBSD NetBSD The New Technology Commentary
syscon stat S_IFREG 0100000 0100000 0100000 0100000 0100000 0100000 # regular file (unix consensus; faked nt)
syscon stat S_IFBLK 0060000 0060000 0060000 0060000 0060000 0060000 # block device (unix consensus; faked nt)
syscon stat S_IFCHR 0020000 0020000 0020000 0020000 0020000 0020000 # character device (unix consensus; faked nt)
syscon stat S_IFDIR 0040000 0040000 0040000 0040000 0040000 0040000 # directory (unix consensus; faked nt)
syscon stat S_IFIFO 0010000 0010000 0010000 0010000 0010000 0010000 # pipe (unix consensus; faked nt)
syscon stat S_IFLNK 0120000 0120000 0120000 0120000 0120000 0120000 # symbolic link (unix consensus; faked nt)
syscon stat S_IFSOCK 0140000 0140000 0140000 0140000 0140000 0140000 # socket (unix consensus; faked nt)
syscon stat S_IFMT 0170000 0170000 0170000 0170000 0170000 0170000 # FILE TYPE MASK (unix consensus; faked nt)
syscon stat S_ISVTX 0001000 0001000 0001000 0001000 0001000 0001000 # THE STICKY BIT (unix consensus; faked nt)
syscon stat S_ISGID 0002000 0002000 0002000 0002000 0002000 0002000 # the setgid bit (unix consensus; faked nt)
syscon stat S_ISUID 0004000 0004000 0004000 0004000 0004000 0004000 # the setuid bit (unix consensus; faked nt)
syscon stat S_IEXEC 0000100 0000100 0000100 0000100 0000100 0000100 # just use octal (unix consensus; faked nt)
syscon stat S_IWRITE 0000200 0000200 0000200 0000200 0000200 0000200 # just use octal (unix consensus; faked nt)
syscon stat S_IREAD 0000400 0000400 0000400 0000400 0000400 0000400 # just use octal (unix consensus; faked nt)
syscon stat S_IXUSR 0000100 0000100 0000100 0000100 0000100 0000100 # just use octal (unix consensus; faked nt)
syscon stat S_IWUSR 0000200 0000200 0000200 0000200 0000200 0000200 # just use octal (unix consensus; faked nt)
syscon stat S_IRUSR 0000400 0000400 0000400 0000400 0000400 0000400 # just use octal (unix consensus; faked nt)
syscon stat S_IRWXU 0000700 0000700 0000700 0000700 0000700 0000700 # just use octal (unix consensus; faked nt)
syscon stat S_IXGRP 0000010 0000010 0000010 0000010 0000010 0000010 # just use octal (unix consensus; faked nt)
syscon stat S_IWGRP 0000020 0000020 0000020 0000020 0000020 0000020 # just use octal (unix consensus; faked nt)
syscon stat S_IRGRP 0000040 0000040 0000040 0000040 0000040 0000040 # just use octal (unix consensus; faked nt)
syscon stat S_IRWXG 0000070 0000070 0000070 0000070 0000070 0000070 # just use octal (unix consensus; faked nt)
syscon stat S_IXOTH 0000001 0000001 0000001 0000001 0000001 0000001 # just use octal (unix consensus; faked nt)
syscon stat S_IWOTH 0000002 0000002 0000002 0000002 0000002 0000002 # just use octal (unix consensus; faked nt)
syscon stat S_IROTH 0000004 0000004 0000004 0000004 0000004 0000004 # just use octal (unix consensus; faked nt)
syscon stat S_IRWXO 0000007 0000007 0000007 0000007 0000007 0000007 # just use octal (unix consensus; faked nt)
# fcntl()
#
# group name GNU/Systemd XNU's Not UNIX! FreeBSD OpenBSD NetBSD The New Technology Commentary
@ -605,7 +578,8 @@ syscon sicode SYS_USER_DISPATCH 2 -1 -1 -1 -1 -1 # SIGSYS; syscall
# sigaltstack() values
#
# group name GNU/Systemd XNU's Not UNIX! FreeBSD OpenBSD NetBSD The New Technology Commentary
syscon ss SIGSTKSZ 0x2000 0x020000 0x8800 0x7000 0x7000 0x2000
syscon ss SIGSTKSZ 8192 131072 34816 28672 28672 8192 # overlayed with STACKSIZE; you need to #undef SIGSTKSZ to access this symbol
syscon ss MINSIGSTKSZ 2048 32768 2048 12288 8192 2048 # overlayed with 32768; you need to #undef MINSIGSTKSZ to access this symbol
syscon ss SS_ONSTACK 1 1 1 1 1 1 # unix consensus
syscon ss SS_DISABLE 2 4 4 4 4 2 # bsd consensus
@ -3131,7 +3105,6 @@ syscon misc CSTATUS 0 20 20 255 255 0
syscon misc DEAD_PROCESS 8 8 7 0 0 0
syscon misc FNM_NOSYS -1 -1 -1 2 2 0
syscon misc INIT_PROCESS 5 5 5 0 0 0
syscon misc MINSIGSTKSZ 0x0800 0x8000 0x0800 0x3000 0x2000 0
syscon misc MQ_PRIO_MAX 0x8000 0 0x40 0 0 0
syscon misc MTERASE 13 0 12 9 9 0
syscon misc MTLOAD 30 0 19 0 0 0

View file

@ -1,2 +1,2 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon compat,MAP_32BIT,0x40,0,0x080000,0,0,0
.syscon compat,MAP_32BIT,0x00000040,0,0x00080000,0,0,0

View file

@ -1,2 +1,2 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon compat,MAP_ANON,0x20,0x1000,0x0001000,0x1000,0x1000,0x20
.syscon compat,MAP_ANON,0x00000020,0x00001000,0x00001000,0x00001000,0x00001000,0x00000020

View file

@ -1,2 +1,2 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon compat,MAP_DENYWRITE,0x0800,0,0,0,0,0
.syscon compat,MAP_DENYWRITE,0x00000800,0,0,0,0,0

View file

@ -1,2 +1,2 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon compat,MAP_EXECUTABLE,0x1000,0,0,0,0,0
.syscon compat,MAP_EXECUTABLE,0x00001000,0,0,0,0,0

View file

@ -1,2 +1,2 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon compat,MAP_FILE,0,0,0,0,0,0
.syscon mmap,MAP_FILE,0,0,0,0,0,0

View file

@ -1,2 +1,2 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon mmap,MAP_FIXED_NOREPLACE,0x08000000,0x08000000,0x08000000,0x08000000,0x08000000,0x08000000
.syscon mmap,MAP_FIXED_NOREPLACE,0x08000000,0x00004010,0x08000000,0x08000000,0x08000000,0x08000000

View file

@ -0,0 +1,2 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon mmap,MAP_HASSEMAPHORE,0,0x00000200,0x00000200,0,0x00000200,0

View file

@ -0,0 +1,2 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon mmap,MAP_INHERIT,-1,-1,-1,-1,0x00000080,-1

View file

@ -1,2 +1,2 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon compat,MAP_NOCORE,0,0,0x0020000,0x8000,0x8000,0
.syscon compat,MAP_NOCORE,0,0,0x00020000,0x00008000,0x00008000,0

View file

@ -0,0 +1,2 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon mmap,MAP_NOSYNC,0,0,0x00000800,0,0,0

View file

@ -1,2 +1,2 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon mmap,MAP_POPULATE,0x00008000,0,0,0,0,0
.syscon mmap,MAP_POPULATE,0x00008000,0,0x00040000,0,0,0

View file

@ -1,2 +1,2 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon misc,MINSIGSTKSZ,0x0800,0x8000,0x0800,0x3000,0x2000,0
.syscon ss,MINSIGSTKSZ,2048,32768,2048,12288,8192,2048

View file

@ -1,2 +1,2 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon ss,SIGSTKSZ,0x2000,0x020000,0x8800,0x7000,0x7000,0x2000
.syscon ss,SIGSTKSZ,8192,131072,34816,28672,28672,8192

View file

@ -1,2 +1,2 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon statvfs,ST_MANDLOCK,0x40,0,0,0,0,0
.syscon statvfs,ST_MANDLOCK,0x0040,0,0,0,0,0

View file

@ -1,2 +1,2 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon statvfs,ST_SYNCHRONOUS,0x10,0,0,0,2,0
.syscon statvfs,ST_SYNCHRONOUS,16,0,0,0,2,0

View file

@ -1,2 +1,2 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon statvfs,ST_WRITE,0x80,0,0,0,0,0
.syscon statvfs,ST_WRITE,0x0080,0,0,0,0,0

View file

@ -1,2 +0,0 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon stat,S_IEXEC,0000100,0000100,0000100,0000100,0000100,0000100

View file

@ -1,2 +0,0 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon stat,S_IFBLK,0060000,0060000,0060000,0060000,0060000,0060000

View file

@ -1,2 +0,0 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon stat,S_IFCHR,0020000,0020000,0020000,0020000,0020000,0020000

View file

@ -1,2 +0,0 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon stat,S_IFDIR,0040000,0040000,0040000,0040000,0040000,0040000

View file

@ -1,2 +0,0 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon stat,S_IFIFO,0010000,0010000,0010000,0010000,0010000,0010000

View file

@ -1,2 +0,0 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon stat,S_IFLNK,0120000,0120000,0120000,0120000,0120000,0120000

View file

@ -1,2 +0,0 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon stat,S_IFMT,0170000,0170000,0170000,0170000,0170000,0170000

View file

@ -1,2 +0,0 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon stat,S_IFREG,0100000,0100000,0100000,0100000,0100000,0100000

View file

@ -1,2 +0,0 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon stat,S_IFSOCK,0140000,0140000,0140000,0140000,0140000,0140000

View file

@ -1,2 +0,0 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon stat,S_IREAD,0000400,0000400,0000400,0000400,0000400,0000400

View file

@ -1,2 +0,0 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon stat,S_IRGRP,0000040,0000040,0000040,0000040,0000040,0000040

View file

@ -1,2 +0,0 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon stat,S_IROTH,0000004,0000004,0000004,0000004,0000004,0000004

View file

@ -1,2 +0,0 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon stat,S_IRUSR,0000400,0000400,0000400,0000400,0000400,0000400

View file

@ -1,2 +0,0 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon stat,S_IRWXG,0000070,0000070,0000070,0000070,0000070,0000070

View file

@ -1,2 +0,0 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon stat,S_IRWXO,0000007,0000007,0000007,0000007,0000007,0000007

View file

@ -1,2 +0,0 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon stat,S_IRWXU,0000700,0000700,0000700,0000700,0000700,0000700

View file

@ -1,2 +0,0 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon stat,S_ISGID,0002000,0002000,0002000,0002000,0002000,0002000

View file

@ -1,2 +0,0 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon stat,S_ISUID,0004000,0004000,0004000,0004000,0004000,0004000

View file

@ -1,2 +0,0 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon stat,S_ISVTX,0001000,0001000,0001000,0001000,0001000,0001000

View file

@ -1,2 +0,0 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon stat,S_IWGRP,0000020,0000020,0000020,0000020,0000020,0000020

View file

@ -1,2 +0,0 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon stat,S_IWOTH,0000002,0000002,0000002,0000002,0000002,0000002

View file

@ -1,2 +0,0 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon stat,S_IWRITE,0000200,0000200,0000200,0000200,0000200,0000200

View file

@ -1,2 +0,0 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon stat,S_IWUSR,0000200,0000200,0000200,0000200,0000200,0000200

View file

@ -1,2 +0,0 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon stat,S_IXGRP,0000010,0000010,0000010,0000010,0000010,0000010

View file

@ -1,2 +0,0 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon stat,S_IXOTH,0000001,0000001,0000001,0000001,0000001,0000001

View file

@ -1,2 +0,0 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon stat,S_IXUSR,0000100,0000100,0000100,0000100,0000100,0000100

View file

@ -12,13 +12,17 @@ extern const long MAP_DENYWRITE;
extern const long MAP_EXECUTABLE;
extern const long MAP_FILE;
extern const long MAP_FIXED;
extern const long MAP_FIXED_NOREPLACE;
extern const long MAP_GROWSDOWN;
extern const long MAP_HASSEMAPHORE;
extern const long MAP_HUGETLB;
extern const long MAP_HUGE_MASK;
extern const long MAP_HUGE_SHIFT;
extern const long MAP_INHERIT;
extern const long MAP_LOCKED;
extern const long MAP_NONBLOCK;
extern const long MAP_NORESERVE;
extern const long MAP_NOSYNC;
extern const long MAP_POPULATE;
extern const long MAP_PRIVATE;
extern const long MAP_SHARED;
@ -32,21 +36,23 @@ COSMOPOLITAN_C_END_
#define MAP_STACK 6
#define MAP_TYPE 15
#define MAP_FIXED 16
#define MAP_FIXED_NOREPLACE 0x8000000
#define MAP_32BIT SYMBOLIC(MAP_32BIT)
#define MAP_ANONYMOUS SYMBOLIC(MAP_ANONYMOUS)
#define MAP_CONCEAL SYMBOLIC(MAP_CONCEAL)
#define MAP_CONCEAL SYMBOLIC(MAP_CONCEAL)
#define MAP_DENYWRITE SYMBOLIC(MAP_DENYWRITE)
#define MAP_EXECUTABLE SYMBOLIC(MAP_EXECUTABLE)
#define MAP_FIXED_NOREPLACE SYMBOLIC(MAP_FIXED_NOREPLACE)
#define MAP_GROWSDOWN SYMBOLIC(MAP_GROWSDOWN)
#define MAP_HASSEMAPHORE SYMBOLIC(MAP_HASSEMAPHORE)
#define MAP_HUGETLB SYMBOLIC(MAP_HUGETLB)
#define MAP_HUGE_MASK SYMBOLIC(MAP_HUGE_MASK)
#define MAP_HUGE_SHIFT SYMBOLIC(MAP_HUGE_SHIFT)
#define MAP_INHERIT SYMBOLIC(MAP_INHERIT)
#define MAP_LOCKED SYMBOLIC(MAP_LOCKED)
#define MAP_NONBLOCK SYMBOLIC(MAP_NONBLOCK)
#define MAP_NORESERVE SYMBOLIC(MAP_NORESERVE)
#define MAP_NOSYNC SYMBOLIC(MAP_NOSYNC)
#define MAP_POPULATE SYMBOLIC(MAP_POPULATE)
#define MAP_ANON MAP_ANONYMOUS

View file

@ -3,12 +3,15 @@
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
extern const long SIGSTKSZ;
extern const long MINSIGSTKSZ;
extern const long SS_DISABLE;
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#define SIGSTKSZ STACKSIZE
#define MINSIGSTKSZ 32768
#define SS_ONSTACK 1
#define SS_DISABLE SS_DISABLE

View file

@ -9,7 +9,15 @@ o/$(MODE)/test/libc/release/cosmopolitan.zip: \
o/$(MODE)/ape/ape-no-modify-self.o \
o/$(MODE)/cosmopolitan.a \
o/$(MODE)/third_party/zip/zip.com
@$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -qj $@ o/cosmopolitan.h o/$(MODE)/ape/ape.lds o/$(MODE)/libc/crt/crt.o o/$(MODE)/ape/ape.o o/$(MODE)/ape/ape-no-modify-self.o o/$(MODE)/cosmopolitan.a
@$(COMPILE) -AZIP -T$@ \
o/$(MODE)/third_party/zip/zip.com \
-qj $@ \
o/cosmopolitan.h \
o/$(MODE)/ape/ape.lds \
o/$(MODE)/libc/crt/crt.o \
o/$(MODE)/ape/ape.o \
o/$(MODE)/ape/ape-no-modify-self.o \
o/$(MODE)/cosmopolitan.a
o/$(MODE)/test/libc/release/smoke.com: \
o/$(MODE)/test/libc/release/smoke.com.dbg

View file

@ -0,0 +1,107 @@
/*-*- 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/sigbits.h"
#include "libc/calls/struct/sigaction.h"
#include "libc/errno.h"
#include "libc/mem/io.h"
#include "libc/runtime/runtime.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/sig.h"
#include "libc/testlib/testlib.h"
STATIC_YOINK("zip_uri_support");
STATIC_YOINK("apetest.com");
STATIC_YOINK("apetest2.com");
char testlib_enable_tmp_setup_teardown_once;
void Extract(const char *from, const char *to, int mode) {
ASSERT_SYS(0, 3, open(from, O_RDONLY));
ASSERT_SYS(0, 4, creat(to, mode));
ASSERT_NE(-1, _copyfd(3, 4, -1));
EXPECT_SYS(0, 0, close(4));
EXPECT_SYS(0, 0, close(3));
}
void SetUpOnce(void) {
ASSERT_SYS(0, 0, mkdir("bin", 0755));
Extract("/zip/apetest.com", "bin/apetest.com", 0755);
Extract("/zip/apetest2.com", "bin/apetest2.com", 0755);
// force the extraction of ape payload
// if system does not have binfmt_misc
ASSERT_SYS(0, 0, mkdir("tmp", 0755));
setenv("TMPDIR", "tmp", true);
}
void RunApeTest(const char *path) {
size_t n;
ssize_t rc, got;
char buf[512] = {0};
sigset_t chldmask, savemask;
int i, pid, fdin, wstatus, pfds[2];
struct sigaction ignore, saveint, savequit, savepipe;
ignore.sa_flags = 0;
ignore.sa_handler = SIG_IGN;
sigemptyset(&ignore.sa_mask);
sigaction(SIGINT, &ignore, &saveint);
sigaction(SIGQUIT, &ignore, &savequit);
sigaction(SIGPIPE, &ignore, &savepipe);
sigemptyset(&chldmask);
sigaddset(&chldmask, SIGCHLD);
sigprocmask(SIG_BLOCK, &chldmask, &savemask);
ASSERT_SYS(0, 0, pipe2(pfds, O_CLOEXEC));
ASSERT_NE(-1, (pid = fork()));
if (!pid) {
__strace = 0;
__ftrace = 0;
close(1);
dup(pfds[1]);
sigaction(SIGINT, &saveint, 0);
sigaction(SIGQUIT, &savequit, 0);
sigaction(SIGPIPE, &savepipe, 0);
sigprocmask(SIG_SETMASK, &savemask, 0);
execv(path, (char *const[]){path, 0});
_Exit(127);
}
close(pfds[1]);
EXPECT_SYS(0, 8, read(pfds[0], buf, sizeof(buf)));
EXPECT_STREQ("success\n", buf);
close(pfds[0]);
EXPECT_NE(-1, wait(&wstatus));
EXPECT_TRUE(WIFEXITED(wstatus));
EXPECT_EQ(0, WEXITSTATUS(wstatus));
sigaction(SIGINT, &saveint, 0);
sigaction(SIGQUIT, &savequit, 0);
sigaction(SIGPIPE, &savepipe, 0);
sigprocmask(SIG_SETMASK, &savemask, 0);
EXPECT_SYS(0, 3, open(path, O_RDONLY));
EXPECT_SYS(0, 6, read(3, buf, 6));
EXPECT_SYS(0, 0, close(3));
EXPECT_STREQN("MZqFpD", buf, 6);
}
TEST(apeNoModifySelf, runsWithoutModifyingSelf) {
RunApeTest("bin/apetest.com");
}
TEST(apeCopySelf, runsWithoutModifyingSelf) {
RunApeTest("bin/apetest2.com");
}

View file

@ -41,6 +41,8 @@ TEST_LIBC_RUNTIME_DIRECTDEPS = \
LIBC_TINYMATH \
LIBC_UNICODE \
LIBC_X \
LIBC_ZIPOS \
TOOL_BUILD_LIB \
THIRD_PARTY_XED
TEST_LIBC_RUNTIME_DEPS := \
@ -59,6 +61,15 @@ o/$(MODE)/test/libc/runtime/%.com.dbg: \
$(APE)
@$(APELINK)
o/$(MODE)/test/libc/runtime/ape_test.com.dbg: \
$(TEST_LIBC_RUNTIME_DEPS) \
o/$(MODE)/test/libc/runtime/ape_test.o \
o/$(MODE)/test/libc/runtime/runtime.pkg \
$(LIBC_TESTMAIN) \
$(CRT) \
$(APE)
@$(APELINK)
$(TEST_LIBC_RUNTIME_OBJS): \
DEFAULT_CCFLAGS += \
-fno-builtin
@ -67,7 +78,6 @@ o/$(MODE)/test/libc/runtime/getenv_test.com.runs: \
o/$(MODE)/test/libc/runtime/getenv_test.com
@HELLO=THERE build/runit $@ $<
o/$(MODE)/test/libc/runtime/fun_test.o \
o/$(MODE)/test/libc/runtime/itsatrap_test.o: \
OVERRIDE_CFLAGS += \
-fno-sanitize=all \

View file

@ -72,13 +72,13 @@ TEST(plinko, worksOrPrintsNiceError) {
struct sigaction ignore, saveint, savequit, savepipe;
ignore.sa_flags = 0;
ignore.sa_handler = SIG_IGN;
EXPECT_EQ(0, sigemptyset(&ignore.sa_mask));
EXPECT_EQ(0, sigaction(SIGINT, &ignore, &saveint));
EXPECT_EQ(0, sigaction(SIGQUIT, &ignore, &savequit));
EXPECT_EQ(0, sigaction(SIGPIPE, &ignore, &savepipe));
EXPECT_EQ(0, sigemptyset(&chldmask));
EXPECT_EQ(0, sigaddset(&chldmask, SIGCHLD));
EXPECT_EQ(0, sigprocmask(SIG_BLOCK, &chldmask, &savemask));
sigemptyset(&ignore.sa_mask);
sigaction(SIGINT, &ignore, &saveint);
sigaction(SIGQUIT, &ignore, &savequit);
sigaction(SIGPIPE, &ignore, &savepipe);
sigemptyset(&chldmask);
sigaddset(&chldmask, SIGCHLD);
sigprocmask(SIG_BLOCK, &chldmask, &savemask);
ASSERT_NE(-1, pipe2(pfds[0], O_CLOEXEC));
ASSERT_NE(-1, pipe2(pfds[1], O_CLOEXEC));
ASSERT_NE(-1, (pid = fork()));
@ -90,14 +90,14 @@ TEST(plinko, worksOrPrintsNiceError) {
close(2), dup(pfds[1][1]);
sigaction(SIGINT, &saveint, 0);
sigaction(SIGQUIT, &savequit, 0);
sigaction(SIGQUIT, &savepipe, 0);
sigaction(SIGPIPE, &savepipe, 0);
sigprocmask(SIG_SETMASK, &savemask, 0);
execve("bin/plinko.com", (char *const[]){"bin/plinko.com", 0},
(char *const[]){0});
_exit(127);
}
EXPECT_NE(-1, close(pfds[0][0]));
EXPECT_NE(-1, close(pfds[1][1]));
close(pfds[0][0]);
close(pfds[1][1]);
for (i = 0; i < ARRAYLEN(kSauces); ++i) {
EXPECT_NE(-1, (fdin = open(kSauces[i], O_RDONLY)));
rc = _copyfd(fdin, pfds[0][1], -1);
@ -118,10 +118,10 @@ TEST(plinko, worksOrPrintsNiceError) {
} else {
EXPECT_EQ(1, WEXITSTATUS(wstatus));
}
EXPECT_EQ(0, sigaction(SIGINT, &saveint, 0));
EXPECT_EQ(0, sigaction(SIGQUIT, &savequit, 0));
EXPECT_EQ(0, sigaction(SIGPIPE, &savepipe, 0));
EXPECT_EQ(0, sigprocmask(SIG_SETMASK, &savemask, 0));
sigaction(SIGINT, &saveint, 0);
sigaction(SIGQUIT, &savequit, 0);
sigaction(SIGPIPE, &savepipe, 0);
sigprocmask(SIG_SETMASK, &savemask, 0);
if (g_testlib_failed) {
kprintf("note: got the following in pipe: %s%n", buf);
}

23
tool/build/lib/apetest.c Normal file
View file

@ -0,0 +1,23 @@
/*-*- 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"
int main(int argc, char *argv[]) {
write(1, "success\n", 8);
}

View file

@ -9,7 +9,9 @@ TOOL_BUILD_LIB_A = o/$(MODE)/tool/build/lib/buildlib.a
TOOL_BUILD_LIB_A_FILES := $(wildcard tool/build/lib/*)
TOOL_BUILD_LIB_A_HDRS = $(filter %.h,$(TOOL_BUILD_LIB_A_FILES))
TOOL_BUILD_LIB_A_SRCS_S = $(filter %.S,$(TOOL_BUILD_LIB_A_FILES))
TOOL_BUILD_LIB_A_SRCS_C = $(filter %.c,$(TOOL_BUILD_LIB_A_FILES))
TOOL_BUILD_LIB_A_SRCS_C = \
$(filter-out tool/build/lib/apetest.c,$(filter %.c,$(TOOL_BUILD_LIB_A_FILES)))
TOOL_BUILD_LIB_A_CHECKS = \
$(TOOL_BUILD_LIB_A_HDRS:%=o/$(MODE)/%.ok) \
@ -19,9 +21,15 @@ TOOL_BUILD_LIB_A_SRCS = \
$(TOOL_BUILD_LIB_A_SRCS_S) \
$(TOOL_BUILD_LIB_A_SRCS_C)
TOOL_BUILD_LIB_COMS = \
o/$(MODE)/tool/build/lib/apetest.com \
o/$(MODE)/tool/build/lib/apetest2.com
TOOL_BUILD_LIB_A_OBJS = \
$(TOOL_BUILD_LIB_A_SRCS_S:%.S=o/$(MODE)/%.o) \
$(TOOL_BUILD_LIB_A_SRCS_C:%.c=o/$(MODE)/%.o)
$(TOOL_BUILD_LIB_A_SRCS_C:%.c=o/$(MODE)/%.o) \
o/$(MODE)/tool/build/lib/apetest.com.zip.o \
o/$(MODE)/tool/build/lib/apetest2.com.zip.o
TOOL_BUILD_LIB_A_DIRECTDEPS = \
LIBC_ALG \
@ -67,6 +75,25 @@ o/$(MODE)/tool/build/lib/ssefloat.o: \
TARGET_ARCH += \
-msse3
o/$(MODE)/tool/build/lib/apetest.com.dbg: \
$(TOOL_BUILD_LIB_A_DEPS) \
o/$(MODE)/tool/build/lib/apetest.o \
$(CRT) \
$(APE_NO_MODIFY_SELF)
@$(APELINK)
o/$(MODE)/tool/build/lib/apetest2.com.dbg: \
$(TOOL_BUILD_LIB_A_DEPS) \
o/$(MODE)/tool/build/lib/apetest.o \
$(CRT) \
$(APE_COPY_SELF)
@$(APELINK)
o/$(MODE)/tool/build/lib/apetest.com.zip.o \
o/$(MODE)/tool/build/lib/apetest2.com.zip.o: \
ZIPOBJ_FLAGS += \
-B
TOOL_BUILD_LIB_LIBS = $(foreach x,$(TOOL_BUILD_LIB_ARTIFACTS),$($(x)))
TOOL_BUILD_LIB_SRCS = $(foreach x,$(TOOL_BUILD_LIB_ARTIFACTS),$($(x)_SRCS))
TOOL_BUILD_LIB_HDRS = $(foreach x,$(TOOL_BUILD_LIB_ARTIFACTS),$($(x)_HDRS))
@ -76,4 +103,6 @@ TOOL_BUILD_LIB_OBJS = $(foreach x,$(TOOL_BUILD_LIB_ARTIFACTS),$($(x)_OBJS))
TOOL_BUILD_LIB_TESTS = $(foreach x,$(TOOL_BUILD_LIB_ARTIFACTS),$($(x)_TESTS))
.PHONY: o/$(MODE)/tool/build/lib
o/$(MODE)/tool/build/lib: $(TOOL_BUILD_LIB_CHECKS)
o/$(MODE)/tool/build/lib: \
$(TOOL_BUILD_LIB_COMS) \
$(TOOL_BUILD_LIB_CHECKS)

View file

@ -7305,7 +7305,6 @@ void RedBean(int argc, char *argv[]) {
#else
GetHostsTxt(); // for effect
GetResolvConf(); // for effect
__print_maps();
if (daemonize || uniprocess || !linenoiseIsTerminal()) {
EventLoop(HEARTBEAT);
} else if (IsWindows()) {