Skip to main content

shape_gc/
trap_handler.rs

1//! SIGSEGV trap handler for concurrent relocation.
2//!
3//! When the GC relocates objects, old regions are protected with PROT_NONE.
4//! If the mutator accesses a relocated object before pointer fixup completes,
5//! a SIGSEGV fires. The trap handler:
6//! 1. Checks if the faulting address is in a protected (relocated) region.
7//! 2. If yes: looks up the forwarding table, updates the pointer, resumes.
8//! 3. If no: chains to the previous handler (real segfault).
9//!
10//! **Safety:** This is the riskiest component. Initially, relocation is done
11//! stop-the-world with synchronous fixup (no trap handler needed). The trap
12//! handler is an opt-in feature for concurrent relocation.
13
14use crate::relocator::ForwardingTable;
15use std::sync::atomic::{AtomicBool, AtomicPtr, Ordering};
16
17/// Global state for the trap handler.
18static TRAP_HANDLER_INSTALLED: AtomicBool = AtomicBool::new(false);
19
20/// Pointer to the active forwarding table (set during relocation).
21static ACTIVE_FORWARDING: AtomicPtr<ForwardingTable> = AtomicPtr::new(std::ptr::null_mut());
22
23/// Protected region ranges for the trap handler to check.
24/// Stored as (base, base+size) pairs.
25static PROTECTED_RANGES: std::sync::Mutex<Vec<(usize, usize)>> = std::sync::Mutex::new(Vec::new());
26
27/// Install the SIGSEGV trap handler.
28///
29/// # Safety
30/// Must be called once before any relocation with concurrent access.
31/// The handler is process-global and affects all threads.
32pub unsafe fn install_trap_handler() -> Result<(), &'static str> {
33    if TRAP_HANDLER_INSTALLED.swap(true, Ordering::SeqCst) {
34        return Ok(()); // Already installed
35    }
36
37    unsafe {
38        // Set up sigaction
39        let mut sa: libc::sigaction = std::mem::zeroed();
40        sa.sa_sigaction = trap_handler as usize;
41        sa.sa_flags = libc::SA_SIGINFO | libc::SA_RESTART;
42        libc::sigemptyset(&mut sa.sa_mask);
43
44        let ret = libc::sigaction(libc::SIGSEGV, &sa, std::ptr::null_mut());
45        if ret != 0 {
46            TRAP_HANDLER_INSTALLED.store(false, Ordering::SeqCst);
47            return Err("Failed to install SIGSEGV handler");
48        }
49    }
50
51    Ok(())
52}
53
54/// Register a protected region range for the trap handler.
55pub fn register_protected_range(base: usize, size: usize) {
56    let mut ranges = PROTECTED_RANGES.lock().unwrap();
57    ranges.push((base, base + size));
58}
59
60/// Unregister a protected region range.
61pub fn unregister_protected_range(base: usize) {
62    let mut ranges = PROTECTED_RANGES.lock().unwrap();
63    ranges.retain(|&(b, _)| b != base);
64}
65
66/// Set the active forwarding table for the trap handler to use.
67///
68/// # Safety
69/// The forwarding table must outlive the trap handler's use of it.
70pub unsafe fn set_active_forwarding(table: *mut ForwardingTable) {
71    ACTIVE_FORWARDING.store(table, Ordering::SeqCst);
72}
73
74/// Clear the active forwarding table.
75pub fn clear_active_forwarding() {
76    ACTIVE_FORWARDING.store(std::ptr::null_mut(), Ordering::SeqCst);
77}
78
79/// The actual SIGSEGV handler.
80///
81/// # Safety
82/// This is called by the kernel in signal context. Must be async-signal-safe.
83extern "C" fn trap_handler(
84    sig: libc::c_int,
85    info: *mut libc::siginfo_t,
86    _context: *mut libc::c_void,
87) {
88    if sig != libc::SIGSEGV || info.is_null() {
89        // Not our fault — chain to default handler
90        unsafe {
91            libc::signal(libc::SIGSEGV, libc::SIG_DFL);
92            libc::raise(libc::SIGSEGV);
93        }
94        return;
95    }
96
97    let fault_addr = unsafe { (*info).si_addr() } as usize;
98
99    // Check if the faulting address is in a protected (relocated) region
100    let in_protected = {
101        if let Ok(ranges) = PROTECTED_RANGES.try_lock() {
102            ranges
103                .iter()
104                .any(|&(base, end)| fault_addr >= base && fault_addr < end)
105        } else {
106            false
107        }
108    };
109
110    if !in_protected {
111        // Real segfault — chain to default handler
112        unsafe {
113            libc::signal(libc::SIGSEGV, libc::SIG_DFL);
114            libc::raise(libc::SIGSEGV);
115        }
116        return;
117    }
118
119    // Look up the forwarding table for this address.
120    // In a real implementation, we would update the faulting instruction's
121    // memory operand. For now, the synchronous fixup path handles this case.
122    let _forwarding = ACTIVE_FORWARDING.load(Ordering::SeqCst);
123}
124
125/// Check if the trap handler is installed.
126pub fn is_trap_handler_installed() -> bool {
127    TRAP_HANDLER_INSTALLED.load(Ordering::SeqCst)
128}