Skip to content
Snippets Groups Projects
loader.go 74.7 KiB
Newer Older
  • Learn to ignore specific revisions
  • 	}
    	r := goobj2.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) + "."
    
    	ndef := r.NSym()
    	nnonpkgdef := r.NNonpkgdef()
    
    	or := &oReader{r, unit, localSymVersion, r.Flags(), pkgprefix, make([]Sym, ndef+nnonpkgdef+r.NNonpkgref()), ndef, uint32(len(l.objs))}
    
    	lib.Autolib = append(lib.Autolib, r.Autolib()...)
    
    	// DWARF file table
    	nfile := r.NDwarfFile()
    	unit.DWARFFileTable = make([]string, nfile)
    	for i := range unit.DWARFFileTable {
    		unit.DWARFFileTable[i] = r.DwarfFile(i)
    	}
    
    
    	l.addObj(lib.Pkg, or)
    	l.preloadSyms(or, pkgDef)
    
    	// The caller expects us consuming all the data
    	f.MustSeek(length, os.SEEK_CUR)
    
    }
    
    // Preload symbols of given kind from an object.
    func (l *Loader) preloadSyms(r *oReader, kind int) {
    
    	ndef := uint32(r.NSym())
    	nnonpkgdef := uint32(r.NNonpkgdef())
    	var start, end uint32
    
    	switch kind {
    	case pkgDef:
    		start = 0
    		end = ndef
    	case nonPkgDef:
    		start = ndef
    		end = ndef + nnonpkgdef
    	default:
    		panic("preloadSyms: bad kind")
    	}
    
    	needNameExpansion := r.NeedNameExpansion()
    
    	loadingRuntimePkg := r.unit.Lib.Pkg == "runtime"
    
    		osym := r.Sym(i)
    
    		name := osym.Name(r.Reader)
    		if needNameExpansion {
    			name = strings.Replace(name, "\"\".", r.pkgprefix, -1)
    		}
    
    		v := abiToVer(osym.ABI(), r.version)
    
    		gi, added := l.AddSym(name, v, r, i, kind, dupok, sym.AbiSymKindToSymKind[objabi.SymKind(osym.Type())])
    
    		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, "go.itablink.") {
    
    			l.itablink[gi] = struct{}{}
    
    		if strings.HasPrefix(name, "runtime.") ||
    			(loadingRuntimePkg && strings.HasPrefix(name, "type.")) {
    
    			if bi := goobj2.BuiltinIdx(name, v); bi != -1 {
    				// This is a definition of a builtin symbol. Record where it is.
    
    		if a := osym.Align(); a != 0 {
    			l.SetSymAlign(gi, int32(a))
    		}
    
    // Add non-package symbols and references to external symbols (which are always
    // named).
    
    func (l *Loader) LoadNonpkgSyms(arch *sys.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) {
    
    	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)
    		}
    
    	}
    }
    
    func abiToVer(abi uint16, localSymVersion int) int {
    	var v int
    	if abi == goobj2.SymABIstatic {
    		// Static
    		v = localSymVersion
    	} else if abiver := sym.ABIToVersion(obj.ABI(abi)); abiver != -1 {
    		// Note that data symbols are "ABI0", which maps to version 0.
    		v = abiver
    	} else {
    		log.Fatalf("invalid symbol ABI: %d", abi)
    	}
    	return v
    }
    
    
    // preprocess looks for integer/floating point constant symbols whose
    // content is encoded into the symbol name, and promotes them into
    // real symbols with RODATA type and a payload that matches the
    // encoded content.
    func (l *Loader) preprocess(arch *sys.Arch, s Sym, name string) {
    	if name != "" && name[0] == '$' && len(name) > 5 && l.SymType(s) == 0 && len(l.Data(s)) == 0 {
    		x, err := strconv.ParseUint(name[5:], 16, 64)
    
    			log.Panicf("failed to parse $-symbol %s: %v", name, err)
    
    		su := l.MakeSymbolUpdater(s)
    		su.SetType(sym.SRODATA)
    		su.SetLocal(true)
    		switch name[:5] {
    
    		case "$f32.":
    			if uint64(uint32(x)) != x {
    
    				log.Panicf("$-symbol %s too large: %d", name, x)
    
    		case "$f64.", "$i64.":
    
    			log.Panicf("unrecognized $-symbol: %s", name)
    
    // 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 l.SymType(s) != sym.SABIALIAS {
    		return s
    	}
    	relocs := l.Relocs(s)
    	target := relocs.At2(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:
    
    // 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([]goobj2.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.At2(i)
    			pp.relocs[i].Set(rel.Off(), rel.Siz(), 0, rel.Add(), goobj2.SymRef{PkgIdx: 0, SymIdx: uint32(rel.Sym())})
    			pp.reltypes[i] = rel.Type()
    		}
    
    	// 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.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++ {
    
    		for ri := 0; ri < relocs.Count(); ri++ {
    
    			r := relocs.At2(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.NSym()+r.NNonpkgdef()); 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 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
    
    // 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...)
    
    // 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++ {
    
    		pi := interface{}("")
    		if l.IsExternal(i) {
    			pi = fmt.Sprintf("<ext %d>", l.extIndex(i))
    		}
    
    		fmt.Println(i, l.SymName(i), l.SymType(i), pi)
    
    	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)
    	}