github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/compile/pkginit/initAsanGlobals.go (about)

     1  // Copyright 2022 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 pkginit
     6  
     7  import (
     8  	"strings"
     9  
    10  	"github.com/go-asm/go/cmd/compile/base"
    11  	"github.com/go-asm/go/cmd/compile/ir"
    12  	"github.com/go-asm/go/cmd/compile/typecheck"
    13  	"github.com/go-asm/go/cmd/compile/types"
    14  	"github.com/go-asm/go/cmd/src"
    15  )
    16  
    17  // instrumentGlobals declares a global array of _asan_global structures and initializes it.
    18  func instrumentGlobals(fn *ir.Func) *ir.Name {
    19  	asanGlobalStruct, asanLocationStruct, defStringstruct := createtypes()
    20  	lname := typecheck.Lookup
    21  	tconv := typecheck.ConvNop
    22  	// Make a global array of asanGlobalStruct type.
    23  	// var asanglobals []asanGlobalStruct
    24  	arraytype := types.NewArray(asanGlobalStruct, int64(len(InstrumentGlobalsMap)))
    25  	symG := lname(".asanglobals")
    26  	globals := ir.NewNameAt(base.Pos, symG, arraytype)
    27  	globals.Class = ir.PEXTERN
    28  	symG.Def = globals
    29  	typecheck.Target.Externs = append(typecheck.Target.Externs, globals)
    30  	// Make a global array of asanLocationStruct type.
    31  	// var asanL []asanLocationStruct
    32  	arraytype = types.NewArray(asanLocationStruct, int64(len(InstrumentGlobalsMap)))
    33  	symL := lname(".asanL")
    34  	asanlocation := ir.NewNameAt(base.Pos, symL, arraytype)
    35  	asanlocation.Class = ir.PEXTERN
    36  	symL.Def = asanlocation
    37  	typecheck.Target.Externs = append(typecheck.Target.Externs, asanlocation)
    38  	// Make three global string variables to pass the global name and module name
    39  	// and the name of the source file that defines it.
    40  	// var asanName string
    41  	// var asanModulename string
    42  	// var asanFilename string
    43  	symL = lname(".asanName")
    44  	asanName := ir.NewNameAt(base.Pos, symL, types.Types[types.TSTRING])
    45  	asanName.Class = ir.PEXTERN
    46  	symL.Def = asanName
    47  	typecheck.Target.Externs = append(typecheck.Target.Externs, asanName)
    48  
    49  	symL = lname(".asanModulename")
    50  	asanModulename := ir.NewNameAt(base.Pos, symL, types.Types[types.TSTRING])
    51  	asanModulename.Class = ir.PEXTERN
    52  	symL.Def = asanModulename
    53  	typecheck.Target.Externs = append(typecheck.Target.Externs, asanModulename)
    54  
    55  	symL = lname(".asanFilename")
    56  	asanFilename := ir.NewNameAt(base.Pos, symL, types.Types[types.TSTRING])
    57  	asanFilename.Class = ir.PEXTERN
    58  	symL.Def = asanFilename
    59  	typecheck.Target.Externs = append(typecheck.Target.Externs, asanFilename)
    60  
    61  	var init ir.Nodes
    62  	var c ir.Node
    63  	// globals[i].odrIndicator = 0 is the default, no need to set it explicitly here.
    64  	for i, n := range InstrumentGlobalsSlice {
    65  		setField := func(f string, val ir.Node, i int) {
    66  			r := ir.NewAssignStmt(base.Pos, ir.NewSelectorExpr(base.Pos, ir.ODOT,
    67  				ir.NewIndexExpr(base.Pos, globals, ir.NewInt(base.Pos, int64(i))), lname(f)), val)
    68  			init.Append(typecheck.Stmt(r))
    69  		}
    70  		// globals[i].beg = uintptr(unsafe.Pointer(&n))
    71  		c = tconv(typecheck.NodAddr(n), types.Types[types.TUNSAFEPTR])
    72  		c = tconv(c, types.Types[types.TUINTPTR])
    73  		setField("beg", c, i)
    74  		// Assign globals[i].size.
    75  		g := n.(*ir.Name)
    76  		size := g.Type().Size()
    77  		c = typecheck.DefaultLit(ir.NewInt(base.Pos, size), types.Types[types.TUINTPTR])
    78  		setField("size", c, i)
    79  		// Assign globals[i].sizeWithRedzone.
    80  		rzSize := GetRedzoneSizeForGlobal(size)
    81  		sizeWithRz := rzSize + size
    82  		c = typecheck.DefaultLit(ir.NewInt(base.Pos, sizeWithRz), types.Types[types.TUINTPTR])
    83  		setField("sizeWithRedzone", c, i)
    84  		// The C string type is terminated by a null character "\0", Go should use three-digit
    85  		// octal "\000" or two-digit hexadecimal "\x00" to create null terminated string.
    86  		// asanName = symbol's linkname + "\000"
    87  		// globals[i].name = (*defString)(unsafe.Pointer(&asanName)).data
    88  		name := g.Linksym().Name
    89  		init.Append(typecheck.Stmt(ir.NewAssignStmt(base.Pos, asanName, ir.NewString(base.Pos, name+"\000"))))
    90  		c = tconv(typecheck.NodAddr(asanName), types.Types[types.TUNSAFEPTR])
    91  		c = tconv(c, types.NewPtr(defStringstruct))
    92  		c = ir.NewSelectorExpr(base.Pos, ir.ODOT, c, lname("data"))
    93  		setField("name", c, i)
    94  
    95  		// Set the name of package being compiled as a unique identifier of a module.
    96  		// asanModulename = pkgName + "\000"
    97  		init.Append(typecheck.Stmt(ir.NewAssignStmt(base.Pos, asanModulename, ir.NewString(base.Pos, types.LocalPkg.Name+"\000"))))
    98  		c = tconv(typecheck.NodAddr(asanModulename), types.Types[types.TUNSAFEPTR])
    99  		c = tconv(c, types.NewPtr(defStringstruct))
   100  		c = ir.NewSelectorExpr(base.Pos, ir.ODOT, c, lname("data"))
   101  		setField("moduleName", c, i)
   102  		// Assign asanL[i].filename, asanL[i].line, asanL[i].column
   103  		// and assign globals[i].location = uintptr(unsafe.Pointer(&asanL[i]))
   104  		asanLi := ir.NewIndexExpr(base.Pos, asanlocation, ir.NewInt(base.Pos, int64(i)))
   105  		filename := ir.NewString(base.Pos, base.Ctxt.PosTable.Pos(n.Pos()).Filename()+"\000")
   106  		init.Append(typecheck.Stmt(ir.NewAssignStmt(base.Pos, asanFilename, filename)))
   107  		c = tconv(typecheck.NodAddr(asanFilename), types.Types[types.TUNSAFEPTR])
   108  		c = tconv(c, types.NewPtr(defStringstruct))
   109  		c = ir.NewSelectorExpr(base.Pos, ir.ODOT, c, lname("data"))
   110  		init.Append(typecheck.Stmt(ir.NewAssignStmt(base.Pos, ir.NewSelectorExpr(base.Pos, ir.ODOT, asanLi, lname("filename")), c)))
   111  		line := ir.NewInt(base.Pos, int64(n.Pos().Line()))
   112  		init.Append(typecheck.Stmt(ir.NewAssignStmt(base.Pos, ir.NewSelectorExpr(base.Pos, ir.ODOT, asanLi, lname("line")), line)))
   113  		col := ir.NewInt(base.Pos, int64(n.Pos().Col()))
   114  		init.Append(typecheck.Stmt(ir.NewAssignStmt(base.Pos, ir.NewSelectorExpr(base.Pos, ir.ODOT, asanLi, lname("column")), col)))
   115  		c = tconv(typecheck.NodAddr(asanLi), types.Types[types.TUNSAFEPTR])
   116  		c = tconv(c, types.Types[types.TUINTPTR])
   117  		setField("sourceLocation", c, i)
   118  	}
   119  	fn.Body.Append(init...)
   120  	return globals
   121  }
   122  
   123  // createtypes creates the asanGlobal, asanLocation and defString struct type.
   124  // Go compiler does not refer to the C types, we represent the struct field
   125  // by a uintptr, then use type conversion to make copies of the data.
   126  // E.g., (*defString)(asanGlobal.name).data to C string.
   127  //
   128  // Keep in sync with src/runtime/asan/asan.go.
   129  // type asanGlobal struct {
   130  //	beg               uintptr
   131  //	size              uintptr
   132  //	size_with_redzone uintptr
   133  //	name              uintptr
   134  //	moduleName        uintptr
   135  //	hasDynamicInit    uintptr
   136  //	sourceLocation    uintptr
   137  //	odrIndicator      uintptr
   138  // }
   139  //
   140  // type asanLocation struct {
   141  //	filename uintptr
   142  //	line     int32
   143  //	column   int32
   144  // }
   145  //
   146  // defString is synthesized struct type meant to capture the underlying
   147  // implementations of string.
   148  // type defString struct {
   149  //	data uintptr
   150  //	len  uintptr
   151  // }
   152  
   153  func createtypes() (*types.Type, *types.Type, *types.Type) {
   154  	up := types.Types[types.TUINTPTR]
   155  	i32 := types.Types[types.TINT32]
   156  	fname := typecheck.Lookup
   157  	nxp := src.NoXPos
   158  	nfield := types.NewField
   159  	asanGlobal := types.NewStruct([]*types.Field{
   160  		nfield(nxp, fname("beg"), up),
   161  		nfield(nxp, fname("size"), up),
   162  		nfield(nxp, fname("sizeWithRedzone"), up),
   163  		nfield(nxp, fname("name"), up),
   164  		nfield(nxp, fname("moduleName"), up),
   165  		nfield(nxp, fname("hasDynamicInit"), up),
   166  		nfield(nxp, fname("sourceLocation"), up),
   167  		nfield(nxp, fname("odrIndicator"), up),
   168  	})
   169  	types.CalcSize(asanGlobal)
   170  
   171  	asanLocation := types.NewStruct([]*types.Field{
   172  		nfield(nxp, fname("filename"), up),
   173  		nfield(nxp, fname("line"), i32),
   174  		nfield(nxp, fname("column"), i32),
   175  	})
   176  	types.CalcSize(asanLocation)
   177  
   178  	defString := types.NewStruct([]*types.Field{
   179  		types.NewField(nxp, fname("data"), up),
   180  		types.NewField(nxp, fname("len"), up),
   181  	})
   182  	types.CalcSize(defString)
   183  
   184  	return asanGlobal, asanLocation, defString
   185  }
   186  
   187  // Calculate redzone for globals.
   188  func GetRedzoneSizeForGlobal(size int64) int64 {
   189  	maxRZ := int64(1 << 18)
   190  	minRZ := int64(32)
   191  	redZone := (size / minRZ / 4) * minRZ
   192  	switch {
   193  	case redZone > maxRZ:
   194  		redZone = maxRZ
   195  	case redZone < minRZ:
   196  		redZone = minRZ
   197  	}
   198  	// Round up to multiple of minRZ.
   199  	if size%minRZ != 0 {
   200  		redZone += minRZ - (size % minRZ)
   201  	}
   202  	return redZone
   203  }
   204  
   205  // InstrumentGlobalsMap contains only package-local (and unlinknamed from somewhere else)
   206  // globals.
   207  // And the key is the object name. For example, in package p, a global foo would be in this
   208  // map as "foo".
   209  // Consider range over maps is nondeterministic, make a slice to hold all the values in the
   210  // InstrumentGlobalsMap and iterate over the InstrumentGlobalsSlice.
   211  var InstrumentGlobalsMap = make(map[string]ir.Node)
   212  var InstrumentGlobalsSlice = make([]ir.Node, 0, 0)
   213  
   214  func canInstrumentGlobal(g ir.Node) bool {
   215  	if g.Op() != ir.ONAME {
   216  		return false
   217  	}
   218  	n := g.(*ir.Name)
   219  	if n.Class == ir.PFUNC {
   220  		return false
   221  	}
   222  	if n.Sym().Pkg != types.LocalPkg {
   223  		return false
   224  	}
   225  	// Do not instrument any _cgo_ related global variables, because they are declared in C code.
   226  	if strings.Contains(n.Sym().Name, "cgo") {
   227  		return false
   228  	}
   229  
   230  	// Do not instrument globals that are linknamed, because their home package will do the work.
   231  	if n.Sym().Linkname != "" {
   232  		return false
   233  	}
   234  
   235  	return true
   236  }