github.com/pkujhd/goloader@v0.0.0-20240411034752-1a28096bd7bd/relocate.go (about)

     1  package goloader
     2  
     3  import (
     4  	"cmd/objfile/sys"
     5  	"fmt"
     6  	"runtime"
     7  
     8  	"github.com/pkujhd/goloader/obj"
     9  	"github.com/pkujhd/goloader/objabi/reloctype"
    10  	"github.com/pkujhd/goloader/objabi/symkind"
    11  	"github.com/pkujhd/goloader/objabi/tls"
    12  )
    13  
    14  const (
    15  	maxExtraCodeSize_ADDRARM64        = 24
    16  	maxExtraCodeSize_CALLARM64        = 16
    17  	maxExtraCodeSize_ARM64_PCREL_LDST = 24
    18  	maxExtraCodeSize_PCRELxMOV        = 18
    19  	maxExtraCodeSize_PCRELxCMPL       = 21
    20  	maxExtraCodeSize_PCRELxCALL       = 11
    21  	maxExtraCodeSize_PCRELxJMP        = 14
    22  	maxExtraCodeSize_CALL             = 11
    23  )
    24  
    25  func expandFunc(linker *Linker, objsym *obj.ObjSymbol, symbol *obj.Sym) {
    26  	// on linux/amd64, mmap force return < 32bit address, don't need add extra instructions
    27  	if linker.Arch.Name == sys.ArchAMD64.Name && runtime.GOOS == "linux" {
    28  		return
    29  	}
    30  	// Pessimistically pad the function text with extra bytes for any relocations which might add extra
    31  	// instructions at the end in the case of a 32 bit overflow. These epilogue PCs need to be added to
    32  	// the PCData, PCLine, PCFile, PCSP etc.
    33  	for i, reloc := range objsym.Reloc {
    34  		epilogue := &(objsym.Reloc[i].Epilogue)
    35  		epilogue.Offset = len(linker.Code) - symbol.Offset
    36  		switch reloc.Type {
    37  		case reloctype.R_ADDRARM64:
    38  			epilogue.Size = maxExtraCodeSize_ADDRARM64
    39  		case reloctype.R_CALLARM64:
    40  			epilogue.Size = maxExtraCodeSize_CALLARM64
    41  		case reloctype.R_ARM64_PCREL_LDST8, reloctype.R_ARM64_PCREL_LDST16, reloctype.R_ARM64_PCREL_LDST32, reloctype.R_ARM64_PCREL_LDST64:
    42  			epilogue.Size = maxExtraCodeSize_ARM64_PCREL_LDST
    43  		case reloctype.R_CALL:
    44  			epilogue.Size = maxExtraCodeSize_CALL
    45  		case reloctype.R_PCREL:
    46  			opCodes := objsym.Data[reloc.Offset-2 : reloc.Offset+reloc.Size]
    47  			switch opCodes[0] {
    48  			case x86amd64MOVcode:
    49  				epilogue.Size = maxExtraCodeSize_PCRELxMOV
    50  			case x86amd64CMPLcode:
    51  				epilogue.Size = maxExtraCodeSize_PCRELxCMPL
    52  			default:
    53  				switch opCodes[1] {
    54  				case x86amd64CALLcode:
    55  					epilogue.Size = maxExtraCodeSize_PCRELxCALL
    56  				case x86amd64JMPcode:
    57  					epilogue.Size = maxExtraCodeSize_PCRELxJMP
    58  				}
    59  			}
    60  		}
    61  		if epilogue.Size > 0 {
    62  			linker.Code = append(linker.Code, createArchNops(linker.Arch, epilogue.Size)...)
    63  		}
    64  	}
    65  }
    66  
    67  func (linker *Linker) relocateADRP(mCode []byte, loc obj.Reloc, segment *segment, symAddr uintptr) (err error) {
    68  	byteorder := linker.Arch.ByteOrder
    69  	offset := int64(symAddr) + int64(loc.Add) - ((int64(segment.codeBase) + int64(loc.Offset)) &^ 0xFFF)
    70  	//overflow
    71  	if offset >= 1<<32 || offset < -1<<32 {
    72  		epilogueOffset := loc.Epilogue.Offset
    73  		symAddr = symAddr + uintptr(loc.Add)
    74  		if symAddr < 0xFFFFFFFF && loc.Type == reloctype.R_ADDRARM64 {
    75  			addr := byteorder.Uint32(mCode)
    76  			//low:	MOV reg imm
    77  			low := uint32(0xD2800000)
    78  			//high: MOVK reg imm LSL#16
    79  			high := uint32(0xF2A00000)
    80  			low = ((addr & 0x1F) | low) | ((uint32(symAddr) & 0xFFFF) << 5)
    81  			high = ((addr & 0x1F) | high) | (uint32(symAddr) >> 16 << 5)
    82  			byteorder.PutUint64(mCode, uint64(low)|(uint64(high)<<32))
    83  		} else {
    84  			addr := byteorder.Uint32(mCode)
    85  			if loc.Type != reloctype.R_ADDRARM64 {
    86  				addr = uint32(byteorder.Uint64(mCode) >> 32)
    87  			}
    88  			blcode := byteorder.Uint32(arm64Bcode)
    89  			blcode |= ((uint32(epilogueOffset) - uint32(loc.Offset)) >> 2) & 0x01FFFFFF
    90  			if epilogueOffset-loc.Offset < 0 {
    91  				blcode |= 0x02000000
    92  			}
    93  			byteorder.PutUint32(mCode, blcode)
    94  			//low: MOV reg imm
    95  			llow := uint32(0xD2800000)
    96  			//lhigh: MOVK reg imm LSL#16
    97  			lhigh := uint32(0xF2A00000)
    98  			//llow: MOVK reg imm LSL#32
    99  			hlow := uint32(0xF2C00000)
   100  			//lhigh: MOVK reg imm LSL#48
   101  			hhigh := uint32(0xF2E00000)
   102  			llow = ((addr & 0x1F) | llow) | ((uint32(symAddr) & 0xFFFF) << 5)
   103  			lhigh = ((addr & 0x1F) | lhigh) | (uint32(symAddr) >> 16 << 5)
   104  			putAddressAddOffset(byteorder, segment.codeByte, &epilogueOffset, uint64(llow)|(uint64(lhigh)<<32))
   105  			hlow = ((addr & 0x1F) | hlow) | uint32(((uint64(symAddr)>>32)&0xFFFF)<<5)
   106  			hhigh = ((addr & 0x1F) | hhigh) | uint32((uint64(symAddr)>>48)<<5)
   107  			putAddressAddOffset(byteorder, segment.codeByte, &epilogueOffset, uint64(hlow)|(uint64(hhigh)<<32))
   108  			if loc.Type != reloctype.R_ADDRARM64 {
   109  				//LDR or STR
   110  				ldrOrStr := (byteorder.Uint32(mCode[4:]) & 0xFFFFFC00) | addr&0x1F | ((addr & 0x1F) << 5)
   111  				byteorder.PutUint32(segment.codeByte[epilogueOffset:], ldrOrStr)
   112  				epilogueOffset += Uint32Size
   113  			}
   114  			blcode = byteorder.Uint32(arm64Bcode)
   115  			blcode |= ((uint32(loc.Offset) - uint32(epilogueOffset) + PtrSize) >> 2) & 0x01FFFFFF
   116  			if loc.Offset-epilogueOffset+PtrSize < 0 {
   117  				blcode |= 0x02000000
   118  			}
   119  			byteorder.PutUint32(segment.codeByte[epilogueOffset:], blcode)
   120  		}
   121  	} else {
   122  		// 2bit + 19bit + low(12bit) = 33bit
   123  		low := (uint32((offset>>12)&3) << 29) | (uint32((offset>>12>>2)&0x7FFFF) << 5)
   124  		high := uint32(0)
   125  		switch loc.Type {
   126  		case reloctype.R_ADDRARM64, reloctype.R_ARM64_PCREL_LDST8:
   127  			high = uint32(offset&0xFFF) << 10
   128  		case reloctype.R_ARM64_PCREL_LDST16:
   129  			if offset&0x1 != 0 {
   130  				err = fmt.Errorf("offset for 16-bit load/store has unaligned value %d", offset&0xFFF)
   131  			}
   132  			high = (uint32(offset&0xFFF) >> 1) << 10
   133  		case reloctype.R_ARM64_PCREL_LDST32:
   134  			if offset&0x3 != 0 {
   135  				err = fmt.Errorf("offset for 32-bit load/store has unaligned value %d", offset&0xFFF)
   136  			}
   137  			high = (uint32(offset&0xFFF) >> 2) << 10
   138  		case reloctype.R_ARM64_PCREL_LDST64:
   139  			if offset&0x7 != 0 {
   140  				err = fmt.Errorf("offset for 64-bit load/store has unaligned value %d", offset&0xFFF)
   141  			}
   142  			high = (uint32(offset&0xFFF) >> 3) << 10
   143  		}
   144  		value := byteorder.Uint64(mCode)
   145  		value = (uint64(uint32(value>>32)|high) << 32) | uint64(uint32(value&0xFFFFFFFF)|low)
   146  		byteorder.PutUint64(mCode, value)
   147  	}
   148  	return err
   149  }
   150  
   151  func (linker *Linker) relocateCALL(addr uintptr, loc obj.Reloc, segment *segment, relocByte []byte, addrBase int) {
   152  	byteorder := linker.Arch.ByteOrder
   153  	offset := int(addr) - (addrBase + loc.Offset + loc.Size) + loc.Add
   154  	if isOverflowInt32(offset) {
   155  		segment.dataOff = alignof(segment.dataOff, PtrSize)
   156  		epilogueOffset := loc.Epilogue.Offset
   157  		offset = (segment.codeBase + epilogueOffset) - (addrBase + loc.Offset + loc.Size)
   158  		relocByte[loc.Offset-1] = x86amd64JMPcode
   159  		copy(segment.codeByte[epilogueOffset:], x86amd64replaceCALLcode)
   160  		off := (segment.dataBase + segment.dataOff - segment.codeBase - epilogueOffset - 6)
   161  		byteorder.PutUint32(segment.codeByte[epilogueOffset+2:], uint32(off))
   162  		putAddressAddOffset(byteorder, segment.dataByte, &segment.dataOff, uint64(addr)+uint64(loc.Add))
   163  		epilogueOffset += len(x86amd64replaceCALLcode)
   164  		off = addrBase + loc.Offset + loc.Size - segment.codeBase - epilogueOffset
   165  		byteorder.PutUint32(segment.codeByte[epilogueOffset-4:], uint32(off))
   166  	}
   167  	byteorder.PutUint32(relocByte[loc.Offset:], uint32(offset))
   168  }
   169  
   170  func (linker *Linker) relocatePCREL(addr uintptr, loc obj.Reloc, segment *segment, relocByte []byte, addrBase int) (err error) {
   171  	byteorder := linker.Arch.ByteOrder
   172  	offset := int(addr) - (addrBase + loc.Offset + loc.Size) + loc.Add
   173  	if isOverflowInt32(offset) {
   174  		epilogueOffset := loc.Epilogue.Offset
   175  		offset = (segment.codeBase + epilogueOffset) - (addrBase + loc.Offset + loc.Size)
   176  		bytes := relocByte[loc.Offset-2:]
   177  		opcode := relocByte[loc.Offset-2]
   178  		register := ZeroByte
   179  		if opcode == x86amd64LEAcode {
   180  			bytes[0] = x86amd64MOVcode
   181  			//not append epilogue for LEA, put the address into data segment.
   182  			offset = (segment.dataBase + segment.dataOff) - (addrBase + loc.Offset + loc.Size)
   183  		} else if opcode == x86amd64MOVcode && loc.Size >= Uint32Size {
   184  			register = (relocByte[loc.Offset-1] >> 3) & 0x7
   185  			copy(bytes, append(x86amd64NOPcode, x86amd64JMPNcode...))
   186  		} else if opcode == x86amd64CMPLcode && loc.Size >= Uint32Size {
   187  			copy(bytes, append(x86amd64NOPcode, x86amd64JMPNcode...))
   188  		} else if (bytes[1] == x86amd64CALLcode) && byteorder.Uint32(relocByte[loc.Offset:]) == 0 {
   189  			opcode = bytes[1]
   190  		} else if bytes[1] == x86amd64JMPcode {
   191  			opcode = bytes[1]
   192  		} else {
   193  			return fmt.Errorf("do not support x86 opcode: %x for symbol %s (offset %d)!\n", relocByte[loc.Offset-2:loc.Offset], loc.SymName, offset)
   194  		}
   195  		byteorder.PutUint32(relocByte[loc.Offset:], uint32(offset))
   196  		switch opcode {
   197  		case x86amd64CMPLcode:
   198  			copy(segment.codeByte[epilogueOffset:], x86amd64replaceCMPLcode)
   199  			segment.codeByte[epilogueOffset+0x11] = relocByte[loc.Offset+loc.Size]
   200  			putAddress(byteorder, segment.codeByte[epilogueOffset+3:], uint64(addr)+uint64(loc.Add))
   201  			epilogueOffset += len(x86amd64replaceCMPLcode)
   202  		case x86amd64MOVcode:
   203  			copy(segment.codeByte[epilogueOffset:], x86amd64replaceMOVQcode)
   204  			segment.codeByte[epilogueOffset+1] |= register
   205  			segment.codeByte[epilogueOffset+0xC] |= register | (register << 3)
   206  			putAddress(byteorder, segment.codeByte[epilogueOffset+2:], uint64(addr)+uint64(loc.Add))
   207  			epilogueOffset += len(x86amd64replaceMOVQcode)
   208  		case x86amd64CALLcode:
   209  			segment.dataOff = alignof(segment.dataOff, PtrSize)
   210  			bytes[1] = x86amd64JMPcode
   211  			copy(segment.codeByte[epilogueOffset:], x86amd64replaceCALLcode)
   212  			off := (segment.dataBase + segment.dataOff - segment.codeBase - epilogueOffset - 6)
   213  			byteorder.PutUint32(segment.codeByte[epilogueOffset+2:], uint32(off))
   214  			epilogueOffset += len(x86amd64replaceCALLcode)
   215  			putAddressAddOffset(byteorder, segment.dataByte, &segment.dataOff, uint64(addr)+uint64(loc.Add))
   216  			off = addrBase + loc.Offset + loc.Size - segment.codeBase - epilogueOffset
   217  			byteorder.PutUint32(segment.codeByte[epilogueOffset-4:], uint32(off))
   218  		case x86amd64JMPcode:
   219  			copy(segment.codeByte[epilogueOffset:], x86amd64JMPLcode)
   220  			epilogueOffset += len(x86amd64JMPLcode)
   221  			copy2Slice(segment.codeByte[epilogueOffset:], addr, PtrSize)
   222  			epilogueOffset += PtrSize
   223  		case x86amd64LEAcode:
   224  			putAddressAddOffset(byteorder, segment.dataByte, &segment.dataOff, uint64(addr)+uint64(loc.Add))
   225  		default:
   226  			return fmt.Errorf("unexpected x86 opcode %x: %x for symbol %s (offset %d)!\n", opcode, relocByte[loc.Offset-2:loc.Offset], loc.SymName, offset)
   227  		}
   228  
   229  		switch opcode {
   230  		case x86amd64CMPLcode, x86amd64MOVcode:
   231  			copy(segment.codeByte[epilogueOffset:], x86amd64JMPNcode)
   232  			epilogueOffset += len(x86amd64JMPNcode)
   233  			returnOffset := (loc.Offset + loc.Size - loc.Add) - epilogueOffset
   234  			byteorder.PutUint32(segment.codeByte[epilogueOffset-4:], uint32(returnOffset))
   235  		}
   236  	} else {
   237  		byteorder.PutUint32(relocByte[loc.Offset:], uint32(offset))
   238  	}
   239  	return nil
   240  }
   241  
   242  func (linker *Linker) relocteCALLARM(addr uintptr, loc obj.Reloc, segment *segment) {
   243  	byteorder := linker.Arch.ByteOrder
   244  	add := loc.Add
   245  	if loc.Type == reloctype.R_CALLARM {
   246  		add = int(signext24(int64(loc.Add&0xFFFFFF)) * 4)
   247  	}
   248  	offset := (int(addr) + add - (segment.codeBase + loc.Offset)) / 4
   249  	if isOverflowInt24(offset) {
   250  		epilogueOffset := loc.Epilogue.Offset
   251  		off := uint32(epilogueOffset-loc.Offset) / 4
   252  		if loc.Type == reloctype.R_CALLARM {
   253  			add = int(signext24(int64(loc.Add&0xFFFFFF)+2) * 4)
   254  			off = uint32(epilogueOffset-loc.Offset-8) / 4
   255  		}
   256  		putUint24(segment.codeByte[loc.Offset:], off)
   257  		if loc.Type == reloctype.R_CALLARM64 {
   258  			copy(segment.codeByte[epilogueOffset:], arm64ReplaceCALLCode)
   259  			epilogueOffset += len(arm64ReplaceCALLCode)
   260  		} else {
   261  			copy(segment.codeByte[epilogueOffset:], armReplaceCallCode)
   262  			epilogueOffset += len(armReplaceCallCode)
   263  		}
   264  		putAddressAddOffset(byteorder, segment.codeByte, &epilogueOffset, uint64(int(addr)+add))
   265  	} else {
   266  		val := byteorder.Uint32(segment.codeByte[loc.Offset:])
   267  		if loc.Type == reloctype.R_CALLARM {
   268  			val |= uint32(offset) & 0x00FFFFFF
   269  		} else {
   270  			val |= uint32(offset) & 0x03FFFFFF
   271  		}
   272  		byteorder.PutUint32(segment.codeByte[loc.Offset:], val)
   273  	}
   274  }
   275  
   276  func (linker *Linker) relocate(codeModule *CodeModule, symbolMap, symPtr map[string]uintptr) (err error) {
   277  	segment := &codeModule.segment
   278  	byteorder := linker.Arch.ByteOrder
   279  	tlsOffset := uint32(tls.GetTLSOffset(linker.Arch, linker.Arch.PtrSize))
   280  	for _, symbol := range linker.SymMap {
   281  		for _, loc := range symbol.Reloc {
   282  			addr := symbolMap[loc.SymName]
   283  			relocByte := segment.dataByte
   284  			addrBase := segment.dataBase
   285  			if symbol.Kind == symkind.STEXT {
   286  				addrBase = segment.codeBase
   287  				relocByte = segment.codeByte
   288  			}
   289  
   290  			if addr != InvalidHandleValue {
   291  				switch loc.Type {
   292  				case reloctype.R_TLS_LE, reloctype.R_TLS_IE:
   293  					byteorder.PutUint32(relocByte[loc.Offset:], tlsOffset)
   294  				case reloctype.R_CALL, reloctype.R_CALL | reloctype.R_WEAK:
   295  					linker.relocateCALL(addr, loc, segment, relocByte, addrBase)
   296  				case reloctype.R_PCREL:
   297  					err = linker.relocatePCREL(addr, loc, segment, relocByte, addrBase)
   298  				case reloctype.R_CALLARM, reloctype.R_CALLARM64, reloctype.R_CALLARM64 | reloctype.R_WEAK:
   299  					linker.relocteCALLARM(addr, loc, segment)
   300  				case reloctype.R_ADDRARM64, reloctype.R_ARM64_PCREL_LDST8, reloctype.R_ARM64_PCREL_LDST16, reloctype.R_ARM64_PCREL_LDST32, reloctype.R_ARM64_PCREL_LDST64:
   301  					if symbol.Kind != symkind.STEXT {
   302  						err = fmt.Errorf("impossible!Sym:%s locate not in code segment!\n", loc.SymName)
   303  					}
   304  					err = linker.relocateADRP(relocByte[loc.Offset:], loc, segment, addr)
   305  				case reloctype.R_ADDR, reloctype.R_WEAKADDR:
   306  					address := uintptr(int(addr) + loc.Add)
   307  					putAddress(byteorder, relocByte[loc.Offset:], uint64(address))
   308  				case reloctype.R_CALLIND:
   309  					//nothing todo
   310  				case reloctype.R_ADDROFF, reloctype.R_WEAKADDROFF:
   311  					offset := int(addr) - addrBase + loc.Add
   312  					if isOverflowInt32(offset) {
   313  						err = fmt.Errorf("symName:%s relocateType:%s, offset:%d is overflow!\n", loc.SymName, reloctype.RelocTypeString(loc.Type), offset)
   314  					}
   315  					byteorder.PutUint32(relocByte[loc.Offset:], uint32(offset))
   316  				case reloctype.R_METHODOFF:
   317  					if linker.SymMap[loc.SymName].Kind == symkind.STEXT {
   318  						addrBase = segment.codeBase
   319  					}
   320  					offset := int(addr) - addrBase + loc.Add
   321  					if isOverflowInt32(offset) {
   322  						err = fmt.Errorf("symName:%s relocateType:%s, offset:%d is overflow!\n", loc.SymName, reloctype.RelocTypeString(loc.Type), offset)
   323  					}
   324  					byteorder.PutUint32(relocByte[loc.Offset:], uint32(offset))
   325  				case reloctype.R_GOTPCREL, reloctype.R_ARM64_GOTPCREL:
   326  					offset := uint32(segment.dataBase + segment.dataOff - addrBase - loc.Offset - loc.Size)
   327  					byteorder.PutUint32(relocByte[loc.Offset:], offset)
   328  					putAddressAddOffset(byteorder, segment.dataByte, &segment.dataOff, uint64(addr))
   329  				case reloctype.R_USETYPE,
   330  					reloctype.R_USEIFACE,
   331  					reloctype.R_USEIFACEMETHOD,
   332  					reloctype.R_ADDRCUOFF,
   333  					reloctype.R_INITORDER:
   334  					//nothing todo
   335  				case reloctype.R_KEEP:
   336  					//nothing todo
   337  				default:
   338  					err = fmt.Errorf("unknown reloc type:%s sym:%s", reloctype.RelocTypeString(loc.Type), loc.SymName)
   339  				}
   340  			}
   341  			if err != nil {
   342  				return err
   343  			}
   344  		}
   345  	}
   346  	return err
   347  }