]> Cypherpunks repositories - gostls13.git/commitdiff
[dev.link] cmd/link: initial support for linker-materialized external symbols
authorThan McIntosh <thanm@google.com>
Wed, 11 Dec 2019 18:39:39 +0000 (13:39 -0500)
committerThan McIntosh <thanm@google.com>
Wed, 18 Dec 2019 14:23:48 +0000 (14:23 +0000)
Create loader infrastructure for constructing the payloads of external
symbols from scratch, as opposed to passing in a sym.Symbol object
containing the payload.

The general idea is that clients can use the loader to create new
external Sym's using loader.AddExtSym, and then can add
relocations/data to the new sym with symbol builder interfaces (to be
provided in an subsequent patch), as opposed to having to use
sym.Symbol.

This change preserves compatibility with the old way of doing things
(passing in sym.Symbol) via a new loader.InstallSym method. If a
client invokes this method for a specific Sym, then the loader keeps
track of this fact and uses the sym.Symbol as the backing store instead.

Also included is a small unit test for the new interfaces -- not clear
whether this really needs to be kept around long term... it was mainly
useful during initial bringup.

Change-Id: If8ab15df7b64636e56b317155dfe6d7cdfe23b71
Reviewed-on: https://go-review.googlesource.com/c/go/+/207606
Run-TryBot: Than McIntosh <thanm@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
Reviewed-by: Jeremy Faller <jeremy@golang.org>
src/cmd/link/internal/loader/loader.go
src/cmd/link/internal/loader/loader_test.go [new file with mode: 0644]

index 47de38b034faab9299160a00fba82dc0535fc42a..764fa5f6e5ed7b7c14ec88d9a5c182d29501517f 100644 (file)
@@ -36,7 +36,7 @@ type Relocs struct {
        r  *oReader // object reader for containing package
        l  *Loader  // loader
 
-       ext *sym.Symbol // external symbol if not nil
+       extIdx Sym // index of external symbol we're examining or 0
 }
 
 // Reloc contains the payload for a specific relocation.
@@ -87,17 +87,62 @@ func (bm bitmap) Has(i Sym) bool {
        return bm[n]&(1<<r) != 0
 }
 
+// return current length of bitmap in bits.
+func (bm bitmap) len() int {
+       return len(bm) * 32
+}
 func makeBitmap(n int) bitmap {
        return make(bitmap, (n+31)/32)
 }
 
+// growBitmap insures that the specified bitmap has enough capacity,
+// reallocating (doubling the size) if needed.
+func growBitmap(reqLen int, b bitmap) bitmap {
+       curLen := b.len()
+       if reqLen > curLen {
+               b = append(b, makeBitmap(reqLen-curLen)...)
+       }
+       return b
+}
+
 // A Loader loads new object files and resolves indexed symbol references.
