about summary refs log tree commit diff
diff options
context:
space:
mode:
authorRémy Rakic <remy.rakic+github@gmail.com>2023-09-20 22:13:43 +0000
committerRémy Rakic <remy.rakic+github@gmail.com>2023-10-18 09:26:05 +0000
commit0bca45f620ef436f2101d24d88c1f5a6b79eb988 (patch)
tree20f6c2e861a7b55cbcc71f9da5c73a64db96e8be
parent39acbed8d6d3d87ca05f204ed0309fc44e5ffa37 (diff)
downloadrust-0bca45f620ef436f2101d24d88c1f5a6b79eb988.tar.gz
rust-0bca45f620ef436f2101d24d88c1f5a6b79eb988.zip
allow target specs to declare self-contained linking components
-rw-r--r--compiler/rustc_codegen_ssa/src/back/link.rs39
-rw-r--r--compiler/rustc_target/src/spec/linux_musl_base.rs5
-rw-r--r--compiler/rustc_target/src/spec/mod.rs159
-rw-r--r--compiler/rustc_target/src/spec/tests/tests_impl.rs2
-rw-r--r--compiler/rustc_target/src/spec/wasm32_wasi.rs5
-rw-r--r--compiler/rustc_target/src/spec/wasm32_wasi_preview1_threads.rs5
-rw-r--r--compiler/rustc_target/src/spec/wasm_base.rs4
-rw-r--r--compiler/rustc_target/src/spec/windows_gnu_base.rs5
8 files changed, 197 insertions, 27 deletions
diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs
index 28a51711b93..9a796356593 100644
--- a/compiler/rustc_codegen_ssa/src/back/link.rs
+++ b/compiler/rustc_codegen_ssa/src/back/link.rs
@@ -22,7 +22,8 @@ use rustc_session::utils::NativeLibKind;
 /// 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::crt_objects::{CrtObjects, LinkSelfContainedDefault};
+use rustc_target::spec::crt_objects::CrtObjects;
+use rustc_target::spec::LinkSelfContained;
 use rustc_target::spec::{Cc, LinkOutputKind, LinkerFlavor, Lld, PanicStrategy};
 use rustc_target::spec::{RelocModel, RelroLevel, SanitizerSet, SplitDebuginfo};
 
