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