mirror of
https://github.com/golang/go.git
synced 2025-05-05 23:53:05 +00:00
Since CL 391014, cmd/compile now requires the -p flag to be set the build system. This CL changes it to initialize LocalPkg.Path to the provided path, rather than relying on writing out `"".` into object files and expecting cmd/link to substitute them. However, this actually involved a rather long tail of fixes. Many have already been submitted, but a few notable ones that have to land simultaneously with changing LocalPkg: 1. When compiling package runtime, there are really two "runtime" packages: types.LocalPkg (the source package itself) and ir.Pkgs.Runtime (the compiler's internal representation, for synthetic references). Previously, these ended up creating separate link symbols (`"".xxx` and `runtime.xxx`, respectively), but now they both end up as `runtime.xxx`, which causes lsym collisions (notably inittask and funcsyms). 2. test/codegen tests need to be updated to expect symbols to be named `command-line-arguments.xxx` rather than `"".foo`. 3. The issue20014 test case is sensitive to the sort order of field tracking symbols. In particular, the local package now sorts to its natural place in the list, rather than to the front. Thanks to David Chase for helping track down all of the fixes needed for this CL. Updates #51734. Change-Id: Iba3041cf7ad967d18c6e17922fa06ba11798b565 Reviewed-on: https://go-review.googlesource.com/c/go/+/393715 Reviewed-by: David Chase <drchase@google.com> Run-TryBot: Matthew Dempsky <mdempsky@google.com> TryBot-Result: Gopher Robot <gobot@golang.org> Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
343 lines
10 KiB
Go
343 lines
10 KiB
Go
// 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"
|
|
"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/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).Linksym(), 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
|
|
}
|
|
|
|
// 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 := 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, "").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.
|
|
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)
|
|
}
|
|
}
|