- Add sigsegv stacktrace in test and regular errors for Darwin Arm64. #1105

This commit is contained in:
Christoffer Lerno
2025-11-20 20:36:33 +01:00
committed by Christoffer Lerno
parent a50de26c5d
commit 5b83108dd1
5 changed files with 131 additions and 38 deletions

View File

@@ -135,11 +135,14 @@ macro bool @typeis(#value, $Type) @const @builtin @deprecated("Use `$typeof(#val
}
fn bool print_backtrace(String message, int backtraces_to_ignore) @if (env::NATIVE_STACKTRACE) => @stack_mem(0x1100; Allocator smem)
fn bool print_backtrace(String message, int backtraces_to_ignore, void *added_backtrace = null) @if (env::NATIVE_STACKTRACE) => @stack_mem(0x1100; Allocator smem)
{
void*[256] buffer;
void*[] backtraces = backtrace::capture_current(&buffer);
backtraces_to_ignore++;
if (added_backtrace)
{
backtraces[++backtraces_to_ignore] = added_backtrace;
}
@stack_mem(2048; Allocator mem)
{
BacktraceList? backtrace = backtrace::symbolize_backtrace(mem, backtraces);
@@ -941,23 +944,20 @@ macro void* get_returnaddress(int n)
}
module std::core::builtin @if((env::LINUX || env::ANDROID || env::DARWIN) && env::COMPILER_SAFE_MODE && env::DEBUG_SYMBOLS);
import libc, std::io;
import libc, std::io, std::os::posix;
fn void sig_panic(String message)
{
default_panic(message, "???", "???", 0);
}
SignalFunction old_bus_error;
SignalFunction old_segmentation_fault;
fn void sig_bus_error(CInt i)
fn void sig_bus_error(CInt i, void* info, void* context)
{
$if !env::NATIVE_STACKTRACE:
sig_panic("Illegal memory access.");
$else
$if $defined(io::stderr):
if (!print_backtrace("Illegal memory access.", 1))
if (!print_backtrace("Illegal memory access.", 2, posix::stack_instruction(context)))
{
io::eprintn("\nERROR: 'Illegal memory access'.");
}
@@ -966,13 +966,13 @@ fn void sig_bus_error(CInt i)
$$trap();
}
fn void sig_segmentation_fault(CInt i)
fn void sig_segmentation_fault(CInt i, void* p1, void* context)
{
$if !env::NATIVE_STACKTRACE:
sig_panic("Out of bounds memory access.");
$else
$if $defined(io::stderr):
if (!print_backtrace("Out of bounds memory access.", 1))
if (!print_backtrace("Out of bounds memory access.", 2, posix::stack_instruction(context)))
{
io::eprintn("\nERROR: Memory error without backtrace, possible stack overflow.");
}
@@ -981,17 +981,12 @@ fn void sig_segmentation_fault(CInt i)
$$trap();
}
fn void install_signal_handler(CInt signal, SignalFunction func) @local
{
SignalFunction old = libc::signal(signal, func);
// Restore
if ((iptr)old > 1024) libc::signal(signal, old);
}
// Clean this up
fn void install_signal_handlers() @init(101) @local @if(env::BACKTRACE)
{
install_signal_handler(libc::SIGBUS, &sig_bus_error);
install_signal_handler(libc::SIGSEGV, &sig_segmentation_fault);
posix::install_signal_handler(libc::SIGBUS, &sig_bus_error);
posix::install_signal_handler(libc::SIGSEGV, &sig_segmentation_fault);
}

View File

@@ -4,8 +4,8 @@
module std::core::runtime;
import std::core::test @public;
import std::core::mem::allocator @public;
import libc, std::time, std::io, std::sort;
import std::os::env;
import libc, std::time, std::io, std::sort, std::os;
alias TestFn = fn void();
@@ -86,17 +86,21 @@ fn bool terminal_has_ansi_codes() @local => @pool()
$endif
}
fn void sig_bus_error(CInt i) @local
fn void sig_bus_error(CInt i, void*, void* context) @local @if(env::POSIX)
{
test_panic("Bus error", "Unknown", "Unknown", 0);
panic_test("Bus error", "Unknown", "Unknown", 1, posix::stack_instruction(context));
}
fn void sig_segmentation_fault(CInt i) @local
fn void sig_segmentation_fault(CInt i, void*, void* context) @local @if(env::POSIX)
{
test_panic("Segmentation fault", "Unknown", "Unknown", 0);
panic_test("Segmentation fault", "Unknown", "Unknown", 1, posix::stack_instruction(context));
}
fn void test_panic(String message, String file, String function, uint line) @local
{
panic_test(message, file, function, line);
}
fn void panic_test(String message, String file, String function, uint line, void* extra_trace = null) @local
{
if (test_context.is_in_panic) return;
test_context.is_in_panic = true;
@@ -106,7 +110,7 @@ fn void test_panic(String message, String file, String function, uint line) @loc
if (test_context.assert_print_backtrace)
{
$if env::NATIVE_STACKTRACE:
builtin::print_backtrace(message, 0);
builtin::print_backtrace(message, extra_trace ? 3 : 0, extra_trace);
$endif
}
io::printf("\nTest failed ^^^ ( %s:%s ) %s\n", file, line, message);
@@ -186,9 +190,9 @@ fn bool run_tests(String[] args, TestUnit[] tests) @private
io::printn("There are no test units to run.");
return true; // no tests == technically a pass
}
$if !env::NO_LIBC:
libc::signal(libc::SIGBUS, &sig_bus_error);
libc::signal(libc::SIGSEGV, &sig_segmentation_fault);
$if !env::NO_LIBC && env::POSIX:
posix::install_signal_handler(libc::SIGBUS, &sig_bus_error);
posix::install_signal_handler(libc::SIGSEGV, &sig_segmentation_fault);
$endif
foreach (&unit : tests)
{

View File

@@ -0,0 +1,44 @@
module std::os::darwin @if(env::DARWIN);
alias __Darwin_sigset_t = uint;
alias __Darwin_size_t = usz;
struct __Darwin_arm_exception_state64
{
ulong __far; /* Virtual Fault Address */
uint __esr; /* Exception syndrome */
uint __exception; /* number of arm exception taken */
}
struct __Darwin_arm_thread_state64
{
ulong[29] __x; /* General purpose registers x0-x28 */
ulong __fp; /* Frame pointer x29 */
ulong __lr; /* Link register x30 */
ulong __sp; /* Stack pointer x31 */
ulong __pc; /* Program counter */
ulong __cpsr; /* Current program status register */
ulong __pad; /* Same size for 32-bit or 64-bit clients */
}
struct __Darwin_arm_neon_state64
{
uint128[32] __v;
uint __fpsr;
uint __fpcr;
}
struct __Darwin_mcontext64
{
__Darwin_arm_exception_state64 __es;
__Darwin_arm_thread_state64 __ss;
__Darwin_arm_neon_state64 __ns;
}
struct __Darwin_sigaltstack
{
void* ss_sp; /* signal stack base */
__Darwin_size_t ss_size; /* signal stack length */
int ss_flags; /* SA_DISABLE and/or SA_ONSTACK */
}

View File

@@ -1,5 +1,5 @@
module std::os::posix @if(env::POSIX);
import libc;
import libc, std::os::darwin;
struct Posix_spawn_file_actions_t
{
@@ -60,6 +60,15 @@ alias BacktraceFn = fn CInt(void** buffer, CInt size);
extern fn CInt backtrace(void** buffer, CInt size) @if(env::OPENBSD);
fn void install_signal_handler(CInt signal, SigActionFunction func)
{
Sigaction action = {
.sa_sigaction = func,
};
Sigaction old;
libc::sigaction(signal, &action, &old);
}
fn CInt backtrace(void** buffer, CInt size) @if(!env::OPENBSD)
{
if (size < 1) return 0;
@@ -75,12 +84,13 @@ fn CInt backtrace(void** buffer, CInt size) @if(!env::OPENBSD)
}
// Loop through the return addresses until we hit a signal.
// This avoids using the frame address.
SignalFunction restore_backtrace = fn void(CInt) {
libc::longjmp(&backtrace_jmpbuf, 1);
};
SignalFunction sig_bus = libc::signal(libc::SIGBUS, restore_backtrace);
SignalFunction sig_segv = libc::signal(libc::SIGSEGV, restore_backtrace);
SignalFunction sig_ill = libc::signal(libc::SIGILL, restore_backtrace);
Sigaction restore_backtrace = {
.sa_sigaction = fn void(CInt, void*, void*) { libc::longjmp(&backtrace_jmpbuf, 1); },
};
Sigaction sig_bus, sig_segv, sig_ill;
libc::sigaction(libc::SIGBUS, &restore_backtrace, &sig_bus);
libc::sigaction(libc::SIGSEGV, &restore_backtrace, &sig_segv);
libc::sigaction(libc::SIGILL, &restore_backtrace, &sig_ill);
void*[128] buffer_first;
int i = 0;
@@ -90,8 +100,47 @@ fn CInt backtrace(void** buffer, CInt size) @if(!env::OPENBSD)
buffer[i] = builtin::get_returnaddress(i);
if (!buffer[i]) break;
}
libc::signal(libc::SIGBUS, sig_bus);
libc::signal(libc::SIGSEGV, sig_segv);
libc::signal(libc::SIGILL, sig_ill);
Sigaction old;
libc::sigaction(libc::SIGBUS, &sig_bus, &old);
libc::sigaction(libc::SIGSEGV, &sig_segv, &old);
libc::sigaction(libc::SIGILL, &sig_ill, &old);
return i;
}
struct PosixUContext_t @if(env::DARWIN)
{
int uc_onstack;
__Darwin_sigset_t uc_sigmask; /* signal mask used by this context */
__Darwin_sigaltstack uc_stack; /* stack used by this context */
PosixUContext_t* uc_link; /* pointer to resuming context */
__Darwin_size_t uc_mcsize; /* size of the machine context passed in */
__Darwin_mcontext64* uc_mcontext; /* pointer to machine specific context */
}
alias PosixUContext_t @if(!env::DARWIN) = void;
macro void* stack_instruction(PosixUContext_t* uc)
{
$switch:
$case env::DARWIN && env::AARCH64:
return (void*)uc.uc_mcontext.__ss.__pc + 1;
/* $case env::DARWIN && env::X86_64:
return uc.uc_mcontext.__ss.__rip;
$case env::LINUX && env::X86:
return uc.uc_mcontext.gregs[REG_EIP];
$case env::LINUX && env::X86_64:
return uc.uc_mcontext.gregs[REG_RIP];
$case env::LINUX && env::AARCH64:
return uc.uc_mcontext.pc;
$case env::FREEBSD && env::X86_64:
return uc.uc_mcontext.mc_rip;
$case env::OPENBSD && env::X86_64:
return uc.sc_rip;
$case env::NETBSD:
uc.uc_mcontext.__gregs[_REG_RIP];*/
$default:
return null;
$endswitch
}