Summary
With lto = "thin" in [profile.release], building an ext crate as crate-type = ["staticlib", "rlib"] internalizes its #[no_mangle] C-ABI symbols in the resulting .a. The prebuilt target/release/libperry_ext_*.a archives export zero js_* symbols.
Crates the auto-optimizer rebuilds with shared tokio regain their symbols (they're recompiled), but CPU-only crates routed to the prebuilt .a (e.g. the compression crate for node:zlib, events) link against a symbol-less archive, so the final link fails with Undefined symbols: _js_ext_zlib_*, _js_zlib_create_*, … even though the archive is present and was logged as routed.
Evidence / repro
$ nm target/release/libperry_ext_zlib.a | grep -c ' T _js_zlib'
0
$ nm target/release/libperry_ext_http.a | grep -c ' T _js_'
0
$ CARGO_PROFILE_RELEASE_LTO=off cargo build --release -p perry-ext-zlib
$ nm target/release/libperry_ext_zlib.a | grep -c ' T _js_zlib'
26
A program that imports node:zlib then fails to link. (Note: setting CARGO_PROFILE_RELEASE_LTO=off as an env on perry compile does not propagate to the auto-optimizer's internal cargo invocations — the crate must be rebuilt directly.)
Workaround
Build the routed crate with CARGO_PROFILE_RELEASE_LTO=off so its staticlib keeps the #[no_mangle] exports.
Suggested fix
Build the routed/prebuilt ext staticlibs with thin-LTO disabled (or otherwise preserve the #[no_mangle] C exports — e.g. #[used] / an explicit export list / -C linker-plugin-lto), independent of the shared-tokio rebuild path. Affects any program importing a non-tokio well-known ext crate (compression, events, …).
Related: the auto-optimizer "archives-fresh" fast-path also dropped these routed libs from the link line — separate fix in #5668. Both are needed to link a node:zlib program from fresh archives.
Summary
With
lto = "thin"in[profile.release], building an ext crate ascrate-type = ["staticlib", "rlib"]internalizes its#[no_mangle]C-ABI symbols in the resulting.a. The prebuilttarget/release/libperry_ext_*.aarchives export zerojs_*symbols.Crates the auto-optimizer rebuilds with shared tokio regain their symbols (they're recompiled), but CPU-only crates routed to the prebuilt
.a(e.g. the compression crate fornode:zlib, events) link against a symbol-less archive, so the final link fails withUndefined symbols: _js_ext_zlib_*, _js_zlib_create_*, …even though the archive is present and was logged as routed.Evidence / repro
A program that imports
node:zlibthen fails to link. (Note: settingCARGO_PROFILE_RELEASE_LTO=offas an env onperry compiledoes not propagate to the auto-optimizer's internal cargo invocations — the crate must be rebuilt directly.)Workaround
Build the routed crate with
CARGO_PROFILE_RELEASE_LTO=offso its staticlib keeps the#[no_mangle]exports.Suggested fix
Build the routed/prebuilt ext staticlibs with thin-LTO disabled (or otherwise preserve the
#[no_mangle]C exports — e.g.#[used]/ an explicit export list /-C linker-plugin-lto), independent of the shared-tokio rebuild path. Affects any program importing a non-tokio well-known ext crate (compression, events, …).Related: the auto-optimizer "archives-fresh" fast-path also dropped these routed libs from the link line — separate fix in #5668. Both are needed to link a
node:zlibprogram from fresh archives.