From da7db81087d8326c887391b58794d3bea1cae6bc Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Sun, 30 Aug 2020 13:26:10 +0200 Subject: [PATCH] cortexm: fix stack size calculation with interrupts Interrupts store 32 bytes on the current stack, which may be a goroutine stack. After that the interrupt switches to the main stack pointer so nothing more is pushed to the current stack. However, these 32 bytes were not included in the stack size calculation. This commit adds those 32 bytes. The code is rather verbose, but that is intentional to make sure it is readable. This is tricky code that's hard to get right, so I'd rather keep it well documented. --- builder/build.go | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/builder/build.go b/builder/build.go index db31b886e..c02d23940 100644 --- a/builder/build.go +++ b/builder/build.go @@ -447,11 +447,28 @@ func modifyStackSizes(executable string, stackSizeLoads []string, stackSizes map return fmt.Errorf("could not find symbol %s in ELF file", name) } if fn.stackSizeType == stacksize.Bounded { - // Note: adding 4 for the stack canary. Even though the size may be + stackSize := uint32(fn.stackSize) + + // Adding 4 for the stack canary. Even though the size may be // automatically determined, stack overflow checking is still // important as the stack size cannot be determined for all // goroutines. - binary.LittleEndian.PutUint32(data[i*4:], uint32(fn.stackSize)+4) + stackSize += 4 + + // Add stack size used by interrupts. + switch elfFile.Machine { + case elf.EM_ARM: + // On Cortex-M (assumed here), this stack size is 8 words or 32 + // bytes. This is only to store the registers that the interrupt + // may modify, the interrupt will switch to the interrupt stack + // (MSP). + // Some background: + // https://interrupt.memfault.com/blog/cortex-m-rtos-context-switching + stackSize += 32 + } + + // Finally write the stack size to the binary. + binary.LittleEndian.PutUint32(data[i*4:], stackSize) } }