mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-23 15:23:46 +00:00
Rollup merge of #73347 - tmiasko:incompatible-sanitizers, r=nikic
Diagnose use of incompatible sanitizers Emit an error when incompatible sanitizer are configured through command line options. Previously the last one configured prevailed and others were silently ignored. Additionally use a set to represent configured sanitizers, making it possible to enable multiple sanitizers at once. At least in principle, since currently all of them are considered to be incompatible with others.
This commit is contained in:
commit
17b80d947d
@ -4278,6 +4278,7 @@ dependencies = [
|
||||
name = "rustc_session"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"getopts",
|
||||
"log",
|
||||
"num_cpus",
|
||||
|
@ -12,8 +12,7 @@ This feature allows for use of one of following sanitizers:
|
||||
* [ThreadSanitizer][clang-tsan] a fast data race detector.
|
||||
|
||||
To enable a sanitizer compile with `-Zsanitizer=address`, `-Zsanitizer=leak`,
|
||||
`-Zsanitizer=memory` or `-Zsanitizer=thread`. Only a single sanitizer can be
|
||||
enabled at a time.
|
||||
`-Zsanitizer=memory` or `-Zsanitizer=thread`.
|
||||
|
||||
# AddressSanitizer
|
||||
|
||||
|
@ -11,7 +11,7 @@ use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
|
||||
use rustc_middle::ty::layout::HasTyCtxt;
|
||||
use rustc_middle::ty::query::Providers;
|
||||
use rustc_middle::ty::{self, TyCtxt};
|
||||
use rustc_session::config::{OptLevel, Sanitizer};
|
||||
use rustc_session::config::{OptLevel, SanitizerSet};
|
||||
use rustc_session::Session;
|
||||
|
||||
use crate::attributes;
|
||||
@ -45,26 +45,16 @@ fn inline(cx: &CodegenCx<'ll, '_>, val: &'ll Value, inline: InlineAttr) {
|
||||
|
||||
/// Apply LLVM sanitize attributes.
|
||||
#[inline]
|
||||
pub fn sanitize(cx: &CodegenCx<'ll, '_>, codegen_fn_flags: CodegenFnAttrFlags, llfn: &'ll Value) {
|
||||
if let Some(ref sanitizer) = cx.tcx.sess.opts.debugging_opts.sanitizer {
|
||||
match *sanitizer {
|
||||
Sanitizer::Address => {
|
||||
if !codegen_fn_flags.contains(CodegenFnAttrFlags::NO_SANITIZE_ADDRESS) {
|
||||
llvm::Attribute::SanitizeAddress.apply_llfn(Function, llfn);
|
||||
}
|
||||
}
|
||||
Sanitizer::Memory => {
|
||||
if !codegen_fn_flags.contains(CodegenFnAttrFlags::NO_SANITIZE_MEMORY) {
|
||||
llvm::Attribute::SanitizeMemory.apply_llfn(Function, llfn);
|
||||
}
|
||||
}
|
||||
Sanitizer::Thread => {
|
||||
if !codegen_fn_flags.contains(CodegenFnAttrFlags::NO_SANITIZE_THREAD) {
|
||||
llvm::Attribute::SanitizeThread.apply_llfn(Function, llfn);
|
||||
}
|
||||
}
|
||||
Sanitizer::Leak => {}
|
||||
}
|
||||
pub fn sanitize(cx: &CodegenCx<'ll, '_>, no_sanitize: SanitizerSet, llfn: &'ll Value) {
|
||||
let enabled = cx.tcx.sess.opts.debugging_opts.sanitizer - no_sanitize;
|
||||
if enabled.contains(SanitizerSet::ADDRESS) {
|
||||
llvm::Attribute::SanitizeAddress.apply_llfn(Function, llfn);
|
||||
}
|
||||
if enabled.contains(SanitizerSet::MEMORY) {
|
||||
llvm::Attribute::SanitizeMemory.apply_llfn(Function, llfn);
|
||||
}
|
||||
if enabled.contains(SanitizerSet::THREAD) {
|
||||
llvm::Attribute::SanitizeThread.apply_llfn(Function, llfn);
|
||||
}
|
||||
}
|
||||
|
||||
@ -123,9 +113,14 @@ fn set_probestack(cx: &CodegenCx<'ll, '_>, llfn: &'ll Value) {
|
||||
// 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.
|
||||
match cx.sess().opts.debugging_opts.sanitizer {
|
||||
Some(Sanitizer::Address | Sanitizer::Thread) => return,
|
||||
_ => {}
|
||||
if cx
|
||||
.sess()
|
||||
.opts
|
||||
.debugging_opts
|
||||
.sanitizer
|
||||
.intersects(SanitizerSet::ADDRESS | SanitizerSet::THREAD)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// probestack doesn't play nice either with `-C profile-generate`.
|
||||
@ -296,7 +291,7 @@ pub fn from_fn_attrs(cx: &CodegenCx<'ll, 'tcx>, llfn: &'ll Value, instance: ty::
|
||||
if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::ALLOCATOR) {
|
||||
Attribute::NoAlias.apply_llfn(llvm::AttributePlace::ReturnValue, llfn);
|
||||
}
|
||||
sanitize(cx, codegen_fn_attrs.flags, llfn);
|
||||
sanitize(cx, codegen_fn_attrs.no_sanitize, llfn);
|
||||
|
||||
// Always annotate functions with the target-cpu they are compiled for.
|
||||
// Without this, ThinLTO won't inline Rust functions into Clang generated
|
||||
|
@ -21,7 +21,7 @@ use rustc_fs_util::{link_or_copy, path_to_c_string};
|
||||
use rustc_hir::def_id::LOCAL_CRATE;
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_session::config::{self, Lto, OutputType, Passes, Sanitizer, SwitchWithOptPath};
|
||||
use rustc_session::config::{self, Lto, OutputType, Passes, SanitizerSet, SwitchWithOptPath};
|
||||
use rustc_session::Session;
|
||||
use rustc_span::InnerSpan;
|
||||
use rustc_target::spec::{CodeModel, RelocModel};
|
||||
@ -394,12 +394,13 @@ pub(crate) unsafe fn optimize_with_new_llvm_pass_manager(
|
||||
let is_lto = opt_stage == llvm::OptStage::ThinLTO || opt_stage == llvm::OptStage::FatLTO;
|
||||
// Sanitizer instrumentation is only inserted during the pre-link optimization stage.
|
||||
let sanitizer_options = if !is_lto {
|
||||
config.sanitizer.as_ref().map(|s| llvm::SanitizerOptions {
|
||||
sanitize_memory: *s == Sanitizer::Memory,
|
||||
sanitize_thread: *s == Sanitizer::Thread,
|
||||
sanitize_address: *s == Sanitizer::Address,
|
||||
sanitize_recover: config.sanitizer_recover.contains(s),
|
||||
Some(llvm::SanitizerOptions {
|
||||
sanitize_address: config.sanitizer.contains(SanitizerSet::ADDRESS),
|
||||
sanitize_address_recover: config.sanitizer_recover.contains(SanitizerSet::ADDRESS),
|
||||
sanitize_memory: config.sanitizer.contains(SanitizerSet::MEMORY),
|
||||
sanitize_memory_recover: config.sanitizer_recover.contains(SanitizerSet::MEMORY),
|
||||
sanitize_memory_track_origins: config.sanitizer_memory_track_origins as c_int,
|
||||
sanitize_thread: config.sanitizer.contains(SanitizerSet::THREAD),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
@ -600,25 +601,18 @@ pub(crate) unsafe fn optimize(
|
||||
}
|
||||
|
||||
unsafe fn add_sanitizer_passes(config: &ModuleConfig, passes: &mut Vec<&'static mut llvm::Pass>) {
|
||||
let sanitizer = match &config.sanitizer {
|
||||
None => return,
|
||||
Some(s) => s,
|
||||
};
|
||||
|
||||
let recover = config.sanitizer_recover.contains(sanitizer);
|
||||
match sanitizer {
|
||||
Sanitizer::Address => {
|
||||
passes.push(llvm::LLVMRustCreateAddressSanitizerFunctionPass(recover));
|
||||
passes.push(llvm::LLVMRustCreateModuleAddressSanitizerPass(recover));
|
||||
}
|
||||
Sanitizer::Memory => {
|
||||
let track_origins = config.sanitizer_memory_track_origins as c_int;
|
||||
passes.push(llvm::LLVMRustCreateMemorySanitizerPass(track_origins, recover));
|
||||
}
|
||||
Sanitizer::Thread => {
|
||||
passes.push(llvm::LLVMRustCreateThreadSanitizerPass());
|
||||
}
|
||||
Sanitizer::Leak => {}
|
||||
if config.sanitizer.contains(SanitizerSet::ADDRESS) {
|
||||
let recover = config.sanitizer_recover.contains(SanitizerSet::ADDRESS);
|
||||
passes.push(llvm::LLVMRustCreateAddressSanitizerFunctionPass(recover));
|
||||
passes.push(llvm::LLVMRustCreateModuleAddressSanitizerPass(recover));
|
||||
}
|
||||
if config.sanitizer.contains(SanitizerSet::MEMORY) {
|
||||
let track_origins = config.sanitizer_memory_track_origins as c_int;
|
||||
let recover = config.sanitizer_recover.contains(SanitizerSet::MEMORY);
|
||||
passes.push(llvm::LLVMRustCreateMemorySanitizerPass(track_origins, recover));
|
||||
}
|
||||
if config.sanitizer.contains(SanitizerSet::THREAD) {
|
||||
passes.push(llvm::LLVMRustCreateThreadSanitizerPass());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -29,12 +29,12 @@ use rustc_codegen_ssa::traits::*;
|
||||
use rustc_codegen_ssa::{ModuleCodegen, ModuleKind};
|
||||
use rustc_data_structures::small_c_str::SmallCStr;
|
||||
use rustc_middle::dep_graph;
|
||||
use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs};
|
||||
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs;
|
||||
use rustc_middle::middle::cstore::EncodedMetadata;
|
||||
use rustc_middle::middle::exported_symbols;
|
||||
use rustc_middle::mir::mono::{Linkage, Visibility};
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_session::config::DebugInfo;
|
||||
use rustc_session::config::{DebugInfo, SanitizerSet};
|
||||
use rustc_span::symbol::Symbol;
|
||||
|
||||
use std::ffi::CString;
|
||||
@ -132,7 +132,7 @@ pub fn compile_codegen_unit(
|
||||
// If this codegen unit contains the main function, also create the
|
||||
// wrapper here
|
||||
if let Some(entry) = maybe_create_entry_wrapper::<Builder<'_, '_, '_>>(&cx) {
|
||||
attributes::sanitize(&cx, CodegenFnAttrFlags::empty(), entry);
|
||||
attributes::sanitize(&cx, SanitizerSet::empty(), entry);
|
||||
}
|
||||
|
||||
// Run replace-all-uses-with for statics that need it
|
||||
|
@ -439,11 +439,12 @@ pub enum OptStage {
|
||||
/// LLVMRustSanitizerOptions
|
||||
#[repr(C)]
|
||||
pub struct SanitizerOptions {
|
||||
pub sanitize_memory: bool,
|
||||
pub sanitize_thread: bool,
|
||||
pub sanitize_address: bool,
|
||||
pub sanitize_recover: bool,
|
||||
pub sanitize_address_recover: bool,
|
||||
pub sanitize_memory: bool,
|
||||
pub sanitize_memory_recover: bool,
|
||||
pub sanitize_memory_track_origins: c_int,
|
||||
pub sanitize_thread: bool,
|
||||
}
|
||||
|
||||
/// LLVMRelocMode
|
||||
|
@ -4,7 +4,7 @@ use rustc_hir::def_id::CrateNum;
|
||||
use rustc_middle::middle::cstore::{EncodedMetadata, LibSource, NativeLib};
|
||||
use rustc_middle::middle::dependency_format::Linkage;
|
||||
use rustc_session::config::{self, CFGuard, CrateType, DebugInfo};
|
||||
use rustc_session::config::{OutputFilenames, OutputType, PrintRequest, Sanitizer};
|
||||
use rustc_session::config::{OutputFilenames, OutputType, PrintRequest, SanitizerSet};
|
||||
use rustc_session::output::{check_file_is_writeable, invalid_output_for_target, out_filename};
|
||||
use rustc_session::search_paths::PathKind;
|
||||
use rustc_session::utils::NativeLibKind;
|
||||
@ -766,23 +766,26 @@ fn link_natively<'a, B: ArchiveBuilder<'a>>(
|
||||
}
|
||||
}
|
||||
|
||||
fn link_sanitizer_runtime(sess: &Session, crate_type: CrateType, linker: &mut dyn Linker) {
|
||||
let sanitizer = match &sess.opts.debugging_opts.sanitizer {
|
||||
Some(s) => s,
|
||||
None => return,
|
||||
};
|
||||
|
||||
fn link_sanitizers(sess: &Session, crate_type: CrateType, linker: &mut dyn Linker) {
|
||||
if crate_type != CrateType::Executable {
|
||||
return;
|
||||
}
|
||||
let sanitizer = sess.opts.debugging_opts.sanitizer;
|
||||
if sanitizer.contains(SanitizerSet::ADDRESS) {
|
||||
link_sanitizer_runtime(sess, linker, "asan");
|
||||
}
|
||||
if sanitizer.contains(SanitizerSet::LEAK) {
|
||||
link_sanitizer_runtime(sess, linker, "lsan");
|
||||
}
|
||||
if sanitizer.contains(SanitizerSet::MEMORY) {
|
||||
link_sanitizer_runtime(sess, linker, "msan");
|
||||
}
|
||||
if sanitizer.contains(SanitizerSet::THREAD) {
|
||||
link_sanitizer_runtime(sess, linker, "tsan");
|
||||
}
|
||||
}
|
||||
|
||||
let name = match sanitizer {
|
||||
Sanitizer::Address => "asan",
|
||||
Sanitizer::Leak => "lsan",
|
||||
Sanitizer::Memory => "msan",
|
||||
Sanitizer::Thread => "tsan",
|
||||
};
|
||||
|
||||
fn link_sanitizer_runtime(sess: &Session, linker: &mut dyn Linker, name: &str) {
|
||||
let default_sysroot = filesearch::get_or_default_sysroot();
|
||||
let default_tlib =
|
||||
filesearch::make_target_lib_path(&default_sysroot, sess.opts.target_triple.triple());
|
||||
@ -1555,9 +1558,10 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>(
|
||||
|
||||
// NO-OPT-OUT, OBJECT-FILES-NO, AUDIT-ORDER
|
||||
if sess.target.target.options.is_like_fuchsia && crate_type == CrateType::Executable {
|
||||
let prefix = match sess.opts.debugging_opts.sanitizer {
|
||||
Some(Sanitizer::Address) => "asan/",
|
||||
_ => "",
|
||||
let prefix = if sess.opts.debugging_opts.sanitizer.contains(SanitizerSet::ADDRESS) {
|
||||
"asan/"
|
||||
} else {
|
||||
""
|
||||
};
|
||||
cmd.arg(format!("--dynamic-linker={}ld.so.1", prefix));
|
||||
}
|
||||
@ -1581,7 +1585,7 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>(
|
||||
}
|
||||
|
||||
// OBJECT-FILES-YES, AUDIT-ORDER
|
||||
link_sanitizer_runtime(sess, crate_type, cmd);
|
||||
link_sanitizers(sess, crate_type, cmd);
|
||||
|
||||
// OBJECT-FILES-NO, AUDIT-ORDER
|
||||
// Linker plugins should be specified early in the list of arguments
|
||||
|
@ -15,7 +15,7 @@ use rustc_middle::ty::query::Providers;
|
||||
use rustc_middle::ty::subst::{GenericArgKind, SubstsRef};
|
||||
use rustc_middle::ty::Instance;
|
||||
use rustc_middle::ty::{SymbolName, TyCtxt};
|
||||
use rustc_session::config::{CrateType, Sanitizer};
|
||||
use rustc_session::config::{CrateType, SanitizerSet};
|
||||
|
||||
pub fn threshold(tcx: TyCtxt<'_>) -> SymbolExportLevel {
|
||||
crates_export_threshold(&tcx.sess.crate_types())
|
||||
@ -204,7 +204,7 @@ fn exported_symbols_provider_local(
|
||||
}));
|
||||
}
|
||||
|
||||
if let Some(Sanitizer::Memory) = tcx.sess.opts.debugging_opts.sanitizer {
|
||||
if tcx.sess.opts.debugging_opts.sanitizer.contains(SanitizerSet::MEMORY) {
|
||||
// Similar to profiling, preserve weak msan symbol during LTO.
|
||||
const MSAN_WEAK_SYMBOLS: [&str; 2] = ["__msan_track_origins", "__msan_keep_going"];
|
||||
|
||||
|
@ -29,7 +29,7 @@ use rustc_middle::middle::exported_symbols::SymbolExportLevel;
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_session::cgu_reuse_tracker::CguReuseTracker;
|
||||
use rustc_session::config::{self, CrateType, Lto, OutputFilenames, OutputType};
|
||||
use rustc_session::config::{Passes, Sanitizer, SwitchWithOptPath};
|
||||
use rustc_session::config::{Passes, SanitizerSet, SwitchWithOptPath};
|
||||
use rustc_session::Session;
|
||||
use rustc_span::source_map::SourceMap;
|
||||
use rustc_span::symbol::{sym, Symbol};
|
||||
@ -86,8 +86,8 @@ pub struct ModuleConfig {
|
||||
pub pgo_gen: SwitchWithOptPath,
|
||||
pub pgo_use: Option<PathBuf>,
|
||||
|
||||
pub sanitizer: Option<Sanitizer>,
|
||||
pub sanitizer_recover: Vec<Sanitizer>,
|
||||
pub sanitizer: SanitizerSet,
|
||||
pub sanitizer_recover: SanitizerSet,
|
||||
pub sanitizer_memory_track_origins: usize,
|
||||
|
||||
// Flags indicating which outputs to produce.
|
||||
@ -195,10 +195,10 @@ impl ModuleConfig {
|
||||
),
|
||||
pgo_use: if_regular!(sess.opts.cg.profile_use.clone(), None),
|
||||
|
||||
sanitizer: if_regular!(sess.opts.debugging_opts.sanitizer.clone(), None),
|
||||
sanitizer: if_regular!(sess.opts.debugging_opts.sanitizer, SanitizerSet::empty()),
|
||||
sanitizer_recover: if_regular!(
|
||||
sess.opts.debugging_opts.sanitizer_recover.clone(),
|
||||
vec![]
|
||||
sess.opts.debugging_opts.sanitizer_recover,
|
||||
SanitizerSet::empty()
|
||||
),
|
||||
sanitizer_memory_track_origins: if_regular!(
|
||||
sess.opts.debugging_opts.sanitizer_memory_track_origins,
|
||||
|
@ -6,7 +6,9 @@ use rustc_session::config::Strip;
|
||||
use rustc_session::config::{build_configuration, build_session_options, to_crate_config};
|
||||
use rustc_session::config::{rustc_optgroups, ErrorOutputType, ExternLocation, Options, Passes};
|
||||
use rustc_session::config::{CFGuard, ExternEntry, LinkerPluginLto, LtoCli, SwitchWithOptPath};
|
||||
use rustc_session::config::{Externs, OutputType, OutputTypes, Sanitizer, SymbolManglingVersion};
|
||||
use rustc_session::config::{
|
||||
Externs, OutputType, OutputTypes, SanitizerSet, SymbolManglingVersion,
|
||||
};
|
||||
use rustc_session::lint::Level;
|
||||
use rustc_session::search_paths::SearchPath;
|
||||
use rustc_session::utils::NativeLibKind;
|
||||
@ -569,9 +571,9 @@ fn test_debugging_options_tracking_hash() {
|
||||
tracked!(relro_level, Some(RelroLevel::Full));
|
||||
tracked!(report_delayed_bugs, true);
|
||||
tracked!(run_dsymutil, false);
|
||||
tracked!(sanitizer, Some(Sanitizer::Address));
|
||||
tracked!(sanitizer, SanitizerSet::ADDRESS);
|
||||
tracked!(sanitizer_memory_track_origins, 2);
|
||||
tracked!(sanitizer_recover, vec![Sanitizer::Address]);
|
||||
tracked!(sanitizer_recover, SanitizerSet::ADDRESS);
|
||||
tracked!(saturating_float_casts, Some(true));
|
||||
tracked!(share_generics, Some(true));
|
||||
tracked!(show_span, Some(String::from("abc")));
|
||||
|
@ -1,5 +1,6 @@
|
||||
use crate::mir::mono::Linkage;
|
||||
use rustc_attr::{InlineAttr, OptimizeAttr};
|
||||
use rustc_session::config::SanitizerSet;
|
||||
use rustc_span::symbol::Symbol;
|
||||
|
||||
#[derive(Clone, RustcEncodable, RustcDecodable, HashStable)]
|
||||
@ -30,6 +31,9 @@ pub struct CodegenFnAttrs {
|
||||
/// The `#[link_section = "..."]` attribute, or what executable section this
|
||||
/// should be placed in.
|
||||
pub link_section: Option<Symbol>,
|
||||
/// The `#[no_sanitize(...)]` attribute. Indicates sanitizers for which
|
||||
/// instrumentation should be disabled inside the annotated function.
|
||||
pub no_sanitize: SanitizerSet,
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
@ -69,20 +73,12 @@ bitflags! {
|
||||
const FFI_RETURNS_TWICE = 1 << 10;
|
||||
/// `#[track_caller]`: allow access to the caller location
|
||||
const TRACK_CALLER = 1 << 11;
|
||||
/// `#[no_sanitize(address)]`: disables address sanitizer instrumentation
|
||||
const NO_SANITIZE_ADDRESS = 1 << 12;
|
||||
/// `#[no_sanitize(memory)]`: disables memory sanitizer instrumentation
|
||||
const NO_SANITIZE_MEMORY = 1 << 13;
|
||||
/// `#[no_sanitize(thread)]`: disables thread sanitizer instrumentation
|
||||
const NO_SANITIZE_THREAD = 1 << 14;
|
||||
/// All `#[no_sanitize(...)]` attributes.
|
||||
const NO_SANITIZE_ANY = Self::NO_SANITIZE_ADDRESS.bits | Self::NO_SANITIZE_MEMORY.bits | Self::NO_SANITIZE_THREAD.bits;
|
||||
/// #[ffi_pure]: applies clang's `pure` attribute to a foreign function
|
||||
/// declaration.
|
||||
const FFI_PURE = 1 << 15;
|
||||
const FFI_PURE = 1 << 12;
|
||||
/// #[ffi_const]: applies clang's `const` attribute to a foreign function
|
||||
/// declaration.
|
||||
const FFI_CONST = 1 << 16;
|
||||
const FFI_CONST = 1 << 13;
|
||||
}
|
||||
}
|
||||
|
||||
@ -98,6 +94,7 @@ impl CodegenFnAttrs {
|
||||
target_features: vec![],
|
||||
linkage: None,
|
||||
link_section: None,
|
||||
no_sanitize: SanitizerSet::empty(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,7 +9,6 @@ use rustc_middle::mir::visit::*;
|
||||
use rustc_middle::mir::*;
|
||||
use rustc_middle::ty::subst::{Subst, SubstsRef};
|
||||
use rustc_middle::ty::{self, ConstKind, Instance, InstanceDef, ParamEnv, Ty, TyCtxt};
|
||||
use rustc_session::config::Sanitizer;
|
||||
use rustc_target::spec::abi::Abi;
|
||||
|
||||
use super::simplify::{remove_dead_blocks, CfgSimplifier};
|
||||
@ -232,24 +231,8 @@ impl Inliner<'tcx> {
|
||||
|
||||
// Avoid inlining functions marked as no_sanitize if sanitizer is enabled,
|
||||
// since instrumentation might be enabled and performed on the caller.
|
||||
match self.tcx.sess.opts.debugging_opts.sanitizer {
|
||||
Some(Sanitizer::Address) => {
|
||||
if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NO_SANITIZE_ADDRESS) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Some(Sanitizer::Memory) => {
|
||||
if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NO_SANITIZE_MEMORY) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Some(Sanitizer::Thread) => {
|
||||
if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NO_SANITIZE_THREAD) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Some(Sanitizer::Leak) => {}
|
||||
None => {}
|
||||
if self.tcx.sess.opts.debugging_opts.sanitizer.intersects(codegen_fn_attrs.no_sanitize) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let hinted = match codegen_fn_attrs.inline {
|
||||
|
@ -9,6 +9,7 @@ name = "rustc_session"
|
||||
path = "lib.rs"
|
||||
|
||||
[dependencies]
|
||||
bitflags = "1.2.1"
|
||||
getopts = "0.2"
|
||||
log = "0.4"
|
||||
rustc_errors = { path = "../librustc_errors" }
|
||||
|
@ -10,6 +10,7 @@ use crate::{early_error, early_warn, Session};
|
||||
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_data_structures::impl_stable_hash_via_hash;
|
||||
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
|
||||
|
||||
use rustc_target::spec::{Target, TargetTriple};
|
||||
|
||||
@ -37,35 +38,55 @@ pub struct Config {
|
||||
pub ptr_width: u32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum Sanitizer {
|
||||
Address,
|
||||
Leak,
|
||||
Memory,
|
||||
Thread,
|
||||
}
|
||||
|
||||
impl fmt::Display for Sanitizer {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match *self {
|
||||
Sanitizer::Address => "address".fmt(f),
|
||||
Sanitizer::Leak => "leak".fmt(f),
|
||||
Sanitizer::Memory => "memory".fmt(f),
|
||||
Sanitizer::Thread => "thread".fmt(f),
|
||||
}
|
||||
bitflags! {
|
||||
#[derive(Default, RustcEncodable, RustcDecodable)]
|
||||
pub struct SanitizerSet: u8 {
|
||||
const ADDRESS = 1 << 0;
|
||||
const LEAK = 1 << 1;
|
||||
const MEMORY = 1 << 2;
|
||||
const THREAD = 1 << 3;
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Sanitizer {
|
||||
type Err = ();
|
||||
fn from_str(s: &str) -> Result<Sanitizer, ()> {
|
||||
match s {
|
||||
"address" => Ok(Sanitizer::Address),
|
||||
"leak" => Ok(Sanitizer::Leak),
|
||||
"memory" => Ok(Sanitizer::Memory),
|
||||
"thread" => Ok(Sanitizer::Thread),
|
||||
_ => Err(()),
|
||||
/// Formats a sanitizer set as a comma separated list of sanitizers' names.
|
||||
impl fmt::Display for SanitizerSet {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let mut first = true;
|
||||
for s in *self {
|
||||
let name = match s {
|
||||
SanitizerSet::ADDRESS => "address",
|
||||
SanitizerSet::LEAK => "leak",
|
||||
SanitizerSet::MEMORY => "memory",
|
||||
SanitizerSet::THREAD => "thread",
|
||||
_ => panic!("unrecognized sanitizer {:?}", s),
|
||||
};
|
||||
if !first {
|
||||
f.write_str(",")?;
|
||||
}
|
||||
f.write_str(name)?;
|
||||
first = false;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoIterator for SanitizerSet {
|
||||
type Item = SanitizerSet;
|
||||
type IntoIter = std::vec::IntoIter<SanitizerSet>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
[SanitizerSet::ADDRESS, SanitizerSet::LEAK, SanitizerSet::MEMORY, SanitizerSet::THREAD]
|
||||
.iter()
|
||||
.copied()
|
||||
.filter(|&s| self.contains(s))
|
||||
.collect::<Vec<_>>()
|
||||
.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<CTX> HashStable<CTX> for SanitizerSet {
|
||||
fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
|
||||
self.bits().hash_stable(ctx, hasher);
|
||||
}
|
||||
}
|
||||
|
||||
@ -726,10 +747,12 @@ pub fn default_configuration(sess: &Session) -> CrateConfig {
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(s) = &sess.opts.debugging_opts.sanitizer {
|
||||
|
||||
for s in sess.opts.debugging_opts.sanitizer {
|
||||
let symbol = Symbol::intern(&s.to_string());
|
||||
ret.insert((sym::sanitize, Some(symbol)));
|
||||
}
|
||||
|
||||
if sess.opts.debug_assertions {
|
||||
ret.insert((Symbol::intern("debug_assertions"), None));
|
||||
}
|
||||
@ -1995,7 +2018,7 @@ impl PpMode {
|
||||
crate mod dep_tracking {
|
||||
use super::{
|
||||
CFGuard, CrateType, DebugInfo, ErrorOutputType, LinkerPluginLto, LtoCli, OptLevel,
|
||||
OutputTypes, Passes, Sanitizer, SourceFileHashAlgorithm, SwitchWithOptPath,
|
||||
OutputTypes, Passes, SanitizerSet, SourceFileHashAlgorithm, SwitchWithOptPath,
|
||||
SymbolManglingVersion,
|
||||
};
|
||||
use crate::lint;
|
||||
@ -2069,8 +2092,7 @@ crate mod dep_tracking {
|
||||
impl_dep_tracking_hash_via_hash!(UnstableFeatures);
|
||||
impl_dep_tracking_hash_via_hash!(OutputTypes);
|
||||
impl_dep_tracking_hash_via_hash!(NativeLibKind);
|
||||
impl_dep_tracking_hash_via_hash!(Sanitizer);
|
||||
impl_dep_tracking_hash_via_hash!(Option<Sanitizer>);
|
||||
impl_dep_tracking_hash_via_hash!(SanitizerSet);
|
||||
impl_dep_tracking_hash_via_hash!(CFGuard);
|
||||
impl_dep_tracking_hash_via_hash!(TargetTriple);
|
||||
impl_dep_tracking_hash_via_hash!(Edition);
|
||||
@ -2085,7 +2107,6 @@ crate mod dep_tracking {
|
||||
impl_dep_tracking_hash_for_sortable_vec_of!((String, lint::Level));
|
||||
impl_dep_tracking_hash_for_sortable_vec_of!((String, Option<String>, NativeLibKind));
|
||||
impl_dep_tracking_hash_for_sortable_vec_of!((String, u64));
|
||||
impl_dep_tracking_hash_for_sortable_vec_of!(Sanitizer);
|
||||
|
||||
impl<T1, T2> DepTrackingHash for (T1, T2)
|
||||
where
|
||||
|
@ -1,6 +1,9 @@
|
||||
#![feature(crate_visibility_modifier)]
|
||||
#![feature(or_patterns)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate bitflags;
|
||||
|
||||
pub mod cgu_reuse_tracker;
|
||||
pub mod utils;
|
||||
#[macro_use]
|
||||
|
@ -248,8 +248,7 @@ macro_rules! options {
|
||||
pub const parse_passes: &str = "a space-separated list of passes, or `all`";
|
||||
pub const parse_panic_strategy: &str = "either `unwind` or `abort`";
|
||||
pub const parse_relro_level: &str = "one of: `full`, `partial`, or `off`";
|
||||
pub const parse_sanitizer: &str = "one of: `address`, `leak`, `memory` or `thread`";
|
||||
pub const parse_sanitizer_list: &str = "comma separated list of sanitizers";
|
||||
pub const parse_sanitizers: &str = "comma separated list of sanitizers: `address`, `leak`, `memory` or `thread`";
|
||||
pub const parse_sanitizer_memory_track_origins: &str = "0, 1, or 2";
|
||||
pub const parse_cfguard: &str = "either `disabled`, `nochecks`, or `checks`";
|
||||
pub const parse_strip: &str = "either `none`, `debuginfo`, or `symbols`";
|
||||
@ -459,24 +458,15 @@ macro_rules! options {
|
||||
true
|
||||
}
|
||||
|
||||
fn parse_sanitizer(slot: &mut Option<Sanitizer>, v: Option<&str>) -> bool {
|
||||
if let Some(Ok(s)) = v.map(str::parse) {
|
||||
*slot = Some(s);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_sanitizer_list(slot: &mut Vec<Sanitizer>, v: Option<&str>) -> bool {
|
||||
fn parse_sanitizers(slot: &mut SanitizerSet, v: Option<&str>) -> bool {
|
||||
if let Some(v) = v {
|
||||
for s in v.split(',').map(str::parse) {
|
||||
if let Ok(s) = s {
|
||||
if !slot.contains(&s) {
|
||||
slot.push(s);
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
for s in v.split(',') {
|
||||
*slot |= match s {
|
||||
"address" => SanitizerSet::ADDRESS,
|
||||
"leak" => SanitizerSet::LEAK,
|
||||
"memory" => SanitizerSet::MEMORY,
|
||||
"thread" => SanitizerSet::THREAD,
|
||||
_ => return false,
|
||||
}
|
||||
}
|
||||
true
|
||||
@ -974,11 +964,11 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
|
||||
// soon.
|
||||
run_dsymutil: bool = (true, parse_bool, [TRACKED],
|
||||
"if on Mac, run `dsymutil` and delete intermediate object files (default: yes)"),
|
||||
sanitizer: Option<Sanitizer> = (None, parse_sanitizer, [TRACKED],
|
||||
sanitizer: SanitizerSet = (SanitizerSet::empty(), parse_sanitizers, [TRACKED],
|
||||
"use a sanitizer"),
|
||||
sanitizer_memory_track_origins: usize = (0, parse_sanitizer_memory_track_origins, [TRACKED],
|
||||
"enable origins tracking in MemorySanitizer"),
|
||||
sanitizer_recover: Vec<Sanitizer> = (vec![], parse_sanitizer_list, [TRACKED],
|
||||
sanitizer_recover: SanitizerSet = (SanitizerSet::empty(), parse_sanitizers, [TRACKED],
|
||||
"enable recovery for selected sanitizers"),
|
||||
saturating_float_casts: Option<bool> = (None, parse_opt_bool, [TRACKED],
|
||||
"make float->int casts UB-free: numbers outside the integer type's range are clipped to \
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::cgu_reuse_tracker::CguReuseTracker;
|
||||
use crate::code_stats::CodeStats;
|
||||
pub use crate::code_stats::{DataTypeKind, FieldInfo, SizeKind, VariantInfo};
|
||||
use crate::config::{self, CrateType, OutputType, PrintRequest, Sanitizer, SwitchWithOptPath};
|
||||
use crate::config::{self, CrateType, OutputType, PrintRequest, SanitizerSet, SwitchWithOptPath};
|
||||
use crate::filesearch;
|
||||
use crate::lint;
|
||||
use crate::parse::ParseSess;
|
||||
@ -650,14 +650,9 @@ impl Session {
|
||||
}
|
||||
pub fn fewer_names(&self) -> bool {
|
||||
let more_names = self.opts.output_types.contains_key(&OutputType::LlvmAssembly)
|
||||
|| self.opts.output_types.contains_key(&OutputType::Bitcode);
|
||||
|
||||
// Address sanitizer and memory sanitizer use alloca name when reporting an issue.
|
||||
let more_names = match self.opts.debugging_opts.sanitizer {
|
||||
Some(Sanitizer::Address) => true,
|
||||
Some(Sanitizer::Memory) => true,
|
||||
_ => more_names,
|
||||
};
|
||||
|| self.opts.output_types.contains_key(&OutputType::Bitcode)
|
||||
// AddressSanitizer and MemorySanitizer use alloca name when reporting an issue.
|
||||
|| self.opts.debugging_opts.sanitizer.intersects(SanitizerSet::ADDRESS | SanitizerSet::MEMORY);
|
||||
|
||||
self.opts.debugging_opts.fewer_names || !more_names
|
||||
}
|
||||
@ -1020,12 +1015,10 @@ impl Session {
|
||||
|
||||
/// Checks if LLVM lifetime markers should be emitted.
|
||||
pub fn emit_lifetime_markers(&self) -> bool {
|
||||
match self.opts.debugging_opts.sanitizer {
|
||||
// AddressSanitizer uses lifetimes to detect use after scope bugs.
|
||||
// MemorySanitizer uses lifetimes to detect use of uninitialized stack variables.
|
||||
Some(Sanitizer::Address | Sanitizer::Memory) => true,
|
||||
_ => self.opts.optimize != config::OptLevel::No,
|
||||
}
|
||||
self.opts.optimize != config::OptLevel::No
|
||||
// AddressSanitizer uses lifetimes to detect use after scope bugs.
|
||||
// MemorySanitizer uses lifetimes to detect use of uninitialized stack variables.
|
||||
|| self.opts.debugging_opts.sanitizer.intersects(SanitizerSet::ADDRESS | SanitizerSet::MEMORY)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1356,34 +1349,37 @@ fn validate_commandline_args_with_session_available(sess: &Session) {
|
||||
);
|
||||
}
|
||||
|
||||
const ASAN_SUPPORTED_TARGETS: &[&str] =
|
||||
&["aarch64-fuchsia", "x86_64-apple-darwin", "x86_64-fuchsia", "x86_64-unknown-linux-gnu"];
|
||||
const LSAN_SUPPORTED_TARGETS: &[&str] = &["x86_64-apple-darwin", "x86_64-unknown-linux-gnu"];
|
||||
const MSAN_SUPPORTED_TARGETS: &[&str] = &["x86_64-unknown-linux-gnu"];
|
||||
const TSAN_SUPPORTED_TARGETS: &[&str] = &["x86_64-apple-darwin", "x86_64-unknown-linux-gnu"];
|
||||
|
||||
// Sanitizers can only be used on some tested platforms.
|
||||
if let Some(ref sanitizer) = sess.opts.debugging_opts.sanitizer {
|
||||
const ASAN_SUPPORTED_TARGETS: &[&str] = &[
|
||||
"x86_64-unknown-linux-gnu",
|
||||
"x86_64-apple-darwin",
|
||||
"x86_64-fuchsia",
|
||||
"aarch64-fuchsia",
|
||||
];
|
||||
const TSAN_SUPPORTED_TARGETS: &[&str] =
|
||||
&["x86_64-unknown-linux-gnu", "x86_64-apple-darwin"];
|
||||
const LSAN_SUPPORTED_TARGETS: &[&str] =
|
||||
&["x86_64-unknown-linux-gnu", "x86_64-apple-darwin"];
|
||||
const MSAN_SUPPORTED_TARGETS: &[&str] = &["x86_64-unknown-linux-gnu"];
|
||||
|
||||
let supported_targets = match *sanitizer {
|
||||
Sanitizer::Address => ASAN_SUPPORTED_TARGETS,
|
||||
Sanitizer::Thread => TSAN_SUPPORTED_TARGETS,
|
||||
Sanitizer::Leak => LSAN_SUPPORTED_TARGETS,
|
||||
Sanitizer::Memory => MSAN_SUPPORTED_TARGETS,
|
||||
for s in sess.opts.debugging_opts.sanitizer {
|
||||
let supported_targets = match s {
|
||||
SanitizerSet::ADDRESS => ASAN_SUPPORTED_TARGETS,
|
||||
SanitizerSet::LEAK => LSAN_SUPPORTED_TARGETS,
|
||||
SanitizerSet::MEMORY => MSAN_SUPPORTED_TARGETS,
|
||||
SanitizerSet::THREAD => TSAN_SUPPORTED_TARGETS,
|
||||
_ => panic!("unrecognized sanitizer {}", s),
|
||||
};
|
||||
|
||||
if !supported_targets.contains(&&*sess.opts.target_triple.triple()) {
|
||||
sess.err(&format!(
|
||||
"{:?}Sanitizer only works with the `{}` target",
|
||||
sanitizer,
|
||||
supported_targets.join("` or `")
|
||||
"`-Zsanitizer={}` only works with targets: {}",
|
||||
s,
|
||||
supported_targets.join(", ")
|
||||
));
|
||||
}
|
||||
let conflicting = sess.opts.debugging_opts.sanitizer - s;
|
||||
if !conflicting.is_empty() {
|
||||
sess.err(&format!(
|
||||
"`-Zsanitizer={}` is incompatible with `-Zsanitizer={}`",
|
||||
s, conflicting,
|
||||
));
|
||||
// Don't report additional errors.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -40,6 +40,7 @@ use rustc_middle::ty::util::Discr;
|
||||
use rustc_middle::ty::util::IntTypeExt;
|
||||
use rustc_middle::ty::{self, AdtKind, Const, ToPolyTraitRef, Ty, TyCtxt};
|
||||
use rustc_middle::ty::{ReprOptions, ToPredicate, WithConstness};
|
||||
use rustc_session::config::SanitizerSet;
|
||||
use rustc_session::lint;
|
||||
use rustc_session::parse::feature_err;
|
||||
use rustc_span::symbol::{kw, sym, Ident, Symbol};
|
||||
@ -2450,11 +2451,11 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs {
|
||||
if let Some(list) = attr.meta_item_list() {
|
||||
for item in list.iter() {
|
||||
if item.check_name(sym::address) {
|
||||
codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_SANITIZE_ADDRESS;
|
||||
codegen_fn_attrs.no_sanitize |= SanitizerSet::ADDRESS;
|
||||
} else if item.check_name(sym::memory) {
|
||||
codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_SANITIZE_MEMORY;
|
||||
codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMORY;
|
||||
} else if item.check_name(sym::thread) {
|
||||
codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_SANITIZE_THREAD;
|
||||
codegen_fn_attrs.no_sanitize |= SanitizerSet::THREAD;
|
||||
} else {
|
||||
tcx.sess
|
||||
.struct_span_err(item.span(), "invalid argument for `no_sanitize`")
|
||||
@ -2554,7 +2555,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs {
|
||||
}
|
||||
}
|
||||
|
||||
if codegen_fn_attrs.flags.intersects(CodegenFnAttrFlags::NO_SANITIZE_ANY) {
|
||||
if !codegen_fn_attrs.no_sanitize.is_empty() {
|
||||
if codegen_fn_attrs.inline == InlineAttr::Always {
|
||||
if let (Some(no_sanitize_span), Some(inline_span)) = (no_sanitize_span, inline_span) {
|
||||
let hir_id = tcx.hir().as_local_hir_id(id.expect_local());
|
||||
|
@ -717,11 +717,12 @@ enum class LLVMRustOptStage {
|
||||
};
|
||||
|
||||
struct LLVMRustSanitizerOptions {
|
||||
bool SanitizeMemory;
|
||||
bool SanitizeThread;
|
||||
bool SanitizeAddress;
|
||||
bool SanitizeRecover;
|
||||
int SanitizeMemoryTrackOrigins;
|
||||
bool SanitizeAddressRecover;
|
||||
bool SanitizeMemory;
|
||||
bool SanitizeMemoryRecover;
|
||||
int SanitizeMemoryTrackOrigins;
|
||||
bool SanitizeThread;
|
||||
};
|
||||
|
||||
extern "C" void
|
||||
@ -808,7 +809,7 @@ LLVMRustOptimizeWithNewPassManager(
|
||||
if (SanitizerOptions->SanitizeMemory) {
|
||||
MemorySanitizerOptions Options(
|
||||
SanitizerOptions->SanitizeMemoryTrackOrigins,
|
||||
SanitizerOptions->SanitizeRecover,
|
||||
SanitizerOptions->SanitizeMemoryRecover,
|
||||
/*CompileKernel=*/false);
|
||||
#if LLVM_VERSION_GE(10, 0)
|
||||
PipelineStartEPCallbacks.push_back([Options](ModulePassManager &MPM) {
|
||||
@ -842,14 +843,14 @@ LLVMRustOptimizeWithNewPassManager(
|
||||
OptimizerLastEPCallbacks.push_back(
|
||||
[SanitizerOptions](FunctionPassManager &FPM, PassBuilder::OptimizationLevel Level) {
|
||||
FPM.addPass(AddressSanitizerPass(
|
||||
/*CompileKernel=*/false, SanitizerOptions->SanitizeRecover,
|
||||
/*CompileKernel=*/false, SanitizerOptions->SanitizeAddressRecover,
|
||||
/*UseAfterScope=*/true));
|
||||
}
|
||||
);
|
||||
PipelineStartEPCallbacks.push_back(
|
||||
[SanitizerOptions](ModulePassManager &MPM) {
|
||||
MPM.addPass(ModuleAddressSanitizerPass(
|
||||
/*CompileKernel=*/false, SanitizerOptions->SanitizeRecover));
|
||||
/*CompileKernel=*/false, SanitizerOptions->SanitizeAddressRecover));
|
||||
}
|
||||
);
|
||||
}
|
||||
|
6
src/test/ui/sanitize/incompatible.rs
Normal file
6
src/test/ui/sanitize/incompatible.rs
Normal file
@ -0,0 +1,6 @@
|
||||
// compile-flags: -Z sanitizer=address -Z sanitizer=memory --target x86_64-unknown-linux-gnu
|
||||
// error-pattern: error: `-Zsanitizer=address` is incompatible with `-Zsanitizer=memory`
|
||||
|
||||
#![feature(no_core)]
|
||||
#![no_core]
|
||||
#![no_main]
|
4
src/test/ui/sanitize/incompatible.stderr
Normal file
4
src/test/ui/sanitize/incompatible.stderr
Normal file
@ -0,0 +1,4 @@
|
||||
error: `-Zsanitizer=address` is incompatible with `-Zsanitizer=memory`
|
||||
|
||||
error: aborting due to previous error
|
||||
|
@ -1,6 +1,5 @@
|
||||
// ignore-tidy-linelength
|
||||
// compile-flags: -Z sanitizer=leak --target i686-unknown-linux-gnu
|
||||
// error-pattern: error: LeakSanitizer only works with the `x86_64-unknown-linux-gnu` or `x86_64-apple-darwin` target
|
||||
// error-pattern: error: `-Zsanitizer=leak` only works with targets:
|
||||
|
||||
#![feature(no_core)]
|
||||
#![no_core]
|
||||
|
@ -1,4 +1,4 @@
|
||||
error: LeakSanitizer only works with the `x86_64-unknown-linux-gnu` or `x86_64-apple-darwin` target
|
||||
error: `-Zsanitizer=leak` only works with targets: x86_64-apple-darwin, x86_64-unknown-linux-gnu
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user