|  | // Copyright 2022 The Go Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style | 
|  | // license that can be found in the LICENSE file. | 
|  |  | 
|  | package pkginit | 
|  |  | 
|  | import ( | 
|  | "strings" | 
|  |  | 
|  | "cmd/compile/internal/base" | 
|  | "cmd/compile/internal/ir" | 
|  | "cmd/compile/internal/typecheck" | 
|  | "cmd/compile/internal/types" | 
|  | "cmd/internal/src" | 
|  | ) | 
|  |  | 
|  | // instrumentGlobals declares a global array of _asan_global structures and initializes it. | 
|  | func instrumentGlobals(fn *ir.Func) *ir.Name { | 
|  | asanGlobalStruct, asanLocationStruct, defStringstruct := createtypes() | 
|  | lname := typecheck.Lookup | 
|  | tconv := typecheck.ConvNop | 
|  | // Make a global array of asanGlobalStruct type. | 
|  | // var asanglobals []asanGlobalStruct | 
|  | arraytype := types.NewArray(asanGlobalStruct, int64(len(InstrumentGlobalsMap))) | 
|  | symG := lname(".asanglobals") | 
|  | globals := typecheck.NewName(symG) | 
|  | globals.SetType(arraytype) | 
|  | globals.Class = ir.PEXTERN | 
|  | symG.Def = globals | 
|  | typecheck.Target.Externs = append(typecheck.Target.Externs, globals) | 
|  | // Make a global array of asanLocationStruct type. | 
|  | // var asanL []asanLocationStruct | 
|  | arraytype = types.NewArray(asanLocationStruct, int64(len(InstrumentGlobalsMap))) | 
|  | symL := lname(".asanL") | 
|  | asanlocation := typecheck.NewName(symL) | 
|  | asanlocation.SetType(arraytype) | 
|  | asanlocation.Class = ir.PEXTERN | 
|  | symL.Def = asanlocation | 
|  | typecheck.Target.Externs = append(typecheck.Target.Externs, asanlocation) | 
|  | // Make three global string variables to pass the global name and module name | 
|  | // and the name of the source file that defines it. | 
|  | // var asanName string | 
|  | // var asanModulename string | 
|  | // var asanFilename string | 
|  | symL = lname(".asanName") | 
|  | asanName := typecheck.NewName(symL) | 
|  | asanName.SetType(types.Types[types.TSTRING]) | 
|  | asanName.Class = ir.PEXTERN | 
|  | symL.Def = asanName | 
|  | typecheck.Target.Externs = append(typecheck.Target.Externs, asanName) | 
|  |  | 
|  | symL = lname(".asanModulename") | 
|  | asanModulename := typecheck.NewName(symL) | 
|  | asanModulename.SetType(types.Types[types.TSTRING]) | 
|  | asanModulename.Class = ir.PEXTERN | 
|  | symL.Def = asanModulename | 
|  | typecheck.Target.Externs = append(typecheck.Target.Externs, asanModulename) | 
|  |  | 
|  | symL = lname(".asanFilename") | 
|  | asanFilename := typecheck.NewName(symL) | 
|  | asanFilename.SetType(types.Types[types.TSTRING]) | 
|  | asanFilename.Class = ir.PEXTERN | 
|  | symL.Def = asanFilename | 
|  | typecheck.Target.Externs = append(typecheck.Target.Externs, asanFilename) | 
|  |  | 
|  | var init ir.Nodes | 
|  | var c ir.Node | 
|  | // globals[i].odrIndicator = 0 is the default, no need to set it explicitly here. | 
|  | for i, n := range InstrumentGlobalsSlice { | 
|  | setField := func(f string, val ir.Node, i int) { | 
|  | r := ir.NewAssignStmt(base.Pos, ir.NewSelectorExpr(base.Pos, ir.ODOT, | 
|  | ir.NewIndexExpr(base.Pos, globals, ir.NewInt(int64(i))), lname(f)), val) | 
|  | init.Append(typecheck.Stmt(r)) | 
|  | } | 
|  | // globals[i].beg = uintptr(unsafe.Pointer(&n)) | 
|  | c = tconv(typecheck.NodAddr(n), types.Types[types.TUNSAFEPTR]) | 
|  | c = tconv(c, types.Types[types.TUINTPTR]) | 
|  | setField("beg", c, i) | 
|  | // Assign globals[i].size. | 
|  | g := n.(*ir.Name) | 
|  | size := g.Type().Size() | 
|  | c = tconv(ir.NewInt(size), types.Types[types.TUINTPTR]) | 
|  | setField("size", c, i) | 
|  | // Assign globals[i].sizeWithRedzone. | 
|  | rzSize := GetRedzoneSizeForGlobal(size) | 
|  | sizeWithRz := rzSize + size | 
|  | c = tconv(ir.NewInt(sizeWithRz), types.Types[types.TUINTPTR]) | 
|  | setField("sizeWithRedzone", c, i) | 
|  | // The C string type is terminated by a null character "\0", Go should use three-digit | 
|  | // octal "\000" or two-digit hexadecimal "\x00" to create null terminated string. | 
|  | // asanName = symbol's linkname + "\000" | 
|  | // globals[i].name = (*defString)(unsafe.Pointer(&asanName)).data | 
|  | name := g.Linksym().Name | 
|  | init.Append(typecheck.Stmt(ir.NewAssignStmt(base.Pos, asanName, ir.NewString(name+"\000")))) | 
|  | c = tconv(typecheck.NodAddr(asanName), types.Types[types.TUNSAFEPTR]) | 
|  | c = tconv(c, types.NewPtr(defStringstruct)) | 
|  | c = ir.NewSelectorExpr(base.Pos, ir.ODOT, c, lname("data")) | 
|  | setField("name", c, i) | 
|  |  | 
|  | // Set the name of package being compiled as a unique identifier of a module. | 
|  | // asanModulename = pkgName + "\000" | 
|  | init.Append(typecheck.Stmt(ir.NewAssignStmt(base.Pos, asanModulename, ir.NewString(types.LocalPkg.Name+"\000")))) | 
|  | c = tconv(typecheck.NodAddr(asanModulename), types.Types[types.TUNSAFEPTR]) | 
|  | c = tconv(c, types.NewPtr(defStringstruct)) | 
|  | c = ir.NewSelectorExpr(base.Pos, ir.ODOT, c, lname("data")) | 
|  | setField("moduleName", c, i) | 
|  | // Assign asanL[i].filename, asanL[i].line, asanL[i].column | 
|  | // and assign globals[i].location = uintptr(unsafe.Pointer(&asanL[i])) | 
|  | asanLi := ir.NewIndexExpr(base.Pos, asanlocation, ir.NewInt(int64(i))) | 
|  | filename := ir.NewString(base.Ctxt.PosTable.Pos(n.Pos()).Filename() + "\000") | 
|  | init.Append(typecheck.Stmt(ir.NewAssignStmt(base.Pos, asanFilename, filename))) | 
|  | c = tconv(typecheck.NodAddr(asanFilename), types.Types[types.TUNSAFEPTR]) | 
|  | c = tconv(c, types.NewPtr(defStringstruct)) | 
|  | c = ir.NewSelectorExpr(base.Pos, ir.ODOT, c, lname("data")) | 
|  | init.Append(typecheck.Stmt(ir.NewAssignStmt(base.Pos, ir.NewSelectorExpr(base.Pos, ir.ODOT, asanLi, lname("filename")), c))) | 
|  | line := ir.NewInt(int64(n.Pos().Line())) | 
|  | init.Append(typecheck.Stmt(ir.NewAssignStmt(base.Pos, ir.NewSelectorExpr(base.Pos, ir.ODOT, asanLi, lname("line")), line))) | 
|  | col := ir.NewInt(int64(n.Pos().Col())) | 
|  | init.Append(typecheck.Stmt(ir.NewAssignStmt(base.Pos, ir.NewSelectorExpr(base.Pos, ir.ODOT, asanLi, lname("column")), col))) | 
|  | c = tconv(typecheck.NodAddr(asanLi), types.Types[types.TUNSAFEPTR]) | 
|  | c = tconv(c, types.Types[types.TUINTPTR]) | 
|  | setField("sourceLocation", c, i) | 
|  | } | 
|  | fn.Body.Append(init...) | 
|  | return globals | 
|  | } | 
|  |  | 
|  | // createtypes creates the asanGlobal, asanLocation and defString struct type. | 
|  | // Go compiler does not refer to the C types, we represent the struct field | 
|  | // by a uintptr, then use type conversion to make copies of the data. | 
|  | // E.g., (*defString)(asanGlobal.name).data to C string. | 
|  | // | 
|  | // Keep in sync with src/runtime/asan/asan.go. | 
|  | // type asanGlobal struct { | 
|  | //	beg               uintptr | 
|  | //	size              uintptr | 
|  | //	size_with_redzone uintptr | 
|  | //	name              uintptr | 
|  | //	moduleName        uintptr | 
|  | //	hasDynamicInit    uintptr | 
|  | //	sourceLocation    uintptr | 
|  | //	odrIndicator      uintptr | 
|  | // } | 
|  | // | 
|  | // type asanLocation struct { | 
|  | //	filename uintptr | 
|  | //	line     int32 | 
|  | //	column   int32 | 
|  | // } | 
|  | // | 
|  | // defString is synthesized struct type meant to capture the underlying | 
|  | // implementations of string. | 
|  | // type defString struct { | 
|  | //	data uintptr | 
|  | //	len  uintptr | 
|  | // } | 
|  |  | 
|  | func createtypes() (*types.Type, *types.Type, *types.Type) { | 
|  | up := types.Types[types.TUINTPTR] | 
|  | i32 := types.Types[types.TINT32] | 
|  | fname := typecheck.Lookup | 
|  | nxp := src.NoXPos | 
|  | nfield := types.NewField | 
|  | asanGlobal := types.NewStruct(types.NoPkg, []*types.Field{ | 
|  | nfield(nxp, fname("beg"), up), | 
|  | nfield(nxp, fname("size"), up), | 
|  | nfield(nxp, fname("sizeWithRedzone"), up), | 
|  | nfield(nxp, fname("name"), up), | 
|  | nfield(nxp, fname("moduleName"), up), | 
|  | nfield(nxp, fname("hasDynamicInit"), up), | 
|  | nfield(nxp, fname("sourceLocation"), up), | 
|  | nfield(nxp, fname("odrIndicator"), up), | 
|  | }) | 
|  | types.CalcSize(asanGlobal) | 
|  |  | 
|  | asanLocation := types.NewStruct(types.NoPkg, []*types.Field{ | 
|  | nfield(nxp, fname("filename"), up), | 
|  | nfield(nxp, fname("line"), i32), | 
|  | nfield(nxp, fname("column"), i32), | 
|  | }) | 
|  | types.CalcSize(asanLocation) | 
|  |  | 
|  | defString := types.NewStruct(types.NoPkg, []*types.Field{ | 
|  | types.NewField(nxp, fname("data"), up), | 
|  | types.NewField(nxp, fname("len"), up), | 
|  | }) | 
|  | types.CalcSize(defString) | 
|  |  | 
|  | return asanGlobal, asanLocation, defString | 
|  | } | 
|  |  | 
|  | // Calculate redzone for globals. | 
|  | func GetRedzoneSizeForGlobal(size int64) int64 { | 
|  | maxRZ := int64(1 << 18) | 
|  | minRZ := int64(32) | 
|  | redZone := (size / minRZ / 4) * minRZ | 
|  | switch { | 
|  | case redZone > maxRZ: | 
|  | redZone = maxRZ | 
|  | case redZone < minRZ: | 
|  | redZone = minRZ | 
|  | } | 
|  | // Round up to multiple of minRZ. | 
|  | if size%minRZ != 0 { | 
|  | redZone += minRZ - (size % minRZ) | 
|  | } | 
|  | return redZone | 
|  | } | 
|  |  | 
|  | // InstrumentGlobalsMap contains only package-local (and unlinknamed from somewhere else) | 
|  | // globals. | 
|  | // And the key is the object name. For example, in package p, a global foo would be in this | 
|  | // map as "foo". | 
|  | // Consider range over maps is nondeterministic, make a slice to hold all the values in the | 
|  | // InstrumentGlobalsMap and iterate over the InstrumentGlobalsSlice. | 
|  | var InstrumentGlobalsMap = make(map[string]ir.Node) | 
|  | var InstrumentGlobalsSlice = make([]ir.Node, 0, 0) | 
|  |  | 
|  | func canInstrumentGlobal(g ir.Node) bool { | 
|  | if g.Op() != ir.ONAME { | 
|  | return false | 
|  | } | 
|  | n := g.(*ir.Name) | 
|  | if n.Class == ir.PFUNC { | 
|  | return false | 
|  | } | 
|  | if n.Sym().Pkg != types.LocalPkg { | 
|  | return false | 
|  | } | 
|  | // Do not instrument any _cgo_ related global variables, because they are declared in C code. | 
|  | if strings.Contains(n.Sym().Name, "cgo") { | 
|  | return false | 
|  | } | 
|  |  | 
|  | // Do not instrument globals that are linknamed, because their home package will do the work. | 
|  | if n.Sym().Linkname != "" { | 
|  | return false | 
|  | } | 
|  |  | 
|  | return true | 
|  | } |