wasmer_engine_dylib/
engine.rs

1//! Dylib Engine.
2
3use crate::DylibArtifact;
4use libloading::Library;
5use loupe::MemoryUsage;
6use std::path::Path;
7use std::sync::Arc;
8use std::sync::Mutex;
9use wasmer_compiler::{CompileError, Target};
10#[cfg(feature = "compiler")]
11use wasmer_compiler::{Compiler, Triple};
12use wasmer_engine::{Artifact, DeserializeError, Engine, EngineId, Tunables};
13#[cfg(feature = "compiler")]
14use wasmer_types::Features;
15use wasmer_types::FunctionType;
16use wasmer_vm::{
17    FuncDataRegistry, SignatureRegistry, VMCallerCheckedAnyfunc, VMFuncRef, VMSharedSignatureIndex,
18};
19
20/// A WebAssembly `Dylib` Engine.
21#[derive(Clone, MemoryUsage)]
22pub struct DylibEngine {
23    inner: Arc<Mutex<DylibEngineInner>>,
24    /// The target for the compiler
25    target: Arc<Target>,
26    engine_id: EngineId,
27}
28
29impl DylibEngine {
30    /// Create a new `DylibEngine` with the given config
31    #[cfg(feature = "compiler")]
32    pub fn new(compiler: Box<dyn Compiler>, target: Target, features: Features) -> Self {
33        let is_cross_compiling = *target.triple() != Triple::host();
34        let linker = Linker::find_linker(is_cross_compiling);
35
36        Self {
37            inner: Arc::new(Mutex::new(DylibEngineInner {
38                compiler: Some(compiler),
39                signatures: SignatureRegistry::new(),
40                func_data: Arc::new(FuncDataRegistry::new()),
41                prefixer: None,
42                features,
43                is_cross_compiling,
44                linker,
45                libraries: vec![],
46            })),
47            target: Arc::new(target),
48            engine_id: EngineId::default(),
49        }
50    }
51
52    /// Create a headless `DylibEngine`
53    ///
54    /// A headless engine is an engine without any compiler attached.
55    /// This is useful for assuring a minimal runtime for running
56    /// WebAssembly modules.
57    ///
58    /// For example, for running in IoT devices where compilers are very
59    /// expensive, or also to optimize startup speed.
60    ///
61    /// # Important
62    ///
63    /// Headless engines can't compile or validate any modules,
64    /// they just take already processed Modules (via `Module::serialize`).
65    pub fn headless() -> Self {
66        Self {
67            inner: Arc::new(Mutex::new(DylibEngineInner {
68                #[cfg(feature = "compiler")]
69                compiler: None,
70                #[cfg(feature = "compiler")]
71                features: Features::default(),
72                signatures: SignatureRegistry::new(),
73                func_data: Arc::new(FuncDataRegistry::new()),
74                prefixer: None,
75                is_cross_compiling: false,
76                linker: Linker::None,
77                libraries: vec![],
78            })),
79            target: Arc::new(Target::default()),
80            engine_id: EngineId::default(),
81        }
82    }
83
84    /// Sets a prefixer for the wasm module, so we can avoid any collisions
85    /// in the exported function names on the generated shared object.
86    ///
87    /// This, allows us to rather than have functions named `wasmer_function_1`
88    /// to be named `wasmer_function_PREFIX_1`.
89    ///
90    /// # Important
91    ///
92    /// This prefixer function should be deterministic, so the compilation
93    /// remains deterministic.
94    pub fn set_deterministic_prefixer<F>(&mut self, prefixer: F)
95    where
96        F: Fn(&[u8]) -> String + Send + 'static,
97    {
98        let mut inner = self.inner_mut();
99        inner.prefixer = Some(Box::new(prefixer));
100    }
101
102    pub(crate) fn inner(&self) -> std::sync::MutexGuard<'_, DylibEngineInner> {
103        self.inner.lock().unwrap()
104    }
105
106    pub(crate) fn inner_mut(&self) -> std::sync::MutexGuard<'_, DylibEngineInner> {
107        self.inner.lock().unwrap()
108    }
109}
110
111impl Engine for DylibEngine {
112    /// The target
113    fn target(&self) -> &Target {
114        &self.target
115    }
116
117    /// Register a signature
118    fn register_signature(&self, func_type: &FunctionType) -> VMSharedSignatureIndex {
119        let compiler = self.inner();
120        compiler.signatures().register(func_type)
121    }
122
123    fn register_function_metadata(&self, func_data: VMCallerCheckedAnyfunc) -> VMFuncRef {
124        let compiler = self.inner();
125        compiler.func_data().register(func_data)
126    }
127
128    /// Lookup a signature
129    fn lookup_signature(&self, sig: VMSharedSignatureIndex) -> Option<FunctionType> {
130        let compiler = self.inner();
131        compiler.signatures().lookup(sig)
132    }
133
134    /// Validates a WebAssembly module
135    fn validate(&self, binary: &[u8]) -> Result<(), CompileError> {
136        self.inner().validate(binary)
137    }
138
139    /// Compile a WebAssembly binary
140    #[cfg(feature = "compiler")]
141    fn compile(
142        &self,
143        binary: &[u8],
144        tunables: &dyn Tunables,
145    ) -> Result<Arc<dyn Artifact>, CompileError> {
146        Ok(Arc::new(DylibArtifact::new(self, binary, tunables)?))
147    }
148
149    /// Compile a WebAssembly binary (it will fail because the `compiler` flag is disabled).
150    #[cfg(not(feature = "compiler"))]
151    fn compile(
152        &self,
153        _binary: &[u8],
154        _tunables: &dyn Tunables,
155    ) -> Result<Arc<dyn Artifact>, CompileError> {
156        Err(CompileError::Codegen(
157            "The `DylibEngine` is operating in headless mode, so it cannot compile a module."
158                .to_string(),
159        ))
160    }
161
162    /// Deserializes a WebAssembly module (binary content of a shared object file)
163    unsafe fn deserialize(&self, bytes: &[u8]) -> Result<Arc<dyn Artifact>, DeserializeError> {
164        Ok(Arc::new(DylibArtifact::deserialize(self, bytes)?))
165    }
166
167    /// Deserializes a WebAssembly module from a path
168    /// It should point to a shared object file generated by this engine.
169    unsafe fn deserialize_from_file(
170        &self,
171        file_ref: &Path,
172    ) -> Result<Arc<dyn Artifact>, DeserializeError> {
173        Ok(Arc::new(DylibArtifact::deserialize_from_file(
174            self, file_ref,
175        )?))
176    }
177
178    fn id(&self) -> &EngineId {
179        &self.engine_id
180    }
181
182    fn cloned(&self) -> Arc<dyn Engine + Send + Sync> {
183        Arc::new(self.clone())
184    }
185}
186
187#[derive(Clone, Copy, MemoryUsage)]
188pub(crate) enum Linker {
189    None,
190    Clang11,
191    Clang10,
192    Clang,
193    Gcc,
194}
195
196impl Linker {
197    #[cfg(feature = "compiler")]
198    fn find_linker(is_cross_compiling: bool) -> Self {
199        let (possibilities, requirements): (&[_], _) = if is_cross_compiling {
200            (
201                &[Self::Clang11, Self::Clang10, Self::Clang],
202                "at least one of `clang-11`, `clang-10`, or `clang`",
203            )
204        } else {
205            (&[Self::Gcc], "`gcc`")
206        };
207        *possibilities
208            .iter()
209            .find(|linker| which::which(linker.executable()).is_ok())
210            .unwrap_or_else(|| {
211                panic!(
212                    "Need {} installed in order to use `DylibEngine` when {}cross-compiling",
213                    requirements,
214                    if is_cross_compiling { "" } else { "not " }
215                )
216            })
217    }
218
219    pub(crate) fn executable(self) -> &'static str {
220        match self {
221            Self::None => "",
222            Self::Clang11 => "clang-11",
223            Self::Clang10 => "clang-10",
224            Self::Clang => "clang",
225            Self::Gcc => "gcc",
226        }
227    }
228}
229
230/// The inner contents of `DylibEngine`
231#[derive(MemoryUsage)]
232pub struct DylibEngineInner {
233    /// The compiler
234    #[cfg(feature = "compiler")]
235    compiler: Option<Box<dyn Compiler>>,
236
237    /// The WebAssembly features to use
238    #[cfg(feature = "compiler")]
239    features: Features,
240
241    /// The signature registry is used mainly to operate with trampolines
242    /// performantly.
243    signatures: SignatureRegistry,
244
245    /// The backing storage of `VMFuncRef`s. This centralized store ensures that 2
246    /// functions with the same `VMCallerCheckedAnyfunc` will have the same `VMFuncRef`.
247    /// It also guarantees that the `VMFuncRef`s stay valid until the engine is dropped.
248    func_data: Arc<FuncDataRegistry>,
249
250    /// The prefixer returns the a String to prefix each of
251    /// the functions in the shared object generated by the `DylibEngine`,
252    /// so we can assure no collisions.
253    #[loupe(skip)]
254    prefixer: Option<Box<dyn Fn(&[u8]) -> String + Send>>,
255
256    /// Whether the Dylib engine will cross-compile.
257    is_cross_compiling: bool,
258
259    /// The linker to use.
260    linker: Linker,
261
262    /// List of libraries loaded by this engine.
263    #[loupe(skip)]
264    libraries: Vec<Library>,
265}
266
267impl DylibEngineInner {
268    /// Gets the compiler associated to this engine.
269    #[cfg(feature = "compiler")]
270    pub fn compiler(&self) -> Result<&dyn Compiler, CompileError> {
271        if self.compiler.is_none() {
272            return Err(CompileError::Codegen("The `DylibEngine` is operating in headless mode, so it can only execute already compiled Modules.".to_string()));
273        }
274        Ok(&**self
275            .compiler
276            .as_ref()
277            .expect("Can't get compiler reference"))
278    }
279
280    #[cfg(feature = "compiler")]
281    pub(crate) fn get_prefix(&self, bytes: &[u8]) -> String {
282        if let Some(prefixer) = &self.prefixer {
283            prefixer(bytes)
284        } else {
285            "".to_string()
286        }
287    }
288
289    #[cfg(feature = "compiler")]
290    pub(crate) fn features(&self) -> &Features {
291        &self.features
292    }
293
294    /// Validate the module
295    #[cfg(feature = "compiler")]
296    pub fn validate(&self, data: &[u8]) -> Result<(), CompileError> {
297        self.compiler()?.validate_module(self.features(), data)
298    }
299
300    /// Validate the module
301    #[cfg(not(feature = "compiler"))]
302    pub fn validate<'data>(&self, _data: &'data [u8]) -> Result<(), CompileError> {
303        Err(CompileError::Validate(
304            "The `DylibEngine` is not compiled with compiler support, which is required for validating".to_string(),
305        ))
306    }
307
308    /// Shared signature registry.
309    pub fn signatures(&self) -> &SignatureRegistry {
310        &self.signatures
311    }
312
313    /// Shared func metadata registry.
314    pub(crate) fn func_data(&self) -> &Arc<FuncDataRegistry> {
315        &self.func_data
316    }
317
318    pub(crate) fn is_cross_compiling(&self) -> bool {
319        self.is_cross_compiling
320    }
321
322    pub(crate) fn linker(&self) -> Linker {
323        self.linker
324    }
325
326    pub(crate) fn add_library(&mut self, library: Library) {
327        self.libraries.push(library);
328    }
329}