diff options
Diffstat (limited to 'lib/libc/internal/arch')
| -rw-r--r-- | lib/libc/internal/arch/aarch64/thread_self.c | 8 | ||||
| -rw-r--r-- | lib/libc/internal/arch/x86_64/Kbuild | 2 | ||||
| -rw-r--r-- | lib/libc/internal/arch/x86_64/_start.c | 2 | ||||
| -rw-r--r-- | lib/libc/internal/arch/x86_64/clone.s | 106 | ||||
| -rw-r--r-- | lib/libc/internal/arch/x86_64/tcb.c | 13 |
5 files changed, 130 insertions, 1 deletions
diff --git a/lib/libc/internal/arch/aarch64/thread_self.c b/lib/libc/internal/arch/aarch64/thread_self.c new file mode 100644 index 00000000..543b07ab --- /dev/null +++ b/lib/libc/internal/arch/aarch64/thread_self.c @@ -0,0 +1,8 @@ +#include <__thread.h> + +inline struct __thread_self *__thread_self(void) +{ + struct __thread_self *self; + __asm__("mrs %0, tpidr_el0" : "=r"(self)); + return self; +} diff --git a/lib/libc/internal/arch/x86_64/Kbuild b/lib/libc/internal/arch/x86_64/Kbuild index 830a1c5b..49e4284f 100644 --- a/lib/libc/internal/arch/x86_64/Kbuild +++ b/lib/libc/internal/arch/x86_64/Kbuild @@ -1,5 +1,7 @@ obj-y += _start.o +obj-y += clone.o obj-y += fenv.o obj-y += longjmp.o obj-y += setjmp.o obj-y += sigsetjmp.o +obj-y += tcb.o diff --git a/lib/libc/internal/arch/x86_64/_start.c b/lib/libc/internal/arch/x86_64/_start.c index f3f4fef2..e8846bfa 100644 --- a/lib/libc/internal/arch/x86_64/_start.c +++ b/lib/libc/internal/arch/x86_64/_start.c @@ -4,5 +4,5 @@ __dead __naked void _start(void) { __asm__ __volatile__("mov %rsp, %rdi\n" - "call __init\n"); + "call __libc_init\n"); } diff --git a/lib/libc/internal/arch/x86_64/clone.s b/lib/libc/internal/arch/x86_64/clone.s new file mode 100644 index 00000000..80f512b1 --- /dev/null +++ b/lib/libc/internal/arch/x86_64/clone.s @@ -0,0 +1,106 @@ +/* + * x86_64 clone syscall trampoline + * + * Goal: provide a reliable way to run a C function in the child after the + * clone(2) syscall, without depending on the parent's stack frame (which is + * invalid in the child because the child starts with a new stack pointer). + * + * API (internal): + * + * long __clone(long (*fn)(void *), void *arg, + * unsigned long flags, void *child_stack, + * int *ptid, void *newtls, int *ctid); + * + * This uses the raw Linux x86_64 clone syscall: + * long clone(unsigned long flags, void *child_stack, + * int *ptid, int *ctid, unsigned long newtls); + * + * Behavior: + * - In the parent, returns child TID (> 0) or -errno (< 0). + * - In the child, calls fn(arg) and then exits the thread/process via + * SYS_exit (does not return). + * + * Notes: + * - We explicitly place (fn,arg) onto the child's new stack before issuing + * the syscall. On success in the child, we recover them from %rsp and call. + * - This avoids relying on any locals from the parent frame. + * - The stack is aligned to 16 bytes before the call into C. + */ + + .text + .globl __clone + .type __clone, @function + +__clone: + /* + * SysV AMD64 calling convention: + * fn in %rdi + * arg in %rsi + * flags in %rdx + * child_stack in %rcx + * ptid in %r8 + * newtls in %r9 + * ctid at 8(%rsp) + */ + movq 8(%rsp), %r10 /* r10 = ctid (7th arg from caller stack) */ + + /* r11 = child_stack (we'll use it as our working "new sp") */ + movq %rcx, %r11 + + /* + * Ensure 16-byte alignment for the eventual C call: + * We'll build the child's stack as: + * [low] fn (8) + * arg (8) + * [high] <-- %rsp in child before call site + * + * We align first, then reserve 16 bytes. + */ + andq $-16, %r11 + subq $16, %r11 + + /* Store fn and arg onto the child's stack */ + movq %rdi, 0(%r11) /* fn */ + movq %rsi, 8(%r11) /* arg */ + + /* + * Prepare registers for Linux x86_64 clone syscall: + * rax = SYS_clone + * rdi = flags + * rsi = child_stack + * rdx = ptid + * r10 = ctid + * r8 = newtls + */ + movq %rdx, %rdi /* flags */ + movq %r11, %rsi /* child_stack (new sp) */ + movq %r8, %rdx /* ptid */ + movq %r9, %r8 /* newtls */ + /* r10 already holds ctid */ + + movq $56, %rax /* SYS_clone on x86_64 */ + syscall + + /* Parent: rax = child tid (>0) or -errno (<0) */ + testq %rax, %rax + jnz 1f + + /* Child: rax == 0 */ + + /* %rsp is already the child_stack we provided. Recover fn/arg from it. */ + movq 8(%rsp), %rdi /* first arg to fn = arg */ + call *0(%rsp) /* call fn(arg) */ + + /* + * If fn returns, exit the thread/process. + * Use SYS_exit (60). Return value of fn is in %rax; pass as status in %rdi. + */ + movq %rax, %rdi + movq $60, %rax /* SYS_exit */ + syscall + hlt /* should not reach */ + +1: + ret + + .size __clone, .-__clone diff --git a/lib/libc/internal/arch/x86_64/tcb.c b/lib/libc/internal/arch/x86_64/tcb.c new file mode 100644 index 00000000..6e1f0c15 --- /dev/null +++ b/lib/libc/internal/arch/x86_64/tcb.c @@ -0,0 +1,13 @@ +#include <libc/tcb.h> + +__attribute__((__always_inline__)) void __libc_tcb_set(uint64_t tcb) +{ + __asm__ volatile("wrfsbase %0" ::"r"(tcb)); +} + +__attribute__((__always_inline__)) void *__libc_tcb_get(void) +{ + void *tcb; + __asm__ volatile("rdfsbase %0" : "=r"(tcb)); + return tcb; +} |
