1 use crate::Engine; 2 use crate::prelude::*; 3 use std::borrow::Cow; 4 use std::path::Path; 5 6 #[cfg(feature = "compile-time-builtins")] 7 use crate::hash_map::HashMap; 8 9 #[cfg(feature = "compile-time-builtins")] 10 mod compile_time_builtins; 11 12 /// Builder-style structure used to create a [`Module`](crate::module::Module) or 13 /// pre-compile a module to a serialized list of bytes. 14 /// 15 /// This structure can be used for more advanced configuration when compiling a 16 /// WebAssembly module. Most configuration can use simpler constructors such as: 17 /// 18 /// * [`Module::new`](crate::Module::new) 19 /// * [`Module::from_file`](crate::Module::from_file) 20 /// * [`Module::from_binary`](crate::Module::from_binary) 21 /// 22 /// Note that a [`CodeBuilder`] always involves compiling WebAssembly bytes 23 /// to machine code. To deserialize a list of bytes use 24 /// [`Module::deserialize`](crate::Module::deserialize) instead. 25 /// 26 /// A [`CodeBuilder`] requires a source of WebAssembly bytes to be configured 27 /// before calling [`compile_module_serialized`] or [`compile_module`]. This can 28 /// be provided with either the [`wasm_binary`] or [`wasm_binary_file`] method. 29 /// Note that only a single source of bytes can be provided. 30 /// 31 /// # WebAssembly Text Format 32 /// 33 /// This builder supports the WebAssembly Text Format (`*.wat` files) through 34 /// the [`CodeBuilder::wasm_binary_or_text`] and 35 /// [`CodeBuilder::wasm_binary_or_text_file`] methods. These methods 36 /// automatically convert WebAssembly text files to binary. Note though that 37 /// this behavior is disabled if the `wat` crate feature is not enabled. 38 /// 39 /// [`compile_module_serialized`]: CodeBuilder::compile_module_serialized 40 /// [`compile_module`]: CodeBuilder::compile_module 41 /// [`wasm_binary`]: CodeBuilder::wasm_binary 42 /// [`wasm_binary_file`]: CodeBuilder::wasm_binary_file 43 pub struct CodeBuilder<'a> { 44 pub(super) engine: &'a Engine, 45 wasm: Option<Cow<'a, [u8]>>, 46 wasm_path: Option<Cow<'a, Path>>, 47 dwarf_package: Option<Cow<'a, [u8]>>, 48 dwarf_package_path: Option<Cow<'a, Path>>, 49 unsafe_intrinsics_import: Option<String>, 50 51 /// A map from import name to the Wasm bytes of the associated compile-time 52 /// builtin and its file path, if any. 53 // 54 // XXX: we can't use `'a` here without forcing us to change a bunch of 55 // callers because `HashMap` has a `Drop` implementation, so `dropck` thinks 56 // that all `'a` borrows could be used by `CodeBuilder`'s `Drop`, which 57 // means that a bunch of the existing calls to `.wasm_bytes` and such would 58 // need to be reordered with the `CodeBuilder`'s construction. 59 #[cfg(feature = "compile-time-builtins")] 60 compile_time_builtins: HashMap<Cow<'a, str>, Cow<'a, [u8]>>, 61 } 62 63 /// Return value of [`CodeBuilder::hint`] 64 pub enum CodeHint { 65 /// Hint that the code being compiled is a module. 66 Module, 67 /// Hint that the code being compiled is a component. 68 Component, 69 } 70 71 impl<'a> CodeBuilder<'a> { 72 /// Creates a new builder which will insert modules into the specified 73 /// [`Engine`]. 74 pub fn new(engine: &'a Engine) -> Self { 75 CodeBuilder { 76 engine, 77 wasm: None, 78 wasm_path: None, 79 dwarf_package: None, 80 dwarf_package_path: None, 81 unsafe_intrinsics_import: None, 82 #[cfg(feature = "compile-time-builtins")] 83 compile_time_builtins: HashMap::default(), 84 } 85 } 86 87 /// Configures the WebAssembly binary that is being compiled. 88 /// 89 /// The `wasm_bytes` parameter must be a binary WebAssembly file. 90 /// This will be stored within the [`CodeBuilder`] for processing later when 91 /// compilation is finalized. 92 /// 93 /// The optional `wasm_path` parameter is the path to the `wasm_bytes` on 94 /// disk, if any. This may be used for diagnostics and other 95 /// debugging-related purposes, but this method will not read the path 96 /// specified. 97 /// 98 /// # Errors 99 /// 100 /// This method will return an error if WebAssembly bytes have already been 101 /// configured. 102 pub fn wasm_binary( 103 &mut self, 104 wasm_bytes: impl Into<Cow<'a, [u8]>>, 105 wasm_path: Option<&'a Path>, 106 ) -> Result<&mut Self> { 107 if self.wasm.is_some() { 108 bail!("cannot configure wasm bytes twice"); 109 } 110 self.wasm = Some(wasm_bytes.into()); 111 self.wasm_path = wasm_path.map(|p| p.into()); 112 113 if self.wasm_path.is_some() { 114 self.dwarf_package_from_wasm_path()?; 115 } 116 117 Ok(self) 118 } 119 120 /// Equivalent of [`CodeBuilder::wasm_binary`] that also accepts the 121 /// WebAssembly text format. 122 /// 123 /// This method will configure the WebAssembly binary to be compiled. The 124 /// input `wasm_bytes` may either be the wasm text format or the binary 125 /// format. If the `wat` crate feature is enabled, which is enabled by 126 /// default, then the text format will automatically be converted to the 127 /// binary format. 128 /// 129 /// # Errors 130 /// 131 /// This method will return an error if WebAssembly bytes have already been 132 /// configured. This method will also return an error if `wasm_bytes` is the 133 /// wasm text format and the text syntax is not valid. 134 pub fn wasm_binary_or_text( 135 &mut self, 136 wasm_bytes: &'a [u8], 137 wasm_path: Option<&'a Path>, 138 ) -> Result<&mut Self> { 139 #[cfg(feature = "wat")] 140 let wasm_bytes = wat::parse_bytes(wasm_bytes).map_err(|mut e| { 141 if let Some(path) = wasm_path { 142 e.set_path(path); 143 } 144 e 145 })?; 146 self.wasm_binary(wasm_bytes, wasm_path) 147 } 148 149 /// Reads the `file` specified for the WebAssembly bytes that are going to 150 /// be compiled. 151 /// 152 /// This method will read `file` from the filesystem and interpret it 153 /// as a WebAssembly binary. 154 /// 155 /// A DWARF package file will be probed using the root of `file` and with a 156 /// `.dwp` extension. If found, it will be loaded and DWARF fusion 157 /// performed. 158 /// 159 /// # Errors 160 /// 161 /// This method will return an error if WebAssembly bytes have already been 162 /// configured. 163 /// 164 /// If `file` can't be read or an error happens reading it then that will 165 /// also be returned. 166 /// 167 /// If DWARF fusion is performed and the DWARF packaged file cannot be read 168 /// then an error will be returned. 169 pub fn wasm_binary_file(&mut self, file: &'a Path) -> Result<&mut Self> { 170 let wasm = std::fs::read(file) 171 .with_context(|| format!("failed to read input file: {}", file.display()))?; 172 self.wasm_binary(wasm, Some(file)) 173 } 174 175 /// Equivalent of [`CodeBuilder::wasm_binary_file`] that also accepts the 176 /// WebAssembly text format. 177 /// 178 /// This method is will read the file at `path` and interpret the contents 179 /// to determine if it's the wasm text format or binary format. The file 180 /// extension of `file` is not consulted. The text format is automatically 181 /// converted to the binary format if the crate feature `wat` is active. 182 /// 183 /// # Errors 184 /// 185 /// In addition to the errors returned by [`CodeBuilder::wasm_binary_file`] 186 /// this may also fail if the text format is read and the syntax is invalid. 187 pub fn wasm_binary_or_text_file(&mut self, file: &'a Path) -> Result<&mut Self> { 188 #[cfg(feature = "wat")] 189 { 190 let wasm = wat::parse_file(file)?; 191 self.wasm_binary(wasm, Some(file)) 192 } 193 #[cfg(not(feature = "wat"))] 194 { 195 self.wasm_binary_file(file) 196 } 197 } 198 199 /// Get the Wasm to be compiled. 200 /// 201 /// When using compile-time builtins, compose the builtins and the guest 202 /// Wasm together, before returning the Wasm. 203 pub(super) fn get_wasm(&self) -> Result<Cow<'_, [u8]>> { 204 let wasm = self 205 .wasm 206 .as_deref() 207 .ok_or_else(|| format_err!("no wasm bytes have been configured"))?; 208 209 #[cfg(not(feature = "compile-time-builtins"))] 210 { 211 Ok(wasm.into()) 212 } 213 214 #[cfg(feature = "compile-time-builtins")] 215 { 216 self.compose_compile_time_builtins(wasm) 217 .context("failed to compose compile-time builtins with the main Wasm") 218 } 219 } 220 221 /// Expose Wasmtime's unsafe intrinsics under the given import name. 222 /// 223 /// These intrinsics provide native memory loads and stores to Wasm; they 224 /// are *extremely* unsafe! If you are not absolutely sure that you need 225 /// these unsafe intrinsics, *do not use them!* See the safety section below 226 /// for details. 227 /// 228 /// This functionality is intended to be used when implementing 229 /// [compile-time builtins][CodeBuilder::compile_time_builtins_binary]; that 230 /// is, satisfying a Wasm import via special-cased, embedder-specific code 231 /// at compile time. You should never use these intrinsics to intentionally 232 /// subvert the Wasm sandbox. You should strive to implement safe functions 233 /// that encapsulate your uses of these intrinsics such that, regardless of 234 /// any value given as arguments, your functions *cannot* result in loading 235 /// from or storing to invalid pointers, or any other kind of unsafety. See 236 /// below for an example of the intended use cases. 237 /// 238 /// Wasmtime's unsafe intrinsics can only be exposed to Wasm components, not 239 /// core modules, currently. 240 /// 241 /// Note that when compile-time builtins are defined on a `CodeBuilder`, 242 /// only the compile-time builtins can import the unsafe intrinsics, and the 243 /// main guest program cannot import them. 244 /// 245 /// # Safety 246 /// 247 /// Extreme care must be taken when using these intrinsics. 248 /// 249 /// All loads of or stores to pointers derived from `store-data-address` are 250 /// inherently tied to a particular `T` type in a `Store<T>`. It is wildly 251 /// unsafe to run a Wasm program that uses unsafe intrinsics to access the 252 /// store's `T` inside a `Store<U>`. You must only run Wasm that uses unsafe 253 /// intrinsics in a `Store<T>` where the `T` is the type expected by the 254 /// Wasm's unsafe-intrinsics usage. 255 /// 256 /// Furthermore, usage of these intrinsics is not only tied to a particular 257 /// `T` type, but also to `T`'s layout on the host platform. The size and 258 /// alignment of `T`, the offsets of its fields, and those fields' size and 259 /// alignment can all vary across not only architecture but also operating 260 /// system. With care, you can define your `T` type such that its layout is 261 /// identical across the platforms that you run Wasm on, allowing you to 262 /// reuse the same Wasm binary and its unsafe-intrinsics usage on all your 263 /// platforms. Failing that, you must only run a Wasm program that uses 264 /// unsafe intrinsics on the host platform that its unsafe-intrinsic usage 265 /// is specialized to. See the portability section and example below for 266 /// more details. 267 /// 268 /// You are *strongly* encouraged to add assertions for the layout 269 /// properties that your unsafe-intrinsic usage's safety relies upon: 270 /// 271 /// ```rust 272 /// /// This type is used as `wasmtime::Store<MyData>` and accessed by Wasm via 273 /// /// unsafe intrinsics. 274 /// #[repr(C, align(8))] 275 /// struct MyData { 276 /// id: u64, 277 /// counter: u32, 278 /// buf: [u8; 4], 279 /// } 280 /// 281 /// // Assert that the layout is what our Wasm's unsafe-intrinsics usage expects. 282 /// static _MY_DATA_LAYOUT_ASSERTIONS: () = { 283 /// assert!(core::mem::size_of::<MyData>() == 16); 284 /// assert!(core::mem::align_of::<MyData>() == 8); 285 /// assert!(core::mem::offset_of!(MyData, id) == 0); 286 /// assert!(core::mem::offset_of!(MyData, counter) == 8); 287 /// assert!(core::mem::offset_of!(MyData, buf) == 12); 288 /// }; 289 /// ``` 290 /// 291 /// Finally, every pointer loaded from or stored to must: 292 /// 293 /// * Be non-null 294 /// 295 /// * Be aligned to the access type's natural alignment (e.g. 8-byte alignment 296 /// for `u64`, 4-byte alignment for `u32`, etc...) 297 /// 298 /// * Point to a memory block that is valid to read from (for loads) or 299 /// valid to write to (for stores) under Rust's pointer provenance rules 300 /// 301 /// * Point to a memory block that is at least as large as the access type's 302 /// natural size (e.g. 1 byte for `u8`, 2 bytes for `u16`, etc...) 303 /// 304 /// * Point to a memory block that is not accessed concurrently by any other 305 /// threads 306 /// 307 /// Failure to uphold any of these invariants will lead to unsafety, 308 /// undefined behavior, and/or data races. 309 /// 310 /// # Intrinsics 311 /// 312 /// | Name | Parameters | Results | 313 /// |----------------------|--------------|---------| 314 /// | `u8-native-load` | `u64` | `u8` | 315 /// | `u16-native-load` | `u64` | `u16` | 316 /// | `u32-native-load` | `u64` | `u32` | 317 /// | `u64-native-load` | `u64` | `u64` | 318 /// | `u8-native-store` | `u64`, `u8` | - | 319 /// | `u16-native-load` | `u64`, `u16` | - | 320 /// | `u32-native-load` | `u64`, `u32` | - | 321 /// | `u64-native-load` | `u64`, `u64` | - | 322 /// | `store-data-address` | - | `u64` | 323 /// 324 /// ## `*-native-load` 325 /// 326 /// These intrinsics perform an unsandboxed, unsynchronized load from native 327 /// memory, using the native endianness. 328 /// 329 /// ## `*-native-store` 330 /// 331 /// These intrinsics perform an unsandboxed, unsynchronized store to native 332 /// memory, using the native endianness. 333 /// 334 /// ## `store-data-address` 335 /// 336 /// This intrinsic function returns the pointer to the embedder's `T` data 337 /// inside a `Store<T>`. 338 /// 339 /// In general, all native load and store intinsics should operate on memory 340 /// addresses that are derived from a call to this intrinsic. If you want to 341 /// expose data for raw memory access by Wasm, put it inside the `T` in your 342 /// `Store<T>` and Wasm's access to that data should derive from this 343 /// intrinsic. 344 /// 345 /// # Portability 346 /// 347 /// Loads and stores are always performed using the architecture's native 348 /// endianness. 349 /// 350 /// Addresses passed to and returned from these intrinsics are always 351 /// 64-bits large. The upper half of the value is simply ignored on 32-bit 352 /// architectures. 353 /// 354 /// With care, you can design your store's `T` type such that accessing it 355 /// via these intrinsics is portable, and you can reuse a single Wasm binary 356 /// (and its set of intrinsic calls) across all of the platforms, with the 357 /// following rules of thumb: 358 /// 359 /// * Only access `u8`, `u16`, `u32`, and `u64` data via these intrinsics. 360 /// 361 /// * If you need to access other types of data, encode it into those types 362 /// and then access the encoded data from the intrinsics. 363 /// 364 /// * Use `union`s to encode pointers and pointer-sized data as a `u64` and 365 /// then access it via the `u64-native-{load,store}` intrinsics. See 366 /// `ExposedPointer` in the example below. 367 /// 368 /// # Example 369 /// 370 /// The following example shows how you can use unsafe intrinsics and 371 /// compile-time builtins to give Wasm direct zero-copy access to a host 372 /// buffer. 373 /// 374 /// ```rust 375 /// use std::mem; 376 /// use wasmtime::*; 377 /// 378 /// // A `*mut u8` pointer that is exposed directly to Wasm via unsafe intrinsics. 379 /// #[repr(align(8))] 380 /// union ExposedPointer { 381 /// pointer: *mut u8, 382 /// padding: u64, 383 /// } 384 /// 385 /// static _EXPOSED_POINTER_LAYOUT_ASSERTIONS: () = { 386 /// assert!(mem::size_of::<ExposedPointer>() == 8); 387 /// assert!(mem::align_of::<ExposedPointer>() == 8); 388 /// }; 389 /// 390 /// impl ExposedPointer { 391 /// /// Wrap the given pointer into an `ExposedPointer`. 392 /// fn new(pointer: *mut u8) -> Self { 393 /// // NB: Zero-initialize to avoid potential footguns with accessing 394 /// // undefined bytes. 395 /// let mut p = Self { padding: 0 }; 396 /// p.pointer = pointer; 397 /// p 398 /// } 399 /// 400 /// /// Get the wrapped pointer. 401 /// fn get(&self) -> *mut u8 { 402 /// unsafe { self.pointer } 403 /// } 404 /// } 405 /// 406 /// /// This is the `T` type we will put inside our 407 /// /// `wasmtime::Store<T>`s. It contains a pointer to a heap-allocated buffer 408 /// /// in host memory, which we will give Wasm zero-copy access to via unsafe 409 /// /// intrinsics. 410 /// #[repr(C)] 411 /// struct StoreData { 412 /// buf_ptr: ExposedPointer, 413 /// buf_len: u64, 414 /// } 415 /// 416 /// static _STORE_DATA_LAYOUT_ASSERTIONS: () = { 417 /// assert!(mem::size_of::<StoreData>() == 16); 418 /// assert!(mem::align_of::<StoreData>() == 8); 419 /// assert!(mem::offset_of!(StoreData, buf_ptr) == 0); 420 /// assert!(mem::offset_of!(StoreData, buf_len) == 8); 421 /// }; 422 /// 423 /// impl Drop for StoreData { 424 /// fn drop(&mut self) { 425 /// let len = usize::try_from(self.buf_len).unwrap(); 426 /// let ptr = std::ptr::slice_from_raw_parts_mut(self.buf_ptr.get(), len); 427 /// unsafe { 428 /// let _ = Box::from_raw(ptr); 429 /// } 430 /// } 431 /// } 432 /// 433 /// impl StoreData { 434 /// /// Create a new `StoreData`, allocating an inner buffer containing 435 /// /// `bytes`. 436 /// fn new(bytes: impl IntoIterator<Item = u8>) -> Self { 437 /// let buf: Box<[u8]> = bytes.into_iter().collect(); 438 /// let ptr = Box::into_raw(buf); 439 /// Self { 440 /// buf_ptr: ExposedPointer::new(ptr.cast::<u8>()), 441 /// buf_len: u64::try_from(ptr.len()).unwrap(), 442 /// } 443 /// } 444 /// 445 /// /// Get the inner buffer as a shared slice. 446 /// fn buf(&self) -> &[u8] { 447 /// let ptr = self.buf_ptr.get().cast_const(); 448 /// let len = usize::try_from(self.buf_len).unwrap(); 449 /// unsafe { 450 /// std::slice::from_raw_parts(ptr, len) 451 /// } 452 /// } 453 /// } 454 /// 455 /// # fn main() -> Result<()> { 456 /// // Enable function inlining during compilation. If you are using unsafe intrinsics, you 457 /// // almost assuredly want them inlined to avoid function call overheads. 458 /// let mut config = Config::new(); 459 /// config.compiler_inlining(true); 460 /// 461 /// let engine = Engine::new(&config)?; 462 /// let linker = wasmtime::component::Linker::new(&engine); 463 /// 464 /// // Create a new builder for configuring a Wasm compilation. 465 /// let mut builder = CodeBuilder::new(&engine); 466 /// 467 /// // Allow the code we are building to use Wasmtime's unsafe intrinsics. 468 /// // 469 /// // SAFETY: we wrap all usage of the intrinsics in safe APIs and only instantiate the code 470 /// // within a `Store<T>` where `T = StoreData`, as the code expects. 471 /// unsafe { 472 /// builder.expose_unsafe_intrinsics("unsafe-intrinsics"); 473 /// } 474 /// 475 /// // Define the compile-time builtin that encapsulates the 476 /// // intrinsics' unsafety and builds a safe API on top of them. 477 /// unsafe { 478 /// builder.compile_time_builtins_binary_or_text( 479 /// "safe-api", 480 /// r#" 481 /// (component 482 /// (import "unsafe-intrinsics" 483 /// (instance $intrinsics 484 /// (export "store-data-address" (func (result u64))) 485 /// (export "u64-native-load" (func (param "pointer" u64) (result u64))) 486 /// (export "u8-native-load" (func (param "pointer" u64) (result u8))) 487 /// (export "u8-native-store" (func (param "pointer" u64) (param "value" u8))) 488 /// ) 489 /// ) 490 /// 491 /// ;; The core Wasm module that implements the safe API. 492 /// (core module $safe-api-impl 493 /// (import "" "store-data-address" (func $store-data-address (result i64))) 494 /// (import "" "u64-native-load" (func $u64-native-load (param i64) (result i64))) 495 /// (import "" "u8-native-load" (func $u8-native-load (param i64) (result i32))) 496 /// (import "" "u8-native-store" (func $u8-native-store (param i64 i32))) 497 /// 498 /// ;; Load the `StoreData::buf_ptr` field 499 /// (func $get-buf-ptr (result i64) 500 /// (call $u64-native-load (i64.add (call $store-data-address) (i64.const 0))) 501 /// ) 502 /// 503 /// ;; Load the `StoreData::buf_len` field 504 /// (func $get-buf-len (result i64) 505 /// (call $u64-native-load (i64.add (call $store-data-address) (i64.const 8))) 506 /// ) 507 /// 508 /// ;; Check that `$i` is within `StoreData` buffer's bounds, raising a trap 509 /// ;; otherwise. 510 /// (func $bounds-check (param $i i64) 511 /// (if (i64.lt_u (local.get $i) (call $get-buf-len)) 512 /// (then (return)) 513 /// (else (unreachable)) 514 /// ) 515 /// ) 516 /// 517 /// ;; A safe function to get the `i`th byte from `StoreData`'s buffer, 518 /// ;; raising a trap on out-of-bounds accesses. 519 /// (func (export "get") (param $i i64) (result i32) 520 /// (call $bounds-check (local.get $i)) 521 /// (call $u8-native-load (i64.add (call $get-buf-ptr) (local.get $i))) 522 /// ) 523 /// 524 /// ;; A safe function to set the `i`th byte in `StoreData`'s buffer, 525 /// ;; raising a trap on out-of-bounds accesses. 526 /// (func (export "set") (param $i i64) (param $value i32) 527 /// (call $bounds-check (local.get $i)) 528 /// (call $u8-native-store (i64.add (call $get-buf-ptr) (local.get $i)) 529 /// (local.get $value)) 530 /// ) 531 /// 532 /// ;; A safe function to get the length of the `StoreData` buffer. 533 /// (func (export "len") (result i64) 534 /// (call $get-buf-len) 535 /// ) 536 /// ) 537 /// 538 /// ;; Lower the imported intrinsics from component functions to core functions. 539 /// (core func $store-data-address' (canon lower (func $intrinsics "store-data-address"))) 540 /// (core func $u64-native-load' (canon lower (func $intrinsics "u64-native-load"))) 541 /// (core func $u8-native-load' (canon lower (func $intrinsics "u8-native-load"))) 542 /// (core func $u8-native-store' (canon lower (func $intrinsics "u8-native-store"))) 543 /// 544 /// ;; Instantiate our safe API implementation, passing in the lowered unsafe 545 /// ;; intrinsics as its imports. 546 /// (core instance $instance 547 /// (instantiate $safe-api-impl 548 /// (with "" (instance 549 /// (export "store-data-address" (func $store-data-address')) 550 /// (export "u64-native-load" (func $u64-native-load')) 551 /// (export "u8-native-load" (func $u8-native-load')) 552 /// (export "u8-native-store" (func $u8-native-store')) 553 /// )) 554 /// ) 555 /// ) 556 /// 557 /// ;; Lift the safe API's exports from core functions to component functions 558 /// ;; and export them. 559 /// (func (export "get") (param "i" u64) (result u8) 560 /// (canon lift (core func $instance "get")) 561 /// ) 562 /// (func (export "set") (param "i" u64) (param "value" u8) 563 /// (canon lift (core func $instance "set")) 564 /// ) 565 /// (func (export "len") (result u64) 566 /// (canon lift (core func $instance "len")) 567 /// ) 568 /// ) 569 /// "#.as_bytes(), 570 /// None, 571 /// )?; 572 /// } 573 /// 574 /// // Provide the guest Wasm that we are compiling, which uses the safe API we 575 /// // implemented as a compile-time builtin. 576 /// builder.wasm_binary_or_text( 577 /// r#" 578 /// (component 579 /// ;; Import the safe API. 580 /// (import "safe-api" 581 /// (instance $safe-api 582 /// (export "get" (func (param "i" u64) (result u8))) 583 /// (export "set" (func (param "i" u64) (param "value" u8))) 584 /// (export "len" (func (result u64))) 585 /// ) 586 /// ) 587 /// 588 /// ;; Define this component's core module implementation. 589 /// (core module $main-impl 590 /// (import "" "get" (func $get (param i64) (result i32))) 591 /// (import "" "set" (func $set (param i64 i32))) 592 /// (import "" "len" (func $len (result i64))) 593 /// 594 /// (func (export "main") 595 /// (local $i i64) 596 /// (local $n i64) 597 /// 598 /// (local.set $i (i64.const 0)) 599 /// (local.set $n (call $len)) 600 /// 601 /// (loop $loop 602 /// ;; When we have iterated over every byte in the 603 /// ;; buffer, exit. 604 /// (if (i64.ge_u (local.get $i) (local.get $n)) 605 /// (then (return))) 606 /// 607 /// ;; Increment the `i`th byte in the buffer. 608 /// (call $set (local.get $i) 609 /// (i32.add (call $get (local.get $i)) 610 /// (i32.const 1))) 611 /// 612 /// ;; Increment `i` and continue to the next iteration 613 /// ;; of the loop. 614 /// (local.set $i (i64.add (local.get $i) (i64.const 1))) 615 /// (br $loop) 616 /// ) 617 /// ) 618 /// ) 619 /// 620 /// ;; Lower the imported safe APIs from component functions to core functions. 621 /// (core func $get' (canon lower (func $safe-api "get"))) 622 /// (core func $set' (canon lower (func $safe-api "set"))) 623 /// (core func $len' (canon lower (func $safe-api "len"))) 624 /// 625 /// ;; Instantiate our module, providing the lowered safe APIs as imports. 626 /// (core instance $instance 627 /// (instantiate $main-impl 628 /// (with "" (instance 629 /// (export "get" (func $get')) 630 /// (export "set" (func $set')) 631 /// (export "len" (func $len')) 632 /// )) 633 /// ) 634 /// ) 635 /// 636 /// ;; Lift the implementation's `main` from a core function to a component function 637 /// ;; and export it! 638 /// (func (export "main") 639 /// (canon lift (core func $instance "main")) 640 /// ) 641 /// ) 642 /// "#.as_bytes(), 643 /// None, 644 /// )?; 645 /// 646 /// // Finish the builder and compile the component. 647 /// let component = builder.compile_component()?; 648 /// 649 /// // Create a new `Store<StoreData>`, wrapping a buffer of the given elements. 650 /// let mut store = Store::new(&engine, StoreData::new([0, 10, 20, 30, 40, 50])); 651 /// 652 /// // Instantiate our component into the store. 653 /// let instance = linker.instantiate(&mut store, &component)?; 654 /// 655 /// // Get the instance's exported `main` function and call it. 656 /// instance 657 /// .get_typed_func::<(), ()>(&mut store, "main")? 658 /// .call(&mut store, ())?; 659 /// 660 /// // Our `StoreData`'s buffer had each element incremented directly from Wasm! 661 /// assert_eq!(store.data().buf(), &[1, 11, 21, 31, 41, 51]); 662 /// # Ok(()) 663 /// # } 664 /// ``` 665 pub unsafe fn expose_unsafe_intrinsics(&mut self, import_name: impl Into<String>) -> &mut Self { 666 self.unsafe_intrinsics_import = Some(import_name.into()); 667 self 668 } 669 670 /// Explicitly specify DWARF `.dwp` path. 671 /// 672 /// # Errors 673 /// 674 /// This method will return an error if the `.dwp` file has already been set 675 /// through [`CodeBuilder::dwarf_package`] or auto-detection in 676 /// [`CodeBuilder::wasm_binary_file`]. 677 /// 678 /// This method will also return an error if `file` cannot be read. 679 pub fn dwarf_package_file(&mut self, file: &Path) -> Result<&mut Self> { 680 if self.dwarf_package.is_some() { 681 bail!("cannot call `dwarf_package` or `dwarf_package_file` twice"); 682 } 683 684 let dwarf_package = std::fs::read(file) 685 .with_context(|| format!("failed to read dwarf input file: {}", file.display()))?; 686 self.dwarf_package_path = Some(Cow::Owned(file.to_owned())); 687 self.dwarf_package = Some(dwarf_package.into()); 688 689 Ok(self) 690 } 691 692 fn dwarf_package_from_wasm_path(&mut self) -> Result<&mut Self> { 693 let dwarf_package_path_buf = self.wasm_path.as_ref().unwrap().with_extension("dwp"); 694 if dwarf_package_path_buf.exists() { 695 return self.dwarf_package_file(dwarf_package_path_buf.as_path()); 696 } 697 698 Ok(self) 699 } 700 701 /// Gets the DWARF package. 702 pub(super) fn get_dwarf_package(&self) -> Option<&[u8]> { 703 self.dwarf_package.as_deref() 704 } 705 706 /// Set the DWARF package binary. 707 /// 708 /// Initializes `dwarf_package` from `dwp_bytes` in preparation for 709 /// DWARF fusion. Allows the DWARF package to be supplied as a byte array 710 /// when the file probing performed in `wasm_file` is not appropriate. 711 /// 712 /// # Errors 713 /// 714 /// Returns an error if the `*.dwp` file is already set via auto-probing in 715 /// [`CodeBuilder::wasm_binary_file`] or explicitly via 716 /// [`CodeBuilder::dwarf_package_file`]. 717 pub fn dwarf_package(&mut self, dwp_bytes: &'a [u8]) -> Result<&mut Self> { 718 if self.dwarf_package.is_some() { 719 bail!("cannot call `dwarf_package` or `dwarf_package_file` twice"); 720 } 721 self.dwarf_package = Some(dwp_bytes.into()); 722 Ok(self) 723 } 724 725 /// Returns a hint, if possible, of what the provided bytes are. 726 /// 727 /// This method can be use to detect what the previously supplied bytes to 728 /// methods such as [`CodeBuilder::wasm_binary_or_text`] are. This will 729 /// return whether a module or a component was found in the provided bytes. 730 /// 731 /// This method will return `None` if wasm bytes have not been configured 732 /// or if the provided bytes don't look like either a component or a 733 /// module. 734 pub fn hint(&self) -> Option<CodeHint> { 735 let wasm = self.wasm.as_ref()?; 736 if wasmparser::Parser::is_component(wasm) { 737 Some(CodeHint::Component) 738 } else if wasmparser::Parser::is_core_wasm(wasm) { 739 Some(CodeHint::Module) 740 } else { 741 None 742 } 743 } 744 745 /// Finishes this compilation and produces a serialized list of bytes. 746 /// 747 /// This method requires that either [`CodeBuilder::wasm_binary`] or 748 /// related methods were invoked prior to indicate what is being compiled. 749 /// 750 /// This method will block the current thread until compilation has 751 /// finished, and when done the serialized artifact will be returned. 752 /// 753 /// Note that this method will never cache compilations, even if the 754 /// `cache` feature is enabled. 755 /// 756 /// # Errors 757 /// 758 /// This can fail if the input wasm module was not valid or if another 759 /// compilation-related error is encountered. 760 pub fn compile_module_serialized(&self) -> Result<Vec<u8>> { 761 ensure!( 762 self.unsafe_intrinsics_import.is_none(), 763 "`CodeBuilder::expose_unsafe_intrinsics` can only be used with components" 764 ); 765 766 #[cfg(feature = "compile-time-builtins")] 767 ensure!( 768 self.get_compile_time_builtins().is_empty(), 769 "compile-time builtins can only be used with components" 770 ); 771 772 let wasm = self.get_wasm()?; 773 let dwarf_package = self.get_dwarf_package(); 774 let (v, _) = 775 super::build_module_artifacts(self.engine, &wasm, dwarf_package.as_deref(), &())?; 776 Ok(v) 777 } 778 779 /// Same as [`CodeBuilder::compile_module_serialized`] except that it 780 /// compiles a serialized [`Component`](crate::component::Component) 781 /// instead of a module. 782 #[cfg(feature = "component-model")] 783 pub fn compile_component_serialized(&self) -> Result<Vec<u8>> { 784 let wasm = self.get_wasm()?; 785 let (v, _) = super::build_component_artifacts( 786 self.engine, 787 &wasm, 788 None, 789 self.get_unsafe_intrinsics_import(), 790 &(), 791 )?; 792 Ok(v) 793 } 794 795 pub(super) fn get_unsafe_intrinsics_import(&self) -> Option<&str> { 796 self.unsafe_intrinsics_import.as_deref() 797 } 798 } 799 800 /// This is a helper struct used when caching to hash the state of an `Engine` 801 /// used for module compilation. 802 /// 803 /// The hash computed for this structure is used to key the global wasmtime 804 /// cache and dictates whether artifacts are reused. Consequently the contents 805 /// of this hash dictate when artifacts are or aren't re-used. 806 pub struct HashedEngineCompileEnv<'a>(pub &'a Engine); 807 808 impl std::hash::Hash for HashedEngineCompileEnv<'_> { 809 fn hash<H: std::hash::Hasher>(&self, hasher: &mut H) { 810 // Hash the compiler's state based on its target and configuration. 811 if let Some(compiler) = self.0.compiler() { 812 compiler.triple().hash(hasher); 813 compiler.flags().hash(hasher); 814 compiler.isa_flags().hash(hasher); 815 } 816 817 // Hash configuration state read for compilation 818 let config = self.0.config(); 819 self.0.tunables().hash(hasher); 820 self.0.features().hash(hasher); 821 config.wmemcheck.hash(hasher); 822 823 // Catch accidental bugs of reusing across crate versions. 824 config.module_version.hash(hasher); 825 } 826 } 827