@@ -1703,21 +1704,37 @@ fn detect_self_contained_mingw(sess: &Session) -> bool {
 /// instead of being found somewhere on the host system.
 /// We only provide such support for a very limited number of targets.
 fn self_contained(sess: &Session, crate_type: CrateType) -> bool {
+    // Emit an error if the user requested self-contained mode on the CLI but the target explicitly
+    // refuses it.
     if let Some(self_contained) = sess.opts.cg.link_self_contained.explicitly_set {
-        if sess.target.link_self_contained == LinkSelfContainedDefault::False {
+        if sess.target.link_self_contained.is_disabled() {
             sess.emit_err(errors::UnsupportedLinkSelfContained);
         }
         return self_contained;
     }
 
     match sess.target.link_self_contained {
-        LinkSelfContainedDefault::False => false,
-        LinkSelfContainedDefault::True => true,
+        LinkSelfContained::True => true,
+        LinkSelfContained::False => false,
+        LinkSelfContained::WithComponents(components) => {
+            if components.is_all() {
+                true
+            } else if components.is_empty() {
+                false
+            } else {
+                // FIXME: Currently no target makes use of individual components to mean
+                // self-contained linking is fully enabled, in the sense of what the code downstream
+                // from here expects. Until components are handled a bit more deeply, we can
+                // consider that it's disabled and remain backwards compatible.
+                false
+            }
+        }
+
         // 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).
-        LinkSelfContainedDefault::Musl => sess.crt_static(Some(crate_type)),
-        LinkSelfContainedDefault::Mingw => {
+        LinkSelfContained::InferredForMusl => sess.crt_static(Some(crate_type)),
+        LinkSelfContained::InferredForMingw => {
             sess.host == sess.target
                 && sess.target.vendor != "uwp"
                 && detect_self_contained_mingw(&sess)
@@ -2978,9 +2995,13 @@ fn add_lld_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) {
     }
 
     // 1. Implement the "self-contained" part of this feature by adding rustc distribution
-    // directories to the tool's search path:
-    // - if the self-contained linker is enabled on the CLI.
-    if sess.opts.cg.link_self_contained.is_linker_enabled() {
+    // directories to the tool's search path, depending on a mix between what users can specify on
+    // the CLI, and what the target spec enables (as it can't disable components):
+    // - if the self-contained linker is enabled on the CLI or by the target spec,
+    // - and if the self-contained linker is not disabled on the CLI.
+    let self_contained_linker = sess.opts.cg.link_self_contained.is_linker_enabled()
+        || sess.target.options.link_self_contained.is_linker_enabled();
+    if self_contained_linker && !sess.opts.cg.link_self_contained.is_linker_disabled() {
         for path in sess.get_tools_search_paths(false) {
             cmd.arg({
                 let mut arg = OsString::from("-B");
diff --git a/compiler/rustc_target/src/spec/linux_musl_base.rs b/compiler/rustc_target/src/spec/linux_musl_base.rs
index 61553e71b45..318d06e9889 100644
--- a/compiler/rustc_target/src/spec/linux_musl_base.rs
+++ b/compiler/rustc_target/src/spec/linux_musl_base.rs
@@ -1,4 +1,5 @@
-use crate::spec::crt_objects::{self, LinkSelfContainedDefault};
+use crate::spec::crt_objects;
+use crate::spec::LinkSelfContained;
 use crate::spec::TargetOptions;
 
 pub fn opts() -> TargetOptions {
@@ -7,7 +8,7 @@ pub fn opts() -> TargetOptions {
     base.env = "musl".into();
     base.pre_link_objects_self_contained = crt_objects::pre_musl_self_contained();
     base.post_link_objects_self_contained = crt_objects::post_musl_self_contained();
-    base.link_self_contained = LinkSelfContainedDefault::Musl;
+    base.link_self_contained = LinkSelfContained::InferredForMusl;
 
     // These targets statically link libc by default
     base.crt_static_default = true;
diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs
index 16f70cf43b3..8c41acdf4c6 100644
--- a/compiler/rustc_target/src/spec/mod.rs
+++ b/compiler/rustc_target/src/spec/mod.rs
@@ -520,6 +520,92 @@ impl ToJson for LinkerFlavorCli {
     }
 }
 
+/// The different `-Clink-self-contained` options that can be specified in a target spec:
+/// - enabling or disabling in bulk
+/// - some target-specific pieces of inference to determine whether to use self-contained linking
+///   if `-Clink-self-contained` is not specified explicitly (e.g. on musl/mingw)
+/// - explicitly enabling some of the self-contained linking components, e.g. the linker component
+///   to use `rust-lld`
+#[derive(Clone, Copy, PartialEq, Debug)]
+pub enum LinkSelfContained {
+    /// The target spec explicitly enables self-contained linking.
+    True,
+
+    /// The target spec explicitly disables self-contained linking.
+    False,
+
+    /// The target spec requests that the self-contained mode is inferred, in the context of musl.
+    InferredForMusl,
+
+    /// The target spec requests that the self-contained mode is inferred, in the context of mingw.
+    InferredForMingw,
+
+    /// The target spec explicitly enables a list of self-contained linking components: e.g. for
+    /// targets opting into a subset of components like the CLI's `-C link-self-contained=+linker`.
+    WithComponents(LinkSelfContainedComponents),
+}
+
+impl ToJson for LinkSelfContained {
+    fn to_json(&self) -> Json {
+        match *self {
+            LinkSelfContained::WithComponents(components) => {
+                // Serialize the components in a json object's `components` field, to prepare for a
+                // future where `crt-objects-fallback` is removed from the json specs and
+                // incorporated as a field here.
+                let mut map = BTreeMap::new();
+                map.insert("components", components);
+                map.to_json()
+            }
+
+            // Stable values backwards-compatible with `LinkSelfContainedDefault`
+            LinkSelfContained::True => "true".to_json(),
+            LinkSelfContained::False => "false".to_json(),
+            LinkSelfContained::InferredForMusl => "musl".to_json(),
+            LinkSelfContained::InferredForMingw => "mingw".to_json(),
+        }
+    }
+}
+
+impl LinkSelfContained {
+    /// Returns whether the target spec has self-contained linking explicitly disabled. Used to emit
+    /// errors if the user then enables it on the CLI.
+    pub fn is_disabled(self) -> bool {
+        self == LinkSelfContained::False
+    }
+
+    /// Returns whether the target spec explictly requests self-contained linking, i.e. not via
+    /// inference.
+    pub fn is_linker_enabled(self) -> bool {
+        match self {
+            LinkSelfContained::True => true,
+            LinkSelfContained::False => false,
+            LinkSelfContained::WithComponents(c) => c.contains(LinkSelfContainedComponents::LINKER),
+            _ => false,
+        }
+    }
+
+    /// Returns the key to use when serializing the setting to json:
+    /// - individual components in a `link-self-contained` object value
+    /// - the other variants as a backwards-compatible `crt-objects-fallback` string
+    fn json_key(self) -> &'static str {
+        match self {
+            LinkSelfContained::WithComponents(_) => "link-self-contained",
+            _ => "crt-objects-fallback",
+        }
+    }
+}
+
+impl From<LinkSelfContainedDefault> for LinkSelfContained {
+    fn from(value: LinkSelfContainedDefault) -> Self {
+        match value {
+            LinkSelfContainedDefault::True => LinkSelfContained::True,
+            LinkSelfContainedDefault::False => LinkSelfContained::False,
+            LinkSelfContainedDefault::Musl => LinkSelfContained::InferredForMusl,
+            LinkSelfContainedDefault::Mingw => LinkSelfContained::InferredForMingw,
+        }
+    }
+}
+
 bitflags::bitflags! {
     #[derive(Default)]
     /// The `-C link-self-contained` components that can individually be enabled or disabled.
@@ -594,6 +680,22 @@ impl IntoIterator for LinkSelfContainedComponents {
     }
 }
 
+impl ToJson for LinkSelfContainedComponents {
+    fn to_json(&self) -> Json {
+        let components: Vec<_> = Self::all_components()
+            .into_iter()
+            .filter(|c| self.contains(*c))
+            .map(|c| {
+                // We can unwrap because we're iterating over all the known singular components,
+                // not an actual set of flags where `as_str` can fail.
+                c.as_str().unwrap().to_owned()
+            })
+            .collect();
+
+        components.to_json()
+    }
+}
+
 #[derive(Clone, Copy, Debug, PartialEq, Hash, Encodable, Decodable, HashStable_Generic)]
 pub enum PanicStrategy {
     Unwind,
@@ -1769,7 +1871,9 @@ pub struct TargetOptions {
     /// Same as `(pre|post)_link_objects`, but when self-contained linking mode is enabled.
     pub pre_link_objects_self_contained: CrtObjects,
     pub post_link_objects_self_contained: CrtObjects,
-    pub link_self_contained: LinkSelfContainedDefault,
+    /// Behavior for the self-contained linking mode: inferred for some targets, or explicitly
+    /// enabled (in bulk, or with individual components).
+    pub link_self_contained: LinkSelfContained,
 
     /// Linker arguments that are passed *before* any user-defined libraries.
     pub pre_link_args: LinkArgs,
@@ -2242,7 +2346,7 @@ impl Default for TargetOptions {
             post_link_objects: Default::default(),
             pre_link_objects_self_contained: Default::default(),
             post_link_objects_self_contained: Default::default(),
-            link_self_contained: LinkSelfContainedDefault::False,
+            link_self_contained: LinkSelfContained::False,
             pre_link_args: LinkArgs::new(),
             pre_link_args_json: LinkArgsCli::new(),
             late_link_args: LinkArgs::new(),
@@ -2723,12 +2827,47 @@ impl Target {
                 }
                 Ok::<(), String>(())
             } );
-
-            ($key_name:ident = $json_name:expr, link_self_contained) => ( {
+            ($key_name:ident, LinkSelfContained) => ( {
+                // Skeleton of what needs to be parsed:
+                //
+                // ```
+                // $name: {
+                //     "components": [
+                //         <array of strings>
+                //     ]
+                // }
+                // ```
+                let name = (stringify!($key_name)).replace("_", "-");
+                if let Some(o) = obj.remove(&name) {
+                    if let Some(o) = o.as_object() {
+                        let component_array = o.get("components")
+                            .ok_or_else(|| format!("{name}: expected a \
+                                JSON object with a `components` field."))?;
+                        let component_array = component_array.as_array()
+                            .ok_or_else(|| format!("{name}.components: expected a JSON array"))?;
+                        let mut components = LinkSelfContainedComponents::empty();
+                        for s in component_array {
+                            components |= match s.as_str() {
+                                Some(s) => {
+                                    LinkSelfContainedComponents::from_str(s)
+                                        .ok_or_else(|| format!("unknown \
+                                        `-Clink-self-contained` component: {s}"))?
+                                },
+                                _ => return Err(format!("not a string: {:?}", s)),
+                            };
+                        }
+                        base.$key_name = LinkSelfContained::WithComponents(components);
+                    } else {
+                        incorrect_type.push(name)
+                    }
+                }
+                Ok::<(), String>(())
+            } );
+            ($key_name:ident = $json_name:expr, LinkSelfContainedDefault) => ( {
                 let name = $json_name;
                 obj.remove(name).and_then(|o| o.as_str().and_then(|s| {
                     match s.parse::<LinkSelfContainedDefault>() {
-                        Ok(lsc_default) => base.$key_name = lsc_default,
+                        Ok(lsc_default) => base.$key_name = lsc_default.into(),
                         _ => return Some(Err(format!("'{}' is not a valid `-Clink-self-contained` default. \
                                                       Use 'false', 'true', 'musl' or 'mingw'", s))),
                     }
@@ -2877,7 +3016,10 @@ impl Target {
         key!(post_link_objects = "post-link-objects", link_objects);
         key!(pre_link_objects_self_contained = "pre-link-objects-fallback", link_objects);
         key!(post_link_objects_self_contained = "post-link-objects-fallback", link_objects);
-        key!(link_self_contained = "crt-objects-fallback", link_self_contained)?;
+        // Deserializes the backwards-compatible variants of `-Clink-self-contained`
+        key!(link_self_contained = "crt-objects-fallback", LinkSelfContainedDefault)?;
+        // Deserializes the components variant of `-Clink-self-contained`
+        key!(link_self_contained, LinkSelfContained)?;
         key!(pre_link_args_json = "pre-link-args", link_args);
         key!(late_link_args_json = "late-link-args", link_args);
         key!(late_link_args_dynamic_json = "late-link-args-dynamic", link_args);
@@ -3133,7 +3275,6 @@ impl ToJson for Target {
         target_option_val!(post_link_objects);
         target_option_val!(pre_link_objects_self_contained, "pre-link-objects-fallback");
         target_option_val!(post_link_objects_self_contained, "post-link-objects-fallback");
-        target_option_val!(link_self_contained, "crt-objects-fallback");
         target_option_val!(link_args - pre_link_args_json, "pre-link-args");
         target_option_val!(link_args - late_link_args_json, "late-link-args");
         target_option_val!(link_args - late_link_args_dynamic_json, "late-link-args-dynamic");
@@ -3230,6 +3371,10 @@ impl ToJson for Target {
             d.insert("default-adjusted-cabi".into(), Abi::name(abi).to_json());
         }
 
+        // Serializing `-Clink-self-contained` needs a dynamic key to support the
+        // backwards-compatible variants.
+        d.insert(self.link_self_contained.json_key().into(), self.link_self_contained.to_json());
+
         Json::Object(d)
     }
 }
diff --git a/compiler/rustc_target/src/spec/tests/tests_impl.rs b/compiler/rustc_target/src/spec/tests/tests_impl.rs
index e0ecf8037c3..257867b1b80 100644
--- a/compiler/rustc_target/src/spec/tests/tests_impl.rs
+++ b/compiler/rustc_target/src/spec/tests/tests_impl.rs
@@ -97,7 +97,7 @@ impl Target {
             );
         }
 
-        if self.link_self_contained == LinkSelfContainedDefault::False {
+        if self.link_self_contained.is_disabled() {
             assert!(
                 self.pre_link_objects_self_contained.is_empty()
                     && self.post_link_objects_self_contained.is_empty()
diff --git a/compiler/rustc_target/src/spec/wasm32_wasi.rs b/compiler/rustc_target/src/spec/wasm32_wasi.rs
index a0476d542e6..2c00c601b39 100644
--- a/compiler/rustc_target/src/spec/wasm32_wasi.rs
+++ b/compiler/rustc_target/src/spec/wasm32_wasi.rs
@@ -72,7 +72,8 @@
 //! best we can with this target. Don't start relying on too much here unless
 //! you know what you're getting in to!
 
-use super::crt_objects::{self, LinkSelfContainedDefault};
+use super::crt_objects;
+use super::LinkSelfContained;
 use super::{wasm_base, Cc, LinkerFlavor, Target};
 
 pub fn target() -> Target {
@@ -85,7 +86,7 @@ pub fn target() -> Target {
     options.post_link_objects_self_contained = crt_objects::post_wasi_self_contained();
 
     // FIXME: Figure out cases in which WASM needs to link with a native toolchain.
-    options.link_self_contained = LinkSelfContainedDefault::True;
+    options.link_self_contained = LinkSelfContained::True;
 
     // 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/compiler/rustc_target/src/spec/wasm32_wasi_preview1_threads.rs b/compiler/rustc_target/src/spec/wasm32_wasi_preview1_threads.rs
index c567155fee6..93a49acb151 100644
--- a/compiler/rustc_target/src/spec/wasm32_wasi_preview1_threads.rs
+++ b/compiler/rustc_target/src/spec/wasm32_wasi_preview1_threads.rs
@@ -72,7 +72,8 @@
 //! best we can with this target. Don't start relying on too much here unless
 //! you know what you're getting in to!
 
-use super::crt_objects::{self, LinkSelfContainedDefault};
+use super::crt_objects;
+use super::LinkSelfContained;
 use super::{wasm_base, Cc, LinkerFlavor, Target};
 
 pub fn target() -> Target {
@@ -98,7 +99,7 @@ pub fn target() -> Target {
     options.post_link_objects_self_contained = crt_objects::post_wasi_self_contained();
 
     // FIXME: Figure out cases in which WASM needs to link with a native toolchain.
-    options.link_self_contained = LinkSelfContainedDefault::True;
+    options.link_self_contained = LinkSelfContained::True;
 
     // 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/compiler/rustc_target/src/spec/wasm_base.rs b/compiler/rustc_target/src/spec/wasm_base.rs
index 341763aadba..a29bddd849b 100644
--- a/compiler/rustc_target/src/spec/wasm_base.rs
+++ b/compiler/rustc_target/src/spec/wasm_base.rs
@@ -1,5 +1,5 @@
-use super::crt_objects::LinkSelfContainedDefault;
 use super::{cvs, Cc, LinkerFlavor, PanicStrategy, RelocModel, TargetOptions, TlsModel};
+use crate::spec::LinkSelfContained;
 
 pub fn options() -> TargetOptions {
     macro_rules! args {
@@ -100,7 +100,7 @@ pub fn options() -> TargetOptions {
         // rust-lang/rust#104137: cannot blindly remove this without putting in
         // some other way to compensate for lack of `-nostartfiles` in linker
         // invocation.
-        link_self_contained: LinkSelfContainedDefault::True,
+        link_self_contained: LinkSelfContained::True,
 
         // This has no effect in LLVM 8 or prior, but in LLVM 9 and later when
         // PIC code is implemented this has quite a drastic effect if it stays
diff --git a/compiler/rustc_target/src/spec/windows_gnu_base.rs b/compiler/rustc_target/src/spec/windows_gnu_base.rs
index 2231983f071..d99a95a77e1 100644
--- a/compiler/rustc_target/src/spec/windows_gnu_base.rs
+++ b/compiler/rustc_target/src/spec/windows_gnu_base.rs
@@ -1,4 +1,5 @@
-use crate::spec::crt_objects::{self, LinkSelfContainedDefault};
+use crate::spec::crt_objects;
+use crate::spec::LinkSelfContained;
 use crate::spec::{cvs, Cc, DebuginfoKind, LinkerFlavor, Lld, SplitDebuginfo, TargetOptions};
 use std::borrow::Cow;
 
@@ -90,7 +91,7 @@ pub fn opts() -> TargetOptions {
         post_link_objects: crt_objects::post_mingw(),
         pre_link_objects_self_contained: crt_objects::pre_mingw_self_contained(),
         post_link_objects_self_contained: crt_objects::post_mingw_self_contained(),
-        link_self_contained: LinkSelfContainedDefault::Mingw,
+        link_self_contained: LinkSelfContained::InferredForMingw,
         late_link_args,
         late_link_args_dynamic,
         late_link_args_static,