diff options
| author | Guoqi Chen <chenguoqi@loongson.cn> | 2026-03-12 15:34:02 +0800 |
|---|---|---|
| committer | abner chenc <chenguoqi@loongson.cn> | 2026-03-18 18:08:22 -0700 |
| commit | 9def4acb06bf9167441caa8af37e27b3cdbf8cf0 (patch) | |
| tree | 00dff920091176d8594de42d8f98f2c4ff3881b7 /src/cmd/link/internal | |
| parent | 6bdfdb5f51951584732e6a95a7ee8a5ba5e8cb93 (diff) | |
| download | go-9def4acb06bf9167441caa8af37e27b3cdbf8cf0.tar.xz | |
cmd/link: support loading R_LARCH_GOT64_PC_{LO12, HI20} relocs on loong64
On loong64, such relocations are increasingly common when built using
an "extreme" code model. To ensure future interoperability with cgo,
the linker needs to be made aware of these relocations.
Ref: https://github.com/loongson/la-abi-specs/blob/release/laelf.adoc
Fixes #78047.
Change-Id: Ibca205ab837279c69fb243a8e8519c952e11c99e
Reviewed-on: https://go-review.googlesource.com/c/go/+/753521
Reviewed-by: Cherry Mui <cherryyz@google.com>
Reviewed-by: Meidan Li <limeidan@loongson.cn>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Carlos Amedee <carlos@golang.org>
Diffstat (limited to 'src/cmd/link/internal')
| -rw-r--r-- | src/cmd/link/internal/loadelf/ldelf.go | 4 | ||||
| -rw-r--r-- | src/cmd/link/internal/loong64/asm.go | 197 |
2 files changed, 162 insertions, 39 deletions
diff --git a/src/cmd/link/internal/loadelf/ldelf.go b/src/cmd/link/internal/loadelf/ldelf.go index c5c4645656..50bce8613b 100644 --- a/src/cmd/link/internal/loadelf/ldelf.go +++ b/src/cmd/link/internal/loadelf/ldelf.go @@ -1071,8 +1071,12 @@ func relSize(arch *sys.Arch, pn string, elftype uint32) (uint8, uint8, error) { LOONG64 | uint32(elf.R_LARCH_B26)<<16, LOONG64 | uint32(elf.R_LARCH_PCALA_HI20)<<16, LOONG64 | uint32(elf.R_LARCH_PCALA_LO12)<<16, + LOONG64 | uint32(elf.R_LARCH_PCALA64_HI12)<<16, + LOONG64 | uint32(elf.R_LARCH_PCALA64_LO20)<<16, LOONG64 | uint32(elf.R_LARCH_GOT_PC_HI20)<<16, LOONG64 | uint32(elf.R_LARCH_GOT_PC_LO12)<<16, + LOONG64 | uint32(elf.R_LARCH_GOT64_PC_LO20)<<16, + LOONG64 | uint32(elf.R_LARCH_GOT64_PC_HI12)<<16, LOONG64 | uint32(elf.R_LARCH_32_PCREL)<<16, LOONG64 | uint32(elf.R_LARCH_PCREL20_S2)<<16: return 4, 4, nil diff --git a/src/cmd/link/internal/loong64/asm.go b/src/cmd/link/internal/loong64/asm.go index 9881e37493..0ebdc61936 100644 --- a/src/cmd/link/internal/loong64/asm.go +++ b/src/cmd/link/internal/loong64/asm.go @@ -105,24 +105,37 @@ func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loade return true case objabi.ElfRelocOffset + objabi.RelocType(elf.R_LARCH_GOT_PC_HI20), - objabi.ElfRelocOffset + objabi.RelocType(elf.R_LARCH_GOT_PC_LO12): + objabi.ElfRelocOffset + objabi.RelocType(elf.R_LARCH_GOT_PC_LO12), + objabi.ElfRelocOffset + objabi.RelocType(elf.R_LARCH_GOT64_PC_HI12), + objabi.ElfRelocOffset + objabi.RelocType(elf.R_LARCH_GOT64_PC_LO20): if targType != sym.SDYNIMPORT { // TODO: turn LDR of GOT entry into ADR of symbol itself } ld.AddGotSym(target, ldr, syms, targ, uint32(elf.R_LARCH_64)) su := ldr.MakeSymbolUpdater(s) - if r.Type() == objabi.ElfRelocOffset+objabi.RelocType(elf.R_LARCH_GOT_PC_HI20) { - su.SetRelocType(rIdx, objabi.R_LOONG64_ADDR_HI) - } else { - su.SetRelocType(rIdx, objabi.R_LOONG64_ADDR_LO) + + var relocType objabi.RelocType + switch r.Type() - objabi.ElfRelocOffset { + case objabi.RelocType(elf.R_LARCH_GOT_PC_HI20): + relocType = objabi.R_LOONG64_ADDR_HI + case objabi.RelocType(elf.R_LARCH_GOT_PC_LO12): + relocType = objabi.R_LOONG64_ADDR_LO + case objabi.RelocType(elf.R_LARCH_GOT64_PC_HI12): + relocType = objabi.R_LOONG64_ADDR64_HI + case objabi.RelocType(elf.R_LARCH_GOT64_PC_LO20): + relocType = objabi.R_LOONG64_ADDR64_LO } + + su.SetRelocType(rIdx, relocType) su.SetRelocSym(rIdx, syms.GOT) su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymGot(targ))) return true case objabi.ElfRelocOffset + objabi.RelocType(elf.R_LARCH_PCALA_HI20), objabi.ElfRelocOffset + objabi.RelocType(elf.R_LARCH_PCALA_LO12), + objabi.ElfRelocOffset + objabi.RelocType(elf.R_LARCH_PCALA64_HI12), + objabi.ElfRelocOffset + objabi.RelocType(elf.R_LARCH_PCALA64_LO20), objabi.ElfRelocOffset + objabi.RelocType(elf.R_LARCH_PCREL20_S2): if targType == sym.SDYNIMPORT { ldr.Errorf(s, "unexpected relocation for dynamic symbol %s", ldr.SymName(targ)) @@ -137,9 +150,14 @@ func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loade relocType = objabi.R_LOONG64_ADDR_HI case objabi.RelocType(elf.R_LARCH_PCALA_LO12): relocType = objabi.R_LOONG64_ADDR_LO + case objabi.RelocType(elf.R_LARCH_PCALA64_HI12): + relocType = objabi.R_LOONG64_ADDR64_HI + case objabi.RelocType(elf.R_LARCH_PCALA64_LO20): + relocType = objabi.R_LOONG64_ADDR64_LO case objabi.RelocType(elf.R_LARCH_PCREL20_S2): relocType = objabi.R_LOONG64_ADDR_PCREL20_S2 } + su := ldr.MakeSymbolUpdater(s) su.SetRelocType(rIdx, relocType) return true @@ -289,14 +307,25 @@ func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loade } case objabi.R_LOONG64_GOT_HI, - objabi.R_LOONG64_GOT_LO: + objabi.R_LOONG64_GOT_LO, + objabi.R_LOONG64_GOT64_HI, + objabi.R_LOONG64_GOT64_LO: ld.AddGotSym(target, ldr, syms, targ, uint32(elf.R_LARCH_64)) su := ldr.MakeSymbolUpdater(s) - if r.Type() == objabi.R_LOONG64_GOT_HI { - su.SetRelocType(rIdx, objabi.R_LOONG64_ADDR_HI) - } else { - su.SetRelocType(rIdx, objabi.R_LOONG64_ADDR_LO) + + var relocType objabi.RelocType + switch r.Type() { + case objabi.R_LOONG64_GOT_HI: + relocType = objabi.R_LOONG64_ADDR_HI + case objabi.R_LOONG64_GOT_LO: + relocType = objabi.R_LOONG64_ADDR_LO + case objabi.R_LOONG64_GOT64_HI: + relocType = objabi.R_LOONG64_ADDR64_HI + case objabi.R_LOONG64_GOT64_LO: + relocType = objabi.R_LOONG64_ADDR64_LO } + + su.SetRelocType(rIdx, relocType) su.SetRelocSym(rIdx, syms.GOT) su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymGot(targ))) return true @@ -454,6 +483,16 @@ func elfreloc1(ctxt *ld.Link, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym, out.Write64(uint64(elf.R_LARCH_PCALA_HI20) | uint64(elfsym)<<32) out.Write64(uint64(r.Xadd)) + case objabi.R_LOONG64_ADDR64_LO: + out.Write64(uint64(sectoff)) + out.Write64(uint64(elf.R_LARCH_PCALA64_LO20) | uint64(elfsym)<<32) + out.Write64(uint64(r.Xadd)) + + case objabi.R_LOONG64_ADDR64_HI: + out.Write64(uint64(sectoff)) + out.Write64(uint64(elf.R_LARCH_PCALA64_HI12) | uint64(elfsym)<<32) + out.Write64(uint64(r.Xadd)) + case objabi.R_LOONG64_ADDR_PCREL20_S2: out.Write64(uint64(sectoff)) out.Write64(uint64(elf.R_LARCH_PCREL20_S2) | uint64(elfsym)<<32) @@ -468,6 +507,16 @@ func elfreloc1(ctxt *ld.Link, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym, out.Write64(uint64(sectoff)) out.Write64(uint64(elf.R_LARCH_GOT_PC_LO12) | uint64(elfsym)<<32) out.Write64(uint64(0x0)) + + case objabi.R_LOONG64_GOT64_HI: + out.Write64(uint64(sectoff)) + out.Write64(uint64(elf.R_LARCH_GOT64_PC_HI12) | uint64(elfsym)<<32) + out.Write64(uint64(0x0)) + + case objabi.R_LOONG64_GOT64_LO: + out.Write64(uint64(sectoff)) + out.Write64(uint64(elf.R_LARCH_GOT64_PC_LO20) | uint64(elfsym)<<32) + out.Write64(uint64(0x0)) } return true @@ -485,6 +534,8 @@ func archreloc(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, r loade return val, 0, false case objabi.R_LOONG64_ADDR_HI, objabi.R_LOONG64_ADDR_LO, + objabi.R_LOONG64_ADDR64_HI, + objabi.R_LOONG64_ADDR64_LO, objabi.R_LOONG64_ADDR_PCREL20_S2: // set up addend for eventual relocation via outer symbol. rs, _ := ld.FoldSubSymbolOffset(ldr, rs) @@ -493,6 +544,7 @@ func archreloc(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, r loade ldr.Errorf(s, "missing section for %s", ldr.SymName(rs)) } return val, 1, true + case objabi.R_LOONG64_TLS_LE_HI, objabi.R_LOONG64_TLS_LE_LO, objabi.R_CALLLOONG64, @@ -500,7 +552,9 @@ func archreloc(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, r loade objabi.R_LOONG64_TLS_IE_HI, objabi.R_LOONG64_TLS_IE_LO, objabi.R_LOONG64_GOT_HI, - objabi.R_LOONG64_GOT_LO: + objabi.R_LOONG64_GOT_LO, + objabi.R_LOONG64_GOT64_HI, + objabi.R_LOONG64_GOT64_LO: return val, 1, true } } @@ -511,20 +565,43 @@ func archreloc(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, r loade switch r.Type() { case objabi.R_CONST: return r.Add(), noExtReloc, isOk + case objabi.R_GOTOFF: return ldr.SymValue(r.Sym()) + r.Add() - ldr.SymValue(syms.GOT), noExtReloc, isOk + case objabi.R_LOONG64_ADDR_HI, - objabi.R_LOONG64_ADDR_LO: + objabi.R_LOONG64_ADDR_LO, + objabi.R_LOONG64_ADDR64_HI, + objabi.R_LOONG64_ADDR64_LO: pc := ldr.SymValue(s) + int64(r.Off()) - t := calculatePCAlignedReloc(r.Type(), ldr.SymAddr(rs)+r.Add(), pc) - if r.Type() == objabi.R_LOONG64_ADDR_LO { - return val&0xffc003ff | (t << 10), noExtReloc, isOk + var t int64 + + switch r.Type() { + case objabi.R_LOONG64_ADDR_LO: + // pcalau12i + t = pc32RelocBits(r.Type(), ldr.SymAddr(rs)+r.Add(), pc) + o = val&0xffc003ff | (t << 10) + case objabi.R_LOONG64_ADDR_HI: + // addi.w/addi.d + t = pc32RelocBits(r.Type(), ldr.SymAddr(rs)+r.Add(), pc) + o = val&0xfe00001f | (t << 5) + case objabi.R_LOONG64_ADDR64_LO: + // lu32i.d + t = pc64RelocBits(r.Type(), ldr.SymAddr(rs)+r.Add(), pc-8) + o = val&0xfe00001f | (t << 5) + case objabi.R_LOONG64_ADDR64_HI: + // lu52i.d + t = pc64RelocBits(r.Type(), ldr.SymAddr(rs)+r.Add(), pc-12) + o = val&0xffc003ff | (t << 10) } - return val&0xfe00001f | (t << 5), noExtReloc, isOk + + return o, noExtReloc, isOk + case objabi.R_LOONG64_ADDR_PCREL20_S2: pc := ldr.SymValue(s) + int64(r.Off()) t := (ldr.SymAddr(rs) + r.Add() - pc) >> 2 return val&0xfe00001f | ((t & 0xfffff) << 5), noExtReloc, isOk + case objabi.R_LOONG64_TLS_LE_HI, objabi.R_LOONG64_TLS_LE_LO: t := ldr.SymAddr(rs) + r.Add() @@ -532,6 +609,7 @@ func archreloc(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, r loade return val&0xffc003ff | ((t & 0xfff) << 10), noExtReloc, isOk } return val&0xfe00001f | (((t) >> 12 << 5) & 0x1ffffe0), noExtReloc, isOk + case objabi.R_CALLLOONG64: pc := ldr.SymValue(s) + int64(r.Off()) t := ldr.SymAddr(rs) + r.Add() - pc @@ -589,8 +667,12 @@ func extreloc(target *ld.Target, ldr *loader.Loader, r loader.Reloc, s loader.Sy switch r.Type() { case objabi.R_LOONG64_ADDR_HI, objabi.R_LOONG64_ADDR_LO, + objabi.R_LOONG64_ADDR64_HI, + objabi.R_LOONG64_ADDR64_LO, objabi.R_LOONG64_GOT_HI, - objabi.R_LOONG64_GOT_LO: + objabi.R_LOONG64_GOT_LO, + objabi.R_LOONG64_GOT64_HI, + objabi.R_LOONG64_GOT64_LO: return ld.ExtrelocViaOuterSym(ldr, r, s), true case objabi.R_LOONG64_TLS_LE_HI, @@ -605,34 +687,71 @@ func extreloc(target *ld.Target, ldr *loader.Loader, r loader.Reloc, s loader.Sy return loader.ExtReloc{}, false } -func isRequestingLowPageBits(t objabi.RelocType) bool { - switch t { - case objabi.R_LOONG64_ADDR_LO: - return true +// Comments from copying binutils/bfd/elfnn-loongarch.c +// +// For example: pc is 0x11000010000100, symbol is 0x1812348ffff812 +// +// offset = (0x1812348ffff812 & ~0xfff) - (0x11000010000100 & ~0xfff) +// offset = 0x712347ffff000 +// +// lo12: 0x1812348ffff812 & 0xfff = 0x812 +// hi20: 0x7ffff + 0x1(lo12 > 0x7ff) = 0x80000 +// lo20: 0x71234 - 0x1(lo12 > 0x7ff) + 0x1(hi20 > 0x7ffff) +// hi12: 0x0 +// +// pcalau12i $t1, hi20 (0x80000) +// +// $t1 = 0x11000010000100 + sign-extend(0x80000 << 12) +// = 0x11000010000100 + 0xffffffff80000000 +// = 0x10ffff90000000 +// +// addi.d $t0, $zero, lo12 (0x812) +// +// $t0 = 0xfffffffffffff812 (if lo12 > 0x7ff, because sign-extend, +// lo20 need to sub 0x1) +// +// lu32i.d $t0, lo20 (0x71234) +// +// $t0 = {0x71234, 0xfffff812} +// = 0x71234fffff812 +// +// lu52i.d $t0, hi12 (0x0) +// +// $t0 = {0x0, 0x71234fffff812} +// = 0x71234fffff812 +func pc32RelocBits(reloc objabi.RelocType, tgt int64, pc int64) int64 { + lo12 := tgt & 0xfff + if reloc == objabi.R_LOONG64_ADDR_LO { + return lo12 } - return false + + off := (tgt & ^0xfff) - (pc & ^0xfff) + if lo12 >= 0x800 { + off += 0x1000 + } + + // objabi.R_LOONG64_ADDR_HI + return (off >> 12) & 0xfffff } -// Calculates the value to put into the immediate slot, according to the -// desired relocation type, target and PC. -// The value to use varies based on the reloc type. Namely, the absolute low -// bits of the target are to be used for the low part, while the page-aligned -// offset is to be used for the higher part. A "page" here is not related to -// the system's actual page size, but rather a fixed 12-bit range (designed to -// cooperate with ADDI/LD/ST's 12-bit immediates). -func calculatePCAlignedReloc(t objabi.RelocType, tgt int64, pc int64) int64 { - if isRequestingLowPageBits(t) { - // corresponding immediate field is 12 bits wide - return tgt & 0xfff +func pc64RelocBits(reloc objabi.RelocType, tgt int64, pc int64) int64 { + lo12 := tgt & 0xfff + off := (tgt & ^0xfff) - (pc & ^0xfff) + + if lo12 >= 0x800 { + off += (0x1000 - 0x100000000) } - pageDelta := (tgt >> 12) - (pc >> 12) - if tgt&0xfff >= 0x800 { - // adjust for sign-extended addition of the low bits - pageDelta += 1 + if (off & 0x80000000) != 0 { + off += 0x100000000 } - // corresponding immediate field is 20 bits wide - return pageDelta & 0xfffff + + if reloc == objabi.R_LOONG64_ADDR64_LO { + return (off >> 32) & 0xfffff + } + + // objabi.R_LOONG64_ADDR64_HI + return (off >> 52) & 0xfff } // Convert the direct jump relocation r to refer to a trampoline if the target is too far. |
