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  	"encoding/base64"
     9  	"fmt"
    10  	"go/constant"
    11  	"io"
    12  	"os"
    13  	"sort"
    14  	"strconv"
    15  	"sync"
    16  
    17  	"cmd/compile/internal/base"
    18  	"cmd/compile/internal/ir"
    19  	"cmd/compile/internal/objw"
    20  	"cmd/compile/internal/types"
    21  	"cmd/internal/notsha256"
    22  	"cmd/internal/obj"
    23  	"cmd/internal/objabi"
    24  	"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  }
   347  

View as plain text