Perform more low-level code cleanup

This commit is contained in:
Justine Tunney 2022-09-09 04:07:08 -07:00
parent c32e2d4486
commit 2d17ab016c
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
42 changed files with 977 additions and 265 deletions

View file

@ -0,0 +1,110 @@
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi
Copyright 2022 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/macros.internal.h"
.privileged
diagnose_syscall:
push %rbp
mov %rsp,%rbp
push %rbx
push %r12
push %r13
push %r14
push %r15
mov $0x7fffffff,%eax
add $4,%eax # set sf/of/pf
mov %rdi,%rax # nr
mov %rsi,%rdi # arg 1
mov %rdx,%rsi # arg 2
mov %rcx,%rdx # arg 3
mov %r8,%r10 # arg 4
mov %r9,%r8 # arg 5
mov 16(%rbp),%r9 # arg 6
push 24(%rbp) # arg 7
push %rax # fake ret addr
mov 32(%rbp),%r12 # ucontext before
mov 40(%rbp),%r13 # ucontext after
xor %ecx,%ecx
xor %r11d,%r11d
mov $0x5555555555555555,%r11
mov $0x5555555555555555,%r14
mov $0x5555555555555555,%r15
mov $0x5555555555555555,%rbx
// save machine state before system call
pushf
pop 176(%r12)
mov %r8,40(%r12)
mov %r9,48(%r12)
mov %r10,56(%r12)
mov %r11,64(%r12)
mov %r12,72(%r12)
mov %r13,80(%r12)
mov %r14,88(%r12)
mov %r15,96(%r12)
mov %rdi,104(%r12)
mov %rsi,112(%r12)
mov %rbp,120(%r12)
mov %rbx,128(%r12)
mov %rdx,136(%r12)
mov %rax,144(%r12)
mov %rcx,152(%r12)
push %rbx
lea 320(%r12),%rbx
mov %rbx,224(%r12) # set fpregs ptr
pop %rbx
syscall
// save machine state after system call
pushf
pop 176(%r13)
mov %r8,40(%r13)
mov %r9,48(%r13)
mov %r10,56(%r13)
mov %r11,64(%r13)
mov %r12,72(%r13)
mov %r13,80(%r13)
mov %r14,88(%r13)
mov %r15,96(%r13)
mov %rdi,104(%r13)
mov %rsi,112(%r13)
mov %rbp,120(%r13)
mov %rbx,128(%r13)
mov %rdx,136(%r13)
mov %rax,144(%r13)
mov %rcx,152(%r13)
push %rbx
lea 320(%r13),%rbx
mov %rbx,224(%r13) # set fpregs ptr
pop %rbx
pop %r13
pop %r13
pop %r15
pop %r14
pop %r13
pop %r12
pop %rbx
pop %rbp
ret
.endfn diagnose_syscall,globl

View file

