blob: f25d8d8ec541f8a68aea5a513e4ae222397549fa [file] [log] [blame]
// Copyright 2009 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 staticdata
import (
"crypto/sha256"
"fmt"
"go/constant"
"internal/buildcfg"
"io"
"io/ioutil"
"os"
"sort"
"strconv"
"sync"
"cmd/compile/internal/base"
"cmd/compile/internal/ir"
"cmd/compile/internal/objw"
"cmd/compile/internal/typecheck"
"cmd/compile/internal/types"
"cmd/internal/obj"
"cmd/internal/objabi"
"cmd/internal/src"
)
// InitAddrOffset writes the static name symbol lsym to n, it does not modify n.
// It's the caller responsibility to make sure lsym is from ONAME/PEXTERN node.
func InitAddrOffset(n *ir.Name, noff int64, lsym *obj.LSym, off int64) {
if n.Op() != ir.ONAME {
base.Fatalf("InitAddr n op %v", n.Op())
}
if n.Sym() == nil {
base.Fatalf("InitAddr nil n sym")
}
s := n.Linksym()
s.WriteAddr(base.Ctxt, noff, types.PtrSize, lsym, off)
}
// InitAddr is InitAddrOffset, with offset fixed to 0.
func InitAddr(n *ir.Name, noff int64, lsym *obj.LSym) {
InitAddrOffset(n, noff, lsym, 0)
}
// InitSlice writes a static slice symbol {lsym, lencap, lencap} to n+noff, it does not modify n.
// It's the caller responsibility to make sure lsym is from ONAME node.
func InitSlice(n *ir.Name, noff int64, lsym *obj.LSym, lencap int64) {
s := n.Linksym()
s.WriteAddr(base.Ctxt, noff, types.PtrSize, lsym, 0)
s.WriteInt(base.Ctxt, noff+types.SliceLenOffset, types.PtrSize, lencap)
s.WriteInt(base.Ctxt, noff+types.SliceCapOffset, types.PtrSize, lencap)
}
func InitSliceBytes(nam *ir.Name, off int64, s string) {
if nam.Op() != ir.ONAME {
base.Fatalf("InitSliceBytes %v", nam)
}
InitSlice(nam, off, slicedata(nam.Pos(), s).Linksym(), int64(len(s)))
}
const (
stringSymPrefix = "go.string."
stringSymPattern = ".gostring.%d.%x"
)
// StringSym returns a symbol containing the string s.
// The symbol contains the string data, not a string header.
func StringSym(pos src.XPos, s string) (data *obj.LSym) {
var symname string
if len(s) > 100 {
// Huge strings are hashed to avoid long names in object files.
// Indulge in some paranoia by writing the length of s, too,
// as protection against length extension attacks.
// Same pattern is known to fileStringSym below.
h := sha256.New()
io.WriteString(h, s)
symname = fmt.Sprintf(stringSymPattern, len(s), h.Sum(nil))
} else {
// Small strings get named directly by their contents.
symname = strconv.Quote(s)
}
symdata := base.Ctxt.Lookup(stringSymPrefix + symname)
if !symdata.OnList() {
off := dstringdata(symdata, 0, s, pos, "string")
objw.Global(symdata, int32(off), obj.DUPOK|obj.RODATA|obj.LOCAL)
symdata.Set(obj.AttrContentAddressable, true)
}
return symdata
}
// maxFileSize is the maximum file size permitted by the linker
// (see issue #9862).
const maxFileSize = int64(2e9)
// fileStringSym returns a symbol for the contents and the size of file.
// If readonly is true, the symbol shares storage with any literal string
// or other file with the same content and is placed in a read-only section.
// If readonly is false, the symbol is a read-write copy separate from any other,
// for use as the backing store of a []byte.
// The content hash of file is copied into hash. (If hash is nil, nothing is copied.)
// The returned symbol contains the data itself, not a string header.
func fileStringSym(pos src.XPos, file string, readonly bool, hash []byte) (*obj.LSym, int64, error) {
f, err := os.Open(file)
if err != nil {
return nil, 0, err
}
defer f.Close()
info, err := f.Stat()
if err != nil {
return nil, 0, err
}
if !info.Mode().IsRegular() {
return nil, 0, fmt.Errorf("not a regular file")
}
size := info.Size()
if size <= 1*1024 {
data, err := ioutil.ReadAll(f)
if err != nil {
return nil, 0, err
}
if int64(len(data)) != size {
return nil, 0, fmt.Errorf("file changed between reads")
}
var sym *obj.LSym
if readonly {
sym = StringSym(pos, string(data))
} else {
sym = slicedata(pos, string(data)).Linksym()
}
if len(hash) > 0 {
sum := sha256.Sum256(data)
copy(hash, sum[:])
}
return sym, size, nil
}
if size > maxFileSize {
// ggloblsym takes an int32,
// and probably the rest of the toolchain
// can't handle such big symbols either.
// See golang.org/issue/9862.
return nil, 0, fmt.Errorf("file too large (%d bytes > %d bytes)", size, maxFileSize)
}
// File is too big to read and keep in memory.
// Compute hash if needed for read-only content hashing or if the caller wants it.
var sum []byte
if readonly || len(hash) > 0 {
h := sha256.New()
n, err := io.Copy(h, f)
if err != nil {
return nil, 0, err
}
if n != size {
return nil, 0, fmt.Errorf("file changed between reads")
}
sum = h.Sum(nil)
copy(hash, sum)
}
var symdata *obj.LSym
if readonly {
symname := fmt.Sprintf(stringSymPattern, size, sum)
symdata = base.Ctxt.Lookup(stringSymPrefix + symname)
if !symdata.OnList() {
info := symdata.NewFileInfo()
info.Name = file
info.Size = size
objw.Global(symdata, int32(size), obj.DUPOK|obj.RODATA|obj.LOCAL)
// Note: AttrContentAddressable cannot be set here,
// because the content-addressable-handling code
// does not know about file symbols.
}
} else {
// Emit a zero-length data symbol
// and then fix up length and content to use file.
symdata = slicedata(pos, "").Linksym()
symdata.Size = size
symdata.Type = objabi.SNOPTRDATA
info := symdata.NewFileInfo()
info.Name = file
info.Size = size
}
return symdata, size, nil
}
var slicedataGen int
func slicedata(pos src.XPos, s string) *ir.Name {
slicedataGen++
symname := fmt.Sprintf(".gobytes.%d", slicedataGen)
sym := types.LocalPkg.Lookup(symname)
symnode := typecheck.NewName(sym)
sym.Def = symnode
lsym := symnode.Linksym()
off := dstringdata(lsym, 0, s, pos, "slice")
objw.Global(lsym, int32(off), obj.NOPTR|obj.LOCAL)
return symnode
}
func dstringdata(s *obj.LSym, off int, t string, pos src.XPos, what string) int {
// Objects that are too large will cause the data section to overflow right away,
// causing a cryptic error message by the linker. Check for oversize objects here
// and provide a useful error message instead.
if int64(len(t)) > 2e9 {
base.ErrorfAt(pos, "%v with length %v is too big", what, len(t))
return 0
}
s.WriteString(base.Ctxt, int64(off), len(t), t)
return off + len(t)
}
var (
funcsymsmu sync.Mutex // protects funcsyms and associated package lookups (see func funcsym)
funcsyms []*ir.Name // functions that need function value symbols
)
// FuncLinksym returns n·f, the function value symbol for n.
func FuncLinksym(n *ir.Name) *obj.LSym {
if n.Op() != ir.ONAME || n.Class != ir.PFUNC {
base.Fatalf("expected func name: %v", n)
}
s := n.Sym()
// funcsymsmu here serves to protect not just mutations of funcsyms (below),
// but also the package lookup of the func sym name,
// since this function gets called concurrently from the backend.
// There are no other concurrent package lookups in the backend,
// except for the types package, which is protected separately.
// Reusing funcsymsmu to also cover this package lookup
// avoids a general, broader, expensive package lookup mutex.
// Note NeedFuncSym also does package look-up of func sym names,
// but that it is only called serially, from the front end.
funcsymsmu.Lock()
sf, existed := s.Pkg.LookupOK(ir.FuncSymName(s))
// Don't export s·f when compiling for dynamic linking.
// When dynamically linking, the necessary function
// symbols will be created explicitly with NeedFuncSym.
// See the NeedFuncSym comment for details.
if !base.Ctxt.Flag_dynlink && !existed {
funcsyms = append(funcsyms, n)
}
funcsymsmu.Unlock()
return sf.Linksym()
}
func GlobalLinksym(n *ir.Name) *obj.LSym {
if n.Op() != ir.ONAME || n.Class != ir.PEXTERN {
base.Fatalf("expected global variable: %v", n)
}
return n.Linksym()
}
// NeedFuncSym ensures that fn·f is exported, if needed.
// It is only used with -dynlink.
// When not compiling for dynamic linking,
// the funcsyms are created as needed by
// the packages that use them.
// Normally we emit the fn·f stubs as DUPOK syms,
// but DUPOK doesn't work across shared library boundaries.
// So instead, when dynamic linking, we only create
// the fn·f stubs in fn's package.
func NeedFuncSym(fn *ir.Func) {
if base.Ctxt.InParallel {
// The append below probably just needs to lock
// funcsymsmu, like in FuncSym.
base.Fatalf("NeedFuncSym must be called in serial")
}
if fn.ABI != obj.ABIInternal && buildcfg.Experiment.RegabiWrappers {
// Function values must always reference ABIInternal
// entry points, so it doesn't make sense to create a
// funcsym for other ABIs.
//
// (If we're using ABI aliases, it doesn't matter.)
base.Fatalf("expected ABIInternal: %v has %v", fn.Nname, fn.ABI)
}
if ir.IsBlank(fn.Nname) {
// Blank functions aren't unique, so we can't make a
// funcsym for them.
base.Fatalf("NeedFuncSym called for _")
}
if !base.Ctxt.Flag_dynlink {
return
}
s := fn.Nname.Sym()
if base.Flag.CompilingRuntime && (s.Name == "getg" || s.Name == "getclosureptr" || s.Name == "getcallerpc" || s.Name == "getcallersp") ||
(base.Ctxt.Pkgpath == "internal/abi" && (s.Name == "FuncPCABI0" || s.Name == "FuncPCABIInternal")) {
// runtime.getg(), getclosureptr(), getcallerpc(), getcallersp(),
// and internal/abi.FuncPCABIxxx() are not real functions and so
// do not get funcsyms.
return
}
funcsyms = append(funcsyms, fn.Nname)
}
func WriteFuncSyms() {
sort.Slice(funcsyms, func(i, j int) bool {
return funcsyms[i].Linksym().Name < funcsyms[j].Linksym().Name
})
for _, nam := range funcsyms {
s := nam.Sym()
sf := s.Pkg.Lookup(ir.FuncSymName(s)).Linksym()
// Function values must always reference ABIInternal
// entry points.
target := s.Linksym()
if target.ABI() != obj.ABIInternal {
base.Fatalf("expected ABIInternal: %v has %v", target, target.ABI())
}
objw.SymPtr(sf, 0, target, 0)
objw.Global(sf, int32(types.PtrSize), obj.DUPOK|obj.RODATA)
}
}
// InitConst writes the static literal c to n.
// Neither n nor c is modified.
func InitConst(n *ir.Name, noff int64, c ir.Node, wid int) {
if n.Op() != ir.ONAME {
base.Fatalf("InitConst n op %v", n.Op())
}
if n.Sym() == nil {
base.Fatalf("InitConst nil n sym")
}
if c.Op() == ir.ONIL {
return
}
if c.Op() != ir.OLITERAL {
base.Fatalf("InitConst c op %v", c.Op())
}
s := n.Linksym()
switch u := c.Val(); u.Kind() {
case constant.Bool:
i := int64(obj.Bool2int(constant.BoolVal(u)))
s.WriteInt(base.Ctxt, noff, wid, i)
case constant.Int:
s.WriteInt(base.Ctxt, noff, wid, ir.IntVal(c.Type(), u))
case constant.Float:
f, _ := constant.Float64Val(u)
switch c.Type().Kind() {
case types.TFLOAT32:
s.WriteFloat32(base.Ctxt, noff, float32(f))
case types.TFLOAT64:
s.WriteFloat64(base.Ctxt, noff, f)
}
case constant.Complex:
re, _ := constant.Float64Val(constant.Real(u))
im, _ := constant.Float64Val(constant.Imag(u))
switch c.Type().Kind() {
case types.TCOMPLEX64:
s.WriteFloat32(base.Ctxt, noff, float32(re))
s.WriteFloat32(base.Ctxt, noff+4, float32(im))
case types.TCOMPLEX128:
s.WriteFloat64(base.Ctxt, noff, re)
s.WriteFloat64(base.Ctxt, noff+8, im)
}
case constant.String:
i := constant.StringVal(u)
symdata := StringSym(n.Pos(), i)
s.WriteAddr(base.Ctxt, noff, types.PtrSize, symdata, 0)
s.WriteInt(base.Ctxt, noff+int64(types.PtrSize), types.PtrSize, int64(len(i)))
default:
base.Fatalf("InitConst unhandled OLITERAL %v", c)
}
}