-
Notifications
You must be signed in to change notification settings - Fork 17.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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 <[email protected]> Reviewed-by: Michael Knyszek <[email protected]> TryBot-Result: Gopher Robot <[email protected]> Reviewed-by: Than McIntosh <[email protected]> Run-TryBot: Quim Muntal <[email protected]>
- Loading branch information
Showing
11 changed files
with
170 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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. | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.