Add a Module::deserialize_file method (#3266)

* Add a `Module::deserialize_file` method

This commit adds a new method to the `wasmtime::Module` type,
`deserialize_file`. This is intended to be the same as the `deserialize`
method except for the serialized module is present as an on-disk file.
This enables Wasmtime to internally use `mmap` to avoid copying bytes
around and generally makes loading a module much faster.

A C API is added in this commit as well for various bindings to use this
accelerated path now as well. Another option perhaps for a Rust-based
API is to have an API taking a `File` itself to allow for a custom file
descriptor in one way or another, but for now that's left for a possible
future refactoring if we find a use case.

* Fix compat with main - handle readdonly mmap

* wip

* Try to fix Windows support
This commit is contained in:
Alex Crichton
2021-08-31 13:05:51 -05:00
committed by GitHub
parent 4378ea8e01
commit 9e0c910023
9 changed files with 339 additions and 20 deletions

View File

@@ -136,23 +136,27 @@ impl CodeMemory {
unsafe {
let text_mut =
std::slice::from_raw_parts_mut(ret.text.as_ptr() as *mut u8, ret.text.len());
let text_offset = ret.text.as_ptr() as usize - ret.mmap.as_ptr() as usize;
let text_range = text_offset..text_offset + text_mut.len();
let mut text_section_readwrite = false;
for (offset, r) in text.relocations() {
// If the text section was mapped at readonly we need to make it
// briefly read/write here as we apply relocations.
if !text_section_readwrite && self.mmap.is_readonly() {
self.mmap
.make_writable(text_range.clone())
.expect("unable to make memory writable");
text_section_readwrite = true;
}
crate::link::apply_reloc(&ret.obj, text_mut, offset, r);
}
// Switch the executable portion from read/write to
// read/execute, notably not using read/write/execute to prevent
// modifications.
assert!(
ret.text.as_ptr() as usize % region::page::size() == 0,
"text section is not page-aligned"
);
region::protect(
ret.text.as_ptr() as *mut _,
ret.text.len(),
region::Protection::READ_EXECUTE,
)
.expect("unable to make memory readonly and executable");
self.mmap
.make_executable(text_range.clone())
.expect("unable to make memory executable");
// With all our memory set up use the platform-specific
// `UnwindRegistration` implementation to inform the general

View File

@@ -1,6 +1,7 @@
use anyhow::{Error, Result};
use anyhow::{Context, Error, Result};
use object::write::{Object, WritableBuffer};
use std::ops::{Deref, DerefMut, Range, RangeTo};
use std::path::Path;
use std::sync::Arc;
use wasmtime_runtime::Mmap;
@@ -73,6 +74,25 @@ impl MmapVec {
}
}
/// Creates a new `MmapVec` which is the `path` specified mmap'd into
/// memory.
///
/// This function will attempt to open the file located at `path` and will
/// then use that file to learn about its size and map the full contents
/// into memory. This will return an error if the file doesn't exist or if
/// it's too large to be fully mapped into memory.
pub fn from_file(path: &Path) -> Result<MmapVec> {
let mmap = Mmap::from_file(path)
.with_context(|| format!("failed to create mmap for file: {}", path.display()))?;
let len = mmap.len();
Ok(MmapVec::new(mmap, len))
}
/// Returns whether the original mmap was created from a readonly mapping.
pub fn is_readonly(&self) -> bool {
self.mmap.is_readonly()
}
/// "Drains" leading bytes up to the end specified in `range` from this
/// `MmapVec`, returning a separately owned `MmapVec` which retains access
/// to the bytes.
@@ -105,6 +125,18 @@ impl MmapVec {
self.range.start += amt;
return ret;
}
/// Makes the specified `range` within this `mmap` to be read/write.
pub unsafe fn make_writable(&self, range: Range<usize>) -> Result<()> {
self.mmap
.make_writable(range.start + self.range.start..range.end + self.range.start)
}
/// Makes the specified `range` within this `mmap` to be read/execute.
pub unsafe fn make_executable(&self, range: Range<usize>) -> Result<()> {
self.mmap
.make_executable(range.start + self.range.start..range.end + self.range.start)
}
}
impl Deref for MmapVec {
@@ -117,6 +149,7 @@ impl Deref for MmapVec {
impl DerefMut for MmapVec {
fn deref_mut(&mut self) -> &mut [u8] {
debug_assert!(!self.is_readonly());
// SAFETY: The underlying mmap is protected behind an `Arc` which means
// there there can be many references to it. We are guaranteed, though,
// that each reference to the underlying `mmap` has a disjoint `range`