Skip to content

Commit 9af3865

Browse files
committed
Auto merge of #110807 - petrochenkov:strictflavor, r=lqd,wesleywiser
linker: Report linker flavors incompatible with the current target The linker flavor is checked for target compatibility even if linker is never used (e.g. we are producing a rlib). If it causes trouble, we can move the check to `link.rs` so it will run if the linker (flavor) is actually used. And also feature gate explicitly specifying linker flavors for tier 3 targets. The next step is supporting all the internal linker flavors in user-visible interfaces (command line and json).
2 parents 871b595 + 2f7328d commit 9af3865

File tree

13 files changed

+156
-60
lines changed

13 files changed

+156
-60
lines changed

compiler/rustc_codegen_ssa/src/back/link.rs

+3-40
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ use rustc_session::utils::NativeLibKind;
2323
use rustc_session::{filesearch, Session};
2424
use rustc_span::symbol::Symbol;
2525
use rustc_target::spec::crt_objects::{CrtObjects, LinkSelfContainedDefault};
26-
use rustc_target::spec::{Cc, LinkOutputKind, LinkerFlavor, LinkerFlavorCli, Lld, PanicStrategy};
26+
use rustc_target::spec::{Cc, LinkOutputKind, LinkerFlavor, Lld, PanicStrategy};
2727
use rustc_target::spec::{RelocModel, RelroLevel, SanitizerSet, SplitDebuginfo};
2828

2929
use super::archive::{ArchiveBuilder, ArchiveBuilderBuilder};
@@ -1302,44 +1302,7 @@ pub fn linker_and_flavor(sess: &Session) -> (PathBuf, LinkerFlavor) {
13021302
let stem = linker.file_stem().and_then(|stem| stem.to_str()).unwrap_or_else(|| {
13031303
sess.emit_fatal(errors::LinkerFileStem);
13041304
});
1305-
1306-
// Remove any version postfix.
1307-
let stem = stem
1308-
.rsplit_once('-')
1309-
.and_then(|(lhs, rhs)| rhs.chars().all(char::is_numeric).then_some(lhs))
1310-
.unwrap_or(stem);
1311-
1312-
// GCC/Clang can have an optional target prefix.
1313-
let flavor = if stem == "emcc" {
1314-
LinkerFlavor::EmCc
1315-
} else if stem == "gcc"
1316-
|| stem.ends_with("-gcc")
1317-
|| stem == "g++"
1318-
|| stem.ends_with("-g++")
1319-
|| stem == "clang"
1320-
|| stem.ends_with("-clang")
1321-
|| stem == "clang++"
1322-
|| stem.ends_with("-clang++")
1323-
{
1324-
LinkerFlavor::from_cli(LinkerFlavorCli::Gcc, &sess.target)
1325-
} else if stem == "wasm-ld" || stem.ends_with("-wasm-ld") {
1326-
LinkerFlavor::WasmLld(Cc::No)
1327-
} else if stem == "ld" || stem.ends_with("-ld") {
1328-
LinkerFlavor::from_cli(LinkerFlavorCli::Ld, &sess.target)
1329-
} else if stem == "ld.lld" {
1330-
LinkerFlavor::Gnu(Cc::No, Lld::Yes)
1331-
} else if stem == "link" {
1332-
LinkerFlavor::Msvc(Lld::No)
1333-
} else if stem == "lld-link" {
1334-
LinkerFlavor::Msvc(Lld::Yes)
1335-
} else if stem == "lld" || stem == "rust-lld" {
1336-
let lld_flavor = sess.target.linker_flavor.lld_flavor();
1337-
LinkerFlavor::from_cli(LinkerFlavorCli::Lld(lld_flavor), &sess.target)
1338-
} else {
1339-
// fall back to the value in the target spec
1340-
sess.target.linker_flavor
1341-
};
1342-
1305+
let flavor = sess.target.linker_flavor.with_linker_hints(stem);
13431306
Some((linker, flavor))
13441307
}
13451308
(None, None) => None,
@@ -1349,7 +1312,7 @@ pub fn linker_and_flavor(sess: &Session) -> (PathBuf, LinkerFlavor) {
13491312
// linker and linker flavor specified via command line have precedence over what the target
13501313
// specification specifies
13511314
let linker_flavor =
1352-
sess.opts.cg.linker_flavor.map(|flavor| LinkerFlavor::from_cli(flavor, &sess.target));
1315+
sess.opts.cg.linker_flavor.map(|flavor| sess.target.linker_flavor.with_cli_hints(flavor));
13531316
if let Some(ret) = infer_from(sess, sess.opts.cg.linker.clone(), linker_flavor) {
13541317
return ret;
13551318
}

compiler/rustc_session/messages.ftl

+4
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ session_feature_gate_error = {$explain}
2727
session_file_is_not_writeable = output file {$file} is not writeable -- check its permissions
2828
2929
session_hexadecimal_float_literal_not_supported = hexadecimal float literal is not supported
30+
31+
session_incompatible_linker_flavor = linker flavor `{$flavor}` is incompatible with the current target
32+
.note = compatible flavors are: {$compatible_list}
33+
3034
session_incorrect_cgu_reuse_type =
3135
CGU-reuse for `{$cgu_user_name}` is `{$actual_reuse}` but should be {$at_least ->
3236
[one] {"at least "}

compiler/rustc_session/src/config.rs

+14-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet};
1212

1313
use rustc_data_structures::stable_hasher::{StableOrd, ToStableHashKey};
1414
use rustc_target::abi::Align;
15-
use rustc_target::spec::{PanicStrategy, SanitizerSet, SplitDebuginfo};
15+
use rustc_target::spec::{LinkerFlavorCli, PanicStrategy, SanitizerSet, SplitDebuginfo};
1616
use rustc_target::spec::{Target, TargetTriple, TargetWarnings, TARGETS};
1717

1818
use crate::parse::{CrateCheckConfig, CrateConfig};
@@ -2525,6 +2525,19 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
25252525
}
25262526
}
25272527

