// This file is part of Substrate.
// Copyright (C) 2019-2021 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
//! Defines the compiled Wasm runtime that uses Wasmtime internally.
use crate::{
host::HostState,
imports::{resolve_imports, Imports},
instance_wrapper::{EntryPoint, InstanceWrapper},
state_holder,
};
use sc_allocator::FreeingBumpHeapAllocator;
use sc_executor_common::{
error::{Result, WasmError},
runtime_blob::{DataSegmentsSnapshot, ExposedMutableGlobalsSet, GlobalsSnapshot, RuntimeBlob},
wasm_runtime::{InvokeMethod, WasmInstance, WasmModule},
};
use sp_runtime_interface::unpack_ptr_and_len;
use sp_wasm_interface::{Function, Pointer, Value, WordSize};
use std::{
path::{Path, PathBuf},
rc::Rc,
sync::Arc,
};
use wasmtime::{Engine, Store};
enum Strategy {
FastInstanceReuse {
instance_wrapper: Rc,
globals_snapshot: GlobalsSnapshot,
data_segments_snapshot: Arc,
heap_base: u32,
},
RecreateInstance(InstanceCreator),
}
struct InstanceCreator {
store: Store,
module: Arc,
imports: Arc,
heap_pages: u32,
}
impl InstanceCreator {
fn instantiate(&self) -> Result {
InstanceWrapper::new(&self.store, &*self.module, &*self.imports, self.heap_pages)
}
}
/// Data required for creating instances with the fast instance reuse strategy.
struct InstanceSnapshotData {
mutable_globals: ExposedMutableGlobalsSet,
data_segments_snapshot: Arc,
}
/// A `WasmModule` implementation using wasmtime to compile the runtime module to machine code
/// and execute the compiled code.
pub struct WasmtimeRuntime {
module: Arc,
snapshot_data: Option,
config: Config,
host_functions: Vec<&'static dyn Function>,
engine: Engine,
}
impl WasmtimeRuntime {
/// Creates the store respecting the set limits.
fn new_store(&self) -> Store {
match self.config.max_memory_pages {
Some(max_memory_pages) => Store::new_with_limits(
&self.engine,
wasmtime::StoreLimitsBuilder::new().memory_pages(max_memory_pages).build(),
),
None => Store::new(&self.engine),
}
}
}
impl WasmModule for WasmtimeRuntime {
fn new_instance(&self) -> Result> {
let store = self.new_store();
// Scan all imports, find the matching host functions, and create stubs that adapt arguments
// and results.
//
// NOTE: Attentive reader may notice that this could've been moved in `WasmModule` creation.
// However, I am not sure if that's a good idea since it would be pushing our luck
// further by assuming that `Store` not only `Send` but also `Sync`.
let imports = resolve_imports(
&store,
&self.module,
&self.host_functions,
self.config.heap_pages,
self.config.allow_missing_func_imports,
)?;
let strategy = if let Some(ref snapshot_data) = self.snapshot_data {
let instance_wrapper =
InstanceWrapper::new(&store, &self.module, &imports, self.config.heap_pages)?;
let heap_base = instance_wrapper.extract_heap_base()?;
// This function panics if the instance was created from a runtime blob different from
// which the mutable globals were collected. Here, it is easy to see that there is only
// a single runtime blob and thus it's the same that was used for both creating the
// instance and collecting the mutable globals.
let globals_snapshot =
GlobalsSnapshot::take(&snapshot_data.mutable_globals, &instance_wrapper);
Strategy::FastInstanceReuse {
instance_wrapper: Rc::new(instance_wrapper),
globals_snapshot,
data_segments_snapshot: snapshot_data.data_segments_snapshot.clone(),
heap_base,
}
} else {
Strategy::RecreateInstance(InstanceCreator {
imports: Arc::new(imports),
module: self.module.clone(),
store,
heap_pages: self.config.heap_pages,
})
};
Ok(Box::new(WasmtimeInstance { strategy }))
}
}
/// A `WasmInstance` implementation that reuses compiled module and spawns instances
/// to execute the compiled code.
pub struct WasmtimeInstance {
strategy: Strategy,
}
// This is safe because `WasmtimeInstance` does not leak reference to `self.imports`
// and all imports don't reference anything, other than host functions and memory
unsafe impl Send for WasmtimeInstance {}
impl WasmInstance for WasmtimeInstance {
fn call(&self, method: InvokeMethod, data: &[u8]) -> Result> {
match &self.strategy {
Strategy::FastInstanceReuse {
instance_wrapper,
globals_snapshot,
data_segments_snapshot,
heap_base,
} => {
let entrypoint = instance_wrapper.resolve_entrypoint(method)?;
data_segments_snapshot.apply(|offset, contents| {
instance_wrapper.write_memory_from(Pointer::new(offset), contents)
})?;
globals_snapshot.apply(&**instance_wrapper);
let allocator = FreeingBumpHeapAllocator::new(*heap_base);
let result =
perform_call(data, Rc::clone(&instance_wrapper), entrypoint, allocator);
// Signal to the OS that we are done with the linear memory and that it can be
// reclaimed.
instance_wrapper.decommit();
result
},
Strategy::RecreateInstance(instance_creator) => {
let instance_wrapper = instance_creator.instantiate()?;
let heap_base = instance_wrapper.extract_heap_base()?;
let entrypoint = instance_wrapper.resolve_entrypoint(method)?;
let allocator = FreeingBumpHeapAllocator::new(heap_base);
perform_call(data, Rc::new(instance_wrapper), entrypoint, allocator)
},
}
}
fn get_global_const(&self, name: &str) -> Result