Skip to content
Snippets Groups Projects
loader.go 78.9 KiB
Newer Older
  • Learn to ignore specific revisions
  • 		panic("need to call Preload first")
    	}
    
    	return (*goobj.FuncInfo)(nil).ReadFile(fi.data, fi.lengths.FileOff, uint32(k))
    
    // TopFrame returns true if the function associated with this FuncInfo
    // is an entry point, meaning that unwinders should stop when they hit
    // this function.
    func (fi *FuncInfo) TopFrame() bool {
    	return (fi.FuncFlag() & objabi.FuncFlag_TOPFRAME) != 0
    }
    
    
    type InlTreeNode struct {
    	Parent   int32
    
    	File     goobj.CUFileIndex
    
    	Line     int32
    	Func     Sym
    	ParentPC int32
    }
    
    func (fi *FuncInfo) NumInlTree() uint32 {
    	if !fi.lengths.Initialized {
    		panic("need to call Preload first")
    	}
    	return fi.lengths.NumInlTree
    }
    
    func (fi *FuncInfo) InlTree(k int) InlTreeNode {
    	if !fi.lengths.Initialized {
    		panic("need to call Preload first")
    	}
    
    	node := (*goobj.FuncInfo)(nil).ReadInlTree(fi.data, fi.lengths.InlTreeOff, uint32(k))
    
    	return InlTreeNode{
    		Parent:   node.Parent,
    
    		Line:     node.Line,
    		Func:     fi.l.resolve(fi.r, node.Func),
    		ParentPC: node.ParentPC,
    	}
    }
    
    	r, auxs := l.auxs(i)
    
    		if a.Type() == goobj.AuxFuncInfo {
    
    			return FuncInfo{l, r, b, goobj.FuncInfoLengths{}}
    
    // Preload a package: adds autolib.
    // Does not add defined package or non-packaged symbols to the symbol table.
    // These are done in LoadSyms.
    
    // 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 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) + "."
    
    	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)),
    
    	lib.Autolib = append(lib.Autolib, r.Autolib()...)
    
    	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)
    
    // 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
    
    		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
    		}
    
    		start = uint32(r.ndef + r.nhashed64def + r.nhasheddef)
    		end = uint32(r.ndef + r.nhashed64def + r.nhasheddef + r.NNonpkgdef())
    
    	needNameExpansion := r.NeedNameExpansion()
    
    	loadingRuntimePkg := r.unit.Lib.Pkg == "runtime"
    
    		osym := r.Sym(i)
    
    		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)
    
    		if osym.Local() {
    			l.SetAttrLocal(gi, true)
    		}
    
    		if osym.UsedInIface() {
    			l.SetAttrUsedInIface(gi, true)
    		}
    
    		if strings.HasPrefix(name, "runtime.") ||
    			(loadingRuntimePkg && strings.HasPrefix(name, "type.")) {
    
    			if bi := goobj.BuiltinIdx(name, int(osym.ABI())); bi != -1 {
    
    				// This is a definition of a builtin symbol. Record where it is.
    
    		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)
    
    
    		hashed64Syms: make(map[uint64]symAndSize, hashed64Size),
    		hashedSyms:   make(map[goobj.HashType]symAndSize, hashedSize),
    	}
    
    	for _, o := range l.objs[goObjStart:] {
    		st.preloadSyms(o.r, pkgDef)
    
    	l.npkgsyms = l.NSym()
    
    		st.preloadSyms(o.r, hashed64Def)
    		st.preloadSyms(o.r, hashedDef)
    		st.preloadSyms(o.r, nonPkgDef)
    
    	l.nhashedsyms = len(st.hashed64Syms) + len(st.hashedSyms)
    
    	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) {
    
    	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)
    
    		if osym.Local() {
    			l.SetAttrLocal(gi, true)
    		}
    
    		if osym.UsedInIface() {
    			l.SetAttrUsedInIface(gi, true)
    		}
    
    	// referenced packages
    	npkg := r.NPkg()
    	r.pkg = make([]uint32, npkg)
    	for i := 1; i < npkg; i++ { // PkgIdx 0 is a dummy invalid package
    		pkg := r.Pkg(i)
    		objidx, ok := l.objByPkg[pkg]
    		if !ok {
    
    			log.Fatalf("%v: reference to nonexistent package %s", r.unit.Lib, pkg)
    
    	// 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 {
    
    }
    
    func abiToVer(abi uint16, localSymVersion int) int {
    	var v int
    
    	if abi == goobj.SymABIstatic {
    
    		// Static
    		v = localSymVersion
    	} else if abiver := sym.ABIToVersion(obj.ABI(abi)); abiver != -1 {
    		// Note that data symbols are "ABI0", which maps to version 0.
    		v = abiver
    	} else {
    		log.Fatalf("invalid symbol ABI: %d", abi)
    	}
    	return v
    }
    
    
    // TopLevelSym tests a symbol (by name and kind) to determine whether
    // the symbol first class sym (participating in the link) or is an
    // anonymous aux or sub-symbol containing some sub-part or payload of
    // another symbol.
    func (l *Loader) TopLevelSym(s Sym) bool {
    	return topLevelSym(l.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:
    
    // 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]
    
    
    	// 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).
    
    
    		// Copy relocations
    		relocs := l.Relocs(symIdx)
    
    		pp.relocs = make([]goobj.Reloc, relocs.Count())
    
    		for i := range pp.relocs {
    			// Copy the relocs slice.
    			// Convert local reference to global reference.
    
    			rel := relocs.At(i)
    
    			pp.relocs[i].Set(rel.Off(), rel.Siz(), uint16(rel.Type()), rel.Add(), goobj.SymRef{PkgIdx: 0, SymIdx: uint32(rel.Sym())})
    
    	// If we're overriding a data symbol, collect the associated
    	// Gotype, so as to propagate it to the new symbol.
    
    	auxs := r.Auxs(li)
    
    	// 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))
    
    // 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.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++ {
    
    		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)
    
    // 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.
    
    		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 i, n := uint32(0), uint32(r.NAlldef()); i < n; i++ {
    
    			osym := r.Sym(i)
    
    			st := sym.AbiSymKindToSymKind[objabi.SymKind(osym.Type())]
    
    			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
    			}
    
    				lib.DupTextSyms = append(lib.DupTextSyms, sym.LoaderSym(gi))
    
    			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 {
    
    					if !assignedToUnit.Has(sym) {
    
    						textp = append(textp, sym)
    
    						unit := l.SymUnit(sym)
    						if unit != nil {
    
    							unit.Textp = append(unit.Textp, s)
    
    							assignedToUnit.Set(sym)
    
    						// Dupok symbols may be defined in multiple packages; the
    						// associated package for a dupok sym is chosen sort of
    						// arbitrarily (the first containing package that the linker
    						// loads). Canonicalizes its Pkg to the package with which
    						// it will be laid down in text.
    						if i == 1 /* DupTextSyms2 */ && l.SymPkg(sym) != lib.Pkg {
    							l.SetSymPkg(sym, lib.Pkg)
    						}
    
    			lib.Textp = nil
    			lib.DupTextSyms = nil
    
    // ErrorReporter is a helper class for reporting errors.
    type ErrorReporter struct {
    	ldr              *Loader
    	AfterErrorAction func()
    }
    
    // Errorf method logs an error message.
    //
    // After each error, the error actions function will be invoked; this
    // will either terminate the link immediately (if -h option given)
    // or it will keep a count and exit if more than 20 errors have been printed.
    //
    // Logging an error means that on exit cmd/link will delete any
    // output file and return a non-zero error code.
    //
    func (reporter *ErrorReporter) Errorf(s Sym, format string, args ...interface{}) {
    	if s != 0 && reporter.ldr.SymName(s) != "" {
    
    		// Note: Replace is needed here because symbol names might have % in them,
    		// due to the use of LinkString for names of instantiating types.
    		format = strings.Replace(reporter.ldr.SymName(s), "%", "%%", -1) + ": " + format
    
    	} else {
    		format = fmt.Sprintf("sym %d: %s", s, format)
    	}
    	format += "\n"
    	fmt.Fprintf(os.Stderr, format, args...)
    	reporter.AfterErrorAction()
    }
    
    // GetErrorReporter returns the loader's associated error reporter.
    func (l *Loader) GetErrorReporter() *ErrorReporter {
    	return l.errorReporter
    }
    
    // Errorf method logs an error message. See ErrorReporter.Errorf for details.
    func (l *Loader) Errorf(s Sym, format string, args ...interface{}) {
    
    	l.errorReporter.Errorf(s, format, args...)
    
    // Symbol statistics.
    func (l *Loader) Stat() string {
    	s := fmt.Sprintf("%d symbols, %d reachable\n", l.NSym(), l.NReachableSym())
    	s += fmt.Sprintf("\t%d package symbols, %d hashed symbols, %d non-package symbols, %d external symbols\n",
    
    		l.npkgsyms, l.nhashedsyms, int(l.extStart)-l.npkgsyms-l.nhashedsyms, l.NSym()-int(l.extStart))
    
    // For debugging.
    func (l *Loader) Dump() {
    	fmt.Println("objs")
    
    		if obj.r != nil {
    			fmt.Println(obj.i, obj.r.unit.Lib)
    		}
    	}
    
    	fmt.Println("Nsyms:", len(l.objSyms))
    
    	for i := Sym(1); i < Sym(len(l.objSyms)); i++ {
    
    		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)
    
    	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)
    	}