Skip to content
Snippets Groups Projects
loader.go 74.8 KiB
Newer Older
  • Learn to ignore specific revisions
  • 		if int(i) < len(l.Syms) && l.Syms[i] != nil {
    
    		}
    		if l.IsExternal(i) {
    			panic("Can't load an external symbol.")
    		}
    
    		return l.loadSymbol(name, version)
    
    	s := l.allocSym(name, version)
    
    // cloneToExternal takes the existing object file symbol (symIdx)
    // and creates a new external symbol 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 a new and similarly
    // named external copy of that symbol.
    func (l *Loader) cloneToExternal(symIdx Sym) Sym {
    	if l.IsExternal(symIdx) {
    		panic("sym is already external, no need for clone")
    	}
    
    	// Read the particulars from object.
    	osym := goobj2.Sym{}
    	r, li := l.toLocal(symIdx)
    	osym.Read(r.Reader, r.SymOff(li))
    	sname := strings.Replace(osym.Name, "\"\".", r.pkgprefix, -1)
    	sver := abiToVer(osym.ABI, r.version)
    	skind := sym.AbiSymKindToSymKind[objabi.SymKind(osym.Type)]
    
    	// Create new symbol, update version and kind.
    	ns := l.newExtSym(sname, sver)
    
    	pp := l.payloads[ns-l.extStart]
    
    	pp.kind = skind
    	pp.ver = sver
    	pp.size = int64(osym.Siz)
    
    
    	// 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 < (r.NSym() + r.NNonpkgdef()) {
    
    		// Copy relocations
    		relocs := l.Relocs(symIdx)
    		pp.relocs = relocs.ReadAll(nil)
    
    		// Copy data
    		pp.data = r.Data(li)
    
    		// Copy read-only attr
    		if r.ReadOnly() {
    			l.attrReadOnly[ns] = true
    		}
    	}
    
    
    	// If we're overriding a data symbol, collect the associated
    	// Gotype, so as to propagate it to the new symbol.
    	naux := r.NAux(li)
    	for j := 0; j < naux; j++ {
    		a := goobj2.Aux{}
    		a.Read(r.Reader, r.AuxOff(li, j))
    		switch a.Type {
    		case goobj2.AuxGotype:
    			pp.gotype = l.resolve(r, a.Sym)
    		default:
    			log.Fatalf("internal error: cloneToExternal applied to %s symbol %s with non-gotype aux data %d", skind.String(), sname, a.Type)
    		}
    	}
    
    
    	// Fix up the lookup tables if the symbol in question was
    	// present in the lookup tables. At the moment it only makes
    	// sense to do this sort of clone/update for symbols that are
    	// in the symbol table (as opposed to anonymous symbols);
    	// issue an error if we can't look up the original symbol.
    	if sver >= sym.SymVerStatic {
    		s, ok := l.extStaticSyms[nameVer{sname, sver}]
    		if !ok || s != symIdx {
    			panic("lookup failed for clone of non-external static symbol")
    		}
    		l.extStaticSyms[nameVer{sname, sver}] = ns
    	} else {
    		s, ok := l.symsByName[sver][sname]
    		if !ok || s != symIdx {
    			panic("lookup failed for clone of non-external symbol")
    		}
    		l.symsByName[sver][sname] = ns
    	}
    
    
    	// Copy over selected attributes / properties. This is
    	// probably overkill for most of these attributes, but it's
    	// simpler just to copy everything.
    	l.copyAttributes(symIdx, ns)
    	if l.SymExtname(symIdx) != "" {
    		l.SetSymExtname(ns, l.SymExtname(symIdx))
    	}
    	if l.SymDynimplib(symIdx) != "" {
    		l.SetSymDynimplib(ns, l.SymDynimplib(symIdx))
    	}
    	if l.SymDynimpvers(symIdx) != "" {
    		l.SetSymDynimpvers(ns, l.SymDynimpvers(symIdx))
    	}
    
    
    	// Add an overwrite entry (in case there are relocations against
    	// the old symbol).
    	l.overwrite[symIdx] = ns
    
    	return ns
    }
    
    
    // copyAttributes copies over all of the attributes of symbol 'src' to
    // symbol 'dst'. The assumption is that 'dst' is an external symbol.
    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))
    
    	l.SetAttrVisibilityHidden(dst, l.AttrVisibilityHidden(src))
    	l.SetAttrDuplicateOK(dst, l.AttrDuplicateOK(src))
    	l.SetAttrShared(dst, l.AttrShared(src))
    	l.SetAttrExternal(dst, l.AttrExternal(src))
    	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))
    
    }
    
    // migrateAttributes copies over all of the attributes of symbol 'src' to
    // sym.Symbol 'dst'.
    func (l *Loader) migrateAttributes(src Sym, dst *sym.Symbol) {
    	src = l.getOverwrite(src)
    	dst.Attr.Set(sym.AttrReachable, l.AttrReachable(src))
    	dst.Attr.Set(sym.AttrOnList, l.AttrOnList(src))
    	dst.Attr.Set(sym.AttrLocal, l.AttrLocal(src))
    
    	dst.Attr.Set(sym.AttrNotInSymbolTable, l.AttrNotInSymbolTable(src))
    
    	dst.Attr.Set(sym.AttrVisibilityHidden, l.AttrVisibilityHidden(src))
    	dst.Attr.Set(sym.AttrDuplicateOK, l.AttrDuplicateOK(src))
    	dst.Attr.Set(sym.AttrShared, l.AttrShared(src))
    	dst.Attr.Set(sym.AttrExternal, l.AttrExternal(src))
    	dst.Attr.Set(sym.AttrTopFrame, l.AttrTopFrame(src))
    	dst.Attr.Set(sym.AttrSpecial, l.AttrSpecial(src))
    	dst.Attr.Set(sym.AttrCgoExportDynamic, l.AttrCgoExportDynamic(src))
    	dst.Attr.Set(sym.AttrCgoExportStatic, l.AttrCgoExportStatic(src))
    
    	dst.Attr.Set(sym.AttrReadOnly, l.AttrReadOnly(src))
    
    
    	// Convert outer/sub relationships
    	if outer, ok := l.outer[src]; ok {
    		dst.Outer = l.Syms[outer]
    	}
    	if sub, ok := l.sub[src]; ok {
    		dst.Sub = l.Syms[sub]
    	}
    
    	// Set sub-symbol attribute. FIXME: would be better to do away
    	// with this and just use l.OuterSymbol() != 0 elsewhere within
    	// the linker.
    	dst.Attr.Set(sym.AttrSubSymbol, dst.Outer != nil)
    
    	// Copy over dynimplib, dynimpvers, extname.
    	if l.SymExtname(src) != "" {
    		dst.SetExtname(l.SymExtname(src))
    	}
    	if l.SymDynimplib(src) != "" {
    		dst.SetDynimplib(l.SymDynimplib(src))
    	}
    	if l.SymDynimpvers(src) != "" {
    		dst.SetDynimpvers(l.SymDynimpvers(src))
    	}
    
    	// Copy ELF type if set.
    	if et, ok := l.elfType[src]; ok {
    		dst.SetElfType(et)
    	}
    }
    
    
    // CreateExtSym creates a new external symbol with the specified name
    // without adding it to any lookup tables, returning a Sym index for it.
    func (l *Loader) CreateExtSym(name string) Sym {
    
    	// Assign a new unique negative version -- this is to mark the
    	// symbol so that it can be skipped when ExtractSymbols is adding
    	// ext syms to the sym.Symbols hash.
    	l.anonVersion--
    	return l.newExtSym(name, l.anonVersion)
    
    // Create creates a symbol with the specified name, returning a
    // sym.Symbol object for it. This method is intended for static/hidden
    // symbols discovered while loading host objects. We can see more than
    // one instance of a given static symbol with the same name/version,
    // so we can't add them to the lookup tables "as is". Instead assign
    // them fictitious (unique) versions, starting at -1 and decreasing by
    // one for each newly created symbol, and record them in the
    // extStaticSyms hash.
    
    func (l *Loader) Create(name string) *sym.Symbol {
    
    	i := l.max + 1
    	l.max++
    	if l.extStart == 0 {
    		l.extStart = i
    	}
    
    
    	// Assign a new unique negative version -- this is to mark the
    	// symbol so that it can be skipped when ExtractSymbols is adding
    	// ext syms to the sym.Symbols hash.
    	l.anonVersion--
    	ver := l.anonVersion
    
    	s := l.allocSym(name, ver)
    
    	l.extStaticSyms[nameVer{name, ver}] = i
    
    
    func loadObjFull(l *Loader, r *oReader) {
    	lib := r.unit.Lib
    
    
    	resolveSymRef := func(s goobj2.SymRef) *sym.Symbol {
    
    	funcs := []funcInfoSym{}
    	fdsyms := []*sym.Symbol{}
    	var funcAllocCounts funcAllocInfo
    
    	for i, n := 0, r.NSym()+r.NNonpkgdef(); i < n; i++ {
    		osym := goobj2.Sym{}
    		osym.Read(r.Reader, r.SymOff(i))
    		name := strings.Replace(osym.Name, "\"\".", r.pkgprefix, -1)
    
    		if name == "" {
    			continue
    		}
    		ver := abiToVer(osym.ABI, r.version)
    
    			if dupsym := l.symsByName[ver][name]; dupsym != istart+Sym(i) {
    
    				if l.attrReachable.has(dupsym) {
    
    					// A dupok symbol is resolved to another package. We still need
    					// to record its presence in the current package, as the trampoline
    					// pass expects packages are laid out in dependency order.
    					s := l.Syms[dupsym]
    					if s.Type == sym.STEXT {
    						lib.DupTextSyms = append(lib.DupTextSyms, s)
    
    						lib.DupTextSyms2 = append(lib.DupTextSyms2, sym.LoaderSym(dupsym))
    
    		if s.Name != name { // Sanity check. We can remove it in the final version.
    			fmt.Println("name mismatch:", lib, i, s.Name, name)
    			panic("name mismatch")
    		}
    
    
    		local := osym.Local()
    		makeTypelink := osym.Typelink()
    
    		size := osym.Siz
    
    		// Symbol data
    		s.P = r.Data(i)
    		s.Attr.Set(sym.AttrReadOnly, r.ReadOnly())
    
    		batch := l.relocBatch
    		s.R = batch[:relocs.Count:relocs.Count]
    		l.relocBatch = batch[relocs.Count:]
    
    		naux := r.NAux(i)
    		for j := 0; j < naux; j++ {
    			a := goobj2.Aux{}
    
    			switch a.Type {
    			case goobj2.AuxGotype:
    				typ := resolveSymRef(a.Sym)
    				if typ != nil {
    					s.Gotype = typ
    				}
    
    				fdsyms = append(fdsyms, resolveSymRef(a.Sym))
    
    			case goobj2.AuxFuncInfo:
    				if a.Sym.PkgIdx != goobj2.PkgIdxSelf {
    					panic("funcinfo symbol not defined in current package")
    				}
    				isym = int(a.Sym.SymIdx)
    
    			case goobj2.AuxDwarfInfo, goobj2.AuxDwarfLoc, goobj2.AuxDwarfRanges, goobj2.AuxDwarfLines:
    				// ignored for now
    
    		s.File = r.pkgprefix[:len(r.pkgprefix)-1]
    
    		if dupok {
    			s.Attr |= sym.AttrDuplicateOK
    		}
    		if s.Size < int64(size) {
    			s.Size = int64(size)
    		}
    		s.Attr.Set(sym.AttrLocal, local)
    		s.Attr.Set(sym.AttrMakeTypelink, makeTypelink)
    
    
    		if s.Type == sym.SDWARFINFO {
    			// For DWARF symbols, replace `"".` to actual package prefix
    			// in the symbol content.
    			// TODO: maybe we should do this in the compiler and get rid
    			// of this.
    			patchDWARFName(s, r)
    		}
    
    
    		if s.Type != sym.STEXT {
    			continue
    		}
    
    		if isym == -1 {
    			continue
    		}
    
    
    		// Record function sym and associated info for additional
    		// processing in the loop below.
    		fwis := funcInfoSym{s: s, isym: isym, osym: osym}
    		funcs = append(funcs, fwis)
    
    		// Read the goobj2.FuncInfo for this text symbol so that we can
    		// collect allocation counts. We'll read it again in the loop
    		// below.
    		b := r.Data(isym)
    		info := goobj2.FuncInfo{}
    		info.Read(b)
    		funcAllocCounts.symPtr += uint32(len(info.File))
    		funcAllocCounts.pcData += uint32(len(info.Pcdata))
    		funcAllocCounts.inlCall += uint32(len(info.InlTree))
    		funcAllocCounts.fdOff += uint32(len(info.Funcdataoff))
    	}
    
    	// At this point we can do batch allocation of the sym.FuncInfo's,
    	// along with the slices of sub-objects they use.
    	fiBatch := make([]sym.FuncInfo, len(funcs))
    	inlCallBatch := make([]sym.InlinedCall, funcAllocCounts.inlCall)
    	symPtrBatch := make([]*sym.Symbol, funcAllocCounts.symPtr)
    	pcDataBatch := make([]sym.Pcdata, funcAllocCounts.pcData)
    	fdOffBatch := make([]int64, funcAllocCounts.fdOff)
    
    	// Populate FuncInfo contents for func symbols.
    	for fi := 0; fi < len(funcs); fi++ {
    		s := funcs[fi].s
    		isym := funcs[fi].isym
    		osym := funcs[fi].osym
    
    		s.FuncInfo = &fiBatch[0]
    		fiBatch = fiBatch[1:]
    
    
    		info := goobj2.FuncInfo{}
    		info.Read(b)
    
    		if info.NoSplit != 0 {
    			s.Attr |= sym.AttrNoSplit
    		}
    
    			s.Attr |= sym.AttrReflectMethod
    		}
    
    		if r.Flags()&goobj2.ObjFlagShared != 0 {
    
    			s.Attr |= sym.AttrShared
    		}
    
    
    		if len(info.Funcdataoff) != 0 {
    			nfd := len(info.Funcdataoff)
    			pc.Funcdata = fdsyms[:nfd:nfd]
    			fdsyms = fdsyms[nfd:]
    
    
    		info.Pcdata = append(info.Pcdata, info.PcdataEnd) // for the ease of knowing where it ends
    
    		pc.Args = int32(info.Args)
    		pc.Locals = int32(info.Locals)
    
    
    		npc := len(info.Pcdata) - 1 // -1 as we appended one above
    		pc.Pcdata = pcDataBatch[:npc:npc]
    		pcDataBatch = pcDataBatch[npc:]
    
    		nfd := len(info.Funcdataoff)
    		pc.Funcdataoff = fdOffBatch[:nfd:nfd]
    		fdOffBatch = fdOffBatch[nfd:]
    
    		nsp := len(info.File)
    		pc.File = symPtrBatch[:nsp:nsp]
    		symPtrBatch = symPtrBatch[nsp:]
    
    		nic := len(info.InlTree)
    		pc.InlTree = inlCallBatch[:nic:nic]
    		inlCallBatch = inlCallBatch[nic:]
    
    
    		pc.Pcsp.P = r.BytesAt(pcdataBase+info.Pcsp, int(info.Pcfile-info.Pcsp))
    		pc.Pcfile.P = r.BytesAt(pcdataBase+info.Pcfile, int(info.Pcline-info.Pcfile))
    		pc.Pcline.P = r.BytesAt(pcdataBase+info.Pcline, int(info.Pcinline-info.Pcline))
    		pc.Pcinline.P = r.BytesAt(pcdataBase+info.Pcinline, int(info.Pcdata[0]-info.Pcinline))
    		for k := range pc.Pcdata {
    			pc.Pcdata[k].P = r.BytesAt(pcdataBase+info.Pcdata[k], int(info.Pcdata[k+1]-info.Pcdata[k]))
    		}
    
    			pc.Funcdataoff[k] = int64(info.Funcdataoff[k])
    		}
    		for k := range pc.File {
    			pc.File[k] = resolveSymRef(info.File[k])
    		}
    
    		for k := range pc.InlTree {
    			inl := &info.InlTree[k]
    			pc.InlTree[k] = sym.InlinedCall{
    				Parent:   inl.Parent,
    				File:     resolveSymRef(inl.File),
    				Line:     inl.Line,
    
    				Func:     l.SymName(l.resolve(r, inl.Func)),
    
    		if !dupok {
    			if s.Attr.OnList() {
    				log.Fatalf("symbol %s listed multiple times", s.Name)
    			}
    			s.Attr.Set(sym.AttrOnList, true)
    			lib.Textp = append(lib.Textp, s)
    
    			lib.Textp2 = append(lib.Textp2, sym.LoaderSym(isym))
    
    			// there may be a dup in another package
    
    			// put into a temp list and add to text later
    			lib.DupTextSyms = append(lib.DupTextSyms, s)
    
    			lib.DupTextSyms2 = append(lib.DupTextSyms2, sym.LoaderSym(isym))
    
    // convertRelocations takes a vector of loader.Reloc relocations and
    // translates them into an equivalent set of sym.Reloc relocations on
    // the symbol "dst", performing fixups along the way for ABI aliases,
    // etc. It is assumed that the called has pre-allocated the dst symbol
    // relocations slice.
    func (l *Loader) convertRelocations(src []Reloc, dst *sym.Symbol) {
    	for j := range dst.R {
    		r := src[j]
    		rs := r.Sym
    		sz := r.Size
    		rt := r.Type
    		if rt == objabi.R_METHODOFF {
    			if l.attrReachable.has(rs) {
    				rt = objabi.R_ADDROFF
    			} else {
    				sz = 0
    				rs = 0
    			}
    		}
    		if rt == objabi.R_WEAKADDROFF && !l.attrReachable.has(rs) {
    			rs = 0
    			sz = 0
    		}
    		if rs != 0 && l.Syms[rs] != nil && l.Syms[rs].Type == sym.SABIALIAS {
    			rsrelocs := l.Relocs(rs)
    			rs = rsrelocs.At(0).Sym
    		}
    		dst.R[j] = sym.Reloc{
    			Off:  r.Off,
    			Siz:  sz,
    			Type: rt,
    			Add:  r.Add,
    			Sym:  l.Syms[rs],
    		}
    	}
    }
    
    
    func patchDWARFName1(p []byte, r *oReader) ([]byte, int) {
    
    	// This is kind of ugly. Really the package name should not
    	// even be included here.
    
    	if len(p) < 1 || p[0] != dwarf.DW_ABRV_FUNCTION {
    		return p, -1
    
    	if !bytes.Contains(p[:e], emptyPkg) {
    		return p, -1
    
    	patched := bytes.Replace(p[:e], emptyPkg, pkgprefix, -1)
    	return append(patched, p[e:]...), e
    }
    
    func patchDWARFName(s *sym.Symbol, r *oReader) {
    	patched, e := patchDWARFName1(s.P, r)
    	if e == -1 {
    		return
    	}
    	s.P = patched
    
    	s.Attr.Set(sym.AttrReadOnly, false)
    	delta := int64(len(s.P)) - s.Size
    	s.Size = int64(len(s.P))
    	for i := range s.R {
    		r := &s.R[i]
    		if r.Off > int32(e) {
    			r.Off += int32(delta)
    		}
    	}
    }
    
    // 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{}
    	rslice := []Reloc{}
    	for si := Sym(1); si <= l.max; si++ {
    		if _, ok := l.overwrite[si]; ok {
    			continue
    		}
    		relocs := l.Relocs(si)
    		rslice = relocs.ReadAll(rslice)
    		for ri := 0; ri < relocs.Count; ri++ {
    			r := &rslice[ri]
    			if r.Sym != 0 && l.SymType(r.Sym) == sym.SXREF && l.RawSymName(r.Sym) != ".got" {
    				result = append(result, r.Sym)
    				if limit != -1 && len(result) >= limit {
    					break
    				}
    			}
    		}
    	}
    	return result
    }
    
    
    // For debugging.
    func (l *Loader) Dump() {
    	fmt.Println("objs")
    	for _, obj := range l.objs {
    		if obj.r != nil {
    			fmt.Println(obj.i, obj.r.unit.Lib)
    		}
    	}
    
    	fmt.Println("max:", l.max)
    
    	fmt.Println("syms")
    	for i, s := range l.Syms {
    		if i == 0 {
    			continue
    		}
    		if s != nil {
    			fmt.Println(i, s, s.Type)
    		} else {
    
    			otag := ""
    			si := Sym(i)
    			if _, ok := l.overwrite[si]; ok {
    				si = l.getOverwrite(si)
    				otag = fmt.Sprintf(" <overwritten to %d>", si)
    			}
    			fmt.Println(i, l.SymName(si), "<not loaded>", otag)
    
    		}
    	}
    	fmt.Println("overwrite:", l.overwrite)
    	fmt.Println("symsByName")
    
    	for name, i := range l.symsByName[0] {
    		fmt.Println(i, name, 0)
    	}
    	for name, i := range l.symsByName[1] {
    		fmt.Println(i, name, 1)