Skip to content

Commit b0a6798

Browse files
committed
allow target specs to declare self-contained linking components
1 parent 9e2265c commit b0a6798

File tree

8 files changed

+224
-29
lines changed

8 files changed

+224
-29
lines changed

compiler/rustc_codegen_ssa/src/back/link.rs

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ use rustc_session::utils::NativeLibKind;
2222
/// need out of the shared crate context before we get rid of it.
2323
use rustc_session::{filesearch, Session};
2424
use rustc_span::symbol::Symbol;
25-
use rustc_target::spec::crt_objects::{CrtObjects, LinkSelfContainedDefault};
25+
use rustc_target::spec::crt_objects::CrtObjects;
26+
use rustc_target::spec::LinkSelfContained;
2627
use rustc_target::spec::{Cc, LinkOutputKind, LinkerFlavor, Lld, PanicStrategy};
2728
use rustc_target::spec::{RelocModel, RelroLevel, SanitizerSet, SplitDebuginfo};
2829

@@ -1709,21 +1710,37 @@ fn detect_self_contained_mingw(sess: &Session) -> bool {
17091710
/// instead of being found somewhere on the host system.
17101711
/// We only provide such support for a very limited number of targets.
17111712
fn self_contained(sess: &Session, crate_type: CrateType) -> bool {
1713+
// Emit an error if the user requested self-contained mode on the CLI but the target explicitly
1714+
// refuses it.
17121715
if let Some(self_contained) = sess.opts.cg.link_self_contained.explicitly_set {
1713-
if sess.target.link_self_contained == LinkSelfContainedDefault::False {
1716+
if sess.target.link_self_contained.is_disabled() {
17141717
sess.emit_err(errors::UnsupportedLinkSelfContained);
17151718
}
17161719
return self_contained;
17171720
}
17181721

17191722
match sess.target.link_self_contained {
1720-
LinkSelfContainedDefault::False => false,
1721-
LinkSelfContainedDefault::True => true,
1723+
LinkSelfContained::True => true,
1724+
LinkSelfContained::False => false,
1725+
LinkSelfContained::WithComponents(components) => {
1726+
if components.is_all() {
1727+
true
1728+
} else if components.is_empty() {
1729+
false
1730+
} else {
1731+
// FIXME: Currently no target makes use of individual components to mean
1732+
// self-contained linking is fully enabled, in the sense of what the code downstream
1733+
// from here expects. Until components are handled a bit more deeply, we can
1734+
// consider that it's disabled and remain backwards compatible.
1735+
false
1736+
}
1737+
}
1738+
17221739
// FIXME: Find a better heuristic for "native musl toolchain is available",
17231740
// based on host and linker path, for example.
17241741
// (https://github.com/rust-lang/rust/pull/71769#issuecomment-626330237).
1725-
LinkSelfContainedDefault::Musl => sess.crt_static(Some(crate_type)),
1726-
LinkSelfContainedDefault::Mingw => {
1742+
LinkSelfContained::InferredForMusl => sess.crt_static(Some(crate_type)),
1743+
LinkSelfContained::InferredForMingw => {
17271744
sess.host == sess.target
17281745
&& sess.target.vendor != "uwp"
17291746
&& detect_self_contained_mingw(&sess)
@@ -2992,12 +3009,14 @@ fn add_lld_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) {
29923009
}
29933010

29943011
// 1. Implement the "self-contained" part of this feature by adding rustc distribution
2995-
// directories to the tool's search path:
2996-
// - if the self-contained linker is enabled on the CLI (or by the unstable CLI flag that will
2997-
// be removed eventually),
3012+
// directories to the tool's search path, depending on a mix between what users can specify on
3013+
// the CLI, and what the target spec enables (as it can't disable components):
3014+
// - if the self-contained linker is enabled on the CLI or by the target spec (or by the
3015+
// unstable CLI flag that will be removed eventually),
29983016
// - and if the self-contained linker is not disabled on the CLI.
2999-
let self_contained_linker =
3000-
sess.opts.cg.link_self_contained.is_linker_enabled() || unstable_use_lld;
3017+
let self_contained_linker = sess.target.options.link_self_contained.is_linker_enabled()
3018+
|| sess.opts.cg.link_self_contained.is_linker_enabled()
3019+
|| unstable_use_lld;
30013020
if self_contained_linker && !sess.opts.cg.link_self_contained.is_linker_disabled() {
30023021
for path in sess.get_tools_search_paths(false) {
30033022
cmd.arg({

compiler/rustc_target/src/spec/linux_musl_base.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
use crate::spec::crt_objects::{self, LinkSelfContainedDefault};
1+
use crate::spec::crt_objects;
2+
use crate::spec::LinkSelfContained;
23
use crate::spec::TargetOptions;
34

45
pub fn opts() -> TargetOptions {
@@ -7,7 +8,7 @@ pub fn opts() -> TargetOptions {
78
base.env = "musl".into();
89
base.pre_link_objects_self_contained = crt_objects::pre_musl_self_contained();
910
base.post_link_objects_self_contained = crt_objects::post_musl_self_contained();
10-
base.link_self_contained = LinkSelfContainedDefault::Musl;
11+
base.link_self_contained = LinkSelfContained::InferredForMusl;
1112

1213
// These targets statically link libc by default
1314
base.crt_static_default = true;

compiler/rustc_target/src/spec/mod.rs

Lines changed: 179 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,92 @@ impl ToJson for LinkerFlavorCli {
531531
}
532532
}
533533

534+
/// The different `-Clink-self-contained` options that can be specified in a target spec:
535+
/// - enabling or disabling in bulk
536+
/// - some target-specific pieces of inference to determine whether to use self-contained linking
537+
/// if `-Clink-self-contained` is not specified explicitly (e.g. on musl/mingw)
538+
/// - explicitly enabling some of the self-contained linking components, e.g. the linker component
539+
/// to use `rust-lld`
540+
#[derive(Clone, Copy, PartialEq, Debug)]
541+
pub enum LinkSelfContained {
542+
/// The target spec explicitly enables self-contained linking.
543+
True,
544+
545+
/// The target spec explicitly disables self-contained linking.
546+
False,
547+
548+
/// The target spec requests that the self-contained mode is inferred, in the context of musl.
549+
InferredForMusl,
550+
551+
/// The target spec requests that the self-contained mode is inferred, in the context of mingw.
552+
InferredForMingw,
553+
554+
/// The target spec explicitly enables a list of self-contained linking components: e.g. for
555+
/// targets opting into a subset of components like the CLI's `-C link-self-contained=+linker`.
556+
WithComponents(LinkSelfContainedComponents),
557+
}
558+
559+
impl ToJson for LinkSelfContained {
560+
fn to_json(&self) -> Json {
561+
match *self {
562+
LinkSelfContained::WithComponents(components) => {
563+
// Serialize the components in a json object's `components` field, to prepare for a
564+
// future where `crt-objects-fallback` is removed from the json specs and
565+
// incorporated as a field here.
566+
let mut map = BTreeMap::new();
567+
map.insert("components", components);
568+
map.to_json()
569+
}
570+
571+
// Stable values backwards-compatible with `LinkSelfContainedDefault`
572+
LinkSelfContained::True => "true".to_json(),
573+
LinkSelfContained::False => "false".to_json(),
574+
LinkSelfContained::InferredForMusl => "musl".to_json(),
575+
LinkSelfContained::InferredForMingw => "mingw".to_json(),
576+
}
577+
}
578+
}
579+
580+
impl LinkSelfContained {
581+
/// Returns whether the target spec has self-contained linking explicitly disabled. Used to emit
582+
/// errors if the user then enables it on the CLI.
583+
pub fn is_disabled(self) -> bool {
584+
self == Self::False
585+
}
586+
587+
/// Returns whether the target spec explictly requests self-contained linking, i.e. not via
588+
/// inference.
589+
pub fn is_linker_enabled(self) -> bool {
590+
match self {
591+
LinkSelfContained::True => true,
592+
LinkSelfContained::False => false,
593+
LinkSelfContained::WithComponents(c) => c.contains(LinkSelfContainedComponents::LINKER),
594+
_ => false,
595+
}
596+
}
597+
598+
/// Returns the key to use when serializing the setting to json:
599+
/// - individual components in a `link-self-contained` object value
600+
/// - the other variants as a backwards-compatible `crt-objects-fallback` string
601+
fn json_key(self) -> &'static str {
602+
match self {
603+
LinkSelfContained::WithComponents(_) => "link-self-contained",
604+
_ => "crt-objects-fallback",
605+
}
606+
}
607+
}
608+
609+
impl From<LinkSelfContainedDefault> for LinkSelfContained {
610+
fn from(value: LinkSelfContainedDefault) -> Self {
611+
match value {
612+
LinkSelfContainedDefault::True => LinkSelfContained::True,
613+
LinkSelfContainedDefault::False => LinkSelfContained::False,
614+
LinkSelfContainedDefault::Musl => LinkSelfContained::InferredForMusl,
615+
LinkSelfContainedDefault::Mingw => LinkSelfContained::InferredForMingw,
616+
}
617+
}
618+
}
619+
534620
bitflags::bitflags! {
535621
#[derive(Default)]
536622
/// The `-C link-self-contained` components that can individually be enabled or disabled.
@@ -563,6 +649,49 @@ impl LinkSelfContainedComponents {
563649
_ => return None,
564650
})
565651
}
652+
653+
/// Return the component's name.
654+
///
655+
/// Returns `None` if the bitflags aren't a singular component (but a mix of multiple flags).
656+
fn as_str(self) -> Option<&'static str> {
657+
Some(match self {
658+
LinkSelfContainedComponents::CRT_OBJECTS => "crto",
659+
LinkSelfContainedComponents::LIBC => "libc",
660+
LinkSelfContainedComponents::UNWIND => "unwind",
661+
LinkSelfContainedComponents::LINKER => "linker",
662+
LinkSelfContainedComponents::SANITIZERS => "sanitizers",
663+
LinkSelfContainedComponents::MINGW => "mingw",
664+
_ => return None,
665+
})
666+
}
667+
668+
/// Returns an array of all the components.
669+
fn all_components() -> [LinkSelfContainedComponents; 6] {
670+
[
671+
LinkSelfContainedComponents::CRT_OBJECTS,
672+
LinkSelfContainedComponents::LIBC,
673+
LinkSelfContainedComponents::UNWIND,
674+
LinkSelfContainedComponents::LINKER,
675+
LinkSelfContainedComponents::SANITIZERS,
676+
LinkSelfContainedComponents::MINGW,
677+
]
678+
}
679+
}
680+
681+
impl ToJson for LinkSelfContainedComponents {
682+
fn to_json(&self) -> Json {
683+
let components: Vec<_> = Self::all_components()
684+
.into_iter()
685+
.filter(|c| self.contains(*c))
686+
.map(|c| {
687+
// We can unwrap because we're iterating over all the known singular components,
688+
// not an actual set of flags where `as_str` can fail.
689+
c.as_str().unwrap().to_owned()
690+
})
691+
.collect();
692+
693+
components.to_json()
694+
}
566695
}
567696

568697
#[derive(Clone, Copy, Debug, PartialEq, Hash, Encodable, Decodable, HashStable_Generic)]
@@ -1739,7 +1868,9 @@ pub struct TargetOptions {
17391868
/// Same as `(pre|post)_link_objects`, but when self-contained linking mode is enabled.
17401869
pub pre_link_objects_self_contained: CrtObjects,
17411870
pub post_link_objects_self_contained: CrtObjects,
1742-
pub link_self_contained: LinkSelfContainedDefault,
1871+
/// Behavior for the self-contained linking mode: inferred for some targets, or explicitly
1872+
/// enabled (in bulk, or with individual components).
1873+
pub link_self_contained: LinkSelfContained,
17431874

17441875
/// Linker arguments that are passed *before* any user-defined libraries.
17451876
pub pre_link_args: LinkArgs,
@@ -2212,7 +2343,7 @@ impl Default for TargetOptions {
22122343
post_link_objects: Default::default(),
22132344
pre_link_objects_self_contained: Default::default(),
22142345
post_link_objects_self_contained: Default::default(),
2215-
link_self_contained: LinkSelfContainedDefault::False,
2346+
link_self_contained: LinkSelfContained::False,
22162347
pre_link_args: LinkArgs::new(),
22172348
pre_link_args_json: LinkArgsCli::new(),
22182349
late_link_args: LinkArgs::new(),
@@ -2693,12 +2824,47 @@ impl Target {
26932824
}
26942825
Ok::<(), String>(())
26952826
} );
2696-
2697-
($key_name:ident = $json_name:expr, link_self_contained) => ( {
2827+
($key_name:ident, LinkSelfContained) => ( {
2828+
// Skeleton of what needs to be parsed:
2829+
//
2830+
// ```
2831+
// $name: {
2832+
// "components": [
2833+
// <array of strings>
2834+
// ]
2835+
// }
2836+
// ```
2837+
let name = (stringify!($key_name)).replace("_", "-");
2838+
if let Some(o) = obj.remove(&name) {
2839+
if let Some(o) = o.as_object() {
2840+
let component_array = o.get("components")
2841+
.ok_or_else(|| format!("{name}: expected a \
2842+
JSON object with a `components` field."))?;
2843+
let component_array = component_array.as_array()
2844+
.ok_or_else(|| format!("{name}.components: expected a JSON array"))?;
2845+
let mut components = LinkSelfContainedComponents::empty();
2846+
for s in component_array {
2847+
components |= match s.as_str() {
2848+
Some(s) => {
2849+
LinkSelfContainedComponents::from_str(s)
2850+
.ok_or_else(|| format!("unknown \
2851+
`-Clink-self-contained` component: {s}"))?
2852+
},
2853+
_ => return Err(format!("not a string: {:?}", s)),
2854+
};
2855+
}
2856+
base.$key_name = LinkSelfContained::WithComponents(components);
2857+
} else {
2858+
incorrect_type.push(name)
2859+
}
2860+
}
2861+
Ok::<(), String>(())
2862+
} );
2863+
($key_name:ident = $json_name:expr, LinkSelfContainedDefault) => ( {
26982864
let name = $json_name;
26992865
obj.remove(name).and_then(|o| o.as_str().and_then(|s| {
27002866
match s.parse::<LinkSelfContainedDefault>() {
2701-
Ok(lsc_default) => base.$key_name = lsc_default,
2867+
Ok(lsc_default) => base.$key_name = lsc_default.into(),
27022868
_ => return Some(Err(format!("'{}' is not a valid `-Clink-self-contained` default. \
27032869
Use 'false', 'true', 'musl' or 'mingw'", s))),
27042870
}
@@ -2847,7 +3013,10 @@ impl Target {
28473013
key!(post_link_objects = "post-link-objects", link_objects);
28483014
key!(pre_link_objects_self_contained = "pre-link-objects-fallback", link_objects);
28493015
key!(post_link_objects_self_contained = "post-link-objects-fallback", link_objects);
2850-
key!(link_self_contained = "crt-objects-fallback", link_self_contained)?;
3016+
// Deserializes the backwards-compatible variants of `-Clink-self-contained`
3017+
key!(link_self_contained = "crt-objects-fallback", LinkSelfContainedDefault)?;
3018+
// Deserializes the components variant of `-Clink-self-contained`
3019+
key!(link_self_contained, LinkSelfContained)?;
28513020
key!(pre_link_args_json = "pre-link-args", link_args);
28523021
key!(late_link_args_json = "late-link-args", link_args);
28533022
key!(late_link_args_dynamic_json = "late-link-args-dynamic", link_args);
@@ -3103,7 +3272,6 @@ impl ToJson for Target {
31033272
target_option_val!(post_link_objects);
31043273
target_option_val!(pre_link_objects_self_contained, "pre-link-objects-fallback");
31053274
target_option_val!(post_link_objects_self_contained, "post-link-objects-fallback");
3106-
target_option_val!(link_self_contained, "crt-objects-fallback");
31073275
target_option_val!(link_args - pre_link_args_json, "pre-link-args");
31083276
target_option_val!(link_args - late_link_args_json, "late-link-args");
31093277
target_option_val!(link_args - late_link_args_dynamic_json, "late-link-args-dynamic");
@@ -3200,6 +3368,10 @@ impl ToJson for Target {
32003368
d.insert("default-adjusted-cabi".into(), Abi::name(abi).to_json());
32013369
}
32023370

3371+
// Serializing `-Clink-self-contained` needs a dynamic key to support the
3372+
// backwards-compatible variants.
3373+
d.insert(self.link_self_contained.json_key().into(), self.link_self_contained.to_json());
3374+
32033375
Json::Object(d)
32043376
}
32053377
}

compiler/rustc_target/src/spec/tests/tests_impl.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ impl Target {
9797
);
9898
}
9999

100-
if self.link_self_contained == LinkSelfContainedDefault::False {
100+
if self.link_self_contained.is_disabled() {
101101
assert!(
102102
self.pre_link_objects_self_contained.is_empty()
103103
&& self.post_link_objects_self_contained.is_empty()

compiler/rustc_target/src/spec/wasm32_wasi.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,8 @@
7272
//! best we can with this target. Don't start relying on too much here unless
7373
//! you know what you're getting in to!
7474
75-
use super::crt_objects::{self, LinkSelfContainedDefault};
75+
use super::crt_objects;
76+
use super::LinkSelfContained;
7677
use super::{wasm_base, Cc, LinkerFlavor, Target};
7778

7879
pub fn target() -> Target {
@@ -85,7 +86,7 @@ pub fn target() -> Target {
8586
options.post_link_objects_self_contained = crt_objects::post_wasi_self_contained();
8687

8788
// FIXME: Figure out cases in which WASM needs to link with a native toolchain.
88-
options.link_self_contained = LinkSelfContainedDefault::True;
89+
options.link_self_contained = LinkSelfContained::True;
8990

9091
// Right now this is a bit of a workaround but we're currently saying that
9192
// the target by default has a static crt which we're taking as a signal

compiler/rustc_target/src/spec/wasm32_wasi_preview1_threads.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,8 @@
7272
//! best we can with this target. Don't start relying on too much here unless
7373
//! you know what you're getting in to!
7474
75-
use super::crt_objects::{self, LinkSelfContainedDefault};
75+
use super::crt_objects;
76+
use super::LinkSelfContained;
7677
use super::{wasm_base, Cc, LinkerFlavor, Target};
7778

7879
pub fn target() -> Target {
@@ -98,7 +99,7 @@ pub fn target() -> Target {
9899
options.post_link_objects_self_contained = crt_objects::post_wasi_self_contained();
99100

100101
// FIXME: Figure out cases in which WASM needs to link with a native toolchain.
101-
options.link_self_contained = LinkSelfContainedDefault::True;
102+
options.link_self_contained = LinkSelfContained::True;
102103

103104
// Right now this is a bit of a workaround but we're currently saying that
104105
// the target by default has a static crt which we're taking as a signal

compiler/rustc_target/src/spec/wasm_base.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
use super::crt_objects::LinkSelfContainedDefault;
21
use super::{cvs, Cc, LinkerFlavor, PanicStrategy, RelocModel, TargetOptions, TlsModel};
2+
use crate::spec::LinkSelfContained;
33

44
pub fn options() -> TargetOptions {
55
macro_rules! args {
@@ -100,7 +100,7 @@ pub fn options() -> TargetOptions {
100100
// rust-lang/rust#104137: cannot blindly remove this without putting in
101101
// some other way to compensate for lack of `-nostartfiles` in linker
102102
// invocation.
103-
link_self_contained: LinkSelfContainedDefault::True,
103+
link_self_contained: LinkSelfContained::True,
104104

105105
// This has no effect in LLVM 8 or prior, but in LLVM 9 and later when
106106
// PIC code is implemented this has quite a drastic effect if it stays

0 commit comments

Comments
 (0)