diff options
Diffstat (limited to 'src/cmd/link/internal/loader')
-rw-r--r-- | src/cmd/link/internal/loader/loader.go | 2683 | ||||
-rw-r--r-- | src/cmd/link/internal/loader/loader_test.go | 454 | ||||
-rw-r--r-- | src/cmd/link/internal/loader/symbolbuilder.go | 440 |
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 + } + } +} |