From 197aa0d4652d618fd8a11c8445fc3df9bd5816bd Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Sun, 2 Jul 2023 03:50:29 -0700 Subject: [PATCH] Implement swapcontext() and makecontext() This change introduces support for Linux-style uc_context manipulation that's fast and works well on all supported OSes and architectures. It also integrates with the Cosmpolitan runtime which can show backtraces comprised of multiple stacks and fibers. See the test and example code for further details. This will be used by Mold once it's been vendored --- examples/setcontext.c | 79 ++++++++++++ libc/calls/calls.mk | 4 +- libc/calls/getcontext.S | 134 ++----------------- libc/calls/getcontext.inc | 89 +++++++++++++ libc/calls/setcontext2.S | 136 -------------------- libc/calls/struct/ucontext.internal.h | 20 ++- libc/calls/{getcontext2.c => swapcontext.S} | 62 ++++++--- libc/calls/tailcontext.S | 89 +++++++++++++ libc/calls/{setcontext.c => ucontext.c} | 26 ++-- libc/calls/ucontext.h | 42 +++--- libc/calls/wincrash.c | 6 +- libc/calls/winerr.internal.h | 8 -- libc/log/oncrash_amd64.c | 7 +- libc/log/perror.c | 16 +-- libc/mem/mem.h | 5 +- libc/runtime/clone.c | 4 +- libc/runtime/grow.c | 4 - libc/runtime/mapstack.c | 6 +- libc/runtime/runtime.h | 2 - libc/runtime/stack.h | 3 + libc/thread/makecontext.c | 129 +++++++++++++++++++ libc/thread/pthread_attr_setstack.c | 6 +- libc/thread/spawn.c | 4 +- test/libc/calls/getcontext_test.c | 16 +++ test/libc/intrin/lock_test.c | 4 +- test/libc/thread/makecontext_test.c | 68 ++++++++++ test/libc/thread/test.mk | 1 + tool/emacs/cosmo-stuff.el | 2 +- 28 files changed, 617 insertions(+), 355 deletions(-) create mode 100644 examples/setcontext.c create mode 100644 libc/calls/getcontext.inc delete mode 100644 libc/calls/setcontext2.S rename libc/calls/{getcontext2.c => swapcontext.S} (63%) create mode 100644 libc/calls/tailcontext.S rename libc/calls/{setcontext.c => ucontext.c} (83%) delete mode 100644 libc/calls/winerr.internal.h create mode 100644 libc/thread/makecontext.c create mode 100644 test/libc/thread/makecontext_test.c 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)