summaryrefslogtreecommitdiffstats
path: root/src/cmd/link/internal/loader/loader.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/link/internal/loader/loader.go')
-rw-r--r--src/cmd/link/internal/loader/loader.go2601
1 files changed, 2601 insertions, 0 deletions
diff --git a/src/cmd/link/internal/loader/loader.go b/src/cmd/link/internal/loader/loader.go
new file mode 100644
index 0000000..8e1575a
--- /dev/null
+++ b/src/cmd/link/internal/loader/loader.go
@@ -0,0 +1,2601 @@
+// 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 (
+ "bytes"
+ "cmd/internal/bio"
+ "cmd/internal/goobj"
+ "cmd/internal/obj"
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/sym"
+ "debug/elf"
+ "fmt"
+ "io"
+ "log"
+ "math/bits"
+ "os"
+ "sort"
+ "strings"
+)
+
+var _ = fmt.Print
+
+// Sym encapsulates a global symbol index, used to identify a specific
+// Go symbol. The 0-valued Sym is corresponds to an invalid symbol.
+type Sym int
+
+// Relocs encapsulates the set of relocations on a given symbol; an
+// instance of this type is returned by the Loader Relocs() method.
+type Relocs struct {
+ rs []goobj.Reloc
+
+ li uint32 // local index of symbol whose relocs we're examining
+ r *oReader // object reader for containing package
+ l *Loader // loader
+}
+
+// ExtReloc contains the payload for an external relocation.
+type ExtReloc struct {
+ Xsym Sym
+ Xadd int64
+ Type objabi.RelocType
+ Size uint8
+}
+
+// Reloc holds a "handle" to access a relocation record from an
+// object file.
+type Reloc struct {
+ *goobj.Reloc
+ r *oReader
+ l *Loader
+}
+
+func (rel Reloc) Type() objabi.RelocType { return objabi.RelocType(rel.Reloc.Type()) &^ objabi.R_WEAK }
+func (rel Reloc) Weak() bool { return objabi.RelocType(rel.Reloc.Type())&objabi.R_WEAK != 0 }
+func (rel Reloc) SetType(t objabi.RelocType) { rel.Reloc.SetType(uint16(t)) }
+func (rel Reloc) Sym() Sym { return rel.l.resolve(rel.r, rel.Reloc.Sym()) }
+func (rel Reloc) SetSym(s Sym) { rel.Reloc.SetSym(goobj.SymRef{PkgIdx: 0, SymIdx: uint32(s)}) }
+func (rel Reloc) IsMarker() bool { return rel.Siz() == 0 }
+
+// Aux holds a "handle" to access an aux symbol record from an
+// object file.
+type Aux struct {
+ *goobj.Aux
+ r *oReader
+ l *Loader
+}
+
+func (a Aux) Sym() Sym { return a.l.resolve(a.r, a.Aux.Sym()) }
+
+// oReader is a wrapper type of obj.Reader, along with some
+// extra information.
+type oReader struct {
+ *goobj.Reader
+ unit *sym.CompilationUnit
+ version int // version of static symbol
+ pkgprefix string
+ syms []Sym // Sym's global index, indexed by local index
+ pkg []uint32 // indices of referenced package by PkgIdx (index into loader.objs array)
+ ndef int // cache goobj.Reader.NSym()
+ nhashed64def int // cache goobj.Reader.NHashed64Def()
+ nhasheddef int // cache goobj.Reader.NHashedDef()
+ objidx uint32 // index of this reader in the objs slice
+}
+
+// Total number of defined symbols (package symbols, hashed symbols, and
+// non-package symbols).
+func (r *oReader) NAlldef() int { return r.ndef + r.nhashed64def + r.nhasheddef + r.NNonpkgdef() }
+
+type objIdx struct {
+ r *oReader
+ i Sym // start index
+}
+
+// objSym represents a symbol in an object file. It is a tuple of
+// the object and the symbol's local index.
+// For external symbols, objidx is the index of l.extReader (extObj),
+// s is its index into the payload array.
+// {0, 0} represents the nil symbol.
+type objSym struct {
+ objidx uint32 // index of the object (in l.objs array)
+ s uint32 // local index
+}
+
+type nameVer struct {
+ name string
+ v int
+}
+
+type Bitmap []uint32
+
+// set the i-th bit.
+func (bm Bitmap) Set(i Sym) {
+ n, r := uint(i)/32, uint(i)%32
+ bm[n] |= 1 << r
+}
+
+// unset the i-th bit.
+func (bm Bitmap) Unset(i Sym) {
+ n, r := uint(i)/32, uint(i)%32
+ bm[n] &^= (1 << r)
+}
+
+// whether the i-th bit is set.
+func (bm Bitmap) Has(i Sym) bool {
+ n, r := uint(i)/32, uint(i)%32
+ return bm[n]&(1<<r) != 0
+}
+
+// return current length of bitmap in bits.
+func (bm Bitmap) Len() int {
+ return len(bm) * 32
+}
+
+// return the number of bits set.
+func (bm Bitmap) Count() int {
+ s := 0
+ for _, x := range bm {
+ s += bits.OnesCount32(x)
+ }
+ return s
+}
+
+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+1-curLen)...)
+ }
+ return b
+}
+
+type symAndSize struct {
+ sym Sym
+ size uint32
+}
+
+// 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 adds its defined package symbols to the global index space.
+// Nonpackage symbols are not yet added.
+//
+// - In loader.LoadNonpkgSyms, add non-package defined symbols and
+// references in all object files to the global index space.
+//
+// - 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 stores symbol payloads in loader.payloads using SymbolBuilder.
+//
+// - Each symbol gets a unique global index. For duplicated and
+// overwriting/overwritten symbols, the second (or later) appearance
+// of the symbol gets the same global index as the first appearance.
+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)
+ extStart Sym // from this index on, the symbols are externally defined
+ builtinSyms []Sym // global index of builtin symbols
+
+ objSyms []objSym // global index mapping to local index
+
+ symsByName [2]map[string]Sym // map symbol name to index, two maps are for ABI0 and ABIInternal
+ extStaticSyms map[nameVer]Sym // externally defined static symbols, keyed by name
+
+ extReader *oReader // a dummy oReader, for external symbols
+ payloadBatch []extSymPayload
+ payloads []*extSymPayload // contents of linker-materialized external syms
+ values []int64 // symbol values, indexed by global sym index
+
+ sects []*sym.Section // sections
+ symSects []uint16 // symbol's section, index to sects array
+
+ align []uint8 // symbol 2^N alignment, indexed by global index
+
+ deferReturnTramp map[Sym]bool // whether the symbol is a trampoline of a deferreturn call
+
+ objByPkg map[string]uint32 // map package path to the index of its Go object reader
+
+ anonVersion int // most recently assigned ext static sym pseudo-version
+
+ // Bitmaps and other side structures used to store data used to store
+ // symbol flags/attributes; these are to be accessed via the
+ // corresponding loader "AttrXXX" and "SetAttrXXX" methods. Please
+ // visit the comments on these methods for more details on the
+ // semantics / interpretation of the specific flags or attribute.
+ attrReachable Bitmap // reachable symbols, indexed by global index
+ attrOnList Bitmap // "on list" symbols, indexed by global index
+ attrLocal Bitmap // "local" symbols, indexed by global index
+ attrNotInSymbolTable Bitmap // "not in symtab" symbols, indexed by global idx
+ attrUsedInIface Bitmap // "used in interface" symbols, indexed by global idx
+ attrVisibilityHidden Bitmap // hidden symbols, indexed by ext sym index
+ attrDuplicateOK Bitmap // dupOK symbols, indexed by ext sym index
+ attrShared Bitmap // shared symbols, indexed by ext sym index
+ attrExternal Bitmap // external symbols, indexed by ext sym index
+
+ attrReadOnly map[Sym]bool // readonly data for this sym
+ attrSpecial map[Sym]struct{} // "special" frame symbols
+ attrCgoExportDynamic map[Sym]struct{} // "cgo_export_dynamic" symbols
+ attrCgoExportStatic map[Sym]struct{} // "cgo_export_static" symbols
+ generatedSyms map[Sym]struct{} // symbols that generate their content
+
+ // Outer and Sub relations for symbols.
+ // TODO: figure out whether it's more efficient to just have these
+ // as fields on extSymPayload (note that this won't be a viable
+ // strategy if somewhere in the linker we set sub/outer for a
+ // non-external sym).
+ outer map[Sym]Sym
+ sub map[Sym]Sym
+
+ dynimplib map[Sym]string // stores Dynimplib symbol attribute
+ dynimpvers map[Sym]string // stores Dynimpvers symbol attribute
+ localentry map[Sym]uint8 // stores Localentry symbol attribute
+ extname map[Sym]string // stores Extname symbol attribute
+ elfType map[Sym]elf.SymType // stores elf type symbol property
+ elfSym map[Sym]int32 // stores elf sym symbol property
+ localElfSym map[Sym]int32 // stores "local" elf sym symbol property
+ symPkg map[Sym]string // stores package for symbol, or library for shlib-derived syms
+ plt map[Sym]int32 // stores dynimport for pe objects
+ got map[Sym]int32 // stores got for pe objects
+ dynid map[Sym]int32 // stores Dynid for symbol
+
+ relocVariant map[relocId]sym.RelocVariant // stores variant relocs
+
+ // Used to implement field tracking; created during deadcode if
+ // field tracking is enabled. Reachparent[K] contains the index of
+ // the symbol that triggered the marking of symbol K as live.
+ Reachparent []Sym
+
+ // CgoExports records cgo-exported symbols by SymName.
+ CgoExports map[string]Sym
+
+ flags uint32
+
+ strictDupMsgs int // number of strict-dup warning/errors, when FlagStrictDups is enabled
+
+ elfsetstring elfsetstringFunc
+
+ errorReporter *ErrorReporter
+
+ npkgsyms int // number of package symbols, for accounting
+ nhashedsyms int // number of hashed symbols, for accounting
+}
+
+const (
+ pkgDef = iota
+ hashed64Def
+ hashedDef
+ nonPkgDef
+ nonPkgRef
+)
+
+// objidx
+const (
+ nilObj = iota
+ extObj
+ goObjStart
+)
+
+type elfsetstringFunc func(str string, off int)
+
+// extSymPayload holds the payload (data + relocations) for linker-synthesized
+// external symbols (note that symbol value is stored in a separate slice).
+type extSymPayload struct {
+ name string // TODO: would this be better as offset into str table?
+ size int64
+ ver int
+ kind sym.SymKind
+ objidx uint32 // index of original object if sym made by cloneToExternal
+ relocs []goobj.Reloc
+ data []byte
+ auxs []goobj.Aux
+}
+
+const (
+ // Loader.flags
+ FlagStrictDups = 1 << iota
+)
+
+func NewLoader(flags uint32, elfsetstring elfsetstringFunc, reporter *ErrorReporter) *Loader {
+ nbuiltin := goobj.NBuiltin()
+ extReader := &oReader{objidx: extObj}
+ ldr := &Loader{
+ start: make(map[*oReader]Sym),
+ objs: []objIdx{{}, {extReader, 0}}, // reserve index 0 for nil symbol, 1 for external symbols
+ objSyms: make([]objSym, 1, 1), // This will get overwritten later.
+ extReader: extReader,
+ symsByName: [2]map[string]Sym{make(map[string]Sym, 80000), make(map[string]Sym, 50000)}, // preallocate ~2MB for ABI0 and ~1MB for ABI1 symbols
+ objByPkg: make(map[string]uint32),
+ outer: make(map[Sym]Sym),
+ sub: make(map[Sym]Sym),
+ dynimplib: make(map[Sym]string),
+ dynimpvers: make(map[Sym]string),
+ localentry: make(map[Sym]uint8),
+ extname: make(map[Sym]string),
+ attrReadOnly: make(map[Sym]bool),
+ elfType: make(map[Sym]elf.SymType),
+ elfSym: make(map[Sym]int32),
+ localElfSym: make(map[Sym]int32),
+ symPkg: make(map[Sym]string),
+ plt: make(map[Sym]int32),
+ got: make(map[Sym]int32),
+ dynid: make(map[Sym]int32),
+ attrSpecial: make(map[Sym]struct{}),
+ attrCgoExportDynamic: make(map[Sym]struct{}),
+ attrCgoExportStatic: make(map[Sym]struct{}),
+ generatedSyms: make(map[Sym]struct{}),
+ deferReturnTramp: make(map[Sym]bool),
+ extStaticSyms: make(map[nameVer]Sym),
+ builtinSyms: make([]Sym, nbuiltin),
+ flags: flags,
+ elfsetstring: elfsetstring,
+ errorReporter: reporter,
+ sects: []*sym.Section{nil}, // reserve index 0 for nil section
+ }
+ reporter.ldr = ldr
+ return ldr
+}
+
+// Add object file r, return the start index.
+func (l *Loader) addObj(pkg string, r *oReader) Sym {
+ if _, ok := l.start[r]; ok {
+ panic("already added")
+ }
+ pkg = objabi.PathToPrefix(pkg) // the object file contains escaped package path
+ if _, ok := l.objByPkg[pkg]; !ok {
+ l.objByPkg[pkg] = r.objidx
+ }
+ i := Sym(len(l.objSyms))
+ l.start[r] = i
+ l.objs = append(l.objs, objIdx{r, i})
+ return i
+}
+
+// Add a symbol from an object file, return the global index.
+// If the symbol already exist, it returns the index of that symbol.
+func (st *loadState) addSym(name string, ver int, r *oReader, li uint32, kind int, osym *goobj.Sym) Sym {
+ l := st.l
+ if l.extStart != 0 {
+ panic("addSym called after external symbol is created")
+ }
+ i := Sym(len(l.objSyms))
+ addToGlobal := func() {
+ l.objSyms = append(l.objSyms, objSym{r.objidx, li})
+ }
+ if name == "" && kind != hashed64Def && kind != hashedDef {
+ addToGlobal()
+ return i // unnamed aux symbol
+ }
+ if ver == r.version {
+ // Static symbol. Add its global index but don't
+ // add to name lookup table, as it cannot be
+ // referenced by name.
+ addToGlobal()
+ return i
+ }
+ switch kind {
+ case 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
+ case hashed64Def, hashedDef:
+ // Hashed (content-addressable) symbol. Check the hash
+ // but don't add to name lookup table, as they are not
+ // referenced by name. Also no need to do overwriting
+ // check, as same hash indicates same content.
+ var checkHash func() (symAndSize, bool)
+ var addToHashMap func(symAndSize)
+ var h64 uint64 // only used for hashed64Def
+ var h *goobj.HashType // only used for hashedDef
+ if kind == hashed64Def {
+ checkHash = func() (symAndSize, bool) {
+ h64 = r.Hash64(li - uint32(r.ndef))
+ s, existed := st.hashed64Syms[h64]
+ return s, existed
+ }
+ addToHashMap = func(ss symAndSize) { st.hashed64Syms[h64] = ss }
+ } else {
+ checkHash = func() (symAndSize, bool) {
+ h = r.Hash(li - uint32(r.ndef+r.nhashed64def))
+ s, existed := st.hashedSyms[*h]
+ return s, existed
+ }
+ addToHashMap = func(ss symAndSize) { st.hashedSyms[*h] = ss }
+ }
+ siz := osym.Siz()
+ if s, existed := checkHash(); existed {
+ // The content hash is built from symbol data and relocations. In the
+ // object file, the symbol data may not always contain trailing zeros,
+ // e.g. for [5]int{1,2,3} and [100]int{1,2,3}, the data is same
+ // (although the size is different).
+ // Also, for short symbols, the content hash is the identity function of
+ // the 8 bytes, and trailing zeros doesn't change the hash value, e.g.
+ // hash("A") == hash("A\0\0\0").
+ // So when two symbols have the same hash, we need to use the one with
+ // larger size.
+ if siz > s.size {
+ // New symbol has larger size, use the new one. Rewrite the index mapping.
+ l.objSyms[s.sym] = objSym{r.objidx, li}
+ addToHashMap(symAndSize{s.sym, siz})
+ }
+ return s.sym
+ }
+ addToHashMap(symAndSize{i, siz})
+ addToGlobal()
+ return i
+ }
+
+ // Non-package (named) symbol. Check if it already exists.
+ oldi, existed := l.symsByName[ver][name]
+ if !existed {
+ l.symsByName[ver][name] = i
+ addToGlobal()
+ return i
+ }
+ // symbol already exists
+ if osym.Dupok() {
+ if l.flags&FlagStrictDups != 0 {
+ l.checkdup(name, r, li, oldi)
+ }
+ // Fix for issue #47185 -- given two dupok symbols with
+ // different sizes, favor symbol with larger size. See
+ // also issue #46653.
+ szdup := l.SymSize(oldi)
+ sz := int64(r.Sym(li).Siz())
+ if szdup < sz {
+ // new symbol overwrites old symbol.
+ l.objSyms[oldi] = objSym{r.objidx, li}
+ }
+ return oldi
+ }
+ oldr, oldli := l.toLocal(oldi)
+ oldsym := oldr.Sym(oldli)
+ if oldsym.Dupok() {
+ return oldi
+ }
+ overwrite := r.DataSize(li) != 0
+ if overwrite {
+ // new symbol overwrites old symbol.
+ oldtyp := sym.AbiSymKindToSymKind[objabi.SymKind(oldsym.Type())]
+ if !(oldtyp.IsData() && oldr.DataSize(oldli) == 0) {
+ log.Fatalf("duplicated definition of symbol %s, from %s and %s", name, r.unit.Lib.Pkg, oldr.unit.Lib.Pkg)
+ }
+ l.objSyms[oldi] = objSym{r.objidx, li}
+ } else {
+ // old symbol overwrites new symbol.
+ typ := sym.AbiSymKindToSymKind[objabi.SymKind(oldsym.Type())]
+ if !typ.IsData() { // only allow overwriting data symbol
+ log.Fatalf("duplicated definition of symbol %s, from %s and %s", name, r.unit.Lib.Pkg, oldr.unit.Lib.Pkg)
+ }
+ }
+ return oldi
+}
+
+// newExtSym creates a new external sym with the specified
+// name/version.
+func (l *Loader) newExtSym(name string, ver int) Sym {
+ i := Sym(len(l.objSyms))
+ if l.extStart == 0 {
+ l.extStart = i
+ }
+ l.growValues(int(i) + 1)
+ l.growAttrBitmaps(int(i) + 1)
+ pi := l.newPayload(name, ver)
+ l.objSyms = append(l.objSyms, objSym{l.extReader.objidx, uint32(pi)})
+ l.extReader.syms = append(l.extReader.syms, i)
+ 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
+ }
+ return i
+}
+
+// AddCgoExport records a cgo-exported symbol in l.CgoExports.
+// This table is used to identify the correct Go symbol ABI to use
+// to resolve references from host objects (which don't have ABIs).
+func (l *Loader) AddCgoExport(s Sym) {
+ if l.CgoExports == nil {
+ l.CgoExports = make(map[string]Sym)
+ }
+ l.CgoExports[l.SymName(s)] = s
+}
+
+// LookupOrCreateCgoExport is like LookupOrCreateSym, but if ver
+// indicates a global symbol, it uses the CgoExport table to determine
+// the appropriate symbol version (ABI) to use. ver must be either 0
+// or a static symbol version.
+func (l *Loader) LookupOrCreateCgoExport(name string, ver int) Sym {
+ if ver >= sym.SymVerStatic {
+ return l.LookupOrCreateSym(name, ver)
+ }
+ if ver != 0 {
+ panic("ver must be 0 or a static version")
+ }
+ // Look for a cgo-exported symbol from Go.
+ if s, ok := l.CgoExports[name]; ok {
+ return s
+ }
+ // Otherwise, this must just be a symbol in the host object.
+ // Create a version 0 symbol for it.
+ return l.LookupOrCreateSym(name, 0)
+}
+
+func (l *Loader) IsExternal(i Sym) bool {
+ r, _ := l.toLocal(i)
+ return l.isExtReader(r)
+}
+
+func (l *Loader) isExtReader(r *oReader) bool {
+ return r == l.extReader
+}
+
+// For external symbol, return its index in the payloads array.
+// XXX result is actually not a global index. We (ab)use the Sym type
+// so we don't need conversion for accessing bitmaps.
+func (l *Loader) extIndex(i Sym) Sym {
+ _, li := l.toLocal(i)
+ return Sym(li)
+}
+
+// Get a new payload for external symbol, return its index in
+// the payloads array.
+func (l *Loader) newPayload(name string, ver int) int {
+ pi := len(l.payloads)
+ pp := l.allocPayload()
+ pp.name = name
+ pp.ver = ver
+ l.payloads = append(l.payloads, pp)
+ l.growExtAttrBitmaps()
+ return pi
+}
+
+// getPayload returns a pointer to the extSymPayload struct for an
+// external symbol if the symbol has a payload. Will panic if the
+// symbol in question is bogus (zero or not an external sym).
+func (l *Loader) getPayload(i Sym) *extSymPayload {
+ if !l.IsExternal(i) {
+ panic(fmt.Sprintf("bogus symbol index %d in getPayload", i))
+ }
+ pi := l.extIndex(i)
+ return l.payloads[pi]
+}
+
+// allocPayload allocates a new payload.
+func (l *Loader) allocPayload() *extSymPayload {
+ batch := l.payloadBatch
+ if len(batch) == 0 {
+ batch = make([]extSymPayload, 1000)
+ }
+ p := &batch[0]
+ l.payloadBatch = batch[1:]
+ return p
+}
+
+func (ms *extSymPayload) Grow(siz int64) {
+ if int64(int(siz)) != siz {
+ log.Fatalf("symgrow size %d too long", siz)
+ }
+ if int64(len(ms.data)) >= siz {
+ return
+ }
+ if cap(ms.data) < int(siz) {
+ cl := len(ms.data)
+ ms.data = append(ms.data, make([]byte, int(siz)+1-cl)...)
+ ms.data = ms.data[0:cl]
+ }
+ ms.data = ms.data[:siz]
+}
+
+// Convert a local index to a global index.
+func (l *Loader) toGlobal(r *oReader, i uint32) Sym {
+ return r.syms[i]
+}
+
+// Convert a global index to a local index.
+func (l *Loader) toLocal(i Sym) (*oReader, uint32) {
+ return l.objs[l.objSyms[i].objidx].r, l.objSyms[i].s
+}
+
+// Resolve a local symbol reference. Return global index.
+func (l *Loader) resolve(r *oReader, s goobj.SymRef) Sym {
+ var rr *oReader
+ switch p := s.PkgIdx; p {
+ case goobj.PkgIdxInvalid:
+ // {0, X} with non-zero X is never a valid sym reference from a Go object.
+ // We steal this space for symbol references from external objects.
+ // In this case, X is just the global index.
+ if l.isExtReader(r) {
+ return Sym(s.SymIdx)
+ }
+ if s.SymIdx != 0 {
+ panic("bad sym ref")
+ }
+ return 0
+ case goobj.PkgIdxHashed64:
+ i := int(s.SymIdx) + r.ndef
+ return r.syms[i]
+ case goobj.PkgIdxHashed:
+ i := int(s.SymIdx) + r.ndef + r.nhashed64def
+ return r.syms[i]
+ case goobj.PkgIdxNone:
+ i := int(s.SymIdx) + r.ndef + r.nhashed64def + r.nhasheddef
+ return r.syms[i]
+ case goobj.PkgIdxBuiltin:
+ if bi := l.builtinSyms[s.SymIdx]; bi != 0 {
+ return bi
+ }
+ l.reportMissingBuiltin(int(s.SymIdx), r.unit.Lib.Pkg)
+ return 0
+ case goobj.PkgIdxSelf:
+ rr = r
+ default:
+ rr = l.objs[r.pkg[p]].r
+ }
+ return l.toGlobal(rr, s.SymIdx)
+}
+
+// reportMissingBuiltin issues an error in the case where we have a
+// relocation against a runtime builtin whose definition is not found
+// when the runtime package is built. The canonical example is
+// "runtime.racefuncenter" -- currently if you do something like
+//
+// go build -gcflags=-race myprogram.go
+//
+// the compiler will insert calls to the builtin runtime.racefuncenter,
+// but the version of the runtime used for linkage won't actually contain
+// definitions of that symbol. See issue #42396 for details.
+//
+// As currently implemented, this is a fatal error. This has drawbacks
+// in that if there are multiple missing builtins, the error will only
+// cite the first one. On the plus side, terminating the link here has
+// advantages in that we won't run the risk of panics or crashes later
+// on in the linker due to R_CALL relocations with 0-valued target
+// symbols.
+func (l *Loader) reportMissingBuiltin(bsym int, reflib string) {
+ bname, _ := goobj.BuiltinName(bsym)
+ log.Fatalf("reference to undefined builtin %q from package %q",
+ bname, reflib)
+}
+
+// Look up a symbol by name, return global index, or 0 if not found.
+// This is more like Syms.ROLookup than Lookup -- it doesn't create
+// new symbol.
+func (l *Loader) Lookup(name string, ver int) Sym {
+ if ver >= sym.SymVerStatic || ver < 0 {
+ return l.extStaticSyms[nameVer{name, ver}]
+ }
+ return l.symsByName[ver][name]
+}
+
+// Check that duplicate symbols have same contents.
+func (l *Loader) checkdup(name string, r *oReader, li uint32, dup Sym) {
+ p := r.Data(li)
+ rdup, ldup := l.toLocal(dup)
+ pdup := rdup.Data(ldup)
+ reason := "same length but different contents"
+ if len(p) != len(pdup) {
+ reason = fmt.Sprintf("new length %d != old length %d", len(p), len(pdup))
+ } else if bytes.Equal(p, pdup) {
+ // For BSS symbols, we need to check size as well, see issue 46653.
+ szdup := l.SymSize(dup)
+ sz := int64(r.Sym(li).Siz())
+ if szdup == sz {
+ return
+ }
+ reason = fmt.Sprintf("different sizes: new size %d != old size %d",
+ sz, szdup)
+ }
+ fmt.Fprintf(os.Stderr, "cmd/link: while reading object for '%v': duplicate symbol '%s', previous def at '%v', with mismatched payload: %s\n", r.unit.Lib, name, rdup.unit.Lib, reason)
+
+ // For the moment, allow DWARF subprogram DIEs for
+ // auto-generated wrapper functions. What seems to happen
+ // here is that we get different line numbers on formal
+ // params; I am guessing that the pos is being inherited
+ // from the spot where the wrapper is needed.
+ allowed := strings.HasPrefix(name, "go:info.go.interface") ||
+ strings.HasPrefix(name, "go:info.go.builtin") ||
+ strings.HasPrefix(name, "go:debuglines")
+ if !allowed {
+ l.strictDupMsgs++
+ }
+}
+
+func (l *Loader) NStrictDupMsgs() int { return l.strictDupMsgs }
+
+// Number of total symbols.
+func (l *Loader) NSym() int {
+ return len(l.objSyms)
+}
+
+// Number of defined Go symbols.
+func (l *Loader) NDef() int {
+ return int(l.extStart)
+}
+
+// Number of reachable symbols.
+func (l *Loader) NReachableSym() int {
+ return l.attrReachable.Count()
+}
+
+// Returns the name of the i-th symbol.
+func (l *Loader) SymName(i Sym) string {
+ if l.IsExternal(i) {
+ pp := l.getPayload(i)
+ return pp.name
+ }
+ r, li := l.toLocal(i)
+ if r == nil {
+ return "?"
+ }
+ return r.Sym(li).Name(r.Reader)
+}
+
+// Returns the version of the i-th symbol.
+func (l *Loader) SymVersion(i Sym) int {
+ if l.IsExternal(i) {
+ pp := l.getPayload(i)
+ return pp.ver
+ }
+ r, li := l.toLocal(i)
+ return int(abiToVer(r.Sym(li).ABI(), r.version))
+}
+
+func (l *Loader) IsFileLocal(i Sym) bool {
+ return l.SymVersion(i) >= sym.SymVerStatic
+}
+
+// IsFromAssembly returns true if this symbol is derived from an
+// object file generated by the Go assembler.
+func (l *Loader) IsFromAssembly(i Sym) bool {
+ if l.IsExternal(i) {
+ return false
+ }
+ r, _ := l.toLocal(i)
+ return r.FromAssembly()
+}
+
+// Returns the type of the i-th symbol.
+func (l *Loader) SymType(i Sym) sym.SymKind {
+ if l.IsExternal(i) {
+ pp := l.getPayload(i)
+ if pp != nil {
+ return pp.kind
+ }
+ return 0
+ }
+ r, li := l.toLocal(i)
+ return sym.AbiSymKindToSymKind[objabi.SymKind(r.Sym(li).Type())]
+}
+
+// Returns the attributes of the i-th symbol.
+func (l *Loader) SymAttr(i Sym) uint8 {
+ if l.IsExternal(i) {
+ // TODO: do something? External symbols have different representation of attributes.
+ // For now, ReflectMethod, NoSplit, GoType, and Typelink are used and they cannot be
+ // set by external symbol.
+ return 0
+ }
+ r, li := l.toLocal(i)
+ return r.Sym(li).Flag()
+}
+
+// Returns the size of the i-th symbol.
+func (l *Loader) SymSize(i Sym) int64 {
+ if l.IsExternal(i) {
+ pp := l.getPayload(i)
+ return pp.size
+ }
+ r, li := l.toLocal(i)
+ return int64(r.Sym(li).Siz())
+}
+
+// AttrReachable returns true for symbols that are transitively
+// referenced from the entry points. Unreachable symbols are not
+// written to the output.
+func (l *Loader) AttrReachable(i Sym) bool {
+ return l.attrReachable.Has(i)
+}
+
+// SetAttrReachable sets the reachability property for a symbol (see
+// AttrReachable).
+func (l *Loader) SetAttrReachable(i Sym, v bool) {
+ if v {
+ l.attrReachable.Set(i)
+ } else {
+ l.attrReachable.Unset(i)
+ }
+}
+
+// AttrOnList returns true for symbols that are on some list (such as
+// the list of all text symbols, or one of the lists of data symbols)
+// and is consulted to avoid bugs where a symbol is put on a list
+// twice.
+func (l *Loader) AttrOnList(i Sym) bool {
+ return l.attrOnList.Has(i)
+}
+
+// SetAttrOnList sets the "on list" property for a symbol (see
+// AttrOnList).
+func (l *Loader) SetAttrOnList(i Sym, v bool) {
+ if v {
+ l.attrOnList.Set(i)
+ } else {
+ l.attrOnList.Unset(i)
+ }
+}
+
+// AttrLocal returns true for symbols that are only visible within the
+// module (executable or shared library) being linked. This attribute
+// is applied to thunks and certain other linker-generated symbols.
+func (l *Loader) AttrLocal(i Sym) bool {
+ return l.attrLocal.Has(i)
+}
+
+// SetAttrLocal the "local" property for a symbol (see AttrLocal above).
+func (l *Loader) SetAttrLocal(i Sym, v bool) {
+ if v {
+ l.attrLocal.Set(i)
+ } else {
+ l.attrLocal.Unset(i)
+ }
+}
+
+// AttrUsedInIface returns true for a type symbol that is used in
+// an interface.
+func (l *Loader) AttrUsedInIface(i Sym) bool {
+ return l.attrUsedInIface.Has(i)
+}
+
+func (l *Loader) SetAttrUsedInIface(i Sym, v bool) {
+ if v {
+ l.attrUsedInIface.Set(i)
+ } else {
+ l.attrUsedInIface.Unset(i)
+ }
+}
+
+// SymAddr checks that a symbol is reachable, and returns its value.
+func (l *Loader) SymAddr(i Sym) int64 {
+ if !l.AttrReachable(i) {
+ panic("unreachable symbol in symaddr")
+ }
+ return l.values[i]
+}
+
+// AttrNotInSymbolTable returns true for symbols that should not be
+// added to the symbol table of the final generated load module.
+func (l *Loader) AttrNotInSymbolTable(i Sym) bool {
+ return l.attrNotInSymbolTable.Has(i)
+}
+
+// SetAttrNotInSymbolTable the "not in symtab" property for a symbol
+// (see AttrNotInSymbolTable above).
+func (l *Loader) SetAttrNotInSymbolTable(i Sym, v bool) {
+ if v {
+ l.attrNotInSymbolTable.Set(i)
+ } else {
+ l.attrNotInSymbolTable.Unset(i)
+ }
+}
+
+// AttrVisibilityHidden symbols returns true for ELF symbols with
+// visibility set to STV_HIDDEN. They become local symbols in
+// the final executable. Only relevant when internally linking
+// on an ELF platform.
+func (l *Loader) AttrVisibilityHidden(i Sym) bool {
+ if !l.IsExternal(i) {
+ return false
+ }
+ return l.attrVisibilityHidden.Has(l.extIndex(i))
+}
+
+// SetAttrVisibilityHidden sets the "hidden visibility" property for a
+// symbol (see AttrVisibilityHidden).
+func (l *Loader) SetAttrVisibilityHidden(i Sym, v bool) {
+ if !l.IsExternal(i) {
+ panic("tried to set visibility attr on non-external symbol")
+ }
+ if v {
+ l.attrVisibilityHidden.Set(l.extIndex(i))
+ } else {
+ l.attrVisibilityHidden.Unset(l.extIndex(i))
+ }
+}
+
+// AttrDuplicateOK returns true for a symbol that can be present in
+// multiple object files.
+func (l *Loader) AttrDuplicateOK(i Sym) bool {
+ if !l.IsExternal(i) {
+ // TODO: if this path winds up being taken frequently, it
+ // might make more sense to copy the flag value out of the object
+ // into a larger bitmap during preload.
+ r, li := l.toLocal(i)
+ return r.Sym(li).Dupok()
+ }
+ return l.attrDuplicateOK.Has(l.extIndex(i))
+}
+
+// SetAttrDuplicateOK sets the "duplicate OK" property for an external
+// symbol (see AttrDuplicateOK).
+func (l *Loader) SetAttrDuplicateOK(i Sym, v bool) {
+ if !l.IsExternal(i) {
+ panic("tried to set dupok attr on non-external symbol")
+ }
+ if v {
+ l.attrDuplicateOK.Set(l.extIndex(i))
+ } else {
+ l.attrDuplicateOK.Unset(l.extIndex(i))
+ }
+}
+
+// AttrShared returns true for symbols compiled with the -shared option.
+func (l *Loader) AttrShared(i Sym) bool {
+ if !l.IsExternal(i) {
+ // TODO: if this path winds up being taken frequently, it
+ // might make more sense to copy the flag value out of the
+ // object into a larger bitmap during preload.
+ r, _ := l.toLocal(i)
+ return r.Shared()
+ }
+ return l.attrShared.Has(l.extIndex(i))
+}
+
+// SetAttrShared sets the "shared" property for an external
+// symbol (see AttrShared).
+func (l *Loader) SetAttrShared(i Sym, v bool) {
+ if !l.IsExternal(i) {
+ panic(fmt.Sprintf("tried to set shared attr on non-external symbol %d %s", i, l.SymName(i)))
+ }
+ if v {
+ l.attrShared.Set(l.extIndex(i))
+ } else {
+ l.attrShared.Unset(l.extIndex(i))
+ }
+}
+
+// AttrExternal returns true for function symbols loaded from host
+// object files.
+func (l *Loader) AttrExternal(i Sym) bool {
+ if !l.IsExternal(i) {
+ return false
+ }
+ return l.attrExternal.Has(l.extIndex(i))
+}
+
+// SetAttrExternal sets the "external" property for an host object
+// symbol (see AttrExternal).
+func (l *Loader) SetAttrExternal(i Sym, v bool) {
+ if !l.IsExternal(i) {
+ panic(fmt.Sprintf("tried to set external attr on non-external symbol %q", l.SymName(i)))
+ }
+ if v {
+ l.attrExternal.Set(l.extIndex(i))
+ } else {
+ l.attrExternal.Unset(l.extIndex(i))
+ }
+}
+
+// AttrSpecial returns true for a symbols that do not have their
+// address (i.e. Value) computed by the usual mechanism of
+// data.go:dodata() & data.go:address().
+func (l *Loader) AttrSpecial(i Sym) bool {
+ _, ok := l.attrSpecial[i]
+ return ok
+}
+
+// SetAttrSpecial sets the "special" property for a symbol (see
+// AttrSpecial).
+func (l *Loader) SetAttrSpecial(i Sym, v bool) {
+ if v {
+ l.attrSpecial[i] = struct{}{}
+ } else {
+ delete(l.attrSpecial, i)
+ }
+}
+
+// AttrCgoExportDynamic returns true for a symbol that has been
+// specially marked via the "cgo_export_dynamic" compiler directive
+// written by cgo (in response to //export directives in the source).
+func (l *Loader) AttrCgoExportDynamic(i Sym) bool {
+ _, ok := l.attrCgoExportDynamic[i]
+ return ok
+}
+
+// SetAttrCgoExportDynamic sets the "cgo_export_dynamic" for a symbol
+// (see AttrCgoExportDynamic).
+func (l *Loader) SetAttrCgoExportDynamic(i Sym, v bool) {
+ if v {
+ l.attrCgoExportDynamic[i] = struct{}{}
+ } else {
+ delete(l.attrCgoExportDynamic, i)
+ }
+}
+
+// AttrCgoExportStatic returns true for a symbol that has been
+// specially marked via the "cgo_export_static" directive
+// written by cgo.
+func (l *Loader) AttrCgoExportStatic(i Sym) bool {
+ _, ok := l.attrCgoExportStatic[i]
+ return ok
+}
+
+// SetAttrCgoExportStatic sets the "cgo_export_static" for a symbol
+// (see AttrCgoExportStatic).
+func (l *Loader) SetAttrCgoExportStatic(i Sym, v bool) {
+ if v {
+ l.attrCgoExportStatic[i] = struct{}{}
+ } else {
+ delete(l.attrCgoExportStatic, i)
+ }
+}
+
+// IsGeneratedSym returns true if a symbol's been previously marked as a
+// generator symbol through the SetIsGeneratedSym. The functions for generator
+// symbols are kept in the Link context.
+func (l *Loader) IsGeneratedSym(i Sym) bool {
+ _, ok := l.generatedSyms[i]
+ return ok
+}
+
+// SetIsGeneratedSym marks symbols as generated symbols. Data shouldn't be
+// stored in generated symbols, and a function is registered and called for
+// each of these symbols.
+func (l *Loader) SetIsGeneratedSym(i Sym, v bool) {
+ if !l.IsExternal(i) {
+ panic("only external symbols can be generated")
+ }
+ if v {
+ l.generatedSyms[i] = struct{}{}
+ } else {
+ delete(l.generatedSyms, i)
+ }
+}
+
+func (l *Loader) AttrCgoExport(i Sym) bool {
+ return l.AttrCgoExportDynamic(i) || l.AttrCgoExportStatic(i)
+}
+
+// AttrReadOnly returns true for a symbol whose underlying data
+// is stored via a read-only mmap.
+func (l *Loader) AttrReadOnly(i Sym) bool {
+ if v, ok := l.attrReadOnly[i]; ok {
+ return v
+ }
+ if l.IsExternal(i) {
+ pp := l.getPayload(i)
+ if pp.objidx != 0 {
+ return l.objs[pp.objidx].r.ReadOnly()
+ }
+ return false
+ }
+ r, _ := l.toLocal(i)
+ return r.ReadOnly()
+}
+
+// SetAttrReadOnly sets the "data is read only" property for a symbol
+// (see AttrReadOnly).
+func (l *Loader) SetAttrReadOnly(i Sym, v bool) {
+ l.attrReadOnly[i] = v
+}
+
+// AttrSubSymbol returns true for symbols that are listed as a
+// sub-symbol of some other outer symbol. The sub/outer mechanism is
+// used when loading host objects (sections from the host object
+// become regular linker symbols and symbols go on the Sub list of
+// their section) and for constructing the global offset table when
+// internally linking a dynamic executable.
+//
+// Note that in later stages of the linker, we set Outer(S) to some
+// container symbol C, but don't set Sub(C). Thus we have two
+// distinct scenarios:
+//
+// - Outer symbol covers the address ranges of its sub-symbols.
+// Outer.Sub is set in this case.
+// - Outer symbol doesn't conver the address ranges. It is zero-sized
+// and doesn't have sub-symbols. In the case, the inner symbol is
+// not actually a "SubSymbol". (Tricky!)
+//
+// This method returns TRUE only for sub-symbols in the first scenario.
+//
+// FIXME: would be better to do away with this and have a better way
+// to represent container symbols.
+
+func (l *Loader) AttrSubSymbol(i Sym) bool {
+ // we don't explicitly store this attribute any more -- return
+ // a value based on the sub-symbol setting.
+ o := l.OuterSym(i)
+ if o == 0 {
+ return false
+ }
+ return l.SubSym(o) != 0
+}
+
+// Note that we don't have a 'SetAttrSubSymbol' method in the loader;
+// clients should instead use the AddInteriorSym method to establish
+// containment relationships for host object symbols.
+
+// Returns whether the i-th symbol has ReflectMethod attribute set.
+func (l *Loader) IsReflectMethod(i Sym) bool {
+ return l.SymAttr(i)&goobj.SymFlagReflectMethod != 0
+}
+
+// Returns whether the i-th symbol is nosplit.
+func (l *Loader) IsNoSplit(i Sym) bool {
+ return l.SymAttr(i)&goobj.SymFlagNoSplit != 0
+}
+
+// Returns whether this is a Go type symbol.
+func (l *Loader) IsGoType(i Sym) bool {
+ return l.SymAttr(i)&goobj.SymFlagGoType != 0
+}
+
+// Returns whether this symbol should be included in typelink.
+func (l *Loader) IsTypelink(i Sym) bool {
+ return l.SymAttr(i)&goobj.SymFlagTypelink != 0
+}
+
+// Returns whether this symbol is an itab symbol.
+func (l *Loader) IsItab(i Sym) bool {
+ if l.IsExternal(i) {
+ return false
+ }
+ r, li := l.toLocal(i)
+ return r.Sym(li).IsItab()
+}
+
+// Returns whether this symbol is a dictionary symbol.
+func (l *Loader) IsDict(i Sym) bool {
+ if l.IsExternal(i) {
+ return false
+ }
+ r, li := l.toLocal(i)
+ return r.Sym(li).IsDict()
+}
+
+// Return whether this is a trampoline of a deferreturn call.
+func (l *Loader) IsDeferReturnTramp(i Sym) bool {
+ return l.deferReturnTramp[i]
+}
+
+// Set that i is a trampoline of a deferreturn call.
+func (l *Loader) SetIsDeferReturnTramp(i Sym, v bool) {
+ l.deferReturnTramp[i] = v
+}
+
+// growValues grows the slice used to store symbol values.
+func (l *Loader) growValues(reqLen int) {
+ curLen := len(l.values)
+ if reqLen > curLen {
+ l.values = append(l.values, make([]int64, reqLen+1-curLen)...)
+ }
+}
+
+// SymValue returns the value of the i-th symbol. i is global index.
+func (l *Loader) SymValue(i Sym) int64 {
+ return l.values[i]
+}
+
+// SetSymValue sets the value of the i-th symbol. i is global index.
+func (l *Loader) SetSymValue(i Sym, val int64) {
+ l.values[i] = val
+}
+
+// AddToSymValue adds to the value of the i-th symbol. i is the global index.
+func (l *Loader) AddToSymValue(i Sym, val int64) {
+ l.values[i] += val
+}
+
+// Returns the symbol content of the i-th symbol. i is global index.
+func (l *Loader) Data(i Sym) []byte {
+ if l.IsExternal(i) {
+ pp := l.getPayload(i)
+ if pp != nil {
+ return pp.data
+ }
+ return nil
+ }
+ r, li := l.toLocal(i)
+ return r.Data(li)
+}
+
+// FreeData clears the symbol data of an external symbol, allowing the memory
+// to be freed earlier. No-op for non-external symbols.
+// i is global index.
+func (l *Loader) FreeData(i Sym) {
+ if l.IsExternal(i) {
+ pp := l.getPayload(i)
+ if pp != nil {
+ pp.data = nil
+ }
+ }
+}
+
+// SymAlign returns the alignment for a symbol.
+func (l *Loader) SymAlign(i Sym) int32 {
+ if int(i) >= len(l.align) {
+ // align is extended lazily -- it the sym in question is
+ // outside the range of the existing slice, then we assume its
+ // alignment has not yet been set.
+ return 0
+ }
+ // TODO: would it make sense to return an arch-specific
+ // alignment depending on section type? E.g. STEXT => 32,
+ // SDATA => 1, etc?
+ abits := l.align[i]
+ if abits == 0 {
+ return 0
+ }
+ return int32(1 << (abits - 1))
+}
+
+// SetSymAlign sets the alignment for a symbol.
+func (l *Loader) SetSymAlign(i Sym, align int32) {
+ // Reject nonsense alignments.
+ if align < 0 || align&(align-1) != 0 {
+ panic("bad alignment value")
+ }
+ if int(i) >= len(l.align) {
+ l.align = append(l.align, make([]uint8, l.NSym()-len(l.align))...)
+ }
+ if align == 0 {
+ l.align[i] = 0
+ }
+ l.align[i] = uint8(bits.Len32(uint32(align)))
+}
+
+// SymValue returns the section of the i-th symbol. i is global index.
+func (l *Loader) SymSect(i Sym) *sym.Section {
+ if int(i) >= len(l.symSects) {
+ // symSects is extended lazily -- it the sym in question is
+ // outside the range of the existing slice, then we assume its
+ // section has not yet been set.
+ return nil
+ }
+ return l.sects[l.symSects[i]]
+}
+
+// SetSymSect sets the section of the i-th symbol. i is global index.
+func (l *Loader) SetSymSect(i Sym, sect *sym.Section) {
+ if int(i) >= len(l.symSects) {
+ l.symSects = append(l.symSects, make([]uint16, l.NSym()-len(l.symSects))...)
+ }
+ l.symSects[i] = sect.Index
+}
+
+// growSects grows the slice used to store symbol sections.
+func (l *Loader) growSects(reqLen int) {
+ curLen := len(l.symSects)
+ if reqLen > curLen {
+ l.symSects = append(l.symSects, make([]uint16, reqLen+1-curLen)...)
+ }
+}
+
+// NewSection creates a new (output) section.
+func (l *Loader) NewSection() *sym.Section {
+ sect := new(sym.Section)
+ idx := len(l.sects)
+ if idx != int(uint16(idx)) {
+ panic("too many sections created")
+ }
+ sect.Index = uint16(idx)
+ l.sects = append(l.sects, sect)
+ return sect
+}
+
+// SymDynImplib returns the "dynimplib" attribute for the specified
+// symbol, making up a portion of the info for a symbol specified
+// on a "cgo_import_dynamic" compiler directive.
+func (l *Loader) SymDynimplib(i Sym) string {
+ return l.dynimplib[i]
+}
+
+// SetSymDynimplib sets the "dynimplib" attribute for a symbol.
+func (l *Loader) SetSymDynimplib(i Sym, value string) {
+ // reject bad symbols
+ if i >= Sym(len(l.objSyms)) || i == 0 {
+ panic("bad symbol index in SetDynimplib")
+ }
+ if value == "" {
+ delete(l.dynimplib, i)
+ } else {
+ l.dynimplib[i] = value
+ }
+}
+
+// SymDynimpvers returns the "dynimpvers" attribute for the specified
+// symbol, making up a portion of the info for a symbol specified
+// on a "cgo_import_dynamic" compiler directive.
+func (l *Loader) SymDynimpvers(i Sym) string {
+ return l.dynimpvers[i]
+}
+
+// SetSymDynimpvers sets the "dynimpvers" attribute for a symbol.
+func (l *Loader) SetSymDynimpvers(i Sym, value string) {
+ // reject bad symbols
+ if i >= Sym(len(l.objSyms)) || i == 0 {
+ panic("bad symbol index in SetDynimpvers")
+ }
+ if value == "" {
+ delete(l.dynimpvers, i)
+ } else {
+ l.dynimpvers[i] = value
+ }
+}
+
+// SymExtname returns the "extname" value for the specified
+// symbol.
+func (l *Loader) SymExtname(i Sym) string {
+ if s, ok := l.extname[i]; ok {
+ return s
+ }
+ return l.SymName(i)
+}
+
+// SetSymExtname sets the "extname" attribute for a symbol.
+func (l *Loader) SetSymExtname(i Sym, value string) {
+ // reject bad symbols
+ if i >= Sym(len(l.objSyms)) || i == 0 {
+ panic("bad symbol index in SetExtname")
+ }
+ if value == "" {
+ delete(l.extname, i)
+ } else {
+ l.extname[i] = value
+ }
+}
+
+// SymElfType returns the previously recorded ELF type for a symbol
+// (used only for symbols read from shared libraries by ldshlibsyms).
+// It is not set for symbols defined by the packages being linked or
+// by symbols read by ldelf (and so is left as elf.STT_NOTYPE).
+func (l *Loader) SymElfType(i Sym) elf.SymType {
+ if et, ok := l.elfType[i]; ok {
+ return et
+ }
+ return elf.STT_NOTYPE
+}
+
+// SetSymElfType sets the elf type attribute for a symbol.
+func (l *Loader) SetSymElfType(i Sym, et elf.SymType) {
+ // reject bad symbols
+ if i >= Sym(len(l.objSyms)) || i == 0 {
+ panic("bad symbol index in SetSymElfType")
+ }
+ if et == elf.STT_NOTYPE {
+ delete(l.elfType, i)
+ } else {
+ l.elfType[i] = et
+ }
+}
+
+// SymElfSym returns the ELF symbol index for a given loader
+// symbol, assigned during ELF symtab generation.
+func (l *Loader) SymElfSym(i Sym) int32 {
+ return l.elfSym[i]
+}
+
+// SetSymElfSym sets the elf symbol index for a symbol.
+func (l *Loader) SetSymElfSym(i Sym, es int32) {
+ if i == 0 {
+ panic("bad sym index")
+ }
+ if es == 0 {
+ delete(l.elfSym, i)
+ } else {
+ l.elfSym[i] = es
+ }
+}
+
+// SymLocalElfSym returns the "local" ELF symbol index for a given loader
+// symbol, assigned during ELF symtab generation.
+func (l *Loader) SymLocalElfSym(i Sym) int32 {
+ return l.localElfSym[i]
+}
+
+// SetSymLocalElfSym sets the "local" elf symbol index for a symbol.
+func (l *Loader) SetSymLocalElfSym(i Sym, es int32) {
+ if i == 0 {
+ panic("bad sym index")
+ }
+ if es == 0 {
+ delete(l.localElfSym, i)
+ } else {
+ l.localElfSym[i] = es
+ }
+}
+
+// SymPlt returns the PLT offset of symbol s.
+func (l *Loader) SymPlt(s Sym) int32 {
+ if v, ok := l.plt[s]; ok {
+ return v
+ }
+ return -1
+}
+
+// SetPlt sets the PLT offset of symbol i.
+func (l *Loader) SetPlt(i Sym, v int32) {
+ if i >= Sym(len(l.objSyms)) || i == 0 {
+ panic("bad symbol for SetPlt")
+ }
+ if v == -1 {
+ delete(l.plt, i)
+ } else {
+ l.plt[i] = v
+ }
+}
+
+// SymGot returns the GOT offset of symbol s.
+func (l *Loader) SymGot(s Sym) int32 {
+ if v, ok := l.got[s]; ok {
+ return v
+ }
+ return -1
+}
+
+// SetGot sets the GOT offset of symbol i.
+func (l *Loader) SetGot(i Sym, v int32) {
+ if i >= Sym(len(l.objSyms)) || i == 0 {
+ panic("bad symbol for SetGot")
+ }
+ if v == -1 {
+ delete(l.got, i)
+ } else {
+ l.got[i] = v
+ }
+}
+
+// SymDynid returns the "dynid" property for the specified symbol.
+func (l *Loader) SymDynid(i Sym) int32 {
+ if s, ok := l.dynid[i]; ok {
+ return s
+ }
+ return -1
+}
+
+// SetSymDynid sets the "dynid" property for a symbol.
+func (l *Loader) SetSymDynid(i Sym, val int32) {
+ // reject bad symbols
+ if i >= Sym(len(l.objSyms)) || i == 0 {
+ panic("bad symbol index in SetSymDynid")
+ }
+ if val == -1 {
+ delete(l.dynid, i)
+ } else {
+ l.dynid[i] = val
+ }
+}
+
+// DynIdSyms returns the set of symbols for which dynID is set to an
+// interesting (non-default) value. This is expected to be a fairly
+// small set.
+func (l *Loader) DynidSyms() []Sym {
+ sl := make([]Sym, 0, len(l.dynid))
+ for s := range l.dynid {
+ sl = append(sl, s)
+ }
+ sort.Slice(sl, func(i, j int) bool { return sl[i] < sl[j] })
+ return sl
+}
+
+// SymGoType returns the 'Gotype' property for a given symbol (set by
+// the Go compiler for variable symbols). This version relies on
+// reading aux symbols for the target sym -- it could be that a faster
+// approach would be to check for gotype during preload and copy the
+// results in to a map (might want to try this at some point and see
+// if it helps speed things up).
+func (l *Loader) SymGoType(i Sym) Sym { return l.aux1(i, goobj.AuxGotype) }
+
+// SymUnit returns the compilation unit for a given symbol (which will
+// typically be nil for external or linker-manufactured symbols).
+func (l *Loader) SymUnit(i Sym) *sym.CompilationUnit {
+ if l.IsExternal(i) {
+ pp := l.getPayload(i)
+ if pp.objidx != 0 {
+ r := l.objs[pp.objidx].r
+ return r.unit
+ }
+ return nil
+ }
+ r, _ := l.toLocal(i)
+ return r.unit
+}
+
+// SymPkg returns the package where the symbol came from (for
+// regular compiler-generated Go symbols), but in the case of
+// building with "-linkshared" (when a symbol is read from a
+// shared library), will hold the library name.
+// NOTE: this corresponds to sym.Symbol.File field.
+func (l *Loader) SymPkg(i Sym) string {
+ if f, ok := l.symPkg[i]; ok {
+ return f
+ }
+ if l.IsExternal(i) {
+ pp := l.getPayload(i)
+ if pp.objidx != 0 {
+ r := l.objs[pp.objidx].r
+ return r.unit.Lib.Pkg
+ }
+ return ""
+ }
+ r, _ := l.toLocal(i)
+ return r.unit.Lib.Pkg
+}
+
+// SetSymPkg sets the package/library for a symbol. This is
+// needed mainly for external symbols, specifically those imported
+// from shared libraries.
+func (l *Loader) SetSymPkg(i Sym, pkg string) {
+ // reject bad symbols
+ if i >= Sym(len(l.objSyms)) || i == 0 {
+ panic("bad symbol index in SetSymPkg")
+ }
+ l.symPkg[i] = pkg
+}
+
+// SymLocalentry returns an offset in bytes of the "local entry" of a symbol.
+func (l *Loader) SymLocalentry(i Sym) uint8 {
+ return l.localentry[i]
+}
+
+// SetSymLocalentry sets the "local entry" offset attribute for a symbol.
+func (l *Loader) SetSymLocalentry(i Sym, value uint8) {
+ // reject bad symbols
+ if i >= Sym(len(l.objSyms)) || i == 0 {
+ panic("bad symbol index in SetSymLocalentry")
+ }
+ if value == 0 {
+ delete(l.localentry, i)
+ } else {
+ l.localentry[i] = value
+ }
+}
+
+// Returns the number of aux symbols given a global index.
+func (l *Loader) NAux(i Sym) int {
+ if l.IsExternal(i) {
+ return 0
+ }
+ r, li := l.toLocal(i)
+ return r.NAux(li)
+}
+
+// Returns the "handle" to the j-th aux symbol of the i-th symbol.
+func (l *Loader) Aux(i Sym, j int) Aux {
+ if l.IsExternal(i) {
+ return Aux{}
+ }
+ r, li := l.toLocal(i)
+ if j >= r.NAux(li) {
+ return Aux{}
+ }
+ return Aux{r.Aux(li, j), r, l}
+}
+
+// GetFuncDwarfAuxSyms collects and returns the auxiliary DWARF
+// symbols associated with a given function symbol. Prior to the
+// introduction of the loader, this was done purely using name
+// lookups, e.f. for function with name XYZ we would then look up
+// go.info.XYZ, etc.
+func (l *Loader) GetFuncDwarfAuxSyms(fnSymIdx Sym) (auxDwarfInfo, auxDwarfLoc, auxDwarfRanges, auxDwarfLines Sym) {
+ if l.SymType(fnSymIdx) != sym.STEXT {
+ log.Fatalf("error: non-function sym %d/%s t=%s passed to GetFuncDwarfAuxSyms", fnSymIdx, l.SymName(fnSymIdx), l.SymType(fnSymIdx).String())
+ }
+ r, auxs := l.auxs(fnSymIdx)
+
+ for i := range auxs {
+ a := &auxs[i]
+ switch a.Type() {
+ case goobj.AuxDwarfInfo:
+ auxDwarfInfo = l.resolve(r, a.Sym())
+ if l.SymType(auxDwarfInfo) != sym.SDWARFFCN {
+ panic("aux dwarf info sym with wrong type")
+ }
+ case goobj.AuxDwarfLoc:
+ auxDwarfLoc = l.resolve(r, a.Sym())
+ if l.SymType(auxDwarfLoc) != sym.SDWARFLOC {
+ panic("aux dwarf loc sym with wrong type")
+ }
+ case goobj.AuxDwarfRanges:
+ auxDwarfRanges = l.resolve(r, a.Sym())
+ if l.SymType(auxDwarfRanges) != sym.SDWARFRANGE {
+ panic("aux dwarf ranges sym with wrong type")
+ }
+ case goobj.AuxDwarfLines:
+ auxDwarfLines = l.resolve(r, a.Sym())
+ if l.SymType(auxDwarfLines) != sym.SDWARFLINES {
+ panic("aux dwarf lines sym with wrong type")
+ }
+ }
+ }
+ return
+}
+
+// AddInteriorSym sets up 'interior' as an interior symbol of
+// container/payload symbol 'container'. An interior symbol does not
+// itself have data, but gives a name to a subrange of the data in its
+// container symbol. The container itself may or may not have a name.
+// This method is intended primarily for use in the host object
+// loaders, to capture the semantics of symbols and sections in an
+// object file. When reading a host object file, we'll typically
+// encounter a static section symbol (ex: ".text") containing content
+// for a collection of functions, then a series of ELF (or macho, etc)
+// symbol table entries each of which points into a sub-section
+// (offset and length) of its corresponding container symbol. Within
+// the go linker we create a loader.Sym for the container (which is
+// expected to have the actual content/payload) and then a set of
+// interior loader.Sym's that point into a portion of the container.
+func (l *Loader) AddInteriorSym(container Sym, interior Sym) {
+ // Container symbols are expected to have content/data.
+ // NB: this restriction may turn out to be too strict (it's possible
+ // to imagine a zero-sized container with an interior symbol pointing
+ // into it); it's ok to relax or remove it if we counter an
+ // oddball host object that triggers this.
+ if l.SymSize(container) == 0 && len(l.Data(container)) == 0 {
+ panic("unexpected empty container symbol")
+ }
+ // The interior symbols for a container are not expected to have
+ // content/data or relocations.
+ if len(l.Data(interior)) != 0 {
+ panic("unexpected non-empty interior symbol")
+ }
+ // Interior symbol is expected to be in the symbol table.
+ if l.AttrNotInSymbolTable(interior) {
+ panic("interior symbol must be in symtab")
+ }
+ // Only a single level of containment is allowed.
+ if l.OuterSym(container) != 0 {
+ panic("outer has outer itself")
+ }
+ // Interior sym should not already have a sibling.
+ if l.SubSym(interior) != 0 {
+ panic("sub set for subsym")
+ }
+ // Interior sym should not already point at a container.
+ if l.OuterSym(interior) != 0 {
+ panic("outer already set for subsym")
+ }
+ l.sub[interior] = l.sub[container]
+ l.sub[container] = interior
+ l.outer[interior] = container
+}
+
+// OuterSym gets the outer symbol for host object loaded symbols.
+func (l *Loader) OuterSym(i Sym) Sym {
+ // FIXME: add check for isExternal?
+ return l.outer[i]
+}
+
+// SubSym gets the subsymbol for host object loaded symbols.
+func (l *Loader) SubSym(i Sym) Sym {
+ // NB: note -- no check for l.isExternal(), since I am pretty sure
+ // that later phases in the linker set subsym for "type:" syms
+ return l.sub[i]
+}
+
+// SetCarrierSym declares that 'c' is the carrier or container symbol
+// for 's'. Carrier symbols are used in the linker to as a container
+// for a collection of sub-symbols where the content of the
+// sub-symbols is effectively concatenated to form the content of the
+// carrier. The carrier is given a name in the output symbol table
+// while the sub-symbol names are not. For example, the Go compiler
+// emits named string symbols (type SGOSTRING) when compiling a
+// package; after being deduplicated, these symbols are collected into
+// a single unit by assigning them a new carrier symbol named
+// "go:string.*" (which appears in the final symbol table for the
+// output load module).
+func (l *Loader) SetCarrierSym(s Sym, c Sym) {
+ if c == 0 {
+ panic("invalid carrier in SetCarrierSym")
+ }
+ if s == 0 {
+ panic("invalid sub-symbol in SetCarrierSym")
+ }
+ // Carrier symbols are not expected to have content/data. It is
+ // ok for them to have non-zero size (to allow for use of generator
+ // symbols).
+ if len(l.Data(c)) != 0 {
+ panic("unexpected non-empty carrier symbol")
+ }
+ l.outer[s] = c
+ // relocsym's foldSubSymbolOffset requires that we only
+ // have a single level of containment-- enforce here.
+ if l.outer[c] != 0 {
+ panic("invalid nested carrier sym")
+ }
+}
+
+// Initialize Reachable bitmap and its siblings for running deadcode pass.
+func (l *Loader) InitReachable() {
+ l.growAttrBitmaps(l.NSym() + 1)
+}
+
+type symWithVal struct {
+ s Sym
+ v int64
+}
+type bySymValue []symWithVal
+
+func (s bySymValue) Len() int { return len(s) }
+func (s bySymValue) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
+func (s bySymValue) Less(i, j int) bool { return s[i].v < s[j].v }
+
+// SortSub walks through the sub-symbols for 's' and sorts them
+// in place by increasing value. Return value is the new
+// sub symbol for the specified outer symbol.
+func (l *Loader) SortSub(s Sym) Sym {
+
+ if s == 0 || l.sub[s] == 0 {
+ return s
+ }
+
+ // Sort symbols using a slice first. Use a stable sort on the off
+ // chance that there's more than once symbol with the same value,
+ // so as to preserve reproducible builds.
+ sl := []symWithVal{}
+ for ss := l.sub[s]; ss != 0; ss = l.sub[ss] {
+ sl = append(sl, symWithVal{s: ss, v: l.SymValue(ss)})
+ }
+ sort.Stable(bySymValue(sl))
+
+ // Then apply any changes needed to the sub map.
+ ns := Sym(0)
+ for i := len(sl) - 1; i >= 0; i-- {
+ s := sl[i].s
+ l.sub[s] = ns
+ ns = s
+ }
+
+ // Update sub for outer symbol, then return
+ l.sub[s] = sl[0].s
+ return sl[0].s
+}
+
+// SortSyms sorts a list of symbols by their value.
+func (l *Loader) SortSyms(ss []Sym) {
+ sort.SliceStable(ss, func(i, j int) bool { return l.SymValue(ss[i]) < l.SymValue(ss[j]) })
+}
+
+// Insure that reachable bitmap and its siblings have enough size.
+func (l *Loader) growAttrBitmaps(reqLen int) {
+ if reqLen > l.attrReachable.Len() {
+ // These are indexed by global symbol
+ l.attrReachable = growBitmap(reqLen, l.attrReachable)
+ l.attrOnList = growBitmap(reqLen, l.attrOnList)
+ l.attrLocal = growBitmap(reqLen, l.attrLocal)
+ l.attrNotInSymbolTable = growBitmap(reqLen, l.attrNotInSymbolTable)
+ l.attrUsedInIface = growBitmap(reqLen, l.attrUsedInIface)
+ }
+ l.growExtAttrBitmaps()
+}
+
+func (l *Loader) growExtAttrBitmaps() {
+ // These are indexed by external symbol index (e.g. l.extIndex(i))
+ extReqLen := len(l.payloads)
+ if extReqLen > l.attrVisibilityHidden.Len() {
+ l.attrVisibilityHidden = growBitmap(extReqLen, l.attrVisibilityHidden)
+ l.attrDuplicateOK = growBitmap(extReqLen, l.attrDuplicateOK)
+ l.attrShared = growBitmap(extReqLen, l.attrShared)
+ l.attrExternal = growBitmap(extReqLen, l.attrExternal)
+ }
+}
+
+func (relocs *Relocs) Count() int { return len(relocs.rs) }
+
+// At returns the j-th reloc for a global symbol.
+func (relocs *Relocs) At(j int) Reloc {
+ if relocs.l.isExtReader(relocs.r) {
+ return Reloc{&relocs.rs[j], relocs.r, relocs.l}
+ }
+ return Reloc{&relocs.rs[j], relocs.r, relocs.l}
+}
+
+// Relocs returns a Relocs object for the given global sym.
+func (l *Loader) Relocs(i Sym) 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)
+}
+
+// Relocs returns a Relocs object given a local sym index and reader.
+func (l *Loader) relocs(r *oReader, li uint32) Relocs {
+ var rs []goobj.Reloc
+ if l.isExtReader(r) {
+ pp := l.payloads[li]
+ rs = pp.relocs
+ } else {
+ rs = r.Relocs(li)
+ }
+ return Relocs{
+ rs: rs,
+ li: li,
+ r: r,
+ l: l,
+ }
+}
+
+func (l *Loader) auxs(i Sym) (*oReader, []goobj.Aux) {
+ if l.IsExternal(i) {
+ pp := l.getPayload(i)
+ return l.objs[pp.objidx].r, pp.auxs
+ } else {
+ r, li := l.toLocal(i)
+ return r, r.Auxs(li)
+ }
+}
+
+// Returns a specific aux symbol of type t for symbol i.
+func (l *Loader) aux1(i Sym, t uint8) Sym {
+ r, auxs := l.auxs(i)
+ for j := range auxs {
+ a := &auxs[j]
+ if a.Type() == t {
+ return l.resolve(r, a.Sym())
+ }
+ }
+ return 0
+}
+
+func (l *Loader) Pcsp(i Sym) Sym { return l.aux1(i, goobj.AuxPcsp) }
+
+// Returns all aux symbols of per-PC data for symbol i.
+// tmp is a scratch space for the pcdata slice.
+func (l *Loader) PcdataAuxs(i Sym, tmp []Sym) (pcsp, pcfile, pcline, pcinline Sym, pcdata []Sym) {
+ pcdata = tmp[:0]
+ r, auxs := l.auxs(i)
+ for j := range auxs {
+ a := &auxs[j]
+ switch a.Type() {
+ case goobj.AuxPcsp:
+ pcsp = l.resolve(r, a.Sym())
+ case goobj.AuxPcline:
+ pcline = l.resolve(r, a.Sym())
+ case goobj.AuxPcfile:
+ pcfile = l.resolve(r, a.Sym())
+ case goobj.AuxPcinline:
+ pcinline = l.resolve(r, a.Sym())
+ case goobj.AuxPcdata:
+ pcdata = append(pcdata, l.resolve(r, a.Sym()))
+ }
+ }
+ return
+}
+
+// Returns the number of pcdata for symbol i.
+func (l *Loader) NumPcdata(i Sym) int {
+ n := 0
+ _, auxs := l.auxs(i)
+ for j := range auxs {
+ a := &auxs[j]
+ if a.Type() == goobj.AuxPcdata {
+ n++
+ }
+ }
+ return n
+}
+
+// Returns all funcdata symbols of symbol i.
+// tmp is a scratch space.
+func (l *Loader) Funcdata(i Sym, tmp []Sym) []Sym {
+ fd := tmp[:0]
+ r, auxs := l.auxs(i)
+ for j := range auxs {
+ a := &auxs[j]
+ if a.Type() == goobj.AuxFuncdata {
+ fd = append(fd, l.resolve(r, a.Sym()))
+ }
+ }
+ return fd
+}
+
+// Returns the number of funcdata for symbol i.
+func (l *Loader) NumFuncdata(i Sym) int {
+ n := 0
+ _, auxs := l.auxs(i)
+ for j := range auxs {
+ a := &auxs[j]
+ if a.Type() == goobj.AuxFuncdata {
+ n++
+ }
+ }
+ return n
+}
+
+// FuncInfo provides hooks to access goobj.FuncInfo in the objects.
+type FuncInfo struct {
+ l *Loader
+ r *oReader
+ data []byte
+ lengths goobj.FuncInfoLengths
+}
+
+func (fi *FuncInfo) Valid() bool { return fi.r != nil }
+
+func (fi *FuncInfo) Args() int {
+ return int((*goobj.FuncInfo)(nil).ReadArgs(fi.data))
+}
+
+func (fi *FuncInfo) Locals() int {
+ return int((*goobj.FuncInfo)(nil).ReadLocals(fi.data))
+}
+
+func (fi *FuncInfo) FuncID() objabi.FuncID {
+ return (*goobj.FuncInfo)(nil).ReadFuncID(fi.data)
+}
+
+func (fi *FuncInfo) FuncFlag() objabi.FuncFlag {
+ return (*goobj.FuncInfo)(nil).ReadFuncFlag(fi.data)
+}
+
+func (fi *FuncInfo) StartLine() int32 {
+ return (*goobj.FuncInfo)(nil).ReadStartLine(fi.data)
+}
+
+// Preload has to be called prior to invoking the various methods
+// below related to pcdata, funcdataoff, files, and inltree nodes.
+func (fi *FuncInfo) Preload() {
+ fi.lengths = (*goobj.FuncInfo)(nil).ReadFuncInfoLengths(fi.data)
+}
+
+func (fi *FuncInfo) NumFile() uint32 {
+ if !fi.lengths.Initialized {
+ panic("need to call Preload first")
+ }
+ return fi.lengths.NumFile
+}
+
+func (fi *FuncInfo) File(k int) goobj.CUFileIndex {
+ if !fi.lengths.Initialized {
+ panic("need to call Preload first")
+ }
+ return (*goobj.FuncInfo)(nil).ReadFile(fi.data, fi.lengths.FileOff, uint32(k))
+}
+
+// TopFrame returns true if the function associated with this FuncInfo
+// is an entry point, meaning that unwinders should stop when they hit
+// this function.
+func (fi *FuncInfo) TopFrame() bool {
+ return (fi.FuncFlag() & objabi.FuncFlag_TOPFRAME) != 0
+}
+
+type InlTreeNode struct {
+ Parent int32
+ File goobj.CUFileIndex
+ Line int32
+ Func Sym
+ ParentPC int32
+}
+
+func (fi *FuncInfo) NumInlTree() uint32 {
+ if !fi.lengths.Initialized {
+ panic("need to call Preload first")
+ }
+ return fi.lengths.NumInlTree
+}
+
+func (fi *FuncInfo) InlTree(k int) InlTreeNode {
+ if !fi.lengths.Initialized {
+ panic("need to call Preload first")
+ }
+ node := (*goobj.FuncInfo)(nil).ReadInlTree(fi.data, fi.lengths.InlTreeOff, uint32(k))
+ return InlTreeNode{
+ Parent: node.Parent,
+ File: node.File,
+ Line: node.Line,
+ Func: fi.l.resolve(fi.r, node.Func),
+ ParentPC: node.ParentPC,
+ }
+}
+
+func (l *Loader) FuncInfo(i Sym) FuncInfo {
+ r, auxs := l.auxs(i)
+ for j := range auxs {
+ a := &auxs[j]
+ if a.Type() == goobj.AuxFuncInfo {
+ b := r.Data(a.Sym().SymIdx)
+ return FuncInfo{l, r, b, goobj.FuncInfoLengths{}}
+ }
+ }
+ return FuncInfo{}
+}
+
+// Preload a package: adds autolib.
+// Does not add defined package or non-packaged symbols to the symbol table.
+// These are done in LoadSyms.
+// Does not read symbol data.
+// Returns the fingerprint of the object.
+func (l *Loader) Preload(localSymVersion int, f *bio.Reader, lib *sym.Library, unit *sym.CompilationUnit, length int64) goobj.FingerprintType {
+ roObject, readonly, err := f.Slice(uint64(length)) // TODO: no need to map blocks that are for tools only (e.g. RefName)
+ if err != nil {
+ log.Fatal("cannot read object file:", err)
+ }
+ r := goobj.NewReaderFromBytes(roObject, readonly)
+ if r == nil {
+ if len(roObject) >= 8 && bytes.Equal(roObject[:8], []byte("\x00go114ld")) {
+ log.Fatalf("found object file %s in old format", f.File().Name())
+ }
+ panic("cannot read object file")
+ }
+ pkgprefix := objabi.PathToPrefix(lib.Pkg) + "."
+ ndef := r.NSym()
+ nhashed64def := r.NHashed64def()
+ nhasheddef := r.NHasheddef()
+ or := &oReader{
+ Reader: r,
+ unit: unit,
+ version: localSymVersion,
+ pkgprefix: pkgprefix,
+ syms: make([]Sym, ndef+nhashed64def+nhasheddef+r.NNonpkgdef()+r.NNonpkgref()),
+ ndef: ndef,
+ nhasheddef: nhasheddef,
+ nhashed64def: nhashed64def,
+ objidx: uint32(len(l.objs)),
+ }
+
+ if r.Unlinkable() {
+ log.Fatalf("link: unlinkable object (from package %s) - compiler requires -p flag", lib.Pkg)
+ }
+
+ // Autolib
+ lib.Autolib = append(lib.Autolib, r.Autolib()...)
+
+ // DWARF file table
+ nfile := r.NFile()
+ unit.FileTable = make([]string, nfile)
+ for i := range unit.FileTable {
+ unit.FileTable[i] = r.File(i)
+ }
+
+ l.addObj(lib.Pkg, or)
+
+ // The caller expects us consuming all the data
+ f.MustSeek(length, io.SeekCurrent)
+
+ return r.Fingerprint()
+}
+
+// Holds the loader along with temporary states for loading symbols.
+type loadState struct {
+ l *Loader
+ hashed64Syms map[uint64]symAndSize // short hashed (content-addressable) symbols, keyed by content hash
+ hashedSyms map[goobj.HashType]symAndSize // hashed (content-addressable) symbols, keyed by content hash
+}
+
+// Preload symbols of given kind from an object.
+func (st *loadState) preloadSyms(r *oReader, kind int) {
+ l := st.l
+ var start, end uint32
+ switch kind {
+ case pkgDef:
+ start = 0
+ end = uint32(r.ndef)
+ case hashed64Def:
+ start = uint32(r.ndef)
+ end = uint32(r.ndef + r.nhashed64def)
+ case hashedDef:
+ start = uint32(r.ndef + r.nhashed64def)
+ end = uint32(r.ndef + r.nhashed64def + r.nhasheddef)
+ case nonPkgDef:
+ start = uint32(r.ndef + r.nhashed64def + r.nhasheddef)
+ end = uint32(r.ndef + r.nhashed64def + r.nhasheddef + r.NNonpkgdef())
+ default:
+ panic("preloadSyms: bad kind")
+ }
+ l.growAttrBitmaps(len(l.objSyms) + int(end-start))
+ loadingRuntimePkg := r.unit.Lib.Pkg == "runtime"
+ for i := start; i < end; i++ {
+ osym := r.Sym(i)
+ var name string
+ var v int
+ if kind != hashed64Def && kind != hashedDef { // we don't need the name, etc. for hashed symbols
+ name = osym.Name(r.Reader)
+ v = abiToVer(osym.ABI(), r.version)
+ }
+ gi := st.addSym(name, v, r, i, kind, osym)
+ r.syms[i] = gi
+ if osym.Local() {
+ l.SetAttrLocal(gi, true)
+ }
+ if osym.UsedInIface() {
+ l.SetAttrUsedInIface(gi, true)
+ }
+ if strings.HasPrefix(name, "runtime.") ||
+ (loadingRuntimePkg && strings.HasPrefix(name, "type:")) {
+ if bi := goobj.BuiltinIdx(name, int(osym.ABI())); bi != -1 {
+ // This is a definition of a builtin symbol. Record where it is.
+ l.builtinSyms[bi] = gi
+ }
+ }
+ if a := int32(osym.Align()); a != 0 && a > l.SymAlign(gi) {
+ l.SetSymAlign(gi, a)
+ }
+ }
+}
+
+// Add syms, hashed (content-addressable) symbols, non-package symbols, and
+// references to external symbols (which are always named).
+func (l *Loader) LoadSyms(arch *sys.Arch) {
+ // Allocate space for symbols, making a guess as to how much space we need.
+ // This function was determined empirically by looking at the cmd/compile on
+ // Darwin, and picking factors for hashed and hashed64 syms.
+ var symSize, hashedSize, hashed64Size int
+ for _, o := range l.objs[goObjStart:] {
+ symSize += o.r.ndef + o.r.nhasheddef/2 + o.r.nhashed64def/2 + o.r.NNonpkgdef()
+ hashedSize += o.r.nhasheddef / 2
+ hashed64Size += o.r.nhashed64def / 2
+ }
+ // Index 0 is invalid for symbols.
+ l.objSyms = make([]objSym, 1, symSize)
+
+ st := loadState{
+ l: l,
+ hashed64Syms: make(map[uint64]symAndSize, hashed64Size),
+ hashedSyms: make(map[goobj.HashType]symAndSize, hashedSize),
+ }
+
+ for _, o := range l.objs[goObjStart:] {
+ st.preloadSyms(o.r, pkgDef)
+ }
+ l.npkgsyms = l.NSym()
+ for _, o := range l.objs[goObjStart:] {
+ st.preloadSyms(o.r, hashed64Def)
+ st.preloadSyms(o.r, hashedDef)
+ st.preloadSyms(o.r, nonPkgDef)
+ }
+ l.nhashedsyms = len(st.hashed64Syms) + len(st.hashedSyms)
+ for _, o := range l.objs[goObjStart:] {
+ loadObjRefs(l, o.r, arch)
+ }
+ l.values = make([]int64, l.NSym(), l.NSym()+1000) // +1000 make some room for external symbols
+}
+
+func loadObjRefs(l *Loader, r *oReader, arch *sys.Arch) {
+ // load non-package refs
+ ndef := uint32(r.NAlldef())
+ for i, n := uint32(0), uint32(r.NNonpkgref()); i < n; i++ {
+ osym := r.Sym(ndef + i)
+ name := osym.Name(r.Reader)
+ v := abiToVer(osym.ABI(), r.version)
+ r.syms[ndef+i] = l.LookupOrCreateSym(name, v)
+ gi := r.syms[ndef+i]
+ if osym.Local() {
+ l.SetAttrLocal(gi, true)
+ }
+ if osym.UsedInIface() {
+ l.SetAttrUsedInIface(gi, true)
+ }
+ }
+
+ // referenced packages
+ npkg := r.NPkg()
+ r.pkg = make([]uint32, npkg)
+ for i := 1; i < npkg; i++ { // PkgIdx 0 is a dummy invalid package
+ pkg := r.Pkg(i)
+ objidx, ok := l.objByPkg[pkg]
+ if !ok {
+ log.Fatalf("%v: reference to nonexistent package %s", r.unit.Lib, pkg)
+ }
+ r.pkg[i] = objidx
+ }
+
+ // load flags of package refs
+ for i, n := 0, r.NRefFlags(); i < n; i++ {
+ rf := r.RefFlags(i)
+ gi := l.resolve(r, rf.Sym())
+ if rf.Flag2()&goobj.SymFlagUsedInIface != 0 {
+ l.SetAttrUsedInIface(gi, true)
+ }
+ }
+}
+
+func abiToVer(abi uint16, localSymVersion int) int {
+ var v int
+ if abi == goobj.SymABIstatic {
+ // Static
+ v = localSymVersion
+ } else if abiver := sym.ABIToVersion(obj.ABI(abi)); abiver != -1 {
+ // Note that data symbols are "ABI0", which maps to version 0.
+ v = abiver
+ } else {
+ log.Fatalf("invalid symbol ABI: %d", abi)
+ }
+ return v
+}
+
+// TopLevelSym tests a symbol (by name and kind) to determine whether
+// the symbol first class sym (participating in the link) or is an
+// anonymous aux or sub-symbol containing some sub-part or payload of
+// another symbol.
+func (l *Loader) TopLevelSym(s Sym) bool {
+ return topLevelSym(l.SymName(s), l.SymType(s))
+}
+
+// topLevelSym tests a symbol name and kind to determine whether
+// the symbol first class sym (participating in the link) or is an
+// anonymous aux or sub-symbol containing some sub-part or payload of
+// another symbol.
+func topLevelSym(sname string, skind sym.SymKind) bool {
+ if sname != "" {
+ return true
+ }
+ switch skind {
+ case sym.SDWARFFCN, sym.SDWARFABSFCN, sym.SDWARFTYPE, sym.SDWARFCONST, sym.SDWARFCUINFO, sym.SDWARFRANGE, sym.SDWARFLOC, sym.SDWARFLINES, sym.SGOFUNC:
+ return true
+ default:
+ return false
+ }
+}
+
+// cloneToExternal takes the existing object file symbol (symIdx)
+// and creates a new external symbol payload that is a clone with
+// respect to name, version, type, relocations, etc. The idea here
+// is that if the linker decides it wants to update the contents of
+// a symbol originally discovered as part of an object file, it's
+// easier to do this if we make the updates to an external symbol
+// payload.
+func (l *Loader) cloneToExternal(symIdx Sym) {
+ if l.IsExternal(symIdx) {
+ panic("sym is already external, no need for clone")
+ }
+
+ // Read the particulars from object.
+ r, li := l.toLocal(symIdx)
+ osym := r.Sym(li)
+ sname := osym.Name(r.Reader)
+ sver := abiToVer(osym.ABI(), r.version)
+ skind := sym.AbiSymKindToSymKind[objabi.SymKind(osym.Type())]
+
+ // Create new symbol, update version and kind.
+ pi := l.newPayload(sname, sver)
+ pp := l.payloads[pi]
+ pp.kind = skind
+ pp.ver = sver
+ pp.size = int64(osym.Siz())
+ pp.objidx = r.objidx
+
+ // If this is a def, then copy the guts. We expect this case
+ // to be very rare (one case it may come up is with -X).
+ if li < uint32(r.NAlldef()) {
+
+ // Copy relocations
+ relocs := l.Relocs(symIdx)
+ pp.relocs = make([]goobj.Reloc, relocs.Count())
+ for i := range pp.relocs {
+ // Copy the relocs slice.
+ // Convert local reference to global reference.
+ rel := relocs.At(i)
+ pp.relocs[i].Set(rel.Off(), rel.Siz(), uint16(rel.Type()), rel.Add(), goobj.SymRef{PkgIdx: 0, SymIdx: uint32(rel.Sym())})
+ }
+
+ // Copy data
+ pp.data = r.Data(li)
+ }
+
+ // If we're overriding a data symbol, collect the associated
+ // Gotype, so as to propagate it to the new symbol.
+ auxs := r.Auxs(li)
+ pp.auxs = auxs
+
+ // Install new payload to global index space.
+ // (This needs to happen at the end, as the accessors above
+ // need to access the old symbol content.)
+ l.objSyms[symIdx] = objSym{l.extReader.objidx, uint32(pi)}
+ l.extReader.syms = append(l.extReader.syms, symIdx)
+
+ // Some attributes were encoded in the object file. Copy them over.
+ l.SetAttrDuplicateOK(symIdx, r.Sym(li).Dupok())
+ l.SetAttrShared(symIdx, r.Shared())
+}
+
+// Copy the payload of symbol src to dst. Both src and dst must be external
+// symbols.
+// The intended use case is that when building/linking against a shared library,
+// where we do symbol name mangling, the Go object file may have reference to
+// the original symbol name whereas the shared library provides a symbol with
+// the mangled name. When we do mangling, we copy payload of mangled to original.
+func (l *Loader) CopySym(src, dst Sym) {
+ if !l.IsExternal(dst) {
+ panic("dst is not external") //l.newExtSym(l.SymName(dst), l.SymVersion(dst))
+ }
+ if !l.IsExternal(src) {
+ panic("src is not external") //l.cloneToExternal(src)
+ }
+ l.payloads[l.extIndex(dst)] = l.payloads[l.extIndex(src)]
+ l.SetSymPkg(dst, l.SymPkg(src))
+ // TODO: other attributes?
+}
+
+// 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, ver int) Sym {
+ return l.newExtSym(name, ver)
+}
+
+// CreateStaticSym creates a new static symbol with the specified name
+// without adding it to any lookup tables, returning a Sym index for it.
+func (l *Loader) CreateStaticSym(name string) Sym {
+ // Assign a new unique negative version -- this is to mark the
+ // symbol so that it is not included in the name lookup table.
+ l.anonVersion--
+ return l.newExtSym(name, l.anonVersion)
+}
+
+func (l *Loader) FreeSym(i Sym) {
+ if l.IsExternal(i) {
+ pp := l.getPayload(i)
+ *pp = extSymPayload{}
+ }
+}
+
+// relocId is essentially a <S,R> tuple identifying the Rth
+// relocation of symbol S.
+type relocId struct {
+ sym Sym
+ ridx int
+}
+
+// SetRelocVariant sets the 'variant' property of a relocation on
+// some specific symbol.
+func (l *Loader) SetRelocVariant(s Sym, ri int, v sym.RelocVariant) {
+ // sanity check
+ if relocs := l.Relocs(s); ri >= relocs.Count() {
+ panic("invalid relocation ID")
+ }
+ if l.relocVariant == nil {
+ l.relocVariant = make(map[relocId]sym.RelocVariant)
+ }
+ if v != 0 {
+ l.relocVariant[relocId{s, ri}] = v
+ } else {
+ delete(l.relocVariant, relocId{s, ri})
+ }
+}
+
+// RelocVariant returns the 'variant' property of a relocation on
+// some specific symbol.
+func (l *Loader) RelocVariant(s Sym, ri int) sym.RelocVariant {
+ return l.relocVariant[relocId{s, ri}]
+}
+
+// UndefinedRelocTargets iterates through the global symbol index
+// space, looking for symbols with relocations targeting undefined
+// references. The linker's loadlib method uses this to determine if
+// there are unresolved references to functions in system libraries
+// (for example, libgcc.a), presumably due to CGO code. Return value
+// is a pair of lists of loader.Sym's. First list corresponds to the
+// corresponding to the undefined symbols themselves, the second list
+// is the symbol that is making a reference to the undef. The "limit"
+// param controls the maximum number of results returned; if "limit"
+// is -1, then all undefs are returned.
+func (l *Loader) UndefinedRelocTargets(limit int) ([]Sym, []Sym) {
+ result, fromr := []Sym{}, []Sym{}
+outerloop:
+ for si := Sym(1); si < Sym(len(l.objSyms)); si++ {
+ relocs := l.Relocs(si)
+ for ri := 0; ri < relocs.Count(); ri++ {
+ r := relocs.At(ri)
+ rs := r.Sym()
+ if rs != 0 && l.SymType(rs) == sym.SXREF && l.SymName(rs) != ".got" {
+ result = append(result, rs)
+ fromr = append(fromr, si)
+ if limit != -1 && len(result) >= limit {
+ break outerloop
+ }
+ }
+ }
+ }
+ return result, fromr
+}
+
+// AssignTextSymbolOrder populates the Textp slices within each
+// library and compilation unit, insuring that packages are laid down
+// in dependency order (internal first, then everything else). Return value
+// is a slice of all text syms.
+func (l *Loader) AssignTextSymbolOrder(libs []*sym.Library, intlibs []bool, extsyms []Sym) []Sym {
+
+ // Library Textp lists should be empty at this point.
+ for _, lib := range libs {
+ if len(lib.Textp) != 0 {
+ panic("expected empty Textp slice for library")
+ }
+ if len(lib.DupTextSyms) != 0 {
+ panic("expected empty DupTextSyms slice for library")
+ }
+ }
+
+ // Used to record which dupok symbol we've assigned to a unit.
+ // Can't use the onlist attribute here because it will need to
+ // clear for the later assignment of the sym.Symbol to a unit.
+ // NB: we can convert to using onList once we no longer have to
+ // call the regular addToTextp.
+ assignedToUnit := MakeBitmap(l.NSym() + 1)
+
+ // Start off textp with reachable external syms.
+ textp := []Sym{}
+ for _, sym := range extsyms {
+ if !l.attrReachable.Has(sym) {
+ continue
+ }
+ textp = append(textp, sym)
+ }
+
+ // Walk through all text symbols from Go object files and append
+ // them to their corresponding library's textp list.
+ for _, o := range l.objs[goObjStart:] {
+ r := o.r
+ lib := r.unit.Lib
+ for i, n := uint32(0), uint32(r.NAlldef()); i < n; i++ {
+ gi := l.toGlobal(r, i)
+ if !l.attrReachable.Has(gi) {
+ continue
+ }
+ osym := r.Sym(i)
+ st := sym.AbiSymKindToSymKind[objabi.SymKind(osym.Type())]
+ if st != sym.STEXT {
+ continue
+ }
+ dupok := osym.Dupok()
+ if r2, i2 := l.toLocal(gi); r2 != r || i2 != i {
+ // A dupok text symbol is resolved to another package.
+ // We still need to record its presence in the current
+ // package, as the trampoline pass expects packages
+ // are laid out in dependency order.
+ lib.DupTextSyms = append(lib.DupTextSyms, sym.LoaderSym(gi))
+ continue // symbol in different object
+ }
+ if dupok {
+ lib.DupTextSyms = append(lib.DupTextSyms, sym.LoaderSym(gi))
+ continue
+ }
+
+ lib.Textp = append(lib.Textp, sym.LoaderSym(gi))
+ }
+ }
+
+ // Now assemble global textp, and assign text symbols to units.
+ for _, doInternal := range [2]bool{true, false} {
+ for idx, lib := range libs {
+ if intlibs[idx] != doInternal {
+ continue
+ }
+ lists := [2][]sym.LoaderSym{lib.Textp, lib.DupTextSyms}
+ for i, list := range lists {
+ for _, s := range list {
+ sym := Sym(s)
+ if !assignedToUnit.Has(sym) {
+ textp = append(textp, sym)
+ unit := l.SymUnit(sym)
+ if unit != nil {
+ unit.Textp = append(unit.Textp, s)
+ assignedToUnit.Set(sym)
+ }
+ // Dupok symbols may be defined in multiple packages; the
+ // associated package for a dupok sym is chosen sort of
+ // arbitrarily (the first containing package that the linker
+ // loads). Canonicalizes its Pkg to the package with which
+ // it will be laid down in text.
+ if i == 1 /* DupTextSyms2 */ && l.SymPkg(sym) != lib.Pkg {
+ l.SetSymPkg(sym, lib.Pkg)
+ }
+ }
+ }
+ }
+ lib.Textp = nil
+ lib.DupTextSyms = nil
+ }
+ }
+
+ return textp
+}
+
+// ErrorReporter is a helper class for reporting errors.
+type ErrorReporter struct {
+ ldr *Loader
+ AfterErrorAction func()
+}
+
+// Errorf method logs an error message.
+//
+// After each error, the error actions function will be invoked; this
+// will either terminate the link immediately (if -h option given)
+// or it will keep a count and exit if more than 20 errors have been printed.
+//
+// Logging an error means that on exit cmd/link will delete any
+// output file and return a non-zero error code.
+func (reporter *ErrorReporter) Errorf(s Sym, format string, args ...interface{}) {
+ if s != 0 && reporter.ldr.SymName(s) != "" {
+ // Note: Replace is needed here because symbol names might have % in them,
+ // due to the use of LinkString for names of instantiating types.
+ format = strings.Replace(reporter.ldr.SymName(s), "%", "%%", -1) + ": " + format
+ } else {
+ format = fmt.Sprintf("sym %d: %s", s, format)
+ }
+ format += "\n"
+ fmt.Fprintf(os.Stderr, format, args...)
+ reporter.AfterErrorAction()
+}
+
+// GetErrorReporter returns the loader's associated error reporter.
+func (l *Loader) GetErrorReporter() *ErrorReporter {
+ return l.errorReporter
+}
+
+// Errorf method logs an error message. See ErrorReporter.Errorf for details.
+func (l *Loader) Errorf(s Sym, format string, args ...interface{}) {
+ l.errorReporter.Errorf(s, format, args...)
+}
+
+// Symbol statistics.
+func (l *Loader) Stat() string {
+ s := fmt.Sprintf("%d symbols, %d reachable\n", l.NSym(), l.NReachableSym())
+ s += fmt.Sprintf("\t%d package symbols, %d hashed symbols, %d non-package symbols, %d external symbols\n",
+ l.npkgsyms, l.nhashedsyms, int(l.extStart)-l.npkgsyms-l.nhashedsyms, l.NSym()-int(l.extStart))
+ return s
+}
+
+// For debugging.
+func (l *Loader) Dump() {
+ fmt.Println("objs")
+ for _, obj := range l.objs[goObjStart:] {
+ if obj.r != nil {
+ fmt.Println(obj.i, obj.r.unit.Lib)
+ }
+ }
+ fmt.Println("extStart:", l.extStart)
+ fmt.Println("Nsyms:", len(l.objSyms))
+ fmt.Println("syms")
+ for i := Sym(1); i < Sym(len(l.objSyms)); i++ {
+ pi := ""
+ if l.IsExternal(i) {
+ pi = fmt.Sprintf("<ext %d>", l.extIndex(i))
+ }
+ sect := ""
+ if l.SymSect(i) != nil {
+ sect = l.SymSect(i).Name
+ }
+ fmt.Printf("%v %v %v %v %x %v\n", i, l.SymName(i), l.SymType(i), pi, l.SymValue(i), sect)
+ }
+ fmt.Println("symsByName")
+ for name, i := range l.symsByName[0] {
+ fmt.Println(i, name, 0)
+ }
+ for name, i := range l.symsByName[1] {
+ fmt.Println(i, name, 1)
+ }
+ fmt.Println("payloads:")
+ for i := range l.payloads {
+ pp := l.payloads[i]
+ fmt.Println(i, pp.name, pp.ver, pp.kind)
+ }
+}