From: Fabien Romano Subject: Re: lang/node, wip v8-wasm support for yarn berry To: Volker Schlecht , ports@openbsd.org Date: Mon, 6 Jul 2026 08:47:06 +0100 On 7/4/26 13:18, Volker Schlecht wrote: > Attached is your diff against 24.18.0 in -current. I don't see any wasm-related > tests failing here, but the failure cases that you mentioned seem to be the same > as or similar to random failures we're seeing while building the chromiums when > running the wasm version of rollup. To reproduce, you need amd64(V8_ENABLE_CET_IBT) and a specific memory layout. When V8 patches a Wasm jump slot and has to fall back to the far jump table, it generates twice endbr ; nop, it emit insn before checking relative offset. V8 WASM uses jump tables; thus, it overwrites an other function call slot. deps/v8/src/wasm/jump-table-assembler.h // The jump table is the central dispatch point for all (direct and indirect) // invocations in WebAssembly. It holds one slot per function in a module, with // each slot containing a dispatch to the currently published {WasmCode} that // corresponds to the function. Fatal error in ../deps/v8/src/wasm/jump-table-assembler.h, line 163 Debug check failed: kJumpTableSlotSize == jtasm.pc_offset() (16 vs. 24). Trace/BPT trap (core dumped) static void PatchJumpTableSlot(WritableJumpTablePair& jump_table_pair, Address jump_table_slot, Address far_jump_table_slot, Address target) { // First, try to patch the jump table slot. JumpTableAssembler jtasm(jump_table_pair.jump_table(), jump_table_slot); if (!jtasm.EmitJumpSlot(target)) { // If that fails, we need to patch the far jump table slot, and then // update the jump table slot to jump to this far jump table slot. DCHECK_NE(kNullAddress, far_jump_table_slot); JumpTableAssembler::PatchFarJumpSlot(jump_table_pair.far_jump_table(), far_jump_table_slot, target); CHECK(jtasm.EmitJumpSlot(far_jump_table_slot)); } // We write nops here instead of skipping to avoid partial instructions in // the jump table. Partial instructions can cause problems for the // disassembler. DCHECK_EQ(kJumpTableSlotSize, jtasm.pc_offset()); FlushInstructionCache(jump_table_slot, kJumpTableSlotSize); } bool JumpTableAssembler::EmitJumpSlot(Address target) { #ifdef V8_ENABLE_CET_IBT uint32_t endbr_insn = 0xfa1e0ff3; uint32_t nop = 0x00401f0f; emit(endbr_insn, kRelaxedStore); // Add a nop to ensure that the next block is 8 byte aligned. emit(nop, kRelaxedStore); #endif intptr_t displacement = target - (pc_ + MacroAssembler::kIntraSegmentJmpInstrSize); if (!is_int32(displacement)) return false; ... } EmitJumpSlot() shouldn't emit before return false. You can compare with V8_TARGET_ARCH_ARM64 / V8_ENABLE_CONTROL_FLOW_INTEGRITY. This got catched with dcheck enabled. CONFIGURE_ARGS += --v8-with-dchecks That's why I include a patch for deps/v8/src/snapshot/embedded/platform-embedded-file-writer-generic.h I'm still running with dcheck enabled, so I didn't actually test the final diff. More tests welcome. I will run both node and node-v8 tests with dchecks. Maybe a separate commit related to dchecks only. > > I'll test drive that for a bit, but what these patches do, or how they might fix > things is beyond my capabilities, so I hope that someone else will look over > them as well, particularly if this might improve things for other ports > including v8 (chromiums, deno, codex...) There is also a regexp issue to fix. > > On 7/2/26 10:36 AM, Fabien Romano wrote: >> >> On 7/1/26 23:13, Volker Schlecht wrote: >>> On 7/1/26 11:26 PM, Fabien Romano wrote: >>>> I have a few wasm issues in node v8 for which I have patches. >>> >>> Please share, I'll be happy to include them in the port. >>> >>> >> >> Actually, I was gathering info in a draft email. >> I still have to update system & ports on my side to confirm the issue. >> >> That's a first step for wasm. Hope the diff applies. >> I manually remove a hack in Makefile. >> >> # node+v8/js-wasm randomly fool itself resulting in >> #    Range Error: Maximum call stack size exceeded >> # XXX enabling pointer compression without enabling sandbox is unsupported by V8 >> # XXX further reduce v8 flags to narrow the code path change to audit (?) >> CONFIGURE_ARGS+=    --experimental-enable-pointer-compression \ >>             --experimental-pointer-compression-shared-cage >> >> Using those two configure args, berry is stable (tested on 24.17). >> By default, the v8 test build with pointer-compression and sandbox enabled... >> so I blindly try to do the same in node except for sandbox (see configure.py). >> I hit just one "Maximum call stack size exceeded" while building electron, once. >> Since then, parcel, swc, electron all extract/install into pobj without issue. >> >> I could make a short reproducer... there is an AI one (repro.sh). >> Open and configure it to your system. >> >> With an online install, with --stack-trace-limit=10000, I get: >> RangeError: ansi-styles@npm:3.2.1: Maximum call stack size exceeded >>      at wasm-function[104]:0xbc29 >>      at wasm-function[104]:0xbc9d >>      ... 7506 frames of wasm-function[104] ... >>      at wasm-function[188]:0x11867 >>      at new PI (.../yarn.js:148:193125) >>      at new hs (.../yarn.js:148:199501) >>      at Object.Dit (.../yarn.js:191:212680) >>      at async e.fetchFromNetwork (.../yarn.js:693:3984) >>      at async e.fetchPackageFromCache (.../yarn.js:198:3374) >>      at async e.fetch (.../yarn.js:693:3337) >> >> I'm trying to understand how it derails in the libzip wasm embeded in berry. >> AI tried to stress the same libzip function. It only reproduce inside berry. >> The pointer-compression hack may be a hint for an expert who know v8 internals. >> >> It should be reproducible offline also (I'm trying an other repro atm). >> If you create a package that uses a local .tgz dependency, install it with berry >> (tested on corepack yarn 4.17.0), *without a cache*, it could fail. >> The code path that triggers looks very specific to what berry is doing. >> I didn't manage to get similar traces using node or v8 tests. >> >> I had another issue with regexp. >> I work around it with --regexp-interpret-all in electron. >> You can stress test node parallel/test-worker-heap-snapshot.js to reproduce. >> >> Program terminated with signal SIGTRAP, Trace/breakpoint trap. >> #0 v8::internal::RegExpMacroAssemblerX64::CheckStackGuardState(...) >> >> I also add the below change while working on node v8 tests. >> I just don't remember if this actually fixed a test. >> OpenBSD has the same 512kB thread stack size by default. >> >> In deps/v8/src/base/platform/platform-posix.cc >> >> @@ -1258,7 +1266,7 @@ bool Thread::Start() { >>     if (result != 0) return false; >>     size_t stack_size = stack_size_; >>     if (stack_size == 0) { >> -#if V8_OS_DARWIN >> +#if V8_OS_DARWIN || V8_OS_OPENBSD >>       // Default on Mac OS X is 512kB -- bump up to 1MB >>       stack_size = 1 * 1024 * 1024; >>   #elif V8_OS_AIX >> >> The below patches may also apply to other V8 copies (chromium ...) >> If I remember correctly, new V8 uses guard pages in StackSegment(). >> Maybe someone can give a look. >> Otherwise, I may write a v8 port to run tests for this purpose. >> >> For node we have to use the embedded version for tests. >> I joined a dirty node-v8-test port I made just to run tests (without gclient). >> I don't think we should run node v8 test by default considering the complexity. >> >> In node-v8-test, you may want to adjust deps/v8/.gn, examples : >> >>    # Disable pointer compression and sandbox like Node build >>    # while debugging OpenBSD Wasm stack failures. >>    v8_enable_pointer_compression = false >>    v8_enable_pointer_compression_shared_cage = false >>    v8_enable_31bit_smis_on_64bit_arch = false >>    v8_enable_external_code_space = false >>    v8_enable_sandbox = false >> >> #  # debug >> #  enable_profiling = true >> #  symbol_level = 2 >> #  v8_code_comments = true >> #  v8_enable_debugging_features = true >> #  v8_enable_disassembler = true >> #  v8_enable_object_print = true >> #  v8_enable_slow_dchecks = true >> #  v8_symbol_level = 2 >> -- Index: Makefile =================================================================== RCS file: /mnt/ext/cvs/ports/lang/node/Makefile,v diff -u -p -r1.169 Makefile --- Makefile 30 Jun 2026 18:46:22 -0000 1.169 +++ Makefile 5 Jul 2026 12:20:53 -0000 @@ -13,6 +13,7 @@ DIST_TUPLE = github qbit node-pledge 1. DISTNAME = node-${NODE_VERSION} PKGNAME = ${DISTNAME:S/v//g} EPOCH = 0 +REVISION = 0 CATEGORIES = lang devel Index: patches/patch-deps_v8_src_base_platform-posix_cc =================================================================== RCS file: /mnt/ext/cvs/ports/lang/node/patches/patch-deps_v8_src_base_platform-posix_cc,v diff -u -p -r1.8 patch-deps_v8_src_base_platform-posix_cc --- patches/patch-deps_v8_src_base_platform-posix_cc 9 May 2026 13:26:47 -0000 1.8 +++ patches/patch-deps_v8_src_base_platform-posix_cc 5 Jul 2026 13:24:14 -0000 @@ -43,6 +43,15 @@ Index: deps/v8/src/base/platform/platfor return true; #else // TODO(bbudge) Return true for all POSIX platforms. +@@ -1258,7 +1266,7 @@ bool Thread::Start() { + if (result != 0) return false; + size_t stack_size = stack_size_; + if (stack_size == 0) { +-#if V8_OS_DARWIN ++#if V8_OS_DARWIN || V8_OS_OPENBSD + // Default on Mac OS X is 512kB -- bump up to 1MB + stack_size = 1 * 1024 * 1024; + #elif V8_OS_AIX @@ -1358,7 +1366,7 @@ void Thread::SetThreadLocal(LocalStorageKey key, void* // keep this version in POSIX as most Linux-compatible derivatives will // support it. MacOS and FreeBSD are different here. Index: patches/patch-deps_v8_src_snapshot_embedded_platform-embedded-file-writer-generic_h =================================================================== RCS file: patches/patch-deps_v8_src_snapshot_embedded_platform-embedded-file-writer-generic_h diff -N patches/patch-deps_v8_src_snapshot_embedded_platform-embedded-file-writer-generic_h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ patches/patch-deps_v8_src_snapshot_embedded_platform-embedded-file-writer-generic_h 5 Jul 2026 09:33:14 -0000 @@ -0,0 +1,13 @@ +Index: deps/v8/src/snapshot/embedded/platform-embedded-file-writer-generic.h +--- deps/v8/src/snapshot/embedded/platform-embedded-file-writer-generic.h.orig ++++ deps/v8/src/snapshot/embedded/platform-embedded-file-writer-generic.h +@@ -19,7 +19,8 @@ class PlatformEmbeddedFileWriterGeneric + : target_arch_(target_arch), target_os_(target_os) { + DCHECK(target_os_ == EmbeddedTargetOs::kChromeOS || + target_os_ == EmbeddedTargetOs::kFuchsia || +- target_os_ == EmbeddedTargetOs::kGeneric); ++ target_os_ == EmbeddedTargetOs::kGeneric || ++ target_os_ == EmbeddedTargetOs::kOpenBSD); + } + + void SectionText() override; Index: patches/patch-deps_v8_src_wasm_jump-table-assembler_cc =================================================================== RCS file: patches/patch-deps_v8_src_wasm_jump-table-assembler_cc diff -N patches/patch-deps_v8_src_wasm_jump-table-assembler_cc --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ patches/patch-deps_v8_src_wasm_jump-table-assembler_cc 5 Jul 2026 16:35:06 -0000 @@ -0,0 +1,54 @@ +Fatal error in ../deps/v8/src/wasm/jump-table-assembler.h, line 163 +Debug check failed: kJumpTableSlotSize == jtasm.pc_offset() (16 vs. 24). +Trace/BPT trap (core dumped) + +This needs x64/amd64 with V8_ENABLE_CET_IBT and a code-space layout where the +compiled Wasm target is outside the rel32 range of its primary jump-table slot. +With CET, an x64 Wasm jump-table slot is 16 bytes: +endbr64; nop; jmp rel32; int3 padding. + +The buggy path tries a direct jump first. EmitJumpSlot() writes endbr64; nop +before checking rel32 reachability. If the target is too far, it returns false +after advancing pc_ by 8 bytes. The far-jump fallback then emits another +endbr64; nop and the final jmp starts at slot+16, overwriting the next slot. +Keep EmitJumpSlot() side-effect-free on failure and compute the rel32 +displacement from the real jmp location, i.e. after the CET marker. + +Index: deps/v8/src/wasm/jump-table-assembler.cc +--- deps/v8/src/wasm/jump-table-assembler.cc.orig ++++ deps/v8/src/wasm/jump-table-assembler.cc +@@ -115,15 +115,14 @@ void JumpTableAssembler::EmitLazyCompileJumpSlot(uint3 + + bool JumpTableAssembler::EmitJumpSlot(Address target) { + #ifdef V8_ENABLE_CET_IBT +- uint32_t endbr_insn = 0xfa1e0ff3; +- uint32_t nop = 0x00401f0f; +- emit(endbr_insn, kRelaxedStore); +- // Add a nop to ensure that the next block is 8 byte aligned. +- emit(nop, kRelaxedStore); ++ static constexpr int kCodeEntryMarkerSize = 2 * kInt32Size; ++#else ++ static constexpr int kCodeEntryMarkerSize = 0; + #endif + + intptr_t displacement = +- target - (pc_ + MacroAssembler::kIntraSegmentJmpInstrSize); ++ target - (pc_ + kCodeEntryMarkerSize + ++ MacroAssembler::kIntraSegmentJmpInstrSize); + if (!is_int32(displacement)) return false; + + uint8_t inst[kJumpTableSlotSize] = { +@@ -134,6 +133,13 @@ bool JumpTableAssembler::EmitJumpSlot(Address target) + memcpy(&inst[1], &displacement32, sizeof(int32_t)); + + // The jump table is updated live, so the write has to be atomic. ++#ifdef V8_ENABLE_CET_IBT ++ uint32_t endbr_insn = 0xfa1e0ff3; ++ uint32_t nop = 0x00401f0f; ++ emit(endbr_insn, kRelaxedStore); ++ // Add a nop to ensure that the next block is 8 byte aligned. ++ emit(nop, kRelaxedStore); ++#endif + emit(*reinterpret_cast(inst), kRelaxedStore); + + return true; Index: patches/patch-deps_v8_src_wasm_stacks_cc =================================================================== RCS file: patches/patch-deps_v8_src_wasm_stacks_cc diff -N patches/patch-deps_v8_src_wasm_stacks_cc --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ patches/patch-deps_v8_src_wasm_stacks_cc 5 Jul 2026 16:18:52 -0000 @@ -0,0 +1,50 @@ +Index: deps/v8/src/wasm/stacks.cc +--- deps/v8/src/wasm/stacks.cc.orig ++++ deps/v8/src/wasm/stacks.cc +@@ -4,6 +4,10 @@ + + #include "src/wasm/stacks.h" + ++#if V8_OS_OPENBSD ++#include ++#endif ++ + #include "src/base/platform/platform.h" + #include "src/execution/simulator.h" + #include "src/wasm/wasm-engine.h" +@@ -66,9 +70,18 @@ StackMemory::StackSegment::StackSegment(size_t pages) + DCHECK_GE(pages, 1); + PageAllocator* allocator = GetPlatformPageAllocator(); + size_ = pages * allocator->AllocatePageSize(); ++#if V8_OS_OPENBSD ++ limit_ = static_cast(mmap(nullptr, size_, PROT_READ | PROT_WRITE, ++ MAP_PRIVATE | MAP_ANON | MAP_STACK, -1, ++ 0)); ++ if (limit_ == MAP_FAILED) { ++ limit_ = nullptr; ++ } ++#else + limit_ = static_cast( + allocator->AllocatePages(nullptr, size_, allocator->AllocatePageSize(), + PageAllocator::kReadWrite)); ++#endif + if (limit_ == nullptr) { + V8::FatalProcessOutOfMemory(nullptr, + "StackMemory::StackSegment::StackSegment"); +@@ -76,10 +89,16 @@ StackMemory::StackSegment::StackSegment(size_t pages) + } + + StackMemory::StackSegment::~StackSegment() { ++#if V8_OS_OPENBSD ++ if (munmap(limit_, size_) != 0) { ++ V8::FatalProcessOutOfMemory(nullptr, "Release stack memory"); ++ } ++#else + PageAllocator* allocator = GetPlatformPageAllocator(); + if (!allocator->DecommitPages(limit_, size_)) { + V8::FatalProcessOutOfMemory(nullptr, "Decommit stack memory"); + } ++#endif + } + + bool StackMemory::Grow(Address current_fp) { Index: patches/patch-deps_v8_src_wasm_wasm-objects_cc =================================================================== RCS file: patches/patch-deps_v8_src_wasm_wasm-objects_cc diff -N patches/patch-deps_v8_src_wasm_wasm-objects_cc --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ patches/patch-deps_v8_src_wasm_wasm-objects_cc 5 Jul 2026 16:19:05 -0000 @@ -0,0 +1,19 @@ +Index: deps/v8/src/wasm/wasm-objects.cc +--- deps/v8/src/wasm/wasm-objects.cc.orig ++++ deps/v8/src/wasm/wasm-objects.cc +@@ -2855,7 +2855,15 @@ DirectHandle WasmContinuationO + wasm::JumpBuffer::StackState state, DirectHandle parent, + AllocationType allocation_type) { + stack->jmpbuf()->stack_limit = stack->jslimit(); ++#if V8_OS_OPENBSD ++ // OpenBSD MAP_STACK mappings cannot be entered with rsp exactly equal to the ++ // high end of the mapping. The first push from that value traps with ++ // SEGV_ACCERR, even though push would write below rsp. Keep the initial stack ++ // pointer strictly inside the mapped stack. ++ stack->jmpbuf()->sp = stack->base() - kSystemPointerSize; ++#else + stack->jmpbuf()->sp = stack->base(); ++#endif + stack->jmpbuf()->fp = kNullAddress; + stack->jmpbuf()->state = state; + DirectHandle result = Index: Makefile =================================================================== RCS file: /mnt/ext/cvs/ports/lang/node/Makefile,v diff -u -p -r1.169 Makefile --- Makefile 30 Jun 2026 18:46:22 -0000 1.169 +++ Makefile 5 Jul 2026 12:20:53 -0000 @@ -13,6 +13,7 @@ DIST_TUPLE = github qbit node-pledge 1. DISTNAME = node-${NODE_VERSION} PKGNAME = ${DISTNAME:S/v//g} EPOCH = 0 +REVISION = 0 CATEGORIES = lang devel Index: patches/patch-deps_v8_src_base_platform-posix_cc =================================================================== RCS file: /mnt/ext/cvs/ports/lang/node/patches/patch-deps_v8_src_base_platform-posix_cc,v diff -u -p -r1.8 patch-deps_v8_src_base_platform-posix_cc --- patches/patch-deps_v8_src_base_platform-posix_cc 9 May 2026 13:26:47 -0000 1.8 +++ patches/patch-deps_v8_src_base_platform-posix_cc 5 Jul 2026 13:24:14 -0000 @@ -43,6 +43,15 @@ Index: deps/v8/src/base/platform/platfor return true; #else // TODO(bbudge) Return true for all POSIX platforms. +@@ -1258,7 +1266,7 @@ bool Thread::Start() { + if (result != 0) return false; + size_t stack_size = stack_size_; + if (stack_size == 0) { +-#if V8_OS_DARWIN ++#if V8_OS_DARWIN || V8_OS_OPENBSD + // Default on Mac OS X is 512kB -- bump up to 1MB + stack_size = 1 * 1024 * 1024; + #elif V8_OS_AIX @@ -1358,7 +1366,7 @@ void Thread::SetThreadLocal(LocalStorageKey key, void* // keep this version in POSIX as most Linux-compatible derivatives will // support it. MacOS and FreeBSD are different here. Index: patches/patch-deps_v8_src_snapshot_embedded_platform-embedded-file-writer-generic_h =================================================================== RCS file: patches/patch-deps_v8_src_snapshot_embedded_platform-embedded-file-writer-generic_h diff -N patches/patch-deps_v8_src_snapshot_embedded_platform-embedded-file-writer-generic_h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ patches/patch-deps_v8_src_snapshot_embedded_platform-embedded-file-writer-generic_h 5 Jul 2026 09:33:14 -0000 @@ -0,0 +1,13 @@ +Index: deps/v8/src/snapshot/embedded/platform-embedded-file-writer-generic.h +--- deps/v8/src/snapshot/embedded/platform-embedded-file-writer-generic.h.orig ++++ deps/v8/src/snapshot/embedded/platform-embedded-file-writer-generic.h +@@ -19,7 +19,8 @@ class PlatformEmbeddedFileWriterGeneric + : target_arch_(target_arch), target_os_(target_os) { + DCHECK(target_os_ == EmbeddedTargetOs::kChromeOS || + target_os_ == EmbeddedTargetOs::kFuchsia || +- target_os_ == EmbeddedTargetOs::kGeneric); ++ target_os_ == EmbeddedTargetOs::kGeneric || ++ target_os_ == EmbeddedTargetOs::kOpenBSD); + } + + void SectionText() override; Index: patches/patch-deps_v8_src_wasm_jump-table-assembler_cc =================================================================== RCS file: patches/patch-deps_v8_src_wasm_jump-table-assembler_cc diff -N patches/patch-deps_v8_src_wasm_jump-table-assembler_cc --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ patches/patch-deps_v8_src_wasm_jump-table-assembler_cc 5 Jul 2026 16:35:06 -0000 @@ -0,0 +1,54 @@ +Fatal error in ../deps/v8/src/wasm/jump-table-assembler.h, line 163 +Debug check failed: kJumpTableSlotSize == jtasm.pc_offset() (16 vs. 24). +Trace/BPT trap (core dumped) + +This needs x64/amd64 with V8_ENABLE_CET_IBT and a code-space layout where the +compiled Wasm target is outside the rel32 range of its primary jump-table slot. +With CET, an x64 Wasm jump-table slot is 16 bytes: +endbr64; nop; jmp rel32; int3 padding. + +The buggy path tries a direct jump first. EmitJumpSlot() writes endbr64; nop +before checking rel32 reachability. If the target is too far, it returns false +after advancing pc_ by 8 bytes. The far-jump fallback then emits another +endbr64; nop and the final jmp starts at slot+16, overwriting the next slot. +Keep EmitJumpSlot() side-effect-free on failure and compute the rel32 +displacement from the real jmp location, i.e. after the CET marker. + +Index: deps/v8/src/wasm/jump-table-assembler.cc +--- deps/v8/src/wasm/jump-table-assembler.cc.orig ++++ deps/v8/src/wasm/jump-table-assembler.cc +@@ -115,15 +115,14 @@ void JumpTableAssembler::EmitLazyCompileJumpSlot(uint3 + + bool JumpTableAssembler::EmitJumpSlot(Address target) { + #ifdef V8_ENABLE_CET_IBT +- uint32_t endbr_insn = 0xfa1e0ff3; +- uint32_t nop = 0x00401f0f; +- emit(endbr_insn, kRelaxedStore); +- // Add a nop to ensure that the next block is 8 byte aligned. +- emit(nop, kRelaxedStore); ++ static constexpr int kCodeEntryMarkerSize = 2 * kInt32Size; ++#else ++ static constexpr int kCodeEntryMarkerSize = 0; + #endif + + intptr_t displacement = +- target - (pc_ + MacroAssembler::kIntraSegmentJmpInstrSize); ++ target - (pc_ + kCodeEntryMarkerSize + ++ MacroAssembler::kIntraSegmentJmpInstrSize); + if (!is_int32(displacement)) return false; + + uint8_t inst[kJumpTableSlotSize] = { +@@ -134,6 +133,13 @@ bool JumpTableAssembler::EmitJumpSlot(Address target) + memcpy(&inst[1], &displacement32, sizeof(int32_t)); + + // The jump table is updated live, so the write has to be atomic. ++#ifdef V8_ENABLE_CET_IBT ++ uint32_t endbr_insn = 0xfa1e0ff3; ++ uint32_t nop = 0x00401f0f; ++ emit(endbr_insn, kRelaxedStore); ++ // Add a nop to ensure that the next block is 8 byte aligned. ++ emit(nop, kRelaxedStore); ++#endif + emit(*reinterpret_cast(inst), kRelaxedStore); + + return true; Index: patches/patch-deps_v8_src_wasm_stacks_cc =================================================================== RCS file: patches/patch-deps_v8_src_wasm_stacks_cc diff -N patches/patch-deps_v8_src_wasm_stacks_cc --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ patches/patch-deps_v8_src_wasm_stacks_cc 5 Jul 2026 16:18:52 -0000 @@ -0,0 +1,50 @@ +Index: deps/v8/src/wasm/stacks.cc +--- deps/v8/src/wasm/stacks.cc.orig ++++ deps/v8/src/wasm/stacks.cc +@@ -4,6 +4,10 @@ + + #include "src/wasm/stacks.h" + ++#if V8_OS_OPENBSD ++#include ++#endif ++ + #include "src/base/platform/platform.h" + #include "src/execution/simulator.h" + #include "src/wasm/wasm-engine.h" +@@ -66,9 +70,18 @@ StackMemory::StackSegment::StackSegment(size_t pages) + DCHECK_GE(pages, 1); + PageAllocator* allocator = GetPlatformPageAllocator(); + size_ = pages * allocator->AllocatePageSize(); ++#if V8_OS_OPENBSD ++ limit_ = static_cast(mmap(nullptr, size_, PROT_READ | PROT_WRITE, ++ MAP_PRIVATE | MAP_ANON | MAP_STACK, -1, ++ 0)); ++ if (limit_ == MAP_FAILED) { ++ limit_ = nullptr; ++ } ++#else + limit_ = static_cast( + allocator->AllocatePages(nullptr, size_, allocator->AllocatePageSize(), + PageAllocator::kReadWrite)); ++#endif + if (limit_ == nullptr) { + V8::FatalProcessOutOfMemory(nullptr, + "StackMemory::StackSegment::StackSegment"); +@@ -76,10 +89,16 @@ StackMemory::StackSegment::StackSegment(size_t pages) + } + + StackMemory::StackSegment::~StackSegment() { ++#if V8_OS_OPENBSD ++ if (munmap(limit_, size_) != 0) { ++ V8::FatalProcessOutOfMemory(nullptr, "Release stack memory"); ++ } ++#else + PageAllocator* allocator = GetPlatformPageAllocator(); + if (!allocator->DecommitPages(limit_, size_)) { + V8::FatalProcessOutOfMemory(nullptr, "Decommit stack memory"); + } ++#endif + } + + bool StackMemory::Grow(Address current_fp) { Index: patches/patch-deps_v8_src_wasm_wasm-objects_cc =================================================================== RCS file: patches/patch-deps_v8_src_wasm_wasm-objects_cc diff -N patches/patch-deps_v8_src_wasm_wasm-objects_cc --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ patches/patch-deps_v8_src_wasm_wasm-objects_cc 5 Jul 2026 16:19:05 -0000 @@ -0,0 +1,19 @@ +Index: deps/v8/src/wasm/wasm-objects.cc +--- deps/v8/src/wasm/wasm-objects.cc.orig ++++ deps/v8/src/wasm/wasm-objects.cc +@@ -2855,7 +2855,15 @@ DirectHandle WasmContinuationO + wasm::JumpBuffer::StackState state, DirectHandle parent, + AllocationType allocation_type) { + stack->jmpbuf()->stack_limit = stack->jslimit(); ++#if V8_OS_OPENBSD ++ // OpenBSD MAP_STACK mappings cannot be entered with rsp exactly equal to the ++ // high end of the mapping. The first push from that value traps with ++ // SEGV_ACCERR, even though push would write below rsp. Keep the initial stack ++ // pointer strictly inside the mapped stack. ++ stack->jmpbuf()->sp = stack->base() - kSystemPointerSize; ++#else + stack->jmpbuf()->sp = stack->base(); ++#endif + stack->jmpbuf()->fp = kNullAddress; + stack->jmpbuf()->state = state; + DirectHandle result =