diff --git a/Cargo.lock b/Cargo.lock index 2f4d5f7f786ed..9c6d6c22de4dd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4101,6 +4101,7 @@ dependencies = [ name = "rustc_interface" version = "0.0.0" dependencies = [ + "rand 0.9.2", "rustc_abi", "rustc_ast", "rustc_ast_lowering", @@ -4602,7 +4603,6 @@ version = "0.0.0" dependencies = [ "getopts", "libc", - "rand 0.9.2", "rustc_abi", "rustc_ast", "rustc_data_structures", diff --git a/compiler/rustc_codegen_cranelift/src/driver/aot.rs b/compiler/rustc_codegen_cranelift/src/driver/aot.rs index bea254b7b3a6f..a9acd0ae34ca2 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/aot.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/aot.rs @@ -150,7 +150,6 @@ fn make_module(sess: &Session, name: String) -> UnwindModule { fn emit_cgu( output_filenames: &OutputFilenames, - invocation_temp: Option<&str>, prof: &SelfProfilerRef, name: String, module: UnwindModule, @@ -166,7 +165,6 @@ fn emit_cgu( let module_regular = emit_module( output_filenames, - invocation_temp, prof, product.object, ModuleKind::Regular, @@ -192,7 +190,6 @@ fn emit_cgu( fn emit_module( output_filenames: &OutputFilenames, - invocation_temp: Option<&str>, prof: &SelfProfilerRef, mut object: cranelift_object::object::write::Object<'_>, kind: ModuleKind, @@ -211,7 +208,7 @@ fn emit_module( object.set_section_data(comment_section, producer, 1); } - let tmp_file = output_filenames.temp_path_for_cgu(OutputType::Object, &name, invocation_temp); + let tmp_file = output_filenames.temp_path_for_cgu(OutputType::Object, &name); let file = match File::create(&tmp_file) { Ok(file) => file, Err(err) => return Err(format!("error creating object file: {}", err)), @@ -251,11 +248,8 @@ fn reuse_workproduct_for_cgu( cgu: &CodegenUnit<'_>, ) -> Result { let work_product = cgu.previous_work_product(tcx); - let obj_out_regular = tcx.output_filenames(()).temp_path_for_cgu( - OutputType::Object, - cgu.name().as_str(), - tcx.sess.invocation_temp.as_deref(), - ); + let obj_out_regular = + tcx.output_filenames(()).temp_path_for_cgu(OutputType::Object, cgu.name().as_str()); let source_file_regular = rustc_incremental::in_incr_comp_dir_sess( tcx.sess, work_product.saved_files.get("o").expect("no saved object file in work product"), @@ -394,7 +388,6 @@ fn module_codegen( let producer = crate::debuginfo::producer(tcx.sess); let profiler = tcx.prof.clone(); - let invocation_temp = tcx.sess.invocation_temp.clone(); let output_filenames = tcx.output_filenames(()).clone(); let should_write_ir = crate::pretty_clif::should_write_ir(tcx.sess); @@ -421,19 +414,13 @@ fn module_codegen( let global_asm_object_file = profiler.generic_activity_with_arg("compile assembly", &*cgu_name).run(|| { - crate::global_asm::compile_global_asm( - &global_asm_config, - &cgu_name, - global_asm, - invocation_temp.as_deref(), - ) + crate::global_asm::compile_global_asm(&global_asm_config, &cgu_name, global_asm) })?; let codegen_result = profiler.generic_activity_with_arg("write object file", &*cgu_name).run(|| { emit_cgu( &global_asm_config.output_filenames, - invocation_temp.as_deref(), &profiler, cgu_name, module, @@ -456,7 +443,6 @@ fn emit_allocator_module(tcx: TyCtxt<'_>) -> Option { match emit_module( tcx.output_filenames(()), - tcx.sess.invocation_temp.as_deref(), &tcx.sess.prof, product.object, ModuleKind::Allocator, diff --git a/compiler/rustc_codegen_cranelift/src/driver/jit.rs b/compiler/rustc_codegen_cranelift/src/driver/jit.rs index 3903e6ea3b1da..33b88d70d6f8d 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/jit.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/jit.rs @@ -33,14 +33,15 @@ fn create_jit_module( (jit_module, cx) } -pub(crate) fn run_jit(tcx: TyCtxt<'_>, crate_info: &CrateInfo, jit_args: Vec) -> ! { +pub(crate) fn run_jit(tcx: TyCtxt<'_>, target_cpu: String, jit_args: Vec) -> ! { if !tcx.crate_types().contains(&rustc_session::config::CrateType::Executable) { tcx.dcx().fatal("can't jit non-executable crate"); } let output_filenames = tcx.output_filenames(()); + let crate_info = CrateInfo::new(tcx, target_cpu); let should_write_ir = crate::pretty_clif::should_write_ir(tcx.sess); - let (mut jit_module, mut debug_context) = create_jit_module(tcx, crate_info); + let (mut jit_module, mut debug_context) = create_jit_module(tcx, &crate_info); let mut cached_context = Context::new(); let cgus = tcx.collect_and_partition_mono_items(()).codegen_units; diff --git a/compiler/rustc_codegen_cranelift/src/global_asm.rs b/compiler/rustc_codegen_cranelift/src/global_asm.rs index 5765601763e44..0c5f4136a32d6 100644 --- a/compiler/rustc_codegen_cranelift/src/global_asm.rs +++ b/compiler/rustc_codegen_cranelift/src/global_asm.rs @@ -185,7 +185,6 @@ pub(crate) fn compile_global_asm( config: &GlobalAsmConfig, cgu_name: &str, global_asm: String, - invocation_temp: Option<&str>, ) -> Result, String> { if global_asm.is_empty() { return Ok(None); @@ -200,7 +199,7 @@ pub(crate) fn compile_global_asm( global_asm.push('\n'); let global_asm_object_file = add_file_stem_postfix( - config.output_filenames.temp_path_for_cgu(OutputType::Object, cgu_name, invocation_temp), + config.output_filenames.temp_path_for_cgu(OutputType::Object, cgu_name), ".asm", ); diff --git a/compiler/rustc_codegen_cranelift/src/lib.rs b/compiler/rustc_codegen_cranelift/src/lib.rs index cbbb0ccbbc215..361f143d99913 100644 --- a/compiler/rustc_codegen_cranelift/src/lib.rs +++ b/compiler/rustc_codegen_cranelift/src/lib.rs @@ -209,12 +209,12 @@ impl CodegenBackend for CraneliftCodegenBackend { .to_owned() } - fn codegen_crate(&self, tcx: TyCtxt<'_>, _crate_info: &CrateInfo) -> Box { + fn codegen_crate(&self, tcx: TyCtxt<'_>) -> Box { info!("codegen crate {}", tcx.crate_name(LOCAL_CRATE)); let config = self.config.get().unwrap(); if config.jit_mode { #[cfg(feature = "jit")] - driver::jit::run_jit(tcx, _crate_info, config.jit_args.clone()); + driver::jit::run_jit(tcx, self.target_cpu(tcx.sess), config.jit_args.clone()); #[cfg(not(feature = "jit"))] tcx.dcx().fatal("jit support was disabled when compiling rustc_codegen_cranelift"); @@ -228,6 +228,7 @@ impl CodegenBackend for CraneliftCodegenBackend { ongoing_codegen: Box, sess: &Session, outputs: &OutputFilenames, + _crate_info: &CrateInfo, ) -> (CompiledModules, FxIndexMap) { ongoing_codegen.downcast::().unwrap().join(sess, outputs) } diff --git a/compiler/rustc_codegen_gcc/src/back/write.rs b/compiler/rustc_codegen_gcc/src/back/write.rs index 64674423de2c6..8fd38a2efd600 100644 --- a/compiler/rustc_codegen_gcc/src/back/write.rs +++ b/compiler/rustc_codegen_gcc/src/back/write.rs @@ -29,16 +29,8 @@ pub(crate) fn codegen( let lto_mode = module.module_llvm.lto_mode; let lto_supported = module.module_llvm.lto_supported; - let bc_out = cgcx.output_filenames.temp_path_for_cgu( - OutputType::Bitcode, - &module.name, - cgcx.invocation_temp.as_deref(), - ); - let obj_out = cgcx.output_filenames.temp_path_for_cgu( - OutputType::Object, - &module.name, - cgcx.invocation_temp.as_deref(), - ); + let bc_out = cgcx.output_filenames.temp_path_for_cgu(OutputType::Bitcode, &module.name); + let obj_out = cgcx.output_filenames.temp_path_for_cgu(OutputType::Object, &module.name); if config.bitcode_needed() { let _timer = @@ -82,22 +74,15 @@ pub(crate) fn codegen( } if config.emit_ir { - let out = cgcx.output_filenames.temp_path_for_cgu( - OutputType::LlvmAssembly, - &module.name, - cgcx.invocation_temp.as_deref(), - ); + let out = + cgcx.output_filenames.temp_path_for_cgu(OutputType::LlvmAssembly, &module.name); std::fs::write(out, "").expect("write file"); } if config.emit_asm { let _timer = prof.generic_activity_with_arg("GCC_module_codegen_emit_asm", &*module.name); - let path = cgcx.output_filenames.temp_path_for_cgu( - OutputType::Assembly, - &module.name, - cgcx.invocation_temp.as_deref(), - ); + let path = cgcx.output_filenames.temp_path_for_cgu(OutputType::Assembly, &module.name); context.compile_to_file(OutputKind::Assembler, path.to_str().expect("path to str")); } @@ -215,7 +200,6 @@ pub(crate) fn codegen( config.emit_asm, config.emit_ir, &cgcx.output_filenames, - cgcx.invocation_temp.as_deref(), ) } diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index 4be25b3fb0934..6ca2ef88ef291 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -291,8 +291,8 @@ impl CodegenBackend for GccCodegenBackend { target_cpu(sess).to_owned() } - fn codegen_crate(&self, tcx: TyCtxt<'_>, crate_info: &CrateInfo) -> Box { - Box::new(codegen_crate(self.clone(), tcx, crate_info)) + fn codegen_crate(&self, tcx: TyCtxt<'_>) -> Box { + Box::new(codegen_crate(self.clone(), tcx)) } fn join_codegen( @@ -300,11 +300,12 @@ impl CodegenBackend for GccCodegenBackend { ongoing_codegen: Box, sess: &Session, _outputs: &OutputFilenames, + crate_info: &CrateInfo, ) -> (CompiledModules, FxIndexMap) { ongoing_codegen .downcast::>() .expect("Expected GccCodegenBackend's OngoingCodegen, found Box") - .join(sess) + .join(sess, crate_info) } fn target_config(&self, sess: &Session) -> TargetConfig { diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 515b571a9f4b3..417f321a6b4a7 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -117,17 +117,13 @@ pub(crate) fn create_target_machine(tcx: TyCtxt<'_>, mod_name: &str) -> OwnedTar tcx.sess.split_debuginfo(), tcx.sess.opts.unstable_opts.split_dwarf_kind, mod_name, - tcx.sess.invocation_temp.as_deref(), ) } else { None }; - let output_obj_file = Some(tcx.output_filenames(()).temp_path_for_cgu( - OutputType::Object, - mod_name, - tcx.sess.invocation_temp.as_deref(), - )); + let output_obj_file = + Some(tcx.output_filenames(()).temp_path_for_cgu(OutputType::Object, mod_name)); let config = TargetMachineFactoryConfig { split_dwarf_file, output_obj_file }; target_machine_factory( @@ -322,11 +318,7 @@ pub(crate) fn save_temp_bitcode( return; } let ext = format!("{name}.bc"); - let path = cgcx.output_filenames.temp_path_ext_for_cgu( - &ext, - &module.name, - cgcx.invocation_temp.as_deref(), - ); + let path = cgcx.output_filenames.temp_path_ext_for_cgu(&ext, &module.name); write_bitcode_to_file(&module.module_llvm, &path) } @@ -949,11 +941,8 @@ pub(crate) fn optimize( if let Some(thin_lto_buffer) = thin_lto_buffer { let thin_lto_buffer = thin_lto_buffer.unwrap(); module.thin_lto_buffer = Some(thin_lto_buffer.data().to_vec()); - let bc_summary_out = cgcx.output_filenames.temp_path_for_cgu( - OutputType::ThinLinkBitcode, - &module.name, - cgcx.invocation_temp.as_deref(), - ); + let bc_summary_out = + cgcx.output_filenames.temp_path_for_cgu(OutputType::ThinLinkBitcode, &module.name); if let Some(thin_lto_summary_buffer) = thin_lto_summary_buffer && let Some(thin_link_bitcode_filename) = bc_summary_out.file_name() { @@ -1008,16 +997,8 @@ pub(crate) fn codegen( // copy it to the .o file, and delete the bitcode if it wasn't // otherwise requested. - let bc_out = cgcx.output_filenames.temp_path_for_cgu( - OutputType::Bitcode, - &module.name, - cgcx.invocation_temp.as_deref(), - ); - let obj_out = cgcx.output_filenames.temp_path_for_cgu( - OutputType::Object, - &module.name, - cgcx.invocation_temp.as_deref(), - ); + let bc_out = cgcx.output_filenames.temp_path_for_cgu(OutputType::Bitcode, &module.name); + let obj_out = cgcx.output_filenames.temp_path_for_cgu(OutputType::Object, &module.name); if config.bitcode_needed() { if config.emit_bc || config.emit_obj == EmitObj::Bitcode { @@ -1055,11 +1036,8 @@ pub(crate) fn codegen( if config.emit_ir { let _timer = prof.generic_activity_with_arg("LLVM_module_codegen_emit_ir", &*module.name); - let out = cgcx.output_filenames.temp_path_for_cgu( - OutputType::LlvmAssembly, - &module.name, - cgcx.invocation_temp.as_deref(), - ); + let out = + cgcx.output_filenames.temp_path_for_cgu(OutputType::LlvmAssembly, &module.name); let out_c = path_to_c_string(&out); extern "C" fn demangle_callback( @@ -1103,11 +1081,7 @@ pub(crate) fn codegen( if config.emit_asm { let _timer = prof.generic_activity_with_arg("LLVM_module_codegen_emit_asm", &*module.name); - let path = cgcx.output_filenames.temp_path_for_cgu( - OutputType::Assembly, - &module.name, - cgcx.invocation_temp.as_deref(), - ); + let path = cgcx.output_filenames.temp_path_for_cgu(OutputType::Assembly, &module.name); // We can't use the same module for asm and object code output, // because that triggers various errors like invalid IR or broken @@ -1136,9 +1110,7 @@ pub(crate) fn codegen( let _timer = prof.generic_activity_with_arg("LLVM_module_codegen_emit_obj", &*module.name); - let dwo_out = cgcx - .output_filenames - .temp_path_dwo_for_cgu(&module.name, cgcx.invocation_temp.as_deref()); + let dwo_out = cgcx.output_filenames.temp_path_dwo_for_cgu(&module.name); let dwo_out = match (cgcx.split_debuginfo, cgcx.split_dwarf_kind) { // Don't change how DWARF is emitted when disabled. (SplitDebuginfo::Off, _) => None, @@ -1203,7 +1175,6 @@ pub(crate) fn codegen( config.emit_asm, config.emit_ir, &cgcx.output_filenames, - cgcx.invocation_temp.as_deref(), ) } diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index 9a9e8287e787f..19dbdc6946b79 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -903,7 +903,6 @@ pub(crate) fn build_compile_unit_di_node<'ll, 'tcx>( tcx.sess.split_debuginfo(), tcx.sess.opts.unstable_opts.split_dwarf_kind, codegen_unit_name, - tcx.sess.invocation_temp.as_deref(), ) { // We get a path relative to the working directory from split_dwarf_path Some(tcx.sess.source_map().path_mapping().to_real_filename(work_dir, f)) diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 575e37d0b171d..b273047b98fc6 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -333,8 +333,8 @@ impl CodegenBackend for LlvmCodegenBackend { crate::llvm_util::target_cpu(sess).to_string() } - fn codegen_crate<'tcx>(&self, tcx: TyCtxt<'tcx>, crate_info: &CrateInfo) -> Box { - Box::new(rustc_codegen_ssa::base::codegen_crate(LlvmCodegenBackend(()), tcx, crate_info)) + fn codegen_crate<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Box { + Box::new(rustc_codegen_ssa::base::codegen_crate(LlvmCodegenBackend(()), tcx)) } fn join_codegen( @@ -342,11 +342,12 @@ impl CodegenBackend for LlvmCodegenBackend { ongoing_codegen: Box, sess: &Session, outputs: &OutputFilenames, + crate_info: &CrateInfo, ) -> (CompiledModules, FxIndexMap) { let (compiled_modules, work_products) = ongoing_codegen .downcast::>() .expect("Expected LlvmCodegenBackend's OngoingCodegen, found Box") - .join(sess); + .join(sess, crate_info); if sess.opts.unstable_opts.llvm_time_trace { sess.time("llvm_dump_timing_file", || { diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index d8e2b54ad50a8..9368bc6e1ba72 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -112,12 +112,7 @@ pub fn link_binary( let path = MaybeTempDir::new(tmpdir, sess.opts.cg.save_temps); let crate_name = format!("{}", crate_info.local_crate_name); - let out_filename = output.file_for_writing( - outputs, - OutputType::Exe, - &crate_name, - sess.invocation_temp.as_deref(), - ); + let out_filename = output.file_for_writing(outputs, OutputType::Exe, &crate_name); match crate_type { CrateType::Rlib => { let _timer = sess.timer("link_rlib"); diff --git a/compiler/rustc_codegen_ssa/src/back/lto.rs b/compiler/rustc_codegen_ssa/src/back/lto.rs index ddfcd8a85f6b8..ed36fae3cdc4d 100644 --- a/compiler/rustc_codegen_ssa/src/back/lto.rs +++ b/compiler/rustc_codegen_ssa/src/back/lto.rs @@ -85,7 +85,7 @@ fn crate_type_allows_lto(crate_type: CrateType) -> bool { } } -pub(super) fn exported_symbols_for_lto( +pub(crate) fn exported_symbols_for_lto( tcx: TyCtxt<'_>, each_linked_rlib_for_lto: &[CrateNum], ) -> Vec { diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index c48e8a58b6964..7b22ac231df1c 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -34,7 +34,7 @@ use rustc_span::{FileName, InnerSpan, Span, SpanData}; use rustc_target::spec::{MergeFunctions, SanitizerSet}; use tracing::debug; -use crate::back::link::{self, ensure_removed}; +use crate::back::link::ensure_removed; use crate::back::lto::{self, SerializedModule, check_lto_allowed}; use crate::errors::ErrorCreatingRemarkDir; use crate::traits::*; @@ -136,7 +136,8 @@ impl ModuleConfig { let emit_obj = if !should_emit_obj { EmitObj::None } else if sess.target.obj_is_bitcode - || (sess.opts.cg.linker_plugin_lto.enabled() && !no_builtins) + || (sess.opts.cg.linker_plugin_lto.enabled() + && (!no_builtins || tcx.sess.is_sanitizer_cfi_enabled())) { // This case is selected if the target uses objects as bitcode, or // if linker plugin LTO is enabled. In the linker plugin LTO case @@ -144,14 +145,23 @@ impl ModuleConfig { // and convert it to object code. This may be done by either the // native linker or rustc itself. // - // Note, however, that the linker-plugin-lto requested here is - // explicitly ignored for `#![no_builtins]` crates. These crates are - // specifically ignored by rustc's LTO passes and wouldn't work if - // loaded into the linker. These crates define symbols that LLVM - // lowers intrinsics to, and these symbol dependencies aren't known - // until after codegen. As a result any crate marked - // `#![no_builtins]` is assumed to not participate in LTO and - // instead goes on to generate object code. + // By default this branch is skipped for `#![no_builtins]` crates so + // they emit native object files (machine code), not LLVM bitcode + // objects for the linker (see rust-lang/rust#146133). + // + // However, when LLVM CFI is enabled (`-Zsanitizer=cfi`), this + // breaks LLVM's expected pipeline: LLVM emits `llvm.type.test` + // intrinsics and related metadata that must be lowered by LLVM's + // `LowerTypeTests` pass before instruction selection during + // link-time LTO. Otherwise, `llvm.type.test` intrinsics and related + // metadata are not lowered by LLVM's `LowerTypeTests` pass before + // reaching the target backend, and LLVM may abort during codegen + // (for example in SelectionDAG type legalization) (see + // rust-lang/rust#142284). + // + // Therefore, with `-Clinker-plugin-lto` and `-Zsanitizer=cfi`, a + // `#![no_builtins]` crate must still use rustc's `EmitObj::Bitcode` + // path (and emit LLVM bitcode in the `.o` for linker-based LTO). EmitObj::Bitcode } else if need_bitcode_in_object(tcx) || sess.target.requires_lto { EmitObj::ObjectCode(BitcodeSection::Full) @@ -285,17 +295,13 @@ impl TargetMachineFactoryConfig { cgcx.split_debuginfo, cgcx.split_dwarf_kind, module_name, - cgcx.invocation_temp.as_deref(), ) } else { None }; - let output_obj_file = Some(cgcx.output_filenames.temp_path_for_cgu( - OutputType::Object, - module_name, - cgcx.invocation_temp.as_deref(), - )); + let output_obj_file = + Some(cgcx.output_filenames.temp_path_for_cgu(OutputType::Object, module_name)); TargetMachineFactoryConfig { split_dwarf_file, output_obj_file } } } @@ -322,7 +328,6 @@ pub struct CodegenContext { pub time_trace: bool, pub crate_types: Vec, pub output_filenames: Arc, - pub invocation_temp: Option, pub module_config: Arc, pub opt_level: OptLevel, pub backend_features: Vec, @@ -389,18 +394,8 @@ fn generate_thin_lto_work( enum MaybeLtoModules { NoLto(CompiledModules), - FatLto { - cgcx: CodegenContext, - exported_symbols_for_lto: Arc>, - each_linked_rlib_file_for_lto: Vec, - needs_fat_lto: Vec>, - }, - ThinLto { - cgcx: CodegenContext, - exported_symbols_for_lto: Arc>, - each_linked_rlib_file_for_lto: Vec, - needs_thin_lto: Vec>, - }, + FatLto { cgcx: CodegenContext, needs_fat_lto: Vec> }, + ThinLto { cgcx: CodegenContext, needs_thin_lto: Vec> }, } fn need_bitcode_in_object(tcx: TyCtxt<'_>) -> bool { @@ -424,7 +419,6 @@ fn need_pre_lto_bitcode_for_incr_comp(sess: &Session) -> bool { pub(crate) fn start_async_codegen( backend: B, tcx: TyCtxt<'_>, - crate_info: &CrateInfo, allocator_module: Option>, ) -> OngoingCodegen { let (coordinator_send, coordinator_receive) = channel(); @@ -440,7 +434,6 @@ pub(crate) fn start_async_codegen( let coordinator_thread = start_executing_work( backend.clone(), tcx, - crate_info, shared_emitter, codegen_worker_send, coordinator_receive, @@ -529,11 +522,7 @@ pub fn produce_final_output_artifacts( if let [module] = &compiled_modules.modules[..] { // 1) Only one codegen unit. In this case it's no difficulty // to copy `foo.0.x` to `foo.x`. - let path = crate_output.temp_path_for_cgu( - output_type, - &module.name, - sess.invocation_temp.as_deref(), - ); + let path = crate_output.temp_path_for_cgu(output_type, &module.name); let output = crate_output.path(output_type); if !output_type.is_text_output() && output.is_tty() { sess.dcx() @@ -912,12 +901,7 @@ fn execute_copy_from_cache_work_item( module.source.saved_files.get("dwo").as_ref().and_then(|saved_dwarf_object_file| { let dwarf_obj_out = cgcx .output_filenames - .split_dwarf_path( - cgcx.split_debuginfo, - cgcx.split_dwarf_kind, - &module.name, - cgcx.invocation_temp.as_deref(), - ) + .split_dwarf_path(cgcx.split_debuginfo, cgcx.split_dwarf_kind, &module.name) .expect( "saved dwarf object in work product but `split_dwarf_path` returned `None`", ); @@ -927,11 +911,7 @@ fn execute_copy_from_cache_work_item( let mut load_from_incr_cache = |perform, output_type: OutputType| { if perform { let saved_file = module.source.saved_files.get(output_type.extension())?; - let output_path = cgcx.output_filenames.temp_path_for_cgu( - output_type, - &module.name, - cgcx.invocation_temp.as_deref(), - ); + let output_path = cgcx.output_filenames.temp_path_for_cgu(output_type, &module.name); load_from_incr_comp_dir(output_path, &saved_file) } else { None @@ -992,8 +972,8 @@ fn do_thin_lto( prof: &SelfProfilerRef, shared_emitter: SharedEmitter, tm_factory: TargetMachineFactoryFn, - exported_symbols_for_lto: Arc>, - each_linked_rlib_for_lto: Vec, + exported_symbols_for_lto: &[String], + each_linked_rlib_for_lto: &[PathBuf], needs_thin_lto: Vec>, ) -> Vec { let _timer = prof.verbose_generic_activity("LLVM_thinlto"); @@ -1231,7 +1211,6 @@ enum MainThreadState { fn start_executing_work( backend: B, tcx: TyCtxt<'_>, - crate_info: &CrateInfo, shared_emitter: SharedEmitter, codegen_worker_send: Sender, coordinator_receive: Receiver>, @@ -1243,22 +1222,9 @@ fn start_executing_work( let sess = tcx.sess; let prof = sess.prof.clone(); - let mut each_linked_rlib_for_lto = Vec::new(); - let mut each_linked_rlib_file_for_lto = Vec::new(); - if sess.lto() != Lto::No && sess.lto() != Lto::ThinLocal { - drop(link::each_linked_rlib(crate_info, None, &mut |cnum, path| { - if link::ignored_for_lto(sess, crate_info, cnum) { - return; - } - - each_linked_rlib_for_lto.push(cnum); - each_linked_rlib_file_for_lto.push(path.to_path_buf()); - })); - } - - // Compute the set of symbols we need to retain when doing LTO (if we need to) + // Compute the set of symbols we need to retain when doing thin local LTO (if we need to) let exported_symbols_for_lto = - Arc::new(lto::exported_symbols_for_lto(tcx, &each_linked_rlib_for_lto)); + if sess.lto() == Lto::ThinLocal { lto::exported_symbols_for_lto(tcx, &[]) } else { vec![] }; // First up, convert our jobserver into a helper thread so we can use normal // mpsc channels to manage our messages and such. @@ -1313,7 +1279,6 @@ fn start_executing_work( split_dwarf_kind: tcx.sess.opts.unstable_opts.split_dwarf_kind, parallel: backend.supports_parallel() && !sess.opts.unstable_opts.no_parallel_backend, pointer_size: tcx.data_layout.pointer_size(), - invocation_temp: sess.invocation_temp.clone(), }; // This is the "main loop" of parallel work happening for parallel codegen. @@ -1757,12 +1722,7 @@ fn start_executing_work( needs_fat_lto.push(FatLtoInput::Serialized { name: wp.cgu_name, bitcode_path }) } - return Ok(MaybeLtoModules::FatLto { - cgcx, - exported_symbols_for_lto, - each_linked_rlib_file_for_lto, - needs_fat_lto, - }); + return Ok(MaybeLtoModules::FatLto { cgcx, needs_fat_lto }); } else if !needs_thin_lto.is_empty() || !lto_import_only_modules.is_empty() { assert!(compiled_modules.is_empty()); assert!(needs_fat_lto.is_empty()); @@ -1777,8 +1737,8 @@ fn start_executing_work( &prof, shared_emitter.clone(), tm_factory, - exported_symbols_for_lto, - each_linked_rlib_file_for_lto, + &exported_symbols_for_lto, + &[], needs_thin_lto, )); } else { @@ -1790,12 +1750,7 @@ fn start_executing_work( }); } - return Ok(MaybeLtoModules::ThinLto { - cgcx, - exported_symbols_for_lto, - each_linked_rlib_file_for_lto, - needs_thin_lto, - }); + return Ok(MaybeLtoModules::ThinLto { cgcx, needs_thin_lto }); } } @@ -2139,7 +2094,11 @@ pub struct OngoingCodegen { } impl OngoingCodegen { - pub fn join(self, sess: &Session) -> (CompiledModules, FxIndexMap) { + pub fn join( + self, + sess: &Session, + crate_info: &CrateInfo, + ) -> (CompiledModules, FxIndexMap) { self.shared_emitter_main.check(sess, true); let maybe_lto_modules = sess.time("join_worker_thread", || match self.coordinator.join() { @@ -2163,12 +2122,7 @@ impl OngoingCodegen { drop(shared_emitter); compiled_modules } - MaybeLtoModules::FatLto { - cgcx, - exported_symbols_for_lto, - each_linked_rlib_file_for_lto, - needs_fat_lto, - } => { + MaybeLtoModules::FatLto { cgcx, needs_fat_lto } => { let tm_factory = self.backend.target_machine_factory( sess, cgcx.opt_level, @@ -2181,19 +2135,14 @@ impl OngoingCodegen { &cgcx, shared_emitter, tm_factory, - &exported_symbols_for_lto, - &each_linked_rlib_file_for_lto, + &crate_info.exported_symbols_for_lto, + &crate_info.each_linked_rlib_file_for_lto, needs_fat_lto, )], allocator_module: None, } } - MaybeLtoModules::ThinLto { - cgcx, - exported_symbols_for_lto, - each_linked_rlib_file_for_lto, - needs_thin_lto, - } => { + MaybeLtoModules::ThinLto { cgcx, needs_thin_lto } => { let tm_factory = self.backend.target_machine_factory( sess, cgcx.opt_level, @@ -2206,8 +2155,8 @@ impl OngoingCodegen { &sess.prof, shared_emitter, tm_factory, - exported_symbols_for_lto, - each_linked_rlib_file_for_lto, + &crate_info.exported_symbols_for_lto, + &crate_info.each_linked_rlib_file_for_lto, needs_thin_lto, ), allocator_module: None, diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 4e2884c8cb63f..947475f8a0b22 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -686,11 +686,7 @@ pub fn allocator_shim_contents(tcx: TyCtxt<'_>, kind: AllocatorKind) -> Vec( - backend: B, - tcx: TyCtxt<'_>, - crate_info: &CrateInfo, -) -> OngoingCodegen { +pub fn codegen_crate(backend: B, tcx: TyCtxt<'_>) -> OngoingCodegen { if tcx.sess.target.need_explicit_cpu && tcx.sess.opts.cg.target_cpu.is_none() { // The target has no default cpu, but none is set explicitly tcx.dcx().emit_fatal(errors::CpuRequired); @@ -734,7 +730,7 @@ pub fn codegen_crate( None }; - let ongoing_codegen = start_async_codegen(backend.clone(), tcx, crate_info, allocator_module); + let ongoing_codegen = start_async_codegen(backend.clone(), tcx, allocator_module); // For better throughput during parallel processing by LLVM, we used to sort // CGUs largest to smallest. This would lead to better thread utilization @@ -959,6 +955,8 @@ impl CrateInfo { natvis_debugger_visualizers: Default::default(), lint_levels: CodegenLintLevels::from_tcx(tcx), metadata_symbol: exported_symbols::metadata_symbol_name(tcx), + each_linked_rlib_file_for_lto: Default::default(), + exported_symbols_for_lto: Default::default(), }; info.native_libraries.reserve(n_crates); @@ -1044,6 +1042,25 @@ impl CrateInfo { }); } + let mut each_linked_rlib_for_lto = Vec::new(); + let mut each_linked_rlib_file_for_lto = Vec::new(); + if tcx.sess.lto() != config::Lto::No && tcx.sess.lto() != config::Lto::ThinLocal { + drop(crate::back::link::each_linked_rlib(&info, None, &mut |cnum, path| { + if crate::back::link::ignored_for_lto(tcx.sess, &info, cnum) { + return; + } + + each_linked_rlib_for_lto.push(cnum); + each_linked_rlib_file_for_lto.push(path.to_path_buf()); + })); + } + info.each_linked_rlib_file_for_lto = each_linked_rlib_file_for_lto; + + // FIXME move to -Zlink-only half such that each_linked_rlib_file_for_lto can be moved there too + // Compute the set of symbols we need to retain when doing LTO (if we need to) + info.exported_symbols_for_lto = + crate::back::lto::exported_symbols_for_lto(tcx, &each_linked_rlib_for_lto); + let embed_visualizers = tcx.crate_types().iter().any(|&crate_type| match crate_type { CrateType::Executable | CrateType::Dylib | CrateType::Cdylib | CrateType::Sdylib => { // These are crate types for which we invoke the linker and can embed diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index 22290d672c611..d48b54166270e 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -95,19 +95,14 @@ impl ModuleCodegen { emit_asm: bool, emit_ir: bool, outputs: &OutputFilenames, - invocation_temp: Option<&str>, ) -> CompiledModule { - let object = emit_obj - .then(|| outputs.temp_path_for_cgu(OutputType::Object, &self.name, invocation_temp)); - let dwarf_object = - emit_dwarf_obj.then(|| outputs.temp_path_dwo_for_cgu(&self.name, invocation_temp)); - let bytecode = emit_bc - .then(|| outputs.temp_path_for_cgu(OutputType::Bitcode, &self.name, invocation_temp)); - let assembly = emit_asm - .then(|| outputs.temp_path_for_cgu(OutputType::Assembly, &self.name, invocation_temp)); - let llvm_ir = emit_ir.then(|| { - outputs.temp_path_for_cgu(OutputType::LlvmAssembly, &self.name, invocation_temp) - }); + let object = emit_obj.then(|| outputs.temp_path_for_cgu(OutputType::Object, &self.name)); + let dwarf_object = emit_dwarf_obj.then(|| outputs.temp_path_dwo_for_cgu(&self.name)); + let bytecode = emit_bc.then(|| outputs.temp_path_for_cgu(OutputType::Bitcode, &self.name)); + let assembly = + emit_asm.then(|| outputs.temp_path_for_cgu(OutputType::Assembly, &self.name)); + let llvm_ir = + emit_ir.then(|| outputs.temp_path_for_cgu(OutputType::LlvmAssembly, &self.name)); CompiledModule { name: self.name, @@ -230,6 +225,8 @@ pub struct CrateInfo { pub natvis_debugger_visualizers: BTreeSet, pub lint_levels: CodegenLintLevels, pub metadata_symbol: String, + pub each_linked_rlib_file_for_lto: Vec, + pub exported_symbols_for_lto: Vec, } /// Target-specific options that get set in `cfg(...)`. diff --git a/compiler/rustc_codegen_ssa/src/traits/backend.rs b/compiler/rustc_codegen_ssa/src/traits/backend.rs index 7b95562ddda37..9898b67b91f78 100644 --- a/compiler/rustc_codegen_ssa/src/traits/backend.rs +++ b/compiler/rustc_codegen_ssa/src/traits/backend.rs @@ -104,7 +104,7 @@ pub trait CodegenBackend { fn target_cpu(&self, sess: &Session) -> String; - fn codegen_crate<'tcx>(&self, tcx: TyCtxt<'tcx>, crate_info: &CrateInfo) -> Box; + fn codegen_crate<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Box; /// This is called on the returned `Box` from [`codegen_crate`](Self::codegen_crate) /// @@ -116,6 +116,7 @@ pub trait CodegenBackend { ongoing_codegen: Box, sess: &Session, outputs: &OutputFilenames, + crate_info: &CrateInfo, ) -> (CompiledModules, FxIndexMap); fn print_pass_timings(&self) {} diff --git a/compiler/rustc_data_structures/src/stable_hasher.rs b/compiler/rustc_data_structures/src/stable_hasher.rs index 17effa07e3bcb..931317f80110b 100644 --- a/compiler/rustc_data_structures/src/stable_hasher.rs +++ b/compiler/rustc_data_structures/src/stable_hasher.rs @@ -459,22 +459,6 @@ impl StableOrd for String { const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = (); } -impl ToStableHashKey for String { - type KeyType = String; - #[inline] - fn to_stable_hash_key(&self, _: &mut Hcx) -> Self::KeyType { - self.clone() - } -} - -impl ToStableHashKey for (T1, T2) { - type KeyType = (T1::KeyType, T2::KeyType); - #[inline] - fn to_stable_hash_key(&self, hcx: &mut Hcx) -> Self::KeyType { - (self.0.to_stable_hash_key(hcx), self.1.to_stable_hash_key(hcx)) - } -} - impl StableHash for bool { #[inline] fn stable_hash(&self, hcx: &mut Hcx, hasher: &mut StableHasher) { diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs index f268192233c0d..917b685958da4 100644 --- a/compiler/rustc_hir/src/def.rs +++ b/compiler/rustc_hir/src/def.rs @@ -4,7 +4,6 @@ use std::fmt::Debug; use rustc_ast as ast; use rustc_ast::NodeId; -use rustc_data_structures::stable_hasher::ToStableHashKey; use rustc_data_structures::unord::UnordMap; use rustc_error_messages::{DiagArgValue, IntoDiagArg}; use rustc_macros::{Decodable, Encodable, StableHash}; @@ -712,15 +711,6 @@ impl IntoDiagArg for Namespace { } } -impl ToStableHashKey for Namespace { - type KeyType = Namespace; - - #[inline] - fn to_stable_hash_key(&self, _: &mut Hcx) -> Namespace { - *self - } -} - /// Just a helper ‒ separate structure for each namespace. #[derive(Copy, Clone, Default, Debug, StableHash)] pub struct PerNS { diff --git a/compiler/rustc_hir/src/stable_hash_impls.rs b/compiler/rustc_hir/src/stable_hash_impls.rs index 606a47bc4a229..77a9f3617f989 100644 --- a/compiler/rustc_hir/src/stable_hash_impls.rs +++ b/compiler/rustc_hir/src/stable_hash_impls.rs @@ -1,59 +1,7 @@ -use rustc_data_structures::stable_hasher::{ - StableHash, StableHashCtxt, StableHasher, ToStableHashKey, -}; -use rustc_span::def_id::DefPathHash; +use rustc_data_structures::stable_hasher::{StableHash, StableHashCtxt, StableHasher}; use crate::HashIgnoredAttrId; -use crate::hir::{ - AttributeMap, BodyId, ForeignItemId, ImplItemId, ItemId, OwnerNodes, TraitItemId, -}; -use crate::hir_id::ItemLocalId; - -impl ToStableHashKey for BodyId { - type KeyType = (DefPathHash, ItemLocalId); - - #[inline] - fn to_stable_hash_key(&self, hcx: &mut Hcx) -> (DefPathHash, ItemLocalId) { - let BodyId { hir_id } = *self; - hir_id.to_stable_hash_key(hcx) - } -} - -impl ToStableHashKey for ItemId { - type KeyType = DefPathHash; - - #[inline] - fn to_stable_hash_key(&self, hcx: &mut Hcx) -> DefPathHash { - self.owner_id.def_id.to_stable_hash_key(hcx) - } -} - -impl ToStableHashKey for TraitItemId { - type KeyType = DefPathHash; - - #[inline] - fn to_stable_hash_key(&self, hcx: &mut Hcx) -> DefPathHash { - self.owner_id.def_id.to_stable_hash_key(hcx) - } -} - -impl ToStableHashKey for ImplItemId { - type KeyType = DefPathHash; - - #[inline] - fn to_stable_hash_key(&self, hcx: &mut Hcx) -> DefPathHash { - self.owner_id.def_id.to_stable_hash_key(hcx) - } -} - -impl ToStableHashKey for ForeignItemId { - type KeyType = DefPathHash; - - #[inline] - fn to_stable_hash_key(&self, hcx: &mut Hcx) -> DefPathHash { - self.owner_id.def_id.to_stable_hash_key(hcx) - } -} +use crate::hir::{AttributeMap, OwnerNodes}; // The following implementations of StableHash for `ItemId`, `TraitItemId`, and // `ImplItemId` deserve special attention. Normally we do not hash `NodeId`s within diff --git a/compiler/rustc_hir_id/src/lib.rs b/compiler/rustc_hir_id/src/lib.rs index 3bbd46f4844e1..752ba4e8de394 100644 --- a/compiler/rustc_hir_id/src/lib.rs +++ b/compiler/rustc_hir_id/src/lib.rs @@ -176,22 +176,3 @@ pub const CRATE_HIR_ID: HirId = HirId { owner: OwnerId { def_id: CRATE_DEF_ID }, local_id: ItemLocalId::ZERO }; pub const CRATE_OWNER_ID: OwnerId = OwnerId { def_id: CRATE_DEF_ID }; - -impl ToStableHashKey for HirId { - type KeyType = (DefPathHash, ItemLocalId); - - #[inline] - fn to_stable_hash_key(&self, hcx: &mut Hcx) -> (DefPathHash, ItemLocalId) { - let def_path_hash = self.owner.def_id.to_stable_hash_key(hcx); - (def_path_hash, self.local_id) - } -} - -impl ToStableHashKey for ItemLocalId { - type KeyType = ItemLocalId; - - #[inline] - fn to_stable_hash_key(&self, _: &mut Hcx) -> ItemLocalId { - *self - } -} diff --git a/compiler/rustc_interface/Cargo.toml b/compiler/rustc_interface/Cargo.toml index c234e21b92541..9c115736a3d4f 100644 --- a/compiler/rustc_interface/Cargo.toml +++ b/compiler/rustc_interface/Cargo.toml @@ -5,6 +5,7 @@ edition = "2024" [dependencies] # tidy-alphabetical-start +rand = "0.9.0" rustc_ast = { path = "../rustc_ast" } rustc_ast_lowering = { path = "../rustc_ast_lowering" } rustc_ast_passes = { path = "../rustc_ast_passes" } diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index cb41974af41b0..bcd1a52ce9dcd 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -1305,8 +1305,6 @@ pub(crate) fn start_codegen<'tcx>( let metadata = rustc_metadata::fs::encode_and_write_metadata(tcx); - let crate_info = CrateInfo::new(tcx, codegen_backend.target_cpu(tcx.sess)); - let codegen = tcx.sess.time("codegen_crate", || { if tcx.sess.opts.unstable_opts.no_codegen || !tcx.sess.opts.output_types.should_codegen() { // Skip crate items and just output metadata in -Z no-codegen mode. @@ -1315,7 +1313,7 @@ pub(crate) fn start_codegen<'tcx>( // Linker::link will skip join_codegen in case of a CodegenResults Any value. Box::new(CompiledModules { modules: vec![], allocator_module: None }) } else { - codegen_backend.codegen_crate(tcx, &crate_info) + codegen_backend.codegen_crate(tcx) } }); @@ -1327,6 +1325,8 @@ pub(crate) fn start_codegen<'tcx>( tcx.sess.code_stats.print_type_sizes(); } + let crate_info = CrateInfo::new(tcx, codegen_backend.target_cpu(tcx.sess)); + (codegen, crate_info, metadata) } diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs index 63c7332893b81..fd1b5104fc6d3 100644 --- a/compiler/rustc_interface/src/queries.rs +++ b/compiler/rustc_interface/src/queries.rs @@ -53,9 +53,12 @@ impl Linker { // This was a check only build Ok(compiled_modules) => (*compiled_modules, IndexMap::default()), - Err(ongoing_codegen) => { - codegen_backend.join_codegen(ongoing_codegen, sess, &self.output_filenames) - } + Err(ongoing_codegen) => codegen_backend.join_codegen( + ongoing_codegen, + sess, + &self.output_filenames, + &self.crate_info, + ), } }); diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index 24b23cc4199e9..d7d306918fd0d 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -5,6 +5,7 @@ use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, OnceLock}; use std::{env, thread}; +use rand::{RngCore, rng}; use rustc_ast as ast; use rustc_attr_parsing::ShouldEmit; use rustc_codegen_ssa::back::archive::{ArArchiveBuilderBuilder, ArchiveBuilderBuilder}; @@ -12,6 +13,7 @@ use rustc_codegen_ssa::back::link::link_binary; use rustc_codegen_ssa::target_features::cfg_target_feature; use rustc_codegen_ssa::traits::CodegenBackend; use rustc_codegen_ssa::{CompiledModules, CrateInfo, TargetConfig}; +use rustc_data_structures::base_n::{CASE_INSENSITIVE, ToBaseN}; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::jobserver::Proxy; use rustc_data_structures::sync; @@ -406,7 +408,7 @@ impl CodegenBackend for DummyCodegenBackend { String::new() } - fn codegen_crate<'tcx>(&self, _tcx: TyCtxt<'tcx>, _crate_info: &CrateInfo) -> Box { + fn codegen_crate<'tcx>(&self, _tcx: TyCtxt<'tcx>) -> Box { Box::new(CompiledModules { modules: vec![], allocator_module: None }) } @@ -415,6 +417,7 @@ impl CodegenBackend for DummyCodegenBackend { ongoing_codegen: Box, _sess: &Session, _outputs: &OutputFilenames, + _crate_info: &CrateInfo, ) -> (CompiledModules, FxIndexMap) { (*ongoing_codegen.downcast().unwrap(), FxIndexMap::default()) } @@ -615,6 +618,12 @@ pub fn build_output_filenames(attrs: &[ast::Attribute], sess: &Session) -> Outpu parse_crate_name(sess, attrs, ShouldEmit::Nothing).map(|i| i.0.to_string()) }); + let invocation_temp = sess + .opts + .incremental + .as_ref() + .map(|_| rng().next_u32().to_base_fixed_len(CASE_INSENSITIVE).to_string()); + match sess.io.output_file { None => { // "-" as input file will cause the parser to read from stdin so we @@ -631,6 +640,7 @@ pub fn build_output_filenames(attrs: &[ast::Attribute], sess: &Session) -> Outpu stem, None, sess.io.temps_dir.clone(), + invocation_temp, sess.opts.unstable_opts.split_dwarf_out_dir.clone(), sess.opts.cg.extra_filename.clone(), sess.opts.output_types.clone(), @@ -661,6 +671,7 @@ pub fn build_output_filenames(attrs: &[ast::Attribute], sess: &Session) -> Outpu out_filestem, ofile, sess.io.temps_dir.clone(), + invocation_temp, sess.opts.unstable_opts.split_dwarf_out_dir.clone(), sess.opts.cg.extra_filename.clone(), sess.opts.output_types.clone(), diff --git a/compiler/rustc_lint/src/types/improper_ctypes.rs b/compiler/rustc_lint/src/types/improper_ctypes.rs index bb047e9782d44..f45c242800830 100644 --- a/compiler/rustc_lint/src/types/improper_ctypes.rs +++ b/compiler/rustc_lint/src/types/improper_ctypes.rs @@ -138,17 +138,16 @@ declare_lint_pass!(ImproperCTypesLint => [ USES_POWER_ALIGNMENT ]); -/// Getting the (normalized) type out of a field (for, e.g., an enum variant or a tuple). -#[inline] -fn get_type_from_field<'tcx>( +/// A common pattern in this lint is to attempt normalize_erasing_regions, +/// but keep the original type if it were to fail. +/// This may or may not be supported in the logic behind the `Unnormalized` wrapper, +/// (FIXME?) +/// but it should be enough for non-wrapped types to be as normalised as this lint needs them to be. +fn maybe_normalize_erasing_regions<'tcx>( cx: &LateContext<'tcx>, - field: &ty::FieldDef, - args: GenericArgsRef<'tcx>, + value: Unnormalized<'tcx, Ty<'tcx>>, ) -> Ty<'tcx> { - let field_ty = field.ty(cx.tcx, args); - cx.tcx - .try_normalize_erasing_regions(cx.typing_env(), Unnormalized::new_wip(field_ty)) - .unwrap_or(field_ty) + cx.tcx.try_normalize_erasing_regions(cx.typing_env(), value).unwrap_or(value.skip_norm_wip()) } /// Check a variant of a non-exhaustive enum for improper ctypes @@ -257,12 +256,22 @@ fn check_struct_for_power_alignment<'tcx>( } } +/// Annotates whether we are in the context of an item *defined* in rust +/// and exposed to an FFI boundary, +/// or the context of an item from elsewhere, whose interface is re-*declared* in rust. #[derive(Clone, Copy)] enum CItemKind { Declaration, Definition, } +/// Annotates whether we are in the context of a function's argument types or return type. +#[derive(Clone, Copy)] +enum FnPos { + Arg, + Ret, +} + enum FfiResult<'tcx> { FfiSafe, FfiPhantom(Ty<'tcx>), @@ -286,8 +295,10 @@ enum IndirectionKind { } bitflags! { + /// VisitorState flags that are linked with the root type's use. + /// (These are the permanent part of the state, kept when visiting new Ty.) #[derive(Clone, Copy, Debug, PartialEq, Eq)] - struct VisitorState: u8 { + struct RootUseFlags: u8 { /// For use in (externally-linked) static variables. const STATIC = 0b000001; /// For use in functions in general. @@ -302,7 +313,45 @@ bitflags! { } } -impl VisitorState { +/// Description of the relationship between current Ty and +/// the type (or lack thereof) immediately containing it +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +enum OuterTyKind { + None, + /// A variant that should not exist, + /// but is needed because we don't change the lint's behavior yet + NoneThroughFnPtr, + /// Placeholder for properties that will be used eventually + Other, +} + +impl OuterTyKind { + /// Computes the relationship by providing the containing Ty itself + fn from_ty<'tcx>(ty: Ty<'tcx>) -> Self { + match ty.kind() { + ty::FnPtr(..) => Self::NoneThroughFnPtr, + ty::RawPtr(..) + | ty::Ref(..) + | ty::Adt(..) + | ty::Tuple(..) + | ty::Array(..) + | ty::Slice(_) => OuterTyKind::Other, + _ => bug!("Unexpected outer type {ty:?}"), + } + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +struct VisitorState { + /// Flags describing both the overall context in which the current Ty is, + /// linked to how the Visitor's original Ty was used. + root_use_flags: RootUseFlags, + /// Flags describing both the immediate context in which the current Ty is, + /// linked to how it relates to its parent Ty (or lack thereof). + outer_ty_kind: OuterTyKind, +} + +impl RootUseFlags { // The values that can be set. const STATIC_TY: Self = Self::STATIC; const ARGUMENT_TY_IN_DEFINITION: Self = @@ -317,86 +366,85 @@ impl VisitorState { const RETURN_TY_IN_FNPTR: Self = Self::from_bits(Self::FUNC.bits() | Self::THEORETICAL.bits() | Self::FN_RETURN.bits()) .unwrap(); +} - /// Get the proper visitor state for a given function's arguments. - fn argument_from_fnmode(fn_mode: CItemKind) -> Self { - match fn_mode { - CItemKind::Definition => VisitorState::ARGUMENT_TY_IN_DEFINITION, - CItemKind::Declaration => VisitorState::ARGUMENT_TY_IN_DECLARATION, +impl VisitorState { + /// From an existing state, compute the state of any subtype of the current type. + /// (General case. For the case where the current type is a function pointer, see `next_in_fnptr`.) + fn next(&self, current_ty: Ty<'_>) -> Self { + assert!(!matches!(current_ty.kind(), ty::FnPtr(..))); + VisitorState { + root_use_flags: self.root_use_flags, + outer_ty_kind: OuterTyKind::from_ty(current_ty), } } - /// Get the proper visitor state for a given function's return type. - fn return_from_fnmode(fn_mode: CItemKind) -> Self { - match fn_mode { - CItemKind::Definition => VisitorState::RETURN_TY_IN_DEFINITION, - CItemKind::Declaration => VisitorState::RETURN_TY_IN_DECLARATION, + /// From an existing state, compute the state of any subtype of the current type. + /// (Case where the current type is a function pointer, + /// meaning we need to specify if the subtype is an argument or the return.) + fn next_in_fnptr(&self, current_ty: Ty<'_>, fn_pos: FnPos) -> Self { + assert!(matches!(current_ty.kind(), ty::FnPtr(..))); + VisitorState { + root_use_flags: match fn_pos { + FnPos::Ret => RootUseFlags::RETURN_TY_IN_FNPTR, + FnPos::Arg => RootUseFlags::ARGUMENT_TY_IN_FNPTR, + }, + outer_ty_kind: OuterTyKind::from_ty(current_ty), } } + /// Get the proper visitor state for a given function's arguments or return type. + fn fn_entry_point(fn_mode: CItemKind, fn_pos: FnPos) -> Self { + let p_flags = match (fn_mode, fn_pos) { + (CItemKind::Definition, FnPos::Ret) => RootUseFlags::RETURN_TY_IN_DEFINITION, + (CItemKind::Declaration, FnPos::Ret) => RootUseFlags::RETURN_TY_IN_DECLARATION, + (CItemKind::Definition, FnPos::Arg) => RootUseFlags::ARGUMENT_TY_IN_DEFINITION, + (CItemKind::Declaration, FnPos::Arg) => RootUseFlags::ARGUMENT_TY_IN_DECLARATION, + }; + VisitorState { root_use_flags: p_flags, outer_ty_kind: OuterTyKind::None } + } + + /// Get the proper visitor state for a static variable's type + fn static_entry_point() -> Self { + VisitorState { root_use_flags: RootUseFlags::STATIC_TY, outer_ty_kind: OuterTyKind::None } + } + /// Whether the type is used in a function. - fn is_in_function(self) -> bool { - let ret = self.contains(Self::FUNC); + fn is_in_function(&self) -> bool { + let ret = self.root_use_flags.contains(RootUseFlags::FUNC); if ret { - debug_assert!(!self.contains(Self::STATIC)); + debug_assert!(!self.root_use_flags.contains(RootUseFlags::STATIC)); } ret } + /// Whether the type is used (directly or not) in a function, in return position. - fn is_in_function_return(self) -> bool { - let ret = self.contains(Self::FN_RETURN); + fn is_in_function_return(&self) -> bool { + let ret = self.root_use_flags.contains(RootUseFlags::FN_RETURN); if ret { debug_assert!(self.is_in_function()); } ret } + /// Whether the type is used (directly or not) in a defined function. /// In other words, whether or not we allow non-FFI-safe types behind a C pointer, /// to be treated as an opaque type on the other side of the FFI boundary. - fn is_in_defined_function(self) -> bool { - self.contains(Self::DEFINED) && self.is_in_function() + fn is_in_defined_function(&self) -> bool { + self.root_use_flags.contains(RootUseFlags::DEFINED) && self.is_in_function() } /// Whether the type is used (directly or not) in a function pointer type. /// Here, we also allow non-FFI-safe types behind a C pointer, /// to be treated as an opaque type on the other side of the FFI boundary. - fn is_in_fnptr(self) -> bool { - self.contains(Self::THEORETICAL) && self.is_in_function() + fn is_in_fnptr(&self) -> bool { + self.root_use_flags.contains(RootUseFlags::THEORETICAL) && self.is_in_function() } /// Whether we can expect type parameters and co in a given type. - fn can_expect_ty_params(self) -> bool { + fn can_expect_ty_params(&self) -> bool { // rust-defined functions, as well as FnPtrs - self.contains(Self::THEORETICAL) || self.is_in_defined_function() - } -} - -bitflags! { - /// Data that summarises how an "outer type" surrounds its inner type(s) - #[derive(Clone, Copy, Debug, PartialEq, Eq)] - struct OuterTyData: u8 { - /// To show that there is no outer type, the current type is directly used by a `static` - /// variable or a function/FnPtr - const NO_OUTER_TY = 0b01; - /// For NO_OUTER_TY cases, show that we are being directly used by a FnPtr specifically - /// FIXME(ctypes): this is only used for "bad behaviour" reproduced for compatibility's sake - const NO_OUTER_TY_FNPTR = 0b10; - } -} - -impl OuterTyData { - /// Get the proper data for a given outer type. - fn from_ty<'tcx>(ty: Ty<'tcx>) -> Self { - match ty.kind() { - ty::FnPtr(..) => Self::NO_OUTER_TY | Self::NO_OUTER_TY_FNPTR, - ty::RawPtr(..) - | ty::Ref(..) - | ty::Adt(..) - | ty::Tuple(..) - | ty::Array(..) - | ty::Slice(_) => Self::empty(), - k @ _ => bug!("unexpected outer type {:?} of kind {:?}", ty, k), - } + self.root_use_flags.contains(RootUseFlags::THEORETICAL) || self.is_in_defined_function() } } @@ -415,8 +463,17 @@ struct ImproperCTypesVisitor<'a, 'tcx> { } impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { - fn new(cx: &'a LateContext<'tcx>, base_ty: Ty<'tcx>, base_fn_mode: CItemKind) -> Self { - Self { cx, base_ty, base_fn_mode, cache: FxHashSet::default() } + fn new( + cx: &'a LateContext<'tcx>, + base_ty: Unnormalized<'tcx, Ty<'tcx>>, + base_fn_mode: CItemKind, + ) -> Self { + ImproperCTypesVisitor { + cx, + base_ty: maybe_normalize_erasing_regions(cx, base_ty), + base_fn_mode, + cache: FxHashSet::default(), + } } /// Checks if the given indirection (box,ref,pointer) is "ffi-safe". @@ -485,7 +542,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { { FfiSafe } else { - self.visit_type(state, OuterTyData::from_ty(ty), inner_ty) + self.visit_type(state.next(ty), inner_ty) } } } @@ -505,8 +562,11 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { let transparent_with_all_zst_fields = if def.repr().transparent() { if let Some(field) = super::transparent_newtype_field(self.cx.tcx, variant) { // Transparent newtypes have at most one non-ZST field which needs to be checked.. - let field_ty = get_type_from_field(self.cx, field, args); - match self.visit_type(state, OuterTyData::from_ty(ty), field_ty) { + let field_ty = maybe_normalize_erasing_regions( + self.cx, + Unnormalized::new_wip(field.ty(self.cx.tcx, args)), + ); + match self.visit_type(state.next(ty), field_ty) { FfiUnsafe { ty, .. } if ty.is_unit() => (), r => return r, } @@ -524,8 +584,11 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { // We can't completely trust `repr(C)` markings, so make sure the fields are actually safe. let mut all_phantom = !variant.fields.is_empty(); for field in &variant.fields { - let field_ty = get_type_from_field(self.cx, field, args); - all_phantom &= match self.visit_type(state, OuterTyData::from_ty(ty), field_ty) { + let field_ty = maybe_normalize_erasing_regions( + self.cx, + Unnormalized::new_wip(field.ty(self.cx.tcx, args)), + ); + all_phantom &= match self.visit_type(state.next(ty), field_ty) { FfiSafe => false, // `()` fields are FFI-safe! FfiUnsafe { ty, .. } if ty.is_unit() => false, @@ -570,7 +633,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { "consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct" )) } else { - // FIXME(ctypes): confirm that this makes sense for unions once #60405 / RFC2645 stabilises + // FIXME(#60405): confirm that this makes sense for unions once #60405 / RFC2645 stabilises Some(msg!( "consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this union" )) @@ -628,7 +691,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { if !def.repr().c() && !def.repr().transparent() && def.repr().int.is_none() { // Special-case types like `Option` and `Result` if let Some(inner_ty) = repr_nullable_ptr(self.cx.tcx, self.cx.typing_env(), ty) { - return self.visit_type(state, OuterTyData::from_ty(ty), inner_ty); + return self.visit_type(state.next(ty), inner_ty); } return FfiUnsafe { @@ -660,12 +723,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { /// Checks if the given type is "ffi-safe" (has a stable, well-defined /// representation which can be exported to C code). - fn visit_type( - &mut self, - state: VisitorState, - outer_ty: OuterTyData, - ty: Ty<'tcx>, - ) -> FfiResult<'tcx> { + fn visit_type(&mut self, state: VisitorState, ty: Ty<'tcx>) -> FfiResult<'tcx> { use FfiResult::*; let tcx = self.cx.tcx; @@ -709,7 +767,8 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { // Pattern types are just extra invariants on the type that you need to uphold, // but only the base type is relevant for being representable in FFI. // (note: this lint was written when pattern types could only be integers constrained to ranges) - ty::Pat(pat_ty, _) => self.visit_type(state, outer_ty, pat_ty), + // (also note: the lack of ".next(ty)" on the state is on purpose) + ty::Pat(pat_ty, _) => self.visit_type(state, pat_ty), // types which likely have a stable representation, if the target architecture defines those // note: before rust 1.77, 128-bit ints were not FFI-safe on x86_64 @@ -740,12 +799,14 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { }, ty::Tuple(tuple) => { - // C functions can return void - let empty_and_safe = tuple.is_empty() - && outer_ty.contains(OuterTyData::NO_OUTER_TY) - && state.is_in_function_return(); - - if empty_and_safe { + if tuple.is_empty() + && state.is_in_function_return() + && matches!( + state.outer_ty_kind, + OuterTyKind::None | OuterTyKind::NoneThroughFnPtr + ) + { + // C functions can return void FfiSafe } else { FfiUnsafe { @@ -774,9 +835,8 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { ty::Array(inner_ty, _) => { if state.is_in_function() - && outer_ty.contains(OuterTyData::NO_OUTER_TY) - // FIXME(ctypes): VVV-this-VVV shouldn't be the case - && !outer_ty.contains(OuterTyData::NO_OUTER_TY_FNPTR) + // FIXME(ctypes): VVV-this-VVV shouldn't make a difference between ::None and ::NoneThroughFnPtr + && matches!(state.outer_ty_kind, OuterTyKind::None) { // C doesn't really support passing arrays by value - the only way to pass an array by value // is through a struct. @@ -788,7 +848,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { } else { // let's allow phantoms to go through, // since an array of 1-ZSTs is also a 1-ZST - self.visit_type(state, OuterTyData::from_ty(ty), inner_ty) + self.visit_type(state.next(ty), inner_ty) } } @@ -806,19 +866,14 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { let sig = tcx.instantiate_bound_regions_with_erased(sig); for arg in sig.inputs() { - match self.visit_type( - VisitorState::ARGUMENT_TY_IN_FNPTR, - OuterTyData::from_ty(ty), - *arg, - ) { + match self.visit_type(state.next_in_fnptr(ty, FnPos::Arg), *arg) { FfiSafe => {} r => return r, } } let ret_ty = sig.output(); - - self.visit_type(VisitorState::RETURN_TY_IN_FNPTR, OuterTyData::from_ty(ty), ret_ty) + self.visit_type(state.next_in_fnptr(ty, FnPos::Ret), ret_ty) } ty::Foreign(..) => FfiSafe, @@ -886,17 +941,17 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { }) } - fn check_type(&mut self, state: VisitorState, ty: Ty<'tcx>) -> FfiResult<'tcx> { - let ty = self - .cx - .tcx - .try_normalize_erasing_regions(self.cx.typing_env(), Unnormalized::new_wip(ty)) - .unwrap_or(ty); + fn check_type( + &mut self, + state: VisitorState, + ty: Unnormalized<'tcx, Ty<'tcx>>, + ) -> FfiResult<'tcx> { + let ty = maybe_normalize_erasing_regions(self.cx, ty); if let Some(res) = self.visit_for_opaque_ty(ty) { return res; } - self.visit_type(state, OuterTyData::NO_OUTER_TY, ty) + self.visit_type(state, ty) } } @@ -925,7 +980,7 @@ impl<'tcx> ImproperCTypesLint { self.spans.push(ty.span); } - hir::intravisit::walk_ty(self, ty) + hir::intravisit::walk_ty(self, ty); } } @@ -949,6 +1004,7 @@ impl<'tcx> ImproperCTypesLint { let all_types = iter::zip(visitor.tys.drain(..), visitor.spans.drain(..)); for (fn_ptr_ty, span) in all_types { + let fn_ptr_ty = Unnormalized::new_wip(fn_ptr_ty); let mut visitor = ImproperCTypesVisitor::new(cx, fn_ptr_ty, fn_mode); // FIXME(ctypes): make a check_for_fnptr let ffi_res = visitor.check_type(state, fn_ptr_ty); @@ -970,12 +1026,12 @@ impl<'tcx> ImproperCTypesLint { let sig = cx.tcx.instantiate_bound_regions_with_erased(sig); for (input_ty, input_hir) in iter::zip(sig.inputs(), decl.inputs) { - let state = VisitorState::argument_from_fnmode(fn_mode); + let state = VisitorState::fn_entry_point(fn_mode, FnPos::Arg); self.check_type_for_external_abi_fnptr(cx, state, input_hir, *input_ty, fn_mode); } if let hir::FnRetTy::Return(ret_hir) = decl.output { - let state = VisitorState::return_from_fnmode(fn_mode); + let state = VisitorState::fn_entry_point(fn_mode, FnPos::Ret); self.check_type_for_external_abi_fnptr(cx, state, ret_hir, sig.output(), fn_mode); } } @@ -998,9 +1054,9 @@ impl<'tcx> ImproperCTypesLint { } fn check_foreign_static(&mut self, cx: &LateContext<'tcx>, id: hir::OwnerId, span: Span) { - let ty = cx.tcx.type_of(id).instantiate_identity().skip_norm_wip(); + let ty = cx.tcx.type_of(id).instantiate_identity(); let mut visitor = ImproperCTypesVisitor::new(cx, ty, CItemKind::Declaration); - let ffi_res = visitor.check_type(VisitorState::STATIC_TY, ty); + let ffi_res = visitor.check_type(VisitorState::static_entry_point(), ty); self.process_ffi_result(cx, span, ffi_res, CItemKind::Declaration); } @@ -1016,16 +1072,18 @@ impl<'tcx> ImproperCTypesLint { let sig = cx.tcx.instantiate_bound_regions_with_erased(sig); for (input_ty, input_hir) in iter::zip(sig.inputs(), decl.inputs) { - let state = VisitorState::argument_from_fnmode(fn_mode); - let mut visitor = ImproperCTypesVisitor::new(cx, *input_ty, fn_mode); - let ffi_res = visitor.check_type(state, *input_ty); + let input_ty = Unnormalized::new_wip(*input_ty); + let state = VisitorState::fn_entry_point(fn_mode, FnPos::Arg); + let mut visitor = ImproperCTypesVisitor::new(cx, input_ty, fn_mode); + let ffi_res = visitor.check_type(state, input_ty); self.process_ffi_result(cx, input_hir.span, ffi_res, fn_mode); } if let hir::FnRetTy::Return(ret_hir) = decl.output { - let state = VisitorState::return_from_fnmode(fn_mode); - let mut visitor = ImproperCTypesVisitor::new(cx, sig.output(), fn_mode); - let ffi_res = visitor.check_type(state, sig.output()); + let output_ty = Unnormalized::new_wip(sig.output()); + let state = VisitorState::fn_entry_point(fn_mode, FnPos::Ret); + let mut visitor = ImproperCTypesVisitor::new(cx, output_ty, fn_mode); + let ffi_res = visitor.check_type(state, output_ty); self.process_ffi_result(cx, ret_hir.span, ffi_res, fn_mode); } } @@ -1124,7 +1182,7 @@ impl<'tcx> LateLintPass<'tcx> for ImproperCTypesLint { | hir::ItemKind::TyAlias(_, _, ty) => { self.check_type_for_external_abi_fnptr( cx, - VisitorState::STATIC_TY, + VisitorState::static_entry_point(), ty, cx.tcx.type_of(item.owner_id).instantiate_identity().skip_norm_wip(), CItemKind::Definition, @@ -1158,7 +1216,7 @@ impl<'tcx> LateLintPass<'tcx> for ImproperCTypesLint { fn check_field_def(&mut self, cx: &LateContext<'tcx>, field: &'tcx hir::FieldDef<'tcx>) { self.check_type_for_external_abi_fnptr( cx, - VisitorState::STATIC_TY, + VisitorState::static_entry_point(), field.ty, cx.tcx.type_of(field.def_id).instantiate_identity().skip_norm_wip(), CItemKind::Definition, diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 517f2954c5461..025f36a34f00d 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -3,12 +3,11 @@ use std::fmt::Display; use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::stable_hasher::{ - StableCompare, StableHash, StableHashCtxt, StableHasher, ToStableHashKey, + StableCompare, StableHash, StableHashCtxt, StableHasher, }; use rustc_error_messages::{DiagArgValue, IntoDiagArg}; -use rustc_hir_id::{HirId, ItemLocalId}; +use rustc_hir_id::HirId; use rustc_macros::{Decodable, Encodable, StableHash}; -use rustc_span::def_id::DefPathHash; pub use rustc_span::edition::Edition; use rustc_span::{AttrId, Ident, Symbol, sym}; use serde::{Deserialize, Serialize}; @@ -154,23 +153,6 @@ impl StableHash for LintExpectationId { } } -impl ToStableHashKey for LintExpectationId { - type KeyType = (DefPathHash, ItemLocalId, u16, u16); - - #[inline] - fn to_stable_hash_key(&self, hcx: &mut Hcx) -> Self::KeyType { - match self { - LintExpectationId::Stable { hir_id, attr_index, lint_index: Some(lint_index) } => { - let (def_path_hash, lint_idx) = hir_id.to_stable_hash_key(hcx); - (def_path_hash, lint_idx, *attr_index, *lint_index) - } - _ => { - unreachable!("StableHash should only be called for a filled `LintExpectationId`") - } - } - } -} - /// Setting for how to handle a lint. /// /// See: @@ -623,15 +605,6 @@ impl StableHash for LintId { } } -impl ToStableHashKey for LintId { - type KeyType = &'static str; - - #[inline] - fn to_stable_hash_key(&self, _: &mut Hcx) -> &'static str { - self.lint_name_raw() - } -} - impl StableCompare for LintId { const CAN_USE_UNSTABLE_SORT: bool = true; diff --git a/compiler/rustc_middle/src/dep_graph/dep_node.rs b/compiler/rustc_middle/src/dep_graph/dep_node.rs index 38ceac7ae5bda..60a4b66d39187 100644 --- a/compiler/rustc_middle/src/dep_graph/dep_node.rs +++ b/compiler/rustc_middle/src/dep_graph/dep_node.rs @@ -51,7 +51,7 @@ use std::fmt; use std::hash::Hash; use rustc_data_structures::fingerprint::{Fingerprint, PackedFingerprint}; -use rustc_data_structures::stable_hasher::{StableHasher, StableOrd, ToStableHashKey}; +use rustc_data_structures::stable_hasher::{StableHasher, StableOrd}; use rustc_hir::def_id::DefId; use rustc_hir::definitions::DefPathHash; use rustc_macros::{Decodable, Encodable, StableHash}; @@ -231,13 +231,7 @@ impl WorkProductId { WorkProductId { hash: hasher.finish() } } } -impl ToStableHashKey for WorkProductId { - type KeyType = Fingerprint; - #[inline] - fn to_stable_hash_key(&self, _: &mut Hcx) -> Self::KeyType { - self.hash - } -} + impl StableOrd for WorkProductId { // Fingerprint can use unstable (just a tuple of `u64`s), so WorkProductId can as well const CAN_USE_UNSTABLE_SORT: bool = true; diff --git a/compiler/rustc_middle/src/ty/impls_ty.rs b/compiler/rustc_middle/src/ty/impls_ty.rs index 729a5049d6333..1f22e0af9c006 100644 --- a/compiler/rustc_middle/src/ty/impls_ty.rs +++ b/compiler/rustc_middle/src/ty/impls_ty.rs @@ -7,11 +7,10 @@ use std::ptr; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::stable_hasher::{ - HashingControls, StableHash, StableHashCtxt, StableHasher, ToStableHashKey, + HashingControls, StableHash, StableHashCtxt, StableHasher, }; use tracing::trace; -use crate::middle::region; use crate::{mir, ty}; impl<'tcx, H, T> StableHash for &'tcx ty::list::RawList @@ -45,20 +44,6 @@ where } } -impl<'tcx, H, T> ToStableHashKey for &'tcx ty::list::RawList -where - T: StableHash, -{ - type KeyType = Fingerprint; - - #[inline] - fn to_stable_hash_key(&self, hcx: &mut Hcx) -> Fingerprint { - let mut hasher = StableHasher::new(); - self.stable_hash(hcx, &mut hasher); - hasher.finish() - } -} - impl<'tcx> StableHash for ty::GenericArg<'tcx> { fn stable_hash(&self, hcx: &mut Hcx, hasher: &mut StableHasher) { self.kind().stable_hash(hcx, hasher); @@ -81,12 +66,3 @@ impl StableHash for mir::interpret::CtfeProvenance { self.into_parts().stable_hash(hcx, hasher); } } - -impl ToStableHashKey for region::Scope { - type KeyType = region::Scope; - - #[inline] - fn to_stable_hash_key(&self, _: &mut Hcx) -> region::Scope { - *self - } -} diff --git a/compiler/rustc_session/Cargo.toml b/compiler/rustc_session/Cargo.toml index d66e04f581069..79d05e2d20a69 100644 --- a/compiler/rustc_session/Cargo.toml +++ b/compiler/rustc_session/Cargo.toml @@ -6,7 +6,6 @@ edition = "2024" [dependencies] # tidy-alphabetical-start getopts = "0.2" -rand = "0.9.0" rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } rustc_data_structures = { path = "../rustc_data_structures" } diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index f8da18632a997..fff1bae2c2264 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -14,7 +14,7 @@ use std::{cmp, fs, iter}; use externs::{ExternOpt, split_extern_opt}; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; -use rustc_data_structures::stable_hasher::{StableHasher, StableOrd, ToStableHashKey}; +use rustc_data_structures::stable_hasher::{StableHasher, StableOrd}; use rustc_errors::emitter::HumanReadableErrorType; use rustc_errors::{ColorConfig, DiagCtxtFlags}; use rustc_feature::UnstableFeatures; @@ -628,7 +628,6 @@ macro_rules! define_output_types { )* } - impl StableOrd for OutputType { const CAN_USE_UNSTABLE_SORT: bool = true; @@ -636,15 +635,6 @@ macro_rules! define_output_types { const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = (); } - impl ToStableHashKey for OutputType { - type KeyType = Self; - - fn to_stable_hash_key(&self, _: &mut Hcx) -> Self::KeyType { - *self - } - } - - impl OutputType { pub fn iter_all() -> impl Iterator { static ALL_VARIANTS: &[OutputType] = &[ @@ -1107,13 +1097,10 @@ impl OutFileName { outputs: &OutputFilenames, flavor: OutputType, codegen_unit_name: &str, - invocation_temp: Option<&str>, ) -> PathBuf { match *self { OutFileName::Real(ref path) => path.clone(), - OutFileName::Stdout => { - outputs.temp_path_for_cgu(flavor, codegen_unit_name, invocation_temp) - } + OutFileName::Stdout => outputs.temp_path_for_cgu(flavor, codegen_unit_name), } } @@ -1138,6 +1125,17 @@ pub struct OutputFilenames { filestem: String, pub single_output_file: Option, temps_directory: Option, + + /// A random string generated per invocation of rustc. + /// + /// This is prepended to all temporary files so that they do not collide + /// during concurrent invocations of rustc, or past invocations that were + /// preserved with a flag like `-C save-temps`, since these files may be + /// hard linked. + // This does not affect incr comp outputs, only where temp files are stored. + #[stable_hasher(ignore)] + invocation_temp: Option, + explicit_dwo_out_directory: Option, pub outputs: OutputTypes, } @@ -1180,6 +1178,7 @@ impl OutputFilenames { out_filestem: String, single_output_file: Option, temps_directory: Option, + invocation_temp: Option, explicit_dwo_out_directory: Option, extra: String, outputs: OutputTypes, @@ -1188,6 +1187,7 @@ impl OutputFilenames { out_directory, single_output_file, temps_directory, + invocation_temp, explicit_dwo_out_directory, outputs, crate_stem: format!("{out_crate_name}{extra}"), @@ -1224,23 +1224,14 @@ impl OutputFilenames { /// Gets the path where a compilation artifact of the given type for the /// given codegen unit should be placed on disk. If codegen_unit_name is /// None, a path distinct from those of any codegen unit will be generated. - pub fn temp_path_for_cgu( - &self, - flavor: OutputType, - codegen_unit_name: &str, - invocation_temp: Option<&str>, - ) -> PathBuf { + pub fn temp_path_for_cgu(&self, flavor: OutputType, codegen_unit_name: &str) -> PathBuf { let extension = flavor.extension(); - self.temp_path_ext_for_cgu(extension, codegen_unit_name, invocation_temp) + self.temp_path_ext_for_cgu(extension, codegen_unit_name) } /// Like `temp_path`, but specifically for dwarf objects. - pub fn temp_path_dwo_for_cgu( - &self, - codegen_unit_name: &str, - invocation_temp: Option<&str>, - ) -> PathBuf { - let p = self.temp_path_ext_for_cgu(DWARF_OBJECT_EXT, codegen_unit_name, invocation_temp); + pub fn temp_path_dwo_for_cgu(&self, codegen_unit_name: &str) -> PathBuf { + let p = self.temp_path_ext_for_cgu(DWARF_OBJECT_EXT, codegen_unit_name); if let Some(dwo_out) = &self.explicit_dwo_out_directory { let mut o = dwo_out.clone(); o.push(p.file_name().unwrap()); @@ -1252,16 +1243,11 @@ impl OutputFilenames { /// Like `temp_path`, but also supports things where there is no corresponding /// OutputType, like noopt-bitcode or lto-bitcode. - pub fn temp_path_ext_for_cgu( - &self, - ext: &str, - codegen_unit_name: &str, - invocation_temp: Option<&str>, - ) -> PathBuf { + pub fn temp_path_ext_for_cgu(&self, ext: &str, codegen_unit_name: &str) -> PathBuf { let mut extension = codegen_unit_name.to_string(); // Append `.{invocation_temp}` to ensure temporary files are unique. - if let Some(rng) = invocation_temp { + if let Some(rng) = &self.invocation_temp { extension.push('.'); extension.push_str(rng); } @@ -1302,10 +1288,9 @@ impl OutputFilenames { split_debuginfo_kind: SplitDebuginfo, split_dwarf_kind: SplitDwarfKind, cgu_name: &str, - invocation_temp: Option<&str>, ) -> Option { - let obj_out = self.temp_path_for_cgu(OutputType::Object, cgu_name, invocation_temp); - let dwo_out = self.temp_path_dwo_for_cgu(cgu_name, invocation_temp); + let obj_out = self.temp_path_for_cgu(OutputType::Object, cgu_name); + let dwo_out = self.temp_path_dwo_for_cgu(cgu_name); match (split_debuginfo_kind, split_dwarf_kind) { (SplitDebuginfo::Off, SplitDwarfKind::Single | SplitDwarfKind::Split) => None, // Single mode doesn't change how DWARF is emitted, but does add Split DWARF attributes diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 3b2fc53381a93..9bd5d434266ec 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -5,8 +5,6 @@ use std::sync::Arc; use std::sync::atomic::{AtomicBool, AtomicUsize}; use std::{env, io}; -use rand::{RngCore, rng}; -use rustc_data_structures::base_n::{CASE_INSENSITIVE, ToBaseN}; use rustc_data_structures::flock; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet}; use rustc_data_structures::profiling::{SelfProfiler, SelfProfilerRef}; @@ -163,14 +161,6 @@ pub struct Session { target_filesearch: FileSearch, host_filesearch: FileSearch, - /// A random string generated per invocation of rustc. - /// - /// This is prepended to all temporary files so that they do not collide - /// during concurrent invocations of rustc, or past invocations that were - /// preserved with a flag like `-C save-temps`, since these files may be - /// hard linked. - pub invocation_temp: Option, - /// The names of intrinsics that the current codegen backend replaces /// with its own implementations. pub replaced_intrinsics: FxHashSet, @@ -1097,11 +1087,6 @@ pub fn build_session( filesearch::FileSearch::new(&sopts.search_paths, &target_tlib_path, &target); let host_filesearch = filesearch::FileSearch::new(&sopts.search_paths, &host_tlib_path, &host); - let invocation_temp = sopts - .incremental - .as_ref() - .map(|_| rng().next_u32().to_base_fixed_len(CASE_INSENSITIVE).to_string()); - let timings = TimingSectionHandler::new(sopts.json_timings); let sess = Session { @@ -1132,7 +1117,6 @@ pub fn build_session( file_depinfo: Default::default(), target_filesearch, host_filesearch, - invocation_temp, replaced_intrinsics: FxHashSet::default(), // filled by `run_compiler` thin_lto_supported: true, // filled by `run_compiler` mir_opt_bisect_eval_count: AtomicUsize::new(0), diff --git a/compiler/rustc_span/src/def_id.rs b/compiler/rustc_span/src/def_id.rs index d07c7fcfd4683..6ce3d6b309f0e 100644 --- a/compiler/rustc_span/src/def_id.rs +++ b/compiler/rustc_span/src/def_id.rs @@ -465,24 +465,6 @@ impl ToStableHashKey for LocalDefId { } } -impl ToStableHashKey for CrateNum { - type KeyType = DefPathHash; - - #[inline] - fn to_stable_hash_key(&self, hcx: &mut Hcx) -> DefPathHash { - self.as_def_id().to_stable_hash_key(hcx) - } -} - -impl ToStableHashKey for DefPathHash { - type KeyType = DefPathHash; - - #[inline] - fn to_stable_hash_key(&self, _: &mut Hcx) -> DefPathHash { - *self - } -} - macro_rules! typed_def_id { ($Name:ident, $LocalName:ident) => { #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Encodable, Decodable, StableHash)] diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index ea2880d8d7dff..a342775b136f9 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -8,7 +8,7 @@ use std::{fmt, str}; use rustc_arena::DroplessArena; use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; use rustc_data_structures::stable_hasher::{ - StableCompare, StableHash, StableHashCtxt, StableHasher, ToStableHashKey, + StableCompare, StableHash, StableHashCtxt, StableHasher, }; use rustc_data_structures::sync::Lock; use rustc_macros::{Decodable, Encodable, StableHash, symbols}; @@ -2641,14 +2641,6 @@ impl StableHash for Symbol { } } -impl ToStableHashKey for Symbol { - type KeyType = String; - #[inline] - fn to_stable_hash_key(&self, _: &mut Hcx) -> String { - self.as_str().to_string() - } -} - impl StableCompare for Symbol { const CAN_USE_UNSTABLE_SORT: bool = true; diff --git a/compiler/rustc_type_ir/src/fast_reject.rs b/compiler/rustc_type_ir/src/fast_reject.rs index 4e4be901dbfff..26d98b5d8ad7d 100644 --- a/compiler/rustc_type_ir/src/fast_reject.rs +++ b/compiler/rustc_type_ir/src/fast_reject.rs @@ -5,12 +5,6 @@ use std::marker::PhantomData; use rustc_ast_ir::Mutability; #[cfg(feature = "nightly")] -use rustc_data_structures::fingerprint::Fingerprint; -#[cfg(feature = "nightly")] -use rustc_data_structures::stable_hasher::{ - StableHash, StableHashCtxt, StableHasher, ToStableHashKey, -}; -#[cfg(feature = "nightly")] use rustc_macros::{Decodable_NoContext, Encodable_NoContext, StableHash}; use crate::inherent::*; @@ -48,18 +42,6 @@ pub enum SimplifiedType { Error, } -#[cfg(feature = "nightly")] -impl ToStableHashKey for SimplifiedType { - type KeyType = Fingerprint; - - #[inline] - fn to_stable_hash_key(&self, hcx: &mut Hcx) -> Fingerprint { - let mut hasher = StableHasher::new(); - self.stable_hash(hcx, &mut hasher); - hasher.finish() - } -} - /// Generic parameters are pretty much just bound variables, e.g. /// the type of `fn foo<'a, T>(x: &'a T) -> u32 { ... }` can be thought of as /// `for<'a, T> fn(&'a T) -> u32`. diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/Cargo.lock b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/Cargo.lock new file mode 100644 index 0000000000000..8d2834ad01aba --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/Cargo.lock @@ -0,0 +1,45 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "cfi-types" +version = "0.0.8" + +[[package]] +name = "cross-lang-cfi-types-crate-abort" +version = "0.1.0" +dependencies = [ + "cfi-types", +] + +[[package]] +name = "cross-lang-cfi-types-crate-not-abort" +version = "0.1.0" +dependencies = [ + "cfi-types", +] + +[[package]] +name = "indirect-arity-mismatch-abort" +version = "0.1.0" + +[[package]] +name = "indirect-pointee-type-mismatch-abort" +version = "0.1.0" + +[[package]] +name = "indirect-return-type-mismatch-abort" +version = "0.1.0" + +[[package]] +name = "indirect-type-mismatch-abort" +version = "0.1.0" + +[[package]] +name = "indirect-type-qualifier-mismatch-abort" +version = "0.1.0" + +[[package]] +name = "invalid-branch-target-abort" +version = "0.1.0" diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/Cargo.toml b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/Cargo.toml new file mode 100644 index 0000000000000..2b22762883c04 --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/Cargo.toml @@ -0,0 +1,13 @@ +# Workspace mirroring the examples in . +[workspace] +resolver = "2" +members = [ + "invalid-branch-target-abort", + "indirect-arity-mismatch-abort", + "indirect-pointee-type-mismatch-abort", + "indirect-return-type-mismatch-abort", + "indirect-type-qualifier-mismatch-abort", + "indirect-type-mismatch-abort", + "cross-lang-cfi-types-crate-abort", + "cross-lang-cfi-types-crate-not-abort", +] diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-abort/Cargo.toml b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-abort/Cargo.toml new file mode 100644 index 0000000000000..2b6082a5b653d --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-abort/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "cross-lang-cfi-types-crate-abort" +version = "0.1.0" +edition = "2021" + +[dependencies] +cfi-types = { path = "../vendor/cfi-types" } diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-abort/build.rs b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-abort/build.rs new file mode 100644 index 0000000000000..e6a0311d2d1be --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-abort/build.rs @@ -0,0 +1,5 @@ +include!("../shared_build_rs.rs"); + +fn main() { + build_foo_static_lib(&[]); +} diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-abort/src/foo.c b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-abort/src/foo.c new file mode 100644 index 0000000000000..9021075763bb3 --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-abort/src/foo.c @@ -0,0 +1,5 @@ +int +do_twice(int (*fn)(int), int arg) +{ + return fn(arg) + fn(arg); +} diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-abort/src/main.rs b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-abort/src/main.rs new file mode 100644 index 0000000000000..dc6490f6d3ff5 --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-abort/src/main.rs @@ -0,0 +1,36 @@ +// This example demonstrates redirecting control flow using an indirect +// branch/call to a function with different return and parameter types than the +// return type expected and arguments intended/passed at the call/branch site, +// across the FFI boundary using the `cfi_types` crate for cross-language LLVM +// CFI. + +use std::mem; + +use cfi_types::{c_int, c_long}; + +#[link(name = "foo")] +unsafe extern "C" { + fn do_twice(f: unsafe extern "C" fn(c_int) -> c_int, arg: i32) -> i32; +} + +unsafe extern "C" fn add_one(x: c_int) -> c_int { + c_int(x.0 + 1) +} + +unsafe extern "C" fn add_two(x: c_long) -> c_long { + c_long(x.0 + 2) +} + +fn main() { + let answer = unsafe { do_twice(add_one, 5) }; + + println!("The answer is: {}", answer); + + println!("With CFI enabled, you should not see the next answer"); + let f: unsafe extern "C" fn(c_int) -> c_int = unsafe { + mem::transmute::<*const u8, unsafe extern "C" fn(c_int) -> c_int>(add_two as *const u8) + }; + let next_answer = unsafe { do_twice(f, 5) }; + + println!("The next answer is: {}", next_answer); +} diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-not-abort/Cargo.toml b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-not-abort/Cargo.toml new file mode 100644 index 0000000000000..81b068d79742e --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-not-abort/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "cross-lang-cfi-types-crate-not-abort" +version = "0.1.0" +edition = "2021" + +[dependencies] +cfi-types = { path = "../vendor/cfi-types" } diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-not-abort/build.rs b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-not-abort/build.rs new file mode 100644 index 0000000000000..e6a0311d2d1be --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-not-abort/build.rs @@ -0,0 +1,5 @@ +include!("../shared_build_rs.rs"); + +fn main() { + build_foo_static_lib(&[]); +} diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-not-abort/src/foo.c b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-not-abort/src/foo.c new file mode 100644 index 0000000000000..d02bbb285883d --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-not-abort/src/foo.c @@ -0,0 +1,23 @@ +#include +#include + +// This definition has the type id "_ZTSFvlE". +void +hello_from_c(long arg) +{ + printf("Hello from C!\n"); +} + +// This definition has the type id "_ZTSFvPFvlElE"--this can be ignored for the +// purposes of this example. +void +indirect_call_from_c(void (*fn)(long), long arg) +{ + // This call site tests whether the destination pointer is a member of the + // group derived from the same type id of the fn declaration, which has the + // type id "_ZTSFvlE". + // + // Notice that since the test is at the call site and generated by Clang, + // the type id used in the test is encoded by Clang. + fn(arg); +} diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-not-abort/src/main.rs b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-not-abort/src/main.rs new file mode 100644 index 0000000000000..a9d1326d6dc43 --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-not-abort/src/main.rs @@ -0,0 +1,67 @@ +use cfi_types::c_long; + +#[link(name = "foo")] +extern "C" { + // This declaration has the type id "_ZTSFvlE" because it uses the CFI types + // for cross-language LLVM CFI support. The cfi_types crate provides a new + // set of C types as user-defined types using the cfi_encoding attribute and + // repr(transparent) to be used for cross-language LLVM CFI support. This + // new set of C types allows the Rust compiler to identify and correctly + // encode C types in extern "C" function types indirectly called across the + // FFI boundary when CFI is enabled. + fn hello_from_c(_: c_long); + + // This declaration has the type id "_ZTSFvPFvlElE" because it uses the CFI + // types for cross-language LLVM CFI support--this can be ignored for the + // purposes of this example. + fn indirect_call_from_c(f: unsafe extern "C" fn(c_long), arg: c_long); +} + +// This definition has the type id "_ZTSFvlE" because it uses the CFI types for +// cross-language LLVM CFI support, similarly to the hello_from_c declaration +// above. +unsafe extern "C" fn hello_from_rust(_: c_long) { + println!("Hello, world!"); +} + +// This definition has the type id "_ZTSFvlE" because it uses the CFI types for +// cross-language LLVM CFI support, similarly to the hello_from_c declaration +// above. +unsafe extern "C" fn hello_from_rust_again(_: c_long) { + println!("Hello from Rust again!"); +} + +// This definition would also have the type id "_ZTSFvPFvlElE" because it uses +// the CFI types for cross-language LLVM CFI support, similarly to the +// hello_from_c declaration above--this can be ignored for the purposes of this +// example. +fn indirect_call(f: unsafe extern "C" fn(c_long), arg: c_long) { + // This indirect call site tests whether the destination pointer is a member + // of the group derived from the same type id of the f declaration, which + // has the type id "_ZTSFvlE" because it uses the CFI types for + // cross-language LLVM CFI support, similarly to the hello_from_c + // declaration above. + unsafe { f(arg) } +} + +// This definition has the type id "_ZTSFvvE"--this can be ignored for the +// purposes of this example. +fn main() { + // This demonstrates an indirect call within Rust-only code using the same + // encoding for hello_from_rust and the test at the indirect call site at + // indirect_call (i.e., "_ZTSFvlE"). + indirect_call(hello_from_rust, c_long(5)); + + // This demonstrates an indirect call across the FFI boundary with the Rust + // compiler and Clang using the same encoding for hello_from_c and the test + // at the indirect call site at indirect_call (i.e., "_ZTSFvlE"). + indirect_call(hello_from_c, c_long(5)); + + // This demonstrates an indirect call to a function passed as a callback + // across the FFI boundary with the Rust compiler and Clang the same + // encoding for the passed-callback declaration and the test at the indirect + // call site at indirect_call_from_c (i.e., "_ZTSFvlE"). + unsafe { + indirect_call_from_c(hello_from_rust_again, c_long(5)); + } +} diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-abort/Cargo.lock b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-abort/Cargo.lock new file mode 100644 index 0000000000000..5c2cbfd47ac55 --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-abort/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "cross-lang-integer-normalization-abort" +version = "0.1.0" diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-abort/Cargo.toml b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-abort/Cargo.toml new file mode 100644 index 0000000000000..b9044fba728ad --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-abort/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "cross-lang-integer-normalization-abort" +version = "0.1.0" +edition = "2021" + +# Not a member of the parent `sanitizer-cfi-build-std-clang` workspace so it can +# be built with different `RUSTFLAGS` (i.e., integer normalization). +[workspace] +members = ["."] +resolver = "2" diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-abort/build.rs b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-abort/build.rs new file mode 100644 index 0000000000000..de04b350552af --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-abort/build.rs @@ -0,0 +1,5 @@ +include!("../shared_build_rs.rs"); + +fn main() { + build_foo_static_lib(&["-fsanitize-cfi-icall-experimental-normalize-integers"]); +} diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-abort/src/foo.c b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-abort/src/foo.c new file mode 100644 index 0000000000000..9021075763bb3 --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-abort/src/foo.c @@ -0,0 +1,5 @@ +int +do_twice(int (*fn)(int), int arg) +{ + return fn(arg) + fn(arg); +} diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-abort/src/main.rs b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-abort/src/main.rs new file mode 100644 index 0000000000000..ef5d1da6ca737 --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-abort/src/main.rs @@ -0,0 +1,34 @@ +// This example demonstrates redirecting control flow using an indirect +// branch/call to a function with different return and parameter types than the +// return type expected and arguments intended/passed at the call/branch site, +// across the FFI boundary using integer normalization for cross-language LLVM +// CFI. + +use std::mem; + +#[link(name = "foo")] +extern "C" { + fn do_twice(f: unsafe extern "C" fn(i32) -> i32, arg: i32) -> i32; +} + +unsafe extern "C" fn add_one(x: i32) -> i32 { + x + 1 +} + +unsafe extern "C" fn add_two(x: i64) -> i64 { + x + 2 +} + +fn main() { + let answer = unsafe { do_twice(add_one, 5) }; + + println!("The answer is: {}", answer); + + println!("With CFI enabled, you should not see the next answer"); + let f: unsafe extern "C" fn(i32) -> i32 = unsafe { + mem::transmute::<*const u8, unsafe extern "C" fn(i32) -> i32>(add_two as *const u8) + }; + let next_answer = unsafe { do_twice(f, 5) }; + + println!("The next answer is: {}", next_answer); +} diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-not-abort/Cargo.lock b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-not-abort/Cargo.lock new file mode 100644 index 0000000000000..957356f828d94 --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-not-abort/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "cross-lang-integer-normalization-not-abort" +version = "0.1.0" diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-not-abort/Cargo.toml b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-not-abort/Cargo.toml new file mode 100644 index 0000000000000..a89a3ae9b3501 --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-not-abort/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "cross-lang-integer-normalization-not-abort" +version = "0.1.0" +edition = "2021" + +# Not a member of the parent `sanitizer-cfi-build-std-clang` workspace so it can +# be built with different `RUSTFLAGS` (i.e., integer normalization). +[workspace] +members = ["."] +resolver = "2" diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-not-abort/build.rs b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-not-abort/build.rs new file mode 100644 index 0000000000000..de04b350552af --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-not-abort/build.rs @@ -0,0 +1,5 @@ +include!("../shared_build_rs.rs"); + +fn main() { + build_foo_static_lib(&["-fsanitize-cfi-icall-experimental-normalize-integers"]); +} diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-not-abort/src/foo.c b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-not-abort/src/foo.c new file mode 100644 index 0000000000000..d02bbb285883d --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-not-abort/src/foo.c @@ -0,0 +1,23 @@ +#include +#include + +// This definition has the type id "_ZTSFvlE". +void +hello_from_c(long arg) +{ + printf("Hello from C!\n"); +} + +// This definition has the type id "_ZTSFvPFvlElE"--this can be ignored for the +// purposes of this example. +void +indirect_call_from_c(void (*fn)(long), long arg) +{ + // This call site tests whether the destination pointer is a member of the + // group derived from the same type id of the fn declaration, which has the + // type id "_ZTSFvlE". + // + // Notice that since the test is at the call site and generated by Clang, + // the type id used in the test is encoded by Clang. + fn(arg); +} diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-not-abort/src/main.rs b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-not-abort/src/main.rs new file mode 100644 index 0000000000000..70a4d9a789e58 --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-not-abort/src/main.rs @@ -0,0 +1,89 @@ +use std::ffi::c_long; + +#[link(name = "foo")] +extern "C" { + // This declaration would have the type id "_ZTSFvlE", but at the time types + // are encoded, all type aliases are already resolved to their respective + // Rust aliased types, so this is encoded either as "_ZTSFvu3i32E" or + // "_ZTSFvu3i64E" depending to what type c_long type alias is resolved to, + // which currently uses the u vendor extended type + // encoding for the Rust integer types--this is the problem demonstrated in + // this example. + fn hello_from_c(_: c_long); + + // This declaration would have the type id "_ZTSFvPFvlElE", but is encoded + // either as "_ZTSFvPFvu3i32ES_E" (compressed) or "_ZTSFvPFvu3i64ES_E" + // (compressed), similarly to the hello_from_c declaration above--this can + // be ignored for the purposes of this example. + fn indirect_call_from_c(f: unsafe extern "C" fn(c_long), arg: c_long); +} + +// This definition would have the type id "_ZTSFvlE", but is encoded either as +// "_ZTSFvu3i32E" or "_ZTSFvu3i64E", similarly to the hello_from_c declaration +// above. +unsafe extern "C" fn hello_from_rust(_: c_long) { + println!("Hello, world!"); +} + +// This definition would have the type id "_ZTSFvlE", but is encoded either as +// "_ZTSFvu3i32E" or "_ZTSFvu3i64E", similarly to the hello_from_c declaration +// above. +unsafe extern "C" fn hello_from_rust_again(_: c_long) { + println!("Hello from Rust again!"); +} + +// This definition would also have the type id "_ZTSFvPFvlElE", but is encoded +// either as "_ZTSFvPFvu3i32ES_E" (compressed) or "_ZTSFvPFvu3i64ES_E" +// (compressed), similarly to the hello_from_c declaration above--this can be +// ignored for the purposes of this example. +fn indirect_call(f: unsafe extern "C" fn(c_long), arg: c_long) { + // This indirect call site tests whether the destination pointer is a member + // of the group derived from the same type id of the f declaration, which + // would have the type id "_ZTSFvlE", but is encoded either as + // "_ZTSFvu3i32E" or "_ZTSFvu3i64E", similarly to the hello_from_c + // declaration above. + // + // Notice that since the test is at the call site and generated by the Rust + // compiler, the type id used in the test is encoded by the Rust compiler. + unsafe { f(arg) } +} + +// This definition has the type id "_ZTSFvvE"--this can be ignored for the +// purposes of this example. +fn main() { + // This demonstrates an indirect call within Rust-only code using the same + // encoding for hello_from_rust and the test at the indirect call site at + // indirect_call (i.e., "_ZTSFvu3i32E" or "_ZTSFvu3i64E"). + indirect_call(hello_from_rust, 5); + + // This demonstrates an indirect call across the FFI boundary with the Rust + // compiler and Clang using different encodings for hello_from_c and the + // test at the indirect call site at indirect_call (i.e., "_ZTSFvu3i32E" or + // "_ZTSFvu3i64E" vs "_ZTSFvlE"). + // + // When using rustc LTO (i.e., -Clto), this works because the type id used + // is from the Rust-declared hello_from_c, which is encoded by the Rust + // compiler (i.e., "_ZTSFvu3i32E" or "_ZTSFvu3i64E"). + // + // When using (proper) LTO (i.e., -Clinker-plugin-lto), this does not work + // because the type id used is from the C-defined hello_from_c, which is + // encoded by Clang (i.e., "_ZTSFvlE"). + indirect_call(hello_from_c, 5); + + // This demonstrates an indirect call to a function passed as a callback + // across the FFI boundary with the Rust compiler and Clang using different + // encodings for the passed-callback declaration and the test at the + // indirect call site at indirect_call_from_c (i.e., "_ZTSFvu3i32E" or + // "_ZTSFvu3i64E" vs "_ZTSFvlE"). + // + // When Rust functions are passed as callbacks across the FFI boundary to be + // called back from C code, the tests are also at the call site but + // generated by Clang instead, so the type ids used in the tests are encoded + // by Clang, which will not match the type ids of declarations encoded by + // the Rust compiler (e.g., hello_from_rust_again). (The same happens the + // other way around for C functions passed as callbacks across the FFI + // boundary to be called back from Rust code.) + unsafe { + indirect_call_from_c(hello_from_rust_again, 5); + } +} diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-arity-mismatch-abort/Cargo.toml b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-arity-mismatch-abort/Cargo.toml new file mode 100644 index 0000000000000..8c7f2b33265f7 --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-arity-mismatch-abort/Cargo.toml @@ -0,0 +1,4 @@ +[package] +name = "indirect-arity-mismatch-abort" +version = "0.1.0" +edition = "2021" diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-arity-mismatch-abort/src/main.rs b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-arity-mismatch-abort/src/main.rs new file mode 100644 index 0000000000000..8cc0dba58b201 --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-arity-mismatch-abort/src/main.rs @@ -0,0 +1,30 @@ +// This example demonstrates redirecting control flow using an indirect +// branch/call to a function with a different number of parameters than +// arguments intended/passed at the call/branch site. + +use std::mem; + +fn add_one(x: i32) -> i32 { + x + 1 +} + +fn add_two(x: i32, _y: i32) -> i32 { + x + 2 +} + +fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 { + f(arg) + f(arg) +} + +fn main() { + let answer = do_twice(add_one, 5); + + println!("The answer is: {}", answer); + + println!("With CFI enabled, you should not see the next answer"); + let f: fn(i32) -> i32 = + unsafe { mem::transmute::<*const u8, fn(i32) -> i32>(add_two as *const u8) }; + let next_answer = do_twice(f, 5); + + println!("The next answer is: {}", next_answer); +} diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-pointee-type-mismatch-abort/Cargo.toml b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-pointee-type-mismatch-abort/Cargo.toml new file mode 100644 index 0000000000000..0526f844363b9 --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-pointee-type-mismatch-abort/Cargo.toml @@ -0,0 +1,4 @@ +[package] +name = "indirect-pointee-type-mismatch-abort" +version = "0.1.0" +edition = "2021" diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-pointee-type-mismatch-abort/src/main.rs b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-pointee-type-mismatch-abort/src/main.rs new file mode 100644 index 0000000000000..ad0d7ddf0192f --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-pointee-type-mismatch-abort/src/main.rs @@ -0,0 +1,32 @@ +// This example demonstrates redirecting control flow using an indirect +// branch/call to a function with different return and parameter (i.e., pointee) +// types than the return type expected and arguments intended/passed at the +// call/branch site. + +use std::mem; + +fn add_one(x: *const i32) -> i32 { + unsafe { *x + 1 } +} + +fn add_two(x: *const i64) -> i32 { + unsafe { (*x + 2) as i32 } +} + +fn do_twice(f: fn(*const i32) -> i32, arg: *const i32) -> i32 { + f(arg) + f(arg) +} + +fn main() { + let value: i32 = 5; + let answer = do_twice(add_one, &value); + + println!("The answer is: {}", answer); + + println!("With CFI enabled, you should not see the next answer"); + let f: fn(*const i32) -> i32 = + unsafe { mem::transmute::<*const u8, fn(*const i32) -> i32>(add_two as *const u8) }; + let next_answer = do_twice(f, &value); + + println!("The next answer is: {}", next_answer); +} diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-return-type-mismatch-abort/Cargo.toml b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-return-type-mismatch-abort/Cargo.toml new file mode 100644 index 0000000000000..392975f872277 --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-return-type-mismatch-abort/Cargo.toml @@ -0,0 +1,4 @@ +[package] +name = "indirect-return-type-mismatch-abort" +version = "0.1.0" +edition = "2021" diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-return-type-mismatch-abort/src/main.rs b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-return-type-mismatch-abort/src/main.rs new file mode 100644 index 0000000000000..e21e71ee71a0f --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-return-type-mismatch-abort/src/main.rs @@ -0,0 +1,30 @@ +// This example demonstrates redirecting control flow using an indirect +// branch/call to a function with a different return type than the return type +// expected at the call/branch site. + +use std::mem; + +fn add_one(x: i32) -> i32 { + x + 1 +} + +fn add_two(x: i32) -> i64 { + i64::from(x + 2) +} + +fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 { + f(arg) + f(arg) +} + +fn main() { + let answer = do_twice(add_one, 5); + + println!("The answer is: {}", answer); + + println!("With CFI enabled, you should not see the next answer"); + let f: fn(i32) -> i32 = + unsafe { mem::transmute::<*const u8, fn(i32) -> i32>(add_two as *const u8) }; + let next_answer = do_twice(f, 5); + + println!("The next answer is: {}", next_answer); +} diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-type-mismatch-abort/Cargo.toml b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-type-mismatch-abort/Cargo.toml new file mode 100644 index 0000000000000..e167b2acfbcf5 --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-type-mismatch-abort/Cargo.toml @@ -0,0 +1,4 @@ +[package] +name = "indirect-type-mismatch-abort" +version = "0.1.0" +edition = "2021" diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-type-mismatch-abort/src/main.rs b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-type-mismatch-abort/src/main.rs new file mode 100644 index 0000000000000..0f40aa316152e --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-type-mismatch-abort/src/main.rs @@ -0,0 +1,30 @@ +// This example demonstrates redirecting control flow using an indirect +// branch/call to a function with different return and parameter types than the +// return type expected and arguments intended/passed at the call/branch site. + +use std::mem; + +fn add_one(x: i32) -> i32 { + x + 1 +} + +fn add_two(x: i64) -> i64 { + x + 2 +} + +fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 { + f(arg) + f(arg) +} + +fn main() { + let answer = do_twice(add_one, 5); + + println!("The answer is: {}", answer); + + println!("With CFI enabled, you should not see the next answer"); + let f: fn(i32) -> i32 = + unsafe { mem::transmute::<*const u8, fn(i32) -> i32>(add_two as *const u8) }; + let next_answer = do_twice(f, 5); + + println!("The next answer is: {}", next_answer); +} diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-type-qualifier-mismatch-abort/Cargo.toml b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-type-qualifier-mismatch-abort/Cargo.toml new file mode 100644 index 0000000000000..1f4b3d4b0c9b6 --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-type-qualifier-mismatch-abort/Cargo.toml @@ -0,0 +1,4 @@ +[package] +name = "indirect-type-qualifier-mismatch-abort" +version = "0.1.0" +edition = "2021" diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-type-qualifier-mismatch-abort/src/main.rs b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-type-qualifier-mismatch-abort/src/main.rs new file mode 100644 index 0000000000000..e37d883addf1d --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/indirect-type-qualifier-mismatch-abort/src/main.rs @@ -0,0 +1,31 @@ +// This example demonstrates redirecting control flow using an indirect +// branch/call to a function with parameter type qualifiers than the argument +// type qualifiers intended/passed at the call/branch site. + +use std::mem; + +fn add_one(x: &i32) -> i32 { + *x + 1 +} + +fn add_two(x: &mut i32) -> i32 { + *x + 2 +} + +fn do_twice(f: fn(&i32) -> i32, arg: &i32) -> i32 { + f(arg) + f(arg) +} + +fn main() { + let value: i32 = 5; + let answer = do_twice(add_one, &value); + + println!("The answer is: {}", answer); + + println!("With CFI enabled, you should not see the next answer"); + let f: fn(&i32) -> i32 = + unsafe { mem::transmute::<*const u8, fn(&i32) -> i32>(add_two as *const u8) }; + let next_answer = do_twice(f, &value); + + println!("The next answer is: {}", next_answer); +} diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/invalid-branch-target-abort/Cargo.toml b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/invalid-branch-target-abort/Cargo.toml new file mode 100644 index 0000000000000..e6af74f961e04 --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/invalid-branch-target-abort/Cargo.toml @@ -0,0 +1,4 @@ +[package] +name = "invalid-branch-target-abort" +version = "0.1.0" +edition = "2021" diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/invalid-branch-target-abort/src/main.rs b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/invalid-branch-target-abort/src/main.rs new file mode 100644 index 0000000000000..0b047bf5fefb6 --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/invalid-branch-target-abort/src/main.rs @@ -0,0 +1,50 @@ +// This example demonstrates redirecting control flow using an indirect +// branch/call to an invalid destination (i.e., within the body of the +// function). + +use std::mem; + +fn add_one(x: i32) -> i32 { + x + 1 +} + +#[unsafe(naked)] +pub extern "C" fn add_two(_x: i32) -> ! { + // x + 2 preceded by a landing pad/nop block + core::arch::naked_asm!( + r#" + nop + nop + nop + nop + nop + nop + nop + nop + nop + lea eax, [rdi + 2] + ret + "#, + ); +} + +fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 { + f(arg) + f(arg) +} + +fn main() { + let answer = do_twice(add_one, 5); + + println!("The answer is: {}", answer); + + println!("With CFI enabled, you should not see the next answer"); + let f: fn(i32) -> i32 = unsafe { + // Offset 0 is a valid branch/call destination (i.e., the function entry + // point), but offsets 1-8 within the landing pad/nop block are invalid + // branch/call destinations (i.e., within the body of the function). + mem::transmute::<*const u8, fn(i32) -> i32>((add_two as *const u8).offset(5)) + }; + let next_answer = do_twice(f, 5); + + println!("The next answer is: {}", next_answer); +} diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/rmake.rs b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/rmake.rs new file mode 100644 index 0000000000000..c85d01a71105a --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/rmake.rs @@ -0,0 +1,151 @@ +//! Verifies that the examples in build and run with +//!`-Zbuild-std` to prevent regressions such as [rust-lang/rust#142284]. + +//@ needs-sanitizer-cfi +//@ needs-force-clang-based-tests +//@ needs-rust-lld +//@ needs-target-std +//@ ignore-cross-compile +//@ only-x86_64-unknown-linux-gnu + +#![deny(warnings)] + +use std::path::Path; + +use run_make_support::external_deps::rustc::sysroot as rustc_sysroot; +use run_make_support::run::cmd; +use run_make_support::{bin_name, cargo, path, target}; + +fn clang_path() -> String { + if let Ok(d) = std::env::var("LLVM_BIN_DIR") { + let clang = Path::new(d.trim_end_matches('/')).join("clang"); + if clang.exists() { + return clang.display().to_string(); + } + } + if let Ok(clang) = std::env::var("CLANG") { + let clang = Path::new(clang.trim_end_matches('/')); + if clang.exists() { + return clang.display().to_string(); + } + } + "clang".to_string() +} + +fn fuse_ld_path() -> String { + if let Ok(d) = std::env::var("LLVM_BIN_DIR") { + let llvm_bin_dir = Path::new(d.trim_end_matches('/')); + let gcc_ld_lld = llvm_bin_dir.join("gcc-ld").join("ld.lld"); + if gcc_ld_lld.exists() { + return gcc_ld_lld.display().to_string(); + } + let ld_lld = llvm_bin_dir.join("ld.lld"); + if ld_lld.exists() { + return ld_lld.display().to_string(); + } + } + if let Ok(clang) = std::env::var("CLANG") { + let clang = Path::new(clang.trim_end_matches('/')); + if let Some(clang_dir) = clang.parent() { + let gcc_ld_lld = clang_dir.join("gcc-ld").join("ld.lld"); + if gcc_ld_lld.exists() { + return gcc_ld_lld.display().to_string(); + } + let ld_lld = clang_dir.join("ld.lld"); + if ld_lld.exists() { + return ld_lld.display().to_string(); + } + } + } + let target_bin_dir = rustc_sysroot().join("lib").join("rustlib").join(target()).join("bin"); + let gcc_ld_lld = target_bin_dir.join("gcc-ld").join("ld.lld"); + if gcc_ld_lld.exists() { + return gcc_ld_lld.display().to_string(); + } + "ld.lld".to_string() +} + +fn run_and_expect_cfi_abort(target_dir: &Path, target: &str, binary: &str) { + let exe = target_dir.join(target).join("release").join(bin_name(binary)); + let output = cmd(&exe).run_fail(); + output + .assert_stdout_contains("With CFI enabled, you should not see the next answer") + .assert_stdout_not_contains("The next answer is:"); +} + +fn run_and_expect_cfi_not_abort(target_dir: &Path, target: &str, binary: &str) { + let exe = target_dir.join(target).join("release").join(bin_name(binary)); + let output = cmd(&exe).run(); + output.assert_stdout_contains("Hello from C!"); +} + +fn main() { + let clang = clang_path(); + let fuse_ld = fuse_ld_path(); + let tgt = target(); + let target_dir = path("target"); + let lib = std::env::var("LIB").unwrap_or_default(); + + let prior_rustflags = std::env::var("RUSTFLAGS").unwrap_or_default(); + + let rustflags = format!( + "{prior_rustflags} -Clinker-plugin-lto -Clinker={clang} \ + -Clink-arg=-fuse-ld={fuse_ld} -Zsanitizer=cfi \ + -Ctarget-feature=-crt-static" + ) + .trim() + .to_owned(); + + let rustflags_with_integer_normalization = + format!("{rustflags} -Zsanitizer-cfi-normalize-integers").trim().to_owned(); + + let run = |manifest: &Path, rustflags: &str, workspace: bool| { + let mut c = cargo(); + c.arg("build") + .arg("--manifest-path") + .arg(manifest) + .arg("--release") + .arg("-Zbuild-std") + .arg("--target") + .arg(&tgt); + if workspace { + c.arg("--workspace"); + } + c.env("RUSTFLAGS", rustflags) + .env("CC", &clang) + .env("CARGO_TARGET_DIR", &target_dir) + .env("RUSTC_BOOTSTRAP", "1") + .env("LIB", &lib) + .run(); + }; + + run(Path::new("Cargo.toml"), &rustflags, true); + for bin in [ + "invalid-branch-target-abort", + "indirect-arity-mismatch-abort", + "indirect-pointee-type-mismatch-abort", + "indirect-return-type-mismatch-abort", + "indirect-type-qualifier-mismatch-abort", + "indirect-type-mismatch-abort", + "cross-lang-cfi-types-crate-abort", + ] { + run_and_expect_cfi_abort(&target_dir, &tgt, bin); + } + for bin in ["cross-lang-cfi-types-crate-not-abort"] { + run_and_expect_cfi_not_abort(&target_dir, &tgt, bin); + } + + run( + Path::new("cross-lang-integer-normalization-abort/Cargo.toml"), + &rustflags_with_integer_normalization, + false, + ); + run_and_expect_cfi_abort(&target_dir, &tgt, "cross-lang-integer-normalization-abort"); + + run( + Path::new("cross-lang-integer-normalization-not-abort/Cargo.toml"), + &rustflags_with_integer_normalization, + false, + ); + run_and_expect_cfi_not_abort(&target_dir, &tgt, "cross-lang-integer-normalization-not-abort"); +} diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/shared_build_rs.rs b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/shared_build_rs.rs new file mode 100644 index 0000000000000..2c2e0fd4ffb81 --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/shared_build_rs.rs @@ -0,0 +1,73 @@ +use std::env; +use std::path::{Path, PathBuf}; +use std::process::Command; + +fn clang_path() -> PathBuf { + if let Ok(d) = env::var("LLVM_BIN_DIR") { + let clang = Path::new(d.trim_end_matches('/')).join("clang"); + if clang.exists() { + return clang; + } + } + if let Ok(clang) = env::var("CLANG") { + let clang = Path::new(clang.trim_end_matches('/')); + if clang.exists() { + return clang.to_path_buf(); + } + } + PathBuf::from("clang") +} + +fn llvm_ar_path() -> PathBuf { + if let Ok(d) = env::var("LLVM_BIN_DIR") { + let llvm_ar = Path::new(d.trim_end_matches('/')).join("llvm-ar"); + if llvm_ar.exists() { + return llvm_ar; + } + } + if let Ok(clang) = env::var("CLANG") { + let clang = Path::new(&clang); + if let Some(clang_dir) = clang.parent() { + let llvm_ar = clang_dir.join("llvm-ar"); + if llvm_ar.exists() { + return llvm_ar; + } + } + } + PathBuf::from("llvm-ar") +} + +fn build_foo_static_lib(extra_flags: &[&str]) { + let out_dir = env::var("OUT_DIR").expect("OUT_DIR"); + let manifest_dir = env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR"); + let c_src = Path::new(&manifest_dir).join("src/foo.c"); + let bc_path = Path::new(&out_dir).join("foo.bc"); + let a_path = Path::new(&out_dir).join("libfoo.a"); + + let clang = clang_path(); + let llvm_ar = llvm_ar_path(); + + let mut clang_args = vec!["-Wall", "-flto=thin", "-fsanitize=cfi"]; + clang_args.extend_from_slice(extra_flags); + clang_args.extend_from_slice(&["-fvisibility=hidden", "-c", "-emit-llvm", "-o"]); + + let st = Command::new(&clang) + .args(&clang_args) + .arg(&bc_path) + .arg(&c_src) + .status() + .unwrap_or_else(|e| panic!("failed to spawn `{}`: {e}", clang.display())); + assert!(st.success(), "`{}` failed with {st}", clang.display()); + + let st = Command::new(&llvm_ar) + .args(["rcs", a_path.to_str().unwrap(), bc_path.to_str().unwrap()]) + .status() + .unwrap_or_else(|e| panic!("failed to spawn `{}`: {e}", llvm_ar.display())); + assert!(st.success(), "`{}` failed with {st}", llvm_ar.display()); + + println!("cargo:rustc-link-search=native={out_dir}"); + println!("cargo:rustc-link-lib=static=foo"); + println!("cargo:rerun-if-changed={}", c_src.display()); + println!("cargo:rerun-if-changed=build.rs"); + println!("cargo:rerun-if-changed=../shared_build_rs.rs"); +} diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/vendor/cfi-types/Cargo.toml b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/vendor/cfi-types/Cargo.toml new file mode 100644 index 0000000000000..180bfea79fa03 --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/vendor/cfi-types/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "cfi-types" +version = "0.0.8" +edition = "2021" + +description = "CFI types for cross-language LLVM CFI support" +homepage = "https://github.com/rcvalle/rust-crate-cfi-types" +license = "MIT OR Apache-2.0" +repository = "https://github.com/rcvalle/rust-crate-cfi-types" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/tests/run-make-cargo/sanitizer-cfi-build-std-clang/vendor/cfi-types/src/lib.rs b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/vendor/cfi-types/src/lib.rs new file mode 100644 index 0000000000000..436097c32fd82 --- /dev/null +++ b/tests/run-make-cargo/sanitizer-cfi-build-std-clang/vendor/cfi-types/src/lib.rs @@ -0,0 +1,96 @@ +//! CFI types for cross-language LLVM CFI support. +//! +//! The cfi_types crate provides a new set of C types as user-defined types +//! using the cfi_encoding attribute and repr(transparent) to be used for +//! cross-language LLVM CFI support. This new set of C types allows the Rust +//! compiler to identify and correctly encode C types in extern "C" function +//! types indirectly called across the FFI boundary when CFI is enabled. +//! +//! The use of these types are optional and are recommended for when enforcement +//! and explicitness of types used across the FFI boundary and no loss of +//! granularity for cross-language LLVM CFI are preferred. +//! +//! Alternatively, the `-Zsanitizer-cfi-normalize-integers` option may be used +//! with the Clang `-fsanitize-cfi-icall-experimental-normalize-integers` option +//! for cross-language LLVM CFI support. + +#![feature(cfg_sanitizer_cfi)] +#![feature(cfi_encoding)] +#![allow(non_camel_case_types)] + +/// CFI type equivalent to Rust's core::ffi::c_char type alias. +#[allow(dead_code)] +#[cfg_attr(not(sanitizer_cfi_normalize_integers), cfi_encoding = "c")] +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)] +#[repr(transparent)] +pub struct c_char(pub core::ffi::c_char); + +/// CFI type equivalent to Rust's core::ffi::c_int type alias. +#[allow(dead_code)] +#[cfg_attr(not(sanitizer_cfi_normalize_integers), cfi_encoding = "i")] +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)] +#[repr(transparent)] +pub struct c_int(pub core::ffi::c_int); + +/// CFI type equivalent to Rust's core::ffi::c_long type alias. +#[allow(dead_code)] +#[cfg_attr(not(sanitizer_cfi_normalize_integers), cfi_encoding = "l")] +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)] +#[repr(transparent)] +pub struct c_long(pub core::ffi::c_long); + +/// CFI type equivalent to Rust's core::ffi::c_longlong type alias. +#[allow(dead_code)] +#[cfg_attr(not(sanitizer_cfi_normalize_integers), cfi_encoding = "x")] +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)] +#[repr(transparent)] +pub struct c_longlong(pub core::ffi::c_longlong); + +/// CFI type equivalent to Rust's core::ffi::c_schar type alias. +#[allow(dead_code)] +#[cfg_attr(not(sanitizer_cfi_normalize_integers), cfi_encoding = "a")] +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)] +#[repr(transparent)] +pub struct c_schar(pub core::ffi::c_schar); + +/// CFI type equivalent to Rust's core::ffi::c_short type alias. +#[allow(dead_code)] +#[cfg_attr(not(sanitizer_cfi_normalize_integers), cfi_encoding = "s")] +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)] +#[repr(transparent)] +pub struct c_short(pub core::ffi::c_short); + +/// CFI type equivalent to Rust's core::ffi::c_uchar type alias. +#[allow(dead_code)] +#[cfg_attr(not(sanitizer_cfi_normalize_integers), cfi_encoding = "h")] +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)] +#[repr(transparent)] +pub struct c_uchar(pub core::ffi::c_uchar); + +/// CFI type equivalent to Rust's core::ffi::c_uint type alias. +#[allow(dead_code)] +#[cfg_attr(not(sanitizer_cfi_normalize_integers), cfi_encoding = "j")] +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)] +#[repr(transparent)] +pub struct c_uint(pub core::ffi::c_uint); + +/// CFI type equivalent to Rust's core::ffi::c_ulong type alias. +#[allow(dead_code)] +#[cfg_attr(not(sanitizer_cfi_normalize_integers), cfi_encoding = "m")] +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)] +#[repr(transparent)] +pub struct c_ulong(pub core::ffi::c_ulong); + +/// CFI type equivalent to Rust's core::ffi::c_ulonglong type alias. +#[allow(dead_code)] +#[cfg_attr(not(sanitizer_cfi_normalize_integers), cfi_encoding = "y")] +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)] +#[repr(transparent)] +pub struct c_ulonglong(pub core::ffi::c_ulonglong); + +/// CFI type equivalent to Rust's core::ffi::c_ushort type alias. +#[allow(dead_code)] +#[cfg_attr(not(sanitizer_cfi_normalize_integers), cfi_encoding = "t")] +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)] +#[repr(transparent)] +pub struct c_ushort(pub core::ffi::c_ushort); diff --git a/tests/ui-fulldeps/codegen-backend/auxiliary/the_backend.rs b/tests/ui-fulldeps/codegen-backend/auxiliary/the_backend.rs index 5dd11b0a016e3..8a7cacf20e2e4 100644 --- a/tests/ui-fulldeps/codegen-backend/auxiliary/the_backend.rs +++ b/tests/ui-fulldeps/codegen-backend/auxiliary/the_backend.rs @@ -37,7 +37,7 @@ impl CodegenBackend for TheBackend { "fake_target_cpu".to_owned() } - fn codegen_crate(&self, _tcx: TyCtxt<'_>, _crate_info: &CrateInfo) -> Box { + fn codegen_crate(&self, _tcx: TyCtxt<'_>) -> Box { Box::new(CompiledModules { modules: vec![], allocator_module: None }) } @@ -46,6 +46,7 @@ impl CodegenBackend for TheBackend { ongoing_codegen: Box, _sess: &Session, _outputs: &OutputFilenames, + _crate_info: &CrateInfo, ) -> (CompiledModules, FxIndexMap) { let codegen_results = ongoing_codegen .downcast::() diff --git a/tests/ui/rmeta/no_optimized_mir.rs b/tests/ui/rmeta/no_optimized_mir.rs index dbf612cd03cc7..c8ed00b039b23 100644 --- a/tests/ui/rmeta/no_optimized_mir.rs +++ b/tests/ui/rmeta/no_optimized_mir.rs @@ -10,5 +10,4 @@ fn main() { rmeta_meta::missing_optimized_mir(); } -//~? ERROR crate `rmeta_meta` required to be available in rlib format, but was not found in this form //~? ERROR missing optimized MIR for `missing_optimized_mir` in the crate `rmeta_meta` diff --git a/tests/ui/rmeta/no_optimized_mir.stderr b/tests/ui/rmeta/no_optimized_mir.stderr index 91aa98172fe56..254f100aa7b5e 100644 --- a/tests/ui/rmeta/no_optimized_mir.stderr +++ b/tests/ui/rmeta/no_optimized_mir.stderr @@ -1,5 +1,3 @@ -error: crate `rmeta_meta` required to be available in rlib format, but was not found in this form - error: missing optimized MIR for `missing_optimized_mir` in the crate `rmeta_meta` | note: missing optimized MIR for this item (was the crate `rmeta_meta` compiled with `--emit=metadata`?) @@ -8,5 +6,5 @@ note: missing optimized MIR for this item (was the crate `rmeta_meta` compiled w LL | pub fn missing_optimized_mir() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 2 previous errors +error: aborting due to 1 previous error