2528+
if let Some(flavor) = cg.linker_flavor {
2529+
if matches!(flavor, LinkerFlavorCli::BpfLinker | LinkerFlavorCli::PtxLinker)
2530+
&& !nightly_options::is_unstable_enabled(matches)
2531+
{
2532+
let msg = format!(
2533+
"linker flavor `{}` is unstable, `-Z unstable-options` \
2534+
flag must also be passed to explicitly use it",
2535+
flavor.desc()
2536+
);
2537+
early_error(error_format, msg);
2538+
}
2539+
}
2540+
25282541
let prints = collect_print_requests(&mut cg, &mut unstable_opts, matches, error_format);
25292542

25302543
let cg = cg;

compiler/rustc_session/src/errors.rs

+8
Original file line numberDiff line numberDiff line change
@@ -422,3 +422,11 @@ pub fn report_lit_error(sess: &ParseSess, err: LitError, lit: token::Lit, span:
422422
pub struct OptimisationFuelExhausted {
423423
pub msg: String,
424424
}
425+
426+
#[derive(Diagnostic)]
427+
#[diag(session_incompatible_linker_flavor)]
428+
#[note]
429+
pub struct IncompatibleLinkerFlavor {
430+
pub flavor: &'static str,
431+
pub compatible_list: String,
432+
}

compiler/rustc_session/src/session.rs

+7
Original file line numberDiff line numberDiff line change
@@ -1675,6 +1675,13 @@ fn validate_commandline_args_with_session_available(sess: &Session) {
16751675
if sess.opts.unstable_opts.instrument_xray.is_some() && !sess.target.options.supports_xray {
16761676
sess.emit_err(errors::InstrumentationNotSupported { us: "XRay".to_string() });
16771677
}
1678+
1679+
if let Some(flavor) = sess.opts.cg.linker_flavor {
1680+
if let Some(compatible_list) = sess.target.linker_flavor.check_compatibility(flavor) {
1681+
let flavor = flavor.desc();
1682+
sess.emit_err(errors::IncompatibleLinkerFlavor { flavor, compatible_list });
1683+
}
1684+
}
16781685
}
16791686

16801687
/// Holds data on the current incremental compilation session, if there is one.

compiler/rustc_target/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#![feature(assert_matches)]
1212
#![feature(associated_type_bounds)]
1313
#![feature(exhaustive_patterns)]
14+
#![feature(iter_intersperse)]
1415
#![feature(min_specialization)]
1516
#![feature(never_type)]
1617
#![feature(rustc_attrs)]

compiler/rustc_target/src/spec/mod.rs

+92-18
Original file line numberDiff line numberDiff line change
@@ -205,15 +205,11 @@ impl ToJson for LldFlavor {
205205
}
206206

207207
impl LinkerFlavor {
208-
pub fn from_cli(cli: LinkerFlavorCli, target: &TargetOptions) -> LinkerFlavor {
209-
Self::from_cli_impl(cli, target.linker_flavor.lld_flavor(), target.linker_flavor.is_gnu())
210-
}
211-
212-
/// The passed CLI flavor is preferred over other args coming from the default target spec,
213-
/// so this function can produce a flavor that is incompatible with the current target.
214-
/// FIXME: Produce errors when `-Clinker-flavor` is set to something incompatible
215-
/// with the current target.
216-
fn from_cli_impl(cli: LinkerFlavorCli, lld_flavor: LldFlavor, is_gnu: bool) -> LinkerFlavor {
208+
/// At this point the target's reference linker flavor doesn't yet exist and we need to infer
209+
/// it. The inference always succeds and gives some result, and we don't report any flavor
210+
/// incompatibility errors for json target specs. The CLI flavor is used as the main source
211+
/// of truth, other flags are used in case of ambiguities.
212+
fn from_cli_json(cli: LinkerFlavorCli, lld_flavor: LldFlavor, is_gnu: bool) -> LinkerFlavor {
217213
match cli {
218214
LinkerFlavorCli::Gcc => match lld_flavor {
219215
LldFlavor::Ld if is_gnu => LinkerFlavor::Gnu(Cc::Yes, Lld::No),
@@ -257,6 +253,85 @@ impl LinkerFlavor {
257253
}
258254
}
259255

256+
fn infer_cli_hints(cli: LinkerFlavorCli) -> (Option<Cc>, Option<Lld>) {
257+
match cli {
258+
LinkerFlavorCli::Gcc | LinkerFlavorCli::Em => (Some(Cc::Yes), None),
259+
LinkerFlavorCli::Lld(_) => (Some(Cc::No), Some(Lld::Yes)),
260+
LinkerFlavorCli::Ld | LinkerFlavorCli::Msvc => (Some(Cc::No), Some(Lld::No)),
261+
LinkerFlavorCli::BpfLinker | LinkerFlavorCli::PtxLinker => (None, None),
262+
}
263+
}
264+
265+
fn infer_linker_hints(linker_stem: &str) -> (Option<Cc>, Option<Lld>) {
266+
// Remove any version postfix.
267+
let stem = linker_stem
268+
.rsplit_once('-')
269+
.and_then(|(lhs, rhs)| rhs.chars().all(char::is_numeric).then_some(lhs))
270+
.unwrap_or(linker_stem);
271+
272+
// GCC/Clang can have an optional target prefix.
273+
if stem == "emcc"
274+
|| stem == "gcc"
275+
|| stem.ends_with("-gcc")
276+
|| stem == "g++"
277+
|| stem.ends_with("-g++")
278+
|| stem == "clang"
279+
|| stem.ends_with("-clang")
280+
|| stem == "clang++"
281+
|| stem.ends_with("-clang++")
282+
{
283+
(Some(Cc::Yes), None)
284+
} else if stem == "wasm-ld"
285+
|| stem.ends_with("-wasm-ld")
286+
|| stem == "ld.lld"
287+
|| stem == "lld"
288+
|| stem == "rust-lld"
289+
|| stem == "lld-link"
290+
{
291+
(Some(Cc::No), Some(Lld::Yes))
292+
} else if stem == "ld" || stem.ends_with("-ld") || stem == "link" {
293+
(Some(Cc::No), Some(Lld::No))
294+
} else {
295+
(None, None)
296+
}
297+
}
298+
299+
fn with_hints(self, (cc_hint, lld_hint): (Option<Cc>, Option<Lld>)) -> LinkerFlavor {
300+
match self {
301+
LinkerFlavor::Gnu(cc, lld) => {
302+
LinkerFlavor::Gnu(cc_hint.unwrap_or(cc), lld_hint.unwrap_or(lld))
303+
}
304+
LinkerFlavor::Darwin(cc, lld) => {
305+
LinkerFlavor::Darwin(cc_hint.unwrap_or(cc), lld_hint.unwrap_or(lld))
306+
}
307+
LinkerFlavor::WasmLld(cc) => LinkerFlavor::WasmLld(cc_hint.unwrap_or(cc)),
308+
LinkerFlavor::Unix(cc) => LinkerFlavor::Unix(cc_hint.unwrap_or(cc)),
309+
LinkerFlavor::Msvc(lld) => LinkerFlavor::Msvc(lld_hint.unwrap_or(lld)),
310+
LinkerFlavor::EmCc | LinkerFlavor::Bpf | LinkerFlavor::Ptx => self,
311+
}
312+
}
313+
314+
pub fn with_cli_hints(self, cli: LinkerFlavorCli) -> LinkerFlavor {
315+
self.with_hints(LinkerFlavor::infer_cli_hints(cli))
316+
}
317+
318+
pub fn with_linker_hints(self, linker_stem: &str) -> LinkerFlavor {
319+
self.with_hints(LinkerFlavor::infer_linker_hints(linker_stem))
320+
}
321+
322+
pub fn check_compatibility(self, cli: LinkerFlavorCli) -> Option<String> {
323+
// The CLI flavor should be compatible with the target if it survives this roundtrip.
324+
let compatible = |cli| cli == self.with_cli_hints(cli).to_cli();
325+
(!compatible(cli)).then(|| {
326+
LinkerFlavorCli::all()
327+
.iter()
328+
.filter(|cli| compatible(**cli))
329+
.map(|cli| cli.desc())
330+
.intersperse(", ")
331+
.collect()
332+
})
333+
}
334+
260335
pub fn lld_flavor(self) -> LldFlavor {
261336
match self {
262337
LinkerFlavor::Gnu(..)
@@ -278,6 +353,10 @@ impl LinkerFlavor {
278353
macro_rules! linker_flavor_cli_impls {
279354
($(($($flavor:tt)*) $string:literal)*) => (
280355
impl LinkerFlavorCli {
356+
const fn all() -> &'static [LinkerFlavorCli] {
357+
&[$($($flavor)*,)*]
358+
}
359+
281360
pub const fn one_of() -> &'static str {
282361
concat!("one of: ", $($string, " ",)*)
283362
}
@@ -289,8 +368,8 @@ macro_rules! linker_flavor_cli_impls {
289368
})
290369
}
291370

292-
pub fn desc(&self) -> &str {
293-
match *self {
371+
pub fn desc(self) -> &'static str {
372+
match self {
294373
$($($flavor)* => $string,)*
295374
}
296375
}
@@ -1801,7 +1880,7 @@ impl TargetOptions {
18011880
}
18021881

18031882
fn update_from_cli(&mut self) {
1804-
self.linker_flavor = LinkerFlavor::from_cli_impl(
1883+
self.linker_flavor = LinkerFlavor::from_cli_json(
18051884
self.linker_flavor_json,
18061885
self.lld_flavor_json,
18071886
self.linker_is_gnu_json,
@@ -1815,12 +1894,7 @@ impl TargetOptions {
18151894
] {
18161895
args.clear();
18171896
for (flavor, args_json) in args_json {
1818-
// Cannot use `from_cli` due to borrow checker.
1819-
let linker_flavor = LinkerFlavor::from_cli_impl(
1820-
*flavor,
1821-
self.lld_flavor_json,
1822-
self.linker_is_gnu_json,
1823-
);
1897+
let linker_flavor = self.linker_flavor.with_cli_hints(*flavor);
18241898
// Normalize to no lld to avoid asserts.
18251899
let linker_flavor = match linker_flavor {
18261900
LinkerFlavor::Gnu(cc, _) => LinkerFlavor::Gnu(cc, Lld::No),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// compile-flags: --target=x86_64-unknown-linux-gnu -C linker-flavor=msvc --crate-type=rlib
2+
// error-pattern: linker flavor `msvc` is incompatible with the current target
3+
// needs-llvm-components:
4+
5+
#![feature(no_core)]
6+
#![no_core]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
error: linker flavor `msvc` is incompatible with the current target
2+
|
3+
= note: compatible flavors are: gcc, ld, ld.lld
4+
5+
error: aborting due to previous error
6+

tests/ui/linkage-attr/issue-10755.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// build-fail
22
// dont-check-compiler-stderr
3-
// compile-flags: -C linker=llllll -C linker-flavor=ld
3+
// compile-flags: -C linker=llllll
44
// error-pattern: `llllll`
55

66
// Before, the error-pattern checked for "not found". On WSL with appendWindowsPath=true, running
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
error: linker flavor `bpf-linker` is unstable, `-Z unstable-options` flag must also be passed to explicitly use it
2+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
error: linker flavor `ptx-linker` is unstable, `-Z unstable-options` flag must also be passed to explicitly use it
2+
+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// revisions: bpf ptx
2+
// [bpf] compile-flags: --target=bpfel-unknown-none -C linker-flavor=bpf-linker --crate-type=rlib
3+
// [bpf] error-pattern: linker flavor `bpf-linker` is unstable, `-Z unstable-options` flag
4+
// [bpf] needs-llvm-components:
5+
// [ptx] compile-flags: --target=nvptx64-nvidia-cuda -C linker-flavor=ptx-linker --crate-type=rlib
6+
// [ptx] error-pattern: linker flavor `ptx-linker` is unstable, `-Z unstable-options` flag
7+
// [ptx] needs-llvm-components:
8+
9+
#![feature(no_core)]
10+
#![no_core]

0 commit comments

Comments
 (0)