[dev.link] cmd/link: add defined package symbols first, before any non-package symbols

Currently, the loader adds defined package symbols and
non-package symbols to the global index space object by object.
This CL changes it to add all the defined package symbols first,
then all the non-package symbols. The advantage of doing this is
that when adding package symbols, by definition they cannot be
dup to each other, so we don't need to do a name lookup when
adding them.

We still add them to the lookup table (for now), since they may
still be referenced by name (e.g. through linkname). This CL is
also a prerequisite if we want to move to not adding package
symbols to the lookup table entirely (e.g. by using
pre-generated in-file lookup table).

Also update some comments to reflect the current state.

Change-Id: Ib757e070b48a9ef6215e47dc3421fc5c055b746c
Reviewed-on: https://go-review.googlesource.com/c/go/+/219078
Run-TryBot: Cherry Zhang <cherryyz@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Than McIntosh <thanm@google.com>
This commit is contained in:
Cherry Zhang 2020-02-11 10:27:15 -05:00
parent ab1253b59a
commit b0275bfbf4
3 changed files with 80 additions and 52 deletions

View file

@ -422,30 +422,33 @@ func (ctxt *Link) loadlib() {
loadobjfile(ctxt, lib) loadobjfile(ctxt, lib)
} }
} }
// At this point, the Go objects are "preloaded". Not all the symbols are
// added to the symbol table (only defined package symbols are). Looking
// up symbol by name may not get expected result.
iscgo = ctxt.loader.Lookup("x_cgo_init", 0) != 0 iscgo = ctxt.LibraryByPkg["runtime/cgo"] != nil
ctxt.canUsePlugins = ctxt.loader.Lookup("plugin.Open", sym.SymVerABIInternal) != 0 ctxt.canUsePlugins = ctxt.LibraryByPkg["plugin"] != nil
// We now have enough information to determine the link mode. // We now have enough information to determine the link mode.
determineLinkMode(ctxt) determineLinkMode(ctxt)
if ctxt.LinkMode == LinkExternal && !iscgo && ctxt.LibraryByPkg["runtime/cgo"] == nil && !(objabi.GOOS == "darwin" && ctxt.BuildMode != BuildModePlugin && (ctxt.Arch.Family == sys.AMD64 || ctxt.Arch.Family == sys.I386)) { if ctxt.LinkMode == LinkExternal && !iscgo && !(objabi.GOOS == "darwin" && ctxt.BuildMode != BuildModePlugin && (ctxt.Arch.Family == sys.AMD64 || ctxt.Arch.Family == sys.I386)) {
// This indicates a user requested -linkmode=external. // This indicates a user requested -linkmode=external.
// The startup code uses an import of runtime/cgo to decide // The startup code uses an import of runtime/cgo to decide
// whether to initialize the TLS. So give it one. This could // whether to initialize the TLS. So give it one. This could
// be handled differently but it's an unusual case. // be handled differently but it's an unusual case.
if lib := loadinternal(ctxt, "runtime/cgo"); lib != nil { if lib := loadinternal(ctxt, "runtime/cgo"); lib != nil && lib.Shlib == "" {
if lib.Shlib != "" { if ctxt.BuildMode == BuildModeShared || ctxt.linkShared {
ldshlibsyms(ctxt, lib.Shlib) Exitf("cannot implicitly include runtime/cgo in a shared library")
} else {
if ctxt.BuildMode == BuildModeShared || ctxt.linkShared {
Exitf("cannot implicitly include runtime/cgo in a shared library")
}
loadobjfile(ctxt, lib)
} }
loadobjfile(ctxt, lib)
} }
} }
// Add non-package symbols and references of externally defined symbols.
ctxt.loader.LoadNonpkgSyms(ctxt.Arch, ctxt.Syms)
// Load symbols from shared libraries, after all Go object symbols are loaded.
for _, lib := range ctxt.Library { for _, lib := range ctxt.Library {
if lib.Shlib != "" { if lib.Shlib != "" {
if ctxt.Debugvlog > 1 { if ctxt.Debugvlog > 1 {
@ -455,9 +458,6 @@ func (ctxt *Link) loadlib() {
} }
} }
// Add references of externally defined symbols.
ctxt.loader.LoadRefs(ctxt.Arch, ctxt.Syms)
// Process cgo directives (has to be done before host object loading). // Process cgo directives (has to be done before host object loading).
ctxt.loadcgodirectives() ctxt.loadcgodirectives()

View file

@ -125,31 +125,20 @@ func growBitmap(reqLen int, b bitmap) bitmap {
// //
// Notes on the layout of global symbol index space: // Notes on the layout of global symbol index space:
// //
// TODO: rework index space reservation.
//
// - Go object files are read before host object files; each Go object // - Go object files are read before host object files; each Go object
// read adds its defined (package + non-package) symbols to the global // read adds its defined package symbols to the global index space.
// index space. // Nonpackage symbols are not yet added.
// //
// - In loader.LoadRefs(), the loader makes a sweep through all of the // - In loader.LoadNonpkgSyms, add non-package defined symbols and
// non-package references in each object file and allocates sym indices // references in all object files to the global index space.
// for any symbols that have not yet been defined (start of this space
// is marked by loader.extStart).
// //
// - Host object file loading happens; the host object loader does a // - Host object file loading happens; the host object loader does a
// name/version lookup for each symbol it finds; this can wind up // name/version lookup for each symbol it finds; this can wind up
// extending the external symbol index space range. The host object // extending the external symbol index space range. The host object
// loader currently stores symbol payloads in sym.Symbol objects, // loader stores symbol payloads in loader.payloads using SymbolBuilder.
// which get handed off to the loader.
// //
// - A given external symbol (Sym) either has a sym.Symbol acting as // - For now, in loader.LoadFull we convert all symbols (Go + external)
// its backing store (this will continue to be the case until we // to sym.Symbols.
// finish rewriting the host object loader to work entirely with
// loader.Sym) or it has a "payload" backing store (represented by
// extSymPayload). Newly created external symbols (created by
// a call to AddExtSym or equivalent) start out in the "has payload"
// state, and continue until installSym is called for the sym
// index in question.
// //
// - At some point (when the wayfront is pushed through all of the // - At some point (when the wayfront is pushed through all of the
// linker), all external symbols will be payload-based, and we can // linker), all external symbols will be payload-based, and we can
@ -237,6 +226,12 @@ type Loader struct {
elfsetstring elfsetstringFunc elfsetstring elfsetstringFunc
} }
const (
pkgDef = iota
nonPkgDef
nonPkgRef
)
type elfsetstringFunc func(s *sym.Symbol, str string, off int) type elfsetstringFunc func(s *sym.Symbol, str string, off int)
// extSymPayload holds the payload (data + relocations) for linker-synthesized // extSymPayload holds the payload (data + relocations) for linker-synthesized
@ -299,17 +294,15 @@ func (l *Loader) addObj(pkg string, r *oReader) Sym {
if _, ok := l.objByPkg[pkg]; !ok { if _, ok := l.objByPkg[pkg]; !ok {
l.objByPkg[pkg] = r l.objByPkg[pkg] = r
} }
n := r.NSym() + r.NNonpkgdef()
i := Sym(len(l.objSyms)) i := Sym(len(l.objSyms))
l.start[r] = i l.start[r] = i
l.objs = append(l.objs, objIdx{r, i}) l.objs = append(l.objs, objIdx{r, i})
l.growValues(int(i) + n - 1)
return i return i
} }
// Add a symbol from an object file, return the global index and whether it is added. // Add a symbol from an object file, return the global index and whether it is added.
// If the symbol already exist, it returns the index of that symbol. // If the symbol already exist, it returns the index of that symbol.
func (l *Loader) AddSym(name string, ver int, r *oReader, li int, dupok bool, typ sym.SymKind) (Sym, bool) { func (l *Loader) AddSym(name string, ver int, r *oReader, li int, kind int, dupok bool, typ sym.SymKind) (Sym, bool) {
if l.extStart != 0 { if l.extStart != 0 {
panic("AddSym called after AddExtSym is called") panic("AddSym called after AddExtSym is called")
} }
@ -328,6 +321,18 @@ func (l *Loader) AddSym(name string, ver int, r *oReader, li int, dupok bool, ty
addToGlobal() addToGlobal()
return i, true return i, true
} }
if kind == pkgDef {
// Defined package symbols cannot be dup to each other.
// We load all the package symbols first, so we don't need
// to check dup here.
// We still add it to the lookup table, as it may still be
// referenced by name (e.g. through linkname).
l.symsByName[ver][name] = i
addToGlobal()
return i, true
}
// Non-package (named) symbol. Check if it already exists.
oldi, existed := l.symsByName[ver][name] oldi, existed := l.symsByName[ver][name]
if !existed { if !existed {
l.symsByName[ver][name] = i l.symsByName[ver][name] = i
@ -1468,8 +1473,9 @@ func (x RelocByOff) Len() int { return len(x) }
func (x RelocByOff) Swap(i, j int) { x[i], x[j] = x[j], x[i] } func (x RelocByOff) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
func (x RelocByOff) Less(i, j int) bool { return x[i].Off < x[j].Off } func (x RelocByOff) Less(i, j int) bool { return x[i].Off < x[j].Off }
// Preload a package: add autolibs, add symbols to the symbol table. // Preload a package: add autolibs, add defined package symbols to the symbol table.
// Does not read symbol data yet. // Does not add non-package symbols yet, which will be done in LoadNonpkgSyms.
// Does not read symbol data.
func (l *Loader) Preload(arch *sys.Arch, syms *sym.Symbols, f *bio.Reader, lib *sym.Library, unit *sym.CompilationUnit, length int64, pn string, flags int) { func (l *Loader) Preload(arch *sys.Arch, syms *sym.Symbols, f *bio.Reader, lib *sym.Library, unit *sym.CompilationUnit, length int64, pn string, flags int) {
roObject, readonly, err := f.Slice(uint64(length)) roObject, readonly, err := f.Slice(uint64(length))
if err != nil { if err != nil {
@ -1495,16 +1501,38 @@ func (l *Loader) Preload(arch *sys.Arch, syms *sym.Symbols, f *bio.Reader, lib *
unit.DWARFFileTable[i] = r.DwarfFile(i) unit.DWARFFileTable[i] = r.DwarfFile(i)
} }
istart := l.addObj(lib.Pkg, or) l.addObj(lib.Pkg, or)
l.growAttrBitmaps(int(istart) + ndef + nnonpkgdef) l.preloadSyms(or, pkgDef)
for i, n := 0, ndef+nnonpkgdef; i < n; i++ {
// The caller expects us consuming all the data
f.MustSeek(length, os.SEEK_CUR)
}
// Preload symbols of given kind from an object.
func (l *Loader) preloadSyms(r *oReader, kind int) {
ndef := r.NSym()
nnonpkgdef := r.NNonpkgdef()
var start, end int
switch kind {
case pkgDef:
start = 0
end = ndef
case nonPkgDef:
start = ndef
end = ndef + nnonpkgdef
default:
panic("preloadSyms: bad kind")
}
l.growSyms(len(l.objSyms) + end - start)
l.growAttrBitmaps(len(l.objSyms) + end - start)
for i := start; i < end; i++ {
osym := goobj2.Sym{} osym := goobj2.Sym{}
osym.Read(r, r.SymOff(i)) osym.Read(r.Reader, r.SymOff(i))
name := strings.Replace(osym.Name, "\"\".", pkgprefix, -1) name := strings.Replace(osym.Name, "\"\".", r.pkgprefix, -1)
v := abiToVer(osym.ABI, localSymVersion) v := abiToVer(osym.ABI, r.version)
dupok := osym.Dupok() dupok := osym.Dupok()
gi, added := l.AddSym(name, v, or, i, dupok, sym.AbiSymKindToSymKind[objabi.SymKind(osym.Type)]) gi, added := l.AddSym(name, v, r, i, kind, dupok, sym.AbiSymKindToSymKind[objabi.SymKind(osym.Type)])
or.syms[i] = gi r.syms[i] = gi
if !added { if !added {
continue continue
} }
@ -1522,14 +1550,14 @@ func (l *Loader) Preload(arch *sys.Arch, syms *sym.Symbols, f *bio.Reader, lib *
l.SetAttrNotInSymbolTable(gi, true) l.SetAttrNotInSymbolTable(gi, true)
} }
} }
// The caller expects us consuming all the data
f.MustSeek(length, os.SEEK_CUR)
} }
// Make sure referenced symbols are added. Most of them should already be added. // Add non-package symbols and references to external symbols (which are always
// This should only be needed for referenced external symbols. // named).
func (l *Loader) LoadRefs(arch *sys.Arch, syms *sym.Symbols) { func (l *Loader) LoadNonpkgSyms(arch *sys.Arch, syms *sym.Symbols) {
for _, o := range l.objs[1:] {
l.preloadSyms(o.r, nonPkgDef)
}
for _, o := range l.objs[1:] { for _, o := range l.objs[1:] {
loadObjRefs(l, o.r, arch, syms) loadObjRefs(l, o.r, arch, syms)
} }

View file

@ -20,7 +20,7 @@ import (
// data or relocations). // data or relocations).
func addDummyObjSym(t *testing.T, ldr *Loader, or *oReader, name string) Sym { func addDummyObjSym(t *testing.T, ldr *Loader, or *oReader, name string) Sym {
idx := len(ldr.objSyms) idx := len(ldr.objSyms)
s, ok := ldr.AddSym(name, 0, or, idx, false, sym.SRODATA) s, ok := ldr.AddSym(name, 0, or, idx, nonPkgDef, false, sym.SRODATA)
if !ok { if !ok {
t.Errorf("AddrSym failed for '" + name + "'") t.Errorf("AddrSym failed for '" + name + "'")
} }