src/pkg/runtime/asm_arm.s | 4 ++++ src/pkg/runtime/traceback_arm.c | 13 +++++++++++++ diff --git a/src/pkg/runtime/asm_arm.s b/src/pkg/runtime/asm_arm.s index 024649be0769e9b6fe2df2b0ac52dcc4faebb3e6..1aea9036a78bdfe453b11aaf34760771c6d3f685 100644 --- a/src/pkg/runtime/asm_arm.s +++ b/src/pkg/runtime/asm_arm.s @@ -394,6 +394,10 @@ // called from deferreturn. // 1. grab stored LR for caller // 2. sub 4 bytes to get back to BL deferreturn // 3. B to fn +// TODO(rsc): Push things on stack and then use pop +// to load all registers simultaneously, so that a profiling +// interrupt can never see mismatched SP/LR/PC. +// (And double-check that pop is atomic in that way.) TEXT runtime·jmpdefer(SB), NOSPLIT, $0-8 MOVW 0(SP), LR MOVW $-4(LR), LR // BL deferreturn diff --git a/src/pkg/runtime/traceback_arm.c b/src/pkg/runtime/traceback_arm.c index 8d1fc542663e07564761fe1c43cf1e0acdd72818..d15244c2a96d7c8638d969e5710dd5dcc126297e 100644 --- a/src/pkg/runtime/traceback_arm.c +++ b/src/pkg/runtime/traceback_arm.c @@ -110,6 +110,19 @@ frame.fp = frame.sp + runtime·funcspdelta(f, frame.pc); if(runtime·topofstack(f)) { frame.lr = 0; flr = nil; + } else if(f->entry == (uintptr)runtime·jmpdefer) { + // jmpdefer modifies SP/LR/PC non-atomically. + // If a profiling interrupt arrives during jmpdefer, + // the stack unwind may see a mismatched register set + // and get confused. Stop if we see PC within jmpdefer + // to avoid that confusion. + // See golang.org/issue/8153. + // This check can be deleted if jmpdefer is changed + // to restore all three atomically using pop. + if(callback != nil) + runtime·throw("traceback_arm: found jmpdefer when tracing with callback"); + frame.lr = 0; + flr = nil; } else { if((n == 0 && frame.sp < frame.fp) || frame.lr == 0) frame.lr = *(uintptr*)frame.sp;