Stream: ideas

Topic: wasm without main


view this post on Zulip Oskar Hahn (Jun 20 2023 at 16:22):

Currently, a platform that is used to build a wasm file has to have a zig-main-function. Without it, you get an error like lib/std/start.zig:546:45: error: root source file has no member called 'main'

But the main-function is not needed. A "normal" platform needs the main function to start. But the wasm file gets called from a wasm-runtime that can call any exported function.

Brian Carroll said:

A good mental model here is that the wasm program is a dynamic library, not an executable. You don't want a main because the main is in the browser or node. That runs the event loop which enters JS, which calls Wasm. So Wasm is not really an entry point.

To remove the main function, zig has to be called with build-lib instead of build-exe and it also needs the argument -dynamic. Here is an example: https://github.com/roc-lang/roc/compare/main...ostcar:roc:wasm_without_main

view this post on Zulip Anton (Jun 20 2023 at 17:23):

I think for wasi we do need build-exe, so it seems like we should start using the currently unused _target: &Triple arg (see link_wasm32 fn) and add an if based on that.

view this post on Zulip Brian Carroll (Jun 20 2023 at 18:47):

Yeah that makes sense to me

view this post on Zulip Anton (Jun 21 2023 at 11:24):

I made #5585 for this

view this post on Zulip Brian Carroll (Jun 21 2023 at 12:49):

How do we specify that triple from the command line? As far as I know we only have --target=wasm32, and nothing to indicate WASI

view this post on Zulip Anton (Jun 21 2023 at 13:38):

Sounds like we should add a wasm32-wasi target flag

view this post on Zulip Richard Feldman (Jun 21 2023 at 13:43):

I don't think we should need it long-term (but maybe we do short-term)

view this post on Zulip Richard Feldman (Jun 21 2023 at 13:44):

because in the future I think we want to infer whether something is executable or not by looking at the precompiled host (and seeing if it's an executable binary or not)

view this post on Zulip Brian Carroll (Jun 21 2023 at 15:54):

Ok in Wasm I think the only way to do that is to parse the headers and check for an export called _start

view this post on Zulip Brian Carroll (Jun 21 2023 at 15:56):

(When the standard was defined, it was for the browser use case, and there was no concept of executables.)

view this post on Zulip Oskar Hahn (Jun 22 2023 at 17:13):

It seems, the entry point for wasi will be renamed from _start to run in preview_2. I did not find any other source then this comment: https://github.com/WebAssembly/WASI/issues/19#issuecomment-1589803513

Another possibility would be to always use build-lib. If a platform author wants to support wasi, there just has to be an exported function _start() (or run() in preview 2). This example works without a main function: https://github.com/roc-lang/roc/compare/main...ostcar:wasm_without_main

view this post on Zulip Anton (Jun 22 2023 at 17:25):

Thanks for looking into this @Oskar Hahn! I'm no wasm expert but that seems like a good solution to me

view this post on Zulip Brian Carroll (Jun 22 2023 at 18:09):

Great idea!
However it does break with conventions a bit, and to a platform developer it might feel like a rough edge of Roc. I wonder, where can we document that this is needed?

view this post on Zulip Brian Carroll (Jun 22 2023 at 18:15):

What happens if we always use build-lib but the platform author defines main as per the usual convention? Does it "just work"? It would be great if it did.

view this post on Zulip Brian Carroll (Jun 22 2023 at 18:17):

(I'm on mobile right now, can't check)

view this post on Zulip Brian Carroll (Jun 22 2023 at 18:19):

I do remember though that the linker actually generates a _start to call main if it's not already present.

view this post on Zulip Brian Carroll (Jun 22 2023 at 18:19):

I actually had to implement this in the Wasm Dev backend to get it to work with WASI.

view this post on Zulip Brian Carroll (Jun 22 2023 at 18:23):

So I'm hoping that the platform author can write main, we can compile it with build-lib, and everything just works. Because lib vs exe is a pretty meaningless distinction in Wasm.

view this post on Zulip Oskar Hahn (Jun 23 2023 at 06:36):

It seems, that with build-lib, that no _start function will be added. For the example to work, I had to add the start function manually: https://github.com/roc-lang/roc/compare/main...ostcar:wasm_without_main

Is this something roc can fix or is this something, zig has to do?

view this post on Zulip Brian Carroll (Jun 25 2023 at 18:11):

_start just contains a single call instruction to main.
Roc should be able generate that if we make it a special case for this target and just insert the appropriate IR.
But then if there's no main in the host, it would probably fail to link.
So we'd need to know this about the host somehow.
And if we know that, then why didn't we just call build-exe?
:thinking:


Last updated: Jun 16 2026 at 16:19 UTC