Skip to main content

logicaffeine_forge/
segv_trace.rs

1//! Diagnostic SIGSEGV/SIGBUS tracer for root-causing faults inside JIT'd code.
2//!
3//! Installed only when `LOGOS_SEGV_TRAP` is set. On a fault it writes the
4//! faulting data address (`si_addr`) and the instruction pointer (RIP) to
5//! stderr, then restores the default handler and re-raises so the process
6//! still dies with the original signal. The faulting *address value* is the
7//! tell: a small/odd value or a recognizable float bit-pattern (e.g.
8//! `0x3ff0000000000000` = `1.0`) means a non-pointer was dereferenced.
9
10#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
11pub fn install() {
12    use std::sync::Once;
13    static ONCE: Once = Once::new();
14    if std::env::var_os("LOGOS_SEGV_TRAP").is_none() {
15        return;
16    }
17    ONCE.call_once(|| unsafe {
18        let mut sa: libc::sigaction = std::mem::zeroed();
19        sa.sa_sigaction = handler as usize;
20        sa.sa_flags = libc::SA_SIGINFO;
21        libc::sigemptyset(&mut sa.sa_mask);
22        libc::sigaction(libc::SIGSEGV, &sa, std::ptr::null_mut());
23        libc::sigaction(libc::SIGBUS, &sa, std::ptr::null_mut());
24    });
25}
26
27#[cfg(not(all(target_os = "linux", target_arch = "x86_64")))]
28pub fn install() {}
29
30#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
31extern "C" fn handler(
32    sig: libc::c_int,
33    info: *mut libc::siginfo_t,
34    ucontext: *mut libc::c_void,
35) {
36    unsafe {
37        // si_addr lives at offset 16 of siginfo_t on x86-64 Linux (after
38        // si_signo/si_errno/si_code = 3 * i32 + padding to 8).
39        let addr_ptr = (info as *const u8).add(16) as *const usize;
40        let fault_addr = addr_ptr.read_unaligned();
41        // glibc x86-64: ucontext_t.uc_mcontext.gregs starts at offset 40.
42        // gregs index order: R8,R9,R10,R11,R12,R13,R14,R15,RDI,RSI,RBP,RBX,
43        // RDX,RAX,RCX,RSP,RIP (0..16).
44        let uc = ucontext as *const u8;
45        let greg = |i: usize| (uc.add(40 + i * 8) as *const i64).read_unaligned();
46        let (rdi, rsi, rdx, rax, rcx, rip) =
47            (greg(8), greg(9), greg(12), greg(13), greg(14), greg(16));
48        let r8 = greg(0);
49        let r9 = greg(1);
50        let rbx = greg(11);
51        // Faulting instruction bytes (rip is in an executable JIT page).
52        let mut code = [0u8; 16];
53        for (k, b) in code.iter_mut().enumerate() {
54            *b = (rip as *const u8).add(k).read();
55        }
56        let msg = format!(
57            "\n*** LOGOS_SEGV_TRAP: sig {sig} fault=0x{fault_addr:016x} rip=0x{rip:016x}\n    rdi(base)=0x{rdi:016x} rsi(sp)=0x{rsi:016x} rax=0x{rax:016x} rdx=0x{rdx:016x} rcx=0x{rcx:016x}\n    r8=0x{r8:016x} r9=0x{r9:016x} rbx=0x{rbx:016x}\n    code@rip={code:02x?} ***\n"
58        );
59        libc::write(2, msg.as_ptr() as *const libc::c_void, msg.len());
60        // Restore default and re-raise so we still die with the real signal.
61        let mut sa: libc::sigaction = std::mem::zeroed();
62        sa.sa_sigaction = libc::SIG_DFL;
63        libc::sigemptyset(&mut sa.sa_mask);
64        libc::sigaction(sig, &sa, std::ptr::null_mut());
65        libc::raise(sig);
66    }
67}