summaryrefslogtreecommitdiff
path: root/lib/libc/arch
diff options
context:
space:
mode:
authorKacper <kacper@mail.openlinux.dev>2025-12-07 20:10:31 +0100
committerKacper <kacper@mail.openlinux.dev>2025-12-07 20:10:31 +0100
commitfc00c656c96528112d05cf0edf8631bd5eaea446 (patch)
treea6e0e6c588191a8bd1c64afc3b7a258e3e66c236 /lib/libc/arch
Add build system scaffolding and libc headers
Diffstat (limited to 'lib/libc/arch')
-rw-r--r--lib/libc/arch/Kbuild1
-rw-r--r--lib/libc/arch/x86_64/Kbuild7
-rw-r--r--lib/libc/arch/x86_64/clock_gettime.c13
-rw-r--r--lib/libc/arch/x86_64/crt0.c59
-rw-r--r--lib/libc/arch/x86_64/fenv.s97
-rw-r--r--lib/libc/arch/x86_64/longjmp.c18
-rw-r--r--lib/libc/arch/x86_64/setjmp.c17
-rw-r--r--lib/libc/arch/x86_64/sigsetjmp.c33
-rw-r--r--lib/libc/arch/x86_64/vdso_setup.c62
9 files changed, 307 insertions, 0 deletions
diff --git a/lib/libc/arch/Kbuild b/lib/libc/arch/Kbuild
new file mode 100644
index 00000000..ad3a7486
--- /dev/null
+++ b/lib/libc/arch/Kbuild
@@ -0,0 +1 @@
+obj-y += $(ARCH)/
diff --git a/lib/libc/arch/x86_64/Kbuild b/lib/libc/arch/x86_64/Kbuild
new file mode 100644
index 00000000..fc8a2d3f
--- /dev/null
+++ b/lib/libc/arch/x86_64/Kbuild
@@ -0,0 +1,7 @@
+obj-y += clock_gettime.o
+obj-y += crt0.o
+obj-y += fenv.o
+obj-y += longjmp.o
+obj-y += setjmp.o
+obj-y += sigsetjmp.o
+obj-y += vdso_setup.o
diff --git a/lib/libc/arch/x86_64/clock_gettime.c b/lib/libc/arch/x86_64/clock_gettime.c
new file mode 100644
index 00000000..6a7a3874
--- /dev/null
+++ b/lib/libc/arch/x86_64/clock_gettime.c
@@ -0,0 +1,13 @@
+#include <asm/vdso.h>
+#include <syscall.h>
+#include <time.h>
+
+int clock_gettime(clockid_t clock_id, struct timespec *tp)
+{
+#if defined(__x86_64__)
+ if (__vdso_clock_gettime)
+ return __vdso_clock_gettime(clock_id, tp);
+#endif
+
+ return syscall(clock_gettime, clock_id, tp);
+}
diff --git a/lib/libc/arch/x86_64/crt0.c b/lib/libc/arch/x86_64/crt0.c
new file mode 100644
index 00000000..2c7943ff
--- /dev/null
+++ b/lib/libc/arch/x86_64/crt0.c
@@ -0,0 +1,59 @@
+#include <linux/auxvec.h>
+#include <linux/elf.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <thread.h>
+#include <unistd.h>
+
+#define weak_reference(old, new) \
+ extern __typeof(old) new __attribute__((__weak__, __alias__(#old)))
+
+extern int main(int, char *[]);
+char **environ;
+
+static struct __thread_self thread = { .tid = 0, ._errno = 0 };
+
+struct __attribute__((packed)) auxv_t {
+ uintptr_t a_type;
+ uintptr_t a_val;
+} *__auxv;
+
+static void __vdso_setup(Elf64_Ehdr *vdso_addr __attribute__((unused)))
+{
+}
+
+weak_reference(__vdso_setup, vdso_setup);
+
+__attribute__((used)) void __libc_start(uintptr_t *sp)
+{
+ char **argv;
+ int argc;
+
+ argc = (int)(*sp);
+ argv = (char **)(++sp);
+ sp += argc;
+ environ = (char **)(++sp);
+
+ while (*sp)
+ sp++;
+
+ __auxv = (struct auxv_t *)(++sp);
+ while (__auxv->a_type != AT_NULL) {
+ if (__auxv->a_type == AT_SYSINFO_EHDR) {
+ vdso_setup((Elf64_Ehdr *)__auxv->a_val);
+ }
+
+ __auxv++;
+ }
+
+ __asm__ volatile("wrfsbase %0" ::"r"(thread));
+
+ exit(main(argc, argv));
+}
+
+__attribute__((noreturn, naked)) void _start(void)
+{
+ __asm__ __volatile__("mov %rsp, %rdi\n"
+ "call __libc_start\n");
+}
diff --git a/lib/libc/arch/x86_64/fenv.s b/lib/libc/arch/x86_64/fenv.s
new file mode 100644
index 00000000..b9a46581
--- /dev/null
+++ b/lib/libc/arch/x86_64/fenv.s
@@ -0,0 +1,97 @@
+.global feclearexcept
+.type feclearexcept,@function
+feclearexcept:
+ mov %edi,%ecx
+ and $0x3f,%ecx
+ fnstsw %ax
+ test %eax,%ecx
+ jz 1f
+ fnclex
+1: stmxcsr -8(%rsp)
+ and $0x3f,%eax
+ or %eax,-8(%rsp)
+ test %ecx,-8(%rsp)
+ jz 1f
+ not %ecx
+ and %ecx,-8(%rsp)
+ ldmxcsr -8(%rsp)
+1: xor %eax,%eax
+ ret
+
+.global feraiseexcept
+.type feraiseexcept,@function
+feraiseexcept:
+ and $0x3f,%edi
+ stmxcsr -8(%rsp)
+ or %edi,-8(%rsp)
+ ldmxcsr -8(%rsp)
+ xor %eax,%eax
+ ret
+
+.global __fesetround
+.hidden __fesetround
+.type __fesetround,@function
+__fesetround:
+ push %rax
+ xor %eax,%eax
+ mov %edi,%ecx
+ fnstcw (%rsp)
+ andb $0xf3,1(%rsp)
+ or %ch,1(%rsp)
+ fldcw (%rsp)
+ stmxcsr (%rsp)
+ shl $3,%ch
+ andb $0x9f,1(%rsp)
+ or %ch,1(%rsp)
+ ldmxcsr (%rsp)
+ pop %rcx
+ ret
+
+.global fegetround
+.type fegetround,@function
+fegetround:
+ push %rax
+ stmxcsr (%rsp)
+ pop %rax
+ shr $3,%eax
+ and $0xc00,%eax
+ ret
+
+.global fegetenv
+.type fegetenv,@function
+fegetenv:
+ xor %eax,%eax
+ fnstenv (%rdi)
+ stmxcsr 28(%rdi)
+ ret
+
+.global fesetenv
+.type fesetenv,@function
+fesetenv:
+ xor %eax,%eax
+ inc %rdi
+ jz 1f
+ fldenv -1(%rdi)
+ ldmxcsr 27(%rdi)
+ ret
+1: push %rax
+ push %rax
+ pushq $0xffff
+ pushq $0x37f
+ fldenv (%rsp)
+ pushq $0x1f80
+ ldmxcsr (%rsp)
+ add $40,%rsp
+ ret
+
+.global fetestexcept
+.type fetestexcept,@function
+fetestexcept:
+ and $0x3f,%edi
+ push %rax
+ stmxcsr (%rsp)
+ pop %rsi
+ fnstsw %ax
+ or %esi,%eax
+ and %edi,%eax
+ ret
diff --git a/lib/libc/arch/x86_64/longjmp.c b/lib/libc/arch/x86_64/longjmp.c
new file mode 100644
index 00000000..6aed69a5
--- /dev/null
+++ b/lib/libc/arch/x86_64/longjmp.c
@@ -0,0 +1,18 @@
+#include <setjmp.h>
+
+__attribute__((noreturn, naked, returns_twice)) void longjmp(jmp_buf env,
+ int val)
+{
+ __asm__ __volatile__("mov 0(%rdi), %rbx\n"
+ "mov 8(%rdi), %rbp\n"
+ "mov 16(%rdi), %r12\n"
+ "mov 24(%rdi), %r13\n"
+ "mov 32(%rdi), %r14\n"
+ "mov 40(%rdi), %r15\n"
+ "mov 48(%rdi), %rsp\n"
+ "xor %rax, %rax\n"
+ "cmp $0, %esi\n"
+ "setne %al\n"
+ "mov %eax, %eax\n"
+ "jmp *56(%rdi)\n");
+}
diff --git a/lib/libc/arch/x86_64/setjmp.c b/lib/libc/arch/x86_64/setjmp.c
new file mode 100644
index 00000000..d268ae25
--- /dev/null
+++ b/lib/libc/arch/x86_64/setjmp.c
@@ -0,0 +1,17 @@
+#include <setjmp.h>
+
+__attribute__((naked, returns_twice)) int setjmp(jmp_buf env)
+{
+ __asm__ __volatile__("mov %rbx,(%rdi)\n"
+ "mov %rbp,8(%rdi)\n"
+ "mov %r12,16(%rdi)\n"
+ "mov %r13,24(%rdi)\n"
+ "mov %r14,32(%rdi)\n"
+ "mov %r15,40(%rdi)\n"
+ "lea 8(%rsp),%rdx\n"
+ "mov %rdx,48(%rdi)\n"
+ "mov (%rsp),%rdx\n"
+ "mov %rdx,56(%rdi)\n"
+ "xor %eax,%eax\n"
+ "ret\n");
+}
diff --git a/lib/libc/arch/x86_64/sigsetjmp.c b/lib/libc/arch/x86_64/sigsetjmp.c
new file mode 100644
index 00000000..d4c0a4db
--- /dev/null
+++ b/lib/libc/arch/x86_64/sigsetjmp.c
@@ -0,0 +1,33 @@
+#include <setjmp.h>
+
+int sigsetjmp(sigjmp_buf env, int savemask)
+{
+ __asm__ __volatile__("movq %%rbx, 0(%0)\n"
+ "movq %%rbp, 8(%0)\n"
+ "movq %%r12, 16(%0)\n"
+ "movq %%r13, 24(%0)\n"
+ "movq %%r14, 32(%0)\n"
+ "movq %%r15, 40(%0)\n"
+
+ "leaq 8(%%rsp), %%rax\n"
+ "movq %%rax, 48(%0)\n"
+ "movq (%%rsp), %%rax\n"
+ "movq %%rax, 56(%0)\n"
+
+ "testl %%esi, %%esi\n"
+ "jz 1f\n"
+ "movq $2, %%rdi\n"
+ "movq $0, %%rsi\n"
+ "leaq 64(%0), %%rdx\n"
+ "movq $128, %%r10\n"
+ "movq $14, %%rax\n"
+ "syscall\n"
+
+ "1:\n"
+ "xor %%eax, %%eax\n"
+ :
+ : "r"(env), "S"(savemask)
+ : "rax", "rdi", "rdx", "r10", "memory");
+
+ return 0;
+}
diff --git a/lib/libc/arch/x86_64/vdso_setup.c b/lib/libc/arch/x86_64/vdso_setup.c
new file mode 100644
index 00000000..c410995d
--- /dev/null
+++ b/lib/libc/arch/x86_64/vdso_setup.c
@@ -0,0 +1,62 @@
+#include <linux/auxvec.h>
+#include <linux/elf.h>
+#include <string.h>
+#include <time.h>
+
+int (*__vdso_clock_gettime)(int, struct timespec *) = NULL;
+int (*__vdso_getcpu)(unsigned *, unsigned *, void *) = NULL;
+int (*__vdso_time)(long *) = NULL;
+
+static __inline __attribute__((used)) void vdso_setup(Elf64_Ehdr *vdso_addr)
+{
+ Elf64_Phdr *phdr =
+ (Elf64_Phdr *)((char *)vdso_addr + vdso_addr->e_phoff);
+
+ for (int i = 0; i < vdso_addr->e_phnum; i++) {
+ if (phdr[i].p_type == PT_DYNAMIC) {
+ Elf64_Dyn *dyn = (Elf64_Dyn *)((char *)vdso_addr +
+ phdr[i].p_offset);
+ const char *strtab = NULL;
+ Elf64_Sym *symtab = NULL;
+ for (; dyn->d_tag != DT_NULL; dyn++) {
+ if (dyn->d_tag == DT_STRTAB) {
+ strtab = (const char *)vdso_addr +
+ dyn->d_un.d_ptr;
+ } else if (dyn->d_tag == DT_SYMTAB) {
+ symtab =
+ (Elf64_Sym *)((char *)vdso_addr +
+ dyn->d_un.d_ptr);
+ }
+ }
+
+ if (strtab == NULL || symtab == NULL) {
+ return;
+ }
+
+ Elf64_Sym *sym = symtab;
+ while ((char *)sym < (char *)strtab) {
+ if (sym->st_name != 0 && sym->st_value != 0) {
+ const char *name =
+ strtab + sym->st_name;
+ if (strcmp(name,
+ "__vdso_clock_gettime") == 0)
+ __vdso_clock_gettime =
+ (void *)(vdso_addr +
+ sym->st_value);
+ else if (strcmp(name,
+ "__vdso_getcpu") == 0)
+ __vdso_getcpu =
+ (void *)(vdso_addr +
+ sym->st_value);
+ else if (strcmp(name, "__vdso_time") ==
+ 0)
+ __vdso_time =
+ (void *)(vdso_addr +
+ sym->st_value);
+ }
+ sym = (Elf64_Sym *)((char *)sym +
+ sizeof(Elf64_Sym));
+ }
+ }
+ }
+}