wasmer/backend/sys/entities/
module.rs

1//! Data types, functions and traits for `sys` runtime's `Module` implementation.
2use std::path::Path;
3use std::sync::Arc;
4
5use bytes::Bytes;
6use wasmer_compiler::{Artifact, ArtifactCreate, Engine};
7use wasmer_types::{
8    CompilationProgressCallback, CompileError, DeserializeError, ExportType, ExportsIterator,
9    ImportType, ImportsIterator, ModuleInfo, SerializeError,
10};
11#[cfg(feature = "experimental-host-interrupt")]
12use wasmer_vm::interrupt_registry;
13use wasmer_vm::{Trap, TrapCode};
14
15use crate::{
16    AsStoreMut, AsStoreRef, BackendModule, IntoBytes, StoreContext,
17    backend::sys::entities::engine::NativeEngineExt, engine::AsEngineRef,
18    error::InstantiationError, vm::VMInstance,
19};
20use wasmer_vm::StoreHandle;
21
22#[derive(Clone, PartialEq, Eq)]
23#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
24/// A WebAssembly `module` in the `sys` runtime.
25pub struct Module {
26    // The field ordering here is actually significant because of the drop
27    // order: we want to drop the artifact before dropping the engine.
28    //
29    // The reason for this is that dropping the Artifact will de-register the
30    // trap handling metadata from the global registry. This must be done before
31    // the code memory for the artifact is freed (which happens when the store
32    // is dropped) since there is a chance that this memory could be reused by
33    // another module which will try to register its own trap information.
34    //
35    // Note that in Rust, the drop order for struct fields is from top to
36    // bottom: the opposite of C++.
37    //
38    // In the future, this code should be refactored to properly describe the
39    // ownership of the code and its metadata.
40    artifact: Arc<Artifact>,
41}
42
43impl Module {
44    pub(crate) fn from_binary(
45        engine: &impl AsEngineRef,
46        binary: &[u8],
47    ) -> Result<Self, CompileError> {
48        Self::validate(engine, binary)?;
49        unsafe { Self::from_binary_unchecked(engine, binary) }
50    }
51
52    pub(crate) fn from_binary_with_progress(
53        engine: &impl AsEngineRef,
54        binary: &[u8],
55        callback: CompilationProgressCallback,
56    ) -> Result<Self, CompileError> {
57        Self::validate(engine, binary)?;
58
59        let artifact = engine
60            .as_engine_ref()
61            .engine()
62            .as_sys()
63            .compile_with_progress(binary, Some(callback))?;
64        Ok(Self::from_artifact(artifact))
65    }
66
67    pub(crate) unsafe fn from_binary_unchecked(
68        engine: &impl AsEngineRef,
69        binary: &[u8],
70    ) -> Result<Self, CompileError> {
71        Self::compile(engine, binary)
72    }
73
74    #[cfg(feature = "compiler")]
75    #[tracing::instrument(level = "debug", skip_all)]
76    pub(crate) fn validate(engine: &impl AsEngineRef, binary: &[u8]) -> Result<(), CompileError> {
77        engine.as_engine_ref().engine().as_sys().validate(binary)
78    }
79
80    #[cfg(not(feature = "compiler"))]
81    pub(crate) fn validate(_engine: &impl AsEngineRef, _binary: &[u8]) -> Result<(), CompileError> {
82        Err(CompileError::UnsupportedTarget(
83            "The compiler feature is not enabled, but is required to validate a Module".to_string(),
84        ))
85    }
86
87    #[cfg(feature = "compiler")]
88    fn compile(engine: &impl AsEngineRef, binary: &[u8]) -> Result<Self, CompileError> {
89        let artifact = engine.as_engine_ref().engine().as_sys().compile(binary)?;
90        Ok(Self::from_artifact(artifact))
91    }
92
93    #[cfg(not(feature = "compiler"))]
94    fn compile(_engine: &impl AsEngineRef, _binary: &[u8]) -> Result<Self, CompileError> {
95        Err(CompileError::UnsupportedTarget(
96            "The compiler feature is not enabled, but is required to compile a Module".to_string(),
97        ))
98    }
99
100    pub(crate) fn serialize(&self) -> Result<Bytes, SerializeError> {
101        self.artifact.serialize().map(|bytes| bytes.into())
102    }
103
104    #[tracing::instrument(level = "debug", skip_all)]
105    pub(crate) unsafe fn deserialize_unchecked(
106        engine: &impl AsEngineRef,
107        bytes: impl IntoBytes,
108    ) -> Result<Self, DeserializeError> {
109        let bytes = bytes.into_bytes();
110
111        let artifact = unsafe {
112            engine
113                .as_engine_ref()
114                .engine()
115                .as_sys()
116                .deserialize_unchecked(bytes.into())?
117        };
118        Ok(Self::from_artifact(artifact))
119    }
120
121    #[tracing::instrument(level = "debug", skip_all)]
122    pub(crate) unsafe fn deserialize(
123        engine: &impl AsEngineRef,
124        bytes: impl IntoBytes,
125    ) -> Result<Self, DeserializeError> {
126        let bytes = bytes.into_bytes();
127        let artifact = unsafe {
128            engine
129                .as_engine_ref()
130                .engine()
131                .as_sys()
132                .deserialize(bytes.into())?
133        };
134        Ok(Self::from_artifact(artifact))
135    }
136
137    pub(crate) unsafe fn deserialize_from_file_unchecked(
138        engine: &impl AsEngineRef,
139        path: impl AsRef<Path>,
140    ) -> Result<Self, DeserializeError> {
141        let artifact = unsafe {
142            engine
143                .as_engine_ref()
144                .engine()
145                .as_sys()
146                .deserialize_from_file_unchecked(path.as_ref())?
147        };
148        Ok(Self::from_artifact(artifact))
149    }
150
151    pub(crate) unsafe fn deserialize_from_file(
152        engine: &impl AsEngineRef,
153        path: impl AsRef<Path>,
154    ) -> Result<Self, DeserializeError> {
155        let artifact = unsafe {
156            engine
157                .as_engine_ref()
158                .engine()
159                .as_sys()
160                .deserialize_from_file(path.as_ref())?
161        };
162        Ok(Self::from_artifact(artifact))
163    }
164
165    pub(super) fn from_artifact(artifact: Arc<Artifact>) -> Self {
166        Self { artifact }
167    }
168
169    #[allow(clippy::result_large_err)]
170    pub(crate) fn instantiate(
171        &self,
172        store: &mut impl AsStoreMut,
173        imports: &[crate::Extern],
174    ) -> Result<VMInstance, InstantiationError> {
175        if !self.artifact.allocated() {
176            // Return an error mentioning that the artifact is compiled for a different
177            // platform.
178            return Err(InstantiationError::DifferentArchOS);
179        }
180        // Ensure all imports come from the same context.
181        for import in imports {
182            if !import.is_from_store(store) {
183                return Err(InstantiationError::DifferentStores);
184            }
185        }
186        let signal_handler = store.as_store_ref().signal_handler();
187        let mut store_mut = store.as_store_mut();
188        let store_ptr = store_mut.inner as *mut _;
189        let (engine, objects) = store_mut.engine_and_objects_mut();
190        let config = engine.tunables().vmconfig();
191        unsafe {
192            let mut instance_handle = self.artifact.instantiate(
193                engine.tunables(),
194                &imports
195                    .iter()
196                    .map(|e| crate::Extern::to_vm_extern(e).unwrap_sys())
197                    .collect::<Vec<_>>(),
198                objects.as_sys_mut(),
199            )?;
200
201            let store_id = objects.id();
202            #[cfg(feature = "experimental-host-interrupt")]
203            let interrupt_guard = match interrupt_registry::install(store_id) {
204                Ok(x) => x,
205                Err(interrupt_registry::InstallError::AlreadyInterrupted) => {
206                    return Err(InstantiationError::Start(
207                        Trap::lib(TrapCode::HostInterrupt).into(),
208                    ));
209                }
210            };
211
212            let store_install_guard = StoreContext::ensure_installed(store_ptr);
213
214            // After the instance handle is created, we need to initialize
215            // the data, call the start function and so. However, if any
216            // of this steps traps, we still need to keep the instance alive
217            // as some of the Instance elements may have placed in other
218            // instance tables.
219            if let Err(err) =
220                self.artifact
221                    .finish_instantiation(config, signal_handler, &mut instance_handle)
222            {
223                // Keep the partially initialized instance alive: its funcrefs may already
224                // have been written into imported tables.
225                let _ = StoreHandle::new(objects.as_sys_mut(), instance_handle);
226                return Err(err.into());
227            }
228
229            drop(store_install_guard);
230            #[cfg(feature = "experimental-host-interrupt")]
231            drop(interrupt_guard);
232
233            Ok(VMInstance::Sys(instance_handle))
234        }
235    }
236
237    pub(crate) fn name(&self) -> Option<&str> {
238        self.info().name.as_deref()
239    }
240
241    pub(crate) fn set_name(&mut self, name: &str) -> bool {
242        Arc::get_mut(&mut self.artifact)
243            .is_some_and(|artifact| artifact.set_module_info_name(name.to_string()))
244    }
245
246    pub(crate) fn imports(&self) -> ImportsIterator<Box<dyn Iterator<Item = ImportType> + '_>> {
247        self.info().imports()
248    }
249
250    pub(crate) fn exports(&self) -> ExportsIterator<Box<dyn Iterator<Item = ExportType> + '_>> {
251        self.info().exports()
252    }
253
254    pub(crate) fn custom_sections<'a>(
255        &'a self,
256        name: &'a str,
257    ) -> Box<dyn Iterator<Item = Box<[u8]>> + 'a> {
258        self.info().custom_sections(name)
259    }
260
261    pub(crate) fn info(&self) -> &ModuleInfo {
262        self.artifact.module_info()
263    }
264}
265
266impl crate::Module {
267    /// Consume [`self`] into a reference [`crate::backend::sys::module::Module`].
268    pub fn into_sys(self) -> crate::backend::sys::module::Module {
269        match self.0 {
270            BackendModule::Sys(s) => s,
271            _ => panic!("Not a `sys` module!"),
272        }
273    }
274
275    /// Convert a reference to [`self`] into a reference [`crate::backend::sys::module::Module`].
276    pub fn as_sys(&self) -> &crate::backend::sys::module::Module {
277        match self.0 {
278            BackendModule::Sys(ref s) => s,
279            _ => panic!("Not a `sys` module!"),
280        }
281    }
282
283    /// Convert a mutable reference to [`self`] into a mutable reference [`crate::backend::sys::module::Module`].
284    pub fn as_sys_mut(&mut self) -> &mut crate::backend::sys::module::Module {
285        match self.0 {
286            BackendModule::Sys(ref mut s) => s,
287            _ => panic!("Not a `sys` module!"),
288        }
289    }
290
291    /// Returns the compiled [`Artifact`] backing this module, or `None` if this
292    /// is not a `sys`-backend module.
293    ///
294    /// # Security
295    ///
296    /// The artifact exposes host-process memory addresses (e.g. via
297    /// [`Artifact::finished_function_extents`]). These are not stable across
298    /// runs and must not be forwarded to untrusted parties, as they reveal
299    /// ASLR layout information.
300    pub fn sys_artifact(&self) -> Option<&Artifact> {
301        match self.0 {
302            BackendModule::Sys(ref s) => Some(&s.artifact),
303            #[allow(unreachable_patterns)]
304            _ => None,
305        }
306    }
307}