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