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 }