]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/internal/obj: save link register in leaf function with non-empty frame on PPC64...
authorCherry Zhang <cherryyz@google.com>
Mon, 24 Oct 2016 02:32:16 +0000 (22:32 -0400)
committerCherry Zhang <cherryyz@google.com>
Tue, 25 Oct 2016 21:44:32 +0000 (21:44 +0000)
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/31760
Reviewed-by: Michael Munday <munday@ca.ibm.com>
src/cmd/internal/obj/arm64/obj7.go
src/cmd/internal/obj/ppc64/obj9.go
src/cmd/internal/obj/s390x/objz.go
test/fixedbugs/issue17381.go [new file with mode: 0644]

index 707ba9f0d4d4a8b422602fddbb17fe96f06276cb..cbe246e882fdf8e39b9c8ceee82c0d4c5e742d1a 100644 (file)
@@ -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
index 7ed302ad968edc14e6368df0b67983c4b6d8fa18..68211eefe4a35dec606b3dc5822999f14ec28ece 100644 (file)
@@ -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
index 615cfc2db267818e34f1acc4fb0c80ee554dea1f..fca8f85c0fb3a7010a18adc5875ef50c6c0442a2 100644 (file)
@@ -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
                                //
diff --git a/test/fixedbugs/issue17381.go b/test/fixedbugs/issue17381.go
new file mode 100644 (file)
index 0000000..be63633
--- /dev/null
@@ -0,0 +1,54 @@
+// 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
+}