@ -20,10 +20,26 @@
// Gets machine state.
//
// @note please use longerjmp() and setlongerjmp() for fibers
// @note currently only gets general registers
// @see setcontext()
getcontext:
pushf
pop 176(%rdi)
movaps %xmm0,480(%rdi)
movaps %xmm1,496(%rdi)
movaps %xmm2,512(%rdi)
movaps %xmm3,528(%rdi)
movaps %xmm4,544(%rdi)
movaps %xmm5,560(%rdi)
movaps %xmm6,576(%rdi)
movaps %xmm7,592(%rdi)
movaps %xmm8,608(%rdi)
movaps %xmm9,624(%rdi)
movaps %xmm10,640(%rdi)
movaps %xmm11,656(%rdi)
movaps %xmm12,672(%rdi)
movaps %xmm13,688(%rdi)
movaps %xmm14,704(%rdi)
movaps %xmm15,720(%rdi)
mov %r8,40(%rdi)
mov %r9,48(%rdi)
mov %r10,56(%rdi)
@ -39,6 +55,8 @@ getcontext:
mov %rdx,136(%rdi)
mov %rax,144(%rdi)
mov %rcx,152(%rdi)
lea 320(%rdi),%rax
mov %rax,224(%rdi)
lea 8(%rsp),%rax
mov %rax,160(%rdi)
mov (%rsp),%rax
@ -50,6 +68,22 @@ getcontext:
.end
////////////////////////////////////////////////////////////////////////////////
noasan noubsan noinstrument int getcontext(ucontext_t *uc) {
asm volatile("movaps\t%%xmm0,%0" : /* no outputs */ : "m"(uc->__fpustate.xmm[0]));
asm volatile("movaps\t%%xmm1,%0" : /* no outputs */ : "m"(uc->__fpustate.xmm[1]));
asm volatile("movaps\t%%xmm2,%0" : /* no outputs */ : "m"(uc->__fpustate.xmm[2]));
asm volatile("movaps\t%%xmm3,%0" : /* no outputs */ : "m"(uc->__fpustate.xmm[3]));
asm volatile("movaps\t%%xmm4,%0" : /* no outputs */ : "m"(uc->__fpustate.xmm[4]));
asm volatile("movaps\t%%xmm5,%0" : /* no outputs */ : "m"(uc->__fpustate.xmm[5]));
asm volatile("movaps\t%%xmm6,%0" : /* no outputs */ : "m"(uc->__fpustate.xmm[6]));
asm volatile("movaps\t%%xmm7,%0" : /* no outputs */ : "m"(uc->__fpustate.xmm[7]));
asm volatile("movaps\t%%xmm8,%0" : /* no outputs */ : "m"(uc->__fpustate.xmm[8]));
asm volatile("movaps\t%%xmm9,%0" : /* no outputs */ : "m"(uc->__fpustate.xmm[9]));
asm volatile("movaps\t%%xmm10,%0" : /* no outputs */ : "m"(uc->__fpustate.xmm[10]));
asm volatile("movaps\t%%xmm11,%0" : /* no outputs */ : "m"(uc->__fpustate.xmm[11]));
asm volatile("movaps\t%%xmm12,%0" : /* no outputs */ : "m"(uc->__fpustate.xmm[12]));
asm volatile("movaps\t%%xmm13,%0" : /* no outputs */ : "m"(uc->__fpustate.xmm[13]));
asm volatile("movaps\t%%xmm14,%0" : /* no outputs */ : "m"(uc->__fpustate.xmm[14]));
asm volatile("movaps\t%%xmm15,%0" : /* no outputs */ : "m"(uc->__fpustate.xmm[15]));
asm volatile("mov\t%%r8,%0" : "=m"(uc->uc_mcontext.r8));
asm volatile("mov\t%%r9,%0" : "=m"(uc->uc_mcontext.r9));
asm volatile("mov\t%%r10,%0" : "=m"(uc->uc_mcontext.r10));
@ -65,6 +99,7 @@ noasan noubsan noinstrument int getcontext(ucontext_t *uc) {
asm volatile("mov\t%%rdx,%0" : "=m"(uc->uc_mcontext.rdx));
asm volatile("mov\t%%rax,%0" : "=m"(uc->uc_mcontext.rax));
asm volatile("mov\t%%rcx,%0" : "=m"(uc->uc_mcontext.rcx));
uc->uc_mcontext.fpregs = &uc->__fpustate;
asm volatile("lea\t8(%%rsp),%%rax\n\t"
"mov\t%%rax,%0"
: "=m"(uc->uc_mcontext.rsp)

View file

@ -20,10 +20,29 @@
// Sets machine state.
//
// @note please use longerjmp() and setlongerjmp() for fibers
// @note currently only sets general registers
// @see getcontext()
setcontext:
mov 224(%rdi),%rax
test %rax,%rax
je 1f
movaps 160(%rax),%xmm0
movaps 176(%rax),%xmm1
movaps 192(%rax),%xmm2
movaps 208(%rax),%xmm3
movaps 224(%rax),%xmm4
movaps 240(%rax),%xmm5
movaps 256(%rax),%xmm6
movaps 272(%rax),%xmm7
movaps 288(%rax),%xmm8
movaps 304(%rax),%xmm9
movaps 320(%rax),%xmm10
movaps 336(%rax),%xmm11
movaps 352(%rax),%xmm12
movaps 368(%rax),%xmm13
movaps 384(%rax),%xmm14
movaps 400(%rax),%xmm15
1: push 176(%rdi)
popf
mov 40(%rdi),%r8
mov 48(%rdi),%r9
mov 56(%rdi),%r10
@ -48,6 +67,24 @@ setcontext:
.end
////////////////////////////////////////////////////////////////////////////////
noasan noubsan int setcontext(const ucontext_t *uc) {
if (uc->uc_mcontext.fpregs) {
asm volatile("movaps\t%0,%%xmm0" : /* no outputs */ : "m"(uc->uc_mcontext.fpregs->xmm[0]));
asm volatile("movaps\t%0,%%xmm1" : /* no outputs */ : "m"(uc->uc_mcontext.fpregs->xmm[1]));
asm volatile("movaps\t%0,%%xmm2" : /* no outputs */ : "m"(uc->uc_mcontext.fpregs->xmm[2]));
asm volatile("movaps\t%0,%%xmm3" : /* no outputs */ : "m"(uc->uc_mcontext.fpregs->xmm[3]));
asm volatile("movaps\t%0,%%xmm4" : /* no outputs */ : "m"(uc->uc_mcontext.fpregs->xmm[4]));
asm volatile("movaps\t%0,%%xmm5" : /* no outputs */ : "m"(uc->uc_mcontext.fpregs->xmm[5]));
asm volatile("movaps\t%0,%%xmm6" : /* no outputs */ : "m"(uc->uc_mcontext.fpregs->xmm[6]));
asm volatile("movaps\t%0,%%xmm7" : /* no outputs */ : "m"(uc->uc_mcontext.fpregs->xmm[7]));
asm volatile("movaps\t%0,%%xmm8" : /* no outputs */ : "m"(uc->uc_mcontext.fpregs->xmm[8]));
asm volatile("movaps\t%0,%%xmm9" : /* no outputs */ : "m"(uc->uc_mcontext.fpregs->xmm[9]));
asm volatile("movaps\t%0,%%xmm10" : /* no outputs */ : "m"(uc->uc_mcontext.fpregs->xmm[10]));
asm volatile("movaps\t%0,%%xmm11" : /* no outputs */ : "m"(uc->uc_mcontext.fpregs->xmm[11]));
asm volatile("movaps\t%0,%%xmm12" : /* no outputs */ : "m"(uc->uc_mcontext.fpregs->xmm[12]));
asm volatile("movaps\t%0,%%xmm13" : /* no outputs */ : "m"(uc->uc_mcontext.fpregs->xmm[13]));
asm volatile("movaps\t%0,%%xmm14" : /* no outputs */ : "m"(uc->uc_mcontext.fpregs->xmm[14]));
asm volatile("movaps\t%0,%%xmm15" : /* no outputs */ : "m"(uc->uc_mcontext.fpregs->xmm[15]));
}
asm volatile("mov\t%0,%%r8" : /* no outputs */ : "m"(uc->uc_mcontext.r8));
asm volatile("mov\t%0,%%r9" : /* no outputs */ : "m"(uc->uc_mcontext.r9));
asm volatile("mov\t%0,%%r10" : /* no outputs */ : "m"(uc->uc_mcontext.r10));

View file

@ -1,9 +1,9 @@
#ifndef COSMOPOLITAN_LIBC_CALLS_STRUCT_UCONTEXT_NETBSD_INTERNAL_H_
#define COSMOPOLITAN_LIBC_CALLS_STRUCT_UCONTEXT_NETBSD_INTERNAL_H_
#include "libc/calls/ucontext.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
// clang-format off
#include "libc/calls/ucontext.h"
#define __UCONTEXT_SIZE 784
#define _UC_SIGMASK 0x01

View file

@ -65,7 +65,7 @@ struct MachineContext {
};
gregset_t gregs;
};
struct FpuState *fpregs;
struct FpuState *fpregs; /* zero when no fpu context */
uint64_t __pad1[8];
};
@ -77,6 +77,7 @@ struct ucontext {
stack_t uc_stack;
mcontext_t uc_mcontext; /* use this */
sigset_t uc_sigmask;
uint64_t __pad;
struct FpuState __fpustate; /* for cosmo on non-linux */
};

View file

@ -68,11 +68,11 @@
/* TODO(jart): Remove this in favor of GetStackSize() */
#if defined(COSMO) && defined(MODE_DBG)
#define STACKSIZE 0x20000 /* 128kb stack */
#define STACKSIZE 131072 /* 128kb stack */
#elif defined(COSMO)
#define STACKSIZE 0x10000 /* 64kb stack */
#define STACKSIZE 65536 /* 64kb stack */
#else
#define STACKSIZE 0x800000 /* 8mb stack */
#define STACKSIZE 8388608 /* 8mb stack */
#endif
#define BIGPAGESIZE 0x200000

View file

@ -0,0 +1,63 @@
/*-*- 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/dce.h"
#include "libc/errno.h"
#include "libc/intrin/fsgsbase.h"
#include "libc/nexgen32e/x86feature.h"
/**
* Returns true if FSGSBASE ISA can be used.
*
* If this function returns true (Linux 5.9+ or FreeBSD) then you should
* be able to read/write to the %gs / %fs x86 segment registers in about
* one or two clock cycles which gives you a free addition operation for
* all assembly ops that reference memory.
*
* The FSGSBASE ISA was introduced by Intel with Ivybridge (c. 2012) but
* the Linux Kernel didn't authorize us to use it until 2020, once Intel
* had to start backdooring customer kernels so that they could have it.
* AMD introduced support for the FSGSBASE ISA in Excavator, aka bdver4.
*
* @return boolean indicating if feature can be used
* @see _rdfsbase()
* @see _rdgsbase()
* @see _wrfsbase()
* @see _wrgsbase()
*/
privileged int _have_fsgsbase(void) {
// Linux 5.9 (c. 2020) introduced close_range() and fsgsbase support.
// it's cheaper to test for close_range() than handle an op crashing.
// Windows lets us use these instructions but they don't really work.
int ax;
if (X86_HAVE(FSGSBASE)) {
if (IsLinux()) {
asm volatile("syscall"
: "=a"(ax)
: "0"(436 /* close_range */), "D"(-1), "S"(-2), "d"(0)
: "rcx", "r11", "memory");
return ax == -22; // EINVAL
} else if (IsFreebsd()) {
return 1;
} else {
return 0;
}
} else {
return 0;
}
}

28
libc/intrin/_rdfsbase.c Normal file
View file

@ -0,0 +1,28 @@
/*-*- 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/intrin/fsgsbase.h"
/**
* Reads `%fs` base address.
*
* @see _have_fsgsbase()
*/
void *(_rdfsbase)(void) {
return _rdfsbase();
}

28
libc/intrin/_rdgsbase.c Normal file
View file

@ -0,0 +1,28 @@
/*-*- 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/intrin/fsgsbase.h"
/**
* Reads `%gs` base address.
*
* @see _have_fsgsbase()
*/
void *(_rdgsbase)(void) {
return _rdgsbase();
}

28
libc/intrin/_wrfsbase.c Normal file
View file

@ -0,0 +1,28 @@
/*-*- 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/intrin/fsgsbase.h"
/**
* Changes `%fs` base address.
*
* @see _have_fsgsbase()
*/
void *(_wrfsbase)(void *p) {
return _wrfsbase(p);
}

28
libc/intrin/_wrgsbase.c Normal file
View file

@ -0,0 +1,28 @@
/*-*- 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/intrin/fsgsbase.h"
/**
* Changes `%gs` base address.
*
* @see _have_fsgsbase()
*/
void *(_wrgsbase)(void *p) {
return _wrgsbase(p);
}

View file

@ -0,0 +1,30 @@
/*-*- 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/fmt/itoa.h"
#include "libc/intrin/describeflags.internal.h"
const char *(DescribeArchPrctlCode)(char buf[12], int x) {
if (x == ARCH_SET_FS) return "ARCH_SET_FS";
if (x == ARCH_GET_FS) return "ARCH_GET_FS";
if (x == ARCH_SET_GS) return "ARCH_SET_GS";
if (x == ARCH_GET_GS) return "ARCH_GET_GS";
FormatInt32(buf, x);
return buf;
}

View file

@ -12,6 +12,7 @@ struct thatispacked DescribeFlags {
const char *DescribeFlags(char *, size_t, struct DescribeFlags *, size_t,
const char *, unsigned);
const char *DescribeArchPrctlCode(char[12], int);
const char *DescribeCapability(char[20], int);
const char *DescribeClockName(char[32], int);
const char *DescribeDirfd(char[12], int);
@ -54,6 +55,7 @@ const char *DescribeSocketProtocol(char[12], int);
const char *DescribeSocketType(char[64], int);
const char *DescribeWhence(char[12], int);
#define DescribeArchPrctlCode(x) DescribeArchPrctlCode(alloca(12), x)
#define DescribeCapability(x) DescribeCapability(alloca(20), x)
#define DescribeClockName(x) DescribeClockName(alloca(32), x)
#define DescribeDirfd(x) DescribeDirfd(alloca(12), x)

47
libc/intrin/fsgsbase.h Normal file
View file

@ -0,0 +1,47 @@
#ifndef COSMOPOLITAN_LIBC_RUNTIME_FSGSBASE_H_
#define COSMOPOLITAN_LIBC_RUNTIME_FSGSBASE_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
void *_rdfsbase(void);
void *_rdgsbase(void);
void *_wrfsbase(void *);
void *_wrgsbase(void *);
int _have_fsgsbase(void);
#if defined(__GNUC__) && !defined(__STRICT_ANSI__)
#define _rdfsbase() \
({ \
void *_p; \
asm("rdfsbase\t%0" : "=r"(_p)); \
_p; \
})
#define _rdgsbase() \
({ \
void *_p; \
asm("rdgsbase\t%0" : "=r"(_p)); \
_p; \
})
#define _wrfsbase(p) \
({ \
void *_p = p; \
asm volatile("wrfsbase\t%0" \
: /* no outputs */ \
: "r"(_p) \
: "memory"); \
_p; \
})
#define _wrgsbase(p) \
({ \
void *_p = p; \
asm volatile("wrgsbase\t%0" \
: /* no outputs */ \
: "r"(_p) \
: "memory"); \
_p; \
})
#endif /* GNUC && !ANSI */
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_RUNTIME_FSGSBASE_H_ */

