summaryrefslogtreecommitdiffstats
path: root/src/cmd/link/internal/loader
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 13:14:23 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 13:14:23 +0000
commit73df946d56c74384511a194dd01dbe099584fd1a (patch)
treefd0bcea490dd81327ddfbb31e215439672c9a068 /src/cmd/link/internal/loader
parentInitial commit. (diff)
downloadgolang-1.16-upstream.tar.xz
golang-1.16-upstream.zip
Adding upstream version 1.16.10.upstream/1.16.10upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/cmd/link/internal/loader')
-rw-r--r--src/cmd/link/internal/loader/loader.go2683
-rw-r--r--src/cmd/link/internal/loader/loader_test.go454
-rw-r--r--src/cmd/link/internal/loader/symbolbuilder.go440
3 files changed, 3577 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..85b9489
--- /dev/null
+++ b/src/cmd/link/internal/loader/loader.go
@@ -0,0 +1,2683 @@
+// 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"
+ "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
+
+ // External reloc types may not fit into a uint8 which the Go object file uses.
+ // Store it here, instead of in the byte of goobj.Reloc.
+ // For Go symbols this will always be zero.
+ // goobj.Reloc.Type() + typ is always the right type, for both Go and external
+ // symbols.
+ typ objabi.RelocType
+}
+
+func (rel Reloc) Type() objabi.RelocType { return objabi.RelocType(rel.Reloc.Type()) + rel.typ }
+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 }
+
+func (rel Reloc) SetType(t objabi.RelocType) {
+ if t != objabi.RelocType(uint8(t)) {
+ panic("SetType: type doesn't fit into Reloc")
+ }
+ rel.Reloc.SetType(uint8(t))
+ if rel.typ != 0 {
+ // should use SymbolBuilder.SetRelocType
+ panic("wrong method to set reloc type")
+ }
+}
+
+// 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
+ flags uint32 // read from object file
+ 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
+ attrTopFrame map[Sym]struct{} // top frame symbols
+ 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
+
+ flags uint32
+
+ hasUnknownPkgPath bool // if any Go object has unknown package path
+
+ 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
+ reltypes []objabi.RelocType // relocation types
+ 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),
+ attrTopFrame: make(map[Sym]struct{}),
+ 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})
+ if r.NeedNameExpansion() && !r.FromAssembly() {
+ l.hasUnknownPkgPath = true
+ }
+ 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 " + name)
+ }
+ 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 " + name)
+ }
+ }
+ 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
+}
+
+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)
+ if bytes.Equal(p, pdup) {
+ return
+ }
+ reason := "same length but different contents"
+ if len(p) != len(pdup) {
+ reason = fmt.Sprintf("new length %d != old length %d", len(p), len(pdup))
+ }
+ 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()
+}
+
+// SymNameLen returns the length of the symbol name, trying hard not to load
+// the name.
+func (l *Loader) SymNameLen(i Sym) int {
+ // Not much we can do about external symbols.
+ if l.IsExternal(i) {
+ return len(l.SymName(i))
+ }
+ r, li := l.toLocal(i)
+ le := r.Sym(li).NameLen(r.Reader)
+ if !r.NeedNameExpansion() {
+ return le
+ }
+ // Just load the symbol name. We don't know how expanded it'll be.
+ return len(l.SymName(i))
+}
+
+// Returns the raw (unpatched) name of the i-th symbol.
+func (l *Loader) RawSymName(i Sym) string {
+ if l.IsExternal(i) {
+ pp := l.getPayload(i)
+ return pp.name
+ }
+ r, li := l.toLocal(i)
+ return r.Sym(li).Name(r.Reader)
+}
+
+// Returns the (patched) 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)
+ name := r.Sym(li).Name(r.Reader)
+ if !r.NeedNameExpansion() {
+ return name
+ }
+ return strings.Replace(name, "\"\".", r.pkgprefix, -1)
+}
+
+// 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.RawSymName(i)))
+ }
+ if v {
+ l.attrExternal.Set(l.extIndex(i))
+ } else {
+ l.attrExternal.Unset(l.extIndex(i))
+ }
+}
+
+// AttrTopFrame returns true for a function symbol that is an entry
+// point, meaning that unwinders should stop when they hit this
+// function.
+func (l *Loader) AttrTopFrame(i Sym) bool {
+ _, ok := l.attrTopFrame[i]
+ return ok
+}
+
+// SetAttrTopFrame sets the "top frame" property for a symbol (see
+// AttrTopFrame).
+func (l *Loader) SetAttrTopFrame(i Sym, v bool) {
+ if v {
+ l.attrTopFrame[i] = struct{}{}
+ } else {
+ delete(l.attrTopFrame, 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()
+}
+
+// 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 value for pe symbols.
+func (l *Loader) SymPlt(s Sym) int32 {
+ if v, ok := l.plt[s]; ok {
+ return v
+ }
+ return -1
+}
+
+// SetPlt sets the plt value for pe symbols.
+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 value for pe symbols.
+func (l *Loader) SymGot(s Sym) int32 {
+ if v, ok := l.got[s]; ok {
+ return v
+ }
+ return -1
+}
+
+// SetGot sets the got value for pe symbols.
+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 {
+ var r *oReader
+ var auxs []goobj.Aux
+ if l.IsExternal(i) {
+ pp := l.getPayload(i)
+ r = l.objs[pp.objidx].r
+ auxs = pp.auxs
+ } else {
+ var li uint32
+ r, li = l.toLocal(i)
+ auxs = r.Auxs(li)
+ }
+ for j := range auxs {
+ a := &auxs[j]
+ switch a.Type() {
+ case goobj.AuxGotype:
+ return l.resolve(r, a.Sym())
+ }
+ }
+ return 0
+}
+
+// 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 correspondes 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 the "local entry" value for the specified
+// symbol.
+func (l *Loader) SymLocalentry(i Sym) uint8 {
+ return l.localentry[i]
+}
+
+// SetSymLocalentry sets the "local entry" 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())
+ }
+ if l.IsExternal(fnSymIdx) {
+ // Current expectation is that any external function will
+ // not have auxsyms.
+ return
+ }
+ r, li := l.toLocal(fnSymIdx)
+ auxs := r.Auxs(li)
+ 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) {
+ pp := relocs.l.payloads[relocs.li]
+ return Reloc{&relocs.rs[j], relocs.r, relocs.l, pp.reltypes[j]}
+ }
+ return Reloc{&relocs.rs[j], relocs.r, relocs.l, 0}
+}
+
+// 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,
+ }
+}
+
+// FuncInfo provides hooks to access goobj.FuncInfo in the objects.
+type FuncInfo struct {
+ l *Loader
+ r *oReader
+ data []byte
+ auxs []goobj.Aux
+ 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 objabi.FuncID((*goobj.FuncInfo)(nil).ReadFuncID(fi.data))
+}
+
+func (fi *FuncInfo) Pcsp() Sym {
+ sym := (*goobj.FuncInfo)(nil).ReadPcsp(fi.data)
+ return fi.l.resolve(fi.r, sym)
+}
+
+func (fi *FuncInfo) Pcfile() Sym {
+ sym := (*goobj.FuncInfo)(nil).ReadPcfile(fi.data)
+ return fi.l.resolve(fi.r, sym)
+}
+
+func (fi *FuncInfo) Pcline() Sym {
+ sym := (*goobj.FuncInfo)(nil).ReadPcline(fi.data)
+ return fi.l.resolve(fi.r, sym)
+}
+
+func (fi *FuncInfo) Pcinline() Sym {
+ sym := (*goobj.FuncInfo)(nil).ReadPcinline(fi.data)
+ return fi.l.resolve(fi.r, sym)
+}
+
+// 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) Pcdata() []Sym {
+ if !fi.lengths.Initialized {
+ panic("need to call Preload first")
+ }
+ syms := (*goobj.FuncInfo)(nil).ReadPcdata(fi.data)
+ ret := make([]Sym, len(syms))
+ for i := range ret {
+ ret[i] = fi.l.resolve(fi.r, syms[i])
+ }
+ return ret
+}
+
+func (fi *FuncInfo) NumFuncdataoff() uint32 {
+ if !fi.lengths.Initialized {
+ panic("need to call Preload first")
+ }
+ return fi.lengths.NumFuncdataoff
+}
+
+func (fi *FuncInfo) Funcdataoff(k int) int64 {
+ if !fi.lengths.Initialized {
+ panic("need to call Preload first")
+ }
+ return (*goobj.FuncInfo)(nil).ReadFuncdataoff(fi.data, fi.lengths.FuncdataoffOff, uint32(k))
+}
+
+func (fi *FuncInfo) Funcdata(syms []Sym) []Sym {
+ if !fi.lengths.Initialized {
+ panic("need to call Preload first")
+ }
+ if int(fi.lengths.NumFuncdataoff) > cap(syms) {
+ syms = make([]Sym, 0, fi.lengths.NumFuncdataoff)
+ } else {
+ syms = syms[:0]
+ }
+ for j := range fi.auxs {
+ a := &fi.auxs[j]
+ if a.Type() == goobj.AuxFuncdata {
+ syms = append(syms, fi.l.resolve(fi.r, a.Sym()))
+ }
+ }
+ return syms
+}
+
+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))
+}
+
+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 {
+ var r *oReader
+ var auxs []goobj.Aux
+ if l.IsExternal(i) {
+ pp := l.getPayload(i)
+ if pp.objidx == 0 {
+ return FuncInfo{}
+ }
+ r = l.objs[pp.objidx].r
+ auxs = pp.auxs
+ } else {
+ var li uint32
+ r, li = l.toLocal(i)
+ auxs = r.Auxs(li)
+ }
+ for j := range auxs {
+ a := &auxs[j]
+ if a.Type() == goobj.AuxFuncInfo {
+ b := r.Data(a.Sym().SymIdx)
+ return FuncInfo{l, r, b, auxs, 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,
+ flags: r.Flags(),
+ pkgprefix: pkgprefix,
+ syms: make([]Sym, ndef+nhashed64def+nhasheddef+r.NNonpkgdef()+r.NNonpkgref()),
+ ndef: ndef,
+ nhasheddef: nhasheddef,
+ nhashed64def: nhashed64def,
+ objidx: uint32(len(l.objs)),
+ }
+
+ // 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, os.SEEK_CUR)
+
+ 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)
+ if l.hasUnknownPkgPath {
+ // The content hash depends on symbol name expansion. If any package is
+ // built without fully expanded names, the content hash is unreliable.
+ // Treat them as named symbols.
+ // This is rare.
+ // (We don't need to do this for hashed64Def case, as there the hash
+ // function is simply the identity function, which doesn't depend on
+ // name expansion.)
+ kind = nonPkgDef
+ }
+ 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))
+ needNameExpansion := r.NeedNameExpansion()
+ 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)
+ if needNameExpansion {
+ name = strings.Replace(name, "\"\".", r.pkgprefix, -1)
+ }
+ v = abiToVer(osym.ABI(), r.version)
+ }
+ gi := st.addSym(name, v, r, i, kind, osym)
+ r.syms[i] = gi
+ if osym.TopFrame() {
+ l.SetAttrTopFrame(gi, true)
+ }
+ 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, v); 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)
+
+ l.npkgsyms = l.NSym()
+ 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)
+ }
+ 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())
+ needNameExpansion := r.NeedNameExpansion()
+ for i, n := uint32(0), uint32(r.NNonpkgref()); i < n; i++ {
+ osym := r.Sym(ndef + i)
+ name := osym.Name(r.Reader)
+ if needNameExpansion {
+ name = strings.Replace(name, "\"\".", r.pkgprefix, -1)
+ }
+ 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("reference of nonexisted package %s, from %v", pkg, r.unit.Lib)
+ }
+ 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
+}
+
+// ResolveABIAlias given a symbol returns the ABI alias target of that
+// symbol. If the sym in question is not an alias, the sym itself is
+// returned.
+func (l *Loader) ResolveABIAlias(s Sym) Sym {
+ if s == 0 {
+ return 0
+ }
+ if l.SymType(s) != sym.SABIALIAS {
+ return s
+ }
+ relocs := l.Relocs(s)
+ target := relocs.At(0).Sym()
+ if l.SymType(target) == sym.SABIALIAS {
+ panic(fmt.Sprintf("ABI alias %s references another ABI alias %s", l.SymName(s), l.SymName(target)))
+ }
+ return target
+}
+
+// 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.RawSymName(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)
+ if r.NeedNameExpansion() {
+ sname = strings.Replace(sname, "\"\".", r.pkgprefix, -1)
+ }
+ 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())
+ pp.reltypes = make([]objabi.RelocType, 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(), 0, rel.Add(), goobj.SymRef{PkgIdx: 0, SymIdx: uint32(rel.Sym())})
+ pp.reltypes[i] = rel.Type()
+ }
+
+ // 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)
+}
+
+// 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?
+}
+
+// CopyAttributes copies over all of the attributes of symbol 'src' to
+// symbol 'dst'.
+func (l *Loader) CopyAttributes(src Sym, dst Sym) {
+ l.SetAttrReachable(dst, l.AttrReachable(src))
+ l.SetAttrOnList(dst, l.AttrOnList(src))
+ l.SetAttrLocal(dst, l.AttrLocal(src))
+ l.SetAttrNotInSymbolTable(dst, l.AttrNotInSymbolTable(src))
+ if l.IsExternal(dst) {
+ l.SetAttrVisibilityHidden(dst, l.AttrVisibilityHidden(src))
+ l.SetAttrDuplicateOK(dst, l.AttrDuplicateOK(src))
+ l.SetAttrShared(dst, l.AttrShared(src))
+ l.SetAttrExternal(dst, l.AttrExternal(src))
+ } else {
+ // Some attributes are modifiable only for external symbols.
+ // In such cases, don't try to transfer over the attribute
+ // from the source even if there is a clash. This comes up
+ // when copying attributes from a dupOK ABI wrapper symbol to
+ // the real target symbol (which may not be marked dupOK).
+ }
+ l.SetAttrTopFrame(dst, l.AttrTopFrame(src))
+ l.SetAttrSpecial(dst, l.AttrSpecial(src))
+ l.SetAttrCgoExportDynamic(dst, l.AttrCgoExportDynamic(src))
+ l.SetAttrCgoExportStatic(dst, l.AttrCgoExportStatic(src))
+ l.SetAttrReadOnly(dst, l.AttrReadOnly(src))
+}
+
+// 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 list of loader.Sym's corresponding to the undefined
+// cross-refs. 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 {
+ result := []Sym{}
+ 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.RawSymName(rs) != ".got" {
+ result = append(result, rs)
+ if limit != -1 && len(result) >= limit {
+ break
+ }
+ }
+ }
+ }
+ return result
+}
+
+// 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 l.attrReachable.Has(sym) && !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) != "" {
+ format = reporter.ldr.SymName(s) + ": " + 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)
+ }
+}
diff --git a/src/cmd/link/internal/loader/loader_test.go b/src/cmd/link/internal/loader/loader_test.go
new file mode 100644
index 0000000..1371c2a
--- /dev/null
+++ b/src/cmd/link/internal/loader/loader_test.go
@@ -0,0 +1,454 @@
+// 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/goobj"
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/sym"
+ "fmt"
+ "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 := uint32(len(ldr.objSyms))
+ st := loadState{l: ldr}
+ return st.addSym(name, 0, or, idx, nonPkgDef, &goobj.Sym{})
+}
+
+func mkLoader() *Loader {
+ edummy := func(str string, off int) {}
+ er := ErrorReporter{}
+ ldr := NewLoader(0, edummy, &er)
+ er.ldr = ldr
+ return ldr
+}
+
+func TestAddMaterializedSymbol(t *testing.T) {
+ ldr := mkLoader()
+ dummyOreader := oReader{version: -1, syms: make([]Sym, 100)}
+ or := &dummyOreader
+
+ // Create some syms from a dummy object file symbol to get things going.
+ ts1 := addDummyObjSym(t, ldr, or, "type.uint8")
+ ts2 := addDummyObjSym(t, ldr, or, "mumble")
+ ts3 := addDummyObjSym(t, ldr, or, "type.string")
+
+ // Create some external symbols.
+ es1 := ldr.LookupOrCreateSym("extnew1", 0)
+ if es1 == 0 {
+ t.Fatalf("LookupOrCreateSym failed for extnew1")
+ }
+ es1x := ldr.LookupOrCreateSym("extnew1", 0)
+ if es1x != es1 {
+ t.Fatalf("LookupOrCreateSym lookup: expected %d got %d for second lookup", es1, es1x)
+ }
+ es2 := ldr.LookupOrCreateSym("go.info.type.uint8", 0)
+ if es2 == 0 {
+ t.Fatalf("LookupOrCreateSym failed for go.info.type.uint8")
+ }
+ // Create a nameless symbol
+ es3 := ldr.CreateStaticSym("")
+ if es3 == 0 {
+ t.Fatalf("CreateStaticSym failed for nameless sym")
+ }
+
+ // Grab symbol builder pointers
+ sb1 := ldr.MakeSymbolUpdater(es1)
+ sb2 := ldr.MakeSymbolUpdater(es2)
+ sb3 := ldr.MakeSymbolUpdater(es3)
+
+ // Suppose we create some more symbols, which triggers a grow.
+ // Make sure the symbol builder's payload pointer is valid,
+ // even across a grow.
+ for i := 0; i < 9999; i++ {
+ ldr.CreateStaticSym("dummy")
+ }
+
+ // Check get/set symbol type
+ es3typ := sb3.Type()
+ if es3typ != sym.Sxxx {
+ t.Errorf("SymType(es3): expected %v, got %v", sym.Sxxx, es3typ)
+ }
+ sb3.SetType(sym.SRODATA)
+ es3typ = sb3.Type()
+ if es3typ != sym.SRODATA {
+ t.Errorf("SymType(es3): expected %v, got %v", sym.SRODATA, es3typ)
+ }
+ es3typ = ldr.SymType(es3)
+ if es3typ != sym.SRODATA {
+ t.Errorf("SymType(es3): expected %v, got %v", sym.SRODATA, es3typ)
+ }
+
+ // New symbols should not initially be reachable.
+ if ldr.AttrReachable(es1) || ldr.AttrReachable(es2) || ldr.AttrReachable(es3) {
+ t.Errorf("newly materialized symbols should not be reachable")
+ }
+
+ // ... however it should be possible to set/unset their reachability.
+ ldr.SetAttrReachable(es3, true)
+ if !ldr.AttrReachable(es3) {
+ t.Errorf("expected reachable symbol after update")
+ }
+ ldr.SetAttrReachable(es3, false)
+ if ldr.AttrReachable(es3) {
+ t.Errorf("expected unreachable symbol after update")
+ }
+
+ // Test expansion of attr bitmaps
+ for idx := 0; idx < 36; idx++ {
+ es := ldr.LookupOrCreateSym(fmt.Sprintf("zext%d", idx), 0)
+ if ldr.AttrOnList(es) {
+ t.Errorf("expected OnList after creation")
+ }
+ ldr.SetAttrOnList(es, true)
+ if !ldr.AttrOnList(es) {
+ t.Errorf("expected !OnList after update")
+ }
+ if ldr.AttrDuplicateOK(es) {
+ t.Errorf("expected DupOK after creation")
+ }
+ ldr.SetAttrDuplicateOK(es, true)
+ if !ldr.AttrDuplicateOK(es) {
+ t.Errorf("expected !DupOK after update")
+ }
+ }
+
+ sb1 = ldr.MakeSymbolUpdater(es1)
+ sb2 = ldr.MakeSymbolUpdater(es2)
+
+ // Get/set a few other attributes
+ if ldr.AttrVisibilityHidden(es3) {
+ t.Errorf("expected initially not hidden")
+ }
+ ldr.SetAttrVisibilityHidden(es3, true)
+ if !ldr.AttrVisibilityHidden(es3) {
+ t.Errorf("expected hidden after update")
+ }
+
+ // Test get/set symbol value.
+ toTest := []Sym{ts2, es3}
+ for i, s := range toTest {
+ if v := ldr.SymValue(s); v != 0 {
+ t.Errorf("ldr.Value(%d): expected 0 got %d\n", s, v)
+ }
+ nv := int64(i + 101)
+ ldr.SetSymValue(s, nv)
+ if v := ldr.SymValue(s); v != nv {
+ t.Errorf("ldr.SetValue(%d,%d): expected %d got %d\n", s, nv, nv, v)
+ }
+ }
+
+ // Check/set alignment
+ es3al := ldr.SymAlign(es3)
+ if es3al != 0 {
+ t.Errorf("SymAlign(es3): expected 0, got %d", es3al)
+ }
+ ldr.SetSymAlign(es3, 128)
+ es3al = ldr.SymAlign(es3)
+ if es3al != 128 {
+ t.Errorf("SymAlign(es3): expected 128, got %d", es3al)
+ }
+
+ // Add some relocations to the new symbols.
+ r1, _ := sb1.AddRel(objabi.R_ADDR)
+ r1.SetOff(0)
+ r1.SetSiz(1)
+ r1.SetSym(ts1)
+ r2, _ := sb1.AddRel(objabi.R_CALL)
+ r2.SetOff(3)
+ r2.SetSiz(8)
+ r2.SetSym(ts2)
+ r3, _ := sb2.AddRel(objabi.R_USETYPE)
+ r3.SetOff(7)
+ r3.SetSiz(1)
+ r3.SetSym(ts3)
+
+ // Add some data to the symbols.
+ d1 := []byte{1, 2, 3}
+ d2 := []byte{4, 5, 6, 7}
+ sb1.AddBytes(d1)
+ sb2.AddBytes(d2)
+
+ // Now invoke the usual loader interfaces to make sure
+ // we're getting the right things back for these symbols.
+ // First relocations...
+ expRel := [][]Reloc{{r1, r2}, {r3}}
+ for k, sb := range []*SymbolBuilder{sb1, sb2} {
+ rsl := sb.Relocs()
+ exp := expRel[k]
+ if !sameRelocSlice(&rsl, exp) {
+ t.Errorf("expected relocs %v, got %v", exp, rsl)
+ }
+ }
+
+ // ... then data.
+ dat := sb2.Data()
+ if bytes.Compare(dat, d2) != 0 {
+ t.Errorf("expected es2 data %v, got %v", d2, dat)
+ }
+
+ // Nameless symbol should still be nameless.
+ es3name := ldr.RawSymName(es3)
+ if "" != es3name {
+ t.Errorf("expected es3 name of '', got '%s'", es3name)
+ }
+
+ // Read value of materialized symbol.
+ es1val := sb1.Value()
+ if 0 != es1val {
+ t.Errorf("expected es1 value of 0, got %v", es1val)
+ }
+
+ // Test other misc methods
+ irm := ldr.IsReflectMethod(es1)
+ if 0 != es1val {
+ t.Errorf("expected IsReflectMethod(es1) value of 0, got %v", irm)
+ }
+}
+
+func sameRelocSlice(s1 *Relocs, s2 []Reloc) bool {
+ if s1.Count() != len(s2) {
+ return false
+ }
+ for i := 0; i < s1.Count(); i++ {
+ r1 := s1.At(i)
+ r2 := &s2[i]
+ if r1.Sym() != r2.Sym() ||
+ r1.Type() != r2.Type() ||
+ r1.Off() != r2.Off() ||
+ r1.Add() != r2.Add() ||
+ r1.Siz() != r2.Siz() {
+ return false
+ }
+ }
+ return true
+}
+
+type addFunc func(l *Loader, s Sym, s2 Sym) Sym
+
+func mkReloc(l *Loader, typ objabi.RelocType, off int32, siz uint8, add int64, sym Sym) Reloc {
+ r := Reloc{&goobj.Reloc{}, l.extReader, l, typ}
+ r.SetOff(off)
+ r.SetSiz(siz)
+ r.SetAdd(add)
+ r.SetSym(sym)
+ return r
+}
+
+func TestAddDataMethods(t *testing.T) {
+ ldr := mkLoader()
+ dummyOreader := oReader{version: -1, syms: make([]Sym, 100)}
+ or := &dummyOreader
+
+ // Populate loader with some symbols.
+ addDummyObjSym(t, ldr, or, "type.uint8")
+ ldr.LookupOrCreateSym("hello", 0)
+
+ arch := sys.ArchAMD64
+ var testpoints = []struct {
+ which string
+ addDataFunc addFunc
+ expData []byte
+ expKind sym.SymKind
+ expRel []Reloc
+ }{
+ {
+ which: "AddUint8",
+ addDataFunc: func(l *Loader, s Sym, _ Sym) Sym {
+ sb := l.MakeSymbolUpdater(s)
+ sb.AddUint8('a')
+ return s
+ },
+ expData: []byte{'a'},
+ expKind: sym.SDATA,
+ },
+ {
+ which: "AddUintXX",
+ addDataFunc: func(l *Loader, s Sym, _ Sym) Sym {
+ sb := l.MakeSymbolUpdater(s)
+ sb.AddUintXX(arch, 25185, 2)
+ return s
+ },
+ expData: []byte{'a', 'b'},
+ expKind: sym.SDATA,
+ },
+ {
+ which: "SetUint8",
+ addDataFunc: func(l *Loader, s Sym, _ Sym) Sym {
+ sb := l.MakeSymbolUpdater(s)
+ sb.AddUint8('a')
+ sb.AddUint8('b')
+ sb.SetUint8(arch, 1, 'c')
+ return s
+ },
+ expData: []byte{'a', 'c'},
+ expKind: sym.SDATA,
+ },
+ {
+ which: "AddString",
+ addDataFunc: func(l *Loader, s Sym, _ Sym) Sym {
+ sb := l.MakeSymbolUpdater(s)
+ sb.Addstring("hello")
+ return s
+ },
+ expData: []byte{'h', 'e', 'l', 'l', 'o', 0},
+ expKind: sym.SNOPTRDATA,
+ },
+ {
+ which: "AddAddrPlus",
+ addDataFunc: func(l *Loader, s Sym, s2 Sym) Sym {
+ sb := l.MakeSymbolUpdater(s)
+ sb.AddAddrPlus(arch, s2, 3)
+ return s
+ },
+ expData: []byte{0, 0, 0, 0, 0, 0, 0, 0},
+ expKind: sym.SDATA,
+ expRel: []Reloc{mkReloc(ldr, objabi.R_ADDR, 0, 8, 3, 6)},
+ },
+ {
+ which: "AddAddrPlus4",
+ addDataFunc: func(l *Loader, s Sym, s2 Sym) Sym {
+ sb := l.MakeSymbolUpdater(s)
+ sb.AddAddrPlus4(arch, s2, 3)
+ return s
+ },
+ expData: []byte{0, 0, 0, 0},
+ expKind: sym.SDATA,
+ expRel: []Reloc{mkReloc(ldr, objabi.R_ADDR, 0, 4, 3, 7)},
+ },
+ {
+ which: "AddCURelativeAddrPlus",
+ addDataFunc: func(l *Loader, s Sym, s2 Sym) Sym {
+ sb := l.MakeSymbolUpdater(s)
+ sb.AddCURelativeAddrPlus(arch, s2, 7)
+ return s
+ },
+ expData: []byte{0, 0, 0, 0, 0, 0, 0, 0},
+ expKind: sym.SDATA,
+ expRel: []Reloc{mkReloc(ldr, objabi.R_ADDRCUOFF, 0, 8, 7, 8)},
+ },
+ }
+
+ var pmi Sym
+ for k, tp := range testpoints {
+ name := fmt.Sprintf("new%d", k+1)
+ mi := ldr.LookupOrCreateSym(name, 0)
+ if mi == 0 {
+ t.Fatalf("LookupOrCreateSym failed for '" + name + "'")
+ }
+ mi = tp.addDataFunc(ldr, mi, pmi)
+ if ldr.SymType(mi) != tp.expKind {
+ t.Errorf("testing Loader.%s: expected kind %s got %s",
+ tp.which, tp.expKind, ldr.SymType(mi))
+ }
+ if bytes.Compare(ldr.Data(mi), tp.expData) != 0 {
+ t.Errorf("testing Loader.%s: expected data %v got %v",
+ tp.which, tp.expData, ldr.Data(mi))
+ }
+ relocs := ldr.Relocs(mi)
+ if !sameRelocSlice(&relocs, tp.expRel) {
+ t.Fatalf("testing Loader.%s: got relocslice %+v wanted %+v",
+ tp.which, relocs, tp.expRel)
+ }
+ pmi = mi
+ }
+}
+
+func TestOuterSub(t *testing.T) {
+ ldr := mkLoader()
+ dummyOreader := oReader{version: -1, syms: make([]Sym, 100)}
+ or := &dummyOreader
+
+ // Populate loader with some symbols.
+ addDummyObjSym(t, ldr, or, "type.uint8")
+ es1 := ldr.LookupOrCreateSym("outer", 0)
+ ldr.MakeSymbolUpdater(es1).SetSize(101)
+ es2 := ldr.LookupOrCreateSym("sub1", 0)
+ es3 := ldr.LookupOrCreateSym("sub2", 0)
+ es4 := ldr.LookupOrCreateSym("sub3", 0)
+ es5 := ldr.LookupOrCreateSym("sub4", 0)
+ es6 := ldr.LookupOrCreateSym("sub5", 0)
+
+ // Should not have an outer sym initially
+ if ldr.OuterSym(es1) != 0 {
+ t.Errorf("es1 outer sym set ")
+ }
+ if ldr.SubSym(es2) != 0 {
+ t.Errorf("es2 outer sym set ")
+ }
+
+ // Establish first outer/sub relationship
+ ldr.AddInteriorSym(es1, es2)
+ if ldr.OuterSym(es1) != 0 {
+ t.Errorf("ldr.OuterSym(es1) got %d wanted %d", ldr.OuterSym(es1), 0)
+ }
+ if ldr.OuterSym(es2) != es1 {
+ t.Errorf("ldr.OuterSym(es2) got %d wanted %d", ldr.OuterSym(es2), es1)
+ }
+ if ldr.SubSym(es1) != es2 {
+ t.Errorf("ldr.SubSym(es1) got %d wanted %d", ldr.SubSym(es1), es2)
+ }
+ if ldr.SubSym(es2) != 0 {
+ t.Errorf("ldr.SubSym(es2) got %d wanted %d", ldr.SubSym(es2), 0)
+ }
+
+ // Establish second outer/sub relationship
+ ldr.AddInteriorSym(es1, es3)
+ if ldr.OuterSym(es1) != 0 {
+ t.Errorf("ldr.OuterSym(es1) got %d wanted %d", ldr.OuterSym(es1), 0)
+ }
+ if ldr.OuterSym(es2) != es1 {
+ t.Errorf("ldr.OuterSym(es2) got %d wanted %d", ldr.OuterSym(es2), es1)
+ }
+ if ldr.OuterSym(es3) != es1 {
+ t.Errorf("ldr.OuterSym(es3) got %d wanted %d", ldr.OuterSym(es3), es1)
+ }
+ if ldr.SubSym(es1) != es3 {
+ t.Errorf("ldr.SubSym(es1) got %d wanted %d", ldr.SubSym(es1), es3)
+ }
+ if ldr.SubSym(es3) != es2 {
+ t.Errorf("ldr.SubSym(es3) got %d wanted %d", ldr.SubSym(es3), es2)
+ }
+
+ // Some more
+ ldr.AddInteriorSym(es1, es4)
+ ldr.AddInteriorSym(es1, es5)
+ ldr.AddInteriorSym(es1, es6)
+
+ // Set values.
+ ldr.SetSymValue(es2, 7)
+ ldr.SetSymValue(es3, 1)
+ ldr.SetSymValue(es4, 13)
+ ldr.SetSymValue(es5, 101)
+ ldr.SetSymValue(es6, 3)
+
+ // Sort
+ news := ldr.SortSub(es1)
+ if news != es3 {
+ t.Errorf("ldr.SortSub leader got %d wanted %d", news, es3)
+ }
+ pv := int64(-1)
+ count := 0
+ for ss := ldr.SubSym(es1); ss != 0; ss = ldr.SubSym(ss) {
+ v := ldr.SymValue(ss)
+ if v <= pv {
+ t.Errorf("ldr.SortSub sortfail at %d: val %d >= prev val %d",
+ ss, v, pv)
+ }
+ pv = v
+ count++
+ }
+ if count != 5 {
+ t.Errorf("expected %d in sub list got %d", 5, count)
+ }
+}
diff --git a/src/cmd/link/internal/loader/symbolbuilder.go b/src/cmd/link/internal/loader/symbolbuilder.go
new file mode 100644
index 0000000..5d37da8
--- /dev/null
+++ b/src/cmd/link/internal/loader/symbolbuilder.go
@@ -0,0 +1,440 @@
+// 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/internal/goobj"
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/sym"
+ "sort"
+)
+
+// SymbolBuilder is a helper designed to help with the construction
+// of new symbol contents.
+type SymbolBuilder struct {
+ *extSymPayload // points to payload being updated
+ symIdx Sym // index of symbol being updated/constructed
+ l *Loader // loader
+}
+
+// MakeSymbolBuilder creates a symbol builder for use in constructing
+// an entirely new symbol.
+func (l *Loader) MakeSymbolBuilder(name string) *SymbolBuilder {
+ // for now assume that any new sym is intended to be static
+ symIdx := l.CreateStaticSym(name)
+ sb := &SymbolBuilder{l: l, symIdx: symIdx}
+ sb.extSymPayload = l.getPayload(symIdx)
+ return sb
+}
+
+// MakeSymbolUpdater creates a symbol builder helper for an existing
+// symbol 'symIdx'. If 'symIdx' is not an external symbol, then create
+// a clone of it (copy name, properties, etc) fix things up so that
+// the lookup tables and caches point to the new version, not the old
+// version.
+func (l *Loader) MakeSymbolUpdater(symIdx Sym) *SymbolBuilder {
+ if symIdx == 0 {
+ panic("can't update the null symbol")
+ }
+ if !l.IsExternal(symIdx) {
+ // Create a clone with the same name/version/kind etc.
+ l.cloneToExternal(symIdx)
+ }
+
+ // Construct updater and return.
+ sb := &SymbolBuilder{l: l, symIdx: symIdx}
+ sb.extSymPayload = l.getPayload(symIdx)
+ return sb
+}
+
+// CreateSymForUpdate creates a symbol with given name and version,
+// returns a CreateSymForUpdate for update. If the symbol already
+// exists, it will update in-place.
+func (l *Loader) CreateSymForUpdate(name string, version int) *SymbolBuilder {
+ s := l.LookupOrCreateSym(name, version)
+ l.SetAttrReachable(s, true)
+ return l.MakeSymbolUpdater(s)
+}
+
+// Getters for properties of the symbol we're working on.
+
+func (sb *SymbolBuilder) Sym() Sym { return sb.symIdx }
+func (sb *SymbolBuilder) Name() string { return sb.name }
+func (sb *SymbolBuilder) Version() int { return sb.ver }
+func (sb *SymbolBuilder) Type() sym.SymKind { return sb.kind }
+func (sb *SymbolBuilder) Size() int64 { return sb.size }
+func (sb *SymbolBuilder) Data() []byte { return sb.data }
+func (sb *SymbolBuilder) Value() int64 { return sb.l.SymValue(sb.symIdx) }
+func (sb *SymbolBuilder) Align() int32 { return sb.l.SymAlign(sb.symIdx) }
+func (sb *SymbolBuilder) Localentry() uint8 { return sb.l.SymLocalentry(sb.symIdx) }
+func (sb *SymbolBuilder) OnList() bool { return sb.l.AttrOnList(sb.symIdx) }
+func (sb *SymbolBuilder) External() bool { return sb.l.AttrExternal(sb.symIdx) }
+func (sb *SymbolBuilder) Extname() string { return sb.l.SymExtname(sb.symIdx) }
+func (sb *SymbolBuilder) CgoExportDynamic() bool { return sb.l.AttrCgoExportDynamic(sb.symIdx) }
+func (sb *SymbolBuilder) Dynimplib() string { return sb.l.SymDynimplib(sb.symIdx) }
+func (sb *SymbolBuilder) Dynimpvers() string { return sb.l.SymDynimpvers(sb.symIdx) }
+func (sb *SymbolBuilder) SubSym() Sym { return sb.l.SubSym(sb.symIdx) }
+func (sb *SymbolBuilder) GoType() Sym { return sb.l.SymGoType(sb.symIdx) }
+func (sb *SymbolBuilder) VisibilityHidden() bool { return sb.l.AttrVisibilityHidden(sb.symIdx) }
+func (sb *SymbolBuilder) Sect() *sym.Section { return sb.l.SymSect(sb.symIdx) }
+
+// Setters for symbol properties.
+
+func (sb *SymbolBuilder) SetType(kind sym.SymKind) { sb.kind = kind }
+func (sb *SymbolBuilder) SetSize(size int64) { sb.size = size }
+func (sb *SymbolBuilder) SetData(data []byte) { sb.data = data }
+func (sb *SymbolBuilder) SetOnList(v bool) { sb.l.SetAttrOnList(sb.symIdx, v) }
+func (sb *SymbolBuilder) SetExternal(v bool) { sb.l.SetAttrExternal(sb.symIdx, v) }
+func (sb *SymbolBuilder) SetValue(v int64) { sb.l.SetSymValue(sb.symIdx, v) }
+func (sb *SymbolBuilder) SetAlign(align int32) { sb.l.SetSymAlign(sb.symIdx, align) }
+func (sb *SymbolBuilder) SetLocalentry(value uint8) { sb.l.SetSymLocalentry(sb.symIdx, value) }
+func (sb *SymbolBuilder) SetExtname(value string) { sb.l.SetSymExtname(sb.symIdx, value) }
+func (sb *SymbolBuilder) SetDynimplib(value string) { sb.l.SetSymDynimplib(sb.symIdx, value) }
+func (sb *SymbolBuilder) SetDynimpvers(value string) { sb.l.SetSymDynimpvers(sb.symIdx, value) }
+func (sb *SymbolBuilder) SetPlt(value int32) { sb.l.SetPlt(sb.symIdx, value) }
+func (sb *SymbolBuilder) SetGot(value int32) { sb.l.SetGot(sb.symIdx, value) }
+func (sb *SymbolBuilder) SetSpecial(value bool) { sb.l.SetAttrSpecial(sb.symIdx, value) }
+func (sb *SymbolBuilder) SetLocal(value bool) { sb.l.SetAttrLocal(sb.symIdx, value) }
+func (sb *SymbolBuilder) SetVisibilityHidden(value bool) {
+ sb.l.SetAttrVisibilityHidden(sb.symIdx, value)
+}
+func (sb *SymbolBuilder) SetNotInSymbolTable(value bool) {
+ sb.l.SetAttrNotInSymbolTable(sb.symIdx, value)
+}
+func (sb *SymbolBuilder) SetSect(sect *sym.Section) { sb.l.SetSymSect(sb.symIdx, sect) }
+
+func (sb *SymbolBuilder) AddBytes(data []byte) {
+ if sb.kind == 0 {
+ sb.kind = sym.SDATA
+ }
+ sb.data = append(sb.data, data...)
+ sb.size = int64(len(sb.data))
+}
+
+func (sb *SymbolBuilder) Relocs() Relocs {
+ return sb.l.Relocs(sb.symIdx)
+}
+
+// ResetRelocs removes all relocations on this symbol.
+func (sb *SymbolBuilder) ResetRelocs() {
+ sb.relocs = sb.relocs[:0]
+ sb.reltypes = sb.reltypes[:0]
+}
+
+// SetRelocType sets the type of the 'i'-th relocation on this sym to 't'
+func (sb *SymbolBuilder) SetRelocType(i int, t objabi.RelocType) {
+ sb.relocs[i].SetType(0)
+ sb.reltypes[i] = t
+}
+
+// SetRelocSym sets the target sym of the 'i'-th relocation on this sym to 's'
+func (sb *SymbolBuilder) SetRelocSym(i int, tgt Sym) {
+ sb.relocs[i].SetSym(goobj.SymRef{PkgIdx: 0, SymIdx: uint32(tgt)})
+}
+
+// SetRelocAdd sets the addend of the 'i'-th relocation on this sym to 'a'
+func (sb *SymbolBuilder) SetRelocAdd(i int, a int64) {
+ sb.relocs[i].SetAdd(a)
+}
+
+// Add n relocations, return a handle to the relocations.
+func (sb *SymbolBuilder) AddRelocs(n int) Relocs {
+ sb.relocs = append(sb.relocs, make([]goobj.Reloc, n)...)
+ sb.reltypes = append(sb.reltypes, make([]objabi.RelocType, n)...)
+ return sb.l.Relocs(sb.symIdx)
+}
+
+// Add a relocation with given type, return its handle and index
+// (to set other fields).
+func (sb *SymbolBuilder) AddRel(typ objabi.RelocType) (Reloc, int) {
+ j := len(sb.relocs)
+ sb.relocs = append(sb.relocs, goobj.Reloc{})
+ sb.reltypes = append(sb.reltypes, typ)
+ relocs := sb.Relocs()
+ return relocs.At(j), j
+}
+
+// Sort relocations by offset.
+func (sb *SymbolBuilder) SortRelocs() {
+ sort.Sort((*relocsByOff)(sb.extSymPayload))
+}
+
+// Implement sort.Interface
+type relocsByOff extSymPayload
+
+func (p *relocsByOff) Len() int { return len(p.relocs) }
+func (p *relocsByOff) Less(i, j int) bool { return p.relocs[i].Off() < p.relocs[j].Off() }
+func (p *relocsByOff) Swap(i, j int) {
+ p.relocs[i], p.relocs[j] = p.relocs[j], p.relocs[i]
+ p.reltypes[i], p.reltypes[j] = p.reltypes[j], p.reltypes[i]
+}
+
+func (sb *SymbolBuilder) Reachable() bool {
+ return sb.l.AttrReachable(sb.symIdx)
+}
+
+func (sb *SymbolBuilder) SetReachable(v bool) {
+ sb.l.SetAttrReachable(sb.symIdx, v)
+}
+
+func (sb *SymbolBuilder) setReachable() {
+ sb.SetReachable(true)
+}
+
+func (sb *SymbolBuilder) ReadOnly() bool {
+ return sb.l.AttrReadOnly(sb.symIdx)
+}
+
+func (sb *SymbolBuilder) SetReadOnly(v bool) {
+ sb.l.SetAttrReadOnly(sb.symIdx, v)
+}
+
+func (sb *SymbolBuilder) DuplicateOK() bool {
+ return sb.l.AttrDuplicateOK(sb.symIdx)
+}
+
+func (sb *SymbolBuilder) SetDuplicateOK(v bool) {
+ sb.l.SetAttrDuplicateOK(sb.symIdx, v)
+}
+
+func (sb *SymbolBuilder) Outer() Sym {
+ return sb.l.OuterSym(sb.symIdx)
+}
+
+func (sb *SymbolBuilder) Sub() Sym {
+ return sb.l.SubSym(sb.symIdx)
+}
+
+func (sb *SymbolBuilder) SortSub() {
+ sb.l.SortSub(sb.symIdx)
+}
+
+func (sb *SymbolBuilder) AddInteriorSym(sub Sym) {
+ sb.l.AddInteriorSym(sb.symIdx, sub)
+}
+
+func (sb *SymbolBuilder) AddUint8(v uint8) int64 {
+ off := sb.size
+ if sb.kind == 0 {
+ sb.kind = sym.SDATA
+ }
+ sb.size++
+ sb.data = append(sb.data, v)
+ return off
+}
+
+func (sb *SymbolBuilder) AddUintXX(arch *sys.Arch, v uint64, wid int) int64 {
+ off := sb.size
+ sb.setUintXX(arch, off, v, int64(wid))
+ return off
+}
+
+func (sb *SymbolBuilder) setUintXX(arch *sys.Arch, off int64, v uint64, wid int64) int64 {
+ if sb.kind == 0 {
+ sb.kind = sym.SDATA
+ }
+ if sb.size < off+wid {
+ sb.size = off + wid
+ sb.Grow(sb.size)
+ }
+
+ switch wid {
+ case 1:
+ sb.data[off] = uint8(v)
+ case 2:
+ arch.ByteOrder.PutUint16(sb.data[off:], uint16(v))
+ case 4:
+ arch.ByteOrder.PutUint32(sb.data[off:], uint32(v))
+ case 8:
+ arch.ByteOrder.PutUint64(sb.data[off:], v)
+ }
+
+ return off + wid
+}
+
+func (sb *SymbolBuilder) AddUint16(arch *sys.Arch, v uint16) int64 {
+ return sb.AddUintXX(arch, uint64(v), 2)
+}
+
+func (sb *SymbolBuilder) AddUint32(arch *sys.Arch, v uint32) int64 {
+ return sb.AddUintXX(arch, uint64(v), 4)
+}
+
+func (sb *SymbolBuilder) AddUint64(arch *sys.Arch, v uint64) int64 {
+ return sb.AddUintXX(arch, v, 8)
+}
+
+func (sb *SymbolBuilder) AddUint(arch *sys.Arch, v uint64) int64 {
+ return sb.AddUintXX(arch, v, arch.PtrSize)
+}
+
+func (sb *SymbolBuilder) SetUint8(arch *sys.Arch, r int64, v uint8) int64 {
+ return sb.setUintXX(arch, r, uint64(v), 1)
+}
+
+func (sb *SymbolBuilder) SetUint16(arch *sys.Arch, r int64, v uint16) int64 {
+ return sb.setUintXX(arch, r, uint64(v), 2)
+}
+
+func (sb *SymbolBuilder) SetUint32(arch *sys.Arch, r int64, v uint32) int64 {
+ return sb.setUintXX(arch, r, uint64(v), 4)
+}
+
+func (sb *SymbolBuilder) SetUint(arch *sys.Arch, r int64, v uint64) int64 {
+ return sb.setUintXX(arch, r, v, int64(arch.PtrSize))
+}
+
+func (sb *SymbolBuilder) SetUintptr(arch *sys.Arch, r int64, v uintptr) int64 {
+ return sb.setUintXX(arch, r, uint64(v), int64(arch.PtrSize))
+}
+
+func (sb *SymbolBuilder) SetAddrPlus(arch *sys.Arch, off int64, tgt Sym, add int64) int64 {
+ if sb.Type() == 0 {
+ sb.SetType(sym.SDATA)
+ }
+ if off+int64(arch.PtrSize) > sb.size {
+ sb.size = off + int64(arch.PtrSize)
+ sb.Grow(sb.size)
+ }
+ r, _ := sb.AddRel(objabi.R_ADDR)
+ r.SetSym(tgt)
+ r.SetOff(int32(off))
+ r.SetSiz(uint8(arch.PtrSize))
+ r.SetAdd(add)
+ return off + int64(r.Siz())
+}
+
+func (sb *SymbolBuilder) SetAddr(arch *sys.Arch, off int64, tgt Sym) int64 {
+ return sb.SetAddrPlus(arch, off, tgt, 0)
+}
+
+func (sb *SymbolBuilder) AddStringAt(off int64, str string) int64 {
+ strLen := int64(len(str))
+ if off+strLen+1 > int64(len(sb.data)) {
+ panic("attempt to write past end of buffer")
+ }
+ copy(sb.data[off:off+strLen], str)
+ sb.data[off+strLen] = 0
+ return off + strLen + 1
+}
+
+func (sb *SymbolBuilder) Addstring(str string) int64 {
+ if sb.kind == 0 {
+ sb.kind = sym.SNOPTRDATA
+ }
+ r := sb.size
+ if sb.name == ".shstrtab" {
+ // FIXME: find a better mechanism for this
+ sb.l.elfsetstring(str, int(r))
+ }
+ sb.data = append(sb.data, str...)
+ sb.data = append(sb.data, 0)
+ sb.size = int64(len(sb.data))
+ return r
+}
+
+func (sb *SymbolBuilder) SetBytesAt(off int64, b []byte) int64 {
+ datLen := int64(len(b))
+ if off+datLen > int64(len(sb.data)) {
+ panic("attempt to write past end of buffer")
+ }
+ copy(sb.data[off:off+datLen], b)
+ return off + datLen
+}
+
+func (sb *SymbolBuilder) addSymRef(tgt Sym, add int64, typ objabi.RelocType, rsize int) int64 {
+ if sb.kind == 0 {
+ sb.kind = sym.SDATA
+ }
+ i := sb.size
+
+ sb.size += int64(rsize)
+ sb.Grow(sb.size)
+
+ r, _ := sb.AddRel(typ)
+ r.SetSym(tgt)
+ r.SetOff(int32(i))
+ r.SetSiz(uint8(rsize))
+ r.SetAdd(add)
+
+ return i + int64(rsize)
+}
+
+// Add a symbol reference (relocation) with given type, addend, and size
+// (the most generic form).
+func (sb *SymbolBuilder) AddSymRef(arch *sys.Arch, tgt Sym, add int64, typ objabi.RelocType, rsize int) int64 {
+ return sb.addSymRef(tgt, add, typ, rsize)
+}
+
+func (sb *SymbolBuilder) AddAddrPlus(arch *sys.Arch, tgt Sym, add int64) int64 {
+ return sb.addSymRef(tgt, add, objabi.R_ADDR, arch.PtrSize)
+}
+
+func (sb *SymbolBuilder) AddAddrPlus4(arch *sys.Arch, tgt Sym, add int64) int64 {
+ return sb.addSymRef(tgt, add, objabi.R_ADDR, 4)
+}
+
+func (sb *SymbolBuilder) AddAddr(arch *sys.Arch, tgt Sym) int64 {
+ return sb.AddAddrPlus(arch, tgt, 0)
+}
+
+func (sb *SymbolBuilder) AddPCRelPlus(arch *sys.Arch, tgt Sym, add int64) int64 {
+ return sb.addSymRef(tgt, add, objabi.R_PCREL, 4)
+}
+
+func (sb *SymbolBuilder) AddCURelativeAddrPlus(arch *sys.Arch, tgt Sym, add int64) int64 {
+ return sb.addSymRef(tgt, add, objabi.R_ADDRCUOFF, arch.PtrSize)
+}
+
+func (sb *SymbolBuilder) AddSize(arch *sys.Arch, tgt Sym) int64 {
+ return sb.addSymRef(tgt, 0, objabi.R_SIZE, arch.PtrSize)
+}
+
+// GenAddAddrPlusFunc returns a function to be called when capturing
+// a function symbol's address. In later stages of the link (when
+// address assignment is done) when doing internal linking and
+// targeting an executable, we can just emit the address of a function
+// directly instead of generating a relocation. Clients can call
+// this function (setting 'internalExec' based on build mode and target)
+// and then invoke the returned function in roughly the same way that
+// loader.*SymbolBuilder.AddAddrPlus would be used.
+func GenAddAddrPlusFunc(internalExec bool) func(s *SymbolBuilder, arch *sys.Arch, tgt Sym, add int64) int64 {
+ if internalExec {
+ return func(s *SymbolBuilder, arch *sys.Arch, tgt Sym, add int64) int64 {
+ if v := s.l.SymValue(tgt); v != 0 {
+ return s.AddUint(arch, uint64(v+add))
+ }
+ return s.AddAddrPlus(arch, tgt, add)
+ }
+ } else {
+ return (*SymbolBuilder).AddAddrPlus
+ }
+}
+
+func (sb *SymbolBuilder) MakeWritable() {
+ if sb.ReadOnly() {
+ sb.data = append([]byte(nil), sb.data...)
+ sb.l.SetAttrReadOnly(sb.symIdx, false)
+ }
+}
+
+func (sb *SymbolBuilder) AddUleb(v uint64) {
+ if v < 128 { // common case: 1 byte
+ sb.AddUint8(uint8(v))
+ return
+ }
+ for {
+ c := uint8(v & 0x7f)
+ v >>= 7
+ if v != 0 {
+ c |= 0x80
+ }
+ sb.AddUint8(c)
+ if c&0x80 == 0 {
+ break
+ }
+ }
+}