Hi, I have a basic C program compiled with emcc which just prints a hello world. Running it through the wasmtime runtime causes absolutely no problem, but running it in Rust using wasmtime and wasmtime-wasi (0.21.0) causes the execution to return an error "Error: failed to invoke command default, Caused by: Exited with i32 exit status 0" (code here: https://hastebin.com/vimenekuzo.rust). Running a similar program but compiled from Rust works in both cases.
I digged into the runtime code and I see no major difference between the techniques I used and the techniques used in wasmtime. Has anyone any idea why this isn't working as intended?
Hi Lucas. Currently a program that calls the WASI exit function will trap with a special trap that signifies an exit. Your func.call
is returning an error because of the trap and it's printing the message you're seeing. For Wasmtime, we check to see if the trap is an exit trap and simply exit the process accordingly (see here: https://github.com/bytecodealliance/wasmtime/blob/c9a3f05afd45961b0b397f97c4ad79cd7a7c807d/src/commands/run.rs#L165-L172)
@Peter Huene seems like I missed this bit of code while researching, thanks for the help!
Hope that helps! Let us know if you run into any other issues with the API.
I wonder if it might make sense to handle this more gracefully by default when embedding Wasmtime, too. Are there scenarios where not treating a WASI exit with status 0 as successful completion would be expected to cause issues?
The problem, currently, is that _start
doesn't return anything, so the only way to successfully get the above func.call
to return success is to have _start
return without calling into WASI to exit (which I believe it won't since it'll always call proc_exit
). Given the export signature, we can't translate an exit trap internally into a return value.
so perhaps a new API method for invoking a WASI module specifically?
i.e. one which would handle the check for an exit trap and translate it to the exit status.
Erm misread the _start
code; it should return provided the original main returns zero (but obviously it won't if the program calls proc_exit itself).
we could simply translate an exit trap with status 0 to Ok(_)
for the invocation of _start
(with a known signature) such that it won't return an error otherwise users have to check the trap for the exit code. that would make it slightly more ergonomic although it feels brittle to me.
At any rate, to answer your question (finally), I don't see a problem with doing such a translation, other than the brittleness (i.e. "is the function a known WASI entry point? If so, ignore exit traps with status 0 and return Ok")
This reminds me, I need to update the .NET API to allow for getting the exit status from the trap.
Is there a real risk of misinterpreting something unrelated as a WASI exit trap with status 0 and that other thing being an error that we should surface?
i don't think we'd mask any errors by handling an exit trap with status 0, since by convention it isn't an error condition. As Trap::i32_exit
is public, any host function, not just proc_exit
, may potentially exit trap. still, the more I think about this, the more I'm inclined to say we would ideally have something in the API that differentiates between invoking a "default" function (like what we have here) and "running a program that exits and treat non-zero as error"
hmm, yeah, I see the value in that. At the same time I'm also slightly concerned by the potential for confusion. Will all consumers of the API always know which is the right function to use?
i think we can make the distinction pretty clear; perhaps rather than something on Func
for a different way to call the function, something on Linker
for "invoking the default function of a module" that will both ensure the signature of the default function accepts no arguments (for now) and returns no values and does the exit trap translation for an exit status
said function could even return Result<i32>
and never fails with an exit trap, perhaps (caller can then just see if the command succeeded by comparing the successful result to 0?). sort of like how you would waitpid
interesting yeah, that seems reasonable, though I don't feel like I actually understand things in this area well enough to say for sure. And it seems like this starts getting into the space of different types of modules, similar to the distinction between Commands and Reactors. Might it make sense to file an issue describing this, and getting feedback?
I just ran into pretty much the same issue described here (see https://bytecodealliance.zulipchat.com/#narrow/stream/217126-wasmtime) and, even though I had read the code @Peter Huene highlighted above (https://github.com/bytecodealliance/wasmtime/blob/c9a3f05afd45961b0b397f97c4ad79cd7a7c807d/src/commands/run.rs#L165-L172) I still forgot to check for exit traps. I would vote for adding something, to the linker perhaps, to help out with this and then document this pattern.
I think the Wasmtime book currently tells us to use linker.get_default("")?.get0::<()>()?()?;
(https://docs.wasmtime.dev/examples-rust-wasi.html) but I think that will run into the issue we're talking about here.
Last updated: Dec 23 2024 at 13:07 UTC