View file

@ -30,5 +30,8 @@ int pthread_attr_init(pthread_attr_t *attr) {
.stacksize = GetStackSize(),
.guardsize = PAGESIZE,
};
if (attr->stacksize >= 1048576) {
attr->guardsize = FRAMESIZE;
}
return 0;
}

View file

@ -16,21 +16,15 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/errno.h"
#include "libc/intrin/pthread.h"
#include "libc/macros.internal.h"
/**
* Sets size of unmapped pages at bottom of stack.
*
* Cosmopolitan Libc stack guards always default to 4096 bytes. Setting
* `guardsize` to zero will disable automatic creation of guard pages.
* Your `guardsize` will be rounded up to `PAGESIZE`.
*
* @param guardsize contains guard size in bytes
* @return 0 on success, or errno on error
*/
int pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize) {
attr->guardsize = ROUNDUP(guardsize, PAGESIZE);
attr->guardsize = guardsize;
return 0;
}

View file

@ -16,19 +16,10 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/intrin/asan.internal.h"
#include "libc/intrin/pthread.h"
#include "libc/macros.internal.h"
#include "libc/runtime/stack.h"
#include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/prot.h"
#define MAP_ANON_OPENBSD 0x1000
#define MAP_STACK_OPENBSD 0x4000
/**
* Configures custom allocated stack for thread, e.g.
@ -82,30 +73,6 @@ int pthread_attr_setstack(pthread_attr_t *attr, void *stackaddr,
(IsAsan() && !__asan_is_valid(stackaddr, stacksize))) {
return EINVAL;
}
if (IsOpenbsd()) {
// OpenBSD: Only permits RSP to occupy memory that's been explicitly
// defined as stack memory. We need to squeeze the provided interval
// in order to successfully call mmap(), which will return EINVAL if
// these calculations should overflow.
size_t n;
int e, rc;
uintptr_t x, y;
n = stacksize;
x = (uintptr_t)stackaddr;
y = ROUNDUP(x, PAGESIZE);
n -= y - x;
n = ROUNDDOWN(n, PAGESIZE);
stackaddr = (void *)y;
stacksize = n;
e = errno;
if (__sys_mmap(stackaddr, stacksize, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANON_OPENBSD | MAP_STACK_OPENBSD, -1, 0,
0) == MAP_FAILED) {
rc = errno;
errno = e;
return rc;
}
}
attr->stackaddr = stackaddr;
attr->stacksize = stacksize;
return 0;

View file

@ -20,34 +20,14 @@
#include "libc/intrin/pthread.h"
/**
* Sets size of thread stack.
* Defines minimum stack size for thread.
*
* Your stack must have at least `PTHREAD_STACK_MIN` bytes, which
* Cosmpolitan Libc defines as `GetStackSize()`. It's a link-time
* constant used by Actually Portable Executable that's 128 kb by
* default. See libc/runtime/stack.h for docs on your stack limit
* since the APE ELF phdrs are the one true source of truth here.
*
* Cosmpolitan Libc runtime magic (e.g. ftrace) and memory safety
* (e.g. kprintf) assumes that stack sizes are two-powers and are
* aligned to that two-power. Conformance isn't required since we
* say caveat emptor to those who don't maintain these invariants
*
* Unlike pthread_attr_setstack() this function should be used if
* you want the Cosmopolitan Libc runtime to allocate a stack for
* you. Since the runtime uses mmap(MAP_STACK) to do that, you'll
* need to choose a multiple of FRAMESIZE, due to Windows.
*
* If this function isn't called it'll default to GetStackSize().
*
* @param x contains stack size in bytes
* @param stacksize contains stack size in bytes
* @return 0 on success, or errno on error
* @raise EINVAL if `x` is less than `PTHREAD_STACK_MIN`
* @raise EINVAL if `stacksize` is less than `PTHREAD_STACK_MIN`
*/
int pthread_attr_setstacksize(pthread_attr_t *a, size_t x) {
if (x < PTHREAD_STACK_MIN) return EINVAL;
if (x & (FRAMESIZE - 1)) return EINVAL;
if (x < FRAMESIZE) return EINVAL;
a->stacksize = x;
int pthread_attr_setstacksize(pthread_attr_t *a, size_t stacksize) {
if (stacksize < PTHREAD_STACK_MIN) return EINVAL;
a->stacksize = stacksize;
return 0;
}

View file

@ -38,24 +38,24 @@ privileged int sys_gettid(void) {
asm("syscall" // xnu/osfmk/kern/ipc_tt.c
: "=a"(tid) // assume success
: "0"(0x1000000 | 27) // Mach thread_self_trap()
: "rcx", "r11", "memory", "cc");
: "rcx", "r8", "r9", "r10", "r11", "memory", "cc");
} else if (IsOpenbsd()) {
asm("syscall"
: "=a"(tid) // man says always succeeds
: "0"(299) // getthrid()
: "rcx", "r11", "memory", "cc");
: "rcx", "r8", "r9", "r10", "r11", "memory", "cc");
} else if (IsNetbsd()) {
asm("syscall"
: "=a"(tid) // man says always succeeds
: "0"(311) // _lwp_self()
: "rcx", "rdx", "r11", "memory", "cc");
: "rcx", "rdx", "r8", "r9", "r10", "r11", "memory", "cc");
} else if (IsFreebsd()) {
asm("syscall"
: "=a"(tid), // only fails w/ EFAULT, which isn't possible
"=m"(wut) // must be 64-bit
: "0"(432), // thr_self()
"D"(&wut) // but not actually 64-bit
: "rcx", "r11", "memory", "cc");
: "rcx", "r8", "r9", "r10", "r11", "memory", "cc");
tid = wut;
} else {
tid = __pid;

View file

@ -17,31 +17,20 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/calls/strace.internal.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/intrin/asan.internal.h"
#include "libc/intrin/asmflag.h"
#include "libc/intrin/bits.h"
#include "libc/intrin/describeflags.internal.h"
#include "libc/nexgen32e/msr.h"
#include "libc/nexgen32e/x86feature.h"
#include "libc/runtime/pc.internal.h"
#include "libc/sysv/consts/sig.h"
#include "libc/sysv/errfuns.h"
/**
* @fileoverview Memory segmentation system calls.
*
* This whole file basically does:
*
* mov foo,%fs
* mov foo,%gs
* mov %fs,foo
* mov %gs,foo
*
* Which is nontrivial due to the limitless authoritarianism of
* operating systems.
*/
#define rdmsr(msr) \
({ \
uint32_t lo, hi; \
@ -58,24 +47,7 @@
"d"((uint32_t)(val_ >> 32))); \
} while (0)
static inline int arch_prctl_fsgsbase(int code, int64_t addr) {
switch (code) {
case ARCH_SET_GS:
asm volatile("wrgsbase\t%0" : /* no outputs */ : "r"(addr));
return 0;
case ARCH_SET_FS:
asm volatile("wrfsbase\t%0" : /* no outputs */ : "r"(addr));
return 0;
case ARCH_GET_GS:
asm volatile("rdgsbase\t%0" : "=r"(*(int64_t *)addr));
return 0;
case ARCH_GET_FS:
asm volatile("rdfsbase\t%0" : "=r"(*(int64_t *)addr));
return 0;
default:
return einval();
}
}
int sys_enable_tls();
static int arch_prctl_msr(int code, int64_t addr) {
switch (code) {
@ -99,29 +71,52 @@ static int arch_prctl_msr(int code, int64_t addr) {
static int arch_prctl_freebsd(int code, int64_t addr) {
switch (code) {
case ARCH_GET_FS:
// sysarch(AMD64_GET_FSBASE)
return sys_arch_prctl(128, addr);
case ARCH_SET_FS:
return sys_arch_prctl(129, addr);
// sysarch(AMD64_SET_FSBASE)
return sys_arch_prctl(129, (intptr_t)&addr);
case ARCH_GET_GS:
// sysarch(AMD64_GET_GSBASE)
return sys_arch_prctl(130, addr);
case ARCH_SET_GS:
return sys_arch_prctl(131, addr);
// sysarch(AMD64_SET_GSBASE)
return sys_arch_prctl(131, (intptr_t)&addr);
default:
return einval();
}
}
static privileged dontinline int arch_prctl_xnu(int code, int64_t addr) {
int ax;
bool failed;
static int arch_prctl_netbsd(int code, int64_t addr) {
switch (code) {
case ARCH_GET_FS:
// sysarch(X86_GET_FSBASE)
return sys_arch_prctl(15, addr);
case ARCH_SET_FS:
// we use _lwp_setprivate() instead of sysarch(X86_SET_FSBASE)
// because the latter has a bug where signal handlers cause it
// to be clobbered. please note, this doesn't apply to %gs :-)
return sys_enable_tls(addr);
case ARCH_GET_GS:
// sysarch(X86_GET_GSBASE)
return sys_arch_prctl(14, addr);
case ARCH_SET_GS:
// sysarch(X86_SET_GSBASE)
return sys_arch_prctl(16, (intptr_t)&addr);
default:
return einval();
}
}
static int arch_prctl_xnu(int code, int64_t addr) {
int e;
switch (code) {
case ARCH_SET_GS:
asm volatile(CFLAG_ASM("syscall")
: CFLAG_CONSTRAINT(failed), "=a"(ax)
: "1"(0x3000003), "D"(addr - 0x30)
: "rcx", "r11", "memory", "cc");
if (failed) errno = ax, ax = -1;
return ax;
// thread_fast_set_cthread_self has a weird ABI
e = errno;
sys_enable_tls(addr);
errno = e;
return 0;
case ARCH_GET_FS:
case ARCH_SET_FS:
case ARCH_GET_GS:
@ -139,7 +134,8 @@ static privileged dontinline int arch_prctl_openbsd(int code, int64_t addr) {
asm volatile(CFLAG_ASM("syscall")
: CFLAG_CONSTRAINT(failed), "=a"(rax)
: "1"(0x014a /* __get_tcb */)
: "rcx", "r11", "cc", "memory");
: "rcx", "rdx", "rdi", "rsi", "r8", "r9", "r10", "r11", "cc",
"memory");
if (failed) {
errno = rax;
return -1;
@ -150,7 +146,8 @@ static privileged dontinline int arch_prctl_openbsd(int code, int64_t addr) {
asm volatile(CFLAG_ASM("syscall")
: CFLAG_CONSTRAINT(failed), "=a"(rax)
: "1"(0x0149 /* __set_tcb */), "D"(addr)
: "rcx", "r11", "cc", "memory");
: "rcx", "rdx", "rsi", "r8", "r9", "r10", "r11", "cc",
"memory");
if (failed) {
errno = rax;
rax = -1;
@ -164,27 +161,80 @@ static privileged dontinline int arch_prctl_openbsd(int code, int64_t addr) {
}
}
static char g_fsgs_once;
/**
* Don't bother.
* Changes x86 segment registers.
*
* Support for segment registers is spotty across platforms. See the
* table of tested combinations below.
*
* This wrapper has the same weird ABI as the Linux Kernel. The type
* Cosmopolitan type signature of the prototype for this function is
* variadic, so no value safety checking will be performed w/o asan.
*
* Cosmopolitan Libc initializes your process by default, to use the
* segment register %fs, for thread-local storage. To safely disable
* this TLS you need to either set `__tls_enabled` to 0, or you must
* follow the same memory layout assumptions as your C library. When
* TLS is disabled you can't use threads unless you call clone() and
* that's not really a good idea since `errno` won't be thread-safe.
*
* Please note if you're only concerned about running on Linux, then
* consider using Cosmopolitan's fsgsbase macros which don't need to
* issue system calls to change %fs and %gs. See _have_fsgsbase() to
* learn more.
*
* @param code may be
* - `ARCH_SET_FS` works on Linux, FreeBSD, NetBSD, OpenBSD, Metal
* - `ARCH_GET_FS` works on Linux, FreeBSD, NetBSD, OpenBSD, Metal
* - `ARCH_SET_GS` works on Linux, FreeBSD, NetBSD, XNU, Metal
* - `ARCH_GET_GS` works on Linux, FreeBSD, NetBSD, Metal
* @param addr is treated as `intptr_t` when setting a register, and
* is an output parameter (i.e. `intptr_t *`) when reading one
* @raise ENOSYS if operating system didn't support changing `code`
* @raise EINVAL if `code` wasn't valid
* @raise EFAULT if `ARCH_SET_FS` or `ARCH_SET_GS` was used and memory
* pointed to by `addr` was invalid
* @see _have_fsgsbase()
*/
int arch_prctl(int code, int64_t addr) {
void *fn = arch_prctl_fsgsbase;
switch (__hostos) {
case METAL:
return arch_prctl_msr(code, addr);
case FREEBSD:
// TODO(jart): this should use sysarch()
return arch_prctl_freebsd(code, addr);
case OPENBSD:
return arch_prctl_openbsd(code, addr);
case LINUX:
return sys_arch_prctl(code, addr);
case XNU:
/* probably won't work */
return arch_prctl_xnu(code, addr);
default:
return enosys();
int rc;
if (IsAsan() && //
(code == ARCH_GET_FS || //
code == ARCH_GET_GS) && //
!__asan_is_valid((int64_t *)addr, 8)) {
rc = efault();
} else {
switch (__hostos) {
case METAL:
rc = arch_prctl_msr(code, addr);
break;
case FREEBSD:
rc = arch_prctl_freebsd(code, addr);
break;
case NETBSD:
rc = arch_prctl_netbsd(code, addr);
break;
case OPENBSD:
rc = arch_prctl_openbsd(code, addr);
break;
case LINUX:
rc = sys_arch_prctl(code, addr);
break;
case XNU:
rc = arch_prctl_xnu(code, addr);
break;
default:
rc = enosys();
break;
}
}
#ifdef SYSDEBUG
if (!rc && (code == ARCH_GET_FS || code == ARCH_GET_GS)) {
STRACE("arch_prctl(%s, [%p]) → %d% m", DescribeArchPrctlCode(code),
*(int64_t *)addr, rc);
} else {
STRACE("arch_prctl(%s, %p) → %d% m", DescribeArchPrctlCode(code), addr, rc);
}
#endif
return rc;
}

View file

@ -172,7 +172,7 @@ XnuThreadMain(void *pthread, // rdi
asm volatile("syscall"
: "=a"(ax)
: "0"(__NR_thread_fast_set_cthread_self), "D"(wt->tls - 0x30)
: "rcx", "r11", "memory", "cc");
: "rcx", "rdx", "r8", "r9", "r10", "r11", "memory", "cc");
}
func(arg, tid);
@ -388,7 +388,7 @@ static int CloneNetbsd(int (*func)(void *, int), char *stk, size_t stksz,
asm volatile(CFLAG_ASM("syscall")
: CFLAG_CONSTRAINT(failed), "=a"(ax)
: "1"(__NR_getcontext_netbsd), "D"(&netbsd_clone_template)
: "rcx", "rdx", "r11", "memory");
: "rcx", "rdx", "r8", "r9", "r10", "r11", "memory");
if (failed) {
broken = ax;
}
@ -443,7 +443,7 @@ static int CloneNetbsd(int (*func)(void *, int), char *stk, size_t stksz,
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");
: "rcx", "r8", "r9", "r10", "r11", "memory");
if (!failed) {
return *tid;
} else {

View file

@ -51,6 +51,8 @@
#define _TLDZ ((intptr_t)_tdata_size)
#define _TIBZ sizeof(struct cthread_descriptor_t)
int sys_enable_tls();
typedef char xmm_t __attribute__((__vector_size__(16), __aligned__(1)));
__msabi extern typeof(TlsAlloc) *const __imp_TlsAlloc;
@ -136,34 +138,21 @@ privileged void __enable_tls(void) {
assert(0 <= __tls_index && __tls_index < 64);
asm("mov\t%1,%%gs:%0" : "=m"(*((long *)0x1480 + __tls_index)) : "r"(tib));
} else if (IsFreebsd()) {
asm volatile("syscall"
: "=a"(ax)
: "0"(__NR_sysarch), "D"(AMD64_SET_FSBASE), "S"(tib)
: "rcx", "r11", "memory", "cc");
sys_enable_tls(AMD64_SET_FSBASE, tib);
} else if (IsLinux()) {
sys_enable_tls(ARCH_SET_FS, tib);
} else if (IsNetbsd()) {
// netbsd has sysarch(X86_SET_FSBASE) but we can't use that because
// signal handlers will cause it to be reset due to net setting the
// signal handlers will cause it to be reset due to not setting the
// _mc_tlsbase field in struct mcontext_netbsd.
asm volatile("syscall"
: "=a"(ax), "=d"(dx)
: "0"(__NR__lwp_setprivate), "D"(tib)
: "rcx", "r11", "memory", "cc");
} else if (IsXnu()) {
asm volatile("syscall"
: "=a"(ax)
: "0"(__NR_thread_fast_set_cthread_self),
"D"((intptr_t)tib - 0x30)
: "rcx", "r11", "memory", "cc");
sys_enable_tls(tib);
} else if (IsOpenbsd()) {
asm volatile("syscall"
: "=a"(ax)
: "0"(__NR___set_tcb), "D"(tib)
: "rcx", "r11", "memory", "cc");
} else if (IsLinux()) {
asm volatile("syscall"
: "=a"(ax)
: "0"(__NR_linux_arch_prctl), "D"(ARCH_SET_FS), "S"(tib)
: "rcx", "r11", "memory");
sys_enable_tls(tib);
} else if (IsXnu()) {
// thread_fast_set_cthread_self has a weird ABI
int e = errno;
sys_enable_tls((intptr_t)tib - 0x30);
errno = e;
} else {
uint64_t val = (uint64_t)tib;
asm volatile("wrmsr"

View file

@ -17,6 +17,7 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/errno.h"
#include "libc/fmt/itoa.h"
#include "libc/intrin/cmpxchg.h"
#include "libc/intrin/kprintf.h"
@ -24,6 +25,7 @@
#include "libc/macros.internal.h"
#include "libc/nexgen32e/gettls.h"
#include "libc/nexgen32e/stackframe.h"
#include "libc/nexgen32e/threaded.h"
#include "libc/runtime/internal.h"
#include "libc/runtime/stack.h"
#include "libc/runtime/symbols.internal.h"
@ -42,6 +44,7 @@
void ftrace_hook(void);
static int g_stackdigs;
static struct Ftrace g_ftrace;
static privileged inline int GetNestingLevelImpl(struct StackFrame *frame) {
int nesting = -2;
@ -52,7 +55,7 @@ static privileged inline int GetNestingLevelImpl(struct StackFrame *frame) {
return MAX(0, nesting);
}
static privileged inline int GetNestingLevel(struct FtraceTls *ft,
static privileged inline int GetNestingLevel(struct Ftrace *ft,
struct StackFrame *sf) {
int nesting;
nesting = GetNestingLevelImpl(sf);
@ -70,9 +73,13 @@ static privileged inline int GetNestingLevel(struct FtraceTls *ft,
*/
privileged void ftracer(void) {
long stackuse;
struct FtraceTls *ft;
struct Ftrace *ft;
struct StackFrame *sf;
ft = (struct FtraceTls *)(__get_tls_privileged() + 0x08);
if (__tls_enabled) {
ft = (struct Ftrace *)(__get_tls_privileged() + 0x08);
} else {
ft = &g_ftrace;
}
if (_cmpxchg(&ft->once, false, true)) {
ft->lastaddr = -1;
ft->skew = GetNestingLevelImpl(__builtin_frame_address(0));

View file

@ -18,12 +18,12 @@
*/
#define ShouldUseMsabiAttribute() 1
#include "libc/assert.h"
#include "libc/intrin/asmflag.h"
#include "libc/calls/internal.h"
#include "libc/calls/strace.internal.h"
#include "libc/calls/struct/sigset.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/intrin/asmflag.h"
#include "libc/intrin/kprintf.h"
#include "libc/nt/enum/pageflags.h"
#include "libc/nt/memory.h"
@ -50,7 +50,7 @@ static privileged void __morph_mprotect(void *addr, size_t size, int prot,
"syscall")
: CFLAG_CONSTRAINT(cf), "=a"(ax), "=d"(dx)
: "1"(__NR_mprotect), "D"(addr), "S"(size), "2"(prot)
: "rcx", "r11", "memory");
: "rcx", "r8", "r9", "r10", "r11", "memory");
#ifndef NDEBUG
if (cf) ax = -ax;
if (ax == -EPERM) {
@ -58,6 +58,7 @@ static privileged void __morph_mprotect(void *addr, size_t size, int prot,
_Exit(26);
}
#endif
if (ax) notpossible;
} else {
__imp_VirtualProtect(addr, size, ntprot, &op);
}
@ -81,15 +82,15 @@ privileged void __morph_begin(void) {
: "=a"(ax), "=d"(dx)
: "0"(__NR_sigprocmask), "D"(SIG_BLOCK), "S"(&ss),
"1"(&oldss)
: "rcx", "r10", "r11", "memory", "cc");
assert(!ax);
: "rcx", "r8", "r9", "r10", "r11", "memory", "cc");
if (ax) notpossible;
} else {
asm volatile(CFLAG_ASM("syscall")
: CFLAG_CONSTRAINT(cf), "=a"(ax), "=d"(dx)
: "1"(__NR_sigprocmask), "D"(SIG_BLOCK), "S"(-1u)
: "rcx", "r11", "memory");
: "rcx", "r8", "r9", "r10", "r11", "memory");
oldss.__bits[0] = ax & 0xffffffff;
assert(!cf);
if (cf) notpossible;
}
}
__morph_mprotect(_base, __privileged_addr - _base, PROT_READ | PROT_WRITE,
@ -112,15 +113,15 @@ privileged void __morph_end(void) {
: "=a"(ax), "=d"(dx)
: "0"(__NR_sigprocmask), "D"(SIG_SETMASK), "S"(&oldss),
"1"(0)
: "rcx", "r10", "r11", "memory", "cc");
assert(!ax);
: "rcx", "r8", "r9", "r10", "r11", "memory", "cc");
if (ax) notpossible;
} else {
asm volatile(CFLAG_ASM("syscall")
: CFLAG_CONSTRAINT(cf), "=a"(ax), "=d"(dx)
: "1"(__NR_sigprocmask), "D"(SIG_SETMASK),
"S"(oldss.__bits[0])
: "rcx", "r11", "memory");
assert(!cf);
: "rcx", "r8", "r9", "r10", "r11", "memory");
if (cf) notpossible;
}
}
STRACE("__morph_end()");

View file

@ -1,2 +1,2 @@
.include "o/libc/sysv/macros.internal.inc"
.scall sys_arch_prctl,0xfff0a50a5ffff09e,globl,hidden
.scall sys_arch_prctl,0x0a50a50a5ffff09e,globl,hidden

View file

@ -0,0 +1,2 @@
.include "o/libc/sysv/macros.internal.inc"
.scall sys_enable_tls,0x13d1490a5300309e,globl,hidden

12
libc/sysv/consts/uc.h Normal file
View file

@ -0,0 +1,12 @@
#ifndef COSMOPOLITAN_LIBC_SYSV_CONSTS_UC_H_
#define COSMOPOLITAN_LIBC_SYSV_CONSTS_UC_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
#define UC_FP_XSTATE 1
#define UC_SIGCONTEXT_SS 2
#define UC_STRICT_RESTORE_SS 4
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_SYSV_CONSTS_UC_H_ */

View file

@ -212,7 +212,8 @@ scall modify_ldt 0xfffffffffffff09a globl
scall sys_pivot_root 0xfffffffffffff09b globl hidden
scall _sysctl 0xfffffffffffff09c globl
#scall prctl 0xfffffffffffff09d globl # wrapped manually
scall sys_arch_prctl 0xfff0a50a5ffff09e globl hidden # sysarch() on bsd
scall sys_arch_prctl 0x0a50a50a5ffff09e globl hidden # sysarch() on bsd
scall sys_enable_tls 0x13d1490a5300309e globl hidden # arch_prctl on linux, sysarch on freebsd, _lwp_setprivate on netbsd, __set_tcb on openbsd, _lwp_setprivate on netbsd, thread_fast_set_cthread_self on xnu
scall adjtimex 0xfffffffffffff09f globl
scall swapon 0xffffff05520550a7 globl
scall swapoff 0xffffff1a8ffff0a8 globl

View file

@ -28,7 +28,6 @@
#include "libc/fmt/fmt.h"
#include "libc/fmt/itoa.h"
#include "libc/intrin/atomic.h"
#include "libc/intrin/kprintf.h"
#include "libc/intrin/pthread.h"
#include "libc/intrin/weaken.h"
#include "libc/log/check.h"

View file

@ -2,7 +2,6 @@
#define COSMOPOLITAN_LIBC_THREAD_POSIXTHREAD_INTERNAL_H_
#include "libc/intrin/pthread.h"
#include "libc/runtime/runtime.h"
#include "libc/thread/spawn.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
@ -54,13 +53,16 @@ enum PosixThreadStatus {
};
struct PosixThread {
struct spawn spawn;
void *(*start_routine)(void *);
void *arg; // start_routine's parameter
void *rc; // start_routine's return value
bool ownstack;
int tid;
int *ctid;
char *tls;
char *tib;
_Atomic(enum PosixThreadStatus) status;
jmp_buf exiter;
size_t stacksize;
pthread_attr_t attr;
};

View file

@ -18,17 +18,22 @@
*/
#include "libc/assert.h"
#include "libc/calls/calls.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/intrin/asan.internal.h"
#include "libc/intrin/atomic.h"
#include "libc/intrin/bits.h"
#include "libc/intrin/pthread.h"
#include "libc/intrin/wait0.internal.h"
#include "libc/intrin/weaken.h"
#include "libc/macros.internal.h"
#include "libc/mem/mem.h"
#include "libc/nexgen32e/gc.internal.h"
#include "libc/nexgen32e/gettls.h"
#include "libc/nexgen32e/threaded.h"
#include "libc/runtime/runtime.h"
#include "libc/runtime/stack.h"
#include "libc/sysv/consts/clone.h"
#include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/prot.h"
@ -37,14 +42,19 @@
#include "libc/thread/spawn.h"
#include "libc/thread/thread.h"
#define MAP_ANON_OPENBSD 0x1000
#define MAP_STACK_OPENBSD 0x4000
void _pthread_wait(struct PosixThread *pt) {
_wait0(pt->spawn.ctid);
_wait0(pt->ctid);
}
void _pthread_free(struct PosixThread *pt) {
free(pt->spawn.tls);
if (pt->stacksize) {
munmap(&pt->spawn.stk, pt->stacksize);
free(pt->tls);
if (pt->ownstack && //
pt->attr.stackaddr && //
pt->attr.stackaddr != MAP_FAILED) {
munmap(&pt->attr.stackaddr, pt->attr.stacksize);
}
free(pt);
}
@ -71,6 +81,36 @@ static int PosixThread(void *arg, int tid) {
return 0;
}
static int FixupCustomStackOnOpenbsd(pthread_attr_t *attr) {
// OpenBSD: Only permits RSP to occupy memory that's been explicitly
// defined as stack memory. We need to squeeze the provided interval
// in order to successfully call mmap(), which will return EINVAL if
// these calculations should overflow.
size_t n;
int e, rc;
uintptr_t x, y;
n = attr->stacksize;
x = (uintptr_t)attr->stackaddr;
y = ROUNDUP(x, PAGESIZE);
n -= y - x;
n = ROUNDDOWN(n, PAGESIZE);
e = errno;
if (__sys_mmap((void *)y, n, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANON_OPENBSD | MAP_STACK_OPENBSD, -1, 0,
0) != MAP_FAILED) {
attr->stackaddr = (void *)y;
attr->stacksize = n;
return 0;
} else {
rc = errno;
errno = e;
if (rc == EOVERFLOW) {
rc = EINVAL;
}
return rc;
}
}
/**
* Creates thread, e.g.
*
@ -124,16 +164,9 @@ int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine)(void *), void *arg) {
int rc, e = errno;
struct PosixThread *pt;
pthread_attr_t default_attr;
TlsIsRequired();
_pthread_zombies_decimate();
// default attributes
if (!attr) {
pthread_attr_init(&default_attr);
attr = &default_attr;
}
// create posix thread object
if (!(pt = calloc(1, sizeof(struct PosixThread)))) {
errno = e;
@ -143,26 +176,48 @@ int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
pt->arg = arg;
// create thread local storage memory
if (!(pt->spawn.tls = _mktls(&pt->spawn.tib))) {
if (!(pt->tls = _mktls(&pt->tib))) {
free(pt);
errno = e;
return EAGAIN;
}
// child thread id is also a condition variable
pt->spawn.ctid = (int *)(pt->spawn.tib + 0x38);
pt->ctid = (int *)(pt->tib + 0x38);
// create stack
if (attr && attr->stackaddr) {
// caller is responsible for creating stacks
pt->spawn.stk = attr->stackaddr;
// setup attributes
if (attr) {
pt->attr = *attr;
attr = 0;
} else {
// cosmo posix threads is managing the stack
pt->spawn.stk = mmap(0, attr->stacksize, PROT_READ | PROT_WRITE,
MAP_STACK | MAP_ANONYMOUS, -1, 0);
if (pt->spawn.stk != MAP_FAILED) {
pt->stacksize = attr->stacksize;
} else {
pthread_attr_init(&pt->attr);
}
// setup stack
if (pt->attr.stackaddr) {
// caller supplied their own stack
// assume they know what they're doing as much as possible
if (IsOpenbsd()) {
if ((rc = FixupCustomStackOnOpenbsd(&pt->attr))) {
_pthread_free(pt);
return rc;
}
}
} else {
// cosmo is managing the stack
// 1. in mono repo optimize for tiniest stack possible
// 2. in public world optimize to *work* regardless of memory
pt->ownstack = true;
pt->attr.stacksize = MAX(pt->attr.stacksize, GetStackSize());
pt->attr.stacksize = roundup2pow(pt->attr.stacksize);
pt->attr.guardsize = ROUNDUP(pt->attr.guardsize, PAGESIZE);
if (pt->attr.guardsize + PAGESIZE >= pt->attr.stacksize) {
_pthread_free(pt);
return EINVAL;
}
pt->attr.stackaddr = mmap(0, pt->attr.stacksize, PROT_READ | PROT_WRITE,
MAP_STACK | MAP_ANONYMOUS, -1, 0);
if (pt->attr.stackaddr == MAP_FAILED) {
rc = errno;
_pthread_free(pt);
errno = e;
@ -173,41 +228,31 @@ int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
}
}
// mmap(MAP_STACK) creates a 4096 guard by default
if (attr->guardsize != PAGESIZE) {
// user requested special guard size
if (attr->guardsize) {
rc = mprotect(pt->spawn.stk, attr->guardsize, PROT_NONE);
if (pt->attr.guardsize != PAGESIZE) {
if (pt->attr.guardsize) {
// user requested special guard size
rc = mprotect(pt->attr.stackaddr, pt->attr.guardsize, PROT_NONE);
} else {
rc = mprotect(pt->spawn.stk, PAGESIZE, PROT_READ | PROT_WRITE);
// user explicitly disabled guard page
rc = mprotect(pt->attr.stackaddr, PAGESIZE, PROT_READ | PROT_WRITE);
}
if (rc) {
notpossible;
}
}
if (IsAsan()) {
if (attr->guardsize) {
__asan_poison(pt->spawn.stk, attr->guardsize, kAsanStackOverflow);
if (pt->attr.guardsize) {
__asan_poison(pt->attr.stackaddr, pt->attr.guardsize,
kAsanStackOverflow);
}
__asan_poison(
pt->spawn.stk + attr->stacksize - 16 /* openbsd:stackbound */, 16,
kAsanStackOverflow);
__asan_poison((char *)pt->attr.stackaddr + pt->attr.stacksize -
16 /* openbsd:stackbound */,
16, kAsanStackOverflow);
}
}
// we only need to save this to support pthread_getattr_np()
pt->attr = *attr;
// if attr->stackaddr == 0,
// the stack is managed by cosmo,
// pt->spawn.stk is from a successful mmap,
// and so pt->attr.stackaddr = pt->spawn.stk
pt->attr.stackaddr = pt->spawn.stk;
// if attr->stackaddr != 0,
// then stack is not managed by cosmo
// pt->attr.stackaddr = pt->spawn.stk = attr->stackaddr
// so the above line is a no-op.
// set initial status
switch (attr->detachstate) {
switch (pt->attr.detachstate) {
case PTHREAD_CREATE_JOINABLE:
pt->status = kPosixThreadJoinable;
break;
@ -221,20 +266,16 @@ int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
}
// launch PosixThread(pt) in new thread
if (clone(PosixThread, pt->spawn.stk,
attr->stacksize - 16 /* openbsd:stackbound */,
if (clone(PosixThread, pt->attr.stackaddr,
pt->attr.stacksize - 16 /* openbsd:stackbound */,
CLONE_VM | CLONE_THREAD | CLONE_FS | CLONE_FILES | CLONE_SIGHAND |
CLONE_SETTLS | CLONE_PARENT_SETTID | CLONE_CHILD_SETTID |
CLONE_CHILD_CLEARTID,
pt, &pt->spawn.ptid, pt->spawn.tib, pt->spawn.ctid) == -1) {
pt, &pt->tid, pt->tib, pt->ctid) == -1) {
rc = errno;
_pthread_free(pt);
errno = e;
if (rc == EINVAL) {
return EINVAL;
} else {
return EAGAIN;
}
return rc;
}
if (thread) {

View file

@ -27,5 +27,5 @@
int pthread_equal(pthread_t t1, pthread_t t2) {
struct PosixThread *a = (struct PosixThread *)t1;
struct PosixThread *b = (struct PosixThread *)t2;
return a->spawn.ptid == b->spawn.ptid;
return a->tid == b->tid;
}

View file

@ -50,7 +50,7 @@ int pthread_getname_np(pthread_t thread, char *name, size_t size) {
if (!size) return 0;
bzero(name, size);
tid = ((struct PosixThread *)thread)->spawn.ptid;
tid = ((struct PosixThread *)thread)->tid;
if (IsLinux()) {
// TASK_COMM_LEN is 16 on Linux so we're just being paranoid.
@ -99,7 +99,7 @@ int pthread_getname_np(pthread_t thread, char *name, size_t size) {
: CFLAG_CONSTRAINT(cf), "=a"(ax), "=d"(dx)
: "1"(324 /* _lwp_getname */), "D"(tid), "S"(name),
"d"(size - 1)
: "rcx", "r11", "memory");
: "rcx", "r8", "r9", "r10", "r11", "memory");
if (!cf) {
// if size + our nul + kernel's nul is the buffer size, then we
// can't say with absolute confidence truncation didn't happen.

View file

@ -24,5 +24,5 @@
*/
int64_t pthread_getunique_np(pthread_t thread) {
struct PosixThread *pt = (struct PosixThread *)thread;
return pt->spawn.ptid;
return pt->tid;
}

View file

@ -56,7 +56,7 @@ int pthread_setname_np(pthread_t thread, const char *name) {
char path[128], *p;
int e, fd, rc, tid, len;
tid = ((struct PosixThread *)thread)->spawn.ptid;
tid = ((struct PosixThread *)thread)->tid;
len = strlen(name);
if (IsLinux()) {
@ -100,7 +100,7 @@ int pthread_setname_np(pthread_t thread, const char *name) {
asm volatile(CFLAG_ASM("syscall")
: CFLAG_CONSTRAINT(cf), "=a"(ax), "=d"(dx)
: "1"(323 /* thr_set_name */), "D"(tid), "S"(name)
: "rcx", "r11", "memory");
: "rcx", "r8", "r9", "r10", "r11", "memory");
return !cf ? 0 : ax;
} else if (IsNetbsd()) {
@ -109,7 +109,7 @@ int pthread_setname_np(pthread_t thread, const char *name) {
asm volatile(CFLAG_ASM("syscall")
: CFLAG_CONSTRAINT(cf), "=a"(ax), "=d"(dx)
: "1"(323 /* _lwp_setname */), "D"(tid), "S"(name)
: "rcx", "r11", "memory");
: "rcx", "r8", "r9", "r10", "r11", "memory");
return !cf ? 0 : ax;
} else {

View file

@ -6,7 +6,7 @@
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
struct FtraceTls { /* 16 */
struct Ftrace { /* 16 */
bool once; /* 0 */
bool noreentry; /* 1 */
int skew; /* 4 */
@ -15,7 +15,7 @@ struct FtraceTls { /* 16 */
struct cthread_descriptor_t {
struct cthread_descriptor_t *self; /* 0x00 */
struct FtraceTls ftrace; /* 0x08 */
struct Ftrace ftrace; /* 0x08 */
void *garbages; /* 0x18 */
locale_t locale; /* 0x20 */
pthread_t pthread; /* 0x28 */

View file

@ -0,0 +1,130 @@
/*-*- 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/ucontext.h"
#include "libc/dce.h"
#include "libc/intrin/kprintf.h"
#include "libc/runtime/gc.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/append.internal.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/nr.h"
#include "libc/testlib/testlib.h"
#define Z 0x5555555555555555
#define FLAGS_cf 0
#define FLAGS_pf 2
#define FLAGS_sf 7
#define FLAGS_of 11
intptr_t diagnose_syscall(intptr_t nr, //
intptr_t arg1, //
intptr_t arg2, //
intptr_t arg3, //
intptr_t arg4, //
intptr_t arg5, //
intptr_t arg6, //
intptr_t arg7, //
ucontext_t *before, //
ucontext_t *after); //
#define GREG(FIELD) \
do { \
uint64_t f1 = x->uc_mcontext.FIELD; \
uint64_t f2 = y->uc_mcontext.FIELD; \
if (f1 != f2) { \
if (b) appendw(&b, ' '); \
appends(&b, #FIELD); \
kprintf("%3s %016lx → %016lx\n", #FIELD, f1, f2); \
} \
} while (0)
#define FLAG(FLAG) \
if ((x->uc_mcontext.eflags & (1ul << FLAGS_##FLAG)) ^ \
(y->uc_mcontext.eflags & (1ul << FLAGS_##FLAG))) { \
if (b) appendw(&b, ' '); \
appends(&b, #FLAG); \
}
char *DiffContexts(ucontext_t *x, ucontext_t *y) {
char *b = 0;
GREG(rax);
GREG(rdx);
GREG(rdi);
GREG(rsi);
GREG(rcx);
GREG(r8);
GREG(r9);
GREG(r10);
GREG(r11);
GREG(r12);
GREG(r13);
GREG(r14);
GREG(r15);
GREG(rbx);
GREG(rbp);
FLAG(cf);
FLAG(sf);
FLAG(of);
FLAG(pf);
return b;
}
void SetUp(void) {
if (IsWindows()) {
exit(0);
}
}
TEST(diagnose_syscall, getpid) {
ucontext_t x, y;
diagnose_syscall(__NR_getpid, Z, Z, Z, Z, Z, Z, Z, &x, &y);
if (IsFreebsd()) {
ASSERT_STREQ("rax rcx r8 r9 r10 r11", _gc(DiffContexts(&x, &y)));
} else if (IsNetbsd() || IsXnu()) {
// netbsd puts parent pid in edx
// xnu seems to just clobber it!
ASSERT_STREQ("rax rdx rcx r11", _gc(DiffContexts(&x, &y)));
} else {
ASSERT_STREQ("rax rcx r11", _gc(DiffContexts(&x, &y)));
}
}
TEST(diagnose_syscall, testWriteSuccess) {
ucontext_t x, y;
diagnose_syscall(__NR_write, 2, Z, 0, Z, Z, Z, Z, &x, &y);
if (IsFreebsd()) {
ASSERT_STREQ("rax rcx r8 r9 r10 r11", _gc(DiffContexts(&x, &y)));
} else {
ASSERT_STREQ("rax rcx r11", _gc(DiffContexts(&x, &y)));
}
}
TEST(diagnose_syscall, testWriteFailed) {
ucontext_t x, y;
diagnose_syscall(__NR_write, -1, Z, Z, Z, Z, Z, Z, &x, &y);
if (IsFreebsd()) {
ASSERT_STREQ("rax rcx r8 r9 r10 r11 cf", _gc(DiffContexts(&x, &y)));
} else if (IsBsd()) {
ASSERT_STREQ("rax rcx r11 cf", _gc(DiffContexts(&x, &y)));
} else {
ASSERT_STREQ("rax rcx r11", _gc(DiffContexts(&x, &y)));
}
}

View file

@ -19,6 +19,7 @@
#include "libc/calls/calls.h"
#include "libc/calls/ucontext.h"
#include "libc/runtime/runtime.h"
#include "libc/testlib/ezbench.h"
#include "libc/testlib/testlib.h"
int x;
@ -47,3 +48,17 @@ TEST(getcontext, test) {
ASSERT_TRUE(ok1);
ASSERT_TRUE(ok2);
}
void SetGetContext(void) {
static int a;
a = 0;
getcontext(&context);
if (!a) {
a = 1;
setcontext(&context);
}
}
BENCH(getcontext, bench) {
EZBENCH2("get/setcontext", donothing, SetGetContext());
}

View file

@ -25,6 +25,7 @@
#include "libc/testlib/ezbench.h"
#include "libc/testlib/testlib.h"
#include "libc/thread/posixthread.internal.h"
#include "libc/thread/spawn.h"
int THREADS = 16;
int ITERATIONS = 100;

View file

@ -16,10 +16,18 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/intrin/segmentation.h"
#include "libc/calls/calls.h"
#include "libc/calls/struct/sigaction.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/intrin/fsgsbase.h"
#include "libc/intrin/kprintf.h"
#include "libc/intrin/segmentation.h"
#include "libc/nexgen32e/threaded.h"
#include "libc/nt/version.h"
#include "libc/sysv/consts/sa.h"
#include "libc/sysv/consts/sig.h"
#include "libc/testlib/testlib.h"
void SetUpOnce(void) {
@ -27,19 +35,35 @@ void SetUpOnce(void) {
ASSERT_SYS(0, 0, pledge("stdio rpath", 0));
}
void OnTrap(int sig, struct siginfo *si, void *vctx) {
struct ucontext *ctx = vctx;
}
void TriggerSignal(void) {
struct sigaction old;
struct sigaction sig = {.sa_sigaction = OnTrap, .sa_flags = SA_SIGINFO};
sched_yield();
sigaction(SIGTRAP, &sig, &old);
asm("int3");
sigaction(SIGTRAP, &old, 0);
sched_yield();
}
TEST(arch_prctl, fs) {
if (IsLinux() || IsOpenbsd()) {
if (IsLinux() || IsFreebsd() || IsNetbsd() || IsOpenbsd()) {
uint64_t n, x;
x = 0xdeadbeef;
arch_prctl(ARCH_SET_FS, &x);
ASSERT_NE(-1, arch_prctl(ARCH_GET_FS, (intptr_t)&n));
ASSERT_EQ((intptr_t)&x, n);
ASSERT_EQ(0xdeadbeef, fs((int64_t *)0));
TriggerSignal();
ASSERT_EQ(0xdeadbeef, fs((int64_t *)0));
}
}
TEST(arch_prctl, pointerRebasingFs) {
if (IsLinux() || IsOpenbsd()) {
if (IsLinux() || IsFreebsd() || IsOpenbsd() || IsNetbsd()) {
unsigned long s[] = {0x0706050403020100, 0x0f0e0d0c0b0a0908};
ASSERT_EQ(0x0706050403020100, s[0]);
ASSERT_EQ(0, arch_prctl(ARCH_SET_FS, 1));
@ -53,26 +77,52 @@ TEST(arch_prctl, pointerRebasingFs) {
}
TEST(arch_prctl, gs) {
if (IsLinux()) {
if (IsLinux() || IsFreebsd() || IsNetbsd() || IsXnu()) {
uint64_t n, x;
x = 0xdeadbeef;
arch_prctl(ARCH_SET_GS, &x);
ASSERT_NE(-1, arch_prctl(ARCH_GET_GS, (intptr_t)&n));
ASSERT_EQ((intptr_t)&x, n);
if (!IsXnu()) {
ASSERT_NE(-1, arch_prctl(ARCH_GET_GS, (intptr_t)&n));
ASSERT_EQ((intptr_t)&x, n);
}
ASSERT_EQ(0xdeadbeef, gs((int64_t *)0));
TriggerSignal();
ASSERT_EQ(0xdeadbeef, gs((int64_t *)0));
}
}
TEST(arch_prctl, pointerRebasing) {
if (IsLinux()) {
if (IsLinux() || IsFreebsd() || IsNetbsd() || IsXnu()) {
unsigned long s[] = {0x0706050403020100, 0x0f0e0d0c0b0a0908};
ASSERT_EQ(0x0706050403020100, s[0]);
ASSERT_EQ(0, arch_prctl(ARCH_SET_GS, 1));
ASSERT_EQ(0x0807060504030201, gs(&s[0]));
ASSERT_EQ(0, arch_prctl(ARCH_SET_GS, 2));
ASSERT_EQ(0x0908070605040302, gs(&s[0]));
intptr_t gs;
ASSERT_EQ(0, arch_prctl(ARCH_GET_GS, &gs));
ASSERT_EQ(2, gs);
if (!IsXnu()) {
intptr_t gs;
ASSERT_EQ(0, arch_prctl(ARCH_GET_GS, &gs));
ASSERT_EQ(2, gs);
}
}
}
TEST(fsgsbase, fs) {
if (!_have_fsgsbase()) return;
int64_t mem = 0xdeadbeef;
_wrfsbase(&mem);
ASSERT_EQ(&mem, _rdfsbase());
ASSERT_EQ(0xdeadbeef, fs((int64_t *)0));
TriggerSignal();
ASSERT_EQ(0xdeadbeef, fs((int64_t *)0));
}
TEST(fsgsbase, gs) {
if (!_have_fsgsbase()) return;
int64_t mem = 0xdeadbeef;
_wrgsbase(&mem);
ASSERT_EQ(&mem, _rdgsbase());
ASSERT_EQ(0xdeadbeef, gs((int64_t *)0));
TriggerSignal();
ASSERT_EQ(0xdeadbeef, gs((int64_t *)0));
}

View file

@ -18,14 +18,18 @@
*/
#include "libc/calls/calls.h"
#include "libc/dce.h"
#include "libc/intrin/kprintf.h"
#include "libc/intrin/pthread.h"
#include "libc/macros.internal.h"
#include "libc/mem/mem.h"
#include "libc/runtime/runtime.h"
#include "libc/runtime/stack.h"
#include "libc/sysv/consts/prot.h"
#include "libc/testlib/ezbench.h"
#include "libc/testlib/subprocess.h"
#include "libc/testlib/testlib.h"
#include "libc/thread/thread.h"
#if 0
static void *Increment(void *arg) {
ASSERT_EQ(gettid(), pthread_getthreadid_np());
return (void *)((uintptr_t)arg + 1);
@ -75,7 +79,7 @@ TEST(pthread_detach, testBigStack) {
pthread_t id;
pthread_attr_t attr;
ASSERT_EQ(0, pthread_attr_init(&attr));
ASSERT_EQ(0, pthread_attr_setstacksize(&attr, 2 * 1024 * 1024));
ASSERT_EQ(0, pthread_attr_setstacksize(&attr, 2 * 1000 * 1000));
ASSERT_EQ(0, pthread_create(&id, &attr, CheckStack, 0));
ASSERT_EQ(0, pthread_attr_destroy(&attr));
ASSERT_EQ(0, pthread_join(id, 0));
@ -101,7 +105,6 @@ TEST(pthread_detach, testCustomStack_withReallySmallSize) {
ASSERT_EQ(0, pthread_join(id, 0));
free(stk);
}
#endif
TEST(pthread_exit, mainThreadWorks) {
// _Exit1() can't set process exit code on XNU/NetBSD/OpenBSD.
@ -115,3 +118,31 @@ TEST(pthread_exit, mainThreadWorks) {
EXITS(0);
}
}
static void CreateJoin(void) {
pthread_t id;
ASSERT_EQ(0, pthread_create(&id, 0, Increment, 0));
ASSERT_EQ(0, pthread_join(id, 0));
}
// this is de facto the same as create+join
static void CreateDetach(void) {
pthread_t id;
ASSERT_EQ(0, pthread_create(&id, 0, Increment, 0));
ASSERT_EQ(0, pthread_detach(id));
}
// this is really fast
static void CreateDetached(void) {
pthread_attr_t attr;
ASSERT_EQ(0, pthread_attr_init(&attr));
ASSERT_EQ(0, pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED));
ASSERT_EQ(0, pthread_create(0, &attr, Increment, 0));
ASSERT_EQ(0, pthread_attr_destroy(&attr));
}
BENCH(pthread_create, bench) {
EZBENCH2("CreateJoin", donothing, CreateJoin());
EZBENCH2("CreateDetach", donothing, CreateDetach());
EZBENCH2("CreateDetached", donothing, CreateDetached());
}