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 }