From bc15070085ec417d4254f8a4eda62b42de88fb37 Mon Sep 17 00:00:00 2001 From: qmuntal Date: Mon, 4 Sep 2023 17:30:08 +0200 Subject: runtime: support SetUnhandledExceptionFilter on Windows The Windows unhandled exception mechanism fails to call the callback set in SetUnhandledExceptionFilter if the stack can't be correctly unwound. Some cgo glue code was not properly chaining the frame pointer, making the stack unwind to fail in case of an exception inside a cgo call. This CL fix that and adds a test case to avoid regressions. Fixes #50951 Change-Id: Ic782b5257fe90b05e3def8dbf0bb8d4ed37a190b Reviewed-on: https://go-review.googlesource.com/c/go/+/525475 Reviewed-by: Bryan Mills Reviewed-by: Cherry Mui Run-TryBot: Quim Muntal LUCI-TryBot-Result: Go LUCI TryBot-Result: Gopher Robot --- src/cmd/internal/obj/x86/seh.go | 36 ++++++++++++++++++++++++++++++------ src/cmd/link/internal/ld/seh.go | 13 ++++++++++++- 2 files changed, 42 insertions(+), 7 deletions(-) (limited to 'src/cmd') diff --git a/src/cmd/internal/obj/x86/seh.go b/src/cmd/internal/obj/x86/seh.go index e7d3d571b7..71cdd36642 100644 --- a/src/cmd/internal/obj/x86/seh.go +++ b/src/cmd/internal/obj/x86/seh.go @@ -97,17 +97,32 @@ func populateSeh(ctxt *obj.Link, s *obj.LSym) (sehsym *obj.LSym) { // 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 + UWOP_PUSH_NONVOL = 0 + UWOP_SET_FPREG = 3 + SEH_REG_BP = 5 + UNW_FLAG_EHANDLER = 1 << 3 ) + var exceptionHandler *obj.LSym + var flags uint8 + if s.Name == "runtime.asmcgocall_landingpad" { + // Most cgo calls go through runtime.asmcgocall_landingpad, + // we can use it to catch exceptions from C code. + // TODO: use a more generic approach to identify which calls need an exception handler. + exceptionHandler = ctxt.Lookup("runtime.sehtramp") + if exceptionHandler == nil { + ctxt.Diag("missing runtime.sehtramp\n") + return + } + flags = UNW_FLAG_EHANDLER + } + // 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(flags | 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 @@ -119,8 +134,10 @@ func populateSeh(ctxt *obj.Link, s *obj.LSym) (sehsym *obj.LSym) { 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. + // The following 4 bytes reference the RVA of the exception handler. + // The value is set to 0 for now, if an exception handler is needed, + // it will be updated later with a R_PEIMAGEOFF relocation to the + // exception handler. buf.write32(0) // The list of unwind infos in a PE binary have very low cardinality @@ -134,6 +151,13 @@ func populateSeh(ctxt *obj.Link, s *obj.LSym) (sehsym *obj.LSym) { s.Type = objabi.SSEHUNWINDINFO s.Set(obj.AttrDuplicateOK, true) s.Set(obj.AttrLocal, true) + if exceptionHandler != nil { + r := obj.Addrel(s) + r.Off = int32(len(buf.data) - 4) + r.Siz = 4 + r.Sym = exceptionHandler + r.Type = objabi.R_PEIMAGEOFF + } // Note: AttrContentAddressable cannot be set here, // because the content-addressable-handling code // does not know about aux symbols. diff --git a/src/cmd/link/internal/ld/seh.go b/src/cmd/link/internal/ld/seh.go index 5379528c30..43b5176a53 100644 --- a/src/cmd/link/internal/ld/seh.go +++ b/src/cmd/link/internal/ld/seh.go @@ -40,7 +40,7 @@ func writeSEHAMD64(ctxt *Link) { // to deduplicate .xdata entries. uwcache := make(map[string]int64) // aux symbol name --> .xdata offset for _, s := range ctxt.Textp { - if fi := ldr.FuncInfo(s); !fi.Valid() || fi.TopFrame() { + if fi := ldr.FuncInfo(s); !fi.Valid() { continue } uw := ldr.SEHUnwindSym(s) @@ -53,6 +53,17 @@ func writeSEHAMD64(ctxt *Link) { off = xdata.Size() uwcache[name] = off xdata.AddBytes(ldr.Data(uw)) + // The SEH unwind data can contain relocations, + // make sure those are copied over. + rels := ldr.Relocs(uw) + for i := 0; i < rels.Count(); i++ { + r := rels.At(i) + rel, _ := xdata.AddRel(r.Type()) + rel.SetOff(int32(off) + r.Off()) + rel.SetSiz(r.Siz()) + rel.SetSym(r.Sym()) + rel.SetAdd(r.Add()) + } } // Reference: -- cgit v1.3-5-g45d5