diff --git a/builder/sizes_test.go b/builder/sizes_test.go index c89c07588b..f786d9c826 100644 --- a/builder/sizes_test.go +++ b/builder/sizes_test.go @@ -43,8 +43,8 @@ func TestBinarySize(t *testing.T) { tests := []sizeTest{ // microcontrollers {"hifive1b", "examples/echo", 3884, 280, 0, 2268}, - {"microbit", "examples/serial", 2924, 388, 8, 2272}, - {"wioterminal", "examples/pininterrupt", 7365, 1491, 116, 6912}, + {"microbit", "examples/serial", 2852, 360, 8, 2272}, + {"wioterminal", "examples/pininterrupt", 7337, 1491, 116, 6912}, // TODO: also check wasm. Right now this is difficult, because // wasm binaries are run through wasm-opt and therefore the diff --git a/src/device/arm/cortexm.S b/src/device/arm/cortexm.S index e9b15aafe1..22b8357b63 100644 --- a/src/device/arm/cortexm.S +++ b/src/device/arm/cortexm.S @@ -1,29 +1,6 @@ .syntax unified .cfi_sections .debug_frame -.section .text.HardFault_Handler -.global HardFault_Handler -.type HardFault_Handler, %function -HardFault_Handler: - .cfi_startproc - // Put the old stack pointer in the first argument, for easy debugging. This - // is especially useful on Cortex-M0, which supports far fewer debug - // facilities. - mov r0, sp - - // Load the default stack pointer from address 0 so that we can call normal - // functions again that expect a working stack. However, it will corrupt the - // old stack so the function below must not attempt to recover from this - // fault. - movs r3, #0 - ldr r3, [r3] - mov sp, r3 - - // Continue handling this error in Go. - bl handleHardFault - .cfi_endproc -.size HardFault_Handler, .-HardFault_Handler - // This is a convenience function for semihosting support. // At some point, this should be replaced by inline assembly. .section .text.SemihostingCall diff --git a/src/runtime/panic.go b/src/runtime/panic.go index e18a306349..5eac60ecd9 100644 --- a/src/runtime/panic.go +++ b/src/runtime/panic.go @@ -93,6 +93,9 @@ func runtimePanicAt(addr unsafe.Pointer, msg string) { trap() } if hasReturnAddr { + // Note: the string "panic: runtime error at " is also used in + // runtime_cortexm_hardfault.go. It is kept the same so that the string + // can be deduplicated by the compiler. printstring("panic: runtime error at ") printptr(uintptr(addr) - callInstSize) printstring(": ") diff --git a/src/runtime/runtime.go b/src/runtime/runtime.go index 3bd98319e8..c9b0959384 100644 --- a/src/runtime/runtime.go +++ b/src/runtime/runtime.go @@ -57,6 +57,12 @@ func memzero(ptr unsafe.Pointer, size uintptr) // the current stack pointer in a platform-independent way. func stacksave() unsafe.Pointer +// Special LLVM intrinsic that returns the SP register on entry to the calling +// function. +// +//export llvm.sponentry.p0 +func llvm_sponentry() unsafe.Pointer + //export strlen func strlen(ptr unsafe.Pointer) uintptr diff --git a/src/runtime/runtime_cortexm_hardfault.go b/src/runtime/runtime_cortexm_hardfault.go index b2449ed910..a1cfc2c3d6 100644 --- a/src/runtime/runtime_cortexm_hardfault.go +++ b/src/runtime/runtime_cortexm_hardfault.go @@ -2,39 +2,25 @@ package runtime -import ( - "unsafe" -) - // This function is called at HardFault. -// Before this function is called, the stack pointer is reset to the initial -// stack pointer (loaded from address 0x0) and the previous stack pointer is -// passed as an argument to this function. This allows for easy inspection of -// the stack the moment a HardFault occurs, but it means that the stack will be -// corrupted by this function and thus this handler must not attempt to recover. // // For details, see: // https://community.arm.com/developer/ip-products/system/f/embedded-forum/3257/debugging-a-cortex-m0-hard-fault // https://blog.feabhas.com/2013/02/developing-a-generic-hard-fault-handler-for-arm-cortex-m3cortex-m4/ // -//export handleHardFault -func handleHardFault(sp *interruptStack) { - print("fatal error: ") - if uintptr(unsafe.Pointer(sp)) < 0x20000000 { - print("stack overflow") - } else { - // TODO: try to find the cause of the hard fault. Especially on - // Cortex-M3 and higher it is possible to find more detailed information - // in special status registers. - print("HardFault") - } - print(" with sp=", sp) - if uintptr(unsafe.Pointer(&sp.PC)) >= 0x20000000 { - // Only print the PC if it points into memory. - // It may not point into memory during a stack overflow, so check that - // first before accessing the stack. - print(" pc=", sp.PC) - } +//export HardFault_Handler +func HardFault_Handler() { + // Obtain the stack pointer as it was on entry to the HardFault. It contains + // the registers that were pushed by the NVIC and that we can now read back + // to print the PC value at the time of the hard fault, for example. + sp := (*interruptStack)(llvm_sponentry()) + + // Note: by reusing the string "panic: runtime error at " we save a little + // bit in terms of code size as the string can be deduplicated. + print("panic: runtime error at ", sp.PC, ": HardFault with sp=", sp) + // TODO: try to find the cause of the hard fault. Especially on Cortex-M3 + // and higher it is possible to find more detailed information in special + // status registers. println() abort() } diff --git a/src/runtime/runtime_cortexm_hardfault_debug.go b/src/runtime/runtime_cortexm_hardfault_debug.go index ea6c507028..7e953a67de 100644 --- a/src/runtime/runtime_cortexm_hardfault_debug.go +++ b/src/runtime/runtime_cortexm_hardfault_debug.go @@ -18,8 +18,13 @@ const ( // See runtime_cortexm_hardfault.go // -//go:export handleHardFault -func handleHardFault(sp *interruptStack) { +//export HardFault_Handler +func HardFault_Handler() { + // Obtain the stack pointer as it was on entry to the HardFault. It contains + // the registers that were pushed by the NVIC and that we can now read back + // to print the PC value at the time of the hard fault, for example. + sp := (*interruptStack)(llvm_sponentry()) + fault := GetFaultStatus() spValid := !fault.Bus().ImpreciseDataBusError()