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>
## 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 -->
// 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" {
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
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
}
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
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)
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 {
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
// 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