github.com/eh-steve/goloader@v0.0.0-20240111193454-90ff3cfdae39/readobj.go (about)

     1  package goloader
     2  
     3  import (
     4  	"cmd/objfile/goobj"
     5  	"cmd/objfile/sys"
     6  	"fmt"
     7  	"github.com/eh-steve/goloader/obj"
     8  	"github.com/eh-steve/goloader/objabi/reloctype"
     9  	"github.com/eh-steve/goloader/objabi/symkind"
    10  	"io"
    11  	"log"
    12  	"math/rand"
    13  	"os"
    14  	"strings"
    15  	"unsafe"
    16  )
    17  
    18  func Parse(f *os.File, pkgpath *string) ([]string, error) {
    19  	pkg := obj.Pkg{Syms: make(map[string]*obj.ObjSymbol, 0),
    20  		F:             f,
    21  		PkgPath:       *pkgpath,
    22  		SymNamesByIdx: make(map[uint32]string),
    23  		Exports:       make(map[string]obj.ExportSymType),
    24  	}
    25  	symbols := make([]string, 0)
    26  	if err := pkg.Symbols(); err != nil {
    27  		return symbols, err
    28  	}
    29  	for _, sym := range pkg.Syms {
    30  		symbols = append(symbols, sym.Name)
    31  	}
    32  	return symbols, nil
    33  }
    34  
    35  func readObj(pkg *obj.Pkg, linker *Linker) error {
    36  	if pkg.PkgPath == EmptyString {
    37  		pkg.PkgPath = DefaultPkgPath
    38  	}
    39  	if err := pkg.Symbols(); err != nil {
    40  		return fmt.Errorf("read error: %v", err)
    41  	}
    42  	if linker.Arch != nil && linker.Arch.Name != pkg.Arch {
    43  		return fmt.Errorf("read obj error: Arch %s != Arch %s", linker.Arch.Name, pkg.Arch)
    44  	} else {
    45  		linker.Arch = getArch(pkg.Arch)
    46  	}
    47  	switch linker.Arch.Name {
    48  	case sys.ArchARM.Name, sys.ArchARM64.Name:
    49  		copy(linker.functab, obj.ModuleHeadarm)
    50  		linker.functab[len(obj.ModuleHeadarm)-1] = PtrSize
    51  	}
    52  
    53  	cuOffset := 0
    54  	for _, cuFiles := range linker.cuFiles {
    55  		cuOffset += len(cuFiles.Files)
    56  	}
    57  
    58  	for _, sym := range pkg.Syms {
    59  		for index, loc := range sym.Reloc {
    60  			if !strings.HasPrefix(sym.Reloc[index].Sym.Name, TypeStringPrefix) {
    61  				sym.Reloc[index].Sym.Name = strings.Replace(loc.Sym.Name, EmptyPkgPath, pkg.PkgPath, -1)
    62  			}
    63  		}
    64  		if sym.Type != EmptyString {
    65  			sym.Type = strings.Replace(sym.Type, EmptyPkgPath, pkg.PkgPath, -1)
    66  		}
    67  		if sym.Func != nil {
    68  			for index, FuncData := range sym.Func.FuncData {
    69  				sym.Func.FuncData[index] = strings.Replace(FuncData, EmptyPkgPath, pkg.PkgPath, -1)
    70  			}
    71  			sym.Func.CUOffset += cuOffset
    72  		}
    73  	}
    74  	for _, sym := range pkg.Syms {
    75  		linker.objsymbolMap[sym.Name] = sym
    76  	}
    77  	linker.cuFiles = append(linker.cuFiles, pkg.CUFiles...)
    78  	linker.initFuncs = append(linker.initFuncs, getInitFuncName(pkg.PkgPath))
    79  	return nil
    80  }
    81  
    82  type LinkerOptFunc func(options *LinkerOptions)
    83  
    84  type LinkerOptions struct {
    85  	SymbolNameOrder                  []string
    86  	RandomSymbolNameOrder            bool
    87  	RelocationDebugWriter            io.Writer
    88  	DumpTextBeforeAndAfterRelocs     bool
    89  	NoRelocationEpilogues            bool
    90  	SkipTypeDeduplicationForPackages []string
    91  	ForceTestRelocationEpilogues     bool
    92  }
    93  
    94  // WithSymbolNameOrder allows you to control the sequence (placement in memory) of symbols from an object file.
    95  // When not set, the order as parsed from the archive file is used.
    96  func WithSymbolNameOrder(symNames []string) func(*LinkerOptions) {
    97  	return func(options *LinkerOptions) {
    98  		options.SymbolNameOrder = symNames
    99  	}
   100  }
   101  
   102  func WithRandomSymbolNameOrder() func(*LinkerOptions) {
   103  	return func(options *LinkerOptions) {
   104  		options.RandomSymbolNameOrder = true
   105  	}
   106  }
   107  
   108  func WithRelocationDebugWriter(writer io.Writer) func(*LinkerOptions) {
   109  	return func(options *LinkerOptions) {
   110  		options.RelocationDebugWriter = writer
   111  	}
   112  }
   113  
   114  func WithNoRelocationEpilogues() func(*LinkerOptions) {
   115  	return func(options *LinkerOptions) {
   116  		options.NoRelocationEpilogues = true
   117  	}
   118  }
   119  
   120  func WithDumpTextBeforeAndAfterRelocs() func(*LinkerOptions) {
   121  	return func(options *LinkerOptions) {
   122  		options.DumpTextBeforeAndAfterRelocs = true
   123  	}
   124  }
   125  
   126  func WithSkipTypeDeduplicationForPackages(packages []string) func(*LinkerOptions) {
   127  	return func(options *LinkerOptions) {
   128  		options.SkipTypeDeduplicationForPackages = packages
   129  	}
   130  }
   131  
   132  func WithForceTestRelocationEpilogues() func(*LinkerOptions) {
   133  	return func(options *LinkerOptions) {
   134  		options.ForceTestRelocationEpilogues = true
   135  	}
   136  }
   137  
   138  func resolveSymRefName(symRef goobj.SymRef, pkgs []*obj.Pkg, objByPkg map[string]uint32, objIdx uint32) (symName, pkgName string) {
   139  	pkg := pkgs[objIdx-1]
   140  	pkgName = pkg.ReferencedPkgs[symRef.PkgIdx]
   141  	fileIdx := objByPkg[pkgName]
   142  	if fileIdx == 0 {
   143  		return "", pkgName
   144  	}
   145  	return pkgs[fileIdx-1].SymNamesByIdx[symRef.SymIdx], pkgName
   146  }
   147  
   148  func ReadObjs(files []string, pkgPath []string, globalSymPtr map[string]uintptr, linkerOpts ...LinkerOptFunc) (*Linker, error) {
   149  	linker, err := initLinker(linkerOpts)
   150  	if err != nil {
   151  		return nil, err
   152  	}
   153  	var osFiles []*os.File
   154  	defer func() {
   155  		for _, f := range osFiles {
   156  			_ = f.Close()
   157  		}
   158  	}()
   159  	var symNames []string
   160  	objByPkg := map[string]uint32{}
   161  	var pkgs = make([]*obj.Pkg, 0, len(files))
   162  	for i, file := range files {
   163  		f, err := os.Open(file)
   164  		if err != nil {
   165  			return nil, err
   166  		}
   167  		osFiles = append(osFiles, f)
   168  		pkg := obj.Pkg{
   169  			Syms:          make(map[string]*obj.ObjSymbol, 0),
   170  			F:             f,
   171  			PkgPath:       pkgPath[i],
   172  			Objidx:        uint32(i + 1),
   173  			SymNamesByIdx: make(map[uint32]string),
   174  			Exports:       make(map[string]obj.ExportSymType),
   175  		}
   176  		objByPkg[pkgPath[i]] = pkg.Objidx
   177  		if err := readObj(&pkg, linker); err != nil {
   178  			return nil, err
   179  		}
   180  		pkgs = append(pkgs, &pkg)
   181  		symNames = append(symNames, pkg.SymNameOrder...)
   182  	}
   183  
   184  	for _, symName := range symNames {
   185  		objSym := linker.objsymbolMap[symName]
   186  		if strings.HasPrefix(objSym.Type, obj.UnresolvedSymRefPrefix) {
   187  			// This type symbol was likely in another package and so was unresolved at the time of loading the archive,
   188  			// but we might have added the missing package in a later archive, so try to resolve again.
   189  			unresolved := objSym.Type
   190  			var pkgName string
   191  			symRef := obj.ParseUnresolvedIdxString(objSym.Type)
   192  			objSym.Type, pkgName = resolveSymRefName(symRef, pkgs, objByPkg, objSym.Objidx)
   193  			if objSym.Type == "" {
   194  				// Still unresolved, add a fake invalid symbol entry and reloc for this symbol to prevent the linker progressing
   195  				linker.symMap[pkgName+"."+unresolved] = &obj.Sym{Name: pkgName + "." + unresolved, Offset: InvalidOffset, Pkg: pkgName}
   196  				objSym.Reloc = append(objSym.Reloc, obj.Reloc{Sym: linker.symMap[pkgName+"."+unresolved], Type: reloctype.R_KEEP})
   197  				linker.pkgNamesWithUnresolved[pkgName] = struct{}{}
   198  				objSym.Type = pkgName + "." + unresolved // keep it reachable by making the type non-empty
   199  				if objSym.Kind == symkind.SDATA || objSym.Kind == symkind.SBSS {
   200  					// Force both the symbol and the type to be reachable if it's a static/global variable which could contain pointers
   201  					// and therefore needs a gcmask/prog, to ensure we rebuild the package which defines the type
   202  					linker.collectReachableSymbols(objSym.Name)
   203  					linker.collectReachableSymbols(objSym.Type)
   204  				}
   205  			}
   206  		}
   207  		if _, presentInFirstModule := globalSymPtr[objSym.Name]; presentInFirstModule {
   208  			// If a symbol is reachable from the firstmodule, we should mark it as reachable for us too,
   209  			// even if our package can't reach it, since the first module might call a previously unreachable method via our JIT module
   210  			linker.collectReachableTypes(objSym.Name)
   211  			linker.collectReachableSymbols(objSym.Name)
   212  		}
   213  	}
   214  
   215  	if len(linker.options.SymbolNameOrder) > 0 {
   216  		if len(symNames) == len(linker.options.SymbolNameOrder) {
   217  			isOk := true
   218  			for _, symName := range linker.options.SymbolNameOrder {
   219  				if _, ok := linker.objsymbolMap[symName]; !ok {
   220  					isOk = false
   221  				}
   222  			}
   223  			if isOk {
   224  				log.Printf("linker using provided symbol name order for %d symbols", len(linker.options.SymbolNameOrder))
   225  				symNames = linker.options.SymbolNameOrder
   226  			}
   227  		}
   228  	}
   229  	if linker.options.RandomSymbolNameOrder {
   230  		rand.Shuffle(len(symNames), func(i, j int) {
   231  			symNames[i], symNames[j] = symNames[j], symNames[i]
   232  		})
   233  	}
   234  	mainPkgSyms := pkgs[len(pkgs)-1].Syms
   235  
   236  	for symName := range mainPkgSyms {
   237  		linker.collectReachableTypes(symName)
   238  	}
   239  	for symName := range mainPkgSyms {
   240  		linker.collectReachableSymbols(symName)
   241  	}
   242  
   243  	firstModuleTypesToForceRebuild := map[*_type]*obj.ObjSymbol{}
   244  
   245  	firstmodule := activeModules()[0]
   246  	for reachable := range linker.reachableSymbols {
   247  		objSym := linker.objsymbolMap[reachable]
   248  		if objSym == nil {
   249  			continue
   250  		}
   251  		for _, reloc := range objSym.Reloc {
   252  			// The new module uses an interface method - we need to check firstmodule's itabs to see whether any types
   253  			// already implement this interface but whose method is unreachable, as there's a risk that the new module
   254  			// could call one of these methods and hit a "fatal error: unreachable method called. linker bug?"
   255  			if reloc.Type == reloctype.R_USEIFACEMETHOD {
   256  				ifaceType := linker.objsymbolMap[reloc.Sym.Name]
   257  				if ifaceType == nil {
   258  					// Must be a firstmodule iface which we didn't rebuild
   259  					for _, itab := range firstmodule.itablinks {
   260  						if TypePrefix+resolveFullyQualifiedSymbolName(&itab.inter.typ) == reloc.Sym.Name {
   261  							m := (*method)(add(unsafe.Pointer(itab.inter), uintptr(reloc.Add)))
   262  							methodName := itab.inter.typ.nameOff(m.name).name()
   263  							u := itab._type.uncommon()
   264  							for _, method := range u.methods() {
   265  								if itab._type.nameOff(method.name).name() == methodName && (method.ifn == -1 || method.tfn == -1) {
   266  									firstModuleTypesToForceRebuild[itab._type] = objSym
   267  								}
   268  							}
   269  						}
   270  					}
   271  				}
   272  			}
   273  		}
   274  	}
   275  
   276  outer:
   277  	for t, objSym := range firstModuleTypesToForceRebuild {
   278  		name := TypePrefix + resolveFullyQualifiedSymbolName(t)
   279  		pkgName := t.PkgPath()
   280  		for _, pkg := range linker.pkgs {
   281  			if pkg.PkgPath == pkgName {
   282  				continue outer
   283  			}
   284  		}
   285  		if _, ok := linker.objsymbolMap[name]; !ok {
   286  			linker.symMap[name] = &obj.Sym{Name: name, Offset: InvalidOffset, Pkg: pkgName, Kind: symkind.Sxxx}
   287  			linker.objsymbolMap[name] = &obj.ObjSymbol{
   288  				Name: name,
   289  				Kind: symkind.Sxxx,
   290  				Func: &obj.FuncInfo{},
   291  				Pkg:  pkgName,
   292  			}
   293  			symNames = append(symNames, name)
   294  			objSym.Reloc = append(objSym.Reloc, obj.Reloc{Sym: linker.symMap[name], Type: reloctype.R_KEEP})
   295  		}
   296  		linker.pkgNamesToForceRebuild[pkgName] = struct{}{}
   297  		linker.collectReachableSymbols(name)
   298  		linker.collectReachableTypes(name)
   299  	}
   300  	if err := linker.addSymbols(symNames, globalSymPtr); err != nil {
   301  		return nil, err
   302  	}
   303  	linker.pkgs = pkgs
   304  
   305  	linker.pkgsByName = map[string]*obj.Pkg{}
   306  	for _, pkg := range pkgs {
   307  		linker.pkgsByName[pkg.PkgPath] = pkg
   308  	}
   309  
   310  	return linker, nil
   311  }
   312  
   313  func (linker *Linker) isTypeReachable(symName string) bool {
   314  	_, reachable := linker.reachableTypes[symName]
   315  	return reachable
   316  }
   317  
   318  func (linker *Linker) isSymbolReachable(symName string) bool {
   319  	_, reachable := linker.reachableSymbols[symName]
   320  	return reachable
   321  }
   322  
   323  func (linker *Linker) collectReachableTypes(symName string) {
   324  	if _, ok := linker.reachableTypes[symName]; ok {
   325  		return
   326  	}
   327  	if strings.HasPrefix(symName, TypePrefix) && !strings.HasPrefix(symName, TypeDoubleDotPrefix) {
   328  		linker.reachableTypes[symName] = struct{}{}
   329  		if strings.HasPrefix(symName, TypePrefix+"*") {
   330  			nonPtr := TypePrefix + strings.TrimPrefix(symName, TypePrefix+"*")
   331  			linker.reachableTypes[nonPtr] = struct{}{}
   332  			if nonPtrSym, ok := linker.symMap[nonPtr]; ok {
   333  				for _, reloc := range nonPtrSym.Reloc {
   334  					if strings.HasPrefix(reloc.Sym.Name, TypePrefix) && !strings.HasPrefix(reloc.Sym.Name, TypeDoubleDotPrefix) {
   335  						linker.collectReachableTypes(reloc.Sym.Name)
   336  					}
   337  				}
   338  			}
   339  		}
   340  
   341  		objsym := linker.objsymbolMap[symName]
   342  		if objsym != nil {
   343  			for _, reloc := range objsym.Reloc {
   344  				if strings.HasPrefix(reloc.Sym.Name, TypePrefix) && !strings.HasPrefix(reloc.Sym.Name, TypeDoubleDotPrefix) {
   345  					linker.collectReachableTypes(reloc.Sym.Name)
   346  				}
   347  			}
   348  		}
   349  	}
   350  }
   351  
   352  func (linker *Linker) collectReachableSymbols(symName string) {
   353  	// Don't have to be as clever as linker's deadcode.go - just add everything we can reference conservatively
   354  	if _, ok := linker.reachableSymbols[symName]; ok {
   355  		return
   356  	}
   357  
   358  	linker.reachableSymbols[symName] = struct{}{}
   359  	if strings.HasPrefix(symName, TypePrefix+"*") {
   360  		nonPtr := TypePrefix + strings.TrimPrefix(symName, TypePrefix+"*")
   361  		linker.collectReachableSymbols(nonPtr)
   362  	}
   363  
   364  	objsym := linker.objsymbolMap[symName]
   365  	if objsym != nil {
   366  		if objsym.Type != "" {
   367  			linker.collectReachableSymbols(objsym.Type)
   368  		}
   369  
   370  		for _, reloc := range objsym.Reloc {
   371  			linker.collectReachableSymbols(reloc.Sym.Name)
   372  		}
   373  		if objsym.Func != nil {
   374  			for _, inl := range objsym.Func.InlTree {
   375  				linker.collectReachableSymbols(inl.Func)
   376  			}
   377  			for _, funcData := range objsym.Func.FuncData {
   378  				linker.collectReachableSymbols(funcData)
   379  			}
   380  		}
   381  	}
   382  }