diff --git a/build-wasix.sh b/build-wasix.sh new file mode 100755 index 0000000000000..4cac922be393b --- /dev/null +++ b/build-wasix.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +set -euo pipefail + +cd src/llvm-project +git status | grep WebAssemblyISelLowering.cpp &> /dev/null +if [ $? -ne 0 ]; then + git apply ../../wasix-llvm.patch +fi +cd ../.. + +./x.py build --target=wasm32-wasmer-wasi --stage 2 +./x.py build --target=wasm32-wasmer-wasi-dl --stage 2 + +rustup toolchain uninstall wasix-dev +rustup toolchain link wasix-dev ./build/host/stage2 + +echo Done diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 6a2a394c63a27..5e39d80efe275 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -1,3 +1,4 @@ +use core::iter::Iterator; use std::collections::BTreeSet; use std::ffi::OsString; use std::fs::{File, OpenOptions, read}; @@ -1116,16 +1117,22 @@ fn link_natively( if sess.target.is_like_osx { let stripcmd = "rust-objcopy"; match (strip, crate_type) { - (Strip::Debuginfo, _) => { - strip_symbols_with_external_utility(sess, stripcmd, out_filename, Some("--strip-debug")) - } + (Strip::Debuginfo, _) => strip_symbols_with_external_utility( + sess, + stripcmd, + out_filename, + Some("--strip-debug"), + ), // Per the manpage, `-x` is the maximum safe strip level for dynamic libraries. (#93988) (Strip::Symbols, CrateType::Dylib | CrateType::Cdylib | CrateType::ProcMacro) => { strip_symbols_with_external_utility(sess, stripcmd, out_filename, Some("-x")) } - (Strip::Symbols, _) => { - strip_symbols_with_external_utility(sess, stripcmd, out_filename, Some("--strip-all")) - } + (Strip::Symbols, _) => strip_symbols_with_external_utility( + sess, + stripcmd, + out_filename, + Some("--strip-all"), + ), (Strip::None, _) => {} } } @@ -2986,6 +2993,11 @@ fn add_dynamic_crate(cmd: &mut dyn Linker, sess: &Session, cratepath: &Path) { } fn relevant_lib(sess: &Session, lib: &NativeLib) -> bool { + if sess.target.is_like_wasm && lib.name.as_str() == "c" { + // For wasm targets, WasmLd decides whether to link libc based on + // the output kind, so we skip it here. + return false; + } match lib.cfg { Some(ref cfg) => rustc_attr::cfg_matches(cfg, sess, CRATE_NODE_ID, None), None => true, diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index bae6d23fd8166..f28af2412fb24 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -1278,7 +1278,7 @@ impl<'a> WasmLd<'a> { // symbols are how the TLS segments are initialized and configured. let mut wasm_ld = WasmLd { cmd, sess }; if sess.target_features.contains(&sym::atomics) { - wasm_ld.link_args(&["--shared-memory", "--max-memory=1073741824", "--import-memory"]); + wasm_ld.link_args(&["--shared-memory", "--max-memory=4294967296", "--import-memory"]); if sess.target.os == "unknown" || sess.target.os == "none" { wasm_ld.link_args(&[ "--export=__wasm_init_tls", @@ -1304,11 +1304,38 @@ impl<'a> Linker for WasmLd<'a> { _out_filename: &Path, ) { match output_kind { - LinkOutputKind::DynamicNoPicExe - | LinkOutputKind::DynamicPicExe - | LinkOutputKind::StaticNoPicExe - | LinkOutputKind::StaticPicExe => {} - LinkOutputKind::DynamicDylib | LinkOutputKind::StaticDylib => { + LinkOutputKind::DynamicNoPicExe | LinkOutputKind::StaticNoPicExe => {} + LinkOutputKind::StaticPicExe | LinkOutputKind::DynamicPicExe => { + // FIXME: The wasm32-wasmer-wasi-dl toolchain produces broken executables + // right now, because the start function isn't linked correctly. Instead of + // _start calling the rust main function, it calls __main_void from libc, + // which is broken because rustc does not generate a __main_argc_argv. + // ... probably because we're linking in libc's _start and overriding rustc's? + + self.link_args(["-pie", "--export-all", "--no-gc-sections"]); + + // We link and export all of libc, as well as all of rust's stdlib into dynamic + // executables, so that side modules can just link against the existing code at + // runtime. This has two benefits: + // * Reduced code size for side modules + // * More importantly, static and thread-local variables will exist only once. + // This is especially important for libc, which stores things such as the + // state of its memory allocator (malloc et al.) in static variables. + let whole_archive = true; + let verbatim = false; // No effect for WasmLd + self.link_staticlib_by_name("c", verbatim, whole_archive); + self.link_staticlib_by_name("resolv", verbatim, whole_archive); + self.link_staticlib_by_name("rt", verbatim, whole_archive); + self.link_staticlib_by_name("m", verbatim, whole_archive); + self.link_staticlib_by_name("pthread", verbatim, whole_archive); + self.link_staticlib_by_name("util", verbatim, whole_archive); + self.link_staticlib_by_name("wasi-emulated-mman", verbatim, whole_archive); + self.link_staticlib_by_name("common-tag-stubs", verbatim, whole_archive); + } + LinkOutputKind::DynamicDylib => { + self.link_args(["--no-entry", "-shared", "--unresolved-symbols=import-dynamic"]); + } + LinkOutputKind::StaticDylib => { self.link_arg("--no-entry"); } LinkOutputKind::WasiReactorExe => { @@ -1394,7 +1421,7 @@ impl<'a> Linker for WasmLd<'a> { fn no_default_libraries(&mut self) {} - fn export_symbols(&mut self, _tmpdir: &Path, _crate_type: CrateType, symbols: &[String]) { + fn export_symbols(&mut self, _tmpdir: &Path, crate_type: CrateType, symbols: &[String]) { for sym in symbols { self.link_args(&["--export", sym]); } @@ -1403,7 +1430,23 @@ impl<'a> Linker for WasmLd<'a> { // symbols explicitly passed via the `--export` flags above and hides all // others. Various bits and pieces of wasm32-unknown-unknown tooling use // this, so be sure these symbols make their way out of the linker as well. - self.link_args(&["--export=__heap_base", "--export=__data_end", "--export=__stack_pointer"]); + self.link_args(&[ + "--export=__wasm_init_tls", + "--export=__tls_size", + "--export=__tls_align", + "--export=__tls_base", + "--export=__wasm_call_ctors", + "--export=__wasm_signal", + "--export-if-defined=__wasm_apply_data_relocs", + ]); + + if matches!(crate_type, CrateType::Executable) { + self.link_args(&[ + "--export-if-defined=__stack_pointer", + "--export-if-defined=__heap_base", + "--export-if-defined=__data_end", + ]); + } } fn subsystem(&mut self, _subsystem: &str) {} diff --git a/compiler/rustc_target/src/spec/crt_objects.rs b/compiler/rustc_target/src/spec/crt_objects.rs index e3b6430a46371..6753268da6c93 100644 --- a/compiler/rustc_target/src/spec/crt_objects.rs +++ b/compiler/rustc_target/src/spec/crt_objects.rs @@ -115,6 +115,8 @@ pub(super) fn pre_wasi_self_contained() -> CrtObjects { (LinkOutputKind::DynamicPicExe, &["crt1-command.o"]), (LinkOutputKind::StaticNoPicExe, &["crt1-command.o"]), (LinkOutputKind::StaticPicExe, &["crt1-command.o"]), + (LinkOutputKind::DynamicDylib, &["scrt1.o"]), + (LinkOutputKind::StaticDylib, &["scrt1.o"]), (LinkOutputKind::WasiReactorExe, &["crt1-reactor.o"]), ]) } diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 5c53da5516cc1..c7b8e15ac4bff 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -1333,9 +1333,9 @@ impl StackProbeType { .and_then(|o| o.as_array()) .ok_or_else(|| "expected `min-llvm-version-for-inline` to be an array")?; let mut iter = min_version.into_iter().map(|v| { - let int = v.as_u64().ok_or_else(|| { - "expected `min-llvm-version-for-inline` values to be integers" - })?; + let int = v.as_u64().ok_or_else( + || "expected `min-llvm-version-for-inline` values to be integers", + )?; u32::try_from(int) .map_err(|_| "`min-llvm-version-for-inline` values don't convert to u32") }); @@ -1809,6 +1809,7 @@ supported_targets! { ("wasm32-wasip2", wasm32_wasip2), ("wasm32-wasip1-threads", wasm32_wasip1_threads), ("wasm32-wasmer-wasi", wasm32_wasmer_wasi), + ("wasm32-wasmer-wasi-dl", wasm32_wasmer_wasi_dl), ("wasm64-unknown-emscripten", wasm64_unknown_emscripten), ("wasm64-unknown-unknown", wasm64_unknown_unknown), ("wasm64-wasmer-wasi", wasm64_wasmer_wasi), @@ -3462,10 +3463,10 @@ impl Target { // Each field should have been read using `Json::remove` so any keys remaining are unused. let remaining_keys = obj.keys(); - Ok((base, TargetWarnings { - unused_fields: remaining_keys.cloned().collect(), - incorrect_type, - })) + Ok(( + base, + TargetWarnings { unused_fields: remaining_keys.cloned().collect(), incorrect_type }, + )) } /// Load a built-in target diff --git a/compiler/rustc_target/src/spec/targets/wasm32_wasmer_wasi.rs b/compiler/rustc_target/src/spec/targets/wasm32_wasmer_wasi.rs index 5def280dc304c..89806e85851d8 100644 --- a/compiler/rustc_target/src/spec/targets/wasm32_wasmer_wasi.rs +++ b/compiler/rustc_target/src/spec/targets/wasm32_wasmer_wasi.rs @@ -71,22 +71,31 @@ //! best we can with this target. Don't start relying on too much here unless //! you know what you're getting in to! -use crate::spec::{base, crt_objects, Cc, LinkSelfContainedDefault, LinkerFlavor, Target}; +use crate::spec::{Cc, LinkSelfContainedDefault, LinkerFlavor, Target, base, crt_objects}; pub(crate) fn target() -> Target { + macro_rules! args { + ($prefix:literal) => { + &[ + // We need shared memory for multithreading + concat!($prefix, "--shared-memory"), + concat!($prefix, "--import-memory"), + concat!($prefix, "--export-dynamic"), + concat!($prefix, "--no-check-features"), + concat!($prefix, "-mllvm"), + concat!($prefix, "--wasm-enable-sjlj"), + ] + }; + } + let mut options = base::wasm::options(); options.os = "wasi".into(); options.vendor = "wasmer".into(); - options.add_pre_link_args( - LinkerFlavor::WasmLld(Cc::Yes), - &[ - "--target=wasm32-wasi", - // We need shared memory for multithreading - "--shared-memory", - "--no-check-features", - ], - ); + options.add_pre_link_args(LinkerFlavor::WasmLld(Cc::Yes), &["--target=wasm32-wasi"]); + + options.add_pre_link_args(LinkerFlavor::WasmLld(Cc::Yes), args!("-Wl,")); + options.add_pre_link_args(LinkerFlavor::WasmLld(Cc::No), args!("")); options.pre_link_objects_self_contained = crt_objects::pre_wasi_self_contained(); options.post_link_objects_self_contained = crt_objects::post_wasi_self_contained(); diff --git a/compiler/rustc_target/src/spec/targets/wasm32_wasmer_wasi_dl.rs b/compiler/rustc_target/src/spec/targets/wasm32_wasmer_wasi_dl.rs new file mode 100644 index 0000000000000..6c7a8fe91cd85 --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/wasm32_wasmer_wasi_dl.rs @@ -0,0 +1,19 @@ +//! `wasm32-wasmer-wasi` with support for dynamic linking. + +use crate::spec::{RelocModel, Target, TlsModel}; + +pub(crate) fn target() -> Target { + let mut target = super::wasm32_wasmer_wasi::target(); + + target.env = "dl".into(); + + target.options.dll_prefix = "lib".into(); + target.options.dll_suffix = ".so".into(); + + target.options.relocation_model = RelocModel::Pic; + target.options.tls_model = TlsModel::GeneralDynamic; + target.options.crt_static_default = false; + target.options.position_independent_executables = true; + + target +} diff --git a/config.toml b/config.toml index d858d83cb21f3..dc32b87d31486 100644 --- a/config.toml +++ b/config.toml @@ -1,9 +1,9 @@ change-id = 125535 [build] -target = ["wasm32-wasmer-wasi", "wasm64-wasmer-wasi"] +target = ["wasm32-wasmer-wasi", "wasm32-wasmer-wasi-dl"] extended = true -tools = [ "clippy", "rustfmt" ] +tools = ["cargo", "clippy", "rustfmt"] configure-args = [] [rust] @@ -14,7 +14,7 @@ llvm-tools = true download-ci-llvm = false [target.wasm32-wasmer-wasi] -wasi-root = "../wasix-libc/sysroot32" +wasi-root = "../wasix-libc/sysroot32-eh" -[target.wasm64-wasmer-wasi] -wasi-root = "../wasix-libc/sysroot64" +[target.wasm32-wasmer-wasi-dl] +wasi-root = "../wasix-libc/sysroot32-ehpic" diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index 3f7e3994073b7..4de062aa04c5b 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -34,7 +34,9 @@ miniz_oxide = { version = "0.7.0", optional = true, default-features = false } addr2line = { version = "0.22.0", optional = true, default-features = false } [target.'cfg(not(all(windows, target_env = "msvc")))'.dependencies] -libc = { git = "https://github.com/wasix-org/libc.git", branch = "wasix-0.2.164", version = "0.2.164", default-features = false, features = ['rustc-dep-of-std'], public = true } +libc = { git = "https://github.com/wasix-org/libc.git", branch = "wasix-0.2.164", version = "0.2.164", default-features = false, features = [ + 'rustc-dep-of-std', +], public = true } [target.'cfg(all(not(target_os = "aix"), not(all(windows, target_env = "msvc", not(target_vendor = "uwp")))))'.dependencies] object = { version = "0.36.0", default-features = false, optional = true, features = [ @@ -80,10 +82,19 @@ wasi = { version = "0.11.0", features = [ ], default-features = false } [target.wasm32-wasmer-wasi.dependencies] -wasi = { package = "wasix", version = "0.13.0", features = ['rustc-dep-of-std'], default-features = false, public = true } +wasi = { package = "wasix", version = "0.13.0", features = [ + 'rustc-dep-of-std', +], default-features = false, public = true } + +[target.wasm32-wasmer-wasi-dl.dependencies] +wasi = { package = "wasix", version = "0.13.0", features = [ + 'rustc-dep-of-std', +], default-features = false, public = true } [target.wasm64-wasmer-wasi.dependencies] -wasi = { package = "wasix", version = "0.13.0", features = ['rustc-dep-of-std'], default-features = false, public = true } +wasi = { package = "wasix", version = "0.13.0", features = [ + 'rustc-dep-of-std', +], default-features = false, public = true } [target.'cfg(target_os = "uefi")'.dependencies] r-efi = { version = "4.5.0", features = ['rustc-dep-of-std'] } @@ -145,6 +156,7 @@ check-cfg = [ 'cfg(bootstrap)', 'cfg(target_arch, values("xtensa"))', 'cfg(target_vendor, values("wasmer"))', + 'cfg(target_env, values("dl"))', # std use #[path] imports to portable-simd `std_float` crate # and to the `backtrace` crate which messes-up with Cargo list # of declared features, we therefor expect any feature cfg diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 8e088682f92de..b4c4e67f99476 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -392,7 +392,28 @@ fn copy_self_contained_objects( target.triple ) }); - for &obj in &["libc.a", "crt1-command.o", "crt1-reactor.o"] { + let mut files = vec!["libc.a", "crt1-command.o", "crt1-reactor.o"]; + if target.contains("-dl") { + files.extend([ + // Needed for DL side modules. + "scrt1.o", + // Need to bring these over for DL main module builds, so we can embed them. + "libcommon-tag-stubs.a", + "libcrypt.a", + "libwasi-emulated-mman.a", + "libresolv.a", + "librt.a", + "libm.a", + "libpthread.a", + "libutil.a", + // We don't link c++ libs in, but we bring them over anyway so that, when + // a rust program needs to load C++-based dylibs, the user can link these + // in manually via linker flags. + "libc++.a", + "libc++abi.a", + ]); + } + for obj in files { copy_and_stamp( builder, &libdir_self_contained, diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index 9f386b7e1c0ce..8a4d79600fe7d 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -711,11 +711,7 @@ impl Build { /// Component directory that Cargo will produce output into (e.g. /// release/debug) fn cargo_dir(&self) -> &'static str { - if self.config.rust_optimize.is_release() { - "release" - } else { - "debug" - } + if self.config.rust_optimize.is_release() { "release" } else { "debug" } } fn tools_dir(&self, compiler: Compiler) -> PathBuf { @@ -1337,20 +1333,18 @@ Executed at: {executed_at}"#, /// configuration, and failing that it assumes that `$WASI_SDK_PATH` is /// set in the environment, and failing that `None` is returned. fn wasi_libdir(&self, target: TargetSelection) -> Option { - let mut target_name = target.to_string(); - if target_name.contains("-wasmer") { - target_name = target_name.replace("-wasmer", ""); - } + let c_target_name = + if target.to_string().contains("wasm32") { "wasm32-wasi" } else { "wasm64-wasi" }; let configured = self.config.target_config.get(&target).and_then(|t| t.wasi_root.as_ref()).map(|p| &**p); if let Some(path) = configured { - return Some(path.join("lib").join(target_name)); + return Some(path.join("lib").join(c_target_name)); } let mut env_root = PathBuf::from(std::env::var_os("WASI_SDK_PATH")?); env_root.push("share"); env_root.push("wasi-sysroot"); env_root.push("lib"); - env_root.push(target_name); + env_root.push(c_target_name); Some(env_root) } @@ -1854,11 +1848,7 @@ Executed at: {executed_at}"#, use std::os::unix::fs::symlink as symlink_file; #[cfg(windows)] use std::os::windows::fs::symlink_file; - if !self.config.dry_run() { - symlink_file(src.as_ref(), link.as_ref()) - } else { - Ok(()) - } + if !self.config.dry_run() { symlink_file(src.as_ref(), link.as_ref()) } else { Ok(()) } } /// Returns if config.ninja is enabled, and checks for ninja existence, diff --git a/wasix-llvm.patch b/wasix-llvm.patch new file mode 100644 index 0000000000000..d7a4db08c28d4 --- /dev/null +++ b/wasix-llvm.patch @@ -0,0 +1,15 @@ +diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp +index f77076d7244c..cafeed98a043 100644 +--- a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp ++++ b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp +@@ -1689,9 +1689,7 @@ WebAssemblyTargetLowering::LowerGlobalTLSAddress(SDValue Op, + // Currently only Emscripten supports dynamic linking with threads. Therefore, + // on other targets, if we have thread-local storage, only the local-exec + // model is possible. +- auto model = Subtarget->getTargetTriple().isOSEmscripten() +- ? GV->getThreadLocalMode() +- : GlobalValue::LocalExecTLSModel; ++ auto model = GV->getThreadLocalMode(); + + // Unsupported TLS modes + assert(model != GlobalValue::NotThreadLocal);