Memory Manipulation
In this example, we'll present how to manipulate the linear memory with the APIs defined in wasmedge_sdk::Memory.
The code in the following example is verified on
- wasmedge-sdk v0.5.0
- wasmedge-sys v0.10.0
- wasmedge-types v0.3.0
Wasm module
Before talking about the code, let's first see the wasm module we use in this example. In the wasm module, a linear memory of 1-page (64KiB) size is defined; in addition, three functions are exported from this module: get_at
, set_at
, and mem_size
.
(module
(type $mem_size_t (func (result i32)))
(type $get_at_t (func (param i32) (result i32)))
(type $set_at_t (func (param i32) (param i32)))
# A memory with initial size of 1 page
(memory $mem 1)
(func $get_at (type $get_at_t) (param $idx i32) (result i32)
(i32.load (local.get $idx)))
(func $set_at (type $set_at_t) (param $idx i32) (param $val i32)
(i32.store (local.get $idx) (local.get $val)))
(func $mem_size (type $mem_size_t) (result i32)
(memory.size))
# Exported functions
(export "get_at" (func $get_at))
(export "set_at" (func $set_at))
(export "mem_size" (func $mem_size))
(export "memory" (memory $mem)))
Next, we'll demonstrate how to manipulate the linear memory by calling the exported functions.
Load and Register Module
Let's start off by getting all imports right away so you can follow along
#![allow(unused)] fn main() { // please add this feature if you're using rust of version < 1.63 // #![feature(explicit_generic_args_with_impl_trait)] use wasmedge_sdk::{params, wat2wasm, Executor, Module, Store, WasmVal}; }
To load a Module
, wasmedge-sdk
defines two methods:
-
from_file loads a wasm module from a file, and meanwhile, validates the loaded wasm module.
-
from_bytes loads a wasm module from an array of in-memory bytes, and meanwhile, validates the loaded wasm module.
Here we use Module::from_bytes
method to load our wasm module from an array of in-memory bytes.
#![allow(unused)] fn main() { let wasm_bytes = wat2wasm( r#" (module (type $mem_size_t (func (result i32))) (type $get_at_t (func (param i32) (result i32))) (type $set_at_t (func (param i32) (param i32))) (memory $mem 1) (func $get_at (type $get_at_t) (param $idx i32) (result i32) (i32.load (local.get $idx))) (func $set_at (type $set_at_t) (param $idx i32) (param $val i32) (i32.store (local.get $idx) (local.get $val))) (func $mem_size (type $mem_size_t) (result i32) (memory.size)) (export "get_at" (func $get_at)) (export "set_at" (func $set_at)) (export "mem_size" (func $mem_size)) (export "memory" (memory $mem))) "# .as_bytes(), )?; // loads a wasm module from the given in-memory bytes let module = Module::from_bytes(None, &wasm_bytes)?; }
The module returned by Module::from_bytes
is a compiled module, also called AST Module in WasmEdge terminology. To use it in WasmEdge runtime environment, we need to instantiate the AST module. We use Store::register_named_module API to achieve the goal.
#![allow(unused)] fn main() { // create an executor let mut executor = Executor::new(None, None)?; // create a store let mut store = Store::new()?; // register the module into the store let extern_instance = store.register_named_module(&mut executor, "extern", &module)?; }
In the code above, we register the AST module into a Store
, in which the module is instantiated, and as a result, a module instance named extern
is returned.
Memory
In the previous section, we get an instance by registering a compiled module into the runtime environment. Now we retrieve the memory instance from the module instance, and make use of the APIs defined in Memory to manipulate the linear memory.
#![allow(unused)] fn main() { // get the exported memory instance let mut memory = extern_instance .memory("memory") .ok_or_else(|| anyhow::anyhow!("failed to get memory instance named 'memory'"))?; // check memory size assert_eq!(memory.size(), 1); assert_eq!(memory.data_size(), 65536); // grow memory size memory.grow(2)?; assert_eq!(memory.size(), 3); assert_eq!(memory.data_size(), 3 * 65536); // get the exported functions: "set_at" and "get_at" let set_at = extern_instance .func("set_at") .ok_or_else(|| anyhow::Error::msg("Not found exported function named 'set_at'."))?; let get_at = extern_instance .func("get_at") .ok_or_else(|| anyhow::Error::msg("Not found exported function named 'get_at`."))?; // call the exported function named "set_at" let mem_addr = 0x2220; let val = 0xFEFEFFE; set_at.call(&mut executor, params!(mem_addr, val))?; // call the exported function named "get_at" let returns = get_at.call(&mut executor, params!(mem_addr))?; assert_eq!(returns[0].to_i32(), val); // call the exported function named "set_at" let page_size = 0x1_0000; let mem_addr = (page_size * 2) - std::mem::size_of_val(&val) as i32; let val = 0xFEA09; set_at.call(&mut executor, params!(mem_addr, val))?; // call the exported function named "get_at" let returns = get_at.call(&mut executor, params!(mem_addr))?; assert_eq!(returns[0].to_i32(), val); }
The comments in the code explain the meaning of the code sample above, so we don't describe more.
The complete code of this example can be found in memory.rs.