diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs index 8fffb91542f3..c75d1b0cc64e 100644 --- a/src/tools/miri/src/bin/miri.rs +++ b/src/tools/miri/src/bin/miri.rs @@ -32,8 +32,9 @@ use rustc_driver::Compilation; use rustc_hir::{self as hir, Node}; use rustc_interface::interface::Config; use rustc_middle::{ - middle::exported_symbols::{ - ExportedSymbol, SymbolExportInfo, SymbolExportKind, SymbolExportLevel, + middle::{ + codegen_fn_attrs::CodegenFnAttrFlags, + exported_symbols::{ExportedSymbol, SymbolExportInfo, SymbolExportKind, SymbolExportLevel}, }, query::LocalCrate, ty::TyCtxt, @@ -136,6 +137,8 @@ impl rustc_driver::Callbacks for MiriBeRustCompilerCalls { config.override_queries = Some(|_, local_providers| { // `exported_symbols` and `reachable_non_generics` provided by rustc always returns // an empty result if `tcx.sess.opts.output_types.should_codegen()` is false. + // In addition we need to add #[used] symbols to exported_symbols for .init_array + // handling. local_providers.exported_symbols = |tcx, LocalCrate| { let reachable_set = tcx.with_stable_hashing_context(|hcx| { tcx.reachable_set(()).to_sorted(&hcx, true) @@ -160,19 +163,28 @@ impl rustc_driver::Callbacks for MiriBeRustCompilerCalls { }) if !tcx.generics_of(local_def_id).requires_monomorphization(tcx) ); - (is_reachable_non_generic - && tcx.codegen_fn_attrs(local_def_id).contains_extern_indicator()) - .then_some(( - ExportedSymbol::NonGeneric(local_def_id.to_def_id()), - // Some dummy `SymbolExportInfo` here. We only use - // `exported_symbols` in shims/foreign_items.rs and the export info - // is ignored. - SymbolExportInfo { - level: SymbolExportLevel::C, - kind: SymbolExportKind::Text, - used: false, - }, - )) + if !is_reachable_non_generic { + return None; + } + let codegen_fn_attrs = tcx.codegen_fn_attrs(local_def_id); + if codegen_fn_attrs.contains_extern_indicator() + || codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::USED) + || codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER) + { + Some(( + ExportedSymbol::NonGeneric(local_def_id.to_def_id()), + // Some dummy `SymbolExportInfo` here. We only use + // `exported_symbols` in shims/foreign_items.rs and the export info + // is ignored. + SymbolExportInfo { + level: SymbolExportLevel::C, + kind: SymbolExportKind::Text, + used: false, + }, + )) + } else { + None + } }), ) } diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index a25d377f3a72..5498d9b83f23 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -158,6 +158,81 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { Ok(()) } + fn lookup_init_array(&mut self) -> InterpResult<'tcx, Vec>> { + let this = self.eval_context_mut(); + let tcx = this.tcx.tcx; + + let mut init_arrays = vec![]; + + let dependency_formats = tcx.dependency_formats(()); + let dependency_format = dependency_formats + .iter() + .find(|(crate_type, _)| *crate_type == CrateType::Executable) + .expect("interpreting a non-executable crate"); + for cnum in iter::once(LOCAL_CRATE).chain( + dependency_format.1.iter().enumerate().filter_map(|(num, &linkage)| { + // We add 1 to the number because that's what rustc also does everywhere it + // calls `CrateNum::new`... + #[allow(clippy::arithmetic_side_effects)] + (linkage != Linkage::NotLinked).then_some(CrateNum::new(num + 1)) + }), + ) { + for &(symbol, _export_info) in tcx.exported_symbols(cnum) { + if let ExportedSymbol::NonGeneric(def_id) = symbol { + let attrs = tcx.codegen_fn_attrs(def_id); + let link_section = if let Some(link_section) = attrs.link_section { + if !link_section.as_str().starts_with(".init_array") { + continue; + } + link_section + } else { + continue; + }; + + init_arrays.push((link_section, def_id)); + } + } + } + + init_arrays.sort_by(|(a, _), (b, _)| a.as_str().cmp(b.as_str())); + + let endianness = tcx.data_layout.endian; + let ptr_size = tcx.data_layout.pointer_size; + + let mut init_array = vec![]; + + for (_, def_id) in init_arrays { + let alloc = tcx.eval_static_initializer(def_id)?.inner(); + let mut expected_offset = Size::ZERO; + for &(offset, prov) in alloc.provenance().ptrs().iter() { + if offset != expected_offset { + throw_ub_format!(".init_array.* may not contain any non-function pointer data"); + } + expected_offset += ptr_size; + + let alloc_id = prov.alloc_id(); + + let reloc_target_alloc = tcx.global_alloc(alloc_id); + match reloc_target_alloc { + GlobalAlloc::Function(instance) => { + let addend = { + let offset = offset.bytes() as usize; + let bytes = &alloc.inspect_with_uninit_and_ptr_outside_interpreter( + offset..offset + ptr_size.bytes() as usize, + ); + read_target_uint(endianness, bytes).unwrap() + }; + assert_eq!(addend, 0); + init_array.push(instance); + } + _ => throw_ub_format!(".init_array.* member is not a function pointer"), + } + } + } + + Ok(init_array) + } + /// Lookup the body of a function that has `link_name` as the symbol name. fn lookup_exported_symbol( &mut self,