Merge branch 'wasix-core-changes' into wasix

This commit is contained in:
Christoph Herzog
2022-12-22 11:17:38 +01:00
14 changed files with 157 additions and 126 deletions

View File

@@ -24,7 +24,7 @@ jobs:
- name: Install Rust - name: Install Rust
uses: dtolnay/rust-toolchain@stable uses: dtolnay/rust-toolchain@stable
with: with:
toolchain: 1.63 toolchain: 1.64
- name: Configure cargo data directory - name: Configure cargo data directory
# After this point, all cargo registry and crate data is stored in # After this point, all cargo registry and crate data is stored in
# $GITHUB_WORKSPACE/.cargo_home. This allows us to cache only the files # $GITHUB_WORKSPACE/.cargo_home. This allows us to cache only the files

View File

@@ -100,7 +100,7 @@ jobs:
- name: Install Rust - name: Install Rust
uses: dtolnay/rust-toolchain@stable uses: dtolnay/rust-toolchain@stable
with: with:
toolchain: 1.63 toolchain: 1.64
target: ${{ matrix.target }} target: ${{ matrix.target }}
- uses: Swatinem/rust-cache@v1 - uses: Swatinem/rust-cache@v1
if: matrix.use_sccache != true if: matrix.use_sccache != true

View File

@@ -21,7 +21,7 @@ jobs:
- name: Install Rust - name: Install Rust
uses: dtolnay/rust-toolchain@stable uses: dtolnay/rust-toolchain@stable
with: with:
toolchain: 1.63 toolchain: 1.64
target: ${{ matrix.target }} target: ${{ matrix.target }}
- name: Install wasm32-wasi target - name: Install wasm32-wasi target
shell: bash shell: bash

View File

@@ -16,7 +16,7 @@ jobs:
- name: Install Rust - name: Install Rust
uses: dtolnay/rust-toolchain@stable uses: dtolnay/rust-toolchain@stable
with: with:
toolchain: 1.63 toolchain: 1.64
- name: Install LLVM - name: Install LLVM
shell: bash shell: bash
run: | run: |

View File

@@ -33,7 +33,7 @@ jobs:
- name: Install Rust - name: Install Rust
uses: dtolnay/rust-toolchain@stable uses: dtolnay/rust-toolchain@stable
with: with:
toolchain: 1.63 toolchain: 1.64
- name: Install NodeJS - name: Install NodeJS
uses: actions/setup-node@v2 uses: actions/setup-node@v2

View File

@@ -113,7 +113,7 @@ jobs:
- name: Install Rust - name: Install Rust
uses: dtolnay/rust-toolchain@stable uses: dtolnay/rust-toolchain@stable
with: with:
toolchain: 1.63 toolchain: 1.64
target: ${{ matrix.target }} target: ${{ matrix.target }}
- uses: Swatinem/rust-cache@v1 - uses: Swatinem/rust-cache@v1
if: matrix.use_sccache != true if: matrix.use_sccache != true

View File

@@ -1,13 +0,0 @@
status = [
#"Audit",
"Code lint",
#"Test on linux-x64",
# "Test on linux-musl-x64",
# "Test on linux-aarch64",
#"Test on macos-arm64",
#"Test on macos-x64",
#"Test on windows-x64",
]
required_approvals = 0
timeout_sec = 7200
delete_merged_branches = true

View File

@@ -54,7 +54,7 @@ impl VMMemory {
} }
/// Copies this memory to a new memory /// Copies this memory to a new memory
pub fn fork(&self) -> Result<VMMemory, wasmer_types::MemoryError> { pub fn duplicate(&self) -> Result<VMMemory, wasmer_types::MemoryError> {
let new_memory = crate::Memory::new_internal(self.ty.clone())?; let new_memory = crate::Memory::new_internal(self.ty.clone())?;
#[cfg(feature = "tracing")] #[cfg(feature = "tracing")]

View File

@@ -265,9 +265,9 @@ impl Memory {
} }
/// Copies this memory to a new memory /// Copies this memory to a new memory
pub fn fork(&mut self, store: &impl AsStoreRef) -> Result<VMMemory, MemoryError> { pub fn duplicate(&mut self, store: &impl AsStoreRef) -> Result<VMMemory, MemoryError> {
let mem = self.handle.get(store.as_store_ref().objects()); let mem = self.handle.get(store.as_store_ref().objects());
mem.fork() mem.duplicate()
} }
} }

