mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-31 11:37:35 +00:00
Get threads working on all platforms
We now have a high-quality clone() implementation for creating lightweight threads on Linux/Windows/FreeBSD/NetBSD/OpenBSD.
This commit is contained in:
parent
4e62cefa6e
commit
fec396037a
43 changed files with 850 additions and 532 deletions
|
@ -11,16 +11,21 @@
|
||||||
#include "libc/calls/sigbits.h"
|
#include "libc/calls/sigbits.h"
|
||||||
#include "libc/calls/struct/sigaction.h"
|
#include "libc/calls/struct/sigaction.h"
|
||||||
#include "libc/calls/struct/sigset.h"
|
#include "libc/calls/struct/sigset.h"
|
||||||
|
#include "libc/calls/struct/timespec.h"
|
||||||
#include "libc/fmt/fmt.h"
|
#include "libc/fmt/fmt.h"
|
||||||
#include "libc/fmt/itoa.h"
|
#include "libc/fmt/itoa.h"
|
||||||
#include "libc/log/internal.h"
|
#include "libc/log/internal.h"
|
||||||
|
#include "libc/log/log.h"
|
||||||
#include "libc/macros.internal.h"
|
#include "libc/macros.internal.h"
|
||||||
#include "libc/runtime/internal.h"
|
#include "libc/runtime/internal.h"
|
||||||
#include "libc/runtime/runtime.h"
|
#include "libc/runtime/runtime.h"
|
||||||
|
#include "libc/stdio/append.internal.h"
|
||||||
#include "libc/stdio/stdio.h"
|
#include "libc/stdio/stdio.h"
|
||||||
#include "libc/str/str.h"
|
#include "libc/str/str.h"
|
||||||
|
#include "libc/sysv/consts/clock.h"
|
||||||
#include "libc/sysv/consts/dt.h"
|
#include "libc/sysv/consts/dt.h"
|
||||||
#include "libc/sysv/consts/sig.h"
|
#include "libc/sysv/consts/sig.h"
|
||||||
|
#include "libc/time/time.h"
|
||||||
#include "libc/x/x.h"
|
#include "libc/x/x.h"
|
||||||
#include "third_party/linenoise/linenoise.h"
|
#include "third_party/linenoise/linenoise.h"
|
||||||
|
|
||||||
|
@ -102,7 +107,11 @@ static char *ShellHint(const char *p, const char **ansi1, const char **ansi2) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
|
bool timeit;
|
||||||
|
int64_t nanos;
|
||||||
int n, ws, pid;
|
int n, ws, pid;
|
||||||
|
struct rusage ru;
|
||||||
|
struct timespec ts1, ts2;
|
||||||
char *prog, path[PATH_MAX];
|
char *prog, path[PATH_MAX];
|
||||||
sigset_t chldmask, savemask;
|
sigset_t chldmask, savemask;
|
||||||
struct sigaction ignore, saveint, savequit;
|
struct sigaction ignore, saveint, savequit;
|
||||||
|
@ -114,6 +123,12 @@ int main(int argc, char *argv[]) {
|
||||||
while ((line = linenoiseWithHistory(prompt, "cmd"))) {
|
while ((line = linenoiseWithHistory(prompt, "cmd"))) {
|
||||||
n = 0;
|
n = 0;
|
||||||
start = line;
|
start = line;
|
||||||
|
if (startswith(start, "time ")) {
|
||||||
|
timeit = true;
|
||||||
|
start += 5;
|
||||||
|
} else {
|
||||||
|
timeit = false;
|
||||||
|
}
|
||||||
args = xcalloc(1, sizeof(*args));
|
args = xcalloc(1, sizeof(*args));
|
||||||
while ((arg = strtok_r(start, " \t\r\n", &state))) {
|
while ((arg = strtok_r(start, " \t\r\n", &state))) {
|
||||||
args = xrealloc(args, (++n + 1) * sizeof(*args));
|
args = xrealloc(args, (++n + 1) * sizeof(*args));
|
||||||
|
@ -132,6 +147,9 @@ int main(int argc, char *argv[]) {
|
||||||
sigaddset(&chldmask, SIGCHLD);
|
sigaddset(&chldmask, SIGCHLD);
|
||||||
sigprocmask(SIG_BLOCK, &chldmask, &savemask);
|
sigprocmask(SIG_BLOCK, &chldmask, &savemask);
|
||||||
|
|
||||||
|
if (timeit) {
|
||||||
|
clock_gettime(CLOCK_REALTIME, &ts1);
|
||||||
|
}
|
||||||
if (!fork()) {
|
if (!fork()) {
|
||||||
sigaction(SIGINT, &saveint, 0);
|
sigaction(SIGINT, &saveint, 0);
|
||||||
sigaction(SIGQUIT, &savequit, 0);
|
sigaction(SIGQUIT, &savequit, 0);
|
||||||
|
@ -139,8 +157,23 @@ int main(int argc, char *argv[]) {
|
||||||
execv(prog, args);
|
execv(prog, args);
|
||||||
_Exit(127);
|
_Exit(127);
|
||||||
}
|
}
|
||||||
|
wait4(0, &ws, 0, &ru);
|
||||||
|
if (timeit) {
|
||||||
|
clock_gettime(CLOCK_REALTIME, &ts2);
|
||||||
|
if (ts2.tv_sec == ts1.tv_sec) {
|
||||||
|
nanos = ts2.tv_nsec - ts1.tv_nsec;
|
||||||
|
} else {
|
||||||
|
nanos = (ts2.tv_sec - ts1.tv_sec) * 1000000000LL;
|
||||||
|
nanos += 1000000000LL - ts1.tv_nsec;
|
||||||
|
nanos += ts2.tv_nsec;
|
||||||
|
}
|
||||||
|
printf("took %,ldµs wall time\n", nanos / 1000);
|
||||||
|
p = 0;
|
||||||
|
AppendResourceReport(&p, &ru, "\n");
|
||||||
|
fputs(p, stdout);
|
||||||
|
free(p);
|
||||||
|
}
|
||||||
|
|
||||||
wait(&ws);
|
|
||||||
p = prompt;
|
p = prompt;
|
||||||
if (WIFEXITED(ws)) {
|
if (WIFEXITED(ws)) {
|
||||||
if (WEXITSTATUS(ws)) {
|
if (WEXITSTATUS(ws)) {
|
||||||
|
|
|
@ -133,6 +133,8 @@ int getpgrp(void) nosideeffect;
|
||||||
int getpid(void);
|
int getpid(void);
|
||||||
int getppid(void);
|
int getppid(void);
|
||||||
int getpriority(int, unsigned);
|
int getpriority(int, unsigned);
|
||||||
|
int getresgid(uint32_t *, uint32_t *, uint32_t *);
|
||||||
|
int getresuid(uint32_t *, uint32_t *, uint32_t *);
|
||||||
int getrlimit(int, struct rlimit *);
|
int getrlimit(int, struct rlimit *);
|
||||||
int getrusage(int, struct rusage *);
|
int getrusage(int, struct rusage *);
|
||||||
int getsid(int) nosideeffect;
|
int getsid(int) nosideeffect;
|
||||||
|
@ -163,6 +165,7 @@ int pause(void);
|
||||||
int personality(uint64_t);
|
int personality(uint64_t);
|
||||||
int pipe(int[hasatleast 2]);
|
int pipe(int[hasatleast 2]);
|
||||||
int pipe2(int[hasatleast 2], int);
|
int pipe2(int[hasatleast 2], int);
|
||||||
|
int pledge(const char *, const char *);
|
||||||
int posix_fadvise(int, uint64_t, uint64_t, int);
|
int posix_fadvise(int, uint64_t, uint64_t, int);
|
||||||
int posix_madvise(void *, uint64_t, int);
|
int posix_madvise(void *, uint64_t, int);
|
||||||
int prctl(int, ...);
|
int prctl(int, ...);
|
||||||
|
@ -186,8 +189,6 @@ int setpriority(int, unsigned, int);
|
||||||
int setregid(uint32_t, uint32_t);
|
int setregid(uint32_t, uint32_t);
|
||||||
int setresgid(uint32_t, uint32_t, uint32_t);
|
int setresgid(uint32_t, uint32_t, uint32_t);
|
||||||
int setresuid(uint32_t, uint32_t, uint32_t);
|
int setresuid(uint32_t, uint32_t, uint32_t);
|
||||||
int getresgid(uint32_t *, uint32_t *, uint32_t *);
|
|
||||||
int getresuid(uint32_t *, uint32_t *, uint32_t *);
|
|
||||||
int setreuid(uint32_t, uint32_t);
|
int setreuid(uint32_t, uint32_t);
|
||||||
int setrlimit(int, const struct rlimit *);
|
int setrlimit(int, const struct rlimit *);
|
||||||
int setsid(void);
|
int setsid(void);
|
||||||
|
@ -203,6 +204,8 @@ int symlinkat(const char *, int, const char *);
|
||||||
int sync_file_range(int, int64_t, int64_t, unsigned);
|
int sync_file_range(int, int64_t, int64_t, unsigned);
|
||||||
int sysctl(const int *, unsigned, void *, size_t *, void *, size_t);
|
int sysctl(const int *, unsigned, void *, size_t *, void *, size_t);
|
||||||
int sysinfo(struct sysinfo *);
|
int sysinfo(struct sysinfo *);
|
||||||
|
int tgkill(int, int, int);
|
||||||
|
int tkill(int, int);
|
||||||
int touch(const char *, uint32_t);
|
int touch(const char *, uint32_t);
|
||||||
int truncate(const char *, uint64_t);
|
int truncate(const char *, uint64_t);
|
||||||
int ttyname_r(int, char *, size_t);
|
int ttyname_r(int, char *, size_t);
|
||||||
|
@ -239,7 +242,6 @@ ssize_t write(int, const void *, size_t);
|
||||||
struct dirent *readdir(DIR *);
|
struct dirent *readdir(DIR *);
|
||||||
void rewinddir(DIR *);
|
void rewinddir(DIR *);
|
||||||
void sync(void);
|
void sync(void);
|
||||||
int pledge(const char *, const char *);
|
|
||||||
|
|
||||||
int clone(int (*)(void *), void *, size_t, int, void *, int *, void *, size_t,
|
int clone(int (*)(void *), void *, size_t, int, void *, int *, void *, size_t,
|
||||||
int *);
|
int *);
|
||||||
|
|
|
@ -158,6 +158,7 @@ i32 sys_fsync(i32) hidden;
|
||||||
i32 sys_ftruncate(i32, i64, i64) hidden;
|
i32 sys_ftruncate(i32, i64, i64) hidden;
|
||||||
i32 sys_futimes(i32, const struct timeval *) hidden;
|
i32 sys_futimes(i32, const struct timeval *) hidden;
|
||||||
i32 sys_futimesat(i32, const char *, const struct timeval *) hidden;
|
i32 sys_futimesat(i32, const char *, const struct timeval *) hidden;
|
||||||
|
i32 sys_getcontext(void *) hidden;
|
||||||
i32 sys_getitimer(i32, struct itimerval *) hidden;
|
i32 sys_getitimer(i32, struct itimerval *) hidden;
|
||||||
i32 sys_getpgid(i32) hidden;
|
i32 sys_getpgid(i32) hidden;
|
||||||
i32 sys_getpgrp(void) hidden;
|
i32 sys_getpgrp(void) hidden;
|
||||||
|
@ -212,6 +213,8 @@ i32 sys_symlinkat(const char *, i32, const char *) hidden;
|
||||||
i32 sys_sync(void) hidden;
|
i32 sys_sync(void) hidden;
|
||||||
i32 sys_sync_file_range(i32, i64, i64, u32) hidden;
|
i32 sys_sync_file_range(i32, i64, i64, u32) hidden;
|
||||||
i32 sys_sysinfo(struct sysinfo *) hidden;
|
i32 sys_sysinfo(struct sysinfo *) hidden;
|
||||||
|
i32 sys_tgkill(i32, i32, i32) hidden;
|
||||||
|
i32 sys_tkill(i32, i32, void *) hidden;
|
||||||
i32 sys_truncate(const char *, u64, u64) hidden;
|
i32 sys_truncate(const char *, u64, u64) hidden;
|
||||||
i32 sys_uname(char *) hidden;
|
i32 sys_uname(char *) hidden;
|
||||||
i32 sys_unlinkat(i32, const char *, i32) hidden;
|
i32 sys_unlinkat(i32, const char *, i32) hidden;
|
||||||
|
|
|
@ -50,8 +50,8 @@ privileged void *sys_mremap(void *p, size_t n, size_t m, int f, void *q) {
|
||||||
r10 = m;
|
r10 = m;
|
||||||
r8 = (f & MREMAP_FIXED) ? MAP_FIXED : 0;
|
r8 = (f & MREMAP_FIXED) ? MAP_FIXED : 0;
|
||||||
asm(CFLAG_ASM("syscall")
|
asm(CFLAG_ASM("syscall")
|
||||||
: CFLAG_CONSTRAINT(cf), "+a"(rax)
|
: CFLAG_CONSTRAINT(cf), "+a"(rax), "=d"(rdx)
|
||||||
: "D"(p), "S"(n), "d"(q), "r"(r10), "r"(r8)
|
: "D"(p), "S"(n), "2"(q), "r"(r10), "r"(r8)
|
||||||
: "rcx", "r9", "r11", "memory", "cc");
|
: "rcx", "r9", "r11", "memory", "cc");
|
||||||
if (cf) errno = rax, rax = -1;
|
if (cf) errno = rax, rax = -1;
|
||||||
} else {
|
} else {
|
||||||
|
|
34
libc/calls/tgkill.c
Normal file
34
libc/calls/tgkill.c
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
/*-*- 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 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/calls/calls.h"
|
||||||
|
#include "libc/calls/internal.h"
|
||||||
|
#include "libc/calls/strace.internal.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Kills thread group.
|
||||||
|
*
|
||||||
|
* @raises ENOSYS on non-Linux
|
||||||
|
* @see tkill()
|
||||||
|
*/
|
||||||
|
int tgkill(int tgid, int tid, int sig) {
|
||||||
|
int rc;
|
||||||
|
rc = sys_tgkill(tgid, tid, sig);
|
||||||
|
STRACE("tgkill(%d, %d, %G) → %d% m", tgid, tid, sig, rc);
|
||||||
|
return rc;
|
||||||
|
}
|
61
libc/calls/tkill.c
Normal file
61
libc/calls/tkill.c
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
/*-*- 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 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/calls/calls.h"
|
||||||
|
#include "libc/calls/internal.h"
|
||||||
|
#include "libc/calls/strace.internal.h"
|
||||||
|
#include "libc/dce.h"
|
||||||
|
#include "libc/intrin/kprintf.h"
|
||||||
|
#include "libc/nt/enum/threadaccess.h"
|
||||||
|
#include "libc/nt/runtime.h"
|
||||||
|
#include "libc/nt/thread.h"
|
||||||
|
#include "libc/sysv/errfuns.h"
|
||||||
|
|
||||||
|
static textwindows int sys_tkill_nt(int tid, int sig) {
|
||||||
|
int rc;
|
||||||
|
int64_t hand;
|
||||||
|
if ((hand = OpenThread(kNtThreadTerminate, false, tid))) {
|
||||||
|
if (TerminateThread(hand, 128 + sig)) {
|
||||||
|
rc = 0;
|
||||||
|
} else {
|
||||||
|
rc = __winerr();
|
||||||
|
}
|
||||||
|
CloseHandle(hand);
|
||||||
|
} else {
|
||||||
|
rc = esrch();
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Kills thread.
|
||||||
|
*
|
||||||
|
* @param tid is thread id
|
||||||
|
* @param sig does nothing on xnu
|
||||||
|
* @return 0 on success, or -1 w/ errno
|
||||||
|
*/
|
||||||
|
int tkill(int tid, int sig) {
|
||||||
|
int rc;
|
||||||
|
if (!IsWindows()) {
|
||||||
|
rc = sys_tkill(tid, sig, 0);
|
||||||
|
} else {
|
||||||
|
rc = sys_tkill_nt(tid, sig);
|
||||||
|
}
|
||||||
|
STRACE("tkill(%d, %G) → %d% m", tid, sig, rc);
|
||||||
|
return rc;
|
||||||
|
}
|
|
@ -16,14 +16,12 @@
|
||||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/bits/weaken.h"
|
|
||||||
#include "libc/calls/internal.h"
|
|
||||||
#include "libc/calls/strace.internal.h"
|
#include "libc/calls/strace.internal.h"
|
||||||
#include "libc/dce.h"
|
#include "libc/dce.h"
|
||||||
|
#include "libc/intrin/setjmp.internal.h"
|
||||||
#include "libc/intrin/winthread.internal.h"
|
#include "libc/intrin/winthread.internal.h"
|
||||||
#include "libc/mem/mem.h"
|
|
||||||
#include "libc/nt/runtime.h"
|
|
||||||
#include "libc/nt/thread.h"
|
#include "libc/nt/thread.h"
|
||||||
|
#include "libc/runtime/runtime.h"
|
||||||
#include "libc/sysv/consts/nr.h"
|
#include "libc/sysv/consts/nr.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -36,18 +34,15 @@
|
||||||
*/
|
*/
|
||||||
privileged wontreturn void _Exit1(int rc) {
|
privileged wontreturn void _Exit1(int rc) {
|
||||||
struct WinThread *wt;
|
struct WinThread *wt;
|
||||||
STRACE("_Exit1(%d)", rc);
|
/* STRACE("_Exit1(%d)", rc); */
|
||||||
if (!IsWindows() && !IsMetal()) {
|
if (!IsWindows() && !IsMetal()) {
|
||||||
|
register long r10 asm("r10") = 0;
|
||||||
asm volatile("syscall"
|
asm volatile("syscall"
|
||||||
: /* no outputs */
|
: /* no outputs */
|
||||||
: "a"(__NR_exit), "D"(IsLinux() ? rc : 0)
|
: "a"(__NR_exit), "D"(IsLinux() ? rc : 0), "S"(0), "d"(0),
|
||||||
|
"r"(r10)
|
||||||
: "rcx", "r11", "memory");
|
: "rcx", "r11", "memory");
|
||||||
__builtin_unreachable();
|
|
||||||
} else if (IsWindows()) {
|
} else if (IsWindows()) {
|
||||||
if ((wt = GetWinThread())) {
|
|
||||||
__releasefd(wt->pid);
|
|
||||||
weaken(free)(wt);
|
|
||||||
}
|
|
||||||
ExitThread(rc);
|
ExitThread(rc);
|
||||||
}
|
}
|
||||||
for (;;) {
|
for (;;) {
|
||||||
|
|
|
@ -19,18 +19,21 @@
|
||||||
#include "libc/calls/calls.h"
|
#include "libc/calls/calls.h"
|
||||||
#include "libc/dce.h"
|
#include "libc/dce.h"
|
||||||
#include "libc/intrin/tls.h"
|
#include "libc/intrin/tls.h"
|
||||||
#include "libc/intrin/winthread.internal.h"
|
|
||||||
#include "libc/nt/thread.h"
|
#include "libc/nt/thread.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns current thread id.
|
* Returns current thread id.
|
||||||
* @asyncsignalsafe
|
* @asyncsignalsafe
|
||||||
*/
|
*/
|
||||||
int gettid(void) {
|
privileged int gettid(void) {
|
||||||
int rc;
|
int rc;
|
||||||
int64_t wut;
|
int64_t wut;
|
||||||
struct WinThread *wt;
|
struct WinThread *wt;
|
||||||
|
|
||||||
|
if (IsWindows()) {
|
||||||
|
return GetCurrentThreadId();
|
||||||
|
}
|
||||||
|
|
||||||
if (IsLinux()) {
|
if (IsLinux()) {
|
||||||
asm("syscall"
|
asm("syscall"
|
||||||
: "=a"(rc) // man says always succeeds
|
: "=a"(rc) // man says always succeeds
|
||||||
|
@ -59,7 +62,7 @@ int gettid(void) {
|
||||||
asm("syscall"
|
asm("syscall"
|
||||||
: "=a"(rc) // man says always succeeds
|
: "=a"(rc) // man says always succeeds
|
||||||
: "0"(311) // _lwp_self()
|
: "0"(311) // _lwp_self()
|
||||||
: "rcx", "r11", "memory", "cc");
|
: "rcx", "rdx", "r11", "memory", "cc");
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,13 +76,5 @@ int gettid(void) {
|
||||||
return wut; // narrowing intentional
|
return wut; // narrowing intentional
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsWindows()) {
|
|
||||||
if ((wt = GetWinThread())) {
|
|
||||||
return wt->pid;
|
|
||||||
} else {
|
|
||||||
return GetCurrentThreadId();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return getpid();
|
return getpid();
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
#include "libc/intrin/lockcmpxchg.h"
|
#include "libc/intrin/lockcmpxchg.h"
|
||||||
#include "libc/intrin/nomultics.internal.h"
|
#include "libc/intrin/nomultics.internal.h"
|
||||||
#include "libc/intrin/spinlock.h"
|
#include "libc/intrin/spinlock.h"
|
||||||
|
#include "libc/intrin/threaded.internal.h"
|
||||||
#include "libc/limits.h"
|
#include "libc/limits.h"
|
||||||
#include "libc/log/internal.h"
|
#include "libc/log/internal.h"
|
||||||
#include "libc/macros.internal.h"
|
#include "libc/macros.internal.h"
|
||||||
|
@ -56,7 +57,6 @@ struct Timestamps {
|
||||||
unsigned long long start;
|
unsigned long long start;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern bool __threaded;
|
|
||||||
unsigned long long __kbirth; // see fork-nt.c
|
unsigned long long __kbirth; // see fork-nt.c
|
||||||
|
|
||||||
privileged static struct Timestamps kenter(void) {
|
privileged static struct Timestamps kenter(void) {
|
||||||
|
|
29
libc/intrin/setjmp.internal.h
Normal file
29
libc/intrin/setjmp.internal.h
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
#ifndef COSMOPOLITAN_LIBC_INTRIN_SETJMP_INTERNAL_H_
|
||||||
|
#define COSMOPOLITAN_LIBC_INTRIN_SETJMP_INTERNAL_H_
|
||||||
|
#include "libc/limits.h"
|
||||||
|
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||||
|
COSMOPOLITAN_C_START_
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encodes nonzero number for longjmp().
|
||||||
|
*
|
||||||
|
* This is a workaround to the fact that the value has to be non-zero.
|
||||||
|
* So we work around it by dedicating the highest bit to being a flag.
|
||||||
|
*/
|
||||||
|
static inline int EncodeLongjmp(int x) {
|
||||||
|
return x | INT_MIN;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decodes nonzero number returned by setjmp().
|
||||||
|
*
|
||||||
|
* This is a workaround to the fact that the value has to be non-zero.
|
||||||
|
* So we work around it by dedicating the highest bit to being a flag.
|
||||||
|
*/
|
||||||
|
static inline int DecodeSetjmp(int x) {
|
||||||
|
return (int)((unsigned)x << 1) >> 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
COSMOPOLITAN_C_END_
|
||||||
|
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||||
|
#endif /* COSMOPOLITAN_LIBC_INTRIN_SETJMP_INTERNAL_H_ */
|
|
@ -2,27 +2,33 @@
|
||||||
#define COSMOPOLITAN_LIBC_INTRIN_SPINLOCK_H_
|
#define COSMOPOLITAN_LIBC_INTRIN_SPINLOCK_H_
|
||||||
|
|
||||||
#ifdef TINY
|
#ifdef TINY
|
||||||
#define _spinlock(lock) \
|
#define _spinlock(lock) \
|
||||||
do { \
|
do { \
|
||||||
while (__sync_lock_test_and_set(lock, 1)) { \
|
while (__atomic_test_and_set(lock, __ATOMIC_SEQ_CST)) { \
|
||||||
__builtin_ia32_pause(); \
|
__builtin_ia32_pause(); \
|
||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
#else
|
#else
|
||||||
#define _spinlock(lock) \
|
#define _spinlock(lock) \
|
||||||
do { \
|
do { \
|
||||||
for (;;) { \
|
for (;;) { \
|
||||||
typeof(*(lock)) x; \
|
typeof(*(lock)) x; \
|
||||||
__atomic_load(lock, &x, __ATOMIC_RELAXED); \
|
__atomic_load(lock, &x, __ATOMIC_RELAXED); \
|
||||||
if (!x && !__sync_lock_test_and_set(lock, 1)) { \
|
if (!x && !__atomic_test_and_set(lock, __ATOMIC_SEQ_CST)) { \
|
||||||
break; \
|
break; \
|
||||||
} else { \
|
} else { \
|
||||||
__builtin_ia32_pause(); \
|
__builtin_ia32_pause(); \
|
||||||
} \
|
} \
|
||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define _spunlock(lock) __sync_lock_release(lock)
|
#define _spunlock(lock) __atomic_clear(lock, __ATOMIC_RELAXED)
|
||||||
|
|
||||||
|
#define _seizelock(lock) \
|
||||||
|
do { \
|
||||||
|
typeof(*(lock)) x = 1; \
|
||||||
|
__atomic_store(lock, &x, __ATOMIC_SEQ_CST); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
#endif /* COSMOPOLITAN_LIBC_INTRIN_SPINLOCK_H_ */
|
#endif /* COSMOPOLITAN_LIBC_INTRIN_SPINLOCK_H_ */
|
||||||
|
|
|
@ -16,5 +16,6 @@
|
||||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
|
#include "libc/intrin/threaded.internal.h"
|
||||||
|
|
||||||
bool __threaded;
|
bool __threaded;
|
||||||
|
|
10
libc/intrin/threaded.internal.h
Normal file
10
libc/intrin/threaded.internal.h
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
#ifndef COSMOPOLITAN_LIBC_INTRIN_THREADED_INTERNAL_H_
|
||||||
|
#define COSMOPOLITAN_LIBC_INTRIN_THREADED_INTERNAL_H_
|
||||||
|
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||||
|
COSMOPOLITAN_C_START_
|
||||||
|
|
||||||
|
extern bool __threaded;
|
||||||
|
|
||||||
|
COSMOPOLITAN_C_END_
|
||||||
|
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||||
|
#endif /* COSMOPOLITAN_LIBC_INTRIN_THREADED_INTERNAL_H_ */
|
|
@ -1,11 +1,16 @@
|
||||||
#ifndef COSMOPOLITAN_LIBC_RUNTIME_WINTHREAD_INTERNAL_H_
|
#ifndef COSMOPOLITAN_LIBC_RUNTIME_WINTHREAD_INTERNAL_H_
|
||||||
#define COSMOPOLITAN_LIBC_RUNTIME_WINTHREAD_INTERNAL_H_
|
#define COSMOPOLITAN_LIBC_RUNTIME_WINTHREAD_INTERNAL_H_
|
||||||
#include "libc/intrin/tls.h"
|
#include "libc/intrin/tls.h"
|
||||||
|
#include "libc/runtime/runtime.h"
|
||||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||||
COSMOPOLITAN_C_START_
|
COSMOPOLITAN_C_START_
|
||||||
|
|
||||||
struct WinThread {
|
struct WinThread {
|
||||||
int pid;
|
uint32_t tid;
|
||||||
|
int flags;
|
||||||
|
int *ctid;
|
||||||
|
int (*func)(void *);
|
||||||
|
void *arg;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern int __winthread;
|
extern int __winthread;
|
||||||
|
|
|
@ -19,8 +19,6 @@
|
||||||
#include "libc/calls/sigbits.h"
|
#include "libc/calls/sigbits.h"
|
||||||
#include "libc/calls/struct/sigaction.h"
|
#include "libc/calls/struct/sigaction.h"
|
||||||
#include "libc/calls/struct/sigaltstack.h"
|
#include "libc/calls/struct/sigaltstack.h"
|
||||||
#include "libc/intrin/kprintf.h"
|
|
||||||
#include "libc/log/backtrace.internal.h"
|
|
||||||
#include "libc/log/internal.h"
|
#include "libc/log/internal.h"
|
||||||
#include "libc/log/log.h"
|
#include "libc/log/log.h"
|
||||||
#include "libc/macros.internal.h"
|
#include "libc/macros.internal.h"
|
||||||
|
@ -35,10 +33,6 @@ STATIC_YOINK("__get_symbol_by_addr"); /* for asan memory origin */
|
||||||
|
|
||||||
extern const unsigned char __oncrash_thunks[8][11];
|
extern const unsigned char __oncrash_thunks[8][11];
|
||||||
|
|
||||||
static void FreeSigaltstack(void *p) {
|
|
||||||
free(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Installs crash signal handlers.
|
* Installs crash signal handlers.
|
||||||
*
|
*
|
||||||
|
@ -73,7 +67,7 @@ void ShowCrashReports(void) {
|
||||||
ss.ss_flags = 0;
|
ss.ss_flags = 0;
|
||||||
ss.ss_size = SIGSTKSZ;
|
ss.ss_size = SIGSTKSZ;
|
||||||
ss.ss_sp = malloc(SIGSTKSZ);
|
ss.ss_sp = malloc(SIGSTKSZ);
|
||||||
__cxa_atexit(FreeSigaltstack, ss.ss_sp, 0);
|
__cxa_atexit(free, ss.ss_sp, 0);
|
||||||
sa.sa_flags = SA_SIGINFO | SA_NODEFER | SA_ONSTACK;
|
sa.sa_flags = SA_SIGINFO | SA_NODEFER | SA_ONSTACK;
|
||||||
sigfillset(&sa.sa_mask);
|
sigfillset(&sa.sa_mask);
|
||||||
for (i = 0; i < ARRAYLEN(kCrashSigs); ++i) {
|
for (i = 0; i < ARRAYLEN(kCrashSigs); ++i) {
|
||||||
|
|
|
@ -37,7 +37,7 @@ __nt2sysv:
|
||||||
push %rbx
|
push %rbx
|
||||||
push %rdi
|
push %rdi
|
||||||
push %rsi
|
push %rsi
|
||||||
pushf
|
pushf # TODO(jart): Do we need it?
|
||||||
lea -0x80(%rbp),%rdi
|
lea -0x80(%rbp),%rdi
|
||||||
call _savexmm
|
call _savexmm
|
||||||
mov %rcx,%rdi
|
mov %rcx,%rdi
|
||||||
|
|
|
@ -1,2 +1,12 @@
|
||||||
.include "o/libc/nt/codegen.inc"
|
.include "o/libc/nt/codegen.inc"
|
||||||
.imp kernel32,__imp_TerminateThread,TerminateThread,0
|
.imp kernel32,__imp_TerminateThread,TerminateThread,0
|
||||||
|
|
||||||
|
.text.windows
|
||||||
|
TerminateThread:
|
||||||
|
push %rbp
|
||||||
|
mov %rsp,%rbp
|
||||||
|
.profilable
|
||||||
|
mov __imp_TerminateThread(%rip),%rax
|
||||||
|
jmp __sysv2nt
|
||||||
|
.endfn TerminateThread,globl
|
||||||
|
.previous
|
||||||
|
|
|
@ -1208,7 +1208,7 @@ imp 'SystemTimeToFileTime' SystemTimeToFileTime kernel32 0 2
|
||||||
imp 'SystemTimeToTzSpecificLocalTime' SystemTimeToTzSpecificLocalTime kernel32 0
|
imp 'SystemTimeToTzSpecificLocalTime' SystemTimeToTzSpecificLocalTime kernel32 0
|
||||||
imp 'SystemTimeToTzSpecificLocalTimeEx' SystemTimeToTzSpecificLocalTimeEx kernel32 0
|
imp 'SystemTimeToTzSpecificLocalTimeEx' SystemTimeToTzSpecificLocalTimeEx kernel32 0
|
||||||
imp 'TerminateJobObject' TerminateJobObject kernel32 1426
|
imp 'TerminateJobObject' TerminateJobObject kernel32 1426
|
||||||
imp 'TerminateThread' TerminateThread kernel32 0
|
imp 'TerminateThread' TerminateThread kernel32 0 2
|
||||||
imp 'TermsrvAppInstallMode' TermsrvAppInstallMode kernel32 1429
|
imp 'TermsrvAppInstallMode' TermsrvAppInstallMode kernel32 1429
|
||||||
imp 'TermsrvConvertSysRootToUserDir' TermsrvConvertSysRootToUserDir kernel32 1430
|
imp 'TermsrvConvertSysRootToUserDir' TermsrvConvertSysRootToUserDir kernel32 1430
|
||||||
imp 'TermsrvCreateRegEntry' TermsrvCreateRegEntry kernel32 1431
|
imp 'TermsrvCreateRegEntry' TermsrvCreateRegEntry kernel32 1431
|
||||||
|
|
|
@ -119,7 +119,7 @@ static privileged dontinline int arch_prctl_xnu(int code, int64_t addr) {
|
||||||
case ARCH_SET_GS:
|
case ARCH_SET_GS:
|
||||||
asm volatile(CFLAG_ASM("syscall")
|
asm volatile(CFLAG_ASM("syscall")
|
||||||
: CFLAG_CONSTRAINT(failed), "=a"(ax)
|
: CFLAG_CONSTRAINT(failed), "=a"(ax)
|
||||||
: "1"(0x3000003), "D"(addr - 0x8a0 /* wat */)
|
: "1"(0x3000003), "D"(addr - 0x30)
|
||||||
: "rcx", "r11", "memory", "cc");
|
: "rcx", "r11", "memory", "cc");
|
||||||
if (failed) errno = ax, ax = -1;
|
if (failed) errno = ax, ax = -1;
|
||||||
return ax;
|
return ax;
|
||||||
|
|
535
libc/runtime/clone.c
Normal file
535
libc/runtime/clone.c
Normal file
|
@ -0,0 +1,535 @@
|
||||||
|
/*-*- 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 2021 Justine Alexandra Roberts Tunney │
|
||||||
|
│ │
|
||||||
|
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||||
|
│ any purpose with or without fee is hereby granted, provided that the │
|
||||||
|
│ above copyright notice and this permission notice appear in all copies. │
|
||||||
|
│ │
|
||||||
|
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||||
|
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||||
|
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||||
|
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||||
|
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||||
|
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||||
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
|
#include "libc/assert.h"
|
||||||
|
#include "libc/calls/calls.h"
|
||||||
|
#include "libc/calls/internal.h"
|
||||||
|
#include "libc/calls/strace.internal.h"
|
||||||
|
#include "libc/calls/struct/ucontext-netbsd.internal.h"
|
||||||
|
#include "libc/errno.h"
|
||||||
|
#include "libc/intrin/asan.internal.h"
|
||||||
|
#include "libc/intrin/kprintf.h"
|
||||||
|
#include "libc/intrin/spinlock.h"
|
||||||
|
#include "libc/intrin/threaded.internal.h"
|
||||||
|
#include "libc/intrin/tls.h"
|
||||||
|
#include "libc/intrin/winthread.internal.h"
|
||||||
|
#include "libc/nt/runtime.h"
|
||||||
|
#include "libc/nt/thread.h"
|
||||||
|
#include "libc/nt/thunk/msabi.h"
|
||||||
|
#include "libc/runtime/runtime.h"
|
||||||
|
#include "libc/sysv/consts/clone.h"
|
||||||
|
#include "libc/sysv/consts/nr.h"
|
||||||
|
#include "libc/sysv/errfuns.h"
|
||||||
|
#include "libc/thread/freebsd.internal.h"
|
||||||
|
#include "libc/thread/xnu.internal.h"
|
||||||
|
|
||||||
|
STATIC_YOINK("gettid"); // for kprintf()
|
||||||
|
|
||||||
|
#define __NR_thr_new 455
|
||||||
|
#define __NR_clone_linux 56
|
||||||
|
#define __NR__lwp_create 309
|
||||||
|
#define __NR_getcontext_netbsd 307
|
||||||
|
#define __NR__lwp_setprivate 317
|
||||||
|
#define __NR_bsdthread_create 0x02000168
|
||||||
|
#define __NR_thread_fast_set_cthread_self 0x03000003
|
||||||
|
#define PTHREAD_START_CUSTOM_XNU 0x01000000
|
||||||
|
#define LWP_DETACHED 0x00000040
|
||||||
|
#define LWP_SUSPENDED 0x00000080
|
||||||
|
|
||||||
|
uint32_t WinThreadThunk(void *warg);
|
||||||
|
asm(".section\t.text.windows,\"ax\",@progbits\n\t"
|
||||||
|
".local\tWinThreadThunk\n"
|
||||||
|
"WinThreadThunk:\n\t"
|
||||||
|
"xor\t%ebp,%ebp\n\t"
|
||||||
|
"mov\t%rcx,%rdi\n\t"
|
||||||
|
"mov\t%rcx,%rsp\n\t"
|
||||||
|
"jmp\tWinThreadMain\n\t"
|
||||||
|
".size\tWinThreadThunk,.-WinThreadThunk\n\t"
|
||||||
|
".previous");
|
||||||
|
__attribute__((__used__, __no_reorder__))
|
||||||
|
|
||||||
|
static textwindows wontreturn void
|
||||||
|
WinThreadMain(struct WinThread *wt) {
|
||||||
|
int rc;
|
||||||
|
if (wt->flags & CLONE_CHILD_SETTID) {
|
||||||
|
*wt->ctid = wt->tid;
|
||||||
|
}
|
||||||
|
// TlsSetValue(__winthread, wt);
|
||||||
|
rc = wt->func(wt->arg);
|
||||||
|
if (wt->flags & CLONE_CHILD_CLEARTID) {
|
||||||
|
*wt->ctid = 0;
|
||||||
|
}
|
||||||
|
_Exit1(rc);
|
||||||
|
}
|
||||||
|
|
||||||
|
static textwindows int CloneWindows(int (*func)(void *), char *stk,
|
||||||
|
size_t stksz, int flags, void *arg,
|
||||||
|
int *ptid, void *tls, size_t tlssz,
|
||||||
|
int *ctid) {
|
||||||
|
int64_t h;
|
||||||
|
struct WinThread *wt;
|
||||||
|
wt = (struct WinThread *)(((intptr_t)(stk + stksz) -
|
||||||
|
sizeof(struct WinThread)) &
|
||||||
|
-alignof(struct WinThread));
|
||||||
|
wt->flags = flags;
|
||||||
|
wt->ctid = ctid;
|
||||||
|
wt->func = func;
|
||||||
|
wt->arg = arg;
|
||||||
|
if ((h = CreateThread(0, 0, WinThreadThunk, wt, 0, &wt->tid))) {
|
||||||
|
CloseHandle(h);
|
||||||
|
if (flags & CLONE_PARENT_SETTID) {
|
||||||
|
*ptid = wt->tid;
|
||||||
|
}
|
||||||
|
return wt->tid;
|
||||||
|
} else {
|
||||||
|
__releasefd(wt->tid);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void XnuThreadThunk(void *pthread, int machport, void *(*func)(void *),
|
||||||
|
void *arg, intptr_t *stack, unsigned flags);
|
||||||
|
asm(".local\tXnuThreadThunk\n"
|
||||||
|
"XnuThreadThunk:\n\t"
|
||||||
|
"xor\t%ebp,%ebp\n\t"
|
||||||
|
"mov\t%r8,%rsp\n\t"
|
||||||
|
"jmp\tXnuThreadMain\n\t"
|
||||||
|
".size\tXnuThreadThunk,.-XnuThreadThunk");
|
||||||
|
__attribute__((__used__, __no_reorder__))
|
||||||
|
|
||||||
|
static wontreturn void
|
||||||
|
XnuThreadMain(void *pthread, int tid, int (*func)(void *arg), void *arg,
|
||||||
|
intptr_t *sp, unsigned flags) {
|
||||||
|
int rc;
|
||||||
|
sp[1] = tid;
|
||||||
|
_spunlock(sp);
|
||||||
|
if (sp[4] & CLONE_SETTLS) {
|
||||||
|
// XNU uses the same 0x30 offset as the WIN32 TIB x64. They told the
|
||||||
|
// Go team at Google that they Apply stands by our ability to use it
|
||||||
|
// https://github.com/golang/go/issues/23617#issuecomment-376662373
|
||||||
|
asm volatile("syscall"
|
||||||
|
: "=a"(rc)
|
||||||
|
: "0"(__NR_thread_fast_set_cthread_self), "D"(sp[3] - 0x30)
|
||||||
|
: "rcx", "r11", "memory", "cc");
|
||||||
|
}
|
||||||
|
if (sp[4] & CLONE_CHILD_SETTID) {
|
||||||
|
*(int *)sp[2] = tid;
|
||||||
|
}
|
||||||
|
rc = func(arg);
|
||||||
|
if (sp[4] & CLONE_CHILD_CLEARTID) {
|
||||||
|
*(int *)sp[2] = 0;
|
||||||
|
}
|
||||||
|
_Exit1(rc);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int CloneXnu(int (*fn)(void *), char *stk, size_t stksz, int flags,
|
||||||
|
void *arg, int *ptid, void *tls, size_t tlssz, int *ctid) {
|
||||||
|
int rc;
|
||||||
|
bool failed;
|
||||||
|
intptr_t *sp;
|
||||||
|
static bool once;
|
||||||
|
static int broken;
|
||||||
|
if (!once) {
|
||||||
|
if (bsdthread_register(XnuThreadThunk, 0, 0, 0, 0, 0, 0) == -1) {
|
||||||
|
broken = errno;
|
||||||
|
}
|
||||||
|
once = true;
|
||||||
|
}
|
||||||
|
if (broken) {
|
||||||
|
errno = broken;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
sp = (intptr_t *)(stk + stksz);
|
||||||
|
*--sp = 0; // 5 padding
|
||||||
|
*--sp = flags; // 4 clone() flags
|
||||||
|
*--sp = (intptr_t)tls; // 3 thread local storage
|
||||||
|
*--sp = (intptr_t)ctid; // 2 child tid api
|
||||||
|
*--sp = 0; // 1 receives tid
|
||||||
|
*--sp = 0; // 0 lock
|
||||||
|
_seizelock(sp); // TODO: How can we get the tid without locking?
|
||||||
|
if ((rc = bsdthread_create(fn, arg, sp, 0, PTHREAD_START_CUSTOM_XNU)) != -1) {
|
||||||
|
_spinlock(sp);
|
||||||
|
if (flags & CLONE_PARENT_SETTID) {
|
||||||
|
*ptid = sp[1];
|
||||||
|
}
|
||||||
|
rc = sp[1];
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FreebsdThreadThunk(void *sp) wontreturn;
|
||||||
|
asm(".local\tFreebsdThreadThunk\n"
|
||||||
|
"FreebsdThreadThunk:\n\t"
|
||||||
|
"xor\t%ebp,%ebp\n\t"
|
||||||
|
"mov\t%rdi,%rsp\n\t"
|
||||||
|
"jmp\tFreebsdThreadMain\n\t"
|
||||||
|
".size\tFreebsdThreadThunk,.-FreebsdThreadThunk");
|
||||||
|
__attribute__((__used__, __no_reorder__))
|
||||||
|
|
||||||
|
static wontreturn void
|
||||||
|
FreebsdThreadMain(intptr_t *sp) {
|
||||||
|
int rc;
|
||||||
|
if (sp[3] & CLONE_CHILD_SETTID) {
|
||||||
|
*(int *)sp[2] = sp[4];
|
||||||
|
}
|
||||||
|
rc = ((int (*)(intptr_t))sp[0])(sp[1]);
|
||||||
|
if (sp[3] & CLONE_CHILD_CLEARTID) {
|
||||||
|
*(int *)sp[2] = 0;
|
||||||
|
}
|
||||||
|
_Exit1(rc);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int CloneFreebsd(int (*func)(void *), char *stk, size_t stksz, int flags,
|
||||||
|
void *arg, int *ptid, void *tls, size_t tlssz,
|
||||||
|
int *ctid) {
|
||||||
|
int ax;
|
||||||
|
bool failed;
|
||||||
|
int64_t tid;
|
||||||
|
intptr_t *sp;
|
||||||
|
sp = (intptr_t *)(stk + stksz);
|
||||||
|
*--sp = 0; // 5 [padding]
|
||||||
|
*--sp = 0; // 4 [child_tid]
|
||||||
|
*--sp = flags; // 3
|
||||||
|
*--sp = (intptr_t)ctid; // 2
|
||||||
|
*--sp = (intptr_t)arg; // 1
|
||||||
|
*--sp = (intptr_t)func; // 0
|
||||||
|
struct thr_param params = {
|
||||||
|
.start_func = FreebsdThreadThunk,
|
||||||
|
.arg = sp,
|
||||||
|
.stack_base = stk,
|
||||||
|
.stack_size = stksz,
|
||||||
|
.tls_base = flags & CLONE_SETTLS ? tls : 0,
|
||||||
|
.tls_size = flags & CLONE_SETTLS ? tlssz : 0,
|
||||||
|
.child_tid = sp + 4,
|
||||||
|
.parent_tid = &tid,
|
||||||
|
};
|
||||||
|
asm volatile(CFLAG_ASM("syscall")
|
||||||
|
: CFLAG_CONSTRAINT(failed), "=a"(ax)
|
||||||
|
: "1"(__NR_thr_new), "D"(¶ms), "S"(sizeof(params))
|
||||||
|
: "rcx", "rdx", "r8", "r9", "r10", "r11", "memory");
|
||||||
|
if (!failed) {
|
||||||
|
if (flags & CLONE_PARENT_SETTID) {
|
||||||
|
*ptid = tid;
|
||||||
|
}
|
||||||
|
return tid;
|
||||||
|
} else {
|
||||||
|
errno = ax;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct __tfork {
|
||||||
|
void *tf_tcb;
|
||||||
|
int32_t *tf_tid;
|
||||||
|
void *tf_stack;
|
||||||
|
};
|
||||||
|
|
||||||
|
int __tfork(struct __tfork *params, size_t psize, intptr_t *stack);
|
||||||
|
asm(".section\t.privileged,\"ax\",@progbits\n\t"
|
||||||
|
".local\t__tfork\n"
|
||||||
|
"__tfork:\n\t"
|
||||||
|
"push\t$8\n\t"
|
||||||
|
"pop\t%rax\n\t"
|
||||||
|
"mov\t%rdx,%r8\n\t"
|
||||||
|
"syscall\n\t"
|
||||||
|
"jc\t1f\n\t"
|
||||||
|
"test\t%eax,%eax\n\t"
|
||||||
|
"jz\t2f\n\t"
|
||||||
|
"ret\n1:\t"
|
||||||
|
"neg\t%eax\n\t"
|
||||||
|
"ret\n2:\t"
|
||||||
|
"xor\t%ebp,%ebp\n\t"
|
||||||
|
"mov\t%r8,%rsp\n\t"
|
||||||
|
"mov\t%r8,%rdi\n\t"
|
||||||
|
"jmp\tOpenbsdThreadMain\n\t"
|
||||||
|
".size\t__tfork,.-__tfork\n\t"
|
||||||
|
".previous");
|
||||||
|
__attribute__((__used__, __no_reorder__))
|
||||||
|
|
||||||
|
static privileged wontreturn void
|
||||||
|
OpenbsdThreadMain(intptr_t *sp) {
|
||||||
|
int rc;
|
||||||
|
rc = ((int (*)(intptr_t))sp[0])(sp[1]);
|
||||||
|
if (sp[3] & CLONE_CHILD_CLEARTID) {
|
||||||
|
*(int *)sp[2] = 0;
|
||||||
|
}
|
||||||
|
_Exit1(rc);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int CloneOpenbsd(int (*func)(void *), char *stk, size_t stksz, int flags,
|
||||||
|
void *arg, int *ptid, void *tls, size_t tlssz,
|
||||||
|
int *ctid) {
|
||||||
|
int tid;
|
||||||
|
intptr_t *sp;
|
||||||
|
struct __tfork params;
|
||||||
|
sp = (intptr_t *)(stk + stksz);
|
||||||
|
*--sp = flags; // 3
|
||||||
|
*--sp = (intptr_t)ctid; // 2
|
||||||
|
*--sp = (intptr_t)arg; // 1
|
||||||
|
*--sp = (intptr_t)func; // 0
|
||||||
|
params.tf_stack = sp;
|
||||||
|
params.tf_tcb = flags & CLONE_SETTLS ? tls : 0;
|
||||||
|
params.tf_tid = flags & CLONE_CHILD_SETTID ? ctid : 0;
|
||||||
|
if ((tid = __tfork(¶ms, sizeof(params), sp)) > 0) {
|
||||||
|
if (flags & CLONE_PARENT_SETTID) {
|
||||||
|
*ptid = tid;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
errno = -tid;
|
||||||
|
tid = -1;
|
||||||
|
}
|
||||||
|
return tid;
|
||||||
|
}
|
||||||
|
|
||||||
|
static wontreturn void NetbsdThreadMain(void *arg, int (*func)(void *arg),
|
||||||
|
int *tid, int *ctid, int flags) {
|
||||||
|
int rc;
|
||||||
|
if (flags & CLONE_CHILD_SETTID) {
|
||||||
|
*ctid = *tid;
|
||||||
|
}
|
||||||
|
rc = func(arg);
|
||||||
|
if (flags & CLONE_CHILD_CLEARTID) {
|
||||||
|
*ctid = 0;
|
||||||
|
}
|
||||||
|
_Exit1(rc);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int CloneNetbsd(int (*func)(void *), char *stk, size_t stksz, int flags,
|
||||||
|
void *arg, int *ptid, void *tls, size_t tlssz,
|
||||||
|
int *ctid) {
|
||||||
|
// NetBSD has its own clone() and it works, but it's technically a
|
||||||
|
// second-class API, intended to help Linux folks migrate to this!
|
||||||
|
// We put it on the thread's stack, to avoid locking this function
|
||||||
|
// so its stack doesn't scope. The ucontext struct needs 784 bytes
|
||||||
|
bool failed;
|
||||||
|
int ax, *tid;
|
||||||
|
intptr_t dx, sp;
|
||||||
|
static bool once;
|
||||||
|
static int broken;
|
||||||
|
struct ucontext_netbsd *ctx;
|
||||||
|
static struct ucontext_netbsd netbsd_clone_template;
|
||||||
|
if (!once) {
|
||||||
|
asm volatile(CFLAG_ASM("syscall")
|
||||||
|
: CFLAG_CONSTRAINT(failed), "=a"(ax)
|
||||||
|
: "1"(__NR_getcontext_netbsd), "D"(&netbsd_clone_template)
|
||||||
|
: "rcx", "rdx", "r11", "memory");
|
||||||
|
if (failed) {
|
||||||
|
broken = ax;
|
||||||
|
}
|
||||||
|
once = true;
|
||||||
|
}
|
||||||
|
if (broken) {
|
||||||
|
errno = broken;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
sp = (intptr_t)(stk + stksz);
|
||||||
|
sp -= sizeof(int);
|
||||||
|
tid = (int *)sp;
|
||||||
|
sp -= sizeof(*ctx);
|
||||||
|
sp = sp & -alignof(*ctx);
|
||||||
|
ctx = (struct ucontext_netbsd *)sp;
|
||||||
|
memcpy(ctx, &netbsd_clone_template, sizeof(*ctx));
|
||||||
|
ctx->uc_link = 0;
|
||||||
|
ctx->uc_mcontext.rbp = 0;
|
||||||
|
ctx->uc_mcontext.rsp = sp;
|
||||||
|
ctx->uc_mcontext.rip = (intptr_t)NetbsdThreadMain;
|
||||||
|
ctx->uc_mcontext.rdi = (intptr_t)arg;
|
||||||
|
ctx->uc_mcontext.rsi = (intptr_t)func;
|
||||||
|
ctx->uc_mcontext.rdx = (intptr_t)tid;
|
||||||
|
ctx->uc_mcontext.rcx = (intptr_t)ctid;
|
||||||
|
ctx->uc_mcontext.r8 = flags;
|
||||||
|
ctx->uc_flags |= _UC_STACK;
|
||||||
|
ctx->uc_stack.ss_sp = stk;
|
||||||
|
ctx->uc_stack.ss_size = stksz;
|
||||||
|
ctx->uc_stack.ss_flags = 0;
|
||||||
|
if (flags & CLONE_SETTLS) {
|
||||||
|
ctx->uc_flags |= _UC_TLSBASE;
|
||||||
|
ctx->uc_mcontext._mc_tlsbase = (intptr_t)tls;
|
||||||
|
}
|
||||||
|
asm volatile(CFLAG_ASM("syscall")
|
||||||
|
: CFLAG_CONSTRAINT(failed), "=a"(ax), "=d"(dx)
|
||||||
|
: "1"(__NR__lwp_create), "D"(ctx), "S"(LWP_DETACHED), "2"(tid)
|
||||||
|
: "rcx", "r11", "memory");
|
||||||
|
if (!failed) {
|
||||||
|
if (flags & CLONE_PARENT_SETTID) {
|
||||||
|
*ptid = *tid;
|
||||||
|
}
|
||||||
|
return *tid;
|
||||||
|
} else {
|
||||||
|
errno = ax;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int CloneLinux(int (*func)(void *), char *stk, size_t stksz, int flags,
|
||||||
|
void *arg, int *ptid, void *tls, size_t tlssz,
|
||||||
|
int *ctid) {
|
||||||
|
int ax;
|
||||||
|
bool failed;
|
||||||
|
intptr_t *stack;
|
||||||
|
register int *r8 asm("r8") = tls;
|
||||||
|
register int (*r9)(void *) asm("r9") = func;
|
||||||
|
register int *r10 asm("r10") = ctid;
|
||||||
|
stack = (intptr_t *)(stk + stksz);
|
||||||
|
*--stack = (long)arg; // push 1
|
||||||
|
asm volatile("syscall"
|
||||||
|
: "=a"(ax)
|
||||||
|
: "0"(__NR_clone_linux), "D"(flags), "S"(stack), "d"(ptid),
|
||||||
|
"r"(r10), "r"(r8), "r"(r9)
|
||||||
|
: "rcx", "r11", "memory");
|
||||||
|
if (ax > -4096u) {
|
||||||
|
errno = -ax;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (ax) return ax;
|
||||||
|
asm volatile("xor\t%%ebp,%%ebp\n\t"
|
||||||
|
"pop\t%%rdi\n\t" // pop 1
|
||||||
|
"call\t*%0\n\t"
|
||||||
|
"xchg\t%%eax,%%edi\n\t"
|
||||||
|
"jmp\t_Exit1"
|
||||||
|
: /* no outputs */
|
||||||
|
: "r"(r9)
|
||||||
|
: "memory");
|
||||||
|
unreachable;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates thread.
|
||||||
|
*
|
||||||
|
* Threads are created in a detached manner. They currently can't be
|
||||||
|
* synchronized using wait() and posix signals. Threads created by this
|
||||||
|
* function should be synchronized using shared memory operations.
|
||||||
|
*
|
||||||
|
* Any memory that's required by this system call wrapper is allocated
|
||||||
|
* to the top of your stack. This is normally about 64 bytes, although
|
||||||
|
* on NetBSD it's currently 800.
|
||||||
|
*
|
||||||
|
* This function follows the same ABI convention as the Linux userspace
|
||||||
|
* libraries, with a few small changes. The varargs has been removed to
|
||||||
|
* help prevent broken code, and the stack size and tls size parameters
|
||||||
|
* are introduced for compatibility with FreeBSD.
|
||||||
|
*
|
||||||
|
* @param func is your callback function
|
||||||
|
* @param stk points to the bottom of a caller allocated stack, which
|
||||||
|
* must be null when fork() and vfork() equivalent flags are used
|
||||||
|
* and furthermore this must be mmap()'d using MAP_STACK in order
|
||||||
|
* to work on OpenBSD
|
||||||
|
* @param stksz is the size of that stack in bytes which must be zero
|
||||||
|
* if the fork() or vfork() equivalent flags are used it's highly
|
||||||
|
* recommended that this value be GetStackSize(), or else kprintf
|
||||||
|
* and other runtime services providing memory safety can't do as
|
||||||
|
* good and quick of a job; this value must be 4096-aligned, plus
|
||||||
|
* it must be at minimum 4096 bytes in size
|
||||||
|
* @param flags usually has one of
|
||||||
|
* - `SIGCHLD` will delegate to fork()
|
||||||
|
* - `CLONE_VFORK|CLONE_VM|SIGCHLD` means vfork()
|
||||||
|
* - `CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND` for threads
|
||||||
|
* as part high bytes, and the low order byte may optionally contain
|
||||||
|
* a signal e.g. SIGCHLD, to enable parent notification on terminate
|
||||||
|
* although the signal isn't supported on non-Linux and non-NetBSD
|
||||||
|
* at the moment; 'flags' may optionally bitwise or the following:
|
||||||
|
* - `CLONE_PARENT_SETTID` is needed for `ctid` should be set
|
||||||
|
* - `CLONE_CHILD_SETTID` is needed for `ptid` should be set
|
||||||
|
* - `CLONE_SETTLS` is needed to set `%fs` segment to `tls`
|
||||||
|
* @param arg will be passed to your callback
|
||||||
|
* @param ptid lets the parent receive the child thread id;
|
||||||
|
* this parameter is ignored if `CLONE_PARENT_SETTID` is not set
|
||||||
|
* @param tls may be used to set the thread local storage segment;
|
||||||
|
* this parameter is ignored if `CLONE_SETTLS` is not set
|
||||||
|
* @param tlssz is the size of tls in bytes
|
||||||
|
* @param ctid lets the child receive its thread id;
|
||||||
|
* this parameter is ignored if `CLONE_CHILD_SETTID` is not set
|
||||||
|
* @return tid on success and 0 to the child, otherwise -1 w/ errno
|
||||||
|
* @threadsafe
|
||||||
|
*/
|
||||||
|
int clone(int (*func)(void *), void *stk, size_t stksz, int flags, void *arg,
|
||||||
|
int *ptid, void *tls, size_t tlssz, int *ctid) {
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
// let kprintf() switch from pids to tids
|
||||||
|
__threaded = true;
|
||||||
|
|
||||||
|
// verify memory is kosher
|
||||||
|
if (IsAsan() &&
|
||||||
|
((stksz > PAGESIZE &&
|
||||||
|
!__asan_is_valid((char *)stk + PAGESIZE, stksz - PAGESIZE)) ||
|
||||||
|
((flags & CLONE_SETTLS) && !__asan_is_valid(tls, tlssz)) ||
|
||||||
|
((flags & CLONE_SETTLS) && !__asan_is_valid(tls, sizeof(long))) ||
|
||||||
|
((flags & CLONE_PARENT_SETTID) &&
|
||||||
|
!__asan_is_valid(ptid, sizeof(*ptid))) ||
|
||||||
|
((flags & CLONE_CHILD_SETTID) &&
|
||||||
|
!__asan_is_valid(ctid, sizeof(*ctid))))) {
|
||||||
|
rc = efault();
|
||||||
|
}
|
||||||
|
|
||||||
|
// delegate to bona fide clone()
|
||||||
|
else if (IsLinux()) {
|
||||||
|
rc = CloneLinux(func, stk, stksz, flags, arg, ptid, tls, tlssz, ctid);
|
||||||
|
}
|
||||||
|
|
||||||
|
// polyfill fork() and vfork() use cases on platforms without clone()
|
||||||
|
else if ((SupportsWindows() || SupportsBsd()) &&
|
||||||
|
flags == (CLONE_VFORK | CLONE_VM | SIGCHLD)) {
|
||||||
|
if (IsTiny()) {
|
||||||
|
rc = einval();
|
||||||
|
} else if (!arg && !stksz) {
|
||||||
|
return vfork(); // don't log clone()
|
||||||
|
} else {
|
||||||
|
rc = einval();
|
||||||
|
}
|
||||||
|
} else if ((SupportsWindows() || SupportsBsd()) && flags == SIGCHLD) {
|
||||||
|
if (IsTiny()) {
|
||||||
|
rc = eopnotsupp();
|
||||||
|
} else if (!arg && !stksz) {
|
||||||
|
return fork(); // don't log clone()
|
||||||
|
} else {
|
||||||
|
rc = einval();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// we now assume we're creating a thread
|
||||||
|
// these platforms can't do signals the way linux does
|
||||||
|
else if (!IsTiny() &&
|
||||||
|
((stksz < PAGESIZE || (stksz & (PAGESIZE - 1))) ||
|
||||||
|
(flags &
|
||||||
|
~(CLONE_SETTLS | CLONE_PARENT_SETTID | CLONE_CHILD_SETTID)) !=
|
||||||
|
(CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND))) {
|
||||||
|
rc = einval();
|
||||||
|
} else if (IsXnu()) {
|
||||||
|
rc = CloneXnu(func, stk, stksz, flags, arg, ptid, tls, tlssz, ctid);
|
||||||
|
} else if (IsFreebsd()) {
|
||||||
|
rc = CloneFreebsd(func, stk, stksz, flags, arg, ptid, tls, tlssz, ctid);
|
||||||
|
} else if (IsNetbsd()) {
|
||||||
|
rc = CloneNetbsd(func, stk, stksz, flags, arg, ptid, tls, tlssz, ctid);
|
||||||
|
} else if (IsOpenbsd()) {
|
||||||
|
rc = CloneOpenbsd(func, stk, stksz, flags, arg, ptid, tls, tlssz, ctid);
|
||||||
|
}
|
||||||
|
|
||||||
|
// These platforms can't do segment registers like linux does
|
||||||
|
else if (flags & CLONE_SETTLS) {
|
||||||
|
rc = einval();
|
||||||
|
} else if (IsWindows()) {
|
||||||
|
rc = CloneWindows(func, stk, stksz, flags, arg, ptid, tls, tlssz, ctid);
|
||||||
|
} else {
|
||||||
|
rc = enosys();
|
||||||
|
}
|
||||||
|
|
||||||
|
STRACE("clone(%p, %p, %'zu, %#x, %p, %p, %p, %'zu, %p) → %d% m", func, stk,
|
||||||
|
stksz, flags, arg, ptid, tls, tlssz, ctid, rc);
|
||||||
|
return rc;
|
||||||
|
}
|
|
@ -228,8 +228,8 @@ textwindows void WinMainForked(void) {
|
||||||
fds->p[1].handle = fds->__init_p[1].handle = GetStdHandle(kNtStdOutputHandle);
|
fds->p[1].handle = fds->__init_p[1].handle = GetStdHandle(kNtStdOutputHandle);
|
||||||
fds->p[2].handle = fds->__init_p[2].handle = GetStdHandle(kNtStdErrorHandle);
|
fds->p[2].handle = fds->__init_p[2].handle = GetStdHandle(kNtStdErrorHandle);
|
||||||
|
|
||||||
// untrack the forked children of the parent since we marked the
|
// untrack children of parent since we specify with both
|
||||||
// CreateProcess() process handle below as non-inheritable
|
// CreateProcess() and CreateThread() as non-inheritable
|
||||||
for (i = 0; i < fds->n; ++i) {
|
for (i = 0; i < fds->n; ++i) {
|
||||||
if (fds->p[i].kind == kFdProcess) {
|
if (fds->p[i].kind == kFdProcess) {
|
||||||
fds->p[i].kind = 0;
|
fds->p[i].kind = 0;
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
#include "libc/dce.h"
|
#include "libc/dce.h"
|
||||||
#include "libc/errno.h"
|
#include "libc/errno.h"
|
||||||
#include "libc/intrin/asan.internal.h"
|
#include "libc/intrin/asan.internal.h"
|
||||||
|
#include "libc/intrin/asancodes.h"
|
||||||
#include "libc/intrin/describeflags.internal.h"
|
#include "libc/intrin/describeflags.internal.h"
|
||||||
#include "libc/intrin/kprintf.h"
|
#include "libc/intrin/kprintf.h"
|
||||||
#include "libc/intrin/spinlock.h"
|
#include "libc/intrin/spinlock.h"
|
||||||
|
@ -381,6 +382,10 @@ static noasan inline void *Mmap(void *addr, size_t size, int prot, int flags,
|
||||||
if (needguard) {
|
if (needguard) {
|
||||||
if (IsWindows()) _spunlock(&_mmi.lock);
|
if (IsWindows()) _spunlock(&_mmi.lock);
|
||||||
mprotect(p, PAGESIZE, PROT_NONE);
|
mprotect(p, PAGESIZE, PROT_NONE);
|
||||||
|
if (IsAsan()) {
|
||||||
|
__repstosb((void *)(((intptr_t)p >> 3) + 0x7fff8000),
|
||||||
|
kAsanStackOverflow, PAGESIZE / 8);
|
||||||
|
}
|
||||||
if (IsWindows()) _spinlock(&_mmi.lock);
|
if (IsWindows()) _spinlock(&_mmi.lock);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -408,7 +413,29 @@ static noasan inline void *Mmap(void *addr, size_t size, int prot, int flags,
|
||||||
* will be rounded up to FRAMESIZE automatically if MAP_ANONYMOUS
|
* will be rounded up to FRAMESIZE automatically if MAP_ANONYMOUS
|
||||||
* is specified
|
* is specified
|
||||||
* @param prot can have PROT_READ/PROT_WRITE/PROT_EXEC/PROT_NONE/etc.
|
* @param prot can have PROT_READ/PROT_WRITE/PROT_EXEC/PROT_NONE/etc.
|
||||||
* @param flags can have MAP_ANONYMOUS, MAP_SHARED, MAP_PRIVATE, etc.
|
* @param flags should have one of the following masked by `MAP_TYPE`
|
||||||
|
* - `MAP_FILE` in which case `fd != -1` should be the case
|
||||||
|
* - `MAP_PRIVATE` for copy-on-write behavior of writeable pages
|
||||||
|
* - `MAP_SHARED` to create shared memory between processes
|
||||||
|
* - `MAP_STACK` to create a grows-down alloc, where a guard page
|
||||||
|
* is automatically protected at the bottom: FreeBSD's behavior
|
||||||
|
* is polyfilled across platforms; uses MAP_GROWSDOWN on Linux
|
||||||
|
* too for extra oomph (do not use MAP_GROWSDOWN!) and this is
|
||||||
|
* completely mandatory on OpenBSD but helps perf elsewhere if
|
||||||
|
* you need to create 10,000 threads. This flag is the reason
|
||||||
|
* why `STACK_FRAME_UNLIMITED` toil is important, because this
|
||||||
|
* only allocates a 4096-byte guard page, thus we need the GCC
|
||||||
|
* compile-time checks to ensure some char[8192] vars will not
|
||||||
|
* create an undetectable overflow into another thread's stack
|
||||||
|
* Your `flags` may optionally bitwise or any of the following:
|
||||||
|
* - `MAP_FIXED` in which case `addr` becomes more than a hint
|
||||||
|
* - `MAP_FIXED_NOREPLACE` to protect existing maps (Linux-only)
|
||||||
|
* - `MAP_ANONYMOUS` in which case `fd == -1` should be the case
|
||||||
|
* - `MAP_CONCEAL` is FreeBSD/NetBSD/OpenBSD-only
|
||||||
|
* - `MAP_NORESERVE` is Linux/XNU/NetBSD-only
|
||||||
|
* - `MAP_LOCKED` is Linux-only
|
||||||
|
* - `MAP_POPULATE` is Linux-only
|
||||||
|
* - `MAP_NONBLOCK` is Linux-only
|
||||||
* @param fd is an open()'d file descriptor, whose contents shall be
|
* @param fd is an open()'d file descriptor, whose contents shall be
|
||||||
* made available w/ automatic reading at the chosen address and
|
* made available w/ automatic reading at the chosen address and
|
||||||
* must be -1 if MAP_ANONYMOUS is specified
|
* must be -1 if MAP_ANONYMOUS is specified
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
.include "o/libc/sysv/macros.internal.inc"
|
|
||||||
.scall __tfork,0xfff008ffffffffff,globl
|
|
|
@ -1,2 +0,0 @@
|
||||||
.include "o/libc/sysv/macros.internal.inc"
|
|
||||||
.scall getcontext,0x133fff1a5fffffff,globl
|
|
2
libc/sysv/calls/sys_getcontext.s
Normal file
2
libc/sysv/calls/sys_getcontext.s
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
.include "o/libc/sysv/macros.internal.inc"
|
||||||
|
.scall sys_getcontext,0x133fff1a5fffffff,globl,hidden
|
2
libc/sysv/calls/sys_tgkill.s
Normal file
2
libc/sysv/calls/sys_tgkill.s
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
.include "o/libc/sysv/macros.internal.inc"
|
||||||
|
.scall sys_tgkill,0xfffffffffffff0ea,globl,hidden
|
2
libc/sysv/calls/sys_tkill.s
Normal file
2
libc/sysv/calls/sys_tkill.s
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
.include "o/libc/sysv/macros.internal.inc"
|
||||||
|
.scall sys_tkill,0x13e0771b121690c8,globl,hidden
|
|
@ -1,2 +0,0 @@
|
||||||
.include "o/libc/sysv/macros.internal.inc"
|
|
||||||
.scall tgkill,0xfffffffffffff0ea,globl
|
|
|
@ -1,2 +0,0 @@
|
||||||
.include "o/libc/sysv/macros.internal.inc"
|
|
||||||
.scall tkill,0xfffffffffffff0c8,globl
|
|
|
@ -218,7 +218,7 @@ syscon compat O_LARGEFILE 0 0 0 0 0 0 #
|
||||||
# the revolutionary praxis of malloc()
|
# the revolutionary praxis of malloc()
|
||||||
#
|
#
|
||||||
# group name GNU/Systemd XNU's Not UNIX! FreeBSD OpenBSD NetBSD The New Technology Commentary
|
# group name GNU/Systemd XNU's Not UNIX! FreeBSD OpenBSD NetBSD The New Technology Commentary
|
||||||
syscon compat MAP_FILE 0 0 0 0 0 0 # consensus
|
syscon mmap MAP_FILE 0 0 0 0 0 0 # consensus
|
||||||
syscon mmap MAP_SHARED 1 1 1 1 1 1 # forced consensus & faked nt
|
syscon mmap MAP_SHARED 1 1 1 1 1 1 # forced consensus & faked nt
|
||||||
syscon mmap MAP_PRIVATE 2 2 2 2 2 2 # forced consensus & faked nt
|
syscon mmap MAP_PRIVATE 2 2 2 2 2 2 # forced consensus & faked nt
|
||||||
syscon mmap MAP_STACK 6 6 6 6 6 6 # our definition
|
syscon mmap MAP_STACK 6 6 6 6 6 6 # our definition
|
||||||
|
@ -2318,9 +2318,7 @@ syscon nr __NR_access_extended 0xfff 0x200011c 0xfff 0xfff 0xfff 0xff
|
||||||
syscon nr __NR_audit_session_join 0xfff 0x20001ad 0xfff 0xfff 0xfff 0xfff
|
syscon nr __NR_audit_session_join 0xfff 0x20001ad 0xfff 0xfff 0xfff 0xfff
|
||||||
syscon nr __NR_audit_session_port 0xfff 0x20001b0 0xfff 0xfff 0xfff 0xfff
|
syscon nr __NR_audit_session_port 0xfff 0x20001b0 0xfff 0xfff 0xfff 0xfff
|
||||||
syscon nr __NR_audit_session_self 0xfff 0x20001ac 0xfff 0xfff 0xfff 0xfff
|
syscon nr __NR_audit_session_self 0xfff 0x20001ac 0xfff 0xfff 0xfff 0xfff
|
||||||
syscon nr __NR_bsdthread_create 0xfff 0x2000168 0xfff 0xfff 0xfff 0xfff
|
|
||||||
syscon nr __NR_bsdthread_ctl 0xfff 0x20001de 0xfff 0xfff 0xfff 0xfff
|
syscon nr __NR_bsdthread_ctl 0xfff 0x20001de 0xfff 0xfff 0xfff 0xfff
|
||||||
syscon nr __NR_bsdthread_register 0xfff 0x200016e 0xfff 0xfff 0xfff 0xfff
|
|
||||||
syscon nr __NR_bsdthread_terminate 0xfff 0x2000169 0xfff 0xfff 0xfff 0xfff
|
syscon nr __NR_bsdthread_terminate 0xfff 0x2000169 0xfff 0xfff 0xfff 0xfff
|
||||||
syscon nr __NR_change_fdguard_np 0xfff 0x20001bc 0xfff 0xfff 0xfff 0xfff
|
syscon nr __NR_change_fdguard_np 0xfff 0x20001bc 0xfff 0xfff 0xfff 0xfff
|
||||||
syscon nr __NR_chmod_extended 0xfff 0x200011a 0xfff 0xfff 0xfff 0xfff
|
syscon nr __NR_chmod_extended 0xfff 0x200011a 0xfff 0xfff 0xfff 0xfff
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
#include "libc/sysv/consts/syscon.internal.h"
|
|
||||||
.syscon nr,__NR_bsdthread_create,0xfff,0x2000168,0xfff,0xfff,0xfff,0xfff
|
|
|
@ -1,2 +0,0 @@
|
||||||
#include "libc/sysv/consts/syscon.internal.h"
|
|
||||||
.syscon nr,__NR_bsdthread_register,0xfff,0x200016e,0xfff,0xfff,0xfff,0xfff
|
|
|
@ -404,9 +404,7 @@ extern const long __NR_access_extended;
|
||||||
extern const long __NR_audit_session_join;
|
extern const long __NR_audit_session_join;
|
||||||
extern const long __NR_audit_session_port;
|
extern const long __NR_audit_session_port;
|
||||||
extern const long __NR_audit_session_self;
|
extern const long __NR_audit_session_self;
|
||||||
extern const long __NR_bsdthread_create;
|
|
||||||
extern const long __NR_bsdthread_ctl;
|
extern const long __NR_bsdthread_ctl;
|
||||||
extern const long __NR_bsdthread_register;
|
|
||||||
extern const long __NR_bsdthread_terminate;
|
extern const long __NR_bsdthread_terminate;
|
||||||
extern const long __NR_change_fdguard_np;
|
extern const long __NR_change_fdguard_np;
|
||||||
extern const long __NR_chmod_extended;
|
extern const long __NR_chmod_extended;
|
||||||
|
@ -1101,7 +1099,6 @@ COSMOPOLITAN_C_END_
|
||||||
#define __NR_pidfd_send_signal SYMBOLIC(__NR_pidfd_send_signal)
|
#define __NR_pidfd_send_signal SYMBOLIC(__NR_pidfd_send_signal)
|
||||||
#define __NR_io_uring_setup SYMBOLIC(__NR_io_uring_setup)
|
#define __NR_io_uring_setup SYMBOLIC(__NR_io_uring_setup)
|
||||||
#define __NR_io_uring_enter SYMBOLIC(__NR_io_uring_enter)
|
#define __NR_io_uring_enter SYMBOLIC(__NR_io_uring_enter)
|
||||||
#define __NR_io_uring_register SYMBOLIC(__NR_io_uring_register)
|
|
||||||
#define __NR_pledge SYMBOLIC(__NR_pledge)
|
#define __NR_pledge SYMBOLIC(__NR_pledge)
|
||||||
#define __NR_msyscall SYMBOLIC(__NR_msyscall)
|
#define __NR_msyscall SYMBOLIC(__NR_msyscall)
|
||||||
#define __NR_ktrace SYMBOLIC(__NR_ktrace)
|
#define __NR_ktrace SYMBOLIC(__NR_ktrace)
|
||||||
|
@ -1173,7 +1170,6 @@ COSMOPOLITAN_C_END_
|
||||||
#define __NR_audit_session_join SYMBOLIC(__NR_audit_session_join)
|
#define __NR_audit_session_join SYMBOLIC(__NR_audit_session_join)
|
||||||
#define __NR_audit_session_port SYMBOLIC(__NR_audit_session_port)
|
#define __NR_audit_session_port SYMBOLIC(__NR_audit_session_port)
|
||||||
#define __NR_audit_session_self SYMBOLIC(__NR_audit_session_self)
|
#define __NR_audit_session_self SYMBOLIC(__NR_audit_session_self)
|
||||||
#define __NR_bsdthread_create SYMBOLIC(__NR_bsdthread_create)
|
|
||||||
#define __NR_bsdthread_ctl SYMBOLIC(__NR_bsdthread_ctl)
|
#define __NR_bsdthread_ctl SYMBOLIC(__NR_bsdthread_ctl)
|
||||||
#define __NR_bsdthread_register SYMBOLIC(__NR_bsdthread_register)
|
#define __NR_bsdthread_register SYMBOLIC(__NR_bsdthread_register)
|
||||||
#define __NR_bsdthread_terminate SYMBOLIC(__NR_bsdthread_terminate)
|
#define __NR_bsdthread_terminate SYMBOLIC(__NR_bsdthread_terminate)
|
||||||
|
|
|
@ -99,7 +99,7 @@ scall __sys_wait4 0x1c100b007200703d globl hidden
|
||||||
scall sys_kill 0x02507a025202503e globl hidden # kill(pid, sig, 1) b/c xnu
|
scall sys_kill 0x02507a025202503e globl hidden # kill(pid, sig, 1) b/c xnu
|
||||||
scall sys_killpg 0xffffff092fffffff globl hidden
|
scall sys_killpg 0xffffff092fffffff globl hidden
|
||||||
scall sys_clone 0x11fffffffffff038 globl hidden
|
scall sys_clone 0x11fffffffffff038 globl hidden
|
||||||
scall tkill 0xfffffffffffff0c8 globl
|
scall sys_tkill 0x13e0771b121690c8 globl hidden # thr_kill() on freebsd; _lwp_kill() on netbsd; thrkill() on openbsd where arg3 should be 0; bsdthread_terminate() on XNU which only has 1 arg
|
||||||
scall futex 0xfff053fffffff0ca globl
|
scall futex 0xfff053fffffff0ca globl
|
||||||
scall set_robust_list 0xfffffffffffff111 globl
|
scall set_robust_list 0xfffffffffffff111 globl
|
||||||
scall get_robust_list 0xfffffffffffff112 globl
|
scall get_robust_list 0xfffffffffffff112 globl
|
||||||
|
@ -265,7 +265,7 @@ scall clock_settime 0x1ac0580e9ffff0e3 globl
|
||||||
scall sys_clock_gettime 0x1ab0570e8ffff0e4 globl hidden # Linux 2.6+ (c. 2003); XNU uses magic address
|
scall sys_clock_gettime 0x1ab0570e8ffff0e4 globl hidden # Linux 2.6+ (c. 2003); XNU uses magic address
|
||||||
scall clock_getres 0x1ad0590eaffff0e5 globl
|
scall clock_getres 0x1ad0590eaffff0e5 globl
|
||||||
scall clock_nanosleep 0xffffff0f4ffff0e6 globl
|
scall clock_nanosleep 0xffffff0f4ffff0e6 globl
|
||||||
scall tgkill 0xfffffffffffff0ea globl
|
scall sys_tgkill 0xfffffffffffff0ea globl hidden
|
||||||
scall mbind 0xfffffffffffff0ed globl
|
scall mbind 0xfffffffffffff0ed globl
|
||||||
scall set_mempolicy 0xfffffffffffff0ee globl
|
scall set_mempolicy 0xfffffffffffff0ee globl
|
||||||
scall get_mempolicy 0xfffffffffffff0ef globl
|
scall get_mempolicy 0xfffffffffffff0ef globl
|
||||||
|
@ -658,7 +658,7 @@ scall fhlink 0xffffff235fffffff globl
|
||||||
scall fhlinkat 0xffffff236fffffff globl
|
scall fhlinkat 0xffffff236fffffff globl
|
||||||
scall fhreadlink 0xffffff237fffffff globl
|
scall fhreadlink 0xffffff237fffffff globl
|
||||||
scall getaudit 0xffffff1c1fffffff globl
|
scall getaudit 0xffffff1c1fffffff globl
|
||||||
scall getcontext 0x133fff1a5fffffff globl
|
scall sys_getcontext 0x133fff1a5fffffff globl hidden
|
||||||
#scall getdomainname 0xffff00a2ffffffff globl
|
#scall getdomainname 0xffff00a2ffffffff globl
|
||||||
scall getfhat 0xffffff234fffffff globl
|
scall getfhat 0xffffff234fffffff globl
|
||||||
scall gethostid 0xffffff08efffffff globl
|
scall gethostid 0xffffff08efffffff globl
|
||||||
|
@ -756,7 +756,6 @@ scall wait 0xffffff054fffffff globl
|
||||||
scall wait6 0x1e1fff214fffffff globl
|
scall wait6 0x1e1fff214fffffff globl
|
||||||
scall yield 0xffffff141fffffff globl
|
scall yield 0xffffff141fffffff globl
|
||||||
#──────────────────────────OPENBSD───────────────────────────
|
#──────────────────────────OPENBSD───────────────────────────
|
||||||
scall __tfork 0xfff008ffffffffff globl
|
|
||||||
scall __thrsleep 0xfff05effffffffff globl
|
scall __thrsleep 0xfff05effffffffff globl
|
||||||
scall __thrwakeup 0xfff12dffffffffff globl
|
scall __thrwakeup 0xfff12dffffffffff globl
|
||||||
scall __threxit 0xfff12effffffffff globl
|
scall __threxit 0xfff12effffffffff globl
|
||||||
|
|
|
@ -1,400 +0,0 @@
|
||||||
/*-*- 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 2021 Justine Alexandra Roberts Tunney │
|
|
||||||
│ │
|
|
||||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
|
||||||
│ any purpose with or without fee is hereby granted, provided that the │
|
|
||||||
│ above copyright notice and this permission notice appear in all copies. │
|
|
||||||
│ │
|
|
||||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
|
||||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
|
||||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
|
||||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
|
||||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
|
||||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
|
||||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
|
||||||
#include "libc/bits/asmflag.h"
|
|
||||||
#include "libc/bits/weaken.h"
|
|
||||||
#include "libc/calls/internal.h"
|
|
||||||
#include "libc/calls/sig.internal.h"
|
|
||||||
#include "libc/calls/strace.internal.h"
|
|
||||||
#include "libc/calls/struct/ucontext-netbsd.internal.h"
|
|
||||||
#include "libc/dce.h"
|
|
||||||
#include "libc/errno.h"
|
|
||||||
#include "libc/intrin/asan.internal.h"
|
|
||||||
#include "libc/intrin/spinlock.h"
|
|
||||||
#include "libc/intrin/tls.h"
|
|
||||||
#include "libc/intrin/winthread.internal.h"
|
|
||||||
#include "libc/mem/mem.h"
|
|
||||||
#include "libc/nexgen32e/nt2sysv.h"
|
|
||||||
#include "libc/nexgen32e/stackframe.h"
|
|
||||||
#include "libc/nt/runtime.h"
|
|
||||||
#include "libc/nt/thread.h"
|
|
||||||
#include "libc/runtime/runtime.h"
|
|
||||||
#include "libc/sysv/consts/clone.h"
|
|
||||||
#include "libc/sysv/consts/o.h"
|
|
||||||
#include "libc/sysv/consts/sicode.h"
|
|
||||||
#include "libc/sysv/consts/sig.h"
|
|
||||||
#include "libc/sysv/errfuns.h"
|
|
||||||
#include "libc/thread/freebsd.internal.h"
|
|
||||||
#include "libc/thread/openbsd.internal.h"
|
|
||||||
|
|
||||||
// TODO(jart): work in progress
|
|
||||||
|
|
||||||
STATIC_YOINK("gettid"); // for kprintf()
|
|
||||||
|
|
||||||
#define __NR_thr_new 455
|
|
||||||
#define __NR___tfork 8
|
|
||||||
#define __NR_clone_linux 56
|
|
||||||
#define __NR__lwp_create 309
|
|
||||||
#define __NR_getcontext_netbsd 307
|
|
||||||
#define __NR__lwp_setprivate 317
|
|
||||||
|
|
||||||
extern bool __threaded;
|
|
||||||
|
|
||||||
static struct Cloner {
|
|
||||||
_Alignas(64) char lock;
|
|
||||||
_Alignas(64) int flags;
|
|
||||||
int64_t tid;
|
|
||||||
int (*func)(void *);
|
|
||||||
void *arg;
|
|
||||||
void *stack;
|
|
||||||
int *ctid;
|
|
||||||
int *ptid;
|
|
||||||
} __cloner;
|
|
||||||
|
|
||||||
static textwindows uint32_t WinThreadMain(void *notused) {
|
|
||||||
intptr_t rdi, rdx;
|
|
||||||
int (*func)(void *);
|
|
||||||
void *arg, *stack;
|
|
||||||
struct WinThread *wt;
|
|
||||||
int exitcode, tid, flags, *ctid;
|
|
||||||
tid = __cloner.tid;
|
|
||||||
arg = __cloner.arg;
|
|
||||||
func = __cloner.func;
|
|
||||||
ctid = __cloner.ctid;
|
|
||||||
flags = __cloner.flags;
|
|
||||||
stack = __cloner.stack;
|
|
||||||
_spunlock(&__cloner.lock);
|
|
||||||
wt = calloc(1, sizeof(struct WinThread));
|
|
||||||
wt->pid = tid;
|
|
||||||
TlsSetValue(__winthread, wt);
|
|
||||||
if (flags & CLONE_CHILD_SETTID) *ctid = tid;
|
|
||||||
asm volatile("push\t%%rbp\n\t"
|
|
||||||
"mov\t%%rsp,%%r15\n\t"
|
|
||||||
"xor\t%%ebp,%%ebp\n\t"
|
|
||||||
"xchg\t%%rax,%%rsp\n\t"
|
|
||||||
"call\t*%2\n\t"
|
|
||||||
"mov\t%%rbx,%%rbp\n\t"
|
|
||||||
"mov\t%%r15,%%rsp\n\t"
|
|
||||||
"pop\t%%rbp"
|
|
||||||
: "=a"(exitcode), "=D"(rdi), "=d"(rdx)
|
|
||||||
: "0"(stack), "1"(arg), "2"(func)
|
|
||||||
: "rbx", "rcx", "rsi", "r8", "r9", "r10", "r11", "r15",
|
|
||||||
"memory");
|
|
||||||
if (flags & CLONE_CHILD_CLEARTID) *ctid = 0;
|
|
||||||
__releasefd(tid);
|
|
||||||
free(wt);
|
|
||||||
return exitcode;
|
|
||||||
}
|
|
||||||
|
|
||||||
static textwindows int CloneWindows(int (*func)(void *), void *stk,
|
|
||||||
size_t stksz, int flags, void *arg,
|
|
||||||
int *ptid, void *tls, size_t tlssz,
|
|
||||||
int *ctid) {
|
|
||||||
int tid;
|
|
||||||
int64_t hand;
|
|
||||||
uint32_t wintid;
|
|
||||||
if ((tid = __reservefd(-1)) == -1) return -1;
|
|
||||||
_spinlock(&__cloner.lock);
|
|
||||||
__cloner.tid = tid;
|
|
||||||
__cloner.arg = arg;
|
|
||||||
__cloner.func = func;
|
|
||||||
__cloner.ctid = ctid;
|
|
||||||
__cloner.flags = flags;
|
|
||||||
__cloner.stack = (char *)stk + stksz;
|
|
||||||
if (!(hand = CreateThread(0, 0, NT2SYSV(WinThreadMain), 0, 0, &wintid))) {
|
|
||||||
_spunlock(&__cloner.lock);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (flags & CLONE_CHILD_SETTID) *ctid = tid;
|
|
||||||
if (flags & CLONE_PARENT_SETTID) *ptid = tid;
|
|
||||||
// XXX: this should be tracked in a separate data structure
|
|
||||||
g_fds.p[tid].kind = kFdProcess;
|
|
||||||
g_fds.p[tid].handle = hand;
|
|
||||||
g_fds.p[tid].flags = O_CLOEXEC;
|
|
||||||
g_fds.p[tid].zombie = false;
|
|
||||||
return tid;
|
|
||||||
}
|
|
||||||
|
|
||||||
static dontinline wontreturn void BsdThreadMain(void *unused) {
|
|
||||||
void *arg;
|
|
||||||
int (*func)(void *);
|
|
||||||
int tid, flags, exitcode, *ctid;
|
|
||||||
asm("xor\t%ebp,%ebp");
|
|
||||||
tid = __cloner.tid;
|
|
||||||
arg = __cloner.arg;
|
|
||||||
func = __cloner.func;
|
|
||||||
ctid = __cloner.ctid;
|
|
||||||
flags = __cloner.flags;
|
|
||||||
_spunlock(&__cloner.lock);
|
|
||||||
if (flags & CLONE_CHILD_SETTID) *ctid = tid;
|
|
||||||
exitcode = func(arg);
|
|
||||||
if (flags & CLONE_CHILD_CLEARTID) *ctid = 0;
|
|
||||||
_Exit1(exitcode);
|
|
||||||
}
|
|
||||||
|
|
||||||
static privileged noasan int CloneFreebsd(int (*func)(void *), void *stk,
|
|
||||||
size_t stksz, int flags, void *arg,
|
|
||||||
int *ptid, void *tls, size_t tlssz,
|
|
||||||
int *ctid) {
|
|
||||||
int ax;
|
|
||||||
bool failed;
|
|
||||||
int64_t tid;
|
|
||||||
struct thr_param params = {0};
|
|
||||||
_spinlock(&__cloner.lock);
|
|
||||||
__cloner.arg = arg;
|
|
||||||
__cloner.func = func;
|
|
||||||
__cloner.ctid = ctid;
|
|
||||||
__cloner.flags = flags;
|
|
||||||
params.start_func = BsdThreadMain;
|
|
||||||
params.stack_base = stk;
|
|
||||||
params.stack_size = stksz;
|
|
||||||
params.tls_base = flags & CLONE_SETTLS ? tls : 0;
|
|
||||||
params.tls_size = flags & CLONE_SETTLS ? tlssz : 0;
|
|
||||||
params.child_tid = &__cloner.tid;
|
|
||||||
params.parent_tid = &tid;
|
|
||||||
asm volatile(CFLAG_ASM("syscall")
|
|
||||||
: CFLAG_CONSTRAINT(failed), "=a"(ax)
|
|
||||||
: "1"(__NR_thr_new), "D"(¶ms), "S"(sizeof(params))
|
|
||||||
: "rcx", "r11", "memory", "cc");
|
|
||||||
if (!failed) {
|
|
||||||
if (flags & CLONE_PARENT_SETTID) *ptid = tid;
|
|
||||||
return tid;
|
|
||||||
} else {
|
|
||||||
errno = ax;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static privileged noasan int CloneOpenbsd(int (*func)(void *), char *stk,
|
|
||||||
size_t stksz, int flags, void *arg,
|
|
||||||
int *ptid, void *tls, size_t tlssz,
|
|
||||||
int *ctid) {
|
|
||||||
int ax;
|
|
||||||
bool failed;
|
|
||||||
struct __tfork params;
|
|
||||||
_spinlock(&__cloner.lock);
|
|
||||||
__cloner.arg = arg;
|
|
||||||
__cloner.func = func;
|
|
||||||
__cloner.ctid = ctid;
|
|
||||||
__cloner.flags = flags;
|
|
||||||
__cloner.tid = 0;
|
|
||||||
asm volatile("" ::: "memory");
|
|
||||||
params.tf_tid = (int *)&__cloner.tid;
|
|
||||||
params.tf_tcb = flags & CLONE_SETTLS ? tls : 0;
|
|
||||||
// we need openbsd:stackbound because openbsd kernel enforces rsp must
|
|
||||||
// be on interval [stack, stack+size) thus the top address is an error
|
|
||||||
// furthermore this needs to be allocated using MAP_STACK OR GROWSDOWN
|
|
||||||
params.tf_stack = (void *)((intptr_t)((char *)stk + stksz - 1) & -16);
|
|
||||||
asm volatile(CFLAG_ASM("syscall")
|
|
||||||
: CFLAG_CONSTRAINT(failed), "=a"(ax)
|
|
||||||
: "1"(__NR___tfork), "D"(¶ms), "S"(sizeof(params))
|
|
||||||
: "r11", "memory", "cc");
|
|
||||||
if (failed) {
|
|
||||||
errno = ax;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (ax) {
|
|
||||||
if (flags & CLONE_PARENT_SETTID) *ptid = ax;
|
|
||||||
return ax;
|
|
||||||
}
|
|
||||||
BsdThreadMain(0);
|
|
||||||
unreachable;
|
|
||||||
}
|
|
||||||
|
|
||||||
static privileged noasan int CloneNetbsd(int (*func)(void *), void *stk,
|
|
||||||
size_t stksz, int flags, void *arg,
|
|
||||||
int *ptid, void *tls, size_t tlssz,
|
|
||||||
int *ctid) {
|
|
||||||
int ax, tid;
|
|
||||||
bool failed;
|
|
||||||
intptr_t *stack;
|
|
||||||
struct ucontext_netbsd ctx;
|
|
||||||
asm volatile(CFLAG_ASM("syscall")
|
|
||||||
: CFLAG_CONSTRAINT(failed), "=a"(ax)
|
|
||||||
: "1"(__NR_getcontext_netbsd), "D"(&ctx)
|
|
||||||
: "rcx", "r11", "memory", "cc");
|
|
||||||
if (failed) {
|
|
||||||
errno = ax;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
stack = (intptr_t *)((intptr_t)((char *)stk + stksz) & -16);
|
|
||||||
*--stack = (intptr_t)_Exit1;
|
|
||||||
ctx.uc_link = 0;
|
|
||||||
ctx.uc_mcontext.rip = (intptr_t)func;
|
|
||||||
ctx.uc_mcontext.rdi = (intptr_t)arg;
|
|
||||||
ctx.uc_mcontext.rsp = (intptr_t)stack;
|
|
||||||
ctx.uc_mcontext.rbp = 0;
|
|
||||||
ctx.uc_flags |= _UC_STACK;
|
|
||||||
ctx.uc_stack.ss_sp = stk;
|
|
||||||
ctx.uc_stack.ss_size = stksz;
|
|
||||||
ctx.uc_stack.ss_flags = 0;
|
|
||||||
if (flags & CLONE_SETTLS) {
|
|
||||||
ctx.uc_flags |= _UC_TLSBASE;
|
|
||||||
ctx.uc_mcontext._mc_tlsbase = (intptr_t)tls;
|
|
||||||
}
|
|
||||||
asm volatile("" ::: "memory");
|
|
||||||
asm volatile(CFLAG_ASM("syscall")
|
|
||||||
: CFLAG_CONSTRAINT(failed), "=a"(ax)
|
|
||||||
: "1"(__NR__lwp_create), "D"(&ctx), "S"(flags), "d"(&tid)
|
|
||||||
: "rcx", "r11", "memory", "cc");
|
|
||||||
if (failed) {
|
|
||||||
errno = ax;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (flags & CLONE_PARENT_SETTID) *ptid = ax;
|
|
||||||
if (flags & CLONE_CHILD_SETTID) *ctid = ax;
|
|
||||||
return tid;
|
|
||||||
}
|
|
||||||
|
|
||||||
static privileged int CloneLinux(int (*func)(void *), void *stk, size_t stksz,
|
|
||||||
int flags, void *arg, int *ptid, void *tls,
|
|
||||||
size_t tlssz, int *ctid) {
|
|
||||||
int ax;
|
|
||||||
bool failed;
|
|
||||||
intptr_t *stack;
|
|
||||||
register int *r8 asm("r8") = tls;
|
|
||||||
register int (*r9)(void *) asm("r9") = func;
|
|
||||||
register int *r10 asm("r10") = ctid;
|
|
||||||
stack = (intptr_t *)((long)((char *)stk + stksz) & -16);
|
|
||||||
*--stack = (long)arg; // push 1
|
|
||||||
asm volatile("syscall"
|
|
||||||
: "=a"(ax)
|
|
||||||
: "0"(__NR_clone_linux), "D"(flags), "S"(stack), "d"(ptid),
|
|
||||||
"r"(r10), "r"(r8), "r"(r9)
|
|
||||||
: "rcx", "r11", "memory");
|
|
||||||
if (ax > -4096u) {
|
|
||||||
errno = -ax;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (ax) return ax;
|
|
||||||
asm volatile("xor\t%%ebp,%%ebp\n\t"
|
|
||||||
"pop\t%%rdi\n\t" // pop 1
|
|
||||||
"call\t*%0\n\t"
|
|
||||||
"xchg\t%%eax,%%edi\n\t"
|
|
||||||
"jmp\t_Exit1"
|
|
||||||
: /* no outputs */
|
|
||||||
: "r"(r9)
|
|
||||||
: "memory");
|
|
||||||
unreachable;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates thread.
|
|
||||||
*
|
|
||||||
* This function follows the same ABI convention as the Linux userspace
|
|
||||||
* libraries, with a few small changes. The varargs has been removed to
|
|
||||||
* help prevent broken code, and the stack size and tls size parameters
|
|
||||||
* are introduced for compatibility with FreeBSD.
|
|
||||||
*
|
|
||||||
* @param func is your callback function
|
|
||||||
* @param stk points to the bottom of a caller allocated stack, which
|
|
||||||
* must be null when fork() and vfork() equivalent flags are used
|
|
||||||
* and furthermore this must be mmap()'d using MAP_STACK in order
|
|
||||||
* to work on OpenBSD
|
|
||||||
* @param stksz is the size of that stack in bytes which must be zero
|
|
||||||
* if the fork() or vfork() equivalent flags are used it's highly
|
|
||||||
* recommended that this value be GetStackSize(), or else kprintf
|
|
||||||
* and other runtime services providing memory safety can't do as
|
|
||||||
* good and quick of a job at that
|
|
||||||
* @param flags usually has one of
|
|
||||||
* - `SIGCHLD` will delegate to fork()
|
|
||||||
* - `CLONE_VFORK|CLONE_VM|SIGCHLD` means vfork()
|
|
||||||
* - `CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND` for threads
|
|
||||||
* as part high bytes, and the low order byte may optionally contain
|
|
||||||
* a signal e.g. SIGCHLD, to enable parent notification on terminate
|
|
||||||
* although the signal isn't supported on non-Linux and non-NetBSD
|
|
||||||
* at the moment; 'flags' may optionally bitwise or the following:
|
|
||||||
* - `CLONE_PARENT_SETTID` is needed for `ctid` should be set
|
|
||||||
* - `CLONE_CHILD_SETTID` is needed for `ptid` should be set
|
|
||||||
* - `CLONE_SETTLS` is needed to set `%fs` segment to `tls`
|
|
||||||
* @param arg will be passed to your callback
|
|
||||||
* @param ptid lets the parent receive the child thread id;
|
|
||||||
* this parameter is ignored if `CLONE_PARENT_SETTID` is not set
|
|
||||||
* @param tls may be used to set the thread local storage segment;
|
|
||||||
* this parameter is ignored if `CLONE_SETTLS` is not set
|
|
||||||
* @param tlssz is the size of tls in bytes
|
|
||||||
* @param ctid lets the child receive its thread id;
|
|
||||||
* this parameter is ignored if `CLONE_CHILD_SETTID` is not set
|
|
||||||
* @return tid on success and 0 to the child, otherwise -1 w/ errno
|
|
||||||
* @returnstwice
|
|
||||||
* @threadsafe
|
|
||||||
*/
|
|
||||||
privileged int clone(int (*func)(void *), void *stk, size_t stksz, int flags,
|
|
||||||
void *arg, int *ptid, void *tls, size_t tlssz, int *ctid) {
|
|
||||||
int rc;
|
|
||||||
__threaded = true;
|
|
||||||
if (IsAsan() &&
|
|
||||||
(!__asan_is_valid(stk, stksz) ||
|
|
||||||
((flags & CLONE_SETTLS) && !__asan_is_valid(tls, tlssz)) ||
|
|
||||||
((flags & CLONE_SETTLS) && !__asan_is_valid(tls, sizeof(long))) ||
|
|
||||||
((flags & CLONE_PARENT_SETTID) &&
|
|
||||||
!__asan_is_valid(ptid, sizeof(*ptid))) ||
|
|
||||||
((flags & CLONE_CHILD_SETTID) &&
|
|
||||||
!__asan_is_valid(ctid, sizeof(*ctid))))) {
|
|
||||||
rc = efault();
|
|
||||||
} else if (IsLinux()) {
|
|
||||||
rc = CloneLinux(func, stk, stksz, flags, arg, ptid, tls, tlssz, ctid);
|
|
||||||
} else if (IsNetbsd()) {
|
|
||||||
rc = CloneNetbsd(func, stk, stksz, flags, arg, ptid, tls, tlssz, ctid);
|
|
||||||
}
|
|
||||||
|
|
||||||
// polyfill fork() and vfork() use case on platforms w/o clone
|
|
||||||
else if ((SupportsWindows() || SupportsBsd()) &&
|
|
||||||
flags == (CLONE_VFORK | CLONE_VM | SIGCHLD)) {
|
|
||||||
if (IsTiny()) {
|
|
||||||
rc = einval();
|
|
||||||
} else if (!arg && !stksz) {
|
|
||||||
return vfork(); // don't log clone()
|
|
||||||
} else {
|
|
||||||
rc = einval();
|
|
||||||
}
|
|
||||||
} else if ((SupportsWindows() || SupportsBsd()) && flags == SIGCHLD) {
|
|
||||||
if (IsTiny()) {
|
|
||||||
rc = eopnotsupp();
|
|
||||||
} else if (!arg && !stksz) {
|
|
||||||
return fork(); // don't log clone()
|
|
||||||
} else {
|
|
||||||
rc = einval();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// we now assume we're creating a thread
|
|
||||||
// these platforms can't do signals the way linux does
|
|
||||||
else if ((flags &
|
|
||||||
~(CLONE_SETTLS | CLONE_PARENT_SETTID | CLONE_CHILD_SETTID)) !=
|
|
||||||
(CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND)) {
|
|
||||||
rc = einval();
|
|
||||||
} else if (IsFreebsd()) {
|
|
||||||
rc = CloneFreebsd(func, stk, stksz, flags, arg, ptid, tls, tlssz, ctid);
|
|
||||||
} else if (IsOpenbsd()) {
|
|
||||||
rc = CloneOpenbsd(func, stk, stksz, flags, arg, ptid, tls, tlssz, ctid);
|
|
||||||
}
|
|
||||||
|
|
||||||
// These platforms can't do segment registers like linux does
|
|
||||||
else if (flags & CLONE_SETTLS) {
|
|
||||||
rc = einval();
|
|
||||||
} else if (IsWindows()) {
|
|
||||||
rc = CloneWindows(func, stk, stksz, flags, arg, ptid, tls, tlssz, ctid);
|
|
||||||
} else {
|
|
||||||
rc = enosys();
|
|
||||||
}
|
|
||||||
|
|
||||||
STRACE("clone(%p, %p, %'zu, %#x, %p, %p, %p, %'zu, %p) → %d% m", func, stk,
|
|
||||||
stksz, flags, arg, ptid, tls, tlssz, ctid, rc);
|
|
||||||
return rc;
|
|
||||||
}
|
|
|
@ -31,17 +31,6 @@ struct thr_param {
|
||||||
struct rtprio *rtp;
|
struct rtprio *rtp;
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline int thr_new(struct thr_param *param, int param_size) {
|
|
||||||
bool failed;
|
|
||||||
long ax, di, si;
|
|
||||||
asm volatile(CFLAG_ASM("syscall")
|
|
||||||
: CFLAG_CONSTRAINT(failed), "=a"(ax), "=D"(di), "=S"(si)
|
|
||||||
: "1"(455), "2"(param), "3"(param_size)
|
|
||||||
: "rcx", "rdx", "r8", "r9", "r10", "r11", "memory");
|
|
||||||
if (failed) ax = -ax;
|
|
||||||
return ax;
|
|
||||||
}
|
|
||||||
|
|
||||||
COSMOPOLITAN_C_END_
|
COSMOPOLITAN_C_END_
|
||||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||||
#endif /* COSMOPOLITAN_LIBC_THREAD_FREEBSD_INTERNAL_H_ */
|
#endif /* COSMOPOLITAN_LIBC_THREAD_FREEBSD_INTERNAL_H_ */
|
||||||
|
|
|
@ -1,14 +0,0 @@
|
||||||
#ifndef COSMOPOLITAN_LIBC_THREAD_OPENBSD_INTERNAL_H_
|
|
||||||
#define COSMOPOLITAN_LIBC_THREAD_OPENBSD_INTERNAL_H_
|
|
||||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
|
||||||
COSMOPOLITAN_C_START_
|
|
||||||
|
|
||||||
struct __tfork {
|
|
||||||
void *tf_tcb;
|
|
||||||
int32_t *tf_tid;
|
|
||||||
void *tf_stack;
|
|
||||||
};
|
|
||||||
|
|
||||||
COSMOPOLITAN_C_END_
|
|
||||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
|
||||||
#endif /* COSMOPOLITAN_LIBC_THREAD_OPENBSD_INTERNAL_H_ */
|
|
|
@ -46,10 +46,9 @@ $(LIBC_THREAD_A).pkg: \
|
||||||
$(LIBC_THREAD_A_OBJS) \
|
$(LIBC_THREAD_A_OBJS) \
|
||||||
$(foreach x,$(LIBC_THREAD_A_DIRECTDEPS),$($(x)_A).pkg)
|
$(foreach x,$(LIBC_THREAD_A_DIRECTDEPS),$($(x)_A).pkg)
|
||||||
|
|
||||||
# no red zone because asm("call")
|
o/tinylinux/libc/thread/clone.o: \
|
||||||
o/$(MODE)/libc/thread/clone.o: \
|
|
||||||
OVERRIDE_CFLAGS += \
|
OVERRIDE_CFLAGS += \
|
||||||
-mno-red-zone
|
-ffunction-sections
|
||||||
|
|
||||||
LIBC_THREAD_LIBS = $(foreach x,$(LIBC_THREAD_ARTIFACTS),$($(x)))
|
LIBC_THREAD_LIBS = $(foreach x,$(LIBC_THREAD_ARTIFACTS),$($(x)))
|
||||||
LIBC_THREAD_SRCS = $(foreach x,$(LIBC_THREAD_ARTIFACTS),$($(x)_SRCS))
|
LIBC_THREAD_SRCS = $(foreach x,$(LIBC_THREAD_ARTIFACTS),$($(x)_SRCS))
|
||||||
|
|
|
@ -8,17 +8,21 @@ COSMOPOLITAN_C_START_
|
||||||
* @see darwin-libpthread/kern/kern_support.c
|
* @see darwin-libpthread/kern/kern_support.c
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void *bsdthread_create(void *func, void *func_arg, void *stack, void *pthread,
|
int bsdthread_create(void *func, void *func_arg, void *stack, void *pthread,
|
||||||
uint32_t flags);
|
uint32_t flags);
|
||||||
int bsdthread_terminate(void *stackaddr, size_t freesize, uint32_t port,
|
int bsdthread_terminate(void *stackaddr, size_t freesize, uint32_t port,
|
||||||
uint32_t sem);
|
uint32_t sem);
|
||||||
int bsdthread_register(void *threadstart, void *wqthread, uint32_t flags,
|
int bsdthread_register(
|
||||||
void *stack_addr_hint, void *targetconc_ptr,
|
void (*threadstart)(void *pthread, int machport, void *(*func)(void *),
|
||||||
uint32_t dispatchqueue_offset, uint32_t tsd_offset);
|
void *arg, intptr_t *, unsigned),
|
||||||
|
void (*wqthread)(void *pthread, void *machport, void *, void *, int),
|
||||||
|
uint32_t flags, void *stack_addr_hint, void *targetconc_ptr,
|
||||||
|
uint32_t dispatchqueue_offset, uint32_t tsd_offset);
|
||||||
int bsdthread_ctl(void *cmd, void *arg1, void *arg2, void *arg3);
|
int bsdthread_ctl(void *cmd, void *arg1, void *arg2, void *arg3);
|
||||||
uint64_t thread_selfid(void);
|
uint64_t thread_selfid(void);
|
||||||
uint64_t thread_selfusage(void);
|
uint64_t thread_selfusage(void);
|
||||||
int thread_selfcounts(int type, void *buf, uint64_t nbytes);
|
int thread_selfcounts(int type, void *buf, uint64_t nbytes);
|
||||||
|
int thread_fast_set_cthread_self(void *tls);
|
||||||
|
|
||||||
COSMOPOLITAN_C_END_
|
COSMOPOLITAN_C_END_
|
||||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||||
|
|
0
test/libc/calls/tkill_test.c
Normal file
0
test/libc/calls/tkill_test.c
Normal file
0
test/libc/intrin/gettid_test.c
Normal file
0
test/libc/intrin/gettid_test.c
Normal file
|
@ -33,6 +33,7 @@
|
||||||
#include "libc/sysv/consts/sa.h"
|
#include "libc/sysv/consts/sa.h"
|
||||||
#include "libc/sysv/consts/sig.h"
|
#include "libc/sysv/consts/sig.h"
|
||||||
#include "libc/testlib/testlib.h"
|
#include "libc/testlib/testlib.h"
|
||||||
|
#include "libc/time/time.h"
|
||||||
|
|
||||||
#define THREADS 8
|
#define THREADS 8
|
||||||
#define ENTRIES 256
|
#define ENTRIES 256
|
||||||
|
@ -76,7 +77,6 @@ TEST(rand64, testThreadSafety_doesntProduceIdenticalValues) {
|
||||||
sigset_t ss, oldss;
|
sigset_t ss, oldss;
|
||||||
void *stacks[THREADS];
|
void *stacks[THREADS];
|
||||||
int i, j, rc, ws, tid[THREADS];
|
int i, j, rc, ws, tid[THREADS];
|
||||||
if (IsXnu()) return;
|
|
||||||
struct sigaction oldsa;
|
struct sigaction oldsa;
|
||||||
struct sigaction sa = {.sa_handler = OnChld, .sa_flags = SA_RESTART};
|
struct sigaction sa = {.sa_handler = OnChld, .sa_flags = SA_RESTART};
|
||||||
EXPECT_NE(-1, sigaction(SIGCHLD, &sa, &oldsa));
|
EXPECT_NE(-1, sigaction(SIGCHLD, &sa, &oldsa));
|
||||||
|
@ -89,9 +89,9 @@ TEST(rand64, testThreadSafety_doesntProduceIdenticalValues) {
|
||||||
}
|
}
|
||||||
ready = false;
|
ready = false;
|
||||||
for (i = 0; i < THREADS; ++i) {
|
for (i = 0; i < THREADS; ++i) {
|
||||||
stacks[i] = mmap(0, FRAMESIZE, PROT_READ | PROT_WRITE,
|
stacks[i] = mmap(0, GetStackSize(), PROT_READ | PROT_WRITE,
|
||||||
MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0);
|
MAP_STACK | MAP_ANONYMOUS, -1, 0);
|
||||||
tid[i] = clone(Thrasher, stacks[i], FRAMESIZE,
|
tid[i] = clone(Thrasher, stacks[i], GetStackSize(),
|
||||||
CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND,
|
CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND,
|
||||||
(void *)(intptr_t)i, 0, 0, 0, 0);
|
(void *)(intptr_t)i, 0, 0, 0, 0);
|
||||||
ASSERT_NE(-1, tid[i]);
|
ASSERT_NE(-1, tid[i]);
|
||||||
|
@ -110,6 +110,10 @@ TEST(rand64, testThreadSafety_doesntProduceIdenticalValues) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (i = 0; i < THREADS; ++i) {
|
for (i = 0; i < THREADS; ++i) {
|
||||||
EXPECT_SYS(0, 0, munmap(stacks[i], FRAMESIZE));
|
tkill(tid[i], SIGKILL);
|
||||||
|
errno = 0;
|
||||||
|
}
|
||||||
|
for (i = 0; i < THREADS; ++i) {
|
||||||
|
EXPECT_SYS(0, 0, munmap(stacks[i], GetStackSize()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,43 +18,52 @@
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/calls/calls.h"
|
#include "libc/calls/calls.h"
|
||||||
#include "libc/dce.h"
|
#include "libc/dce.h"
|
||||||
|
#include "libc/errno.h"
|
||||||
#include "libc/intrin/spinlock.h"
|
#include "libc/intrin/spinlock.h"
|
||||||
|
#include "libc/runtime/stack.h"
|
||||||
#include "libc/sysv/consts/clone.h"
|
#include "libc/sysv/consts/clone.h"
|
||||||
#include "libc/sysv/consts/map.h"
|
#include "libc/sysv/consts/map.h"
|
||||||
#include "libc/sysv/consts/prot.h"
|
#include "libc/sysv/consts/prot.h"
|
||||||
|
#include "libc/sysv/consts/sig.h"
|
||||||
#include "libc/testlib/testlib.h"
|
#include "libc/testlib/testlib.h"
|
||||||
|
#include "libc/time/time.h"
|
||||||
|
|
||||||
int x, thechilde;
|
char *stack;
|
||||||
|
int x, me, thechilde;
|
||||||
_Alignas(64) volatile char lock;
|
_Alignas(64) volatile char lock;
|
||||||
|
|
||||||
void SetUp(void) {
|
void SetUp(void) {
|
||||||
x = 0;
|
x = 0;
|
||||||
lock = 0;
|
lock = 0;
|
||||||
|
me = gettid();
|
||||||
thechilde = 0;
|
thechilde = 0;
|
||||||
|
ASSERT_NE(MAP_FAILED, (stack = mmap(0, GetStackSize(), PROT_READ | PROT_WRITE,
|
||||||
|
MAP_STACK | MAP_ANONYMOUS, -1, 0)));
|
||||||
}
|
}
|
||||||
|
|
||||||
int thread(void *arg) {
|
void TearDown(void) {
|
||||||
|
tkill(thechilde, SIGKILL), errno = 0;
|
||||||
|
sched_yield();
|
||||||
|
EXPECT_SYS(0, 0, munmap(stack, GetStackSize()));
|
||||||
|
}
|
||||||
|
|
||||||
|
int CloneTest1(void *arg) {
|
||||||
x = 42;
|
x = 42;
|
||||||
ASSERT_EQ(23, (intptr_t)arg);
|
ASSERT_EQ(23, (intptr_t)arg);
|
||||||
thechilde = gettid();
|
thechilde = gettid();
|
||||||
|
ASSERT_NE(gettid(), getpid());
|
||||||
_spunlock(&lock);
|
_spunlock(&lock);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(clone, test) {
|
TEST(clone, test1) {
|
||||||
if (IsXnu()) return;
|
int tid;
|
||||||
int me, tid;
|
|
||||||
char *stack;
|
|
||||||
me = gettid();
|
|
||||||
_spinlock(&lock);
|
_spinlock(&lock);
|
||||||
stack = mmap(0, FRAMESIZE, PROT_READ | PROT_WRITE,
|
ASSERT_NE(-1, (tid = clone(CloneTest1, stack, GetStackSize(),
|
||||||
MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0);
|
|
||||||
ASSERT_NE(-1, (tid = clone(thread, stack, FRAMESIZE,
|
|
||||||
CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND,
|
CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND,
|
||||||
(void *)23, 0, 0, 0, 0)));
|
(void *)23, 0, 0, 0, 0)));
|
||||||
_spinlock(&lock);
|
_spinlock(&lock);
|
||||||
ASSERT_EQ(42, x);
|
ASSERT_EQ(42, x);
|
||||||
ASSERT_NE(me, tid);
|
ASSERT_NE(me, tid);
|
||||||
ASSERT_EQ(tid, thechilde);
|
ASSERT_EQ(tid, thechilde);
|
||||||
EXPECT_SYS(0, 0, munmap(stack, FRAMESIZE));
|
|
||||||
}
|
}
|
Loading…
Reference in a new issue