]> Cypherpunks.ru repositories - gostls13.git/blob - src/cmd/link/internal/loadpe/seh.go
cmd/internal/link: merge .pdata and .xdata sections from host object files
[gostls13.git] / src / cmd / link / internal / loadpe / seh.go
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
5 package loadpe
6
7 import (
8         "cmd/internal/objabi"
9         "cmd/internal/sys"
10         "cmd/link/internal/loader"
11         "cmd/link/internal/sym"
12         "fmt"
13         "sort"
14 )
15
16 const (
17         UNW_FLAG_EHANDLER  = 1 << 3
18         UNW_FLAG_UHANDLER  = 2 << 3
19         UNW_FLAG_CHAININFO = 3 << 3
20         unwStaticDataSize  = 8
21 )
22
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 {
27         switch arch.Family {
28         case sys.AMD64:
29                 ldr.SetAttrReachable(pdata, true)
30                 if xdata != 0 {
31                         ldr.SetAttrReachable(xdata, true)
32                 }
33                 return processSEHAMD64(ldr, pdata)
34         default:
35                 // TODO: support SEH on other architectures.
36                 return fmt.Errorf("unsupported architecture for SEH: %v", arch.Family)
37         }
38 }
39
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.
46         // Reference:
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))
51         }
52         for i := 0; i < rels.Count(); i += 3 {
53                 xrel := rels.At(i + 2)
54                 handler := findHandlerInXDataAMD64(ldr, xrel.Sym(), xrel.Add())
55                 if handler != 0 {
56                         sb := ldr.MakeSymbolUpdater(rels.At(i).Sym())
57                         r, _ := sb.AddRel(objabi.R_KEEP)
58                         r.SetSym(handler)
59                 }
60         }
61         return nil
62 }
63
64 // findHandlerInXDataAMD64 finds the symbol in the .xdata section that
65 // corresponds to the exception handler.
66 // Reference:
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)) {
71                 return 0
72         }
73         data = data[add:]
74         var isChained bool
75         switch flag := data[0]; {
76         case flag&UNW_FLAG_EHANDLER != 0 || flag&UNW_FLAG_UHANDLER != 0:
77                 // Exception handler.
78         case flag&UNW_FLAG_CHAININFO != 0:
79                 isChained = true
80         default:
81                 // Nothing to do.
82                 return 0
83         }
84         codes := data[3]
85         if codes%2 != 0 {
86                 // There are always an even number of unwind codes, even if the last one is unused.
87                 codes += 1
88         }
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
95         })
96         if idx == 0 {
97                 return 0
98         }
99         if isChained {
100                 // The third relocations references the next .xdata entry in the chain, recurse.
101                 idx += 2
102                 if idx >= xrels.Count() {
103                         return 0
104                 }
105                 r := xrels.At(idx)
106                 return findHandlerInXDataAMD64(ldr, r.Sym(), r.Add())
107         }
108         return xrels.At(idx).Sym()
109 }