Hi, is there a good way to read and write the full linear memory of a core module in the component model from the wasmtime host? For context, I am using Binaryen's Asyncify on the core module. My goal is to make a snapshot of the linear memory when unwinding to stop the runtime and resume execution later. I got this working with wasm32-wasip1. I am now adapting the code to use wasm32-wasip2, to import richer host functions. But I'm struggling to get access to the linear memory of the core module, since it's not exportable to the wasmtime host.
For now, the answer is unfortunately no: the intent of the host API design is to enforce the same encapsulation that components provide at the Wasm type level, so if a memory is not exported, it's not reachable.
The component-init tool (now folded into Wizer I think?) has some tricks to work around this, for the same purpose (snapshotting), that definitely involved rewriting with additional exports but I'm not sure how it actually allowed external access (injecting accessors? something else?). @Joel Dice would probably be able to say more
Yes, Wizer now supports snapshotting components, using the same trick component-init did: adding an extra function which returns a list<u8> that covers the entire linear memory, then creating a new component as output with a data section containing that snapshot. It also adds accessor functions for each mutable global variable so it can snapshot those, too.
See the README.md and/or source code for details.
Perfect! I'm happy to transform the wasm code a bit if needed (some transforms are already needed to make Asyncify and the component model play nicely together). Is there any performance hit by using list<u8>?
I was hoping the runtime was allowed to bend the rules a bit. Like if it a component instance could give access to an underlying core module instance, similar to what you get without components (if there even is one?).
Jonas said:
Is there any performance hit by using
list<u8>?
In the case of component-init, yes; it requires copying the whole memory into a Vec<u8>. One of my goals was to make it runtime-agnostic (i.e. not depend on Wasmtime directly), so I couldn't use any Wasmtime-specific APIs. Otherwise, I would have used WasmList, which gives you direct, read-only access to the guest memory without any copies.
AFAIK, there's no public API in Wasmtime to get read or write access to any of the (possibly many) memories inside a component besides WasmList plus Wasm code transformation. It would be technically feasible to add something like that, but we'd need a strong justification for piercing the component abstraction.
Thank you! WasmList looks like what I need.
Btw, to check my understanding, are the mutable parts of a wasm module memories, globals and tables? And component-init does not support mutable tables yet (https://github.com/dicej/component-init/blob/2d96595716615426e26b4346003a29f557e2f31b/transform/src/lib.rs#L64-L69). Why are tables more complicated to support? (also, do you know which rust constructs compile to tables?)
component-init and Wizer support snapshotting memory and mutable globals. My understanding is that runtime table mutations are not possible to snapshot in general because funcrefs are not serializable; e.g. they could point to imported host functions which can't be saved and restored in any meaningful way. To my knowledge no Rust constructs compile to table mutations -- only to statically-initialized tables which are not modified at runtime.
Thank you for all the answers. I'll probably come back later with some more questions :)
I suppose in principle that one could add some more instrumentation to track funcref provenance -- e.g. a new Wasm memory with as many u32s as table slots in a given table, and instrument table.get/table.set/... to carry a shadow i32 alongside the funcref, and assign a unique ID to each initial funcref and to those that come from imports; then in the snapshotting output one could generate code to do the equivalent assignments to reproduce a given state. All that assuming that new funcrefs do not come in as return values from outside, only as taken-references to imports or Wasm functions. But all that's superfluous because (as Joel says) LLVM will use u32s as function pointers and never mutate the table they index
Last updated: Jan 09 2026 at 13:15 UTC