Don't copy executable code into a CodeMemory (#3265)

* Don't copy executable code into a `CodeMemory`

This commit moves a copy from compiled artifacts into a `CodeMemory`. In
general this commit drastically changes the meaning of a `CodeMemory`.
Previously it was an iteratively-pushed-on structure that would
accumulate executable code over time. Afterwards, however, it's a
manager for an `MmapVec` which updates the permissions on text section
to ensure that the pages are executable.

By taking ownership of an `MmapVec` within a `CodeMemory` there's no
need to copy any data around, which means that the `.text` section in
the ELF image produced by Wasmtime is usable as-is after placement in
memory and relocations have been resolved. This moves Wasmtime one step
closer to being able to directly use a module after it's `mmap`'d into
memory, optimizing when a module is loaded.

* Fix windows section alignment

* Review comments
This commit is contained in:
Alex Crichton
2021-08-30 13:38:35 -05:00
committed by GitHub
parent eb251deca9
commit ef3ec594ce
8 changed files with 215 additions and 236 deletions

View File

@@ -732,7 +732,7 @@ impl<'a> Instantiator<'a> {
.allocator()
.allocate(InstanceAllocationRequest {
module: compiled_module.module().clone(),
image_base: compiled_module.code().range().0,
image_base: compiled_module.code().as_ptr() as usize,
functions: compiled_module.functions(),
imports: self.cur.build(),
shared_signatures: self.cur.module.signatures().as_module_map().into(),

View File

@@ -44,7 +44,6 @@ impl ModuleRegistry {
/// Registers a new module with the registry.
pub fn register(&mut self, module: &Module) {
let compiled_module = module.compiled_module();
let (start, end) = compiled_module.code().range();
// If there's not actually any functions in this module then we may
// still need to preserve it for its data segments. Instances of this
@@ -59,8 +58,10 @@ impl ModuleRegistry {
// The module code range is exclusive for end, so make it inclusive as it
// may be a valid PC value
assert!(start < end);
let end = end - 1;
let code = compiled_module.code();
assert!(!code.is_empty());
let start = code.as_ptr() as usize;
let end = start + code.len() - 1;
// Ensure the module isn't already present in the registry
// This is expected when a module is instantiated multiple times in the

View File

@@ -6,7 +6,7 @@ use std::any::Any;
use std::panic::{self, AssertUnwindSafe};
use std::sync::Arc;
use wasmtime_environ::{EntityIndex, Module, ModuleType, PrimaryMap, SignatureIndex};
use wasmtime_jit::CodeMemory;
use wasmtime_jit::{CodeMemory, MmapVec};
use wasmtime_runtime::{
Imports, InstanceAllocationRequest, InstanceAllocator, InstanceHandle,
OnDemandInstanceAllocator, VMContext, VMFunctionBody, VMSharedSignatureIndex, VMTrampoline,
@@ -82,20 +82,18 @@ pub fn create_function(
stub_fn as usize,
&mut obj,
)?;
let obj = obj.write()?;
let obj = MmapVec::from_obj(obj)?;
// Copy the results of JIT compilation into executable memory, and this will
// also take care of unwind table registration.
let mut code_memory = CodeMemory::new();
let (alloc, _obj) = code_memory.allocate_for_object_unparsed(&obj)?;
let mut code_memory = CodeMemory::new(obj);
let code = code_memory.publish()?;
// Extract the host/wasm trampolines from the results of compilation since
// we know their start/length.
let host_trampoline = alloc[t1.start as usize..][..t1.length as usize].as_ptr();
let wasm_trampoline = &mut alloc[t2.start as usize..][..t2.length as usize];
let wasm_trampoline = wasm_trampoline as *mut [u8] as *mut [VMFunctionBody];
code_memory.publish();
let host_trampoline = code.text[t1.start as usize..][..t1.length as usize].as_ptr();
let wasm_trampoline = &code.text[t2.start as usize..][..t2.length as usize];
let wasm_trampoline = wasm_trampoline as *const [u8] as *mut [VMFunctionBody];
let sig = engine.signatures().register(ft.as_wasm_func_type());