github.com/bir3/gocompiler@v0.9.2202/src/cmd/link/internal/loadpe/seh.go (about) 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 "github.com/bir3/gocompiler/src/cmd/internal/objabi" 9 "github.com/bir3/gocompiler/src/cmd/internal/sys" 10 "github.com/bir3/gocompiler/src/cmd/link/internal/loader" 11 "github.com/bir3/gocompiler/src/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 = 4 << 3 20 unwStaticDataSize = 4 // Bytes of unwind data before the variable length part. 21 unwCodeSize = 2 // Bytes per unwind code. 22 ) 23 24 // processSEH walks all pdata relocations looking for exception handler function symbols. 25 // We want to mark these as reachable if the function that they protect is reachable 26 // in the final binary. 27 func processSEH(ldr *loader.Loader, arch *sys.Arch, pdata sym.LoaderSym, xdata sym.LoaderSym) error { 28 switch arch.Family { 29 case sys.AMD64: 30 ldr.SetAttrReachable(pdata, true) 31 if xdata != 0 { 32 ldr.SetAttrReachable(xdata, true) 33 } 34 return processSEHAMD64(ldr, pdata) 35 default: 36 // TODO: support SEH on other architectures. 37 return fmt.Errorf("unsupported architecture for SEH: %v", arch.Family) 38 } 39 } 40 41 func processSEHAMD64(ldr *loader.Loader, pdata sym.LoaderSym) error { 42 // The following loop traverses a list of pdata entries, 43 // each entry being 3 relocations long. The first relocation 44 // is a pointer to the function symbol to which the pdata entry 45 // corresponds. The third relocation is a pointer to the 46 // corresponding .xdata entry. 47 // Reference: 48 // https://learn.microsoft.com/en-us/cpp/build/exception-handling-x64#struct-runtime_function 49 rels := ldr.Relocs(pdata) 50 if rels.Count()%3 != 0 { 51 return fmt.Errorf(".pdata symbol %q has invalid relocation count", ldr.SymName(pdata)) 52 } 53 for i := 0; i < rels.Count(); i += 3 { 54 xrel := rels.At(i + 2) 55 handler := findHandlerInXDataAMD64(ldr, xrel.Sym(), xrel.Add()) 56 if handler != 0 { 57 sb := ldr.MakeSymbolUpdater(rels.At(i).Sym()) 58 r, _ := sb.AddRel(objabi.R_KEEP) 59 r.SetSym(handler) 60 } 61 } 62 return nil 63 } 64 65 // findHandlerInXDataAMD64 finds the symbol in the .xdata section that 66 // corresponds to the exception handler. 67 // Reference: 68 // https://learn.microsoft.com/en-us/cpp/build/exception-handling-x64#struct-unwind_info 69 func findHandlerInXDataAMD64(ldr *loader.Loader, xsym sym.LoaderSym, add int64) loader.Sym { 70 data := ldr.Data(xsym) 71 if add < 0 || add+unwStaticDataSize > int64(len(data)) { 72 return 0 73 } 74 data = data[add:] 75 var isChained bool 76 switch flag := data[0]; { 77 case flag&UNW_FLAG_EHANDLER != 0 || flag&UNW_FLAG_UHANDLER != 0: 78 // Exception handler. 79 case flag&UNW_FLAG_CHAININFO != 0: 80 isChained = true 81 default: 82 // Nothing to do. 83 return 0 84 } 85 codes := data[2] 86 if codes%2 != 0 { 87 // There are always an even number of unwind codes, even if the last one is unused. 88 codes += 1 89 } 90 // The exception handler relocation is the first relocation after the unwind codes, 91 // unless it is chained, but we will handle this case later. 92 targetOff := add + unwStaticDataSize + unwCodeSize*int64(codes) 93 xrels := ldr.Relocs(xsym) 94 xrelsCount := xrels.Count() 95 idx := sort.Search(xrelsCount, func(i int) bool { 96 return int64(xrels.At(i).Off()) >= targetOff 97 }) 98 if idx == xrelsCount { 99 return 0 100 } 101 if isChained { 102 // The third relocations references the next .xdata entry in the chain, recurse. 103 idx += 2 104 if idx >= xrelsCount { 105 return 0 106 } 107 r := xrels.At(idx) 108 return findHandlerInXDataAMD64(ldr, r.Sym(), r.Add()) 109 } 110 return xrels.At(idx).Sym() 111 }