1// Copyright 2023 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package ld
6
7import (
8	"cmd/internal/sys"
9	"cmd/link/internal/loader"
10	"cmd/link/internal/sym"
11)
12
13var sehp struct {
14	pdata []sym.LoaderSym
15	xdata []sym.LoaderSym
16}
17
18func writeSEH(ctxt *Link) {
19	switch ctxt.Arch.Family {
20	case sys.AMD64:
21		writeSEHAMD64(ctxt)
22	}
23}
24
25func writeSEHAMD64(ctxt *Link) {
26	ldr := ctxt.loader
27	mkSecSym := func(name string, kind sym.SymKind) *loader.SymbolBuilder {
28		s := ldr.CreateSymForUpdate(name, 0)
29		s.SetType(kind)
30		s.SetAlign(4)
31		return s
32	}
33	pdata := mkSecSym(".pdata", sym.SSEHSECT)
34	xdata := mkSecSym(".xdata", sym.SSEHSECT)
35	// The .xdata entries have very low cardinality
36	// as it only contains frame pointer operations,
37	// which are very similar across functions.
38	// These are referenced by .pdata entries using
39	// an RVA, so it is possible, and binary-size wise,
40	// to deduplicate .xdata entries.
41	uwcache := make(map[string]int64) // aux symbol name --> .xdata offset
42	for _, s := range ctxt.Textp {
43		if fi := ldr.FuncInfo(s); !fi.Valid() {
44			continue
45		}
46		uw := ldr.SEHUnwindSym(s)
47		if uw == 0 {
48			continue
49		}
50		name := ctxt.SymName(uw)
51		off, cached := uwcache[name]
52		if !cached {
53			off = xdata.Size()
54			uwcache[name] = off
55			xdata.AddBytes(ldr.Data(uw))
56			// The SEH unwind data can contain relocations,
57			// make sure those are copied over.
58			rels := ldr.Relocs(uw)
59			for i := 0; i < rels.Count(); i++ {
60				r := rels.At(i)
61				rel, _ := xdata.AddRel(r.Type())
62				rel.SetOff(int32(off) + r.Off())
63				rel.SetSiz(r.Siz())
64				rel.SetSym(r.Sym())
65				rel.SetAdd(r.Add())
66			}
67		}
68
69		// Reference:
70		// https://learn.microsoft.com/en-us/cpp/build/exception-handling-x64#struct-runtime_function
71		pdata.AddPEImageRelativeAddrPlus(ctxt.Arch, s, 0)
72		pdata.AddPEImageRelativeAddrPlus(ctxt.Arch, s, ldr.SymSize(s))
73		pdata.AddPEImageRelativeAddrPlus(ctxt.Arch, xdata.Sym(), off)
74	}
75	sehp.pdata = append(sehp.pdata, pdata.Sym())
76	sehp.xdata = append(sehp.xdata, xdata.Sym())
77}
78