Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 14 additions & 4 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ project('cimba', ['c'],
]
)

# We also need the portable Netwide Assembler for assembly code
add_languages('nasm', required : true, native : true)
# We also need the portable Netwide Assembler for the x86-64 assembly code
if host_machine.cpu_family() == 'x86_64'
add_languages('nasm', required : true, native : true)
endif

# Try to find CUDA, but don't fail if it's missing
add_languages('cuda', required : false, native : true)
Expand All @@ -33,11 +35,18 @@ cc = meson.get_compiler('c')
# Flags common to all build types
common_flags = [
'-Wno-pedantic',
'-D_POSIX_C_SOURCE=200809L',
'-fno-semantic-interposition',
'-ffp-contract=off',
]

if host_machine.system() == 'darwin'
# Keep Darwin extensions such as pthread_get_stackaddr_np() and
# arc4random_buf() visible
common_flags += '-D_DARWIN_C_SOURCE'
else
common_flags += '-D_POSIX_C_SOURCE=200809L'
endif

# Flags per build type
# 'debug' : full asserts and logging, no optimization
# 'release' : standard optimized build, release asserts only
Expand Down Expand Up @@ -81,7 +90,8 @@ elif get_option('pgo') == 'use'
desired_flags += pgo_flags
endif

if host_machine.system() == 'linux'
# Matches CMB_THREAD_LOCAL in cmi_config.h: initial-exec on x86-64 Linux only
if host_machine.system() == 'linux' and host_machine.cpu_family() == 'x86_64'
desired_flags += ['-ftls-model=initial-exec' ]
endif

Expand Down
16 changes: 16 additions & 0 deletions src/cimba.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,17 @@
* limitations under the License.
*/

/* glibc's _FORTIFY_SOURCE (on by default in e.g. Ubuntu's GCC) replaces
* longjmp with __longjmp_chk, which aborts with "longjmp causes uninitialized
* stack frame" when a trial abandons from a coroutine stack back to the worker
* stack. x86-64 uses __builtin_longjmp; arm64 Linux needs the plain longjmp. */
#if defined(__linux__) && defined(__aarch64__)
#undef _FORTIFY_SOURCE
#endif

#include <inttypes.h>
#include <pthread.h>
#include <setjmp.h>
#include <stdint.h>

#include "cimba.h"
Expand Down Expand Up @@ -75,9 +84,16 @@ static uint64_t leaking_trials;
static CMB_THREAD_LOCAL cimba_trial_cleanup_func *trial_cleanup_func = NULL;
static CMB_THREAD_LOCAL void *trial_cleanup_arg = NULL;

#if CMI_ARCH == CMI_ARM64
/* Clang does not implement __builtin_setjmp on AArch64, use the libc version */
static CMB_THREAD_LOCAL jmp_buf recovery_buf;
#define recovery_set() setjmp(recovery_buf)
#define recovery_jump() longjmp(recovery_buf, 1)
#else
static CMB_THREAD_LOCAL intptr_t recovery_buf[5];
#define recovery_set() __builtin_setjmp((void **)recovery_buf)
#define recovery_jump() __builtin_longjmp((void **)recovery_buf, 1)
#endif

extern void cmi_coroutine_recovery_prepare(void *stack_marker);
extern void cmi_coroutine_recovery_finalize(void *stack_marker);
Expand Down
24 changes: 17 additions & 7 deletions src/cmi_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,34 +23,41 @@

/*
* Supported toolchains:
* - Architecture: x86-64 / AMD64
* - Operating system: Linux, or Windows via MinGW-w64
* - Architecture: x86-64 / AMD64, or arm64 (Apple Silicon, Linux)
* - Operating system: Linux, Windows via MinGW-w64, or macOS
* - Compiler: GCC or Clang, C11 or later
* (the library itself is built as C23)
*
* In particular, MSVC is not supported.
*/

/*
* Identify the processor architecture. So far, only AMD64/x86-64 is supported.
* Identify the processor architecture. AMD64/x86-64 everywhere, and arm64 on
* macOS (Apple Silicon) and Linux.
*/
#define CMI_AMD64 1
#define CMI_ARM64 2
#if (defined (__amd64__) || defined (__amd64) || defined (__x86_64__) || \
defined (__x86_64) || defined (_M_X64) || defined (_M_AMD64))
#define CMI_ARCH CMI_AMD64
#elif defined (__aarch64__) || defined (__arm64__)
#define CMI_ARCH CMI_ARM64
#else
#error "Platform architecture not yet supported."
#endif

/*
* Identify the operating system. So far, only Linux and Windows are supported.
* Identify the operating system. Linux, Windows, and macOS are supported.
*/
#define CMI_LINUX 1
#define CMI_WINDOWS 2
#define CMI_MACOS 3
#if defined (__linux__) || defined (__linux) || defined (linux)
#define CMI_OS CMI_LINUX
#elif defined (_WIN64) || defined (_WIN32) || defined (__WIN32__)
#define CMI_OS CMI_WINDOWS
#elif defined (__APPLE__) && defined (__MACH__)
#define CMI_OS CMI_MACOS
#else
#error "Platform operating system not yet supported."
#endif
Expand Down Expand Up @@ -121,10 +128,13 @@
__attribute__((format(printf, fmt_idx, first_arg)))

