Skip to content

Commit 85c8949

Browse files
committed
add support for global constructors (i.e. life before main)
1 parent 62aea72 commit 85c8949

File tree

5 files changed

+165
-32
lines changed

5 files changed

+165
-32
lines changed

src/tools/miri/src/eval.rs

Lines changed: 63 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,14 @@ use rustc_abi::ExternAbi;
1111
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
1212
use rustc_hir::def::Namespace;
1313
use rustc_hir::def_id::DefId;
14-
use rustc_middle::ty::layout::LayoutCx;
14+
use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv, LayoutCx};
1515
use rustc_middle::ty::{self, Ty, TyCtxt};
1616
use rustc_session::config::EntryFnType;
1717

1818
use crate::concurrency::GenmcCtx;
1919
use crate::concurrency::thread::TlsAllocAction;
2020
use crate::diagnostics::report_leaks;
21-
use crate::shims::tls;
21+
use crate::shims::{ctor, tls};
2222
use crate::*;
2323

2424
#[derive(Copy, Clone, Debug)]
@@ -216,9 +216,15 @@ impl Default for MiriConfig {
216216
}
217217

218218
/// The state of the main thread. Implementation detail of `on_main_stack_empty`.
219-
#[derive(Default, Debug)]
219+
#[derive(Debug)]
220220
enum MainThreadState<'tcx> {
221-
#[default]
221+
GlobalCtors {
222+
ctor_state: ctor::GlobalCtorState<'tcx>,
223+
entry_id: DefId,
224+
entry_type: MiriEntryFnType,
225+
argc: ImmTy<'tcx>,
226+
argv: ImmTy<'tcx>,
227+
},
222228
Running,
223229
TlsDtors(tls::TlsDtorsState<'tcx>),
224230
Yield {
@@ -234,6 +240,15 @@ impl<'tcx> MainThreadState<'tcx> {
234240
) -> InterpResult<'tcx, Poll<()>> {
235241
use MainThreadState::*;
236242
match self {
243+
GlobalCtors { ctor_state, entry_id, entry_type, argc, argv } => {
244+
match ctor_state.on_stack_empty(this)? {
245+
Poll::Pending => {} // just keep going
246+
Poll::Ready(()) => {
247+
call_main(this, *entry_id, *entry_type, argc.clone(), argv.clone())?;
248+
*self = Running;
249+
}
250+
}
251+
}
237252
Running => {
238253
*self = TlsDtors(Default::default());
239254
}
@@ -309,26 +324,6 @@ pub fn create_ecx<'tcx>(
309324
MiriMachine::new(config, layout_cx, genmc_ctx),
310325
);
311326

312-
// Some parts of initialization require a full `InterpCx`.
313-
MiriMachine::late_init(&mut ecx, config, {
314-
let mut state = MainThreadState::default();
315-
// Cannot capture anything GC-relevant here.
316-
Box::new(move |m| state.on_main_stack_empty(m))
317-
})?;
318-
319-
// Make sure we have MIR. We check MIR for some stable monomorphic function in libcore.
320-
let sentinel =
321-
helpers::try_resolve_path(tcx, &["core", "ascii", "escape_default"], Namespace::ValueNS);
322-
if !matches!(sentinel, Some(s) if tcx.is_mir_available(s.def.def_id())) {
323-
tcx.dcx().fatal(
324-
"the current sysroot was built without `-Zalways-encode-mir`, or libcore seems missing. \
325-
Use `cargo miri setup` to prepare a sysroot that is suitable for Miri."
326-
);
327-
}
328-
329-
// Setup first stack frame.
330-
let entry_instance = ty::Instance::mono(tcx, entry_id);
331-
332327
// First argument is constructed later, because it's skipped for `miri_start.`
333328

334329
// Second argument (argc): length of `config.args`.
@@ -395,11 +390,51 @@ pub fn create_ecx<'tcx>(
395390
ImmTy::from_immediate(imm, layout)
396391
};
397392

393+
// Some parts of initialization require a full `InterpCx`.
394+
MiriMachine::late_init(&mut ecx, config, {
395+
let mut state = MainThreadState::GlobalCtors {
396+
entry_id,
397+
entry_type,
398+
argc,
399+
argv,
400+
ctor_state: ctor::GlobalCtorState::default(),
401+
};
402+
403+
// Cannot capture anything GC-relevant here.
404+
Box::new(move |m| state.on_main_stack_empty(m))
405+
})?;
406+
407+
// Make sure we have MIR. We check MIR for some stable monomorphic function in libcore.
408+
let sentinel =
409+
helpers::try_resolve_path(tcx, &["core", "ascii", "escape_default"], Namespace::ValueNS);
410+
if !matches!(sentinel, Some(s) if tcx.is_mir_available(s.def.def_id())) {
411+
tcx.dcx().fatal(
412+
"the current sysroot was built without `-Zalways-encode-mir`, or libcore seems missing. \
413+
Use `cargo miri setup` to prepare a sysroot that is suitable for Miri."
414+
);
415+
}
416+
417+
interp_ok(ecx)
418+
}
419+
420+
// Call the entry function.
421+
fn call_main<'tcx>(
422+
ecx: &mut MiriInterpCx<'tcx>,
423+
entry_id: DefId,
424+
entry_type: MiriEntryFnType,
425+
argc: ImmTy<'tcx>,
426+
argv: ImmTy<'tcx>,
427+
) -> InterpResult<'tcx, ()> {
428+
let tcx = ecx.tcx();
429+
430+
// Setup first stack frame.
431+
let entry_instance = ty::Instance::mono(tcx, entry_id);
432+
398433
// Return place (in static memory so that it does not count as leak).
399434
let ret_place = ecx.allocate(ecx.machine.layouts.isize, MiriMemoryKind::Machine.into())?;
400435
ecx.machine.main_fn_ret_place = Some(ret_place.clone());
401-
// Call start function.
402436