View File

@@ -117,10 +117,12 @@ mod tests {
.unwrap() .unwrap()
} }
} }
fn try_clone(&self) -> Option<Box<dyn LinearMemory + 'static>> { fn try_clone(&self) -> Option<Box<dyn LinearMemory + 'static>> {
None None
} }
fn fork(&mut self) -> Result<Box<dyn LinearMemory + 'static>, MemoryError> {
fn duplicate(&mut self) -> Result<Box<dyn LinearMemory + 'static>, MemoryError> {
let mem = self.mem.clone(); let mem = self.mem.clone();
Ok(Box::new(Self { Ok(Box::new(Self {
memory_definition: Some(UnsafeCell::new(VMMemoryDefinition { memory_definition: Some(UnsafeCell::new(VMMemoryDefinition {

View File

@@ -110,8 +110,6 @@ pub fn sbrk(mut ctx: FunctionEnvMut<EmEnv>, increment: i32) -> i32 {
increment, increment,
total_memory total_memory
); );
let _ = dynamictop_ptr;
if increment > 0 && new_dynamic_top < old_dynamic_top || new_dynamic_top < 0 { if increment > 0 && new_dynamic_top < old_dynamic_top || new_dynamic_top < 0 {
abort_on_cannot_grow_memory_old(ctx); abort_on_cannot_grow_memory_old(ctx);
return -1; return -1;

View File

@@ -119,11 +119,11 @@ impl WasmMmap {
/// Copies the memory /// Copies the memory
/// (in this case it performs a copy-on-write to save memory) /// (in this case it performs a copy-on-write to save memory)
pub fn fork(&mut self) -> Result<Self, MemoryError> { pub fn duplicate(&mut self) -> Result<Self, MemoryError> {
let mem_length = self.size.bytes().0; let mem_length = self.size.bytes().0;
let mut alloc = self let mut alloc = self
.alloc .alloc
.fork(Some(mem_length)) .duplicate(Some(mem_length))
.map_err(MemoryError::Generic)?; .map_err(MemoryError::Generic)?;
let base_ptr = alloc.as_mut_ptr(); let base_ptr = alloc.as_mut_ptr();
Ok(Self { Ok(Self {
@@ -289,9 +289,9 @@ impl VMOwnedMemory {
} }
/// Copies this memory to a new memory /// Copies this memory to a new memory
pub fn fork(&mut self) -> Result<Self, MemoryError> { pub fn duplicate(&mut self) -> Result<Self, MemoryError> {
Ok(Self { Ok(Self {
mmap: self.mmap.fork()?, mmap: self.mmap.duplicate()?,
config: self.config.clone(), config: self.config.clone(),
}) })
} }
@@ -333,8 +333,8 @@ impl LinearMemory for VMOwnedMemory {
} }
/// Copies this memory to a new memory /// Copies this memory to a new memory
fn fork(&mut self) -> Result<Box<dyn LinearMemory + 'static>, MemoryError> { fn duplicate(&mut self) -> Result<Box<dyn LinearMemory + 'static>, MemoryError> {
let forked = Self::fork(self)?; let forked = Self::duplicate(self)?;
Ok(Box::new(forked)) Ok(Box::new(forked))
} }
} }
@@ -376,10 +376,10 @@ impl VMSharedMemory {
} }
/// Copies this memory to a new memory /// Copies this memory to a new memory
pub fn fork(&mut self) -> Result<Self, MemoryError> { pub fn duplicate(&mut self) -> Result<Self, MemoryError> {
let mut guard = self.mmap.write().unwrap(); let mut guard = self.mmap.write().unwrap();
Ok(Self { Ok(Self {
mmap: Arc::new(RwLock::new(guard.fork()?)), mmap: Arc::new(RwLock::new(guard.duplicate()?)),
config: self.config.clone(), config: self.config.clone(),
}) })
} }
@@ -427,8 +427,8 @@ impl LinearMemory for VMSharedMemory {
} }
/// Copies this memory to a new memory /// Copies this memory to a new memory
fn fork(&mut self) -> Result<Box<dyn LinearMemory + 'static>, MemoryError> { fn duplicate(&mut self) -> Result<Box<dyn LinearMemory + 'static>, MemoryError> {
let forked = Self::fork(self)?; let forked = Self::duplicate(self)?;
Ok(Box::new(forked)) Ok(Box::new(forked))
} }
} }
@@ -495,8 +495,8 @@ impl LinearMemory for VMMemory {
} }
/// Copies this memory to a new memory /// Copies this memory to a new memory
fn fork(&mut self) -> Result<Box<dyn LinearMemory + 'static>, MemoryError> { fn duplicate(&mut self) -> Result<Box<dyn LinearMemory + 'static>, MemoryError> {
self.0.fork() self.0.duplicate()
} }
} }
@@ -558,8 +558,8 @@ impl VMMemory {
} }
/// Copies this memory to a new memory /// Copies this memory to a new memory
pub fn fork(&mut self) -> Result<Box<dyn LinearMemory + 'static>, MemoryError> { pub fn duplicate(&mut self) -> Result<Box<dyn LinearMemory + 'static>, MemoryError> {
LinearMemory::fork(self) LinearMemory::duplicate(self)
} }
} }
@@ -615,5 +615,5 @@ where
} }
/// Copies this memory to a new memory /// Copies this memory to a new memory
fn fork(&mut self) -> Result<Box<dyn LinearMemory + 'static>, MemoryError>; fn duplicate(&mut self) -> Result<Box<dyn LinearMemory + 'static>, MemoryError>;
} }

