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