github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/link/arm/asm.go (about) 1 // Inferno utils/5l/asm.c 2 // https://bitbucket.org/inferno-os/inferno-os/src/master/utils/5l/asm.c 3 // 4 // Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. 5 // Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) 6 // Portions Copyright © 1997-1999 Vita Nuova Limited 7 // Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) 8 // Portions Copyright © 2004,2006 Bruce Ellis 9 // Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) 10 // Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others 11 // Portions Copyright © 2009 The Go Authors. All rights reserved. 12 // 13 // Permission is hereby granted, free of charge, to any person obtaining a copy 14 // of this software and associated documentation files (the "Software"), to deal 15 // in the Software without restriction, including without limitation the rights 16 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 17 // copies of the Software, and to permit persons to whom the Software is 18 // furnished to do so, subject to the following conditions: 19 // 20 // The above copyright notice and this permission notice shall be included in 21 // all copies or substantial portions of the Software. 22 // 23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 29 // THE SOFTWARE. 30 31 package arm 32 33 import ( 34 "debug/elf" 35 "fmt" 36 "log" 37 38 "github.com/go-asm/go/cmd/link/ld" 39 "github.com/go-asm/go/cmd/link/loader" 40 "github.com/go-asm/go/cmd/link/sym" 41 "github.com/go-asm/go/cmd/objabi" 42 "github.com/go-asm/go/cmd/sys" 43 ) 44 45 // This assembler: 46 // 47 // .align 2 48 // local.dso_init: 49 // ldr r0, .Lmoduledata 50 // .Lloadfrom: 51 // ldr r0, [r0] 52 // b runtime.addmoduledata@plt 53 // .align 2 54 // .Lmoduledata: 55 // .word local.moduledata(GOT_PREL) + (. - (.Lloadfrom + 4)) 56 // assembles to: 57 // 58 // 00000000 <local.dso_init>: 59 // 0: e59f0004 ldr r0, [pc, #4] ; c <local.dso_init+0xc> 60 // 4: e5900000 ldr r0, [r0] 61 // 8: eafffffe b 0 <runtime.addmoduledata> 62 // 8: R_ARM_JUMP24 runtime.addmoduledata 63 // c: 00000004 .word 0x00000004 64 // c: R_ARM_GOT_PREL local.moduledata 65 66 func gentext(ctxt *ld.Link, ldr *loader.Loader) { 67 initfunc, addmoduledata := ld.PrepareAddmoduledata(ctxt) 68 if initfunc == nil { 69 return 70 } 71 72 o := func(op uint32) { 73 initfunc.AddUint32(ctxt.Arch, op) 74 } 75 o(0xe59f0004) 76 o(0xe08f0000) 77 78 o(0xeafffffe) 79 rel, _ := initfunc.AddRel(objabi.R_CALLARM) 80 rel.SetOff(8) 81 rel.SetSiz(4) 82 rel.SetSym(addmoduledata) 83 rel.SetAdd(0xeafffffe) // vomit 84 85 o(0x00000000) 86 87 rel2, _ := initfunc.AddRel(objabi.R_PCREL) 88 rel2.SetOff(12) 89 rel2.SetSiz(4) 90 rel2.SetSym(ctxt.Moduledata) 91 rel2.SetAdd(4) 92 } 93 94 // Preserve highest 8 bits of a, and do addition to lower 24-bit 95 // of a and b; used to adjust ARM branch instruction's target. 96 func braddoff(a int32, b int32) int32 { 97 return int32((uint32(a))&0xff000000 | 0x00ffffff&uint32(a+b)) 98 } 99 100 func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym, r loader.Reloc, rIdx int) bool { 101 102 targ := r.Sym() 103 var targType sym.SymKind 104 if targ != 0 { 105 targType = ldr.SymType(targ) 106 } 107 108 switch r.Type() { 109 default: 110 if r.Type() >= objabi.ElfRelocOffset { 111 ldr.Errorf(s, "unexpected relocation type %d (%s)", r.Type(), sym.RelocName(target.Arch, r.Type())) 112 return false 113 } 114 115 // Handle relocations found in ELF object files. 116 case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_PLT32): 117 su := ldr.MakeSymbolUpdater(s) 118 su.SetRelocType(rIdx, objabi.R_CALLARM) 119 120 if targType == sym.SDYNIMPORT { 121 addpltsym(target, ldr, syms, targ) 122 su.SetRelocSym(rIdx, syms.PLT) 123 su.SetRelocAdd(rIdx, int64(braddoff(int32(r.Add()), ldr.SymPlt(targ)/4))) 124 } 125 126 return true 127 128 case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_THM_PC22): // R_ARM_THM_CALL 129 ld.Exitf("R_ARM_THM_CALL, are you using -marm?") 130 return false 131 132 case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_GOT32): // R_ARM_GOT_BREL 133 if targType != sym.SDYNIMPORT { 134 addgotsyminternal(target, ldr, syms, targ) 135 } else { 136 ld.AddGotSym(target, ldr, syms, targ, uint32(elf.R_ARM_GLOB_DAT)) 137 } 138 139 su := ldr.MakeSymbolUpdater(s) 140 su.SetRelocType(rIdx, objabi.R_CONST) // write r->add during relocsym 141 su.SetRelocSym(rIdx, 0) 142 su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymGot(targ))) 143 return true 144 145 case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_GOT_PREL): // GOT(nil) + A - nil 146 if targType != sym.SDYNIMPORT { 147 addgotsyminternal(target, ldr, syms, targ) 148 } else { 149 ld.AddGotSym(target, ldr, syms, targ, uint32(elf.R_ARM_GLOB_DAT)) 150 } 151 su := ldr.MakeSymbolUpdater(s) 152 su.SetRelocType(rIdx, objabi.R_PCREL) 153 su.SetRelocSym(rIdx, syms.GOT) 154 su.SetRelocAdd(rIdx, r.Add()+4+int64(ldr.SymGot(targ))) 155 return true 156 157 case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_GOTOFF): // R_ARM_GOTOFF32 158 su := ldr.MakeSymbolUpdater(s) 159 su.SetRelocType(rIdx, objabi.R_GOTOFF) 160 return true 161 162 case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_GOTPC): // R_ARM_BASE_PREL 163 su := ldr.MakeSymbolUpdater(s) 164 su.SetRelocType(rIdx, objabi.R_PCREL) 165 su.SetRelocSym(rIdx, syms.GOT) 166 su.SetRelocAdd(rIdx, r.Add()+4) 167 return true 168 169 case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_CALL): 170 su := ldr.MakeSymbolUpdater(s) 171 su.SetRelocType(rIdx, objabi.R_CALLARM) 172 if targType == sym.SDYNIMPORT { 173 addpltsym(target, ldr, syms, targ) 174 su.SetRelocSym(rIdx, syms.PLT) 175 su.SetRelocAdd(rIdx, int64(braddoff(int32(r.Add()), ldr.SymPlt(targ)/4))) 176 } 177 return true 178 179 case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_REL32): // R_ARM_REL32 180 su := ldr.MakeSymbolUpdater(s) 181 su.SetRelocType(rIdx, objabi.R_PCREL) 182 su.SetRelocAdd(rIdx, r.Add()+4) 183 return true 184 185 case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_ABS32): 186 if targType == sym.SDYNIMPORT { 187 ldr.Errorf(s, "unexpected R_ARM_ABS32 relocation for dynamic symbol %s", ldr.SymName(targ)) 188 } 189 su := ldr.MakeSymbolUpdater(s) 190 su.SetRelocType(rIdx, objabi.R_ADDR) 191 return true 192 193 case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_PC24), 194 objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_JUMP24): 195 su := ldr.MakeSymbolUpdater(s) 196 su.SetRelocType(rIdx, objabi.R_CALLARM) 197 if targType == sym.SDYNIMPORT { 198 addpltsym(target, ldr, syms, targ) 199 su.SetRelocSym(rIdx, syms.PLT) 200 su.SetRelocAdd(rIdx, int64(braddoff(int32(r.Add()), ldr.SymPlt(targ)/4))) 201 } 202 203 return true 204 } 205 206 // Handle references to ELF symbols from our own object files. 207 if targType != sym.SDYNIMPORT { 208 return true 209 } 210 211 // Reread the reloc to incorporate any changes in type above. 212 relocs := ldr.Relocs(s) 213 r = relocs.At(rIdx) 214 215 switch r.Type() { 216 case objabi.R_CALLARM: 217 if target.IsExternal() { 218 // External linker will do this relocation. 219 return true 220 } 221 addpltsym(target, ldr, syms, targ) 222 su := ldr.MakeSymbolUpdater(s) 223 su.SetRelocSym(rIdx, syms.PLT) 224 su.SetRelocAdd(rIdx, int64(braddoff(int32(r.Add()), ldr.SymPlt(targ)/4))) // TODO: don't use r.Add for instruction bytes (issue 19811) 225 return true 226 227 case objabi.R_ADDR: 228 if ldr.SymType(s) != sym.SDATA { 229 break 230 } 231 if target.IsElf() { 232 ld.Adddynsym(ldr, target, syms, targ) 233 rel := ldr.MakeSymbolUpdater(syms.Rel) 234 rel.AddAddrPlus(target.Arch, s, int64(r.Off())) 235 rel.AddUint32(target.Arch, elf.R_INFO32(uint32(ldr.SymDynid(targ)), uint32(elf.R_ARM_GLOB_DAT))) // we need a nil + A dynamic reloc 236 su := ldr.MakeSymbolUpdater(s) 237 su.SetRelocType(rIdx, objabi.R_CONST) // write r->add during relocsym 238 su.SetRelocSym(rIdx, 0) 239 return true 240 } 241 242 case objabi.R_GOTPCREL: 243 if target.IsExternal() { 244 // External linker will do this relocation. 245 return true 246 } 247 if targType != sym.SDYNIMPORT { 248 ldr.Errorf(s, "R_GOTPCREL target is not SDYNIMPORT symbol: %v", ldr.SymName(targ)) 249 } 250 ld.AddGotSym(target, ldr, syms, targ, uint32(elf.R_ARM_GLOB_DAT)) 251 su := ldr.MakeSymbolUpdater(s) 252 su.SetRelocType(rIdx, objabi.R_PCREL) 253 su.SetRelocSym(rIdx, syms.GOT) 254 su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymGot(targ))) 255 return true 256 } 257 258 return false 259 } 260 261 func elfreloc1(ctxt *ld.Link, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym, r loader.ExtReloc, ri int, sectoff int64) bool { 262 out.Write32(uint32(sectoff)) 263 264 elfsym := ld.ElfSymForReloc(ctxt, r.Xsym) 265 siz := r.Size 266 switch r.Type { 267 default: 268 return false 269 case objabi.R_ADDR, objabi.R_DWARFSECREF: 270 if siz == 4 { 271 out.Write32(uint32(elf.R_ARM_ABS32) | uint32(elfsym)<<8) 272 } else { 273 return false 274 } 275 case objabi.R_PCREL: 276 if siz == 4 { 277 out.Write32(uint32(elf.R_ARM_REL32) | uint32(elfsym)<<8) 278 } else { 279 return false 280 } 281 case objabi.R_CALLARM: 282 if siz == 4 { 283 relocs := ldr.Relocs(s) 284 r := relocs.At(ri) 285 if r.Add()&0xff000000 == 0xeb000000 { // BL // TODO: using r.Add here is bad (issue 19811) 286 out.Write32(uint32(elf.R_ARM_CALL) | uint32(elfsym)<<8) 287 } else { 288 out.Write32(uint32(elf.R_ARM_JUMP24) | uint32(elfsym)<<8) 289 } 290 } else { 291 return false 292 } 293 case objabi.R_TLS_LE: 294 out.Write32(uint32(elf.R_ARM_TLS_LE32) | uint32(elfsym)<<8) 295 case objabi.R_TLS_IE: 296 out.Write32(uint32(elf.R_ARM_TLS_IE32) | uint32(elfsym)<<8) 297 case objabi.R_GOTPCREL: 298 if siz == 4 { 299 out.Write32(uint32(elf.R_ARM_GOT_PREL) | uint32(elfsym)<<8) 300 } else { 301 return false 302 } 303 } 304 305 return true 306 } 307 308 func elfsetupplt(ctxt *ld.Link, ldr *loader.Loader, plt, got *loader.SymbolBuilder, dynamic loader.Sym) { 309 if plt.Size() == 0 { 310 // str lr, [sp, #-4]! 311 plt.AddUint32(ctxt.Arch, 0xe52de004) 312 313 // ldr lr, [pc, #4] 314 plt.AddUint32(ctxt.Arch, 0xe59fe004) 315 316 // add lr, pc, lr 317 plt.AddUint32(ctxt.Arch, 0xe08fe00e) 318 319 // ldr pc, [lr, #8]! 320 plt.AddUint32(ctxt.Arch, 0xe5bef008) 321 322 // .word &GLOBAL_OFFSET_TABLE[0] - . 323 plt.AddPCRelPlus(ctxt.Arch, got.Sym(), 4) 324 325 // the first .plt entry requires 3 .plt.got entries 326 got.AddUint32(ctxt.Arch, 0) 327 328 got.AddUint32(ctxt.Arch, 0) 329 got.AddUint32(ctxt.Arch, 0) 330 } 331 } 332 333 func machoreloc1(*sys.Arch, *ld.OutBuf, *loader.Loader, loader.Sym, loader.ExtReloc, int64) bool { 334 return false 335 } 336 337 func pereloc1(arch *sys.Arch, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym, r loader.ExtReloc, sectoff int64) bool { 338 rs := r.Xsym 339 rt := r.Type 340 341 if ldr.SymDynid(rs) < 0 { 342 ldr.Errorf(s, "reloc %d (%s) to non-coff symbol %s type=%d (%s)", rt, sym.RelocName(arch, rt), ldr.SymName(rs), ldr.SymType(rs), ldr.SymType(rs)) 343 return false 344 } 345 346 out.Write32(uint32(sectoff)) 347 out.Write32(uint32(ldr.SymDynid(rs))) 348 349 var v uint32 350 switch rt { 351 default: 352 // unsupported relocation type 353 return false 354 355 case objabi.R_DWARFSECREF: 356 v = ld.IMAGE_REL_ARM_SECREL 357 358 case objabi.R_ADDR: 359 v = ld.IMAGE_REL_ARM_ADDR32 360 361 case objabi.R_PEIMAGEOFF: 362 v = ld.IMAGE_REL_ARM_ADDR32NB 363 } 364 365 out.Write16(uint16(v)) 366 367 return true 368 } 369 370 // sign extend a 24-bit integer. 371 func signext24(x int64) int32 { 372 return (int32(x) << 8) >> 8 373 } 374 375 // encode an immediate in ARM's imm12 format. copied from ../../../github.com/go-asm/go/obj/arm/asm5.go 376 func immrot(v uint32) uint32 { 377 for i := 0; i < 16; i++ { 378 if v&^0xff == 0 { 379 return uint32(i<<8) | v | 1<<25 380 } 381 v = v<<2 | v>>30 382 } 383 return 0 384 } 385 386 // Convert the direct jump relocation r to refer to a trampoline if the target is too far. 387 func trampoline(ctxt *ld.Link, ldr *loader.Loader, ri int, rs, s loader.Sym) { 388 relocs := ldr.Relocs(s) 389 r := relocs.At(ri) 390 switch r.Type() { 391 case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_CALL), 392 objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_PC24), 393 objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_JUMP24): 394 // Host object relocations that will be turned into a PLT call. 395 // The PLT may be too far. Insert a trampoline for them. 396 fallthrough 397 case objabi.R_CALLARM: 398 var t int64 399 // ldr.SymValue(rs) == 0 indicates a cross-package jump to a function that is not yet 400 // laid out. Conservatively use a trampoline. This should be rare, as we lay out packages 401 // in dependency order. 402 if ldr.SymValue(rs) != 0 { 403 // Workaround for issue #58425: it appears that the 404 // external linker doesn't always take into account the 405 // relocation addend when doing reachability checks. This 406 // means that if you have a call from function XYZ at 407 // offset 8 to runtime.duffzero with addend 800 (for 408 // example), where the distance between the start of XYZ 409 // and the start of runtime.duffzero is just over the 410 // limit (by 100 bytes, say), you can get "relocation 411 // doesn't fit" errors from the external linker. To deal 412 // with this, ignore the addend when performing the 413 // distance calculation (this assumes that we're only 414 // handling backward jumps; ideally we might want to check 415 // both with and without the addend). 416 if ctxt.IsExternal() { 417 t = (ldr.SymValue(rs) - (ldr.SymValue(s) + int64(r.Off()))) / 4 418 } else { 419 // r.Add is the instruction 420 // low 24-bit encodes the target address 421 t = (ldr.SymValue(rs) + int64(signext24(r.Add()&0xffffff)*4) - (ldr.SymValue(s) + int64(r.Off()))) / 4 422 } 423 } 424 if t > 0x7fffff || t <= -0x800000 || ldr.SymValue(rs) == 0 || (*ld.FlagDebugTramp > 1 && ldr.SymPkg(s) != ldr.SymPkg(rs)) { 425 // direct call too far, need to insert trampoline. 426 // look up existing trampolines first. if we found one within the range 427 // of direct call, we can reuse it. otherwise create a new one. 428 offset := (signext24(r.Add()&0xffffff) + 2) * 4 429 var tramp loader.Sym 430 for i := 0; ; i++ { 431 oName := ldr.SymName(rs) 432 name := oName + fmt.Sprintf("%+d-tramp%d", offset, i) 433 tramp = ldr.LookupOrCreateSym(name, int(ldr.SymVersion(rs))) 434 ldr.SetAttrReachable(tramp, true) 435 if ldr.SymType(tramp) == sym.SDYNIMPORT { 436 // don't reuse trampoline defined in other module 437 continue 438 } 439 if oName == "runtime.deferreturn" { 440 ldr.SetIsDeferReturnTramp(tramp, true) 441 } 442 if ldr.SymValue(tramp) == 0 { 443 // either the trampoline does not exist -- we need to create one, 444 // or found one the address which is not assigned -- this will be 445 // laid down immediately after the current function. use this one. 446 break 447 } 448 449 t = (ldr.SymValue(tramp) - 8 - (ldr.SymValue(s) + int64(r.Off()))) / 4 450 if t >= -0x800000 && t < 0x7fffff { 451 // found an existing trampoline that is not too far 452 // we can just use it 453 break 454 } 455 } 456 if ldr.SymType(tramp) == 0 { 457 // trampoline does not exist, create one 458 trampb := ldr.MakeSymbolUpdater(tramp) 459 ctxt.AddTramp(trampb) 460 if ctxt.DynlinkingGo() || ldr.SymType(rs) == sym.SDYNIMPORT { 461 if immrot(uint32(offset)) == 0 { 462 ctxt.Errorf(s, "odd offset in dynlink direct call: %v+%d", ldr.SymName(rs), offset) 463 } 464 gentrampdyn(ctxt.Arch, trampb, rs, int64(offset)) 465 } else if ctxt.BuildMode == ld.BuildModeCArchive || ctxt.BuildMode == ld.BuildModeCShared || ctxt.BuildMode == ld.BuildModePIE { 466 gentramppic(ctxt.Arch, trampb, rs, int64(offset)) 467 } else { 468 gentramp(ctxt.Arch, ctxt.LinkMode, ldr, trampb, rs, int64(offset)) 469 } 470 } 471 // modify reloc to point to tramp, which will be resolved later 472 sb := ldr.MakeSymbolUpdater(s) 473 relocs := sb.Relocs() 474 r := relocs.At(ri) 475 r.SetSym(tramp) 476 r.SetAdd(r.Add()&0xff000000 | 0xfffffe) // clear the offset embedded in the instruction 477 } 478 default: 479 ctxt.Errorf(s, "trampoline called with non-jump reloc: %d (%s)", r.Type(), sym.RelocName(ctxt.Arch, r.Type())) 480 } 481 } 482 483 // generate a trampoline to target+offset. 484 func gentramp(arch *sys.Arch, linkmode ld.LinkMode, ldr *loader.Loader, tramp *loader.SymbolBuilder, target loader.Sym, offset int64) { 485 tramp.SetSize(12) // 3 instructions 486 P := make([]byte, tramp.Size()) 487 t := ldr.SymValue(target) + offset 488 o1 := uint32(0xe5900000 | 12<<12 | 15<<16) // MOVW (R15), R12 // R15 is actual pc + 8 489 o2 := uint32(0xe12fff10 | 12) // JMP (R12) 490 o3 := uint32(t) // WORD $target 491 arch.ByteOrder.PutUint32(P, o1) 492 arch.ByteOrder.PutUint32(P[4:], o2) 493 arch.ByteOrder.PutUint32(P[8:], o3) 494 tramp.SetData(P) 495 496 if linkmode == ld.LinkExternal || ldr.SymValue(target) == 0 { 497 r, _ := tramp.AddRel(objabi.R_ADDR) 498 r.SetOff(8) 499 r.SetSiz(4) 500 r.SetSym(target) 501 r.SetAdd(offset) 502 } 503 } 504 505 // generate a trampoline to target+offset in position independent code. 506 func gentramppic(arch *sys.Arch, tramp *loader.SymbolBuilder, target loader.Sym, offset int64) { 507 tramp.SetSize(16) // 4 instructions 508 P := make([]byte, tramp.Size()) 509 o1 := uint32(0xe5900000 | 12<<12 | 15<<16 | 4) // MOVW 4(R15), R12 // R15 is actual pc + 8 510 o2 := uint32(0xe0800000 | 12<<12 | 15<<16 | 12) // ADD R15, R12, R12 511 o3 := uint32(0xe12fff10 | 12) // JMP (R12) 512 o4 := uint32(0) // WORD $(target-pc) // filled in with relocation 513 arch.ByteOrder.PutUint32(P, o1) 514 arch.ByteOrder.PutUint32(P[4:], o2) 515 arch.ByteOrder.PutUint32(P[8:], o3) 516 arch.ByteOrder.PutUint32(P[12:], o4) 517 tramp.SetData(P) 518 519 r, _ := tramp.AddRel(objabi.R_PCREL) 520 r.SetOff(12) 521 r.SetSiz(4) 522 r.SetSym(target) 523 r.SetAdd(offset + 4) 524 } 525 526 // generate a trampoline to target+offset in dynlink mode (using GOT). 527 func gentrampdyn(arch *sys.Arch, tramp *loader.SymbolBuilder, target loader.Sym, offset int64) { 528 tramp.SetSize(20) // 5 instructions 529 o1 := uint32(0xe5900000 | 12<<12 | 15<<16 | 8) // MOVW 8(R15), R12 // R15 is actual pc + 8 530 o2 := uint32(0xe0800000 | 12<<12 | 15<<16 | 12) // ADD R15, R12, R12 531 o3 := uint32(0xe5900000 | 12<<12 | 12<<16) // MOVW (R12), R12 532 o4 := uint32(0xe12fff10 | 12) // JMP (R12) 533 o5 := uint32(0) // WORD $target@GOT // filled in with relocation 534 o6 := uint32(0) 535 if offset != 0 { 536 // insert an instruction to add offset 537 tramp.SetSize(24) // 6 instructions 538 o6 = o5 539 o5 = o4 540 o4 = 0xe2800000 | 12<<12 | 12<<16 | immrot(uint32(offset)) // ADD $offset, R12, R12 541 o1 = uint32(0xe5900000 | 12<<12 | 15<<16 | 12) // MOVW 12(R15), R12 542 } 543 P := make([]byte, tramp.Size()) 544 arch.ByteOrder.PutUint32(P, o1) 545 arch.ByteOrder.PutUint32(P[4:], o2) 546 arch.ByteOrder.PutUint32(P[8:], o3) 547 arch.ByteOrder.PutUint32(P[12:], o4) 548 arch.ByteOrder.PutUint32(P[16:], o5) 549 if offset != 0 { 550 arch.ByteOrder.PutUint32(P[20:], o6) 551 } 552 tramp.SetData(P) 553 554 r, _ := tramp.AddRel(objabi.R_GOTPCREL) 555 r.SetOff(16) 556 r.SetSiz(4) 557 r.SetSym(target) 558 r.SetAdd(8) 559 if offset != 0 { 560 // increase reloc offset by 4 as we inserted an ADD instruction 561 r.SetOff(20) 562 r.SetAdd(12) 563 } 564 } 565 566 func archreloc(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, r loader.Reloc, s loader.Sym, val int64) (o int64, nExtReloc int, ok bool) { 567 rs := r.Sym() 568 if target.IsExternal() { 569 switch r.Type() { 570 case objabi.R_CALLARM: 571 // set up addend for eventual relocation via outer symbol. 572 _, off := ld.FoldSubSymbolOffset(ldr, rs) 573 xadd := int64(signext24(r.Add()&0xffffff))*4 + off 574 if xadd/4 > 0x7fffff || xadd/4 < -0x800000 { 575 ldr.Errorf(s, "direct call too far %d", xadd/4) 576 } 577 return int64(braddoff(int32(0xff000000&uint32(r.Add())), int32(0xffffff&uint32(xadd/4)))), 1, true 578 } 579 return -1, 0, false 580 } 581 582 const isOk = true 583 const noExtReloc = 0 584 switch r.Type() { 585 // The following three arch specific relocations are only for generation of 586 // Linux/ARM ELF's PLT entry (3 assembler instruction) 587 case objabi.R_PLT0: // add ip, pc, #0xXX00000 588 if ldr.SymValue(syms.GOTPLT) < ldr.SymValue(syms.PLT) { 589 ldr.Errorf(s, ".got.plt should be placed after .plt section.") 590 } 591 return 0xe28fc600 + (0xff & (int64(uint32(ldr.SymValue(rs)-(ldr.SymValue(syms.PLT)+int64(r.Off()))+r.Add())) >> 20)), noExtReloc, isOk 592 case objabi.R_PLT1: // add ip, ip, #0xYY000 593 return 0xe28cca00 + (0xff & (int64(uint32(ldr.SymValue(rs)-(ldr.SymValue(syms.PLT)+int64(r.Off()))+r.Add()+4)) >> 12)), noExtReloc, isOk 594 case objabi.R_PLT2: // ldr pc, [ip, #0xZZZ]! 595 return 0xe5bcf000 + (0xfff & int64(uint32(ldr.SymValue(rs)-(ldr.SymValue(syms.PLT)+int64(r.Off()))+r.Add()+8))), noExtReloc, isOk 596 case objabi.R_CALLARM: // bl XXXXXX or b YYYYYY 597 // r.Add is the instruction 598 // low 24-bit encodes the target address 599 t := (ldr.SymValue(rs) + int64(signext24(r.Add()&0xffffff)*4) - (ldr.SymValue(s) + int64(r.Off()))) / 4 600 if t > 0x7fffff || t < -0x800000 { 601 ldr.Errorf(s, "direct call too far: %s %x", ldr.SymName(rs), t) 602 } 603 return int64(braddoff(int32(0xff000000&uint32(r.Add())), int32(0xffffff&t))), noExtReloc, isOk 604 } 605 606 return val, 0, false 607 } 608 609 func archrelocvariant(*ld.Target, *loader.Loader, loader.Reloc, sym.RelocVariant, loader.Sym, int64, []byte) int64 { 610 log.Fatalf("unexpected relocation variant") 611 return -1 612 } 613 614 func extreloc(target *ld.Target, ldr *loader.Loader, r loader.Reloc, s loader.Sym) (loader.ExtReloc, bool) { 615 rs := r.Sym() 616 var rr loader.ExtReloc 617 switch r.Type() { 618 case objabi.R_CALLARM: 619 // set up addend for eventual relocation via outer symbol. 620 rs, off := ld.FoldSubSymbolOffset(ldr, rs) 621 rr.Xadd = int64(signext24(r.Add()&0xffffff))*4 + off 622 rst := ldr.SymType(rs) 623 if rst != sym.SHOSTOBJ && rst != sym.SDYNIMPORT && rst != sym.SUNDEFEXT && ldr.SymSect(rs) == nil { 624 ldr.Errorf(s, "missing section for %s", ldr.SymName(rs)) 625 } 626 rr.Xsym = rs 627 rr.Type = r.Type() 628 rr.Size = r.Siz() 629 return rr, true 630 } 631 return rr, false 632 } 633 634 func addpltreloc(ldr *loader.Loader, plt *loader.SymbolBuilder, got *loader.SymbolBuilder, s loader.Sym, typ objabi.RelocType) { 635 r, _ := plt.AddRel(typ) 636 r.SetSym(got.Sym()) 637 r.SetOff(int32(plt.Size())) 638 r.SetSiz(4) 639 r.SetAdd(int64(ldr.SymGot(s)) - 8) 640 641 plt.SetReachable(true) 642 plt.SetSize(plt.Size() + 4) 643 plt.Grow(plt.Size()) 644 } 645 646 func addpltsym(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym) { 647 if ldr.SymPlt(s) >= 0 { 648 return 649 } 650 651 ld.Adddynsym(ldr, target, syms, s) 652 653 if target.IsElf() { 654 plt := ldr.MakeSymbolUpdater(syms.PLT) 655 got := ldr.MakeSymbolUpdater(syms.GOTPLT) 656 rel := ldr.MakeSymbolUpdater(syms.RelPLT) 657 if plt.Size() == 0 { 658 panic("plt is not set up") 659 } 660 661 // .got entry 662 ldr.SetGot(s, int32(got.Size())) 663 664 // In theory, all GOT should point to the first PLT entry, 665 // Linux/ARM's dynamic linker will do that for us, but FreeBSD/ARM's 666 // dynamic linker won't, so we'd better do it ourselves. 667 got.AddAddrPlus(target.Arch, plt.Sym(), 0) 668 669 // .plt entry, this depends on the .got entry 670 ldr.SetPlt(s, int32(plt.Size())) 671 672 addpltreloc(ldr, plt, got, s, objabi.R_PLT0) // add lr, pc, #0xXX00000 673 addpltreloc(ldr, plt, got, s, objabi.R_PLT1) // add lr, lr, #0xYY000 674 addpltreloc(ldr, plt, got, s, objabi.R_PLT2) // ldr pc, [lr, #0xZZZ]! 675 676 // rel 677 rel.AddAddrPlus(target.Arch, got.Sym(), int64(ldr.SymGot(s))) 678 679 rel.AddUint32(target.Arch, elf.R_INFO32(uint32(ldr.SymDynid(s)), uint32(elf.R_ARM_JUMP_SLOT))) 680 } else { 681 ldr.Errorf(s, "addpltsym: unsupported binary format") 682 } 683 } 684 685 func addgotsyminternal(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym) { 686 if ldr.SymGot(s) >= 0 { 687 return 688 } 689 690 got := ldr.MakeSymbolUpdater(syms.GOT) 691 ldr.SetGot(s, int32(got.Size())) 692 got.AddAddrPlus(target.Arch, s, 0) 693 694 if target.IsElf() { 695 } else { 696 ldr.Errorf(s, "addgotsyminternal: unsupported binary format") 697 } 698 }