Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 97a8240

Browse files
committedNov 28, 2024·
Target modifiers (special marked options) are recorded in metainfo and compared to be equal in different crates
1 parent f005c74 commit 97a8240

File tree

18 files changed

+478
-21
lines changed

18 files changed

+478
-21
lines changed
 

‎compiler/rustc_driver_impl/src/lib.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1170,11 +1170,12 @@ fn describe_codegen_flags() {
11701170

11711171
fn print_flag_list<T>(
11721172
cmdline_opt: &str,
1173-
flag_list: &[(&'static str, T, &'static str, &'static str)],
1173+
flag_list: &[(&'static str, T, &'static str, &'static str, bool)],
11741174
) {
1175-
let max_len = flag_list.iter().map(|&(name, _, _, _)| name.chars().count()).max().unwrap_or(0);
1175+
let max_len =
1176+
flag_list.iter().map(|&(name, _, _, _, _)| name.chars().count()).max().unwrap_or(0);
11761177

1177-
for &(name, _, _, desc) in flag_list {
1178+
for &(name, _, _, desc, _) in flag_list {
11781179
safe_println!(
11791180
" {} {:>width$}=val -- {}",
11801181
cmdline_opt,

‎compiler/rustc_interface/src/passes.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,7 @@ fn configure_and_expand(
270270

271271
resolver.resolve_crate(&krate);
272272

273+
CStore::from_tcx(tcx).report_incompatible_target_modifiers(tcx, &krate, resolver.lint_buffer());
273274
krate
274275
}
275276

‎compiler/rustc_lint/messages.ftl

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,13 @@ lint_improper_ctypes_union_layout_help = consider adding a `#[repr(C)]` or `#[re
414414
lint_improper_ctypes_union_layout_reason = this union has unspecified layout
415415
lint_improper_ctypes_union_non_exhaustive = this union is non-exhaustive
416416

417+
lint_incompatible_target_modifiers =
418+
mixing `{$flag_name_prefixed}` will cause an ABI mismatch
419+
.note1 = `{$flag_name_prefixed}={$flag_local_value}` in this crate is incompatible with `{$flag_name_prefixed}={$flag_extern_value}` in dependency `{$extern_crate}`
420+
.help = `{$flag_name_prefixed}` modifies the ABI and Rust crates compiled with different values of this flag cannot be used together safely
421+
.suggestion = set `{$flag_name_prefixed}=${flag_extern_value}` in this crate or `{$flag_name_prefixed}=${flag_local_value}` in `{$extern_crate}`
422+
.note2 = alternatively, use `-Cunsafe-allow-abi-mismatch={$flag_name}` to silence this error
423+
417424
lint_incomplete_include =
418425
include macro expected single expression in source
419426

‎compiler/rustc_lint/src/context/diagnostics.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,22 @@ pub(super) fn decorate_lint(sess: &Session, diagnostic: BuiltinLintDiag, diag: &
422422
lints::UnusedCrateDependency { extern_crate, local_crate }.decorate_lint(diag)
423423
}
424424
BuiltinLintDiag::WasmCAbi => lints::WasmCAbi.decorate_lint(diag),
425+
BuiltinLintDiag::IncompatibleTargetModifiers {
426+
extern_crate,
427+
local_crate,
428+
flag_name,
429+
flag_name_prefixed,
430+
flag_local_value,
431+
flag_extern_value,
432+
} => lints::IncompatibleTargetModifiers {
433+
extern_crate,
434+
local_crate,
435+
flag_name,
436+
flag_name_prefixed,
437+
flag_local_value,
438+
flag_extern_value,
439+
}
440+
.decorate_lint(diag),
425441
BuiltinLintDiag::IllFormedAttributeInput { suggestions } => {
426442
lints::IllFormedAttributeInput {
427443
num_suggestions: suggestions.len(),

‎compiler/rustc_lint/src/lints.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2474,6 +2474,20 @@ pub(crate) struct UnusedCrateDependency {
24742474
pub local_crate: Symbol,
24752475
}
24762476

2477+
#[derive(LintDiagnostic)]
2478+
#[diag(lint_incompatible_target_modifiers)]
2479+
#[help]
2480+
#[note(lint_note1)]
2481+
#[note(lint_note2)]
2482+
pub(crate) struct IncompatibleTargetModifiers {
2483+
pub extern_crate: Symbol,
2484+
pub local_crate: Symbol,
2485+
pub flag_name: String,
2486+
pub flag_name_prefixed: String,
2487+
pub flag_local_value: String,
2488+
pub flag_extern_value: String,
2489+
}
2490+
24772491
#[derive(LintDiagnostic)]
24782492
#[diag(lint_wasm_c_abi)]
24792493
pub(crate) struct WasmCAbi;

‎compiler/rustc_lint_defs/src/builtin.rs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ declare_lint_pass! {
5050
FUZZY_PROVENANCE_CASTS,
5151
HIDDEN_GLOB_REEXPORTS,
5252
ILL_FORMED_ATTRIBUTE_INPUT,
53+
INCOMPATIBLE_TARGET_MODIFIERS,
5354
INCOMPLETE_INCLUDE,
5455
INEFFECTIVE_UNSTABLE_TRAIT_IMPL,
5556
INLINE_NO_SANITIZE,
@@ -582,6 +583,46 @@ declare_lint! {
582583
crate_level_only
583584
}
584585

586+
declare_lint! {
587+
/// The `incompatible_target_modifiers` lint detects crates with incompatible target modifiers
588+
/// (abi-changing or vulnerability-affecting flags).
589+
///
590+
/// ### Example
591+
///
592+
/// ```rust,ignore (needs extern crate)
593+
/// #![deny(incompatible_target_modifiers)]
594+
/// ```
595+
///
596+
/// When main and dependency crates are compiled with `-Zregparm=1` and `-Zregparm=2` correspondingly.
597+
///
598+
/// This will produce:
599+
///
600+
/// ```text
601+
/// error: mixing `-Zregparm` will cause an ABI mismatch
602+
/// --> $DIR/incompatible_regparm.rs:12:1
603+
/// |
604+
/// LL | #![crate_type = "lib"]
605+
/// | ^
606+
/// |
607+
/// = help: `-Zregparm` modifies the ABI and Rust crates compiled with different values of this flag cannot be used together safely
608+
/// = note: `-Zregparm=1` in this crate is incompatible with `-Zregparm=2` in dependency `wrong_regparm`
609+
/// = note: alternatively, use `-Cunsafe-allow-abi-mismatch=regparm` to silence this error
610+
/// = note: `#[deny(incompatible_target_modifiers)]` on by default
611+
/// ```
612+
///
613+
/// ### Explanation
614+
///
615+
/// `Target modifiers` are compilation flags that affects abi or vulnerability resistance.
616+
/// Linking together crates with incompatible target modifiers would produce incorrect code
617+
/// or degradation of vulnerability resistance.
618+
/// So this lint should find such inconsistency.
619+
///
620+
pub INCOMPATIBLE_TARGET_MODIFIERS,
621+
Deny,
622+
"Incompatible target modifiers",
623+
crate_level_only
624+
}
625+
585626
declare_lint! {
586627
/// The `unused_qualifications` lint detects unnecessarily qualified
587628
/// names.

‎compiler/rustc_lint_defs/src/lib.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -768,6 +768,14 @@ pub enum BuiltinLintDiag {
768768
AvoidUsingIntelSyntax,
769769
AvoidUsingAttSyntax,
770770
IncompleteInclude,
771+
IncompatibleTargetModifiers {
772+
extern_crate: Symbol,
773+
local_crate: Symbol,
774+
flag_name: String,
775+
flag_name_prefixed: String,
776+
flag_local_value: String,
777+
flag_extern_value: String,
778+
},
771779
UnnameableTestItems,
772780
DuplicateMacroAttribute,
773781
CfgAttrNoAttributes,

‎compiler/rustc_metadata/src/creader.rs

Lines changed: 118 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@ use rustc_hir::definitions::Definitions;
2222
use rustc_index::IndexVec;
2323
use rustc_middle::bug;
2424
use rustc_middle::ty::{TyCtxt, TyCtxtFeed};
25-
use rustc_session::config::{self, CrateType, ExternLocation};
25+
use rustc_session::config::{self, CrateType, ExternLocation, TargetModifier};
2626
use rustc_session::cstore::{CrateDepKind, CrateSource, ExternCrate, ExternCrateSource};
27-
use rustc_session::lint::{self, BuiltinLintDiag};
27+
use rustc_session::lint::{self, BuiltinLintDiag, LintBuffer};
2828
use rustc_session::output::validate_crate_name;
2929
use rustc_session::search_paths::PathKind;
3030
use rustc_span::edition::Edition;
@@ -35,7 +35,9 @@ use tracing::{debug, info, trace};
3535

3636
use crate::errors;
3737
use crate::locator::{CrateError, CrateLocator, CratePaths};
38-
use crate::rmeta::{CrateDep, CrateMetadata, CrateNumMap, CrateRoot, MetadataBlob};
38+
use crate::rmeta::{
39+
CrateDep, CrateMetadata, CrateNumMap, CrateRoot, MetadataBlob, TargetModifiers,
40+
};
3941

4042
/// The backend's way to give the crate store access to the metadata in a library.
4143
/// Note that it returns the raw metadata bytes stored in the library file, whether
@@ -290,6 +292,98 @@ impl CStore {
290292
}
291293
}
292294

295+
pub fn report_incompatible_target_modifiers(
296+
&self,
297+
tcx: TyCtxt<'_>,
298+
krate: &Crate,
299+
lints: &mut LintBuffer,
300+
) {
301+
if tcx.crate_types().contains(&CrateType::ProcMacro) {
302+
return;
303+
}
304+
let sess = tcx.sess;
305+
let empty_vec = Vec::<String>::new();
306+
let allowed_flag_mismatches = match sess.opts.cg.unsafe_allow_abi_mismatch.as_ref() {
307+
Some(vec) => {
308+
if vec.is_empty() {
309+
// Setting `-Zunsafe-allow-abi-mismatch=` to an empty
310+
// value allows all target modifier mismatches.
311+
return;
312+
}
313+
vec
314+
}
315+
None => &empty_vec,
316+
};
317+
let span = krate.spans.inner_span.shrink_to_lo();
318+
319+
let name = tcx.crate_name(LOCAL_CRATE);
320+
let mods = sess.opts.gather_target_modifiers();
321+
for (_cnum, data) in self.iter_crate_data() {
322+
if data.is_proc_macro_crate() {
323+
continue;
324+
}
325+
let mut report_diff = |opt_name_hash: u64,
326+
flag_local_value: &String,
327+
flag_extern_value: &String| {
328+
let name_info = sess.opts.target_modifier_info_by_hash(opt_name_hash);
329+
let (prefix, opt_name) = name_info.expect("Target modifier not found by name hash");
330+
if allowed_flag_mismatches.contains(&opt_name) {
331+
return;
332+
}
333+
lints.buffer_lint(
334+
lint::builtin::INCOMPATIBLE_TARGET_MODIFIERS,
335+
ast::CRATE_NODE_ID,
336+
span,
337+
BuiltinLintDiag::IncompatibleTargetModifiers {
338+
extern_crate: data.name(),
339+
local_crate: name,
340+
flag_name: opt_name.clone(),
341+
flag_name_prefixed: format!("-{}{}", prefix, opt_name),
342+
flag_local_value: flag_local_value.to_string(),
343+
flag_extern_value: flag_extern_value.to_string(),
344+
},
345+
);
346+
};
347+
let mut it1 = mods.iter();
348+
let mut it2 = data.target_modifiers();
349+
let mut left_name_val: Option<TargetModifier> = None;
350+
let mut right_name_val: Option<TargetModifier> = None;
351+
let no_val = "*".to_string();
352+
loop {
353+
left_name_val = left_name_val.or_else(|| it1.next().cloned());
354+
right_name_val = right_name_val.or_else(|| it2.next().cloned());
355+
match (&left_name_val, &right_name_val) {
356+
(Some(l), Some(r)) => match l.name_hash.cmp(&r.name_hash) {
357+
cmp::Ordering::Equal => {
358+
if l.value_code != r.value_code {
359+
report_diff(l.name_hash, &l.value_name, &r.value_name);
360+
}
361+
left_name_val = None;
362+
right_name_val = None;
363+
}
364+
cmp::Ordering::Greater => {
365+
report_diff(r.name_hash, &no_val, &r.value_name);
366+
right_name_val = None;
367+
}
368+
cmp::Ordering::Less => {
369+
report_diff(l.name_hash, &l.value_name, &no_val);
370+
left_name_val = None;
371+
}
372+
},
373+
(Some(l), None) => {
374+
report_diff(l.name_hash, &l.value_name, &no_val);
375+
left_name_val = None;
376+
}
377+
(None, Some(r)) => {
378+
report_diff(r.name_hash, &no_val, &r.value_name);
379+
right_name_val = None;
380+
}
381+
(None, None) => break,
382+
}
383+
}
384+
}
385+
}
386+
293387
pub fn new(metadata_loader: Box<MetadataLoaderDyn>) -> CStore {
294388
CStore {
295389
metadata_loader,
@@ -432,6 +526,7 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {
432526
};
433527

434528
let cnum_map = self.resolve_crate_deps(root, &crate_root, &metadata, cnum, dep_kind)?;
529+
let target_modifiers = self.resolve_target_modifiers(&crate_root, &metadata, cnum)?;
435530

436531
let raw_proc_macros = if crate_root.is_proc_macro_crate() {
437532
let temp_root;
@@ -456,6 +551,7 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {
456551
raw_proc_macros,
457552
cnum,
458553
cnum_map,
554+
target_modifiers,
459555
dep_kind,
460556
source,
461557
private_dep,
@@ -694,6 +790,25 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {
694790
Ok(crate_num_map)
695791
}
696792

793+
fn resolve_target_modifiers(
794+
&mut self,
795+
crate_root: &CrateRoot,
796+
metadata: &MetadataBlob,
797+
krate: CrateNum,
798+
) -> Result<TargetModifiers, CrateError> {
799+
debug!("resolving target modifiers of external crate");
800+
if crate_root.is_proc_macro_crate() {
801+
return Ok(TargetModifiers::new());
802+
}
803+
let mods = crate_root.decode_target_modifiers(metadata);
804+
let mut target_modifiers = TargetModifiers::with_capacity(mods.len());
805+
for modifier in mods {
806+
target_modifiers.push(modifier);
807+
}
808+
debug!("resolve_target_modifiers: target mods for {:?} is {:?}", krate, target_modifiers);
809+
Ok(target_modifiers)
810+
}
811+
697812
fn dlsym_proc_macros(
698813
&self,
699814
path: &Path,

‎compiler/rustc_metadata/src/rmeta/decoder.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ use rustc_middle::{bug, implement_ty_decoder};
2929
use rustc_serialize::opaque::MemDecoder;
3030
use rustc_serialize::{Decodable, Decoder};
3131
use rustc_session::Session;
32+
use rustc_session::config::TargetModifier;
3233
use rustc_session::cstore::{CrateSource, ExternCrate};
3334
use rustc_span::hygiene::HygieneDecodeContext;
3435
use rustc_span::symbol::kw;
@@ -74,6 +75,9 @@ impl MetadataBlob {
7475
/// own crate numbers.
7576
pub(crate) type CrateNumMap = IndexVec<CrateNum, CrateNum>;
7677

78+
/// Target modifiers - abi / vulnerability-resist affecting flags
79+
pub(crate) type TargetModifiers = Vec<TargetModifier>;
80+
7781
pub(crate) struct CrateMetadata {
7882
/// The primary crate data - binary metadata blob.
7983
blob: MetadataBlob,
@@ -111,6 +115,8 @@ pub(crate) struct CrateMetadata {
111115
cnum_map: CrateNumMap,
112116
/// Same ID set as `cnum_map` plus maybe some injected crates like panic runtime.
113117
dependencies: Vec<CrateNum>,
118+
/// Target modifiers - abi and vulnerability-resist affecting flags the crate was compiled with
119+
target_modifiers: TargetModifiers,
114120
/// How to link (or not link) this crate to the currently compiled crate.
115121
dep_kind: CrateDepKind,
116122
/// Filesystem location of this crate.
@@ -961,6 +967,13 @@ impl CrateRoot {
961967
) -> impl ExactSizeIterator<Item = CrateDep> + Captures<'a> {
962968
self.crate_deps.decode(metadata)
963969
}
970+
971+
pub(crate) fn decode_target_modifiers<'a>(
972+
&self,
973+
metadata: &'a MetadataBlob,
974+
) -> impl ExactSizeIterator<Item = TargetModifier> + Captures<'a> {
975+
self.target_modifiers.decode(metadata)
976+
}
964977
}
965978

966979
impl<'a> CrateMetadataRef<'a> {
@@ -1818,6 +1831,7 @@ impl CrateMetadata {
18181831
raw_proc_macros: Option<&'static [ProcMacro]>,
18191832
cnum: CrateNum,
18201833
cnum_map: CrateNumMap,
1834+
target_modifiers: TargetModifiers,
18211835
dep_kind: CrateDepKind,
18221836
source: CrateSource,
18231837
private_dep: bool,
@@ -1849,6 +1863,7 @@ impl CrateMetadata {
18491863
cnum,
18501864
cnum_map,
18511865
dependencies,
1866+
target_modifiers,
18521867
dep_kind,
18531868
source: Lrc::new(source),
18541869
private_dep,
@@ -1878,6 +1893,10 @@ impl CrateMetadata {
18781893
self.dependencies.push(cnum);
18791894
}
18801895

1896+
pub(crate) fn target_modifiers(&self) -> impl Iterator<Item = &TargetModifier> + '_ {
1897+
self.target_modifiers.iter()
1898+
}
1899+
18811900
pub(crate) fn update_extern_crate(&mut self, new_extern_crate: ExternCrate) -> bool {
18821901
let update =
18831902
Some(new_extern_crate.rank()) > self.extern_crate.as_ref().map(ExternCrate::rank);

‎compiler/rustc_metadata/src/rmeta/encoder.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use rustc_middle::ty::{AssocItemContainer, SymbolName};
2525
use rustc_middle::util::common::to_readable_str;
2626
use rustc_middle::{bug, span_bug};
2727
use rustc_serialize::{Decodable, Decoder, Encodable, Encoder, opaque};
28-
use rustc_session::config::{CrateType, OptLevel};
28+
use rustc_session::config::{CrateType, OptLevel, TargetModifier};
2929
use rustc_span::hygiene::HygieneEncodeContext;
3030
use rustc_span::symbol::sym;
3131
use rustc_span::{
@@ -692,6 +692,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
692692
// Encode source_map. This needs to be done last, because encoding `Span`s tells us which
693693
// `SourceFiles` we actually need to encode.
694694
let source_map = stat!("source-map", || self.encode_source_map());
695+
let target_modifiers = stat!("target-modifiers", || self.encode_target_modifiers());
695696

696697
let root = stat!("final", || {
697698
let attrs = tcx.hir().krate_attrs();
@@ -732,6 +733,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
732733
native_libraries,
733734
foreign_modules,
734735
source_map,
736+
target_modifiers,
735737
traits,
736738
impls,
737739
incoherent_impls,
@@ -1988,6 +1990,12 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
19881990
self.lazy_array(deps.iter().map(|(_, dep)| dep))
19891991
}
19901992

1993+
fn encode_target_modifiers(&mut self) -> LazyArray<TargetModifier> {
1994+
empty_proc_macro!(self);
1995+
let tcx = self.tcx;
1996+
self.lazy_array(tcx.sess.opts.gather_target_modifiers())
1997+
}
1998+
19911999
fn encode_lib_features(&mut self) -> LazyArray<(Symbol, FeatureStability)> {
19922000
empty_proc_macro!(self);
19932001
let tcx = self.tcx;

‎compiler/rustc_metadata/src/rmeta/mod.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use std::marker::PhantomData;
22
use std::num::NonZero;
33

4-
pub(crate) use decoder::{CrateMetadata, CrateNumMap, MetadataBlob};
4+
pub(crate) use decoder::{CrateMetadata, CrateNumMap, MetadataBlob, TargetModifiers};
55
use decoder::{DecodeContext, Metadata};
66
use def_path_hash_map::DefPathHashMapRef;
77
use encoder::EncodeContext;
@@ -32,7 +32,7 @@ use rustc_middle::ty::{
3232
use rustc_middle::util::Providers;
3333
use rustc_middle::{mir, trivially_parameterized_over_tcx};
3434
use rustc_serialize::opaque::FileEncoder;
35-
use rustc_session::config::SymbolManglingVersion;
35+
use rustc_session::config::{SymbolManglingVersion, TargetModifier};
3636
use rustc_session::cstore::{CrateDepKind, ForeignModule, LinkagePreference, NativeLib};
3737
use rustc_span::edition::Edition;
3838
use rustc_span::hygiene::{ExpnIndex, MacroKind, SyntaxContextData};
@@ -283,6 +283,7 @@ pub(crate) struct CrateRoot {
283283
def_path_hash_map: LazyValue<DefPathHashMapRef<'static>>,
284284

285285
source_map: LazyTable<u32, Option<LazyValue<rustc_span::SourceFile>>>,
286+
target_modifiers: LazyArray<TargetModifier>,
286287

287288
compiler_builtins: bool,
288289
needs_allocator: bool,

‎compiler/rustc_middle/src/ty/parameterized.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ trivially_parameterized_over_tcx! {
101101
rustc_session::cstore::ForeignModule,
102102
rustc_session::cstore::LinkagePreference,
103103
rustc_session::cstore::NativeLib,
104+
rustc_session::config::TargetModifier,
104105
rustc_span::ExpnData,
105106
rustc_span::ExpnHash,
106107
rustc_span::ExpnId,

‎compiler/rustc_session/src/config.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1190,6 +1190,7 @@ impl Default for Options {
11901190
color: ColorConfig::Auto,
11911191
logical_env: FxIndexMap::default(),
11921192
verbose: false,
1193+
target_modifiers: BTreeMap::default(),
11931194
}
11941195
}
11951196
}
@@ -2333,14 +2334,16 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M
23332334
let crate_types = parse_crate_types_from_list(unparsed_crate_types)
23342335
.unwrap_or_else(|e| early_dcx.early_fatal(e));
23352336

2336-
let mut unstable_opts = UnstableOptions::build(early_dcx, matches);
2337+
let mut target_modifiers = BTreeMap::<String, String>::new();
2338+
2339+
let mut unstable_opts = UnstableOptions::build(early_dcx, matches, &mut target_modifiers);
23372340
let (lint_opts, describe_lints, lint_cap) = get_cmd_lint_options(early_dcx, matches);
23382341

23392342
check_error_format_stability(early_dcx, &unstable_opts, error_format);
23402343

23412344
let output_types = parse_output_types(early_dcx, &unstable_opts, matches);
23422345

2343-
let mut cg = CodegenOptions::build(early_dcx, matches);
2346+
let mut cg = CodegenOptions::build(early_dcx, matches, &mut target_modifiers);
23442347
let (disable_local_thinlto, codegen_units) = should_override_cgus_and_disable_thinlto(
23452348
early_dcx,
23462349
&output_types,
@@ -2611,6 +2614,7 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M
26112614
color,
26122615
logical_env,
26132616
verbose,
2617+
target_modifiers,
26142618
}
26152619
}
26162620

‎compiler/rustc_session/src/options.rs

Lines changed: 180 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use rustc_data_structures::profiling::TimePassesFormat;
99
use rustc_data_structures::stable_hasher::Hash64;
1010
use rustc_errors::{ColorConfig, LanguageIdentifier, TerminalUrl};
1111
use rustc_feature::UnstableFeatures;
12+
use rustc_macros::{Decodable, Encodable};
1213
use rustc_span::edition::Edition;
1314
use rustc_span::{RealFileName, SourceFileHashAlgorithm};
1415
use rustc_target::spec::{
@@ -58,18 +59,118 @@ macro_rules! hash_substruct {
5859
};
5960
}
6061

62+
// A recorded -Zopt_name=opt_value (or -Copt_name=opt_value)
63+
// for abi-changing or vulnerability-affecting options
64+
#[derive(Debug, Clone, Encodable, Decodable)]
65+
pub struct TargetModifier {
66+
pub name_hash: u64, // option name hash
67+
pub value_name: String, // User-provided option value (before parsing) for display
68+
pub value_code: String, // Technical option value representation for comparison
69+
}
70+
71+
macro_rules! tmod_push {
72+
($opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr) => {
73+
$mods.push(TargetModifier {
74+
name_hash: calc_opt_name_hash(stringify!($opt_name)),
75+
value_name: $tmod_vals.get(stringify!($opt_name)).cloned().unwrap_or_default(),
76+
value_code: format!("{}={:?}", stringify!($opt_name), $opt_expr),
77+
});
78+
};
79+
}
80+
81+
macro_rules! gather_tmods {
82+
($_opt_name:ident, $init:expr, $opt_expr:expr, $mods:expr, $tmod_vals:expr, [SUBSTRUCT], [TMOD]) => {
83+
compile_error!("SUBSTRUCT can't be target modifier");
84+
};
85+
($opt_name:ident, $init:expr, $opt_expr:expr, $mods:expr, $tmod_vals:expr, [UNTRACKED], [TMOD]) => {
86+
tmod_push!($opt_name, $opt_expr, $mods, $tmod_vals)
87+
};
88+
($opt_name:ident, $init:expr, $opt_expr:expr, $mods:expr, $tmod_vals:expr, [TRACKED], [TMOD]) => {
89+
tmod_push!($opt_name, $opt_expr, $mods, $tmod_vals)
90+
};
91+
($opt_name:ident, $init:expr, $opt_expr:expr, $mods:expr, $tmod_vals:expr, [TRACKED_NO_CRATE_HASH], [TMOD]) => {
92+
tmod_push!($opt_name, $opt_expr, $mods, $tmod_vals)
93+
};
94+
($_opt_name:ident, $init:expr, $opt_expr:expr, $mods:expr, $tmod_vals:expr, [SUBSTRUCT], []) => {
95+
$opt_expr.gather_target_modifiers($mods, $tmod_vals);
96+
};
97+
($_opt_name:ident, $init:expr, $_opt_expr:expr, $_mods:expr, $tmod_vals:expr, [UNTRACKED], []) => {{}};
98+
($_opt_name:ident, $init:expr, $_opt_expr:expr, $_mods:expr, $tmod_vals:expr, [TRACKED], []) => {{}};
99+
($_opt_name:ident, $init:expr, $_opt_expr:expr, $_mods:expr, $tmod_vals:expr, [TRACKED_NO_CRATE_HASH], []) => {{}};
100+
}
101+
102+
fn calc_opt_name_hash(opt_name: &str) -> u64 {
103+
let mut hasher = DefaultHasher::new();
104+
use std::hash::Hash;
105+
opt_name.hash(&mut hasher);
106+
hasher.finish()
107+
}
108+
109+
macro_rules! tmod_info_ret {
110+
($prefix:expr, $opt_name:ident, $name_hash:expr) => {
111+
if $name_hash == calc_opt_name_hash(stringify!($opt_name)) {
112+
return Some(($prefix.to_string(), stringify!($opt_name).to_string().replace('_', "-")));
113+
}
114+
};
115+
}
116+
117+
macro_rules! tmod_info {
118+
($opt_expr:expr, $prefix:expr, $_opt_name:ident, $_name_hash:expr, [SUBSTRUCT], [TMOD]) => {
119+
compile_error!("SUBSTRUCT can't be target modifier");
120+
};
121+
($opt_expr:expr, $prefix:expr, $opt_name:ident, $name_hash:expr, [UNTRACKED], [TMOD]) => {
122+
tmod_info_ret!($prefix, $opt_name, $name_hash);
123+
};
124+
($opt_expr:expr, $prefix:expr, $opt_name:ident, $name_hash:expr, [TRACKED], [TMOD]) => {
125+
tmod_info_ret!($prefix, $opt_name, $name_hash);
126+
};
127+
($opt_expr:expr, $prefix:expr, $opt_name:ident, $name_hash:expr, [TRACKED_NO_CRATE_HASH], [TMOD]) => {
128+
tmod_info_ret!($prefix, $opt_name, $name_hash);
129+
};
130+
($opt_expr:expr, $prefix:expr, $_opt_name:ident, $name_hash:expr, [SUBSTRUCT], []) => {
131+
if let Some(v) = $opt_expr.target_modifier_info_by_hash($name_hash) {
132+
return Some(v);
133+
}
134+
};
135+
($opt_expr:expr, $prefix:expr, $_opt_name:ident, $_name_hash:expr, [UNTRACKED], []) => {{}};
136+
($opt_expr:expr, $prefix:expr, $_opt_name:ident, $_name_hash:expr, [TRACKED], []) => {{}};
137+
($opt_expr:expr, $prefix:expr, $_opt_name:ident, $_name_hash:expr, [TRACKED_NO_CRATE_HASH], []) => {{}};
138+
}
139+
140+
macro_rules! gather_tmods_top_level {
141+
($_opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr, [SUBSTRUCT], [TMOD]) => {
142+
compile_error!("SUBSTRUCT can't be target modifier");
143+
};
144+
($opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr, [UNTRACKED], [TMOD]) => {
145+
compile_error!("Top level option can't be target modifier");
146+
};
147+
($opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr, [TRACKED], [TMOD]) => {
148+
compile_error!("Top level option can't be target modifier");
149+
};
150+
($opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr, [TRACKED_NO_CRATE_HASH], [TMOD]) => {
151+
compile_error!("Top level option can't be target modifier");
152+
};
153+
($_opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr, [SUBSTRUCT], []) => {
154+
$opt_expr.gather_target_modifiers($mods, $tmod_vals);
155+
};
156+
($_opt_name:ident, $_opt_expr:expr, $_mods:expr, $_tmod_vals:expr, [UNTRACKED], []) => {{}};
157+
($_opt_name:ident, $_opt_expr:expr, $_mods:expr, $_tmod_vals:expr, [TRACKED], []) => {{}};
158+
($_opt_name:ident, $_opt_expr:expr, $_mods:expr, $_tmod_vals:expr, [TRACKED_NO_CRATE_HASH], []) => {{}};
159+
}
160+
61161
macro_rules! top_level_options {
62162
( $( #[$top_level_attr:meta] )* pub struct Options { $(
63163
$( #[$attr:meta] )*
64-
$opt:ident : $t:ty [$dep_tracking_marker:ident],
164+
$opt:ident : $t:ty [$dep_tracking_marker:ident $( $tmod:ident )?],
65165
)* } ) => (
66166
#[derive(Clone)]
67167
$( #[$top_level_attr] )*
68168
pub struct Options {
69169
$(
70170
$( #[$attr] )*
71171
pub $opt: $t
72-
),*
172+
),*,
173+
pub target_modifiers: BTreeMap<String, String>,
73174
}
74175

75176
impl Options {
@@ -97,6 +198,25 @@ macro_rules! top_level_options {
97198
})*
98199
hasher.finish()
99200
}
201+
202+
// Returns options prefix ("Z"/"C") and option name
203+
pub fn target_modifier_info_by_hash(&self, opt_name_hash: u64) -> Option<(String, String)> {
204+
$({
205+
tmod_info!(&self.$opt, "", $opt, opt_name_hash, [$dep_tracking_marker], [$($tmod),*]);
206+
})*
207+
None
208+
}
209+
210+
pub fn gather_target_modifiers(&self) -> Vec<TargetModifier> {
211+
let mut mods = Vec::<TargetModifier>::new();
212+
$({
213+
gather_tmods_top_level!($opt,
214+
&self.$opt, &mut mods, &self.target_modifiers,
215+
[$dep_tracking_marker], [$($tmod),*]);
216+
})*
217+
mods.sort_by(|a, b| a.name_hash.cmp(&b.name_hash));
218+
mods
219+
}
100220
}
101221
);
102222
}
@@ -225,6 +345,15 @@ top_level_options!(
225345
}
226346
);
227347

348+
macro_rules! has_tmod {
349+
($v:ident) => {
350+
true
351+
};
352+
() => {
353+
false
354+
};
355+
}
356+
228357
/// Defines all `CodegenOptions`/`DebuggingOptions` fields and parsers all at once. The goal of this
229358
/// macro is to define an interface that can be programmatically used by the option parser
230359
/// to initialize the struct without hardcoding field names all over the place.
@@ -238,7 +367,7 @@ macro_rules! options {
238367
$($( #[$attr:meta] )* $opt:ident : $t:ty = (
239368
$init:expr,
240369
$parse:ident,
241-
[$dep_tracking_marker:ident],
370+
[$dep_tracking_marker:ident $( $tmod:ident )*],
242371
$desc:expr)
243372
),* ,) =>
244373
(
@@ -256,8 +385,9 @@ macro_rules! options {
256385
pub fn build(
257386
early_dcx: &EarlyDiagCtxt,
258387
matches: &getopts::Matches,
388+
target_modifiers: &mut BTreeMap<String, String>,
259389
) -> $struct_name {
260-
build_options(early_dcx, matches, $stat, $prefix, $outputname)
390+
build_options(early_dcx, matches, target_modifiers, $stat, $prefix, $outputname)
261391
}
262392

263393
fn dep_tracking_hash(&self, for_crate_hash: bool, error_format: ErrorOutputType) -> u64 {
@@ -277,10 +407,29 @@ macro_rules! options {
277407
);
278408
hasher.finish()
279409
}
410+
411+
// Returns option prefix ("Z"/"C") and name if found by hash
412+
pub fn target_modifier_info_by_hash(&self, _opt_name_hash: u64) -> Option<(String, String)> {
413+
$({
414+
tmod_info!(&self.$opt, $prefix, $opt, _opt_name_hash, [$dep_tracking_marker], [$($tmod),*]);
415+
})*
416+
None
417+
}
418+
419+
pub fn gather_target_modifiers(
420+
&self,
421+
_mods: &mut Vec<TargetModifier>,
422+
_tmod_vals: &BTreeMap<String, String>,
423+
) {
424+
$({
425+
gather_tmods!($opt, $init,
426+
&self.$opt, _mods, _tmod_vals, [$dep_tracking_marker], [$($tmod),*]);
427+
})*
428+
}
280429
}
281430

282431
pub const $stat: OptionDescrs<$struct_name> =
283-
&[ $( (stringify!($opt), $optmod::$opt, desc::$parse, $desc) ),* ];
432+
&[ $( (stringify!($opt), $optmod::$opt, desc::$parse, $desc, has_tmod!($($tmod),*)) ),* ];
284433

285434
mod $optmod {
286435
$(
@@ -315,12 +464,14 @@ macro_rules! redirect_field {
315464
}
316465

317466
type OptionSetter<O> = fn(&mut O, v: Option<&str>) -> bool;
318-
type OptionDescrs<O> = &'static [(&'static str, OptionSetter<O>, &'static str, &'static str)];
467+
type OptionDescrs<O> =
468+
&'static [(&'static str, OptionSetter<O>, &'static str, &'static str, bool)];
319469

320470
#[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable
321471
fn build_options<O: Default>(
322472
early_dcx: &EarlyDiagCtxt,
323473
matches: &getopts::Matches,
474+
target_modifiers: &mut BTreeMap<String, String>,
324475
descrs: OptionDescrs<O>,
325476
prefix: &str,
326477
outputname: &str,
@@ -334,7 +485,7 @@ fn build_options<O: Default>(
334485

335486
let option_to_lookup = key.replace('-', "_");
336487
match descrs.iter().find(|(name, ..)| *name == option_to_lookup) {
337-
Some((_, setter, type_desc, _)) => {
488+
Some((_, setter, type_desc, _, tmod)) => {
338489
if !setter(&mut op, value) {
339490
match value {
340491
None => early_dcx.early_fatal(
@@ -349,6 +500,9 @@ fn build_options<O: Default>(
349500
),
350501
}
351502
}
503+
if *tmod && let Some(value) = value {
504+
target_modifiers.insert(option_to_lookup, value.to_string());
505+
}
352506
}
353507
None => early_dcx.early_fatal(format!("unknown {outputname} option: `{key}`")),
354508
}
@@ -372,6 +526,7 @@ mod desc {
372526
"a comma-separated list of strings, with elements beginning with + or -";
373527
pub(crate) const parse_comma_list: &str = "a comma-separated list of strings";
374528
pub(crate) const parse_opt_comma_list: &str = parse_comma_list;
529+
pub(crate) const parse_opt_comma_list_or_empty: &str = parse_comma_list;
375530
pub(crate) const parse_number: &str = "a number";
376531
pub(crate) const parse_opt_number: &str = parse_number;
377532
pub(crate) const parse_frame_pointer: &str = "one of `true`/`yes`/`on`, `false`/`no`/`off`, or (with -Zunstable-options) `non-leaf` or `always`";
@@ -659,6 +814,20 @@ pub mod parse {
659814
}
660815
}
661816

817+
// Allows both "-C(Z)flag=value1,value2,...,valueN" and "-C(Z)flag" (setting empty vector)
818+
pub(crate) fn parse_opt_comma_list_or_empty(
819+
slot: &mut Option<Vec<String>>,
820+
v: Option<&str>,
821+
) -> bool {
822+
match v {
823+
Some(_) => parse_opt_comma_list(slot, v),
824+
None => {
825+
*slot = Some(Vec::<String>::new());
826+
true
827+
}
828+
}
829+
}
830+
662831
pub(crate) fn parse_threads(slot: &mut usize, v: Option<&str>) -> bool {
663832
let ret = match v.and_then(|s| s.parse().ok()) {
664833
Some(0) => {
@@ -1652,6 +1821,9 @@ options! {
16521821
target_feature: String = (String::new(), parse_target_feature, [TRACKED],
16531822
"target specific attributes. (`rustc --print target-features` for details). \
16541823
This feature is unsafe."),
1824+
unsafe_allow_abi_mismatch: Option<Vec<String>> = (None, parse_opt_comma_list_or_empty, [UNTRACKED],
1825+
"Allow incompatible target modifiers in dependency crates \
1826+
(comma separated list or empty to allow all)"),
16551827
// tidy-alphabetical-end
16561828

16571829
// If you add a new option, please update:
@@ -1985,7 +2157,7 @@ options! {
19852157
"enable queries of the dependency graph for regression testing (default: no)"),
19862158
randomize_layout: bool = (false, parse_bool, [TRACKED],
19872159
"randomize the layout of types (default: no)"),
1988-
regparm: Option<u32> = (None, parse_opt_number, [TRACKED],
2160+
regparm: Option<u32> = (None, parse_opt_number, [TRACKED TMOD],
19892161
"On x86-32 targets, setting this to N causes the compiler to pass N arguments \
19902162
in registers EAX, EDX, and ECX instead of on the stack for\
19912163
\"C\", \"cdecl\", and \"stdcall\" fn.\

‎src/librustdoc/config.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -382,8 +382,9 @@ impl Options {
382382
config::parse_error_format(early_dcx, matches, color, json_color, json_rendered);
383383
let diagnostic_width = matches.opt_get("diagnostic-width").unwrap_or_default();
384384

385-
let codegen_options = CodegenOptions::build(early_dcx, matches);
386-
let unstable_opts = UnstableOptions::build(early_dcx, matches);
385+
let mut target_modifiers = BTreeMap::<String, String>::new();
386+
let codegen_options = CodegenOptions::build(early_dcx, matches, &mut target_modifiers);
387+
let unstable_opts = UnstableOptions::build(early_dcx, matches, &mut target_modifiers);
387388

388389
let remap_path_prefix = match parse_remap_path_prefix(matches) {
389390
Ok(prefix_mappings) => prefix_mappings,
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
//@ compile-flags: --target i686-unknown-linux-gnu -Zregparm=2 -Cpanic=abort
2+
//@ needs-llvm-components: x86
3+
#![crate_type = "lib"]
4+
#![no_core]
5+
#![feature(no_core, lang_items, repr_simd)]
6+
7+
#[lang = "sized"]
8+
trait Sized {}
9+
#[lang = "copy"]
10+
trait Copy {}
11+
12+
pub fn somefun() {}
13+
14+
pub struct S;
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
error: mixing `-Zregparm` will cause an ABI mismatch
2+
--> $DIR/incompatible_regparm.rs:12:1
3+
|
4+
LL | #![crate_type = "lib"]
5+
| ^
6+
|
7+
= help: `-Zregparm` modifies the ABI and Rust crates compiled with different values of this flag cannot be used together safely
8+
= note: `-Zregparm=1` in this crate is incompatible with `-Zregparm=2` in dependency `wrong_regparm`
9+
= note: alternatively, use `-Cunsafe-allow-abi-mismatch=regparm` to silence this error
10+
= note: `#[deny(incompatible_target_modifiers)]` on by default
11+
12+
error: aborting due to 1 previous error
13+
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//@ aux-crate:wrong_regparm=wrong_regparm.rs
2+
//@ compile-flags: --target i686-unknown-linux-gnu -Zregparm=1 -Cpanic=abort
3+
//@ needs-llvm-components: x86
4+
//@ revisions:error_generated allow_regparm_mismatch allow_any_mismatch allow_attr
5+
6+
//@[allow_regparm_mismatch] compile-flags: -Cunsafe-allow-abi-mismatch=regparm
7+
//@[allow_any_mismatch] compile-flags: -Cunsafe-allow-abi-mismatch
8+
//@[allow_regparm_mismatch] build-pass
9+
//@[allow_any_mismatch] build-pass
10+
//@[allow_attr] build-pass
11+
12+
#![crate_type = "lib"]
13+
//[error_generated]~^ ERROR 12:1: 12:1: mixing `-Zregparm` will cause an ABI mismatch [incompatible_target_modifiers]
14+
#![no_core]
15+
#![feature(no_core, lang_items, repr_simd)]
16+
17+
#![cfg_attr(allow_attr, allow(incompatible_target_modifiers))]
18+
19+
fn foo() {
20+
wrong_regparm::somefun();
21+
}

0 commit comments

Comments
 (0)
Please sign in to comment.