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