Open ospencer opened 4 years ago
We don't have a lot of documentation for this yet, but here's a quick overview:
WASI-using applications export a function named "_start", which is expected to be invoked once the module and all its dependencies are instantiated. This runs the program (it calls the user "main" function), and when it returns, the program is considered to have exited. At program entry, WASI programs expect to have file descriptors 0, 1, and 2 open, behaving like POSIX standard input, output, and error file descriptors. File descriptors starting at 3 and counting up may also be provided, representing pre-opened directories.
There's also work going on to support modules that don't operate in terms of a "_start" function, but it's not very polished in the toolchains yet.
WASI-using programs also export their linear memory with the name "memory".
WASI-using applications then import WASI functions. The current snapshot describing which functions are avilable to be imported is here: https://github.com/WebAssembly/WASI/blob/master/phases/snapshot/witx/wasi_snapshot_preview1.witx . Note that while this snapshot (version 1) is stable, the WASI community will be publishing new snapshots over time as WASI evolves.
A lot of the pieces are under development, and over time, many of the details above are likely to change. We hope to use the snapshot mechanism to also migrate to new mechanisms as they become available, while still providing levels of stability for people to target.
Some parts of this are documented in https://github.com/WebAssembly/WASI/blob/master/design/application-abi.md
Thanks for the pointers; this was really helpful.
I hadn't realized that wasmtime doesn't yet support external imports, so it might be a little while before we can go through pure wasm—but I was able to get the language to make some WASI calls via wasmer!
I believe with the rest of the documentation, we've got enough to get going. I appreciate the help!
Can you say more about what you mean by "external imports"? There are a lot of different but related use cases in this space, and I'm interested in how not just Wasmtime, but also WASI and the tooling ecosystem can better support the kinds of things people want to do.
Sure. It's pretty much this line from https://bytecodealliance.github.io/wasmtime/cli-options.html#run:
The wasmtime CLI will automatically hook up any WASI-related imported functionality, but at this time if your module imports anything else it will fail instantiation.
In Grain, we compile all modules to separate wasm files. So if you wrote this program:
import List from 'lists'
List.map(n => n + 1, [1, 2, 3])
the generated wasm would look like
(module
(import "lists" "map" (func ...))
...
)
and then our module loader finds and loads all of the imports for the module before instantiating it, with those imports.
Wasmtime doesn't (yet?) have an option to specify any wasm files available for import, with something maybe like wasmtime file.wasm -I imported1.wasm,imported2.wasm
, so we couldn't run this wasm file using wasmtime.
From the wasmtime guide it looks like Rust/C users can get around this by writing a wrapper program that manually loads a module's imports and then instantiates it (using WASI too—that's super cool!) so they end up with a wasm file that doesn't import anything (and thus can be run with wasmtime), but still loads separate, imported files.
We effectively end up providing this glue with our runner/runtime (just written JS instead). We could do a similar thing, but it'd be nice to keep users from having to see/write code that deals with module loading/instantiation.
It sounds like you are asking about load-time dynamic linking of different wasm modules.
That is certainly something that we are hoping to specify one day but we are not there yet. See https://github.com/WebAssembly/tool-conventions/blob/master/DynamicLinking.md
for how emscripten currently does this.
If I am right and If you are asking about something along the lines of traditional load-time dynamic linking (a la ls.so
) where the different modules are essentially part of the same program ("shared everything linking" as we are starting to call it) then a stopgap solution for now might be to use the static linker (wasm-ld) build one big program (think of it as building against static libs rather than shared objects). Is there any fundamental reason you can't do this today?
Actually I just realized to are writing your own compiler so you are probably not going through the static linker at all. In that case you might find this document useful: https://aardappel.github.io/lobster/implementation_wasm.html. It describes how the language author ported his compiler to produce object files the wasm-ld could then link (for example his runtime library is written in C++ and he links that directly with the compiled output).
Includes a single-header implementation of a wasm object file streamer: https://github.com/aardappel/lobster/blob/master/dev/src/lobster/wasm_binary_writer.h
Yeah, dynamic linking is exactly what I'm talking about, and it's good to know that we'll get there one day. In the meanwhile, we could probably make some changes to get wasm-ld working with our modules. That seems like the best route for us to run on more runtimes.
@sunfishcode
There's also work going on to support modules that don't operate in terms of a "_start" function, but it's not very polished in the toolchains yet.
This sounds extremely interesting. Are you able to point to an issue that I can track to follow along with this work, as I am extremely interested in "main-less" WASI modules?
The term we're currently using for main-less wasm modules is "reactor". See here for the current basic design. If you're using a new enough wasi-sdk, you should be able to compile with -mexec-model=reactor and it'll produce a main-less program. Things are still a little rough right now, but the basics are in place, so if you find something that isn't clear or isn't working, please report a bug!
Hi! I'm a dev over on https://github.com/grain-lang/grain. Grain is a language that primary compiles to WebAssembly, and we want to start transitioning to become WASI-compliant.
We're mostly looking to know the bare minimum for us to get started—how WASI-compliant modules are loaded (how imports are handled, what export from a module is invoked on instantiation (if any)) and how wasm modules access WASI values (if functions/etc look like any other import, or if some wasm magic happens).
If there's any relevant documentation you could point us to, that'd be fantastic; otherwise it'd be greatly appreciated if you could give us a quick primer! 🙏🏽