Skip to content

Commit 00b0414

Browse files
committed
allow target specs to declare self-contained linking components
1 parent 39acbed commit 00b0414

File tree

8 files changed

+197
-27
lines changed

8 files changed

+197
-27
lines changed

compiler/rustc_codegen_ssa/src/back/link.rs

+30-9
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)
@@ -2978,9 +2995,13 @@ fn add_lld_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) {
29782995
}
29792996

29802997
// 1. Implement the "self-contained" part of this feature by adding rustc distribution
2981-
// directories to the tool's search path:
2982-
// - if the self-contained linker is enabled on the CLI.
2983-
if sess.opts.cg.link_self_contained.is_linker_enabled() {
2998+
// directories to the tool's search path, depending on a mix between what users can specify on
2999+
// the CLI, and what the target spec enables (as it can't disable components):
3000+
// - if the self-contained linker is enabled on the CLI or by the target spec,
3001+
// - and if the self-contained linker is not disabled on the CLI.
3002+
let self_contained_linker = sess.opts.cg.link_self_contained.is_linker_enabled()
3003+
|| sess.target.options.link_self_contained.is_linker_enabled();
3004+
if self_contained_linker && !sess.opts.cg.link_self_contained.is_linker_disabled() {
29843005
for path in sess.get_tools_search_paths(false) {
29853006
cmd.arg({
29863007
let mut arg = OsString::from("-B");

compiler/rustc_target/src/spec/linux_musl_base.rs

+3-2
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

+152-7
Original file line numberDiff line numberDiff line change
@@ -520,6 +520,92 @@ impl ToJson for LinkerFlavorCli {
520520
}
521521
}
522522

523+
/// The different `-Clink-self-contained` options that can be specified in a target spec:
524+
/// - enabling or disabling in bulk
525+
/// - some target-specific pieces of inference to determine whether to use self-contained linking
526+
/// if `-Clink-self-contained` is not specified explicitly (e.g. on musl/mingw)
527+
/// - explicitly enabling some of the self-contained linking components, e.g. the linker component
528+
/// to use `rust-lld`
529+
#[derive(Clone, Copy, PartialEq, Debug)]
530+
pub enum LinkSelfContained {
531+
/// The target spec explicitly enables self-contained linking.
532+
True,
533+
534+
/// The target spec explicitly disables self-contained linking.
535+
False,
536+
537+
/// The target spec requests that the self-contained mode is inferred, in the context of musl.
538+
InferredForMusl,
539+
540+
/// The target spec requests that the self-contained mode is inferred, in the context of mingw.
541+
InferredForMingw,
542+
543+
/// The target spec explicitly enables a list of self-contained linking components: e.g. for
544+
/// targets opting into a subset of components like the CLI's `-C link-self-contained=+linker`.
545+
WithComponents(LinkSelfContainedComponents),
546+
}
547+
548+
impl ToJson for LinkSelfContained {
549+
fn to_json(&self) -> Json {
550+
match *self {
551+
LinkSelfContained::WithComponents(components) => {
552+
// Serialize the components in a json object's `components` field, to prepare for a
553+
// future where `crt-objects-fallback` is removed from the json specs and
554+
// incorporated as a field here.
555+
let mut map = BTreeMap::new();
556+
map.insert("components", components);
557+
map.to_json()
558+
}
559+
560+
// Stable values backwards-compatible with `LinkSelfContainedDefault`
561+
LinkSelfContained::True => "true".to_json(),
562+
LinkSelfContained::False => "false".to_json(),
563+
LinkSelfContained::InferredForMusl => "musl".to_json(),
564+
LinkSelfContained::InferredForMingw => "mingw".to_json(),
565+
}
566+
}
567+
}
568+
569+
impl LinkSelfContained {
570+
/// Returns whether the target spec has self-contained linking explicitly disabled. Used to emit
571+
/// errors if the user then enables it on the CLI.
572+
pub fn is_disabled(self) -> bool {
573+
self == Self::False
574+
}
575+
576+
/// Returns whether the target spec explictly requests self-contained linking, i.e. not via
577+
/// inference.
578+
pub fn is_linker_enabled(self) -> bool {
579+
match self {
580+
LinkSelfContained::True => true,
581+
LinkSelfContained::False => false,
582+
LinkSelfContained::WithComponents(c) => c.contains(LinkSelfContainedComponents::LINKER),
583+
_ => false,
584+
}
585+
}
586+
587+
/// Returns the key to use when serializing the setting to json:
588+
/// - individual components in a `link-self-contained` object value
589+
/// - the other variants as a backwards-compatible `crt-objects-fallback` string
590+
fn json_key(self) -> &'static str {
591+
match self {
592+
LinkSelfContained::WithComponents(_) => "link-self-contained",
593+
_ => "crt-objects-fallback",
594+
}
595+
}
596+
}
597+
598+
impl From<LinkSelfContainedDefault> for LinkSelfContained {
599+
fn from(value: LinkSelfContainedDefault) -> Self {
600+
match value {
601+
LinkSelfContainedDefault::True => LinkSelfContained::True,
602+
LinkSelfContainedDefault::False => LinkSelfContained::False,
603+
LinkSelfContainedDefault::Musl => LinkSelfContained::InferredForMusl,
604+
LinkSelfContainedDefault::Mingw => LinkSelfContained::InferredForMingw,
605+
}
606+
}
607+
}
608+
523609
bitflags::bitflags! {
524610
#[derive(Default)]
525611
/// The `-C link-self-contained` components that can individually be enabled or disabled.
@@ -594,6 +680,22 @@ impl IntoIterator for LinkSelfContainedComponents {
594680
}
595681
}
596682

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+
}
697+
}
698+
597699
#[derive(Clone, Copy, Debug, PartialEq, Hash, Encodable, Decodable, HashStable_Generic)]
598700
pub enum PanicStrategy {
599701
Unwind,
@@ -1769,7 +1871,9 @@ pub struct TargetOptions {
17691871
/// Same as `(pre|post)_link_objects`, but when self-contained linking mode is enabled.
17701872
pub pre_link_objects_self_contained: CrtObjects,
17711873
pub post_link_objects_self_contained: CrtObjects,
1772-
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,
17731877

17741878
/// Linker arguments that are passed *before* any user-defined libraries.
17751879
pub pre_link_args: LinkArgs,
@@ -2242,7 +2346,7 @@ impl Default for TargetOptions {
22422346
post_link_objects: Default::default(),
22432347
pre_link_objects_self_contained: Default::default(),
22442348
post_link_objects_self_contained: Default::default(),
2245-
link_self_contained: LinkSelfContainedDefault::False,
2349+
link_self_contained: LinkSelfContained::False,
22462350
pre_link_args: LinkArgs::new(),
22472351
pre_link_args_json: LinkArgsCli::new(),
22482352
late_link_args: LinkArgs::new(),
@@ -2723,12 +2827,47 @@ impl Target {
27232827
}
27242828
Ok::<(), String>(())
27252829
} );
2726-
2727-
($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) => ( {
27282867
let name = $json_name;
27292868
obj.remove(name).and_then(|o| o.as_str().and_then(|s| {
27302869
match s.parse::<LinkSelfContainedDefault>() {
2731-
Ok(lsc_default) => base.$key_name = lsc_default,
2870+
Ok(lsc_default) => base.$key_name = lsc_default.into(),
27322871
_ => return Some(Err(format!("'{}' is not a valid `-Clink-self-contained` default. \
27332872
Use 'false', 'true', 'musl' or 'mingw'", s))),
27342873
}
@@ -2877,7 +3016,10 @@ impl Target {
28773016
key!(post_link_objects = "post-link-objects", link_objects);
28783017
key!(pre_link_objects_self_contained = "pre-link-objects-fallback", link_objects);
28793018
key!(post_link_objects_self_contained = "post-link-objects-fallback", link_objects);
2880-
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)?;
28813023
key!(pre_link_args_json = "pre-link-args", link_args);
28823024
key!(late_link_args_json = "late-link-args", link_args);
28833025
key!(late_link_args_dynamic_json = "late-link-args-dynamic", link_args);
@@ -3133,7 +3275,6 @@ impl ToJson for Target {
31333275
target_option_val!(post_link_objects);
31343276
target_option_val!(pre_link_objects_self_contained, "pre-link-objects-fallback");
31353277
target_option_val!(post_link_objects_self_contained, "post-link-objects-fallback");
3136-
target_option_val!(link_self_contained, "crt-objects-fallback");
31373278
target_option_val!(link_args - pre_link_args_json, "pre-link-args");
31383279
target_option_val!(link_args - late_link_args_json, "late-link-args");
31393280
target_option_val!(link_args - late_link_args_dynamic_json, "late-link-args-dynamic");
@@ -3230,6 +3371,10 @@ impl ToJson for Target {
32303371
d.insert("default-adjusted-cabi".into(), Abi::name(abi).to_json());
32313372
}
32323373

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+
32333378
Json::Object(d)
32343379
}
32353380
}

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

+1-1
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

+3-2
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

+3-2
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

+2-2
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

compiler/rustc_target/src/spec/windows_gnu_base.rs

+3-2
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::{cvs, Cc, DebuginfoKind, LinkerFlavor, Lld, SplitDebuginfo, TargetOptions};
34
use std::borrow::Cow;
45

@@ -90,7 +91,7 @@ pub fn opts() -> TargetOptions {
9091
post_link_objects: crt_objects::post_mingw(),
9192
pre_link_objects_self_contained: crt_objects::pre_mingw_self_contained(),
9293
post_link_objects_self_contained: crt_objects::post_mingw_self_contained(),
93-
link_self_contained: LinkSelfContainedDefault::Mingw,
94+
link_self_contained: LinkSelfContained::InferredForMingw,
9495
late_link_args,
9596
late_link_args_dynamic,
9697
late_link_args_static,

0 commit comments

Comments
 (0)