wasmer_runtime_core_fl/
fault.rs

1//! The fault module contains the implementation for handling breakpoints, traps, and signals
2//! for wasm code.
3
4pub mod raw {
5    //! The raw module contains required externed function interfaces for the fault module.
6    use std::ffi::c_void;
7
8    #[cfg(target_arch = "x86_64")]
9    extern "C" {
10        /// Load registers and return on the stack [stack_end..stack_begin].
11        pub fn run_on_alternative_stack(stack_end: *mut u64, stack_begin: *mut u64) -> u64;
12        /// Internal routine for switching into a backend without information about where registers are preserved.
13        pub fn register_preservation_trampoline(); // NOT safe to call directly
14    }
15
16    /// Internal routine for switching into a backend without information about where registers are preserved.
17    #[cfg(not(target_arch = "x86_64"))]
18    pub extern "C" fn register_preservation_trampoline() {
19        unimplemented!("register_preservation_trampoline");
20    }
21
22    extern "C" {
23        /// libc setjmp
24        pub fn setjmp(env: *mut c_void) -> i32;
25        /// libc longjmp
26        pub fn longjmp(env: *mut c_void, val: i32) -> !;
27    }
28}
29
30use crate::codegen::{BreakpointInfo, BreakpointMap};
31use crate::error::{InvokeError, RuntimeError};
32use crate::state::x64::{build_instance_image, read_stack, X64Register, GPR};
33use crate::state::{CodeVersion, ExecutionStateImage};
34use crate::vm;
35use libc::{mmap, mprotect, siginfo_t, MAP_ANON, MAP_PRIVATE, PROT_NONE, PROT_READ, PROT_WRITE};
36use nix::sys::signal::{
37    sigaction, SaFlags, SigAction, SigHandler, SigSet, Signal, SIGBUS, SIGFPE, SIGILL, SIGINT,
38    SIGSEGV, SIGTRAP,
39};
40use std::cell::{Cell, RefCell, UnsafeCell};
41use std::ffi::c_void;
42use std::process;
43use std::sync::atomic::{AtomicBool, Ordering};
44use std::sync::Once;
45
46#[cfg(target_arch = "x86_64")]
47pub(crate) unsafe fn run_on_alternative_stack(stack_end: *mut u64, stack_begin: *mut u64) -> u64 {
48    raw::run_on_alternative_stack(stack_end, stack_begin)
49}
50
51#[cfg(not(target_arch = "x86_64"))]
52pub(crate) unsafe fn run_on_alternative_stack(_stack_end: *mut u64, _stack_begin: *mut u64) -> u64 {
53    unimplemented!("run_on_alternative_stack");
54}
55
56const TRAP_STACK_SIZE: usize = 1048576; // 1MB
57
58const SETJMP_BUFFER_LEN: usize = 128;
59type SetJmpBuffer = [i32; SETJMP_BUFFER_LEN];
60
61struct UnwindInfo {
62    jmpbuf: SetJmpBuffer, // in
63    breakpoints: Option<BreakpointMap>,
64    payload: Option<Box<RuntimeError>>, // out
65}
66
67/// A store for boundary register preservation.
68#[repr(packed)]
69#[derive(Default, Copy, Clone)]
70pub struct BoundaryRegisterPreservation {
71    /// R15.
72    pub r15: u64,
73    /// R14.
74    pub r14: u64,
75    /// R13.
76    pub r13: u64,
77    /// R12.
78    pub r12: u64,
79    /// RBX.
80    pub rbx: u64,
81}
82
83thread_local! {
84    static UNWIND: UnsafeCell<Option<UnwindInfo>> = UnsafeCell::new(None);
85    static CURRENT_CTX: UnsafeCell<*mut vm::Ctx> = UnsafeCell::new(::std::ptr::null_mut());
86    static CURRENT_CODE_VERSIONS: RefCell<Vec<CodeVersion>> = RefCell::new(vec![]);
87    static WAS_SIGINT_TRIGGERED: Cell<bool> = Cell::new(false);
88    static BOUNDARY_REGISTER_PRESERVATION: UnsafeCell<BoundaryRegisterPreservation> = UnsafeCell::new(BoundaryRegisterPreservation::default());
89}
90
91/// Gets a mutable pointer to the `BoundaryRegisterPreservation`.
92#[no_mangle]
93pub unsafe extern "C" fn get_boundary_register_preservation() -> *mut BoundaryRegisterPreservation {
94    BOUNDARY_REGISTER_PRESERVATION.with(|x| x.get())
95}
96
97struct InterruptSignalMem(*mut u8);
98unsafe impl Send for InterruptSignalMem {}
99unsafe impl Sync for InterruptSignalMem {}
100
101const INTERRUPT_SIGNAL_MEM_SIZE: usize = 4096;
102
103lazy_static! {
104    static ref INTERRUPT_SIGNAL_MEM: InterruptSignalMem = {
105        let ptr = unsafe {
106            mmap(
107                ::std::ptr::null_mut(),
108                INTERRUPT_SIGNAL_MEM_SIZE,
109                PROT_READ | PROT_WRITE,
110                MAP_PRIVATE | MAP_ANON,
111                -1,
112                0,
113            )
114        };
115        if ptr as isize == -1 {
116            panic!("cannot allocate code memory");
117        }
118        InterruptSignalMem(ptr as _)
119    };
120}
121static INTERRUPT_SIGNAL_DELIVERED: AtomicBool = AtomicBool::new(false);
122
123/// Returns a boolean indicating if SIGINT triggered the fault.
124pub fn was_sigint_triggered_fault() -> bool {
125    WAS_SIGINT_TRIGGERED.with(|x| x.get())
126}
127
128/// Runs a callback function with the given `Ctx`.
129pub unsafe fn with_ctx<R, F: FnOnce() -> R>(ctx: *mut vm::Ctx, cb: F) -> R {
130    let addr = CURRENT_CTX.with(|x| x.get());
131    let old = *addr;
132    *addr = ctx;
133    let ret = cb();
134    *addr = old;
135    ret
136}
137
138/// Pushes a new `CodeVersion` to the current code versions.
139pub fn push_code_version(version: CodeVersion) {
140    CURRENT_CODE_VERSIONS.with(|x| x.borrow_mut().push(version));
141}
142
143/// Pops a `CodeVersion` from the current code versions.
144pub fn pop_code_version() -> Option<CodeVersion> {
145    CURRENT_CODE_VERSIONS.with(|x| x.borrow_mut().pop())
146}
147
148/// Gets the wasm interrupt signal mem.
149pub unsafe fn get_wasm_interrupt_signal_mem() -> *mut u8 {
150    INTERRUPT_SIGNAL_MEM.0
151}
152
153/// Sets the wasm interrupt on the given `Ctx`.
154pub unsafe fn set_wasm_interrupt_on_ctx(ctx: *mut vm::Ctx) {
155    if mprotect(
156        (&*ctx).internal.interrupt_signal_mem as _,
157        INTERRUPT_SIGNAL_MEM_SIZE,
158        PROT_NONE,
159    ) < 0
160    {
161        panic!("cannot set PROT_NONE on signal mem");
162    }
163}
164
165/// Sets a wasm interrupt.
166pub unsafe fn set_wasm_interrupt() {
167    let mem: *mut u8 = INTERRUPT_SIGNAL_MEM.0;
168    if mprotect(mem as _, INTERRUPT_SIGNAL_MEM_SIZE, PROT_NONE) < 0 {
169        panic!("cannot set PROT_NONE on signal mem");
170    }
171}
172
173/// Clears the wasm interrupt.
174pub unsafe fn clear_wasm_interrupt() {
175    let mem: *mut u8 = INTERRUPT_SIGNAL_MEM.0;
176    if mprotect(mem as _, INTERRUPT_SIGNAL_MEM_SIZE, PROT_READ | PROT_WRITE) < 0 {
177        panic!("cannot set PROT_READ | PROT_WRITE on signal mem");
178    }
179}
180
181/// Catches an unsafe unwind with the given functions and breakpoints.
182pub unsafe fn catch_unsafe_unwind<R, F: FnOnce() -> R>(
183    f: F,
184    breakpoints: Option<BreakpointMap>,
185) -> Result<R, RuntimeError> {
186    let unwind = UNWIND.with(|x| x.get());
187    let old = (*unwind).take();
188    *unwind = Some(UnwindInfo {
189        jmpbuf: [0; SETJMP_BUFFER_LEN],
190        breakpoints: breakpoints,
191        payload: None,
192    });
193
194    if raw::setjmp(&mut (*unwind).as_mut().unwrap().jmpbuf as *mut SetJmpBuffer as *mut _) != 0 {
195        // error
196        let ret = (*unwind).as_mut().unwrap().payload.take().unwrap();
197        *unwind = old;
198        Err(*ret)
199    } else {
200        let ret = f();
201        // implicit control flow to the error case...
202        *unwind = old;
203        Ok(ret)
204    }
205}
206
207/// Begins an unsafe unwind.
208pub unsafe fn begin_unsafe_unwind(e: Box<RuntimeError>) -> ! {
209    let unwind = UNWIND.with(|x| x.get());
210    let inner = (*unwind)
211        .as_mut()
212        .expect("not within a catch_unsafe_unwind scope");
213    inner.payload = Some(e);
214    raw::longjmp(&mut inner.jmpbuf as *mut SetJmpBuffer as *mut _, 0xffff);
215}
216
217unsafe fn with_breakpoint_map<R, F: FnOnce(Option<&BreakpointMap>) -> R>(f: F) -> R {
218    let unwind = UNWIND.with(|x| x.get());
219    let inner = (*unwind)
220        .as_mut()
221        .expect("not within a catch_unsafe_unwind scope");
222    f(inner.breakpoints.as_ref())
223}
224
225#[cfg(not(target_arch = "x86_64"))]
226/// Allocates and runs with the given stack size and closure.
227pub fn allocate_and_run<R, F: FnOnce() -> R>(_size: usize, f: F) -> R {
228    f()
229}
230
231#[cfg(target_arch = "x86_64")]
232/// Allocates and runs with the given stack size and closure.
233pub fn allocate_and_run<R, F: FnOnce() -> R>(size: usize, f: F) -> R {
234    struct Context<F: FnOnce() -> R, R> {
235        f: Option<F>,
236        ret: Option<R>,
237    }
238
239    extern "C" fn invoke<F: FnOnce() -> R, R>(ctx: &mut Context<F, R>) {
240        let f = ctx.f.take().unwrap();
241        ctx.ret = Some(f());
242    }
243
244    unsafe {
245        let mut ctx = Context {
246            f: Some(f),
247            ret: None,
248        };
249        assert!(size % 16 == 0);
250        assert!(size >= 4096);
251
252        let mut stack: Vec<u64> = vec![0; size / 8];
253        let end_offset = stack.len();
254
255        stack[end_offset - 4] = invoke::<F, R> as usize as u64;
256
257        // NOTE: Keep this consistent with `image-loading-*.s`.
258        stack[end_offset - 4 - 10] = &mut ctx as *mut Context<F, R> as usize as u64; // rdi
259        const NUM_SAVED_REGISTERS: usize = 31;
260        let stack_begin = stack.as_mut_ptr().add(end_offset - 4 - NUM_SAVED_REGISTERS);
261        let stack_end = stack.as_mut_ptr().add(end_offset);
262
263        raw::run_on_alternative_stack(stack_end, stack_begin);
264        ctx.ret.take().unwrap()
265    }
266}
267
268extern "C" fn signal_trap_handler(
269    signum: ::nix::libc::c_int,
270    siginfo: *mut siginfo_t,
271    ucontext: *mut c_void,
272) {
273    use crate::backend::{Architecture, InlineBreakpointType};
274
275    #[cfg(target_arch = "x86_64")]
276    static ARCH: Architecture = Architecture::X64;
277
278    #[cfg(target_arch = "aarch64")]
279    static ARCH: Architecture = Architecture::Aarch64;
280
281    let mut should_unwind = false;
282    let mut unwind_result: Option<Box<RuntimeError>> = None;
283    let get_unwind_result = |uw_result: Option<Box<RuntimeError>>| -> Box<RuntimeError> {
284        uw_result
285            .unwrap_or_else(|| Box::new(RuntimeError::InvokeError(InvokeError::FailedWithNoError)))
286    };
287
288    unsafe {
289        let fault = get_fault_info(siginfo as _, ucontext);
290        let early_return = allocate_and_run(TRAP_STACK_SIZE, || {
291            CURRENT_CODE_VERSIONS.with(|versions| {
292                let versions = versions.borrow();
293                for v in versions.iter() {
294                    let magic_size =
295                        if let Some(x) = v.runnable_module.get_inline_breakpoint_size(ARCH) {
296                            x
297                        } else {
298                            continue;
299                        };
300                    let ip = fault.ip.get();
301                    let end = v.base + v.msm.total_size;
302                    if ip >= v.base && ip < end && ip + magic_size <= end {
303                        if let Some(ib) = v.runnable_module.read_inline_breakpoint(
304                            ARCH,
305                            std::slice::from_raw_parts(ip as *const u8, magic_size),
306                        ) {
307                            match ib.ty {
308                                InlineBreakpointType::Middleware => {
309                                    let out: Option<Result<(), RuntimeError>> =
310                                        with_breakpoint_map(|bkpt_map| {
311                                            bkpt_map.and_then(|x| x.get(&ip)).map(|x| {
312                                                x(BreakpointInfo {
313                                                    fault: Some(&fault),
314                                                })
315                                            })
316                                        });
317                                    if let Some(Ok(())) = out {
318                                    } else if let Some(Err(e)) = out {
319                                        should_unwind = true;
320                                        unwind_result = Some(Box::new(e));
321                                    }
322                                }
323                            }
324
325                            fault.ip.set(ip + magic_size);
326                            return true;
327                        }
328                        break;
329                    }
330                }
331                false
332            })
333        });
334        if should_unwind {
335            begin_unsafe_unwind(get_unwind_result(unwind_result));
336        }
337        if early_return {
338            return;
339        }
340
341        should_unwind = allocate_and_run(TRAP_STACK_SIZE, || {
342            let mut is_suspend_signal = false;
343
344            WAS_SIGINT_TRIGGERED.with(|x| x.set(false));
345
346            match Signal::from_c_int(signum) {
347                Ok(SIGTRAP) => {
348                    // breakpoint
349                    let out: Option<Result<(), RuntimeError>> =
350                        with_breakpoint_map(|bkpt_map| -> Option<Result<(), RuntimeError>> {
351                            bkpt_map.and_then(|x| x.get(&(fault.ip.get()))).map(
352                                |x| -> Result<(), RuntimeError> {
353                                    x(BreakpointInfo {
354                                        fault: Some(&fault),
355                                    })
356                                },
357                            )
358                        });
359                    match out {
360                        Some(Ok(())) => {
361                            return false;
362                        }
363                        Some(Err(e)) => {
364                            unwind_result = Some(Box::new(e));
365                            return true;
366                        }
367                        None => {}
368                    }
369                }
370                Ok(SIGSEGV) | Ok(SIGBUS) => {
371                    if fault.faulting_addr as usize == get_wasm_interrupt_signal_mem() as usize {
372                        is_suspend_signal = true;
373                        clear_wasm_interrupt();
374                        if INTERRUPT_SIGNAL_DELIVERED.swap(false, Ordering::SeqCst) {
375                            WAS_SIGINT_TRIGGERED.with(|x| x.set(true));
376                        }
377                    }
378                }
379                _ => {}
380            }
381
382            // Now we have looked up all possible handler tables but failed to find a handler
383            // for this exception that allows a normal return.
384            //
385            // So here we check whether this exception is caused by a suspend signal, return the
386            // state image if so, or throw the exception out otherwise.
387
388            let ctx: &mut vm::Ctx = &mut **CURRENT_CTX.with(|x| x.get());
389            let es_image = fault
390                .read_stack(None)
391                .expect("fault.read_stack() failed. Broken invariants?");
392
393            if is_suspend_signal {
394                // If this is a suspend signal, we parse the runtime state and return the resulting image.
395                let image = build_instance_image(ctx, es_image);
396                unwind_result = Some(Box::new(RuntimeError::InstanceImage(Box::new(image))));
397            } else {
398                // Otherwise, this is a real exception and we just throw it to the caller.
399                if !es_image.frames.is_empty() {
400                    eprintln!(
401                        "\n{}",
402                        "Wasmer encountered an error while running your WebAssembly program."
403                    );
404                    es_image.print_backtrace_if_needed();
405                }
406
407                // Look up the exception tables and try to find an exception code.
408                let exc_code = CURRENT_CODE_VERSIONS.with(|versions| {
409                    let versions = versions.borrow();
410                    for v in versions.iter() {
411                        if let Some(table) = v.runnable_module.get_exception_table() {
412                            let ip = fault.ip.get();
413                            let end = v.base + v.msm.total_size;
414                            if ip >= v.base && ip < end {
415                                if let Some(exc_code) = table.offset_to_code.get(&(ip - v.base)) {
416                                    return Some(*exc_code);
417                                }
418                            }
419                        }
420                    }
421                    None
422                });
423                if let Some(code) = exc_code {
424                    unwind_result =
425                        Some(Box::new(RuntimeError::InvokeError(InvokeError::TrapCode {
426                            code,
427                            // TODO:
428                            srcloc: 0,
429                        })));
430                }
431            }
432
433            true
434        });
435
436        if should_unwind {
437            begin_unsafe_unwind(get_unwind_result(unwind_result));
438        }
439    }
440}
441
442extern "C" fn sigint_handler(
443    _signum: ::nix::libc::c_int,
444    _siginfo: *mut siginfo_t,
445    _ucontext: *mut c_void,
446) {
447    if INTERRUPT_SIGNAL_DELIVERED.swap(true, Ordering::SeqCst) {
448        eprintln!("Got another SIGINT before trap is triggered on WebAssembly side, aborting");
449        process::abort();
450    }
451    unsafe {
452        set_wasm_interrupt();
453    }
454}
455
456/// Ensure the signal handler is installed.
457pub fn ensure_sighandler() {
458    INSTALL_SIGHANDLER.call_once(|| unsafe {
459        install_sighandler();
460    });
461}
462
463static INSTALL_SIGHANDLER: Once = Once::new();
464
465unsafe fn install_sighandler() {
466    let sa_trap = SigAction::new(
467        SigHandler::SigAction(signal_trap_handler),
468        SaFlags::SA_ONSTACK,
469        SigSet::empty(),
470    );
471    sigaction(SIGFPE, &sa_trap).unwrap();
472    sigaction(SIGILL, &sa_trap).unwrap();
473    sigaction(SIGSEGV, &sa_trap).unwrap();
474    sigaction(SIGBUS, &sa_trap).unwrap();
475    sigaction(SIGTRAP, &sa_trap).unwrap();
476
477    let sa_interrupt = SigAction::new(
478        SigHandler::SigAction(sigint_handler),
479        SaFlags::SA_ONSTACK,
480        SigSet::empty(),
481    );
482    sigaction(SIGINT, &sa_interrupt).unwrap();
483}
484
485#[derive(Debug, Clone)]
486/// Info about the fault
487pub struct FaultInfo {
488    /// Faulting address.
489    pub faulting_addr: *const c_void,
490    /// Instruction pointer.
491    pub ip: &'static Cell<usize>,
492    /// Values of known registers.
493    pub known_registers: [Option<u64>; 32],
494}
495
496impl FaultInfo {
497    /// Parses the stack and builds an execution state image.
498    pub unsafe fn read_stack(&self, max_depth: Option<usize>) -> Option<ExecutionStateImage> {
499        let rsp = self.known_registers[X64Register::GPR(GPR::RSP).to_index().0]?;
500
501        Some(CURRENT_CODE_VERSIONS.with(|versions| {
502            let versions = versions.borrow();
503            read_stack(
504                || versions.iter(),
505                rsp as usize as *const u64,
506                self.known_registers,
507                Some(self.ip.get() as u64),
508                max_depth,
509            )
510        }))
511    }
512}
513
514#[cfg(all(target_os = "freebsd", target_arch = "aarch64"))]
515/// Get fault info from siginfo and ucontext.
516pub unsafe fn get_fault_info(siginfo: *const c_void, ucontext: *mut c_void) -> FaultInfo {
517    #[repr(C)]
518    pub struct ucontext_t {
519        uc_sigmask: libc::sigset_t,
520        uc_mcontext: mcontext_t,
521        uc_link: *mut ucontext_t,
522        uc_stack: libc::stack_t,
523        uc_flags: i32,
524        __spare__: [i32; 4],
525    }
526    #[repr(C)]
527    pub struct gpregs {
528        gp_x: [u64; 30],
529        gp_lr: u64,
530        gp_sp: u64,
531        gp_elr: u64,
532        gp_spsr: u64,
533        gp_pad: i32,
534    };
535    #[repr(C)]
536    pub struct fpregs {
537        fp_q: [u128; 32],
538        fp_sr: u32,
539        fp_cr: u32,
540        fp_flags: i32,
541        fp_pad: i32,
542    };
543    #[repr(C)]
544    pub struct mcontext_t {
545        mc_gpregs: gpregs,
546        mc_fpregs: fpregs,
547        mc_flags: i32,
548        mc_pad: i32,
549        mc_spare: [u64; 8],
550    }
551
552    let siginfo = siginfo as *const siginfo_t;
553    let si_addr = (*siginfo).si_addr;
554
555    let ucontext = ucontext as *mut ucontext_t;
556    let gregs = &(*ucontext).uc_mcontext.mc_gpregs;
557
558    let mut known_registers: [Option<u64>; 32] = [None; 32];
559
560    known_registers[X64Register::GPR(GPR::R15).to_index().0] = Some(gregs.gp_x[15] as _);
561    known_registers[X64Register::GPR(GPR::R14).to_index().0] = Some(gregs.gp_x[14] as _);
562    known_registers[X64Register::GPR(GPR::R13).to_index().0] = Some(gregs.gp_x[13] as _);
563    known_registers[X64Register::GPR(GPR::R12).to_index().0] = Some(gregs.gp_x[12] as _);
564    known_registers[X64Register::GPR(GPR::R11).to_index().0] = Some(gregs.gp_x[11] as _);
565    known_registers[X64Register::GPR(GPR::R10).to_index().0] = Some(gregs.gp_x[10] as _);
566    known_registers[X64Register::GPR(GPR::R9).to_index().0] = Some(gregs.gp_x[9] as _);
567    known_registers[X64Register::GPR(GPR::R8).to_index().0] = Some(gregs.gp_x[8] as _);
568    known_registers[X64Register::GPR(GPR::RSI).to_index().0] = Some(gregs.gp_x[6] as _);
569    known_registers[X64Register::GPR(GPR::RDI).to_index().0] = Some(gregs.gp_x[7] as _);
570    known_registers[X64Register::GPR(GPR::RDX).to_index().0] = Some(gregs.gp_x[2] as _);
571    known_registers[X64Register::GPR(GPR::RCX).to_index().0] = Some(gregs.gp_x[1] as _);
572    known_registers[X64Register::GPR(GPR::RBX).to_index().0] = Some(gregs.gp_x[3] as _);
573    known_registers[X64Register::GPR(GPR::RAX).to_index().0] = Some(gregs.gp_x[0] as _);
574
575    known_registers[X64Register::GPR(GPR::RBP).to_index().0] = Some(gregs.gp_x[5] as _);
576    known_registers[X64Register::GPR(GPR::RSP).to_index().0] = Some(gregs.gp_x[28] as _);
577
578    FaultInfo {
579        faulting_addr: si_addr as usize as _,
580        ip: std::mem::transmute::<&mut u64, &'static Cell<usize>>(
581            &mut (*ucontext).uc_mcontext.mc_gpregs.gp_elr,
582        ),
583        known_registers,
584    }
585}
586
587#[cfg(all(target_os = "freebsd", target_arch = "x86_64"))]
588/// Get fault info from siginfo and ucontext.
589pub unsafe fn get_fault_info(siginfo: *const c_void, ucontext: *mut c_void) -> FaultInfo {
590    use crate::state::x64::XMM;
591    #[repr(C)]
592    pub struct ucontext_t {
593        uc_sigmask: libc::sigset_t,
594        uc_mcontext: mcontext_t,
595        uc_link: *mut ucontext_t,
596        uc_stack: libc::stack_t,
597        uc_flags: i32,
598        __spare__: [i32; 4],
599    }
600    #[repr(C)]
601    pub struct mcontext_t {
602        mc_onstack: u64,
603        mc_rdi: u64,
604        mc_rsi: u64,
605        mc_rdx: u64,
606        mc_rcx: u64,
607        mc_r8: u64,
608        mc_r9: u64,
609        mc_rax: u64,
610        mc_rbx: u64,
611        mc_rbp: u64,
612        mc_r10: u64,
613        mc_r11: u64,
614        mc_r12: u64,
615        mc_r13: u64,
616        mc_r14: u64,
617        mc_r15: u64,
618        mc_trapno: u32,
619        mc_fs: u16,
620        mc_gs: u16,
621        mc_addr: u64,
622        mc_flags: u32,
623        mc_es: u16,
624        mc_ds: u16,
625        mc_err: u64,
626        mc_rip: u64,
627        mc_cs: u64,
628        mc_rflags: u64,
629        mc_rsp: u64,
630        mc_ss: u64,
631        mc_len: i64,
632
633        mc_fpformat: i64,
634        mc_ownedfp: i64,
635        mc_savefpu: *const savefpu,
636        mc_fpstate: [i64; 63], // mc_fpstate[0] is a pointer to savefpu
637
638        mc_fsbase: u64,
639        mc_gsbase: u64,
640
641        mc_xfpustate: u64,
642        mc_xfpustate_len: u64,
643
644        mc_spare: [i64; 4],
645    }
646    #[repr(C)]
647    pub struct xmmacc {
648        element: [u32; 4],
649    }
650    #[repr(C)]
651    pub struct __envxmm64 {
652        en_cw: u16,
653        en_sw: u16,
654        en_tw: u8,
655        en_zero: u8,
656        en_opcode: u16,
657        en_rip: u64,
658        en_rdp: u64,
659        en_mxcsr: u32,
660        en_mxcsr_mask: u32,
661    }
662    #[repr(C)]
663    pub struct fpacc87 {
664        fp_bytes: [u8; 10],
665    }
666    #[repr(C)]
667    pub struct sv_fp {
668        fp_acc: fpacc87,
669        fp_pad: [u8; 6],
670    }
671    #[repr(C, align(16))]
672    pub struct savefpu {
673        sv_env: __envxmm64,
674        sv_fp_t: [sv_fp; 8],
675        sv_xmm: [xmmacc; 16],
676        sv_pad: [u8; 96],
677    }
678
679    let siginfo = siginfo as *const siginfo_t;
680    let si_addr = (*siginfo).si_addr;
681
682    let ucontext = ucontext as *mut ucontext_t;
683    let gregs = &mut (*ucontext).uc_mcontext;
684
685    fn read_xmm(reg: &xmmacc) -> u64 {
686        (reg.element[0] as u64) | ((reg.element[1] as u64) << 32)
687    }
688
689    let mut known_registers: [Option<u64>; 32] = [None; 32];
690    known_registers[X64Register::GPR(GPR::R15).to_index().0] = Some(gregs.mc_r15);
691    known_registers[X64Register::GPR(GPR::R14).to_index().0] = Some(gregs.mc_r14);
692    known_registers[X64Register::GPR(GPR::R13).to_index().0] = Some(gregs.mc_r13);
693    known_registers[X64Register::GPR(GPR::R12).to_index().0] = Some(gregs.mc_r12);
694    known_registers[X64Register::GPR(GPR::R11).to_index().0] = Some(gregs.mc_r11);
695    known_registers[X64Register::GPR(GPR::R10).to_index().0] = Some(gregs.mc_r10);
696    known_registers[X64Register::GPR(GPR::R9).to_index().0] = Some(gregs.mc_r9);
697    known_registers[X64Register::GPR(GPR::R8).to_index().0] = Some(gregs.mc_r8);
698    known_registers[X64Register::GPR(GPR::RSI).to_index().0] = Some(gregs.mc_rsi);
699    known_registers[X64Register::GPR(GPR::RDI).to_index().0] = Some(gregs.mc_rdi);
700    known_registers[X64Register::GPR(GPR::RDX).to_index().0] = Some(gregs.mc_rdx);
701    known_registers[X64Register::GPR(GPR::RCX).to_index().0] = Some(gregs.mc_rcx);
702    known_registers[X64Register::GPR(GPR::RBX).to_index().0] = Some(gregs.mc_rbx);
703    known_registers[X64Register::GPR(GPR::RAX).to_index().0] = Some(gregs.mc_rax);
704
705    known_registers[X64Register::GPR(GPR::RBP).to_index().0] = Some(gregs.mc_rbp);
706    known_registers[X64Register::GPR(GPR::RSP).to_index().0] = Some(gregs.mc_rsp);
707
708    // https://lists.freebsd.org/pipermail/freebsd-arch/2011-December/012077.html
709    // https://people.freebsd.org/~kib/misc/defer_sig.c
710    const _MC_HASFPXSTATE: u32 = 0x4;
711    if (gregs.mc_flags & _MC_HASFPXSTATE) == 0 {
712        // XXX mc_fpstate[0] is actually a pointer to a struct savefpu
713        let fpregs = &*(*ucontext).uc_mcontext.mc_savefpu;
714        known_registers[X64Register::XMM(XMM::XMM0).to_index().0] =
715            Some(read_xmm(&fpregs.sv_xmm[0]));
716        known_registers[X64Register::XMM(XMM::XMM1).to_index().0] =
717            Some(read_xmm(&fpregs.sv_xmm[1]));
718        known_registers[X64Register::XMM(XMM::XMM2).to_index().0] =
719            Some(read_xmm(&fpregs.sv_xmm[2]));
720        known_registers[X64Register::XMM(XMM::XMM3).to_index().0] =
721            Some(read_xmm(&fpregs.sv_xmm[3]));
722        known_registers[X64Register::XMM(XMM::XMM4).to_index().0] =
723            Some(read_xmm(&fpregs.sv_xmm[4]));
724        known_registers[X64Register::XMM(XMM::XMM5).to_index().0] =
725            Some(read_xmm(&fpregs.sv_xmm[5]));
726        known_registers[X64Register::XMM(XMM::XMM6).to_index().0] =
727            Some(read_xmm(&fpregs.sv_xmm[6]));
728        known_registers[X64Register::XMM(XMM::XMM7).to_index().0] =
729            Some(read_xmm(&fpregs.sv_xmm[7]));
730        known_registers[X64Register::XMM(XMM::XMM8).to_index().0] =
731            Some(read_xmm(&fpregs.sv_xmm[8]));
732        known_registers[X64Register::XMM(XMM::XMM9).to_index().0] =
733            Some(read_xmm(&fpregs.sv_xmm[9]));
734        known_registers[X64Register::XMM(XMM::XMM10).to_index().0] =
735            Some(read_xmm(&fpregs.sv_xmm[10]));
736        known_registers[X64Register::XMM(XMM::XMM11).to_index().0] =
737            Some(read_xmm(&fpregs.sv_xmm[11]));
738        known_registers[X64Register::XMM(XMM::XMM12).to_index().0] =
739            Some(read_xmm(&fpregs.sv_xmm[12]));
740        known_registers[X64Register::XMM(XMM::XMM13).to_index().0] =
741            Some(read_xmm(&fpregs.sv_xmm[13]));
742        known_registers[X64Register::XMM(XMM::XMM14).to_index().0] =
743            Some(read_xmm(&fpregs.sv_xmm[14]));
744        known_registers[X64Register::XMM(XMM::XMM15).to_index().0] =
745            Some(read_xmm(&fpregs.sv_xmm[15]));
746    }
747
748    FaultInfo {
749        faulting_addr: si_addr,
750        ip: std::mem::transmute::<&mut u64, &'static Cell<usize>>(
751            &mut (*ucontext).uc_mcontext.mc_rip,
752        ),
753        known_registers,
754    }
755}
756
757#[cfg(all(
758    any(target_os = "linux", target_os = "android"),
759    target_arch = "aarch64"
760))]
761/// Get fault info from siginfo and ucontext.
762pub unsafe fn get_fault_info(siginfo: *const c_void, ucontext: *mut c_void) -> FaultInfo {
763    #[allow(dead_code)]
764    #[allow(non_camel_case_types)]
765    #[repr(packed)]
766    struct sigcontext {
767        fault_address: u64,
768        regs: [u64; 31],
769        sp: u64,
770        pc: u64,
771        pstate: u64,
772        reserved: [u8; 4096],
773    }
774
775    #[allow(dead_code)]
776    #[allow(non_camel_case_types)]
777    #[repr(packed)]
778    struct ucontext {
779        unknown: [u8; 176],
780        uc_mcontext: sigcontext,
781    }
782
783    #[allow(dead_code)]
784    #[allow(non_camel_case_types)]
785    #[repr(C)]
786    struct siginfo_t {
787        si_signo: i32,
788        si_errno: i32,
789        si_code: i32,
790        si_addr: u64,
791        // ...
792    }
793
794    let siginfo = siginfo as *const siginfo_t;
795    let si_addr = (*siginfo).si_addr;
796
797    let ucontext = ucontext as *mut ucontext;
798    let gregs = &(*ucontext).uc_mcontext.regs;
799
800    let mut known_registers: [Option<u64>; 32] = [None; 32];
801
802    known_registers[X64Register::GPR(GPR::R15).to_index().0] = Some(gregs[15] as _);
803    known_registers[X64Register::GPR(GPR::R14).to_index().0] = Some(gregs[14] as _);
804    known_registers[X64Register::GPR(GPR::R13).to_index().0] = Some(gregs[13] as _);
805    known_registers[X64Register::GPR(GPR::R12).to_index().0] = Some(gregs[12] as _);
806    known_registers[X64Register::GPR(GPR::R11).to_index().0] = Some(gregs[11] as _);
807    known_registers[X64Register::GPR(GPR::R10).to_index().0] = Some(gregs[10] as _);
808    known_registers[X64Register::GPR(GPR::R9).to_index().0] = Some(gregs[9] as _);
809    known_registers[X64Register::GPR(GPR::R8).to_index().0] = Some(gregs[8] as _);
810    known_registers[X64Register::GPR(GPR::RSI).to_index().0] = Some(gregs[6] as _);
811    known_registers[X64Register::GPR(GPR::RDI).to_index().0] = Some(gregs[7] as _);
812    known_registers[X64Register::GPR(GPR::RDX).to_index().0] = Some(gregs[2] as _);
813    known_registers[X64Register::GPR(GPR::RCX).to_index().0] = Some(gregs[1] as _);
814    known_registers[X64Register::GPR(GPR::RBX).to_index().0] = Some(gregs[3] as _);
815    known_registers[X64Register::GPR(GPR::RAX).to_index().0] = Some(gregs[0] as _);
816
817    known_registers[X64Register::GPR(GPR::RBP).to_index().0] = Some(gregs[5] as _);
818    known_registers[X64Register::GPR(GPR::RSP).to_index().0] = Some(gregs[28] as _);
819
820    FaultInfo {
821        faulting_addr: si_addr as usize as _,
822        ip: std::mem::transmute::<&mut u64, &'static Cell<usize>>(&mut (*ucontext).uc_mcontext.pc),
823        known_registers,
824    }
825}
826
827#[cfg(all(
828    any(target_os = "linux", target_os = "android"),
829    target_arch = "x86_64"
830))]
831/// Get fault info from siginfo and ucontext.
832pub unsafe fn get_fault_info(siginfo: *const c_void, ucontext: *mut c_void) -> FaultInfo {
833    use libc::{
834        ucontext_t, REG_R10, REG_R11, REG_R12, REG_R13, REG_R14, REG_R15, REG_R8, REG_R9, REG_RAX,
835        REG_RBP, REG_RBX, REG_RCX, REG_RDI, REG_RDX, REG_RIP, REG_RSI, REG_RSP,
836    };
837
838    #[cfg(not(target_env = "musl"))]
839    fn read_xmm(reg: &libc::_libc_xmmreg) -> u64 {
840        (reg.element[0] as u64) | ((reg.element[1] as u64) << 32)
841    }
842
843    #[allow(dead_code)]
844    #[repr(C)]
845    struct siginfo_t {
846        si_signo: i32,
847        si_errno: i32,
848        si_code: i32,
849        si_addr: u64,
850        // ...
851    }
852
853    let siginfo = siginfo as *const siginfo_t;
854    let si_addr = (*siginfo).si_addr;
855
856    let ucontext = ucontext as *mut ucontext_t;
857    let gregs = &mut (*ucontext).uc_mcontext.gregs;
858
859    let mut known_registers: [Option<u64>; 32] = [None; 32];
860    known_registers[X64Register::GPR(GPR::R15).to_index().0] = Some(gregs[REG_R15 as usize] as _);
861    known_registers[X64Register::GPR(GPR::R14).to_index().0] = Some(gregs[REG_R14 as usize] as _);
862    known_registers[X64Register::GPR(GPR::R13).to_index().0] = Some(gregs[REG_R13 as usize] as _);
863    known_registers[X64Register::GPR(GPR::R12).to_index().0] = Some(gregs[REG_R12 as usize] as _);
864    known_registers[X64Register::GPR(GPR::R11).to_index().0] = Some(gregs[REG_R11 as usize] as _);
865    known_registers[X64Register::GPR(GPR::R10).to_index().0] = Some(gregs[REG_R10 as usize] as _);
866    known_registers[X64Register::GPR(GPR::R9).to_index().0] = Some(gregs[REG_R9 as usize] as _);
867    known_registers[X64Register::GPR(GPR::R8).to_index().0] = Some(gregs[REG_R8 as usize] as _);
868    known_registers[X64Register::GPR(GPR::RSI).to_index().0] = Some(gregs[REG_RSI as usize] as _);
869    known_registers[X64Register::GPR(GPR::RDI).to_index().0] = Some(gregs[REG_RDI as usize] as _);
870    known_registers[X64Register::GPR(GPR::RDX).to_index().0] = Some(gregs[REG_RDX as usize] as _);
871    known_registers[X64Register::GPR(GPR::RCX).to_index().0] = Some(gregs[REG_RCX as usize] as _);
872    known_registers[X64Register::GPR(GPR::RBX).to_index().0] = Some(gregs[REG_RBX as usize] as _);
873    known_registers[X64Register::GPR(GPR::RAX).to_index().0] = Some(gregs[REG_RAX as usize] as _);
874
875    known_registers[X64Register::GPR(GPR::RBP).to_index().0] = Some(gregs[REG_RBP as usize] as _);
876    known_registers[X64Register::GPR(GPR::RSP).to_index().0] = Some(gregs[REG_RSP as usize] as _);
877
878    // Skip reading floating point registers when building with musl libc.
879    // FIXME: Depends on https://github.com/rust-lang/libc/pull/1646
880    #[cfg(not(target_env = "musl"))]
881    {
882        use crate::state::x64::XMM;
883        if !(*ucontext).uc_mcontext.fpregs.is_null() {
884            let fpregs = &*(*ucontext).uc_mcontext.fpregs;
885            known_registers[X64Register::XMM(XMM::XMM0).to_index().0] =
886                Some(read_xmm(&fpregs._xmm[0]));
887            known_registers[X64Register::XMM(XMM::XMM1).to_index().0] =
888                Some(read_xmm(&fpregs._xmm[1]));
889            known_registers[X64Register::XMM(XMM::XMM2).to_index().0] =
890                Some(read_xmm(&fpregs._xmm[2]));
891            known_registers[X64Register::XMM(XMM::XMM3).to_index().0] =
892                Some(read_xmm(&fpregs._xmm[3]));
893            known_registers[X64Register::XMM(XMM::XMM4).to_index().0] =
894                Some(read_xmm(&fpregs._xmm[4]));
895            known_registers[X64Register::XMM(XMM::XMM5).to_index().0] =
896                Some(read_xmm(&fpregs._xmm[5]));
897            known_registers[X64Register::XMM(XMM::XMM6).to_index().0] =
898                Some(read_xmm(&fpregs._xmm[6]));
899            known_registers[X64Register::XMM(XMM::XMM7).to_index().0] =
900                Some(read_xmm(&fpregs._xmm[7]));
901            known_registers[X64Register::XMM(XMM::XMM8).to_index().0] =
902                Some(read_xmm(&fpregs._xmm[8]));
903            known_registers[X64Register::XMM(XMM::XMM9).to_index().0] =
904                Some(read_xmm(&fpregs._xmm[9]));
905            known_registers[X64Register::XMM(XMM::XMM10).to_index().0] =
906                Some(read_xmm(&fpregs._xmm[10]));
907            known_registers[X64Register::XMM(XMM::XMM11).to_index().0] =
908                Some(read_xmm(&fpregs._xmm[11]));
909            known_registers[X64Register::XMM(XMM::XMM12).to_index().0] =
910                Some(read_xmm(&fpregs._xmm[12]));
911            known_registers[X64Register::XMM(XMM::XMM13).to_index().0] =
912                Some(read_xmm(&fpregs._xmm[13]));
913            known_registers[X64Register::XMM(XMM::XMM14).to_index().0] =
914                Some(read_xmm(&fpregs._xmm[14]));
915            known_registers[X64Register::XMM(XMM::XMM15).to_index().0] =
916                Some(read_xmm(&fpregs._xmm[15]));
917        }
918    }
919
920    FaultInfo {
921        faulting_addr: si_addr as usize as _,
922        ip: std::mem::transmute::<&mut i64, &'static Cell<usize>>(&mut gregs[REG_RIP as usize]),
923        known_registers,
924    }
925}
926
927/// Get fault info from siginfo and ucontext.
928#[cfg(all(target_os = "macos", target_arch = "x86_64"))]
929pub unsafe fn get_fault_info(siginfo: *const c_void, ucontext: *mut c_void) -> FaultInfo {
930    use crate::state::x64::XMM;
931    #[allow(dead_code)]
932    #[repr(C)]
933    struct ucontext_t {
934        uc_onstack: u32,
935        uc_sigmask: u32,
936        uc_stack: libc::stack_t,
937        uc_link: *const ucontext_t,
938        uc_mcsize: u64,
939        uc_mcontext: *mut mcontext_t,
940    }
941    #[repr(C)]
942    struct exception_state {
943        trapno: u16,
944        cpu: u16,
945        err: u32,
946        faultvaddr: u64,
947    }
948    #[repr(C)]
949    struct regs {
950        rax: u64,
951        rbx: u64,
952        rcx: u64,
953        rdx: u64,
954        rdi: u64,
955        rsi: u64,
956        rbp: u64,
957        rsp: u64,
958        r8: u64,
959        r9: u64,
960        r10: u64,
961        r11: u64,
962        r12: u64,
963        r13: u64,
964        r14: u64,
965        r15: u64,
966        rip: u64,
967        rflags: u64,
968        cs: u64,
969        fs: u64,
970        gs: u64,
971    }
972    #[repr(C)]
973    struct fpstate {
974        _cwd: u16,
975        _swd: u16,
976        _ftw: u16,
977        _fop: u16,
978        _rip: u64,
979        _rdp: u64,
980        _mxcsr: u32,
981        _mxcr_mask: u32,
982        _st: [[u16; 8]; 8],
983        xmm: [[u64; 2]; 16],
984        _padding: [u32; 24],
985    }
986    #[allow(dead_code)]
987    #[repr(C)]
988    struct mcontext_t {
989        es: exception_state,
990        ss: regs,
991        fs: fpstate,
992    }
993
994    let siginfo = siginfo as *const siginfo_t;
995    let si_addr = (*siginfo).si_addr;
996
997    let ucontext = ucontext as *mut ucontext_t;
998    let ss = &mut (*(*ucontext).uc_mcontext).ss;
999    let fs = &(*(*ucontext).uc_mcontext).fs;
1000
1001    let mut known_registers: [Option<u64>; 32] = [None; 32];
1002
1003    known_registers[X64Register::GPR(GPR::R15).to_index().0] = Some(ss.r15);
1004    known_registers[X64Register::GPR(GPR::R14).to_index().0] = Some(ss.r14);
1005    known_registers[X64Register::GPR(GPR::R13).to_index().0] = Some(ss.r13);
1006    known_registers[X64Register::GPR(GPR::R12).to_index().0] = Some(ss.r12);
1007    known_registers[X64Register::GPR(GPR::R11).to_index().0] = Some(ss.r11);
1008    known_registers[X64Register::GPR(GPR::R10).to_index().0] = Some(ss.r10);
1009    known_registers[X64Register::GPR(GPR::R9).to_index().0] = Some(ss.r9);
1010    known_registers[X64Register::GPR(GPR::R8).to_index().0] = Some(ss.r8);
1011    known_registers[X64Register::GPR(GPR::RSI).to_index().0] = Some(ss.rsi);
1012    known_registers[X64Register::GPR(GPR::RDI).to_index().0] = Some(ss.rdi);
1013    known_registers[X64Register::GPR(GPR::RDX).to_index().0] = Some(ss.rdx);
1014    known_registers[X64Register::GPR(GPR::RCX).to_index().0] = Some(ss.rcx);
1015    known_registers[X64Register::GPR(GPR::RBX).to_index().0] = Some(ss.rbx);
1016    known_registers[X64Register::GPR(GPR::RAX).to_index().0] = Some(ss.rax);
1017
1018    known_registers[X64Register::GPR(GPR::RBP).to_index().0] = Some(ss.rbp);
1019    known_registers[X64Register::GPR(GPR::RSP).to_index().0] = Some(ss.rsp);
1020
1021    known_registers[X64Register::XMM(XMM::XMM0).to_index().0] = Some(fs.xmm[0][0]);
1022    known_registers[X64Register::XMM(XMM::XMM1).to_index().0] = Some(fs.xmm[1][0]);
1023    known_registers[X64Register::XMM(XMM::XMM2).to_index().0] = Some(fs.xmm[2][0]);
1024    known_registers[X64Register::XMM(XMM::XMM3).to_index().0] = Some(fs.xmm[3][0]);
1025    known_registers[X64Register::XMM(XMM::XMM4).to_index().0] = Some(fs.xmm[4][0]);
1026    known_registers[X64Register::XMM(XMM::XMM5).to_index().0] = Some(fs.xmm[5][0]);
1027    known_registers[X64Register::XMM(XMM::XMM6).to_index().0] = Some(fs.xmm[6][0]);
1028    known_registers[X64Register::XMM(XMM::XMM7).to_index().0] = Some(fs.xmm[7][0]);
1029    known_registers[X64Register::XMM(XMM::XMM8).to_index().0] = Some(fs.xmm[8][0]);
1030    known_registers[X64Register::XMM(XMM::XMM9).to_index().0] = Some(fs.xmm[9][0]);
1031    known_registers[X64Register::XMM(XMM::XMM10).to_index().0] = Some(fs.xmm[10][0]);
1032    known_registers[X64Register::XMM(XMM::XMM11).to_index().0] = Some(fs.xmm[11][0]);
1033    known_registers[X64Register::XMM(XMM::XMM12).to_index().0] = Some(fs.xmm[12][0]);
1034    known_registers[X64Register::XMM(XMM::XMM13).to_index().0] = Some(fs.xmm[13][0]);
1035    known_registers[X64Register::XMM(XMM::XMM14).to_index().0] = Some(fs.xmm[14][0]);
1036    known_registers[X64Register::XMM(XMM::XMM15).to_index().0] = Some(fs.xmm[15][0]);
1037
1038    FaultInfo {
1039        faulting_addr: si_addr,
1040        ip: std::mem::transmute::<&mut u64, &'static Cell<usize>>(&mut ss.rip),
1041        known_registers,
1042    }
1043}