diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs index 0c754936bc242..c56114f14caa9 100644 --- a/src/bootstrap/compile.rs +++ b/src/bootstrap/compile.rs @@ -125,15 +125,16 @@ fn copy_third_party_objects( target_deps.push(target); }; - // Copies the crt(1,i,n).o startup objects + // Copies the CRT 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. + // rustc historically provides a more self-contained installation for musl targets + // not requiring the presence of a native musl toolchain. For example, it can fall back + // to using gcc from a glibc-targeting toolchain for linking. + // To do that we have to distribute musl startup objects as a part of Rust toolchain + // and link with them manually in the self-contained mode. if target.contains("musl") { let srcdir = builder.musl_root(target).unwrap().join("lib"); - for &obj in &["crt1.o", "crti.o", "crtn.o"] { + for &obj in &["crt1.o", "Scrt1.o", "rcrt1.o", "crti.o", "crtn.o"] { copy_and_stamp(&srcdir, obj); } } else if target.ends_with("-wasi") { diff --git a/src/librustc_codegen_ssa/back/link.rs b/src/librustc_codegen_ssa/back/link.rs index d8b38cf33707c..3b9355404ea49 100644 --- a/src/librustc_codegen_ssa/back/link.rs +++ b/src/librustc_codegen_ssa/back/link.rs @@ -11,7 +11,9 @@ use rustc_session::search_paths::PathKind; /// need out of the shared crate context before we get rid of it. use rustc_session::{filesearch, Session}; use rustc_span::symbol::Symbol; -use rustc_target::spec::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel, RelroLevel}; +use rustc_target::spec::crt_objects::CrtObjectsFallback; +use rustc_target::spec::{LinkOutputKind, LinkerFlavor, LldFlavor}; +use rustc_target::spec::{PanicStrategy, RelocModel, RelroLevel}; use super::archive::ArchiveBuilder; use super::command::Command; @@ -1130,33 +1132,70 @@ fn exec_linker( } } -/// Add begin object files defined by the target spec. -fn add_pre_link_objects(cmd: &mut dyn Linker, sess: &Session, crate_type: CrateType) { - let pre_link_objects = if crate_type == CrateType::Executable { - &sess.target.target.options.pre_link_objects_exe - } else { - &sess.target.target.options.pre_link_objects_dll +fn link_output_kind(sess: &Session, crate_type: CrateType) -> LinkOutputKind { + let kind = match (crate_type, sess.crt_static(Some(crate_type)), sess.relocation_model()) { + (CrateType::Executable, false, RelocModel::Pic) => LinkOutputKind::DynamicPicExe, + (CrateType::Executable, false, _) => LinkOutputKind::DynamicNoPicExe, + (CrateType::Executable, true, RelocModel::Pic) => LinkOutputKind::StaticPicExe, + (CrateType::Executable, true, _) => LinkOutputKind::StaticNoPicExe, + (_, true, _) => LinkOutputKind::StaticDylib, + (_, false, _) => LinkOutputKind::DynamicDylib, }; - for obj in pre_link_objects { - cmd.add_object(&get_object_file_path(sess, obj)); + + // Adjust the output kind to target capabilities. + let pic_exe_supported = sess.target.target.options.position_independent_executables; + let static_pic_exe_supported = false; // FIXME: Add this option to target specs. + let static_dylib_supported = sess.target.target.options.crt_static_allows_dylibs; + match kind { + LinkOutputKind::DynamicPicExe if !pic_exe_supported => LinkOutputKind::DynamicNoPicExe, + LinkOutputKind::StaticPicExe if !static_pic_exe_supported => LinkOutputKind::StaticNoPicExe, + LinkOutputKind::StaticDylib if !static_dylib_supported => LinkOutputKind::DynamicDylib, + _ => kind, } +} - if crate_type == CrateType::Executable && sess.crt_static(Some(crate_type)) { - for obj in &sess.target.target.options.pre_link_objects_exe_crt { - cmd.add_object(&get_object_file_path(sess, obj)); - } +/// Whether we link to our own CRT objects instead of relying on gcc to pull them. +/// We only provide such support for a very limited number of targets. +fn crt_objects_fallback(sess: &Session, crate_type: CrateType) -> bool { + match sess.target.target.options.crt_objects_fallback { + // FIXME: Find a better heuristic for "native musl toolchain is available", + // based on host and linker path, for example. + // (https://github.com/rust-lang/rust/pull/71769#issuecomment-626330237). + Some(CrtObjectsFallback::Musl) => sess.crt_static(Some(crate_type)), + // FIXME: Find some heuristic for "native mingw toolchain is available", + // likely based on `get_crt_libs_path` (https://github.com/rust-lang/rust/pull/67429). + Some(CrtObjectsFallback::Mingw) => sess.target.target.target_vendor != "uwp", + // FIXME: Figure out cases in which WASM needs to link with a native toolchain. + Some(CrtObjectsFallback::Wasm) => true, + None => false, } } -/// Add end object files defined by the target spec. -fn add_post_link_objects(cmd: &mut dyn Linker, sess: &Session, crate_type: CrateType) { - for obj in &sess.target.target.options.post_link_objects { +/// Add pre-link object files defined by the target spec. +fn add_pre_link_objects( + cmd: &mut dyn Linker, + sess: &Session, + link_output_kind: LinkOutputKind, + fallback: bool, +) { + let opts = &sess.target.target.options; + let objects = if fallback { &opts.pre_link_objects_fallback } else { &opts.pre_link_objects }; + for obj in objects.get(&link_output_kind).iter().copied().flatten() { cmd.add_object(&get_object_file_path(sess, obj)); } - if sess.crt_static(Some(crate_type)) { - for obj in &sess.target.target.options.post_link_objects_crt { - cmd.add_object(&get_object_file_path(sess, obj)); - } +} + +/// Add post-link object files defined by the target spec. +fn add_post_link_objects( + cmd: &mut dyn Linker, + sess: &Session, + link_output_kind: LinkOutputKind, + fallback: bool, +) { + let opts = &sess.target.target.options; + let objects = if fallback { &opts.post_link_objects_fallback } else { &opts.post_link_objects }; + for obj in objects.get(&link_output_kind).iter().copied().flatten() { + cmd.add_object(&get_object_file_path(sess, obj)); } } @@ -1320,38 +1359,6 @@ fn add_library_search_dirs(cmd: &mut dyn Linker, sess: &Session) { cmd.include_path(&fix_windows_verbatim_for_gcc(&lib_path)); } -/// Add options requesting executables to be position-independent or not position-independent. -fn add_position_independent_executable_args( - cmd: &mut dyn Linker, - sess: &Session, - flavor: LinkerFlavor, - crate_type: CrateType, - codegen_results: &CodegenResults, -) { - if crate_type != CrateType::Executable { - return; - } - - if sess.target.target.options.position_independent_executables { - let attr_link_args = &*codegen_results.crate_info.link_args; - let mut user_defined_link_args = sess.opts.cg.link_args.iter().chain(attr_link_args); - if sess.relocation_model() == RelocModel::Pic - && !sess.crt_static(Some(crate_type)) - && !user_defined_link_args.any(|x| x == "-static") - { - cmd.position_independent_executable(); - return; - } - } - - // Recent versions of gcc can be configured to generate position - // independent executables by default. We have to pass -no-pie to - // explicitly turn that off. Not applicable to ld. - if sess.target.target.options.linker_is_gnu && flavor != LinkerFlavor::Ld { - cmd.no_position_independent_executable(); - } -} - /// Add options making relocation sections in the produced ELF files read-only /// and suppressing lazy binding. fn add_relro_args(cmd: &mut dyn Linker, sess: &Session) { @@ -1417,6 +1424,8 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>( // to the linker args construction. assert!(base_cmd.get_args().is_empty() || sess.target.target.target_vendor == "uwp"); let cmd = &mut *codegen_results.linker_info.to_linker(base_cmd, &sess, flavor, target_cpu); + let link_output_kind = link_output_kind(sess, crate_type); + let crt_objects_fallback = crt_objects_fallback(sess, crate_type); // NO-OPT-OUT, OBJECT-FILES-MAYBE, CUSTOMIZATION-POINT add_pre_link_args(cmd, sess, flavor, crate_type); @@ -1430,8 +1439,13 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>( cmd.arg(format!("--dynamic-linker={}ld.so.1", prefix)); } + // NO-OPT-OUT, OBJECT-FILES-NO + if crt_objects_fallback { + cmd.no_crt_objects(); + } + // NO-OPT-OUT, OBJECT-FILES-YES - add_pre_link_objects(cmd, sess, crate_type); + add_pre_link_objects(cmd, sess, link_output_kind, crt_objects_fallback); // NO-OPT-OUT, OBJECT-FILES-NO, AUDIT-ORDER if sess.target.target.options.is_like_emscripten { @@ -1490,7 +1504,16 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>( } // NO-OPT-OUT, OBJECT-FILES-NO, AUDIT-ORDER - add_position_independent_executable_args(cmd, sess, flavor, crate_type, codegen_results); + // FIXME: Support `StaticPicExe` correctly. + match link_output_kind { + LinkOutputKind::DynamicPicExe | LinkOutputKind::StaticPicExe => { + cmd.position_independent_executable() + } + LinkOutputKind::DynamicNoPicExe | LinkOutputKind::StaticNoPicExe => { + cmd.no_position_independent_executable() + } + _ => {} + } // OBJECT-FILES-NO, AUDIT-ORDER add_relro_args(cmd, sess); @@ -1520,12 +1543,14 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>( ); // NO-OPT-OUT, OBJECT-FILES-NO, AUDIT-ORDER - // Tell the linker what we're doing. - if crate_type != CrateType::Executable { - cmd.build_dylib(out_filename); - } - if crate_type == CrateType::Executable && sess.crt_static(Some(crate_type)) { - cmd.build_static_executable(); + // FIXME: Merge with the previous `link_output_kind` match, + // and support `StaticPicExe` and `StaticDylib` correctly. + match link_output_kind { + LinkOutputKind::StaticNoPicExe | LinkOutputKind::StaticPicExe => { + cmd.build_static_executable() + } + LinkOutputKind::DynamicDylib | LinkOutputKind::StaticDylib => cmd.build_dylib(out_filename), + _ => {} } // OBJECT-FILES-NO, AUDIT-ORDER @@ -1551,7 +1576,7 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>( add_late_link_args(cmd, sess, flavor, crate_type, codegen_results); // NO-OPT-OUT, OBJECT-FILES-YES - add_post_link_objects(cmd, sess, crate_type); + add_post_link_objects(cmd, sess, link_output_kind, crt_objects_fallback); // NO-OPT-OUT, OBJECT-FILES-MAYBE, CUSTOMIZATION-POINT add_post_link_args(cmd, sess, flavor); diff --git a/src/librustc_codegen_ssa/back/linker.rs b/src/librustc_codegen_ssa/back/linker.rs index 535c4ff092f5f..ee5bcf4b9f58b 100644 --- a/src/librustc_codegen_ssa/back/linker.rs +++ b/src/librustc_codegen_ssa/back/linker.rs @@ -123,6 +123,7 @@ pub trait Linker { fn pgo_gen(&mut self); fn control_flow_guard(&mut self); fn debuginfo(&mut self, strip: Strip); + fn no_crt_objects(&mut self); fn no_default_libraries(&mut self); fn build_dylib(&mut self, out_filename: &Path); fn build_static_executable(&mut self); @@ -266,7 +267,9 @@ impl<'a> Linker for GccLinker<'a> { self.cmd.arg("-pie"); } fn no_position_independent_executable(&mut self) { - self.cmd.arg("-no-pie"); + if !self.is_ld { + self.cmd.arg("-no-pie"); + } } fn full_relro(&mut self) { self.linker_arg("-zrelro"); @@ -404,6 +407,12 @@ impl<'a> Linker for GccLinker<'a> { } } + fn no_crt_objects(&mut self) { + if !self.is_ld { + self.cmd.arg("-nostartfiles"); + } + } + fn no_default_libraries(&mut self) { if !self.is_ld { self.cmd.arg("-nodefaultlibs"); @@ -644,6 +653,10 @@ impl<'a> Linker for MsvcLinker<'a> { // noop } + fn no_crt_objects(&mut self) { + // noop + } + fn no_default_libraries(&mut self) { self.cmd.arg("/NODEFAULTLIB"); } @@ -907,6 +920,8 @@ impl<'a> Linker for EmLinker<'a> { }); } + fn no_crt_objects(&mut self) {} + fn no_default_libraries(&mut self) { self.cmd.args(&["-s", "DEFAULT_LIBRARY_FUNCS_TO_INCLUDE=[]"]); } @@ -1106,6 +1121,8 @@ impl<'a> Linker for WasmLd<'a> { self.sess.warn("Windows Control Flow Guard is not supported by this linker."); } + fn no_crt_objects(&mut self) {} + fn no_default_libraries(&mut self) {} fn build_dylib(&mut self, _out_filename: &Path) { @@ -1271,6 +1288,8 @@ impl<'a> Linker for PtxLinker<'a> { fn pgo_gen(&mut self) {} + fn no_crt_objects(&mut self) {} + fn no_default_libraries(&mut self) {} fn control_flow_guard(&mut self) { diff --git a/src/librustc_target/spec/crt_objects.rs b/src/librustc_target/spec/crt_objects.rs new file mode 100644 index 0000000000000..8991691a9a30c --- /dev/null +++ b/src/librustc_target/spec/crt_objects.rs @@ -0,0 +1,145 @@ +//! Object files providing support for basic runtime facilities and added to the produced binaries +//! at the start and at the end of linking. +//! +//! Table of CRT objects for popular toolchains. +//! The `crtx` ones are generally distributed with libc and the `begin/end` ones with gcc. +//! See https://dev.gentoo.org/~vapier/crt.txt for some more details. +//! +//! | Pre-link CRT objects | glibc | musl | bionic | mingw | wasi | +//! |----------------------|------------------------|------------------------|------------------|-------------------|------| +//! | dynamic-nopic-exe | crt1, crti, crtbegin | crt1, crti, crtbegin | crtbegin_dynamic | crt2, crtbegin | crt1 | +//! | dynamic-pic-exe | Scrt1, crti, crtbeginS | Scrt1, crti, crtbeginS | crtbegin_dynamic | crt2, crtbegin | crt1 | +//! | static-nopic-exe | crt1, crti, crtbeginT | crt1, crti, crtbegin | crtbegin_static | crt2, crtbegin | crt1 | +//! | static-pic-exe | rcrt1, crti, crtbeginS | rcrt1, crti, crtbeginS | crtbegin_dynamic | crt2, crtbegin | crt1 | +//! | dynamic-dylib | crti, crtbeginS | crti, crtbeginS | crtbegin_so | dllcrt2, crtbegin | - | +//! | static-dylib (gcc) | crti, crtbeginT | crti, crtbeginS | crtbegin_so | dllcrt2, crtbegin | - | +//! | static-dylib (clang) | crti, crtbeginT | N/A | crtbegin_static | dllcrt2, crtbegin | - | +//! +//! | Post-link CRT objects | glibc | musl | bionic | mingw | wasi | +//! |-----------------------|---------------|---------------|----------------|--------|------| +//! | dynamic-nopic-exe | crtend, crtn | crtend, crtn | crtend_android | crtend | - | +//! | dynamic-pic-exe | crtendS, crtn | crtendS, crtn | crtend_android | crtend | - | +//! | static-nopic-exe | crtend, crtn | crtend, crtn | crtend_android | crtend | - | +//! | static-pic-exe | crtendS, crtn | crtendS, crtn | crtend_android | crtend | - | +//! | dynamic-dylib | crtendS, crtn | crtendS, crtn | crtend_so | crtend | - | +//! | static-dylib (gcc) | crtend, crtn | crtendS, crtn | crtend_so | crtend | - | +//! | static-dylib (clang) | crtendS, crtn | N/A | crtend_so | crtend | - | +//! +//! Use cases for rustc linking the CRT objects explicitly: +//! - rustc needs to add its own Rust-specific objects (mingw is the example) +//! - gcc wrapper cannot be used for some reason and linker like ld or lld is used directly. +//! - gcc wrapper pulls wrong CRT objects (e.g. from glibc when we are targeting musl). +//! +//! In general it is preferable to rely on the target's native toolchain to pull the objects. +//! However, for some targets (musl, mingw) rustc historically provides a more self-contained +//! installation not requiring users to install the native target's toolchain. +//! In that case rustc distributes the objects as a part of the target's Rust toolchain +//! and falls back to linking with them manually. +//! Unlike native toolchains, rustc only currently adds the libc's objects during linking, +//! but not gcc's. As a result rustc cannot link with C++ static libraries (#36710) +//! when linking in self-contained mode. + +use crate::spec::LinkOutputKind; +use rustc_serialize::json::{Json, ToJson}; +use std::collections::BTreeMap; +use std::str::FromStr; + +pub type CrtObjects = BTreeMap>; + +pub(super) fn new(obj_table: &[(LinkOutputKind, &[&str])]) -> CrtObjects { + obj_table.iter().map(|(z, k)| (*z, k.iter().map(|b| b.to_string()).collect())).collect() +} + +pub(super) fn all(obj: &str) -> CrtObjects { + new(&[ + (LinkOutputKind::DynamicNoPicExe, &[obj]), + (LinkOutputKind::DynamicPicExe, &[obj]), + (LinkOutputKind::StaticNoPicExe, &[obj]), + (LinkOutputKind::StaticPicExe, &[obj]), + (LinkOutputKind::DynamicDylib, &[obj]), + (LinkOutputKind::StaticDylib, &[obj]), + ]) +} + +pub(super) fn pre_musl_fallback() -> CrtObjects { + new(&[ + (LinkOutputKind::DynamicNoPicExe, &["crt1.o", "crti.o"]), + (LinkOutputKind::DynamicPicExe, &["Scrt1.o", "crti.o"]), + (LinkOutputKind::StaticNoPicExe, &["crt1.o", "crti.o"]), + (LinkOutputKind::StaticPicExe, &["rcrt1.o", "crti.o"]), + (LinkOutputKind::DynamicDylib, &["crti.o"]), + (LinkOutputKind::StaticDylib, &["crti.o"]), + ]) +} + +pub(super) fn post_musl_fallback() -> CrtObjects { + all("crtn.o") +} + +pub(super) fn pre_mingw_fallback() -> CrtObjects { + new(&[ + (LinkOutputKind::DynamicNoPicExe, &["crt2.o", "rsbegin.o"]), + (LinkOutputKind::DynamicPicExe, &["crt2.o", "rsbegin.o"]), + (LinkOutputKind::StaticNoPicExe, &["crt2.o", "rsbegin.o"]), + (LinkOutputKind::StaticPicExe, &["crt2.o", "rsbegin.o"]), + (LinkOutputKind::DynamicDylib, &["dllcrt2.o", "rsbegin.o"]), + (LinkOutputKind::StaticDylib, &["dllcrt2.o", "rsbegin.o"]), + ]) +} + +pub(super) fn post_mingw_fallback() -> CrtObjects { + all("rsend.o") +} + +pub(super) fn pre_mingw() -> CrtObjects { + all("rsbegin.o") +} + +pub(super) fn post_mingw() -> CrtObjects { + all("rsend.o") +} + +pub(super) fn pre_wasi_fallback() -> CrtObjects { + new(&[ + (LinkOutputKind::DynamicNoPicExe, &["crt1.o"]), + (LinkOutputKind::DynamicPicExe, &["crt1.o"]), + (LinkOutputKind::StaticNoPicExe, &["crt1.o"]), + (LinkOutputKind::StaticPicExe, &["crt1.o"]), + ]) +} + +pub(super) fn post_wasi_fallback() -> CrtObjects { + new(&[]) +} + +/// Which logic to use to determine whether to fall back to the "self-contained" mode or not. +#[derive(Clone, Copy, PartialEq, Hash, Debug)] +pub enum CrtObjectsFallback { + Musl, + Mingw, + Wasm, +} + +impl FromStr for CrtObjectsFallback { + type Err = (); + + fn from_str(s: &str) -> Result { + Ok(match s { + "musl" => CrtObjectsFallback::Musl, + "mingw" => CrtObjectsFallback::Mingw, + "wasm" => CrtObjectsFallback::Wasm, + _ => return Err(()), + }) + } +} + +impl ToJson for CrtObjectsFallback { + fn to_json(&self) -> Json { + match *self { + CrtObjectsFallback::Musl => "musl", + CrtObjectsFallback::Mingw => "mingw", + CrtObjectsFallback::Wasm => "wasm", + } + .to_json() + } +} diff --git a/src/librustc_target/spec/fuchsia_base.rs b/src/librustc_target/spec/fuchsia_base.rs index 4060b126cddb7..96b5328e1ee46 100644 --- a/src/librustc_target/spec/fuchsia_base.rs +++ b/src/librustc_target/spec/fuchsia_base.rs @@ -1,4 +1,4 @@ -use crate::spec::{LinkArgs, LinkerFlavor, LldFlavor, TargetOptions}; +use crate::spec::{crt_objects, LinkArgs, LinkOutputKind, LinkerFlavor, LldFlavor, TargetOptions}; pub fn opts() -> TargetOptions { let mut pre_link_args = LinkArgs::new(); @@ -23,7 +23,12 @@ pub fn opts() -> TargetOptions { linker_is_gnu: true, has_rpath: false, pre_link_args, - pre_link_objects_exe: vec!["Scrt1.o".to_string()], + pre_link_objects: crt_objects::new(&[ + (LinkOutputKind::DynamicNoPicExe, &["Scrt1.o"]), + (LinkOutputKind::DynamicPicExe, &["Scrt1.o"]), + (LinkOutputKind::StaticNoPicExe, &["Scrt1.o"]), + (LinkOutputKind::StaticPicExe, &["Scrt1.o"]), + ]), position_independent_executables: true, has_elf_tls: true, ..Default::default() diff --git a/src/librustc_target/spec/linux_musl_base.rs b/src/librustc_target/spec/linux_musl_base.rs index e294e63982de4..0fdd876080677 100644 --- a/src/librustc_target/spec/linux_musl_base.rs +++ b/src/librustc_target/spec/linux_musl_base.rs @@ -1,29 +1,18 @@ +use crate::spec::crt_objects::{self, CrtObjectsFallback}; use crate::spec::{LinkerFlavor, TargetOptions}; pub fn opts() -> TargetOptions { let mut base = super::linux_base::opts(); - // Make sure that the linker/gcc really don't pull in anything, including - // default objects, libs, etc. - base.pre_link_args_crt.insert(LinkerFlavor::Gcc, Vec::new()); - base.pre_link_args_crt.get_mut(&LinkerFlavor::Gcc).unwrap().push("-nostdlib".to_string()); - // At least when this was tested, the linker would not add the // `GNU_EH_FRAME` program header to executables generated, which is required // when unwinding to locate the unwinding information. I'm not sure why this // argument is *not* necessary for normal builds, but it can't hurt! base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-Wl,--eh-frame-hdr".to_string()); - // When generating a statically linked executable there's generally some - // small setup needed which is listed in these files. These are provided by - // a musl toolchain and are linked by default by the `musl-gcc` script. Note - // that `gcc` also does this by default, it just uses some different files. - // - // Each target directory for musl has these object files included in it so - // they'll be included from there. - base.pre_link_objects_exe_crt.push("crt1.o".to_string()); - base.pre_link_objects_exe_crt.push("crti.o".to_string()); - base.post_link_objects_crt.push("crtn.o".to_string()); + base.pre_link_objects_fallback = crt_objects::pre_musl_fallback(); + base.post_link_objects_fallback = crt_objects::post_musl_fallback(); + base.crt_objects_fallback = Some(CrtObjectsFallback::Musl); // These targets statically link libc by default base.crt_static_default = true; diff --git a/src/librustc_target/spec/mod.rs b/src/librustc_target/spec/mod.rs index 477161dc658d9..d39ab55fd97b7 100644 --- a/src/librustc_target/spec/mod.rs +++ b/src/librustc_target/spec/mod.rs @@ -35,6 +35,7 @@ //! to the list specified by the target, rather than replace. use crate::spec::abi::{lookup as lookup_abi, Abi}; +use crate::spec::crt_objects::{CrtObjects, CrtObjectsFallback}; use rustc_serialize::json::{Json, ToJson}; use std::collections::BTreeMap; use std::path::{Path, PathBuf}; @@ -44,6 +45,8 @@ use std::{fmt, io}; use rustc_macros::HashStable_Generic; pub mod abi; +pub mod crt_objects; + mod android_base; mod apple_base; mod apple_sdk_base; @@ -341,6 +344,54 @@ impl ToJson for TlsModel { } } +/// Everything is flattened to a single enum to make the json encoding/decoding less annoying. +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] +pub enum LinkOutputKind { + /// Dynamically linked non position-independent executable. + DynamicNoPicExe, + /// Dynamically linked position-independent executable. + DynamicPicExe, + /// Statically linked non position-independent executable. + StaticNoPicExe, + /// Statically linked position-independent executable. + StaticPicExe, + /// Regular dynamic library ("dynamically linked"). + DynamicDylib, + /// Dynamic library with bundled libc ("statically linked"). + StaticDylib, +} + +impl LinkOutputKind { + fn as_str(&self) -> &'static str { + match self { + LinkOutputKind::DynamicNoPicExe => "dynamic-nopic-exe", + LinkOutputKind::DynamicPicExe => "dynamic-pic-exe", + LinkOutputKind::StaticNoPicExe => "static-nopic-exe", + LinkOutputKind::StaticPicExe => "static-pic-exe", + LinkOutputKind::DynamicDylib => "dynamic-dylib", + LinkOutputKind::StaticDylib => "static-dylib", + } + } + + pub(super) fn from_str(s: &str) -> Option { + Some(match s { + "dynamic-nopic-exe" => LinkOutputKind::DynamicNoPicExe, + "dynamic-pic-exe" => LinkOutputKind::DynamicPicExe, + "static-nopic-exe" => LinkOutputKind::StaticNoPicExe, + "static-pic-exe" => LinkOutputKind::StaticPicExe, + "dynamic-dylib" => LinkOutputKind::DynamicDylib, + "static-dylib" => LinkOutputKind::StaticDylib, + _ => return None, + }) + } +} + +impl fmt::Display for LinkOutputKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.as_str()) + } +} + pub enum LoadTargetError { BuiltinTargetNotFound(String), Other(String), @@ -644,13 +695,19 @@ pub struct TargetOptions { /// Linker arguments that are passed *before* any user-defined libraries. pub pre_link_args: LinkArgs, // ... unconditionally pub pre_link_args_crt: LinkArgs, // ... when linking with a bundled crt - /// Objects to link before all others, always found within the - /// sysroot folder. - pub pre_link_objects_exe: Vec, // ... when linking an executable, unconditionally - pub pre_link_objects_exe_crt: Vec, // ... when linking an executable with a bundled crt - pub pre_link_objects_dll: Vec, // ... when linking a dylib + /// Objects to link before and after all other object code. + pub pre_link_objects: CrtObjects, + pub post_link_objects: CrtObjects, + /// Same as `(pre|post)_link_objects`, but when we fail to pull the objects with help of the + /// target's native gcc and fall back to the "self-contained" mode and pull them manually. + /// See `crt_objects.rs` for some more detailed documentation. + pub pre_link_objects_fallback: CrtObjects, + pub post_link_objects_fallback: CrtObjects, + /// Which logic to use to determine whether to fall back to the "self-contained" mode or not. + pub crt_objects_fallback: Option, + /// Linker arguments that are unconditionally passed after any - /// user-defined but before post_link_objects. Standard platform + /// user-defined but before post-link objects. Standard platform /// libraries that should be always be linked to, usually go here. pub late_link_args: LinkArgs, /// Linker arguments used in addition to `late_link_args` if at least one @@ -659,10 +716,6 @@ pub struct TargetOptions { /// Linker arguments used in addition to `late_link_args` if aall Rust /// dependencies are statically linked. pub late_link_args_static: LinkArgs, - /// Objects to link after all others, always found within the - /// sysroot folder. - pub post_link_objects: Vec, // ... unconditionally - pub post_link_objects_crt: Vec, // ... when linking with a bundled crt /// Linker arguments that are unconditionally passed *after* any /// user-defined libraries. pub post_link_args: LinkArgs, @@ -932,11 +985,11 @@ impl Default for TargetOptions { position_independent_executables: false, needs_plt: false, relro_level: RelroLevel::None, - pre_link_objects_exe: Vec::new(), - pre_link_objects_exe_crt: Vec::new(), - pre_link_objects_dll: Vec::new(), - post_link_objects: Vec::new(), - post_link_objects_crt: Vec::new(), + pre_link_objects: Default::default(), + post_link_objects: Default::default(), + pre_link_objects_fallback: Default::default(), + post_link_objects_fallback: Default::default(), + crt_objects_fallback: None, late_link_args: LinkArgs::new(), late_link_args_dynamic: LinkArgs::new(), late_link_args_static: LinkArgs::new(), @@ -1191,6 +1244,45 @@ impl Target { }) })).unwrap_or(Ok(())) } ); + ($key_name:ident, crt_objects_fallback) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + obj.find(&name[..]).and_then(|o| o.as_string().and_then(|s| { + match s.parse::() { + Ok(fallback) => base.options.$key_name = Some(fallback), + _ => return Some(Err(format!("'{}' is not a valid CRT objects fallback. \ + Use 'musl', 'mingw' or 'wasm'", s))), + } + Some(Ok(())) + })).unwrap_or(Ok(())) + } ); + ($key_name:ident, link_objects) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + if let Some(val) = obj.find(&name[..]) { + let obj = val.as_object().ok_or_else(|| format!("{}: expected a \ + JSON object with fields per CRT object kind.", name))?; + let mut args = CrtObjects::new(); + for (k, v) in obj { + let kind = LinkOutputKind::from_str(&k).ok_or_else(|| { + format!("{}: '{}' is not a valid value for CRT object kind. \ + Use '(dynamic,static)-(nopic,pic)-exe' or \ + '(dynamic,static)-dylib'", name, k) + })?; + + let v = v.as_array().ok_or_else(|| + format!("{}.{}: expected a JSON array", name, k) + )?.iter().enumerate() + .map(|(i,s)| { + let s = s.as_string().ok_or_else(|| + format!("{}.{}[{}]: expected a JSON string", name, k, i))?; + Ok(s.to_owned()) + }) + .collect::, String>>()?; + + args.insert(kind, v); + } + base.options.$key_name = args; + } + } ); ($key_name:ident, link_args) => ( { let name = (stringify!($key_name)).replace("_", "-"); if let Some(val) = obj.find(&name[..]) { @@ -1238,16 +1330,16 @@ impl Target { key!(is_builtin, bool); key!(linker, optional); key!(lld_flavor, LldFlavor)?; + key!(pre_link_objects, link_objects); + key!(post_link_objects, link_objects); + key!(pre_link_objects_fallback, link_objects); + key!(post_link_objects_fallback, link_objects); + key!(crt_objects_fallback, crt_objects_fallback)?; key!(pre_link_args, link_args); key!(pre_link_args_crt, link_args); - key!(pre_link_objects_exe, list); - key!(pre_link_objects_exe_crt, list); - key!(pre_link_objects_dll, list); key!(late_link_args, link_args); key!(late_link_args_dynamic, link_args); key!(late_link_args_static, link_args); - key!(post_link_objects, list); - key!(post_link_objects_crt, list); key!(post_link_args, link_args); key!(link_env, env); key!(link_env_remove, list); @@ -1468,16 +1560,16 @@ impl ToJson for Target { target_option_val!(is_builtin); target_option_val!(linker); target_option_val!(lld_flavor); + target_option_val!(pre_link_objects); + target_option_val!(post_link_objects); + target_option_val!(pre_link_objects_fallback); + target_option_val!(post_link_objects_fallback); + target_option_val!(crt_objects_fallback); target_option_val!(link_args - pre_link_args); target_option_val!(link_args - pre_link_args_crt); - target_option_val!(pre_link_objects_exe); - target_option_val!(pre_link_objects_exe_crt); - target_option_val!(pre_link_objects_dll); target_option_val!(link_args - late_link_args); target_option_val!(link_args - late_link_args_dynamic); target_option_val!(link_args - late_link_args_static); - target_option_val!(post_link_objects); - target_option_val!(post_link_objects_crt); target_option_val!(link_args - post_link_args); target_option_val!(env - link_env); target_option_val!(link_env_remove); diff --git a/src/librustc_target/spec/wasm32_base.rs b/src/librustc_target/spec/wasm32_base.rs index bb19b9d00e8bf..d4a65aa1a2574 100644 --- a/src/librustc_target/spec/wasm32_base.rs +++ b/src/librustc_target/spec/wasm32_base.rs @@ -1,3 +1,4 @@ +use super::crt_objects::CrtObjectsFallback; use super::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel, TargetOptions, TlsModel}; use std::collections::BTreeMap; @@ -123,6 +124,8 @@ pub fn options() -> TargetOptions { pre_link_args, + crt_objects_fallback: Some(CrtObjectsFallback::Wasm), + // This has no effect in LLVM 8 or prior, but in LLVM 9 and later when // PIC code is implemented this has quite a drastric effect if it stays // at the default, `pic`. In an effort to keep wasm binaries as minimal diff --git a/src/librustc_target/spec/wasm32_unknown_unknown.rs b/src/librustc_target/spec/wasm32_unknown_unknown.rs index 22d3885e4afa7..ded95a34d55d4 100644 --- a/src/librustc_target/spec/wasm32_unknown_unknown.rs +++ b/src/librustc_target/spec/wasm32_unknown_unknown.rs @@ -21,10 +21,6 @@ pub fn target() -> Result { // otherwise clang_args.push("--target=wasm32-unknown-unknown".to_string()); - // Disable attempting to link crt1.o since it typically isn't present and - // isn't needed currently. - clang_args.push("-nostdlib".to_string()); - // For now this target just never has an entry symbol no matter the output // type, so unconditionally pass this. clang_args.push("-Wl,--no-entry".to_string()); diff --git a/src/librustc_target/spec/wasm32_wasi.rs b/src/librustc_target/spec/wasm32_wasi.rs index d5ef230dcf7d2..0bba7bdd4735c 100644 --- a/src/librustc_target/spec/wasm32_wasi.rs +++ b/src/librustc_target/spec/wasm32_wasi.rs @@ -73,7 +73,7 @@ //! you know what you're getting in to! use super::wasm32_base; -use super::{LinkerFlavor, LldFlavor, Target}; +use super::{crt_objects, LinkerFlavor, LldFlavor, Target}; pub fn target() -> Result { let mut options = wasm32_base::options(); @@ -84,9 +84,8 @@ pub fn target() -> Result { .or_insert(Vec::new()) .push("--target=wasm32-wasi".to_string()); - // When generating an executable be sure to put the startup object at the - // front so the main function is correctly hooked up. - options.pre_link_objects_exe_crt.push("crt1.o".to_string()); + options.pre_link_objects_fallback = crt_objects::pre_wasi_fallback(); + options.post_link_objects_fallback = crt_objects::post_wasi_fallback(); // Right now this is a bit of a workaround but we're currently saying that // the target by default has a static crt which we're taking as a signal diff --git a/src/librustc_target/spec/windows_gnu_base.rs b/src/librustc_target/spec/windows_gnu_base.rs index 33ecb1d0d48ce..f556bf03f02a0 100644 --- a/src/librustc_target/spec/windows_gnu_base.rs +++ b/src/librustc_target/spec/windows_gnu_base.rs @@ -1,3 +1,4 @@ +use crate::spec::crt_objects::{self, CrtObjectsFallback}; use crate::spec::{LinkArgs, LinkerFlavor, TargetOptions}; pub fn opts() -> TargetOptions { @@ -10,8 +11,6 @@ pub fn opts() -> TargetOptions { "-fno-use-linker-plugin".to_string(), // Always enable DEP (NX bit) when it is available "-Wl,--nxcompat".to_string(), - // Do not use the standard system startup files or libraries when linking - "-nostdlib".to_string(), ], ); @@ -80,18 +79,14 @@ pub fn opts() -> TargetOptions { is_like_windows: true, allows_weak_linkage: false, pre_link_args, - pre_link_objects_exe: vec![ - "crt2.o".to_string(), // mingw C runtime initialization for executables - "rsbegin.o".to_string(), // Rust compiler runtime initialization, see rsbegin.rs - ], - pre_link_objects_dll: vec![ - "dllcrt2.o".to_string(), // mingw C runtime initialization for dlls - "rsbegin.o".to_string(), - ], + pre_link_objects: crt_objects::pre_mingw(), + post_link_objects: crt_objects::post_mingw(), + pre_link_objects_fallback: crt_objects::pre_mingw_fallback(), + post_link_objects_fallback: crt_objects::post_mingw_fallback(), + crt_objects_fallback: Some(CrtObjectsFallback::Mingw), late_link_args, late_link_args_dynamic, late_link_args_static, - post_link_objects: vec!["rsend.o".to_string()], abi_return_struct_as_int: true, emit_debug_gdb_scripts: false, requires_uwtable: true, diff --git a/src/librustc_target/spec/windows_uwp_gnu_base.rs b/src/librustc_target/spec/windows_uwp_gnu_base.rs index dd3b60344be22..e12a37144da5e 100644 --- a/src/librustc_target/spec/windows_uwp_gnu_base.rs +++ b/src/librustc_target/spec/windows_uwp_gnu_base.rs @@ -3,20 +3,8 @@ use crate::spec::{LinkArgs, LinkerFlavor, TargetOptions}; pub fn opts() -> TargetOptions { let base = super::windows_gnu_base::opts(); - // FIXME: Consider adding `-nostdlib` and inheriting from `windows_gnu_base`. - let mut pre_link_args = LinkArgs::new(); - pre_link_args.insert( - LinkerFlavor::Gcc, - vec![ - // Tell GCC to avoid linker plugins, because we are not bundling - // them with Windows installer, and Rust does its own LTO anyways. - "-fno-use-linker-plugin".to_string(), - // Always enable DEP (NX bit) when it is available - "-Wl,--nxcompat".to_string(), - ], - ); - - // FIXME: This should be updated for the exception machinery changes from #67502. + // FIXME: This should be updated for the exception machinery changes from #67502 + // and inherit from `windows_gnu_base`, at least partially. let mut late_link_args = LinkArgs::new(); let late_link_args_dynamic = LinkArgs::new(); let late_link_args_static = LinkArgs::new(); @@ -40,11 +28,6 @@ pub fn opts() -> TargetOptions { TargetOptions { executables: false, limit_rdylib_exports: false, - pre_link_args, - // FIXME: Consider adding `-nostdlib` and inheriting from `windows_gnu_base`. - pre_link_objects_exe: vec!["rsbegin.o".to_string()], - // FIXME: Consider adding `-nostdlib` and inheriting from `windows_gnu_base`. - pre_link_objects_dll: vec!["rsbegin.o".to_string()], late_link_args, late_link_args_dynamic, late_link_args_static, diff --git a/src/librustc_target/spec/x86_64_fortanix_unknown_sgx.rs b/src/librustc_target/spec/x86_64_fortanix_unknown_sgx.rs index 3e9552ef0cf34..d26efc0985952 100644 --- a/src/librustc_target/spec/x86_64_fortanix_unknown_sgx.rs +++ b/src/librustc_target/spec/x86_64_fortanix_unknown_sgx.rs @@ -1,6 +1,6 @@ use std::iter; -use super::{LinkerFlavor, LldFlavor, PanicStrategy, Target, TargetOptions}; +use super::{crt_objects, LinkerFlavor, LldFlavor, PanicStrategy, Target, TargetOptions}; pub fn target() -> Result { const PRE_LINK_ARGS: &[&str] = &[ @@ -68,7 +68,8 @@ pub fn target() -> Result { PRE_LINK_ARGS.iter().cloned().map(String::from).collect(), )) .collect(), - post_link_objects: vec!["libunwind.a".into()], + // FIXME: libunwind is certainly not a CRT object, use some other option instead. + post_link_objects: crt_objects::all("libunwind.a"), override_export_symbols: Some(EXPORT_SYMBOLS.iter().cloned().map(String::from).collect()), relax_elf_relocations: true, ..Default::default() diff --git a/src/libserialize/json.rs b/src/libserialize/json.rs index 8f46649048aa9..2d4e953ac5165 100644 --- a/src/libserialize/json.rs +++ b/src/libserialize/json.rs @@ -2684,11 +2684,11 @@ impl ToJson for Vec { } } -impl ToJson for BTreeMap { +impl ToJson for BTreeMap { fn to_json(&self) -> Json { let mut d = BTreeMap::new(); for (key, value) in self { - d.insert((*key).clone(), value.to_json()); + d.insert(key.to_string(), value.to_json()); } Json::Object(d) }