diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/runtime/arch_386.go | 7 | ||||
-rw-r--r-- | src/runtime/arch_amd64.go | 7 | ||||
-rw-r--r-- | src/runtime/arch_arm.go | 7 | ||||
-rw-r--r-- | src/runtime/arch_arm64.go | 7 | ||||
-rw-r--r-- | src/runtime/arch_mips.go | 7 | ||||
-rw-r--r-- | src/runtime/arch_mipsle.go | 7 | ||||
-rw-r--r-- | src/runtime/os_darwin.go | 8 | ||||
-rw-r--r-- | src/runtime/os_linux.go | 6 | ||||
-rw-r--r-- | src/runtime/runtime_unix.c | 56 | ||||
-rw-r--r-- | src/runtime/runtime_unix.go | 51 |
10 files changed, 157 insertions, 6 deletions
diff --git a/src/runtime/arch_386.go b/src/runtime/arch_386.go index 4e9cce72b..90ec8e8ba 100644 --- a/src/runtime/arch_386.go +++ b/src/runtime/arch_386.go @@ -9,7 +9,12 @@ const deferExtraRegs = 0 const callInstSize = 5 // "call someFunction" is 5 bytes -const linux_MAP_ANONYMOUS = 0x20 +const ( + linux_MAP_ANONYMOUS = 0x20 + linux_SIGBUS = 7 + linux_SIGILL = 4 + linux_SIGSEGV = 11 +) // Align on word boundary. func align(ptr uintptr) uintptr { diff --git a/src/runtime/arch_amd64.go b/src/runtime/arch_amd64.go index 3bb03e3c7..436d6e384 100644 --- a/src/runtime/arch_amd64.go +++ b/src/runtime/arch_amd64.go @@ -9,7 +9,12 @@ const deferExtraRegs = 0 const callInstSize = 5 // "call someFunction" is 5 bytes -const linux_MAP_ANONYMOUS = 0x20 +const ( + linux_MAP_ANONYMOUS = 0x20 + linux_SIGBUS = 7 + linux_SIGILL = 4 + linux_SIGSEGV = 11 +) // Align a pointer. // Note that some amd64 instructions (like movaps) expect 16-byte aligned diff --git a/src/runtime/arch_arm.go b/src/runtime/arch_arm.go index e28e85410..ea6b540d2 100644 --- a/src/runtime/arch_arm.go +++ b/src/runtime/arch_arm.go @@ -11,7 +11,12 @@ const deferExtraRegs = 0 const callInstSize = 4 // "bl someFunction" is 4 bytes -const linux_MAP_ANONYMOUS = 0x20 +const ( + linux_MAP_ANONYMOUS = 0x20 + linux_SIGBUS = 7 + linux_SIGILL = 4 + linux_SIGSEGV = 11 +) // Align on the maximum alignment for this platform (double). func align(ptr uintptr) uintptr { diff --git a/src/runtime/arch_arm64.go b/src/runtime/arch_arm64.go index 4e798e36b..6d3c856cf 100644 --- a/src/runtime/arch_arm64.go +++ b/src/runtime/arch_arm64.go @@ -9,7 +9,12 @@ const deferExtraRegs = 0 const callInstSize = 4 // "bl someFunction" is 4 bytes -const linux_MAP_ANONYMOUS = 0x20 +const ( + linux_MAP_ANONYMOUS = 0x20 + linux_SIGBUS = 7 + linux_SIGILL = 4 + linux_SIGSEGV = 11 +) // Align on word boundary. func align(ptr uintptr) uintptr { diff --git a/src/runtime/arch_mips.go b/src/runtime/arch_mips.go index bfaf890ae..5a7d05c89 100644 --- a/src/runtime/arch_mips.go +++ b/src/runtime/arch_mips.go @@ -9,7 +9,12 @@ const deferExtraRegs = 0 const callInstSize = 8 // "jal someFunc" is 4 bytes, plus a MIPS delay slot -const linux_MAP_ANONYMOUS = 0x800 +const ( + linux_MAP_ANONYMOUS = 0x800 + linux_SIGBUS = 10 + linux_SIGILL = 4 + linux_SIGSEGV = 11 +) // It appears that MIPS has a maximum alignment of 8 bytes. func align(ptr uintptr) uintptr { diff --git a/src/runtime/arch_mipsle.go b/src/runtime/arch_mipsle.go index b6bf7d516..498cf862b 100644 --- a/src/runtime/arch_mipsle.go +++ b/src/runtime/arch_mipsle.go @@ -9,7 +9,12 @@ const deferExtraRegs = 0 const callInstSize = 8 // "jal someFunc" is 4 bytes, plus a MIPS delay slot -const linux_MAP_ANONYMOUS = 0x800 +const ( + linux_MAP_ANONYMOUS = 0x800 + linux_SIGBUS = 10 + linux_SIGILL = 4 + linux_SIGSEGV = 11 +) // It appears that MIPS has a maximum alignment of 8 bytes. func align(ptr uintptr) uintptr { diff --git a/src/runtime/os_darwin.go b/src/runtime/os_darwin.go index eeb192dda..9255fb90f 100644 --- a/src/runtime/os_darwin.go +++ b/src/runtime/os_darwin.go @@ -22,6 +22,14 @@ const ( clock_MONOTONIC_RAW = 4 ) +// Source: +// https://opensource.apple.com/source/xnu/xnu-7195.141.2/bsd/sys/signal.h.auto.html +const ( + sig_SIGBUS = 10 + sig_SIGILL = 4 + sig_SIGSEGV = 11 +) + // https://opensource.apple.com/source/xnu/xnu-7195.141.2/EXTERNAL_HEADERS/mach-o/loader.h.auto.html type machHeader struct { magic uint32 diff --git a/src/runtime/os_linux.go b/src/runtime/os_linux.go index 403f00246..df5870a2d 100644 --- a/src/runtime/os_linux.go +++ b/src/runtime/os_linux.go @@ -23,6 +23,12 @@ const ( clock_MONOTONIC_RAW = 4 ) +const ( + sig_SIGBUS = linux_SIGBUS + sig_SIGILL = linux_SIGILL + sig_SIGSEGV = linux_SIGSEGV +) + // For the definition of the various header structs, see: // https://refspecs.linuxfoundation.org/elf/elf.pdf // Also useful: diff --git a/src/runtime/runtime_unix.c b/src/runtime/runtime_unix.c new file mode 100644 index 000000000..79dd7ce91 --- /dev/null +++ b/src/runtime/runtime_unix.c @@ -0,0 +1,56 @@ +//go:build none + +// This file is included on Darwin and Linux (despite the //go:build line above). + +#define _GNU_SOURCE +#define _XOPEN_SOURCE +#include <signal.h> +#include <unistd.h> +#include <stdint.h> +#include <ucontext.h> +#include <string.h> + +void tinygo_handle_fatal_signal(int sig, uintptr_t addr); + +static void signal_handler(int sig, siginfo_t *info, void *context) { + ucontext_t* uctx = context; + uintptr_t addr = 0; + #if __APPLE__ + #if __arm64__ + addr = uctx->uc_mcontext->__ss.__pc; + #elif __x86_64__ + addr = uctx->uc_mcontext->__ss.__rip; + #else + #error unknown architecture + #endif + #elif __linux__ + // Note: this can probably be simplified using the MC_PC macro in musl, + // but this works for now. + #if __arm__ + addr = uctx->uc_mcontext.arm_pc; + #elif __i386__ + addr = uctx->uc_mcontext.gregs[REG_EIP]; + #elif __x86_64__ + addr = uctx->uc_mcontext.gregs[REG_RIP]; + #else // aarch64, mips, maybe others + addr = uctx->uc_mcontext.pc; + #endif + #else + #error unknown platform + #endif + tinygo_handle_fatal_signal(sig, addr); +} + +void tinygo_register_fatal_signals(void) { + struct sigaction act = { 0 }; + // SA_SIGINFO: we want the 2 extra parameters + // SA_RESETHAND: only catch the signal once (the handler will re-raise the signal) + act.sa_flags = SA_SIGINFO | SA_RESETHAND; + act.sa_sigaction = &signal_handler; + + // Register the signal handler for common issues. There are more signals, + // which can be added if needed. + sigaction(SIGBUS, &act, NULL); + sigaction(SIGILL, &act, NULL); + sigaction(SIGSEGV, &act, NULL); +} diff --git a/src/runtime/runtime_unix.go b/src/runtime/runtime_unix.go index 8c5a42ff7..ba5d5a593 100644 --- a/src/runtime/runtime_unix.go +++ b/src/runtime/runtime_unix.go @@ -26,6 +26,9 @@ func abort() //export exit func exit(code int) +//export raise +func raise(sig int32) + //export clock_gettime func libc_clock_gettime(clk_id int32, ts *timespec) @@ -74,6 +77,10 @@ func main(argc int32, argv *unsafe.Pointer) int { main_argc = argc main_argv = argv + // Register some fatal signals, so that we can print slightly better error + // messages. + tinygo_register_fatal_signals() + // Obtain the initial stack pointer right before calling the run() function. // The run function has been moved to a separate (non-inlined) function so // that the correct stack pointer is read. @@ -119,6 +126,50 @@ func runMain() { run() } +//export tinygo_register_fatal_signals +func tinygo_register_fatal_signals() + +// Print fatal errors when they happen, including the instruction location. +// With the particular formatting below, `tinygo run` can extract the location +// where the signal happened and try to show the source location based on DWARF +// information. +// +//export tinygo_handle_fatal_signal +func tinygo_handle_fatal_signal(sig int32, addr uintptr) { + if panicStrategy() == panicStrategyTrap { + trap() + } + + // Print signal including the faulting instruction. + if addr != 0 { + printstring("panic: runtime error at ") + printptr(addr) + } else { + printstring("panic: runtime error") + } + printstring(": caught signal ") + switch sig { + case sig_SIGBUS: + println("SIGBUS") + case sig_SIGILL: + println("SIGILL") + case sig_SIGSEGV: + println("SIGSEGV") + default: + println(sig) + } + + // TODO: it might be interesting to also print the invalid address for + // SIGSEGV and SIGBUS. + + // Do *not* abort here, instead raise the same signal again. The signal is + // registered with SA_RESETHAND which means it executes only once. So when + // we raise the signal again below, the signal isn't handled specially but + // is handled in the default way (probably exiting the process, maybe with a + // core dump). + raise(sig) +} + //go:extern environ var environ *unsafe.Pointer |