► Using memory pointers

In this example we will learn how to pass data between the host system and Wasm using memory pointers.

Linear memory is a major concept in WebAssembly:

Because WebAssembly is sandboxed, memory must be copied between the host (your Rust application) and the Wasm module. Upcoming proposals like WebAssembly Interface Types will make this process much easier, but it is still a work in progress.

The way that this memory is allocated, freed, passed, organized, etc... can vary depending on the ABI of the Wasm module.

For example, some ABIs will provide functions, either as imports or exports, for allocation and freeing of memory from the host or guest. Some Wasm ABIs may want to control their memory implicitly, for example the ABI may say that the host can reserve memory addresses 0 to 1000 for a special purpose and simply write there directly. You will want to take a look at the documentation of the ABI of your Wasm module, to see what conventions are used for memory allocation.

In this example, let's say we have a Wasm module that can perform transformations on a string passed into the module's memory. This module exports a function that returns a pointer to a fixed size static buffer. This Wasm module will take in a string, and append the string " Wasm is cool!" to the end. This example shows how we can read and write memory from the host (your Rust application), and how the Wasm module can also read and write to the same memory.

First we are going to want to initialize a new project. To do this we can navigate to our project folder, or create one. In this example, we will create a new project. Lets create it and navigate to it:

The final code for this example can be found on GitHub.

Please take a look at the setup steps for Rust.

cargo new exported-memory
cd exported-memory

We have to modify Cargo.toml to add the Wasmer dependencies as shown below:

[dependencies]
# The Wasmer API
wasmer = "1.0"

Now that we have everything set up, let's go ahead and try it out!

Before you go further in this example, it is recommended you read the example on the basics of interacting with guest memory:

Reading the memory

The guest module exports a bunch of things that will be useful for us:

  • A memory exported as mem;

  • A function to retrieve information about the memory contents exported as load.

The load function will return two things: the offset of the contents and its length. Let's see how we can use that to read the memory.

let load = instance
    .exports
    .get_native_function::<(), (WasmPtr<u8, Array>, i32)>("load")?;

let memory = instance.exports.get_memory("mem")?;

When importing the function we tell Wasmer that it return a WasmPtr as its first return value. This WasmPtr is what will give us the ability to interact with the memory.

let (ptr, length) = load.call()?;
println!("String offset: {:?}", ptr.offset());
println!("String length: {:?}", length);

Now that we have our pointer, let's read the data. We know we are about to read a string and the load function gave us its length. We have everything we need!

let str = ptr.get_utf8_string(memory, length as u32).unwrap();
println!("Memory contents: {:?}", str);

Done! We were able to get a string out of the Wasm module memory.

Writing to the memory

It's now time to write a new string int the guest module's memory. To do that will continue using our pointer.

We could have just changed a single word in the old string. To do that we would have changed the offset and length of the slice when dereferencing the pointer. Here is how we would have done the dereferencing part if we wanted to only change the "World!" part:

let new_str = b"Wasmer!";
let values = ptr.deref(memory, 7, new_str.len() as u32).unwrap();

Running

We now have everything we need to run the WASM module, let's do it!

If you want to run the examples from the Wasmer repository codebase directly, you can also do:

git clone https://github.com/wasmerio/wasmer.git
cd wasmer
cargo run --example exported-memory --release --features "cranelift"

Last updated