mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-07-02 17:28:30 +00:00
Perform more low-level code cleanup
This commit is contained in:
parent
c32e2d4486
commit
2d17ab016c
42 changed files with 977 additions and 265 deletions
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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()");
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue