Hi folks, in hopes of getting rid of some of the panic machinery in std, I've been referring to the awesome min-sized-rust guide.
The project I'm trying to build uses rquickjs and targets wasm32-wasi
.
I'm not at all familiar with the toolchain involved here and find myself quite stumped by this error. I haven't been able to find anything on Google (that I understood) to explain what's going on and how to fix it.
cargo +nightly build -Z build-std=std,panic_abort -Z build-std-features=panic_immediate_abort --target wasm32-wasi --release -p quicky-wasm
error: linking with `rust-lld` failed: exit status: 1
|
= note: LC_ALL="C" PATH="<REDACTED>" VSLANG="1033" "rust-lld" "-flavor" "wasm" "--rsp-quoting=posix" "--export" "lodash" "--export" "wizer.initialize" "-z" "stack-size=1048576" "--stack-first" "--allow-undefined" "--no-demangle" "<REDACTED>/quicky/target/wasm32-wasi/release/deps/quicky_wasm.quicky_wasm.471d786854812ccd-cgu.0.rcgu.o" "-L" "<REDACTED>/quicky/target/wasm32-wasi/release/deps" "-L" "<REDACTED>/quicky/target/release/deps" "-L" "<REDACTED>/quicky/target/wasm32-wasi/release/build/rquickjs-sys-b4b653f5d54f8d95/out" "-L" "<REDACTED>/.rustup/toolchains/nightly-aarch64-apple-darwin/lib/rustlib/wasm32-wasi/lib" "/var/folders/cd/x5jxg5sn0ws55pk2ptz8qz9r0000gp/T/rustcSPiY3m/librquickjs_sys-e42df950feb7a650.rlib" "-l" "c" "<REDACTED>/quicky/target/wasm32-wasi/release/deps/libcompiler_builtins-dc1c01caff7cab16.rlib" "-L" "<REDACTED>/.rustup/toolchains/nightly-aarch64-apple-darwin/lib/rustlib/wasm32-wasi/lib" "-L" "<REDACTED>/.rustup/toolchains/nightly-aarch64-apple-darwin/lib/rustlib/wasm32-wasi/lib/self-contained" "-o" "<REDACTED>/quicky/target/wasm32-wasi/release/deps/quicky_wasm.wasm" "--gc-sections" "--no-entry" "-O2" "--strip-all"
= note: rust-lld: error: unable to find library -lc
Does this sort of error look familiar to the experts? Any advice on fixing or diving deeper?
I haven't tried this myself, but you might need to install wasi-sdk
and then set e.g. RUSTFLAGS="-L /opt/wasi-sdk-sockets/share/wasi-sysroot/lib/wasm32-wasi"
. I _think_ Rust usually bundles a copy of wasi-sdk
as part of the toolchain, but since you're building std
from source maybe it's not used? Just guessing here.
Joel's right, when using -Zbuild-std
with wasi targets you'll need to provide link flags to find the wasi sysroot so wasm-ld
can find libc.a
. That file is typically distributed at $(rustc --print sysroot)/lib/rustlib/wasm32-wasi/lib/self-contained/libc.a
Interesting, so basically that's because the libc
for wasi is totally distinct, right?
indeed!
Is it typically distributed as a binary like rust's own std or am I 'already' compiling from source?
Yes, when you rustup target add wasm32-wasi
it'll download libc.a
which is packaged along with libstd-*.rlib
So the same feature flags will work with the wasi-sdk
right? And that would be because rust's stdlib is where the flags live and it links against the sysroot libc?
The rust feature flags won't have any effect on libc itself, no, but you can compile wasi-sdk/wasi-libc with custom flags
You're running into one of the major weaknesses of -Zbuild-std
which is that while it can easily compile Rust code it can't compile supporting code for all targets, which for wasi includes wasi-libc
Oh I see; is that one of the outstanding hurdles preventing it from landing in stable?
As someone who (currently) only uses rust for WASM, I kinda wished #[no_std]
was the _default_ and you had to opt into (a) libc.
I believe so yeah, although I might analogize it to you've got an entire track meet's worth of hurdles in front of you and this is one of them on the road to stabilization
It turns out that Rust's wasi suppot in the standard library only relies pretty lightly on libc, so it might be reasonable to remove some integration points on an opt-in basis
for example have a build mode that doesn't use wasi-libc
although these sorts of situations are also great opportunities to improve wasi-libc as well
Is it possible to supply libc stubs when authoring rust code (in the codebase itself) so that the compiler doesn't implicitly reference them from elsewhere?
Technically, yes, you could write a bunch of #[no_mangle]
symbols, but that'll be brittle because the surface area libstd uses from wasi-libc isn't guaranteed to be a stable set
So if you define symbols like that, what happens? It prevents the linker from looking for them elsewhere?
No you'd get conflicts and linker errors if they were also defined elsewhere
otherwise should work ok
In rquickjs
' build.rs
file, they dynamically pull a copy of the wasi-sdk (https://github.com/DelSkayn/rquickjs/blob/master/sys/build.rs#L13). If I have an alternative copy of the wasi-sdk
that I use to build my own lib, how does that work with respect to deduplication in the final binary?
I know for Javy, the team allows a custom wasi sdk via WASI_SDK
env var. Is that the sort of approach that would be favoured so that both c and rust code can link against a single wasi libc?
(I'm patching together a loose understanding of how compiling and linking work and might have a fundamentally wrong mental model.)
The precise answer I think depends on exactly what the final linker invocation looks like which is tough to predict. You might get symbol conflicts or everything might work out, it all depends. I'd recommend setting WASI_SDK
and using that for both quickjs and linking as that's probably the most robust
I've spent some time banging my head against this and feel like I might be in too deep. I've lifted some of the logic from rquickjs (lifted from javy: https://github.com/DelSkayn/rquickjs/blob/master/sys/build.rs#L199). I dumped that in my build.rs
and set WASI_SDK
to the resulting path.
fn main() {
if env::var("CARGO_CFG_TARGET_OS").unwrap() == "wasi" {
let wasi_sdk_path = get_wasi_sdk_path();
if !wasi_sdk_path.try_exists().unwrap() {
panic!(
"wasi-sdk not installed in specified path of {}",
wasi_sdk_path.display()
);
}
env::set_var("WASI_SDK", wasi_sdk_path.to_str().unwrap());
}
}
I'm not actually sure if that's 'working'. The idea is that the WASI_SDK
env var is understood by rquickjs
's own build script. My thinking is that I can then instruct rquickjs
and std
to use that same instance.
But what I can't get working is how to get rid of the linker paths that seem to be injected by cargo and instead use 'the paths' (not sure which) in this downloaded sdk.
The linker seems to be run link this:
LC_ALL="C" PATH="<REDACTED>" VSLANG="1033" "rust-lld" "-flavor" "wasm" "--rsp-quoting=posix" "--export" "wizer.initialize" "-z" "stack-size=1048576" "--stack-first" "--allow-undefined" "--no-demangle" \
"$PROJECT_ROOT/js-rs-wasm-bridge/target/wasm32-wasi/release/deps/js_rs_wasm_bridge.js_rs_wasm_bridge.238c6f55f859b731-cgu.0.rcgu.o" \
"-L" "$PROJECT_ROOT/js-rs-wasm-bridge/target/wasm32-wasi/release/deps" \
"-L" "$PROJECT_ROOT/js-rs-wasm-bridge/target/release/deps" \
"-L" "$PROJECT_ROOT/js-rs-wasm-bridge/target/wasm32-wasi/release/build/rquickjs-sys-f16313b4d9f51e5b/out" \
"-L" "$HOME/.rustup/toolchains/nightly-aarch64-apple-darwin/lib/rustlib/wasm32-wasi/lib" "/var/folders/zw/14fs16lx58z4xs0t1kkgrls00000gn/T/rustcIK3zuh/librquickjs_sys-4622d9f4f719ba74.rlib" \
"-l" "c" "$PROJECT_ROOT/js-rs-wasm-bridge/target/wasm32-wasi/release/deps/libcompiler_builtins-5cd41716d77b1ead.rlib" \
"-L" "$HOME/.rustup/toolchains/nightly-aarch64-apple-darwin/lib/rustlib/wasm32-wasi/lib" \
"-L" "$HOME/.rustup/toolchains/nightly-aarch64-apple-darwin/lib/rustlib/wasm32-wasi/lib/self-contained" \
"-o" "$PROJECT_ROOT/js-rs-wasm-bridge/target/wasm32-wasi/release/deps/js_rs_wasm_bridge.wasm" "--gc-sections" "--no-entry" "-O2"```
I've discovered that I can inject -L
args via cargo:rustc-link-arg
but injecting -L ${WASI_SDK}/share/wasi-sysroot/lib/wasm32-wasi
hasn't changed the error :(
I got it working with this it seems:
println!(
"cargo:rustc-link-search={}",
wasi_sdk_path
.join("share/wasi-sysroot/lib/wasm32-wasi")
.display()
);
A problem remains that it seems like my lib's build.rs
runs well _after_ the rquickjs
dependency's build.rs
runs. As a result, it seems like I can't interject early enough to set up the SDK for both deps. Maybe I'm misunderstanding linking and we only care about the final linking but not sure..
You can't set an env var for another build script. env::set_var
only affects the current process and it's children and cargo doesn't provide a way to set env vars for other build scripts. You will have to set WASI_SDK
when invoking cargo.
Last updated: Nov 26 2024 at 00:12 UTC