Skip to content

fix(compile): ensure bytecode alignment accounts for section header - #26299

Merged
Jarred-Sumner merged 1 commit into
mainfrom
claude/fix-bytecode-alignment-26298
Jan 21, 2026
Merged

Jarred-Sumner merged 1 commit into
mainfrom
claude/fix-bytecode-alignment-26298

Conversation

@robobun

@robobun robobun commented Jan 20, 2026

Copy link
Copy Markdown
Collaborator

Summary

Fixes bytecode alignment in standalone executables to prevent crashes when loading bytecode cache on Windows.

The bytecode offset needs to be aligned such that when loaded at runtime, the bytecode pointer is 128-byte aligned. Previously, alignment was based on arbitrary memory addresses during compilation, which didn't account for the 8-byte section header prepended at runtime. This caused the bytecode to be misaligned, leading to segfaults in JSC::CachedJSValue::decode on Windows.

Root Cause

At runtime, embedded data starts 8 bytes after the PE/Mach-O section virtual address (which is page-aligned, hence 128-byte aligned). For bytecode at offset O to be aligned:

(section_va + 8 + O) % 128 == 0
=> (8 + O) % 128 == 0
=> O % 128 == 120

The previous code used std.mem.alignInSlice() which found aligned addresses based on the compilation buffer's arbitrary address, not accounting for the 8-byte header offset at load time.

Changes

  • src/StandaloneModuleGraph.zig: Calculate bytecode offset to satisfy offset % 128 == 120 instead of using alignInSlice
  • test/regression/issue/26298.test.ts: Added regression tests for bytecode cache in standalone executables

Test plan

  • Added regression test test/regression/issue/26298.test.ts with 3 test cases
  • Existing HelloWorldBytecode test passes
  • Build succeeds

Fixes #26298

🤖 Generated with Claude Code

@robobun

robobun commented Jan 20, 2026 •

Copy link
Copy Markdown
Collaborator Author
Updated 8:45 AM PT - Jan 20th, 2026

❌ Your commit 0b340105 has 2 failures in Build #35405 (All Failures):


🧪   To try this PR locally:

bunx bun-pr 26299

That installs a local version of the PR into your bun-26299 executable, so you can run:

bun-26299 --bun

@coderabbitai

coderabbitai Bot commented Jan 20, 2026 •

Copy link
Copy Markdown
Contributor

Walkthrough

Replaces the prior 256-byte alignment approach with a padding-based alignment that ensures (current_offset + padding) % 128 == 120 when embedding bytecode into the standalone executable; adds a regression test validating standalone bytecode compilation, execution, multi-module behavior, and disk-cache handling.

Changes

Cohort / File(s) Summary
Bytecode alignment logic
src/StandaloneModuleGraph.zig
Reworked bytecode embedding: compute current_offset, calculate padding so (current_offset + padding) % 128 == 120, advance the string builder by padding, use the aligned offset for the bytecode write, copy bytecode into the writable region, compute unaligned_space and total length, and update the StringPointer offset and builder length.
Regression test suite
test/regression/issue/26298.test.ts
Added tests for building/running standalone executables with --compile --bytecode covering single- and multi-module apps and exercising the disk bytecode cache (verbose), asserting stdout/stderr and exit codes; handles Windows .exe.

Possibly related PRs

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The PR title 'fix(compile): ensure bytecode alignment accounts for section header' clearly and specifically summarizes the main change: fixing bytecode alignment by accounting for the section header.
Description check ✅ Passed The PR description comprehensively covers both required template sections: it explains what the PR does (fixes bytecode alignment issue) and how it was verified (regression tests added, existing tests pass, build succeeds).
Linked Issues check ✅ Passed Code changes fully address issue #26298 requirements: bytecode offset now calculated to satisfy (offset % 128 == 120) for proper alignment accounting for the 8-byte header, preventing segfaults on Windows.
Out of Scope Changes check ✅ Passed All changes are directly in-scope: StandaloneModuleGraph.zig implements the alignment fix and regression test adds validation for bytecode cache behavior in standalone executables as required.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@test/regression/issue/26298.test.ts`:
- Around line 34-41: The variable buildStdout captured from Promise.all is
unused; update the destructuring to either omit it (e.g., const [, buildStderr,
buildExitCode] = await Promise.all([...]) or destructure only build.stderr and
build.exited) or rename it to _buildStdout to mark it intentionally unused;
apply the same change to the other similar destructuring block that collects
build.stdout, build.stderr, build.exited so there are no unused variable
warnings.

Comment thread test/regression/issue/26298.test.ts Outdated
@robobun
robobun force-pushed the claude/fix-bytecode-alignment-26298 branch from 90e72a5 to 110dcb9 Compare January 20, 2026 15:04
@robobun

robobun commented Jan 20, 2026

Copy link
Copy Markdown
Collaborator Author

Fixed the unused variable warning by changing const [buildStdout, buildStderr, buildExitCode] to const [, buildStderr, buildExitCode] in both test cases.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@test/regression/issue/26298.test.ts`:
- Around line 138-141: The test depends on an exact internal JSC diagnostic
string ("[Disk Cache] Cache hit for sourceCode") which is brittle; update the
assertion around exeStderr in test/regression/issue/26298.test.ts to be
resilient: either replace the strict toContain check with a relaxed check (e.g.,
assert that exeStderr contains "Disk Cache" or matches a regex like /Disk
Cache.*Cache hit/), or convert it to an optional/non-fatal verification (log a
warning instead of failing) and add a short comment near the expect for future
maintainers explaining the dependency on an internal JSC message; keep the
existing checks on exeStdout and exeExitCode unchanged (exeStdout, exeExitCode).

