mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
CL 328051 introduced new syncImplicitTypes, but forgot to add a sync after syncAddBody in linker.relocFuncExt, cause the compiler crashes when reading in package data. Adding missing w.sync(syncImplicitTypes) call fixes this. While at it, also run go generate to update code generated for syncImplicitTypes, which is also missed in CL 328051. Change-Id: Ic65092f69f8d8e63de15989c7f15b6e5633d8f9e Reviewed-on: https://go-review.googlesource.com/c/go/+/328054 Trust: Cuong Manh Le <cuong.manhle.vn@gmail.com> Run-TryBot: Cuong Manh Le <cuong.manhle.vn@gmail.com> Reviewed-by: Matthew Dempsky <mdempsky@google.com> TryBot-Result: Go Bot <gobot@golang.org>
297 lines
6.9 KiB
Go
297 lines
6.9 KiB
Go
// UNREVIEWED
|
|
|
|
// Copyright 2021 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 noder
|
|
|
|
import (
|
|
"io"
|
|
|
|
"cmd/compile/internal/base"
|
|
"cmd/compile/internal/ir"
|
|
"cmd/compile/internal/reflectdata"
|
|
"cmd/compile/internal/types"
|
|
"cmd/internal/goobj"
|
|
"cmd/internal/obj"
|
|
)
|
|
|
|
// This file implements the unified IR linker, which combines the
|
|
// local package's stub data with imported package data to produce a
|
|
// complete export data file. It also rewrites the compiler's
|
|
// extension data sections based on the results of compilation (e.g.,
|
|
// the function inlining cost and linker symbol index assignments).
|
|
//
|
|
// TODO(mdempsky): Using the name "linker" here is confusing, because
|
|
// readers are likely to mistake references to it for cmd/link. But
|
|
// there's a shortage of good names for "something that combines
|
|
// multiple parts into a cohesive whole"... e.g., "assembler" and
|
|
// "compiler" are also already taken.
|
|
|
|
type linker struct {
|
|
pw pkgEncoder
|
|
|
|
pkgs map[string]int
|
|
decls map[*types.Sym]int
|
|
}
|
|
|
|
func (l *linker) relocAll(pr *pkgReader, relocs []relocEnt) []relocEnt {
|
|
res := make([]relocEnt, len(relocs))
|
|
for i, rent := range relocs {
|
|
rent.idx = l.relocIdx(pr, rent.kind, rent.idx)
|
|
res[i] = rent
|
|
}
|
|
return res
|
|
}
|
|
|
|
func (l *linker) relocIdx(pr *pkgReader, k reloc, idx int) int {
|
|
assert(pr != nil)
|
|
|
|
absIdx := pr.absIdx(k, idx)
|
|
|
|
if newidx := pr.newindex[absIdx]; newidx != 0 {
|
|
return ^newidx
|
|
}
|
|
|
|
var newidx int
|
|
switch k {
|
|
case relocString:
|
|
newidx = l.relocString(pr, idx)
|
|
case relocPkg:
|
|
newidx = l.relocPkg(pr, idx)
|
|
case relocObj:
|
|
newidx = l.relocObj(pr, idx)
|
|
|
|
default:
|
|
// Generic relocations.
|
|
//
|
|
// TODO(mdempsky): Deduplicate more sections? In fact, I think
|
|
// every section could be deduplicated. This would also be easier
|
|
// if we do external relocations.
|
|
|
|
w := l.pw.newEncoderRaw(k)
|
|
l.relocCommon(pr, &w, k, idx)
|
|
newidx = w.idx
|
|
}
|
|
|
|
pr.newindex[absIdx] = ^newidx
|
|
|
|
return newidx
|
|
}
|
|
|
|
func (l *linker) relocString(pr *pkgReader, idx int) int {
|
|
return l.pw.stringIdx(pr.stringIdx(idx))
|
|
}
|
|
|
|
func (l *linker) relocPkg(pr *pkgReader, idx int) int {
|
|
path := pr.peekPkgPath(idx)
|
|
|
|
if newidx, ok := l.pkgs[path]; ok {
|
|
return newidx
|
|
}
|
|
|
|
r := pr.newDecoder(relocPkg, idx, syncPkgDef)
|
|
w := l.pw.newEncoder(relocPkg, syncPkgDef)
|
|
l.pkgs[path] = w.idx
|
|
|
|
// TODO(mdempsky): We end up leaving an empty string reference here
|
|
// from when the package was originally written as "". Probably not
|
|
// a big deal, but a little annoying. Maybe relocating
|
|
// cross-references in place is the way to go after all.
|
|
w.relocs = l.relocAll(pr, r.relocs)
|
|
|
|
_ = r.string() // original path
|
|
w.string(path)
|
|
|
|
io.Copy(&w.data, &r.data)
|
|
|
|
return w.flush()
|
|
}
|
|
|
|
func (l *linker) relocObj(pr *pkgReader, idx int) int {
|
|
path, name, tag, _ := pr.peekObj(idx)
|
|
sym := types.NewPkg(path, "").Lookup(name)
|
|
|
|
if newidx, ok := l.decls[sym]; ok {
|
|
return newidx
|
|
}
|
|
|
|
if tag == objStub && path != "builtin" && path != "unsafe" {
|
|
pri, ok := objReader[sym]
|
|
if !ok {
|
|
base.Fatalf("missing reader for %q.%v", path, name)
|
|
}
|
|
assert(ok)
|
|
|
|
pr = pri.pr
|
|
idx = pri.idx
|
|
|
|
path2, name2, tag2, _ := pr.peekObj(idx)
|
|
sym2 := types.NewPkg(path2, "").Lookup(name2)
|
|
assert(sym == sym2)
|
|
assert(tag2 != objStub)
|
|
}
|
|
|
|
w := l.pw.newEncoderRaw(relocObj)
|
|
bside := l.pw.newEncoderRaw(relocObjExt)
|
|
assert(bside.idx == w.idx)
|
|
l.decls[sym] = w.idx
|
|
|
|
l.relocCommon(pr, &w, relocObj, idx)
|
|
|
|
var obj *ir.Name
|
|
if path == "" {
|
|
var ok bool
|
|
obj, ok = sym.Def.(*ir.Name)
|
|
|
|
// Generic types and functions won't have definitions.
|
|
// For now, just generically copy their extension data.
|
|
if !ok && base.Flag.G == 0 {
|
|
base.Fatalf("missing definition for %v", sym)
|
|
}
|
|
}
|
|
|
|
if obj != nil {
|
|
bside.sync(syncObject1)
|
|
switch tag {
|
|
case objFunc:
|
|
l.relocFuncExt(&bside, obj)
|
|
case objType:
|
|
l.relocTypeExt(&bside, obj)
|
|
case objVar:
|
|
l.relocVarExt(&bside, obj)
|
|
}
|
|
bside.flush()
|
|
} else {
|
|
l.relocCommon(pr, &bside, relocObjExt, idx)
|
|
}
|
|
|
|
return w.idx
|
|
}
|
|
|
|
func (l *linker) relocCommon(pr *pkgReader, w *encoder, k reloc, idx int) {
|
|
r := pr.newDecoderRaw(k, idx)
|
|
w.relocs = l.relocAll(pr, r.relocs)
|
|
io.Copy(&w.data, &r.data)
|
|
w.flush()
|
|
}
|
|
|
|
func (l *linker) pragmaFlag(w *encoder, pragma ir.PragmaFlag) {
|
|
w.sync(syncPragma)
|
|
w.int(int(pragma))
|
|
}
|
|
|
|
func (l *linker) relocFuncExt(w *encoder, name *ir.Name) {
|
|
w.sync(syncFuncExt)
|
|
|
|
l.pragmaFlag(w, name.Func.Pragma)
|
|
l.linkname(w, name)
|
|
|
|
// Relocated extension data.
|
|
w.bool(true)
|
|
|
|
// Record definition ABI so cross-ABI calls can be direct.
|
|
// This is important for the performance of calling some
|
|
// common functions implemented in assembly (e.g., bytealg).
|
|
w.uint64(uint64(name.Func.ABI))
|
|
|
|
// Escape analysis.
|
|
for _, fs := range &types.RecvsParams {
|
|
for _, f := range fs(name.Type()).FieldSlice() {
|
|
w.string(f.Note)
|
|
}
|
|
}
|
|
|
|
if inl := name.Func.Inl; w.bool(inl != nil) {
|
|
w.len(int(inl.Cost))
|
|
w.bool(inl.CanDelayResults)
|
|
|
|
pri, ok := bodyReader[name.Func]
|
|
assert(ok)
|
|
w.sync(syncAddBody)
|
|
w.sync(syncImplicitTypes)
|
|
w.reloc(relocBody, l.relocIdx(pri.pr, relocBody, pri.idx))
|
|
}
|
|
|
|
w.sync(syncEOF)
|
|
}
|
|
|
|
func (l *linker) relocTypeExt(w *encoder, name *ir.Name) {
|
|
w.sync(syncTypeExt)
|
|
|
|
typ := name.Type()
|
|
|
|
l.pragmaFlag(w, name.Pragma())
|
|
|
|
// For type T, export the index of type descriptor symbols of T and *T.
|
|
l.lsymIdx(w, "", reflectdata.TypeLinksym(typ))
|
|
l.lsymIdx(w, "", reflectdata.TypeLinksym(typ.PtrTo()))
|
|
|
|
if typ.Kind() != types.TINTER {
|
|
for _, method := range typ.Methods().Slice() {
|
|
l.relocFuncExt(w, method.Nname.(*ir.Name))
|
|
}
|
|
}
|
|
}
|
|
|
|
func (l *linker) relocVarExt(w *encoder, name *ir.Name) {
|
|
w.sync(syncVarExt)
|
|
l.linkname(w, name)
|
|
}
|
|
|
|
func (l *linker) linkname(w *encoder, name *ir.Name) {
|
|
w.sync(syncLinkname)
|
|
|
|
linkname := name.Sym().Linkname
|
|
if !l.lsymIdx(w, linkname, name.Linksym()) {
|
|
w.string(linkname)
|
|
}
|
|
}
|
|
|
|
func (l *linker) lsymIdx(w *encoder, linkname string, lsym *obj.LSym) bool {
|
|
if lsym.PkgIdx > goobj.PkgIdxSelf || (lsym.PkgIdx == goobj.PkgIdxInvalid && !lsym.Indexed()) || linkname != "" {
|
|
w.int64(-1)
|
|
return false
|
|
}
|
|
|
|
// For a defined symbol, export its index.
|
|
// For re-exporting an imported symbol, pass its index through.
|
|
w.int64(int64(lsym.SymIdx))
|
|
return true
|
|
}
|
|
|
|
// @@@ Helpers
|
|
|
|
// TODO(mdempsky): These should probably be removed. I think they're a
|
|
// smell that the export data format is not yet quite right.
|
|
|
|
func (pr *pkgDecoder) peekPkgPath(idx int) string {
|
|
r := pr.newDecoder(relocPkg, idx, syncPkgDef)
|
|
path := r.string()
|
|
if path == "" {
|
|
path = pr.pkgPath
|
|
}
|
|
return path
|
|
}
|
|
|
|
func (pr *pkgDecoder) peekObj(idx int) (string, string, codeObj, []int) {
|
|
r := pr.newDecoder(relocObj, idx, syncObject1)
|
|
r.sync(syncSym)
|
|
r.sync(syncPkg)
|
|
path := pr.peekPkgPath(r.reloc(relocPkg))
|
|
name := r.string()
|
|
assert(name != "")
|
|
|
|
r.sync(syncTypeParamBounds)
|
|
r.len() // implicits
|
|
bounds := make([]int, r.len())
|
|
for i := range bounds {
|
|
r.sync(syncType)
|
|
bounds[i] = r.reloc(relocType)
|
|
}
|
|
|
|
tag := codeObj(r.code(syncCodeObj))
|
|
|
|
return path, name, tag, bounds
|
|
}
|