Fix macOS Apple Silicon build (Homebrew paths, rpath quoting, JIT W^X SIGBUS)#1768
Open
dg1sbg wants to merge 2 commits into
Open
Fix macOS Apple Silicon build (Homebrew paths, rpath quoting, JIT W^X SIGBUS)#1768dg1sbg wants to merge 2 commits into
dg1sbg wants to merge 2 commits into
Conversation
Two problems prevented `ninja -C build` from working on an Apple Silicon Mac with a Homebrew toolchain: * boost (used by clbind/config.h) ships no pkg-config file and is therefore not declared as a koga `library`; clasp relies on it being on the default include path. units.lisp only added the Intel Homebrew prefix `/usr/local/include`, so `/opt/homebrew/include` was never searched and the build failed immediately with "'boost/config.hpp' file not found". Add `/opt/homebrew/include` for darwin when it exists (probe-guarded, so Intel and Linux are unaffected). * The rpath was emitted as an unquoted `-Wl,-rpath,<abs path>`. When the build directory contains a space, ninja passes the flag through /bin/sh which then splits it at the space, and the link fails with "no such file or directory: '<tail of path>'". Quote the path. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
On Apple Silicon, the LLVM ORC JITLink memory slab is mapped MAP_JIT and is write-protected (execute mode) per-thread by default; a thread must call pthread_jit_write_protect_np(false) before writing to it. clasp writes Lisp object pointers into each native module's literals vector, which lives in that JIT memory. Those writes were not bracketed by a switch to write mode, so on Apple Silicon they fault with SIGBUS (EXC_BAD_ACCESS code=2, KERN_PROTECTION_FAILURE) -- which manifested as a crash in loadltv::attr_clasp_module_native while loading freshly compiled native FASLs during the "Compiling Clasp native image" bootstrap step. (On x86-64 and Linux the rwx page is genuinely writable, so the bug was latent there.) The helpers JITDataReadWriteMaybeExecute()/JITDataReadExecute() already exist for exactly this but had no callers. Bracket every write into JIT-resident literals memory with them: * core::core__literals_vset (compiler.cc) * llvmo::code_literal_set (code.cc) * loadltv::attr_clasp_module_native (loadltv.cc) * loadltv::attr_clasp_function_native_estranged (loadltv.cc) * snapshot-load literal relocation memcpy (snapshotSaveLoad.cc) Reads of the literals vector are left untouched: MAP_JIT memory is readable in execute mode, only writes fault. The write-mode window is kept as small as possible (the bare store) so no JIT code is executed while the thread is in write mode. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2667fb9 to
1e7644f
Compare
This was referenced Jun 9, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Makes
ninja -C buildwork on a macOS Apple Silicon machine with a Homebrew LLVM 22 toolchain. Three independent problems are fixed; the build now compiles, links, completes the native-image bootstrap, and produces a working clasp.1. koga: Homebrew include path (Apple Silicon)
boost (pulled in by
clbind/config.h) ships no pkg-config file, so it isn't declared as a kogalibrary; clasp relies on it being on the default include path.units.lisponly added the Intel Homebrew prefix/usr/local/include, so on Apple Silicon/opt/homebrew/includewas never searched and the build failed at the first compile with'boost/config.hpp' file not found. Now adds/opt/homebrew/includefor darwin when present (probe-guarded — Intel and Linux unaffected).2. koga: quote the rpath
The rpath was emitted as an unquoted
-Wl,-rpath,<abs path>. When the build directory contains a space, ninja hands the flag to/bin/sh, which splits it at the space and the link fails withno such file or directory: '<tail>'. The path is now quoted.3. SIGBUS writing native-module literals on Apple Silicon (JIT W^X)
The substantive one. During the Compiling Clasp native image step, loading a freshly-compiled native FASL crashed with:
lldb pinned it to a
str(store) into anrwxpage that still faulted withEXC_BAD_ACCESS code=2(KERN_PROTECTION_FAILURE) — the signature ofMAP_JITmemory under Apple Silicon hardware W^X. clasp writes Lisp pointers into each native module's literals vector, which lives in the LLVM ORC JITLinkMAP_JITslab; a thread must switch to write mode (pthread_jit_write_protect_np(false)) before writing. On x86-64/Linux therwxpage is genuinely writable, so the bug was latent there; LLVM 22's JITLink on macOS ARM64 exposes it.The helpers
llvmo::JITDataReadWriteMaybeExecute()/JITDataReadExecute()already existed for exactly this purpose but had no callers. This wires them around every write into JIT-resident literals memory:core::core__literals_vset(compiler.cc)llvmo::code_literal_set(code.cc)loadltv::attr_clasp_module_native(loadltv.cc)loadltv::attr_clasp_function_native_estranged(loadltv.cc)memcpy(snapshotSaveLoad.cc)Reads are left untouched (MAP_JIT memory is readable in execute mode; only writes fault), and each write-mode window is kept to the bare store so no JIT code runs while the thread is in write mode. Each fix made the crash advance to the next write site, confirming a single root cause across these locations rather than guesswork.
Testing
ninja -C build→ exit 0; re-run reportsno work to do; zeroFAILED:/Bus error/Condition of typelines in the log.(defun fib …)→(compile 'fib)→(fib 20)=6765(exercises Cleavir → native codegen → JIT → literals writes),(require :asdf)loads, clean exit.Notes for reviewers
🤖 Generated with Claude Code