Skip to main content

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(&mut self, ecx: &mut MiriInterpCx<'tcx>) -> InterpResult<'tcx, Poll<()>> {
25        use GlobalCtorStatePriv::*;
26        let new_state = 'new_state: {
27            match &mut self.0 {
28                Init => {
29                    // Lookup constructors from the relevant magic link section.
30                    let ctors = match ecx.tcx.sess.target.binary_format {
31                        // Read the CRT library section on Windows.
32                        BinaryFormat::Coff =>
33                            ecx.lookup_link_section(|section| section == ".CRT$XCU")?,
34
35                        // Read the `__mod_init_func` section on macOS.
36                        BinaryFormat::MachO =>
37                            ecx.lookup_link_section(|section| {
38                                let mut parts = section.splitn(3, ',');
39                                let (segment_name, section_name, section_type) =
40                                    (parts.next(), parts.next(), parts.next());
41
42                                segment_name == Some("__DATA")
43                                    && section_name == Some("__mod_init_func")
44                                    // The `mod_init_funcs` directive ensures that the
45                                    // `S_MOD_INIT_FUNC_POINTERS` flag is set on the section. LLVM
46                                    // adds this automatically so we currently do not require it.
47                                    // FIXME: is this guaranteed LLVM behavior? If not, we shouldn't
48                                    // implicitly add it here. Also see
49                                    // <https://github.com/rust-lang/miri/pull/4459#discussion_r2200115629>.
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` and `.ctors`?
56                        BinaryFormat::Elf | BinaryFormat::Wasm =>
57                            ecx.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, span)) = ctors.pop() {
67                        let ctor = ctor.to_scalar().to_pointer(ecx);
68                        let thread_callback = ecx.get_ptr_fn(ctor)?.as_instance()?;
69
70                        // The signature of this function is `unsafe extern "C" fn()`.
71                        ecx.call_thread_root_function(
72                            thread_callback,
73                            ExternAbi::C { unwind: false },
74                            &[],
75                            None,
76                            span,
77                        )?;
78
79                        return interp_ok(Poll::Pending); // we stay in this state (but `ctors` got shorter)
80                    }
81
82                    // No more constructors to run.
83                    break 'new_state Done;
84                }
85                Done => return interp_ok(Poll::Ready(())),
86            }
87        };
88
89        self.0 = new_state;
90        interp_ok(Poll::Pending)
91    }
92}