From 0bca45f620ef436f2101d24d88c1f5a6b79eb988 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Wed, 20 Sep 2023 22:13:43 +0000 Subject: [PATCH 1/6] allow target specs to declare self-contained linking components --- compiler/rustc_codegen_ssa/src/back/link.rs | 39 ++++- .../rustc_target/src/spec/linux_musl_base.rs | 5 +- compiler/rustc_target/src/spec/mod.rs | 159 +++++++++++++++++- .../rustc_target/src/spec/tests/tests_impl.rs | 2 +- compiler/rustc_target/src/spec/wasm32_wasi.rs | 5 +- .../src/spec/wasm32_wasi_preview1_threads.rs | 5 +- compiler/rustc_target/src/spec/wasm_base.rs | 4 +- .../rustc_target/src/spec/windows_gnu_base.rs | 5 +- 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 28a51711b9361..9a796356593f2 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 61553e71b4500..318d06e9889dd 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 16f70cf43b3fb..8c41acdf4c656 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 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": [ + // + // ] + // } + // ``` + 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::() { - 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 e0ecf8037c3e5..257867b1b807b 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 a0476d542e642..2c00c601b394e 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 c567155fee653..93a49acb15180 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 341763aadbaf1..a29bddd849b43 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 2231983f07126..d99a95a77e145 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, From 5f24e314ef9a87a796dd8a06b2e0ecff4ac2e8da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Wed, 20 Sep 2023 22:50:49 +0000 Subject: [PATCH 2/6] use asymmetric json roundtripping this ensures roundtripping of stable and unstable values: - backwards-compatible values can be deserialized, as well as the new unstable values - unstable values are serialized. --- compiler/rustc_target/src/spec/mod.rs | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 8c41acdf4c656..2d8b1b2398081 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -278,6 +278,7 @@ impl LinkerFlavor { } } + /// Returns the corresponding backwards-compatible CLI flavor. fn to_cli(self) -> LinkerFlavorCli { match self { LinkerFlavor::Gnu(Cc::Yes, _) @@ -298,6 +299,20 @@ impl LinkerFlavor { } } + /// Returns the modern CLI flavor that is the counterpart of this flavor. + fn to_cli_counterpart(self) -> LinkerFlavorCli { + match self { + LinkerFlavor::Gnu(cc, lld) => LinkerFlavorCli::Gnu(cc, lld), + LinkerFlavor::Darwin(cc, lld) => LinkerFlavorCli::Darwin(cc, lld), + LinkerFlavor::WasmLld(cc) => LinkerFlavorCli::WasmLld(cc), + LinkerFlavor::Unix(cc) => LinkerFlavorCli::Unix(cc), + LinkerFlavor::Msvc(lld) => LinkerFlavorCli::Msvc(lld), + LinkerFlavor::EmCc => LinkerFlavorCli::EmCc, + LinkerFlavor::Bpf => LinkerFlavorCli::Bpf, + LinkerFlavor::Ptx => LinkerFlavorCli::Ptx, + } + } + fn infer_cli_hints(cli: LinkerFlavorCli) -> (Option, Option) { match cli { LinkerFlavorCli::Gnu(cc, lld) | LinkerFlavorCli::Darwin(cc, lld) => { @@ -2273,7 +2288,7 @@ impl TargetOptions { } fn update_to_cli(&mut self) { - self.linker_flavor_json = self.linker_flavor.to_cli(); + self.linker_flavor_json = self.linker_flavor.to_cli_counterpart(); self.lld_flavor_json = self.linker_flavor.lld_flavor(); self.linker_is_gnu_json = self.linker_flavor.is_gnu(); for (args, args_json) in [ @@ -2283,8 +2298,10 @@ impl TargetOptions { (&self.late_link_args_static, &mut self.late_link_args_static_json), (&self.post_link_args, &mut self.post_link_args_json), ] { - *args_json = - args.iter().map(|(flavor, args)| (flavor.to_cli(), args.clone())).collect(); + *args_json = args + .iter() + .map(|(flavor, args)| (flavor.to_cli_counterpart(), args.clone())) + .collect(); } } } From fe7a843278343ed7406c420548da0c26aebdd170 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Thu, 21 Sep 2023 14:05:09 +0000 Subject: [PATCH 3/6] add end-to-end test of custom target using rust-lld starting from the x86_64-unknown-linux-gnu specs, we add the lld linker flavor and self-contained linker component --- .../run-make/rust-lld-custom-target/Makefile | 7 +++ .../rust-lld-custom-target/custom-target.json | 57 +++++++++++++++++++ tests/run-make/rust-lld-custom-target/lib.rs | 9 +++ 3 files changed, 73 insertions(+) create mode 100644 tests/run-make/rust-lld-custom-target/Makefile create mode 100644 tests/run-make/rust-lld-custom-target/custom-target.json create mode 100644 tests/run-make/rust-lld-custom-target/lib.rs diff --git a/tests/run-make/rust-lld-custom-target/Makefile b/tests/run-make/rust-lld-custom-target/Makefile new file mode 100644 index 0000000000000..007493ab0b99c --- /dev/null +++ b/tests/run-make/rust-lld-custom-target/Makefile @@ -0,0 +1,7 @@ +include ../tools.mk + +# needs-rust-lld +# only-x86_64-unknown-linux-gnu +all: + RUSTC_LOG=rustc_codegen_ssa::back::link=info $(RUSTC) --crate-type cdylib --target custom-target.json -Clink-args=-Wl,-v lib.rs 2> $(TMPDIR)/output.txt + $(CGREP) -e "^LLD [0-9]+\.[0-9]+\.[0-9]+" < $(TMPDIR)/output.txt diff --git a/tests/run-make/rust-lld-custom-target/custom-target.json b/tests/run-make/rust-lld-custom-target/custom-target.json new file mode 100644 index 0000000000000..7828a99f235c1 --- /dev/null +++ b/tests/run-make/rust-lld-custom-target/custom-target.json @@ -0,0 +1,57 @@ +{ + "arch": "x86_64", + "cpu": "x86-64", + "crt-static-respected": true, + "data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128", + "dynamic-linking": true, + "env": "gnu", + "has-rpath": true, + "has-thread-local": true, + "link-self-contained": { + "components": [ + "linker" + ] + }, + "linker-flavor": "gnu-lld-cc", + "llvm-target": "x86_64-unknown-linux-gnu", + "max-atomic-width": 64, + "os": "linux", + "plt-by-default": false, + "position-independent-executables": true, + "pre-link-args": { + "gnu-cc": [ + "-m64" + ], + "gnu-lld-cc": [ + "-m64" + ] + }, + "relro-level": "full", + "stack-probes": { + "kind": "inline-or-call", + "min-llvm-version-for-inline": [ + 16, + 0, + 0 + ] + }, + "static-position-independent-executables": true, + "supported-sanitizers": [ + "address", + "cfi", + "leak", + "memory", + "thread", + "safestack" + ], + "supported-split-debuginfo": [ + "packed", + "unpacked", + "off" + ], + "supports-xray": true, + "target-family": [ + "unix" + ], + "target-pointer-width": "64" +} diff --git a/tests/run-make/rust-lld-custom-target/lib.rs b/tests/run-make/rust-lld-custom-target/lib.rs new file mode 100644 index 0000000000000..d8f5e310821f5 --- /dev/null +++ b/tests/run-make/rust-lld-custom-target/lib.rs @@ -0,0 +1,9 @@ +// Test linking using `cc` with `rust-lld`, using a custom target with features described in MCP 510 +// see https://github.com/rust-lang/compiler-team/issues/510 for more info: +// +// Starting from the `x86_64-unknown-linux-gnu` target spec, we add the following options: +// - a linker-flavor using lld via a C compiler +// - the self-contained linker component is enabled + +#![feature(no_core)] +#![no_core] From b816207e0562a1e9fb6e6e7b93b2fcc3e3c324f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Wed, 27 Sep 2023 13:41:34 +0000 Subject: [PATCH 4/6] limit `lld` flavors to the llvm backend --- compiler/rustc_codegen_ssa/src/back/link.rs | 23 +++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 9a796356593f2..fb79df4f791c0 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -2999,8 +2999,27 @@ fn add_lld_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) { // 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(); + let self_contained_cli = sess.opts.cg.link_self_contained.is_linker_enabled(); + let self_contained_target = sess.target.options.link_self_contained.is_linker_enabled(); + + // FIXME: in the future, codegen backends may need to have more control over this process: they + // don't always support all the features the linker expects here, and vice versa. For example, + // at the time of writing this, lld expects a newer style of aarch64 TLS relocations that + // cranelift doesn't implement yet. That in turn can impact whether linking would succeed on + // such a target when using the `cg_clif` backend and lld. + // + // Until interactions between backends and linker features are expressible, we limit target + // specs to opt-in to lld only when we're on the llvm backend, where it's expected to work and + // tested on CI. As usual, the CLI still has precedence over this, so that users and developers + // can still override this default when needed (e.g. for tests). + let uses_llvm_backend = + matches!(sess.opts.unstable_opts.codegen_backend.as_deref(), None | Some("llvm")); + if !uses_llvm_backend && !self_contained_cli && sess.opts.cg.linker_flavor.is_none() { + // We bail if we're not using llvm and lld was not explicitly requested on the CLI. + return; + } + + let self_contained_linker = self_contained_cli || self_contained_target; if self_contained_linker && !sess.opts.cg.link_self_contained.is_linker_disabled() { for path in sess.get_tools_search_paths(false) { cmd.arg({ From e569a3691a80e597b382ec6773e4d227402fcc3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Wed, 18 Oct 2023 13:15:20 +0000 Subject: [PATCH 5/6] unify `LinkSelfContained` and `LinkSelfContainedDefault` Removes the backwards-compatible `LinkSelfContainedDefault`, by incorporating the remaining specifics into `LinkSelfContained`. Then renames the modern options to keep the old name. --- compiler/rustc_codegen_ssa/src/back/link.rs | 12 +-- compiler/rustc_target/src/spec/crt_objects.rs | 38 --------- .../rustc_target/src/spec/linux_musl_base.rs | 5 +- compiler/rustc_target/src/spec/mod.rs | 77 +++++++++++-------- compiler/rustc_target/src/spec/wasm32_wasi.rs | 4 +- .../src/spec/wasm32_wasi_preview1_threads.rs | 7 +- compiler/rustc_target/src/spec/wasm_base.rs | 4 +- .../rustc_target/src/spec/windows_gnu_base.rs | 4 +- 8 files changed, 60 insertions(+), 91 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index fb79df4f791c0..104f30697bd9f 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -23,7 +23,7 @@ use rustc_session::utils::NativeLibKind; use rustc_session::{filesearch, Session}; use rustc_span::symbol::Symbol; use rustc_target::spec::crt_objects::CrtObjects; -use rustc_target::spec::LinkSelfContained; +use rustc_target::spec::LinkSelfContainedDefault; use rustc_target::spec::{Cc, LinkOutputKind, LinkerFlavor, Lld, PanicStrategy}; use rustc_target::spec::{RelocModel, RelroLevel, SanitizerSet, SplitDebuginfo}; @@ -1714,9 +1714,9 @@ fn self_contained(sess: &Session, crate_type: CrateType) -> bool { } match sess.target.link_self_contained { - LinkSelfContained::True => true, - LinkSelfContained::False => false, - LinkSelfContained::WithComponents(components) => { + LinkSelfContainedDefault::False => false, + LinkSelfContainedDefault::True => true, + LinkSelfContainedDefault::WithComponents(components) => { if components.is_all() { true } else if components.is_empty() { @@ -1733,8 +1733,8 @@ fn self_contained(sess: &Session, crate_type: CrateType) -> bool { // 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). - LinkSelfContained::InferredForMusl => sess.crt_static(Some(crate_type)), - LinkSelfContained::InferredForMingw => { + LinkSelfContainedDefault::InferredForMusl => sess.crt_static(Some(crate_type)), + LinkSelfContainedDefault::InferredForMingw => { sess.host == sess.target && sess.target.vendor != "uwp" && detect_self_contained_mingw(&sess) diff --git a/compiler/rustc_target/src/spec/crt_objects.rs b/compiler/rustc_target/src/spec/crt_objects.rs index c126390f5a908..53f710b8f9e14 100644 --- a/compiler/rustc_target/src/spec/crt_objects.rs +++ b/compiler/rustc_target/src/spec/crt_objects.rs @@ -40,11 +40,9 @@ //! but not gcc's. As a result rustc cannot link with C++ static libraries (#36710) //! when linking in self-contained mode. -use crate::json::{Json, ToJson}; use crate::spec::LinkOutputKind; use std::borrow::Cow; use std::collections::BTreeMap; -use std::str::FromStr; pub type CrtObjects = BTreeMap>>; @@ -123,39 +121,3 @@ pub(super) fn pre_wasi_self_contained() -> CrtObjects { pub(super) fn post_wasi_self_contained() -> CrtObjects { new(&[]) } - -/// Which logic to use to determine whether to use self-contained linking mode -/// if `-Clink-self-contained` is not specified explicitly. -#[derive(Clone, Copy, PartialEq, Hash, Debug)] -pub enum LinkSelfContainedDefault { - False, - True, - Musl, - Mingw, -} - -impl FromStr for LinkSelfContainedDefault { - type Err = (); - - fn from_str(s: &str) -> Result { - Ok(match s { - "false" => LinkSelfContainedDefault::False, - "true" | "wasm" => LinkSelfContainedDefault::True, - "musl" => LinkSelfContainedDefault::Musl, - "mingw" => LinkSelfContainedDefault::Mingw, - _ => return Err(()), - }) - } -} - -impl ToJson for LinkSelfContainedDefault { - fn to_json(&self) -> Json { - match *self { - LinkSelfContainedDefault::False => "false", - LinkSelfContainedDefault::True => "true", - LinkSelfContainedDefault::Musl => "musl", - LinkSelfContainedDefault::Mingw => "mingw", - } - .to_json() - } -} diff --git a/compiler/rustc_target/src/spec/linux_musl_base.rs b/compiler/rustc_target/src/spec/linux_musl_base.rs index 318d06e9889dd..b698bcbcef698 100644 --- a/compiler/rustc_target/src/spec/linux_musl_base.rs +++ b/compiler/rustc_target/src/spec/linux_musl_base.rs @@ -1,6 +1,5 @@ use crate::spec::crt_objects; -use crate::spec::LinkSelfContained; -use crate::spec::TargetOptions; +use crate::spec::{LinkSelfContainedDefault, TargetOptions}; pub fn opts() -> TargetOptions { let mut base = super::linux_base::opts(); @@ -8,7 +7,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 = LinkSelfContained::InferredForMusl; + base.link_self_contained = LinkSelfContainedDefault::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 2d8b1b2398081..9141b169ae984 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -38,7 +38,7 @@ use crate::abi::call::Conv; use crate::abi::{Endian, Integer, Size, TargetDataLayout, TargetDataLayoutErrors}; use crate::json::{Json, ToJson}; use crate::spec::abi::{lookup as lookup_abi, Abi}; -use crate::spec::crt_objects::{CrtObjects, LinkSelfContainedDefault}; +use crate::spec::crt_objects::CrtObjects; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_fs_util::try_canonicalize; use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; @@ -542,7 +542,7 @@ impl ToJson for LinkerFlavorCli { /// - 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 { +pub enum LinkSelfContainedDefault { /// The target spec explicitly enables self-contained linking. True, @@ -560,10 +560,25 @@ pub enum LinkSelfContained { WithComponents(LinkSelfContainedComponents), } -impl ToJson for LinkSelfContained { +/// Parses a backwards-compatible `-Clink-self-contained` option string, without components. +impl FromStr for LinkSelfContainedDefault { + type Err = (); + + fn from_str(s: &str) -> Result { + Ok(match s { + "false" => LinkSelfContainedDefault::False, + "true" | "wasm" => LinkSelfContainedDefault::True, + "musl" => LinkSelfContainedDefault::InferredForMusl, + "mingw" => LinkSelfContainedDefault::InferredForMingw, + _ => return Err(()), + }) + } +} + +impl ToJson for LinkSelfContainedDefault { fn to_json(&self) -> Json { match *self { - LinkSelfContained::WithComponents(components) => { + LinkSelfContainedDefault::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. @@ -572,29 +587,31 @@ impl ToJson for LinkSelfContained { 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(), + // Stable backwards-compatible values + LinkSelfContainedDefault::True => "true".to_json(), + LinkSelfContainedDefault::False => "false".to_json(), + LinkSelfContainedDefault::InferredForMusl => "musl".to_json(), + LinkSelfContainedDefault::InferredForMingw => "mingw".to_json(), } } } -impl LinkSelfContained { +impl LinkSelfContainedDefault { /// 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 + self == LinkSelfContainedDefault::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), + LinkSelfContainedDefault::True => true, + LinkSelfContainedDefault::False => false, + LinkSelfContainedDefault::WithComponents(c) => { + c.contains(LinkSelfContainedComponents::LINKER) + } _ => false, } } @@ -604,23 +621,12 @@ impl LinkSelfContained { /// - the other variants as a backwards-compatible `crt-objects-fallback` string fn json_key(self) -> &'static str { match self { - LinkSelfContained::WithComponents(_) => "link-self-contained", + LinkSelfContainedDefault::WithComponents(_) => "link-self-contained", _ => "crt-objects-fallback", } } } -impl From 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. @@ -1888,7 +1894,7 @@ pub struct TargetOptions { pub post_link_objects_self_contained: CrtObjects, /// 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, + pub link_self_contained: LinkSelfContainedDefault, /// Linker arguments that are passed *before* any user-defined libraries. pub pre_link_args: LinkArgs, @@ -2363,7 +2369,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: LinkSelfContained::False, + link_self_contained: LinkSelfContainedDefault::False, pre_link_args: LinkArgs::new(), pre_link_args_json: LinkArgsCli::new(), late_link_args: LinkArgs::new(), @@ -2844,7 +2850,7 @@ impl Target { } Ok::<(), String>(()) } ); - ($key_name:ident, LinkSelfContained) => ( { + ($key_name:ident, link_self_contained_components) => ( { // Skeleton of what needs to be parsed: // // ``` @@ -2873,18 +2879,18 @@ impl Target { _ => return Err(format!("not a string: {:?}", s)), }; } - base.$key_name = LinkSelfContained::WithComponents(components); + base.$key_name = LinkSelfContainedDefault::WithComponents(components); } else { incorrect_type.push(name) } } Ok::<(), String>(()) } ); - ($key_name:ident = $json_name:expr, LinkSelfContainedDefault) => ( { + ($key_name:ident = $json_name:expr, link_self_contained_backwards_compatible) => ( { let name = $json_name; obj.remove(name).and_then(|o| o.as_str().and_then(|s| { match s.parse::() { - Ok(lsc_default) => base.$key_name = lsc_default.into(), + Ok(lsc_default) => base.$key_name = lsc_default, _ => return Some(Err(format!("'{}' is not a valid `-Clink-self-contained` default. \ Use 'false', 'true', 'musl' or 'mingw'", s))), } @@ -3034,9 +3040,12 @@ impl Target { key!(pre_link_objects_self_contained = "pre-link-objects-fallback", link_objects); key!(post_link_objects_self_contained = "post-link-objects-fallback", link_objects); // Deserializes the backwards-compatible variants of `-Clink-self-contained` - key!(link_self_contained = "crt-objects-fallback", LinkSelfContainedDefault)?; + key!( + link_self_contained = "crt-objects-fallback", + link_self_contained_backwards_compatible + )?; // Deserializes the components variant of `-Clink-self-contained` - key!(link_self_contained, LinkSelfContained)?; + key!(link_self_contained, link_self_contained_components)?; 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); diff --git a/compiler/rustc_target/src/spec/wasm32_wasi.rs b/compiler/rustc_target/src/spec/wasm32_wasi.rs index 2c00c601b394e..23fabcdc90de5 100644 --- a/compiler/rustc_target/src/spec/wasm32_wasi.rs +++ b/compiler/rustc_target/src/spec/wasm32_wasi.rs @@ -73,7 +73,7 @@ //! you know what you're getting in to! use super::crt_objects; -use super::LinkSelfContained; +use super::LinkSelfContainedDefault; use super::{wasm_base, Cc, LinkerFlavor, Target}; pub fn target() -> Target { @@ -86,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 = LinkSelfContained::True; + options.link_self_contained = LinkSelfContainedDefault::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 93a49acb15180..ba9a99ae380e4 100644 --- a/compiler/rustc_target/src/spec/wasm32_wasi_preview1_threads.rs +++ b/compiler/rustc_target/src/spec/wasm32_wasi_preview1_threads.rs @@ -72,9 +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; -use super::LinkSelfContained; -use super::{wasm_base, Cc, LinkerFlavor, Target}; +use super::{crt_objects, wasm_base}; +use super::{Cc, LinkSelfContainedDefault, LinkerFlavor, Target}; pub fn target() -> Target { let mut options = wasm_base::options(); @@ -99,7 +98,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 = LinkSelfContained::True; + options.link_self_contained = LinkSelfContainedDefault::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 a29bddd849b43..82a3afeae31e1 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::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: LinkSelfContained::True, + link_self_contained: LinkSelfContainedDefault::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 d99a95a77e145..b84e0fc078303 100644 --- a/compiler/rustc_target/src/spec/windows_gnu_base.rs +++ b/compiler/rustc_target/src/spec/windows_gnu_base.rs @@ -1,5 +1,5 @@ use crate::spec::crt_objects; -use crate::spec::LinkSelfContained; +use crate::spec::LinkSelfContainedDefault; use crate::spec::{cvs, Cc, DebuginfoKind, LinkerFlavor, Lld, SplitDebuginfo, TargetOptions}; use std::borrow::Cow; @@ -91,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: LinkSelfContained::InferredForMingw, + link_self_contained: LinkSelfContainedDefault::InferredForMingw, late_link_args, late_link_args_dynamic, late_link_args_static, From 0b40c7c6822939e97c008864067d9dac59cc3931 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Wed, 18 Oct 2023 21:24:02 +0000 Subject: [PATCH 6/6] make `self_contained` return `LinkSelfContainedComponents` --- compiler/rustc_codegen_ssa/src/back/link.rs | 115 +++++++++++--------- compiler/rustc_target/src/spec/mod.rs | 15 +++ 2 files changed, 80 insertions(+), 50 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 104f30697bd9f..62b482cf92e2c 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -23,6 +23,7 @@ use rustc_session::utils::NativeLibKind; use rustc_session::{filesearch, Session}; use rustc_span::symbol::Symbol; use rustc_target::spec::crt_objects::CrtObjects; +use rustc_target::spec::LinkSelfContainedComponents; use rustc_target::spec::LinkSelfContainedDefault; use rustc_target::spec::{Cc, LinkOutputKind, LinkerFlavor, Lld, PanicStrategy}; use rustc_target::spec::{RelocModel, RelroLevel, SanitizerSet, SplitDebuginfo}; @@ -721,6 +722,7 @@ fn link_natively<'a>( ) -> Result<(), ErrorGuaranteed> { info!("preparing {:?} to {:?}", crate_type, out_filename); let (linker_path, flavor) = linker_and_flavor(sess); + let self_contained_components = self_contained_components(sess, crate_type); let mut cmd = linker_with_args( &linker_path, flavor, @@ -730,6 +732,7 @@ fn link_natively<'a>( tmpdir, out_filename, codegen_results, + self_contained_components, )?; linker::disable_localization(&mut cmd); @@ -805,14 +808,14 @@ fn link_natively<'a>( "Linker does not support -static-pie command line option. Retrying with -static instead." ); // Mirror `add_(pre,post)_link_objects` to replace CRT objects. - let self_contained = self_contained(sess, crate_type); + let self_contained_crt_objects = self_contained_components.is_crt_objects_enabled(); let opts = &sess.target; - let pre_objects = if self_contained { + let pre_objects = if self_contained_crt_objects { &opts.pre_link_objects_self_contained } else { &opts.pre_link_objects }; - let post_objects = if self_contained { + let post_objects = if self_contained_crt_objects { &opts.post_link_objects_self_contained } else { &opts.post_link_objects @@ -823,7 +826,9 @@ fn link_natively<'a>( .iter() .copied() .flatten() - .map(|obj| get_object_file_path(sess, obj, self_contained).into_os_string()) + .map(|obj| { + get_object_file_path(sess, obj, self_contained_crt_objects).into_os_string() + }) .collect::>() }; let pre_objects_static_pie = get_objects(pre_objects, LinkOutputKind::StaticPicExe); @@ -1703,42 +1708,43 @@ fn detect_self_contained_mingw(sess: &Session) -> bool { /// Various toolchain components used during linking are used from rustc distribution /// 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.is_disabled() { - sess.emit_err(errors::UnsupportedLinkSelfContained); - } - return self_contained; - } - - match sess.target.link_self_contained { - LinkSelfContainedDefault::False => false, - LinkSelfContainedDefault::True => true, - LinkSelfContainedDefault::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 +fn self_contained_components(sess: &Session, crate_type: CrateType) -> LinkSelfContainedComponents { + // Turn the backwards compatible bool values for `self_contained` into fully inferred + // `LinkSelfContainedComponents`. + let self_contained = + if let Some(self_contained) = sess.opts.cg.link_self_contained.explicitly_set { + // Emit an error if the user requested self-contained mode on the CLI but the target + // explicitly refuses it. + if sess.target.link_self_contained.is_disabled() { + sess.emit_err(errors::UnsupportedLinkSelfContained); } - } + self_contained + } else { + match sess.target.link_self_contained { + LinkSelfContainedDefault::False => false, + LinkSelfContainedDefault::True => true, + + LinkSelfContainedDefault::WithComponents(components) => { + // For target specs with explicitly enabled components, we can return them + // directly. + return components; + } - // 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::InferredForMusl => sess.crt_static(Some(crate_type)), - LinkSelfContainedDefault::InferredForMingw => { - sess.host == sess.target - && sess.target.vendor != "uwp" - && detect_self_contained_mingw(&sess) - } + // 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::InferredForMusl => sess.crt_static(Some(crate_type)), + LinkSelfContainedDefault::InferredForMingw => { + sess.host == sess.target + && sess.target.vendor != "uwp" + && detect_self_contained_mingw(&sess) + } + } + }; + if self_contained { + LinkSelfContainedComponents::all() + } else { + LinkSelfContainedComponents::empty() } } @@ -2062,13 +2068,14 @@ fn linker_with_args<'a>( tmpdir: &Path, out_filename: &Path, codegen_results: &CodegenResults, + self_contained_components: LinkSelfContainedComponents, ) -> Result { - let self_contained = self_contained(sess, crate_type); + let self_contained_crt_objects = self_contained_components.is_crt_objects_enabled(); let cmd = &mut *super::linker::get_linker( sess, path, flavor, - self_contained, + self_contained_components.are_any_components_enabled(), &codegen_results.crate_info.target_cpu, ); let link_output_kind = link_output_kind(sess, crate_type); @@ -2095,7 +2102,7 @@ fn linker_with_args<'a>( // ------------ Object code and libraries, order-dependent ------------ // Pre-link CRT objects. - add_pre_link_objects(cmd, sess, flavor, link_output_kind, self_contained); + add_pre_link_objects(cmd, sess, flavor, link_output_kind, self_contained_crt_objects); add_linked_symbol_object( cmd, @@ -2238,7 +2245,7 @@ fn linker_with_args<'a>( cmd, sess, link_output_kind, - self_contained, + self_contained_components, flavor, crate_type, codegen_results, @@ -2254,7 +2261,7 @@ fn linker_with_args<'a>( // ------------ Object code and libraries, order-dependent ------------ // Post-link CRT objects. - add_post_link_objects(cmd, sess, link_output_kind, self_contained); + add_post_link_objects(cmd, sess, link_output_kind, self_contained_crt_objects); // ------------ Late order-dependent options ------------ @@ -2271,7 +2278,7 @@ fn add_order_independent_options( cmd: &mut dyn Linker, sess: &Session, link_output_kind: LinkOutputKind, - self_contained: bool, + self_contained_components: LinkSelfContainedComponents, flavor: LinkerFlavor, crate_type: CrateType, codegen_results: &CodegenResults, @@ -2279,7 +2286,7 @@ fn add_order_independent_options( tmpdir: &Path, ) { // Take care of the flavors and CLI options requesting the `lld` linker. - add_lld_args(cmd, sess, flavor); + add_lld_args(cmd, sess, flavor, self_contained_components); add_apple_sdk(cmd, sess, flavor); @@ -2304,7 +2311,7 @@ fn add_order_independent_options( // Make the binary compatible with data execution prevention schemes. cmd.add_no_exec(); - if self_contained { + if self_contained_components.is_crt_objects_enabled() { cmd.no_crt_objects(); } @@ -2335,7 +2342,7 @@ fn add_order_independent_options( cmd.linker_plugin_lto(); - add_library_search_dirs(cmd, sess, self_contained); + add_library_search_dirs(cmd, sess, self_contained_components.are_any_components_enabled()); cmd.output_filename(out_filename); @@ -2985,8 +2992,16 @@ fn get_apple_sdk_root(sdk_name: &str) -> Result bool { + !self.is_empty() + } + + /// Returns whether `LinkSelfContainedComponents::LINKER` is enabled. + pub fn is_linker_enabled(self) -> bool { + self.contains(LinkSelfContainedComponents::LINKER) + } + + /// Returns whether `LinkSelfContainedComponents::CRT_OBJECTS` is enabled. + pub fn is_crt_objects_enabled(self) -> bool { + self.contains(LinkSelfContainedComponents::CRT_OBJECTS) + } } impl IntoIterator for LinkSelfContainedComponents {