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.
This commit is contained in:
parent
c9e135cdff
commit
2dab9f53d2
1 changed files with 82 additions and 9 deletions
|
|
@ -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<T: Copy>(base: &[u8], offset: usize) -> Result<T, &'static str> {
|
|||
Ok(unsafe { (bytes.as_ptr().cast::<T>()).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<Self, Box<dyn std::error::Error>> {
|
||||
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::<u8>(),
|
||||
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::<c_void>(), self.len);
|
||||
libc::close(self.fd);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
mod win {
|
||||
use std::ffi::c_void;
|
||||
|
|
@ -128,7 +184,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
.nth(1)
|
||||
.ok_or("Usage: cranelift-compiler <shm-fd-or-handle>")?;
|
||||
|
||||
#[cfg(unix)]
|
||||
#[cfg(all(unix, not(target_os = "macos")))]
|
||||
let (file, mut owned_mapped): (File, Vec<u8>) = {
|
||||
let shmfd = arg.parse::<i32>()?;
|
||||
if shmfd < 0 {
|
||||
|
|
@ -146,11 +202,22 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
(file, buf)
|
||||
};
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
let mut mac_mapping = {
|
||||
let shmfd = arg.parse::<i32>()?;
|
||||
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<dyn std::error::Error>> {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[cfg(all(unix, not(target_os = "macos")))]
|
||||
{
|
||||
write_all_at_offset(
|
||||
&file,
|
||||
|
|
@ -259,6 +326,12 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
)?;
|
||||
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(())
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue