From 0f0c77076392a06afcb80028ac59e7dff1be3b58 Mon Sep 17 00:00:00 2001 From: Axy Date: Tue, 12 May 2026 16:11:08 +0200 Subject: [PATCH] Impl is back and even better :D --- Makefile | 7 ++-- src/align.h | 43 +++++++++++++++++++++ src/ccera.h | 3 +- src/defer.c | 93 ++++++++++++++++------------------------------ src/defer.h | 50 +++++++++---------------- src/framealloc.c | 53 ++++++++++++++++++++++++++ src/framealloc.h | 87 +++++++++++++++++++++++++++++++++++++++++++ src/jmp.c | 46 ----------------------- src/jmp.h | 19 ++-------- src/panic.c | 19 +++++----- src/panic.h | 18 +++++---- src/return_patch.h | 25 +++++++++++++ test.c | 56 ++++++++-------------------- 13 files changed, 302 insertions(+), 217 deletions(-) create mode 100644 src/align.h create mode 100644 src/framealloc.c create mode 100644 src/framealloc.h delete mode 100644 src/jmp.c create mode 100644 src/return_patch.h diff --git a/Makefile b/Makefile index 275ecad..e09a1fc 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,8 @@ NAME=ccera.a -SRCS=src/arc.c src/defer.c src/eval.c src/jmp.c src/panic.c src/value_destroy.c src/value_get.c src/value_lifetime.c src/value_new.c src/value_readf.c +SRCS=src/arc.c src/defer.c src/eval.c src/framealloc.c src/panic.c src/value_destroy.c src/value_get.c src/value_lifetime.c src/value_new.c src/value_readf.c -HEADERS=src/ccera.h src/defer.h src/jmp.h src/panic.h +HEADERS=src/align.h src/ccera.h src/defer.h src/framealloc.h src/jmp.h src/panic.h src/return_patch.h BUILDDIR=.build @@ -15,7 +15,8 @@ CFLAGS += -g #CFLAGS += -O3 #CFLAGS += -flto -ffat-lto-objects -CC=clang +#CC=clang +CC=gcc AR=ar diff --git a/src/align.h b/src/align.h new file mode 100644 index 0000000..371cd30 --- /dev/null +++ b/src/align.h @@ -0,0 +1,43 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* align.h :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: agilliar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2026/05/12 10:03:30 by agilliar #+# #+# */ +/* Updated: 2026/05/12 13:36:28 by agilliar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#ifndef ALIGN_H +# define ALIGN_H + +# include + +# ifdef __builtin_align_up + +__attribute__((always_inline)) +static inline void *align_up(void *p, uintptr_t align) +{ + return (__builtin_align_up(p, align)); +} + +# else + +__attribute__((always_inline)) +static inline void *align_up(void *p, uintptr_t align) +{ + uintptr_t n; + + n = (intptr_t) p; + if (n % align) + n = ((n / align) + n % align) * align; + n -= (intptr_t) p; + p = &((char *) p)[n]; + return (p); +} + +# endif + +#endif diff --git a/src/ccera.h b/src/ccera.h index 06a714f..dd5e495 100644 --- a/src/ccera.h +++ b/src/ccera.h @@ -6,7 +6,7 @@ /* By: agilliar +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2026/04/29 16:08:57 by agilliar #+# #+# */ -/* Updated: 2026/05/10 22:11:53 by agilliar ### ########.fr */ +/* Updated: 2026/05/11 19:26:32 by agilliar ### ########.fr */ /* */ /* ************************************************************************** */ @@ -20,6 +20,7 @@ # include "jmp.h" # include "defer.h" # include "panic.h" +# include "framealloc.h" # if UINTPTR_MAX != 0xFFFFFFFFFFFFFFFF # error "Platform's pointers are not 64 bits" diff --git a/src/defer.c b/src/defer.c index f1fe0e3..c0336ac 100644 --- a/src/defer.c +++ b/src/defer.c @@ -6,76 +6,20 @@ /* By: agilliar +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2026/05/05 17:31:36 by agilliar #+# #+# */ -/* Updated: 2026/05/11 04:13:54 by agilliar ### ########.fr */ +/* Updated: 2026/05/12 14:02:39 by agilliar ### ########.fr */ /* */ /* ************************************************************************** */ #include "defer.h" -#include -static_assert((sizeof(t_defer_frame)) == 32); -#define T_DEFER_FRAME_SIZE "32" - -static_assert((alignof(t_defer_frame)) == 8); -#define T_DEFER_FRAME_ALIGN "8" - -asm ( - ".globl _defer_store\n" - ".section .tbss,\"awT\",@nobits\n" - ".align " T_DEFER_FRAME_ALIGN "\n" - ".type _defer_store, @object\n" - ".size _defer_store, " T_DEFER_FRAME_SIZE "\n" - "_defer_store:\n" - ".zero " T_DEFER_FRAME_SIZE "\n" - ".text\n" - ); - -// TODO: impl -// -//typedef struct s_defer_frame -//{ -// void *ret_ptr; -// void *stack_ptr; -// struct s_defer_frame *prev; -// t_defer *frame; -//} t_defer_frame; - -__attribute__((naked)) -void defer_unpatch_exit(void) +t_defer_frame *defer_store(void) { - asm ( - "movq %%rax, %%rdi\n" - "movq %%rdx, %%rsi\n" - "movq %%rsp, %%rdx\n" - "movq %%fs:_defer_store@TPOFF+%c0, %%rsp\n" - "pushq %%rdi\n" - "pushq %%rsi\n" - "pushq %%rdx\n" - "pushq %%fs:_defer_store@TPOFF+%c1\n" - "callq defer_pop_frame\n" - "popq %%rdi\n" - "popq %%rsi\n" - "popq %%rdx\n" - "popq %%rax\n" - "movq %%rsi, %%rsp\n" - "movq %%rdi, -8(%%rsp)\n" - "jmpq *%%rdi\n" - :: - "n" (offsetof(t_defer_frame, stack_ptr)), - "n" (offsetof(t_defer_frame, ret_ptr))); -} + static __thread t_defer_frame store; -// TODO: impl -//typedef struct s_defer -//{ -// struct s_defer *prev; -// void (*f)(void *); -// void *dat; -// bool err; -//} t_defer; + return (&store); +} -__attribute__((used)) -void defer_pop_frame(bool err) +void defer_pop(bool err) { t_defer *curr; @@ -89,3 +33,28 @@ void defer_pop_frame(bool err) if (defer_store()->prev) *defer_store() = *defer_store()->prev; } + +__attribute__((used)) +void *defer_pop_ret(void) +{ + void *ret; + + ret = defer_store()->ret_ptr; + defer_pop(false); + return (ret); +} + +__attribute__((naked)) +void defer_pop_shim(void) +{ + asm ( + "pushq %rax\n" + "pushq %rdx\n" + "callq defer_pop_ret\n" + "movq %rax, %rsi\n" + "popq %rdx\n" + "popq %rax\n" + "pushq %rsi\n" + "ret\n" + ); +} diff --git a/src/defer.h b/src/defer.h index 6e3e62b..25f5225 100644 --- a/src/defer.h +++ b/src/defer.h @@ -6,7 +6,7 @@ /* By: agilliar +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2026/05/10 22:08:53 by agilliar #+# #+# */ -/* Updated: 2026/05/11 04:46:59 by agilliar ### ########.fr */ +/* Updated: 2026/05/12 14:01:36 by agilliar ### ########.fr */ /* */ /* ************************************************************************** */ @@ -14,6 +14,8 @@ # define DEFER_H # include +# include "framealloc.h" +# include "return_patch.h" typedef struct s_defer { @@ -26,53 +28,35 @@ typedef struct s_defer typedef struct s_defer_frame { void *ret_ptr; - void *stack_ptr; + void *frame_ptr; struct s_defer_frame *prev; t_defer *frame; } t_defer_frame; -__attribute__((always_inline)) -static inline t_defer_frame *defer_store(void) -{ - extern __thread t_defer_frame _defer_store; - - return (&_defer_store); -} -void defer_unpatch_exit(void) +t_defer_frame *defer_store(void); +void defer_pop(bool err); +void defer_pop_shim(void) __attribute__((naked)); -// Store current rsp + red zone, aligned down to stack align -// No clang 22, so no __builtin_stack_address -__attribute__((always_inline)) -static inline void defer_stack_update(void) -{ - void *addr; - - asm ("leaq -128(%%rsp), %0\n" : "=r" (addr)); - //defer_store()->stack_ptr = __builtin_align_down(addr, 16); - defer_store()->stack_ptr = (void *)((unsigned long long )addr & ~16ull); -} - __attribute__((always_inline)) static inline void defer_patch(void) { - void *ret_ptr; t_defer_frame *prev; t_defer_frame *curr; + void *ret; - ret_ptr = __builtin_return_address(0); - if (&defer_unpatch_exit != ret_ptr) + if (defer_store()->frame_ptr != __builtin_frame_address(0)) { - asm ("movq %0, 8(%%rbp)\n" : : "r" (&defer_unpatch_exit)); - prev = __builtin_alloca(sizeof(t_defer_frame)); + ret = __builtin_return_address(0); + return_patch(&defer_pop_shim); + prev = cera_alloca_align(sizeof(t_defer_frame), alignof(t_defer_frame)); curr = defer_store(); *prev = *curr; - curr->ret_ptr = ret_ptr; + curr->ret_ptr = ret; curr->frame = NULL; + curr->prev = prev; + defer_store()->frame_ptr = __builtin_frame_address(0); } - defer_stack_update(); - ret_ptr = __builtin_return_address(0); - __builtin_assume(&defer_unpatch_exit == ret_ptr); } __attribute__((always_inline)) @@ -80,7 +64,7 @@ static inline void defer(void *f, void *dat) { t_defer *curr; - curr = __builtin_alloca(sizeof(t_defer)); + curr = cera_alloca_align(sizeof(t_defer), alignof(t_defer)); defer_patch(); curr->prev = defer_store()->frame; curr->f = f; @@ -94,7 +78,7 @@ static inline void errdefer(void *f, void *dat) { t_defer *curr; - curr = __builtin_alloca(sizeof(t_defer)); + curr = cera_alloca_align(sizeof(t_defer), alignof(t_defer)); defer_patch(); curr->prev = defer_store()->frame; curr->f = f; diff --git a/src/framealloc.c b/src/framealloc.c new file mode 100644 index 0000000..4089208 --- /dev/null +++ b/src/framealloc.c @@ -0,0 +1,53 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* framealloc.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: agilliar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2026/05/11 19:22:02 by agilliar #+# #+# */ +/* Updated: 2026/05/12 14:01:24 by agilliar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "framealloc.h" + +t_framealloc *framealloc_store(void) +{ + static __thread t_framealloc store; + + return (&store); +} + +void framealloc_pop(void) +{ + t_framealloc *store; + + store = framealloc_store(); + store->frame = *store->frame.prev; +} + +__attribute__((used)) +void *framealloc_pop_ret(void) +{ + void *ret_ptr; + + ret_ptr = framealloc_store()->frame.ret_ptr; + framealloc_pop(); + return (ret_ptr); +} + +__attribute__((naked)) +void framealloc_pop_shim(void) +{ + asm ( + "pushq %rax\n" + "pushq %rdx\n" + "callq framealloc_pop_ret\n" + "movq %rax, %rsi\n" + "popq %rdx\n" + "popq %rax\n" + "pushq %rsi\n" + "retq\n" + ); +} diff --git a/src/framealloc.h b/src/framealloc.h new file mode 100644 index 0000000..a97b755 --- /dev/null +++ b/src/framealloc.h @@ -0,0 +1,87 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* framealloc.h :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: agilliar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2026/05/11 19:17:56 by agilliar #+# #+# */ +/* Updated: 2026/05/12 13:35:45 by agilliar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#ifndef FRAMEALLOC_H +# define FRAMEALLOC_H + +# include +# include "return_patch.h" +# include "align.h" + +# ifndef FRAMEALLOC_SIZE +# define FRAMEALLOC_SIZE 32768 +# endif + +typedef struct s_framealloc_frame +{ + uintptr_t used; + struct s_framealloc_frame *prev; + void *ret_ptr; + void *frame_ptr; +} t_framealloc_frame; + +typedef struct s_framealloc +{ + t_framealloc_frame frame; + char buf[FRAMEALLOC_SIZE]; +} t_framealloc; + +t_framealloc *framealloc_store(void); +void framealloc_pop_shim(void) + __attribute__((naked)); +void framealloc_pop(void); + +__attribute__((always_inline, malloc, alloc_size(1), alloc_align(2))) +static inline void *cera_alloca_align_nopatch(uintptr_t len, uintptr_t align) +{ + t_framealloc *store; + void *res; + + store = framealloc_store(); + res = &store->buf[store->frame.used]; + store->frame.used += ((uintptr_t)(align_up(res, align) - res)) + + len; + res = align_up(res, align); + return (res); +} + +__attribute__((always_inline)) +static inline void cera_alloca_patch(void) +{ + t_framealloc *store; + t_framealloc_frame prev_frame; + t_framealloc_frame *frame_p; + void *ret; + + store = framealloc_store(); + if (store->frame.frame_ptr != __builtin_frame_address(0)) + { + ret = __builtin_return_address(0); + return_patch(&framealloc_pop_shim); + prev_frame = store->frame; + frame_p = cera_alloca_align_nopatch(sizeof(t_framealloc_frame), + alignof(t_framealloc_frame)); + *frame_p = prev_frame; + store->frame.prev = frame_p; + store->frame.ret_ptr = ret; + store->frame.frame_ptr = __builtin_frame_address(0); + } +} + +__attribute__((always_inline, malloc, alloc_size(1), alloc_align(2))) +static inline void *cera_alloca_align(uintptr_t len, uintptr_t align) +{ + cera_alloca_patch(); + return (cera_alloca_align_nopatch(len, align)); +} + +#endif diff --git a/src/jmp.c b/src/jmp.c deleted file mode 100644 index 6771dba..0000000 --- a/src/jmp.c +++ /dev/null @@ -1,46 +0,0 @@ -/* ************************************************************************** */ -/* */ -/* ::: :::::::: */ -/* jmp.c :+: :+: :+: */ -/* +:+ +:+ +:+ */ -/* By: agilliar +#+ +:+ +#+ */ -/* +#+#+#+#+#+ +#+ */ -/* Created: 2026/05/10 06:19:18 by agilliar #+# #+# */ -/* Updated: 2026/05/11 04:24:42 by agilliar ### ########.fr */ -/* */ -/* ************************************************************************** */ - -#include -#include "jmp.h" - -__attribute__((returns_twice, naked)) -bool cera_setjmp(void *buf[2]) -{ - asm ( - "movq %%rbp, %c0(%%rdi)\n" - "movq %%rsp, %c1(%%rdi)\n" - "movq 0(%%rsp), %%rax\n" - "movq %%rax, %c2(%%rdi)\n" - "xorq %%rax, %%rax\n" - "retq\n" - :: - "n" (offsetof(t_jmp, base)), - "n" (offsetof(t_jmp, top)), - "n" (offsetof(t_jmp, ret))); -} - -__attribute__((noreturn, naked)) -void cera_longjmp(void *buf[2]) -{ - asm ( - "movq %c0(%%rdi), %%rbp\n" - "movq %c1(%%rdi), %%rsp\n" - "movq %c2(%%rdi), %%rax\n" - "movq %%rax, 0(%%rsp)\n" - "movl $1, %%eax\n" - "retq\n" - :: - "n" (offsetof(t_jmp, base)), - "n" (offsetof(t_jmp, top)), - "n" (offsetof(t_jmp, ret))); -} diff --git a/src/jmp.h b/src/jmp.h index 841045c..746e9fe 100644 --- a/src/jmp.h +++ b/src/jmp.h @@ -6,28 +6,15 @@ /* By: agilliar +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2026/05/10 22:08:02 by agilliar #+# #+# */ -/* Updated: 2026/05/11 01:43:42 by agilliar ### ########.fr */ +/* Updated: 2026/05/12 13:12:47 by agilliar ### ########.fr */ /* */ /* ************************************************************************** */ #ifndef JMP_H # define JMP_H -// We are storing the return address so as to always return to the right -// function, as a defer might have patched the return address of any later -// stack frames -// Clang's impl would doom us -typedef struct s_jmp -{ - void *base; - void *top; - void *ret; -} t_jmp; +# include -bool cera_setjmp(void *buf[2]) - __attribute__((returns_twice, naked)); - -void cera_longjmp(void *buf[2]) - __attribute__((noreturn, naked)); +typedef intptr_t t_jmp[3]; #endif diff --git a/src/panic.c b/src/panic.c index eb1eb10..be74604 100644 --- a/src/panic.c +++ b/src/panic.c @@ -6,13 +6,12 @@ /* By: agilliar +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2026/05/05 15:38:33 by agilliar #+# #+# */ -/* Updated: 2026/05/11 02:26:13 by agilliar ### ########.fr */ +/* Updated: 2026/05/12 13:13:03 by agilliar ### ########.fr */ /* */ /* ************************************************************************** */ #include "ccera.h" -/* t_panic_info *panic_store(void) { static __thread t_panic_info store; @@ -23,13 +22,13 @@ t_panic_info *panic_store(void) void *catch(void *f, void *dat) { void *res; - t_defer defer_store; t_panic_info prev; + cera_alloca_patch(); + defer_patch(); prev = *panic_store(); - panic_store()->defer = &defer_store; - defer_new(&defer_store); - if (__builtin_setjmp(panic_store()->jmp)) + panic_store()->frame_ptr = __builtin_frame_address(0); + if (__builtin_setjmp(&panic_store()->jmp)) { res = panic_store()->data; *panic_store() = prev; @@ -43,13 +42,15 @@ void *catch(void *f, void *dat) __attribute__((noreturn, nonnull(1))) void panic(void *err) { - defer_exit(panic_store()->defer); + while (defer_store()->frame_ptr != panic_store()->frame_ptr) + defer_pop(true); + while (framealloc_store()->frame.frame_ptr != panic_store()->frame_ptr) + framealloc_pop(); panic_store()->data = err; - __builtin_longjmp(panic_store()->jmp, 1); + __builtin_longjmp(&panic_store()->jmp, 1); } bool panicking(void) { return (panic_store()->data != NULL); } -*/ diff --git a/src/panic.h b/src/panic.h index abee2f6..213fbf5 100644 --- a/src/panic.h +++ b/src/panic.h @@ -6,23 +6,27 @@ /* By: agilliar +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2026/05/10 22:11:10 by agilliar #+# #+# */ -/* Updated: 2026/05/10 22:11:44 by agilliar ### ########.fr */ +/* Updated: 2026/05/12 12:16:56 by agilliar ### ########.fr */ /* */ /* ************************************************************************** */ #ifndef PANIC_H # define PANIC_H +# include "jmp.h" + typedef struct s_panic_info { void *data; - void *jmp[2]; - t_defer *defer; + t_jmp jmp; + void *frame_ptr; } t_panic_info; -void *catch(void *f, void *dat); -void panic(void *err) - __attribute__((noreturn, nonnull(1))); -bool panicking(void); +t_panic_info *panic_store(void); + +void panic(void *err) + __attribute__((noreturn, nonnull(1))); +void *catch(void *f, void *dat); +bool panicking(void); #endif diff --git a/src/return_patch.h b/src/return_patch.h new file mode 100644 index 0000000..a30ed14 --- /dev/null +++ b/src/return_patch.h @@ -0,0 +1,25 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* return_patch.h :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: agilliar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2026/05/11 20:08:23 by agilliar #+# #+# */ +/* Updated: 2026/05/12 08:54:17 by agilliar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#ifndef RETURN_PATCH_H +# define RETURN_PATCH_H + +__attribute__((always_inline)) +static inline void return_patch(void *p) +{ + void **frame_addr; + + frame_addr = __builtin_frame_address(0); + frame_addr[1] = p; +} + +#endif diff --git a/test.c b/test.c index 373e1aa..d1b2984 100644 --- a/test.c +++ b/test.c @@ -6,7 +6,7 @@ /* By: agilliar +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2026/04/30 17:19:58 by agilliar #+# #+# */ -/* Updated: 2026/05/11 04:12:03 by agilliar ### ########.fr */ +/* Updated: 2026/05/12 14:07:55 by agilliar ### ########.fr */ /* */ /* ************************************************************************** */ @@ -15,53 +15,29 @@ #include #include -/* -void main2(void *s) +void main2(char *orig) { - t_defer defer_store __attribute__((cleanup(defer_exit))); - defer_new(&defer_store); - - t_value arg = value_bytes_new(strlen(s) + 1); - defer(value_destroy, &arg); - panic("A"); - t_value state = value_list_new(0); - defer(value_destroy, &state); - //panic("B"); - t_value idx = value_int_new(0); - t_value fn = value_list_new(2); - defer(value_destroy, &fn); - //panic("C"); - t_value expr = value_list_new(2); - defer(value_destroy, &expr); - //panic("D"); - defer_re(&defer_store); - value_list_get(fn)->list[0] = state; - value_list_get(fn)->list[1] = idx; - value_list_get(expr)->list[0] = fn; - value_list_get(expr)->list[1] = arg; - defer(value_destroy, &expr); - //panic("E"); - t_value val = value_copy(expr); - errdefer(value_destroy, &val); - //panic("F"); -} - -int main(void) -{ - char *err = catch(main2, "hello cera"); - if (err) - { - printf("%s\n", err); - return (1); - } + char *s = malloc(strlen(orig) + 1); + defer(free, s); + char *s2 = malloc(strlen(orig) + 1); + defer(free, s2); + panic("error!"); } -*/ int main(void) { + char *msg; + char *orig = "hello cera!"; char *s = malloc(strlen(orig) + 1); + cera_alloca_align(10, 30); defer(free, s); + msg = catch(main2, orig); + if (msg) + { + printf("%s\n", msg); + return (-1); + } char *s2 = malloc(strlen(orig) + 1); defer(free, s2); } -- 2.53.0