Comment thread test/regression/issue/26298.test.ts
@robobun
robobun force-pushed the claude/fix-bytecode-alignment-26298 branch from 110dcb9 to ce56e85 Compare January 20, 2026 15:16
@robobun

robobun commented Jan 20, 2026

Copy link
Copy Markdown
Collaborator Author

Updated the disk cache assertion to use a more flexible regex pattern (/\[Disk Cache\].*Cache hit/i) instead of the exact string match. This makes the test more resilient to potential changes in JSC's diagnostic format while still verifying that the bytecode cache is being used.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Comment thread src/StandaloneModuleGraph.zig
@robobun
robobun force-pushed the claude/fix-bytecode-alignment-26298 branch from ce56e85 to 724eb9b Compare January 20, 2026 15:41
@robobun

robobun commented Jan 20, 2026

Copy link
Copy Markdown
Collaborator Author

Addressed the review comment about platform-specific alignment:

  • Added comprehensive documentation explaining how the alignment works on each platform:

    • PE (Windows) / Mach-O (macOS): Data is memory-mapped 8 bytes after a page-aligned section start
    • ELF (Linux): Data is read into a heap-allocated buffer with natural alignment
  • Changed the magic number 120 to 128 - @sizeOf(u64) to make the relationship to the 8-byte header explicit

  • Explained why the target_mod=120 strategy works safely for all platforms (it's the worst-case offset needed for the 8-byte header scenario)

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@test/regression/issue/26298.test.ts`:
- Around line 114-145: The test currently runs the Bun.spawn build (variable
build) but doesn't assert its stdout/stderr or exit code before running the
produced executable; capture the build outputs (await Promise.all for
build.stdout.text(), build.stderr.text(), and build.exited or await build.exited
then read streams) and add assertions that the build exit code is 0 and
optionally that build.stderr is empty or contains expected messages, referencing
the existing Bun.spawn call that creates build and the outfile variable so
failures during the build are diagnosed before running exe.

Comment thread test/regression/issue/26298.test.ts
@robobun
robobun force-pushed the claude/fix-bytecode-alignment-26298 branch from 724eb9b to daf731e Compare January 20, 2026 15:50
@robobun

robobun commented Jan 20, 2026

Copy link
Copy Markdown
Collaborator Author

Added build step assertions to the third test case - now captures and validates that build stderr is empty and exit code is 0 before running the produced executable, matching the pattern used in the other two tests.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@src/StandaloneModuleGraph.zig`:
- Around line 432-472: The code advances string_builder.len by padding without
writing bytes, which can leave uninitialized data in the embedded blob; before
bumping string_builder.len use the string_builder.writable() slice at the
current offset (or writable[0..padding] relative to current writable) and memset
or fill it with zeros (or write zero bytes) to ensure deterministic
output—adjust the logic around computing padding and before setting
string_builder.len and aligned_offset (symbols: target_mod, padding,
string_builder.len, writable, aligned_offset) so the padding region is
explicitly zeroed.

Comment thread src/StandaloneModuleGraph.zig
The bytecode offset in standalone executables needs to be aligned such
that when loaded at runtime, the bytecode pointer is 128-byte aligned.

Previously, the bytecode was aligned based on arbitrary memory addresses
during compilation (`std.mem.alignInSlice`), which only ensured alignment
relative to the compilation buffer's address. However, at runtime, the
data starts 8 bytes after the PE/Mach-O section header (which is page-aligned).
This caused the bytecode to be misaligned by 8 bytes, leading to crashes
in JSC's bytecode cache deserialization on Windows (segfault at
`JSC::CachedJSValue::decode`).

The fix calculates the bytecode offset to satisfy: (8 + offset) % 128 == 0,
ensuring proper alignment regardless of the section's virtual address.

Fixes #26298

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@robobun
robobun force-pushed the claude/fix-bytecode-alignment-26298 branch from daf731e to 0b34010 Compare January 20, 2026 16:05
@robobun

robobun commented Jan 20, 2026

Copy link
Copy Markdown
Collaborator Author

Fixed the uninitialized padding bytes issue - now explicitly zeroing the padding region with @memset(writable[0..padding], 0) before advancing string_builder.len. This ensures deterministic output in the embedded blob.

@Jarred-Sumner
Jarred-Sumner merged commit 08103aa into main Jan 21, 2026
54 of 55 checks passed
@Jarred-Sumner
Jarred-Sumner deleted the claude/fix-bytecode-alignment-26298 branch January 21, 2026 06:42
@coderabbitai coderabbitai Bot mentioned this pull request Jan 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Segmentation fault when executing bunx oh-my-opencode@beta install

2 participants