Source file src/cmd/link/internal/ld/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 ld
     6  
     7  import (
     8  	"cmd/internal/sys"
     9  	"cmd/link/internal/loader"
    10  	"cmd/link/internal/sym"
    11  	"cmp"
    12  	"slices"
    13  )
    14  
    15  var sehp struct {
    16  	pdata []sym.LoaderSym
    17  	xdata []sym.LoaderSym
    18  }
    19  
    20  // collectSEH collects the SEH unwind information for all functions and organizes
    21  // it into .pdata and .xdata sections.
    22  func collectSEH(ctxt *Link) {
    23  	switch ctxt.Arch.Family {
    24  	case sys.AMD64:
    25  		collectSEHAMD64(ctxt)
    26  	}
    27  }
    28  
    29  func collectSEHAMD64(ctxt *Link) {
    30  	ldr := ctxt.loader
    31  	mkSecSym := func(name string, kind sym.SymKind) *loader.SymbolBuilder {
    32  		s := ldr.CreateSymForUpdate(name, 0)
    33  		s.SetType(kind)
    34  		s.SetAlign(4)
    35  		return s
    36  	}
    37  	pdata := mkSecSym(".pdata", sym.SSEHSECT)
    38  	xdata := mkSecSym(".xdata", sym.SSEHSECT)
    39  	// The .xdata entries have very low cardinality
    40  	// as it only contains frame pointer operations,
    41  	// which are very similar across functions.
    42  	// These are referenced by .pdata entries using
    43  	// an RVA, so it is possible, and binary-size wise,
    44  	// to deduplicate .xdata entries.
    45  	uwcache := make(map[string]int64) // aux symbol name --> .xdata offset
    46  	type pdataEntry struct {
    47  		start    sym.LoaderSym
    48  		xdataOff int64
    49  	}
    50  	var entries []pdataEntry
    51  	for _, s := range ctxt.Textp {
    52  		if fi := ldr.FuncInfo(s); !fi.Valid() {
    53  			continue
    54  		}
    55  		uw := ldr.SEHUnwindSym(s)
    56  		if uw == 0 {
    57  			continue
    58  		}
    59  		name := ctxt.SymName(uw)
    60  		off, cached := uwcache[name]
    61  		if !cached {
    62  			off = xdata.Size()
    63  			uwcache[name] = off
    64  			xdata.AddBytes(ldr.Data(uw))
    65  			// The SEH unwind data can contain relocations,
    66  			// make sure those are copied over.
    67  			rels := ldr.Relocs(uw)
    68  			for i := 0; i < rels.Count(); i++ {
    69  				r := rels.At(i)
    70  				rel, _ := xdata.AddRel(r.Type())
    71  				rel.SetOff(int32(off) + r.Off())
    72  				rel.SetSiz(r.Siz())
    73  				rel.SetSym(r.Sym())
    74  				rel.SetAdd(r.Add())
    75  			}
    76  		}
    77  
    78  		entries = append(entries, pdataEntry{start: s, xdataOff: off})
    79  	}
    80  	slices.SortFunc(entries, func(a, b pdataEntry) int {
    81  		return cmp.Compare(ldr.SymAddr(a.start), ldr.SymAddr(b.start))
    82  	})
    83  	for _, ent := range entries {
    84  		// Reference:
    85  		// https://learn.microsoft.com/en-us/cpp/build/exception-handling-x64#struct-runtime_function
    86  		pdata.AddPEImageRelativeAddrPlus(ctxt.Arch, ent.start, 0)                      // function start address
    87  		pdata.AddPEImageRelativeAddrPlus(ctxt.Arch, ent.start, ldr.SymSize(ent.start)) // function end address
    88  		pdata.AddPEImageRelativeAddrPlus(ctxt.Arch, xdata.Sym(), ent.xdataOff)         // xdata symbol offset
    89  	}
    90  	if pdata.Size() > 0 {
    91  		sehp.pdata = append(sehp.pdata, pdata.Sym())
    92  	}
    93  	if xdata.Size() > 0 {
    94  		sehp.xdata = append(sehp.xdata, xdata.Sym())
    95  	}
    96  }
    97  

View as plain text