summaryrefslogtreecommitdiff
path: root/lib/libc/internal/arch
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libc/internal/arch')
-rw-r--r--lib/libc/internal/arch/aarch64/thread_self.c8
-rw-r--r--lib/libc/internal/arch/x86_64/Kbuild2
-rw-r--r--lib/libc/internal/arch/x86_64/_start.c2
-rw-r--r--lib/libc/internal/arch/x86_64/clone.s106
-rw-r--r--lib/libc/internal/arch/x86_64/tcb.c13
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;
+}