summaryrefslogtreecommitdiffstats
path: root/src/cmd/compile/internal/staticdata
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/compile/internal/staticdata')
-rw-r--r--src/cmd/compile/internal/staticdata/data.go346
-rw-r--r--src/cmd/compile/internal/staticdata/embed.go174
2 files changed, 520 insertions, 0 deletions
diff --git a/src/cmd/compile/internal/staticdata/data.go b/src/cmd/compile/internal/staticdata/data.go
new file mode 100644
index 0000000..78c332e
--- /dev/null
+++ b/src/cmd/compile/internal/staticdata/data.go
@@ -0,0 +1,346 @@
+// 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 (
+ "encoding/base64"
+ "fmt"
+ "go/constant"
+ "io"
+ "os"
+ "sort"
+ "strconv"
+ "sync"
+
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/objw"
+ "cmd/compile/internal/types"
+ "cmd/internal/notsha256"
+ "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), int64(len(s)))
+}
+
+const (
+ stringSymPrefix = "go:string."
+ stringSymPattern = ".gostring.%d.%s"
+)
+
+// shortHashString converts the hash to a string for use with stringSymPattern.
+// We cut it to 16 bytes and then base64-encode to make it even smaller.
+func shortHashString(hash []byte) string {
+ return base64.StdEncoding.EncodeToString(hash[:16])
+}
+
+// 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 := notsha256.New()
+ io.WriteString(h, s)
+ symname = fmt.Sprintf(stringSymPattern, len(s), shortHashString(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
+}
+
+// StringSymNoCommon is like StringSym, but produces a symbol that is not content-
+// addressable. This symbol is not supposed to appear in the final binary, it is
+// only used to pass string arguments to the linker like R_USENAMEDMETHOD does.
+func StringSymNoCommon(s string) (data *obj.LSym) {
+ var nameSym obj.LSym
+ nameSym.WriteString(base.Ctxt, 0, len(s), s)
+ objw.Global(&nameSym, int32(len(s)), obj.RODATA)
+ return &nameSym
+}
+
+// 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 := io.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))
+ }
+ if len(hash) > 0 {
+ sum := notsha256.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 := notsha256.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, shortHashString(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, "")
+ 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) *obj.LSym {
+ slicedataGen++
+ symname := fmt.Sprintf(".gobytes.%d", slicedataGen)
+ lsym := types.LocalPkg.Lookup(symname).LinksymABI(obj.ABI0)
+ off := dstringdata(lsym, 0, s, pos, "slice")
+ objw.Global(lsym, int32(off), obj.NOPTR|obj.LOCAL)
+
+ return lsym
+}
+
+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, 0, "%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.
+ funcsymsmu.Lock()
+ sf, existed := s.Pkg.LookupOK(ir.FuncSymName(s))
+ if !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()
+}
+
+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()
+
+ // While compiling package runtime, we might try to create
+ // funcsyms for functions from both types.LocalPkg and
+ // ir.Pkgs.Runtime.
+ if base.Flag.CompilingRuntime && sf.OnList() {
+ continue
+ }
+
+ // 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)
+ }
+}
diff --git a/src/cmd/compile/internal/staticdata/embed.go b/src/cmd/compile/internal/staticdata/embed.go
new file mode 100644
index 0000000..a4d493c
--- /dev/null
+++ b/src/cmd/compile/internal/staticdata/embed.go
@@ -0,0 +1,174 @@
+// Copyright 2020 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 (
+ "path"
+ "sort"
+ "strings"
+
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/objw"
+ "cmd/compile/internal/types"
+ "cmd/internal/obj"
+)
+
+const (
+ embedUnknown = iota
+ embedBytes
+ embedString
+ embedFiles
+)
+
+func embedFileList(v *ir.Name, kind int) []string {
+ // Build list of files to store.
+ have := make(map[string]bool)
+ var list []string
+ for _, e := range *v.Embed {
+ for _, pattern := range e.Patterns {
+ files, ok := base.Flag.Cfg.Embed.Patterns[pattern]
+ if !ok {
+ base.ErrorfAt(e.Pos, 0, "invalid go:embed: build system did not map pattern: %s", pattern)
+ }
+ for _, file := range files {
+ if base.Flag.Cfg.Embed.Files[file] == "" {
+ base.ErrorfAt(e.Pos, 0, "invalid go:embed: build system did not map file: %s", file)
+ continue
+ }
+ if !have[file] {
+ have[file] = true
+ list = append(list, file)
+ }
+ if kind == embedFiles {
+ for dir := path.Dir(file); dir != "." && !have[dir]; dir = path.Dir(dir) {
+ have[dir] = true
+ list = append(list, dir+"/")
+ }
+ }
+ }
+ }
+ }
+ sort.Slice(list, func(i, j int) bool {
+ return embedFileLess(list[i], list[j])
+ })
+
+ if kind == embedString || kind == embedBytes {
+ if len(list) > 1 {
+ base.ErrorfAt(v.Pos(), 0, "invalid go:embed: multiple files for type %v", v.Type())
+ return nil
+ }
+ }
+
+ return list
+}
+
+// embedKind determines the kind of embedding variable.
+func embedKind(typ *types.Type) int {
+ if typ.Sym() != nil && typ.Sym().Name == "FS" && typ.Sym().Pkg.Path == "embed" {
+ return embedFiles
+ }
+ if typ.Kind() == types.TSTRING {
+ return embedString
+ }
+ if typ.IsSlice() && typ.Elem().Kind() == types.TUINT8 {
+ return embedBytes
+ }
+ return embedUnknown
+}
+
+func embedFileNameSplit(name string) (dir, elem string, isDir bool) {
+ if name[len(name)-1] == '/' {
+ isDir = true
+ name = name[:len(name)-1]
+ }
+ i := len(name) - 1
+ for i >= 0 && name[i] != '/' {
+ i--
+ }
+ if i < 0 {
+ return ".", name, isDir
+ }
+ return name[:i], name[i+1:], isDir
+}
+
+// embedFileLess implements the sort order for a list of embedded files.
+// See the comment inside ../../../../embed/embed.go's Files struct for rationale.
+func embedFileLess(x, y string) bool {
+ xdir, xelem, _ := embedFileNameSplit(x)
+ ydir, yelem, _ := embedFileNameSplit(y)
+ return xdir < ydir || xdir == ydir && xelem < yelem
+}
+
+// WriteEmbed emits the init data for a //go:embed variable,
+// which is either a string, a []byte, or an embed.FS.
+func WriteEmbed(v *ir.Name) {
+ // TODO(mdempsky): User errors should be reported by the frontend.
+
+ commentPos := (*v.Embed)[0].Pos
+ if base.Flag.Cfg.Embed.Patterns == nil {
+ base.ErrorfAt(commentPos, 0, "invalid go:embed: build system did not supply embed configuration")
+ return
+ }
+ kind := embedKind(v.Type())
+ if kind == embedUnknown {
+ base.ErrorfAt(v.Pos(), 0, "go:embed cannot apply to var of type %v", v.Type())
+ return
+ }
+
+ files := embedFileList(v, kind)
+ switch kind {
+ case embedString, embedBytes:
+ file := files[0]
+ fsym, size, err := fileStringSym(v.Pos(), base.Flag.Cfg.Embed.Files[file], kind == embedString, nil)
+ if err != nil {
+ base.ErrorfAt(v.Pos(), 0, "embed %s: %v", file, err)
+ }
+ sym := v.Linksym()
+ off := 0
+ off = objw.SymPtr(sym, off, fsym, 0) // data string
+ off = objw.Uintptr(sym, off, uint64(size)) // len
+ if kind == embedBytes {
+ objw.Uintptr(sym, off, uint64(size)) // cap for slice
+ }
+
+ case embedFiles:
+ slicedata := v.Sym().Pkg.Lookup(v.Sym().Name + `.files`).Linksym()
+ off := 0
+ // []files pointed at by Files
+ off = objw.SymPtr(slicedata, off, slicedata, 3*types.PtrSize) // []file, pointing just past slice
+ off = objw.Uintptr(slicedata, off, uint64(len(files)))
+ off = objw.Uintptr(slicedata, off, uint64(len(files)))
+
+ // embed/embed.go type file is:
+ // name string
+ // data string
+ // hash [16]byte
+ // Emit one of these per file in the set.
+ const hashSize = 16
+ hash := make([]byte, hashSize)
+ for _, file := range files {
+ off = objw.SymPtr(slicedata, off, StringSym(v.Pos(), file), 0) // file string
+ off = objw.Uintptr(slicedata, off, uint64(len(file)))
+ if strings.HasSuffix(file, "/") {
+ // entry for directory - no data
+ off = objw.Uintptr(slicedata, off, 0)
+ off = objw.Uintptr(slicedata, off, 0)
+ off += hashSize
+ } else {
+ fsym, size, err := fileStringSym(v.Pos(), base.Flag.Cfg.Embed.Files[file], true, hash)
+ if err != nil {
+ base.ErrorfAt(v.Pos(), 0, "embed %s: %v", file, err)
+ }
+ off = objw.SymPtr(slicedata, off, fsym, 0) // data string
+ off = objw.Uintptr(slicedata, off, uint64(size))
+ off = int(slicedata.WriteBytes(base.Ctxt, int64(off), hash))
+ }
+ }
+ objw.Global(slicedata, int32(off), obj.RODATA|obj.LOCAL)
+ sym := v.Linksym()
+ objw.SymPtr(sym, 0, slicedata, 0)
+ }
+}