]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/internal/obj: generate SEH aux symbols for windows/amd64
authorqmuntal <quimmuntal@gmail.com>
Mon, 16 Jan 2023 15:21:48 +0000 (16:21 +0100)
committerQuim Muntal <quimmuntal@gmail.com>
Wed, 5 Apr 2023 19:44:37 +0000 (19:44 +0000)
This CL updates the Go compiler so it generate SEH unwind info [1] as a
function auxiliary symbol when building for windows/amd64.

A follow up CL will teach the Go linker how to assemble these codes
into the PE .xdata section.

Updates #57302

[1] https://learn.microsoft.com/en-us/cpp/build/exception-handling-x64#struct-unwind_info

Change-Id: I40ae0437bfee326c1a67c2b5e1496f0bf3ecea17
Reviewed-on: https://go-review.googlesource.com/c/go/+/461749
Reviewed-by: Davis Goodin <dagood@microsoft.com>
Reviewed-by: Michael Knyszek <mknyszek@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Than McIntosh <thanm@google.com>
Run-TryBot: Quim Muntal <quimmuntal@gmail.com>

src/cmd/internal/goobj/objfile.go
src/cmd/internal/obj/link.go
src/cmd/internal/obj/objfile.go
src/cmd/internal/obj/plist.go
src/cmd/internal/obj/sym.go
src/cmd/internal/obj/x86/obj6.go
src/cmd/internal/obj/x86/seh.go [new file with mode: 0644]
src/cmd/internal/objabi/symkind.go
src/cmd/internal/objabi/symkind_string.go
src/cmd/link/internal/sym/symkind.go
src/cmd/link/internal/sym/symkind_string.go

index 0364f856cfeb0cad86c2ea5a91eae4f11195c832..64d453abdc52725fbea7f64a03bdd41aa5a21471 100644 (file)
@@ -443,6 +443,7 @@ const (
        AuxPcinline
        AuxPcdata
        AuxWasmImport
+       AuxSehUnwindInfo
 )
 
 func (a *Aux) Type() uint8 { return a[0] }
index 077562a267cca20f03bd6735da78ccb894b5d162..037561db1fa81146da327ae65b1e42c8176f6dea 100644 (file)
@@ -503,6 +503,8 @@ type FuncInfo struct {
        FuncInfoSym   *LSym
        WasmImportSym *LSym
        WasmImport    *WasmImport
+
+       sehUnwindInfoSym *LSym
 }
 
 // JumpTable represents a table used for implementing multi-way
@@ -1072,6 +1074,7 @@ type LinkArch struct {
        Preprocess     func(*Link, *LSym, ProgAlloc)
        Assemble       func(*Link, *LSym, ProgAlloc)
        Progedit       func(*Link, *Prog, ProgAlloc)
+       SEH            func(*Link, *LSym) *LSym
        UnaryDst       map[As]bool // Instruction takes one operand, a destination.
        DWARFRegisters map[int16]int16
 }
