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