allow target specs to declare self-contained linking components

This commit is contained in:
Rémy Rakic 2023-09-20 22:13:43 +00:00
parent 39acbed8d6
commit 0bca45f620
8 changed files with 197 additions and 27 deletions

View File

@ -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");

View File

@ -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;

View File

@ -520,6 +520,92 @@ impl ToJson for LinkerFlavorCli {
}
}
/// The different `-Clink-self-contained` options that can be specified in a target spec:
/// - enabling or disabling in bulk
/// - some target-specific pieces of inference to determine whether to use self-contained linking
/// if `-Clink-self-contained` is not specified explicitly (e.g. on musl/mingw)
/// - explicitly enabling some of the self-contained linking components, e.g. the linker component
/// to use `rust-lld`
#[derive(Clone, Copy, PartialEq, Debug)]
pub enum LinkSelfContained {
/// The target spec explicitly enables self-contained linking.
True,
/// The target spec explicitly disables self-contained linking.
False,
/// The target spec requests that the self-contained mode is inferred, in the context of musl.
InferredForMusl,
/// The target spec requests that the self-contained mode is inferred, in the context of mingw.
InferredForMingw,
/// The target spec explicitly enables a list of self-contained linking components: e.g. for
/// targets opting into a subset of components like the CLI's `-C link-self-contained=+linker`.
WithComponents(LinkSelfContainedComponents),
}
impl ToJson for LinkSelfContained {
fn to_json(&self) -> Json {
match *self {
LinkSelfContained::WithComponents(components) => {
// Serialize the components in a json object's `components` field, to prepare for a
// future where `crt-objects-fallback` is removed from the json specs and
// incorporated as a field here.
let mut map = BTreeMap::new();
map.insert("components", components);
map.to_json()
}
// Stable values backwards-compatible with `LinkSelfContainedDefault`
LinkSelfContained::True => "true".to_json(),
LinkSelfContained::False => "false".to_json(),
LinkSelfContained::InferredForMusl => "musl".to_json(),
LinkSelfContained::InferredForMingw => "mingw".to_json(),
}
}
}
impl LinkSelfContained {
/// Returns whether the target spec has self-contained linking explicitly disabled. Used to emit
/// errors if the user then enables it on the CLI.
pub fn is_disabled(self) -> bool {
self == LinkSelfContained::False
}
/// Returns whether the target spec explictly requests self-contained linking, i.e. not via
/// inference.
pub fn is_linker_enabled(self) -> bool {
match self {
LinkSelfContained::True => true,
LinkSelfContained::False => false,
LinkSelfContained::WithComponents(c) => c.contains(LinkSelfContainedComponents::LINKER),
_ => false,
}
}
/// Returns the key to use when serializing the setting to json:
/// - individual components in a `link-self-contained` object value
/// - the other variants as a backwards-compatible `crt-objects-fallback` string
fn json_key(self) -> &'static str {
match self {
LinkSelfContained::WithComponents(_) => "link-self-contained",
_ => "crt-objects-fallback",
}
}
}
impl From<LinkSelfContainedDefault> for LinkSelfContained {
fn from(value: LinkSelfContainedDefault) -> Self {
match value {
LinkSelfContainedDefault::True => LinkSelfContained::True,
LinkSelfContainedDefault::False => LinkSelfContained::False,
LinkSelfContainedDefault::Musl => LinkSelfContained::InferredForMusl,
LinkSelfContainedDefault::Mingw => LinkSelfContained::InferredForMingw,
}
}
}
bitflags::bitflags! {
#[derive(Default)]
/// The `-C link-self-contained` components that can individually be enabled or disabled.
@ -594,6 +680,22 @@ impl IntoIterator for LinkSelfContainedComponents {
}
}
impl ToJson for LinkSelfContainedComponents {
fn to_json(&self) -> Json {
let components: Vec<_> = Self::all_components()
.into_iter()
.filter(|c| self.contains(*c))
.map(|c| {
// We can unwrap because we're iterating over all the known singular components,
// not an actual set of flags where `as_str` can fail.
c.as_str().unwrap().to_owned()
})
.collect();
components.to_json()
}
}
#[derive(Clone, Copy, Debug, PartialEq, Hash, Encodable, Decodable, HashStable_Generic)]
pub enum PanicStrategy {
Unwind,
@ -1769,7 +1871,9 @@ pub struct TargetOptions {
/// Same as `(pre|post)_link_objects`, but when self-contained linking mode is enabled.
pub pre_link_objects_self_contained: CrtObjects,
pub post_link_objects_self_contained: CrtObjects,
pub link_self_contained: LinkSelfContainedDefault,
/// Behavior for the self-contained linking mode: inferred for some targets, or explicitly
/// enabled (in bulk, or with individual components).
pub link_self_contained: LinkSelfContained,
/// Linker arguments that are passed *before* any user-defined libraries.
pub pre_link_args: LinkArgs,
@ -2242,7 +2346,7 @@ impl Default for TargetOptions {
post_link_objects: Default::default(),
pre_link_objects_self_contained: Default::default(),
post_link_objects_self_contained: Default::default(),
link_self_contained: LinkSelfContainedDefault::False,
link_self_contained: LinkSelfContained::False,
pre_link_args: LinkArgs::new(),
pre_link_args_json: LinkArgsCli::new(),
late_link_args: LinkArgs::new(),
@ -2723,12 +2827,47 @@ impl Target {
}
Ok::<(), String>(())
} );
($key_name:ident = $json_name:expr, link_self_contained) => ( {
($key_name:ident, LinkSelfContained) => ( {
// Skeleton of what needs to be parsed:
//
// ```
// $name: {
// "components": [
// <array of strings>
// ]
// }
// ```
let name = (stringify!($key_name)).replace("_", "-");
if let Some(o) = obj.remove(&name) {
if let Some(o) = o.as_object() {
let component_array = o.get("components")
.ok_or_else(|| format!("{name}: expected a \
JSON object with a `components` field."))?;
let component_array = component_array.as_array()
.ok_or_else(|| format!("{name}.components: expected a JSON array"))?;
let mut components = LinkSelfContainedComponents::empty();
for s in component_array {
components |= match s.as_str() {
Some(s) => {
LinkSelfContainedComponents::from_str(s)
.ok_or_else(|| format!("unknown \
`-Clink-self-contained` component: {s}"))?
},
_ => return Err(format!("not a string: {:?}", s)),
};
}
base.$key_name = LinkSelfContained::WithComponents(components);
} else {
incorrect_type.push(name)
}
}
Ok::<(), String>(())
} );
($key_name:ident = $json_name:expr, LinkSelfContainedDefault) => ( {
let name = $json_name;
obj.remove(name).and_then(|o| o.as_str().and_then(|s| {
match s.parse::<LinkSelfContainedDefault>() {
Ok(lsc_default) => base.$key_name = lsc_default,
Ok(lsc_default) => base.$key_name = lsc_default.into(),
_ => return Some(Err(format!("'{}' is not a valid `-Clink-self-contained` default. \
Use 'false', 'true', 'musl' or 'mingw'", s))),
}
@ -2877,7 +3016,10 @@ impl Target {
key!(post_link_objects = "post-link-objects", link_objects);
key!(pre_link_objects_self_contained = "pre-link-objects-fallback", link_objects);
key!(post_link_objects_self_contained = "post-link-objects-fallback", link_objects);
key!(link_self_contained = "crt-objects-fallback", link_self_contained)?;
// Deserializes the backwards-compatible variants of `-Clink-self-contained`
key!(link_self_contained = "crt-objects-fallback", LinkSelfContainedDefault)?;
// Deserializes the components variant of `-Clink-self-contained`
key!(link_self_contained, LinkSelfContained)?;
key!(pre_link_args_json = "pre-link-args", link_args);
key!(late_link_args_json = "late-link-args", link_args);
key!(late_link_args_dynamic_json = "late-link-args-dynamic", link_args);
@ -3133,7 +3275,6 @@ impl ToJson for Target {
target_option_val!(post_link_objects);
target_option_val!(pre_link_objects_self_contained, "pre-link-objects-fallback");
target_option_val!(post_link_objects_self_contained, "post-link-objects-fallback");
target_option_val!(link_self_contained, "crt-objects-fallback");
target_option_val!(link_args - pre_link_args_json, "pre-link-args");
target_option_val!(link_args - late_link_args_json, "late-link-args");
target_option_val!(link_args - late_link_args_dynamic_json, "late-link-args-dynamic");
@ -3230,6 +3371,10 @@ impl ToJson for Target {
d.insert("default-adjusted-cabi".into(), Abi::name(abi).to_json());
}
// Serializing `-Clink-self-contained` needs a dynamic key to support the
// backwards-compatible variants.
d.insert(self.link_self_contained.json_key().into(), self.link_self_contained.to_json());
Json::Object(d)
}
}

View File

@ -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()

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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,