mirror of https://github.com/golang/go.git
cmd/internal/obj: generate SEH aux symbols for windows/amd64
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>
This commit is contained in:
parent
d15fcbc79f
commit
76ac54b50e
|
|
@ -443,6 +443,7 @@ const (
|
|||
AuxPcinline
|
||||
AuxPcdata
|
||||
AuxWasmImport
|
||||
AuxSehUnwindInfo
|
||||
)
|
||||
|
||||
func (a *Aux) Type() uint8 { return a[0] }
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
})
|
||||
}
|
||||
|
|
@ -72,5 +72,6 @@ const (
|
|||
SCOVERAGE_COUNTER
|
||||
SCOVERAGE_AUXVAR
|
||||
|
||||
SSEHUNWINDINFO
|
||||
// Update cmd/link/internal/sym/AbiSymKindToSymKind for new SymKind values.
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue