Source file src/cmd/compile/internal/staticdata/data.go

     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  	"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.
    31  func 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.
    43  func 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.
    49  func 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  
    56  func 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  
    63  const (
    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.
    70  func 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).
    97  const 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.
   106  func 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  
   191  var slicedataGen int
   192  
   193  func 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  
   207  func 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  
   220  var (
   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.
   226  func 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  
   255  func 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.
   271  func 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  
   304  func 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.
   324  func 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  

View as plain text