]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/link: internal linking support for windows/arm64
authorqmuntal <quimmuntal@gmail.com>
Tue, 16 Sep 2025 16:00:10 +0000 (18:00 +0200)
committerQuim Muntal <quimmuntal@gmail.com>
Tue, 28 Oct 2025 05:57:10 +0000 (22:57 -0700)
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>
doc/next/5-toolchain.md
src/cmd/dist/build.go
src/cmd/dist/test.go
src/cmd/link/internal/arm64/asm.go
src/cmd/link/internal/ld/data.go
src/cmd/link/internal/loadpe/ldpe.go
src/internal/platform/supported.go

index cc32f30a521a6dd58cb2776be58f10c8825c03c1..b5893288e5cbf0a432c928de820d8d2ddb1218f8 100644 (file)
@@ -4,6 +4,10 @@
 
 ## Linker {#linker}
 
+On 64-bit ARM-based Windows (the `windows/arm64` port), the linker now supports internal
+linking mode of cgo programs, which can be requested with the
+`-ldflags=-linkmode=internal` flag.
+
 ## Bootstrap {#bootstrap}
 
 <!-- go.dev/issue/69315 -->
index 2fcdb2d3915b9bf33a1d6687b7274a802f136f7b..2b382a1c025649df81e0f85861ea16e3355ff2d4 100644 (file)
@@ -624,11 +624,6 @@ func mustLinkExternal(goos, goarch string, cgoEnabled bool) bool {
                        // Internally linking cgo is incomplete on some architectures.
                        // https://golang.org/issue/14449
                        return true
-               case "arm64":
-                       if goos == "windows" {
-                               // windows/arm64 internal linking is not implemented.
-                               return true
-                       }
                case "ppc64":
                        // Big Endian PPC64 cgo internal linking is not implemented for aix or linux.
                        if goos == "aix" || goos == "linux" {
index 1e74438f48db39ea2c90f6b6b86b28646e3125a7..9c9e1b85f0cd76d59b8d99692adcb59040a96611 100644 (file)
@@ -1183,9 +1183,6 @@ func (t *tester) internalLink() bool {
        if goos == "ios" {
                return false
        }
-       if goos == "windows" && goarch == "arm64" {
-               return false
-       }
        // Internally linking cgo is incomplete on some architectures.
        // https://golang.org/issue/10373
        // https://golang.org/issue/14449
@@ -1212,7 +1209,7 @@ func (t *tester) internalLinkPIE() bool {
        case "darwin-amd64", "darwin-arm64",
                "linux-amd64", "linux-arm64", "linux-loong64", "linux-ppc64le",
                "android-arm64",
-               "windows-amd64", "windows-386":
+               "windows-amd64", "windows-386", "windows-arm64":
                return true
        }
        return false
index 1f4282adecc747fbf2487c8853e673a709b44e52..7c4546fb179663c555b72c828c2bc51e59542b3e 100644 (file)
@@ -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)
index dfb1d7bafbd51d8d9864efc80cac494469dd8e3e..b70d050c99bc0bcdf669c3fd410d5209ad8b8b47 100644 (file)
@@ -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 {
index d3a050135c28286d5f7dff8744befcefa07846ad..2073045c479a97def0c84d2dacd92c386654cd85 100644 (file)
@@ -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
index f9706a6988e94bfdf40aa289df32118b7cd78cdb..778d727086ac498e6aaac90c60c8f4a2cd3fe4ba 100644 (file)
@@ -89,11 +89,6 @@ func MustLinkExternal(goos, goarch string, withCgo bool) bool {
                        // Internally linking cgo is incomplete on some architectures.
                        // https://go.dev/issue/14449
                        return true
-               case "arm64":
-                       if goos == "windows" {
-                               // windows/arm64 internal linking is not implemented.
-                               return true
-                       }
                case "ppc64":
                        // Big Endian PPC64 cgo internal linking is not implemented for aix or linux.
                        // https://go.dev/issue/8912