github.com/bir3/gocompiler@v0.3.205/src/cmd/compile/internal/staticdata/data.go (about) 1 // Copyright 2009 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 staticdata 6 7 import ( 8 "encoding/base64" 9 "fmt" 10 "github.com/bir3/gocompiler/src/go/constant" 11 "io" 12 "os" 13 "sort" 14 "strconv" 15 "sync" 16 17 "github.com/bir3/gocompiler/src/cmd/compile/internal/base" 18 "github.com/bir3/gocompiler/src/cmd/compile/internal/ir" 19 "github.com/bir3/gocompiler/src/cmd/compile/internal/objw" 20 "github.com/bir3/gocompiler/src/cmd/compile/internal/typecheck" 21 "github.com/bir3/gocompiler/src/cmd/compile/internal/types" 22 "github.com/bir3/gocompiler/src/cmd/internal/notsha256" 23 "github.com/bir3/gocompiler/src/cmd/internal/obj" 24 "github.com/bir3/gocompiler/src/cmd/internal/objabi" 25 "github.com/bir3/gocompiler/src/cmd/internal/src" 26 ) 27 28 // InitAddrOffset writes the static name symbol lsym to n, it does not modify n. 29 // It's the caller responsibility to make sure lsym is from ONAME/PEXTERN node. 30 func InitAddrOffset(n *ir.Name, noff int64, lsym *obj.LSym, off int64) { 31 if n.Op() != ir.ONAME { 32 base.Fatalf("InitAddr n op %v", n.Op()) 33 } 34 if n.Sym() == nil { 35 base.Fatalf("InitAddr nil n sym") 36 } 37 s := n.Linksym() 38 s.WriteAddr(base.Ctxt, noff, types.PtrSize, lsym, off) 39 } 40 41 // InitAddr is InitAddrOffset, with offset fixed to 0. 42 func InitAddr(n *ir.Name, noff int64, lsym *obj.LSym) { 43 InitAddrOffset(n, noff, lsym, 0) 44 } 45 46 // InitSlice writes a static slice symbol {lsym, lencap, lencap} to n+noff, it does not modify n. 47 // It's the caller responsibility to make sure lsym is from ONAME node. 48 func InitSlice(n *ir.Name, noff int64, lsym *obj.LSym, lencap int64) { 49 s := n.Linksym() 50 s.WriteAddr(base.Ctxt, noff, types.PtrSize, lsym, 0) 51 s.WriteInt(base.Ctxt, noff+types.SliceLenOffset, types.PtrSize, lencap) 52 s.WriteInt(base.Ctxt, noff+types.SliceCapOffset, types.PtrSize, lencap) 53 } 54 55 func InitSliceBytes(nam *ir.Name, off int64, s string) { 56 if nam.Op() != ir.ONAME { 57 base.Fatalf("InitSliceBytes %v", nam) 58 } 59 InitSlice(nam, off, slicedata(nam.Pos(), s).Linksym(), int64(len(s))) 60 } 61 62 const ( 63 stringSymPrefix = "go:string." 64 stringSymPattern = ".gostring.%d.%s" 65 ) 66 67 // shortHashString converts the hash to a string for use with stringSymPattern. 68 // We cut it to 16 bytes and then base64-encode to make it even smaller. 69 func shortHashString(hash []byte) string { 70 return base64.StdEncoding.EncodeToString(hash[:16]) 71 } 72 73 // StringSym returns a symbol containing the string s. 74 // The symbol contains the string data, not a string header. 75 func StringSym(pos src.XPos, s string) (data *obj.LSym) { 76 var symname string 77 if len(s) > 100 { 78 // Huge strings are hashed to avoid long names in object files. 79 // Indulge in some paranoia by writing the length of s, too, 80 // as protection against length extension attacks. 81 // Same pattern is known to fileStringSym below. 82 h := notsha256.New() 83 io.WriteString(h, s) 84 symname = fmt.Sprintf(stringSymPattern, len(s), shortHashString(h.Sum(nil))) 85 } else { 86 // Small strings get named directly by their contents. 87 symname = strconv.Quote(s) 88 } 89 90 symdata := base.Ctxt.Lookup(stringSymPrefix + symname) 91 if !symdata.OnList() { 92 off := dstringdata(symdata, 0, s, pos, "string") 93 objw.Global(symdata, int32(off), obj.DUPOK|obj.RODATA|obj.LOCAL) 94 symdata.Set(obj.AttrContentAddressable, true) 95 } 96 97 return symdata 98 } 99 100 // maxFileSize is the maximum file size permitted by the linker 101 // (see issue #9862). 102 const maxFileSize = int64(2e9) 103 104 // fileStringSym returns a symbol for the contents and the size of file. 105 // If readonly is true, the symbol shares storage with any literal string 106 // or other file with the same content and is placed in a read-only section. 107 // If readonly is false, the symbol is a read-write copy separate from any other, 108 // for use as the backing store of a []byte. 109 // The content hash of file is copied into hash. (If hash is nil, nothing is copied.) 110 // The returned symbol contains the data itself, not a string header. 111 func fileStringSym(pos src.XPos, file string, readonly bool, hash []byte) (*obj.LSym, int64, error) { 112 f, err := os.Open(file) 113 if err != nil { 114 return nil, 0, err 115 } 116 defer f.Close() 117 info, err := f.Stat() 118 if err != nil { 119 return nil, 0, err 120 } 121 if !info.Mode().IsRegular() { 122 return nil, 0, fmt.Errorf("not a regular file") 123 } 124 size := info.Size() 125 if size <= 1*1024 { 126 data, err := io.ReadAll(f) 127 if err != nil { 128 return nil, 0, err 129 } 130 if int64(len(data)) != size { 131 return nil, 0, fmt.Errorf("file changed between reads") 132 } 133 var sym *obj.LSym 134 if readonly { 135 sym = StringSym(pos, string(data)) 136 } else { 137 sym = slicedata(pos, string(data)).Linksym() 138 } 139 if len(hash) > 0 { 140 sum := notsha256.Sum256(data) 141 copy(hash, sum[:]) 142 } 143 return sym, size, nil 144 } 145 if size > maxFileSize { 146 // ggloblsym takes an int32, 147 // and probably the rest of the toolchain 148 // can't handle such big symbols either. 149 // See golang.org/issue/9862. 150 return nil, 0, fmt.Errorf("file too large (%d bytes > %d bytes)", size, maxFileSize) 151 } 152 153 // File is too big to read and keep in memory. 154 // Compute hash if needed for read-only content hashing or if the caller wants it. 155 var sum []byte 156 if readonly || len(hash) > 0 { 157 h := notsha256.New() 158 n, err := io.Copy(h, f) 159 if err != nil { 160 return nil, 0, err 161 } 162 if n != size { 163 return nil, 0, fmt.Errorf("file changed between reads") 164 } 165 sum = h.Sum(nil) 166 copy(hash, sum) 167 } 168 169 var symdata *obj.LSym 170 if readonly { 171 symname := fmt.Sprintf(stringSymPattern, size, shortHashString(sum)) 172 symdata = base.Ctxt.Lookup(stringSymPrefix + symname) 173 if !symdata.OnList() { 174 info := symdata.NewFileInfo() 175 info.Name = file 176 info.Size = size 177 objw.Global(symdata, int32(size), obj.DUPOK|obj.RODATA|obj.LOCAL) 178 // Note: AttrContentAddressable cannot be set here, 179 // because the content-addressable-handling code 180 // does not know about file symbols. 181 } 182 } else { 183 // Emit a zero-length data symbol 184 // and then fix up length and content to use file. 185 symdata = slicedata(pos, "").Linksym() 186 symdata.Size = size 187 symdata.Type = objabi.SNOPTRDATA 188 info := symdata.NewFileInfo() 189 info.Name = file 190 info.Size = size 191 } 192 193 return symdata, size, nil 194 } 195 196 var slicedataGen int 197 198 func slicedata(pos src.XPos, s string) *ir.Name { 199 slicedataGen++ 200 symname := fmt.Sprintf(".gobytes.%d", slicedataGen) 201 sym := types.LocalPkg.Lookup(symname) 202 symnode := typecheck.NewName(sym) 203 sym.Def = symnode 204 205 lsym := symnode.Linksym() 206 off := dstringdata(lsym, 0, s, pos, "slice") 207 objw.Global(lsym, int32(off), obj.NOPTR|obj.LOCAL) 208 209 return symnode 210 } 211 212 func dstringdata(s *obj.LSym, off int, t string, pos src.XPos, what string) int { 213 // Objects that are too large will cause the data section to overflow right away, 214 // causing a cryptic error message by the linker. Check for oversize objects here 215 // and provide a useful error message instead. 216 if int64(len(t)) > 2e9 { 217 base.ErrorfAt(pos, "%v with length %v is too big", what, len(t)) 218 return 0 219 } 220 221 s.WriteString(base.Ctxt, int64(off), len(t), t) 222 return off + len(t) 223 } 224 225 var ( 226 funcsymsmu sync.Mutex // protects funcsyms and associated package lookups (see func funcsym) 227 funcsyms []*ir.Name // functions that need function value symbols 228 ) 229 230 // FuncLinksym returns n·f, the function value symbol for n. 231 func FuncLinksym(n *ir.Name) *obj.LSym { 232 if n.Op() != ir.ONAME || n.Class != ir.PFUNC { 233 base.Fatalf("expected func name: %v", n) 234 } 235 s := n.Sym() 236 237 // funcsymsmu here serves to protect not just mutations of funcsyms (below), 238 // but also the package lookup of the func sym name, 239 // since this function gets called concurrently from the backend. 240 // There are no other concurrent package lookups in the backend, 241 // except for the types package, which is protected separately. 242 // Reusing funcsymsmu to also cover this package lookup 243 // avoids a general, broader, expensive package lookup mutex. 244 funcsymsmu.Lock() 245 sf, existed := s.Pkg.LookupOK(ir.FuncSymName(s)) 246 if !existed { 247 funcsyms = append(funcsyms, n) 248 } 249 funcsymsmu.Unlock() 250 251 return sf.Linksym() 252 } 253 254 func GlobalLinksym(n *ir.Name) *obj.LSym { 255 if n.Op() != ir.ONAME || n.Class != ir.PEXTERN { 256 base.Fatalf("expected global variable: %v", n) 257 } 258 return n.Linksym() 259 } 260 261 func WriteFuncSyms() { 262 sort.Slice(funcsyms, func(i, j int) bool { 263 return funcsyms[i].Linksym().Name < funcsyms[j].Linksym().Name 264 }) 265 for _, nam := range funcsyms { 266 s := nam.Sym() 267 sf := s.Pkg.Lookup(ir.FuncSymName(s)).Linksym() 268 269 // While compiling package runtime, we might try to create 270 // funcsyms for functions from both types.LocalPkg and 271 // ir.Pkgs.Runtime. 272 if base.Flag.CompilingRuntime && sf.OnList() { 273 continue 274 } 275 276 // Function values must always reference ABIInternal 277 // entry points. 278 target := s.Linksym() 279 if target.ABI() != obj.ABIInternal { 280 base.Fatalf("expected ABIInternal: %v has %v", target, target.ABI()) 281 } 282 objw.SymPtr(sf, 0, target, 0) 283 objw.Global(sf, int32(types.PtrSize), obj.DUPOK|obj.RODATA) 284 } 285 } 286 287 // InitConst writes the static literal c to n. 288 // Neither n nor c is modified. 289 func InitConst(n *ir.Name, noff int64, c ir.Node, wid int) { 290 if n.Op() != ir.ONAME { 291 base.Fatalf("InitConst n op %v", n.Op()) 292 } 293 if n.Sym() == nil { 294 base.Fatalf("InitConst nil n sym") 295 } 296 if c.Op() == ir.ONIL { 297 return 298 } 299 if c.Op() != ir.OLITERAL { 300 base.Fatalf("InitConst c op %v", c.Op()) 301 } 302 s := n.Linksym() 303 switch u := c.Val(); u.Kind() { 304 case constant.Bool: 305 i := int64(obj.Bool2int(constant.BoolVal(u))) 306 s.WriteInt(base.Ctxt, noff, wid, i) 307 308 case constant.Int: 309 s.WriteInt(base.Ctxt, noff, wid, ir.IntVal(c.Type(), u)) 310 311 case constant.Float: 312 f, _ := constant.Float64Val(u) 313 switch c.Type().Kind() { 314 case types.TFLOAT32: 315 s.WriteFloat32(base.Ctxt, noff, float32(f)) 316 case types.TFLOAT64: 317 s.WriteFloat64(base.Ctxt, noff, f) 318 } 319 320 case constant.Complex: 321 re, _ := constant.Float64Val(constant.Real(u)) 322 im, _ := constant.Float64Val(constant.Imag(u)) 323 switch c.Type().Kind() { 324 case types.TCOMPLEX64: 325 s.WriteFloat32(base.Ctxt, noff, float32(re)) 326 s.WriteFloat32(base.Ctxt, noff+4, float32(im)) 327 case types.TCOMPLEX128: 328 s.WriteFloat64(base.Ctxt, noff, re) 329 s.WriteFloat64(base.Ctxt, noff+8, im) 330 } 331 332 case constant.String: 333 i := constant.StringVal(u) 334 symdata := StringSym(n.Pos(), i) 335 s.WriteAddr(base.Ctxt, noff, types.PtrSize, symdata, 0) 336 s.WriteInt(base.Ctxt, noff+int64(types.PtrSize), types.PtrSize, int64(len(i))) 337 338 default: 339 base.Fatalf("InitConst unhandled OLITERAL %v", c) 340 } 341 }