]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/asm, cmd/link, runtime: introduce FuncInfo flag bits
authorRuss Cox <rsc@golang.org>
Thu, 28 Jan 2021 20:21:33 +0000 (15:21 -0500)
committerRuss Cox <rsc@golang.org>
Fri, 19 Feb 2021 00:02:30 +0000 (00:02 +0000)
The runtime traceback code has its own definition of which functions
mark the top frame of a stack, separate from the TOPFRAME bits that
exist in the assembly and are passed along in DWARF information.
It's error-prone and redundant to have two different sources of truth.
This CL provides the actual TOPFRAME bits to the runtime, so that
the runtime can use those bits instead of reinventing its own category.

This CL also adds a new bit, SPWRITE, which marks functions that
write directly to SP (anything but adding and subtracting constants).
Such functions must stop a traceback, because the traceback has no
way to rederive the SP on entry. Again, the runtime has its own definition
which is mostly correct, but also missing some functions. During ordinary
goroutine context switches, such functions do not appear on the stack,
so the incompleteness in the runtime usually doesn't matter.
But profiling signals can arrive at any moment, and the runtime may
crash during traceback if it attempts to unwind an SP-writing frame
and gets out-of-sync with the actual stack. The runtime contains code
to try to detect likely candidates but again it is incomplete.
Deriving the SPWRITE bit automatically from the actual assembly code
provides the complete truth, and passing it to the runtime lets the
runtime use it.

