aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--compileopts/target.go4
m---------lib/macos-minimal-sdk0
-rw-r--r--src/runtime/arch_386.go7
-rw-r--r--src/runtime/arch_amd64.go7
-rw-r--r--src/runtime/arch_arm.go7
-rw-r--r--src/runtime/arch_arm64.go7
-rw-r--r--src/runtime/arch_mips.go7
-rw-r--r--src/runtime/arch_mipsle.go7
-rw-r--r--src/runtime/os_darwin.go8
-rw-r--r--src/runtime/os_linux.go6
-rw-r--r--src/runtime/runtime_unix.c56
-rw-r--r--src/runtime/runtime_unix.go51
12 files changed, 161 insertions, 6 deletions
diff --git a/compileopts/target.go b/compileopts/target.go
index 3368e20c4..501d99f11 100644
--- a/compileopts/target.go
+++ b/compileopts/target.go
@@ -388,6 +388,8 @@ func defaultTarget(options *Options) (*TargetSpec, error) {
"-arch", llvmarch,
"-platform_version", "macos", platformVersion, platformVersion,
)
+ spec.ExtraFiles = append(spec.ExtraFiles,
+ "src/runtime/runtime_unix.c")
case "linux":
spec.Linker = "ld.lld"
spec.RTLib = "compiler-rt"
@@ -407,6 +409,8 @@ func defaultTarget(options *Options) (*TargetSpec, error) {
// proper threading.
spec.CFlags = append(spec.CFlags, "-mno-outline-atomics")
}
+ spec.ExtraFiles = append(spec.ExtraFiles,
+ "src/runtime/runtime_unix.c")
case "windows":
spec.Linker = "ld.lld"
spec.Libc = "mingw-w64"
diff --git a/lib/macos-minimal-sdk b/lib/macos-minimal-sdk
-Subproject ebb736fda2bec7cea38dcda807518b835a53952
+Subproject 91ac2eabd80f10d95cb4255c78999d9d2c45a3b
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