Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion compiler/rustc_codegen_llvm/src/abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ fn get_attrs<'ll>(this: &ArgAttributes, cx: &CodegenCx<'ll, '_>) -> SmallVec<[&'
}
}
}
} else if cx.tcx.sess.opts.unstable_opts.sanitizer.contains(SanitizerSet::MEMORY) {
} else if cx.tcx.sess.sanitizers().contains(SanitizerSet::MEMORY) {
// If we're not optimising, *but* memory sanitizer is on, emit noundef, since it affects
// memory sanitizer's behavior.

Expand Down
10 changes: 2 additions & 8 deletions compiler/rustc_codegen_llvm/src/attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ pub(crate) fn sanitize_attrs<'ll, 'tcx>(
no_sanitize: SanitizerSet,
) -> SmallVec<[&'ll Attribute; 4]> {
let mut attrs = SmallVec::new();
let enabled = tcx.sess.opts.unstable_opts.sanitizer - no_sanitize;
let enabled = tcx.sess.sanitizers() - no_sanitize;
if enabled.contains(SanitizerSet::ADDRESS) || enabled.contains(SanitizerSet::KERNELADDRESS) {
attrs.push(llvm::AttributeKind::SanitizeAddress.create_attr(cx.llcx));
}
Expand Down Expand Up @@ -240,13 +240,7 @@ fn probestack_attr<'ll, 'tcx>(cx: &SimpleCx<'ll>, tcx: TyCtxt<'tcx>) -> Option<&
// Currently stack probes seem somewhat incompatible with the address
// sanitizer and thread sanitizer. With asan we're already protected from
// stack overflow anyway so we don't really need stack probes regardless.
if tcx
.sess
.opts
.unstable_opts
.sanitizer
.intersects(SanitizerSet::ADDRESS | SanitizerSet::THREAD)
{
if tcx.sess.sanitizers().intersects(SanitizerSet::ADDRESS | SanitizerSet::THREAD) {
return None;
}

Expand Down
8 changes: 2 additions & 6 deletions compiler/rustc_codegen_ssa/src/back/link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1227,7 +1227,7 @@ fn add_sanitizer_libraries(
return;
}

let sanitizer = sess.opts.unstable_opts.sanitizer;
let sanitizer = sess.sanitizers();
if sanitizer.contains(SanitizerSet::ADDRESS) {
link_sanitizer_runtime(sess, flavor, linker, "asan");
}
Expand Down Expand Up @@ -2497,11 +2497,7 @@ fn add_order_independent_options(
&& crate_type == CrateType::Executable
&& !matches!(flavor, LinkerFlavor::Gnu(Cc::Yes, _))
{
let prefix = if sess.opts.unstable_opts.sanitizer.contains(SanitizerSet::ADDRESS) {
"asan/"
} else {
""
};
let prefix = if sess.sanitizers().contains(SanitizerSet::ADDRESS) { "asan/" } else { "" };
cmd.link_arg(format!("--dynamic-linker={prefix}ld.so.1"));
}

Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_codegen_ssa/src/back/write.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ impl ModuleConfig {
debug_info_for_profiling: sess.opts.unstable_opts.debug_info_for_profiling,
instrument_coverage: if_regular!(sess.instrument_coverage(), false),

sanitizer: if_regular!(sess.opts.unstable_opts.sanitizer, SanitizerSet::empty()),
sanitizer: if_regular!(sess.sanitizers(), SanitizerSet::empty()),
sanitizer_dataflow_abilist: if_regular!(
sess.opts.unstable_opts.sanitizer_dataflow_abilist.clone(),
Vec::new()
Expand Down
73 changes: 73 additions & 0 deletions compiler/rustc_lint/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2875,6 +2875,71 @@ enum AsmLabelKind {
Binary,
}

/// Checks if a potential label is actually a Hexagon register span notation.
///
/// Hexagon assembly uses register span notation like `r1:0`, `V5:4.w`, `p1:0` etc.
/// These follow the pattern: `[letter][digit(s)]:[digit(s)][optional_suffix]`
///
/// Returns `true` if the string matches a valid Hexagon register span pattern.
pub fn is_hexagon_register_span(possible_label: &str) -> bool {
// Extract the full register span from the context
if let Some(colon_idx) = possible_label.find(':') {
let after_colon = &possible_label[colon_idx + 1..];
is_hexagon_register_span_impl(&possible_label[..colon_idx], after_colon)
} else {
false
}
}

/// Helper function for use within the lint when we have statement context.
fn is_hexagon_register_span_context(
possible_label: &str,
statement: &str,
colon_idx: usize,
) -> bool {
// Extract what comes after the colon in the statement
let after_colon_start = colon_idx + 1;
if after_colon_start >= statement.len() {
return false;
}

// Get the part after the colon, up to the next whitespace or special character
let after_colon_full = &statement[after_colon_start..];
let after_colon = after_colon_full
.chars()
.take_while(|&c| c.is_ascii_alphanumeric() || c == '.')
.collect::<String>();

is_hexagon_register_span_impl(possible_label, &after_colon)
}

/// Core implementation for checking hexagon register spans.
fn is_hexagon_register_span_impl(before_colon: &str, after_colon: &str) -> bool {
if before_colon.len() < 1 || after_colon.is_empty() {
return false;
}

let mut chars = before_colon.chars();
let start = chars.next().unwrap();

// Must start with a letter (r, V, p, etc.)
if !start.is_ascii_alphabetic() {
return false;
}

let rest = &before_colon[1..];

// Check if the part after the first letter is all digits and non-empty
if rest.is_empty() || !rest.chars().all(|c| c.is_ascii_digit()) {
return false;
}

// Check if after colon starts with digits (may have suffix like .w, .h)
let digits_after = after_colon.chars().take_while(|c| c.is_ascii_digit()).collect::<String>();

!digits_after.is_empty()
}

impl<'tcx> LateLintPass<'tcx> for AsmLabels {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
if let hir::Expr {
Expand Down Expand Up @@ -2957,6 +3022,14 @@ impl<'tcx> LateLintPass<'tcx> for AsmLabels {
break 'label_loop;
}

// Check for Hexagon register span notation (e.g., "r1:0", "V5:4", "V3:2.w")
// This is valid Hexagon assembly syntax, not a label
if matches!(cx.tcx.sess.asm_arch, Some(InlineAsmArch::Hexagon))
&& is_hexagon_register_span_context(possible_label, statement, idx)
{
break 'label_loop;
}

for c in chars {
// Inside a template format arg, any character is permitted for the
// purposes of label detection because we assume that it can be
Expand Down
27 changes: 27 additions & 0 deletions compiler/rustc_lint/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

use rustc_span::{Symbol, create_default_session_globals_then};

use crate::builtin::is_hexagon_register_span;
use crate::levels::parse_lint_and_tool_name;

#[test]
Expand All @@ -27,3 +28,29 @@ fn parse_lint_multiple_path() {
)
});
}

#[test]
fn test_hexagon_register_span_patterns() {
// Valid Hexagon register span patterns
assert!(is_hexagon_register_span("r1:0"));
assert!(is_hexagon_register_span("r15:14"));
assert!(is_hexagon_register_span("V5:4"));
assert!(is_hexagon_register_span("V3:2"));
assert!(is_hexagon_register_span("V5:4.w"));
assert!(is_hexagon_register_span("V3:2.h"));
assert!(is_hexagon_register_span("r99:98"));
assert!(is_hexagon_register_span("V123:122.whatever"));

// Invalid patterns - these should be treated as potential labels
assert!(!is_hexagon_register_span("label1"));
assert!(!is_hexagon_register_span("foo:"));
assert!(!is_hexagon_register_span(":0"));
assert!(!is_hexagon_register_span("r:0")); // missing digits before colon
assert!(!is_hexagon_register_span("r1:")); // missing digits after colon
assert!(!is_hexagon_register_span("r1:a")); // non-digit after colon
assert!(!is_hexagon_register_span("1:0")); // starts with digit, not letter
assert!(!is_hexagon_register_span("r1")); // no colon
assert!(!is_hexagon_register_span("r")); // too short
assert!(!is_hexagon_register_span("")); // empty
assert!(!is_hexagon_register_span("ra:0")); // letter in first digit group
}
10 changes: 5 additions & 5 deletions compiler/rustc_metadata/src/creader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -412,7 +412,7 @@ impl CStore {
match (&left_name_val, &right_name_val) {
(Some(l), Some(r)) => match l.1.opt.cmp(&r.1.opt) {
cmp::Ordering::Equal => {
if !l.1.consistent(&tcx.sess.opts, Some(&r.1)) {
if !l.1.consistent(&tcx.sess, Some(&r.1)) {
report_diff(
&l.0.prefix,
&l.0.name,
Expand All @@ -424,26 +424,26 @@ impl CStore {
right_name_val = None;
}
cmp::Ordering::Greater => {
if !r.1.consistent(&tcx.sess.opts, None) {
if !r.1.consistent(&tcx.sess, None) {
report_diff(&r.0.prefix, &r.0.name, None, Some(&r.1.value_name));
}
right_name_val = None;
}
cmp::Ordering::Less => {
if !l.1.consistent(&tcx.sess.opts, None) {
if !l.1.consistent(&tcx.sess, None) {
report_diff(&l.0.prefix, &l.0.name, Some(&l.1.value_name), None);
}
left_name_val = None;
}
},
(Some(l), None) => {
if !l.1.consistent(&tcx.sess.opts, None) {
if !l.1.consistent(&tcx.sess, None) {
report_diff(&l.0.prefix, &l.0.name, Some(&l.1.value_name), None);
}
left_name_val = None;
}
(None, Some(r)) => {
if !r.1.consistent(&tcx.sess.opts, None) {
if !r.1.consistent(&tcx.sess, None) {
report_diff(&r.0.prefix, &r.0.name, None, Some(&r.1.value_name));
}
right_name_val = None;
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_metadata/src/native_libs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ pub fn walk_native_lib_search_dirs<R>(
|| sess.target.os == "linux"
|| sess.target.os == "fuchsia"
|| sess.target.is_like_aix
|| sess.target.is_like_darwin && !sess.opts.unstable_opts.sanitizer.is_empty()
|| sess.target.is_like_darwin && !sess.sanitizers().is_empty()
{
f(&sess.target_tlib_path.dir, false)?;
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_session/src/config/cfg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ pub(crate) fn default_configuration(sess: &Session) -> Cfg {
ins_sym!(sym::relocation_model, sess.target.relocation_model.desc_symbol());
}

for mut s in sess.opts.unstable_opts.sanitizer {
for mut s in sess.sanitizers() {
// KASAN is still ASAN under the hood, so it uses the same attribute.
if s == SanitizerSet::KERNELADDRESS {
s = SanitizerSet::ADDRESS;
Expand Down
11 changes: 6 additions & 5 deletions compiler/rustc_session/src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use rustc_target::spec::{
use crate::config::*;
use crate::search_paths::SearchPath;
use crate::utils::NativeLib;
use crate::{EarlyDiagCtxt, lint};
use crate::{EarlyDiagCtxt, Session, lint};

macro_rules! insert {
($opt_name:ident, $opt_expr:expr, $sub_hashes:expr) => {
Expand Down Expand Up @@ -111,12 +111,12 @@ mod target_modifier_consistency_check {
lparsed & tmod_sanitizers == rparsed & tmod_sanitizers
}
pub(super) fn sanitizer_cfi_normalize_integers(
opts: &Options,
sess: &Session,
l: &TargetModifier,
r: Option<&TargetModifier>,
) -> bool {
// For kCFI, the helper flag -Zsanitizer-cfi-normalize-integers should also be a target modifier
if opts.unstable_opts.sanitizer.contains(SanitizerSet::KCFI) {
if sess.sanitizers().contains(SanitizerSet::KCFI) {
if let Some(r) = r {
return l.extend().tech_value == r.extend().tech_value;
} else {
Expand All @@ -133,7 +133,7 @@ impl TargetModifier {
}
// Custom consistency check for target modifiers (or default `l.tech_value == r.tech_value`)
// When other is None, consistency with default value is checked
pub fn consistent(&self, opts: &Options, other: Option<&TargetModifier>) -> bool {
pub fn consistent(&self, sess: &Session, other: Option<&TargetModifier>) -> bool {
assert!(other.is_none() || self.opt == other.unwrap().opt);
match self.opt {
OptionsTargetModifiers::UnstableOptions(unstable) => match unstable {
Expand All @@ -142,7 +142,7 @@ impl TargetModifier {
}
UnstableOptionsTargetModifiers::sanitizer_cfi_normalize_integers => {
return target_modifier_consistency_check::sanitizer_cfi_normalize_integers(
opts, self, other,
sess, self, other,
);
}
_ => {}
Expand Down Expand Up @@ -2575,6 +2575,7 @@ written to standard error output)"),
retpoline_external_thunk: bool = (false, parse_bool, [TRACKED TARGET_MODIFIER],
"enables retpoline-external-thunk, retpoline-indirect-branches and retpoline-indirect-calls \
target features (default: no)"),
#[rustc_lint_opt_deny_field_access("use `Session::sanitizers()` instead of this field")]
sanitizer: SanitizerSet = (SanitizerSet::empty(), parse_sanitizers, [TRACKED TARGET_MODIFIER],
"use a sanitizer"),
sanitizer_cfi_canonical_jump_tables: Option<bool> = (Some(true), parse_opt_bool, [TRACKED],
Expand Down
10 changes: 7 additions & 3 deletions compiler/rustc_session/src/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,7 @@ impl Session {
}

pub fn is_sanitizer_cfi_enabled(&self) -> bool {
self.opts.unstable_opts.sanitizer.contains(SanitizerSet::CFI)
self.sanitizers().contains(SanitizerSet::CFI)
}

pub fn is_sanitizer_cfi_canonical_jump_tables_disabled(&self) -> bool {
Expand All @@ -347,7 +347,7 @@ impl Session {
}

pub fn is_sanitizer_kcfi_enabled(&self) -> bool {
self.opts.unstable_opts.sanitizer.contains(SanitizerSet::KCFI)
self.sanitizers().contains(SanitizerSet::KCFI)
}

pub fn is_split_lto_unit_enabled(&self) -> bool {
Expand Down Expand Up @@ -527,7 +527,7 @@ impl Session {
// AddressSanitizer and KernelAddressSanitizer uses lifetimes to detect use after scope bugs.
// MemorySanitizer uses lifetimes to detect use of uninitialized stack variables.
// HWAddressSanitizer will use lifetimes to detect use after scope bugs in the future.
|| self.opts.unstable_opts.sanitizer.intersects(SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS | SanitizerSet::MEMORY | SanitizerSet::HWADDRESS)
|| self.sanitizers().intersects(SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS | SanitizerSet::MEMORY | SanitizerSet::HWADDRESS)
}

pub fn diagnostic_width(&self) -> usize {
Expand Down Expand Up @@ -922,6 +922,10 @@ impl Session {
min
}
}

pub fn sanitizers(&self) -> SanitizerSet {
return self.opts.unstable_opts.sanitizer | self.target.options.default_sanitizers;
}
}

// JUSTIFICATION: part of session construction
Expand Down
7 changes: 7 additions & 0 deletions compiler/rustc_target/src/spec/json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,11 @@ impl Target {
supported_sanitizers.into_iter().fold(SanitizerSet::empty(), |a, b| a | b);
}

if let Some(default_sanitizers) = json.default_sanitizers {
base.default_sanitizers =
default_sanitizers.into_iter().fold(SanitizerSet::empty(), |a, b| a | b);
}

forward!(generate_arange_section);
forward!(supports_stack_protector);
forward!(small_data_threshold_support);
Expand Down Expand Up @@ -392,6 +397,7 @@ impl ToJson for Target {
target_option_val!(split_debuginfo);
target_option_val!(supported_split_debuginfo);
target_option_val!(supported_sanitizers);
target_option_val!(default_sanitizers);
target_option_val!(c_enum_min_bits);
target_option_val!(generate_arange_section);
target_option_val!(supports_stack_protector);
Expand Down Expand Up @@ -612,6 +618,7 @@ struct TargetSpecJson {
split_debuginfo: Option<SplitDebuginfo>,
supported_split_debuginfo: Option<StaticCow<[SplitDebuginfo]>>,
supported_sanitizers: Option<Vec<SanitizerSet>>,
default_sanitizers: Option<Vec<SanitizerSet>>,
generate_arange_section: Option<bool>,
supports_stack_protector: Option<bool>,
small_data_threshold_support: Option<SmallDataThresholdSupport>,
Expand Down
8 changes: 8 additions & 0 deletions compiler/rustc_target/src/spec/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2410,6 +2410,13 @@ pub struct TargetOptions {
/// distributed with the target, the sanitizer should still appear in this list for the target.
pub supported_sanitizers: SanitizerSet,

/// The sanitizers that are enabled by default on this target.
///
/// Note that the support here is at a codegen level. If the machine code with sanitizer
/// enabled can generated on this target, but the necessary supporting libraries are not
/// distributed with the target, the sanitizer should still appear in this list for the target.
pub default_sanitizers: SanitizerSet,

/// Minimum number of bits in #[repr(C)] enum. Defaults to the size of c_int
pub c_enum_min_bits: Option<u64>,

Expand Down Expand Up @@ -2658,6 +2665,7 @@ impl Default for TargetOptions {
// `Off` is supported by default, but targets can remove this manually, e.g. Windows.
supported_split_debuginfo: Cow::Borrowed(&[SplitDebuginfo::Off]),
supported_sanitizers: SanitizerSet::empty(),
default_sanitizers: SanitizerSet::empty(),
c_enum_min_bits: None,
generate_arange_section: true,
supports_stack_protector: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ pub(crate) fn target() -> Target {
| SanitizerSet::CFI
| SanitizerSet::LEAK
| SanitizerSet::SHADOWCALLSTACK;
base.default_sanitizers = SanitizerSet::SHADOWCALLSTACK;
base.supports_xray = true;

base.add_pre_link_args(
Expand Down
Loading
Loading