Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion compiler/rustc_codegen_ssa/src/back/linker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1286,9 +1286,24 @@ impl<'a> Linker for EmLinker<'a> {

self.cc_arg("-s");

// Emscripten exposes the program entry point under the JS name `_main`
// regardless of the underlying wasm symbol (which is `__main_argc_argv`
// per the wasm C ABI in the tool-conventions BasicCABI spec), bridging
// the two internally. So the entry symbol must be requested as `_main`
// here rather than as a `_`-prefixed form of its wasm name.
let entry_name = self.sess.target.entry_name.as_ref();
let mut arg = OsString::from("EXPORTED_FUNCTIONS=");
let encoded = serde_json::to_string(
&symbols.iter().map(|sym| "_".to_owned() + &sym.name).collect::<Vec<_>>(),
&symbols
.iter()
.map(|sym| {
if sym.name == entry_name {
"_main".to_owned()
} else {
"_".to_owned() + &sym.name
}
})
.collect::<Vec<_>>(),
)
.unwrap();
debug!("{encoded}");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ pub(crate) fn target() -> Target {
crt_static_default: true,
crt_static_allows_dylibs: true,
main_needs_argc_argv: true,
// Use the wasm C-ABI entry name from the tool-conventions BasicCABI
// spec rather than a raw `main`, as referenced by emscripten's crt/libc.
// Required for entry paths like `-sPROXY_TO_PTHREAD`, whose
// `crt1_proxy_main` links against `__main_argc_argv`.
entry_name: "__main_argc_argv".into(),
panic_strategy: PanicStrategy::Unwind,
no_default_libraries: false,
families: cvs!["unix", "wasm"],
Expand Down
1 change: 1 addition & 0 deletions tests/run-make/wasm-emscripten-main-symbol/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fn main() {}
45 changes: 45 additions & 0 deletions tests/run-make/wasm-emscripten-main-symbol/rmake.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
//@ only-wasm32-unknown-emscripten

// On wasm the age-old C trick of a `main` that is either `int main(void)` or
// `int main(int, char**)` doesn't work, because wasm requires caller and callee
// signatures to match. The platform ABI (as used by Clang) therefore mangles
// the entry point by signature; the argc/argv form is emitted as
// `__main_argc_argv`. Rust's emscripten target uses the argc/argv form, so its
// entry point must be emitted under that name rather than as a raw `main`.
//
// This matters beyond cosmetics: emscripten's own crt references
// `__main_argc_argv` directly on some entry paths (notably `crt1_proxy_main.o`,
// used by `-sPROXY_TO_PTHREAD`), which fail to link against a raw `main`.
//
// The JS-visible name of the entry point stays `_main`; emscripten bridges that
// to the wasm `__main_argc_argv` export internally.

use run_make_support::{rfs, rustc, wasmparser};
use wasmparser::ExternalKind;

fn main() {
rustc().input("main.rs").target("wasm32-unknown-emscripten").output("main.js").run();

let file = rfs::read("main.wasm");
let mut entry = None;
let mut has_plain_main = false;
for payload in wasmparser::Parser::new(0).parse_all(&file) {
if let wasmparser::Payload::ExportSection(s) = payload.unwrap() {
for export in s {
let export = export.unwrap();
match export.name {
"__main_argc_argv" => entry = Some(export.kind),
"main" => has_plain_main = true,
_ => {}
}
}
}
}

assert_eq!(
entry,
Some(ExternalKind::Func),
"the emscripten entry point must be exported as the wasm C-ABI symbol `__main_argc_argv`",
);
assert!(!has_plain_main, "a raw `main` symbol must not be exported on wasm");
}
Loading