Newer
Older
// 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
Cherry Zhang
committed
"bytes"
Cherry Zhang
committed
"cmd/internal/dwarf"
"cmd/internal/goobj2"
"cmd/internal/obj"
"cmd/internal/objabi"
"cmd/internal/sys"
"cmd/link/internal/sym"
"fmt"
"log"
"os"
"sort"
"strconv"
"strings"
)
var _ = fmt.Print
Than McIntosh
committed
// 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 {
Count int // number of relocs
li int // local index of symbol whose relocs we're examining
r *oReader // object reader for containing package
l *Loader // loader
Than McIntosh
committed
extIdx Sym // index of external symbol we're examining or 0
}
// Reloc contains the payload for a specific relocation.
// TODO: replace this with sym.Reloc, once we change the
// relocation target from "*sym.Symbol" to "loader.Sym" in sym.Reloc.
type Reloc struct {
Off int32 // offset to rewrite
Size uint8 // number of bytes to rewrite: 0, 1, 2, or 4
Type objabi.RelocType // the relocation type
Add int64 // addend
Sym Sym // global index of symbol the reloc addresses
}
Cherry Zhang
committed
// oReader is a wrapper type of obj.Reader, along with some
// extra information.
// TODO: rename to objReader once the old one is gone?
type oReader struct {
*goobj2.Reader
unit *sym.CompilationUnit
version int // version of static symbol
flags uint32 // read from object file
Cherry Zhang
committed
pkgprefix string
rcache []Sym // cache mapping local PkgNone symbol to resolved Sym
Cherry Zhang
committed
}
Cherry Zhang
committed
r *oReader
Than McIntosh
committed
i Sym // start index
Than McIntosh
committed
e Sym // end 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
}
Than McIntosh
committed
// return current length of bitmap in bits.
func (bm bitmap) len() int {
return len(bm) * 32
}
func makeBitmap(n int) bitmap {
return make(bitmap, (n+31)/32)
}
Than McIntosh
committed
// 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)...)
Than McIntosh
committed
}
return b
}
// A Loader loads new object files and resolves indexed symbol references.
Than McIntosh
committed
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
//
// Notes on the layout of global symbol index space:
//
// - Go object files are read before host object files; each Go object
// read allocates a new chunk of global index space of size P + NP,
// where P is the number of package defined symbols in the object and
// NP is the number of non-package defined symbols.
//
// - In loader.LoadRefs(), the loader makes a sweep through all of the
// non-package references in each object file and allocates sym indices
// for any symbols that have not yet been defined (start of this space
// is marked by loader.extStart).
//
// - Host object file loading happens; the host object loader does a
// name/version lookup for each symbol it finds; this can wind up
// extending the external symbol index space range. The host object
// loader currently stores symbol payloads in sym.Symbol objects,
// which get handed off to the loader.
//
// - A given external symbol (Sym) either has a sym.Symbol acting as
// its backing store (this will continue to be the case until we
// finish rewriting the host object loader to work entirely with
// loader.Sym) or it has a "payload" backing store (represented by
// extSymPayload). Newly created external symbols (created by
// a call to AddExtSym or equivalent) start out in the "has payload"
// state, and continue until installSym is called for the sym
// index in question.
//
// - At some point (when the wayfront is pushed through all of the
// linker), all external symbols will be payload-based, and we can
// get rid of the loader.Syms array.
//
start map[*oReader]Sym // map from object file to its start index
objs []objIdx // sorted by start index (i.e. objIdx.i)
max Sym // current max index
extStart Sym // from this index on, the symbols are externally defined
builtinSyms []Sym // global index of builtin symbols
Than McIntosh
committed
ocache int // index (into 'objs') of most recent lookup
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
overwrite map[Sym]Sym // overwrite[i]=j if symbol j overwrites symbol i
Than McIntosh
committed
payloads []extSymPayload // contents of linker-materialized external syms
values []int64 // symbol values, indexed by global sym index
Than McIntosh
committed
itablink map[Sym]struct{} // itablink[j] defined if j is go.itablink.*
Cherry Zhang
committed
objByPkg map[string]*oReader // map package path to its Go object reader
Syms []*sym.Symbol // indexed symbols. XXX we still make sym.Symbol for now.
symBatch []sym.Symbol // batch of symbols.
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
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
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
// 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
align map[Sym]int32 // stores alignment for symbols
// 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
relocBatch []sym.Reloc // for bulk allocation of relocations
flags uint32
strictDupMsgs int // number of strict-dup warning/errors, when FlagStrictDups is enabled
Than McIntosh
committed
// extSymPayload holds the payload (data + relocations) for linker-synthesized
// external symbols (note that symbol value is stored in a separate slice).
Than McIntosh
committed
type extSymPayload struct {
name string // TODO: would this be better as offset into str table?
size int64
ver int
kind sym.SymKind
relocs []Reloc
data []byte
}
const (
// Loader.flags
FlagStrictDups = 1 << iota
)
func NewLoader(flags uint32) *Loader {
nbuiltin := goobj2.NBuiltin()
start: make(map[*oReader]Sym),
Than McIntosh
committed
objs: []objIdx{{nil, 0, 0}},
symsByName: [2]map[string]Sym{make(map[string]Sym), make(map[string]Sym)},
objByPkg: make(map[string]*oReader),
outer: make(map[Sym]Sym),
sub: make(map[Sym]Sym),
align: make(map[Sym]int32),
overwrite: make(map[Sym]Sym),
itablink: make(map[Sym]struct{}),
extStaticSyms: make(map[nameVer]Sym),
builtinSyms: make([]Sym, nbuiltin),
flags: flags,
}
}
// Return the start index in the global index space for a given object file.
func (l *Loader) startIndex(r *oReader) Sym {
return l.start[r]
}
// 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
Cherry Zhang
committed
if _, ok := l.objByPkg[pkg]; !ok {
l.objByPkg[pkg] = r
}
n := r.NSym() + r.NNonpkgdef()
i := l.max + 1
l.start[r] = i
Than McIntosh
committed
l.objs = append(l.objs, objIdx{r, i, i + Sym(n) - 1})
Than McIntosh
committed
l.max += Sym(n)
l.growValues(int(l.max))
return i
}
// Add a symbol with a given index, return if it is added.
func (l *Loader) AddSym(name string, ver int, i Sym, r *oReader, dupok bool, typ sym.SymKind) bool {
if l.extStart != 0 {
panic("AddSym called after AddExtSym is called")
}
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.
return true
}
if oldi, ok := l.symsByName[ver][name]; ok {
if l.flags&FlagStrictDups != 0 {
l.checkdup(name, i, r, oldi)
}
oldr, li := l.toLocal(oldi)
oldsym := goobj2.Sym{}
oldsym.Read(oldr.Reader, oldr.SymOff(li))
if oldsym.Dupok() {
return false
}
overwrite := r.DataSize(int(i-l.startIndex(r))) != 0
if overwrite {
// new symbol overwrites old symbol.
oldtyp := sym.AbiSymKindToSymKind[objabi.SymKind(oldsym.Type)]
if !oldtyp.IsData() && r.DataSize(li) == 0 {
log.Fatalf("duplicated definition of symbol " + name)
}
l.overwrite[oldi] = i
} else {
// old symbol overwrites new symbol.
if typ != sym.SDATA && typ != sym.SNOPTRDATA && typ != sym.SBSS && typ != sym.SNOPTRBSS { // only allow overwriting data symbol
log.Fatalf("duplicated definition of symbol " + name)
}
l.overwrite[i] = oldi
return false
}
}
l.symsByName[ver][name] = i
return true
}
Than McIntosh
committed
// newExtSym creates a new external sym with the specified
// name/version.
func (l *Loader) newExtSym(name string, ver int) Sym {
l.max++
i := l.max
if l.extStart == 0 {
l.extStart = i
}
l.growSyms(int(i))
pi := i - l.extStart
l.payloads[pi].name = name
l.payloads[pi].ver = ver
return i
}
// Add an external symbol (without index). Return the index of newly added
// symbol, or 0 if not added.
Than McIntosh
committed
func (l *Loader) AddExtSym(name string, ver int) Sym {
Than McIntosh
committed
i := l.Lookup(name, ver)
if i != 0 {
return 0
}
i = l.newExtSym(name, ver)
static := ver >= sym.SymVerStatic || ver < 0
Than McIntosh
committed
l.extStaticSyms[nameVer{name, ver}] = i
Than McIntosh
committed
l.symsByName[ver][name] = i
Than McIntosh
committed
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
}
func (l *Loader) IsExternal(i Sym) bool {
return l.extStart != 0 && i >= l.extStart
}
Than McIntosh
committed
// getPayload returns a pointer to the extSymPayload struct for an
// external symbol if the symbol has a payload, or nil if the
// data for the sym is being stored in a sym.Symbol. Will panic if
// the symbol in question is bogus (zero or not an external sym).
func (l *Loader) getPayload(i Sym) *extSymPayload {
if l.extStart == 0 || i < l.extStart {
panic(fmt.Sprintf("bogus symbol index %d in getPayload", i))
}
if l.Syms[i] != nil {
return nil
}
pi := i - l.extStart
return &l.payloads[pi]
}
// Ensure Syms slice has enough space, as well as growing the
// 'payloads' slice.
func (l *Loader) growSyms(i int) {
n := len(l.Syms)
if n > i {
return
}
l.Syms = append(l.Syms, make([]*sym.Symbol, i+1-n)...)
Than McIntosh
committed
l.payloads = append(l.payloads, make([]extSymPayload, i+1-n)...)
l.growValues(int(i) + 1)
l.growAttrBitmaps(int(i) + 1)
}
// Convert a local index to a global index.
func (l *Loader) toGlobal(r *oReader, i int) Sym {
g := l.startIndex(r) + Sym(i)
if ov, ok := l.overwrite[g]; ok {
return ov
}
return g
Cherry Zhang
committed
// Convert a global index to a local index.
func (l *Loader) toLocal(i Sym) (*oReader, int) {
if ov, ok := l.overwrite[i]; ok {
i = ov
}
if l.IsExternal(i) {
return nil, int(i - l.extStart)
Than McIntosh
committed
oc := l.ocache
if oc != 0 && i >= l.objs[oc].i && i <= l.objs[oc].e {
return l.objs[oc].r, int(i - l.objs[oc].i)
}
// Search for the local object holding index i.
// Below k is the first one that has its start index > i,
// so k-1 is the one we want.
k := sort.Search(len(l.objs), func(k int) bool {
return l.objs[k].i > i
})
Than McIntosh
committed
l.ocache = k - 1
return l.objs[k-1].r, int(i - l.objs[k-1].i)
// rcacheGet checks for a valid entry for 's' in the readers cache,
// where 's' is a local PkgIdxNone ref or def, or zero if
// the cache is empty or doesn't contain a value for 's'.
func (or *oReader) rcacheGet(symIdx uint32) Sym {
if len(or.rcache) > 0 {
return or.rcache[symIdx]
}
return 0
}
// rcacheSet installs a new entry in the oReader's PkgNone
// resolver cache for the specified PkgIdxNone ref or def,
// allocating a new cache if needed.
func (or *oReader) rcacheSet(symIdx uint32, gsym Sym) {
if len(or.rcache) == 0 {
or.rcache = make([]Sym, or.NNonpkgdef()+or.NNonpkgref())
}
or.rcache[symIdx] = gsym
}
Cherry Zhang
committed
// Resolve a local symbol reference. Return global index.
func (l *Loader) resolve(r *oReader, s goobj2.SymRef) Sym {
Cherry Zhang
committed
var rr *oReader
switch p := s.PkgIdx; p {
case goobj2.PkgIdxInvalid:
if s.SymIdx != 0 {
panic("bad sym ref")
}
return 0
case goobj2.PkgIdxNone:
// Check for cached version first
if cached := r.rcacheGet(s.SymIdx); cached != 0 {
return cached
}
Cherry Zhang
committed
// Resolve by name
i := int(s.SymIdx) + r.NSym()
osym := goobj2.Sym{}
osym.Read(r.Reader, r.SymOff(i))
name := strings.Replace(osym.Name, "\"\".", r.pkgprefix, -1)
v := abiToVer(osym.ABI, r.version)
gsym := l.Lookup(name, v)
// Add to cache, then return.
r.rcacheSet(s.SymIdx, gsym)
return gsym
Cherry Zhang
committed
case goobj2.PkgIdxBuiltin:
return l.builtinSyms[s.SymIdx]
Cherry Zhang
committed
case goobj2.PkgIdxSelf:
rr = r
default:
pkg := r.Pkg(int(p))
var ok bool
rr, ok = l.objByPkg[pkg]
if !ok {
log.Fatalf("reference of nonexisted package %s, from %v", pkg, r.unit.Lib)
}
Cherry Zhang
committed
}
return l.toGlobal(rr, int(s.SymIdx))
Cherry Zhang
committed
}
// 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.
Than McIntosh
committed
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]
// Returns whether i is a dup of another symbol, and i is not
// "primary", i.e. Lookup i by name will not return i.
func (l *Loader) IsDup(i Sym) bool {
if _, ok := l.overwrite[i]; ok {
return true
}
if l.IsExternal(i) {
return false
}
r, li := l.toLocal(i)
osym := goobj2.Sym{}
osym.Read(r.Reader, r.SymOff(li))
if !osym.Dupok() {
return false
}
if osym.Name == "" {
return false // Unnamed aux symbol cannot be dup.
}
if osym.ABI == goobj2.SymABIstatic {
return false // Static symbol cannot be dup.
}
name := strings.Replace(osym.Name, "\"\".", r.pkgprefix, -1)
ver := abiToVer(osym.ABI, r.version)
return l.symsByName[ver][name] != i
}
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
// Check that duplicate symbols have same contents.
func (l *Loader) checkdup(name string, i Sym, r *oReader, dup Sym) {
li := int(i - l.startIndex(r))
p := r.Data(li)
if strings.HasPrefix(name, "go.info.") {
p, _ = patchDWARFName1(p, r)
}
rdup, ldup := l.toLocal(dup)
pdup := rdup.Data(ldup)
if strings.HasPrefix(name, "go.info.") {
pdup, _ = patchDWARFName1(pdup, rdup)
}
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, whitelist 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.
whitelist := strings.HasPrefix(name, "go.info.go.interface") ||
strings.HasPrefix(name, "go.info.go.builtin") ||
strings.HasPrefix(name, "go.debuglines")
if !whitelist {
l.strictDupMsgs++
}
}
func (l *Loader) NStrictDupMsgs() int { return l.strictDupMsgs }
// Number of total symbols.
func (l *Loader) NSym() int {
return int(l.max + 1)
}
// Number of defined Go symbols.
func (l *Loader) NDef() int {
return int(l.extStart)
}
// Returns the raw (unpatched) name of the i-th symbol.
func (l *Loader) RawSymName(i Sym) string {
if l.IsExternal(i) {
if s := l.Syms[i]; s != nil {
return s.Name
}
Than McIntosh
committed
pp := l.getPayload(i)
return pp.name
r, li := l.toLocal(i)
osym := goobj2.Sym{}
osym.Read(r.Reader, r.SymOff(li))
return osym.Name
}
// Returns the (patched) name of the i-th symbol.
func (l *Loader) SymName(i Sym) string {
if l.IsExternal(i) {
if s := l.Syms[i]; s != nil {
return s.Name // external name should already be patched?
}
Than McIntosh
committed
pp := l.getPayload(i)
return pp.name
r, li := l.toLocal(i)
osym := goobj2.Sym{}
osym.Read(r.Reader, r.SymOff(li))
return strings.Replace(osym.Name, "\"\".", r.pkgprefix, -1)
}
// Returns the type of the i-th symbol.
func (l *Loader) SymType(i Sym) sym.SymKind {
if l.IsExternal(i) {
if s := l.Syms[i]; s != nil {
return s.Type
}
Than McIntosh
committed
pp := l.getPayload(i)
if pp != nil {
return pp.kind
}
r, li := l.toLocal(i)
osym := goobj2.Sym{}
osym.Read(r.Reader, r.SymOff(li))
return sym.AbiSymKindToSymKind[objabi.SymKind(osym.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 is the only thing matters and it cannot be set by external symbol.
return 0
}
r, li := l.toLocal(i)
osym := goobj2.Sym{}
osym.Read(r.Reader, r.SymOff(li))
return osym.Flag
}
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
// 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)
}
}
// 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 i < l.extStart {
return false
}
return l.attrVisibilityHidden.has(i - l.extStart)
}
// SetAttrVisibilityHidden sets the "hidden visibility" property for a
// symbol (see AttrVisibilityHidden).
func (l *Loader) SetAttrVisibilityHidden(i Sym, v bool) {
if i < l.extStart {
panic("tried to set visibility attr on non-external symbol")
}
if v {
l.attrVisibilityHidden.set(i - l.extStart)
} else {
l.attrVisibilityHidden.unset(i - l.extStart)
}
}
// AttrDuplicateOK returns true for a symbol that can be present in
// multiple object files.
func (l *Loader) AttrDuplicateOK(i Sym) bool {
if i < l.extStart {
// 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)
osym := goobj2.Sym{}
osym.Read(r.Reader, r.SymOff(li))
return osym.Dupok()
}
return l.attrDuplicateOK.has(i - l.extStart)
}
// SetAttrDuplicateOK sets the "duplicate OK" property for an external
// symbol (see AttrDuplicateOK).
func (l *Loader) SetAttrDuplicateOK(i Sym, v bool) {
if i < l.extStart {
panic("tried to set dupok attr on non-external symbol")
}
if v {
l.attrDuplicateOK.set(i - l.extStart)
} else {
l.attrDuplicateOK.unset(i - l.extStart)
}
}
// AttrShared returns true for symbols compiled with the -shared option.
func (l *Loader) AttrShared(i Sym) bool {
if i < l.extStart {
// 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.Flags() & goobj2.ObjFlagShared) != 0
}
return l.attrShared.has(i - l.extStart)
}
// SetAttrShared sets the "shared" property for an external
// symbol (see AttrShared).
func (l *Loader) SetAttrShared(i Sym, v bool) {
if i < l.extStart {
panic("tried to set shared attr on non-external symbol")
}
if v {
l.attrShared.set(i - l.extStart)
} else {
l.attrShared.unset(i - l.extStart)
}
}
// AttrExternal returns true for function symbols loaded from host
// object files.
func (l *Loader) AttrExternal(i Sym) bool {
if i < l.extStart {
return false
}
return l.attrExternal.has(i - l.extStart)
}
// SetAttrExternal sets the "external" property for an host object
// symbol (see AttrExternal).
func (l *Loader) SetAttrExternal(i Sym, v bool) {
if i < l.extStart {
panic("tried to set external attr on non-external symbol")
}
if v {
l.attrExternal.set(i - l.extStart)
} else {
l.attrExternal.unset(i - l.extStart)
}
}
// 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_dynamic" for a symbol
// (see AttrCgoExportStatic).
func (l *Loader) SetAttrCgoExportStatic(i Sym, v bool) {
if v {
l.attrCgoExportStatic[i] = struct{}{}
} else {
delete(l.attrCgoExportStatic, i)
}
}
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
// 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.
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.
return l.OuterSym(i) != 0
}
// AttrContainer 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.
func (l *Loader) AttrContainer(i Sym) bool {
// we don't explicitly store this attribute any more -- return
// a value based on the sub-symbol setting.
return l.SubSym(i) != 0
}
// Note that we don't have SetAttrSubSymbol' or 'SetAttrContainer' methods
// in the loader; clients should just use methods like PrependSub
// to establish these relationships
// Returns whether the i-th symbol has ReflectMethod attribute set.
func (l *Loader) IsReflectMethod(i Sym) bool {
return l.SymAttr(i)&goobj2.SymFlagReflectMethod != 0
}
// Returns whether this is a Go type symbol.
func (l *Loader) IsGoType(i Sym) bool {
return l.SymAttr(i)&goobj2.SymFlagGoType != 0
}
// Returns whether this is a "go.itablink.*" symbol.
func (l *Loader) IsItabLink(i Sym) bool {
if _, ok := l.itablink[i]; ok {
return true
}
return false
}
// 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
}
// Returns the symbol content of the i-th symbol. i is global index.
func (l *Loader) Data(i Sym) []byte {
if l.IsExternal(i) {
if s := l.Syms[i]; s != nil {
return s.P
}
Than McIntosh
committed
pp := l.getPayload(i)
if pp != nil {
return pp.data
}
return nil
}
r, li := l.toLocal(i)
return r.Data(li)
}
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
// SymAlign returns the alignment for a symbol.
func (l *Loader) SymAlign(i Sym) int32 {
// If an alignment has been recorded, return that.
if align, ok := l.align[i]; ok {
return align
}
// TODO: would it make sense to return an arch-specific
// alignment depending on section type? E.g. STEXT => 32,
// SDATA => 1, etc?
return 0
}
// SetSymAlign sets the alignment for a symbol.
func (l *Loader) SetSymAlign(i Sym, align int32) {
// reject bad synbols
if i > l.max || i == 0 {
panic("bad symbol index in SetSymAlign")
}
// Reject nonsense alignments.
// TODO: do we need this?
if align < 0 {
panic("bad alignment value")
}
if align == 0 {
delete(l.align, i)
} else {
// Alignment should be a power of 2.
if bits.OnesCount32(uint32(align)) != 1 {
panic("bad alignment value")
}
l.align[i] = align
}
}
// Returns the number of aux symbols given a global index.
func (l *Loader) NAux(i Sym) int {
if l.IsExternal(i) {
r, li := l.toLocal(i)
return r.NAux(li)
}
// Returns the referred symbol of the j-th aux symbol of the i-th
// symbol.
func (l *Loader) AuxSym(i Sym, j int) Sym {
if l.IsExternal(i) {
return 0
}
r, li := l.toLocal(i)
a := goobj2.Aux{}
a.Read(r.Reader, r.AuxOff(li, j))
return l.resolve(r, a.Sym)
// ReadAuxSyms reads the aux symbol ids for the specified symbol into the
// slice passed as a parameter. If the slice capacity is not large enough, a new
// larger slice will be allocated. Final slice is returned.
func (l *Loader) ReadAuxSyms(symIdx Sym, dst []Sym) []Sym {
return dst[:0]
}
naux := l.NAux(symIdx)
if naux == 0 {
return dst[:0]
}
if cap(dst) < naux {
dst = make([]Sym, naux)
}
dst = dst[:0]
r, li := l.toLocal(symIdx)
for i := 0; i < naux; i++ {
a := goobj2.Aux{}
a.Read(r.Reader, r.AuxOff(li, i))
dst = append(dst, l.resolve(r, a.Sym))
}
return dst
}
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
// PrependSub prepends 'sub' onto the sub list for outer symbol 'outer'.
// Will panic if 'sub' already has an outer sym or sub sym.
// FIXME: should this be instead a method on SymbolBuilder?
func (l *Loader) PrependSub(outer Sym, sub Sym) {
if l.Syms[outer] != nil {
panic("not implemented for sym.Symbol based syms")
}
// NB: this presupposes that an outer sym can't be a sub symbol of
// some other outer-outer sym (I'm assuming this is true, but I
// haven't tested exhaustively).
if l.OuterSym(outer) != 0 {
panic("outer has outer itself")
}
if l.SubSym(sub) != 0 {
panic("sub set for subsym")
}
if l.OuterSym(sub) != 0 {
panic("outer already set for subsym")
}
l.sub[sub] = l.sub[outer]
l.sub[outer] = sub
l.outer[sub] = outer
}
// OuterSym gets the outer symbol for host object loaded symbols.
func (l *Loader) OuterSym(i Sym) Sym {
sym := l.Syms[i]
if sym != nil && sym.Outer != nil {
outer := sym.Outer
return l.Lookup(outer.Name, int(outer.Version))
}
// 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 {
sym := l.Syms[i]
if sym != nil && sym.Sub != nil {
sub := sym.Sub
return l.Lookup(sub.Name, int(sub.Version))
}
// 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]
// Initialize Reachable bitmap and its siblings for running deadcode pass.
func (l *Loader) InitReachable() {
l.growAttrBitmaps(l.NSym() + 1)
Than McIntosh
committed
}
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
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
}
// 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.attrReachable)
}
// These are indexed by external symbol offset (e.g. i - l.extStart)
if l.extStart == 0 {
return
}
extReqLen := reqLen - int(l.extStart)
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)
Than McIntosh
committed
}
// At method returns the j-th reloc for a global symbol.
func (relocs *Relocs) At(j int) Reloc {
Than McIntosh
committed
if s := relocs.l.Syms[relocs.extIdx]; s != nil {
rel := s.R[j]
return Reloc{
Off: rel.Off,
Size: rel.Siz,
Type: rel.Type,
Add: rel.Add,
Sym: relocs.l.Lookup(rel.Sym.Name, int(rel.Sym.Version)),
}
}
Than McIntosh
committed
if relocs.extIdx != 0 {
pp := relocs.l.getPayload(relocs.extIdx)
return pp.relocs[j]
}
rel := goobj2.Reloc{}
rel.Read(relocs.r.Reader, relocs.r.RelocOff(relocs.li, j))
target := relocs.l.resolve(relocs.r, rel.Sym)
return Reloc{
Off: rel.Off,
Size: rel.Siz,
Type: objabi.RelocType(rel.Type),
Add: rel.Add,
Sym: target,
}
}
// ReadAll method reads all relocations for a symbol into the
// specified slice. If the slice capacity is not large enough, a new
// larger slice will be allocated. Final slice is returned.
func (relocs *Relocs) ReadAll(dst []Reloc) []Reloc {
if relocs.Count == 0 {
return dst[:0]
}
if cap(dst) < relocs.Count {
dst = make([]Reloc, relocs.Count)
}
dst = dst[:0]
Than McIntosh
committed
if s := relocs.l.Syms[relocs.extIdx]; s != nil {
for i := 0; i < relocs.Count; i++ {
Than McIntosh
committed
erel := &s.R[i]
rel := Reloc{
Off: erel.Off,
Size: erel.Siz,
Type: erel.Type,
Add: erel.Add,
Sym: relocs.l.Lookup(erel.Sym.Name, int(erel.Sym.Version)),
}
dst = append(dst, rel)
}
return dst
}
Than McIntosh
committed
if relocs.extIdx != 0 {
pp := relocs.l.getPayload(relocs.extIdx)
dst = append(dst, pp.relocs...)
return dst
}
off := relocs.r.RelocOff(relocs.li, 0)
for i := 0; i < relocs.Count; i++ {
rel := goobj2.Reloc{}
rel.Read(relocs.r.Reader, off)
off += uint32(rel.Size())
target := relocs.l.resolve(relocs.r, rel.Sym)
dst = append(dst, Reloc{
Off: rel.Off,
Size: rel.Siz,
Type: objabi.RelocType(rel.Type),
Add: rel.Add,
Sym: target,
})
}
return dst
}
// Relocs returns a Relocs object for the given global sym.
func (l *Loader) Relocs(i Sym) Relocs {
if l.IsExternal(i) {
if s := l.Syms[i]; s != nil {
Than McIntosh
committed
return Relocs{Count: len(s.R), l: l, extIdx: i}
}
pp := l.getPayload(i)
if pp != nil {
return Relocs{Count: len(pp.relocs), l: l, extIdx: i}
return Relocs{}
}
r, li := l.toLocal(i)
Than McIntosh
committed
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 int) Relocs {
return Relocs{
Count: r.NReloc(li),
li: li,
r: r,
l: l,
}
}
// Preload a package: add autolibs, add symbols to the symbol table.
// Does not read symbol data yet.
func (l *Loader) Preload(arch *sys.Arch, syms *sym.Symbols, f *bio.Reader, lib *sym.Library, unit *sym.CompilationUnit, length int64, pn string, flags int) {
roObject, readonly, err := f.Slice(uint64(length))
if err != nil {
log.Fatal("cannot read object file:", err)
}
r := goobj2.NewReaderFromBytes(roObject, readonly)
if r == nil {
panic("cannot read object file")
}
localSymVersion := syms.IncVersion()
pkgprefix := objabi.PathToPrefix(lib.Pkg) + "."
or := &oReader{r, unit, localSymVersion, r.Flags(), pkgprefix, nil}
Cherry Zhang
committed
lib.ImportStrings = append(lib.ImportStrings, r.Autolib()...)
Cherry Zhang
committed
// DWARF file table
nfile := r.NDwarfFile()
unit.DWARFFileTable = make([]string, nfile)
for i := range unit.DWARFFileTable {
unit.DWARFFileTable[i] = r.DwarfFile(i)
}
istart := l.addObj(lib.Pkg, or)
ndef := r.NSym()
nnonpkgdef := r.NNonpkgdef()
for i, n := 0, ndef+nnonpkgdef; i < n; i++ {
osym := goobj2.Sym{}
osym.Read(r, r.SymOff(i))
name := strings.Replace(osym.Name, "\"\".", pkgprefix, -1)
if name == "" {
continue // don't add unnamed aux symbol
}
v := abiToVer(osym.ABI, localSymVersion)
dupok := osym.Dupok()
added := l.AddSym(name, v, istart+Sym(i), or, dupok, sym.AbiSymKindToSymKind[objabi.SymKind(osym.Type)])
if added && strings.HasPrefix(name, "go.itablink.") {
l.itablink[istart+Sym(i)] = struct{}{}
}
if added && strings.HasPrefix(name, "runtime.") {
if bi := goobj2.BuiltinIdx(name, v); bi != -1 {
// This is a definition of a builtin symbol. Record where it is.
l.builtinSyms[bi] = istart + Sym(i)
}
}
}
// The caller expects us consuming all the data
f.MustSeek(length, os.SEEK_CUR)
}
// Make sure referenced symbols are added. Most of them should already be added.
// This should only be needed for referenced external symbols.
func (l *Loader) LoadRefs(arch *sys.Arch, syms *sym.Symbols) {
Cherry Zhang
committed
for _, o := range l.objs[1:] {
loadObjRefs(l, o.r, arch, syms)
}
}
func loadObjRefs(l *Loader, r *oReader, arch *sys.Arch, syms *sym.Symbols) {
ndef := r.NSym() + r.NNonpkgdef()
for i, n := 0, r.NNonpkgref(); i < n; i++ {
osym := goobj2.Sym{}
Cherry Zhang
committed
osym.Read(r.Reader, r.SymOff(ndef+i))
name := strings.Replace(osym.Name, "\"\".", r.pkgprefix, -1)
Cherry Zhang
committed
v := abiToVer(osym.ABI, r.version)
l.AddExtSym(name, v)
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
}
}
func abiToVer(abi uint16, localSymVersion int) int {
var v int
if abi == goobj2.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
}
func preprocess(arch *sys.Arch, s *sym.Symbol) {
if s.Name != "" && s.Name[0] == '$' && len(s.Name) > 5 && s.Type == 0 && len(s.P) == 0 {
x, err := strconv.ParseUint(s.Name[5:], 16, 64)
if err != nil {
log.Panicf("failed to parse $-symbol %s: %v", s.Name, err)
}
s.Type = sym.SRODATA
s.Attr |= sym.AttrLocal
switch s.Name[:5] {
case "$f32.":
if uint64(uint32(x)) != x {
log.Panicf("$-symbol %s too large: %d", s.Name, x)
}
s.AddUint32(arch, uint32(x))
case "$f64.", "$i64.":
s.AddUint64(arch, x)
default:
log.Panicf("unrecognized $-symbol: %s", s.Name)
}
}
}
// Load full contents.
func (l *Loader) LoadFull(arch *sys.Arch, syms *sym.Symbols) {
// create all Symbols first.
l.growSyms(l.NSym())
nr := 0 // total number of sym.Reloc's we'll need
for _, o := range l.objs[1:] {
nr += loadObjSyms(l, syms, o.r)
}
// allocate a single large slab of relocations for all live symbols
l.relocBatch = make([]sym.Reloc, nr)
// external symbols
for i := l.extStart; i <= l.max; i++ {
if s := l.Syms[i]; s != nil {
s.Attr.Set(sym.AttrReachable, l.attrReachable.has(i))
continue // already loaded from external object
}
Than McIntosh
committed
sname := l.payloads[i-l.extStart].name
sver := l.payloads[i-l.extStart].ver
if l.attrReachable.has(i) || strings.HasPrefix(sname, "gofile..") { // XXX file symbols are used but not marked
Than McIntosh
committed
s := l.allocSym(sname, sver)
pp := l.getPayload(i)
if pp != nil {
if pp.kind != sym.Sxxx || len(pp.relocs) != 0 || len(pp.data) != 0 {
// Unpack payload into sym. Currently there is nothing
// to do here, but eventually we'll need a real
// implementation.
panic("need to handle this")
}
}
preprocess(arch, s)
s.Attr.Set(sym.AttrReachable, l.attrReachable.has(i))
Than McIntosh
committed
l.installSym(i, s)
}
}
// load contents of defined symbols
Cherry Zhang
committed
for _, o := range l.objs[1:] {
loadObjFull(l, o.r)
Cherry Zhang
committed
}
// Resolve ABI aliases for external symbols. This is only
// needed for internal cgo linking.
// (The old code does this in deadcode, but deadcode2 doesn't
// do this.)
for i := l.extStart; i <= l.max; i++ {
if s := l.Syms[i]; s != nil && s.Attr.Reachable() {
for ri := range s.R {
r := &s.R[ri]
if r.Sym != nil && r.Sym.Type == sym.SABIALIAS {
r.Sym = r.Sym.R[0].Sym
}
}
}
}
Cherry Zhang
committed
}
// ExtractSymbols grabs the symbols out of the loader for work that hasn't been
// ported to the new symbol type.
func (l *Loader) ExtractSymbols(syms *sym.Symbols) {
// Nil out overwritten symbols.
// Overwritten Go symbols aren't a problem (as they're lazy loaded), but
// symbols loaded from host object loaders are fully loaded, and we might
// have multiple symbols with the same name. This loop nils them out.
for oldI := range l.overwrite {
l.Syms[oldI] = nil
}
// Add symbols to the ctxt.Syms lookup table. This explicitly skips things
// created via loader.Create (marked with versions less than zero), since
// if we tried to add these we'd wind up with collisions. We do, however,
// add these symbols to the list of global symbols so that other future
// steps (like pclntab generation) can find these symbols if neceassary.
// Along the way, update the version from the negative anon version to
// something larger than sym.SymVerStatic (needed so that
// sym.symbol.IsFileLocal() works properly).
anonVerReplacement := syms.IncVersion()
for _, s := range l.Syms {
if s == nil {
continue
}
if s.Name != "" && s.Version >= 0 {
} else {
syms.Allsym = append(syms.Allsym, s)
if s.Version < 0 {
s.Version = int16(anonVerReplacement)
}
}
}
// allocSym allocates a new symbol backing.
func (l *Loader) allocSym(name string, version int) *sym.Symbol {
batch := l.symBatch
if len(batch) == 0 {
batch = make([]sym.Symbol, 1000)
}
s := &batch[0]
l.symBatch = batch[1:]
s.Dynid = -1
s.Name = name
s.Version = int16(version)
return s
}
Than McIntosh
committed
// installSym sets the underlying sym.Symbol for the specified sym index.
func (l *Loader) installSym(i Sym, s *sym.Symbol) {
if s == nil {
panic("installSym nil symbol")
}
if l.Syms[i] != nil {
panic("sym already present in installSym")
Than McIntosh
committed
}
if l.IsExternal(i) {
// temporary sanity check: make sure that the payload
// is empty, e.g. nobody has added symbol content already.
pp := l.getPayload(i)
if pp != nil && (len(pp.relocs) != 0 || len(pp.data) != 0) {
panic("expected empty payload")
}
}
l.Syms[i] = s
}
// addNewSym adds a new sym.Symbol to the i-th index in the list of symbols.
func (l *Loader) addNewSym(i Sym, name string, ver int, unit *sym.CompilationUnit, t sym.SymKind) *sym.Symbol {
s := l.allocSym(name, ver)
if s.Type != 0 && s.Type != sym.SXREF {
fmt.Println("symbol already processed:", unit.Lib, i, s)
panic("symbol already processed")
}
if t == sym.SBSS && (s.Type == sym.SRODATA || s.Type == sym.SNOPTRBSS) {
t = s.Type
}
s.Type = t
s.Unit = unit
l.growSyms(int(i))
Than McIntosh
committed
l.installSym(i, s)
return s
}
// loadObjSyms creates sym.Symbol objects for the live Syms in the
// object corresponding to object reader "r". Return value is the
// number of sym.Reloc entries required for all the new symbols.
func loadObjSyms(l *Loader, syms *sym.Symbols, r *oReader) int {
istart := l.startIndex(r)
for i, n := 0, r.NSym()+r.NNonpkgdef(); i < n; i++ {
// If it's been previously loaded in host object loading, we don't need to do it again.
if s := l.Syms[istart+Sym(i)]; s != nil {
// Mark symbol as reachable as it wasn't marked as such before.
s.Attr.Set(sym.AttrReachable, l.attrReachable.has(istart+Sym(i)))
nr += r.NReloc(i)
continue
}
osym := goobj2.Sym{}
Cherry Zhang
committed
osym.Read(r.Reader, r.SymOff(i))
name := strings.Replace(osym.Name, "\"\".", r.pkgprefix, -1)
if name == "" {
continue
ver := abiToVer(osym.ABI, r.version)
if osym.ABI != goobj2.SymABIstatic && l.symsByName[ver][name] != istart+Sym(i) {
continue
t := sym.AbiSymKindToSymKind[objabi.SymKind(osym.Type)]
if t == sym.SXREF {
log.Fatalf("bad sxref")
}
if t == 0 {
log.Fatalf("missing type for %s in %s", name, r.unit.Lib)
if !l.attrReachable.has(istart+Sym(i)) && !(t == sym.SRODATA && strings.HasPrefix(name, "type.")) && name != "runtime.addmoduledata" && name != "runtime.lastmoduledatap" {
// No need to load unreachable symbols.
// XXX some type symbol's content may be needed in DWARF code, but they are not marked.
// XXX reference to runtime.addmoduledata may be generated later by the linker in plugin mode.
s := l.addNewSym(istart+Sym(i), name, ver, r.unit, t)
s.Attr.Set(sym.AttrReachable, l.attrReachable.has(istart+Sym(i)))
nr += r.NReloc(i)
return nr
}
// funcInfoSym records the sym.Symbol for a function, along with a copy
// of the corresponding goobj2.Sym and the index of its FuncInfo aux sym.
// We use this to delay populating FuncInfo until we can batch-allocate
// slices for their sub-objects.
type funcInfoSym struct {
s *sym.Symbol // sym.Symbol for a live function
osym goobj2.Sym // object file symbol data for that function
isym int // global symbol index of FuncInfo aux sym for func
}
// funcAllocInfo records totals/counts for all functions in an objfile;
// used to help with bulk allocation of sym.Symbol sub-objects.
type funcAllocInfo struct {
symPtr uint32 // number of *sym.Symbol's needed in file slices
inlCall uint32 // number of sym.InlinedCall's needed in inltree slices
pcData uint32 // number of sym.Pcdata's needed in pdata slices
fdOff uint32 // number of int64's needed in all Funcdataoff slices
}
// loadSymbol loads a single symbol by name.
// NB: This function does NOT set the symbol as reachable.
func (l *Loader) loadSymbol(name string, version int) *sym.Symbol {
global := l.Lookup(name, version)
// If we're already loaded, bail.
if global != 0 && int(global) < len(l.Syms) && l.Syms[global] != nil {
return l.Syms[global]
}
// Read the symbol.
r, i := l.toLocal(global)
istart := l.startIndex(r)
osym := goobj2.Sym{}
osym.Read(r.Reader, r.SymOff(int(i)))
if l.symsByName[version][name] != istart+Sym(i) {
return nil
}
return l.addNewSym(istart+Sym(i), name, version, r.unit, sym.AbiSymKindToSymKind[objabi.SymKind(osym.Type)])
// LookupOrCreate looks up a symbol by name, and creates one if not found.
// Either way, it will also create a sym.Symbol for it, if not already.
// This should only be called when interacting with parts of the linker
// that still works on sym.Symbols (i.e. internal cgo linking, for now).
func (l *Loader) LookupOrCreate(name string, version int) *sym.Symbol {
i := l.Lookup(name, version)
if i != 0 {
// symbol exists
if int(i) < len(l.Syms) && l.Syms[i] != nil {
}
if l.IsExternal(i) {
panic("Can't load an external symbol.")
}
return l.loadSymbol(name, version)
}
i = l.AddExtSym(name, version)
s := l.allocSym(name, version)
l.Syms[i] = s
return s
}
Than McIntosh
committed
// CreateExtSym creates a new external symbol with the specified name
// without adding it to any lookup tables, returning a Sym index for it.
func (l *Loader) CreateExtSym(name string) Sym {
return l.newExtSym(name, sym.SymVerABI0)
}
// Create creates a symbol with the specified name, returning a
// sym.Symbol object for it. This method is intended for static/hidden
// symbols discovered while loading host objects. We can see more than
// one instance of a given static symbol with the same name/version,
// so we can't add them to the lookup tables "as is". Instead assign
// them fictitious (unique) versions, starting at -1 and decreasing by
// one for each newly created symbol, and record them in the
// extStaticSyms hash.
func (l *Loader) Create(name string) *sym.Symbol {
i := l.max + 1
l.max++
if l.extStart == 0 {
l.extStart = i
}
// Assign a new unique negative version -- this is to mark the
// symbol so that it can be skipped when ExtractSymbols is adding
// ext syms to the sym.Symbols hash.
l.anonVersion--
ver := l.anonVersion
l.growSyms(int(i))
s := l.allocSym(name, ver)
Than McIntosh
committed
l.installSym(i, s)
l.extStaticSyms[nameVer{name, ver}] = i
return s
}
func loadObjFull(l *Loader, r *oReader) {
lib := r.unit.Lib
istart := l.startIndex(r)
resolveSymRef := func(s goobj2.SymRef) *sym.Symbol {
i := l.resolve(r, s)
return l.Syms[i]
}
funcs := []funcInfoSym{}
fdsyms := []*sym.Symbol{}
var funcAllocCounts funcAllocInfo
pcdataBase := r.PcdataBase()
rslice := []Reloc{}
for i, n := 0, r.NSym()+r.NNonpkgdef(); i < n; i++ {
osym := goobj2.Sym{}
osym.Read(r.Reader, r.SymOff(i))
name := strings.Replace(osym.Name, "\"\".", r.pkgprefix, -1)
if name == "" {
continue
}
ver := abiToVer(osym.ABI, r.version)
dupok := osym.Dupok()
if dupok {
if dupsym := l.symsByName[ver][name]; dupsym != istart+Sym(i) {
if l.attrReachable.has(dupsym) {
// A dupok 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.
s := l.Syms[dupsym]
if s.Type == sym.STEXT {
lib.DupTextSyms = append(lib.DupTextSyms, s)
}
}
continue
}
}
s := l.Syms[istart+Sym(i)]
if s == nil {
continue
}
if s.Name != name { // Sanity check. We can remove it in the final version.
fmt.Println("name mismatch:", lib, i, s.Name, name)
panic("name mismatch")
}
local := osym.Local()
makeTypelink := osym.Typelink()
size := osym.Siz
// Symbol data
s.P = r.Data(i)
s.Attr.Set(sym.AttrReadOnly, r.ReadOnly())
// Relocs
relocs := l.relocs(r, i)
rslice = relocs.ReadAll(rslice)
batch := l.relocBatch
s.R = batch[:relocs.Count:relocs.Count]
l.relocBatch = batch[relocs.Count:]
for j := range s.R {
r := rslice[j]
rs := r.Sym
sz := r.Size
rt := r.Type
if rt == objabi.R_METHODOFF {
if l.attrReachable.has(rs) {
rt = objabi.R_ADDROFF
} else {
sz = 0
rs = 0
}
}
if rt == objabi.R_WEAKADDROFF && !l.attrReachable.has(rs) {
Than McIntosh
committed
if rs != 0 && l.Syms[rs] != nil && l.Syms[rs].Type == sym.SABIALIAS {
rsrelocs := l.Relocs(rs)
rs = rsrelocs.At(0).Sym
Off: r.Off,
Add: r.Add,
// Aux symbol info
isym := -1
naux := r.NAux(i)
for j := 0; j < naux; j++ {
a := goobj2.Aux{}
Cherry Zhang
committed
a.Read(r.Reader, r.AuxOff(i, j))
switch a.Type {
case goobj2.AuxGotype:
typ := resolveSymRef(a.Sym)
if typ != nil {
s.Gotype = typ
}
case goobj2.AuxFuncdata:
fdsyms = append(fdsyms, resolveSymRef(a.Sym))
case goobj2.AuxFuncInfo:
if a.Sym.PkgIdx != goobj2.PkgIdxSelf {
panic("funcinfo symbol not defined in current package")
}
isym = int(a.Sym.SymIdx)
case goobj2.AuxDwarfInfo, goobj2.AuxDwarfLoc, goobj2.AuxDwarfRanges, goobj2.AuxDwarfLines:
// ignored for now
default:
panic("unknown aux type")
}
}
s.File = r.pkgprefix[:len(r.pkgprefix)-1]
if dupok {
s.Attr |= sym.AttrDuplicateOK
}
if s.Size < int64(size) {
s.Size = int64(size)
}
s.Attr.Set(sym.AttrLocal, local)
s.Attr.Set(sym.AttrMakeTypelink, makeTypelink)
Cherry Zhang
committed
if s.Type == sym.SDWARFINFO {
// For DWARF symbols, replace `"".` to actual package prefix
// in the symbol content.
// TODO: maybe we should do this in the compiler and get rid
// of this.
patchDWARFName(s, r)
}
if s.Type != sym.STEXT {
continue
}
if isym == -1 {
continue
}
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
// Record function sym and associated info for additional
// processing in the loop below.
fwis := funcInfoSym{s: s, isym: isym, osym: osym}
funcs = append(funcs, fwis)
// Read the goobj2.FuncInfo for this text symbol so that we can
// collect allocation counts. We'll read it again in the loop
// below.
b := r.Data(isym)
info := goobj2.FuncInfo{}
info.Read(b)
funcAllocCounts.symPtr += uint32(len(info.File))
funcAllocCounts.pcData += uint32(len(info.Pcdata))
funcAllocCounts.inlCall += uint32(len(info.InlTree))
funcAllocCounts.fdOff += uint32(len(info.Funcdataoff))
}
// At this point we can do batch allocation of the sym.FuncInfo's,
// along with the slices of sub-objects they use.
fiBatch := make([]sym.FuncInfo, len(funcs))
inlCallBatch := make([]sym.InlinedCall, funcAllocCounts.inlCall)
symPtrBatch := make([]*sym.Symbol, funcAllocCounts.symPtr)
pcDataBatch := make([]sym.Pcdata, funcAllocCounts.pcData)
fdOffBatch := make([]int64, funcAllocCounts.fdOff)
// Populate FuncInfo contents for func symbols.
for fi := 0; fi < len(funcs); fi++ {
s := funcs[fi].s
isym := funcs[fi].isym
osym := funcs[fi].osym
s.FuncInfo = &fiBatch[0]
fiBatch = fiBatch[1:]
Cherry Zhang
committed
b := r.Data(isym)
info := goobj2.FuncInfo{}
info.Read(b)
if info.NoSplit != 0 {
s.Attr |= sym.AttrNoSplit
}
if osym.ReflectMethod() {
s.Attr |= sym.AttrReflectMethod
}
if r.Flags()&goobj2.ObjFlagShared != 0 {
s.Attr |= sym.AttrShared
}
if osym.TopFrame() {
s.Attr |= sym.AttrTopFrame
}
pc := s.FuncInfo
if len(info.Funcdataoff) != 0 {
nfd := len(info.Funcdataoff)
pc.Funcdata = fdsyms[:nfd:nfd]
fdsyms = fdsyms[nfd:]
info.Pcdata = append(info.Pcdata, info.PcdataEnd) // for the ease of knowing where it ends
pc.Args = int32(info.Args)
pc.Locals = int32(info.Locals)
npc := len(info.Pcdata) - 1 // -1 as we appended one above
pc.Pcdata = pcDataBatch[:npc:npc]
pcDataBatch = pcDataBatch[npc:]
nfd := len(info.Funcdataoff)
pc.Funcdataoff = fdOffBatch[:nfd:nfd]
fdOffBatch = fdOffBatch[nfd:]
nsp := len(info.File)
pc.File = symPtrBatch[:nsp:nsp]
symPtrBatch = symPtrBatch[nsp:]
nic := len(info.InlTree)
pc.InlTree = inlCallBatch[:nic:nic]
inlCallBatch = inlCallBatch[nic:]
pc.Pcsp.P = r.BytesAt(pcdataBase+info.Pcsp, int(info.Pcfile-info.Pcsp))
pc.Pcfile.P = r.BytesAt(pcdataBase+info.Pcfile, int(info.Pcline-info.Pcfile))
pc.Pcline.P = r.BytesAt(pcdataBase+info.Pcline, int(info.Pcinline-info.Pcline))
pc.Pcinline.P = r.BytesAt(pcdataBase+info.Pcinline, int(info.Pcdata[0]-info.Pcinline))
for k := range pc.Pcdata {
pc.Pcdata[k].P = r.BytesAt(pcdataBase+info.Pcdata[k], int(info.Pcdata[k+1]-info.Pcdata[k]))
}
for k := range pc.Funcdataoff {
pc.Funcdataoff[k] = int64(info.Funcdataoff[k])
}
for k := range pc.File {
pc.File[k] = resolveSymRef(info.File[k])
}
for k := range pc.InlTree {
inl := &info.InlTree[k]
pc.InlTree[k] = sym.InlinedCall{
Parent: inl.Parent,
File: resolveSymRef(inl.File),
Line: inl.Line,
Func: l.SymName(l.resolve(r, inl.Func)),
ParentPC: inl.ParentPC,
}
}
dupok := osym.Dupok()
if !dupok {
if s.Attr.OnList() {
log.Fatalf("symbol %s listed multiple times", s.Name)
}
s.Attr.Set(sym.AttrOnList, true)
lib.Textp = append(lib.Textp, s)
} else {
// there may be a dup in another package
// put into a temp list and add to text later
lib.DupTextSyms = append(lib.DupTextSyms, s)
}
Cherry Zhang
committed
var emptyPkg = []byte(`"".`)
func patchDWARFName1(p []byte, r *oReader) ([]byte, int) {
Cherry Zhang
committed
// This is kind of ugly. Really the package name should not
// even be included here.
if len(p) < 1 || p[0] != dwarf.DW_ABRV_FUNCTION {
return p, -1
Cherry Zhang
committed
}
e := bytes.IndexByte(p, 0)
Cherry Zhang
committed
if e == -1 {
return p, -1
Cherry Zhang
committed
}
if !bytes.Contains(p[:e], emptyPkg) {
return p, -1
Cherry Zhang
committed
}
pkgprefix := []byte(r.pkgprefix)
patched := bytes.Replace(p[:e], emptyPkg, pkgprefix, -1)
return append(patched, p[e:]...), e
}
Cherry Zhang
committed
func patchDWARFName(s *sym.Symbol, r *oReader) {
patched, e := patchDWARFName1(s.P, r)
if e == -1 {
return
}
s.P = patched
Cherry Zhang
committed
s.Attr.Set(sym.AttrReadOnly, false)
delta := int64(len(s.P)) - s.Size
s.Size = int64(len(s.P))
for i := range s.R {
r := &s.R[i]
if r.Off > int32(e) {
r.Off += int32(delta)
}
}
}
// For debugging.
func (l *Loader) Dump() {
fmt.Println("objs")
for _, obj := range l.objs {
if obj.r != nil {
fmt.Println(obj.i, obj.r.unit.Lib)
}
}
Than McIntosh
committed
fmt.Println("extStart:", l.extStart)
fmt.Println("syms")
for i, s := range l.Syms {
if i == 0 {
continue
}
if s != nil {
fmt.Println(i, s, s.Type)
} else {
fmt.Println(i, l.SymName(Sym(i)), "<not loaded>")
}
}
fmt.Println("overwrite:", l.overwrite)
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)