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