Skip to content
Open
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
30 changes: 30 additions & 0 deletions vlib/v/gen/c/cgen.v
Original file line number Diff line number Diff line change
Expand Up @@ -9739,6 +9739,36 @@ fn (mut g Gen) write_init_function() {
// provide a constructor/destructor pair, ensuring that all constants
// are initialized just once, and that they will be freed too.
// Note: os.args in this case will be [].
//
// Fix for vlang/v#27178: when the GC mode is one of the Boehm
// variants, host-spawned threads (Rust cargo workers, C# tasks,
// JNI threads, etc.) that enter V code must be registered with
// libgc via GC_register_my_thread() to avoid the libgc
// "Collecting from unknown thread" abort. The per-thread step
// is inherently caller-side, but `GC_allow_register_threads()`
// is process-level and can be done by V itself at library load
// time. Emit a constructor for it so the caller doesn't have
// to remember.
if g.pref.os != .windows && g.pref.gc_mode in [.boehm_full, .boehm_incr,
.boehm_full_opt, .boehm_incr_opt, .boehm_leak] {
g.writeln('__attribute__ ((constructor))')
g.writeln('static void _v_shared_lib_gc_init(void) {')
Comment on lines +9754 to +9755
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Assign constructor priority for GC init

The new _v_shared_lib_gc_init and existing _vinit_caller are both emitted as plain __attribute__((constructor)) functions, but no priority is specified, so their relative execution order is not guaranteed by the toolchain. If _vinit_caller runs first, _vinit() can touch GC before GC_set_pages_executable(0) / GC_allow_register_threads() / GC_INIT() run, which defeats the fix and can still surface hardened-runtime or "unknown thread" failures in shared-library hosts. Use explicit constructor priorities (or call GC init from _vinit_caller before _vinit) to enforce ordering.

Useful? React with 👍 / 👎.

// Fix for macOS hardened-runtime: libgc's trampoline allocator
// requests rwx pages via mprotect(PROT_EXEC), which macOS
// hardened runtime refuses by default. Disabling executable
// pages before GC_INIT() opts libgc out of trampolines (it
// falls back to a non-JIT mark stack). Required for V `-prod`
// + shared lib + macOS, and harmless on Linux.
g.writeln('\tGC_set_pages_executable(0);')
g.writeln('#if defined(GC_THREADS)')
// Fix for vlang/v#27178: per-process thread-registration enable.
g.writeln('\tGC_allow_register_threads();')
g.writeln('#endif')
// Force explicit GC_INIT() so libgc doesn't lazy-init AFTER
// the trampoline pref has been overridden by something else.
g.writeln('\tGC_INIT();')
g.writeln('}')
}
if g.pref.os != .windows {
g.writeln('__attribute__ ((constructor))')
}
Expand Down
Loading