/*
* Thread-local storage qualifier. _Thread_local is C11; on Linux we pin the TLS
* model to initial-exec for fast access from the worker threads.
* Thread-local storage qualifier. _Thread_local is C11; on x86-64 Linux we pin
* the TLS model to initial-exec for fast access from the worker threads. Not on
* arm64 Linux: a library dlopen'ed late (e.g. from Python) can then fail with
* "cannot allocate memory in static TLS block", and the default TLS descriptor
* model is already cheap there.
*/
#if CMI_OS == CMI_LINUX
#if CMI_OS == CMI_LINUX && CMI_ARCH == CMI_AMD64
#define CMB_THREAD_LOCAL _Thread_local __attribute__((tls_model("initial-exec")))
#else
#define CMB_THREAD_LOCAL _Thread_local
Expand Down
3 changes: 2 additions & 1 deletion src/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@ if host_machine.system() == 'windows'
elif lto_feature.enabled() or (lto_feature.auto() and get_option('buildtype').startswith('release'))
cimba_lto = ['b_lto=true']
if get_option('default_library') != 'shared'
cimba_lto_args = ['-ffat-lto-objects']
# Apple Clang does not accept -ffat-lto-objects
cimba_lto_args = cc.get_supported_arguments('-ffat-lto-objects')
endif
endif

Expand Down
38 changes: 38 additions & 0 deletions src/port/aarch64/linux/cmb_random_hwseed.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Random seed generation for arm64 Linux.
*
* Copyright (c) FBarrca 2026.
* Licensed under the Apache License, Version 2.0.
*/

#include <pthread.h>
#include <stdint.h>
#include <sys/random.h>
#include <time.h>

#include "cmb_assert.h"
#include "cmb_random.h"

uint64_t cmb_random_hwseed(void)
{
uint64_t seed = 0u;
if (getrandom(&seed, sizeof(seed), 0) != (ssize_t)sizeof(seed)) {
seed = 0u;
}

if (seed == 0u) {
/* No kernel entropy, mash the thread id, clock and virtual counter */
seed = (uint64_t)pthread_self() << 32;
struct timespec ts;
const int r = clock_gettime(CLOCK_REALTIME, &ts);
cmb_assert_release(r == 0);
seed += (uint64_t)(ts.tv_nsec ^ ts.tv_sec);

uint64_t ticks;
__asm__ volatile("mrs %0, cntvct_el0" : "=r"(ticks));
seed ^= ticks;
}

cmb_assert_debug(seed != 0u);
return seed;
}
76 changes: 76 additions & 0 deletions src/port/aarch64/linux/cmi_coroutine_context.S
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
* Context switch and coroutine trampoline for arm64 Linux (AAPCS64, ELF).
*
* Same frame layout as the arm64 macOS port: callee-saved X19-X30, D8-D15,
* then FPCR and FPSR, 176 bytes in total. See cmi_coroutine_context.c for the
* initial frame of a new coroutine.
*
* The object carries no GNU_PROPERTY_AARCH64_FEATURE_1_AND note, so the
* linker clears BTI/PAC marking from any binary that links Cimba. The
* hand-rolled stack switch returns into frames it did not call, which BTI
* landing pads and return address signing are not designed for.
*
* Copyright (c) FBarrca 2026.
* Licensed under the Apache License, Version 2.0.
*/

.text
.p2align 2

.globl cmi_coroutine_context_switch
.type cmi_coroutine_context_switch, %function
cmi_coroutine_context_switch:
sub sp, sp, #176
stp x19, x20, [sp, #0]
stp x21, x22, [sp, #16]
stp x23, x24, [sp, #32]
stp x25, x26, [sp, #48]
stp x27, x28, [sp, #64]
stp x29, x30, [sp, #80]
stp d8, d9, [sp, #96]
stp d10, d11, [sp, #112]
stp d12, d13, [sp, #128]
stp d14, d15, [sp, #144]
mrs x9, fpcr
mrs x10, fpsr
stp x9, x10, [sp, #160]

mov x9, sp
str x9, [x0]
ldr x9, [x1]
mov sp, x9

ldp x9, x10, [sp, #160]
msr fpcr, x9
msr fpsr, x10
ldp d14, d15, [sp, #144]
ldp d12, d13, [sp, #128]
ldp d10, d11, [sp, #112]
ldp d8, d9, [sp, #96]
ldp x29, x30, [sp, #80]
ldp x27, x28, [sp, #64]
ldp x25, x26, [sp, #48]
ldp x23, x24, [sp, #32]
ldp x21, x22, [sp, #16]
ldp x19, x20, [sp, #0]
add sp, sp, #176

mov x0, x2
ret
.size cmi_coroutine_context_switch, . - cmi_coroutine_context_switch

.p2align 2
.globl cmi_coroutine_trampoline
.type cmi_coroutine_trampoline, %function
cmi_coroutine_trampoline:
stp x29, x30, [sp, #-16]!
mov x29, sp
mov x0, x20
mov x1, x21
blr x19
mov x16, x22
ldp x29, x30, [sp], #16
br x16
.size cmi_coroutine_trampoline, . - cmi_coroutine_trampoline

.section .note.GNU-stack, "", %progbits
Loading