diff options
| author | qmuntal <quimmuntal@gmail.com> | 2025-09-16 18:00:10 +0200 |
|---|---|---|
| committer | Quim Muntal <quimmuntal@gmail.com> | 2025-10-27 22:57:10 -0700 |
| commit | 9e25c2f6dee50f9ce0fcfe1807befad9e54dfe4b (patch) | |
| tree | ede452f8948510084cdfa307759580399ba6df00 /src/cmd/link/internal | |
| parent | ff2ebf69c4f099adf90d6c6284f2b3fd2ff789f0 (diff) | |
| download | go-9e25c2f6dee50f9ce0fcfe1807befad9e54dfe4b.tar.xz | |
cmd/link: internal linking support for windows/arm64
The internal linker was missing some pieces to support windows/arm64.
Closes #75485
Cq-Include-Trybots: luci.golang.try:gotip-windows-arm64
Change-Id: I5c18a47e63e09b8ae22c9b24832249b54f544b7e
Reviewed-on: https://go-review.googlesource.com/c/go/+/704295
Reviewed-by: Cherry Mui <cherryyz@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Michael Knyszek <mknyszek@google.com>
Diffstat (limited to 'src/cmd/link/internal')
| -rw-r--r-- | src/cmd/link/internal/arm64/asm.go | 21 | ||||
| -rw-r--r-- | src/cmd/link/internal/ld/data.go | 17 | ||||
| -rw-r--r-- | src/cmd/link/internal/loadpe/ldpe.go | 44 |
3 files changed, 75 insertions, 7 deletions
diff --git a/src/cmd/link/internal/arm64/asm.go b/src/cmd/link/internal/arm64/asm.go index 1f4282adec..7c4546fb17 100644 --- a/src/cmd/link/internal/arm64/asm.go +++ b/src/cmd/link/internal/arm64/asm.go @@ -1027,25 +1027,35 @@ func archreloc(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, r loade } case objabi.R_ARM64_PCREL: + // When targetting Windows, the instruction immediate field is cleared + // before applying relocations, as it contains the offset as bytes + // instead of pages. It has already been accounted for in loadpe.Load + // by adjusting r.Add(). if (val>>24)&0x9f == 0x90 { - // R_AARCH64_ADR_PREL_PG_HI21 + // ELF R_AARCH64_ADR_PREL_PG_HI21, or Mach-O ARM64_RELOC_PAGE21, or PE IMAGE_REL_ARM64_PAGEBASE_REL21 // patch instruction: adrp t := ldr.SymAddr(rs) + r.Add() - ((ldr.SymValue(s) + int64(r.Off())) &^ 0xfff) if t >= 1<<32 || t < -1<<32 { ldr.Errorf(s, "program too large, address relocation distance = %d", t) } o0 := (uint32((t>>12)&3) << 29) | (uint32((t>>12>>2)&0x7ffff) << 5) + if target.IsWindows() { + val &^= 3<<29 | 0x7ffff<<5 + } return val | int64(o0), noExtReloc, isOk } else if (val>>24)&0x9f == 0x91 { - // ELF R_AARCH64_ADD_ABS_LO12_NC or Mach-O ARM64_RELOC_PAGEOFF12 + // ELF R_AARCH64_ADD_ABS_LO12_NC, or Mach-O ARM64_RELOC_PAGEOFF12, or PE IMAGE_REL_ARM64_PAGEOFFSET_12A // patch instruction: add t := ldr.SymAddr(rs) + r.Add() - ((ldr.SymValue(s) + int64(r.Off())) &^ 0xfff) o1 := uint32(t&0xfff) << 10 + if target.IsWindows() { + val &^= 0xfff << 10 + } return val | int64(o1), noExtReloc, isOk } else if (val>>24)&0x3b == 0x39 { - // Mach-O ARM64_RELOC_PAGEOFF12 + // Mach-O ARM64_RELOC_PAGEOFF12 or PE IMAGE_REL_ARM64_PAGEOFFSET_12L // patch ldr/str(b/h/w/d/q) (integer or vector) instructions, which have different scaling factors. - // Mach-O uses same relocation type for them. + // Mach-O and PE use same relocation type for them. shift := uint32(val) >> 30 if shift == 0 && (val>>20)&0x048 == 0x048 { // 128-bit vector load shift = 4 @@ -1055,6 +1065,9 @@ func archreloc(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, r loade ldr.Errorf(s, "invalid address: %x for relocation type: ARM64_RELOC_PAGEOFF12", t) } o1 := (uint32(t&0xfff) >> shift) << 10 + if target.IsWindows() { + val &^= 0xfff << 10 + } return val | int64(o1), noExtReloc, isOk } else { ldr.Errorf(s, "unsupported instruction for %x R_ARM64_PCREL", val) diff --git a/src/cmd/link/internal/ld/data.go b/src/cmd/link/internal/ld/data.go index dfb1d7bafb..b70d050c99 100644 --- a/src/cmd/link/internal/ld/data.go +++ b/src/cmd/link/internal/ld/data.go @@ -913,6 +913,23 @@ func windynrelocsym(ctxt *Link, rel *loader.SymbolBuilder, s loader.Sym) error { rel.AddPCRelPlus(ctxt.Arch, targ, 0) rel.AddUint8(0x90) rel.AddUint8(0x90) + case sys.ARM64: + // adrp x16, addr + rel.AddUint32(ctxt.Arch, 0x90000010) + r, _ := rel.AddRel(objabi.R_ARM64_PCREL) + r.SetOff(int32(rel.Size() - 4)) + r.SetSiz(4) + r.SetSym(targ) + + // ldr x17, [x16, <offset>] + rel.AddUint32(ctxt.Arch, 0xf9400211) + r, _ = rel.AddRel(objabi.R_ARM64_PCREL) + r.SetOff(int32(rel.Size() - 4)) + r.SetSiz(4) + r.SetSym(targ) + + // br x17 + rel.AddUint32(ctxt.Arch, 0xd61f0220) } } else if tplt >= 0 { if su == nil { diff --git a/src/cmd/link/internal/loadpe/ldpe.go b/src/cmd/link/internal/loadpe/ldpe.go index d3a050135c..2073045c47 100644 --- a/src/cmd/link/internal/loadpe/ldpe.go +++ b/src/cmd/link/internal/loadpe/ldpe.go @@ -392,21 +392,59 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read switch r.Type { case IMAGE_REL_ARM64_ADDR32: rType = objabi.R_ADDR + case IMAGE_REL_ARM64_ADDR64: + rType = objabi.R_ADDR + rSize = 8 case IMAGE_REL_ARM64_ADDR32NB: rType = objabi.R_PEIMAGEOFF + case IMAGE_REL_ARM64_BRANCH26: + rType = objabi.R_CALLARM64 + case IMAGE_REL_ARM64_PAGEBASE_REL21, + IMAGE_REL_ARM64_PAGEOFFSET_12A, + IMAGE_REL_ARM64_PAGEOFFSET_12L: + rType = objabi.R_ARM64_PCREL } } if rType == 0 { return nil, fmt.Errorf("%s: %v: unknown relocation type %v", pn, state.sectsyms[rsect], r.Type) } - var rAdd int64 + var val int64 switch rSize { default: panic("unexpected relocation size " + strconv.Itoa(int(rSize))) case 4: - rAdd = int64(int32(binary.LittleEndian.Uint32(state.sectdata[rsect][rOff:]))) + val = int64(int32(binary.LittleEndian.Uint32(state.sectdata[rsect][rOff:]))) case 8: - rAdd = int64(binary.LittleEndian.Uint64(state.sectdata[rsect][rOff:])) + val = int64(binary.LittleEndian.Uint64(state.sectdata[rsect][rOff:])) + } + var rAdd int64 + if arch.Family == sys.ARM64 { + switch r.Type { + case IMAGE_REL_ARM64_BRANCH26: + // This instruction doesn't support an addend. + case IMAGE_REL_ARM64_PAGEOFFSET_12A: + // The addend is stored in the immediate field of the instruction. + // Get the addend from the instruction. + rAdd = (val >> 10) & 0xfff + case IMAGE_REL_ARM64_PAGEOFFSET_12L: + // Same as IMAGE_REL_ARM64_PAGEOFFSET_12A, but taking into account the shift. + shift := uint32(val) >> 30 + if shift == 0 && (val>>20)&0x048 == 0x048 { // 128-bit vector load + shift = 4 + } + rAdd = ((val >> 10) & 0xfff) << shift + case IMAGE_REL_ARM64_PAGEBASE_REL21: + // The addend is stored in the immediate field of the instruction + // as a byte offset. Get the addend from the instruction and clear + // the immediate bits. + immlo := (val >> 29) & 3 + immhi := (val >> 5) & 0x7ffff + rAdd = (immhi << 2) | immlo + default: + rAdd = val + } + } else { + rAdd = val } // ld -r could generate multiple section symbols for the // same section but with different values, we have to take |
