Skip to content
Snippets Groups Projects
loader.go 84.7 KiB
Newer Older
  • Learn to ignore specific revisions
  • // 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.
    
    
    	"cmd/internal/goobj"
    
    	"cmd/internal/obj"
    	"cmd/internal/objabi"
    	"cmd/internal/sys"
    	"cmd/link/internal/sym"
    
    // 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 = sym.LoaderSym
    
    // 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
    
    type Reloc struct {
    
    func (rel Reloc) Type() objabi.RelocType     { return objabi.RelocType(rel.Reloc.Type()) &^ objabi.R_WEAK }
    func (rel Reloc) Weak() bool                 { return objabi.RelocType(rel.Reloc.Type())&objabi.R_WEAK != 0 }
    
    func (rel Reloc) SetType(t objabi.RelocType) { rel.Reloc.SetType(uint16(t)) }
    func (rel Reloc) Sym() Sym                   { return rel.l.resolve(rel.r, rel.Reloc.Sym()) }
    func (rel Reloc) SetSym(s Sym)               { rel.Reloc.SetSym(goobj.SymRef{PkgIdx: 0, SymIdx: uint32(s)}) }
    func (rel Reloc) IsMarker() bool             { return rel.Siz() == 0 }
    
    // Aux holds a "handle" to access an aux symbol record from an
    
    type Aux struct {
    
    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 {
    
    	version      int // version of static symbol
    
    	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() }
    
    // 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.
    
    	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
    }
    
    
    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 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()
    
    		b = append(b, MakeBitmap(reqLen+1-curLen)...)
    
    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:
    //
    
    Russ Cox's avatar
    Russ Cox committed
    //   - 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.
    
    Russ Cox's avatar
    Russ Cox committed
    //   - In loader.LoadNonpkgSyms, add non-package defined symbols and
    //     references in all object files to the global index space.
    
    Russ Cox's avatar
    Russ Cox committed
    //   - 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.
    
    Russ Cox's avatar
    Russ Cox committed
    //   - 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.
    
    	objs        []*oReader
    	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
    
    	attrSpecial          Bitmap // "special" frame 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
    
    	generatedSyms        Bitmap // symbols that generate their content, indexed by ext sym idx
    
    	attrReadOnly         map[Sym]bool     // readonly data for this sym
    
    	attrCgoExportDynamic map[Sym]struct{} // "cgo_export_dynamic" symbols
    	attrCgoExportStatic  map[Sym]struct{} // "cgo_export_static" symbols
    
    	// Outer and Sub relations for symbols.
    
    	outer []Sym // indexed by global index
    
    	dynimplib   map[Sym]string      // stores Dynimplib symbol attribute
    	dynimpvers  map[Sym]string      // stores Dynimpvers symbol attribute
    	localentry  map[Sym]uint8       // stores Localentry symbol attribute
    	extname     map[Sym]string      // stores Extname symbol attribute
    	elfType     map[Sym]elf.SymType // stores elf type symbol property
    	elfSym      map[Sym]int32       // stores elf sym symbol property
    	localElfSym map[Sym]int32       // stores "local" elf sym symbol property
    	symPkg      map[Sym]string      // stores package for symbol, or library for shlib-derived syms
    	plt         map[Sym]int32       // stores dynimport for pe objects
    	got         map[Sym]int32       // stores got for pe objects
    	dynid       map[Sym]int32       // stores Dynid for symbol
    
    	relocVariant map[relocId]sym.RelocVariant // stores variant relocs
    
    
    	// Used to implement field tracking; created during deadcode if
    	// field tracking is enabled. Reachparent[K] contains the index of
    	// the symbol that triggered the marking of symbol K as live.
    	Reachparent []Sym
    
    	// CgoExports records cgo-exported symbols by SymName.
    	CgoExports map[string]Sym
    
    
    	WasmExports []Sym
    
    
    	flags uint32
    
    	strictDupMsgs int // number of strict-dup warning/errors, when FlagStrictDups is enabled
    
    	errorReporter *ErrorReporter
    
    	npkgsyms    int // number of package symbols, for accounting
    	nhashedsyms int // number of hashed symbols, for accounting
    
    // extSymPayload holds the payload (data + relocations) for linker-synthesized
    
    // external symbols (note that symbol value is stored in a separate slice).
    
    	name   string // TODO: would this be better as offset into str table?
    	size   int64
    	ver    int
    	kind   sym.SymKind
    	objidx uint32 // index of original object if sym made by cloneToExternal
    	relocs []goobj.Reloc
    	data   []byte
    	auxs   []goobj.Aux
    
    const (
    	// Loader.flags
    	FlagStrictDups = 1 << iota
    
    	FlagCheckLinkname
    
    func NewLoader(flags uint32, reporter *ErrorReporter) *Loader {
    
    	nbuiltin := goobj.NBuiltin()
    
    		objs:                 []*oReader{nil, extReader}, // reserve index 0 for nil symbol, 1 for external symbols
    		objSyms:              make([]objSym, 1, 1),       // This will get overwritten later.
    
    		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),
    
    		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),
    
    		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),
    
    		attrCgoExportDynamic: make(map[Sym]struct{}),
    		attrCgoExportStatic:  make(map[Sym]struct{}),
    
    		deferReturnTramp:     make(map[Sym]bool),
    
    		extStaticSyms:        make(map[nameVer]Sym),
    		builtinSyms:          make([]Sym, nbuiltin),
    		flags:                flags,
    
    		sects:                []*sym.Section{nil}, // reserve index 0 for nil section
    
    // Add object file r
    func (l *Loader) addObj(pkg string, r *oReader) {
    
    	pkg = objabi.PathToPrefix(pkg) // the object file contains escaped package path
    
    		l.objByPkg[pkg] = r.objidx
    
    	l.objs = append(l.objs, r)
    
    // 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 {
    
    	if l.extStart != 0 {
    
    		panic("addSym called after external symbol is created")
    
    	if int(i) != len(l.objSyms) { // overflow
    		panic("too many symbols")
    	}
    
    		l.objSyms = append(l.objSyms, objSym{r.objidx, li})
    
    	if name == "" && kind != hashed64Def && kind != hashedDef {
    
    		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.
    
    		// 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()
    
    	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]
    
    			addToHashMap = func(ss symAndSize) { st.hashed64Syms[h64] = ss }
    
    			checkHash = func() (symAndSize, bool) {
    
    				h = r.Hash(li - uint32(r.ndef+r.nhashed64def))
    
    				s, existed := st.hashedSyms[*h]
    
    			addToHashMap = func(ss symAndSize) { st.hashedSyms[*h] = ss }
    
    		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
    
    				// 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})
    
    		addToHashMap(symAndSize{i, siz})
    
    	// Non-package (named) symbol.
    	// Check if it already exists.
    
    	oldi, existed := l.symsByName[ver][name]
    	if !existed {
    		l.symsByName[ver][name] = i
    		addToGlobal()
    
    	// Fix for issue #47185 -- given two dupok or BSS symbols with
    	// different sizes, favor symbol with larger size. See also
    	// issue #46653 and #72032.
    	oldsz := l.SymSize(oldi)
    	sz := int64(r.Sym(li).Siz())
    
    		if l.flags&FlagStrictDups != 0 {
    			l.checkdup(name, r, li, oldi)
    		}
    
    			// new symbol overwrites old symbol.
    			l.objSyms[oldi] = objSym{r.objidx, li}
    		}
    
    	}
    	oldr, oldli := l.toLocal(oldi)
    
    	oldsym := oldr.Sym(oldli)
    
    	// If one is a DATA symbol (i.e. has content, DataSize != 0)
    	// and the other is BSS, the one with content wins.
    	// If both are BSS, the one with larger size wins.
    	// Specifically, the "overwrite" variable and the final result are
    	//
    	// new sym       old sym       overwrite
    	// ---------------------------------------------
    	// DATA          DATA          true  => ERROR
    	// DATA lg/eq    BSS  sm/eq    true  => new wins
    	// DATA small    BSS  large    true  => ERROR
    	// BSS  large    DATA small    true  => ERROR
    	// BSS  large    BSS  small    true  => new wins
    	// BSS  sm/eq    D/B  lg/eq    false => old wins
    	overwrite := r.DataSize(li) != 0 || oldsz < sz
    
    	if overwrite {
    		// new symbol overwrites old symbol.
    
    		oldtyp := sym.AbiSymKindToSymKind[objabi.SymKind(oldsym.Type())]
    
    		if !(oldtyp.IsData() && oldr.DataSize(oldli) == 0) || oldsz > sz {
    
    			log.Fatalf("duplicated definition of symbol %s, from %s and %s", name, r.unit.Lib.Pkg, oldr.unit.Lib.Pkg)
    
    	} else {
    		// old symbol overwrites new symbol.
    
    		typ := sym.AbiSymKindToSymKind[objabi.SymKind(oldsym.Type())]
    
    		if !typ.IsData() { // only allow overwriting data symbol
    
    			log.Fatalf("duplicated definition of symbol %s, from %s and %s", name, r.unit.Lib.Pkg, oldr.unit.Lib.Pkg)
    
    // 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 int(i) != len(l.objSyms) { // overflow
    		panic("too many symbols")
    	}
    
    	l.growValues(int(i) + 1)
    
    	l.growOuter(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
    	}
    
    // AddCgoExport records a cgo-exported symbol in l.CgoExports.
    // This table is used to identify the correct Go symbol ABI to use
    // to resolve references from host objects (which don't have ABIs).
    func (l *Loader) AddCgoExport(s Sym) {
    	if l.CgoExports == nil {
    		l.CgoExports = make(map[string]Sym)
    	}
    	l.CgoExports[l.SymName(s)] = s
    }
    
    // LookupOrCreateCgoExport is like LookupOrCreateSym, but if ver
    // indicates a global symbol, it uses the CgoExport table to determine
    // the appropriate symbol version (ABI) to use. ver must be either 0
    // or a static symbol version.
    func (l *Loader) LookupOrCreateCgoExport(name string, ver int) Sym {
    	if ver >= sym.SymVerStatic {
    		return l.LookupOrCreateSym(name, ver)
    	}
    	if ver != 0 {
    		panic("ver must be 0 or a static version")
    	}
    	// Look for a cgo-exported symbol from Go.
    	if s, ok := l.CgoExports[name]; ok {
    		return s
    	}
    	// Otherwise, this must just be a symbol in the host object.
    	// Create a version 0 symbol for it.
    	return l.LookupOrCreateSym(name, 0)
    }
    
    
    func (l *Loader) IsExternal(i Sym) bool {
    
    	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 {
    
    		panic(fmt.Sprintf("bogus symbol index %d in getPayload", 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 {
    
    // Convert a global index to a local index.
    
    func (l *Loader) toLocal(i Sym) (*oReader, uint32) {
    
    	return l.objs[l.objSyms[i].objidx], l.objSyms[i].s
    
    // Resolve a local symbol reference. Return global index.
    
    func (l *Loader) resolve(r *oReader, s goobj.SymRef) Sym {
    
    	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)
    		}
    
    	case goobj.PkgIdxHashed64:
    
    	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
    
    	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 = l.objs[r.pkg[p]]
    
    // 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
    //
    
    Russ Cox's avatar
    Russ Cox committed
    //	go build -gcflags=-race myprogram.go
    
    //
    // the compiler will insert calls to the builtin runtime.racefuncenter,
    // but the version of the runtime used for linkage won't actually contain
    // definitions of that symbol. See issue #42396 for details.
    //
    // As currently implemented, this is a fatal error. This has drawbacks
    // in that if there are multiple missing builtins, the error will only
    // cite the first one. On the plus side, terminating the link here has
    // advantages in that we won't run the risk of panics or crashes later
    // on in the linker due to R_CALL relocations with 0-valued target
    // symbols.
    func (l *Loader) reportMissingBuiltin(bsym int, reflib string) {
    	bname, _ := goobj.BuiltinName(bsym)
    	log.Fatalf("reference to undefined builtin %q from package %q",
    		bname, reflib)
    }
    
    
    // Look up a symbol by name, return global index, or 0 if not found.
    // This is more like Syms.ROLookup than Lookup -- it doesn't create
    // new symbol.
    
    func (l *Loader) Lookup(name string, ver int) Sym {
    
    	if ver >= sym.SymVerStatic || ver < 0 {
    
    		return l.extStaticSyms[nameVer{name, ver}]
    	}
    	return l.symsByName[ver][name]
    
    // Check that duplicate symbols have same contents.
    
    func (l *Loader) checkdup(name string, r *oReader, li uint32, dup Sym) {
    
    	p := r.Data(li)
    	rdup, ldup := l.toLocal(dup)
    	pdup := rdup.Data(ldup)
    	reason := "same length but different contents"
    	if len(p) != len(pdup) {
    		reason = fmt.Sprintf("new length %d != old length %d", len(p), len(pdup))
    
    	} else if bytes.Equal(p, pdup) {
    		// For BSS symbols, we need to check size as well, see issue 46653.
    		szdup := l.SymSize(dup)
    		sz := int64(r.Sym(li).Siz())
    		if szdup == sz {
    			return
    		}
    		reason = fmt.Sprintf("different sizes: new size %d != old size %d",
    			sz, szdup)
    
    	}
    	fmt.Fprintf(os.Stderr, "cmd/link: while reading object for '%v': duplicate symbol '%s', previous def at '%v', with mismatched payload: %s\n", r.unit.Lib, name, rdup.unit.Lib, reason)
    
    
    	// For the moment, allow DWARF subprogram DIEs for
    
    	// auto-generated wrapper functions. What seems to happen
    	// here is that we get different line numbers on formal
    	// params; I am guessing that the pos is being inherited
    	// from the spot where the wrapper is needed.
    
    	allowed := strings.HasPrefix(name, "go:info.go.interface") ||
    		strings.HasPrefix(name, "go:info.go.builtin") ||
    		strings.HasPrefix(name, "go:debuglines")
    
    	if !allowed {
    
    		l.strictDupMsgs++
    	}
    }
    
    func (l *Loader) NStrictDupMsgs() int { return l.strictDupMsgs }
    
    
    // Number of total symbols.
    func (l *Loader) NSym() int {
    
    	return len(l.objSyms)
    
    // Number of defined Go symbols.
    func (l *Loader) NDef() int {
    	return int(l.extStart)
    }
    
    
    // Number of reachable symbols.
    func (l *Loader) NReachableSym() int {
    	return l.attrReachable.Count()
    }
    
    
    // Returns the name of the i-th symbol.
    
    func (l *Loader) SymName(i Sym) string {
    
    	if r == nil {
    		return "?"
    	}
    
    	return r.Sym(li).Name(r.Reader)
    
    // Returns the version of the i-th symbol.
    func (l *Loader) SymVersion(i Sym) int {
    	if l.IsExternal(i) {
    		pp := l.getPayload(i)
    		return pp.ver
    	}
    	r, li := l.toLocal(i)
    
    	return int(abiToVer(r.Sym(li).ABI(), r.version))
    
    func (l *Loader) IsFileLocal(i Sym) bool {
    	return l.SymVersion(i) >= sym.SymVerStatic
    }
    
    // IsFromAssembly returns true if this symbol is derived from an
    // object file generated by the Go assembler.
    func (l *Loader) IsFromAssembly(i Sym) bool {
    	if l.IsExternal(i) {
    
    		pp := l.getPayload(i)
    		if pp.objidx != 0 {
    			r := l.objs[pp.objidx]
    			return r.FromAssembly()
    		}
    
    		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 {
    
    		pp := l.getPayload(i)
    		if pp != nil {
    			return pp.kind
    		}
    
    	return sym.AbiSymKindToSymKind[objabi.SymKind(r.Sym(li).Type())]
    
    // Returns the attributes of the i-th symbol.
    func (l *Loader) SymAttr(i Sym) uint8 {
    
    		// 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 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)
    
    		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.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.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)
    
    		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 {
    
    	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) {
    
    		panic("tried to set visibility attr on non-external symbol")
    	}
    	if v {
    
    		l.attrVisibilityHidden.Set(l.extIndex(i))
    
    		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 {
    
    		// 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) {
    
    		panic("tried to set dupok attr on non-external symbol")
    	}
    	if v {
    
    		l.attrDuplicateOK.Set(l.extIndex(i))
    
    		l.attrDuplicateOK.Unset(l.extIndex(i))
    
    	}
    }
    
    // AttrShared returns true for symbols compiled with the -shared option.
    func (l *Loader) AttrShared(i Sym) bool {
    
    		// 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 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 {
    
    	return l.attrExternal.Has(l.extIndex(i))
    
    // SetAttrExternal sets the "external" property for a host object