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  }