index 78fa4c1076e1b4becb53fe8afe01a536bf590063..1e9e5a827b9968c970ac8a44617a825fe51227c3 100644 (file)
@@ -602,6 +602,9 @@ func (w *writer) Aux(s *LSym) {
                if fn.Pcln.Pcinline != nil && fn.Pcln.Pcinline.Size != 0 {
                        w.aux1(goobj.AuxPcinline, fn.Pcln.Pcinline)
                }
+               if fn.sehUnwindInfoSym != nil && fn.sehUnwindInfoSym.Size != 0 {
+                       w.aux1(goobj.AuxSehUnwindInfo, fn.sehUnwindInfoSym)
+               }
                for _, pcSym := range fn.Pcln.Pcdata {
                        w.aux1(goobj.AuxPcdata, pcSym)
                }
@@ -707,6 +710,9 @@ func nAuxSym(s *LSym) int {
                if fn.Pcln.Pcinline != nil && fn.Pcln.Pcinline.Size != 0 {
                        n++
                }
+               if fn.sehUnwindInfoSym != nil && fn.sehUnwindInfoSym.Size != 0 {
+                       n++
+               }
                n += len(fn.Pcln.Pcdata)
                if fn.WasmImport != nil {
                        if fn.WasmImportSym == nil || fn.WasmImportSym.Size == 0 {
@@ -770,7 +776,7 @@ func genFuncInfoSyms(ctxt *Link) {
                fn.FuncInfoSym = isym
                b.Reset()
 
-               auxsyms := []*LSym{fn.dwarfRangesSym, fn.dwarfLocSym, fn.dwarfDebugLinesSym, fn.dwarfInfoSym, fn.WasmImportSym}
+               auxsyms := []*LSym{fn.dwarfRangesSym, fn.dwarfLocSym, fn.dwarfDebugLinesSym, fn.dwarfInfoSym, fn.WasmImportSym, fn.sehUnwindInfoSym}
                for _, s := range auxsyms {
                        if s == nil || s.Size == 0 {
                                continue
index 835f37f2ffc2eb1ec8aa26e8cf73b2ebb3d727cc..278ba65d9769499bc3252a091c919a37378f8d52 100644 (file)
@@ -157,6 +157,9 @@ func Flushplist(ctxt *Link, plist *Plist, newprog ProgAlloc, myimportpath string
                if myimportpath != "" {
                        ctxt.populateDWARF(plist.Curfn, s, myimportpath)
                }
+               if ctxt.Headtype == objabi.Hwindows && ctxt.Arch.SEH != nil {
+                       s.Func().sehUnwindInfoSym = ctxt.Arch.SEH(ctxt, s)
+               }
        }
 }
 
index 4a01af39271f2b08f86fe91aa7cc696020c11663..49968d317729e3abcfa4e03c9a289cfd76d9bab7 100644 (file)
@@ -416,7 +416,7 @@ func (ctxt *Link) traverseFuncAux(flag traverseFlag, fsym *LSym, fn func(parent
                }
        }
 
-       auxsyms := []*LSym{fninfo.dwarfRangesSym, fninfo.dwarfLocSym, fninfo.dwarfDebugLinesSym, fninfo.dwarfInfoSym, fninfo.WasmImportSym}
+       auxsyms := []*LSym{fninfo.dwarfRangesSym, fninfo.dwarfLocSym, fninfo.dwarfDebugLinesSym, fninfo.dwarfInfoSym, fninfo.WasmImportSym, fninfo.sehUnwindInfoSym}
        for _, s := range auxsyms {
                if s == nil || s.Size == 0 {
                        continue
index 6b6aa8809a6b7714251b6b914b2bd35d1922afe6..c85b5018eb4c5d595891720cab95eea4c581415d 100644 (file)
@@ -628,6 +628,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
                p.To.Offset += int64(bpsize)
        } else {
                bpsize = 0
+               p.From.Sym.Set(obj.AttrNoFrame, true)
        }
 
        textarg := int64(p.To.Val.(int32))
@@ -1526,6 +1527,7 @@ var Linkamd64 = obj.LinkArch{
        Preprocess:     preprocess,
        Assemble:       span6,
        Progedit:       progedit,
+       SEH:            populateSeh,
        UnaryDst:       unaryDst,
        DWARFRegisters: AMD64DWARFRegisters,
 }
diff --git a/src/cmd/internal/obj/x86/seh.go b/src/cmd/internal/obj/x86/seh.go
new file mode 100644 (file)
index 0000000..e7d3d57
--- /dev/null
@@ -0,0 +1,141 @@
+// Copyright 2023 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.
+
+package x86
+
+import (
+       "cmd/internal/obj"
+       "cmd/internal/objabi"
+       "cmd/internal/src"
+       "encoding/base64"
+       "fmt"
+       "math"
+)
+
+type sehbuf struct {
+       ctxt *obj.Link
+       data []byte
+       off  int
+}
+
+func newsehbuf(ctxt *obj.Link, nodes uint8) sehbuf {
+       // - 8 bytes for the header
+       // - 2 bytes for each node
+       // - 2 bytes in case nodes is not even
+       size := 8 + nodes*2
+       if nodes%2 != 0 {
+               size += 2
+       }
+       return sehbuf{ctxt, make([]byte, size), 0}
+}
+
+func (b *sehbuf) write8(v uint8) {
+       b.data[b.off] = v
+       b.off++
+}
+
+func (b *sehbuf) write32(v uint32) {
+       b.ctxt.Arch.ByteOrder.PutUint32(b.data[b.off:], v)
+       b.off += 4
+}
+
+func (b *sehbuf) writecode(op, value uint8) {
+       b.write8(value<<4 | op)
+}
+
+// populateSeh generates the SEH unwind information for s.
+func populateSeh(ctxt *obj.Link, s *obj.LSym) (sehsym *obj.LSym) {
+       if s.NoFrame() {
+               return
+       }
+
+       // This implementation expects the following function prologue layout:
+       // - Stack split code (optional)
+       // - PUSHQ      BP
+       // - MOVQ       SP,     BP
+       //
+       // If the prologue layout change, the unwind information should be updated
+       // accordingly.
+
+       // Search for the PUSHQ BP instruction inside the prologue.
+       var pushbp *obj.Prog
+       for p := s.Func().Text; p != nil; p = p.Link {
+               if p.As == APUSHQ && p.From.Type == obj.TYPE_REG && p.From.Reg == REG_BP {
+                       pushbp = p
+                       break
+               }
+               if p.Pos.Xlogue() == src.PosPrologueEnd {
+                       break
+               }
+       }
+       if pushbp == nil {
+               ctxt.Diag("missing frame pointer instruction: PUSHQ BP")
+               return
+       }
+
+       // It must be followed by a MOVQ SP, BP.
+       movbp := pushbp.Link
+       if movbp == nil {
+               ctxt.Diag("missing frame pointer instruction: MOVQ SP, BP")
+               return
+       }
+       if !(movbp.As == AMOVQ && movbp.From.Type == obj.TYPE_REG && movbp.From.Reg == REG_SP &&
+               movbp.To.Type == obj.TYPE_REG && movbp.To.Reg == REG_BP && movbp.From.Offset == 0) {
+               ctxt.Diag("unexpected frame pointer instruction\n%v", movbp)
+               return
+       }
+       if movbp.Link.Pc > math.MaxUint8 {
+               // SEH unwind information don't support prologues that are more than 255 bytes long.
+               // These are very rare, but still possible, e.g., when compiling functions with many
+               // parameters with -gcflags=-d=maymorestack=runtime.mayMoreStackPreempt.
+               // Return without reporting an error.
+               return
+       }
+
+       // Reference:
+       // https://learn.microsoft.com/en-us/cpp/build/exception-handling-x64#struct-unwind_info
+
+       const (
+               UWOP_PUSH_NONVOL = 0
+               UWOP_SET_FPREG   = 3
+               SEH_REG_BP       = 5
+       )
+
+       // Fow now we only support operations which are encoded
+       // using a single 2-byte node, so the number of nodes
+       // is the number of operations.
+       nodes := uint8(2)
+       buf := newsehbuf(ctxt, nodes)
+       buf.write8(1)                    // Flags + version
+       buf.write8(uint8(movbp.Link.Pc)) // Size of prolog
+       buf.write8(nodes)                // Count of nodes
+       buf.write8(SEH_REG_BP)           // FP register
+
+       // Notes are written in reverse order of appearance.
+       buf.write8(uint8(movbp.Link.Pc))
+       buf.writecode(UWOP_SET_FPREG, 0)
+
+       buf.write8(uint8(pushbp.Link.Pc))
+       buf.writecode(UWOP_PUSH_NONVOL, SEH_REG_BP)
+
+       // The following 4 bytes reference the RVA of the exception handler,
+       // in case the function has one. We don't use it for now.
+       buf.write32(0)
+
+       // The list of unwind infos in a PE binary have very low cardinality
+       // as each info only contains frame pointer operations,
+       // which are very similar across functions.
+       // Dedup them when possible.
+       hash := base64.StdEncoding.EncodeToString(buf.data)
+       symname := fmt.Sprintf("%d.%s", len(buf.data), hash)
+       return ctxt.LookupInit("go:sehuw."+symname, func(s *obj.LSym) {
+               s.WriteBytes(ctxt, 0, buf.data)
+               s.Type = objabi.SSEHUNWINDINFO
+               s.Set(obj.AttrDuplicateOK, true)
+               s.Set(obj.AttrLocal, true)
+               // Note: AttrContentAddressable cannot be set here,
+               // because the content-addressable-handling code
+               // does not know about aux symbols.
+       })
+}
index a58816e2927a822575a6b176fa18309df63967fc..bafc51b46f1f8613f6715842a9170b37afc40699 100644 (file)
@@ -72,5 +72,6 @@ const (
        SCOVERAGE_COUNTER
        SCOVERAGE_AUXVAR
 
+       SSEHUNWINDINFO
        // Update cmd/link/internal/sym/AbiSymKindToSymKind for new SymKind values.
 )
index be4e91f53fc3b5db865f53803bbc2d530040fd41..3f2ad43fca364fc7302b8b86357bd7eed4658cd6 100644 (file)
@@ -28,11 +28,12 @@ func _() {
        _ = x[SLIBFUZZER_8BIT_COUNTER-17]
        _ = x[SCOVERAGE_COUNTER-18]
        _ = x[SCOVERAGE_AUXVAR-19]
+       _ = x[SSEHUNWINDINFO-20]
 }
 
-const _SymKind_name = "SxxxSTEXTSRODATASNOPTRDATASDATASBSSSNOPTRBSSSTLSBSSSDWARFCUINFOSDWARFCONSTSDWARFFCNSDWARFABSFCNSDWARFTYPESDWARFVARSDWARFRANGESDWARFLOCSDWARFLINESSLIBFUZZER_8BIT_COUNTERSCOVERAGE_COUNTERSCOVERAGE_AUXVAR"
+const _SymKind_name = "SxxxSTEXTSRODATASNOPTRDATASDATASBSSSNOPTRBSSSTLSBSSSDWARFCUINFOSDWARFCONSTSDWARFFCNSDWARFABSFCNSDWARFTYPESDWARFVARSDWARFRANGESDWARFLOCSDWARFLINESSLIBFUZZER_8BIT_COUNTERSCOVERAGE_COUNTERSCOVERAGE_AUXVARSSEHUNWINDINFO"
 
-var _SymKind_index = [...]uint8{0, 4, 9, 16, 26, 31, 35, 44, 51, 63, 74, 83, 95, 105, 114, 125, 134, 145, 168, 185, 201}
+var _SymKind_index = [...]uint8{0, 4, 9, 16, 26, 31, 35, 44, 51, 63, 74, 83, 95, 105, 114, 125, 134, 145, 168, 185, 201, 215}
 
 func (i SymKind) String() string {
        if i >= SymKind(len(_SymKind_index)-1) {
index 2f8e8fe1336bb26557d7d46e3012a87ef78bd06d..db87212a174ce3ec324749d2ecb33ef37e6b25a0 100644 (file)
@@ -123,6 +123,9 @@ const (
        SDWARFRANGE
        SDWARFLOC
        SDWARFLINES
+
+       // SEH symbol types
+       SSEHUNWINDINFO
 )
 
 // AbiSymKindToSymKind maps values read from object files (which are
@@ -148,6 +151,7 @@ var AbiSymKindToSymKind = [...]SymKind{
        objabi.SLIBFUZZER_8BIT_COUNTER: SLIBFUZZER_8BIT_COUNTER,
        objabi.SCOVERAGE_COUNTER:       SCOVERAGE_COUNTER,
        objabi.SCOVERAGE_AUXVAR:        SCOVERAGE_AUXVAR,
+       objabi.SSEHUNWINDINFO:          SSEHUNWINDINFO,
 }
 
 // ReadOnly are the symbol kinds that form read-only sections. In some
index 1cd7ab17ef2569be13241a7039a05380d800307a..09508ce76602c510b68067396b3dbfc574caef2a 100644 (file)
@@ -1,4 +1,4 @@
-// Code generated by "stringer -type=SymKind symkind.go"; DO NOT EDIT.
+// Code generated by "stringer -type=SymKind"; DO NOT EDIT.
 
 package sym
 
@@ -67,11 +67,12 @@ func _() {
        _ = x[SDWARFRANGE-56]
        _ = x[SDWARFLOC-57]
        _ = x[SDWARFLINES-58]
+       _ = x[SSEHUNWINDINFO-59]
 }
 
-const _SymKind_name = "SxxxSTEXTSELFRXSECTSMACHOPLTSTYPESSTRINGSGOSTRINGSGOFUNCSGCBITSSRODATASFUNCTABSELFROSECTSTYPERELROSSTRINGRELROSGOSTRINGRELROSGOFUNCRELROSGCBITSRELROSRODATARELROSFUNCTABRELROSTYPELINKSITABLINKSSYMTABSPCLNTABSFirstWritableSBUILDINFOSELFSECTSMACHOSMACHOGOTSWINDOWSSELFGOTSNOPTRDATASINITARRSDATASXCOFFTOCSBSSSNOPTRBSSSLIBFUZZER_8BIT_COUNTERSCOVERAGE_COUNTERSCOVERAGE_AUXVARSTLSBSSSXREFSMACHOSYMSTRSMACHOSYMTABSMACHOINDIRECTPLTSMACHOINDIRECTGOTSFILEPATHSDYNIMPORTSHOSTOBJSUNDEFEXTSDWARFSECTSDWARFCUINFOSDWARFCONSTSDWARFFCNSDWARFABSFCNSDWARFTYPESDWARFVARSDWARFRANGESDWARFLOCSDWARFLINES"
+const _SymKind_name = "SxxxSTEXTSELFRXSECTSMACHOPLTSTYPESSTRINGSGOSTRINGSGOFUNCSGCBITSSRODATASFUNCTABSELFROSECTSTYPERELROSSTRINGRELROSGOSTRINGRELROSGOFUNCRELROSGCBITSRELROSRODATARELROSFUNCTABRELROSTYPELINKSITABLINKSSYMTABSPCLNTABSFirstWritableSBUILDINFOSELFSECTSMACHOSMACHOGOTSWINDOWSSELFGOTSNOPTRDATASINITARRSDATASXCOFFTOCSBSSSNOPTRBSSSLIBFUZZER_8BIT_COUNTERSCOVERAGE_COUNTERSCOVERAGE_AUXVARSTLSBSSSXREFSMACHOSYMSTRSMACHOSYMTABSMACHOINDIRECTPLTSMACHOINDIRECTGOTSFILEPATHSDYNIMPORTSHOSTOBJSUNDEFEXTSDWARFSECTSDWARFCUINFOSDWARFCONSTSDWARFFCNSDWARFABSFCNSDWARFTYPESDWARFVARSDWARFRANGESDWARFLOCSDWARFLINESSSEHUNWINDINFO"
 
-var _SymKind_index = [...]uint16{0, 4, 9, 19, 28, 33, 40, 49, 56, 63, 70, 78, 88, 98, 110, 124, 136, 148, 160, 173, 182, 191, 198, 206, 220, 230, 238, 244, 253, 261, 268, 278, 286, 291, 300, 304, 313, 336, 353, 369, 376, 381, 393, 405, 422, 439, 448, 458, 466, 475, 485, 497, 508, 517, 529, 539, 548, 559, 568, 579}
+var _SymKind_index = [...]uint16{0, 4, 9, 19, 28, 33, 40, 49, 56, 63, 70, 78, 88, 98, 110, 124, 136, 148, 160, 173, 182, 191, 198, 206, 220, 230, 238, 244, 253, 261, 268, 278, 286, 291, 300, 304, 313, 336, 353, 369, 376, 381, 393, 405, 422, 439, 448, 458, 466, 475, 485, 497, 508, 517, 529, 539, 548, 559, 568, 579, 593}
 
 func (i SymKind) String() string {
        if i >= SymKind(len(_SymKind_index)-1) {