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

     1  //go:build go1.19
     2  // +build go1.19
     3  
     4  package obj
     5  
     6  import (
     7  	"cmd/objfile/archive"
     8  	"cmd/objfile/objabi"
     9  	"debug/pe"
    10  	"encoding/binary"
    11  	"fmt"
    12  	"github.com/eh-steve/goloader/objabi/symkind"
    13  	"sort"
    14  	"strings"
    15  )
    16  
    17  // See cmd/link/internal/loadpd/ldpe.go for reference
    18  const (
    19  	IMAGE_SYM_CLASS_STATIC   = 3
    20  	IMAGE_REL_I386_DIR32     = 0x0006
    21  	IMAGE_REL_I386_DIR32NB   = 0x0007
    22  	IMAGE_REL_I386_REL32     = 0x0014
    23  	IMAGE_REL_AMD64_ADDR64   = 0x0001
    24  	IMAGE_REL_AMD64_ADDR32   = 0x0002
    25  	IMAGE_REL_AMD64_ADDR32NB = 0x0003
    26  	IMAGE_REL_AMD64_REL32    = 0x0004
    27  )
    28  
    29  func readPESym(arch string, pesym *pe.COFFSymbol, f *pe.File, defWithImp map[string]struct{}) (string, error) {
    30  	symname, err := pesym.FullName(f.StringTable)
    31  	if err != nil {
    32  		return "", fmt.Errorf("failed to read full name of pesym: %w", err)
    33  	}
    34  	symIsSect := pesym.StorageClass == IMAGE_SYM_CLASS_STATIC && pesym.Type == 0 && pesym.Name[0] == '.'
    35  	var name string
    36  	if symIsSect {
    37  		name = f.Sections[pesym.SectionNumber-1].Name
    38  	} else {
    39  		name = symname
    40  		if strings.HasPrefix(symname, "__imp_") {
    41  			orig := symname[len("__imp_"):]
    42  			if _, ok := defWithImp[orig]; ok {
    43  				// Don't rename __imp_XXX to XXX, since if we do this
    44  				// we'll wind up with a duplicate definition. One
    45  				// example is "__acrt_iob_func"; see commit b295099
    46  				// from git://git.code.sf.net/p/mingw-w64/mingw-w64
    47  				// for details.
    48  			} else {
    49  				name = strings.TrimPrefix(name, "__imp_") // __imp_Name => Name
    50  			}
    51  		}
    52  		// A note on the "_main" exclusion below: the main routine
    53  		// defined by the Go runtime is named "_main", not "main", so
    54  		// when reading references to _main from a host object we want
    55  		// to avoid rewriting "_main" to "main" in this specific
    56  		// instance. See #issuecomment-1143698749 on #35006 for more
    57  		// details on this problem.
    58  
    59  		if arch == "386" && name[0] == '_' && name != "_main" {
    60  			name = name[1:] // _Name => Name
    61  		}
    62  	}
    63  
    64  	// remove last @XXX
    65  	if i := strings.LastIndex(name, "@"); i >= 0 {
    66  		name = name[:i]
    67  	}
    68  	return name, nil
    69  }
    70  
    71  func preprocessSymbols(f *pe.File) (map[string]struct{}, error) {
    72  
    73  	// preprocessSymbols walks the COFF symbols for the PE file we're
    74  	// reading and looks for cases where we have both a symbol definition
    75  	// for "XXX" and an "__imp_XXX" symbol, recording these cases in a map
    76  	// in the state struct. This information will be used in readpesym()
    77  	// above to give such symbols special treatment. This function also
    78  	// gathers information about COMDAT sections/symbols for later use
    79  	// in readpesym().
    80  
    81  	// Locate comdat sections.
    82  	comdats := make(map[uint16]int64)
    83  	for i, s := range f.Sections {
    84  		if s.Characteristics&uint32(pe.IMAGE_SCN_LNK_COMDAT) != 0 {
    85  			comdats[uint16(i)] = int64(s.Size)
    86  		}
    87  	}
    88  
    89  	// Examine symbol defs.
    90  	imp := make(map[string]struct{})
    91  	def := make(map[string]struct{})
    92  	for i, numaux := 0, 0; i < len(f.COFFSymbols); i += numaux + 1 {
    93  		pesym := &f.COFFSymbols[i]
    94  		numaux = int(pesym.NumberOfAuxSymbols)
    95  		if pesym.SectionNumber == 0 { // extern
    96  			continue
    97  		}
    98  		symname, err := pesym.FullName(f.StringTable)
    99  		if err != nil {
   100  			return nil, err
   101  		}
   102  		def[symname] = struct{}{}
   103  		if strings.HasPrefix(symname, "__imp_") {
   104  			imp[strings.TrimPrefix(symname, "__imp_")] = struct{}{}
   105  		}
   106  		if _, isc := comdats[uint16(pesym.SectionNumber-1)]; !isc {
   107  			continue
   108  		}
   109  		if pesym.StorageClass != uint8(IMAGE_SYM_CLASS_STATIC) {
   110  			continue
   111  		}
   112  		// This symbol corresponds to a COMDAT section. Read the
   113  		// aux data for it.
   114  		auxsymp, err := f.COFFSymbolReadSectionDefAux(i)
   115  		if err != nil {
   116  			return nil, fmt.Errorf("unable to read aux info for section def symbol %d %s: pe.COFFSymbolReadComdatInfo returns %v", i, symname, err)
   117  		}
   118  		if auxsymp.Selection == pe.IMAGE_COMDAT_SELECT_SAME_SIZE {
   119  			// This is supported.
   120  		} else if auxsymp.Selection == pe.IMAGE_COMDAT_SELECT_ANY {
   121  			// Also supported.
   122  			comdats[uint16(pesym.SectionNumber-1)] = int64(-1)
   123  		} else {
   124  			// We don't support any of the other strategies at the
   125  			// moment. I suspect that we may need to also support
   126  			// "associative", we'll see.
   127  			return nil, fmt.Errorf("internal error: unsupported COMDAT selection strategy found in sec=%d strategy=%d idx=%d, please file a bug", auxsymp.SecNum, auxsymp.Selection, i)
   128  		}
   129  	}
   130  	defWithImp := make(map[string]struct{})
   131  	for n := range imp {
   132  		if _, ok := def[n]; ok {
   133  			defWithImp[n] = struct{}{}
   134  		}
   135  	}
   136  	return defWithImp, nil
   137  }
   138  
   139  func issectCoff(s *pe.COFFSymbol) bool {
   140  	return s.StorageClass == IMAGE_SYM_CLASS_STATIC && s.Type == 0 && s.Name[0] == '.'
   141  }
   142  
   143  func issect(s *pe.Symbol) bool {
   144  	return s.StorageClass == IMAGE_SYM_CLASS_STATIC && s.Type == 0 && s.Name[0] == '.'
   145  }
   146  
   147  func (pkg *Pkg) convertPERelocs(f *pe.File, e archive.Entry) error {
   148  	textSect := f.Section(".text")
   149  	if textSect == nil {
   150  		return nil
   151  	}
   152  	defWithImp, err := preprocessSymbols(f)
   153  	if err != nil {
   154  		return err
   155  	}
   156  
   157  	// Build sorted list of addresses of all symbols.
   158  	// We infer the size of a symbol by looking at where the next symbol begins.
   159  	var objSymAddr []uint32
   160  	for _, s := range f.Symbols {
   161  		if issect(s) || s.SectionNumber <= 0 || int(s.SectionNumber) > len(f.Sections) {
   162  			continue
   163  		}
   164  		objSymAddr = append(objSymAddr, s.Value)
   165  	}
   166  
   167  	var objSymbols []*ObjSymbol
   168  
   169  	for _, s := range f.Symbols {
   170  		if issect(s) || s.SectionNumber <= 0 || int(s.SectionNumber) > len(f.Sections) {
   171  			continue
   172  		}
   173  
   174  		sect := f.Sections[s.SectionNumber-1]
   175  		var text []byte
   176  		if sect.Characteristics&pe.IMAGE_SCN_CNT_UNINITIALIZED_DATA != 0 {
   177  			text = make([]byte, sect.Size)
   178  		} else {
   179  			text, err = sect.Data()
   180  			if err != nil {
   181  				return fmt.Errorf("failed to read section data for symbol %s, section %d (%s): %w", s.Name, s.SectionNumber, sect.Name, err)
   182  			}
   183  		}
   184  
   185  		var sym *ObjSymbol
   186  		var addr uint32
   187  
   188  		sym = &ObjSymbol{Name: s.Name, Func: &FuncInfo{}, Pkg: pkg.PkgPath}
   189  
   190  		i := sort.Search(len(objSymAddr), func(x int) bool { return objSymAddr[x] > s.Value })
   191  		if i < len(objSymAddr) {
   192  			sym.Size = int64(objSymAddr[i] - s.Value)
   193  		} else {
   194  			sym.Size = int64(len(text))
   195  		}
   196  
   197  		if sym.Size > 0 && s.SectionNumber > 0 && f.Sections[s.SectionNumber-1] == textSect {
   198  			addr = s.Value
   199  			data := make([]byte, sym.Size)
   200  			copy(data, text[addr:])
   201  			sym.Data = data
   202  		}
   203  
   204  		objSymbols = append(objSymbols, sym)
   205  
   206  		switch sect.Characteristics & (pe.IMAGE_SCN_CNT_UNINITIALIZED_DATA | pe.IMAGE_SCN_CNT_INITIALIZED_DATA | pe.IMAGE_SCN_MEM_READ | pe.IMAGE_SCN_MEM_WRITE | pe.IMAGE_SCN_CNT_CODE | pe.IMAGE_SCN_MEM_EXECUTE) {
   207  		case pe.IMAGE_SCN_CNT_INITIALIZED_DATA | pe.IMAGE_SCN_MEM_READ: //.rdata
   208  			sym.Kind = symkind.SRODATA
   209  
   210  		case pe.IMAGE_SCN_CNT_UNINITIALIZED_DATA | pe.IMAGE_SCN_MEM_READ | pe.IMAGE_SCN_MEM_WRITE: //.bss
   211  			sym.Kind = symkind.SNOPTRBSS
   212  
   213  		case pe.IMAGE_SCN_CNT_INITIALIZED_DATA | pe.IMAGE_SCN_MEM_READ | pe.IMAGE_SCN_MEM_WRITE: //.data
   214  			sym.Kind = symkind.SNOPTRDATA
   215  
   216  		case pe.IMAGE_SCN_CNT_CODE | pe.IMAGE_SCN_MEM_EXECUTE | pe.IMAGE_SCN_MEM_READ: //.text
   217  			sym.Kind = symkind.STEXT
   218  		default:
   219  			return fmt.Errorf("unexpected flags %#06x for PE section %s", sect.Characteristics, sect.Name)
   220  
   221  		}
   222  	}
   223  
   224  	for _, section := range f.Sections {
   225  		if strings.HasPrefix(section.Name, ".debug_") {
   226  			// Don't bother relocating debug sections
   227  			continue
   228  		}
   229  		if section.NumberOfRelocations == 0 {
   230  			continue
   231  		}
   232  		if section.Characteristics&pe.IMAGE_SCN_MEM_DISCARDABLE != 0 {
   233  			continue
   234  		}
   235  		if section.Characteristics&(pe.IMAGE_SCN_CNT_CODE|pe.IMAGE_SCN_CNT_INITIALIZED_DATA|pe.IMAGE_SCN_CNT_UNINITIALIZED_DATA) == 0 {
   236  			// This has been seen for .idata sections, which we
   237  			// want to ignore. See issues 5106 and 5273.
   238  			continue
   239  		}
   240  
   241  		sectdata, err := section.Data()
   242  		if err != nil {
   243  			return fmt.Errorf("failed to read section data for %s: %w", section.Name, err)
   244  		}
   245  		for j, reloc := range section.Relocs {
   246  			if int(reloc.SymbolTableIndex) >= len(f.COFFSymbols) {
   247  				return fmt.Errorf("relocation number %d symbol index idx=%d cannot be larger than number of symbols %d", j, reloc.SymbolTableIndex, len(f.COFFSymbols))
   248  			}
   249  			pesym := f.COFFSymbols[reloc.SymbolTableIndex]
   250  			relocSymName, err := readPESym(pkg.Arch, &pesym, f, defWithImp)
   251  			if err != nil {
   252  				return err
   253  			}
   254  			if f.Section(relocSymName) != nil {
   255  				continue
   256  			}
   257  			rSize := uint8(4)
   258  			var rAdd int64
   259  			var rType objabi.RelocType
   260  			rOff := int32(reloc.VirtualAddress)
   261  
   262  			switch reloc.Type {
   263  			case IMAGE_REL_I386_REL32, IMAGE_REL_AMD64_REL32,
   264  				IMAGE_REL_AMD64_ADDR32, // R_X86_64_PC32
   265  				IMAGE_REL_AMD64_ADDR32NB:
   266  				rType = objabi.R_PCREL
   267  				rAdd = int64(int32(binary.LittleEndian.Uint32(sectdata[rOff:])))
   268  
   269  			case IMAGE_REL_I386_DIR32NB, IMAGE_REL_I386_DIR32:
   270  				rType = objabi.R_ADDR
   271  
   272  				// load addend from image
   273  				rAdd = int64(int32(binary.LittleEndian.Uint32(sectdata[rOff:])))
   274  
   275  			case IMAGE_REL_AMD64_ADDR64: // R_X86_64_64
   276  				rSize = 8
   277  
   278  				rType = objabi.R_ADDR
   279  
   280  				// load addend from image
   281  				rAdd = int64(binary.LittleEndian.Uint64(sectdata[rOff:]))
   282  			default:
   283  				return fmt.Errorf("unsupported PE relocation type: %d in symbol %s", reloc.Type, relocSymName)
   284  			}
   285  
   286  			var target *ObjSymbol
   287  			var targetAddr uint32
   288  			for i, objSymbol := range objSymbols {
   289  				if objSymbol == nil {
   290  					continue
   291  				}
   292  				nextAddr := objSymAddr[i] + uint32(objSymbol.Size)
   293  				if reloc.VirtualAddress >= objSymAddr[i] && reloc.VirtualAddress < nextAddr {
   294  					target = objSymbol
   295  					targetAddr = objSymAddr[i]
   296  					break
   297  				}
   298  			}
   299  			if target == nil {
   300  				fmt.Println("Goloader PE Reloc error - couldn't find target for offset ", reloc.VirtualAddress, relocSymName)
   301  				continue
   302  			}
   303  
   304  			if issectCoff(&pesym) {
   305  				rAdd += int64(pesym.Value)
   306  			}
   307  
   308  			target.Reloc = append(target.Reloc, Reloc{
   309  				Offset: int(reloc.VirtualAddress - targetAddr),
   310  				Sym:    &Sym{Name: relocSymName, Offset: InvalidOffset},
   311  				Size:   int(rSize),
   312  				Type:   int(rType),
   313  				Add:    int(rAdd),
   314  			})
   315  		}
   316  	}
   317  
   318  	for _, symbol := range objSymbols {
   319  		if symbol != nil && symbol.Name != "" && symbol.Size > 0 {
   320  			if _, ok := pkg.Syms[symbol.Name]; !ok {
   321  				pkg.SymNameOrder = append(pkg.SymNameOrder, symbol.Name)
   322  			}
   323  			pkg.Syms[symbol.Name] = symbol
   324  		}
   325  	}
   326  	return nil
   327  }