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 }