aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/link
diff options
context:
space:
mode:
authorqmuntal <quimmuntal@gmail.com>2025-09-16 18:00:10 +0200
committerQuim Muntal <quimmuntal@gmail.com>2025-10-27 22:57:10 -0700
commit9e25c2f6dee50f9ce0fcfe1807befad9e54dfe4b (patch)
treeede452f8948510084cdfa307759580399ba6df00 /src/cmd/link
parentff2ebf69c4f099adf90d6c6284f2b3fd2ff789f0 (diff)
downloadgo-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')
-rw-r--r--src/cmd/link/internal/arm64/asm.go21
-rw-r--r--src/cmd/link/internal/ld/data.go17
-rw-r--r--src/cmd/link/internal/loadpe/ldpe.go44
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