Fully support OpenBSD 7.3

This change (1) upgrades to OpenBSD's newer kernel ABIs, and (2)
modifies APE to have a read-only data segment. Doing this required
creating APE Loader v1.1, which is backwards and forwards compatible
with the previous version.

If you've run the following commands in the past to install your APE
Loader systemwide, then you need to run them again. Ad-hoc installations
shouldn't be impacted. It's also recommended that APE binaries be remade
after upgrading, since they embed old versions of the APE Loader.

    ape/apeuninstall.sh
    ape/apeinstall.sh

This change does more than just fix OpenBSD. The new loader is smarter
and more reliable. We're now able create much tinier ELF and Mach-O data
structures than we could before. Both APE Loader and execvpe() will now
normalize ambiguous argv[0] resolution the same way as the UNIX shell.
Badness with TLS linkage has been solved.

Fixes #826
This commit is contained in:
Justine Tunney 2023-07-01 05:10:12 -07:00
parent 963e10b9bf
commit 40eb3b9d5d
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
48 changed files with 772 additions and 903 deletions

View file

@ -264,6 +264,7 @@ printf 'main() { printf("hello world\\n"); }\n' >hello.c
gcc -g -Os -static -nostdlib -nostdinc -fno-pie -no-pie -mno-red-zone \
-fno-omit-frame-pointer -pg -mnop-mcount -mno-tls-direct-seg-refs -gdwarf-4 \
-o hello.com.dbg hello.c -fuse-ld=bfd -Wl,-T,ape.lds -Wl,--gc-sections \
-Wl,-z,common-page-size=0x1000 -Wl,-z,max-page-size=0x1000 \
-include cosmopolitan.h crt.o ape-no-modify-self.o cosmopolitan.a
objcopy -S -O binary hello.com.dbg hello.com
```
@ -427,7 +428,7 @@ server. You're welcome to join us! <https://discord.gg/FwAVVu7eJ4>
| Linux | 2.6.18 | 2007 |
| Windows | 8 [1] | 2012 |
| Mac OS X | 15.6 | 2018 |
| OpenBSD | 6.4 | 2018 |
| OpenBSD | 7 | 2021 |
| FreeBSD | 13 | 2020 |
| NetBSD | 9.2 | 2021 |

View file

@ -59,7 +59,7 @@ struct Syslib {
#define TROUBLESHOOT 0
#define ELFCLASS64 2
#define ELFCLASS32 1
#define ELFDATA2LSB 1
#define EM_AARCH64 183
#define ET_EXEC 2
@ -431,7 +431,7 @@ __attribute__((__noreturn__)) static void Spawn(const char *exe, int fd,
codesize = 0;
for (i = e->e_phnum; i--;) {
if (p[i].p_type == PT_DYNAMIC) {
Pexit(exe, 0, "not a real executable");
Pexit(exe, 0, "not a static executable");
}
if (p[i].p_type != PT_LOAD) {
continue;
@ -525,14 +525,15 @@ __attribute__((__noreturn__)) static void Spawn(const char *exe, int fd,
static void TryElf(struct ApeLoader *M, const char *exe, int fd, long *sp,
long *bp, char *execfn) {
unsigned long n;
if (Read32(M->ehdr.buf) == Read32("\177ELF") &&
M->ehdr.ehdr.e_type == ET_EXEC && M->ehdr.ehdr.e_machine == EM_AARCH64 &&
M->ehdr.ehdr.e_ident[EI_CLASS] == ELFCLASS64 &&
M->ehdr.ehdr.e_ident[EI_DATA] == ELFDATA2LSB &&
(n = M->ehdr.ehdr.e_phnum * sizeof(M->phdr.phdr)) <=
unsigned size;
if (Read32(M->ehdr.buf) == Read32("\177ELF") && //
M->ehdr.ehdr.e_type == ET_EXEC && //
M->ehdr.ehdr.e_machine == EM_AARCH64 && //
M->ehdr.ehdr.e_ident[EI_CLASS] != ELFCLASS32 && //
M->ehdr.ehdr.e_phentsize >= sizeof(M->phdr.phdr) && //
(size = (unsigned)M->ehdr.ehdr.e_phnum * M->ehdr.ehdr.e_phentsize) <=
sizeof(M->phdr.buf) &&
pread(fd, M->phdr.buf, n, M->ehdr.ehdr.e_phoff) == n) {
pread(fd, M->phdr.buf, size, M->ehdr.ehdr.e_phoff) == size) {
long auxv[][2] = {
{AT_PHDR, (long)&M->phdr.phdr}, //
{AT_PHENT, M->ehdr.ehdr.e_phentsize}, //

104
ape/ape.S
View file

@ -43,6 +43,7 @@
#include "libc/nt/pedef.internal.h"
#include "libc/runtime/pc.internal.h"
#include "ape/ape.internal.h"
#include "libc/thread/tls.h"
#include "libc/sysv/consts/prot.h"
#define USE_SYMBOL_HACK 1
@ -599,7 +600,7 @@ apesh: .ascii "\n@\n#'\"\n" // sixth edition shebang
// There isn't a one-size-fits-all approach for this, thus we
// present two choices.
.ascii "o=\"$(command -v \"$0\")\"\n"
// Try to use a system-wide APE loader.
// Try to use system-wide APE loader.
.ascii "[ x\"$1\" != x--assimilate ] && "
.ascii "type ape >/dev/null 2>&1 && "
.ascii "exec ape \"$o\" \"$@\"\n"
@ -609,7 +610,7 @@ apesh: .ascii "\n@\n#'\"\n" // sixth edition shebang
// extract the loader into a temp folder, and use it to
// load the APE without modifying it.
.ascii "[ x\"$1\" != x--assimilate ] && {\n"
.ascii "t=\"${TMPDIR:-${HOME:-.}}/.ape\"\n"
.ascii "t=\"${TMPDIR:-${HOME:-.}}/.ape-1.1\"\n"
.ascii "[ -x \"$t\" ] || {\n"
.ascii "mkdir -p \"${t%/*}\" &&\n"
.ascii "dd if=\"$o\" of=\"$t.$$\" skip="
@ -622,7 +623,7 @@ apesh: .ascii "\n@\n#'\"\n" // sixth edition shebang
.ascii "dd if=\"$t.$$\""
.ascii " of=\"$t.$$\""
.ascii " skip=6"
.ascii " count=10"
.ascii " count=6"
.ascii " bs=64"
.ascii " conv=notrunc"
.ascii " 2>/dev/null\n"
@ -746,6 +747,15 @@ ape_loader_end:
.long PT_LOAD
.long PF_R|PF_X
.stub ape_cod_offset,quad
.stub ape_cod_vaddr,quad
.stub ape_cod_paddr,quad
.stub ape_cod_filesz,quad
.stub ape_cod_memsz,quad
.stub ape_cod_align,quad
.long PT_LOAD
.long PF_R
.stub ape_rom_offset,quad
.stub ape_rom_vaddr,quad
.stub ape_rom_paddr,quad
@ -868,8 +878,8 @@ ape_macho:
.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 6 // number of load commands
.long 70f-10f // size of all load commands
.long MAC_NOUNDEFS // flags
.long 0 // reserved
10: .long MAC_LC_SEGMENT_64
@ -880,26 +890,27 @@ ape_macho:
20: .long MAC_LC_SEGMENT_64
.long 30f-20b
.ascin "__TEXT",16
.stub ape_cod_vaddr,quad
.stub ape_cod_memsz,quad
.stub ape_cod_offset,quad
.stub ape_cod_filesz,quad
.long PROT_EXEC|PROT_READ|PROT_WRITE // maxprot
.long PROT_EXEC|PROT_READ // initprot
.long 0 // segment section count
.long 0 // flags
30: .long MAC_LC_SEGMENT_64
.long 40f-30b
.ascin "__RODATA",16
.stub ape_rom_vaddr,quad
.stub ape_rom_memsz,quad
.stub ape_rom_offset,quad
.stub ape_rom_filesz,quad
.long PROT_EXEC|PROT_READ|PROT_WRITE // maxprot
.long PROT_EXEC|PROT_READ // initprot
.long 1 // segment section count
.long PROT_READ // initprot
.long 0 // segment section count
.long 0 // flags
210: .ascin "__text",16 // section name (.text)
.ascin "__TEXT",16
.stub ape_text_vaddr,quad
.stub ape_text_memsz,quad
.stub ape_text_offset,long
.long 12 // align 2**12 = 4096
.long 0 // reloc table offset
.long 0 // relocation count
.long MAC_S_ATTR_SOME_INSTRUCTIONS // section type & attributes
.long 0,0,0 // reserved
30: .long MAC_LC_SEGMENT_64
.long 40f-30b
40: .long MAC_LC_SEGMENT_64
.long 50f-40b
.ascin "__DATA",16
.stub ape_ram_vaddr,quad
.stub ape_ram_memsz,quad
@ -907,39 +918,19 @@ ape_macho:
.stub ape_ram_filesz,quad
.long PROT_EXEC|PROT_READ|PROT_WRITE // maxprot
.long PROT_READ|PROT_WRITE // initprot
.long 2 // segment section count
.long 0 // segment section count
.long 0 // flags
310: .ascin "__data",16 // section name (.data)
.ascin "__DATA",16
.stub ape_data_vaddr,quad
.stub ape_data_memsz,quad
.stub ape_data_offset,long
.long 12 // align 2**12 = 4096
.long 0 // reloc table offset
.long 0 // relocation count
.long 0 // section type & attributes
.long 0,0,0 // reserved
320: .ascin "__bss",16 // section name (.bss)
.ascin "__DATA",16
.stub ape_bss_vaddr,quad // virtual address
.stub ape_bss_memsz,quad // memory size
.long 0 // file offset
.long 12 // align 2**12 = 4096
.long 0 // reloc table offset
.long 0 // relocation count
.long MAC_S_ZEROFILL // section type & attributes
.long 0,0,0 // reserved
40: .long MAC_LC_UUID
.long 50f-40b
50: .long MAC_LC_UUID
.long 60f-50b
.stub ape_uuid1,quad
.stub ape_uuid2,quad
50: .long MAC_LC_UNIXTHREAD
.long 60f-50b // cmdsize
60: .long MAC_LC_UNIXTHREAD
.long 70f-60b // cmdsize
.long MAC_THREAD_NEXGEN32E // flavaflav
.long (520f-510f)/4 // count
510: .quad 0 // rax
.quad IMAGE_BASE_VIRTUAL // rbx
.quad _HOSTXNU // rcx
.long (620f-610f)/4 // count
610: .quad 0 // rax
.quad 0 // rbx
.quad 0 // rcx
.quad 0 // rdx
.quad 0 // rdi
.quad 0 // rsi
@ -953,13 +944,13 @@ ape_macho:
.quad 0 // r13
.quad 0 // r14
.quad 0 // r15
.quad _start // rip
.quad _apple // rip
.quad 0 // rflags
.quad 0 // cs
.quad 0 // fs
.quad 0 // gs
520:
60:
620:
70:
.endobj ape_macho,globl,hidden
.previous /* .macho */
@ -1785,7 +1776,6 @@ kernel: movabs $ape_stack_vaddr,%rsp
.ldsvar v_ape_realbytes
.ldsvar v_ape_highsectors
.ldsvar ape_idata_ro
.ldsvar ape_pad_rodata
.ldsvar ape_piro
.ldsvar ape_piro_end
.type ape_macho_end,@object
@ -1829,12 +1819,6 @@ ape_pad_text:
ape_pad_privileged:
.previous
.section .ape.pad.rodata,"a",@progbits
.type ape_pad_rodata,@object
.hidden ape_pad_rodata
ape_pad_rodata:
.previous
.section .ape.pad.data,"a",@progbits
.type ape_pad_data,@object
.hidden ape_pad_data
@ -1877,5 +1861,9 @@ __bss_start:
__bss_end:
.previous
.section .fstls,"awT",@nobits
.align TLS_ALIGNMENT
.previous
.end


View file

@ -206,10 +206,10 @@ ENTRY(_start)
PHDRS {
Head PT_LOAD FLAGS(PF_X|PF_R);
Rom PT_LOAD FLAGS(PF_X|PF_R);
Ram PT_LOAD FLAGS(PF_W|PF_R);
Cod PT_LOAD FLAGS(PF_X|PF_R);
Rom PT_LOAD FLAGS(PF_R);
Tls PT_TLS FLAGS(PF_W|PF_R);
Bss PT_LOAD FLAGS(PF_W|PF_R);
Ram PT_LOAD FLAGS(PF_W|PF_R);
stack PT_GNU_STACK FLAGS(PF_W|PF_R);
}
@ -221,7 +221,7 @@ SECTIONS {
/*BEGIN: bsd addressability guarantee */
.head SEGMENT_START("text-segment", IMAGE_BASE_VIRTUAL) + SKEW : AT(IMAGE_BASE_REAL) {
HIDDEN(__executable_start = .);
__executable_start = .;
/* Real Mode */
KEEP(*(.head))
@ -230,9 +230,9 @@ SECTIONS {
/* Executable & Linkable Format */
. = ALIGN(__SIZEOF_POINTER__);
HIDDEN(ape_phdrs = .);
ape_phdrs = .;
KEEP(*(.elf.phdrs))
HIDDEN(ape_phdrs_end = .);
ape_phdrs_end = .;
KEEP(*(.emushprologue))
KEEP(*(.emush))
@ -240,25 +240,29 @@ SECTIONS {
/* OpenBSD */
. = ALIGN(__SIZEOF_POINTER__);
HIDDEN(ape_note = .);
ape_note = .;
KEEP(*(.note.openbsd.ident))
KEEP(*(.note.netbsd.ident))
HIDDEN(ape_note_end = .);
ape_note_end = .;
/* Portable Executable */
KEEP(*(.pe.header))
HIDDEN(ape_pe_sections = .);
ape_pe_sections = .;
KEEP(*(.pe.sections))
HIDDEN(ape_pe_sections_end = .);
ape_pe_sections_end = .;
/* Mach-O */
KEEP(*(.macho))
. = ALIGN(__SIZEOF_POINTER__);
HIDDEN(ape_macho_end = .);
ape_macho_end = .;
/* APE loader */
KEEP(*(.ape.loader))
. = ALIGN(CODE_GRANULE);
KEEP(*(.ape.pad.head))
. = ALIGN(SupportsWindows() || SupportsMetal() ? CONSTANT(COMMONPAGESIZE) : 16);
HIDDEN(_ehead = .);
_ehead = .;
} :Head
/*BEGIN: nt addressability guarantee */
@ -268,10 +272,7 @@ 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))
. = ALIGN(CODE_GRANULE);
HIDDEN(_ereal = .);
_ereal = .;
/*END: realmode addressability guarantee */
/*BEGIN: morphable code */
. += CODE_GRANULE;
@ -318,9 +319,14 @@ SECTIONS {
__privileged_end = .;
. += . > 0 ? CODE_GRANULE : 0;
KEEP(*(.ape.pad.text))
. = ALIGN(CONSTANT(COMMONPAGESIZE));
/*END: Read Only Data (only needed for initialization) */
} :Cod
/*BEGIN: Read Only Data */
KEEP(*(.ape.pad.rodata));
.rodata . : {
KEEP(*(.rodata.pytab.0));
KEEP(*(.rodata.pytab.1));
KEEP(*(.rodata.pytab.2));
@ -329,10 +335,6 @@ SECTIONS {
*(.ubsan.types)
*(.ubsan.data)
/* Unit Test & Fixture Registry */
/*BEGIN: Read only data that needn't be mapped after initialization */
/* Legal Notices */
#if !defined(IM_FEELING_NAUGHTY) || defined(EMBED_NOTICES)
KEEP(*(.commentprologue))
@ -340,26 +342,28 @@ SECTIONS {
KEEP(*(.commentepilogue))
#endif
/*BEGIN: read-only data that's only needed for initialization */
/* Windows DLL Import Directory */
KEEP(*(.idata.ro));
KEEP(*(SORT_BY_NAME(.idata.ro.*)))
. = ALIGN(__SIZEOF_POINTER__);
PROVIDE_HIDDEN(__init_array_start = .);
__init_array_start = .;
KEEP(*(SORT_BY_INIT_PRIORITY(.init_array.*)
SORT_BY_INIT_PRIORITY(.ctors.*)))
KEEP(*(.ctors))
KEEP(*(.init_array))
KEEP(*(.preinit_array))
PROVIDE_HIDDEN(__init_array_end = .);
__init_array_end = .;
. = ALIGN(__SIZEOF_POINTER__);
PROVIDE_HIDDEN(__fini_array_start = .);
__fini_array_start = .;
KEEP(*(SORT_BY_INIT_PRIORITY(.fini_array.*)
SORT_BY_INIT_PRIORITY(.dtors.*)))
KEEP(*(.fini_array))
KEEP(*(.dtors))
PROVIDE_HIDDEN(__fini_array_end = .);
__fini_array_end = .;
/* Encoded Data Structures w/ Linear Initialization Order */
KEEP(*(.initroprologue))
@ -367,40 +371,39 @@ SECTIONS {
KEEP(*(.initroepilogue))
KEEP(*(SORT_BY_NAME(.sort.rodata.*)))
KEEP(*(.ape.pad.text))
. = ALIGN(CONSTANT(COMMONPAGESIZE));
HIDDEN(_etext = .);
PROVIDE_HIDDEN(etext = .);
/*END: Read Only Data (only needed for initialization) */
} :Rom
/*END: read-only data that's only needed for initialization */
. = DATA_SEGMENT_ALIGN(CONSTANT(MAXPAGESIZE), CONSTANT(COMMONPAGESIZE));
} :Rom
/* initialization image for thread-local storage, this is copied */
/* out to actual TLS areas at runtime, so just make it read-only */
.tdata : {
.tdata . : {
_tdata_start = .;
*(SORT_BY_ALIGNMENT(.tdata))
*(SORT_BY_ALIGNMENT(.tdata.*))
_tdata_end = .;
KEEP(*(.ape.pad.rodata))
. = ALIGN(CONSTANT(COMMONPAGESIZE));
_etext = .;
PROVIDE(etext = .);
} :Tls :Rom
/*END: Read Only Data */
. = DATA_SEGMENT_ALIGN(CONSTANT(MAXPAGESIZE), CONSTANT(COMMONPAGESIZE));
. = DATA_SEGMENT_RELRO_END(0, .);
/* this only tells the linker about the layout of uninitialized */
/* TLS data, and does not advance the linker's location counter */
.tbss : {
_tbss_start = .;
*(SORT_BY_ALIGNMENT(.tbss))
*(SORT_BY_ALIGNMENT(.tbss.*))
. = ALIGN(TLS_ALIGNMENT);
KEEP(*(.fstls))
/* the %fs register is based on this location */
_tbss_end = .;
} :Tls
. = DATA_SEGMENT_RELRO_END(0, .);
.data : {
.data . : {
/*BEGIN: Read/Write Data */
KEEP(*(SORT_BY_NAME(.piro.data.sort.iat.*)))
/*BEGIN: NT FORK COPYING */
@ -427,23 +430,22 @@ SECTIONS {
KEEP(*(.piro.pad.data))
KEEP(*(.dataepilogue))
/*END: NT FORK COPYING */
HIDDEN(_edata = .);
PROVIDE_HIDDEN(edata = .);
HIDDEN(_ezip = .); /* <-- very deprecated */
. = ALIGN(CONSTANT(COMMONPAGESIZE));
_edata = .;
PROVIDE(edata = .);
_ezip = .; /* <-- very deprecated */
} :Ram
/*END: file content that's loaded by o/s */
/*END: file content */
/*BEGIN: bss memory that's addressable */
.bss ALIGN(64) : {
.bss : {
/*BEGIN: NT FORK COPYING */
KEEP(*(.bssprologue))
KEEP(*(SORT_BY_NAME(.piro.bss.init.*)))
*(.piro.bss)
KEEP(*(SORT_BY_NAME(.piro.bss.sort.*)))
HIDDEN(__piro_end = .);
__piro_end = .;
. += . > 0 ? CODE_GRANULE : 0;
/*END: Post-Initialization Read-Only */
@ -455,11 +457,13 @@ SECTIONS {
KEEP(*(SORT_BY_NAME(.sort.bss.*)))
KEEP(*(.bssepilogue))
. = ALIGN(64);
/*END: NT FORK COPYING */
. = ALIGN(CONSTANT(COMMONPAGESIZE));
HIDDEN(_end = .);
PROVIDE_HIDDEN(end = .);
} :Bss
_end = .;
PROVIDE(end = .);
} :Ram
. = DATA_SEGMENT_END(.);
@ -527,67 +531,60 @@ PFSTUB4(ape_elf_phnum, (ape_phdrs_end - ape_phdrs) / 56);
PFSTUB4(ape_elf_shnum, 0);
PFSTUB4(ape_elf_shstrndx, 0);
HIDDEN(_tls_size = _tbss_end - _tdata_start);
HIDDEN(_tdata_size = _tdata_end - _tdata_start);
HIDDEN(_tbss_size = _tbss_end - _tbss_start);
HIDDEN(_tbss_offset = _tbss_start - _tdata_start);
HIDDEN(_tls_content = (_tdata_end - _tdata_start) + (_tbss_end - _tbss_start));
HIDDEN(_tls_align = 1);
_tls_size = _tbss_end - _tdata_start;
_tdata_size = _tdata_end - _tdata_start;
_tbss_size = _tbss_end - _tbss_start;
_tbss_offset = _tbss_start - _tdata_start;
_tls_content = (_tdata_end - _tdata_start) + (_tbss_end - _tbss_start);
_tls_align = 1;
HIDDEN(ape_rom_offset = 0);
HIDDEN(ape_rom_vaddr = ADDR(.head));
HIDDEN(ape_rom_paddr = LOADADDR(.head));
HIDDEN(ape_rom_filesz = LOADADDR(.data) - ape_rom_paddr);
HIDDEN(ape_rom_memsz = ADDR(.data) - ADDR(.head));
HIDDEN(ape_rom_align = CONSTANT(COMMONPAGESIZE));
HIDDEN(ape_rom_rva = RVA(ape_rom_vaddr));
ape_cod_offset = 0;
ape_cod_vaddr = ADDR(.head);
ape_cod_paddr = LOADADDR(.head);
ape_cod_filesz = SIZEOF(.head) + SIZEOF(.text);
ape_cod_memsz = ape_cod_filesz;
ape_cod_align = CONSTANT(COMMONPAGESIZE);
ape_cod_rva = RVA(ape_cod_vaddr);
HIDDEN(ape_ram_offset = ape_rom_offset + ape_rom_filesz);
HIDDEN(ape_ram_vaddr = ADDR(.data));
HIDDEN(ape_ram_paddr = LOADADDR(.data));
HIDDEN(ape_ram_filesz = SIZEOF(.data));
HIDDEN(ape_ram_memsz = ADDR(.bss) + SIZEOF(.bss) - ape_ram_vaddr);
HIDDEN(ape_ram_align = CONSTANT(COMMONPAGESIZE));
HIDDEN(ape_ram_rva = RVA(ape_ram_vaddr));
ape_rom_offset = ape_cod_offset + ape_cod_filesz;
ape_rom_vaddr = ADDR(.rodata);
ape_rom_paddr = LOADADDR(.rodata);
ape_rom_filesz = SIZEOF(.rodata) + SIZEOF(.tdata);
ape_rom_memsz = ape_rom_filesz;
ape_rom_align = CONSTANT(COMMONPAGESIZE);
ape_rom_rva = RVA(ape_rom_vaddr);
HIDDEN(ape_stack_pf = DEFINED(ape_stack_pf) ? ape_stack_pf : PF_R | PF_W);
HIDDEN(ape_stack_prot = _PF2PROT(ape_stack_pf));
HIDDEN(ape_stack_offset = ape_ram_offset + ape_ram_filesz);
HIDDEN(ape_stack_vaddr = DEFINED(ape_stack_vaddr) ? ape_stack_vaddr : 0x700000000000);
HIDDEN(ape_stack_paddr = ape_ram_paddr + ape_ram_filesz);
HIDDEN(ape_stack_filesz = 0);
HIDDEN(ape_stack_memsz = DEFINED(ape_stack_memsz) ? ape_stack_memsz : APE_STACKSIZE);
HIDDEN(ape_stack_align = 16);
ape_ram_offset = ape_rom_offset + ape_rom_filesz;
ape_ram_vaddr = ADDR(.data);
ape_ram_paddr = LOADADDR(.data);
ape_ram_filesz = SIZEOF(.data);
ape_ram_memsz = SIZEOF(.data) + SIZEOF(.bss);
ape_ram_align = CONSTANT(COMMONPAGESIZE);
ape_ram_rva = RVA(ape_ram_vaddr);
HIDDEN(ape_note_offset = ape_rom_offset + (ape_note - ape_rom_vaddr));
HIDDEN(ape_note_vaddr = ape_note);
HIDDEN(ape_note_paddr = ape_rom_paddr + ape_note_offset);
HIDDEN(ape_note_filesz = ape_note_end - ape_note);
HIDDEN(ape_note_memsz = ape_note_filesz);
HIDDEN(ape_note_align = __SIZEOF_POINTER__);
ape_stack_pf = DEFINED(ape_stack_pf) ? ape_stack_pf : PF_R | PF_W;
ape_stack_prot = _PF2PROT(ape_stack_pf);
ape_stack_offset = 0;
ape_stack_vaddr = DEFINED(ape_stack_vaddr) ? ape_stack_vaddr : 0x700000000000;
ape_stack_paddr = ape_ram_paddr + ape_ram_filesz;
ape_stack_filesz = 0;
ape_stack_memsz = DEFINED(ape_stack_memsz) ? ape_stack_memsz : APE_STACKSIZE;
ape_stack_align = 16;
HIDDEN(ape_text_offset = ape_rom_offset + LOADADDR(.text) - ape_rom_paddr);
HIDDEN(ape_text_paddr = LOADADDR(.text));
HIDDEN(ape_text_vaddr = ADDR(.text));
HIDDEN(ape_text_filesz = SIZEOF(.text) + SIZEOF(.tdata));
HIDDEN(ape_text_memsz = SIZEOF(.text) + SIZEOF(.tdata));
HIDDEN(ape_text_align = CONSTANT(COMMONPAGESIZE));
HIDDEN(ape_text_rva = RVA(ape_text_vaddr));
ape_note_offset = ape_cod_offset + (ape_note - ape_cod_vaddr);
ape_note_vaddr = ape_note;
ape_note_paddr = ape_cod_paddr + ape_note_offset;
ape_note_filesz = ape_note_end - ape_note;
ape_note_memsz = ape_note_filesz;
ape_note_align = __SIZEOF_POINTER__;
HIDDEN(ape_data_offset = ape_ram_offset + LOADADDR(.data) - ape_ram_paddr);
HIDDEN(ape_data_paddr = LOADADDR(.data));
HIDDEN(ape_data_vaddr = ADDR(.data));
HIDDEN(ape_data_filesz = SIZEOF(.data));
HIDDEN(ape_data_memsz = SIZEOF(.data));
HIDDEN(ape_data_align = CONSTANT(COMMONPAGESIZE));
HIDDEN(ape_data_rva = RVA(ape_data_vaddr));
HIDDEN(ape_bss_offset = ape_ram_offset + LOADADDR(.bss) - ape_ram_paddr);
HIDDEN(ape_bss_paddr = LOADADDR(.bss));
HIDDEN(ape_bss_vaddr = ADDR(.bss));
HIDDEN(ape_bss_filesz = 0);
HIDDEN(ape_bss_memsz = SIZEOF(.bss));
HIDDEN(ape_bss_align = CONSTANT(COMMONPAGESIZE));
ape_text_offset = ape_cod_offset + LOADADDR(.text) - ape_cod_paddr;
ape_text_paddr = LOADADDR(.text);
ape_text_vaddr = ADDR(.text);
ape_text_filesz = SIZEOF(.text) + SIZEOF(.rodata) + SIZEOF(.tdata);
ape_text_memsz = ape_text_filesz;
ape_text_align = CONSTANT(COMMONPAGESIZE);
ape_text_rva = RVA(ape_text_vaddr);
/* 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 */
@ -602,17 +599,17 @@ SHSTUB2(ape_loader_dd_count,
#define IDENTITY(X) X
#define APE_DECLARE_FIXED_DECIMAL(F, X) \
HIDDEN(X##_quad = DEFINED(X) ? ((F(X) < 1000000000 ? 32 : F(X) / 1000000000 % 10 + 48) << 000 | \
(F(X) < 100000000 ? 32 : F(X) / 100000000 % 10 + 48) << 010 | \
(F(X) < 10000000 ? 32 : F(X) / 10000000 % 10 + 48) << 020 | \
(F(X) < 1000000 ? 32 : F(X) / 1000000 % 10 + 48) << 030 | \
(F(X) < 100000 ? 32 : F(X) / 100000 % 10 + 48) << 040 | \
(F(X) < 10000 ? 32 : F(X) / 10000 % 10 + 48) << 050 | \
(F(X) < 1000 ? 32 : F(X) / 1000 % 10 + 48) << 060 | \
(F(X) < 100 ? 32 : F(X) / 100 % 10 + 48) << 070) : 0); \
HIDDEN(X##_short = DEFINED(X) ? ((F(X) < 10 ? 32 : F(X) / 10 % 10 + 48) << 000 | \
(F(X) % 10 + 48) << 010) : 0)
#define APE_DECLARE_FIXED_DECIMAL(F, X) \
X##_quad = DEFINED(X) ? ((F(X) < 1000000000 ? 32 : F(X) / 1000000000 % 10 + 48) << 000 | \
(F(X) < 100000000 ? 32 : F(X) / 100000000 % 10 + 48) << 010 | \
(F(X) < 10000000 ? 32 : F(X) / 10000000 % 10 + 48) << 020 | \
(F(X) < 1000000 ? 32 : F(X) / 1000000 % 10 + 48) << 030 | \
(F(X) < 100000 ? 32 : F(X) / 100000 % 10 + 48) << 040 | \
(F(X) < 10000 ? 32 : F(X) / 10000 % 10 + 48) << 050 | \
(F(X) < 1000 ? 32 : F(X) / 1000 % 10 + 48) << 060 | \
(F(X) < 100 ? 32 : F(X) / 100 % 10 + 48) << 070) : 0; \
X##_short = DEFINED(X) ? ((F(X) < 10 ? 32 : F(X) / 10 % 10 + 48) << 000 | \
(F(X) % 10 + 48) << 010) : 0
APE_DECLARE_FIXED_DECIMAL(RVA, blink_linux_aarch64);
APE_DECLARE_FIXED_DECIMAL(IDENTITY, blink_linux_aarch64_size);
@ -622,13 +619,12 @@ APE_DECLARE_FIXED_DECIMAL(IDENTITY, blink_xnu_aarch64_size);
#endif /* APE_IS_SHELL_SCRIPT */
#if SupportsMetal()
HIDDEN(v_ape_realsectors =
MIN(0x70000 - IMAGE_BASE_REAL, ROUNDUP(RVA(_ezip), 512)) / 512);
HIDDEN(v_ape_realbytes = v_ape_realsectors * 512);
HIDDEN(v_ape_realdwords = v_ape_realsectors * (512 / 4));
HIDDEN(v_ape_allsectors = ROUNDUP(RVA(_ezip), 512) / 512);
HIDDEN(v_ape_allbytes = v_ape_allsectors * 512);
HIDDEN(v_ape_highsectors = MIN(0xffff, v_ape_allsectors - v_ape_realsectors));
v_ape_realsectors = MIN(0x70000 - IMAGE_BASE_REAL, ROUNDUP(RVA(_ezip), 512)) / 512;
v_ape_realbytes = v_ape_realsectors * 512;
v_ape_realdwords = v_ape_realsectors * (512 / 4);
v_ape_allsectors = ROUNDUP(RVA(_ezip), 512) / 512;
v_ape_allbytes = v_ape_allsectors * 512;
v_ape_highsectors = MIN(0xffff, v_ape_allsectors - v_ape_realsectors);
TSSDESCSTUB2(_tss, _tss, _tss_end ? _tss_end - _tss - 1 : 0);
#endif
@ -650,19 +646,8 @@ TSSDESCSTUB2(_tss, _tss, _tss_end ? _tss_end - _tss - 1 : 0);
KMH(ape_uuid1, X); \
XORSHIFT(ape_uuid2, X); \
KMH(ape_uuid2, X)
HIDDEN(ape_uuid1 = 88172645463325252);
HIDDEN(ape_uuid2 = 88172645463325252);
CHURN(ape_bss_align);
CHURN(ape_bss_filesz);
CHURN(ape_bss_memsz);
CHURN(ape_bss_offset);
CHURN(ape_bss_paddr);
CHURN(ape_data_filesz);
CHURN(ape_data_memsz);
CHURN(ape_data_offset);
CHURN(ape_data_paddr);
CHURN(ape_data_rva);
CHURN(ape_data_vaddr);
ape_uuid1 = 88172645463325252;
ape_uuid2 = 88172645463325252;
CHURN(ape_elf_entry);
CHURN(ape_elf_phnum);
CHURN(ape_elf_phoff);
@ -691,7 +676,14 @@ CHURN(ape_rom_memsz);
CHURN(ape_rom_offset);
CHURN(ape_rom_paddr);
CHURN(ape_rom_rva);
CHURN(ape_rom_vaddr);
CHURN(ape_cod_vaddr);
CHURN(ape_cod_align);
CHURN(ape_cod_filesz);
CHURN(ape_cod_memsz);
CHURN(ape_cod_offset);
CHURN(ape_cod_paddr);
CHURN(ape_cod_rva);
CHURN(ape_cod_vaddr);
CHURN(ape_text_align);
CHURN(ape_text_filesz);
CHURN(ape_text_memsz);
@ -724,22 +716,22 @@ CHURN(WinMain);
#if SupportsWindows() || SupportsMetal()
#define LINK_WINDOWS (SupportsWindows() && !DEFINED(EfiMain))
PFSTUB4(ape_pe_offset, ape_pe - ape_mz);
HIDDEN(ape_pe_optsz = ape_pe_sections - (ape_pe + 24));
HIDDEN(ape_pe_shnum = (ape_pe_sections_end - ape_pe_sections) / 40);
HIDDEN(ape_pe_base = IMAGE_BASE_VIRTUAL);
HIDDEN(ape_idata = LINK_WINDOWS ? RVA(ape_idata_iat) : 0);
HIDDEN(ape_idata_iatsize = LINK_WINDOWS ? ape_idata_iatend - ape_idata_iat : 0);
HIDDEN(ape_idata = LINK_WINDOWS ? RVA(ape_idata_idt) : 0);
HIDDEN(ape_idata_idtsize = LINK_WINDOWS ? ape_idata_idtend - ape_idata_idt : 0);
HIDDEN(v_ntversion = LINK_WINDOWS ? 6 : 1);
HIDDEN(v_ntdllchar = LINK_WINDOWS ? 288 : 0);
HIDDEN(v_ntsubversion = LINK_WINDOWS ? 6 : 5);
HIDDEN(v_ntsubsystem = (LINK_WINDOWS
? (DEFINED(GetMessage)
? kNtImageSubsystemWindowsGui
: kNtImageSubsystemWindowsCui)
: kNtImageSubsystemEfiApplication));
HIDDEN(ape_pe_entry = LINK_WINDOWS ? WinMain : EfiMain);
ape_pe_optsz = ape_pe_sections - (ape_pe + 24);
ape_pe_shnum = (ape_pe_sections_end - ape_pe_sections) / 40;
ape_pe_base = IMAGE_BASE_VIRTUAL;
ape_idata = LINK_WINDOWS ? RVA(ape_idata_iat) : 0;
ape_idata_iatsize = LINK_WINDOWS ? ape_idata_iatend - ape_idata_iat : 0;
ape_idata = LINK_WINDOWS ? RVA(ape_idata_idt) : 0;
ape_idata_idtsize = LINK_WINDOWS ? ape_idata_idtend - ape_idata_idt : 0;
v_ntversion = LINK_WINDOWS ? 6 : 1;
v_ntdllchar = LINK_WINDOWS ? 288 : 0;
v_ntsubversion = LINK_WINDOWS ? 6 : 5;
v_ntsubsystem = (LINK_WINDOWS
? (DEFINED(GetMessage)
? kNtImageSubsystemWindowsGui
: kNtImageSubsystemWindowsCui)
: kNtImageSubsystemEfiApplication);
ape_pe_entry = LINK_WINDOWS ? WinMain : EfiMain;
#endif
#if SupportsXnu()

View file

@ -86,7 +86,6 @@ APE_LOADER_FLAGS = \
-Os \
-ffreestanding \
-mgeneral-regs-only \
-mno-red-zone \
-fno-ident \
-fno-gnu-unique \
-c \
@ -178,14 +177,14 @@ o/$(MODE)/ape/ape-copy-self.o: \
$(OUTPUT_OPTION) \
-DAPE_NO_MODIFY_SELF $<
o/$(MODE)/ape/loader.o: ape/loader.c ape/loader.h
o/$(MODE)/ape/loader.o: ape/loader.c
@$(COMPILE) -AOBJECTIFY.c $(CC) -DSUPPORT_VECTOR=0b01111001 -g $(APE_LOADER_FLAGS)
o/$(MODE)/ape/loader-gcc.asm: ape/loader.c
@$(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/loader-xnu.o: ape/loader.c ape/loader.h
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)
@ -212,7 +211,7 @@ o/$(MODE)/ape/ape.macho.dbg: private \
o/$(MODE)/ape/ape.macho.dbg: \
o/$(MODE)/ape/loader-xnu.o \
o/$(MODE)/ape/loader-macho.o \
ape/loader-macho.lds
ape/loader.lds
@$(ELFLINK)
.PHONY: o/$(MODE)/ape

View file

@ -11,7 +11,7 @@ fi
echo "APE Uninstaller intends to run (in pseudo-shell)"
echo
echo " sudo echo -1 into /proc/sys/fs/binfmt_misc/APE*"
echo " sudo rm -f /usr/bin/ape ~/.ape o/tmp/.ape /tmp/.ape"
echo " sudo rm -f /usr/bin/ape ~/.ape /tmp/.ape # etc."
echo
echo "You may then use ape/apeinstall.sh to reinstall it"
echo
@ -23,4 +23,23 @@ for f in /proc/sys/fs/binfmt_misc/APE*; do
$SUDO sh -c "echo -1 >$f" || exit
fi
done
$SUDO rm -f /usr/bin/ape ~/.ape o/tmp/.ape o/tmp/ape /tmp/.ape /tmp/ape || exit
# system installation
if [ -f /usr/bin/ape ]; then
$SUDO rm -f /usr/bin/ape
fi
# legacy installations
rm -f o/tmp/ape /tmp/ape "${TMPDIR:-/tmp}/ape"
# ad-hoc installations
for x in .ape \
.ape-1.1 \
.ape-blink-0.9.2 \
.ape-blink-1.0.0; do
rm -f \
~/$x \
/tmp/$x \
o/tmp/$x \
"${TMPDIR:-/tmp}/$x"
done

View file

@ -25,6 +25,8 @@
// APE Loader Executable Structure
// Linux, FreeBSD, NetBSD, OpenBSD
.section .head
.balign 8
ehdr: .ascii "\177ELF"
.byte ELFCLASS64
@ -49,21 +51,21 @@ ehdr: .ascii "\177ELF"
.balign 8
phdrs: .long PT_LOAD // p_type
.long PF_R|PF_X // p_flags
.long PF_R // p_flags
.quad 0 // p_offset
.quad ehdr // p_vaddr
.quad ehdr // p_paddr
.quad filesz // p_filesz
.quad filesz // p_memsz
.quad rosize // p_filesz
.quad rosize // p_memsz
.quad 4096 // 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
.long PF_X // p_flags
.quad rosize // p_offset
.quad text // p_vaddr
.quad text // p_paddr
.quad textsz // p_filesz
.quad textsz // p_memsz
.quad 4096 // p_align
.long PT_GNU_STACK // p_type
@ -72,7 +74,7 @@ phdrs: .long PT_LOAD // p_type
.quad 0 // p_vaddr
.quad 0 // p_paddr
.quad 0 // p_filesz
.quad 0 // p_memsz
.quad 8*1024*1024 // p_memsz
.quad 16 // p_align
.long PT_NOTE // p_type
@ -101,7 +103,7 @@ note: .long 2f-1f
notesize = . - note
.balign 64,0 // for ape.S dd
.org 0x180 // for ape.S dd
.org 64*6 // for ape.S dd
// APE Loader XNU Header
//
@ -113,8 +115,8 @@ 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 4 // number of load commands
.long 50f-10f // size of all load commands
.long MAC_NOUNDEFS // flags
.long 0 // reserved
10: .long MAC_LC_SEGMENT_64
@ -126,56 +128,25 @@ macho: .long 0xFEEDFACE+1
.long 30f-20b
.ascin "__TEXT",16
.quad ehdr // vaddr
.quad 4096 // memsz
.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 // 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
30: .long MAC_LC_UUID
.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
.quad 0xdd2c9bb866d9eef9 // uuid2
40: .long MAC_LC_UNIXTHREAD
.long 50f-40b // cmdsize
.long MAC_THREAD_NEXGEN32E // flavaflav
.long (520f-510f)/4 // count
510: .quad 0 // rax
.long (420f-410f)/4 // count
410: .quad 0 // rax
.quad 0 // rbx
.quad 0 // rcx
.quad _HOSTXNU // rdx
.quad 0 // rdx
.quad 0 // rdi
.quad 0 // rsi
.quad 0 // rbp
@ -188,62 +159,41 @@ macho: .long 0xFEEDFACE+1
.quad 0 // r13
.quad 0 // r14
.quad 0 // r15
.quad _start // rip
.quad _apple // rip
.quad 0 // rflags
.quad 0 // cs
.quad 0 // fs
.quad 0 // gs
520:
60:
420:
50:
.endobj macho
.balign 64,0 // for ape.S dd
.org 0x400 // for ape.S dd
.org 64*12 // 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:
// Hack for detecting M1 Rosetta environment.
// https://github.com/jart/cosmopolitan/issues/429#issuecomment-1166704377
cmp $-1,%ebx
jne 0f
cmp $+1,%edx
jne 0f
mov $_HOSTXNU,%dl
xor %ecx,%ecx
0: mov %rsp,%rsi
.text
_apple: mov $_HOSTXNU,%dl // xnu's not unix!
_start: mov %rsp,%rsi // save real stack
sub $1024*1024,%rsp // room for allocs
jmp ApeLoader
.endfn _start,globl
.endfn _apple,globl
// System Call Entrpoint
// Invokes system call.
//
// 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:
// This function has eight parameters. The first seven are for
// arguments passed along to the system call. The eight is for
// the magic number that indicates which system call is called
//
// The return value follows the Linux kernel convention, where
// errors are returned as `-errno`. BSD systems are normalized
// to follow this convention automatically.
SystemCall:
mov %rcx,%r10
mov 16(%rsp),%eax
clc
syscall
jc 1f
ret
1: neg %rax
ret
.endfn __syscall_loader,globl
jnc 1f
neg %rax
1: ret
.endfn SystemCall,globl

View file

@ -23,116 +23,88 @@
// APE Loader Executable Structure for XNU
.balign 4096
.section .head
.balign 64
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
.long 4 // number of load commands
.long 50f-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 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 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**3 = 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
.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 0 // segment section count
.long 0 // flags
30: .long MAC_LC_UUID
.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 _HOSTXNU # 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:
.quad 0x3fb29ee4ac6c87aa // uuid1
.quad 0xdd2c9bb866d9eef8 // uuid2
40: .long MAC_LC_UNIXTHREAD
.long 50f-40b // cmdsize
.long MAC_THREAD_NEXGEN32E // flavaflav
.long (420f-410f)/4 // count
410: .quad 0 // rax
.quad 0 // rbx
.quad 0 // rcx
.quad 0 // rdx
.quad 0 // rdi
.quad 0 // rsi
.quad 0 // rbp
.quad 0 // rsp
.quad 0 // r8
.quad 0 // r9
.quad 0 // r10
.quad 0 // r11
.quad 0 // r12
.quad 0 // r13
.quad 0 // r14
.quad 0 // r15
.quad _start // rip
.quad 0 // rflags
.quad 0 // cs
.quad 0 // fs
.quad 0 // gs
420:
50:
.endobj macho,globl
.balign 64
_start:
// Hack for detecting M1 Rosetta environment.
// https://github.com/jart/cosmopolitan/issues/429#issuecomment-1166704377
cmp $-1,%ebx
jne 0f
cmp $+1,%edx
jne 0f
mov $_HOSTXNU,%dl
xor %ecx,%ecx
0: mov %rsp,%rsi
_start: mov $_HOSTXNU,%dl // xnu's not unix!
mov %rsp,%rsi // save real stack
sub $1024*1024,%rsp // room for allocs
jmp ApeLoader
.endfn _start,globl
__syscall_loader:
// Invokes system call.
//
// This function has eight parameters. The first seven are for
// arguments passed along to the system call. The eight is for
// the magic number that indicates which system call is called
//
// The return value follows the Linux kernel convention, where
// errors are returned as `-errno`. BSD systems are normalized
// to follow this convention automatically.
SystemCall:
mov %rcx,%r10
mov 16(%rsp),%eax
clc
syscall
jc 1f
ret
1: neg %rax
ret
.endfn __syscall_loader,globl
jnc 1f
neg %rax
1: ret
.endfn SystemCall,globl

View file

@ -1,42 +0,0 @@
/*-*- 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)
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;

View file

@ -16,11 +16,6 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "ape/loader.h"
#define SET_EXE_FILE 0 /* needs root ;_; */
#define TROUBLESHOOT 0
#define TROUBLESHOOT_OS LINUX
/**
* @fileoverview APE Loader for GNU/Systemd/XNU/FreeBSD/NetBSD/OpenBSD
@ -110,7 +105,7 @@
#define MAP_ANONYMOUS (IsLinux() ? 32 : 4096)
#define AT_EXECFN_LINUX 31
#define AT_EXECFN_NETBSD 2014
#define ELFCLASS64 2
#define ELFCLASS32 1
#define ELFDATA2LSB 1
#define EM_NEXGEN32E 62
#define ET_EXEC 2
@ -127,6 +122,10 @@
#define PR_SET_MM 35
#define PR_SET_MM_EXE_FILE 13
#define Min(X, Y) ((Y) > (X) ? (X) : (Y))
#define Roundup(X, K) (((X) + (K)-1) & -(K))
#define Rounddown(X, K) ((X) & -(K))
#define Read32(S) \
((unsigned)(255 & (S)[3]) << 030 | (unsigned)(255 & (S)[2]) << 020 | \
(unsigned)(255 & (S)[1]) << 010 | (unsigned)(255 & (S)[0]) << 000)
@ -141,14 +140,6 @@
(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;
@ -177,26 +168,43 @@ struct ElfPhdr {
unsigned long p_align;
};
extern char ehdr[];
extern char _end[];
static void *syscall_;
static struct PathSearcher ps;
extern char __syscall_loader[];
union ElfEhdrBuf {
struct ElfEhdr ehdr;
char buf[4096];
};
#if SET_EXE_FILE
static char relocated;
#endif
union ElfPhdrBuf {
struct ElfPhdr phdr;
char buf[4096];
};
struct PathSearcher {
int os;
const char *name;
const char *syspath;
unsigned long namelen;
char path[1024];
};
struct ApeLoader {
union ElfEhdrBuf ehdr;
union ElfPhdrBuf phdr;
struct PathSearcher ps;
};
long SystemCall(long arg1, //
long arg2, //
long arg3, //
long arg4, //
long arg5, //
long arg6, //
long arg7, //
long magi);
static int ToLower(int c) {
return 'A' <= c && c <= 'Z' ? c + ('a' - 'A') : c;
}
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;
@ -212,6 +220,22 @@ static const char *MemChr(const char *s, unsigned char c, unsigned long n) {
return 0;
}
static void *MemMove(void *a, const void *b, unsigned long n) {
char *d = a;
unsigned long i;
const char *s = b;
if (d > s) {
for (i = n; i--;) {
d[i] = s[i];
}
} else {
for (i = 0; i < n; ++i) {
d[i] = s[i];
}
}
return d;
}
static char *GetEnv(char **p, const char *s) {
unsigned long i, j;
if (p) {
@ -256,168 +280,105 @@ static char *Itoa(char p[21], long x) {
return Utoa(p, x);
}
#if TROUBLESHOOT
const char *DescribeOs(int os) {
if (IsLinux()) {
return "GNU/SYSTEMD";
} else if (IsXnu()) {
return "XNU";
} else if (IsFreebsd()) {
return "FREEBSD";
} else if (IsOpenbsd()) {
return "OPENBSD";
} else if (IsNetbsd()) {
return "NETBSD";
} else {
return "WUT";
}
}
#endif
__attribute__((__noreturn__)) static void Exit(int rc, int os) {
asm volatile("call\t*%2"
: /* no outputs */
: "a"((IsLinux() ? 60 : 1) | (IsXnu() ? 0x2000000 : 0)), "D"(rc),
"rm"(syscall_)
: "memory");
SystemCall(rc, 0, 0, 0, 0, 0, 0,
(IsLinux() ? 60 : 1) | (IsXnu() ? 0x2000000 : 0));
__builtin_unreachable();
}
static void Close(int fd, int os) {
int ax, di;
asm volatile("call\t*%4"
: "=a"(ax), "=D"(di)
: "0"((IsLinux() ? 3 : 6) | (IsXnu() ? 0x2000000 : 0)), "1"(fd),
"rm"(syscall_)
: "rcx", "rdx", "rsi", "r8", "r9", "r10", "r11", "memory", "cc");
static int Close(int fd, int os) {
return SystemCall(fd, 0, 0, 0, 0, 0, 0,
(IsLinux() ? 3 : 6) | (IsXnu() ? 0x2000000 : 0));
}
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"((IsLinux() ? 0 : 3) | (IsXnu() ? 0x2000000 : 0)), "1"(fd),
"2"(data), "3"(size), "rm"(syscall_)
: "rcx", "r8", "r9", "r10", "r11", "memory");
return ax;
static long Pread(int fd, void *data, unsigned long size, long off, int os) {
long magi;
if (IsLinux()) {
magi = 0x011;
} else if (IsXnu()) {
magi = 0x2000099;
} else if (IsFreebsd()) {
magi = 0x1db;
} else if (IsOpenbsd()) {
magi = 0x0a9; // OpenBSD v7.3+
} else if (IsNetbsd()) {
magi = 0x0ad;
} else {
__builtin_unreachable();
}
return SystemCall(fd, (long)data, size, off, off, 0, 0, magi);
}
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"((IsLinux() ? 1 : 4) | (IsXnu() ? 0x2000000 : 0)), "1"(fd),
"2"(data), "3"(size), "rm"(syscall_)
: "rcx", "r8", "r9", "r10", "r11", "memory", "cc");
static long Write(int fd, const void *data, unsigned long size, int os) {
return SystemCall(fd, (long)data, size, 0, 0, 0, 0,
(IsLinux() ? 1 : 4) | (IsXnu() ? 0x2000000 : 0));
}
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 | (IsXnu() ? 0x2000000 : 0)), "1"(prog), "2"(argv),
"3"(envp), "rm"(syscall_)
: "rcx", "r8", "r9", "r10", "r11", "memory", "cc");
static int Execve(const char *prog, char **argv, char **envp, int os) {
return SystemCall((long)prog, (long)argv, (long)envp, 0, 0, 0, 0,
59 | (IsXnu() ? 0x2000000 : 0));
}
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"((IsLinux() ? 21 : 33) | (IsXnu() ? 0x2000000 : 0)),
"1"(path), "2"(mode), "rm"(syscall_)
: "rcx", "r8", "r9", "r10", "r11", "memory", "cc");
return ax;
return SystemCall((long)path, mode, 0, 0, 0, 0, 0,
(IsLinux() ? 21 : 33) | (IsXnu() ? 0x2000000 : 0));
}
static int Msyscall(long p, long n, int os) {
int ax;
long di, si;
if (!IsOpenbsd()) {
return 0;
static int Msyscall(long p, unsigned long n, int os) {
if (IsOpenbsd()) {
return SystemCall(p, n, 0, 0, 0, 0, 0, 37);
} else {
asm volatile("call\t*%6"
: "=a"(ax), "=D"(di), "=S"(si)
: "0"(37), "1"(p), "2"(n), "rm"(syscall_)
: "rcx", "rdx", "r8", "r9", "r10", "r11", "memory", "cc");
return ax;
return 0;
}
}
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"((IsLinux() ? 2 : 5) | (IsXnu() ? 0x2000000 : 0)),
"1"(path), "2"(flags), "3"(mode), "rm"(syscall_)
: "rcx", "r8", "r9", "r10", "r11", "memory", "cc");
return ax;
return SystemCall((long)path, flags, mode, 0, 0, 0, 0,
(IsLinux() ? 2 : 5) | (IsXnu() ? 0x2000000 : 0));
}
__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"
"push\t%%r9\n\t"
"call\t*%7\n\t"
"pop\t%%r9\n\t"
"pop\t%%r9"
: "=a"(ax), "=D"(di), "=S"(si), "=d"(dx), "+r"(flags_),
"+r"(fd_), "+r"(off_)
: "rm"(syscall_),
"0"((IsLinux() ? 9
: IsFreebsd() ? 477
: 197) |
(IsXnu() ? 0x2000000 : 0)),
"1"(addr), "2"(size), "3"(prot)
: "rcx", "r11", "memory", "cc");
return ax;
}
int MunmapLinux(const void *addr, unsigned long size) {
int ax;
asm volatile("syscall"
: "=a"(ax)
: "0"(11), "D"(addr), "S"(size)
: "rcx", "r11", "memory");
return ax;
}
int PrctlLinux(int op, long a, long b, long c, long d) {
int rc;
asm volatile("mov\t%5,%%r10\n\t"
"mov\t%6,%%r8\n\t"
"syscall"
: "=a"(rc)
: "0"(157), "D"(op), "S"(a), "d"(b), "g"(c), "g"(d)
: "rcx", "r8", "r10", "r11", "memory");
return rc;
}
static void Emit(int os, const char *s) {
Write(2, s, StrLen(s), os);
}
static void Perror(int os, const char *c, int rc, const char *s) {
char ibuf[21];
Emit(os, "ape error: ");
Emit(os, c);
Emit(os, ": ");
Emit(os, s);
if (rc) {
Emit(os, " failed errno=");
Itoa(ibuf, -rc);
Emit(os, ibuf);
static long Mmap(void *addr, unsigned long size, int prot, int flags, int fd,
long off, int os) {
long magi;
if (IsLinux()) {
magi = 9;
} else if (IsXnu()) {
magi = 0x2000000 | 197;
} else if (IsFreebsd()) {
magi = 477;
} else if (IsOpenbsd()) {
magi = 49; // OpenBSD v7.3+
} else if (IsNetbsd()) {
magi = 197;
} else {
__builtin_unreachable();
}
Emit(os, "\n");
return SystemCall((long)addr, size, prot, flags, fd, off, off, magi);
}
static long Print(int os, int fd, const char *s, ...) {
int c;
unsigned n;
char b[512];
__builtin_va_list va;
__builtin_va_start(va, s);
for (n = 0; s; s = __builtin_va_arg(va, const char *)) {
while ((c = *s++)) {
if (n < sizeof(b)) {
b[n++] = c;
}
}
}
__builtin_va_end(va);
return Write(fd, b, n, os);
}
static void Perror(int os, const char *thing, long rc, const char *reason) {
char ibuf[21];
ibuf[0] = 0;
if (rc) Itoa(ibuf, -rc);
Print(os, 2, "ape error: ", thing, ": ", reason,
rc ? " failed w/ errno " : "", ibuf, "\n", 0l);
}
__attribute__((__noreturn__)) static void Pexit(int os, const char *c, int rc,
@ -432,6 +393,15 @@ static int StrCmp(const char *l, const char *r) {
return (l[i] & 255) - (r[i] & 255);
}
static void *MemSet(void *a, int c, unsigned long n) {
char *d = a;
unsigned long i;
for (i = 0; i < n; ++i) {
d[i] = c;
}
return d;
}
static char EndsWithIgnoreCase(const char *p, unsigned long n, const char *s) {
unsigned long i, m;
if (n >= (m = StrLen(s))) {
@ -458,8 +428,8 @@ static char AccessCommand(struct PathSearcher *ps, const char *suffix,
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);
MemMove(ps->path + pathlen, ps->name, ps->namelen);
MemMove(ps->path + pathlen + ps->namelen, suffix, suffixlen + 1);
return !Access(ps->path, X_OK, ps->os);
}
@ -507,37 +477,30 @@ static char *Commandv(struct PathSearcher *ps, int os, const char *name,
}
__attribute__((__noreturn__)) static void Spawn(int os, const char *exe, int fd,
long *sp, char *page,
struct ElfEhdr *e) {
long *sp, struct ElfEhdr *e,
struct ElfPhdr *p) {
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) {
Pexit(os, exe, 0, "ELF e_machine != EM_NEXGEN32E");
}
if (e->e_ident[EI_CLASS] != ELFCLASS64) {
Pexit(os, exe, 0, "ELF e_ident[EI_CLASS] != ELFCLASS64");
}
if (e->e_ident[EI_DATA] != ELFDATA2LSB) {
Pexit(os, exe, 0, "ELF e_ident[EI_DATA] != ELFDATA2LSB");
}
if (e->e_phoff + e->e_phnum * sizeof(*p) > 0x1000) {
Pexit(os, exe, 0, "ELF phdrs need to be in first page");
}
unsigned long a, b, i;
code = 0;
codesize = 0;
for (p = (struct ElfPhdr *)(page + e->e_phoff), i = e->e_phnum; i--;) {
for (i = e->e_phnum; i--;) {
if (p[i].p_type == PT_DYNAMIC) {
Pexit(os, exe, 0, "not a real executable");
Pexit(os, exe, 0, "not a static executable");
}
if (p[i].p_type != PT_LOAD) continue;
if ((p[i].p_vaddr | p[i].p_filesz | p[i].p_memsz | p[i].p_offset) & 0xfff) {
Pexit(os, exe, 0, "APE phdrs must be 4096-aligned and 4096-padded");
if (p[i].p_type != PT_LOAD) {
continue;
}
if (!p[i].p_memsz) {
continue;
}
if (p[i].p_vaddr & 4095) {
Pexit(os, exe, 0, "APE phdr addr must be 4096-aligned");
}
if (p[i].p_offset & 4095) {
Pexit(os, exe, 0, "APE phdr offset must be 4096-aligned");
}
prot = 0;
flags = MAP_FIXED | MAP_PRIVATE;
@ -552,16 +515,20 @@ __attribute__((__noreturn__)) static void Spawn(int os, const char *exe, int fd,
code = p[i].p_vaddr;
codesize = p[i].p_filesz;
}
if (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, os)) < 0) {
Pexit(os, exe, rc, "bss mmap()");
if (p[i].p_filesz) {
if ((rc = Mmap((void *)p[i].p_vaddr, p[i].p_filesz, prot, flags, fd,
p[i].p_offset, os)) < 0) {
Pexit(os, exe, rc, "image mmap");
}
if ((a = Min(-p[i].p_filesz & 4095, p[i].p_memsz - p[i].p_filesz))) {
MemSet((void *)(p[i].p_vaddr + p[i].p_filesz), 0, a);
}
}
if (p[i].p_filesz) {
if ((rc = Mmap(p[i].p_vaddr, p[i].p_filesz, prot, flags, fd,
p[i].p_offset, os)) < 0) {
Pexit(os, exe, rc, "image mmap()");
if ((b = Roundup(p[i].p_memsz, 4096)) >
(a = Roundup(p[i].p_filesz, 4096))) {
if ((rc = Mmap((void *)p[i].p_vaddr + a, b - a, prot,
flags | MAP_ANONYMOUS, -1, 0, os)) < 0) {
Pexit(os, exe, rc, "bss mmap");
}
}
}
@ -569,34 +536,15 @@ __attribute__((__noreturn__)) static void Spawn(int os, const char *exe, int fd,
Pexit(os, exe, 0, "ELF needs PT_LOAD phdr w/ PF_X");
}
#if SET_EXE_FILE
// change /proc/pid/exe to new executable path
if (IsLinux() && relocated) {
MunmapLinux((char *)0x200000, (long)(_end - ehdr));
PrctlLinux(PR_SET_MM, PR_SET_MM_EXE_FILE, fd, 0, 0);
}
#endif
Close(fd, 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
Msyscall(code, codesize, os);
// 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%%r8d,%%r8d\n\t"
"xor\t%%r9d,%%r9d\n\t"
"xor\t%%r10d,%%r10d\n\t"
"xor\t%%r11d,%%r11d\n\t"
@ -612,28 +560,36 @@ __attribute__((__noreturn__)) static void Spawn(int os, const char *exe, int fd,
"xor\t%%ebp,%%ebp\n\t"
"ret"
: /* no outputs */
: "D"(IsFreebsd() ? sp : 0), "S"(e->e_entry), "d"(sp), "c"(os),
"r"(r8)
: "D"(IsFreebsd() ? sp : 0), "S"(e->e_entry), "d"(sp), "c"(os)
: "memory");
__builtin_unreachable();
}
__attribute__((__noreturn__)) void ApeLoader(long di, long *sp, char dl,
struct ApeLoader *handoff) {
static void TryElf(struct ApeLoader *M, const char *exe, int fd, long *sp,
int os) {
unsigned size;
if (Read32(M->ehdr.buf) == Read32("\177ELF") && //
M->ehdr.ehdr.e_type == ET_EXEC && //
M->ehdr.ehdr.e_machine == EM_NEXGEN32E && //
M->ehdr.ehdr.e_ident[EI_CLASS] != ELFCLASS32 && //
M->ehdr.ehdr.e_phentsize >= sizeof(M->phdr.phdr) && //
(size = (unsigned)M->ehdr.ehdr.e_phnum * M->ehdr.ehdr.e_phentsize) <=
sizeof(M->phdr.buf) &&
Pread(fd, M->phdr.buf, size, M->ehdr.ehdr.e_phoff, os) == size) {
Spawn(os, exe, fd, sp, &M->ehdr.ehdr, &M->phdr.phdr);
}
}
__attribute__((__noreturn__)) void ApeLoader(long di, long *sp, char dl) {
int rc;
long *auxv;
struct ElfEhdr *eh;
int c, i, fd, os, argc;
char *p, *exe, *prog, **argv, **envp, *page;
static union {
struct ElfEhdr ehdr;
char p[0x1000];
} u;
unsigned i, n;
int c, fd, os, argc;
struct ApeLoader *M;
long *auxv, *ap, *ew;
char *p, *exe, *prog, **argv, **envp;
// detect freebsd
if (handoff) {
os = handoff->os;
} else if (SupportsXnu() && dl == XNU) {
if (SupportsXnu() && dl == XNU) {
os = XNU;
} else if (SupportsFreebsd() && di) {
os = FREEBSD;
@ -653,134 +609,109 @@ __attribute__((__noreturn__)) void ApeLoader(long di, long *sp, char dl,
}
}
// get syscall function pointer
if (handoff && handoff->systemcall) {
syscall_ = handoff->systemcall;
// detect openbsd
if (SupportsOpenbsd() && !os && !auxv[0]) {
os = OPENBSD;
}
// detect netbsd and find end of words
for (ap = auxv; ap[0]; ap += 2) {
if (SupportsNetbsd() && !os && ap[0] == AT_EXECFN_NETBSD) {
os = NETBSD;
}
}
ew = ap + 1;
// allocate loader memory
n = sizeof(*M) / sizeof(long);
MemMove(sp - n, sp, (char *)ew - (char *)sp);
sp -= n, argv -= n, envp -= n, auxv -= n;
M = (struct ApeLoader *)(ew - n);
// 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
// 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) {
Print(os, 2,
"usage: ape PROG [ARGV1,ARGV2,...]\n"
" ape - PROG [ARGV0,ARGV1,...]\n"
"αcτµαlly pδrταblε εxεcµταblε loader v1.1\n"
"copyright 2022 justine alexandra roberts tunney\n"
"https://justine.lol/ape.html\n",
0l);
Exit(1, os);
} else {
syscall_ = __syscall_loader;
prog = (char *)sp[2];
argc = sp[1] = sp[0] - 1;
argv = (char **)((sp += 1) + 1);
}
if (handoff) {
// we were called by ape_execve()
// no argument parsing is needed
// no path searching is needed
exe = handoff->prog;
fd = handoff->fd;
exe = handoff->prog;
page = handoff->page;
eh = (struct ElfEhdr *)handoff->page;
} else {
// detect openbsd
if (SupportsOpenbsd() && !os && !auxv[0]) {
os = OPENBSD;
}
// detect netbsd
if (SupportsNetbsd() && !os) {
for (; auxv[0]; auxv += 2) {
if (auxv[0] == AT_EXECFN_NETBSD) {
os = NETBSD;
break;
}
}
}
// default operating system
if (!os) {
os = LINUX;
}
#if SET_EXE_FILE
if (IsLinux() && !relocated) {
char *b1 = (char *)0x200000;
char *b2 = (char *)0x300000;
void (*pApeLoader)(long, long *, char, struct ApeLoader *);
Mmap((long)b2, (long)(_end - ehdr), PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0, os);
relocated = 1;
MemCpy(b2, b1, (long)(_end - ehdr));
pApeLoader = (void *)((char *)&ApeLoader - b1 + b2);
pApeLoader(di, sp, dl, handoff);
}
#endif
// 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;
eh = &u.ehdr;
// resolve path of executable and read its first page
if (!(exe = Commandv(&M->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 = Pread(fd, M->ehdr.buf, sizeof(M->ehdr.buf), 0, os)) < 0) {
Pexit(os, exe, rc, "read");
} else if (rc != sizeof(M->ehdr.buf)) {
Pexit(os, exe, 0, "too small");
}
#if TROUBLESHOOT
Emit(TROUBLESHOOT_OS, "os = ");
Emit(TROUBLESHOOT_OS, DescribeOs(os));
Emit(TROUBLESHOOT_OS, "\n");
for (i = 0; i < argc; ++i) {
Emit(TROUBLESHOOT_OS, "argv = ");
Emit(TROUBLESHOOT_OS, argv[i]);
Emit(TROUBLESHOOT_OS, "\n");
// change argv[0] to resolved path if it's ambiguous
if (argc > 0 && *prog != '/' && *exe == '/' && !StrCmp(prog, argv[0])) {
argv[0] = exe;
}
#endif
if ((IsXnu() && Read32(page) == 0xFEEDFACE + 1) ||
(!IsXnu() && Read32(page) == Read32("\177ELF"))) {
// ape intended behavior
// 1. if file is a native executable, try to run it natively
// 2. if ape, will scan shell script for elf printf statements
// 3. shell script may have multiple lines producing elf headers
// 4. all elf printf lines must exist in the first 4096 bytes of file
// 5. elf program headers may appear anywhere in the binary
if ((IsXnu() && Read32(M->ehdr.buf) == 0xFEEDFACE + 1) ||
(!IsXnu() && Read32(M->ehdr.buf) == 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++) != '\'';) {
if (c == '\\') {
if ('0' <= *p && *p <= '7') {
c = *p++ - '0';
if (Read64(M->ehdr.buf) == Read64("MZqFpD='") ||
Read64(M->ehdr.buf) == Read64("jartsr='")) {
for (p = M->ehdr.buf; p < M->ehdr.buf + sizeof(M->ehdr.buf); ++p) {
if (Read64(p) != Read64("printf '")) {
continue;
}
for (i = 0, p += 8;
p + 3 < M->ehdr.buf + sizeof(M->ehdr.buf) && (c = *p++) != '\'';) {
if (c == '\\') {
if ('0' <= *p && *p <= '7') {
c *= 8;
c += *p++ - '0';
c = *p++ - '0';
if ('0' <= *p && *p <= '7') {
c *= 8;
c += *p++ - '0';
if ('0' <= *p && *p <= '7') {
c *= 8;
c += *p++ - '0';
}
}
}
}
M->ehdr.buf[i++] = c;
}
if (i >= sizeof(M->ehdr.ehdr)) {
TryElf(M, exe, fd, sp, os);
}
page[i++] = c;
}
if (i >= 64 && Read32(page) == Read32("\177ELF")) {
Spawn(os, exe, fd, sp, page, eh);
}
}
Pexit(os, exe, 0, "could not find printf elf in first page");
TryElf(M, exe, fd, sp, os);
Pexit(os, exe, 0, "Not an acceptable APE/ELF executable for x86-64");
}

View file

@ -1,20 +0,0 @@
#ifndef COSMOPOLITAN_APE_LOADER_H_
#define COSMOPOLITAN_APE_LOADER_H_
#define APE_LOADER_BASE 0x200000
#define APE_LOADER_SIZE 0x200000
#define APE_LOADER_ENTRY 0x200400
#define APE_LOADER_BSS (4096 * 2)
#define APE_LOADER_STACK 0x7f0000000000
#define APE_BLOCK_BASE 0x7e0000000000
#define APE_BLOCK_SIZE 0x000200000000
struct ApeLoader {
int fd;
int os;
char *prog;
char *page;
void *systemcall;
};
#endif /* COSMOPOLITAN_APE_LOADER_H_ */

View file

@ -20,24 +20,20 @@ ENTRY(_start)
SECTIONS {
. = 0x200000;
.text : {
*(.text)
.rodata : {
KEEP(*(.head))
*(.rodata .rodata.*)
. = ALIGN(64);
}
filesz = . - ehdr;
textsz = . - _start;
.bss ALIGN(4096) : {
bss = .;
*(.bss)
. = ALIGN(4096);
}
_end = .;
memsz = _end - ehdr;
.text : {
text = .;
*(.text)
}
/DISCARD/ : {
*(.*)
}
}
bsssize = SIZEOF(.bss);
textoff = _start - ehdr;
textsz = SIZEOF(.text);
rosize = SIZEOF(.rodata);
filesz = SIZEOF(.rodata) + SIZEOF(.text);

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -91,7 +91,7 @@ VM = o/third_party/qemu/qemu-aarch64
HOSTS ?= pi silicon
else
ARCH = x86_64
HOSTS ?= freebsd openbsd netbsd rhel7 rhel5 xnu win10
HOSTS ?= freebsd openbsd openbsd73 netbsd rhel7 rhel5 xnu win10
endif
ifeq ($(PREFIX),)
@ -241,7 +241,10 @@ DEFAULT_LDFLAGS = \
-nostdlib \
--gc-sections \
--build-id=none \
--no-dynamic-linker #--cref -Map=$@.map
--no-dynamic-linker
# # generate linker report files
# DEFAULT_LDFLAGS += --cref -Map=$@.map
ifeq ($(ARCH), aarch64)
DEFAULT_LDFLAGS += \

View file

@ -123,6 +123,7 @@ endif
o/$(MODE)/libc/calls/execl.o \
o/$(MODE)/libc/calls/execle.o \
o/$(MODE)/libc/calls/execlp.o \
o/$(MODE)/libc/calls/execvpe.o \
o/$(MODE)/libc/calls/statfs.o \
o/$(MODE)/libc/calls/fstatfs.o \
o/$(MODE)/libc/calls/execve-sysv.o \

View file

@ -124,6 +124,6 @@ ssize_t copy_file_range(int infd, int64_t *opt_in_out_inoffset, int outfd,
END_CANCELLATION_POINT;
STRACE("copy_file_range(%d, %s, %d, %s, %'zu, %#x) → %'ld% m", infd,
DescribeInOutInt64(rc, opt_in_out_inoffset), outfd,
DescribeInOutInt64(rc, opt_in_out_outoffset), uptobytes, flags);
DescribeInOutInt64(rc, opt_in_out_outoffset), uptobytes, flags, rc);
return rc;
}

View file

@ -19,6 +19,7 @@
#include "libc/calls/calls.h"
#include "libc/mem/alloca.h"
#include "libc/runtime/runtime.h"
#include "libc/str/str.h"
#include "libc/sysv/errfuns.h"
/**
@ -40,10 +41,16 @@ int execlp(const char *prog, const char *arg, ... /*, NULL*/) {
char **argv;
va_list va, vb;
char pathbuf[PATH_MAX];
if (!(exe = commandv(prog, pathbuf, sizeof(pathbuf)))) return -1;
// resolve path of executable
if (!(exe = commandv(prog, pathbuf, sizeof(pathbuf)))) {
return -1;
}
// turn varargs into array
va_copy(vb, va);
va_start(va, arg);
for (i = 0; va_arg(va, const char *); ++i) donothing;
for (i = 0; va_arg(va, const char *); ++i) (void)0;
va_end(va);
argv = alloca((i + 2) * sizeof(char *));
va_start(vb, arg);
@ -52,5 +59,14 @@ int execlp(const char *prog, const char *arg, ... /*, NULL*/) {
if (!(argv[i] = va_arg(vb, const char *))) break;
}
va_end(vb);
// change argv[0] to resolved path if it's ambiguous
// otherwise the program won't have much luck finding itself
if (argv[0] && *prog != '/' && *exe == '/' && !strcmp(prog, argv[0])) {
argv[0] = exe;
}
// execute program
// tail call shouldn't be possible
return execv(exe, argv);
}

View file

@ -85,9 +85,9 @@ int sys_execve(const char *prog, char *const argv[], char *const envp[]) {
(CanExecute((ape = "/usr/bin/ape")) ||
CanExecute((ape = Join(firstnonnull(getenv("TMPDIR"),
firstnonnull(getenv("HOME"), ".")),
".ape", buf))) ||
CanExecute(
(ape = Join(firstnonnull(getenv("HOME"), "."), ".ape", buf))))) {
".ape-1.1", buf))) ||
CanExecute((ape = Join(firstnonnull(getenv("HOME"), "."), ".ape-1.1",
buf))))) {
shargs[0] = ape;
shargs[1] = "-";
shargs[2] = prog;

View file

@ -19,7 +19,9 @@
#include "libc/calls/calls.h"
#include "libc/dce.h"
#include "libc/intrin/asan.internal.h"
#include "libc/mem/alloca.h"
#include "libc/mem/mem.h"
#include "libc/str/str.h"
#include "libc/sysv/errfuns.h"
/**
@ -35,9 +37,32 @@
* @vforksafe
*/
int execvpe(const char *prog, char *const argv[], char *const *envp) {
char *exe;
size_t i;
char *exe, **argv2;
char pathbuf[PATH_MAX];
if (IsAsan() && !__asan_is_valid_str(prog)) return efault();
if (!(exe = commandv(prog, pathbuf, sizeof(pathbuf)))) return -1;
// validate memory
if (IsAsan() &&
(!__asan_is_valid_str(prog) || !__asan_is_valid_strlist(argv))) {
return efault();
}
// resolve path of executable
if (!(exe = commandv(prog, pathbuf, sizeof(pathbuf)))) {
return -1;
}
// change argv[0] to resolved path if it's ambiguous
// otherwise the program won't have much luck finding itself
if (argv[0] && *prog != '/' && *exe == '/' && !strcmp(prog, argv[0])) {
for (i = 0; argv[i++];) (void)0;
argv2 = alloca(i * sizeof(*argv));
memcpy(argv2, argv, i * sizeof(*argv));
argv2[0] = exe;
argv = argv2;
}
// execute program
// tail call shouldn't be possible
return execve(exe, argv, envp);
}

View file

@ -25,6 +25,7 @@
#include "libc/intrin/strace.internal.h"
#include "libc/intrin/weaken.h"
#include "libc/log/backtrace.internal.h"
#include "libc/sysv/errfuns.h"
#include "libc/zipos/zipos.internal.h"
/**
@ -79,12 +80,14 @@ int64_t lseek(int fd, int64_t offset, int whence) {
if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) {
rc = _weaken(__zipos_lseek)(
(struct ZiposHandle *)(intptr_t)g_fds.p[fd].handle, offset, whence);
} else if (!IsWindows() && !IsOpenbsd() && !IsNetbsd()) {
} else if (IsLinux() || IsXnu() || IsFreebsd() || IsOpenbsd()) {
rc = sys_lseek(fd, offset, whence, 0);
} else if (IsOpenbsd() || IsNetbsd()) {
} else if (IsNetbsd()) {
rc = sys_lseek(fd, offset, offset, whence);
} else {
} else if (IsWindows()) {
rc = sys_lseek_nt(fd, offset, whence);
} else {
rc = enosys();
}
STRACE("lseek(%d, %'ld, %s) → %'ld% m", fd, offset, DescribeWhence(whence),
rc);

View file

@ -22,6 +22,16 @@
#include "libc/runtime/internal.h"
.section .start,"ax",@progbits
#if SupportsXnu() && defined(__x86_64__)
// XNU AMD64 Entrypoint
//
// @note Rosetta barely implements MAC_LC_UNIXTHREAD
// @note Rosetta sets many registers to weird values
_apple: mov $_HOSTXNU,%cl
jmp 1f
.endfn _apple,weak,hidden
#endif
// System Five userspace program entrypoint.
//
// @param rsp is [n,argv₀..argvₙ₋₁,0,envp₀..,0,auxv₀..,0,..]
@ -32,18 +42,6 @@
_start:
#ifdef __x86_64__
#if SupportsXnu()
// Hack for detecting M1 Rosetta environment.
// https://github.com/jart/cosmopolitan/issues/429#issuecomment-1166704377
cmp $-1,%ebx
jne 0f
cmp $+1,%edx
jne 0f
mov $_HOSTXNU,%cl
xor %edi,%edi
0:
#endif
#if SupportsFreebsd()
// detect free besiyata dishmaya
test %rdi,%rdi
@ -54,7 +52,7 @@ _start:
#endif
// set operating system when already detected
mov %cl,__hostos(%rip)
1: mov %cl,__hostos(%rip)
// get startup timestamp as early as possible
// its used by --strace flag and kprintf() %T

View file

@ -1,2 +1,2 @@
#include "libc/sysv/macros.internal.h"
.scall __sys_mmap,0x0c50c51dd20c5009,222,197,globl,hidden
.scall __sys_mmap,0x0c50311dd20c5009,222,197,globl,hidden

View file

@ -1,2 +1,2 @@
#include "libc/sysv/macros.internal.h"
.scall sys_ftruncate,0x8c98c99e028c984d,46,201,globl,hidden
.scall sys_ftruncate,0x8c98a89e028c984d,46,201,globl,hidden

View file

@ -1,2 +1,2 @@
#include "libc/sysv/macros.internal.h"
.scall sys_lseek,0x0c70c71de20c7008,62,199,globl,hidden
.scall sys_lseek,0x0c70a61de20c7008,62,199,globl,hidden

View file

@ -1,2 +1,2 @@
#include "libc/sysv/macros.internal.h"
.scall sys_pread,0x8ad8ad9db2899811,67,153,globl,hidden
.scall sys_pread,0x8ad8a99db2899811,67,153,globl,hidden

View file

@ -1,2 +1,2 @@
#include "libc/sysv/macros.internal.h"
.scall sys_preadv,0x92190b9212a1c927,69,540,globl,hidden
.scall sys_preadv,0x9218ab9212a1c927,69,540,globl,hidden

View file

@ -1,2 +1,2 @@
#include "libc/sysv/macros.internal.h"
.scall sys_pwrite,0x8ae8ae9dc289a812,68,154,globl,hidden
.scall sys_pwrite,0x8ae8aa9dc289a812,68,154,globl,hidden

View file

@ -1,2 +1,2 @@
#include "libc/sysv/macros.internal.h"
.scall sys_pwritev,0x92290c9222a1d928,70,541,globl,hidden
.scall sys_pwritev,0x9228ac9222a1d928,70,541,globl,hidden

View file

@ -1,2 +1,2 @@
#include "libc/sysv/macros.internal.h"
.scall sys_truncate,0x8c88c89df28c884c,45,200,globl,hidden
.scall sys_truncate,0x8c88a79df28c884c,45,200,globl,hidden

View file

@ -1706,16 +1706,16 @@ syscon nr __NR_fstat 0x0005 0x0050 0x2000153 0x0153 0x0227 0x0035
syscon nr __NR_lstat 0x0006 0x0fff 0x2000154 0x0154 0x0028 0x0028 0x1b9 0xfff
syscon nr __NR_poll 0x0007 0x0fff 0x20000e6 0x00e6 0x00d1 0x00fc 0x0d1 0xfff
syscon nr __NR_ppoll 0x010f 0x0049 0xfff 0xfff 0x0221 0x006d 0xfff 0xfff
syscon nr __NR_lseek 0x0008 0x003e 0x20000c7 0x00c7 0x01de 0x00c7 0x0c7 0xfff
syscon nr __NR_mmap 0x0009 0x00de 0x20000c5 0x00c5 0x01dd 0x00c5 0x0c5 0xfff
syscon nr __NR_lseek 0x0008 0x003e 0x20000c7 0x00c7 0x01de 0x00a6 0x0c7 0xfff # OpenBSD 7.3+
syscon nr __NR_mmap 0x0009 0x00de 0x20000c5 0x00c5 0x01dd 0x0031 0x0c5 0xfff # OpenBSD 7.3+
syscon nr __NR_msync 0x001a 0x00e3 0x2000041 0x0041 0x0041 0x0100 0x115 0xfff
syscon nr __NR_mprotect 0x000a 0x00e2 0x200004a 0x004a 0x004a 0x004a 0x04a 0xfff
syscon nr __NR_munmap 0x000b 0x00d7 0x2000049 0x0049 0x0049 0x0049 0x049 0xfff
syscon nr __NR_sigaction 0x000d 0x0086 0x200002e 0x002e 0x01a0 0x002e 0x154 0xfff
syscon nr __NR_sigprocmask 0x000e 0x0087 0x2000149 0x0149 0x0154 0x0030 0x125 0xfff
syscon nr __NR_ioctl 0x0010 0x001d 0x2000036 0x0036 0x0036 0x0036 0x036 0xfff
syscon nr __NR_pread 0x0011 0x0043 0x2000099 0x0099 0x01db 0x00ad 0x0ad 0xfff
syscon nr __NR_pwrite 0x0012 0x0044 0x200009a 0x009a 0x01dc 0x00ae 0x0ae 0xfff
syscon nr __NR_pread 0x0011 0x0043 0x2000099 0x0099 0x01db 0x00a9 0x0ad 0xfff # OpenBSD 7.3+
syscon nr __NR_pwrite 0x0012 0x0044 0x200009a 0x009a 0x01dc 0x00aa 0x0ae 0xfff # OpenBSD 7.3+
syscon nr __NR_readv 0x0013 0x0041 0x2000078 0x0078 0x0078 0x0078 0x078 0xfff
syscon nr __NR_writev 0x0014 0x0042 0x2000079 0x0079 0x0079 0x0079 0x079 0xfff
syscon nr __NR_access 0x0015 0x0fff 0x2000021 0x0021 0x0021 0x0021 0x021 0xfff
@ -1779,8 +1779,8 @@ syscon nr __NR_fcntl 0x0048 0x0019 0x200005c 0x005c 0x005c 0x005c
syscon nr __NR_flock 0x0049 0x0020 0x2000083 0x0083 0x0083 0x0083 0x083 0xfff
syscon nr __NR_fsync 0x004a 0x0052 0x200005f 0x005f 0x005f 0x005f 0x05f 0xfff
syscon nr __NR_fdatasync 0x004b 0x0053 0x20000bb 0x00bb 0x0226 0x005f 0x0f1 0xfff
syscon nr __NR_truncate 0x004c 0x002d 0x20000c8 0x00c8 0x01df 0x00c8 0x0c8 0xfff
syscon nr __NR_ftruncate 0x004d 0x002e 0x20000c9 0x00c9 0x01e0 0x00c9 0x0c9 0xfff
syscon nr __NR_truncate 0x004c 0x002d 0x20000c8 0x00c8 0x01df 0x00a7 0x0c8 0xfff # OpenBSD 7.3+
syscon nr __NR_ftruncate 0x004d 0x002e 0x20000c9 0x00c9 0x01e0 0x00a8 0x0c9 0xfff # OpenBSD 7.3+
syscon nr __NR_getcwd 0x004f 0x0011 0xfff 0xfff 0x0146 0x0130 0x128 0xfff
syscon nr __NR_chdir 0x0050 0x0031 0x200000c 0x000c 0x000c 0x000c 0x00c 0xfff
syscon nr __NR_fchdir 0x0051 0x0032 0x200000d 0x000d 0x000d 0x000d 0x00d 0xfff
@ -1964,8 +1964,8 @@ syscon nr __NR_sync_file_range 0x0115 0x0054 0xfff 0xfff 0xfff 0xfff
syscon nr __NR_vmsplice 0x0116 0x004b 0xfff 0xfff 0xfff 0xfff 0xfff 0xfff
syscon nr __NR_migrate_pages 0x0100 0x00ee 0xfff 0xfff 0xfff 0xfff 0xfff 0xfff
syscon nr __NR_move_pages 0x0117 0x00ef 0xfff 0xfff 0xfff 0xfff 0xfff 0xfff
syscon nr __NR_preadv 0x0127 0x0045 0xfff 0xfff 0x0121 0x010b 0x121 0xfff
syscon nr __NR_pwritev 0x0128 0x0046 0xfff 0xfff 0x0122 0x010c 0x122 0xfff
syscon nr __NR_preadv 0x0127 0x0045 0xfff 0xfff 0x0121 0x00ab 0x121 0xfff # OpenBSD 7.3+
syscon nr __NR_pwritev 0x0128 0x0046 0xfff 0xfff 0x0122 0x00aa 0x122 0xfff # OpenBSD 7.3+
syscon nr __NR_utimensat 0x0118 0x0058 0xfff 0xfff 0x0223 0x0054 0x1d3 0xfff
syscon nr __NR_fallocate 0x011d 0x002f 0xfff 0xfff 0xfff 0xfff 0xfff 0xfff
syscon nr __NR_posix_fallocate 0xfff 0xfff 0xfff 0xfff 0x0212 0xfff 0x1df 0xfff

View file

@ -1,2 +1,2 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon nr,__NR_ftruncate,0x004d,0x002e,0x20000c9,0x00c9,0x01e0,0x00c9,0x0c9,0xfff
.syscon nr,__NR_ftruncate,0x004d,0x002e,0x20000c9,0x00c9,0x01e0,0x00a8,0x0c9,0xfff

View file

@ -1,2 +1,2 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon nr,__NR_lseek,0x0008,0x003e,0x20000c7,0x00c7,0x01de,0x00c7,0x0c7,0xfff
.syscon nr,__NR_lseek,0x0008,0x003e,0x20000c7,0x00c7,0x01de,0x00a6,0x0c7,0xfff

View file

@ -1,2 +1,2 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon nr,__NR_mmap,0x0009,0x00de,0x20000c5,0x00c5,0x01dd,0x00c5,0x0c5,0xfff
.syscon nr,__NR_mmap,0x0009,0x00de,0x20000c5,0x00c5,0x01dd,0x0031,0x0c5,0xfff

View file

@ -1,2 +1,2 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon nr,__NR_pread,0x0011,0x0043,0x2000099,0x0099,0x01db,0x00ad,0x0ad,0xfff
.syscon nr,__NR_pread,0x0011,0x0043,0x2000099,0x0099,0x01db,0x00a9,0x0ad,0xfff

View file

@ -1,2 +1,2 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon nr,__NR_preadv,0x0127,0x0045,0xfff,0xfff,0x0121,0x010b,0x121,0xfff
.syscon nr,__NR_preadv,0x0127,0x0045,0xfff,0xfff,0x0121,0x00ab,0x121,0xfff

View file

@ -1,2 +1,2 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon nr,__NR_pwrite,0x0012,0x0044,0x200009a,0x009a,0x01dc,0x00ae,0x0ae,0xfff
.syscon nr,__NR_pwrite,0x0012,0x0044,0x200009a,0x009a,0x01dc,0x00aa,0x0ae,0xfff

View file

@ -1,2 +1,2 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon nr,__NR_pwritev,0x0128,0x0046,0xfff,0xfff,0x0122,0x010c,0x122,0xfff
.syscon nr,__NR_pwritev,0x0128,0x0046,0xfff,0xfff,0x0122,0x00aa,0x122,0xfff

View file

@ -1,2 +1,2 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon nr,__NR_truncate,0x004c,0x002d,0x20000c8,0x00c8,0x01df,0x00c8,0x0c8,0xfff
.syscon nr,__NR_truncate,0x004c,0x002d,0x20000c8,0x00c8,0x01df,0x00a7,0x0c8,0xfff

View file

@ -50,6 +50,8 @@
ldr w9,[x9,#:lo12:__hostos]
tbz x9,#3,1f // !IsXnu()
mov x16,#\arm_xnu // apple ordinal
mov x9,#0 // clear carry flag
adds x9,x9,#0 // clear carry flag
svc #0 // issue system call
bcs 1f
b _sysret

View file

@ -42,8 +42,8 @@ scall __sys_fstat 0x1b80352272153005 0x050 globl hidden # needs __stat2linux()
scall __sys_lstat 0x1b90280282154006 0xfff globl hidden # needs __stat2linux(); blocked on Android
scall __sys_poll 0x8d18fc8d128e6807 0xfff globl hidden
scall sys_ppoll 0xfff86da21ffff90f 0x049 globl hidden # consider INTON/INTOFF tutorial in examples/unbourne.c
scall sys_lseek 0x0c70c71de20c7008 0x03e globl hidden # netbsd+openbsd:evilpad
scall __sys_mmap 0x0c50c51dd20c5009 0x0de globl hidden # netbsd+openbsd:pad
scall sys_lseek 0x0c70a61de20c7008 0x03e globl hidden # netbsd:evilpad, OpenBSD 7.3+
scall __sys_mmap 0x0c50311dd20c5009 0x0de globl hidden # netbsd:pad, OpenBSD 7.3+
scall sys_msync 0x915900841284181a 0x0e3 globl hidden
scall sys_mprotect 0x04a04a04a204a00a 0x0e2 globl hidden
scall __sys_munmap 0x049049049204900b 0x0d7 globl hidden
@ -51,8 +51,8 @@ scall sys_sigaction 0x15402e1a0202e00d 0x086 globl hidden # rt_sigaction on Lun
scall __sys_sigprocmask 0x125030154214900e 0x087 globl hidden # a.k.a. rt_sigprocmask, openbsd:byvalue, a.k.a. pthread_sigmask
scall sys_ioctl 0x0360360362036010 0x01d globl hidden
scall sys_ioctl_cp 0x8368368362836810 0x01d globl hidden # intended for TCSBRK
scall sys_pread 0x8ad8ad9db2899811 0x043 globl hidden # a.k.a. pread64; netbsd+openbsd:pad
scall sys_pwrite 0x8ae8ae9dc289a812 0x044 globl hidden # a.k.a. pwrite64; netbsd+openbsd:pad
scall sys_pread 0x8ad8a99db2899811 0x043 globl hidden # a.k.a. pread64; netbsd:pad, OpenBSD 7.3+
scall sys_pwrite 0x8ae8aa9dc289a812 0x044 globl hidden # a.k.a. pwrite64; netbsd:pad, OpenBSD 7.3+
scall sys_readv 0x8788788782878813 0x041 globl hidden
scall sys_writev 0x8798798792879814 0x042 globl hidden
scall __sys_pipe 0x02a10721e202a016 0x03b globl hidden # NOTE: pipe2() on FreeBSD and Linux Aarch64; XNU is pipe(void)→eax:edx
@ -118,8 +118,8 @@ scall __sys_fcntl_cp 0x85c85c85c285c848 0x019 globl hidden # intended for F_SET
scall sys_flock 0x8838838832883849 0x020 globl hidden
scall sys_fsync 0x85f85f85f285f84a 0x052 globl hidden
scall sys_fdatasync 0x8f185fa2628bb84b 0x053 globl hidden # fsync() on openbsd
scall sys_truncate 0x8c88c89df28c884c 0x02d globl hidden # netbsd+openbsd:pad
scall sys_ftruncate 0x8c98c99e028c984d 0x02e globl hidden # netbsd+openbsd:pad
scall sys_truncate 0x8c88a79df28c884c 0x02d globl hidden # netbsd:pad, OpenBSD 7.3+
scall sys_ftruncate 0x8c98a89e028c984d 0x02e globl hidden # netbsd:pad, OpenBSD 7.3+
scall sys_getcwd 0x128130146ffff04f 0x011 globl hidden
scall sys_chdir 0x00c00c00c200c050 0x031 globl hidden
scall sys_fchdir 0x00d00d00d200d051 0x032 globl hidden
@ -296,8 +296,8 @@ scall sys_vmsplice 0xfffffffffffff116 0x04b globl hidden
scall sys_migrate_pages 0xfffffffffffff100 0x0ee globl # no wrapper; numa numa yay
scall sys_move_pages 0xfffffffffffff117 0x0ef globl # no wrapper; NOTE: We view Red Hat versions as "epochs" for all distros.
#──────────────────────RHEL 5.0 LIMIT──────────────────────────────── # ←┬─ last distro with gplv2 licensed compiler c. 2007
scall sys_preadv 0x92190b9212a1c927 0x045 globl hidden # ├─ last distro with system v shell script init
scall sys_pwritev 0x92290c9222a1d928 0x046 globl hidden # ├─ rob landley unleashes busybox gpl lawsuits
scall sys_preadv 0x9218ab9212a1c927 0x045 globl hidden # ├─ last distro with system v shell script init
scall sys_pwritev 0x9228ac9222a1d928 0x046 globl hidden # ├─ rob landley unleashes busybox gpl lawsuits
scall __sys_utimensat 0x1d3054223ffff118 0x058 globl hidden # ├─ python modules need this due to pep513
scall sys_fallocate 0xfffffffffffff91d 0x02f globl # ├─ end of life 2020-11-30 (extended)
scall sys_posix_fallocate 0x9dffffa12fffffff 0xfff globl hidden # └─ cosmopolitan supports rhel5+
@ -774,7 +774,7 @@ scall sys_rtprio_thread 0xffffff1d2fffffff 0xfff globl # no wrapper
#scall getrtable 0xfff137ffffffffff 0xfff globl
#scall getthrid 0xfff12bffffffffff 0xfff globl
#scall kbind 0xfff056ffffffffff 0xfff globl
#scall mquery 0xfff11effffffffff 0xfff globl # openbsd:pad
#scall mquery 0xfff11effffffffff 0xfff globl # openbsd:pad (todo)
#scall obreak 0x011011ffffffffff 0xfff globl
#scall sendsyslog 0xfff070ffffffffff 0xfff globl
#scall setrtable 0xfff136ffffffffff 0xfff globl

View file

@ -16,6 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/calls/calls.h"
#include "libc/errno.h"
#include "libc/fmt/fmt.h"
@ -49,9 +50,9 @@ void testlib_showerror(const char *file, int line, const char *func,
"\t\t got %s\n"
"\t%s%s\n"
"\t%s%s\n",
RED2, UNBOLD, BLUE1, file, (long)line, RESET, method, func,
g_fixturename, hostname, getpid(), gettid(), code, v1, symbol, v2,
SUBTLE, strerror(errno), GetProgramExecutableName(), RESET);
RED2, UNBOLD, BLUE1, file, line, RESET, method, func, g_fixturename,
hostname, getpid(), gettid(), code, v1, symbol, v2, SUBTLE,
strerror(errno), GetProgramExecutableName(), RESET);
free(v1);
free(v2);
}

View file

@ -16,6 +16,8 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "ape/sections.internal.h"
#include "libc/assert.h"
#include "libc/calls/calls.h"
#include "libc/calls/struct/rlimit.h"
#include "libc/calls/struct/sigaction.h"
@ -49,6 +51,7 @@
#include "libc/sysv/consts/f.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/poll.h"
#include "libc/sysv/consts/prot.h"
#include "libc/sysv/consts/rlimit.h"
#include "libc/sysv/consts/sig.h"
#include "libc/testlib/testlib.h"

View file

@ -18,6 +18,7 @@
*/
#include "libc/assert.h"
#include "libc/calls/calls.h"
#include "libc/calls/cp.internal.h"
#include "libc/calls/internal.h"
#include "libc/calls/struct/iovec.h"
#include "libc/calls/struct/iovec.internal.h"
@ -100,8 +101,10 @@ BENCH(write, bench) {
ASSERT_SYS(0, 3, open("/dev/null", O_WRONLY));
EZBENCH2("write", donothing, write(3, "hello", 5));
EZBENCH2("writev", donothing, writev(3, &(struct iovec){"hello", 5}, 1));
BEGIN_CANCELLATION_POINT;
EZBENCH2("sys_write", donothing, sys_write(3, "hello", 5));
EZBENCH2("sys_writev", donothing,
sys_writev(3, &(struct iovec){"hello", 5}, 1));
END_CANCELLATION_POINT;
ASSERT_SYS(0, 0, close(3));
}

View file

@ -35,8 +35,11 @@ o/$(MODE)/test/libc/release/smoke.o: \
-Os \
-fno-pie \
-nostdinc \
-Wl,--gc-sections \
-fno-omit-frame-pointer \
-include o/cosmopolitan.h \
-Wl,-z,max-page-size=0x1000 \
-Wl,-z,common-page-size=0x1000 \
$<
o/$(MODE)/test/libc/release/smoke.com.dbg: \
@ -49,6 +52,9 @@ o/$(MODE)/test/libc/release/smoke.com.dbg: \
-static \
-no-pie \
-nostdlib \
--gc-sections \
-z max-page-size=0x1000 \
-z common-page-size=0x1000 \
-T o/$(MODE)/ape/public/ape.lds \
o/$(MODE)/test/libc/release/smoke.o \
o/$(MODE)/libc/crt/crt.o \
@ -66,6 +72,9 @@ o/$(MODE)/test/libc/release/smoke-nms.com.dbg: \
-static \
-no-pie \
-nostdlib \
--gc-sections \
-z max-page-size=0x1000 \
-z common-page-size=0x1000 \
-T o/$(MODE)/ape/public/ape.lds \
o/$(MODE)/test/libc/release/smoke.o \
o/$(MODE)/libc/crt/crt.o \
@ -84,6 +93,9 @@ o/$(MODE)/test/libc/release/smoke-chibicc.com.dbg: \
-static \
-no-pie \
-nostdlib \
--gc-sections \
-z max-page-size=0x1000 \
-z common-page-size=0x1000 \
-T o/$(MODE)/ape/public/ape.lds \
o/$(MODE)/test/libc/release/smoke-chibicc.o \
o/$(MODE)/libc/crt/crt.o \
@ -121,6 +133,9 @@ o/$(MODE)/test/libc/release/smokecxx.com.dbg: \
-static \
-no-pie \
-nostdlib \
--gc-sections \
-z max-page-size=0x1000 \
-z common-page-size=0x1000 \
-T o/$(MODE)/ape/public/ape.lds \
o/$(MODE)/test/libc/release/smokecxx.o \
o/$(MODE)/libc/crt/crt.o \
@ -138,7 +153,10 @@ o/$(MODE)/test/libc/release/smokecxx.o: \
-Os \
-fno-pie \
-nostdinc \
-Wl,--gc-sections \
-fno-omit-frame-pointer \
-z max-page-size=0x1000 \
-z common-page-size=0x1000 \
-include o/cosmopolitan.h \
test/libc/release/smokecxx.cc
@ -152,6 +170,9 @@ o/$(MODE)/test/libc/release/smokeansi.com.dbg: \
-static \
-no-pie \
-nostdlib \
--gc-sections \
-z max-page-size=0x1000 \
-z common-page-size=0x1000 \
-T o/$(MODE)/ape/public/ape.lds \
o/$(MODE)/test/libc/release/smokeansi.o \
o/$(MODE)/libc/crt/crt.o \
@ -170,8 +191,11 @@ o/$(MODE)/test/libc/release/smokeansi.o: \
-static \
-fno-pie \
-nostdinc \
-Wl,--gc-sections \
-fno-omit-frame-pointer \
-include o/cosmopolitan.h \
-Wl,-z,max-page-size=0x1000 \
-Wl,-z,common-page-size=0x1000 \
test/libc/release/smoke.c
# TODO(jart): Rewrite these shell scripts as C code.

View file

@ -45,7 +45,10 @@
'("__builtin_va_list"))
(gcc-builtin-functions
'("__builtin_add_overflow"
'("__builtin_va_start"
"__builtin_va_arg"
"__builtin_va_end"
"__builtin_add_overflow"
"__builtin_add_overflow_p"
"__builtin_alloc"
"__builtin_alloca"