Run static initializers
This commit is contained in:
parent
a8a88fe524
commit
3305e71095
2 changed files with 102 additions and 15 deletions
|
@ -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
|
||||
}
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -158,6 +158,81 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn lookup_init_array(&mut self) -> InterpResult<'tcx, Vec<ty::Instance<'tcx>>> {
|
||||
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,
|
||||
|
|
Loading…
Add table
Reference in a new issue