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

     1  package goloader
     2  
     3  import (
     4  	"cmd/objfile/objabi"
     5  	"encoding/binary"
     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  	"github.com/eh-steve/goloader/objabi/tls"
    11  	"strings"
    12  	"unsafe"
    13  )
    14  
    15  var (
    16  	maxExtraInstructionBytesADRP            = int(unsafe.Sizeof(armLDRCode8Bytes)) + len(arm64Bcode) + PtrSize
    17  	maxExtraInstructionBytesADRPLDST        = int(unsafe.Sizeof(armLDRCode8Bytes)) + int(unsafe.Sizeof(armLDRCode12Bytes)) + len(arm64Bcode) + PtrSize
    18  	maxExtraInstructionBytesCALLARM64       = len(arm64CALLCode) + PtrSize
    19  	maxExtraInstructionBytesPCRELxLEAQ      = PtrSize
    20  	maxExtraInstructionBytesPCRELxMOVShort  = len(x86amd64replaceMOVQcode) + len(x86amd64JMPShortCode)
    21  	maxExtraInstructionBytesPCRELxMOVNear   = len(x86amd64replaceMOVQcode) + len(x86amd64JMPNearCode)
    22  	maxExtraInstructionBytesPCRELxCMPLShort = len(x86amd64replaceCMPLcode) + len(x86amd64JMPShortCode)
    23  	maxExtraInstructionBytesPCRELxCMPLNear  = len(x86amd64replaceCMPLcode) + len(x86amd64JMPNearCode)
    24  	maxExtraInstructionBytesPCRELxCALL2     = PtrSize
    25  	maxExtraInstructionBytesPCRELxJMP       = len(x86amd64JMPLcode) + PtrSize
    26  	maxExtraInstructionBytesCALLShort       = len(x86amd64CALLFarCode) + len(x86amd64JMPShortCode) + PtrSize
    27  	maxExtraInstructionBytesCALLNear        = len(x86amd64CALLFarCode) + len(x86amd64JMPNearCode) + PtrSize
    28  	maxExtraInstructionBytesGOTPCREL        = PtrSize
    29  	maxExtraInstructionBytesARM64GOTPCREL   = PtrSize
    30  )
    31  
    32  func (linker *Linker) relocateADRP(mCode []byte, loc obj.Reloc, segment *segment, symAddr uintptr) (err error) {
    33  	byteorder := linker.Arch.ByteOrder
    34  	signedOffset := int64(symAddr) + int64(loc.Add) - ((int64(segment.codeBase) + int64(loc.Offset)) &^ 0xFFF)
    35  	if oldMcode, ok := linker.appliedADRPRelocs[&mCode[0]]; !ok {
    36  		linker.appliedADRPRelocs[&mCode[0]] = make([]byte, 8)
    37  		copy(linker.appliedADRPRelocs[&mCode[0]], mCode)
    38  	} else {
    39  		copy(mCode, oldMcode)
    40  	}
    41  	epilogueOffset := loc.EpilogueOffset
    42  	copy(segment.codeByte[epilogueOffset:epilogueOffset+loc.EpilogueSize], createARM64Nops(loc.EpilogueSize))
    43  
    44  	if loc.Type == reloctype.R_ARM64_GOTPCREL || loc.Type == reloctype.R_ARM64_TLS_IE {
    45  		epilogueToRelocDistance := epilogueOffset - loc.Offset
    46  		if epilogueToRelocDistance < 0 || epilogueToRelocDistance > 1<<32 {
    47  			return fmt.Errorf("unexpected R_ARM64_GOTPCREL relocation with negative or >32-bit offset %d: %s", epilogueToRelocDistance, loc.Sym.Name)
    48  		}
    49  		signedOffset = int64(alignof((segment.codeBase+epilogueOffset)-((segment.codeBase+loc.Offset)&^0xFFF), PtrSize))
    50  		putAddress(byteorder, mCode[epilogueToRelocDistance:], uint64(symAddr+uintptr(loc.Add)))
    51  	}
    52  	// R_ADDRARM64 relocs include 2x 32 bit instructions, one ADRP, and one ADD/LDR/STR - both contain the destination register in the lowest 5 bits
    53  	if signedOffset > 1<<32 || signedOffset < -1<<32 || (linker.options.ForceTestRelocationEpilogues && loc.EpilogueSize > 0 && !(loc.Type == reloctype.R_ARM64_GOTPCREL || loc.Type == reloctype.R_ARM64_TLS_IE)) {
    54  		if loc.EpilogueSize == 0 {
    55  			return fmt.Errorf("relocation epilogue not available but got a >32-bit ADRP reloc with offset %d: %s", signedOffset, loc.Sym.Name)
    56  		}
    57  		// Too far to fit inside an ADRP+ADD, do a jump to some extra code we add at the end big enough to fit any 64 bit address
    58  		symAddr += uintptr(loc.Add)
    59  		adrp := byteorder.Uint32(mCode)
    60  		bcode := byteorder.Uint32(arm64Bcode) // Unconditional branch
    61  		bcode |= ((uint32(epilogueOffset) - uint32(loc.Offset)) >> 2) & 0x01FFFFFF
    62  		if epilogueOffset-loc.Offset < 0 {
    63  			bcode |= 0x02000000 // 26th bit is sign bit
    64  		}
    65  		byteorder.PutUint32(mCode, bcode) // The second ADD/LD/ST instruction in the ADRP reloc will be bypassed as we return from the jump after it
    66  
    67  		ldrCode8Bytes := armLDRCode8Bytes   // LDR PC+8
    68  		ldrCode12Bytes := armLDRCode12Bytes // LDR PC+12
    69  		ldrCode8Bytes |= adrp & 0x1F        // Set the register
    70  		ldrCode12Bytes |= adrp & 0x1F       // Set the register
    71  
    72  		if loc.Type == reloctype.R_ADDRARM64 {
    73  			byteorder.PutUint32(segment.codeByte[epilogueOffset:], ldrCode8Bytes)
    74  			epilogueOffset += Uint32Size
    75  		} else {
    76  			// must be LDR/STR reloc - the entire 64 bit address will be loaded in the register specified in the ADRP instruction,
    77  			// so should be able to just append the LDR or STR immediately after
    78  			byteorder.PutUint32(segment.codeByte[epilogueOffset:], ldrCode12Bytes)
    79  			epilogueOffset += Uint32Size
    80  			ldOrSt := byteorder.Uint32(mCode[4:])
    81  			byteorder.PutUint32(segment.codeByte[epilogueOffset:], ldOrSt)
    82  			epilogueOffset += Uint32Size
    83  		}
    84  
    85  		bcode = byteorder.Uint32(arm64Bcode)
    86  		bcode |= ((uint32(loc.Offset) - uint32(epilogueOffset) + PtrSize) >> 2) & 0x01FFFFFF
    87  		if loc.Offset-epilogueOffset+PtrSize < 0 {
    88  			bcode |= 0x02000000
    89  		}
    90  		byteorder.PutUint32(segment.codeByte[epilogueOffset:], bcode)
    91  		epilogueOffset += Uint32Size
    92  
    93  		putAddressAddOffset(byteorder, segment.codeByte, &epilogueOffset, uint64(symAddr))
    94  	} else {
    95  		// Bit layout of ADRP instruction is:
    96  
    97  		// 31  30  29  28  27  26  25  24  23  22  21  20  19  18  17  16  15  14  13  11  10  09  08  07  06  05  04  03  02  01  00
    98  		// op  [imlo]   1   0   0   0   0  [<----------------------------- imm hi ----------------------------->]  [  dst register  ]
    99  
   100  		// Bit layout of ADD instruction (64-bit) is:
   101  
   102  		// 31  30  29  28  27  26  25  24  23  22  21  20  19  18  17  16  15  14  13  11  10  09  08  07  06  05  04  03  02  01  00
   103  		//  1   0   0   1   0   0   0   1   0   0  [<--------------- imm12 ---------------->]  [  src register  ]  [  dst register  ]
   104  		// sf <- 64 bit                        sh <- whether to left shift imm12 by 12 bits
   105  
   106  		immLow := uint32((uint64(signedOffset)>>12)&3) << 29
   107  		immHigh := uint32((uint64(signedOffset)>>12>>2)&0x7FFFF) << 5
   108  		adrp := byteorder.Uint32(mCode[0:4])
   109  		adrp |= immLow | immHigh
   110  		addOrLdOrSt := byteorder.Uint32(mCode[4:8])
   111  		switch loc.Type {
   112  		case reloctype.R_ADDRARM64, reloctype.R_ARM64_PCREL_LDST8:
   113  			addOrLdOrSt |= uint32(uint64(signedOffset)&0xFFF) << 10
   114  		case reloctype.R_ARM64_PCREL_LDST16:
   115  			if signedOffset&0x1 != 0 {
   116  				err = fmt.Errorf("offset for 16-bit load/store has unaligned value %d", signedOffset&0xFFF)
   117  			}
   118  			addOrLdOrSt |= (uint32(signedOffset&0xFFF) >> 1) << 10
   119  		case reloctype.R_ARM64_PCREL_LDST32:
   120  			if signedOffset&0x3 != 0 {
   121  				err = fmt.Errorf("offset for 32-bit load/store has unaligned value %d", signedOffset&0xFFF)
   122  			}
   123  			addOrLdOrSt |= (uint32(signedOffset&0xFFF) >> 2) << 10
   124  		case reloctype.R_ARM64_PCREL_LDST64, reloctype.R_ARM64_GOTPCREL, reloctype.R_ARM64_TLS_IE:
   125  			if signedOffset&0x7 != 0 {
   126  				err = fmt.Errorf("offset for 64-bit load/store has unaligned value %d", signedOffset&0xFFF)
   127  			}
   128  			addOrLdOrSt |= (uint32(signedOffset&0xFFF) >> 3) << 10
   129  		}
   130  		byteorder.PutUint32(mCode, adrp)
   131  		byteorder.PutUint32(mCode[4:], addOrLdOrSt)
   132  	}
   133  	return err
   134  }
   135  
   136  func (linker *Linker) relocateCALL(addr uintptr, loc obj.Reloc, segment *segment, relocByte []byte, addrBase int) error {
   137  	byteorder := linker.Arch.ByteOrder
   138  	offset := int(addr) - (addrBase + loc.Offset + loc.Size) + loc.Add
   139  	epilogueOffset := loc.EpilogueOffset
   140  	copy(segment.codeByte[epilogueOffset:epilogueOffset+loc.EpilogueSize], createX86Nops(loc.EpilogueSize))
   141  
   142  	if offset > 0x7FFFFFFF || offset < -0x80000000 || (linker.options.ForceTestRelocationEpilogues && loc.EpilogueSize > 0) {
   143  		// JMP into the epilogue, then CALL into the actual func using a PCREL 8 byte address placed after the JMP back from the epilogue
   144  		if loc.EpilogueSize == 0 {
   145  			return fmt.Errorf("relocation epilogue not available but got a >32-bit CALL reloc (x86 code: %x) with offset %d: %s", relocByte[loc.Offset-2:loc.Offset+loc.Size], offset, loc.Sym.Name)
   146  		}
   147  		// Replace the E8 CALL with a E9 JMP into the epilogue, the CALL the function, then JMP (near or far) back
   148  		relocByte[loc.Offset-1] = x86amd64JMPcode
   149  		offset = (segment.codeBase + epilogueOffset) - (addrBase + loc.Offset + loc.Size) // Point the JMP offset at the epilogue
   150  		copy(segment.codeByte[epilogueOffset:], x86amd64CALLFarCode)
   151  		epilogueOffset += len(x86amd64CALLFarCode)
   152  		returnOffset := (loc.Offset + loc.Size) - epilogueOffset - len(x86amd64JMPShortCode) // assumes short jump - if we need a near jump, we'll adjust
   153  		if returnOffset > -0x80 && returnOffset < 0 {
   154  			byteorder.PutUint32(relocByte[epilogueOffset-4:], uint32(len(x86amd64JMPShortCode))) // Read the 8 bytes after the length of the JMP back
   155  			copy(segment.codeByte[epilogueOffset:], x86amd64JMPShortCode)
   156  			segment.codeByte[epilogueOffset+1] = uint8(returnOffset)
   157  			epilogueOffset += len(x86amd64JMPShortCode)
   158  		} else {
   159  			byteorder.PutUint32(relocByte[epilogueOffset-4:], uint32(len(x86amd64JMPNearCode))) // Read the 8 bytes after the length of the JMP back
   160  			returnOffset -= len(x86amd64JMPNearCode) - len(x86amd64JMPShortCode)
   161  			copy(segment.codeByte[epilogueOffset:], x86amd64JMPNearCode)
   162  			byteorder.PutUint32(segment.codeByte[epilogueOffset+1:], uint32(returnOffset))
   163  			epilogueOffset += len(x86amd64JMPNearCode)
   164  		}
   165  		putAddressAddOffset(byteorder, segment.codeByte, &epilogueOffset, uint64(addr)+uint64(loc.Add))
   166  	}
   167  	byteorder.PutUint32(relocByte[loc.Offset:], uint32(offset))
   168  	return nil
   169  }
   170  
   171  func (linker *Linker) relocateGOTPCREL(addr uintptr, loc obj.Reloc, relocByte []byte) {
   172  	putAddress(linker.Arch.ByteOrder, relocByte[loc.EpilogueOffset:], uint64(addr))
   173  	linker.Arch.ByteOrder.PutUint32(relocByte[loc.Offset:], uint32(loc.EpilogueOffset-loc.Offset-loc.Size))
   174  }
   175  
   176  func (linker *Linker) relocatePCREL(addr uintptr, loc obj.Reloc, segment *segment, relocByte []byte, addrBase int) (err error) {
   177  	byteorder := linker.Arch.ByteOrder
   178  	offset := int(addr) - (addrBase + loc.Offset + loc.Size) + loc.Add
   179  	epilogueOffset := loc.EpilogueOffset
   180  	if oldMcode, ok := linker.appliedPCRelRelocs[&relocByte[loc.Offset]]; !ok {
   181  		linker.appliedPCRelRelocs[&relocByte[loc.Offset]] = make([]byte, loc.Size+2)
   182  		copy(linker.appliedPCRelRelocs[&relocByte[loc.Offset]], relocByte[loc.Offset-2:])
   183  	} else {
   184  		copy(relocByte[loc.Offset-2:], oldMcode)
   185  	}
   186  	copy(segment.codeByte[epilogueOffset:epilogueOffset+loc.EpilogueSize], createX86Nops(loc.EpilogueSize))
   187  
   188  	if offset > 0x7FFFFFFF || offset < -0x80000000 || (linker.options.ForceTestRelocationEpilogues && loc.EpilogueSize > 0) {
   189  		if loc.EpilogueSize == 0 {
   190  			return fmt.Errorf("relocation epilogue not available but got a >32-bit PCREL reloc (x86 code: %x) with offset %d: %s", relocByte[loc.Offset-3:loc.Offset+loc.Size], offset, loc.Sym.Name)
   191  		}
   192  		extraJMPDistance := 0
   193  		relocOffsetIdx := 0
   194  		cmplComparator := relocByte[loc.Offset+loc.Size]
   195  		relocToEpilogueOffset := (segment.codeBase + epilogueOffset) - (addrBase + loc.Offset + loc.Size)
   196  		bytes := relocByte[loc.Offset-2:]
   197  		opcode := relocByte[loc.Offset-2]
   198  		rexPrefix := relocByte[loc.Offset-3]
   199  		dstRegister := ZeroByte
   200  
   201  		if opcode == x86amd64LEAcode {
   202  			bytes[0] = x86amd64MOVcode
   203  			relocOffsetIdx = 2
   204  		} else if opcode == x86amd64MOVcode {
   205  			dstRegister = ((relocByte[loc.Offset-1] >> 3) & 0x7) | ((rexPrefix & 0x4) << 1) // rex prefix encodes high bit of dst register in bit 3
   206  			srcRegister := relocByte[loc.Offset-1] & 0x7
   207  			if srcRegister != 0x5 { // 0x5 == PC (RIP) register - if it's not a PCREL address, then that's an unexpected MOV instruction using an R_PCREL reloc
   208  				return fmt.Errorf("unexpected src register %x (not RIP) for MOV PCREL reloc (x86 code: %x) with offset %d: %s", relocByte[loc.Offset-1], relocByte[loc.Offset-3:loc.Offset+loc.Size], offset, loc.Sym.Name)
   209  			}
   210  			copy(bytes, append(x86amd64JMPNearCode, x86amd64NOPcode))
   211  			relocOffsetIdx = 1
   212  		} else if opcode == x86amd64CMPLcode {
   213  			copy(bytes, append(x86amd64JMPNearCode, x86amd64NOPcode, x86amd64NOPcode))
   214  			relocOffsetIdx = 1
   215  		} else if opcode == x86amd64CALL2code {
   216  			// Probably a CGo FF15 call - CALL into the epilogue, then immediately JMP into function, then RET will bring us back to callsite
   217  			relocOffsetIdx = 2
   218  		} else if (bytes[1] == x86amd64CALLcode) && binary.LittleEndian.Uint32(relocByte[loc.Offset:]) == 0 {
   219  			// Probably a CGo call - CALL into the epilogue, then immediately JMP into function, then RET will bring us back to callsite
   220  			opcode = bytes[1]
   221  			relocOffsetIdx = 2
   222  		} else if bytes[1] == x86amd64JMPcode {
   223  			// Also a CGo call
   224  			opcode = bytes[1]
   225  			relocOffsetIdx = 2
   226  		} else {
   227  			return fmt.Errorf("do not support x86 opcode: %x for symbol %s (offset %d)!\n", relocByte[loc.Offset-2:loc.Offset+loc.Size], loc.Sym.Name, offset)
   228  		}
   229  		extraJMPDistance = 2 - relocOffsetIdx
   230  		byteorder.PutUint32(bytes[relocOffsetIdx:], uint32(relocToEpilogueOffset+extraJMPDistance))
   231  		switch opcode {
   232  		case x86amd64CMPLcode:
   233  			copy(segment.codeByte[epilogueOffset:], x86amd64replaceCMPLcode)
   234  			segment.codeByte[epilogueOffset+14] = cmplComparator // The 8 bit number to compare against
   235  			putAddress(linker.Arch.ByteOrder, segment.codeByte[epilogueOffset+3:], uint64(addr+uintptr(loc.Add+extraJMPDistance)))
   236  			epilogueOffset += len(x86amd64replaceCMPLcode)
   237  		case x86amd64MOVcode:
   238  			if dstRegister == 0x00 { // RAX
   239  				copy(segment.codeByte[epilogueOffset:], x86amd64replaceMOVQcodeRAX)
   240  				putAddress(linker.Arch.ByteOrder, segment.codeByte[epilogueOffset+2:], uint64(addr+uintptr(loc.Add)))
   241  				epilogueOffset += len(x86amd64replaceMOVQcodeRAX)
   242  			} else {
   243  				copy(segment.codeByte[epilogueOffset:], x86amd64replaceMOVQcode)
   244  				putAddress(linker.Arch.ByteOrder, segment.codeByte[epilogueOffset+3:], uint64(addr+uintptr(loc.Add)))
   245  				segment.codeByte[epilogueOffset+11] |= (dstRegister & 0x8) >> 1
   246  				segment.codeByte[epilogueOffset+13] = (dstRegister & 0x7) << 3
   247  				epilogueOffset += len(x86amd64replaceMOVQcode)
   248  			}
   249  		case x86amd64CALLcode:
   250  			bytes[1] = x86amd64JMPcode
   251  			offset = (segment.codeBase + epilogueOffset) - (addrBase + loc.Offset + loc.Size) // Point the JMP offset at the epilogue
   252  			copy(segment.codeByte[epilogueOffset:], x86amd64CALLFarCode)
   253  			epilogueOffset += len(x86amd64CALLFarCode)
   254  			returnOffset := (loc.Offset + loc.Size) - epilogueOffset - len(x86amd64JMPShortCode) // assumes short jump - if we need a near jump, we'll adjust
   255  			if returnOffset > -0x80 && returnOffset < 0 {
   256  				byteorder.PutUint32(relocByte[epilogueOffset-4:], uint32(len(x86amd64JMPShortCode))) // Read the 8 bytes after the length of the JMP back
   257  				copy(segment.codeByte[epilogueOffset:], x86amd64JMPShortCode)
   258  				segment.codeByte[epilogueOffset+1] = uint8(returnOffset)
   259  				epilogueOffset += len(x86amd64JMPShortCode)
   260  			} else {
   261  				byteorder.PutUint32(relocByte[epilogueOffset-4:], uint32(len(x86amd64JMPNearCode))) // Read the 8 bytes after the length of the JMP back
   262  				returnOffset -= len(x86amd64JMPNearCode) - len(x86amd64JMPShortCode)
   263  				copy(segment.codeByte[epilogueOffset:], x86amd64JMPNearCode)
   264  				byteorder.PutUint32(segment.codeByte[epilogueOffset+1:], uint32(returnOffset))
   265  				epilogueOffset += len(x86amd64JMPNearCode)
   266  			}
   267  			putAddressAddOffset(byteorder, segment.codeByte, &epilogueOffset, uint64(addr)+uint64(loc.Add))
   268  		case x86amd64CALL2code:
   269  			putAddressAddOffset(byteorder, segment.codeByte, &epilogueOffset, uint64(addr)+uint64(loc.Add))
   270  		case x86amd64JMPcode:
   271  			copy(segment.codeByte[epilogueOffset:], x86amd64JMPLcode)
   272  			epilogueOffset += len(x86amd64JMPLcode)
   273  			putAddress(linker.Arch.ByteOrder, segment.codeByte[epilogueOffset:], uint64(addr+uintptr(loc.Add)))
   274  			epilogueOffset += PtrSize
   275  		case x86amd64LEAcode:
   276  			putAddressAddOffset(byteorder, segment.codeByte, &epilogueOffset, uint64(addr+uintptr(loc.Add)))
   277  		default:
   278  			return fmt.Errorf("unexpected x86 opcode %x: %x for symbol %s (offset %d)!\n", opcode, relocByte[loc.Offset-2:loc.Offset+loc.Size], loc.Sym.Name, offset)
   279  		}
   280  
   281  		switch opcode {
   282  		case x86amd64CMPLcode, x86amd64MOVcode:
   283  			returnOffset := (loc.Offset + loc.Size) - epilogueOffset - len(x86amd64JMPShortCode) // assumes short jump - if we need a near jump, we'll adjust
   284  			if returnOffset > -0x80 && returnOffset < 0 {
   285  				copy(segment.codeByte[epilogueOffset:], x86amd64JMPShortCode)
   286  				segment.codeByte[epilogueOffset+1] = uint8(returnOffset)
   287  				epilogueOffset += len(x86amd64JMPShortCode)
   288  			} else {
   289  				returnOffset -= len(x86amd64JMPNearCode) - len(x86amd64JMPShortCode)
   290  				copy(segment.codeByte[epilogueOffset:], x86amd64JMPNearCode)
   291  				byteorder.PutUint32(segment.codeByte[epilogueOffset+1:], uint32(returnOffset))
   292  				epilogueOffset += len(x86amd64JMPNearCode)
   293  			}
   294  		}
   295  	} else {
   296  		byteorder.PutUint32(relocByte[loc.Offset:], uint32(offset))
   297  	}
   298  	return err
   299  }
   300  
   301  func (linker *Linker) relocateCALLARM(addr uintptr, loc obj.Reloc, segment *segment) error {
   302  	byteorder := linker.Arch.ByteOrder
   303  	add := loc.Add
   304  	if loc.Type == reloctype.R_CALLARM {
   305  		add = int(signext24(int64(loc.Add&0xFFFFFF)) * 4)
   306  	}
   307  	epilogueOffset := loc.EpilogueOffset
   308  	copy(segment.codeByte[epilogueOffset:epilogueOffset+loc.EpilogueSize], make([]byte, loc.EpilogueSize))
   309  	offset := (int(addr) + add - (segment.codeBase + loc.Offset)) / 4
   310  	if offset > 0x7FFFFF || offset < -0x800000 || (linker.options.ForceTestRelocationEpilogues && loc.EpilogueSize > 0) {
   311  		if loc.EpilogueSize == 0 {
   312  			return fmt.Errorf("relocation epilogue not available but got a >24-bit CALLARM reloc with offset %d: %s", offset, loc.Sym.Name)
   313  		}
   314  		off := uint32(epilogueOffset-loc.Offset) / 4
   315  		if loc.Type == reloctype.R_CALLARM {
   316  			add = int(signext24(int64(loc.Add&0xFFFFFF)+2) * 4)
   317  			off = uint32(epilogueOffset-loc.Offset-8) / 4
   318  		}
   319  		putUint24(segment.codeByte[loc.Offset:], off)
   320  		if loc.Type == reloctype.R_CALLARM64 {
   321  			copy(segment.codeByte[epilogueOffset:], arm64CALLCode)
   322  			epilogueOffset += len(arm64CALLCode)
   323  		} else {
   324  			copy(segment.codeByte[epilogueOffset:], armcode)
   325  			epilogueOffset += len(armcode)
   326  		}
   327  		putAddressAddOffset(byteorder, segment.codeByte, &epilogueOffset, uint64(int(addr)+add))
   328  	} else {
   329  		val := byteorder.Uint32(segment.codeByte[loc.Offset:])
   330  		if loc.Type == reloctype.R_CALLARM {
   331  			val |= uint32(offset) & 0x00FFFFFF
   332  		} else {
   333  			val |= uint32(offset) & 0x03FFFFFF
   334  		}
   335  		byteorder.PutUint32(segment.codeByte[loc.Offset:], val)
   336  	}
   337  	return nil
   338  }
   339  
   340  func (linker *Linker) relocate(codeModule *CodeModule, symbolMap map[string]uintptr) (err error) {
   341  	segment := &codeModule.segment
   342  	byteorder := linker.Arch.ByteOrder
   343  
   344  	for _, symbol := range linker.symMap {
   345  		if linker.options.DumpTextBeforeAndAfterRelocs && linker.options.RelocationDebugWriter != nil && symbol.Kind == symkind.STEXT && symbol.Offset >= 0 {
   346  			_, _ = fmt.Fprintf(linker.options.RelocationDebugWriter, "BEFORE RELOC (%x - %x) %142s: %x\n", codeModule.codeBase+symbol.Offset, codeModule.codeBase+symbol.Offset+symbol.Size, symbol.Name, codeModule.codeByte[symbol.Offset:symbol.Offset+symbol.Size])
   347  		}
   348  		for _, loc := range symbol.Reloc {
   349  			addr := symbolMap[loc.Sym.Name]
   350  			fmAddr, duplicated := symbolMap[FirstModulePrefix+loc.Sym.Name]
   351  			if strings.HasPrefix(loc.Sym.Name, TypePrefix) && !duplicated {
   352  				if variant, ok := symbolIsVariant(loc.Sym.Name); ok {
   353  					fmAddr, duplicated = symbolMap[variant]
   354  				}
   355  			}
   356  			if duplicated {
   357  				isTypeWhichShouldNotBeDeduped := false
   358  				for _, pkgPath := range linker.options.SkipTypeDeduplicationForPackages {
   359  					if loc.Sym.Pkg == pkgPath {
   360  						isTypeWhichShouldNotBeDeduped = true
   361  					}
   362  				}
   363  				if !isTypeWhichShouldNotBeDeduped {
   364  					// Always use the new module types initially - we will later check for type equality and
   365  					// deduplicate them if they're structurally equal. If we used the firstmodule types here, there's a
   366  					// risk they're not structurally equal, but it would be too late
   367  					if !strings.HasPrefix(loc.Sym.Name, TypePrefix) {
   368  						// If not a type, and not skipping deduplication for this package, use the firstmodule version
   369  						addr = fmAddr
   370  					}
   371  				}
   372  			}
   373  			sym := loc.Sym
   374  			relocByte := segment.dataByte
   375  			addrBase := segment.dataBase
   376  			if symbol.Kind == symkind.STEXT {
   377  				addrBase = segment.codeBase
   378  				relocByte = segment.codeByte
   379  			}
   380  			if strings.HasPrefix(sym.Name, ItabPrefix) {
   381  				isItabWhichShouldNotBeDeduped := false
   382  				for _, pkgPath := range linker.options.SkipTypeDeduplicationForPackages {
   383  					if strings.HasPrefix(strings.TrimLeft(strings.TrimPrefix(sym.Name, ItabPrefix), "*"), pkgPath) {
   384  						isItabWhichShouldNotBeDeduped = true
   385  					}
   386  				}
   387  				if (addr == 0 || isItabWhichShouldNotBeDeduped) && linker.isSymbolReachable(sym.Name) {
   388  					addr = uintptr(segment.dataBase + loc.Sym.Offset)
   389  					symbolMap[loc.Sym.Name] = addr
   390  					codeModule.module.itablinks = append(codeModule.module.itablinks, (*itab)(adduintptr(uintptr(segment.dataBase), loc.Sym.Offset)))
   391  				}
   392  			}
   393  
   394  			if linker.options.RelocationDebugWriter != nil && loc.Offset != InvalidOffset {
   395  				isDup := "    "
   396  				if duplicated {
   397  					isDup = "DUP "
   398  				}
   399  				var weakness string
   400  				if loc.Type&reloctype.R_WEAK > 0 {
   401  					weakness = "WEAK|"
   402  				}
   403  				relocType := weakness + objabi.RelocType(loc.Type&^reloctype.R_WEAK).String()
   404  				_, _ = fmt.Fprintf(linker.options.RelocationDebugWriter, "RELOCATING %s %10s %10s %18s Base: 0x%x Pos: 0x%08x, Addr: 0x%016x AddrFromBase: %12d %s   to    %s\n",
   405  					isDup, objabi.SymKind(symbol.Kind), objabi.SymKind(sym.Kind), relocType, addrBase, uintptr(unsafe.Pointer(&relocByte[loc.Offset])),
   406  					addr, int(addr)-addrBase, symbol.Name, sym.Name)
   407  			}
   408  
   409  			if addr != InvalidHandleValue {
   410  				switch loc.Type {
   411  				case reloctype.R_ARM64_TLS_LE:
   412  					if _, ok := symbolMap[TLSNAME]; !ok {
   413  						symbolMap[TLSNAME] = tls.GetTLSOffset(linker.Arch, PtrSize)
   414  					}
   415  					v := symbolMap[TLSNAME] + 2*PtrSize
   416  					if v < 0 || v >= 32678 {
   417  						err = fmt.Errorf("got a R_ARM64_TLS_LE relocation inside %s (%s) with TLS offset out of range: %d", symbol.Name, loc.Sym.Name, v)
   418  					}
   419  					val := byteorder.Uint32(relocByte[loc.Offset:])
   420  					val |= uint32(v) << 5
   421  					byteorder.PutUint32(relocByte[loc.Offset:], val)
   422  				case reloctype.R_TLS_LE:
   423  					if _, ok := symbolMap[TLSNAME]; !ok {
   424  						symbolMap[TLSNAME] = tls.GetTLSOffset(linker.Arch, PtrSize)
   425  					}
   426  					byteorder.PutUint32(relocByte[loc.Offset:], uint32(symbolMap[TLSNAME]))
   427  				case reloctype.R_CALL, reloctype.R_CALL | reloctype.R_WEAK:
   428  					err = linker.relocateCALL(addr, loc, segment, relocByte, addrBase)
   429  				case reloctype.R_PCREL:
   430  					if symbol.Kind != symkind.STEXT {
   431  						err = fmt.Errorf("impossible! Sym: %s (target %s) is not in code segment! (kind %s)\n", symbol.Name, sym.Name, objabi.SymKind(sym.Kind))
   432  						break
   433  					}
   434  					err = linker.relocatePCREL(addr, loc, segment, relocByte, addrBase)
   435  				case reloctype.R_CALLARM, reloctype.R_CALLARM64, reloctype.R_CALLARM64 | reloctype.R_WEAK:
   436  					err = linker.relocateCALLARM(addr, loc, segment)
   437  				case reloctype.R_ADDRARM64, reloctype.R_ARM64_PCREL_LDST8, reloctype.R_ARM64_PCREL_LDST16, reloctype.R_ARM64_PCREL_LDST32, reloctype.R_ARM64_PCREL_LDST64, reloctype.R_ARM64_GOTPCREL:
   438  					if symbol.Kind != symkind.STEXT {
   439  						err = fmt.Errorf("impossible! Sym: %s is not in code segment! (kind %s)\n", sym.Name, objabi.SymKind(sym.Kind))
   440  						break
   441  					}
   442  					err = linker.relocateADRP(relocByte[loc.Offset:], loc, segment, addr)
   443  				case reloctype.R_ADDR, reloctype.R_WEAKADDR:
   444  					address := uintptr(int(addr) + loc.Add)
   445  					putAddress(byteorder, relocByte[loc.Offset:], uint64(address))
   446  				case reloctype.R_CALLIND:
   447  					// nothing todo
   448  				case reloctype.R_ADDROFF, reloctype.R_WEAKADDROFF:
   449  					offset := int(addr) - addrBase + loc.Add
   450  					if offset > 0x7FFFFFFF || offset < -0x80000000 {
   451  						err = fmt.Errorf("symName: %s offset for %s: %d overflows!\n", sym.Name, objabi.RelocType(loc.Type), offset)
   452  					}
   453  					byteorder.PutUint32(relocByte[loc.Offset:], uint32(offset))
   454  				case reloctype.R_METHODOFF:
   455  					if loc.Sym.Kind == symkind.STEXT {
   456  						addrBase = segment.codeBase
   457  					}
   458  					offset := int(addr) - addrBase + loc.Add
   459  					if offset > 0x7FFFFFFF || offset < -0x80000000 {
   460  						err = fmt.Errorf("symName: %s offset for R_METHODOFF: %d overflows!\n", sym.Name, offset)
   461  					}
   462  					byteorder.PutUint32(relocByte[loc.Offset:], uint32(offset))
   463  				case reloctype.R_GOTPCREL:
   464  					linker.relocateGOTPCREL(addr, loc, relocByte)
   465  				case reloctype.R_TLS_IE:
   466  					if _, ok := symbolMap[TLSNAME]; !ok {
   467  						symbolMap[TLSNAME] = tls.GetTLSOffset(linker.Arch, PtrSize)
   468  					}
   469  					linker.relocateGOTPCREL(symbolMap[TLSNAME], loc, relocByte)
   470  				case reloctype.R_ARM64_TLS_IE:
   471  					if _, ok := symbolMap[TLSNAME]; !ok {
   472  						symbolMap[TLSNAME] = tls.GetTLSOffset(linker.Arch, PtrSize)
   473  					}
   474  					err = linker.relocateADRP(relocByte[loc.Offset:], loc, segment, addr)
   475  				case reloctype.R_USETYPE:
   476  					// nothing todo
   477  				case reloctype.R_USEIFACE:
   478  					// nothing todo
   479  				case reloctype.R_USEIFACEMETHOD:
   480  					// nothing todo
   481  				case reloctype.R_ADDRCUOFF:
   482  					// nothing todo
   483  				case reloctype.R_KEEP:
   484  					// nothing todo
   485  				case reloctype.R_INITORDER:
   486  					// nothing todo
   487  				default:
   488  					err = fmt.Errorf("unknown reloc type: %s sym: %s", objabi.RelocType(loc.Type).String(), sym.Name)
   489  				}
   490  			} else {
   491  				if linker.isSymbolReachable(sym.Name) {
   492  					panic(fmt.Sprintf("could not find address of symbol '%s' for relocation inside '%s'", loc.Sym.Name, sym.Name))
   493  				}
   494  			}
   495  			if err != nil {
   496  				return err
   497  			}
   498  		}
   499  		if linker.options.DumpTextBeforeAndAfterRelocs && linker.options.RelocationDebugWriter != nil && symbol.Kind == symkind.STEXT && symbol.Offset >= 0 {
   500  			_, _ = fmt.Fprintf(linker.options.RelocationDebugWriter, " AFTER RELOC (%x - %x) %142s : %x\n", codeModule.codeBase+symbol.Offset, codeModule.codeBase+symbol.Offset+symbol.Size, symbol.Name, codeModule.codeByte[symbol.Offset:symbol.Offset+symbol.Size])
   501  		}
   502  	}
   503  	return err
   504  }