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.
10 "cmd/link/internal/loader"
11 "cmd/link/internal/sym"
17 UNW_FLAG_EHANDLER = 1 << 3
18 UNW_FLAG_UHANDLER = 2 << 3
19 UNW_FLAG_CHAININFO = 3 << 3
23 // processSEH walks all pdata relocations looking for exception handler function symbols.
24 // We want to mark these as reachable if the function that they protect is reachable
25 // in the final binary.
26 func processSEH(ldr *loader.Loader, arch *sys.Arch, pdata sym.LoaderSym, xdata sym.LoaderSym) error {
29 ldr.SetAttrReachable(pdata, true)
31 ldr.SetAttrReachable(xdata, true)
33 return processSEHAMD64(ldr, pdata)
35 // TODO: support SEH on other architectures.
36 return fmt.Errorf("unsupported architecture for SEH: %v", arch.Family)
40 func processSEHAMD64(ldr *loader.Loader, pdata sym.LoaderSym) error {
41 // The following loop traverses a list of pdata entries,
42 // each entry being 3 relocations long. The first relocation
43 // is a pointer to the function symbol to which the pdata entry
44 // corresponds. The third relocation is a pointer to the
45 // corresponding .xdata entry.
47 // https://learn.microsoft.com/en-us/cpp/build/exception-handling-x64#struct-runtime_function
48 rels := ldr.Relocs(pdata)
49 if rels.Count()%3 != 0 {
50 return fmt.Errorf(".pdata symbol %q has invalid relocation count", ldr.SymName(pdata))
52 for i := 0; i < rels.Count(); i += 3 {
53 xrel := rels.At(i + 2)
54 handler := findHandlerInXDataAMD64(ldr, xrel.Sym(), xrel.Add())
56 sb := ldr.MakeSymbolUpdater(rels.At(i).Sym())
57 r, _ := sb.AddRel(objabi.R_KEEP)
64 // findHandlerInXDataAMD64 finds the symbol in the .xdata section that
65 // corresponds to the exception handler.
67 // https://learn.microsoft.com/en-us/cpp/build/exception-handling-x64#struct-unwind_info
68 func findHandlerInXDataAMD64(ldr *loader.Loader, xsym sym.LoaderSym, add int64) loader.Sym {
69 data := ldr.Data(xsym)
70 if add < 0 || add+unwStaticDataSize > int64(len(data)) {
75 switch flag := data[0]; {
76 case flag&UNW_FLAG_EHANDLER != 0 || flag&UNW_FLAG_UHANDLER != 0:
78 case flag&UNW_FLAG_CHAININFO != 0:
86 // There are always an even number of unwind codes, even if the last one is unused.
89 // The exception handler relocation is the first relocation after the unwind codes,
90 // unless it is chained, but we will handle this case later.
91 targetOff := add + unwStaticDataSize*(1+int64(codes))
92 xrels := ldr.Relocs(xsym)
93 idx := sort.Search(xrels.Count(), func(i int) bool {
94 return int64(xrels.At(i).Off()) >= targetOff
100 // The third relocations references the next .xdata entry in the chain, recurse.
102 if idx >= xrels.Count() {
106 return findHandlerInXDataAMD64(ldr, r.Sym(), r.Add())
108 return xrels.At(idx).Sym()