miri/shims/global_ctor.rs
1//! Implement global constructors.
2
3use std::task::Poll;
4
5use rustc_abi::ExternAbi;
6use rustc_target::spec::BinaryFormat;
7
8use crate::*;
9
10#[derive(Debug, Default)]
11pub struct GlobalCtorState<'tcx>(GlobalCtorStatePriv<'tcx>);
12
13#[derive(Debug, Default)]
14enum 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
22impl<'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
49 // `S_MOD_INIT_FUNC_POINTERS` flag is set on the section. LLVM
50 // adds this automatically so we currently do not require it.
51 // FIXME: is this guaranteed LLVM behavior? If not, we shouldn't
52 // implicitly add it here. Also see
53 // <https://github.com/rust-lang/miri/pull/4459#discussion_r2200115629>.
54 && matches!(section_type, None | Some("mod_init_funcs"))
55 })?,
56
57 // Read the standard `.init_array` section on platforms that use ELF, or WASM,
58 // which supports the same linker directive.
59 // FIXME: Add support for `.init_array.N` and `.ctors`?
60 BinaryFormat::Elf | BinaryFormat::Wasm =>
61 this.lookup_link_section(|section| section == ".init_array")?,
62
63 // Other platforms have no global ctor support.
64 _ => break 'new_state Done,
65 };
66
67 break 'new_state Ctors(ctors);
68 }
69 Ctors(ctors) => {
70 if let Some(ctor) = ctors.pop() {
71 let this = this.eval_context_mut();
72
73 let ctor = ctor.to_scalar().to_pointer(this)?;
74 let thread_callback = this.get_ptr_fn(ctor)?.as_instance()?;
75
76 // The signature of this function is `unsafe extern "C" fn()`.
77 this.call_function(
78 thread_callback,
79 ExternAbi::C { unwind: false },
80 &[],
81 None,
82 ReturnContinuation::Stop { cleanup: true },
83 )?;
84
85 return interp_ok(Poll::Pending); // we stay in this state (but `ctors` got shorter)
86 }
87
88 // No more constructors to run.
89 break 'new_state Done;
90 }
91 Done => return interp_ok(Poll::Ready(())),
92 }
93 };
94
95 self.0 = new_state;
96 interp_ok(Poll::Pending)
97 }
98}