Skip to content
Snippets Groups Projects
loader.go 78.2 KiB
Newer Older
  • Learn to ignore specific revisions
  • 			return s
    		}
    		s := l.allocSym(name, ver)
    		l.installSym(i, s)
    		syms.Allsym = append(syms.Allsym, s) // XXX see above
    		return s
    	}
    	syms.ROLookup = func(name string, ver int) *sym.Symbol {
    		i := l.Lookup(name, ver)
    		return l.Syms[i]
    	}
    	syms.Rename = func(old, new string, ver int) {
    		// annoying... maybe there is a better way to do this
    		if ver >= 2 {
    			panic("cannot rename static symbol")
    		}
    		i := l.Lookup(old, ver)
    		s := l.Syms[i]
    		s.Name = new
    		if s.Extname() == old {
    			s.SetExtname(new)
    		}
    		delete(l.symsByName[ver], old)
    
    		// This mirrors the old code. But I'm not sure if the logic of
    		// handling dup in the old code actually works, or necessary.
    		dupi := l.symsByName[ver][new]
    		dup := l.Syms[dupi]
    		if dup == nil {
    			l.symsByName[ver][new] = i
    		} else {
    			if s.Type == 0 {
    				dup.Attr |= s.Attr
    				*s = *dup
    			} else if dup.Type == 0 {
    				s.Attr |= dup.Attr
    				*dup = *s
    				l.symsByName[ver][new] = i
    			}
    		}
    	}
    
    // allocSym allocates a new symbol backing.
    func (l *Loader) allocSym(name string, version int) *sym.Symbol {
    	batch := l.symBatch
    	if len(batch) == 0 {
    		batch = make([]sym.Symbol, 1000)
    	}
    	s := &batch[0]
    	l.symBatch = batch[1:]
    
    	s.Dynid = -1
    	s.Name = name
    	s.Version = int16(version)
    
    	return s
    }
    
    
    // installSym sets the underlying sym.Symbol for the specified sym index.
    func (l *Loader) installSym(i Sym, s *sym.Symbol) {
    	if s == nil {
    		panic("installSym nil symbol")
    	}
    	if l.Syms[i] != nil {
    
    		panic("sym already present in installSym")
    
    // addNewSym adds a new sym.Symbol to the i-th index in the list of symbols.
    
    func (l *Loader) addNewSym(i Sym, name string, ver int, unit *sym.CompilationUnit, t sym.SymKind) *sym.Symbol {
    	s := l.allocSym(name, ver)
    
    	if s.Type != 0 && s.Type != sym.SXREF {
    		fmt.Println("symbol already processed:", unit.Lib, i, s)
    		panic("symbol already processed")
    	}
    	if t == sym.SBSS && (s.Type == sym.SRODATA || s.Type == sym.SNOPTRBSS) {
    		t = s.Type
    	}
    	s.Type = t
    	s.Unit = unit
    
    // loadObjSyms creates sym.Symbol objects for the live Syms in the
    // object corresponding to object reader "r". Return value is the
    // number of sym.Reloc entries required for all the new symbols.
    func loadObjSyms(l *Loader, syms *sym.Symbols, r *oReader) int {
    	nr := 0
    
    	for i, n := 0, r.NSym()+r.NNonpkgdef(); i < n; i++ {
    
    		if r2, i2 := l.toLocal(gi); r2 != r || i2 != i {
    
    			continue // come from a different object
    		}
    
    		name := strings.Replace(osym.Name, "\"\".", r.pkgprefix, -1)
    		if name == "" {
    			continue
    
    		ver := abiToVer(osym.ABI, r.version)
    
    		t := sym.AbiSymKindToSymKind[objabi.SymKind(osym.Type)]
    		if t == sym.SXREF {
    			log.Fatalf("bad sxref")
    		}
    		if t == 0 {
    
    			log.Fatalf("missing type for %s in %s", name, r.unit.Lib)
    
    		if !l.attrReachable.Has(gi) && name != "runtime.addmoduledata" && name != "runtime.lastmoduledatap" {
    
    			// No need to load unreachable symbols.
    
    			// XXX reference to runtime.addmoduledata may be generated later by the linker in plugin mode.
    
    		s := l.addNewSym(gi, name, ver, r.unit, t)
    		l.migrateAttributes(gi, s)
    
    	return nr
    }
    
    // funcInfoSym records the sym.Symbol for a function, along with a copy
    // of the corresponding goobj2.Sym and the index of its FuncInfo aux sym.
    // We use this to delay populating FuncInfo until we can batch-allocate
    // slices for their sub-objects.
    type funcInfoSym struct {
    	s    *sym.Symbol // sym.Symbol for a live function
    	osym goobj2.Sym  // object file symbol data for that function
    	isym int         // global symbol index of FuncInfo aux sym for func
    }
    
    // funcAllocInfo records totals/counts for all functions in an objfile;
    // used to help with bulk allocation of sym.Symbol sub-objects.
    type funcAllocInfo struct {
    	symPtr  uint32 // number of *sym.Symbol's needed in file slices
    	inlCall uint32 // number of sym.InlinedCall's needed in inltree slices
    	pcData  uint32 // number of sym.Pcdata's needed in pdata slices
    	fdOff   uint32 // number of int64's needed in all Funcdataoff slices
    
    // 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.
    // XXX maybe rename? makeExtPayload?
    func (l *Loader) cloneToExternal(symIdx 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.
    
    	pi := l.newPayload(sname, sver)
    	pp := l.payloads[pi]
    
    	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)
    	}
    
    
    	// 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)
    		}
    	}
    
    
    	// 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, pi}
    	l.extReader.syms = append(l.extReader.syms, symIdx)
    
    // 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) {
    	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.AttrNoSplit, l.IsNoSplit(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)
    	}
    
    
    	// Copy pe objects values if set.
    	if plt, ok := l.plt[src]; ok {
    		dst.SetPlt(plt)
    	}
    	if got, ok := l.got[src]; ok {
    		dst.SetGot(got)
    	}
    
    // 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)
    
    func loadObjFull(l *Loader, r *oReader) {
    
    	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++ {
    
    		// A symbol may be a dup or overwritten. In this case, its
    		// content will actually be provided by a different object
    		// (to which its global index points). Skip those symbols.
    		gi := l.toGlobal(r, i)
    		var isdup bool
    		if r2, i2 := l.toLocal(gi); r2 != r || i2 != i {
    			isdup = true
    		}
    
    
    		osym.ReadWithoutName(r.Reader, r.SymOff(i))
    
    			if l.attrReachable.Has(gi) {
    
    				// 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[gi]
    				if s.Type == sym.STEXT {
    					lib.DupTextSyms = append(lib.DupTextSyms, s)
    
    			continue // come from a different object
    		}
    		s := l.Syms[gi]
    
    		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:]
    
    		l.convertRelocations(rslice, s, false)
    
    		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.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)
    
    
    			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)
    		} else {
    
    			// there may be a dup in another package
    
    			// put into a temp list and add to text later
    			lib.DupTextSyms = append(lib.DupTextSyms, s)
    		}
    
    // 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 caller has pre-allocated the dst symbol
    // relocations slice. If 'strict' is set, then this method will
    // panic if it finds a relocation targeting a nil symbol.
    func (l *Loader) convertRelocations(src []Reloc, dst *sym.Symbol, strict bool) {
    
    	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) {
    
    		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
    		}
    
    		if strict && rs != 0 && l.Syms[rs] == nil && rt != objabi.R_USETYPE {
    			panic("nil reloc target in convertRelocations")
    		}
    
    		dst.R[j] = sym.Reloc{
    			Off:  r.Off,
    			Siz:  sz,
    			Type: rt,
    			Add:  r.Add,
    			Sym:  l.Syms[rs],
    		}
    	}
    }
    
    
    // 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 < Sym(len(l.objSyms)); si++ {
    
    		rslice = relocs.ReadSyms(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
    }
    
    
    // AssignTextSymbolOrder populates the Textp2 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 Textp2 lists should be empty at this point.
    	for _, lib := range libs {
    		if len(lib.Textp2) != 0 {
    			panic("expected empty Textp2 slice for library")
    		}
    		if len(lib.DupTextSyms2) != 0 {
    			panic("expected empty DupTextSyms2 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 textp2 with reachable external syms.
    	textp2 := []Sym{}
    	for _, sym := range extsyms {
    		if !l.attrReachable.Has(sym) {
    			continue
    		}
    		textp2 = append(textp2, sym)
    	}
    
    
    	// Walk through all text symbols from Go object files and append
    	// them to their corresponding library's textp2 list.
    	for _, o := range l.objs[1:] {
    		r := o.r
    		lib := r.unit.Lib
    		for i, n := 0, r.NSym()+r.NNonpkgdef(); i < n; i++ {
    			gi := l.toGlobal(r, i)
    
    			osym := goobj2.Sym{}
    			osym.ReadWithoutName(r.Reader, r.SymOff(i))
    			st := sym.AbiSymKindToSymKind[objabi.SymKind(osym.Type)]
    			if st != sym.STEXT {
    				continue
    			}
    
    			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.DupTextSyms2 = append(lib.DupTextSyms2, sym.LoaderSym(gi))
    
    				continue // symbol in different object
    			}
    
    			if dupok {
    				lib.DupTextSyms2 = append(lib.DupTextSyms2, sym.LoaderSym(gi))
    			}
    
    
    			lib.Textp2 = append(lib.Textp2, sym.LoaderSym(gi))
    		}
    	}
    
    	// Now redo the assignment of text symbols to libs/units.
    	for _, doInternal := range [2]bool{true, false} {
    		for idx, lib := range libs {
    			if intlibs[idx] != doInternal {
    				continue
    			}
    			libtextp2 := []sym.LoaderSym{}
    
    			lists := [2][]sym.LoaderSym{lib.Textp2, lib.DupTextSyms2}
    			for _, list := range lists {
    				for _, s := range list {
    
    					if l.attrReachable.Has(sym) && !assignedToUnit.Has(sym) {
    
    						libtextp2 = append(libtextp2, s)
    
    						unit := l.SymUnit(sym)
    						if unit != nil {
    							unit.Textp2 = append(unit.Textp2, s)
    
    							assignedToUnit.Set(sym)
    
    // 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("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))
    		}
    		var s *sym.Symbol
    		if int(i) < len(l.Syms) {
    			s = l.Syms[i]
    
    			fmt.Println(i, s, s.Type, pi)
    
    			fmt.Println(i, l.SymName(i), "<not loaded>", 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)
    	}