From e0d1ba38d1f7e6cbd7e3fbb1043bdf9a32d4813c Mon Sep 17 00:00:00 2001 From: Ivan FB Date: Tue, 9 Jun 2026 23:47:30 +0200 Subject: [PATCH] feat(build): enforce -d:noSignalHandler at compile time A nim-ffi library is loaded into a foreign host (Go/Rust/...) that must own OS signal handling; if the Nim runtime installs its own handlers it clobbers the host's (e.g. Go's SIGSEGV -> sigpanic recovery, stack growth, goroutine preemption), turning recoverable faults into hard process crashes. The flag can only be set on the consumer's final build command, so a dependency cannot inject it -- but it CAN refuse to compile without it. Add a compile-time guard so any consumer that omits -d:noSignalHandler fails the build with an actionable message instead of crashing at runtime (the cause of a real status-go regression). Standalone Nim binaries (nim-ffi's own tests) build with -d:ffiAllowSignalHandler. Co-Authored-By: Claude Opus 4.8 --- ffi.nimble | 6 +++--- ffi/ffi_context.nim | 15 ++++++++++++++- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/ffi.nimble b/ffi.nimble index 8312302..9e195a6 100644 --- a/ffi.nimble +++ b/ffi.nimble @@ -1,6 +1,6 @@ # ffi.nimble -version = "0.1.5" +version = "0.1.6" author = "Institute of Free Technology" description = "FFI framework with custom header generation" license = "MIT or Apache License 2.0" @@ -13,8 +13,8 @@ requires "chronicles" requires "taskpools" requires "cbor_serialization" -const nimFlagsOrc = "--mm:orc -d:chronicles_log_level=WARN" -const nimFlagsRefc = "--mm:refc -d:chronicles_log_level=WARN" +const nimFlagsOrc = "--mm:orc -d:chronicles_log_level=WARN -d:ffiAllowSignalHandler" +const nimFlagsRefc = "--mm:refc -d:chronicles_log_level=WARN -d:ffiAllowSignalHandler" import std/[algorithm, os, strutils] diff --git a/ffi/ffi_context.nim b/ffi/ffi_context.nim index 48e5e1b..c05b301 100644 --- a/ffi/ffi_context.nim +++ b/ffi/ffi_context.nim @@ -6,7 +6,20 @@ {.passc: "-fPIC".} -import std/[atomics, locks, options, tables, sequtils] +# Embedded in a foreign host (Go/Rust/...) the host must own OS signal handling; +# Nim installing its own handlers clobbers it (e.g. Go's SIGSEGV -> sigpanic). +# Enforce -d:noSignalHandler; standalone Nim binaries opt out via -d:ffiAllowSignalHandler. +when not defined(noSignalHandler) and not defined(ffiAllowSignalHandler): + {. + error: + "nim-ffi: missing required compile flag. If this library is embedded in a " & + "host process (Go/Rust/...), build with -d:noSignalHandler so the host keeps " & + "ownership of OS signal handlers (it needs SIGSEGV for crash recovery, stack " & + "growth and preemption). If instead this is a standalone Nim program that owns " & + "its own process, build with -d:ffiAllowSignalHandler." + .} + +import std/[atomics, locks, json, tables, sequtils] import chronicles, chronos, chronos/threadsync, taskpools/channels_spsc_single, results import ./ffi_types, ./ffi_thread_request, ./internal/ffi_macro, ./logging