Auto merge of #116035 - lqd:mcp-510-target-specs, r=petrochenkov
Allow target specs to use an LLD flavor, and self-contained linking components This PR allows: - target specs to use an LLD linker-flavor: this is needed to switch `x86_64-unknown-linux-gnu` to using LLD, and is currently not possible because the current flavor json serialization fails to roundtrip on the modern linker-flavors. This can e.g. be seen in https://github.com/rust-lang/rust/pull/115622#discussion_r1321312880 which explains where an `Lld::Yes` is ultimately deserialized into an `Lld::No`. - target specs to declare self-contained linking components: this is needed to switch `x86_64-unknown-linux-gnu` to using `rust-lld` - adds an end-to-end test of a custom target json simulating `x86_64-unknown-linux-gnu` being switched to using `rust-lld` - disables codegen backends from participating because they don't support `-Zgcc-ld=lld` which is the basis of mcp510. r? `@petrochenkov:` if the approach discussed https://github.com/rust-lang/rust/pull/115622#discussion_r1329403467 and on zulip would work for you: basically, see if we can emit only modern linker flavors in the json specs, but accept both old and new flavors while reading them, to fix the roundtrip issue. The backwards compatible `LinkSelfContainedDefault` variants are still serialized and deserialized in `crt-objects-fallback`, while the spec equivalent of e.g. `-Clink-self-contained=+linker` is serialized into a different json object (with future-proofing to incorporate `crt-objects-fallback` in the future). --- I've been test-driving this in https://github.com/rust-lang/rust/pull/113382 to test actually switching `x86_64-unknown-linux-gnu` to `rust-lld` (and fix what needs to be fixed in CI, bootstrap, etc), and it seems to work fine.
This commit is contained in:
commit
31ffe48723
12 changed files with 372 additions and 94 deletions
|
@ -22,7 +22,9 @@ 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::LinkSelfContainedComponents;
|
||||
use rustc_target::spec::LinkSelfContainedDefault;
|
||||
use rustc_target::spec::{Cc, LinkOutputKind, LinkerFlavor, Lld, PanicStrategy};
|
||||
use rustc_target::spec::{RelocModel, RelroLevel, SanitizerSet, SplitDebuginfo};
|
||||
|
||||
|
@ -728,6 +730,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,
|
||||
|
@ -737,6 +740,7 @@ fn link_natively<'a>(
|
|||
tmpdir,
|
||||
out_filename,
|
||||
codegen_results,
|
||||
self_contained_components,
|
||||
)?;
|
||||
|
||||
linker::disable_localization(&mut cmd);
|
||||
|
@ -812,14 +816,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
|
||||
|
@ -830,7 +834,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::<Vec<_>>()
|
||||
};
|
||||
let pre_objects_static_pie = get_objects(pre_objects, LinkOutputKind::StaticPicExe);
|
||||
|
@ -1710,26 +1716,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 {
|
||||
if let Some(self_contained) = sess.opts.cg.link_self_contained.explicitly_set {
|
||||
if sess.target.link_self_contained == LinkSelfContainedDefault::False {
|
||||
sess.emit_err(errors::UnsupportedLinkSelfContained);
|
||||
}
|
||||
return self_contained;
|
||||
}
|
||||
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,
|
||||
|
||||
match sess.target.link_self_contained {
|
||||
LinkSelfContainedDefault::False => false,
|
||||
LinkSelfContainedDefault::True => true,
|
||||
// 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 => {
|
||||
sess.host == sess.target
|
||||
&& sess.target.vendor != "uwp"
|
||||
&& detect_self_contained_mingw(&sess)
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
};
|
||||
if self_contained {
|
||||
LinkSelfContainedComponents::all()
|
||||
} else {
|
||||
LinkSelfContainedComponents::empty()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2030,13 +2053,14 @@ fn linker_with_args<'a>(
|
|||
tmpdir: &Path,
|
||||
out_filename: &Path,
|
||||
codegen_results: &CodegenResults,
|
||||
self_contained_components: LinkSelfContainedComponents,
|
||||
) -> Result<Command, ErrorGuaranteed> {
|
||||
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);
|
||||
|
@ -2063,7 +2087,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,
|
||||
|
@ -2206,7 +2230,7 @@ fn linker_with_args<'a>(
|
|||
cmd,
|
||||
sess,
|
||||
link_output_kind,
|
||||
self_contained,
|
||||
self_contained_components,
|
||||
flavor,
|
||||
crate_type,
|
||||
codegen_results,
|
||||
|
@ -2222,7 +2246,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 ------------
|
||||
|
||||
|
@ -2239,7 +2263,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,
|
||||
|
@ -2247,7 +2271,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);
|
||||
|
||||
|
@ -2272,7 +2296,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();
|
||||
}
|
||||
|
||||
|
@ -2303,7 +2327,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);
|
||||
|
||||
|
@ -2953,8 +2977,16 @@ fn get_apple_sdk_root(sdk_name: &str) -> Result<String, errors::AppleSdkRootErro
|
|||
/// invoke it:
|
||||
/// - when the self-contained linker flag is active: the build of `lld` distributed with rustc,
|
||||
/// - or any `lld` available to `cc`.
|
||||
fn add_lld_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) {
|
||||
debug!("add_lld_args requested, flavor: '{flavor:?}'");
|
||||
fn add_lld_args(
|
||||
cmd: &mut dyn Linker,
|
||||
sess: &Session,
|
||||
flavor: LinkerFlavor,
|
||||
self_contained_components: LinkSelfContainedComponents,
|
||||
) {
|
||||
debug!(
|
||||
"add_lld_args requested, flavor: '{:?}', target self-contained components: {:?}",
|
||||
flavor, self_contained_components,
|
||||
);
|
||||
|
||||
// If the flavor doesn't use a C/C++ compiler to invoke the linker, or doesn't opt in to `lld`,
|
||||
// we don't need to do anything.
|
||||
|
@ -2963,9 +2995,32 @@ 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_cli = sess.opts.cg.link_self_contained.is_linker_enabled();
|
||||
let self_contained_target = self_contained_components.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({
|
||||
let mut arg = OsString::from("-B");
|
||||
|
|
|
@ -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<LinkOutputKind, Vec<Cow<'static, str>>>;
|
||||
|
||||
|
@ -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<LinkSelfContainedDefault, ()> {
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::spec::crt_objects::{self, LinkSelfContainedDefault};
|
||||
use crate::spec::TargetOptions;
|
||||
use crate::spec::crt_objects;
|
||||
use crate::spec::{LinkSelfContainedDefault, TargetOptions};
|
||||
|
||||
pub fn opts() -> TargetOptions {
|
||||
let mut base = super::linux_base::opts();
|
||||
|
@ -7,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 = LinkSelfContainedDefault::Musl;
|
||||
base.link_self_contained = LinkSelfContainedDefault::InferredForMusl;
|
||||
|
||||
// These targets statically link libc by default
|
||||
base.crt_static_default = true;
|
||||
|
|
|
@ -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};
|
||||
|
@ -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<Cc>, Option<Lld>) {
|
||||
match cli {
|
||||
LinkerFlavorCli::Gnu(cc, lld) | LinkerFlavorCli::Darwin(cc, lld) => {
|
||||
|
@ -520,6 +535,98 @@ 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 LinkSelfContainedDefault {
|
||||
/// 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),
|
||||
}
|
||||
|
||||
/// Parses a backwards-compatible `-Clink-self-contained` option string, without components.
|
||||
impl FromStr for LinkSelfContainedDefault {
|
||||
type Err = ();
|
||||
|
||||
fn from_str(s: &str) -> Result<LinkSelfContainedDefault, ()> {
|
||||
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 {
|
||||
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.
|
||||
let mut map = BTreeMap::new();
|
||||
map.insert("components", components);
|
||||
map.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 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 == 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 {
|
||||
LinkSelfContainedDefault::True => true,
|
||||
LinkSelfContainedDefault::False => false,
|
||||
LinkSelfContainedDefault::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 {
|
||||
LinkSelfContainedDefault::WithComponents(_) => "link-self-contained",
|
||||
_ => "crt-objects-fallback",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bitflags::bitflags! {
|
||||
#[derive(Default)]
|
||||
/// The `-C link-self-contained` components that can individually be enabled or disabled.
|
||||
|
@ -579,6 +686,21 @@ impl LinkSelfContainedComponents {
|
|||
LinkSelfContainedComponents::MINGW,
|
||||
]
|
||||
}
|
||||
|
||||
/// Returns whether at least a component is enabled.
|
||||
pub fn are_any_components_enabled(self) -> 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 {
|
||||
|
@ -594,6 +716,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,
|
||||
|
@ -1770,6 +1908,8 @@ 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,
|
||||
/// Behavior for the self-contained linking mode: inferred for some targets, or explicitly
|
||||
/// enabled (in bulk, or with individual components).
|
||||
pub link_self_contained: LinkSelfContainedDefault,
|
||||
|
||||
/// Linker arguments that are passed *before* any user-defined libraries.
|
||||
|
@ -2170,7 +2310,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 [
|
||||
|
@ -2180,8 +2320,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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2724,8 +2866,43 @@ impl Target {
|
|||
}
|
||||
Ok::<(), String>(())
|
||||
} );
|
||||
|
||||
($key_name:ident = $json_name:expr, link_self_contained) => ( {
|
||||
($key_name:ident, link_self_contained_components) => ( {
|
||||
// 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 = LinkSelfContainedDefault::WithComponents(components);
|
||||
} else {
|
||||
incorrect_type.push(name)
|
||||
}
|
||||
}
|
||||
Ok::<(), String>(())
|
||||
} );
|
||||
($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::<LinkSelfContainedDefault>() {
|
||||
|
@ -2878,7 +3055,13 @@ 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",
|
||||
link_self_contained_backwards_compatible
|
||||
)?;
|
||||
// Deserializes the components variant of `-Clink-self-contained`
|
||||
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);
|
||||
|
@ -3134,7 +3317,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");
|
||||
|
@ -3231,6 +3413,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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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::LinkSelfContainedDefault;
|
||||
use super::{wasm_base, Cc, LinkerFlavor, Target};
|
||||
|
||||
pub fn target() -> Target {
|
||||
|
|
|
@ -72,8 +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::{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();
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use super::crt_objects::LinkSelfContainedDefault;
|
||||
use super::LinkSelfContainedDefault;
|
||||
use super::{cvs, Cc, LinkerFlavor, PanicStrategy, RelocModel, TargetOptions, TlsModel};
|
||||
|
||||
pub fn options() -> TargetOptions {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use crate::spec::crt_objects::{self, LinkSelfContainedDefault};
|
||||
use crate::spec::crt_objects;
|
||||
use crate::spec::LinkSelfContainedDefault;
|
||||
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: LinkSelfContainedDefault::InferredForMingw,
|
||||
late_link_args,
|
||||
late_link_args_dynamic,
|
||||
late_link_args_static,
|
||||
|
|
7
tests/run-make/rust-lld-custom-target/Makefile
Normal file
7
tests/run-make/rust-lld-custom-target/Makefile
Normal file
|
@ -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
|
57
tests/run-make/rust-lld-custom-target/custom-target.json
Normal file
57
tests/run-make/rust-lld-custom-target/custom-target.json
Normal file
|
@ -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"
|
||||
}
|
9
tests/run-make/rust-lld-custom-target/lib.rs
Normal file
9
tests/run-make/rust-lld-custom-target/lib.rs
Normal file
|
@ -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]
|
Loading…
Add table
Add a link
Reference in a new issue