mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-02-07 23:13:34 +00:00
130 lines
4.9 KiB
C
130 lines
4.9 KiB
C
|
/*-*- 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;
|
||
|
}
|