mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-03-03 07:29:23 +00:00
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
This commit is contained in:
parent
7ec84655b4
commit
197aa0d465
28 changed files with 617 additions and 355 deletions
79
examples/setcontext.c
Normal file
79
examples/setcontext.c
Normal file
|
@ -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);
|
||||
}
|
|
@ -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)))
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
89
libc/calls/getcontext.inc
Normal file
89
libc/calls/getcontext.inc
Normal file
|
@ -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
|
|
@ -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();
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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
|
89
libc/calls/tailcontext.S
Normal file
89
libc/calls/tailcontext.S
Normal file
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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) */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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_ */
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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_ */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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 *);
|
||||
|
|
|
@ -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 */
|
||||
|
|
129
libc/thread/makecontext.c
Normal file
129
libc/thread/makecontext.c
Normal file
|
@ -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;
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
|
|
68
test/libc/thread/makecontext_test.c
Normal file
68
test/libc/thread/makecontext_test.c
Normal file
|
@ -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"));
|
||||
}
|
|
@ -35,6 +35,7 @@ TEST_LIBC_THREAD_DIRECTDEPS = \
|
|||
LIBC_TESTLIB \
|
||||
LIBC_THREAD \
|
||||
LIBC_TIME \
|
||||
LIBC_X \
|
||||
THIRD_PARTY_NSYNC \
|
||||
THIRD_PARTY_NSYNC_MEM
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue