Stream: git-wasmtime

Topic: wasmtime / issue #10156 List and access non-exported glob...


view this post on Zulip Wasmtime GitHub notifications bot (Jan 30 2025 at 10:54):

laurmaedje opened issue #10156:

Feature

I'd like to access (list/get/set) the value of globals that were not explicitly exported by the module.

Benefit

I'm trying to take a snapshot of a store's state, so that I can spin up a second instance that is behaviourally equivalent to the first one. Crucially, I don't want to snapshot in the middle of a function call, just at rest when the call stack should be empty.

I went through the WebAssembly instruction reference and as far as can tell, when I instantiate a new module, the only things I would need to restore would be the main memory size & data and the globals.[^1] I'm already snapshotting the memory (which is required to be exported in my case), but I can't snapshot the globals as far as I can tell.

As far as I'm aware, LLVM-based compilers will typically only have one internal global with a shadow stack pointer. Since I'm only snapshotting when the call stack is empty, I expect not snapshotting globals for now will not immediately break in practice, but it does not seem quite correct and of course not every WebAssembly module comes from LLVM. So it'd be nice if that use case was covered somehow.

Alternatives

An alternative that would be even better for my purposes is a direct snapshotting API as discussed in https://github.com/bytecodealliance/wasmtime/issues/3017. But I expect such an API to be _much_ harder to implement and the requirement might be that it also works with a non-empty call stack. The feature proposed here would be a minimal addition to implement snapshotting of an instance with an empty call stack manually.

Another option is that there already is a way to do this and I just wasn't able to find it.

Notes

Background info: My motivation for this is adding support for automic behind-the-scenes multi-threading for Typst's plugin system. Typst uses wasmi rather than wasmtime, but I'm proposing the feature here first, because as far as I can tell, wasmi's API mostly mirrors wasmtime's, so proposing it here first seemed more fruitful to me.

[^1]: I'm not taking any of the WebAssembly extensions into account here, which I don't intend to support at this time. I'm not sure whether they would add complication, but e.g. multi-memories might.

view this post on Zulip Wasmtime GitHub notifications bot (Jan 30 2025 at 11:05):

bjorn3 commented on issue #10156:

Wizer handles this by intrumenting (modifying) the wasm module to allow access to non-exported globals and memories: https://github.com/bytecodealliance/wizer/blob/main/src/instrument.rs

view this post on Zulip Wasmtime GitHub notifications bot (Jan 30 2025 at 11:13):

laurmaedje commented on issue #10156:

Thanks for the info! I saw the __wizer_global_{} in snapshot.rs, but didn't make the connection to instrument.rs, so was unsure how that worked.

I expect doing it this way would add quite a bit of dependency burden on top of wasmi (and also some performance overhead for doing this at runtime). However, as a last resort, it would indeed work.

view this post on Zulip Wasmtime GitHub notifications bot (Jan 31 2025 at 16:20):

alexcrichton commented on issue #10156:

Wasmtime doesn't support accessing unexported globals/functions/tables/etc, so purely using the wasmtime crate API you'll be unable to solve this goal. As @bjorn3 mentioned though what you're doing is very similar to what Wizer does and Wizer achieve this goal by modifying the wasm binary ahead-of-time.

If you're interested in doing that it may not be too too hard to implement this by using a combination of wasmparser and wasm-encoder, likely using the wasm_encoder::reencode module.

view this post on Zulip Wasmtime GitHub notifications bot (Jan 31 2025 at 16:31):

laurmaedje commented on issue #10156:

Thanks for the link! Is it an explicit decision to not support this or is it just currently not supported? If it's the former, I of course respect that and would then probably embark on the wizer-like route. Though I'd still be curious if there's any particular reason why it would be a bad idea to have such an API.

view this post on Zulip Wasmtime GitHub notifications bot (Jan 31 2025 at 16:39):

alexcrichton commented on issue #10156:

The former yeah, it's an intentional design decision to only give access to explicit exports. That enables opportunities for us to optimize more, for example.

view this post on Zulip Wasmtime GitHub notifications bot (Jan 31 2025 at 17:09):

laurmaedje closed issue #10156:

Feature

I'd like to access (list/get/set) the value of globals that were not explicitly exported by the module.

Benefit

I'm trying to take a snapshot of a store's state, so that I can spin up a second instance that is behaviourally equivalent to the first one. Crucially, I don't want to snapshot in the middle of a function call, just at rest when the call stack should be empty.

I went through the WebAssembly instruction reference and as far as can tell, when I instantiate a new module, the only things I would need to restore would be the main memory size & data and the globals.[^1] I'm already snapshotting the memory (which is required to be exported in my case), but I can't snapshot the globals as far as I can tell.

As far as I'm aware, LLVM-based compilers will typically only have one internal global with a shadow stack pointer. Since I'm only snapshotting when the call stack is empty, I expect not snapshotting globals for now will not immediately break in practice, but it does not seem quite correct and of course not every WebAssembly module comes from LLVM. So it'd be nice if that use case was covered somehow.

Alternatives

An alternative that would be even better for my purposes is a direct snapshotting API as discussed in https://github.com/bytecodealliance/wasmtime/issues/3017. But I expect such an API to be _much_ harder to implement and the requirement might be that it also works with a non-empty call stack. The feature proposed here would be a minimal addition to implement snapshotting of an instance with an empty call stack manually.

Another option is that there already is a way to do this and I just wasn't able to find it.

Notes

Background info: My motivation for this is adding support for automic behind-the-scenes multi-threading for Typst's plugin system. Typst uses wasmi rather than wasmtime, but I'm proposing the feature here first, because as far as I can tell, wasmi's API mostly mirrors wasmtime's, so proposing it here first seemed more fruitful to me.

[^1]: I'm not taking any of the WebAssembly extensions into account here, which I don't intend to support at this time. I'm not sure whether they would add complication, but e.g. multi-memories might.

view this post on Zulip Wasmtime GitHub notifications bot (Jan 31 2025 at 17:09):

laurmaedje commented on issue #10156:

Okay thanks for all the info!


Last updated: Feb 28 2025 at 03:10 UTC