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

     1  //go:build go1.16 && !go1.23
     2  // +build go1.16,!go1.23
     3  
     4  package obj
     5  
     6  import (
     7  	"bytes"
     8  	"cmd/objfile/archive"
     9  	"cmd/objfile/goobj"
    10  	"cmd/objfile/obj"
    11  	"cmd/objfile/objabi"
    12  	"compress/zlib"
    13  	"debug/elf"
    14  	"debug/macho"
    15  	"debug/pe"
    16  	"encoding/binary"
    17  	"encoding/hex"
    18  	"fmt"
    19  	"github.com/eh-steve/goloader/objabi/reloctype"
    20  	"github.com/eh-steve/goloader/objabi/symkind"
    21  	"go/token"
    22  	"io"
    23  	"sort"
    24  	"strings"
    25  )
    26  
    27  func (pkg *Pkg) Symbols() error {
    28  	a, err := archive.Parse(pkg.F, false)
    29  	if err != nil {
    30  		return err
    31  	}
    32  
    33  	for _, e := range a.Entries {
    34  		switch e.Type {
    35  		case archive.EntryPkgDef:
    36  			// nothing todo
    37  		case archive.EntryGoObj:
    38  			b := make([]byte, e.Obj.Size)
    39  			_, err := pkg.F.ReadAt(b, e.Obj.Offset)
    40  			if err != nil {
    41  				return err
    42  			}
    43  			r := goobj.NewReaderFromBytes(b, false)
    44  			// Name of referenced indexed symbols.
    45  			nrefName := r.NRefName()
    46  			refNames := make(map[goobj.SymRef]string, nrefName)
    47  			for i := 0; i < nrefName; i++ {
    48  				rn := r.RefName(i)
    49  				refNames[rn.Sym()] = rn.Name(r)
    50  			}
    51  			prevPkgLen := len(pkg.ReferencedPkgs)
    52  			npkg := r.NPkg()
    53  			pkg.ReferencedPkgs = append(pkg.ReferencedPkgs, make([]string, npkg)...)
    54  
    55  			for i := 1; i < npkg; i++ { // PkgIdx 0 is a dummy invalid package
    56  				pkgName := r.Pkg(i)
    57  				pkg.ReferencedPkgs[i+prevPkgLen] = pkgName
    58  			}
    59  			for _, imported := range r.Autolib() {
    60  				pkg.AutoLib = append(pkg.AutoLib, imported.Pkg)
    61  			}
    62  			pkg.Arch = e.Obj.Arch
    63  			nsym := r.NSym() + r.NHashed64def() + r.NHasheddef() + r.NNonpkgdef()
    64  			for i := 0; i < nsym; i++ {
    65  				pkg.addSym(r, uint32(i), &refNames, objabi.PathToPrefix(pkg.PkgPath))
    66  			}
    67  			files := make([]string, r.NFile())
    68  			for i := range files {
    69  				files[i] = r.File(i)
    70  			}
    71  
    72  			pkg.CUFiles = append(pkg.CUFiles, CompilationUnitFiles{
    73  				ArchiveName: e.Name,
    74  				Files:       files,
    75  			})
    76  		case archive.EntryNativeObj:
    77  			// CGo files must be parsed by an elf/macho etc. native reader
    78  			nr := io.NewSectionReader(pkg.F, e.Offset, e.Size)
    79  			elfFile, err := elf.NewFile(nr)
    80  			if err != nil {
    81  				_, _ = nr.Seek(0, 0)
    82  				machoFile, errMacho := macho.NewFile(nr)
    83  				if errMacho != nil {
    84  					_, _ = nr.Seek(0, 0)
    85  					peFile, errPE := pe.NewFile(nr)
    86  					if errPE != nil {
    87  						return fmt.Errorf("only elf, macho and PE relocations currently supported, failed to open as either: (%s): %w", err, errPE)
    88  					}
    89  					err = pkg.convertPERelocs(peFile, e)
    90  					if err != nil {
    91  						return err
    92  					}
    93  				} else {
    94  					err = pkg.convertMachoRelocs(machoFile, e)
    95  					if err != nil {
    96  						return err
    97  					}
    98  				}
    99  			} else {
   100  				err = pkg.convertElfRelocs(elfFile, e)
   101  				if err != nil {
   102  					return err
   103  				}
   104  			}
   105  		default:
   106  			return fmt.Errorf("Parse open %s: unrecognized archive member %s (%d)\n", pkg.F.Name(), e.Name, e.Type)
   107  		}
   108  	}
   109  	for _, sym := range pkg.Syms {
   110  		if !strings.HasPrefix(sym.Name, TypeStringPrefix) {
   111  			sym.Name = strings.Replace(sym.Name, EmptyPkgPath, pkg.PkgPath, -1)
   112  		}
   113  	}
   114  	for i, symName := range pkg.SymNameOrder {
   115  		if !strings.HasPrefix(symName, TypeStringPrefix) {
   116  			pkg.SymNameOrder[i] = strings.Replace(symName, EmptyPkgPath, pkg.PkgPath, -1)
   117  		}
   118  	}
   119  	return nil
   120  }
   121  
   122  func typePkgPath(symName string) (pkgName string) {
   123  	if strings.HasPrefix(symName, TypePrefix) && !strings.HasPrefix(symName, TypeDoubleDotPrefix) {
   124  		typeName := strings.TrimLeft(strings.TrimPrefix(symName, TypePrefix), "*")
   125  		if !strings.HasPrefix(typeName, "func(") &&
   126  			!strings.HasPrefix(typeName, "noalg") &&
   127  			!strings.HasPrefix(typeName, "map[") &&
   128  			!strings.HasPrefix(typeName, "map.bucket[") &&
   129  			!strings.HasPrefix(typeName, "map.iter[") &&
   130  			!strings.HasPrefix(typeName, "map.hdr[") &&
   131  			!strings.HasPrefix(typeName, "struct {") {
   132  			// Likely a named type defined in a package, but for some reason PkgIdx is PkgIdxNone
   133  			if strings.Count(typeName, ".") > 0 {
   134  				pkgName = funcPkgPath(typeName)
   135  			}
   136  		}
   137  	}
   138  	return
   139  }
   140  
   141  // TODO - share this implementation with goloader's
   142  func funcPkgPath(funcName string) string {
   143  	funcName = strings.TrimPrefix(funcName, TypeDoubleDotPrefix+"eq.")
   144  
   145  	// Anonymous struct methods can't have a package
   146  	if strings.HasPrefix(funcName, "go"+ObjSymbolSeparator+"struct {") || strings.HasPrefix(funcName, "go"+ObjSymbolSeparator+"*struct {") || strings.HasPrefix(funcName, "struct {") {
   147  		return ""
   148  	}
   149  	lastSlash := strings.LastIndexByte(funcName, '/')
   150  	if lastSlash == -1 {
   151  		lastSlash = 0
   152  	}
   153  
   154  	// Generic dictionaries
   155  	firstDict := strings.Index(funcName, "..dict")
   156  	if firstDict > 0 {
   157  		return funcName[:firstDict]
   158  	} else {
   159  		// Methods on structs embedding structs from other packages look funny, e.g.:
   160  		// regexp.(*onePassInst).regexp/syntax.op
   161  		firstBracket := strings.LastIndex(funcName, ".(")
   162  		if firstBracket > 0 && lastSlash > firstBracket {
   163  			lastSlash = firstBracket
   164  		}
   165  		firstSquareBracket := strings.Index(funcName, "[")
   166  		if firstSquareBracket > 0 && lastSlash > firstSquareBracket {
   167  			i := firstSquareBracket
   168  			for ; funcName[i] != '.' && i > 0; i-- {
   169  			}
   170  			return funcName[:i]
   171  		}
   172  	}
   173  
   174  	dot := lastSlash
   175  	for ; dot < len(funcName) && funcName[dot] != '.' && funcName[dot] != '(' && funcName[dot] != '['; dot++ {
   176  	}
   177  	pkgPath := funcName[:dot]
   178  	return strings.Trim(strings.TrimPrefix(pkgPath, "[...]"), " ")
   179  }
   180  
   181  func resolveSymRef(s goobj.SymRef, r *goobj.Reader, refNames *map[goobj.SymRef]string, pkgName string) (string, string, uint32) {
   182  	i := InvalidIndex
   183  	switch p := s.PkgIdx; p {
   184  	case goobj.PkgIdxInvalid:
   185  		if s.SymIdx != 0 {
   186  			panic("bad sym ref")
   187  		}
   188  		return EmptyString, "", i
   189  	case goobj.PkgIdxHashed64:
   190  		i = s.SymIdx + uint32(r.NSym())
   191  	case goobj.PkgIdxHashed:
   192  		i = s.SymIdx + uint32(r.NSym()+r.NHashed64def())
   193  	case goobj.PkgIdxNone:
   194  		i = s.SymIdx + uint32(r.NSym()+r.NHashed64def()+r.NHasheddef())
   195  		symName := r.Sym(i).Name(r)
   196  		if (strings.HasPrefix(symName, TypePrefix) && !strings.HasPrefix(symName, TypeDoubleDotPrefix+"eq.")) || strings.HasPrefix(symName, "go"+ObjSymbolSeparator+"info") || strings.HasPrefix(symName, "go"+ObjSymbolSeparator+"cuinfo") || strings.HasPrefix(symName, "go"+ObjSymbolSeparator+"interface {") {
   197  			pkgName = typePkgPath(symName)
   198  		} else {
   199  			pkgName = funcPkgPath(symName)
   200  		}
   201  	case goobj.PkgIdxBuiltin:
   202  		name, _ := goobj.BuiltinName(int(s.SymIdx))
   203  		return name, "", i
   204  	case goobj.PkgIdxSelf:
   205  		i = s.SymIdx
   206  	default:
   207  		return (*refNames)[s], r.Pkg(int(s.PkgIdx)), i
   208  	}
   209  	return r.Sym(i).Name(r), pkgName, i
   210  }
   211  
   212  func UnresolvedIdxString(symRef goobj.SymRef) string {
   213  	buf := make([]byte, len(UnresolvedSymRefPrefix)+8+8)
   214  	copy(buf, UnresolvedSymRefPrefix)
   215  	uint32buf := make([]byte, 4)
   216  	binary.LittleEndian.PutUint32(uint32buf, symRef.PkgIdx)
   217  	hex.Encode(buf[len(UnresolvedSymRefPrefix):], uint32buf)
   218  	binary.LittleEndian.PutUint32(uint32buf, symRef.SymIdx)
   219  	hex.Encode(buf[len(UnresolvedSymRefPrefix)+8:], uint32buf)
   220  	return string(buf)
   221  }
   222  
   223  func ParseUnresolvedIdxString(unresolved string) goobj.SymRef {
   224  	buf := make([]byte, 8)
   225  	n, err := hex.Decode(buf, []byte(unresolved[len(UnresolvedSymRefPrefix):]))
   226  	if err != nil || n != 8 {
   227  		panic(fmt.Sprintf("failed to decode %s: %s", unresolved, err))
   228  	}
   229  	return goobj.SymRef{
   230  		PkgIdx: binary.LittleEndian.Uint32(buf),
   231  		SymIdx: binary.LittleEndian.Uint32(buf[4:]),
   232  	}
   233  }
   234  
   235  func (pkg *Pkg) addSym(r *goobj.Reader, idx uint32, refNames *map[goobj.SymRef]string, pkgPath string) {
   236  	s := r.Sym(idx)
   237  	symbol := ObjSymbol{Name: s.Name(r), Kind: int(s.Type()), DupOK: s.Dupok(), Size: (int64)(s.Siz()), Func: &FuncInfo{ABI: s.ABI()}, Objidx: pkg.Objidx, Pkg: pkgPath}
   238  	if original, ok := pkg.Syms[symbol.Name]; ok {
   239  		if symbol.Kind == original.Kind && symbol.Func.ABI == original.Func.ABI && symbol.Size == original.Size {
   240  			return
   241  		}
   242  		if objabi.SymKind(symbol.Kind) == objabi.STEXT {
   243  			// We have only read FuncInfo of the original symbol by this point, not the new symbol (yet), so we have to infer which one is a wrapper and which one is the real func based on that
   244  			// Valid duplicate symbol names may be caused by ASM ABI0 versions of functions, and their autogenerated ABIInternal wrappers (or vice versa, ABIInternal funcs with ABI0 wrappers)
   245  			// We can keep the wrapper func under a separate mangled symbol name in case it's needed for direct calls,
   246  			// and replace the main symbol with the non-wrapper version (the compiler will have inlined the wrapper at any callsites anyway)
   247  			// https://go.googlesource.com/go/+/refs/heads/dev.regabi/src/cmd/compile/internal-abi.md
   248  			if obj.ABI(symbol.Func.ABI) == obj.ABI0 {
   249  				// reflect.callReflect/reflect.makeFuncStub are special ABI wrappers, not like typical ABI0/ABIInternal wrappers
   250  				if obj.ABI(original.Func.ABI) == obj.ABIInternal && symbol.Name != "reflect.callReflect" && symbol.Name != "reflect.makeFuncStub" {
   251  					if original.Func.FuncID == uint8(FuncIDWrapper) {
   252  						original.Name += ABIInternalSuffix
   253  						original.Pkg = symbol.Pkg
   254  						if _, ok := pkg.Syms[original.Name]; !ok {
   255  							pkg.SymNameOrder = append(pkg.SymNameOrder, original.Name)
   256  						}
   257  						pkg.Syms[original.Name] = original
   258  					} else {
   259  						symbol.Name += ABI0Suffix
   260  					}
   261  				} else {
   262  					if symbol.Name != "reflect.callReflect" && symbol.Name != "reflect.makeFuncStub" {
   263  						panic(fmt.Sprintf("unexpected duplicate symbol %s (original %s, new %s)", symbol.Name, obj.ABI(original.Func.ABI), obj.ABI(symbol.Func.ABI)))
   264  					}
   265  				}
   266  			} else if obj.ABI(symbol.Func.ABI) == obj.ABIInternal {
   267  				if obj.ABI(original.Func.ABI) == obj.ABI0 {
   268  					if original.Func.FuncID == uint8(FuncIDWrapper) {
   269  						original.Name += ABI0Suffix
   270  						original.Pkg = symbol.Pkg
   271  						if _, ok := pkg.Syms[original.Name]; !ok {
   272  							pkg.SymNameOrder = append(pkg.SymNameOrder, original.Name)
   273  						}
   274  						pkg.Syms[original.Name] = original
   275  					} else {
   276  						symbol.Name += ABIInternalSuffix
   277  					}
   278  				} else {
   279  					panic(fmt.Sprintf("unexpected duplicate symbol %s (original %s, new %s)", symbol.Name, obj.ABI(original.Func.ABI), obj.ABI(symbol.Func.ABI)))
   280  				}
   281  			}
   282  		} else if symbol.Size == 0 {
   283  			return
   284  		}
   285  	}
   286  	if objabi.SymKind(symbol.Kind) == objabi.Sxxx || symbol.Name == EmptyString {
   287  		return
   288  	}
   289  
   290  	if int(idx) > r.NSym()+r.NHashed64def()+r.NHasheddef() {
   291  		// Is nonpkgdef or nonpkgref
   292  		if !strings.HasPrefix(symbol.Name, "_cgo") {
   293  			// TODO - this really isn't ideal - needs to be more robust
   294  			if (strings.HasPrefix(symbol.Name, TypePrefix) && !strings.HasPrefix(symbol.Name, TypeDoubleDotPrefix+"eq.")) || strings.HasPrefix(symbol.Name, "go"+ObjSymbolSeparator+"info") || strings.HasPrefix(symbol.Name, "go"+ObjSymbolSeparator+"cuinfo") || strings.HasPrefix(symbol.Name, "go"+ObjSymbolSeparator+"interface {") {
   295  				symbol.Pkg = ""
   296  			} else {
   297  				symbol.Pkg = funcPkgPath(symbol.Name)
   298  			}
   299  		}
   300  	}
   301  	if objabi.SymKind(symbol.Kind) == objabi.SNOPTRBSS && strings.HasPrefix(symbol.Name, "_cgo_") && symbol.Size == 1 {
   302  		// This is a dummy symbol representing a byte whose address is taken to act as the function pointer to a CGo text address via the //go:linkname pragma
   303  		// We handle this separately at the end of convertMachoRelocs() by adding the actual target address as text under this symbol name.
   304  		return
   305  	}
   306  	if symbol.Size > 0 {
   307  		symbol.Data = r.Data(idx)
   308  		grow(&symbol.Data, (int)(symbol.Size))
   309  	} else {
   310  		symbol.Data = make([]byte, 0)
   311  	}
   312  
   313  	if _, ok := pkg.Syms[symbol.Name]; !ok {
   314  		pkg.SymNameOrder = append(pkg.SymNameOrder, symbol.Name)
   315  	}
   316  	pkg.SymNamesByIdx[idx] = symbol.Name
   317  	pkg.Syms[symbol.Name] = &symbol
   318  
   319  	auxs := r.Auxs(idx)
   320  	for k := 0; k < len(auxs); k++ {
   321  		auxSymRef := auxs[k].Sym()
   322  		parentPkgPath := pkgPath
   323  		name, pkgPath, index := resolveSymRef(auxSymRef, r, refNames, pkgPath)
   324  
   325  		switch auxs[k].Type() {
   326  		case goobj.AuxGotype:
   327  			if name == "" {
   328  				// Likely this type is defined in another package not yet loaded, so mark it as unresolved and resolve it later, after all packages
   329  				symbol.Type = UnresolvedIdxString(auxSymRef)
   330  			} else if objabi.SymKind(r.Sym(index).Type()) == objabi.Sxxx {
   331  				// This aux symref doesn't actually exist in the current package reader, so we add a fake reloc to force the package containing the symbol to be built
   332  				symbol.Reloc = append(symbol.Reloc, Reloc{
   333  					Offset: InvalidOffset,
   334  					Sym:    &Sym{Name: name, Offset: InvalidOffset, Pkg: pkgPath},
   335  					Type:   reloctype.R_KEEP,
   336  				})
   337  				symbol.Type = name
   338  			} else {
   339  				symbol.Type = name
   340  				symName := strings.TrimPrefix(symbol.Name, parentPkgPath+".")
   341  				if token.IsExported(symName) {
   342  					pkg.Exports[symName] = ExportSymType{
   343  						SymName:  symbol.Name,
   344  						TypeName: name,
   345  					}
   346  				}
   347  			}
   348  		case goobj.AuxFuncInfo:
   349  			funcInfo := goobj.FuncInfo{}
   350  			readFuncInfo(&funcInfo, r.Data(index), symbol.Func)
   351  			for _, index := range funcInfo.File {
   352  				symbol.Func.File = append(symbol.Func.File, r.File(int(index)))
   353  			}
   354  			cuOffset := 0
   355  			for _, cuFiles := range pkg.CUFiles {
   356  				cuOffset += len(cuFiles.Files)
   357  			}
   358  			symbol.Func.CUOffset = cuOffset
   359  			for _, inl := range funcInfo.InlTree {
   360  				funcname, pkgPath, _ := resolveSymRef(inl.Func, r, refNames, pkgPath)
   361  				funcname = strings.Replace(funcname, EmptyPkgPath, pkgPath, -1)
   362  				inlNode := InlTreeNode{
   363  					Parent:   int64(inl.Parent),
   364  					File:     r.File(int(inl.File)),
   365  					Line:     int64(inl.Line),
   366  					Func:     funcname,
   367  					ParentPC: int64(inl.ParentPC),
   368  				}
   369  				symbol.Func.InlTree = append(symbol.Func.InlTree, inlNode)
   370  			}
   371  		case goobj.AuxFuncdata:
   372  			symbol.Func.FuncData = append(symbol.Func.FuncData, name)
   373  		case goobj.AuxDwarfInfo:
   374  		case goobj.AuxDwarfLoc:
   375  		case goobj.AuxDwarfRanges:
   376  		case goobj.AuxDwarfLines:
   377  		case goobj.AuxPcsp:
   378  			symbol.Func.PCSP = r.Data(index)
   379  		case goobj.AuxPcfile:
   380  			symbol.Func.PCFile = r.Data(index)
   381  		case goobj.AuxPcline:
   382  			symbol.Func.PCLine = r.Data(index)
   383  		case goobj.AuxPcinline:
   384  			symbol.Func.PCInline = r.Data(index)
   385  		case goobj.AuxPcdata:
   386  			symbol.Func.PCData = append(symbol.Func.PCData, r.Data(index))
   387  		}
   388  		if _, ok := pkg.Syms[name]; !ok && index != InvalidIndex {
   389  			pkg.addSym(r, index, refNames, pkgPath)
   390  		}
   391  	}
   392  
   393  	relocs := r.Relocs(idx)
   394  	priorRelocs := len(symbol.Reloc)
   395  	for k := priorRelocs; k < len(relocs)+priorRelocs; k++ {
   396  		symbol.Reloc = append(symbol.Reloc, Reloc{})
   397  		symbol.Reloc[k].Add = int(relocs[k-priorRelocs].Add())
   398  		symbol.Reloc[k].Offset = int(relocs[k-priorRelocs].Off())
   399  		symbol.Reloc[k].Size = int(relocs[k-priorRelocs].Siz())
   400  		symbol.Reloc[k].Type = int(relocs[k-priorRelocs].Type())
   401  		name, pkgPath, index := resolveSymRef(relocs[k-priorRelocs].Sym(), r, refNames, pkgPath)
   402  		symbol.Reloc[k].Sym = &Sym{Name: name, Offset: InvalidOffset, Pkg: pkgPath}
   403  		if _, ok := pkg.Syms[name]; !ok && index != InvalidIndex {
   404  			pkg.addSym(r, index, refNames, pkgPath)
   405  		}
   406  	}
   407  }
   408  
   409  func (pkg *Pkg) convertElfRelocs(f *elf.File, e archive.Entry) error {
   410  	if f.Class != elf.ELFCLASS64 {
   411  		return fmt.Errorf("only 64-bit elf relocations currently supported")
   412  	}
   413  	if f.Machine != elf.EM_X86_64 && f.Machine != elf.EM_AARCH64 {
   414  		return fmt.Errorf("only amd64 and arm64 elf relocations currently supported")
   415  	}
   416  
   417  	elfSyms, err := f.Symbols()
   418  
   419  	if err != nil {
   420  		return fmt.Errorf("failed to extract symbols from objfile entry %s: %w", e.Name, err)
   421  	}
   422  
   423  	var textSect *elf.Section
   424  	var textIndex int
   425  	for i, s := range f.Sections {
   426  		if s.Name == ".text" {
   427  			textSect = s
   428  			textIndex = i
   429  			break
   430  		}
   431  	}
   432  
   433  	if textSect == nil {
   434  		return fmt.Errorf("failed to find .text elf section in objfile entry %s: %w", e.Name, err)
   435  	}
   436  
   437  	text, err := textSect.Data()
   438  	if err != nil {
   439  		return fmt.Errorf("failed to read text data from elf .text section %s: %w", e.Name, err)
   440  	}
   441  	textOffset := textSect.Addr
   442  
   443  	var (
   444  		dlen              uint64
   445  		compressionOffset int
   446  		dbuf              []byte
   447  	)
   448  	if len(text) >= 12 && string(text[:4]) == "ZLIB" {
   449  		dlen = binary.BigEndian.Uint64(text[4:12])
   450  		compressionOffset = 12
   451  	}
   452  	if dlen == 0 && len(text) >= 12 && textSect.Flags&elf.SHF_COMPRESSED != 0 &&
   453  		textSect.Flags&elf.SHF_ALLOC == 0 &&
   454  		f.FileHeader.ByteOrder.Uint32(text[:]) == uint32(elf.COMPRESS_ZLIB) {
   455  		switch f.FileHeader.Class {
   456  		case elf.ELFCLASS32:
   457  			// Chdr32.Size offset
   458  			dlen = uint64(f.FileHeader.ByteOrder.Uint32(text[4:]))
   459  			compressionOffset = 12
   460  		case elf.ELFCLASS64:
   461  			if len(text) < 24 {
   462  				return fmt.Errorf("invalid compress header 64")
   463  			}
   464  			// Chdr64.Size offset
   465  			dlen = f.FileHeader.ByteOrder.Uint64(text[8:])
   466  			compressionOffset = 24
   467  		default:
   468  			return fmt.Errorf("unsupported compress header:%s", f.FileHeader.Class)
   469  		}
   470  	}
   471  	if dlen > 0 {
   472  		dbuf = make([]byte, dlen)
   473  		r, err := zlib.NewReader(bytes.NewBuffer(text[compressionOffset:]))
   474  		if err != nil {
   475  			return fmt.Errorf("failed to decompress zlib elf section %s: %w", e.Name, err)
   476  		}
   477  		if _, err := io.ReadFull(r, dbuf); err != nil {
   478  			return fmt.Errorf("failed to read decompressed zlib elf section %s: %w", e.Name, err)
   479  		}
   480  		if err := r.Close(); err != nil {
   481  			return fmt.Errorf("failed to close zlib elf section %s: %w", e.Name, err)
   482  		}
   483  		text = dbuf
   484  	}
   485  
   486  	var objSymbols []*ObjSymbol
   487  	var objSymAddr []uint64
   488  	for _, s := range elfSyms {
   489  		sectionData := text
   490  		if s.Section < elf.SHN_LORESERVE && !(s.Section < 0 || int(s.Section) >= len(f.Sections)) {
   491  			sect := f.Sections[s.Section]
   492  			if sect.Type != elf.SHT_NOBITS {
   493  				sectionData, err = sect.Data()
   494  				if err != nil {
   495  					return fmt.Errorf("failed to read section data from elf section %s %s (size %d): %w", e.Name, sect.Name, sect.Size, err)
   496  				}
   497  			}
   498  		}
   499  		var sym *ObjSymbol
   500  		var addr uint64
   501  		if s.Name != "" {
   502  			addr = s.Value
   503  			data := make([]byte, s.Size)
   504  			copy(data, sectionData[addr+textOffset:])
   505  			sym = &ObjSymbol{Name: s.Name, Data: data, Size: int64(s.Size), Func: &FuncInfo{}, Pkg: pkg.PkgPath}
   506  		}
   507  		objSymbols = append(objSymbols, sym)
   508  		objSymAddr = append(objSymAddr, addr)
   509  		if sym == nil {
   510  			continue
   511  		}
   512  
   513  		switch s.Section {
   514  		case elf.SHN_UNDEF:
   515  			sym.Kind = symkind.Sxxx
   516  		case elf.SHN_COMMON:
   517  			sym.Kind = symkind.SBSS
   518  		default:
   519  			i := int(s.Section)
   520  			if i < 0 || i >= len(f.Sections) {
   521  				break
   522  			}
   523  			sect := f.Sections[i]
   524  			switch sect.Flags & (elf.SHF_WRITE | elf.SHF_ALLOC | elf.SHF_EXECINSTR) {
   525  			case elf.SHF_ALLOC | elf.SHF_EXECINSTR:
   526  				sym.Kind = symkind.STEXT
   527  			case elf.SHF_ALLOC:
   528  				sym.Kind = symkind.SRODATA
   529  
   530  			case elf.SHF_ALLOC | elf.SHF_WRITE:
   531  				sym.Kind = symkind.SDATA
   532  			}
   533  		}
   534  	}
   535  
   536  	for _, r := range f.Sections {
   537  		if r.Type != elf.SHT_RELA && r.Type != elf.SHT_REL {
   538  			continue
   539  		}
   540  		if int(r.Info) != textIndex {
   541  			continue
   542  		}
   543  		rd, err := r.Data()
   544  		if err != nil {
   545  			return fmt.Errorf("failed to read relocation data from elf section %s %s: %w", e.Name, r.Name, err)
   546  		}
   547  
   548  		relR := bytes.NewReader(rd)
   549  		var rela elf.Rela64
   550  
   551  		for relR.Len() > 0 {
   552  			binary.Read(relR, f.ByteOrder, &rela)
   553  			symNo := rela.Info >> 32
   554  			if symNo == 0 || symNo > uint64(len(elfSyms)) {
   555  				continue
   556  			}
   557  			sym := &elfSyms[symNo-1]
   558  
   559  			var target *ObjSymbol
   560  			var targetAddr uint64
   561  			for i, objSymbol := range objSymbols {
   562  				if objSymbol == nil {
   563  					continue
   564  				}
   565  				nextAddr := objSymAddr[i] + uint64(objSymbol.Size)
   566  				if rela.Off >= objSymAddr[i] && rela.Off < nextAddr {
   567  					target = objSymbol
   568  					targetAddr = objSymAddr[i]
   569  					break
   570  				}
   571  			}
   572  			if target == nil {
   573  				fmt.Println("Couldn't find target for offset ", rela.Off, sym.Name)
   574  				continue
   575  			}
   576  
   577  			if sym.Section == elf.SHN_UNDEF || sym.Section < elf.SHN_LORESERVE || sym.Section == elf.SHN_COMMON {
   578  				if sym.Name == "" || target.Kind != symkind.STEXT {
   579  					// Don't create PCREL relocs for data for now... but might need to fix this at some point
   580  					continue
   581  				}
   582  				switch f.Machine {
   583  				case elf.EM_AARCH64:
   584  					t := elf.R_AARCH64(rela.Info & 0xffff)
   585  					switch t {
   586  					case elf.R_AARCH64_CALL26, elf.R_AARCH64_JUMP26:
   587  						target.Reloc = append(target.Reloc, Reloc{
   588  							Offset: int(rela.Off - targetAddr),
   589  							Sym:    &Sym{Name: sym.Name, Offset: InvalidOffset},
   590  							Size:   4,
   591  							Type:   reloctype.R_CALLARM64,
   592  							Add:    0, // Even though elf addend is -4, a Go PCREL reloc doesn't need this.
   593  						})
   594  					case elf.R_AARCH64_ADR_GOT_PAGE:
   595  						target.Reloc = append(target.Reloc, Reloc{
   596  							Offset: int(rela.Off - targetAddr),
   597  							Sym:    &Sym{Name: sym.Name, Offset: InvalidOffset},
   598  							Size:   8,
   599  							Type:   reloctype.R_ARM64_GOTPCREL,
   600  							Add:    int(rela.Addend), // TODO - test that this is correct
   601  						})
   602  					case elf.R_AARCH64_LD64_GOT_LO12_NC:
   603  						// Should be taken care of in above as these should always come in pairs?
   604  						// TODO - test that this is correct
   605  					default:
   606  						return fmt.Errorf("only a limited subset of elf relocations currently supported, got %s for symbol %s reloc to %s", t.GoString(), target.Name, sym.Name)
   607  					}
   608  				case elf.EM_X86_64:
   609  					t := elf.R_X86_64(rela.Info & 0xffff)
   610  					switch t {
   611  					case elf.R_X86_64_64, elf.R_X86_64_32:
   612  						return fmt.Errorf("TODO: only a limited subset of elf relocations currently supported, got %s", t.GoString())
   613  					case elf.R_X86_64_PLT32, elf.R_X86_64_PC32:
   614  						target.Reloc = append(target.Reloc, Reloc{
   615  							Offset: int(rela.Off - targetAddr),
   616  							Sym:    &Sym{Name: sym.Name, Offset: InvalidOffset},
   617  							Size:   4,
   618  							Type:   reloctype.R_PCREL,
   619  							Add:    0, // Even though elf addend is -4, a Go PCREL reloc doesn't need this.
   620  						})
   621  					case elf.R_X86_64_REX_GOTPCRELX:
   622  						target.Reloc = append(target.Reloc, Reloc{
   623  							Offset: int(rela.Off - targetAddr),
   624  							Sym:    &Sym{Name: sym.Name, Offset: InvalidOffset},
   625  							Size:   4,
   626  							Type:   reloctype.R_GOTPCREL,
   627  							Add:    int(rela.Addend),
   628  						})
   629  					default:
   630  						return fmt.Errorf("only a limited subset of elf relocations currently supported, got %s for symbol %s reloc to %s", t.GoString(), target.Name, sym.Name)
   631  					}
   632  				}
   633  			} else {
   634  				return fmt.Errorf("got an unexpected symbol section %d", sym.Section)
   635  			}
   636  		}
   637  	}
   638  
   639  	for _, symbol := range objSymbols {
   640  		if symbol != nil && symbol.Name != "" && symbol.Kind != symkind.Sxxx {
   641  			if _, ok := pkg.Syms[symbol.Name]; !ok {
   642  				pkg.SymNameOrder = append(pkg.SymNameOrder, symbol.Name)
   643  			}
   644  			pkg.Syms[symbol.Name] = symbol
   645  		}
   646  	}
   647  	return nil
   648  }
   649  
   650  type uint64s []uint64
   651  
   652  func (x uint64s) Len() int           { return len(x) }
   653  func (x uint64s) Swap(i, j int)      { x[i], x[j] = x[j], x[i] }
   654  func (x uint64s) Less(i, j int) bool { return x[i] < x[j] }
   655  
   656  func (pkg *Pkg) convertMachoRelocs(f *macho.File, e archive.Entry) error {
   657  	if f.Symtab == nil {
   658  		return nil
   659  	}
   660  	var allData []byte
   661  
   662  	sectionsSortedByAddr := make([]*macho.Section, len(f.Sections))
   663  	copy(sectionsSortedByAddr, f.Sections)
   664  
   665  	sort.Slice(sectionsSortedByAddr, func(i, j int) bool {
   666  		return sectionsSortedByAddr[i].Addr < sectionsSortedByAddr[j].Addr
   667  	})
   668  
   669  	for _, section := range sectionsSortedByAddr {
   670  		data, err := section.Data()
   671  		if err != nil {
   672  			return fmt.Errorf("failed to read data for section %s: %w", section.Name, err)
   673  		}
   674  		grow(&data, int(section.Size+uint64(section.Align)))
   675  		allData = append(allData, data...)
   676  	}
   677  	// Build sorted list of addresses of all symbols.
   678  	// We infer the size of a symbol by looking at where the next symbol begins.
   679  	var addrs []uint64
   680  	for _, s := range f.Symtab.Syms {
   681  		addrs = append(addrs, s.Value)
   682  	}
   683  	sort.Sort(uint64s(addrs))
   684  
   685  	var objSymbols []*ObjSymbol
   686  	for _, s := range f.Symtab.Syms {
   687  		if s.Type&0xe0 != 0 {
   688  			// Skip stab debug info.
   689  			continue
   690  		}
   691  
   692  		if s.Name == "" || s.Sect == 0 {
   693  			continue
   694  		}
   695  
   696  		var sym *ObjSymbol
   697  		var addr uint64
   698  
   699  		sym = &ObjSymbol{Name: s.Name, Func: &FuncInfo{}, Pkg: pkg.PkgPath}
   700  
   701  		i := sort.Search(len(addrs), func(x int) bool { return addrs[x] > s.Value })
   702  		if i < len(addrs) {
   703  			sym.Size = int64(addrs[i] - s.Value)
   704  		} else {
   705  			sym.Size = int64(f.Sections[s.Sect-1].Addr + f.Sections[s.Sect-1].Size - s.Value)
   706  		}
   707  
   708  		if sym.Size > 0 && s.Sect > 0 {
   709  			addr = s.Value
   710  			data := make([]byte, sym.Size)
   711  			copy(data, allData[addr:])
   712  			sym.Data = data
   713  		}
   714  
   715  		objSymbols = append(objSymbols, sym)
   716  
   717  		if int(s.Sect) <= len(f.Sections) {
   718  			sect := f.Sections[s.Sect-1]
   719  			switch sect.Seg {
   720  			case "__TEXT", "__DATA_CONST":
   721  				sym.Kind = symkind.SRODATA
   722  			case "__DATA":
   723  				sym.Kind = symkind.SDATA
   724  			}
   725  			switch sect.Seg + " " + sect.Name {
   726  			case "__TEXT __text":
   727  				sym.Kind = symkind.STEXT
   728  			case "__DATA __bss":
   729  				sym.Kind = symkind.SBSS
   730  			case "__DATA __noptrbss":
   731  				sym.Kind = symkind.SNOPTRBSS
   732  			}
   733  		}
   734  
   735  		for _, reloc := range append(f.Sections[s.Sect-1].Relocs) {
   736  			// TODO - review https://opensource.apple.com/source/xnu/xnu-4570.71.2/EXTERNAL_HEADERS/mach-o/reloc.h.auto.html
   737  			if uint64(reloc.Addr) < s.Value+uint64(sym.Size) && uint64(reloc.Addr) > s.Value {
   738  				// when Scattered == false && Extern == true, Value is the symbol number.
   739  				// when Scattered == false && Extern == false, Value is the section number.
   740  				// when Scattered == true, Value is the value that this reloc refers to.
   741  
   742  				if pkg.Arch == "arm64" {
   743  					// https://opensource.apple.com/source/cctools/cctools-877.5/include/mach-o/arm64/reloc.h.auto.html
   744  					var rType int
   745  					var rSize int
   746  					switch macho.RelocTypeARM64(reloc.Type) {
   747  					case macho.ARM64_RELOC_BRANCH26:
   748  						rType = reloctype.R_CALLARM64
   749  						rSize = 4
   750  						if rSize != 1<<reloc.Len {
   751  							return fmt.Errorf("unexpected size of macho reloc - Expected 4, got %d %#v", 1<<reloc.Len, reloc)
   752  						}
   753  					case macho.ARM64_RELOC_GOT_LOAD_PAGE21:
   754  						rType = reloctype.R_ADDRARM64
   755  						rSize = 8
   756  					case macho.ARM64_RELOC_GOT_LOAD_PAGEOFF12:
   757  						// Presumably paired with above?
   758  						continue
   759  					case macho.ARM64_RELOC_PAGE21:
   760  						rType = reloctype.R_ADDRARM64
   761  						rSize = 8
   762  					case macho.ARM64_RELOC_PAGEOFF12:
   763  						// Presumably paired with above?
   764  						continue
   765  					case macho.ARM64_RELOC_POINTER_TO_GOT:
   766  						rType = reloctype.R_ARM64_GOTPCREL
   767  						rSize = 8
   768  					case macho.ARM64_RELOC_UNSIGNED,
   769  						macho.ARM64_RELOC_SUBTRACTOR,
   770  						macho.ARM64_RELOC_TLVP_LOAD_PAGE21,
   771  						macho.ARM64_RELOC_TLVP_LOAD_PAGEOFF12,
   772  						macho.ARM64_RELOC_ADDEND:
   773  
   774  						return fmt.Errorf("got an unsupported macho reloc: %#v", reloc)
   775  					}
   776  					if !reloc.Scattered && reloc.Pcrel {
   777  						if reloc.Extern {
   778  							sym.Reloc = append(sym.Reloc, Reloc{
   779  								Offset: int(uint64(reloc.Addr) - s.Value),
   780  								Sym:    &Sym{Name: f.Symtab.Syms[reloc.Value].Name, Offset: InvalidOffset},
   781  								Size:   rSize,
   782  								Type:   rType,
   783  								Add:    0, // TODO - Is this correct?
   784  							})
   785  						} else {
   786  							sym.Reloc = append(sym.Reloc, Reloc{
   787  								Offset: int(uint64(reloc.Addr) - f.Sections[s.Value].Addr),
   788  								Sym:    &Sym{Name: f.Sections[s.Value].Name, Offset: InvalidOffset},
   789  								Size:   rSize,
   790  								Type:   rType,
   791  								Add:    0,
   792  							})
   793  						}
   794  
   795  					} else if !reloc.Scattered && !reloc.Pcrel {
   796  						sym.Reloc = append(sym.Reloc, Reloc{
   797  							Offset: int(uint64(reloc.Addr) - s.Value),
   798  							Sym:    &Sym{Name: f.Symtab.Syms[reloc.Value].Name, Offset: InvalidOffset},
   799  							Size:   rSize,
   800  							Type:   rType,
   801  							Add:    0,
   802  						})
   803  					} else {
   804  						return fmt.Errorf("got an unsupported macho reloc: %#v", reloc)
   805  					}
   806  				} else if pkg.Arch == "amd64" {
   807  					// https://opensource.apple.com/source/xnu/xnu-1504.7.4/EXTERNAL_HEADERS/mach-o/x86_64/reloc.h.auto.html
   808  
   809  					switch macho.RelocTypeX86_64(reloc.Type) {
   810  					case macho.X86_64_RELOC_UNSIGNED,
   811  						macho.X86_64_RELOC_SIGNED,
   812  						macho.X86_64_RELOC_BRANCH,
   813  						macho.X86_64_RELOC_GOT_LOAD,
   814  						macho.X86_64_RELOC_GOT,
   815  						macho.X86_64_RELOC_SUBTRACTOR,
   816  						macho.X86_64_RELOC_SIGNED_1,
   817  						macho.X86_64_RELOC_SIGNED_2,
   818  						macho.X86_64_RELOC_SIGNED_4,
   819  						macho.X86_64_RELOC_TLV:
   820  						// TODO - set the size and reloctype based on these instead of blindly assuming R_PCREL
   821  					}
   822  					if !reloc.Scattered && reloc.Pcrel {
   823  						sym.Reloc = append(sym.Reloc, Reloc{
   824  							Offset: int(uint64(reloc.Addr) - s.Value),
   825  							Sym:    &Sym{Name: f.Symtab.Syms[reloc.Value].Name, Offset: InvalidOffset},
   826  							Size:   4,
   827  							Type:   reloctype.R_PCREL,
   828  							Add:    0,
   829  						})
   830  					} else {
   831  						return fmt.Errorf("got an unsupported macho reloc: %#v", reloc)
   832  					}
   833  				} else {
   834  					return fmt.Errorf("unsupported arch: %s", pkg.Arch)
   835  				}
   836  
   837  			}
   838  		}
   839  	}
   840  
   841  	for _, symbol := range objSymbols {
   842  		if _, ok := pkg.Syms[symbol.Name]; !ok {
   843  			pkg.SymNameOrder = append(pkg.SymNameOrder, symbol.Name)
   844  		}
   845  		pkg.Syms[symbol.Name] = symbol
   846  		if strings.HasPrefix(symbol.Name, "__cgo_") {
   847  			// Need to add symbol as _cgo_* so that the Go generated
   848  			pkg.Syms[symbol.Name[1:]] = &ObjSymbol{
   849  				Name:  symbol.Name[1:],
   850  				Kind:  symbol.Kind,
   851  				DupOK: true,
   852  				Size:  symbol.Size,
   853  				Data:  symbol.Data,
   854  				Type:  symbol.Type,
   855  				Reloc: symbol.Reloc,
   856  				Func:  symbol.Func,
   857  				Pkg:   symbol.Pkg,
   858  			}
   859  		}
   860  	}
   861  	return nil
   862  }