// Crash diagnostics: interposes sigaction so Factorio's own crash handler runs after ours. // Ours logs the faulting PC, LR, fault address and the frame-pointer chain as image+offset, // which tools/symbolize_native.py resolves against the original factorio.dSYM on the Mac. #include #include #include #include #include #include "fx.h" static struct sigaction fx_previous[NSIG]; static int fx_reporting; static void fx_describe(const char *label, uintptr_t address) { Dl_info info; if (address && dladdr((void *)address, &info) && info.dli_fname) { const char *base = strrchr(info.dli_fname, '/'); fx_log("[fx-crash] %s %#lx %s+%#lx %s", label, (unsigned long)address, base ? base + 1 : info.dli_fname, (unsigned long)(address - (uintptr_t)info.dli_fbase), info.dli_sname ? info.dli_sname : ""); } else { fx_log("[fx-crash] %s %#lx", label, (unsigned long)address); } } static void fx_crash_handler(int sig, siginfo_t *si, void *context) { if (!fx_reporting++) { ucontext_t *uc = context; struct __darwin_arm_thread_state64 *ts = &uc->uc_mcontext->__ss; uintptr_t pc = (uintptr_t)__darwin_arm_thread_state64_get_pc(*ts); uintptr_t lr = (uintptr_t)__darwin_arm_thread_state64_get_lr(*ts); uintptr_t fp = (uintptr_t)__darwin_arm_thread_state64_get_fp(*ts); uintptr_t sp = (uintptr_t)__darwin_arm_thread_state64_get_sp(*ts); fx_log("[fx-crash] signal %d code %d fault address %p thread %p%s", sig, si->si_code, si->si_addr, (void *)pthread_self(), pthread_main_np() ? " (main)" : ""); fx_describe("pc", pc); fx_describe("lr", lr); for (int r = 0; r < 29; r++) fx_log("[fx-crash] x%d = %#llx", r, ts->__x[r]); fx_log("[fx-crash] sp = %#lx fp = %#lx", (unsigned long)sp, (unsigned long)fp); // Frame-pointer walk, bounded to plausible stack addresses above sp. for (int depth = 0; depth < 64 && fp && (fp & 7) == 0 && fp >= sp && fp - sp < (512u << 20); depth++) { uintptr_t next = ((uintptr_t *)fp)[0], ret = ((uintptr_t *)fp)[1]; char label[16]; snprintf(label, sizeof label, "#%d", depth); fx_describe(label, ret); if (next <= fp) break; fp = next; } } struct sigaction *prev = &fx_previous[sig]; if (prev->sa_flags & SA_SIGINFO) { if (prev->sa_sigaction) { prev->sa_sigaction(sig, si, context); return; } } else if (prev->sa_handler != SIG_DFL && prev->sa_handler != SIG_IGN) { prev->sa_handler(sig); return; } signal(sig, SIG_DFL); raise(sig); } static int fx_is_fatal(int sig) { return sig == SIGSEGV || sig == SIGBUS || sig == SIGILL || sig == SIGFPE || sig == SIGTRAP || sig == SIGABRT; } static int fx_sigaction(int sig, const struct sigaction *act, struct sigaction *old) { if (sig <= 0 || sig >= NSIG || !fx_is_fatal(sig) || !act) return sigaction(sig, act, old); if (old) *old = fx_previous[sig]; fx_previous[sig] = *act; struct sigaction ours = *act; ours.sa_sigaction = fx_crash_handler; ours.sa_flags |= SA_SIGINFO; return sigaction(sig, &ours, NULL); } static void (*fx_signal(int sig, void (*handler)(int)))(int) { if (sig <= 0 || sig >= NSIG || !fx_is_fatal(sig)) return signal(sig, handler); struct sigaction act = {0}, old; act.sa_handler = handler; fx_sigaction(sig, &act, &old); return old.sa_handler; } FX_EXPORT void fx_install_crash_reporter(void) { for (int sig = 1; sig < NSIG; sig++) { if (!fx_is_fatal(sig)) continue; struct sigaction act = {0}; act.sa_handler = SIG_DFL; fx_sigaction(sig, &act, NULL); } } __attribute__((used, section("__DATA,__interpose"))) static const struct { const void *replacement, *original; } fx_interpose[] = { {(const void *)fx_sigaction, (const void *)sigaction}, {(const void *)fx_signal, (const void *)signal}, };