diff --git a/config.toml.example b/config.toml.example index 962be2e608501..fd1f03b9d0e24 100644 --- a/config.toml.example +++ b/config.toml.example @@ -292,6 +292,12 @@ # build native code. #android-ndk = "/path/to/ndk" +# Force static or dynamic linkage of the standard library for this target. If +# this target is a host for rustc, this will also affect the linkage of the +# compiler itself. This is useful for building rustc on targets that normally +# only use static libraries. If unset, the target's default linkage is used. +#crt-static = false + # The root location of the MUSL installation directory. The library directory # will also need to contain libunwind.a for an unwinding implementation. Note # that this option only makes sense for MUSL targets that produce statically diff --git a/src/bootstrap/bin/rustc.rs b/src/bootstrap/bin/rustc.rs index f6ed4ee91b3c5..0baca9e58f4fe 100644 --- a/src/bootstrap/bin/rustc.rs +++ b/src/bootstrap/bin/rustc.rs @@ -237,9 +237,13 @@ fn main() { } } - if target.contains("pc-windows-msvc") { - cmd.arg("-Z").arg("unstable-options"); - cmd.arg("-C").arg("target-feature=+crt-static"); + if let Ok(s) = env::var("RUSTC_CRT_STATIC") { + if s == "true" { + cmd.arg("-C").arg("target-feature=+crt-static"); + } + if s == "false" { + cmd.arg("-C").arg("target-feature=-crt-static"); + } } // Force all crates compiled by this compiler to (a) be unstable and (b) diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index e325fc65f051d..298f6a004a20a 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -503,6 +503,10 @@ impl<'a> Builder<'a> { cargo.env("RUSTC_METADATA_SUFFIX", "rustc"); } + if let Some(x) = self.crt_static(target) { + cargo.env("RUSTC_CRT_STATIC", x.to_string()); + } + // Enable usage of unstable features cargo.env("RUSTC_BOOTSTRAP", "1"); self.add_rust_test_threads(&mut cargo); diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs index 78bc225447bd0..335e1690a2ea0 100644 --- a/src/bootstrap/compile.rs +++ b/src/bootstrap/compile.rs @@ -77,6 +77,14 @@ impl Step for Std { target, }); println!("Uplifting stage1 std ({} -> {})", from.host, target); + + // Even if we're not building std this stage, the new sysroot must + // still contain the musl startup objects. + if target.contains("musl") && !target.contains("mips") { + let libdir = builder.sysroot_libdir(compiler, target); + copy_musl_third_party_objects(build, target, &libdir); + } + builder.ensure(StdLink { compiler: from, target_compiler: compiler, @@ -89,6 +97,11 @@ impl Step for Std { println!("Building stage{} std artifacts ({} -> {})", compiler.stage, &compiler.host, target); + if target.contains("musl") && !target.contains("mips") { + let libdir = builder.sysroot_libdir(compiler, target); + copy_musl_third_party_objects(build, target, &libdir); + } + let out_dir = build.cargo_out(compiler, Mode::Libstd, target); build.clear_if_dirty(&out_dir, &builder.rustc(compiler)); let mut cargo = builder.cargo(compiler, Mode::Libstd, target, "build"); @@ -105,6 +118,20 @@ impl Step for Std { } } +/// Copies the crt(1,i,n).o startup objects +/// +/// Since musl supports fully static linking, we can cross link for it even +/// with a glibc-targeting toolchain, given we have the appropriate startup +/// files. As those shipped with glibc won't work, copy the ones provided by +/// musl so we have them on linux-gnu hosts. +fn copy_musl_third_party_objects(build: &Build, + target: Interned, + into: &Path) { + for &obj in &["crt1.o", "crti.o", "crtn.o"] { + copy(&build.musl_root(target).unwrap().join("lib").join(obj), &into.join(obj)); + } +} + /// Configure cargo to compile the standard library, adding appropriate env vars /// and such. pub fn std_cargo(build: &Build, @@ -189,10 +216,6 @@ impl Step for StdLink { let libdir = builder.sysroot_libdir(target_compiler, target); add_to_sysroot(&libdir, &libstd_stamp(build, compiler, target)); - if target.contains("musl") && !target.contains("mips") { - copy_musl_third_party_objects(build, target, &libdir); - } - if build.config.sanitizers && compiler.stage != 0 && target == "x86_64-apple-darwin" { // The sanitizers are only built in stage1 or above, so the dylibs will // be missing in stage0 and causes panic. See the `std()` function above @@ -208,15 +231,6 @@ impl Step for StdLink { } } -/// Copies the crt(1,i,n).o startup objects -/// -/// Only required for musl targets that statically link to libc -fn copy_musl_third_party_objects(build: &Build, target: Interned, into: &Path) { - for &obj in &["crt1.o", "crti.o", "crtn.o"] { - copy(&build.musl_root(target).unwrap().join("lib").join(obj), &into.join(obj)); - } -} - fn copy_apple_sanitizer_dylibs(native_dir: &Path, platform: &str, into: &Path) { for &sanitizer in &["asan", "tsan"] { let filename = format!("libclang_rt.{}_{}_dynamic.dylib", sanitizer, platform); diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index aa688fc66e267..f43035fbfe8a1 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -143,6 +143,7 @@ pub struct Target { pub cc: Option, pub cxx: Option, pub ndk: Option, + pub crt_static: Option, pub musl_root: Option, pub qemu_rootfs: Option, } @@ -275,6 +276,7 @@ struct TomlTarget { cc: Option, cxx: Option, android_ndk: Option, + crt_static: Option, musl_root: Option, qemu_rootfs: Option, } @@ -446,6 +448,7 @@ impl Config { } target.cxx = cfg.cxx.clone().map(PathBuf::from); target.cc = cfg.cc.clone().map(PathBuf::from); + target.crt_static = cfg.crt_static.clone(); target.musl_root = cfg.musl_root.clone().map(PathBuf::from); target.qemu_rootfs = cfg.qemu_rootfs.clone().map(PathBuf::from); diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs index b6e0098157633..55358f2ffcb73 100644 --- a/src/bootstrap/lib.rs +++ b/src/bootstrap/lib.rs @@ -656,6 +656,16 @@ impl Build { base } + /// Returns if this target should statically link the C runtime, if specified + fn crt_static(&self, target: Interned) -> Option { + if target.contains("pc-windows-msvc") { + Some(true) + } else { + self.config.target_config.get(&target) + .and_then(|t| t.crt_static) + } + } + /// Returns the "musl root" for this `target`, if defined fn musl_root(&self, target: Interned) -> Option<&Path> { self.config.target_config.get(&target) diff --git a/src/bootstrap/sanity.rs b/src/bootstrap/sanity.rs index 436a13500f254..a64a6130929cb 100644 --- a/src/bootstrap/sanity.rs +++ b/src/bootstrap/sanity.rs @@ -151,8 +151,15 @@ pub fn check(build: &mut Build) { panic!("the iOS target is only supported on macOS"); } - // Make sure musl-root is valid if specified + // Make sure musl-root is valid if target.contains("musl") && !target.contains("mips") { + // If this is a native target (host is also musl) and no musl-root is given, + // fall back to the system toolchain in /usr before giving up + if build.musl_root(*target).is_none() && build.config.build == *target { + let target = build.config.target_config.entry(target.clone()) + .or_insert(Default::default()); + target.musl_root = Some("/usr".into()); + } match build.musl_root(*target) { Some(root) => { if fs::metadata(root.join("lib/libc.a")).is_err() { diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index 3aea0722d0e2f..23dcaf27c2c70 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -429,6 +429,31 @@ impl Session { .unwrap_or(self.opts.debug_assertions) } + pub fn crt_static(&self) -> bool { + // If the target does not opt in to crt-static support, use its default. + if self.target.target.options.crt_static_respected { + self.crt_static_feature() + } else { + self.target.target.options.crt_static_default + } + } + + pub fn crt_static_feature(&self) -> bool { + let requested_features = self.opts.cg.target_feature.split(','); + let found_negative = requested_features.clone().any(|r| r == "-crt-static"); + let found_positive = requested_features.clone().any(|r| r == "+crt-static"); + + // If the target we're compiling for requests a static crt by default, + // then see if the `-crt-static` feature was passed to disable that. + // Otherwise if we don't have a static crt by default then see if the + // `+crt-static` feature was passed. + if self.target.target.options.crt_static_default { + !found_negative + } else { + found_positive + } + } + pub fn must_not_eliminate_frame_pointers(&self) -> bool { self.opts.debuginfo != DebugInfoLevel::NoDebugInfo || !self.target.target.options.eliminate_frame_pointer diff --git a/src/librustc_back/target/linux_musl_base.rs b/src/librustc_back/target/linux_musl_base.rs index 236f2c1ef0aa3..6e5e139715ccc 100644 --- a/src/librustc_back/target/linux_musl_base.rs +++ b/src/librustc_back/target/linux_musl_base.rs @@ -60,15 +60,10 @@ pub fn opts() -> TargetOptions { base.pre_link_objects_exe.push("crti.o".to_string()); base.post_link_objects.push("crtn.o".to_string()); - // MUSL support doesn't currently include dynamic linking, so there's no - // need for dylibs or rpath business. Additionally `-pie` is incompatible - // with `-static`, so we can't pass `-pie`. - base.dynamic_linking = false; - base.has_rpath = false; - base.position_independent_executables = false; - // These targets statically link libc by default base.crt_static_default = true; + // These targets allow the user to choose between static and dynamic linking. + base.crt_static_respected = true; base } diff --git a/src/librustc_back/target/mod.rs b/src/librustc_back/target/mod.rs index 08b94d5a01cb7..130e1b695dbd5 100644 --- a/src/librustc_back/target/mod.rs +++ b/src/librustc_back/target/mod.rs @@ -416,8 +416,12 @@ pub struct TargetOptions { /// ABIs are considered to be supported on all platforms and cannot be blacklisted. pub abi_blacklist: Vec, + /// Whether or not linking dylibs to a static CRT is allowed. + pub crt_static_allows_dylibs: bool, /// Whether or not the CRT is statically linked by default. pub crt_static_default: bool, + /// Whether or not crt-static is respected by the compiler (or is a no-op). + pub crt_static_respected: bool, /// Whether or not stack probes (__rust_probestack) are enabled pub stack_probes: bool, @@ -478,7 +482,9 @@ impl Default for TargetOptions { max_atomic_width: None, panic_strategy: PanicStrategy::Unwind, abi_blacklist: vec![], + crt_static_allows_dylibs: false, crt_static_default: false, + crt_static_respected: false, stack_probes: false, } } @@ -714,7 +720,9 @@ impl Target { key!(max_atomic_width, Option); key!(min_atomic_width, Option); try!(key!(panic_strategy, PanicStrategy)); + key!(crt_static_allows_dylibs, bool); key!(crt_static_default, bool); + key!(crt_static_respected, bool); key!(stack_probes, bool); if let Some(array) = obj.find("abi-blacklist").and_then(Json::as_array) { @@ -902,7 +910,9 @@ impl ToJson for Target { target_option_val!(min_atomic_width); target_option_val!(max_atomic_width); target_option_val!(panic_strategy); + target_option_val!(crt_static_allows_dylibs); target_option_val!(crt_static_default); + target_option_val!(crt_static_respected); target_option_val!(stack_probes); if default.abi_blacklist != self.options.abi_blacklist { diff --git a/src/librustc_back/target/windows_msvc_base.rs b/src/librustc_back/target/windows_msvc_base.rs index c07321e418e64..42a4e6f5f1188 100644 --- a/src/librustc_back/target/windows_msvc_base.rs +++ b/src/librustc_back/target/windows_msvc_base.rs @@ -63,6 +63,8 @@ pub fn opts() -> TargetOptions { is_like_windows: true, is_like_msvc: true, pre_link_args: args, + crt_static_allows_dylibs: true, + crt_static_respected: true, .. Default::default() } diff --git a/src/librustc_driver/target_features.rs b/src/librustc_driver/target_features.rs index bee61bb398029..96264472b5f8e 100644 --- a/src/librustc_driver/target_features.rs +++ b/src/librustc_driver/target_features.rs @@ -25,21 +25,7 @@ pub fn add_configuration(cfg: &mut ast::CrateConfig, sess: &Session) { cfg.insert((tf, Some(feat))); } - let requested_features = sess.opts.cg.target_feature.split(','); - let found_negative = requested_features.clone().any(|r| r == "-crt-static"); - let found_positive = requested_features.clone().any(|r| r == "+crt-static"); - - // If the target we're compiling for requests a static crt by default, - // then see if the `-crt-static` feature was passed to disable that. - // Otherwise if we don't have a static crt by default then see if the - // `+crt-static` feature was passed. - let crt_static = if sess.target.target.options.crt_static_default { - !found_negative - } else { - found_positive - }; - - if crt_static { + if sess.crt_static_feature() { cfg.insert((tf, Some(Symbol::intern("crt-static")))); } } diff --git a/src/librustc_trans/back/link.rs b/src/librustc_trans/back/link.rs index 338f3bb08aa97..4e211d83cff3e 100644 --- a/src/librustc_trans/back/link.rs +++ b/src/librustc_trans/back/link.rs @@ -902,7 +902,7 @@ fn link_args(cmd: &mut Linker, let mut args = args.iter().chain(more_args.iter()).chain(used_link_args.iter()); if get_reloc_model(sess) == llvm::RelocMode::PIC - && !args.any(|x| *x == "-static") { + && !sess.crt_static() && !args.any(|x| *x == "-static") { cmd.position_independent_executable(); } } @@ -966,11 +966,13 @@ fn link_args(cmd: &mut Linker, add_upstream_rust_crates(cmd, sess, crate_type, tmpdir); add_upstream_native_libraries(cmd, sess, crate_type); - // # Telling the linker what we're doing - + // Tell the linker what we're doing. if crate_type != config::CrateTypeExecutable { cmd.build_dylib(out_filename); } + if crate_type == config::CrateTypeExecutable && sess.crt_static() { + cmd.build_static_executable(); + } // FIXME (#2397): At some point we want to rpath our guesses as to // where extern libraries might live, based on the diff --git a/src/librustc_trans/back/linker.rs b/src/librustc_trans/back/linker.rs index ab401465b560b..9b0a5e3f4a5b1 100644 --- a/src/librustc_trans/back/linker.rs +++ b/src/librustc_trans/back/linker.rs @@ -110,6 +110,7 @@ pub trait Linker { fn debuginfo(&mut self); fn no_default_libraries(&mut self); fn build_dylib(&mut self, out_filename: &Path); + fn build_static_executable(&mut self); fn args(&mut self, args: &[String]); fn export_symbols(&mut self, tmpdir: &Path, crate_type: CrateType); fn subsystem(&mut self, subsystem: &str); @@ -179,6 +180,7 @@ impl<'a> Linker for GccLinker<'a> { fn position_independent_executable(&mut self) { self.cmd.arg("-pie"); } fn partial_relro(&mut self) { self.linker_arg("-z,relro"); } fn full_relro(&mut self) { self.linker_arg("-z,relro,-z,now"); } + fn build_static_executable(&mut self) { self.cmd.arg("-static"); } fn args(&mut self, args: &[String]) { self.cmd.args(args); } fn link_rust_dylib(&mut self, lib: &str, _path: &Path) { @@ -396,6 +398,10 @@ impl<'a> Linker for MsvcLinker<'a> { self.cmd.arg(arg); } + fn build_static_executable(&mut self) { + // noop + } + fn gc_sections(&mut self, _keep_metadata: bool) { // MSVC's ICF (Identical COMDAT Folding) link optimization is // slow for Rust and thus we disable it by default when not in @@ -683,6 +689,10 @@ impl<'a> Linker for EmLinker<'a> { bug!("building dynamic library is unsupported on Emscripten") } + fn build_static_executable(&mut self) { + // noop + } + fn export_symbols(&mut self, _tmpdir: &Path, crate_type: CrateType) { let symbols = &self.info.exports[&crate_type]; diff --git a/src/librustc_trans_utils/link.rs b/src/librustc_trans_utils/link.rs index 264158f0de9ee..aa8637fabe85f 100644 --- a/src/librustc_trans_utils/link.rs +++ b/src/librustc_trans_utils/link.rs @@ -123,8 +123,11 @@ pub fn invalid_output_for_target(sess: &Session, match (sess.target.target.options.dynamic_linking, sess.target.target.options.executables, crate_type) { (false, _, config::CrateTypeCdylib) | - (false, _, config::CrateTypeProcMacro) | - (false, _, config::CrateTypeDylib) => true, + (false, _, config::CrateTypeDylib) | + (false, _, config::CrateTypeProcMacro) => true, + (true, _, config::CrateTypeCdylib) | + (true, _, config::CrateTypeDylib) => sess.crt_static() && + !sess.target.target.options.crt_static_allows_dylibs, (_, false, config::CrateTypeExecutable) => true, _ => false } diff --git a/src/libstd/build.rs b/src/libstd/build.rs index ab304f4c9652a..f57dec98b795b 100644 --- a/src/libstd/build.rs +++ b/src/libstd/build.rs @@ -30,7 +30,7 @@ fn main() { println!("cargo:rustc-link-lib=dl"); println!("cargo:rustc-link-lib=log"); println!("cargo:rustc-link-lib=gcc"); - } else if !target.contains("musl") || target.contains("mips") { + } else if !target.contains("musl") { println!("cargo:rustc-link-lib=dl"); println!("cargo:rustc-link-lib=rt"); println!("cargo:rustc-link-lib=pthread"); diff --git a/src/libunwind/build.rs b/src/libunwind/build.rs index cb8cb90e9ca7a..dc1464b905b0d 100644 --- a/src/libunwind/build.rs +++ b/src/libunwind/build.rs @@ -16,7 +16,7 @@ fn main() { if target.contains("linux") { if target.contains("musl") && !target.contains("mips") { - println!("cargo:rustc-link-lib=static=unwind"); + // musl is handled in lib.rs } else if !target.contains("android") { println!("cargo:rustc-link-lib=gcc_s"); } diff --git a/src/libunwind/lib.rs b/src/libunwind/lib.rs index d4d52322adab0..1ff0a1e19d7b3 100644 --- a/src/libunwind/lib.rs +++ b/src/libunwind/lib.rs @@ -15,6 +15,7 @@ #![deny(warnings)] #![feature(cfg_target_vendor)] +#![feature(link_cfg)] #![feature(staged_api)] #![feature(unwind_attributes)] #![feature(static_nobundle)] @@ -28,3 +29,8 @@ extern crate libc; mod libunwind; #[cfg(not(target_env = "msvc"))] pub use libunwind::*; + +#[cfg(target_env = "musl")] +#[link(name = "unwind", kind = "static-nobundle", cfg(target_feature = "crt-static"))] +#[link(name = "gcc_s", cfg(not(target_feature = "crt-static")))] +extern {} diff --git a/src/test/run-pass-fulldeps/issue-13560.rs b/src/test/run-pass-fulldeps/issue-13560.rs index 88be7fe1212d1..0ceb5ed5e755d 100644 --- a/src/test/run-pass-fulldeps/issue-13560.rs +++ b/src/test/run-pass-fulldeps/issue-13560.rs @@ -11,7 +11,6 @@ // aux-build:issue-13560-1.rs // aux-build:issue-13560-2.rs // aux-build:issue-13560-3.rs -// ignore-musl // Regression test for issue #13560, the test itself is all in the dependent // libraries. The fail which previously failed to compile is the one numbered 3. diff --git a/src/test/run-pass-fulldeps/linkage-visibility.rs b/src/test/run-pass-fulldeps/linkage-visibility.rs index f884bb2098eb1..9839a2c704105 100644 --- a/src/test/run-pass-fulldeps/linkage-visibility.rs +++ b/src/test/run-pass-fulldeps/linkage-visibility.rs @@ -11,7 +11,6 @@ // aux-build:linkage-visibility.rs // ignore-android: FIXME(#10356) // ignore-windows: std::dynamic_lib does not work on Windows well -// ignore-musl // ignore-emscripten no dynamic linking extern crate linkage_visibility as foo;