+//
+// Notes on the layout of global symbol index space:
+//
+// - Go object files are read before host object files; each Go object
+//   read allocates a new chunk of global index space of size P + NP,
+//   where P is the number of package defined symbols in the object and
+//   NP is the number of non-package defined symbols.
+//
+// - In loader.LoadRefs(), the loader makes a sweep through all of the
+//   non-package references in each object file and allocates sym indices
+//   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
+//   name/version lookup for each symbol it finds; this can wind up
+//   extending the external symbol index space range. The host object
+//   loader currently stores symbol payloads in sym.Symbol objects,
+//   which get handed off to the loader.
+//
+// - A given external symbol (Sym) either has a sym.Symbol acting as
+//   its backing store (this will continue to be the case until we
+//   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
+//   linker), all external symbols will be payload-based, and we can
+//   get rid of the loader.Syms array.
+//
 type Loader struct {
        start       map[*oReader]Sym // map from object file to its start index
        objs        []objIdx         // sorted by start index (i.e. objIdx.i)
        max         Sym              // current max index
        extStart    Sym              // from this index on, the symbols are externally defined
-       extSyms     []nameVer        // externally defined symbols
        builtinSyms []Sym            // global index of builtin symbols
        ocache      int              // index (into 'objs') of most recent lookup
 
@@ -105,6 +150,8 @@ type Loader struct {
        extStaticSyms map[nameVer]Sym   // externally defined static symbols, keyed by name
        overwrite     map[Sym]Sym       // overwrite[i]=j if symbol j overwrites symbol i
 
+       payloads []extSymPayload // contents of linker-materialized external syms
+
        itablink map[Sym]struct{} // itablink[j] defined if j is go.itablink.*
 
        objByPkg map[string]*oReader // map package path to its Go object reader
@@ -128,6 +175,18 @@ type Loader struct {
        strictDupMsgs int // number of strict-dup warning/errors, when FlagStrictDups is enabled
 }
 
+// extSymPayload holds the payload (data + relocations) for linker-synthesized
+// external symbols.
+type extSymPayload struct {
+       name   string // TODO: would this be better as offset into str table?
+       size   int64
+       value  int64
+       ver    int
+       kind   sym.SymKind
+       relocs []Reloc
+       data   []byte
+}
+
 const (
        // Loader.flags
        FlagStrictDups = 1 << iota
@@ -215,31 +274,53 @@ func (l *Loader) AddSym(name string, ver int, i Sym, r *oReader, dupok bool, typ
        return true
 }
 
+// newExtSym creates a new external sym with the specified
+// name/version.
+func (l *Loader) newExtSym(name string, ver int) Sym {
+       l.max++
+       i := l.max
+       if l.extStart == 0 {
+               l.extStart = i
+       }
+       l.growSyms(int(i))
+       pi := i - l.extStart
+       l.payloads[pi].name = name
+       l.payloads[pi].ver = ver
+       return i
+}
+
 // Add an external symbol (without index). Return the index of newly added
 // symbol, or 0 if not added.
 func (l *Loader) AddExtSym(name string, ver int) Sym {
-       static := ver >= sym.SymVerStatic
+       i := l.Lookup(name, ver)
+       if i != 0 {
+               return 0
+       }
+       i = l.newExtSym(name, ver)
+       static := ver >= sym.SymVerStatic || ver < 0
        if static {
-               if _, ok := l.extStaticSyms[nameVer{name, ver}]; ok {
-                       return 0
-               }
+               l.extStaticSyms[nameVer{name, ver}] = i
        } else {
-               if _, ok := l.symsByName[ver][name]; ok {
-                       return 0
-               }
+               l.symsByName[ver][name] = i
        }
-       i := l.max + 1
+       return i
+}
+
+// LookupOrCreateSym looks up the symbol with the specified name/version,
+// returning its Sym index if found. If the lookup fails, a new external
+// Sym will be created, entered into the lookup tables, and returned.
+func (l *Loader) LookupOrCreateSym(name string, ver int) Sym {
+       i := l.Lookup(name, ver)
+       if i != 0 {
+               return i
+       }
+       i = l.newExtSym(name, ver)
+       static := ver >= sym.SymVerStatic || ver < 0
        if static {
                l.extStaticSyms[nameVer{name, ver}] = i
        } else {
                l.symsByName[ver][name] = i
        }
-       l.max++
-       if l.extStart == 0 {
-               l.extStart = i
-       }
-       l.extSyms = append(l.extSyms, nameVer{name, ver})
-       l.growSyms(int(i))
        return i
 }
 
@@ -247,6 +328,21 @@ func (l *Loader) IsExternal(i Sym) bool {
        return l.extStart != 0 && i >= l.extStart
 }
 
+// getPayload returns a pointer to the extSymPayload struct for an
+// external symbol if the symbol has a payload, or nil if the
+// data for the sym is being stored in a sym.Symbol. Will panic if
+// the symbol in question is bogus (zero or not an external sym).
+func (l *Loader) getPayload(i Sym) *extSymPayload {
+       if l.extStart == 0 || i < l.extStart {
+               panic(fmt.Sprintf("bogus symbol index %d in getPayload", i))
+       }
+       if l.Syms[i] != nil {
+               return nil
+       }
+       pi := i - l.extStart
+       return &l.payloads[pi]
+}
+
 // Ensure Syms slice has enough space.
 func (l *Loader) growSyms(i int) {
        n := len(l.Syms)
@@ -254,6 +350,8 @@ func (l *Loader) growSyms(i int) {
                return
        }
        l.Syms = append(l.Syms, make([]*sym.Symbol, i+1-n)...)
+       l.payloads = append(l.payloads, make([]extSymPayload, i+1-n)...)
+       l.growReachable(int(i))
 }
 
 // Convert a local index to a global index.
@@ -434,7 +532,8 @@ func (l *Loader) RawSymName(i Sym) string {
                if s := l.Syms[i]; s != nil {
                        return s.Name
                }
-               return ""
+               pp := l.getPayload(i)
+               return pp.name
        }
        r, li := l.toLocal(i)
        osym := goobj2.Sym{}
@@ -448,7 +547,8 @@ func (l *Loader) SymName(i Sym) string {
                if s := l.Syms[i]; s != nil {
                        return s.Name // external name should already be patched?
                }
-               return ""
+               pp := l.getPayload(i)
+               return pp.name
        }
        r, li := l.toLocal(i)
        osym := goobj2.Sym{}
@@ -462,6 +562,10 @@ func (l *Loader) SymType(i Sym) sym.SymKind {
                if s := l.Syms[i]; s != nil {
                        return s.Type
                }
+               pp := l.getPayload(i)
+               if pp != nil {
+                       return pp.kind
+               }
                return 0
        }
        r, li := l.toLocal(i)
@@ -506,6 +610,10 @@ func (l *Loader) Data(i Sym) []byte {
                if s := l.Syms[i]; s != nil {
                        return s.P
                }
+               pp := l.getPayload(i)
+               if pp != nil {
+                       return pp.data
+               }
                return nil
        }
        r, li := l.toLocal(i)
@@ -582,13 +690,20 @@ func (l *Loader) SubSym(i Sym) Sym {
 
 // Initialize Reachable bitmap for running deadcode pass.
 func (l *Loader) InitReachable() {
-       l.Reachable = makeBitmap(l.NSym())
+       l.growReachable(l.NSym())
+}
+
+// Insure that reachable bitmap has enough size.
+func (l *Loader) growReachable(reqLen int) {
+       if reqLen > l.Reachable.len() {
+               l.Reachable = growBitmap(reqLen, l.Reachable)
+       }
 }
 
 // At method returns the j-th reloc for a global symbol.
 func (relocs *Relocs) At(j int) Reloc {
-       if relocs.ext != nil {
-               rel := &relocs.ext.R[j]
+       if s := relocs.l.Syms[relocs.extIdx]; s != nil {
+               rel := s.R[j]
                return Reloc{
                        Off:  rel.Off,
                        Size: rel.Siz,
@@ -597,6 +712,10 @@ func (relocs *Relocs) At(j int) Reloc {
                        Sym:  relocs.l.Lookup(rel.Sym.Name, int(rel.Sym.Version)),
                }
        }
+       if relocs.extIdx != 0 {
+               pp := relocs.l.getPayload(relocs.extIdx)
+               return pp.relocs[j]
+       }
        rel := goobj2.Reloc{}
        rel.Read(relocs.r.Reader, relocs.r.RelocOff(relocs.li, j))
        target := relocs.l.resolve(relocs.r, rel.Sym)
@@ -622,9 +741,9 @@ func (relocs *Relocs) ReadAll(dst []Reloc) []Reloc {
        }
        dst = dst[:0]
 
-       if relocs.ext != nil {
+       if s := relocs.l.Syms[relocs.extIdx]; s != nil {
                for i := 0; i < relocs.Count; i++ {
-                       erel := &relocs.ext.R[i]
+                       erel := &s.R[i]
                        rel := Reloc{
                                Off:  erel.Off,
                                Size: erel.Siz,
@@ -637,6 +756,12 @@ func (relocs *Relocs) ReadAll(dst []Reloc) []Reloc {
                return dst
        }
 
+       if relocs.extIdx != 0 {
+               pp := relocs.l.getPayload(relocs.extIdx)
+               dst = append(dst, pp.relocs...)
+               return dst
+       }
+
        off := relocs.r.RelocOff(relocs.li, 0)
        for i := 0; i < relocs.Count; i++ {
                rel := goobj2.Reloc{}
@@ -658,11 +783,18 @@ func (relocs *Relocs) ReadAll(dst []Reloc) []Reloc {
 func (l *Loader) Relocs(i Sym) Relocs {
        if l.IsExternal(i) {
                if s := l.Syms[i]; s != nil {
-                       return Relocs{Count: len(s.R), l: l, ext: s}
+                       return Relocs{Count: len(s.R), l: l, extIdx: i}
+               }
+               pp := l.getPayload(i)
+               if pp != nil {
+                       return Relocs{Count: len(pp.relocs), l: l, extIdx: i}
                }
                return Relocs{}
        }
        r, li := l.toLocal(i)
+       if r == nil {
+               panic(fmt.Sprintf("trying to get oreader for invalid sym %d\n\n", i))
+       }
        return l.relocs(r, li)
 }
 
@@ -804,12 +936,22 @@ func (l *Loader) LoadFull(arch *sys.Arch, syms *sym.Symbols) {
                        s.Attr.Set(sym.AttrReachable, l.Reachable.Has(i))
                        continue // already loaded from external object
                }
-               nv := l.extSyms[i-l.extStart]
-               if l.Reachable.Has(i) || strings.HasPrefix(nv.name, "gofile..") { // XXX file symbols are used but not marked
-                       s := l.allocSym(nv.name, nv.v)
+               sname := l.payloads[i-l.extStart].name
+               sver := l.payloads[i-l.extStart].ver
+               if l.Reachable.Has(i) || strings.HasPrefix(sname, "gofile..") { // XXX file symbols are used but not marked
+                       s := l.allocSym(sname, sver)
+                       pp := l.getPayload(i)
+                       if pp != nil {
+                               if pp.kind != sym.Sxxx || len(pp.relocs) != 0 || len(pp.data) != 0 {
+                                       // Unpack payload into sym. Currently there is nothing
+                                       // to do here, but eventually we'll need a real
+                                       // implementation.
+                                       panic("need to handle this")
+                               }
+                       }
                        preprocess(arch, s)
                        s.Attr.Set(sym.AttrReachable, l.Reachable.Has(i))
-                       l.Syms[i] = s
+                       l.installSym(i, s)
                }
        }
 
@@ -885,6 +1027,25 @@ func (l *Loader) allocSym(name string, version int) *sym.Symbol {
        return s
 }
 
+// installSym sets the underlying sym.Symbol for the specified sym index.
+func (l *Loader) installSym(i Sym, s *sym.Symbol) {
+       if s == nil {
+               panic("installSym nil symbol")
+       }
+       if l.Syms[i] != nil {
+               panic("sym already present in addNewSym")
+       }
+       if l.IsExternal(i) {
+               // temporary sanity check: make sure that the payload
+               // is empty, e.g. nobody has added symbol content already.
+               pp := l.getPayload(i)
+               if pp != nil && (len(pp.relocs) != 0 || len(pp.data) != 0) {
+                       panic("expected empty payload")
+               }
+       }
+       l.Syms[i] = s
+}
+
 // addNewSym adds a new sym.Symbol to the i-th index in the list of symbols.
 func (l *Loader) addNewSym(i Sym, name string, ver int, unit *sym.CompilationUnit, t sym.SymKind) *sym.Symbol {
        s := l.allocSym(name, ver)
@@ -898,7 +1059,7 @@ func (l *Loader) addNewSym(i Sym, name string, ver int, unit *sym.CompilationUni
        s.Type = t
        s.Unit = unit
        l.growSyms(int(i))
-       l.Syms[i] = s
+       l.installSym(i, s)
        return s
 }
 
@@ -1013,6 +1174,12 @@ func (l *Loader) LookupOrCreate(name string, version int) *sym.Symbol {
        return s
 }
 
+// CreateExtSym creates a new external symbol with the specified name
+// without adding it to any lookup tables, returning a Sym index for it.
+func (l *Loader) CreateExtSym(name string) Sym {
+       return l.newExtSym(name, sym.SymVerABI0)
+}
+
 // Create creates a symbol with the specified name, returning a
 // sym.Symbol object for it. This method is intended for static/hidden
 // symbols discovered while loading host objects. We can see more than
@@ -1033,10 +1200,9 @@ func (l *Loader) Create(name string) *sym.Symbol {
        // ext syms to the sym.Symbols hash.
        l.anonVersion--
        ver := l.anonVersion
-       l.extSyms = append(l.extSyms, nameVer{name, ver})
        l.growSyms(int(i))
        s := l.allocSym(name, ver)
-       l.Syms[i] = s
+       l.installSym(i, s)
        l.extStaticSyms[nameVer{name, ver}] = i
 
        return s
@@ -1348,6 +1514,7 @@ func (l *Loader) Dump() {
                        fmt.Println(obj.i, obj.r.unit.Lib)
                }
        }
+       fmt.Println("extStart:", l.extStart)
        fmt.Println("syms")
        for i, s := range l.Syms {
                if i == 0 {
diff --git a/src/cmd/link/internal/loader/loader_test.go b/src/cmd/link/internal/loader/loader_test.go
new file mode 100644 (file)
index 0000000..044e08e
--- /dev/null
@@ -0,0 +1,55 @@
+// Copyright 2019 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 loader
+
+import (
+       "cmd/link/internal/sym"
+       "testing"
+)
+
+// dummyAddSym adds the named symbol to the loader as if it had been
+// read from a Go object file. Note that it allocates a global
+// index without creating an associated object reader, so one can't
+// do anything interesting with this symbol (such as look at its
+// data or relocations).
+func addDummyObjSym(t *testing.T, ldr *Loader, or *oReader, name string) Sym {
+       idx := ldr.max + 1
+       ldr.max++
+       if ok := ldr.AddSym(name, 0, idx, or, false, sym.SRODATA); !ok {
+               t.Errorf("AddrSym failed for '" + name + "'")
+       }
+
+       return idx
+}
+
+func TestAddMaterializedSymbol(t *testing.T) {
+       ldr := NewLoader(0)
+       dummyOreader := oReader{version: -1}
+       or := &dummyOreader
+
+       // Create some syms from a dummy object file symbol to get things going.
+       addDummyObjSym(t, ldr, or, "type.uint8")
+       addDummyObjSym(t, ldr, or, "mumble")
+       addDummyObjSym(t, ldr, or, "type.string")
+
+       // Create some external symbols.
+       es1 := ldr.AddExtSym("extnew1", 0)
+       if es1 == 0 {
+               t.Fatalf("AddExtSym failed for extnew1")
+       }
+       es1x := ldr.AddExtSym("extnew1", 0)
+       if es1x != 0 {
+               t.Fatalf("AddExtSym lookup: expected 0 got %d for second lookup", es1x)
+       }
+       es2 := ldr.AddExtSym("go.info.type.uint8", 0)
+       if es2 == 0 {
+               t.Fatalf("AddExtSym failed for go.info.type.uint8")
+       }
+       // Create a nameless symbol
+       es3 := ldr.CreateExtSym("")
+       if es3 == 0 {
+               t.Fatalf("CreateExtSym failed for nameless sym")
+       }
+}