Skip to content

Commit 8e2d5e3

Browse files
committed
Auto merge of rust-lang#112910 - lqd:mcp510, r=petrochenkov
Implement most of MCP510 This implements most of what remains to be done for MCP510: - turns `-C link-self-contained` into a `+`/`-` list of components, like `-C link-self-contained=+linker,+crto,+libc,+unwind,+sanitizers,+mingw`. The scaffolding is present for all these expected components to be implemented and stabilized in the future on their own time. This PR only handles the `-Zgcc-ld=lld` subset of these link-self-contained components as `-Clink-self-contained=+linker` - handles `-C link-self-contained=y|n` as-is today, for compatibility with `rustc_codegen_ssa::back::link::self_contained`'s [explicit opt-in and opt-out](https://github.com/lqd/rust/blob/9eee230cd0a56bfba3ce65121798d9f9f4341cdd/compiler/rustc_codegen_ssa/src/back/link.rs#L1671-L1676). - therefore supports our plan to opt out of `rust-lld` (when it's enabled by default) even for current `-Clink-self-contained` users, with e.g. `-Clink-self-contained -Clink-self-contained=-linker` - turns `add_gcc_ld_path` into its expected final form, by using the `-C link-self-contained=+linker` CLI flag, and whether the `LinkerFlavor` has the expected `Cc::Yes` and `Lld::Yes` shape (this is not yet the case in practice for any CLI linker flavor) - makes the [new clean linker flavors](rust-lang#96827 (comment)) selectable in the CLI in addition to the legacy ones, in order to opt-in to using `cc` and `lld` to emulate `-Zgcc-ld=lld` - ensure the new `-C link-self-contained` components, and `-C linker-flavor`s are unstable, and require `-Z unstable-options` to be used The up-to-date set of flags for the future stable CLI version of `-Zgcc-ld=lld` is currently: `-Clink-self-contained=+linker -Clinker-flavor=gnu-lld-cc -Zunstable-options`. It's possible we'll also need to do something for distros that don't ship `rust-lld`, but maybe there are already no tool search paths to be added to `cc` in this situation anyways. r? `@petrochenkov`
2 parents ba76096 + 38f5a99 commit 8e2d5e3

File tree

13 files changed

+398
-88
lines changed

13 files changed

+398
-88
lines changed

Cargo.lock

+1
Original file line numberDiff line numberDiff line change
@@ -3995,6 +3995,7 @@ name = "rustc_session"
39953995
version = "0.0.0"
39963996
dependencies = [
39973997
"atty",
3998+
"bitflags",
39983999
"getopts",
39994000
"libc",
40004001
"rustc_ast",

compiler/rustc_codegen_ssa/src/back/link.rs

+64-52
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use rustc_metadata::fs::{copy_to_stdout, emit_wrapper_file, METADATA_FILENAME};
1212
use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile;
1313
use rustc_middle::middle::dependency_format::Linkage;
1414
use rustc_middle::middle::exported_symbols::SymbolExportKind;
15-
use rustc_session::config::{self, CFGuard, CrateType, DebugInfo, LdImpl, Strip};
15+
use rustc_session::config::{self, CFGuard, CrateType, DebugInfo, Strip};
1616
use rustc_session::config::{OutputFilenames, OutputType, PrintRequest, SplitDwarfKind};
1717
use rustc_session::cstore::DllImport;
1818
use rustc_session::output::{check_file_is_writeable, invalid_output_for_target, out_filename};
@@ -1688,7 +1688,7 @@ fn detect_self_contained_mingw(sess: &Session) -> bool {
16881688
/// instead of being found somewhere on the host system.
16891689
/// We only provide such support for a very limited number of targets.
16901690
fn self_contained(sess: &Session, crate_type: CrateType) -> bool {
1691-
if let Some(self_contained) = sess.opts.cg.link_self_contained {
1691+
if let Some(self_contained) = sess.opts.cg.link_self_contained.explicitly_set {
16921692
if sess.target.link_self_contained == LinkSelfContainedDefault::False {
16931693
sess.emit_err(errors::UnsupportedLinkSelfContained);
16941694
}
@@ -2246,7 +2246,8 @@ fn add_order_independent_options(
22462246
out_filename: &Path,
22472247
tmpdir: &Path,
22482248
) {
2249-
add_gcc_ld_path(cmd, sess, flavor);
2249+
// Take care of the flavors and CLI options requesting the `lld` linker.
2250+
add_lld_args(cmd, sess, flavor);
22502251

22512252
add_apple_sdk(cmd, sess, flavor);
22522253

@@ -2948,55 +2949,66 @@ fn get_apple_sdk_root(sdk_name: &str) -> Result<String, errors::AppleSdkRootErro
29482949
}
29492950
}
29502951

2951-
fn add_gcc_ld_path(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) {
2952-
if let Some(ld_impl) = sess.opts.unstable_opts.gcc_ld {
2953-
if let LinkerFlavor::Gnu(Cc::Yes, _)
2954-
| LinkerFlavor::Darwin(Cc::Yes, _)
2955-
| LinkerFlavor::WasmLld(Cc::Yes) = flavor
2956-
{
2957-
match ld_impl {
2958-
LdImpl::Lld => {
2959-
// Implement the "self-contained" part of -Zgcc-ld
2960-
// by adding rustc distribution directories to the tool search path.
2961-
for path in sess.get_tools_search_paths(false) {
2962-
cmd.arg({
2963-
let mut arg = OsString::from("-B");
2964-
arg.push(path.join("gcc-ld"));
2965-
arg
2966-
});
2967-
}
2968-
// Implement the "linker flavor" part of -Zgcc-ld
2969-
// by asking cc to use some kind of lld.
2970-
cmd.arg("-fuse-ld=lld");
2971-
2972-
if !flavor.is_gnu() {
2973-
// Tell clang to use a non-default LLD flavor.
2974-
// Gcc doesn't understand the target option, but we currently assume
2975-
// that gcc is not used for Apple and Wasm targets (#97402).
2976-
//
2977-
// Note that we don't want to do that by default on macOS: e.g. passing a
2978-
// 10.7 target to LLVM works, but not to recent versions of clang/macOS, as
2979-
// shown in issue #101653 and the discussion in PR #101792.
2980-
//
2981-
// It could be required in some cases of cross-compiling with
2982-
// `-Zgcc-ld=lld`, but this is generally unspecified, and we don't know
2983-
// which specific versions of clang, macOS SDK, host and target OS
2984-
// combinations impact us here.
2985-
//
2986-
// So we do a simple first-approximation until we know more of what the
2987-
// Apple targets require (and which would be handled prior to hitting this
2988-
// `-Zgcc-ld=lld` codepath anyway), but the expectation is that until then
2989-
// this should be manually passed if needed. We specify the target when
2990-
// targeting a different linker flavor on macOS, and that's also always
2991-
// the case when targeting WASM.
2992-
if sess.target.linker_flavor != sess.host.linker_flavor {
2993-
cmd.arg(format!("--target={}", sess.target.llvm_target));
2994-
}
2995-
}
2996-
}
2997-
}
2998-
} else {
2999-
sess.emit_fatal(errors::OptionGccOnly);
2952+
/// When using the linker flavors opting in to `lld`, or the unstable `-Zgcc-ld=lld` flag, add the
2953+
/// necessary paths and arguments to invoke it:
2954+
/// - when the self-contained linker flag is active: the build of `lld` distributed with rustc,
2955+
/// - or any `lld` available to `cc`.
2956+
fn add_lld_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) {
2957+
let unstable_use_lld = sess.opts.unstable_opts.gcc_ld.is_some();
2958+
debug!("add_lld_args requested, flavor: '{flavor:?}', `-Zgcc-ld=lld`: {unstable_use_lld}");
2959+
2960+
// Sanity check: using the old unstable `-Zgcc-ld=lld` option requires a `cc`-using flavor.
2961+
let flavor_uses_cc = flavor.uses_cc();
2962+
if unstable_use_lld && !flavor_uses_cc {
2963+
sess.emit_fatal(errors::OptionGccOnly);
2964+
}
2965+
2966+
// If the flavor doesn't use a C/C++ compiler to invoke the linker, or doesn't opt in to `lld`,
2967+
// we don't need to do anything.
2968+
let use_lld = flavor.uses_lld() || unstable_use_lld;
2969+
if !flavor_uses_cc || !use_lld {
2970+
return;
2971+
}
2972+
2973+
// 1. Implement the "self-contained" part of this feature by adding rustc distribution
2974+
// directories to the tool's search path.
2975+
let self_contained_linker = sess.opts.cg.link_self_contained.linker() || unstable_use_lld;
2976+
if self_contained_linker {
2977+
for path in sess.get_tools_search_paths(false) {
2978+
cmd.arg({
2979+
let mut arg = OsString::from("-B");
2980+
arg.push(path.join("gcc-ld"));
2981+
arg
2982+
});
2983+
}
2984+
}
2985+
2986+
// 2. Implement the "linker flavor" part of this feature by asking `cc` to use some kind of
2987+
// `lld` as the linker.
2988+
cmd.arg("-fuse-ld=lld");
2989+
2990+
if !flavor.is_gnu() {
2991+
// Tell clang to use a non-default LLD flavor.
2992+
// Gcc doesn't understand the target option, but we currently assume
2993+
// that gcc is not used for Apple and Wasm targets (#97402).
2994+
//
2995+
// Note that we don't want to do that by default on macOS: e.g. passing a
2996+
// 10.7 target to LLVM works, but not to recent versions of clang/macOS, as
2997+
// shown in issue #101653 and the discussion in PR #101792.
2998+
//
2999+
// It could be required in some cases of cross-compiling with
3000+
// `-Zgcc-ld=lld`, but this is generally unspecified, and we don't know
3001+
// which specific versions of clang, macOS SDK, host and target OS
3002+
// combinations impact us here.
3003+
//
3004+
// So we do a simple first-approximation until we know more of what the
3005+
// Apple targets require (and which would be handled prior to hitting this
3006+
// `-Zgcc-ld=lld` codepath anyway), but the expectation is that until then
3007+
// this should be manually passed if needed. We specify the target when
3008+
// targeting a different linker flavor on macOS, and that's also always
3009+
// the case when targeting WASM.
3010+
if sess.target.linker_flavor != sess.host.linker_flavor {
3011+
cmd.arg(format!("--target={}", sess.target.llvm_target));
30003012
}
30013013
}
30023014
}

compiler/rustc_interface/src/tests.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use rustc_session::config::rustc_optgroups;
88
use rustc_session::config::DebugInfo;
99
use rustc_session::config::Input;
1010
use rustc_session::config::InstrumentXRay;
11+
use rustc_session::config::LinkSelfContained;
1112
use rustc_session::config::TraitSolver;
1213
use rustc_session::config::{build_configuration, build_session_options, to_crate_config};
1314
use rustc_session::config::{
@@ -579,7 +580,7 @@ fn test_codegen_options_tracking_hash() {
579580
untracked!(incremental, Some(String::from("abc")));
580581
// `link_arg` is omitted because it just forwards to `link_args`.
581582
untracked!(link_args, vec![String::from("abc"), String::from("def")]);
582-
untracked!(link_self_contained, Some(true));
583+
untracked!(link_self_contained, LinkSelfContained::on());
583584
untracked!(linker, Some(PathBuf::from("linker")));
584585
untracked!(linker_flavor, Some(LinkerFlavorCli::Gcc));
585586
untracked!(no_stack_check, true);

compiler/rustc_session/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ edition = "2021"
55

66
[dependencies]
77
atty = "0.2.13"
8+
bitflags = "1.2.1"
89
getopts = "0.2"
910
rustc_macros = { path = "../rustc_macros" }
1011
tracing = "0.1"

compiler/rustc_session/src/config.rs

+144-20
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,9 @@ use crate::{lint, HashStableContext};
99
use crate::{EarlyErrorHandler, Session};
1010

1111
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
12-
1312
use rustc_data_structures::stable_hasher::{StableOrd, ToStableHashKey};
1413
use rustc_target::abi::Align;
15-
use rustc_target::spec::{LinkerFlavorCli, PanicStrategy, SanitizerSet, SplitDebuginfo};
14+
use rustc_target::spec::{PanicStrategy, SanitizerSet, SplitDebuginfo};
1615
use rustc_target::spec::{Target, TargetTriple, TargetWarnings, TARGETS};
1716

1817
use crate::parse::{CrateCheckConfig, CrateConfig};
@@ -201,6 +200,128 @@ pub enum LinkerPluginLto {
201200
Disabled,
202201
}
203202

203+
impl LinkerPluginLto {
204+
pub fn enabled(&self) -> bool {
205+
match *self {
206+
LinkerPluginLto::LinkerPlugin(_) | LinkerPluginLto::LinkerPluginAuto => true,
207+
LinkerPluginLto::Disabled => false,
208+
}
209+
}
210+
}
211+
212+
/// The different values `-C link-self-contained` can take: a list of individually enabled or
213+
/// disabled components used during linking, coming from the rustc distribution, instead of being
214+
/// found somewhere on the host system.
215+
///
216+
/// They can be set in bulk via `-C link-self-contained=yes|y|on` or `-C
217+
/// link-self-contained=no|n|off`, and those boolean values are the historical defaults.
218+
///
219+
/// But each component is fine-grained, and can be unstably targeted, to use:
220+
/// - some CRT objects
221+
/// - the libc static library
222+
/// - libgcc/libunwind libraries
223+
/// - a linker we distribute
224+
/// - some sanitizer runtime libraries
225+
/// - all other MinGW libraries and Windows import libs
226+
///
227+
#[derive(Default, Clone, PartialEq, Debug)]
228+
pub struct LinkSelfContained {
229+
/// Whether the user explicitly set `-C link-self-contained` on or off, the historical values.
230+
/// Used for compatibility with the existing opt-in and target inference.
231+
pub explicitly_set: Option<bool>,
232+
233+
/// The components that are enabled.
234+
components: LinkSelfContainedComponents,
235+
}
236+
237+
bitflags::bitflags! {
238+
#[derive(Default)]
239+
/// The `-C link-self-contained` components that can individually be enabled or disabled.
240+
pub struct LinkSelfContainedComponents: u8 {
241+
/// CRT objects (e.g. on `windows-gnu`, `musl`, `wasi` targets)
242+
const CRT_OBJECTS = 1 << 0;
243+
/// libc static library (e.g. on `musl`, `wasi` targets)
244+
const LIBC = 1 << 1;
245+
/// libgcc/libunwind (e.g. on `windows-gnu`, `fuchsia`, `fortanix`, `gnullvm` targets)
246+
const UNWIND = 1 << 2;
247+
/// Linker, dlltool, and their necessary libraries (e.g. on `windows-gnu` and for `rust-lld`)
248+
const LINKER = 1 << 3;
249+
/// Sanitizer runtime libraries
250+
const SANITIZERS = 1 << 4;
251+
/// Other MinGW libs and Windows import libs
252+
const MINGW = 1 << 5;
253+
}
254+
}
255+
256+
impl FromStr for LinkSelfContainedComponents {
257+
type Err = ();
258+
259+
fn from_str(s: &str) -> Result<Self, Self::Err> {
260+
Ok(match s {
261+
"crto" => LinkSelfContainedComponents::CRT_OBJECTS,
262+
"libc" => LinkSelfContainedComponents::LIBC,
263+
"unwind" => LinkSelfContainedComponents::UNWIND,
264+
"linker" => LinkSelfContainedComponents::LINKER,
265+
"sanitizers" => LinkSelfContainedComponents::SANITIZERS,
266+
"mingw" => LinkSelfContainedComponents::MINGW,
267+
_ => return Err(()),
268+
})
269+
}
270+
}
271+
272+
impl LinkSelfContained {
273+
/// Incorporates an enabled or disabled component as specified on the CLI, if possible.
274+
/// For example: `+linker`, and `-crto`.
275+
pub(crate) fn handle_cli_component(&mut self, component: &str) -> Result<(), ()> {
276+
// Note that for example `-Cself-contained=y -Cself-contained=-linker` is not an explicit
277+
// set of all values like `y` or `n` used to be. Therefore, if this flag had previously been
278+
// set in bulk with its historical values, then manually setting a component clears that
279+
// `explicitly_set` state.
280+
if let Some(component_to_enable) = component.strip_prefix("+") {
281+
self.explicitly_set = None;
282+
self.components.insert(component_to_enable.parse()?);
283+
Ok(())
284+
} else if let Some(component_to_disable) = component.strip_prefix("-") {
285+
self.explicitly_set = None;
286+
self.components.remove(component_to_disable.parse()?);
287+
Ok(())
288+
} else {
289+
Err(())
290+
}
291+
}
292+
293+
/// Turns all components on or off and records that this was done explicitly for compatibility
294+
/// purposes.
295+
pub(crate) fn set_all_explicitly(&mut self, enabled: bool) {
296+
self.explicitly_set = Some(enabled);
297+
self.components = if enabled {
298+
LinkSelfContainedComponents::all()
299+
} else {
300+
LinkSelfContainedComponents::empty()
301+
};
302+
}
303+
304+
/// Helper creating a fully enabled `LinkSelfContained` instance. Used in tests.
305+
pub fn on() -> Self {
306+
let mut on = LinkSelfContained::default();
307+
on.set_all_explicitly(true);
308+
on
309+
}
310+
311+
/// To help checking CLI usage while some of the values are unstable: returns whether one of the
312+
/// components was set individually. This would also require the `-Zunstable-options` flag, to
313+
/// be allowed.
314+
fn are_unstable_variants_set(&self) -> bool {
315+
let any_component_set = !self.components.is_empty();
316+
self.explicitly_set.is_none() && any_component_set
317+
}
318+
319+
/// Returns whether the self-contained linker component is enabled.
320+
pub fn linker(&self) -> bool {
321+
self.components.contains(LinkSelfContainedComponents::LINKER)
322+
}
323+
}
324+
204325
/// Used with `-Z assert-incr-state`.
205326
#[derive(Clone, Copy, PartialEq, Hash, Debug)]
206327
pub enum IncrementalStateAssertion {
@@ -213,15 +334,6 @@ pub enum IncrementalStateAssertion {
213334
NotLoaded,
214335
}
215336

216-
impl LinkerPluginLto {
217-
pub fn enabled(&self) -> bool {
218-
match *self {
219-
LinkerPluginLto::LinkerPlugin(_) | LinkerPluginLto::LinkerPluginAuto => true,
220-
LinkerPluginLto::Disabled => false,
221-
}
222-
}
223-
}
224-
225337
/// The different settings that can be enabled via the `-Z location-detail` flag.
226338
#[derive(Copy, Clone, PartialEq, Hash, Debug)]
227339
pub struct LocationDetail {
@@ -2544,16 +2656,28 @@ pub fn build_session_options(
25442656
}
25452657
}
25462658

2547-
if let Some(flavor) = cg.linker_flavor {
2548-
if matches!(flavor, LinkerFlavorCli::BpfLinker | LinkerFlavorCli::PtxLinker)
2549-
&& !nightly_options::is_unstable_enabled(matches)
2550-
{
2551-
let msg = format!(
2552-
"linker flavor `{}` is unstable, `-Z unstable-options` \
2553-
flag must also be passed to explicitly use it",
2554-
flavor.desc()
2659+
// For testing purposes, until we have more feedback about these options: ensure `-Z
2660+
// unstable-options` is required when using the unstable `-C link-self-contained` options, like
2661+
// `-C link-self-contained=+linker`, and when using the unstable `-C linker-flavor` options, like
2662+
// `-C linker-flavor=gnu-lld-cc`.
2663+
if !nightly_options::is_unstable_enabled(matches) {
2664+
let uses_unstable_self_contained_option =
2665+
cg.link_self_contained.are_unstable_variants_set();
2666+
if uses_unstable_self_contained_option {
2667+
handler.early_error(
2668+
"only `-C link-self-contained` values `y`/`yes`/`on`/`n`/`no`/`off` are stable, \
2669+
the `-Z unstable-options` flag must also be passed to use the unstable values",
25552670
);
2556-
handler.early_error(msg);
2671+
}
2672+
2673+
if let Some(flavor) = cg.linker_flavor {
2674+
if flavor.is_unstable() {
2675+
handler.early_error(format!(
2676+
"the linker flavor `{}` is unstable, the `-Z unstable-options` \
2677+
flag must also be passed to use the unstable values",
2678+
flavor.desc()
2679+
));
2680+
}
25572681
}
25582682
}
25592683

0 commit comments

Comments
 (0)