diff --git a/examples/setcontext.c b/examples/setcontext.c new file mode 100644 index 000000000..07afabe30 --- /dev/null +++ b/examples/setcontext.c @@ -0,0 +1,79 @@ +#if 0 +/*─────────────────────────────────────────────────────────────────╗ +│ To the extent possible under law, Justine Tunney has waived │ +│ all copyright and related or neighboring rights to this file, │ +│ as it is written in the following disclaimers: │ +│ • http://unlicense.org/ │ +│ • http://creativecommons.org/publicdomain/zero/1.0/ │ +╚─────────────────────────────────────────────────────────────────*/ +#endif +#include "libc/calls/calls.h" +#include "libc/calls/ucontext.h" +#include "libc/runtime/runtime.h" +#include "libc/stdio/stdio.h" +#include "libc/str/str.h" +#include "libc/sysv/consts/exit.h" + +/** + * @fileoverview swapcontext() and makecontext() example + * @note adapted from the Linux man-pages project + */ + +static ucontext_t uctx_main; +static ucontext_t uctx_func1; +static ucontext_t uctx_func2; + +#define say(s) write(1, s, strlen(s)) +#define handle_error(msg) \ + do { \ + perror(msg); \ + exit(EXIT_FAILURE); \ + } while (0) + +static void func1(void) { + say("func1: started\n"); + say("func1: swapcontext(&uctx_func1, &uctx_func2)\n"); + if (swapcontext(&uctx_func1, &uctx_func2) == -1) { + handle_error("swapcontext"); + } + say("func1: returning\n"); +} + +static void func2(void) { + say("func2: started\n"); + say("func2: swapcontext(&uctx_func2, &uctx_func1)\n"); + if (swapcontext(&uctx_func2, &uctx_func1) == -1) { + handle_error("swapcontext"); + } + say("func2: returning\n"); +} + +int main(int argc, char *argv[]) { + char func1_stack[8192]; + char func2_stack[8192]; + + if (getcontext(&uctx_func1) == -1) { + handle_error("getcontext"); + } + uctx_func1.uc_stack.ss_sp = func1_stack; + uctx_func1.uc_stack.ss_size = sizeof(func1_stack); + uctx_func1.uc_link = &uctx_main; + makecontext(&uctx_func1, func1, 0); + + if (getcontext(&uctx_func2) == -1) { + handle_error("getcontext"); + } + uctx_func2.uc_stack.ss_sp = func2_stack; + uctx_func2.uc_stack.ss_size = sizeof(func2_stack); + /* Successor context is f1(), unless argc > 1 */ + uctx_func2.uc_link = (argc > 1) ? NULL : &uctx_func1; + makecontext(&uctx_func2, func2, 0); + + say("main: swapcontext(&uctx_main, &uctx_func2)\n"); + if (swapcontext(&uctx_main, &uctx_func2) == -1) { + handle_error("swapcontext"); + } + + say("main: exiting\n"); + exit(EXIT_SUCCESS); +} diff --git a/libc/calls/calls.mk b/libc/calls/calls.mk index 8ab4e6e9a..655261f8c 100644 --- a/libc/calls/calls.mk +++ b/libc/calls/calls.mk @@ -214,7 +214,9 @@ o/$(MODE)/libc/calls/pledge-linux.o: private \ # these assembly files are safe to build on aarch64 o/$(MODE)/libc/calls/getcontext.o: libc/calls/getcontext.S @$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $< -o/$(MODE)/libc/calls/setcontext2.o: libc/calls/setcontext2.S +o/$(MODE)/libc/calls/swapcontext.o: libc/calls/swapcontext.S + @$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $< +o/$(MODE)/libc/calls/tailcontext.o: libc/calls/tailcontext.S @$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $< LIBC_CALLS_LIBS = $(foreach x,$(LIBC_CALLS_ARTIFACTS),$($(x))) diff --git a/libc/calls/getcontext.S b/libc/calls/getcontext.S index de9364eb9..b1ab2850e 100644 --- a/libc/calls/getcontext.S +++ b/libc/calls/getcontext.S @@ -20,132 +20,14 @@ // Gets machine state. // +// @return 0 on success, or -1 w/ errno +// @see makecontext() +// @see swapcontext() // @see setcontext() +// @threadsafe + .ftrace1 getcontext: -#ifdef __x86_64__ - - pushf - pop 176(%rdi) - movaps %xmm0,480(%rdi) - movaps %xmm1,496(%rdi) - movaps %xmm2,512(%rdi) - movaps %xmm3,528(%rdi) - movaps %xmm4,544(%rdi) - movaps %xmm5,560(%rdi) - movaps %xmm6,576(%rdi) - movaps %xmm7,592(%rdi) - movaps %xmm8,608(%rdi) - movaps %xmm9,624(%rdi) - movaps %xmm10,640(%rdi) - movaps %xmm11,656(%rdi) - movaps %xmm12,672(%rdi) - movaps %xmm13,688(%rdi) - movaps %xmm14,704(%rdi) - movaps %xmm15,720(%rdi) - mov %r8,40(%rdi) - mov %r9,48(%rdi) - mov %r10,56(%rdi) - mov %r11,64(%rdi) - mov %r12,72(%rdi) - mov %r13,80(%rdi) - mov %r14,88(%rdi) - mov %r15,96(%rdi) - mov %rdi,104(%rdi) - mov %rsi,112(%rdi) - mov %rbp,120(%rdi) - mov %rbx,128(%rdi) - mov %rdx,136(%rdi) - mov %rax,144(%rdi) - mov %rcx,152(%rdi) - lea 320(%rdi),%rax - mov %rax,224(%rdi) - lea 8(%rsp),%rax - mov %rax,160(%rdi) - mov (%rsp),%rax - mov %rax,168(%rdi) - jmp __getcontext - -#elif defined(__aarch64__) - - stp x0,x1,[x0,184] - stp x2,x3,[x0,200] - stp x4,x5,[x0,216] - stp x6,x7,[x0,232] - stp x8,x9,[x0,248] - stp x10,x11,[x0,264] - stp x12,x13,[x0,280] - stp x14,x15,[x0,296] - stp x16,x17,[x0,312] - stp x18,x19,[x0,328] - stp x20,x21,[x0,344] - stp x22,x23,[x0,360] - stp x24,x25,[x0,376] - stp x26,x27,[x0,392] - stp x28,x29,[x0,408] - str x30,[x0,424] - mov x1,sp - str x1,[x0,432] // sp = caller's sp - str x30,[x0,440] // pc = caller's pc - b __getcontext - -#else -#error "unsupported architecture" -#endif + .ftrace2 +#include "libc/calls/getcontext.inc" + jmp __getcontextsig .endfn getcontext,globl - - .end -//////////////////////////////////////////////////////////////////////////////// - -noasan noubsan dontinstrument int getcontext(ucontext_t *uc) { - asm volatile("movaps\t%%xmm0,%0" : /* no outputs */ : "m"(uc->__fpustate.xmm[0])); - asm volatile("movaps\t%%xmm1,%0" : /* no outputs */ : "m"(uc->__fpustate.xmm[1])); - asm volatile("movaps\t%%xmm2,%0" : /* no outputs */ : "m"(uc->__fpustate.xmm[2])); - asm volatile("movaps\t%%xmm3,%0" : /* no outputs */ : "m"(uc->__fpustate.xmm[3])); - asm volatile("movaps\t%%xmm4,%0" : /* no outputs */ : "m"(uc->__fpustate.xmm[4])); - asm volatile("movaps\t%%xmm5,%0" : /* no outputs */ : "m"(uc->__fpustate.xmm[5])); - asm volatile("movaps\t%%xmm6,%0" : /* no outputs */ : "m"(uc->__fpustate.xmm[6])); - asm volatile("movaps\t%%xmm7,%0" : /* no outputs */ : "m"(uc->__fpustate.xmm[7])); - asm volatile("movaps\t%%xmm8,%0" : /* no outputs */ : "m"(uc->__fpustate.xmm[8])); - asm volatile("movaps\t%%xmm9,%0" : /* no outputs */ : "m"(uc->__fpustate.xmm[9])); - asm volatile("movaps\t%%xmm10,%0" : /* no outputs */ : "m"(uc->__fpustate.xmm[10])); - asm volatile("movaps\t%%xmm11,%0" : /* no outputs */ : "m"(uc->__fpustate.xmm[11])); - asm volatile("movaps\t%%xmm12,%0" : /* no outputs */ : "m"(uc->__fpustate.xmm[12])); - asm volatile("movaps\t%%xmm13,%0" : /* no outputs */ : "m"(uc->__fpustate.xmm[13])); - asm volatile("movaps\t%%xmm14,%0" : /* no outputs */ : "m"(uc->__fpustate.xmm[14])); - asm volatile("movaps\t%%xmm15,%0" : /* no outputs */ : "m"(uc->__fpustate.xmm[15])); - asm volatile("mov\t%%r8,%0" : "=m"(uc->uc_mcontext.r8)); - asm volatile("mov\t%%r9,%0" : "=m"(uc->uc_mcontext.r9)); - asm volatile("mov\t%%r10,%0" : "=m"(uc->uc_mcontext.r10)); - asm volatile("mov\t%%r11,%0" : "=m"(uc->uc_mcontext.r11)); - asm volatile("mov\t%%r12,%0" : "=m"(uc->uc_mcontext.r12)); - asm volatile("mov\t%%r13,%0" : "=m"(uc->uc_mcontext.r13)); - asm volatile("mov\t%%r14,%0" : "=m"(uc->uc_mcontext.r14)); - asm volatile("mov\t%%r15,%0" : "=m"(uc->uc_mcontext.r15)); - asm volatile("mov\t%%rdi,%0" : "=m"(uc->uc_mcontext.rdi)); - asm volatile("mov\t%%rsi,%0" : "=m"(uc->uc_mcontext.rsi)); - asm volatile("mov\t%%rbp,%0" : "=m"(uc->uc_mcontext.rbp)); - asm volatile("mov\t%%rbx,%0" : "=m"(uc->uc_mcontext.rbx)); - asm volatile("mov\t%%rdx,%0" : "=m"(uc->uc_mcontext.rdx)); - asm volatile("mov\t%%rax,%0" : "=m"(uc->uc_mcontext.rax)); - asm volatile("mov\t%%rcx,%0" : "=m"(uc->uc_mcontext.rcx)); - uc->uc_mcontext.fpregs = &uc->__fpustate; - asm volatile("lea\t8(%%rsp),%%rax\n\t" - "mov\t%%rax,%0" - : "=m"(uc->uc_mcontext.rsp) - : /* no inputs */ - : "rax"); - asm volatile("mov\t(%%rsp),%%rax\n\t" - "mov\t%%rax,%0" - : "=m"(uc->uc_mcontext.rip) - : /* no inputs */ - : "rax"); - return 0; -} - -noasan noubsan dontinstrument int getcontext(ucontext_t *uc) { - asm volatile("stp\tx0,x1,%0" : "=m"(uc->uc_mcontext.regs[0])); - asm volatile("stp\tx2,x3,%0" : "=m"(uc->uc_mcontext.regs[2])); - asm volatile("mov\tx1,sp\n\tstr\tx1,%0" : "=m"(uc->uc_mcontext.sp)); - asm volatile("str\tx30,%0" : "=m"(uc->uc_mcontext.pc)); - return 0; -} diff --git a/libc/calls/getcontext.inc b/libc/calls/getcontext.inc new file mode 100644 index 000000000..39a6d5ad6 --- /dev/null +++ b/libc/calls/getcontext.inc @@ -0,0 +1,89 @@ +/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ +│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 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. │ +╚─────────────────────────────────────────────────────────────────────────────*/ + +// @fileoverview textual include for loading processor state + +#ifdef __x86_64__ + + mov %r8,40(%rdi) + mov %r9,48(%rdi) + mov %r10,56(%rdi) + mov %r11,64(%rdi) + mov %r12,72(%rdi) + mov %r13,80(%rdi) + mov %r14,88(%rdi) + mov %r15,96(%rdi) + mov %rdi,104(%rdi) + mov %rsi,112(%rdi) + mov %rbp,120(%rdi) + mov %rbx,128(%rdi) + mov %rdx,136(%rdi) + mov %rcx,152(%rdi) + lea 8(%rsp),%rax + mov %rax,160(%rdi) // rsp = caller's rsp + mov (%rsp),%rax + mov %rax,168(%rdi) // rip = return address + + lea 608(%rdi),%rax + movaps %xmm0,-0x80(%rax) + movaps %xmm1,-0x70(%rax) + movaps %xmm2,-0x60(%rax) + movaps %xmm3,-0x50(%rax) + movaps %xmm4,-0x40(%rax) + movaps %xmm5,-0x30(%rax) + movaps %xmm6,-0x20(%rax) + movaps %xmm7,-0x10(%rax) + movaps %xmm8,0x00(%rax) + movaps %xmm9,0x10(%rax) + movaps %xmm10,0x20(%rax) + movaps %xmm11,0x30(%rax) + movaps %xmm12,0x40(%rax) + movaps %xmm13,0x50(%rax) + movaps %xmm14,0x60(%rax) + movaps %xmm15,0x70(%rax) + lea 320(%rdi),%rax // rax = &__fpustate + mov %rax,224(%rdi) // fpregs = rax + +#elif defined(__aarch64__) +#define REGS(i) 184+i*8 + + stp xzr,x1,[x0,REGS(0)] // x0 = 0 + stp x2,x3,[x0,REGS(2)] + stp x4,x5,[x0,REGS(4)] + stp x6,x7,[x0,REGS(6)] + stp x8,x9,[x0,REGS(8)] + stp x10,x11,[x0,REGS(10)] + stp x12,x13,[x0,REGS(12)] + stp x14,x15,[x0,REGS(14)] + stp x16,x17,[x0,REGS(16)] + stp x18,x19,[x0,REGS(18)] + stp x20,x21,[x0,REGS(20)] + stp x22,x23,[x0,REGS(22)] + stp x24,x25,[x0,REGS(24)] + stp x26,x27,[x0,REGS(26)] + stp x28,x29,[x0,REGS(28)] + str x30,[x0,REGS(30)] + mov x15,sp + stp x15,x30,[x0,REGS(31)] // sp, pc = caller sp, ret address + str xzr,[x0,448] // pstate = 0 + str xzr,[x0,456] // no vectors yet + +#else +#error "unsupported architecture" +#endif diff --git a/libc/calls/setcontext2.S b/libc/calls/setcontext2.S deleted file mode 100644 index 6f03452b1..000000000 --- a/libc/calls/setcontext2.S +++ /dev/null @@ -1,136 +0,0 @@ -/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ -│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 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/macros.internal.h" - -// tailed called by setcontext() implementation -__setcontext: -#ifdef __x86_64__ - - mov 224(%rdi),%rax - test %rax,%rax - je 1f - movaps 160(%rax),%xmm0 - movaps 176(%rax),%xmm1 - movaps 192(%rax),%xmm2 - movaps 208(%rax),%xmm3 - movaps 224(%rax),%xmm4 - movaps 240(%rax),%xmm5 - movaps 256(%rax),%xmm6 - movaps 272(%rax),%xmm7 - movaps 288(%rax),%xmm8 - movaps 304(%rax),%xmm9 - movaps 320(%rax),%xmm10 - movaps 336(%rax),%xmm11 - movaps 352(%rax),%xmm12 - movaps 368(%rax),%xmm13 - movaps 384(%rax),%xmm14 - movaps 400(%rax),%xmm15 -1: push 176(%rdi) - popf - mov %rsi,%rsp - mov 40(%rdi),%r8 - mov 48(%rdi),%r9 - mov 56(%rdi),%r10 - mov 64(%rdi),%r11 - mov 72(%rdi),%r12 - mov 80(%rdi),%r13 - mov 88(%rdi),%r14 - mov 96(%rdi),%r15 - mov 112(%rdi),%rsi - mov 120(%rdi),%rbp - mov 128(%rdi),%rbx - mov 136(%rdi),%rdx - mov 144(%rdi),%rax - mov 152(%rdi),%rcx - xor %rax,%rax - push 168(%rdi) - mov 104(%rdi),%rdi - ret - -#elif defined(__aarch64__) - - mov sp,x1 // sp = second argument - ldr x30,[x0,440] // return address <- pc - ldp x28,x29,[x0,408] // x0 and x30 discarded - ldp x26,x27,[x0,392] - ldp x24,x25,[x0,376] - ldp x22,x23,[x0,360] - ldp x20,x21,[x0,344] - ldp x18,x19,[x0,328] - ldp x16,x17,[x0,312] - ldp x14,x15,[x0,296] - ldp x12,x13,[x0,280] - ldp x10,x11,[x0,264] - ldp x8,x9,[x0,248] - ldp x6,x7,[x0,232] - ldp x4,x5,[x0,216] - ldp x2,x3,[x0,200] - ldr x1,[x0,192] - mov w0,0 - ret - -#else -#error "unsupported architecture" -#endif - .endfn __setcontext,globl - - .end -//////////////////////////////////////////////////////////////////////////////// -noasan noubsan int __setcontext(const ucontext_t *uc) { - if (uc->uc_mcontext.fpregs) { - asm volatile("movaps\t%0,%%xmm0" : /* no outputs */ : "m"(uc->uc_mcontext.fpregs->xmm[0])); - asm volatile("movaps\t%0,%%xmm1" : /* no outputs */ : "m"(uc->uc_mcontext.fpregs->xmm[1])); - asm volatile("movaps\t%0,%%xmm2" : /* no outputs */ : "m"(uc->uc_mcontext.fpregs->xmm[2])); - asm volatile("movaps\t%0,%%xmm3" : /* no outputs */ : "m"(uc->uc_mcontext.fpregs->xmm[3])); - asm volatile("movaps\t%0,%%xmm4" : /* no outputs */ : "m"(uc->uc_mcontext.fpregs->xmm[4])); - asm volatile("movaps\t%0,%%xmm5" : /* no outputs */ : "m"(uc->uc_mcontext.fpregs->xmm[5])); - asm volatile("movaps\t%0,%%xmm6" : /* no outputs */ : "m"(uc->uc_mcontext.fpregs->xmm[6])); - asm volatile("movaps\t%0,%%xmm7" : /* no outputs */ : "m"(uc->uc_mcontext.fpregs->xmm[7])); - asm volatile("movaps\t%0,%%xmm8" : /* no outputs */ : "m"(uc->uc_mcontext.fpregs->xmm[8])); - asm volatile("movaps\t%0,%%xmm9" : /* no outputs */ : "m"(uc->uc_mcontext.fpregs->xmm[9])); - asm volatile("movaps\t%0,%%xmm10" : /* no outputs */ : "m"(uc->uc_mcontext.fpregs->xmm[10])); - asm volatile("movaps\t%0,%%xmm11" : /* no outputs */ : "m"(uc->uc_mcontext.fpregs->xmm[11])); - asm volatile("movaps\t%0,%%xmm12" : /* no outputs */ : "m"(uc->uc_mcontext.fpregs->xmm[12])); - asm volatile("movaps\t%0,%%xmm13" : /* no outputs */ : "m"(uc->uc_mcontext.fpregs->xmm[13])); - asm volatile("movaps\t%0,%%xmm14" : /* no outputs */ : "m"(uc->uc_mcontext.fpregs->xmm[14])); - asm volatile("movaps\t%0,%%xmm15" : /* no outputs */ : "m"(uc->uc_mcontext.fpregs->xmm[15])); - } - asm volatile("mov\t%0,%%r8" : /* no outputs */ : "m"(uc->uc_mcontext.r8)); - asm volatile("mov\t%0,%%r9" : /* no outputs */ : "m"(uc->uc_mcontext.r9)); - asm volatile("mov\t%0,%%r10" : /* no outputs */ : "m"(uc->uc_mcontext.r10)); - asm volatile("mov\t%0,%%r11" : /* no outputs */ : "m"(uc->uc_mcontext.r11)); - asm volatile("mov\t%0,%%r12" : /* no outputs */ : "m"(uc->uc_mcontext.r12)); - asm volatile("mov\t%0,%%r13" : /* no outputs */ : "m"(uc->uc_mcontext.r13)); - asm volatile("mov\t%0,%%r14" : /* no outputs */ : "m"(uc->uc_mcontext.r14)); - asm volatile("mov\t%0,%%r15" : /* no outputs */ : "m"(uc->uc_mcontext.r15)); - asm volatile("mov\t%0,%%rsi" : /* no outputs */ : "m"(uc->uc_mcontext.rsi)); - asm volatile("mov\t%0,%%rbp" : /* no outputs */ : "m"(uc->uc_mcontext.rbp)); - asm volatile("mov\t%0,%%rbx" : /* no outputs */ : "m"(uc->uc_mcontext.rbx)); - asm volatile("mov\t%0,%%rdx" : /* no outputs */ : "m"(uc->uc_mcontext.rdx)); - asm volatile("mov\t%0,%%rax" : /* no outputs */ : "m"(uc->uc_mcontext.rax)); - asm volatile("mov\t%0,%%rcx" : /* no outputs */ : "m"(uc->uc_mcontext.rcx)); - asm volatile("mov\t%0,%%rsp" : /* no outputs */ : "m"(uc->uc_mcontext.rsp)); - asm volatile("xor\t%%rax,%%rax\n\t" - "push\t%0\n\t" - "mov\t%1,%%rdi\n\t" - "ret" - : /* no outputs */ - : "m"(uc->uc_mcontext.rip), "m"(uc->uc_mcontext.rdi)); - __builtin_unreachable(); -} diff --git a/libc/calls/struct/ucontext.internal.h b/libc/calls/struct/ucontext.internal.h index d2348fe54..35e189f96 100644 --- a/libc/calls/struct/ucontext.internal.h +++ b/libc/calls/struct/ucontext.internal.h @@ -6,9 +6,25 @@ COSMOPOLITAN_C_START_ #ifdef __x86_64__ -#define SP rsp +#define PC rip +#define SP rsp +#define BP rbp +#define ARG0 rdi +#define ARG1 rsi +#define ARG2 rdx +#define ARG3 rcx +#define ARG4 r8 +#define ARG5 r9 #elif defined(__aarch64__) -#define SP sp +#define PC pc +#define SP sp +#define BP regs[29] +#define ARG0 regs[0] +#define ARG1 regs[1] +#define ARG2 regs[2] +#define ARG3 regs[3] +#define ARG4 regs[4] +#define ARG5 regs[5] #else #error "unsupported architecture" #endif diff --git a/libc/calls/getcontext2.c b/libc/calls/swapcontext.S similarity index 63% rename from libc/calls/getcontext2.c rename to libc/calls/swapcontext.S index 16e9ca6ff..3cc9e81cd 100644 --- a/libc/calls/getcontext2.c +++ b/libc/calls/swapcontext.S @@ -1,7 +1,7 @@ -/*-*- 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│ +/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ +│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ ╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2023 Justine Alexandra Roberts Tunney │ +│ 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 │ @@ -16,19 +16,45 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/struct/sigset.h" -#include "libc/calls/struct/ucontext.internal.h" -#include "libc/calls/ucontext.h" -#include "libc/runtime/stack.h" -#include "libc/sysv/consts/sig.h" -#include "libc/sysv/consts/ss.h" +#include "libc/macros.internal.h" -// this is tail called by the getcontext() implementation -int __getcontext(ucontext_t *uc) { - uc->uc_flags = 0; - uc->uc_link = 0; - uc->uc_stack.ss_sp = (void *)uc->uc_mcontext.SP; - uc->uc_stack.ss_flags = SS_ONSTACK; - uc->uc_stack.ss_size = uc->uc_mcontext.SP - GetStackAddr(); - return sigprocmask(SIG_SETMASK, 0, &uc->uc_sigmask); -} +// Saves machine to 𝑥 and activates 𝑦, i.e. +// +// getcontext(x); +// setcontext(y); +// +// Except using this API is safer and goes 2x faster: +// +// swapcontext(x, y); +// +// @return 0 on success, or -1 w/ errno +// @returnstwice +// @threadsafe + .ftrace1 +swapcontext: + .ftrace2 +#include "libc/calls/getcontext.inc" +#ifdef __x86_64__ + push %rbp + mov %rsp,%rbp + push %rsi + push %rsi + call __swapcontextsig + pop %rdi + pop %rdi + pop %rbp + test %eax,%eax + jnz 1f +#elif defined(__aarch64__) + stp x29,x30,[sp,#-32]! + mov x29,sp + str x1,[sp,16] + bl __swapcontextsig + ldr x1,[sp,16] + ldp x29,x30,[sp],#32 + cbnz w0,1f + mov x0,x1 +#endif + jmp __tailcontext +1: ret + .endfn swapcontext,globl diff --git a/libc/calls/tailcontext.S b/libc/calls/tailcontext.S new file mode 100644 index 000000000..01dcba312 --- /dev/null +++ b/libc/calls/tailcontext.S @@ -0,0 +1,89 @@ +/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ +│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 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/macros.internal.h" + +// tailed called by setcontext() implementation +__tailcontext: +#ifdef __x86_64__ + + lea 608(%rdi),%rax + movaps -0x80(%rax),%xmm0 + movaps -0x70(%rax),%xmm1 + movaps -0x60(%rax),%xmm2 + movaps -0x50(%rax),%xmm3 + movaps -0x40(%rax),%xmm4 + movaps -0x30(%rax),%xmm5 + movaps -0x20(%rax),%xmm6 + movaps -0x10(%rax),%xmm7 + movaps 0x00(%rax),%xmm8 + movaps 0x10(%rax),%xmm9 + movaps 0x20(%rax),%xmm10 + movaps 0x30(%rax),%xmm11 + movaps 0x40(%rax),%xmm12 + movaps 0x50(%rax),%xmm13 + movaps 0x60(%rax),%xmm14 + movaps 0x70(%rax),%xmm15 + + mov 40(%rdi),%r8 + mov 48(%rdi),%r9 + mov 56(%rdi),%r10 + mov 64(%rdi),%r11 + mov 72(%rdi),%r12 + mov 80(%rdi),%r13 + mov 88(%rdi),%r14 + mov 96(%rdi),%r15 + mov 112(%rdi),%rsi + mov 120(%rdi),%rbp + mov 128(%rdi),%rbx + mov 136(%rdi),%rdx + mov 152(%rdi),%rcx + mov 160(%rdi),%rsp + push 168(%rdi) + mov 104(%rdi),%rdi + + xor %eax,%eax + ret + +#elif defined(__aarch64__) +#define REGS(i) 184+i*8 + + ldp x1,x16,[x0,REGS(31)] // sp, pc + mov sp,x1 + ldr x30,[x0,REGS(30)] + ldp x28,x29,[x0,REGS(28)] + ldp x26,x27,[x0,REGS(26)] + ldp x24,x25,[x0,REGS(24)] + ldp x22,x23,[x0,REGS(22)] + ldp x20,x21,[x0,REGS(20)] + ldp x18,x19,[x0,REGS(18)] + ldr x17,[x0,REGS(17)] + ldp x14,x15,[x0,REGS(14)] + ldp x12,x13,[x0,REGS(12)] + ldp x10,x11,[x0,REGS(10)] + ldp x8,x9,[x0,REGS(8)] + ldp x6,x7,[x0,REGS(6)] + ldp x4,x5,[x0,REGS(4)] + ldp x2,x3,[x0,REGS(2)] + ldp x0,x1,[x0,REGS(0)] + br x16 + +#else +#error "unsupported architecture" +#endif + .endfn __tailcontext,globl diff --git a/libc/calls/setcontext.c b/libc/calls/ucontext.c similarity index 83% rename from libc/calls/setcontext.c rename to libc/calls/ucontext.c index 1fb5585ca..caae7faba 100644 --- a/libc/calls/setcontext.c +++ b/libc/calls/ucontext.c @@ -16,26 +16,30 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/struct/sigset.h" -#include "libc/calls/struct/ucontext.internal.h" #include "libc/calls/ucontext.h" +#include "libc/calls/struct/sigset.h" #include "libc/sysv/consts/sig.h" -#include "libc/sysv/consts/ss.h" -int __setcontext(const ucontext_t *, uintptr_t); +int __tailcontext(const ucontext_t *); /** * Sets machine context. * + * @return -1 on error w/ errno, otherwise won't return unless sent back + * @see swapcontext() + * @see makecontext() * @see getcontext() + * @threadsafe */ int setcontext(const ucontext_t *uc) { - uintptr_t sp; if (sigprocmask(SIG_SETMASK, &uc->uc_sigmask, 0)) return -1; - if (uc->uc_stack.ss_flags & (SS_DISABLE | SS_ONSTACK)) { - sp = uc->uc_mcontext.SP; - } else { - sp = (uintptr_t)uc->uc_stack.ss_sp & -16; - } - return __setcontext(uc, sp); + return __tailcontext(uc); +} + +int __getcontextsig(ucontext_t *uc) { + return sigprocmask(SIG_SETMASK, 0, &uc->uc_sigmask); +} + +int __swapcontextsig(ucontext_t *x, const ucontext_t *y) { + return sigprocmask(SIG_SETMASK, &y->uc_sigmask, &x->uc_sigmask); } diff --git a/libc/calls/ucontext.h b/libc/calls/ucontext.h index a907aac68..dae2192f5 100644 --- a/libc/calls/ucontext.h +++ b/libc/calls/ucontext.h @@ -41,24 +41,24 @@ struct sigcontext { #ifdef __x86_64__ union { struct { - uint64_t r8; - uint64_t r9; - uint64_t r10; - uint64_t r11; - uint64_t r12; - uint64_t r13; - uint64_t r14; - uint64_t r15; - uint64_t rdi; - uint64_t rsi; - uint64_t rbp; - uint64_t rbx; - uint64_t rdx; - uint64_t rax; - uint64_t rcx; - uint64_t rsp; - uint64_t rip; - uint64_t eflags; + uint64_t r8; /* 40 */ + uint64_t r9; /* 48 */ + uint64_t r10; /* 56 */ + uint64_t r11; /* 64 */ + uint64_t r12; /* 72 */ + uint64_t r13; /* 80 */ + uint64_t r14; /* 88 */ + uint64_t r15; /* 96 */ + uint64_t rdi; /* 104 */ + uint64_t rsi; /* 112 */ + uint64_t rbp; /* 120 */ + uint64_t rbx; /* 128 */ + uint64_t rdx; /* 136 */ + uint64_t rax; /* 144 */ + uint64_t rcx; /* 152 */ + uint64_t rsp; /* 160 */ + uint64_t rip; /* 168 */ + uint64_t eflags; /* 176 */ uint16_t cs; uint16_t gs; uint16_t fs; @@ -102,8 +102,10 @@ struct ucontext { typedef struct ucontext ucontext_t; -int getcontext(ucontext_t *); -int setcontext(const ucontext_t *); +int getcontext(ucontext_t *) dontthrow; +int setcontext(const ucontext_t *) dontthrow; +int swapcontext(ucontext_t *, const ucontext_t *) dontthrow returnstwice; +void makecontext(ucontext_t *, void (*)(), int, ...) dontthrow nocallback; COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/calls/wincrash.c b/libc/calls/wincrash.c index f4b2963f9..0f0d65b51 100644 --- a/libc/calls/wincrash.c +++ b/libc/calls/wincrash.c @@ -21,10 +21,10 @@ #include "libc/calls/struct/ucontext.internal.h" #include "libc/calls/ucontext.h" #include "libc/intrin/describebacktrace.internal.h" -#include "libc/intrin/kprintf.h" #include "libc/intrin/strace.internal.h" #include "libc/nt/enum/exceptionhandleractions.h" #include "libc/nt/enum/signal.h" +#include "libc/nt/enum/status.h" #include "libc/nt/runtime.h" #include "libc/nt/struct/ntexceptionpointers.h" #include "libc/str/str.h" @@ -90,6 +90,10 @@ unsigned __wincrash(struct NtExceptionPointers *ep) { code = SI_USER; sig = SIGABRT; break; + case kNtStatusIntegerOverflow: + code = FPE_INTOVF; + sig = SIGFPE; + break; case kNtSignalFltDivideByZero: code = FPE_FLTDIV; sig = SIGFPE; diff --git a/libc/calls/winerr.internal.h b/libc/calls/winerr.internal.h deleted file mode 100644 index e71eb74a9..000000000 --- a/libc/calls/winerr.internal.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef COSMOPOLITAN_LIBC_CALLS_WINERR_INTERNAL_H_ -#define COSMOPOLITAN_LIBC_CALLS_WINERR_INTERNAL_H_ -#if !(__ASSEMBLER__ + __LINKER__ + 0) -COSMOPOLITAN_C_START_ - -COSMOPOLITAN_C_END_ -#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ -#endif /* COSMOPOLITAN_LIBC_CALLS_WINERR_INTERNAL_H_ */ diff --git a/libc/log/oncrash_amd64.c b/libc/log/oncrash_amd64.c index 8725d68bc..35f52bde0 100644 --- a/libc/log/oncrash_amd64.c +++ b/libc/log/oncrash_amd64.c @@ -45,7 +45,6 @@ #include "libc/str/str.h" #include "libc/thread/thread.h" #include "libc/thread/tls.h" -#include "libc/str/str.h" #include "third_party/libcxx/math.h" #ifdef __x86_64__ @@ -230,8 +229,8 @@ relegated void ShowCrashReport(int err, int sig, struct siginfo *si, ctx->uc_mcontext.rsp <= GetStaticStackAddr(0) + APE_GUARDSIZE)) ? "Stack Overflow" : GetSiCodeName(sig, si->si_code), - host, getpid(), gettid(), program_invocation_name, strerror(err), names.sysname, - names.version, names.nodename, names.release); + host, getpid(), gettid(), program_invocation_name, strerror(err), + names.sysname, names.version, names.nodename, names.release); if (ctx) { p = ShowGeneralRegisters(p, ctx); p = ShowSseRegisters(p, ctx); @@ -314,6 +313,7 @@ relegated void __oncrash_amd64(int sig, struct siginfo *si, void *arg) { DebugBreak(); } else if (__nocolor || g_isrunningundermake) { gdbpid = -1; +#if 0 } else if (!IsTiny() && IsLinux() && FindDebugBinary() && !__isworker) { // RestoreDefaultCrashSignalHandlers(); gdbpid = AttachDebugger( @@ -321,6 +321,7 @@ relegated void __oncrash_amd64(int sig, struct siginfo *si, void *arg) { (rip >= (intptr_t)&__executable_start && rip < (intptr_t)&_etext)) ? rip : 0); +#endif } if (!(gdbpid > 0 && (sig == SIGTRAP || sig == SIGQUIT))) { __restore_tty(); diff --git a/libc/log/perror.c b/libc/log/perror.c index 18429f8c3..348d3921e 100644 --- a/libc/log/perror.c +++ b/libc/log/perror.c @@ -16,21 +16,19 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/calls.h" #include "libc/errno.h" -#include "libc/log/log.h" +#include "libc/fmt/magnumstrs.internal.h" #include "libc/stdio/stdio.h" -#include "libc/str/str.h" /** * Writes error messages to standard error. */ void perror(const char *message) { int err; - err = errno; - if (message && *message) { - fputs(message, stderr); - fputs(": ", stderr); - } - fputs(strerror(err), stderr); - fputc('\n', stderr); + const char *estr; + estr = _strerdoc(errno); + if (!message) message = ""; + if (!estr) estr = "Unknown error"; + tinyprint(2, message, *message ? ": " : "", estr, "\n", NULL); } diff --git a/libc/mem/mem.h b/libc/mem/mem.h index 6d6d2c637..7c0ad0b02 100644 --- a/libc/mem/mem.h +++ b/libc/mem/mem.h @@ -26,7 +26,6 @@ char *strndup(const char *, size_t) paramsnonnull() mallocesque; void *aligned_alloc(size_t, size_t) attributeallocalign((1)) attributeallocsize((2)) returnspointerwithnoaliases libcesque dontdiscard; int posix_memalign(void **, size_t, size_t); -bool __grow(void *, size_t *, size_t, size_t) paramsnonnull((1, 2)) libcesque; int mallopt(int, int); int malloc_trim(size_t); @@ -59,6 +58,10 @@ size_t malloc_set_footprint_limit(size_t); void malloc_inspect_all(void (*handler)(void *, void *, size_t, void *), void *); +#ifdef COSMO +bool __grow(void *, size_t *, size_t, size_t) paramsnonnull((1, 2)) libcesque; +#endif + COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #endif /* COSMOPOLITAN_LIBC_MEM_MEM_H_ */ diff --git a/libc/runtime/clone.c b/libc/runtime/clone.c index 4ebb109df..c699051ce 100644 --- a/libc/runtime/clone.c +++ b/libc/runtime/clone.c @@ -533,7 +533,7 @@ static int CloneLinux(int (*func)(void *arg, int rc), char *stk, size_t stksz, * int worker(void *arg) { return 0; } * struct CosmoTib tib = {.tib_self = &tib, .tib_tid = -1}; * atomic_int tid; - * char *stk = _mapstack(); + * char *stk = NewCosmoStack(); * clone(worker, stk, GetStackSize() - 16, * CLONE_VM | CLONE_THREAD | CLONE_FS | CLONE_FILES | * CLONE_SYSVSEM | CLONE_SIGHAND | CLONE_PARENT_SETTID | @@ -545,7 +545,7 @@ static int CloneLinux(int (*func)(void *arg, int rc), char *stk, size_t stksz, * // thread is running * while (atomic_load(&tib.tib_tid) > 0) sched_yield(); * // thread has terminated - * _freestack(stk); + * FreeCosmoStack(stk); * * Threads are created in a detached manner. They currently can't be * synchronized using wait() or posix signals. Threads created by this diff --git a/libc/runtime/grow.c b/libc/runtime/grow.c index d89e98f6d..e0132e159 100644 --- a/libc/runtime/grow.c +++ b/libc/runtime/grow.c @@ -32,10 +32,6 @@ #define GUARANTEE_TERMINATOR 1 #define INITIAL_CAPACITY (32 - GUARANTEE_TERMINATOR) -/** - * Grows array. - * @deprecated favor realloc - */ bool __grow(void *pp, size_t *capacity, size_t itemsize, size_t extra) { void **p, *p1, *p2; size_t n1, n2; diff --git a/libc/runtime/mapstack.c b/libc/runtime/mapstack.c index 2ef19c01e..043e74941 100644 --- a/libc/runtime/mapstack.c +++ b/libc/runtime/mapstack.c @@ -39,7 +39,7 @@ * * @return stack bottom address on success, or null w/ errno */ -void *_mapstack(void) { +void *NewCosmoStack(void) { char *p; if ((p = mmap(0, GetStackSize(), PROT_READ | PROT_WRITE, MAP_STACK | MAP_ANONYMOUS, -1, 0)) != MAP_FAILED) { @@ -56,8 +56,8 @@ void *_mapstack(void) { /** * Frees stack. * - * @param stk was allocated by _mapstack() + * @param stk was allocated by NewCosmoStack() */ -int _freestack(void *stk) { +int FreeCosmoStack(void *stk) { return munmap(stk, GetStackSize()); } diff --git a/libc/runtime/runtime.h b/libc/runtime/runtime.h index c664757be..e2b4ef27e 100644 --- a/libc/runtime/runtime.h +++ b/libc/runtime/runtime.h @@ -120,8 +120,6 @@ long _missingno(); void _weakfree(void *); void *_mapanon(size_t) attributeallocsize((1)) mallocesque; void *_mapshared(size_t) attributeallocsize((1)) mallocesque; -void *_mapstack(void) returnsaligned((APE_PAGESIZE)) mallocesque; -int _freestack(void *); void __oom_hook(size_t); void _peekall(void); bool _isheap(void *); diff --git a/libc/runtime/stack.h b/libc/runtime/stack.h index a3a6bbcad..374425ca9 100644 --- a/libc/runtime/stack.h +++ b/libc/runtime/stack.h @@ -133,6 +133,9 @@ forceinline void CheckLargeStackAllocation(void *p, ssize_t n) { } } +void *NewCosmoStack(void) vallocesque; +int FreeCosmoStack(void *) libcesque; + #endif /* COSMO */ COSMOPOLITAN_C_END_ #endif /* GNU ELF */ diff --git a/libc/thread/makecontext.c b/libc/thread/makecontext.c new file mode 100644 index 000000000..14f9536f2 --- /dev/null +++ b/libc/thread/makecontext.c @@ -0,0 +1,129 @@ +/*-*- 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 2023 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/assert.h" +#include "libc/calls/struct/ucontext.internal.h" +#include "libc/calls/ucontext.h" +#include "libc/nexgen32e/nexgen32e.h" +#include "libc/nexgen32e/stackframe.h" +#include "libc/runtime/runtime.h" +#include "libc/stdalign.internal.h" +#include "libc/str/str.h" +#include "libc/thread/thread.h" + +struct Gadget { + void (*func)(); + int args[6]; +}; + +static void runcontext(struct Gadget *call, ucontext_t *link) { + call->func(call->args[0], // + call->args[1], // + call->args[2], // + call->args[3], // + call->args[4], // + call->args[5]); + if (link) { + setcontext(link); + abort(); + } else { + pthread_exit(0); + } +} + +/** + * Creates coroutine gadget, e.g. + * + * ucontext_t uc; + * getcontext(&uc); + * uc.uc_link = 0; + * uc.uc_stack.ss_sp = NewCosmoStack(); + * uc.uc_stack.ss_size = GetStackSize(); + * makecontext(&uc, exit, 1, 42); + * setcontext(&uc); + * + * Is equivalent to: + * + * exit(42); + * + * @param uc stores processor state; the caller must: + * 1. initialize it using getcontext() + * 2. assign new value to `uc->uc_stack.ss_sp` + * 3. use `uc->uc_link` to define successor context + * @param func is the function to call when `uc` is activated; + * when `func` returns control passes to the linked context + * which if null will result in pthread_exit() being called + * @param argc is number of `int` arguments for `func` (max 6) + * @threadsafe + */ +void makecontext(ucontext_t *uc, void func(), int argc, ...) { + int i; + va_list va; + uintptr_t sp; + struct Gadget *call; + struct StackFrame *sf; + assert(argc <= 6u); + + // allocate call + sp = (uintptr_t)uc->uc_stack.ss_sp; + sp += uc->uc_stack.ss_size; + sp -= sizeof(*call); + sp &= -alignof(*call); + call = (struct Gadget *)sp; + + // get arguments + va_start(va, argc); + call->func = func; + for (i = 0; i < argc; ++i) { + call->args[i] = va_arg(va, int); + } + va_end(va); + + // construct fake function call on new stack + // + // the location where getcontext() was called shall be the previous + // entry in the backtrace when runcontext was called, e.g. + // + // 1000800bf160 423024 systemfive_linux+31 + // 1000800fff90 405299 abort+58 + // 1000800fffb0 40d98c runcontext+42 + // 1000800fffd0 40b308 makecontext_backtrace+20 + // 7fff22a7ff50 40c2d5 testlib_runtestcases+218 + // 7fff22a7ff90 40b1c3 testlib_runalltests+79 + // 7fff22a7ffa0 4038cb main+475 + // 7fff22a7ffe0 403cfb cosmo+69 + // 7fff22a7fff0 4034e2 _start+137 + // + // is about what it should look like. + sp &= -(sizeof(uintptr_t) * 2); +#ifdef __x86__ + *(uintptr_t *)(sp -= sizeof(uintptr_t)) = uc->uc_mcontext.PC; +#elif defined(__aarch64__) + *(uintptr_t *)(sp -= sizeof(uintptr_t)) = uc->uc_mcontext.regs[30]; + *(uintptr_t *)(sp -= sizeof(uintptr_t)) = uc->uc_mcontext.regs[29]; + uc->uc_mcontext.BP = uc->uc_mcontext.SP; +#else +#error "unsupported architecture" +#endif + + // program context + uc->uc_mcontext.SP = sp; + uc->uc_mcontext.PC = (uintptr_t)runcontext; + uc->uc_mcontext.ARG0 = (uintptr_t)call; + uc->uc_mcontext.ARG1 = (uintptr_t)uc->uc_link; +} diff --git a/libc/thread/pthread_attr_setstack.c b/libc/thread/pthread_attr_setstack.c index 39d589bd7..8a2650860 100644 --- a/libc/thread/pthread_attr_setstack.c +++ b/libc/thread/pthread_attr_setstack.c @@ -26,13 +26,13 @@ * * pthread_t id; * pthread_attr_t attr; - * char *stk = _mapstack(); + * char *stk = NewCosmoStack(); * pthread_attr_init(&attr); * pthread_attr_setstack(&attr, stk, GetStackSize()); * pthread_create(&id, &attr, func, 0); * pthread_attr_destroy(&attr); * pthread_join(id, 0); - * _freestack(stk); + * FreeCosmoStack(stk); * * Your stack must have at least `PTHREAD_STACK_MIN` bytes, which * Cosmpolitan Libc defines as `GetStackSize()`. It's a link-time @@ -44,7 +44,7 @@ * (e.g. kprintf) assumes that stack sizes are two-powers and are * aligned to that two-power. Conformance isn't required since we * say caveat emptor to those who don't maintain these invariants - * please consider using _mapstack() which always does it perfect + * please consider using NewCosmoStack(), which is always perfect * or use `mmap(0, GetStackSize() << 1, ...)` for a bigger stack. * * Unlike pthread_attr_setstacksize(), this function permits just diff --git a/libc/thread/spawn.c b/libc/thread/spawn.c index d92ed76a3..5793f15d7 100644 --- a/libc/thread/spawn.c +++ b/libc/thread/spawn.c @@ -117,7 +117,7 @@ int _spawn(int fun(void *, int), void *arg, struct spawn *opt_out_thread) { // we must use _mapstack() to allocate the stack because OpenBSD has // very strict requirements for what's allowed to be used for stacks - if (!(th->stk = _mapstack())) { + if (!(th->stk = NewCosmoStack())) { free(th->tls); return -1; } @@ -132,7 +132,7 @@ int _spawn(int fun(void *, int), void *arg, struct spawn *opt_out_thread) { spawner, &th->ptid, __adj_tls(th->tib), &th->tib->tib_tid); if (rc) { errno = rc; - _freestack(th->stk); + FreeCosmoStack(th->stk); free(th->tls); return -1; } diff --git a/test/libc/calls/getcontext_test.c b/test/libc/calls/getcontext_test.c index ecb5a9f02..5db8e416d 100644 --- a/test/libc/calls/getcontext_test.c +++ b/test/libc/calls/getcontext_test.c @@ -83,3 +83,19 @@ void SetGetContext(void) { BENCH(getcontext, bench) { EZBENCH2("get/setcontext", donothing, SetGetContext()); } + +BENCH(swapcontext, bench) { + ucontext_t main, loop; + volatile bool ready = false; + getcontext(&main); + if (ready) { + for (;;) { + swapcontext(&main, &loop); + // kprintf("boom\n"); + } + } else { + ready = true; + EZBENCH2("x2 swapcontext", donothing, swapcontext(&loop, &main)); + // kprintf("dollar\n"); + } +} diff --git a/test/libc/intrin/lock_test.c b/test/libc/intrin/lock_test.c index 3a2b51c02..f388b25df 100644 --- a/test/libc/intrin/lock_test.c +++ b/test/libc/intrin/lock_test.c @@ -123,7 +123,7 @@ void TestContendedLock(const char *name, int kind) { pthread_mutexattr_destroy(&attr); atomic_store(&ready, 0); atomic_store(&success, 0); - stk = _mapstack(); + stk = NewCosmoStack(); rc = clone(Worker, stk, GetStackSize() - 16 /* openbsd:stackbound */, CLONE_VM | CLONE_THREAD | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_SYSVSEM | CLONE_PARENT_SETTID | CLONE_CHILD_SETTID | @@ -146,7 +146,7 @@ void TestContendedLock(const char *name, int kind) { while (tib.tib_tid) donothing; ASSERT_EQ(1, atomic_load(&success)); ASSERT_EQ(0, atomic_load(&counter)); - _freestack(stk); + FreeCosmoStack(stk); ASSERT_EQ(0, pthread_mutex_destroy(&mu)); ns = time2dbl(timespec_sub(t2, t1)) / n; kprintf("%s contended took %s\n", name, time2str(ns)); diff --git a/test/libc/thread/makecontext_test.c b/test/libc/thread/makecontext_test.c new file mode 100644 index 000000000..ba97c12be --- /dev/null +++ b/test/libc/thread/makecontext_test.c @@ -0,0 +1,68 @@ +/*-*- 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 2023 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/ucontext.h" +#include "libc/limits.h" +#include "libc/mem/gc.internal.h" +#include "libc/runtime/runtime.h" +#include "libc/runtime/stack.h" +#include "libc/runtime/symbols.internal.h" +#include "libc/str/str.h" +#include "libc/sysv/consts/sig.h" +#include "libc/testlib/subprocess.h" +#include "libc/testlib/testlib.h" +#include "libc/thread/thread.h" +#include "libc/x/x.h" +#include "third_party/libcxx/math.h" + +ucontext_t uc; +char testlib_enable_tmp_setup_teardown; + +void itsatrap(int x, int y) { + *(int *)(intptr_t)x = scalbn(x, y); +} + +TEST(makecontext, test) { + SPAWN(fork); + getcontext(&uc); + uc.uc_link = 0; + uc.uc_stack.ss_sp = NewCosmoStack(); + uc.uc_stack.ss_size = GetStackSize(); + makecontext(&uc, exit, 1, 42); + setcontext(&uc); + EXITS(42); +} + +TEST(makecontext, backtrace) { + SPAWN(fork); + ASSERT_SYS(0, 0, close(2)); + ASSERT_SYS(0, 2, creat("log", 0644)); + getcontext(&uc); + uc.uc_link = 0; + uc.uc_stack.ss_sp = NewCosmoStack(); + uc.uc_stack.ss_size = GetStackSize(); + makecontext(&uc, itsatrap, 2, 123, 456); + setcontext(&uc); + EXITS(128 + SIGSEGV); + if (!GetSymbolTable()) return; + char *log = gc(xslurp("log", 0)); + EXPECT_NE(0, strstr(log, "itsatrap")); + EXPECT_NE(0, strstr(log, "runcontext")); + EXPECT_NE(0, strstr(log, "makecontext_backtrace")); +} diff --git a/test/libc/thread/test.mk b/test/libc/thread/test.mk index 3f7e11e73..4b6994451 100644 --- a/test/libc/thread/test.mk +++ b/test/libc/thread/test.mk @@ -35,6 +35,7 @@ TEST_LIBC_THREAD_DIRECTDEPS = \ LIBC_TESTLIB \ LIBC_THREAD \ LIBC_TIME \ + LIBC_X \ THIRD_PARTY_NSYNC \ THIRD_PARTY_NSYNC_MEM diff --git a/tool/emacs/cosmo-stuff.el b/tool/emacs/cosmo-stuff.el index 42648c5c7..aa8e862bc 100644 --- a/tool/emacs/cosmo-stuff.el +++ b/tool/emacs/cosmo-stuff.el @@ -25,7 +25,7 @@ (require 'ld-script) (require 'make-mode) -(setq cosmo-dbg-mode "dbg") +(setq cosmo-dbg-mode "zero") (setq cosmo-default-mode "") (setq c-doc-comment-style 'javadown)