Commit 698bfa17 authored by Cherry Zhang's avatar Cherry Zhang

cmd/internal/obj: save link register in leaf function with non-empty frame on PPC64, ARM64, S390X

The runtime traceback code assumes non-empty frame has link
link register saved on LR architectures. Make sure it is so in
the assember.

Also make sure that LR is stored before update SP, so the traceback
code will not see a half-updated stack frame if a signal comes
during the execution of function prologue.

Fixes #17381.

Change-Id: I668b04501999b7f9b080275a2d1f8a57029cbbb3
Reviewed-on: https://go-review.googlesource.com/31760Reviewed-by: 's avatarMichael Munday <munday@ca.ibm.com>
parent cf73bbfa
......@@ -750,38 +750,54 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
if ctxt.Autosize == 0 {
break
}
aoffset = 0
}
// Frame is non-empty. Make sure to save link register, even if
// it is a leaf function, so that traceback works.
q = p
if ctxt.Autosize > aoffset {
q = ctxt.NewProg()
q.As = ASUB
// Frame size is too large for a MOVD.W instruction.
// Store link register before decrementing SP, so if a signal comes
// during the execution of the function prologue, the traceback
// code will not see a half-updated stack frame.
q = obj.Appendp(ctxt, q)
q.Lineno = p.Lineno
q.As = ASUB
q.From.Type = obj.TYPE_CONST
q.From.Offset = int64(ctxt.Autosize) - int64(aoffset)
q.From.Offset = int64(ctxt.Autosize)
q.Reg = REGSP
q.To.Type = obj.TYPE_REG
q.To.Reg = REGSP
q.Spadj = int32(q.From.Offset)
q.Link = p.Link
p.Link = q
if cursym.Text.Mark&LEAF != 0 {
break
}
}
q.To.Reg = REGTMP
q1 = ctxt.NewProg()
q1.As = AMOVD
q1.Lineno = p.Lineno
q1.From.Type = obj.TYPE_REG
q1.From.Reg = REGLINK
q1.To.Type = obj.TYPE_MEM
q1.Scond = C_XPRE
q1.To.Offset = int64(-aoffset)
q1.To.Reg = REGSP
q1.Link = q.Link
q1.Spadj = aoffset
q.Link = q1
q = obj.Appendp(ctxt, q)
q.Lineno = p.Lineno
q.As = AMOVD
q.From.Type = obj.TYPE_REG
q.From.Reg = REGLINK
q.To.Type = obj.TYPE_MEM
q.To.Reg = REGTMP
q1 = obj.Appendp(ctxt, q)
q1.Lineno = p.Lineno
q1.As = AMOVD
q1.From.Type = obj.TYPE_REG
q1.From.Reg = REGTMP
q1.To.Type = obj.TYPE_REG
q1.To.Reg = REGSP
q1.Spadj = ctxt.Autosize
} else {
// small frame, update SP and save LR in a single MOVD.W instruction
q1 = obj.Appendp(ctxt, q)
q1.As = AMOVD
q1.Lineno = p.Lineno
q1.From.Type = obj.TYPE_REG
q1.From.Reg = REGLINK
q1.To.Type = obj.TYPE_MEM
q1.Scond = C_XPRE
q1.To.Offset = int64(-aoffset)
q1.To.Reg = REGSP
q1.Spadj = aoffset
}
if cursym.Text.From3.Offset&obj.WRAPPER != 0 {
// if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
......
......@@ -445,16 +445,12 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
}
autosize := int32(0)
var aoffset int
var mov obj.As
var p1 *obj.Prog
var p2 *obj.Prog
for p := cursym.Text; p != nil; p = p.Link {
o := p.As
switch o {
case obj.ATEXT:
mov = AMOVD
aoffset = 0
autosize = int32(textstksiz)
if p.Mark&LEAF != 0 && autosize == 0 {
......@@ -520,11 +516,49 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
}
if autosize != 0 {
/* use MOVDU to adjust R1 when saving R31, if autosize is small */
// Make sure to save link register for non-empty frame, even if
// it is a leaf function, so that traceback works.
if cursym.Text.Mark&LEAF == 0 && autosize >= -BIG && autosize <= BIG {
mov = AMOVDU
aoffset = int(-autosize)
// Use MOVDU to adjust R1 when saving R31, if autosize is small.
q = obj.Appendp(ctxt, q)
q.As = AMOVD
q.Lineno = p.Lineno
q.From.Type = obj.TYPE_REG
q.From.Reg = REG_LR
q.To.Type = obj.TYPE_REG
q.To.Reg = REGTMP
q = obj.Appendp(ctxt, q)
q.As = AMOVDU
q.Lineno = p.Lineno
q.From.Type = obj.TYPE_REG
q.From.Reg = REGTMP
q.To.Type = obj.TYPE_MEM
q.To.Offset = int64(-autosize)
q.To.Reg = REGSP
q.Spadj = int32(autosize)
} else {
// Frame size is too large for a MOVDU instruction.
// Store link register before decrementing SP, so if a signal comes
// during the execution of the function prologue, the traceback
// code will not see a half-updated stack frame.
q = obj.Appendp(ctxt, q)
q.As = AMOVD
q.Lineno = p.Lineno
q.From.Type = obj.TYPE_REG
q.From.Reg = REG_LR
q.To.Type = obj.TYPE_REG
q.To.Reg = REG_R29 // REGTMP may be used to synthesize large offset in the next instruction
q = obj.Appendp(ctxt, q)
q.As = AMOVD
q.Lineno = p.Lineno
q.From.Type = obj.TYPE_REG
q.From.Reg = REG_R29
q.To.Type = obj.TYPE_MEM
q.To.Offset = int64(-autosize)
q.To.Reg = REGSP
q = obj.Appendp(ctxt, q)
q.As = AADD
q.Lineno = p.Lineno
......@@ -546,26 +580,6 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
break
}
q = obj.Appendp(ctxt, q)
q.As = AMOVD
q.Lineno = p.Lineno
q.From.Type = obj.TYPE_REG
q.From.Reg = REG_LR
q.To.Type = obj.TYPE_REG
q.To.Reg = REGTMP
q = obj.Appendp(ctxt, q)
q.As = mov
q.Lineno = p.Lineno
q.From.Type = obj.TYPE_REG
q.From.Reg = REGTMP
q.To.Type = obj.TYPE_MEM
q.To.Offset = int64(aoffset)
q.To.Reg = REGSP
if q.As == AMOVDU {
q.Spadj = int32(-aoffset)
}
if ctxt.Flag_shared {
q = obj.Appendp(ctxt, q)
q.As = AMOVD
......
......@@ -408,8 +408,21 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
}
if autosize != 0 {
// Make sure to save link register for non-empty frame, even if
// it is a leaf function, so that traceback works.
// Store link register before decrementing SP, so if a signal comes
// during the execution of the function prologue, the traceback
// code will not see a half-updated stack frame.
q = obj.Appendp(ctxt, p)
q.As = AMOVD
q.From.Type = obj.TYPE_REG
q.From.Reg = REG_LR
q.To.Type = obj.TYPE_MEM
q.To.Reg = REGSP
q.To.Offset = int64(-autosize)
q = obj.Appendp(ctxt, q)
q.As = AMOVD
q.From.Type = obj.TYPE_ADDR
q.From.Offset = int64(-autosize)
q.From.Reg = REGSP // not actually needed - REGSP is assumed if no reg is provided
......@@ -428,14 +441,6 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
break
}
q = obj.Appendp(ctxt, q)
q.As = AMOVD
q.From.Type = obj.TYPE_REG
q.From.Reg = REG_LR
q.To.Type = obj.TYPE_MEM
q.To.Reg = REGSP
q.To.Offset = 0
if cursym.Text.From3.Offset&obj.WRAPPER != 0 {
// if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
//
......
// run
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// issue 17381: make sure leave function with non-empty frame
// saves link register, so that traceback will work.
package main
import (
"runtime"
"unsafe"
)
func main() {
defer func() {
if recover() == nil {
panic("did not panic")
}
pcs := make([]uintptr, 20)
n := runtime.Callers(1, pcs)
for _, pc := range pcs[:n] {
if runtime.FuncForPC(pc).Name() == "main.main" {
return
}
}
panic("cannot find main.main in backtrace")
}()
prep()
f() // should panic
}
func funcPC(f interface{}) uintptr {
var ptr uintptr
return **(**uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&f)) + unsafe.Sizeof(ptr)))
}
//go:noinline
func f() {
var t [1]int // non-empty frame
*(*int)(nil) = t[0]
}
var p = funcPC(runtime.GC) + 8
//go:noinline
func prep() {
// put some garbage on stack
var x = [20]uintptr{p, p, p, p, p, p, p, p, p, p, p, p, p, p, p, p, p, p, p, p}
_ = x
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment