Skip to content

Commit e1fe7bb

Browse files
committed
allow target specs to declare self-contained linking components
1 parent a8da717 commit e1fe7bb

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

@@ -1703,21 +1704,37 @@ fn detect_self_contained_mingw(sess: &Session) -> bool {
17031704
/// instead of being found somewhere on the host system.
17041705
/// We only provide such support for a very limited number of targets.
17051706
fn self_contained(sess: &Session, crate_type: CrateType) -> bool {
1707+
// Emit an error if the user requested self-contained mode on the CLI but the target explicitly
1708+
// refuses it.
17061709
if let Some(self_contained) = sess.opts.cg.link_self_contained.explicitly_set {
1707-
if sess.target.link_self_contained == LinkSelfContainedDefault::False {
1710+
if sess.target.link_self_contained.is_disabled() {
17081711
sess.emit_err(errors::UnsupportedLinkSelfContained);
17091712
}
17101713
return self_contained;
17111714
}
17121715

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

29883005
// 1. Implement the "self-contained" part of this feature by adding rustc distribution
2989-
// directories to the tool's search path:
2990-
// - if the self-contained linker is enabled on the CLI (or by the unstable CLI flag that will
2991-
// be removed eventually),
3006+
// directories to the tool's search path, depending on a mix between what users can specify on
3007+
// the CLI, and what the target spec enables (as it can't disable components):
3008+
// - if the self-contained linker is enabled on the CLI or by the target spec (or by the
3009+
// unstable CLI flag that will be removed eventually),
29923010
// - and if the self-contained linker is not disabled on the CLI.
2993-
let self_contained_linker =
2994-
sess.opts.cg.link_self_contained.is_linker_enabled() || unstable_use_lld;
3011+
let self_contained_linker = sess.target.options.link_self_contained.is_linker_enabled()
3012+
|| sess.opts.cg.link_self_contained.is_linker_enabled()
3013+
|| unstable_use_lld;
29953014
if self_contained_linker && !sess.opts.cg.link_self_contained.is_linker_disabled() {
29963015
for path in sess.get_tools_search_paths(false) {
29973016
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
@@ -533,6 +533,92 @@ impl ToJson for LinkerFlavorCli {
533533
}
534534
}
535535

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

570699
#[derive(Clone, Copy, Debug, PartialEq, Hash, Encodable, Decodable, HashStable_Generic)]
@@ -1742,7 +1871,9 @@ pub struct TargetOptions {
17421871
/// Same as `(pre|post)_link_objects`, but when self-contained linking mode is enabled.
17431872
pub pre_link_objects_self_contained: CrtObjects,
17441873
pub post_link_objects_self_contained: CrtObjects,
1745-
pub link_self_contained: LinkSelfContainedDefault,
1874+
/// Behavior for the self-contained linking mode: inferred for some targets, or explicitly
1875+
/// enabled (in bulk, or with individual components).
1876+
pub link_self_contained: LinkSelfContained,
17461877

17471878
/// Linker arguments that are passed *before* any user-defined libraries.
17481879
pub pre_link_args: LinkArgs,
@@ -2215,7 +2346,7 @@ impl Default for TargetOptions {
22152346
post_link_objects: Default::default(),
22162347
pre_link_objects_self_contained: Default::default(),
22172348
post_link_objects_self_contained: Default::default(),
2218-
link_self_contained: LinkSelfContainedDefault::False,
2349+
link_self_contained: LinkSelfContained::False,
22192350
pre_link_args: LinkArgs::new(),
22202351
pre_link_args_json: LinkArgsCli::new(),
22212352
late_link_args: LinkArgs::new(),
@@ -2696,12 +2827,47 @@ impl Target {
26962827
}
26972828
Ok::<(), String>(())
26982829
} );
2699-
2700-
($key_name:ident = $json_name:expr, link_self_contained) => ( {
2830+
($key_name:ident, LinkSelfContained) => ( {
2831+
// Skeleton of what needs to be parsed:
2832+
//
2833+
// ```
2834+
// $name: {
2835+
// "components": [
2836+
// <array of strings>
2837+
// ]
2838+
// }
2839+
// ```
2840+
let name = (stringify!($key_name)).replace("_", "-");
2841+
if let Some(o) = obj.remove(&name) {
2842+
if let Some(o) = o.as_object() {
2843+
let component_array = o.get("components")
2844+
.ok_or_else(|| format!("{name}: expected a \
2845+
JSON object with a `components` field."))?;
2846+
let component_array = component_array.as_array()
2847+
.ok_or_else(|| format!("{name}.components: expected a JSON array"))?;
2848+
let mut components = LinkSelfContainedComponents::empty();
2849+
for s in component_array {
2850+
components |= match s.as_str() {
2851+
Some(s) => {
2852+
LinkSelfContainedComponents::from_str(s)
2853+
.ok_or_else(|| format!("unknown \
2854+
`-Clink-self-contained` component: {s}"))?
2855+
},
2856+
_ => return Err(format!("not a string: {:?}", s)),
2857+
};
2858+
}
2859+
base.$key_name = LinkSelfContained::WithComponents(components);
2860+
} else {
2861+
incorrect_type.push(name)
2862+
}
2863+
}
2864+
Ok::<(), String>(())
2865+
} );
2866+
($key_name:ident = $json_name:expr, LinkSelfContainedDefault) => ( {
27012867
let name = $json_name;
27022868
obj.remove(name).and_then(|o| o.as_str().and_then(|s| {
27032869
match s.parse::<LinkSelfContainedDefault>() {
2704-
Ok(lsc_default) => base.$key_name = lsc_default,
2870+
Ok(lsc_default) => base.$key_name = lsc_default.into(),
27052871
_ => return Some(Err(format!("'{}' is not a valid `-Clink-self-contained` default. \
27062872
Use 'false', 'true', 'musl' or 'mingw'", s))),
27072873
}
@@ -2850,7 +3016,10 @@ impl Target {
28503016
key!(post_link_objects = "post-link-objects", link_objects);
28513017
key!(pre_link_objects_self_contained = "pre-link-objects-fallback", link_objects);
28523018
key!(post_link_objects_self_contained = "post-link-objects-fallback", link_objects);
2853-
key!(link_self_contained = "crt-objects-fallback", link_self_contained)?;
3019+
// Deserializes the backwards-compatible variants of `-Clink-self-contained`
3020+
key!(link_self_contained = "crt-objects-fallback", LinkSelfContainedDefault)?;
3021+
// Deserializes the components variant of `-Clink-self-contained`
3022+
key!(link_self_contained, LinkSelfContained)?;
28543023
key!(pre_link_args_json = "pre-link-args", link_args);
28553024
key!(late_link_args_json = "late-link-args", link_args);
28563025
key!(late_link_args_dynamic_json = "late-link-args-dynamic", link_args);
@@ -3106,7 +3275,6 @@ impl ToJson for Target {
31063275
target_option_val!(post_link_objects);
31073276
target_option_val!(pre_link_objects_self_contained, "pre-link-objects-fallback");
31083277
target_option_val!(post_link_objects_self_contained, "post-link-objects-fallback");
3109-
target_option_val!(link_self_contained, "crt-objects-fallback");
31103278
target_option_val!(link_args - pre_link_args_json, "pre-link-args");
31113279
target_option_val!(link_args - late_link_args_json, "late-link-args");
31123280
target_option_val!(link_args - late_link_args_dynamic_json, "late-link-args-dynamic");
@@ -3203,6 +3371,10 @@ impl ToJson for Target {
32033371
d.insert("default-adjusted-cabi".into(), Abi::name(abi).to_json());
32043372
}
32053373

3374+
// Serializing `-Clink-self-contained` needs a dynamic key to support the
3375+
// backwards-compatible variants.
3376+
d.insert(self.link_self_contained.json_key().into(), self.link_self_contained.to_json());
3377+
32063378
Json::Object(d)
32073379
}
32083380
}

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)