From 1e0cc99a8e7815d9efe83c74a6e39776759b23ee Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Tue, 16 Jun 2026 12:10:18 -0700 Subject: [PATCH] Pass --no-entry to emcc when linking emscripten cdylib/dylib A cdylib/dylib has no `main`, but rustc invokes emcc in its default executable mode, which links in emscripten's standalone-wasm entry shim and then fails with `undefined symbol: main`. Mirror the WasmLd linker: emit `--no-entry` for `DynamicDylib`/ `StaticDylib` output kinds in EmLinker::set_output_kind, conditioned on LinkOutputKind so executables keep their entry. Extend the emscripten cdylib run-make test to also link a bin crate with `main`, guarding against regressing the executable entry path. --- compiler/rustc_codegen_ssa/src/back/linker.rs | 3 +++ tests/run-make/wasm-emscripten-cdylib/main.rs | 3 +++ tests/run-make/wasm-emscripten-cdylib/rmake.rs | 10 ++++++++++ 3 files changed, 16 insertions(+) create mode 100644 tests/run-make/wasm-emscripten-cdylib/main.rs diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index d40340e4d8c09..40704f9dfa287 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -1233,6 +1233,9 @@ impl<'a> Linker for EmLinker<'a> { } LinkOutputKind::DynamicDylib | LinkOutputKind::StaticDylib => { self.cmd.arg("-sSIDE_MODULE=2"); + // Shared libraries have no `main`, so emcc must not link in the + // standalone-wasm entry shim that expects one. Mirrors `WasmLd`. + self.link_arg("--no-entry"); } // -fno-pie is the default on Emscripten. LinkOutputKind::StaticNoPicExe | LinkOutputKind::StaticPicExe => {} diff --git a/tests/run-make/wasm-emscripten-cdylib/main.rs b/tests/run-make/wasm-emscripten-cdylib/main.rs new file mode 100644 index 0000000000000..7ff7f07644748 --- /dev/null +++ b/tests/run-make/wasm-emscripten-cdylib/main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("{}", 42); +} diff --git a/tests/run-make/wasm-emscripten-cdylib/rmake.rs b/tests/run-make/wasm-emscripten-cdylib/rmake.rs index ef5fc17c2bbe2..b205745092fdc 100644 --- a/tests/run-make/wasm-emscripten-cdylib/rmake.rs +++ b/tests/run-make/wasm-emscripten-cdylib/rmake.rs @@ -1,11 +1,17 @@ //! Check that cdylib crate type is supported for the wasm32-unknown-emscripten //! target and produces a valid Emscripten dynamic library. +//! +//! A cdylib has no `main`, so rustc must link it as entryless (`--no-entry`), +//! otherwise emcc pulls in the standalone-wasm entry shim and `wasm-ld` errors +//! with `undefined symbol: main`. Executables must keep their entry, so this +//! also checks that a `bin` crate with `main` still links. //@ only-wasm32-unknown-emscripten use run_make_support::{bare_rustc, rfs, wasmparser}; fn main() { + // A cdylib must link without `--no-entry` being supplied by the user. bare_rustc().input("foo.rs").target("wasm32-unknown-emscripten").crate_type("cdylib").run(); // Verify the output is a valid wasm file with a dylink.0 section @@ -22,4 +28,8 @@ fn main() { } assert!(has_dylink, "expected dylink.0 section in emscripten cdylib output"); + + // An executable has a `main`, so `--no-entry` must NOT be applied: it must + // still link with its entry preserved. + bare_rustc().input("main.rs").target("wasm32-unknown-emscripten").crate_type("bin").run(); }