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 }