From 2dab9f53d22084f9bc7c33c63ff4cdf32545884e Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Mon, 11 May 2026 08:15:24 +0200 Subject: [PATCH] LibWasm: Use mmap to read shm fd on macOS in cranelift-compiler macOS POSIX shared memory objects returned by shm_open() only support mmap() and ftruncate(). Calls to read(), write(), pread() or pwrite() all fail with ESPIPE. The cranelift-compiler child was using pread/ pwrite via File::read_exact_at/write_all_at, which works fine on Linux where the parent uses memfd_create(), but fails on macOS where the parent uses shm_open(). Add a macOS-specific path that mmaps the inherited shm fd with MAP_SHARED, mirroring the Windows MapViewOfFile path. Writes through the mapping are automatically visible to the parent, so the explicit write-back step at the end is also skipped on macOS. --- .../Rust/src/bin/cranelift-compiler.rs | 91 +++++++++++++++++-- 1 file changed, 82 insertions(+), 9 deletions(-) diff --git a/Libraries/LibWasm/Rust/src/bin/cranelift-compiler.rs b/Libraries/LibWasm/Rust/src/bin/cranelift-compiler.rs index 49646f37f7..f92388da3d 100644 --- a/Libraries/LibWasm/Rust/src/bin/cranelift-compiler.rs +++ b/Libraries/LibWasm/Rust/src/bin/cranelift-compiler.rs @@ -10,13 +10,13 @@ use libwasm_cranelift::{CraneliftInsn, RuntimeHelpers, compile_to_bytes}; use std::env; use std::mem::{size_of, size_of_val}; -#[cfg(unix)] +#[cfg(all(unix, not(target_os = "macos")))] use std::fs::File; -#[cfg(unix)] +#[cfg(all(unix, not(target_os = "macos")))] use std::io; -#[cfg(unix)] +#[cfg(all(unix, not(target_os = "macos")))] use std::os::fd::FromRawFd; -#[cfg(unix)] +#[cfg(all(unix, not(target_os = "macos")))] use std::os::unix::fs::FileExt; #[repr(C)] @@ -55,16 +55,72 @@ fn read_pod(base: &[u8], offset: usize) -> Result { Ok(unsafe { (bytes.as_ptr().cast::()).read_unaligned() }) } -#[cfg(unix)] +#[cfg(all(unix, not(target_os = "macos")))] fn read_exact_at_offset(file: &File, buf: &mut [u8], offset: u64) -> io::Result<()> { file.read_exact_at(buf, offset) } -#[cfg(unix)] +#[cfg(all(unix, not(target_os = "macos")))] fn write_all_at_offset(file: &File, data: &[u8], offset: u64) -> io::Result<()> { file.write_all_at(data, offset) } +// macOS POSIX shm objects don't support read/write/pread/pwrite, only mmap and +// ftruncate. So we mmap the parent's shm fd directly instead of using the +// file-based path the Linux memfd build takes. +#[cfg(target_os = "macos")] +mod mac { + use std::ffi::c_void; + use std::io; + + pub struct Mapping { + ptr: *mut u8, + len: usize, + fd: i32, + } + + impl Mapping { + pub fn open(fd: i32) -> Result> { + let mut st: libc::stat = unsafe { std::mem::zeroed() }; + if unsafe { libc::fstat(fd, &raw mut st) } != 0 { + return Err(io::Error::last_os_error().into()); + } + let len = usize::try_from(st.st_size).map_err(|_| "stat size overflow")?; + let ptr = unsafe { + libc::mmap( + std::ptr::null_mut(), + len, + libc::PROT_READ | libc::PROT_WRITE, + libc::MAP_SHARED, + fd, + 0, + ) + }; + if ptr == libc::MAP_FAILED { + return Err(io::Error::last_os_error().into()); + } + Ok(Mapping { + ptr: ptr.cast::(), + len, + fd, + }) + } + + pub fn as_slice_mut(&mut self) -> &mut [u8] { + unsafe { std::slice::from_raw_parts_mut(self.ptr, self.len) } + } + } + + impl Drop for Mapping { + fn drop(&mut self) { + unsafe { + libc::munmap(self.ptr.cast::(), self.len); + libc::close(self.fd); + } + } + } +} + #[cfg(windows)] mod win { use std::ffi::c_void; @@ -128,7 +184,7 @@ fn main() -> Result<(), Box> { .nth(1) .ok_or("Usage: cranelift-compiler ")?; - #[cfg(unix)] + #[cfg(all(unix, not(target_os = "macos")))] let (file, mut owned_mapped): (File, Vec) = { let shmfd = arg.parse::()?; if shmfd < 0 { @@ -146,11 +202,22 @@ fn main() -> Result<(), Box> { (file, buf) }; + #[cfg(target_os = "macos")] + let mut mac_mapping = { + let shmfd = arg.parse::()?; + if shmfd < 0 { + return Err("invalid fd".into()); + } + mac::Mapping::open(shmfd)? + }; + #[cfg(windows)] let mut mapping = win::Mapping::open(&arg)?; - #[cfg(unix)] + #[cfg(all(unix, not(target_os = "macos")))] let mapped: &mut [u8] = &mut owned_mapped; + #[cfg(target_os = "macos")] + let mapped: &mut [u8] = mac_mapping.as_slice_mut(); #[cfg(windows)] let mapped: &mut [u8] = mapping.as_slice_mut(); @@ -245,7 +312,7 @@ fn main() -> Result<(), Box> { } } - #[cfg(unix)] + #[cfg(all(unix, not(target_os = "macos")))] { write_all_at_offset( &file, @@ -259,6 +326,12 @@ fn main() -> Result<(), Box> { )?; let _ = file.sync_all(); } + // On macOS we mmap'd the parent's shm fd with MAP_SHARED, so the writes + // above are already visible in the parent's mapping. Nothing to flush. + #[cfg(target_os = "macos")] + { + let _ = (out_entries_offset, code_base_offset, code_cursor); + } Ok(()) }