437+
// Call start function.
403438
match entry_type {
404439
MiriEntryFnType::Rustc(EntryFnType::Main { .. }) => {
405440
let start_id = tcx.lang_items().start_fn().unwrap_or_else(|| {
@@ -409,7 +444,7 @@ pub fn create_ecx<'tcx>(
409444
let main_ret_ty = main_ret_ty.no_bound_vars().unwrap();
410445
let start_instance = ty::Instance::try_resolve(
411446
tcx,
412-
typing_env,
447+
ecx.typing_env(),
413448
start_id,
414449
tcx.mk_args(&[ty::GenericArg::from(main_ret_ty)]),
415450
)
@@ -427,7 +462,7 @@ pub fn create_ecx<'tcx>(
427462
ExternAbi::Rust,
428463
&[
429464
ImmTy::from_scalar(
430-
Scalar::from_pointer(main_ptr, &ecx),
465+
Scalar::from_pointer(main_ptr, ecx),
431466
// FIXME use a proper fn ptr type
432467
ecx.machine.layouts.const_raw_ptr,
433468
),
@@ -450,7 +485,7 @@ pub fn create_ecx<'tcx>(
450485
}
451486
}
452487

453-
interp_ok(ecx)
488+
interp_ok(())
454489
}
455490

456491
/// Evaluates the entry function specified by `entry_id`.

src/tools/miri/src/helpers.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1235,8 +1235,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
12351235
interp_ok(())
12361236
}
12371237

1238-
/// Lookup an array of immediates stored as a linker section of name `name`.
1239-
fn lookup_link_section(&mut self, name: &str) -> InterpResult<'tcx, Vec<ImmTy<'tcx>>> {
1238+
/// Lookup an array of immediates from any linker sections matching the provided predicate.
1239+
fn lookup_link_section(
1240+
&mut self,
1241+
include_name: impl Fn(&str) -> bool,
1242+
) -> InterpResult<'tcx, Vec<ImmTy<'tcx>>> {
12401243
let this = self.eval_context_mut();
12411244
let tcx = this.tcx.tcx;
12421245

@@ -1247,7 +1250,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
12471250
let Some(link_section) = attrs.link_section else {
12481251
return interp_ok(());
12491252
};
1250-
if link_section.as_str() == name {
1253+
if include_name(link_section.as_str()) {
12511254
let instance = ty::Instance::mono(tcx, def_id);
12521255
let const_val = this.eval_global(instance).unwrap_or_else(|err| {
12531256
panic!(

src/tools/miri/src/shims/ctor.rs

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
//! Implement global constructors.
2+
3+
use std::task::Poll;
4+
5+
use rustc_abi::ExternAbi;
6+
use rustc_target::spec::BinaryFormat;
7+
8+
use crate::*;
9+
10+
#[derive(Debug, Default)]
11+
pub struct GlobalCtorState<'tcx>(GlobalCtorStatePriv<'tcx>);
12+
13+
#[derive(Debug, Default)]
14+
enum GlobalCtorStatePriv<'tcx> {
15+
#[default]
16+
Init,
17+
/// The list of constructor functions that we still have to call.
18+
Ctors(Vec<ImmTy<'tcx>>),
19+
Done,
20+
}
21+
22+
impl<'tcx> GlobalCtorState<'tcx> {
23+
pub fn on_stack_empty(
24+
&mut self,
25+
this: &mut MiriInterpCx<'tcx>,
26+
) -> InterpResult<'tcx, Poll<()>> {
27+
use GlobalCtorStatePriv::*;
28+
let new_state = 'new_state: {
29+
match &mut self.0 {
30+
Init => {
31+
let this = this.eval_context_mut();
32+
33+
// Lookup constructors from the relevant magic link section.
34+
let ctors = match this.tcx.sess.target.binary_format {
35+
// Read the CRT library section on Windows.
36+
BinaryFormat::Coff =>
37+
this.lookup_link_section(|section| section == ".CRT$XCU")?,
38+
39+
// Read the `__mod_init_func` section on macOS.
40+
BinaryFormat::MachO =>
41+
this.lookup_link_section(|section| {
42+
let mut parts = section.splitn(3, ',');
43+
let (segment_name, section_name, section_type) =
44+
(parts.next(), parts.next(), parts.next());
45+
46+
segment_name == Some("__DATA")
47+
&& section_name == Some("__mod_init_func")
48+
// The `mod_init_funcs` directive ensures that the `S_MOD_INIT_FUNC_POINTERS` flag
49+
// is set on the section, but it is not strictly required.
50+
&& matches!(section_type, None | Some("mod_init_funcs"))
51+
})?,
52+
53+
// Read the standard `.init_array` section on platforms that use ELF, or WASM,
54+
// which supports the same linker directive.
55+
// FIXME: Add support for `.init_array.N`.
56+
BinaryFormat::Elf | BinaryFormat::Wasm =>
57+
this.lookup_link_section(|section| section == ".init_array")?,
58+
59+
// Other platforms have no global ctor support.
60+
_ => break 'new_state Done,
61+
};
62+
63+
break 'new_state Ctors(ctors);
64+
}
65+
Ctors(ctors) => {
66+
if let Some(ctor) = ctors.pop() {
67+
let this = this.eval_context_mut();
68+
69+
let ctor = ctor.to_scalar().to_pointer(this)?;
70+
let thread_callback = this.get_ptr_fn(ctor)?.as_instance()?;
71+
72+
// The signature of this function is `unsafe extern "C" fn()`.
73+
this.call_function(
74+
thread_callback,
75+
ExternAbi::C { unwind: false },
76+
&[],
77+
None,
78+
ReturnContinuation::Stop { cleanup: true },
79+
)?;
80+
81+
return interp_ok(Poll::Pending); // we stay in this state (but `ctors` got shorter)
82+
}
83+
84+
// No more constructors to run.
85+
break 'new_state Done;
86+
}
87+
Done => return interp_ok(Poll::Ready(())),
88+
}
89+
};
90+
91+
self.0 = new_state;
92+
interp_ok(Poll::Pending)
93+
}
94+
}

src/tools/miri/src/shims/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ mod wasi;
1111
mod windows;
1212
mod x86;
1313

14+
pub mod ctor;
1415
pub mod env;
1516
pub mod extern_static;
1617
pub mod foreign_items;

src/tools/miri/src/shims/tls.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
302302

303303
// Windows has a special magic linker section that is run on certain events.
304304
// We don't support most of that, but just enough to make thread-local dtors in `std` work.
305-
interp_ok(this.lookup_link_section(".CRT$XLB")?)
305+
interp_ok(this.lookup_link_section(|section| section == ".CRT$XLB")?)
306306
}
307307

308308
fn schedule_windows_tls_dtor(&mut self, dtor: ImmTy<'tcx>) -> InterpResult<'tcx> {

0 commit comments

Comments
 (0)