I talked with Thomas Lively about getting a new repo in the WebAssembly org. He was open to it, but pushed slightly back and asked why we can't just rename 'wasi-sdk'. I missed last weeks meeting, so I believe this was discussed more then and Alex strongly preferred not renaming it. Alex, what are your concerns again? I told Thomas I'd get back to him with more rationale.
That aside, there is the question of what to name the new repo. I wonder if it's possible to name the new repo to just 'component-sdk' and publish both new artifacts there. Then eventually when enough things are migrated, we could deprecate the 'wasi-sdk' repo. It feels like a nice goal to eventually have a single repo for all this code, and 'component' is generic enough to cover both.
I don't think 'web-sdk' would fly, it's too generic and we'd probably get pushback. 'web-component-sdk' collides with web components. So that would leave 'component-web-sdk' as a fallback.
@Alex Crichton @Ben Visness
My main worry was basically the volume of locations that refer to "wasi" or "wasi-sdk" that have to be updated to something else when the wasi-sdk repo is archived/renamed/etc. Some examples are:
#include <wasi/version.h> which is the primary vector for conditional compilation based on wasi versions (e.g. __wasip{1,2,3}__)For myself this was then all capped off with the realization that this would sort of continue to perpetuate the feeling of instability for WASI and such.
Overall though I very much agree that the long-term goal should be one place to get clang & co. I do not want a long-term solution where there's both a wasi-sdk and component-web-sdk for example.
tl;dr; I got cold feet and had second thoughts about renaming wasi-sdk
Maintenance of wasi-sdk is also pretty onerous right now insofar as every PR has like ~6 hours of CI because windows is slow and refuses to ccache correctly.
I'll definitely admit though that some of this is sort of selfish of me insofar as I suspect that a lot of the renaming work would fall on me and I'm not particularly eager to go do all that
but also I mean none of what I bring up is like a super strong objection, and it's definitely technically feasible to rename
Sure I get that, not trying to add unreasonable work here, especially with all the stuff going on.
It sounds like eventually renaming things over makes sense, but it will just take a while. I wonder if instead of renaming the wasi-sdk repo, we could just create component-sdk as a mirror that automatically follows along with wasi-sdk. We could keep issues/PRs on wasi-sdk. Eventually when things are migrated over, we could switch from component-sdk being the mirror to being the development target. Or alternatively start with wasi-sdk being a mirror that is eventually disabled.
My concern is to avoid fragmenting development here. I think the web.wit work and other things are going to need to happen in the same repo as the wasi target.
yeah IIRC that was the general conclusion, component-sdk would repackage wasi-sdk's artifacts (wasi-sdk would include wasm32-webp2) and would possibly include web.h and libweb.a or similar for a web-sdk-style-download
we should probably talk more in depth this week about web.h to hammer out a more concrete plan there
Ah okay. I had heard an idea around having component-sdk embed wasi-sdk as a submodule. I think a true git mirror would be cleaner, because I'm not sure what would actually live in the repo besides just the submodule
my rough assertion is that it's going to be best to not actually have parallel CI builds for anything since it takes so long to build everything
there's no problem adding wasm32-webp2 of course to wasi-sdk, so the actual production of things is of no concern, it's basically around the optics and where things are downloaded
that's where the thinking that (component-)web-sdk would repackage the wasi-sdk artifacts
although this also touches on distribution of web.wit/web.h b/c I was thinking that wasi-sdk wouldn't ever want to include that
another example was wasi-sdk not including bindings to wasi:http as an example
I guess it depends on how libc bootstraps some of its API's. I was imagining it'd need access to a small subset of things like console.log/etc. Definitely doesn't need many web API's. But a few
yeah building wasi-libc needs WITs for sure, but right now those bindings are internal to wasi-libc and aren't published
or, well, actually they are published, but not necessarily easily accessible
so wasi-libc would include browser.wit and bind things like performance.now or w/e for sure, but web.h is like "ok but if you actually do web development use this"
Yeah that makes sense. In that case, web.h doesn't need to live with wasi-sdk/component-sdk. We probably could hand-craft a minimal web-libc.wit that has just the basics and include that there.
partly this is where I think we should talk about web.h in more detail because personally I think it might make the most sense to not include that in a *-sdk download
but this has a large impact on developer experience though so we'll definitely want to make an actual decision about this
I would consider web.h to be the equivalent of a web-sys crate for C/C++ developers and not part of the "toolchain". It could live in the same repo as toolchain things but shouldn't be part of a toolchain release imo.
agreed yeah, although I'd be concerned about living in the toolchain repo insofar as CI for a toolchain is typically quite expensive whereas CI for web.h in theory should be pretty cheap
Depends on what triggers the CI, I guess?
it's true, and I'm perhaps biased a bit here, but most repos I work on operate on the model of everything that merges runs the full CI of the repo to ensure nothing gets out of sync
running a subset of CI checks makes sense for PRs IMO but when merging I personally don't trust filters much b/c they often miss subtle interactions
but overall what I'm thinking is we should nail down web.h distribution, and that might re-shift things to biting the bullet and renaming wasi-sdk
it also might not, iunno
I suppose development & distribution of web.h
I'm just torn between the idea of having a separate repo for browser.wit / web.h / other bindings artifacts (?) that can move at their own pace, and the idea of having a one-stop shop for the whole affair (incl. a natural place to centralize issues, since I imagine many might be cross-cutting)
Along those lines the issue trackers of wasilibc and wasisdk are quite blurred yeah and not great being separate
It might also be important to consider how interlinked web.h / web.a / etc. will be with the actual clang toolchains. If they need to remain in sync, then it might be best to release them together (sometimes releasing smaller updates to just web.h)
If web.h were a separate repo, I'd personally say that the produced artifact should be *.{h,c} files (maybe *.{hh,cc} too) rather than *.a, and that should keep compat pretty orthgonal b/c then users don't have to align targets or we have to deal with abis or anything like that, just #ifdefs in a few places
if web.h ships with the sdk though then that's all different, we'd be shipping libweb.a, but it'd also be a solved problem for clang compat then since it's only precompiled
Honestly, it would be great to precompile as little as possible and distribute all the bindings SQLite style (one header, one impl file). Is that feasible or do the modded toolchains need object files with mystery magic in them somehow?
oh that's true it'd also need the *.o that wit-bindgen generates
I thought I remembered something funny there
in theory it's it's just another "include this in your build" kind of file
what does that object file do again?
encodes the WIT in a custom section that wasm-component-ld later picks up to generate a component
WIT or component binary stuff?
elaborate on "component binary stuff"?
the object contains a custom section which is the WIT-encoded-as-wasm in wasm binary form, so sort of a wasm-component-in-a-custom-section-in-a-core-module
just was wondering if wasm-component-ld literally had to re-parse WIT or if it saw the component binary format
ah yeah, component binary in that case
Here we go!
https://github.com/WebAssembly/component-sdk
https://github.com/WebAssembly/component-web-bindings
So now that these repos exist, what are the general next steps? Presumably for component-sdk we want to add a wasm32-webp2 target (to align with the Rust name) but that work happens in wasi-sdk first, right? I doubt there's much to do in the component-sdk repo until we get that taken care of first.
And for the bindings, I assume we can just start adding some WIT and some scripts for generating language bindings?
The rough timeline in my head is:
component-web-bindings with *.wit *.wit to bootstrap the wasm32-webp2 target in wasi-libc.wasm32-webp2 to the list of targets to buildcomponent-sdk to (temporarily for now) pull artifacts from wasi-sdk, rename them/shuffle, and then republish on the component-sdk repothen later assuming everything goes well
Does it actually make sense to make wasi-libc's web target depend on an artifact from component-web-bindings? Ryan and I were thinking that something more minimal might make more sense, e.g. hand-writing bindings that do exactly what you need for printing, time, randomness, etc.
If you want, we could certainly do that in component-web-bindings and have an artifact for you to consume, but it seems like it would be nice one way or another to have some really concise bindings for that
I guess I'm also curious to know what that artifact actually needs to look like
in the long-term I'd like to manage it similar to the WASI WITs where the artifact that wasi-libc consumes is some sort of published WIT, but no need for it to depend on C/H files or such, that'll get handled by wasi-libc's own wit-bindgen steps
oh ok, so you literally only need WIT
in the meantime though if it's handwritten WIT I think that's totally fine, no need to jump to the end originally
yeah
and if it's easier to bootstrap by just copying/vendoring some files that's also fine IMO
just long-term I'd like it to be a curl-the-thing situation
well that seems like a good way for me to get my feet wet one way or another
Is there something I can build that will scream errors at me to guide me through this? Should I just take a crack at scaffolding a new target in wasi-libc somehow?
in theory the build steps should be cmake -S . -B build -DTARGET_TRIPLE=wasm32-webp2 -G Ninja && ninja -C build
and if you can get that working that's probably worth a PR heh
we shall see what happens
So...in this new world, we presumably don't have "command" or "reactor" interfaces at all anymore
but that seems to be the first thing I'm crashing into with libc-bottom-half
or would we exclusively be doing this "reactor"-style?
I'm really not clear on why libc would care about such things
yeah that'd be my hunch -- you'd basically not include crt1-command.o and would only have crt1-reactor.o in the sysroot
I'm making some progress but just slogging through cmake things. Trying to get to a point where it builds but literally everything is stubbed out to just trap.
makes sense yeah, and I figure that most functions are probably going to just return an error in the end
well, time to pivot right back to wit-bindgen
![]()
@Alex Crichton Could we cut a release of wasm-tools with getter/setter support?
I can do the wit-bindgen updates right away...hopefully pretty easy since they're mostly just sugar for functions once they're validated
awesome! thanks
What do I do with this new canonical_names parameter in wit-component? I'm having a hard time figuring out what it actually changes
but it's showing up in a lot of places
set it to false
Yan Chen will work on plumbing that orthogonally
of course now I find bugs in wasm-tools :upside_down:
well...I will hack around locally and hopefully avoid multiple minor patches
cue disney animals signing a song about the circle of life
Do we want any stdio in wasi-libc's webp2 mode? My gut says no, but then I look at all the occurrences of fprintf(stderr and just think...man, seems pretty stupid to ifdef every last one of them to console.error.
But certainly we don't have stdin and the browser console is only vaguely like stdout.
But also people will probably just want to printf.
we are making PROGRESS
![]()
through enough of the libc implementation to have linker errors for the various things I have stubbed out
this has been a very pleasant little exercise so far, figuring out how to implement all the libc stuff we care about on top of web APIs
[245/246] Generating sysroot/lib/wasm32-webp2/libc.a
mozilla@Bens-MacBook-Pro wasi-libc % echo $?
0
nice! FWIW I do think that stdio will want to get hooked up, at least stdout/stderr, as otherwise it's likely going to be a bit too overly onerous to port
So I have a big TODO comment in there about how to handle entry points, and the upshot is that I think we need something that is halfway between Reactor and Command
(eventually)
Basically, importing a module should be allowed side effects, just like top-level code in JS today. That would naturally map to some kind of "main"-ish function in a language like C. But when that function exits the program isn't done and nothing should be torn down. Also it probably would have zero args.
In my ideal world I imagine that in this reactor-plus-side-effects mode, the main function would get linked as a component-level start function with zero params and maybe at most a result<T> return value.
it might be worth seeing how Emscripten models this, and I agree that int main probably isn't the way to do this. The only thing I can think of is some sort of annotation which the toolchain slurps up, but that's not easy to implement AFAIK
Right, and also I think a main function is the natural choice for code that "just runs", especially since we're moving toward a world where you can basically put an entire app in one wasm file. Maybe we should actually just figure out how to support command-style linking (and always provide argc=0)
But also, if you are writing a game and your main function just goes into an infinite loop, idk how we're supposed to translate that to the web (something something JSPI?)
So, I think I have wasi-libc far enough along to start moving on to clang itself. What roughly do I have to do there? I imagine clone wasi-sdk and just add some patches to somehow teach it about wasm32-webp2 and the new webp2 sysroot?
(I don't think I'll actually attempt to merge my wasi-libc branch until I can validate that it works at all against a patched version of clang.)
yeah, the wasi-sdk build it split into "two cmakes" sort of where one builds clang and one builds the sysroot, so long as you're not changing clang you just need to build the sysroot where you specify your compiler as probably wasi-sdk-34
./ci/build.sh has everything combined together and you can tweak that script to run things locally
Claude is telling me a clang patch will probably be necessary in order to teach it of the existence of a webp2 "OS"
because of some jank about which paths it chooses to search
ah yeah that sounds about right
we currently model that with *.patch files which are applied to the submodule locally
it's a bit brittle
but you can just edit the submodule and make commits and worry about that at the end
ah sure, that sounds good
What is this config submodule within wasi-sdk? It's pulling from some gnu repo that isn't responding right now.
Cloning into '/Users/mozilla/Developer/WebAssembly/wasi-sdk/src/config'...
fatal: unable to access 'https://git.savannah.gnu.org/git/config.git/': Failed to connect to git.savannah.gnu.org port 443 after 4055 ms: Couldn't connect to server
fatal: clone of 'https://git.savannah.gnu.org/git/config.git' into submodule path '/Users/mozilla/Developer/WebAssembly/wasi-sdk/src/config' failed
it's... honestly I have no idea
it's some thing for ./configure or something like that i never understood
I keep forgetting to turn that into a git subtree because that remote is super unreliable
well I managed to build clang without it, so hopefully it's fine
it's not fine
I was able to do the initial set of clang updates, but I can't run the full wasi-sdk cmake unfortunately
do you have a local checkout of the config stuff that you could zip up and send my way, or something?
if you try it now does it work? that server randomly goes offline and comes back up
I have tried several times today with no success :/
maybe I'm just unlucky
no it's a bad server lol
I think that worked, thanks
thankfully adding the target triple was easy
Ben Visness said:
In my ideal world I imagine that in this reactor-plus-side-effects mode, the main function would get linked as a component-level start function with zero params and maybe at most a result<T> return value.
@Luke Wagner told me today that core wasm start functions should be sufficient for side effects, and that would probably remain compatible with the reactor approach generally. I remain a bit skeptical because what if the initialization work you want to do in some way depends on things that aren't yet instantiated in the component?
It feels like a start function should run once the entire component is instantiated, although I can't actually think of an example right now
oh that actually might be core-wasm-start vs the _initialize export -- start isn't a great fit because when you run it your module's imports aren't hooked up yet (e.g. the indirect adapters needed for components), but the _initialize function is a toolchain convention that's run as soon as everything's hooked up, and as part of instantiation
so _initialize is run by a module's start section, just not the "main module"'s start section
IIRC ctors in C/C++ go into _initialize, but I could also be misremembering
Alex Crichton said:
so
_initializeis run by a module'sstartsection, just not the "main module"'s start section
I don't quite follow, what's the difference? Is there one _initialize per core module in a component?
_initialize is a special core wasm export name recognized by wasm-tools component new (wit-component) which is handled specially by running it once the main module's imports are all hooked up
basically using start won't work b/c imports aren't hooked up (e.g. you couldn't call JS things), but using _initialize should work b/c imports are all hooked up
What do you mean imports aren't hooked up? Core wasm start functions are perfectly capable of calling imported functions, aren't they?
it can yeah, but for example if you instantiate a core wasm module on the web it's generally not helpful to call imports from start b/c you can't have access to the exported memory of the module (instantiation hasn't finished yet). So in a sense while you can call imports in start that's not super useful.
For components when I say "hooked up" I mean the thing where if you import a component function that needs memory (e.g. returns or takes a string) then you have to instantiate the main module with a trapping shim first, and then afterwards you fill in the shim such that future calls work (the call_indirect table stuff). So when the main module is instantiated it has imports and can call them, but if they're called they all trap. Only after instantiation is the "fixup" module instantiated, and that module fills in all the shims where all future calls succeed
Ah, earlier I was saying that it was possible to call imports from start that use memory in pure WAT, which is technically true, but I forgot that wasm-tools component new does the call_indirect stuff instead which happens after start. So yeah, I guess you do want the _initialize after all; but it's called at component-instantiation-time, as-if it was a start function (I'm guessing via some helper core module that imports _initialize and calls it from its own start function) right?
right yeah
Ok, that makes sense. I wasn't aware of _initialize but if that's already working for C++ constructors etc. then I don't see why it wouldn't work for general-purpose initialization too. Maybe we just need to designate how user code gets hooked up to _initialize.
double-check me on this, but I think C++ constructors in theory already "just work"
That said, would a component start function allow us to elide the helper module with a core start function?
it would not
(still need the shim/fixup module to break the import cycle)
Which import cycle is this?
canon lower needs a linear memory, the main module exports linear memory, the main module needs an import to be instantiated, the import is canon lower
That makes sense generally, but I don't think I've seen that in my experiments...maybe I need to look again
oh man you're finding all the cranks to turn aren't you
There are so many cranks
and yet Claude would turn them all faster if it could
I created a pre-push commit hook specifically to stop it from pushing on my behalf, and guess what it just did? It amended its commit to remove the Co-Authored-By line and pushed anyway
all I want is for it to rebase and then NOT push before I review it
Emscripten has support for not shutting down the program when main exits: https://emscripten.org/docs/tools_reference/settings_reference.html#exit-runtime
So, one interesting point of jankness: because I am making the web builds default to the reactor model, main doesn't get executed, so main actually gets dropped most of the time in linking if provided, which is fine except that it totally breaks CMake's own checks like check_library_exists
because apparently these functions just compile a tiny C file whose main calls a library function, and if it successfully links, it assumes the library exists
but if main gets dropped, linking can succeed by accident even if the library does not exist, which can lead to bad cmake config
I worked around this for the sysroot builds with:
-DCMAKE_EXE_LINKER_FLAGS=-Wl,--export-if-defined=main,--export-if-defined=__main_argc_argv
but it seems the same thing could affect general users of the webp2 target
And it seems quite wrong to define such flags by default for normal SDK users
...maybe this whole problem will just go away if I just hook up main to _initialize
I will say that the further the target diverges from looking like a posix-y thing the more readily issues like this are going to come up, this'll never be a fully posix-y target per se but some things will definitely be more major than others. Not that I know how to solve it per se, but we might want to brainstorm how best to keep at least the major things mostly posix-y
For those wondering, the validation bug is that Firefox is complaining about a version suffix when an external ID is present (really when any attributes are present on the import name)
we are instantiating!
![]()
and crashing headlong into the lack of stdout
all according to plan
printf!
![]()
printf that doesn't trap
![]()
(I suspect this code is quite slow since it's some pretty naive UTF-8 code. It would be relatively easy to detect long runs of ASCII using simd and memcpy them.)
Does anything in the component tooling run binaryen on the core modules inside the components?
Surely people in the wasi space must do something like this?
iostreams work but they bloat the component binary size to 3MB lol
Nothing currently does, no
The main reason IMO is that binary size matters far less in out-of-browser scenarios, not to the point of irrelevance but to the point that binaryen's wins haven't made it worth it to integrate. We've talked about possibilities in the past and have more-or-less never reached a state of "it's clearly worth it to do this".
makes sense
but, I might have to whip up a little wasm-component-opt wrapper
the closest equivalent is to extract every core module, optimize, then put the core modules back -- but nothing automates this yet AFAIK. Components give a much more fine-grained liveness analysis for core wasm imports/exports which is also the next thing to hook up, so just the most naive version is still kind of far from what yu'd want
mm, sure
you should talk to @Till Schneidereit who's actually worked on precisely that and has a version somewhere which is IIRC LLM-coded and unreviewed but likely suitable for at least testing
is there tooling yet that does that liveness analysis?
not in wasm-tools, no
and in general, probably not
I've done similar for core modules themselves already actually
https://github.com/bvisness/wasm-isolate
it's not super difficult insofar as it's mostly just "you didn't alias out the thing therefore its dead", so in theory not too ocmplicated
oh nice!
IIRC binaryen has a similar command for extracting a function, but not as comprehensive as this and I haven't used the wasm-opt version in quite some time
I forget what it does with imports and exports, it might just ignore those because I built it for testcase reduction
but overall it's really quite simple
and just uses wasm-tools' reencoding functionality
if such a tool existed, would it make sense to integrate into the Rust tooling?
I think that'd make a reasonable wasm-tools subcommand yeah
oh, I meant the Rust toolchain support
oh you mean wasm-opt?
I know some wasm toolchains mysteriously auto-detect wasm-opt and run it when optimizations are enabled
I can't say I like that but it is a thing they do
yes that's what clang does, and AFAIK most of us consider that a bug and I've at least personally and intentionally not added that to rustc
IMO wasm-opt should either unconditionally be run, or never run
well then let's not reverse that decision :)
but yeah in general there's no preexisting plan to add wasm-opt to rustc
I think it's fine as a post-processing step or as part of a "bundler"
tbh it would make perfect sense when slotted into typical web bundling workflows
waaaay more sense there than in the compiler imo
that's what I've felt myself yeah
what a wild suggestion
![]()
ezpz just write a browser
It would be cool if eventually we can just run headless Firefox in CI or something, but obviously today is not that day
or probably the SM shell would suffice
agreed yeah
are we really about to implement clock_gettime on top of Date.now() on top of clock_gettime on top of whatever wasi does for clocks on top of clock_gettime?
:turtle: :turtle: :turtle: :turtle: :turtle: :turtle:
I recently wrote my second fetch implementation in terms of wasi:http, which works in JCO's implementation on top of the browser's fetch implementation
I guess that's two fewer layers, so clearly we should compile wasmtime to JS so we can run it in StarlingMonkey, complete with wasi:http
wit-bindgen is now published and should be usable in wasi-libc
Great, will hopefully get that added soon. My mind is addled today for unknown reasons and I'm just trying to get through the testing stuff first.
the current test failure is hurting my head and I think might be my main function decision coming back to bite me already
it seems that, in a dynamic linking situation, the order of _initialize functions is currently determined by the order in which libraries were passed to wasm-component-ld (or wasm-tools component link or whatever)
oh are they all calling main?
well possibly main is running more than once, which seems very bad, but also the actual issue right now seems to be:
mainenviron is nulloh yeah that'd do it
it seems bad to depend on a particular order of _initialize
so probably I shouldn't try and "fix" it that way
one possible fix: undo the _initialize hack, go back to having crt1-command.o, use that for tests, but for the webp2 target it doesn't export anything it instead installs a static ctor
some ctor w/ like a really low priority
although hm I'm not sure that's right either...
bah
to clarify, the command model just exports a function called _start that the host is responsible for calling?
that's wasip1, for wasip2/3 it's a direct export of the component model wasi:cli/run function
oh I see
but still then the host will invoke that when it feels like it, it's not a start function
and then there's no need for separate _initialize?
kinda yeah, _initialize is just a convention between toolchain/wit-component really
interim fix is to probably just disable shared tests
longer-term we probably want changes in wit-component to support this, for example _initialize priorities or some sort of _initialize_low_priority symbol
although _initialize doesn't actually set anything up for libc.so it looks like?
it mostly exists to call __wasm_call_ctors but for libc.so that should be an empty function
oh goodness crt1-reactor.o is being linked into libc.so
which is why claude is badgering me about main running twice now
this is all so dumb
indeed libc.so has _initialize, which runs ctors:
(func $__wasm_call_ctors (;189;) (type 0)
call $__wasilibc_initialize_environ_eagerly
call $__wasilibc_init_ssp
)
and also calls __main_void again, which is clearly bad
that might be the main bad part -- calling main twice - initializing the environment twice I think should be ok insofar as it should be somewhat idempotent
well afaik the environment will only be initialized once because this code is owned by libc.so and therefore its version of __wasm_call_ctors is the only one that performs that
true
whereas __main_void is imported as "env" "__main_void"
although confusingly libc.so does have its own __main_void
(func $"#func203 __main_void" (@name "__main_void") (;203;) (type 8) (result i32)
block ;; label = @1
global.get $main
br_if 0 (;@1;)
i32.const 0
return
end
i32.const 1
global.get $__memory_base
i32.const 205888
i32.add
call $main
)
(which is _not_ the one that gets called inadvertently from _initialize, this is a different one that is called at a time unclear to me)
(actually this just ends up calling the imported __main_argc_argv, but it's not clear to me that anyone would ever call this particular __main_void?)
there's some comments in libc-bottom-half/sources/__main_void.c for that I think
so afaik that one's not called in libc.so typically
in theory the import should somehow be tagged as a weak import for the dynamic linking conventions to kick in thre
I think perhaps for a start I will pull away from running main in the reactor model and just tell people to use __attribute__((constructor)) for initialization / side effects. That will fix the "main runs twice" problem. Then we need to figure out how to keep cmake happy when it does its check_library_exists tests that require main to not be dropped from the executable...afaik we just need to ensure that it persists through linking, but not that it actually needs to be run. Then we will need to figure out how to rig up the test runner so that main does run, but presumably wasmtime can call an export or something.
oh! how about this --
_initialize, which calls maincommand.o will still want to run dtors too though, no?
the webp2 target kind of doesn't have the concept of destructors really though
so skipping that seems ok
I'm actually just not sure what your second point means, does crt1-command.o even have _initialize? Or just wasi:cli/run?
not currently, it's just wasi:cli/run right now, but we've got the #ifdefs to do whatever in that file
what I'm thinking is that it'd be a "special" _initialize
will wasm-ld / wasm-component-ld plug in _initialize if it sees it?
yes
I see
(that's how it works with reactors right now)
with your change every *.so tries to call main, but with this change only one will
b/c crt1-command.o is in theory only linked once
oh, because that's just a fundamental difference with -mexec-mode-command or whatever?
kinda yeah, it's basically a convention that only the "main program" gets crt1-command.o
and everything else, if anything, gets crt1-reactor.o
if we go back to command as the default for webp2, and even if we disable dtors, will that cause other problems with calling exports after "main" is finished?
in that situation, in theory at least, everyone's using a reactor
or, well, hm, nah well in any case no
we can construct it so it doesn't destroy state when main finishes in crt1-command.o
from looking at the code I imagine that's just skipping __wasm_call_dtors()
the rest is wasi-specific shutdown stuff we'd skip anyway
oh, I'm looking at cli/run anyway
yeah that sounds right to me
I forgot we'd have a new function probably?
convention with C is to add to the #ifdef soup (I jest)
but realistically this'd just extend the #ifdefs to define the function
and it'd be _initialize on webp2
right instead of _start or cli/run
and then dtors/exit would be only wasi targets
in the limit what we'd ideally want is that if there are meaningful destructors then the component would export some sort of toolchain convention for "call me to invoke destructors"
and that'd be opt-in-called if the embedding wanted to actually clean things out
but that's pretty niche and can be deferred for later IMO
so all this means I _can_ just keep using main for setup, huh
and cmake-probes should all work by default too
tests still have to hack around things to get wasi:cli/run exported but that's a wasm-tools compose bug
error: failed to encode a component from modules
Caused by:
0: library shared-compose-simple-resource-callee.wasm exports both `__wasm_call_ctors` and `_initialize`; expected at most one of the two
this is when building tests...probably this is fixable
I admit I don't know why __wasm_call_ctors is getting exported
yeah that seems... wrong
I guess this is just what -shared does
so the combination of dynamic linking and an explicit _initialize export is causing jank
hm there's something else going on here though -- what about this is webp2 specific?
explicitly exporting _start as _initialize
but for libraries they shouldn't be using crt1-command.o
like how it's exported as wasi:cli/run
ah hm
it could be leftover test framework jank
that error itself is coming from wit-component it looks like
I...I hate to do this, but can I just paste some claude output and you can tell me if it makes sense
please do lol
![]()
ah I kind of thought this was built-in cmake stuff, but I see it is not
hm... can you package this up and push it as a commit so I can repro/poke at it?
pushed
I feel we're probably very close one way or another, the whole library is building fine
what cmake command to repro?
here's my build script at the moment
#!/bin/bash
set -euxo pipefail
export CC=/opt/homebrew/opt/llvm/bin/clang
rm -rf build
cmake -S . -B build -G Ninja \
-DTARGET_TRIPLE=wasm32-webp2 \
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
-DBUILD_TESTS=ON
ninja -C build bindings-webp2-edit
ninja -C build
that export CC is probably a leftover :P
this is an organically grown free-range build script
bah I had to install a newer wit-bindgen
well first I reset --hard to your wasi-sdk branch by accident
error: no dependencies of component /home/alex/code/wasi-libc/build/p2web/test/compose-barge-in-caller.wasm were found
ok nvmd fixed it
well, got to the same error
oh it's -fvisibility=default
or not jk
well I deleted the call to __wasm_call_ctors and it's still showing up
ok so the issue seems to be that when wasm-ld is missing --entry=foo it just goes ahead and exports __wasm_call_ctors. That only became an issue with webp2 where it also exports _initialize. Passing --entry=_start seems to work because then wasm-ld doesn't auto-export __wasm_call_ctors.
There's one failing test with that where it directly references the environ symbol, and that's where things matter. Initialization of environ happens via a ctor, which means that libc's ctors must happen first, but the current test link order happens to run the main module's _initialize before libc's _initialize, and that means with the main module running main from _initialize it sees an uninitialized environ
https://github.com/WebAssembly/wasi-libc/pull/883/changes/3f7379698ff8f2403f8eebf7f9494e51fb35f0f6 is an attempt
tbh these tests are built in a bit of a weird manner so reordering things isn't the end of the world
Just as long as it doesn’t cause trouble for users, I guess
that's probably a bit TBD, but otherwise this I think is only related to dynamic linking which is already quite a reach goal for this iteration of webp2 I'd expect
Ok, aside from utf-8 stuff, I think wasi-libc is in good shape
did you have any particular desires for how the utf-8 stuff would be handled / merged with the existing validation in file_utils.c?
nah I mostly just wanted to flag it in case it duplicated things, how exactly it fits together I haven't thought too closely and would be happy to defer to you
I'm not sure there's a very sensible way to unify them because validating and sanitizing are rather different logically and also both are less than 100 lines of code
but maybe I can at least put them next to each other
that sounds reasonable yeah
ok utf-8 stuff is now done too
all the stdio was reworked to use the descriptor table, and now we support dup as well
nice I'll take a look probably tomorrow for review
@Alex Crichton, what would you think of continuing to report errors to the browser console using reportError (which generates a stack trace + clearly says "not supported yet") but also trying to implement APIs with EINVAL returns and the like?
Personally I'd prefer if that were gated behind some sort of -DFOO passed to CMake to make it conditional in the build of libc itself -- suitable for development of libc but not showing up for anyone else
but that sounds ok otherwise
Would the usual NDEBUG be acceptable?
or is that really for other things
personally I'd prefer a new one, and I'd frame it as not necessarily webp2-specific per se
something like -DLOG_UNSUPPORTED_CALLS=ON or somethign
and that'd just delegate to fprintf(stderr, "...")
on all platforms or just non-webp2 platforms? because I really do want stack traces
with a -D it's pretty flexible -- perhaps a trap on all platforms?
basically I think by-default things should be libc/posix-ish, and then ideally target-specific code is minimized, and then after that it's kind of whatever works
Yeah, that makes sense. Do you have any concerns about __wasilibc_report_error being consistently used for -DLOG_UNSUPPORTED_CALLS? I set that up early on in the wasi-libc work to just to go fprintf(stderr) on non-web platforms and reportError on web, which I feel gives the best ergonomics for each platform (as far as I know for wasi, anyway)
The other thing I wanted to mention is that I'm actually going through and auditing what Emscripten does for various APIs so we can have that as a point of reference. I suspect mirroring Emscripten's behavior is the past of least breakage for this component preview, although some of their decisions are Very Interesting (e.g. stdin pops a dialog box using window.prompt apparently)
yeah that I think makes sense to me, consistent __wasilibc_report_error with a target-specific definition
and yeah that sounds quite reasonable to audit against Emscripten!
whether or not we match everything perfectly... I think we could leave some of that to interpretation
Alex Crichton said:
personally I'd prefer a new one, and I'd frame it as not necessarily webp2-specific per se
Hey what do you know, there are already calls that can make use of this
![]()
wat
I had no idea...
Last updated: Oct 31 2026 at 23:03 UTC