From d4c87c1f94c4678eea7d32a8f5f41c72420fadb6 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 | 13 +++++++++++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/ffi.nimble b/ffi.nimble index 67e780e..14984dd 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" @@ -12,8 +12,8 @@ requires "chronos" requires "chronicles" requires "taskpools" -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" task buildffi, "Compile the library": exec "nim c " & nimFlagsOrc & " --app:lib --noMain ffi.nim" diff --git a/ffi/ffi_context.nim b/ffi/ffi_context.nim index 729d9a4..61d806c 100644 --- a/ffi/ffi_context.nim +++ b/ffi/ffi_context.nim @@ -2,6 +2,19 @@ {.pragma: callback, cdecl, raises: [], gcsafe.} {.passc: "-fPIC".} +# 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