This CL is part of a stack adding windows/arm64
support (#36439), intended to land in the Go 1.17 cycle.
This CL is, however, not windows/arm64-specific.
It is cleanup meant to make the port (and future ports) easier.

Change-Id: I227f53b23ac5b3dabfcc5e8ee3f00df4e113cf58
Reviewed-on: https://go-review.googlesource.com/c/go/+/288800
Trust: Russ Cox <rsc@golang.org>
Trust: Jason A. Donenfeld <Jason@zx2c4.com>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
Reviewed-by: Jason A. Donenfeld <Jason@zx2c4.com>
24 files changed:
src/cmd/asm/internal/asm/endtoend_test.go
src/cmd/asm/internal/flags/flags.go
src/cmd/asm/main.go
src/cmd/compile/internal/test/fixedbugs_test.go
src/cmd/compile/internal/test/global_test.go
src/cmd/internal/goobj/funcinfo.go
src/cmd/internal/goobj/objfile.go
src/cmd/internal/obj/arm/obj5.go
src/cmd/internal/obj/arm64/obj7.go
src/cmd/internal/obj/link.go
src/cmd/internal/obj/mips/obj0.go
src/cmd/internal/obj/objfile.go
src/cmd/internal/obj/plist.go
src/cmd/internal/obj/ppc64/obj9.go
src/cmd/internal/obj/riscv/obj.go
src/cmd/internal/obj/s390x/objz.go
src/cmd/internal/obj/util.go
src/cmd/internal/obj/x86/obj6.go
src/cmd/internal/objabi/funcid.go
src/cmd/link/internal/ld/dwarf.go
src/cmd/link/internal/ld/pcln.go
src/cmd/link/internal/loader/loader.go
src/runtime/runtime2.go
src/runtime/symtab.go

index 7472507caf18ef64725e1a1822d8fb63632bed86..a4153f3af12fe1f78dd2bf8babc3cd467467d64f 100644 (file)
@@ -36,6 +36,7 @@ func testEndToEnd(t *testing.T, goarch, file string) {
        var ok bool
        testOut = new(bytes.Buffer) // The assembler writes test output to this buffer.
        ctxt.Bso = bufio.NewWriter(os.Stdout)
+       ctxt.IsAsm = true
        defer ctxt.Bso.Flush()
        failed := false
        ctxt.DiagFunc = func(format string, args ...interface{}) {
@@ -278,6 +279,7 @@ func testErrors(t *testing.T, goarch, file string) {
        var ok bool
        testOut = new(bytes.Buffer) // The assembler writes test output to this buffer.
        ctxt.Bso = bufio.NewWriter(os.Stdout)
+       ctxt.IsAsm = true
        defer ctxt.Bso.Flush()
        failed := false
        var errBuf bytes.Buffer
index 1335860315a839ffc2d48580eda32d87790876ed..dd947c7b5ba5b805d4319d78fe432cafdbf1faad 100644 (file)
@@ -32,11 +32,13 @@ var (
        D        MultiFlag
        I        MultiFlag
        PrintOut int
+       DebugV   bool
 )
 
 func init() {
        flag.Var(&D, "D", "predefined symbol with optional simple value -D=identifier=value; can be set multiple times")
        flag.Var(&I, "I", "include directory; can be set multiple times")
+       flag.BoolVar(&DebugV, "v", false, "print debug output")
        objabi.AddVersionFlag() // -V
        objabi.Flagcount("S", "print assembly and machine code", &PrintOut)
 }
index 31636e30458974920aa6d5a4ced11513a48cecdc..98618a67ef5311c94ab19adfaa0d9e728898aa26 100644 (file)
@@ -36,6 +36,7 @@ func main() {
 
        ctxt := obj.Linknew(architecture.LinkArch)
        ctxt.Debugasm = flags.PrintOut
+       ctxt.Debugvlog = flags.DebugV
        ctxt.Flag_dynlink = *flags.Dynlink
        ctxt.Flag_linkshared = *flags.Linkshared
        ctxt.Flag_shared = *flags.Shared || *flags.Dynlink
index e7e2f7e58eb29d88ccbc13bea5429266d5ffcbbb..376b45edfcf00c9f0bfce4b0668d06fcdd5d43d4 100644 (file)
@@ -75,7 +75,7 @@ func TestIssue16214(t *testing.T) {
        cmd := exec.Command(testenv.GoToolPath(t), "tool", "compile", "-S", "-o", filepath.Join(dir, "out.o"), src)
        out, err := cmd.CombinedOutput()
        if err != nil {
-               t.Fatalf("fail to run go tool compile: %v", err)
+               t.Fatalf("go tool compile: %v\n%s", err, out)
        }
 
        if strings.Contains(string(out), "unknown line number") {
index 5f5f7d619860f0bc895078746bda9dc49d388e9e..93de894f37e12329f55022d15780f6589b28ef17 100644 (file)
@@ -50,7 +50,7 @@ func main() {
        cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", dst, src)
        out, err := cmd.CombinedOutput()
        if err != nil {
-               t.Fatalf("could not build target: %v", err)
+               t.Fatalf("could not build target: %v\n%s", err, out)
        }
 
        // Check destination to see if scanf code was included.
@@ -95,7 +95,7 @@ func main() {
        cmd := exec.Command(testenv.GoToolPath(t), "build", "-gcflags", "-S", "-o", filepath.Join(dir, "test"), src)
        out, err := cmd.CombinedOutput()
        if err != nil {
-               t.Fatalf("could not build target: %v", err)
+               t.Fatalf("could not build target: %v\n%s", err, out)
        }
 
        patterns := []string{
index 2cca8f6c4ef9c05f831cf8597c7190c4f2defabd..6d33a10a51cda4cde69a36aaf281f6c61a520a1c 100644 (file)
@@ -19,9 +19,10 @@ type CUFileIndex uint32
 //
 // TODO: make each pcdata a separate symbol?
 type FuncInfo struct {
-       Args   uint32
-       Locals uint32
-       FuncID objabi.FuncID
+       Args     uint32
+       Locals   uint32
+       FuncID   objabi.FuncID
+       FuncFlag objabi.FuncFlag
 
        Pcsp        SymRef
        Pcfile      SymRef
@@ -35,6 +36,9 @@ type FuncInfo struct {
 }
 
 func (a *FuncInfo) Write(w *bytes.Buffer) {
+       writeUint8 := func(x uint8) {
+               w.WriteByte(x)
+       }
        var b [4]byte
        writeUint32 := func(x uint32) {
                binary.LittleEndian.PutUint32(b[:], x)
@@ -47,8 +51,10 @@ func (a *FuncInfo) Write(w *bytes.Buffer) {
 
        writeUint32(a.Args)
        writeUint32(a.Locals)
-       writeUint32(uint32(a.FuncID))
-
+       writeUint8(uint8(a.FuncID))
+       writeUint8(uint8(a.FuncFlag))
+       writeUint8(0) // pad to uint32 boundary
+       writeUint8(0)
        writeSymRef(a.Pcsp)
        writeSymRef(a.Pcfile)
        writeSymRef(a.Pcline)
@@ -72,46 +78,6 @@ func (a *FuncInfo) Write(w *bytes.Buffer) {
        }
 }
 
-func (a *FuncInfo) Read(b []byte) {
-       readUint32 := func() uint32 {
-               x := binary.LittleEndian.Uint32(b)
-               b = b[4:]
-               return x
-       }
-       readSymIdx := func() SymRef {
-               return SymRef{readUint32(), readUint32()}
-       }
-
-       a.Args = readUint32()
-       a.Locals = readUint32()
-       a.FuncID = objabi.FuncID(readUint32())
-
-       a.Pcsp = readSymIdx()
-       a.Pcfile = readSymIdx()
-       a.Pcline = readSymIdx()
-       a.Pcinline = readSymIdx()
-       a.Pcdata = make([]SymRef, readUint32())
-       for i := range a.Pcdata {
-               a.Pcdata[i] = readSymIdx()
-       }
-
-       funcdataofflen := readUint32()
-       a.Funcdataoff = make([]uint32, funcdataofflen)
-       for i := range a.Funcdataoff {
-               a.Funcdataoff[i] = readUint32()
-       }
-       filelen := readUint32()
-       a.File = make([]CUFileIndex, filelen)
-       for i := range a.File {
-               a.File[i] = CUFileIndex(readUint32())
-       }
-       inltreelen := readUint32()
-       a.InlTree = make([]InlTreeNode, inltreelen)
-       for i := range a.InlTree {
-               b = a.InlTree[i].Read(b)
-       }
-}
-
 // FuncInfoLengths is a cache containing a roadmap of offsets and
 // lengths for things within a serialized FuncInfo. Each length field
 // stores the number of items (e.g. files, inltree nodes, etc), and the
@@ -159,7 +125,9 @@ func (*FuncInfo) ReadArgs(b []byte) uint32 { return binary.LittleEndian.Uint32(b
 
 func (*FuncInfo) ReadLocals(b []byte) uint32 { return binary.LittleEndian.Uint32(b[4:]) }
 
-func (*FuncInfo) ReadFuncID(b []byte) uint32 { return binary.LittleEndian.Uint32(b[8:]) }
+func (*FuncInfo) ReadFuncID(b []byte) objabi.FuncID { return objabi.FuncID(b[8]) }
+
+func (*FuncInfo) ReadFuncFlag(b []byte) objabi.FuncFlag { return objabi.FuncFlag(b[9]) }
 
 func (*FuncInfo) ReadPcsp(b []byte) SymRef {
        return SymRef{binary.LittleEndian.Uint32(b[12:]), binary.LittleEndian.Uint32(b[16:])}
index e6447e455dca6f8acfa4a6786b356187c491a092..d1b838f676c8256963be3f02608107c294d2fe38 100644 (file)
@@ -298,7 +298,6 @@ const (
        SymFlagNoSplit
        SymFlagReflectMethod
        SymFlagGoType
-       SymFlagTopFrame
 )
 
 // Sym.Flag2
@@ -332,7 +331,6 @@ func (s *Sym) Leaf() bool          { return s.Flag()&SymFlagLeaf != 0 }
 func (s *Sym) NoSplit() bool       { return s.Flag()&SymFlagNoSplit != 0 }
 func (s *Sym) ReflectMethod() bool { return s.Flag()&SymFlagReflectMethod != 0 }
 func (s *Sym) IsGoType() bool      { return s.Flag()&SymFlagGoType != 0 }
-func (s *Sym) TopFrame() bool      { return s.Flag()&SymFlagTopFrame != 0 }
 func (s *Sym) UsedInIface() bool   { return s.Flag2()&SymFlagUsedInIface != 0 }
 func (s *Sym) IsItab() bool        { return s.Flag2()&SymFlagItab != 0 }
 
index 29d3a5867d5245e75f77f842427dd0a8d6228bb0..7de04302d918fe7d0d42b24f2172dcf3d3df75f9 100644 (file)
@@ -34,6 +34,7 @@ import (
        "cmd/internal/obj"
        "cmd/internal/objabi"
        "cmd/internal/sys"
+       "log"
 )
 
 var progedit_tlsfallback *obj.LSym
@@ -613,6 +614,21 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
                                p.From.Reg = REGSP
                        }
                }
+
+               if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.Spadj == 0 {
+                       f := c.cursym.Func()
+                       if f.FuncFlag&objabi.FuncFlag_SPWRITE == 0 {
+                               c.cursym.Func().FuncFlag |= objabi.FuncFlag_SPWRITE
+                               if ctxt.Debugvlog || !ctxt.IsAsm {
+                                       ctxt.Logf("auto-SPWRITE: %s %v\n", c.cursym.Name, p)
+                                       if !ctxt.IsAsm {
+                                               ctxt.Diag("invalid auto-SPWRITE in non-assembly")
+                                               ctxt.DiagFlush()
+                                               log.Fatalf("bad SPWRITE")
+                                       }
+                               }
+                       }
+               }
        }
 }
 
index 0baf51973ade229dbd5ae975a25233304bcd9e59..3b885438520aa2e74f29901637169850fff6224d 100644 (file)
@@ -35,6 +35,7 @@ import (
        "cmd/internal/objabi"
        "cmd/internal/src"
        "cmd/internal/sys"
+       "log"
        "math"
 )
 
@@ -970,6 +971,21 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
                                p = q5
                        }
                }
+
+               if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.Spadj == 0 {
+                       f := c.cursym.Func()
+                       if f.FuncFlag&objabi.FuncFlag_SPWRITE == 0 {
+                               c.cursym.Func().FuncFlag |= objabi.FuncFlag_SPWRITE
+                               if ctxt.Debugvlog || !ctxt.IsAsm {
+                                       ctxt.Logf("auto-SPWRITE: %s %v\n", c.cursym.Name, p)
+                                       if !ctxt.IsAsm {
+                                               ctxt.Diag("invalid auto-SPWRITE in non-assembly")
+                                               ctxt.DiagFlush()
+                                               log.Fatalf("bad SPWRITE")
+                                       }
+                               }
+                       }
+               }
        }
 }
 
index 82069023280773ea8524f49905a920ced2793c7b..a48db3bdc871ff83f1830f7b16308e7d8cf07ad2 100644 (file)
@@ -454,6 +454,7 @@ type FuncInfo struct {
        Locals   int32
        Align    int32
        FuncID   objabi.FuncID
+       FuncFlag objabi.FuncFlag
        Text     *Prog
        Autot    map[*LSym]struct{}
        Pcln     Pcln
@@ -619,10 +620,6 @@ const (
        // target of an inline during compilation
        AttrWasInlined
 
-       // TopFrame means that this function is an entry point and unwinders should not
-       // keep unwinding beyond this frame.
-       AttrTopFrame
-
        // Indexed indicates this symbol has been assigned with an index (when using the
        // new object file format).
        AttrIndexed
@@ -663,7 +660,6 @@ func (a *Attribute) NeedCtxt() bool           { return a.load()&AttrNeedCtxt !=
 func (a *Attribute) NoFrame() bool            { return a.load()&AttrNoFrame != 0 }
 func (a *Attribute) Static() bool             { return a.load()&AttrStatic != 0 }
 func (a *Attribute) WasInlined() bool         { return a.load()&AttrWasInlined != 0 }
-func (a *Attribute) TopFrame() bool           { return a.load()&AttrTopFrame != 0 }
 func (a *Attribute) Indexed() bool            { return a.load()&AttrIndexed != 0 }
 func (a *Attribute) UsedInIface() bool        { return a.load()&AttrUsedInIface != 0 }
 func (a *Attribute) ContentAddressable() bool { return a.load()&AttrContentAddressable != 0 }
@@ -713,14 +709,13 @@ var textAttrStrings = [...]struct {
        {bit: AttrNoFrame, s: "NOFRAME"},
        {bit: AttrStatic, s: "STATIC"},
        {bit: AttrWasInlined, s: ""},
-       {bit: AttrTopFrame, s: "TOPFRAME"},
        {bit: AttrIndexed, s: ""},
        {bit: AttrContentAddressable, s: ""},
        {bit: AttrABIWrapper, s: "ABIWRAPPER"},
 }
 
-// TextAttrString formats a for printing in as part of a TEXT prog.
-func (a Attribute) TextAttrString() string {
+// String formats a for printing in as part of a TEXT prog.
+func (a Attribute) String() string {
        var s string
        for _, x := range textAttrStrings {
                if a&x.bit != 0 {
@@ -746,6 +741,18 @@ func (a Attribute) TextAttrString() string {
        return s
 }
 
+// TextAttrString formats the symbol attributes for printing in as part of a TEXT prog.
+func (s *LSym) TextAttrString() string {
+       attr := s.Attribute.String()
+       if s.Func().FuncFlag&objabi.FuncFlag_TOPFRAME != 0 {
+               if attr != "" {
+                       attr += "|"
+               }
+               attr += "TOPFRAME"
+       }
+       return attr
+}
+
 func (s *LSym) String() string {
        return s.Name
 }
index 135a8df3aaa4c0c29cb69432e4fc2c44e747c224..91bba90d4197d48aa9f5a8354af79f5fbbdb8150 100644 (file)
@@ -35,6 +35,7 @@ import (
        "cmd/internal/sys"
        "encoding/binary"
        "fmt"
+       "log"
        "math"
 )
 
@@ -536,6 +537,21 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
                                p.From.Reg = REGSP
                        }
                }
+
+               if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.Spadj == 0 {
+                       f := c.cursym.Func()
+                       if f.FuncFlag&objabi.FuncFlag_SPWRITE == 0 {
+                               c.cursym.Func().FuncFlag |= objabi.FuncFlag_SPWRITE
+                               if ctxt.Debugvlog || !ctxt.IsAsm {
+                                       ctxt.Logf("auto-SPWRITE: %s %v\n", c.cursym.Name, p)
+                                       if !ctxt.IsAsm {
+                                               ctxt.Diag("invalid auto-SPWRITE in non-assembly")
+                                               ctxt.DiagFlush()
+                                               log.Fatalf("bad SPWRITE")
+                                       }
+                               }
+                       }
+               }
        }
 
        if c.ctxt.Arch.Family == sys.MIPS {
index bb58b4f0c2b62d4103b99118f3aa458737850c73..85f0570e5df6a1057db39fe667dea0dfe075f975 100644 (file)
@@ -330,9 +330,6 @@ func (w *writer) Sym(s *LSym) {
        if s.ReflectMethod() {
                flag |= goobj.SymFlagReflectMethod
        }
-       if s.TopFrame() {
-               flag |= goobj.SymFlagTopFrame
-       }
        if strings.HasPrefix(s.Name, "type.") && s.Name[5] != '.' && s.Type == objabi.SRODATA {
                flag |= goobj.SymFlagGoType
        }
@@ -673,9 +670,10 @@ func genFuncInfoSyms(ctxt *Link) {
                        continue
                }
                o := goobj.FuncInfo{
-                       Args:   uint32(fn.Args),
-                       Locals: uint32(fn.Locals),
-                       FuncID: objabi.FuncID(fn.FuncID),
+                       Args:     uint32(fn.Args),
+                       Locals:   uint32(fn.Locals),
+                       FuncID:   fn.FuncID,
+                       FuncFlag: fn.FuncFlag,
                }
                pc := &fn.Pcln
                o.Pcsp = makeSymRef(preparePcSym(pc.Pcsp))
@@ -788,7 +786,7 @@ func (ctxt *Link) writeSymDebugNamed(s *LSym, name string) {
        if s.NoSplit() {
                fmt.Fprintf(ctxt.Bso, "nosplit ")
        }
-       if s.TopFrame() {
+       if s.Func() != nil && s.Func().FuncFlag&objabi.FuncFlag_TOPFRAME != 0 {
                fmt.Fprintf(ctxt.Bso, "topframe ")
        }
        fmt.Fprintf(ctxt.Bso, "size=%d", s.Size)
index 679ce7eb8f61d34fa3167996f8e405a1e330a4c1..177083261c1b4947d5d8907945f0ae89c703de89 100644 (file)
@@ -134,6 +134,7 @@ func (ctxt *Link) InitTextSym(s *LSym, flag int) {
        }
        name := strings.Replace(s.Name, "\"\"", ctxt.Pkgpath, -1)
        s.Func().FuncID = objabi.GetFuncID(name, flag&WRAPPER != 0)
+       s.Func().FuncFlag = toFuncFlag(flag)
        s.Set(AttrOnList, true)
        s.Set(AttrDuplicateOK, flag&DUPOK != 0)
        s.Set(AttrNoSplit, flag&NOSPLIT != 0)
@@ -142,7 +143,6 @@ func (ctxt *Link) InitTextSym(s *LSym, flag int) {
        s.Set(AttrABIWrapper, flag&ABIWRAPPER != 0)
        s.Set(AttrNeedCtxt, flag&NEEDCTXT != 0)
        s.Set(AttrNoFrame, flag&NOFRAME != 0)
-       s.Set(AttrTopFrame, flag&TOPFRAME != 0)
        s.Type = objabi.STEXT
        ctxt.Text = append(ctxt.Text, s)
 
@@ -150,6 +150,14 @@ func (ctxt *Link) InitTextSym(s *LSym, flag int) {
        ctxt.dwarfSym(s)
 }
 
+func toFuncFlag(flag int) objabi.FuncFlag {
+       var out objabi.FuncFlag
+       if flag&TOPFRAME != 0 {
+               out |= objabi.FuncFlag_TOPFRAME
+       }
+       return out
+}
+
 func (ctxt *Link) Globl(s *LSym, size int64, flag int) {
        if s.OnList() {
                ctxt.Diag("symbol %s listed multiple times", s.Name)
index fddf552156cfbbe831758a2fdb26b613f2027ab4..a77be29cf09c70373c6a09d749ba18e0551c779c 100644 (file)
@@ -34,6 +34,7 @@ import (
        "cmd/internal/objabi"
        "cmd/internal/src"
        "cmd/internal/sys"
+       "log"
 )
 
 func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) {
@@ -984,6 +985,21 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
                                p.From.Reg = REGSP
                        }
                }
+
+               if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.Spadj == 0 && p.As != ACMPU {
+                       f := c.cursym.Func()
+                       if f.FuncFlag&objabi.FuncFlag_SPWRITE == 0 {
+                               c.cursym.Func().FuncFlag |= objabi.FuncFlag_SPWRITE
+                               if ctxt.Debugvlog || !ctxt.IsAsm {
+                                       ctxt.Logf("auto-SPWRITE: %s %v\n", c.cursym.Name, p)
+                                       if !ctxt.IsAsm {
+                                               ctxt.Diag("invalid auto-SPWRITE in non-assembly")
+                                               ctxt.DiagFlush()
+                                               log.Fatalf("bad SPWRITE")
+                                       }
+                               }
+                       }
+               }
        }
 }
 
index 5ef334dd6a73e2112ab4430ed98bf748f5a52403..d104f1cfa514d9e8f645f32fcd2fb80a749d79ed 100644 (file)
@@ -25,6 +25,7 @@ import (
        "cmd/internal/objabi"
        "cmd/internal/sys"
        "fmt"
+       "log"
 )
 
 func buildop(ctxt *obj.Link) {}
@@ -716,6 +717,21 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
                                p.Spadj = int32(-p.From.Offset)
                        }
                }
+
+               if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.Spadj == 0 {
+                       f := cursym.Func()
+                       if f.FuncFlag&objabi.FuncFlag_SPWRITE == 0 {
+                               f.FuncFlag |= objabi.FuncFlag_SPWRITE
+                               if ctxt.Debugvlog || !ctxt.IsAsm {
+                                       ctxt.Logf("auto-SPWRITE: %s %v\n", cursym.Name, p)
+                                       if !ctxt.IsAsm {
+                                               ctxt.Diag("invalid auto-SPWRITE in non-assembly")
+                                               ctxt.DiagFlush()
+                                               log.Fatalf("bad SPWRITE")
+                                       }
+                               }
+                       }
+               }
        }
 
        // Rewrite MOV pseudo-instructions. This cannot be done in
index 970cf827d6fee381b2a444b52ab0861ce92560cd..a02c4fc17f482797478d7334d2ca9d1d3727538f 100644 (file)
@@ -33,6 +33,7 @@ import (
        "cmd/internal/obj"
        "cmd/internal/objabi"
        "cmd/internal/sys"
+       "log"
        "math"
 )
 
@@ -545,6 +546,21 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
                                p.From.Reg = REGSP
                        }
                }
+
+               if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.Spadj == 0 {
+                       f := c.cursym.Func()
+                       if f.FuncFlag&objabi.FuncFlag_SPWRITE == 0 {
+                               c.cursym.Func().FuncFlag |= objabi.FuncFlag_SPWRITE
+                               if ctxt.Debugvlog || !ctxt.IsAsm {
+                                       ctxt.Logf("auto-SPWRITE: %s\n", c.cursym.Name)
+                                       if !ctxt.IsAsm {
+                                               ctxt.Diag("invalid auto-SPWRITE in non-assembly")
+                                               ctxt.DiagFlush()
+                                               log.Fatalf("bad SPWRITE")
+                                       }
+                               }
+                       }
+               }
        }
        if wasSplit {
                c.stacksplitPost(pLast, pPre, pPreempt, autosize) // emit post part of split check
index b9bacb7a2279126d552603f38c8bbdbca2dcc4e1..1c34b4e833e2257dde4ac3e6a7e9c642258bd1f2 100644 (file)
@@ -187,7 +187,7 @@ func (p *Prog) WriteInstructionString(w io.Writer) {
                // In short, print one of these two:
                // TEXT foo(SB), DUPOK|NOSPLIT, $0
                // TEXT foo(SB), $0
-               s := p.From.Sym.Attribute.TextAttrString()
+               s := p.From.Sym.TextAttrString()
                if s != "" {
                        fmt.Fprintf(w, "%s%s", sep, s)
                        sep = ", "
index 84de58a4c4b87290a1437b82b4db2c5e40599b9b..bc3a3b4bbe7f47c146300d79482cf6f289d5d845 100644 (file)
@@ -35,6 +35,7 @@ import (
        "cmd/internal/objabi"
        "cmd/internal/src"
        "cmd/internal/sys"
+       "log"
        "math"
        "strings"
 )
@@ -839,6 +840,20 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
 
                switch p.As {
                default:
+                       if p.To.Type == obj.TYPE_REG && p.To.Reg == REG_SP && p.As != ACMPL && p.As != ACMPQ {
+                               f := cursym.Func()
+                               if f.FuncFlag&objabi.FuncFlag_SPWRITE == 0 {
+                                       f.FuncFlag |= objabi.FuncFlag_SPWRITE
+                                       if ctxt.Debugvlog || !ctxt.IsAsm {
+                                               ctxt.Logf("auto-SPWRITE: %s %v\n", cursym.Name, p)
+                                               if !ctxt.IsAsm {
+                                                       ctxt.Diag("invalid auto-SPWRITE in non-assembly")
+                                                       ctxt.DiagFlush()
+                                                       log.Fatalf("bad SPWRITE")
+                                               }
+                                       }
+                               }
+                       }
                        continue
 
                case APUSHL, APUSHFL:
index e921a82c0c425e438ad8b0de7203a05c652f55d9..6e188e31bb435e45ad646564f78d3d1a75004a23 100644 (file)
@@ -6,13 +6,22 @@ package objabi
 
 import "strings"
 
+// A FuncFlag records bits about a function, passed to the runtime.
+type FuncFlag uint8
+
+// Note: This list must match the list in runtime/symtab.go.
+const (
+       FuncFlag_TOPFRAME = 1 << iota
+       FuncFlag_SPWRITE
+)
+
 // A FuncID identifies particular functions that need to be treated
 // specially by the runtime.
 // Note that in some situations involving plugins, there may be multiple
 // copies of a particular special runtime function.
-// Note: this list must match the list in runtime/symtab.go.
 type FuncID uint8
 
+// Note: this list must match the list in runtime/symtab.go.
 const (
        FuncID_normal FuncID = iota // not a special function
        FuncID_asmcgocall
index 2ab9a55e969b8c0f868e7aa5f4936b2f28e2b22a..561f6f1475994c6117d57a9772231a2eb494d443 100644 (file)
@@ -1454,7 +1454,7 @@ func (d *dwctxt) writeframes(fs loader.Sym) dwarfSecInfo {
                // Emit a FDE, Section 6.4.1.
                // First build the section contents into a byte buffer.
                deltaBuf = deltaBuf[:0]
-               if haslr && d.ldr.AttrTopFrame(fn) {
+               if haslr && fi.TopFrame() {
                        // Mark the link register as having an undefined value.
                        // This stops call stack unwinders progressing any further.
                        // TODO: similar mark on non-LR architectures.
@@ -1480,7 +1480,7 @@ func (d *dwctxt) writeframes(fs loader.Sym) dwarfSecInfo {
                                spdelta += int64(d.arch.PtrSize)
                        }
 
-                       if haslr && !d.ldr.AttrTopFrame(fn) {
+                       if haslr && !fi.TopFrame() {
                                // TODO(bryanpkc): This is imprecise. In general, the instruction
                                // that stores the return address to the stack frame is not the
                                // same one that allocates the frame.
index 72bf33e611e59ea5b249e9639b0baacb73258a89..fb733117be4bd6101c8addc46d1bb46431076942 100644 (file)
@@ -796,7 +796,14 @@ func writeFuncs(ctxt *Link, sb *loader.SymbolBuilder, funcs []loader.Sym, inlSym
                }
                off = uint32(sb.SetUint8(ctxt.Arch, int64(off), uint8(funcID)))
 
-               off += 2 // pad
+               // flag uint8
+               var flag objabi.FuncFlag
+               if fi.Valid() {
+                       flag = fi.FuncFlag()
+               }
+               off = uint32(sb.SetUint8(ctxt.Arch, int64(off), uint8(flag)))
+
+               off += 1 // pad
 
                // nfuncdata must be the final entry.
                funcdata, funcdataoff = funcData(fi, 0, funcdata, funcdataoff)
index 98c2131c2b43f1106f9d19e547ac62ac87914acf..68dc3de2738276c7afb1518845ee89a2a7bd21ea 100644 (file)
@@ -241,7 +241,6 @@ type Loader struct {
        attrExternal         Bitmap // external symbols, indexed by ext sym index
 
        attrReadOnly         map[Sym]bool     // readonly data for this sym
-       attrTopFrame         map[Sym]struct{} // top frame symbols
        attrSpecial          map[Sym]struct{} // "special" frame symbols
        attrCgoExportDynamic map[Sym]struct{} // "cgo_export_dynamic" symbols
        attrCgoExportStatic  map[Sym]struct{} // "cgo_export_static" symbols
@@ -349,7 +348,6 @@ func NewLoader(flags uint32, elfsetstring elfsetstringFunc, reporter *ErrorRepor
                plt:                  make(map[Sym]int32),
                got:                  make(map[Sym]int32),
                dynid:                make(map[Sym]int32),
-               attrTopFrame:         make(map[Sym]struct{}),
                attrSpecial:          make(map[Sym]struct{}),
                attrCgoExportDynamic: make(map[Sym]struct{}),
                attrCgoExportStatic:  make(map[Sym]struct{}),
@@ -1009,24 +1007,6 @@ func (l *Loader) SetAttrExternal(i Sym, v bool) {
        }
 }
 
-// AttrTopFrame returns true for a function symbol that is an entry
-// point, meaning that unwinders should stop when they hit this
-// function.
-func (l *Loader) AttrTopFrame(i Sym) bool {
-       _, ok := l.attrTopFrame[i]
-       return ok
-}
-
-// SetAttrTopFrame sets the "top frame" property for a symbol (see
-// AttrTopFrame).
-func (l *Loader) SetAttrTopFrame(i Sym, v bool) {
-       if v {
-               l.attrTopFrame[i] = struct{}{}
-       } else {
-               delete(l.attrTopFrame, i)
-       }
-}
-
 // AttrSpecial returns true for a symbols that do not have their
 // address (i.e. Value) computed by the usual mechanism of
 // data.go:dodata() & data.go:address().
@@ -1905,7 +1885,11 @@ func (fi *FuncInfo) Locals() int {
 }
 
 func (fi *FuncInfo) FuncID() objabi.FuncID {
-       return objabi.FuncID((*goobj.FuncInfo)(nil).ReadFuncID(fi.data))
+       return (*goobj.FuncInfo)(nil).ReadFuncID(fi.data)
+}
+
+func (fi *FuncInfo) FuncFlag() objabi.FuncFlag {
+       return (*goobj.FuncInfo)(nil).ReadFuncFlag(fi.data)
 }
 
 func (fi *FuncInfo) Pcsp() Sym {
@@ -1992,6 +1976,13 @@ func (fi *FuncInfo) File(k int) goobj.CUFileIndex {
        return (*goobj.FuncInfo)(nil).ReadFile(fi.data, fi.lengths.FileOff, uint32(k))
 }
 
+// TopFrame returns true if the function associated with this FuncInfo
+// is an entry point, meaning that unwinders should stop when they hit
+// this function.
+func (fi *FuncInfo) TopFrame() bool {
+       return (fi.FuncFlag() & objabi.FuncFlag_TOPFRAME) != 0
+}
+
 type InlTreeNode struct {
        Parent   int32
        File     goobj.CUFileIndex
@@ -2151,9 +2142,6 @@ func (st *loadState) preloadSyms(r *oReader, kind int) {
                }
                gi := st.addSym(name, v, r, i, kind, osym)
                r.syms[i] = gi
-               if osym.TopFrame() {
-                       l.SetAttrTopFrame(gi, true)
-               }
                if osym.Local() {
                        l.SetAttrLocal(gi, true)
                }
@@ -2411,7 +2399,6 @@ func (l *Loader) CopyAttributes(src Sym, dst Sym) {
                // when copying attributes from a dupOK ABI wrapper symbol to
                // the real target symbol (which may not be marked dupOK).
        }
-       l.SetAttrTopFrame(dst, l.AttrTopFrame(src))
        l.SetAttrSpecial(dst, l.AttrSpecial(src))
        l.SetAttrCgoExportDynamic(dst, l.AttrCgoExportDynamic(src))
        l.SetAttrCgoExportStatic(dst, l.AttrCgoExportStatic(src))
index 675c613b6e7438692adc15a78e84219875fbc2e5..05520d07b2e0c706ace8ab41cb4a0fa4a4765fd5 100644 (file)
@@ -833,10 +833,11 @@ type _func struct {
        pcfile    uint32
        pcln      uint32
        npcdata   uint32
-       cuOffset  uint32  // runtime.cutab offset of this function's CU
-       funcID    funcID  // set for certain special runtime functions
-       _         [2]byte // pad
-       nfuncdata uint8   // must be last
+       cuOffset  uint32 // runtime.cutab offset of this function's CU
+       funcID    funcID // set for certain special runtime functions
+       flag      funcFlag
+       _         [1]byte // pad
+       nfuncdata uint8   // must be last, must end on a uint32-aligned boundary
 }
 
 // Pseudo-Func that is returned for PCs that occur in inlined code.
index fc93c00c2d70628a1955b55052a02b4f6908b592..d7da255e435a6162ddc8734bef52dffcfdc53075 100644 (file)
@@ -332,6 +332,15 @@ const (
        funcID_wrapper // any autogenerated code (hash/eq algorithms, method wrappers, etc.)
 )
 
+// A FuncFlag holds bits about a function.
+// This list must match the list in cmd/internal/objabi/funcid.go.
+type funcFlag uint8
+
+const (
+       funcFlag_TOPFRAME funcFlag = 1 << iota
+       funcFlag_SPWRITE
+)
+
 // pcHeader holds data used by the pclntab lookups.
 type pcHeader struct {
        magic          uint32  // 0xFFFFFFFA