View File

@@ -317,8 +317,10 @@ impl Mmap {
/// Copies the memory to a new swap file (using copy-on-write if available) /// Copies the memory to a new swap file (using copy-on-write if available)
#[cfg(not(target_os = "windows"))] #[cfg(not(target_os = "windows"))]
pub fn fork(&mut self, hint_used: Option<usize>) -> Result<Self, String> { pub fn duplicate(&mut self, hint_used: Option<usize>) -> Result<Self, String> {
// Empty memory is an edge case // Empty memory is an edge case
use std::os::unix::prelude::FromRawFd;
if self.len == 0 { if self.len == 0 {
return Ok(Self::new()); return Ok(Self::new());
} }
@@ -355,28 +357,11 @@ impl Mmap {
}; };
// The shallow copy failed so we have to do it the hard way // The shallow copy failed so we have to do it the hard way
let mut off_in: libc::off_t = 0;
let mut off_out: libc::off_t = 0;
cfg_if::cfg_if! { let mut source = std::fs::File::from_raw_fd(self.fd.0);
if #[cfg(not(any(target_env = "musl", target_vendor = "apple")))] let mut out = std::fs::File::from_raw_fd(fd.0);
{ copy_file_range(&mut source, 0, &mut out, 0, len)
let ret = libc::copy_file_range(self.fd.0, &mut off_in, fd.0, &mut off_out, len, 0); .map_err(|err| format!("Could not copy memory: {err}"))?;
} else {
// TODO: don't use as casts...
let ret = match copy_file_range_impl(self.fd.0, off_in as u64, fd.0, off_out as u64, len) {
Ok(_) => 0,
Err(_err) => -1,
};
}
}
if ret < 0 {
return Err(format!(
"failed to copy temporary file data - {}",
io::Error::last_os_error()
));
}
#[cfg(feature = "tracing")] #[cfg(feature = "tracing")]
trace!("memory copy finished (size={})", len); trace!("memory copy finished (size={})", len);
@@ -410,7 +395,7 @@ impl Mmap {
/// Copies the memory to a new swap file (using copy-on-write if available) /// Copies the memory to a new swap file (using copy-on-write if available)
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
pub fn fork(&mut self, hint_used: Option<usize>) -> Result<Self, String> { pub fn duplicate(&mut self, hint_used: Option<usize>) -> Result<Self, String> {
// Create a new memory which we will copy to // Create a new memory which we will copy to
let new_mmap = Self::with_at_least(self.len)?; let new_mmap = Self::with_at_least(self.len)?;
@@ -436,66 +421,6 @@ impl Mmap {
} }
} }
/// Rust implementation of libc::copy_file_range.
///
/// Needed because that function is not available on all platforms.
// TODO: better implementation! (this is very quick, low effort)
// TODO: this function needs tests!
#[cfg(target_family = "unix")]
#[allow(dead_code)]
fn copy_file_range_impl(
source_fd: i32,
source_offset: u64,
out_fd: i32,
out_offset: u64,
len: usize,
) -> Result<(), std::io::Error> {
use std::{
io::{Seek, SeekFrom},
os::unix::io::FromRawFd,
};
let mut f1 = unsafe { std::fs::File::from_raw_fd(source_fd) };
let f1_original_pos = f1.stream_position()?;
// TODO: don't cast with as
f1.seek(SeekFrom::Start(source_offset))?;
let mut f2 = unsafe { std::fs::File::from_raw_fd(out_fd) };
let f2_original_pos = f2.stream_position()?;
f2.seek(SeekFrom::Start(out_offset))?;
let mut reader = std::io::BufReader::new(f1);
let mut writer = std::io::BufWriter::new(f2);
let mut buffer = vec![0u8; 4096];
let mut offset = 0;
let end = len.saturating_sub(buffer.len());
while offset < end {
let read = reader.read(&mut buffer)?;
writer.write_all(&buffer)?;
offset += read;
}
// Need to read the last chunk.
let remaining = len - offset;
if remaining > 0 {
reader.read_exact(&mut buffer[0..remaining])?;
writer.write_all(&buffer[0..remaining])?;
}
writer.flush()?;
// Restore files to original position.
let mut f1 = reader.into_inner();
f1.seek(SeekFrom::Start(f1_original_pos))?;
let mut f2 = writer.into_inner()?;
f2.seek(SeekFrom::Start(f2_original_pos))?;
Ok(())
}
impl Drop for Mmap { impl Drop for Mmap {
#[cfg(not(target_os = "windows"))] #[cfg(not(target_os = "windows"))]
fn drop(&mut self) { fn drop(&mut self) {
@@ -522,6 +447,54 @@ fn _assert() {
_assert_send_sync::<Mmap>(); _assert_send_sync::<Mmap>();
} }
/// Copy a range of a file to another file.
// We could also use libc::copy_file_range on some systems, but it's
// hard to do this because it is not available on many libc implementations.
// (not on Mac OS, musl, ...)
#[cfg(target_family = "unix")]
fn copy_file_range(
source: &mut std::fs::File,
source_offset: u64,
out: &mut std::fs::File,
out_offset: u64,
len: usize,
) -> Result<(), std::io::Error> {
use std::io::{Seek, SeekFrom};
let source_original_pos = source.stream_position()?;
source.seek(SeekFrom::Start(source_offset))?;
// TODO: don't cast with as
let out_original_pos = out.stream_position()?;
out.seek(SeekFrom::Start(out_offset))?;
// TODO: don't do this horrible "triple buffering" below".
// let mut reader = std::io::BufReader::new(source);
// TODO: larger buffer?
let mut buffer = vec![0u8; 4096];
let mut to_read = len;
while to_read > 0 {
let chunk_size = std::cmp::min(to_read, buffer.len());
let read = source.read(&mut buffer[0..chunk_size])?;
out.write_all(&buffer[0..read])?;
to_read -= read;
}
// Need to read the last chunk.
out.flush()?;
// Restore files to original position.
source.seek(SeekFrom::Start(source_original_pos))?;
out.flush()?;
out.sync_data()?;
out.seek(SeekFrom::Start(out_original_pos))?;
Ok(())
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
@@ -533,4 +506,59 @@ mod tests {
assert_eq!(round_up_to_page_size(4096, 4096), 4096); assert_eq!(round_up_to_page_size(4096, 4096), 4096);
assert_eq!(round_up_to_page_size(4097, 4096), 8192); assert_eq!(round_up_to_page_size(4097, 4096), 8192);
} }
#[cfg(target_family = "unix")]
#[test]
fn test_copy_file_range() -> Result<(), std::io::Error> {
// I know tempfile:: exists, but this doesn't bring in an extra
// dependency.
use std::{fs::OpenOptions, io::Seek};
let dir = std::env::temp_dir().join("wasmer/copy_file_range");
if dir.is_dir() {
std::fs::remove_dir_all(&dir).unwrap()
}
std::fs::create_dir_all(&dir).unwrap();
let pa = dir.join("a");
let pb = dir.join("b");
let data: Vec<u8> = (0..100).collect();
let mut a = OpenOptions::new()
.read(true)
.write(true)
.create_new(true)
.open(&pa)
.unwrap();
a.write_all(&data).unwrap();
let datb: Vec<u8> = (100..200).collect();
let mut b = OpenOptions::new()
.read(true)
.write(true)
.create_new(true)
.open(&pb)
.unwrap();
b.write_all(&datb).unwrap();
a.seek(io::SeekFrom::Start(30)).unwrap();
b.seek(io::SeekFrom::Start(99)).unwrap();
copy_file_range(&mut a, 10, &mut b, 40, 15).unwrap();
assert_eq!(a.stream_position().unwrap(), 30);
assert_eq!(b.stream_position().unwrap(), 99);
b.seek(io::SeekFrom::Start(0)).unwrap();
let mut out = Vec::new();
let len = b.read_to_end(&mut out).unwrap();
assert_eq!(len, 100);
assert_eq!(out[0..40], datb[0..40]);
assert_eq!(out[40..55], data[10..25]);
assert_eq!(out[55..100], datb[55..100]);
// TODO: needs more variant tests, but this is enough for now.
Ok(())
}
} }

View File

@@ -1,3 +1,4 @@
use core::slice::Iter;
use std::{ use std::{
cell::UnsafeCell, cell::UnsafeCell,
fmt, fmt,
@@ -9,9 +10,9 @@ use std::{
use wasmer_types::StoreSnapshot; use wasmer_types::StoreSnapshot;
use crate::VMExternObj; use crate::{
InstanceHandle, VMExternObj, VMFunction, VMFunctionEnvironment, VMGlobal, VMMemory, VMTable,
use crate::{InstanceHandle, VMFunction, VMFunctionEnvironment, VMGlobal, VMMemory, VMTable}; };
/// Unique ID to identify a context. /// Unique ID to identify a context.
/// ///
@@ -104,6 +105,21 @@ impl StoreObjects {
} }
} }
/// Return an immutable iterator over all globals
pub fn iter_globals(&self) -> Iter<VMGlobal> {
self.globals.iter()
}
/// Set a global, at index idx. Will panic if idx is out of range
/// Safety: the caller should check taht the raw value is compatible
/// with destination VMGlobal type
pub fn set_global_unchecked(&self, idx: usize, val: u128) {
assert!(idx < self.globals.len());
unsafe {
self.globals[idx].vmglobal().as_mut().val.u128 = val;
}
}
/// Serializes the mutable things into a snapshot /// Serializes the mutable things into a snapshot
pub fn save_snapshot(&self) -> StoreSnapshot { pub fn save_snapshot(&self) -> StoreSnapshot {
let mut ret = StoreSnapshot::default(); let mut ret = StoreSnapshot::default();