Problem
I've got wasmtime
up-and-running, calling a TinyGo WASM/WASI module from a Rust host. All is well until I try to return a string from the Go WASI module, which seems like something everyone struggles with. I understand the concept of accessing the WASM module's memory at a particular location and reading for a particular length; what I don't understand is how to do that with an offset
instead of a pointer.
I'm thinking clarification of wasmtime
's own example from their docs may point me in the right direction:
use wasmtime::{Memory, Store, MemoryAccessError};
fn safe_examples(mem: Memory, store: &mut Store<()>) -> Result<(), MemoryAccessError> {
let offset = 5;
mem.write(&mut *store, offset, b"hello")?;
let mut buffer = [0u8; 5];
mem.read(&store, offset, &mut buffer)?;
assert_eq!(b"hello", &buffer);
assert_eq!(&mem.data(&store)[offset..offset + 5], b"hello");
mem.data_mut(&mut *store)[offset..offset + 5].copy_from_slice(b"bye!!");
Ok(())
}
Questions
- What is
offset
? My impression is that it is NOT a pointer address, but a usize offset from the beginning of the WASM module's memory. - Assuming that is correct, how do I get the offset of a particular variable? I see plenty of examples where a random value (say
5
or10
is used), but in my own examples, anything greater than 0 segfaults. I think I may be misunderstanding whatoffset
is. - Does shared WASM memory need to be allocated by the host? My assumption has been that the WASM module itself would expand its own memory naturally (as it would if running natively). If I have to allocate on the host, how can I be sure of how much memory to allocate, if it's the WASM module that is creating the variables that use the memory?
Regarding your questions
offset
is an offset from the beginning of the WASM memory. It is like a pointer inside the WASM memory. Trying to accessoffset
as a normal pointer inside the host Rust application will likely result in a segfault.malloc()
function (more on that later). For every allocation, you need to know how many bytes you need.How to read a string from a TinyGo WASM module
(ptr, len)
tuple when crossing the WASM boundary [0, 1].(ptr, len)
tuple, it asks you to pass a pointer to a free memory segment/buffer where it can store the(ptr, len)
tuple. Becauseptr
andlen
are of typei32
, you need to pass an 8-byte buffer.malloc
function of the module.(ptr, len)
tuple from the WASM memory.[ptr..ptr+len]
from the WASM memory and convert the bytes to a Rust String.A simple example:
Compile it to WASM using TinyGo:
tinygo build -o return_string.wasm -target wasm ./return_string.go
Output: