go/src/cmd/link/internal/loong64/asm.go
WANG Xuerui 4b0e3cc1d6 cmd/link: support loading R_LARCH_PCREL20_S2 and R_LARCH_CALL36 relocs
Host libgcc.a (among other libraries) on loong64 systems may contain
such relocs if built with the "medium" code model and/or linker
relaxation, which is increasingly the case. Make the internal linker
aware of these for cgo interopability going forward.

While at it, fix some of the comments for the loong64-specific
RelocTypes.

Fixes #75562

Change-Id: I0810969dcd229c5131ef06b0f70f51d81a3be4cd
Reviewed-on: https://go-review.googlesource.com/c/go/+/709717
Reviewed-by: abner chenc <chenguoqi@loongson.cn>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Mark Freeman <markfreeman@google.com>
Reviewed-by: Meidan Li <limeidan@loongson.cn>
Reviewed-by: Junyang Shao <shaojunyang@google.com>
2025-11-20 19:04:50 -08:00

763 lines
24 KiB
Go

// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package loong64
import (
"cmd/internal/objabi"
"cmd/internal/sys"
"cmd/link/internal/ld"
"cmd/link/internal/loader"
"cmd/link/internal/sym"
"debug/elf"
"fmt"
"log"
)
func gentext(ctxt *ld.Link, ldr *loader.Loader) {
initfunc, addmoduledata := ld.PrepareAddmoduledata(ctxt)
if initfunc == nil {
return
}
o := func(op uint32) {
initfunc.AddUint32(ctxt.Arch, op)
}
// Emit the following function:
//
// local.dso_init:
// la.pcrel $a0, local.moduledata
// b runtime.addmoduledata
// 0000000000000000 <local.dso_init>:
// 0: 1a000004 pcalau12i $a0, 0
// 0: R_LARCH_PCALA_HI20 local.moduledata
o(0x1a000004)
rel, _ := initfunc.AddRel(objabi.R_LOONG64_ADDR_HI)
rel.SetOff(0)
rel.SetSiz(4)
rel.SetSym(ctxt.Moduledata)
// 4: 02c00084 addi.d $a0, $a0, 0
// 4: R_LARCH_PCALA_LO12 local.moduledata
o(0x02c00084)
rel2, _ := initfunc.AddRel(objabi.R_LOONG64_ADDR_LO)
rel2.SetOff(4)
rel2.SetSiz(4)
rel2.SetSym(ctxt.Moduledata)
// 8: 50000000 b 0
// 8: R_LARCH_B26 runtime.addmoduledata
o(0x50000000)
rel3, _ := initfunc.AddRel(objabi.R_CALLLOONG64)
rel3.SetOff(8)
rel3.SetSiz(4)
rel3.SetSym(addmoduledata)
}
func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym, r loader.Reloc, rIdx int) bool {
targ := r.Sym()
var targType sym.SymKind
if targ != 0 {
targType = ldr.SymType(targ)
}
switch r.Type() {
default:
if r.Type() >= objabi.ElfRelocOffset {
ldr.Errorf(s, "adddynrel: unexpected reloction type %d (%s)", r.Type(), sym.RelocName(target.Arch, r.Type()))
return false
}
case objabi.ElfRelocOffset + objabi.RelocType(elf.R_LARCH_64):
if targType == sym.SDYNIMPORT {
ldr.Errorf(s, "unexpected R_LARCH_64 relocation for dynamic symbol %s", ldr.SymName(targ))
}
su := ldr.MakeSymbolUpdater(s)
su.SetRelocType(rIdx, objabi.R_ADDR)
if target.IsPIE() && target.IsInternal() {
// For internal linking PIE, this R_ADDR relocation cannot
// be resolved statically. We need to generate a dynamic
// relocation. Let the code below handle it.
break
}
return true
case objabi.ElfRelocOffset + objabi.RelocType(elf.R_LARCH_B26),
objabi.ElfRelocOffset + objabi.RelocType(elf.R_LARCH_CALL36):
if targType == sym.SDYNIMPORT {
addpltsym(target, ldr, syms, targ)
su := ldr.MakeSymbolUpdater(s)
su.SetRelocSym(rIdx, syms.PLT)
su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymPlt(targ)))
}
if targType == 0 || targType == sym.SXREF {
ldr.Errorf(s, "unknown symbol %s in callloong64", ldr.SymName(targ))
}
relocType := objabi.R_CALLLOONG64
if r.Type() == objabi.ElfRelocOffset+objabi.RelocType(elf.R_LARCH_CALL36) {
relocType = objabi.R_LOONG64_CALL36
}
su := ldr.MakeSymbolUpdater(s)
su.SetRelocType(rIdx, relocType)
return true
case objabi.ElfRelocOffset + objabi.RelocType(elf.R_LARCH_GOT_PC_HI20),
objabi.ElfRelocOffset + objabi.RelocType(elf.R_LARCH_GOT_PC_LO12):
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)
}
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_PCREL20_S2):
if targType == sym.SDYNIMPORT {
ldr.Errorf(s, "unexpected relocation for dynamic symbol %s", ldr.SymName(targ))
}
if targType == 0 || targType == sym.SXREF {
ldr.Errorf(s, "unknown symbol %s", ldr.SymName(targ))
}
var relocType objabi.RelocType
switch r.Type() - objabi.ElfRelocOffset {
case objabi.RelocType(elf.R_LARCH_PCALA_HI20):
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_PCREL20_S2):
relocType = objabi.R_LOONG64_ADDR_PCREL20_S2
}
su := ldr.MakeSymbolUpdater(s)
su.SetRelocType(rIdx, relocType)
return true
case objabi.ElfRelocOffset + objabi.RelocType(elf.R_LARCH_ADD64),
objabi.ElfRelocOffset + objabi.RelocType(elf.R_LARCH_SUB64):
su := ldr.MakeSymbolUpdater(s)
if r.Type() == objabi.ElfRelocOffset+objabi.RelocType(elf.R_LARCH_ADD64) {
su.SetRelocType(rIdx, objabi.R_LOONG64_ADD64)
} else {
su.SetRelocType(rIdx, objabi.R_LOONG64_SUB64)
}
return true
case objabi.ElfRelocOffset + objabi.RelocType(elf.R_LARCH_B16),
objabi.ElfRelocOffset + objabi.RelocType(elf.R_LARCH_B21):
if targType == sym.SDYNIMPORT {
addpltsym(target, ldr, syms, targ)
su := ldr.MakeSymbolUpdater(s)
su.SetRelocSym(rIdx, syms.PLT)
su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymPlt(targ)))
}
if targType == 0 || targType == sym.SXREF {
ldr.Errorf(s, "unknown symbol %s in R_JMPxxLOONG64", ldr.SymName(targ))
}
su := ldr.MakeSymbolUpdater(s)
if r.Type() == objabi.ElfRelocOffset+objabi.RelocType(elf.R_LARCH_B16) {
su.SetRelocType(rIdx, objabi.R_JMP16LOONG64)
} else {
su.SetRelocType(rIdx, objabi.R_JMP21LOONG64)
}
return true
}
relocs := ldr.Relocs(s)
r = relocs.At(rIdx)
switch r.Type() {
case objabi.R_CALLLOONG64:
if targType != sym.SDYNIMPORT {
return true
}
if target.IsExternal() {
return true
}
// Internal linking.
if r.Add() != 0 {
ldr.Errorf(s, "PLT call with no-zero addend (%v)", r.Add())
}
// Build a PLT entry and change the relocation target to that entry.
addpltsym(target, ldr, syms, targ)
su := ldr.MakeSymbolUpdater(s)
su.SetRelocSym(rIdx, syms.PLT)
su.SetRelocAdd(rIdx, int64(ldr.SymPlt(targ)))
return true
case objabi.R_ADDR:
if ldr.SymType(s) == sym.STEXT && target.IsElf() {
// The code is asking for the address of an external
// function. We provide it with the address of the
// correspondent GOT symbol.
ld.AddGotSym(target, ldr, syms, targ, uint32(elf.R_LARCH_64))
su := ldr.MakeSymbolUpdater(s)
su.SetRelocSym(rIdx, syms.GOT)
su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymGot(targ)))
return true
}
// Process dynamic relocations for the data sections.
if target.IsPIE() && target.IsInternal() {
// When internally linking, generate dynamic relocations
// for all typical R_ADDR relocations. The exception
// are those R_ADDR that are created as part of generating
// the dynamic relocations and must be resolved statically.
//
// There are three phases relevant to understanding this:
//
// dodata() // we are here
// address() // symbol address assignment
// reloc() // resolution of static R_ADDR relocs
//
// At this point symbol addresses have not been
// assigned yet (as the final size of the .rela section
// will affect the addresses), and so we cannot write
// the Elf64_Rela.r_offset now. Instead we delay it
// until after the 'address' phase of the linker is
// complete. We do this via Addaddrplus, which creates
// a new R_ADDR relocation which will be resolved in
// the 'reloc' phase.
//
// These synthetic static R_ADDR relocs must be skipped
// now, or else we will be caught in an infinite loop
// of generating synthetic relocs for our synthetic
// relocs.
//
// Furthermore, the rela sections contain dynamic
// relocations with R_ADDR relocations on
// Elf64_Rela.r_offset. This field should contain the
// symbol offset as determined by reloc(), not the
// final dynamically linked address as a dynamic
// relocation would provide.
switch ldr.SymName(s) {
case ".dynsym", ".rela", ".rela.plt", ".got.plt", ".dynamic":
return false
}
} else {
// Either internally linking a static executable,
// in which case we can resolve these relocations
// statically in the 'reloc' phase, or externally
// linking, in which case the relocation will be
// prepared in the 'reloc' phase and passed to the
// external linker in the 'asmb' phase.
if ldr.SymType(s) != sym.SDATA && ldr.SymType(s) != sym.SRODATA {
break
}
}
if target.IsElf() {
// Generate R_LARCH_RELATIVE relocations for best
// efficiency in the dynamic linker.
//
// As noted above, symbol addresses have not been
// assigned yet, so we can't generate the final reloc
// entry yet. We ultimately want:
//
// r_offset = s + r.Off
// r_info = R_LARCH_RELATIVE
// r_addend = targ + r.Add
//
// The dynamic linker will set *offset = base address +
// addend.
//
// AddAddrPlus is used for r_offset and r_addend to
// generate new R_ADDR relocations that will update
// these fields in the 'reloc' phase.
rela := ldr.MakeSymbolUpdater(syms.Rela)
rela.AddAddrPlus(target.Arch, s, int64(r.Off()))
if r.Siz() == 8 {
rela.AddUint64(target.Arch, elf.R_INFO(0, uint32(elf.R_LARCH_RELATIVE)))
} else {
ldr.Errorf(s, "unexpected relocation for dynamic symbol %s", ldr.SymName(targ))
}
rela.AddAddrPlus(target.Arch, targ, r.Add())
return true
}
case objabi.R_LOONG64_GOT_HI,
objabi.R_LOONG64_GOT_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)
}
su.SetRelocSym(rIdx, syms.GOT)
su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymGot(targ)))
return true
}
return false
}
func elfsetupplt(ctxt *ld.Link, ldr *loader.Loader, plt, gotplt *loader.SymbolBuilder, dynamic loader.Sym) {
if plt.Size() == 0 {
// pcalau12i $r14, imm
plt.AddSymRef(ctxt.Arch, gotplt.Sym(), 0, objabi.R_LOONG64_ADDR_HI, 4)
plt.SetUint32(ctxt.Arch, plt.Size()-4, 0x1a00000e)
// sub.d $r13, $r13, $r15
plt.AddUint32(ctxt.Arch, 0x0011bdad)
// ld.d $r15, $r14, imm
plt.AddSymRef(ctxt.Arch, gotplt.Sym(), 0, objabi.R_LOONG64_ADDR_LO, 4)
plt.SetUint32(ctxt.Arch, plt.Size()-4, 0x28c001cf)
// addi.d $r13, $r13, -40
plt.AddUint32(ctxt.Arch, 0x02ff61ad)
// addi.d $r12, $r14, imm
plt.AddSymRef(ctxt.Arch, gotplt.Sym(), 0, objabi.R_LOONG64_ADDR_LO, 4)
plt.SetUint32(ctxt.Arch, plt.Size()-4, 0x2c001cc)
// srli.d $r13, $r13, 1
plt.AddUint32(ctxt.Arch, 0x004505ad)
// ld.d $r12, $r12, 8
plt.AddUint32(ctxt.Arch, 0x28c0218c)
// jirl $r0, $r15, 0
plt.AddUint32(ctxt.Arch, 0x4c0001e0)
// check gotplt.size == 0
if gotplt.Size() != 0 {
ctxt.Errorf(gotplt.Sym(), "got.plt is not empty at the very beginning")
}
gotplt.AddUint64(ctxt.Arch, 0)
gotplt.AddUint64(ctxt.Arch, 0)
}
}
func addpltsym(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym) {
if ldr.SymPlt(s) >= 0 {
return
}
ld.Adddynsym(ldr, target, syms, s)
if target.IsElf() {
plt := ldr.MakeSymbolUpdater(syms.PLT)
gotplt := ldr.MakeSymbolUpdater(syms.GOTPLT)
rela := ldr.MakeSymbolUpdater(syms.RelaPLT)
if plt.Size() == 0 {
panic("plt is not set up")
}
// pcalau12i $r15, imm
plt.AddAddrPlus4(target.Arch, gotplt.Sym(), gotplt.Size())
plt.SetUint32(target.Arch, plt.Size()-4, 0x1a00000f)
relocs := plt.Relocs()
plt.SetRelocType(relocs.Count()-1, objabi.R_LOONG64_ADDR_HI)
// ld.d $r15, $r15, imm
plt.AddAddrPlus4(target.Arch, gotplt.Sym(), gotplt.Size())
plt.SetUint32(target.Arch, plt.Size()-4, 0x28c001ef)
relocs = plt.Relocs()
plt.SetRelocType(relocs.Count()-1, objabi.R_LOONG64_ADDR_LO)
// pcaddu12i $r13, 0
plt.AddUint32(target.Arch, 0x1c00000d)
// jirl r0, r15, 0
plt.AddUint32(target.Arch, 0x4c0001e0)
// add to got.plt: pointer to plt[0]
gotplt.AddAddrPlus(target.Arch, plt.Sym(), 0)
// rela
rela.AddAddrPlus(target.Arch, gotplt.Sym(), gotplt.Size()-8)
sDynid := ldr.SymDynid(s)
rela.AddUint64(target.Arch, elf.R_INFO(uint32(sDynid), uint32(elf.R_LARCH_JUMP_SLOT)))
rela.AddUint64(target.Arch, 0)
ldr.SetPlt(s, int32(plt.Size()-16))
} else {
ldr.Errorf(s, "addpltsym: unsupport binary format")
}
}
func elfreloc1(ctxt *ld.Link, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym, r loader.ExtReloc, ri int, sectoff int64) bool {
// loong64 ELF relocation (endian neutral)
// offset uint64
// symreloc uint64 // The high 32-bit is the symbol, the low 32-bit is the relocation type.
// addend int64
elfsym := ld.ElfSymForReloc(ctxt, r.Xsym)
switch r.Type {
default:
return false
case objabi.R_ADDR, objabi.R_DWARFSECREF:
switch r.Size {
case 4:
out.Write64(uint64(sectoff))
out.Write64(uint64(elf.R_LARCH_32) | uint64(elfsym)<<32)
out.Write64(uint64(r.Xadd))
case 8:
out.Write64(uint64(sectoff))
out.Write64(uint64(elf.R_LARCH_64) | uint64(elfsym)<<32)
out.Write64(uint64(r.Xadd))
default:
return false
}
case objabi.R_LOONG64_TLS_LE_LO:
out.Write64(uint64(sectoff))
out.Write64(uint64(elf.R_LARCH_TLS_LE_LO12) | uint64(elfsym)<<32)
out.Write64(uint64(r.Xadd))
case objabi.R_LOONG64_TLS_LE_HI:
out.Write64(uint64(sectoff))
out.Write64(uint64(elf.R_LARCH_TLS_LE_HI20) | uint64(elfsym)<<32)
out.Write64(uint64(r.Xadd))
case objabi.R_CALLLOONG64:
out.Write64(uint64(sectoff))
out.Write64(uint64(elf.R_LARCH_B26) | uint64(elfsym)<<32)
out.Write64(uint64(r.Xadd))
case objabi.R_LOONG64_CALL36:
out.Write64(uint64(sectoff))
out.Write64(uint64(elf.R_LARCH_CALL36) | uint64(elfsym)<<32)
out.Write64(uint64(r.Xadd))
case objabi.R_LOONG64_TLS_IE_HI:
out.Write64(uint64(sectoff))
out.Write64(uint64(elf.R_LARCH_TLS_IE_PC_HI20) | uint64(elfsym)<<32)
out.Write64(uint64(0x0))
case objabi.R_LOONG64_TLS_IE_LO:
out.Write64(uint64(sectoff))
out.Write64(uint64(elf.R_LARCH_TLS_IE_PC_LO12) | uint64(elfsym)<<32)
out.Write64(uint64(0x0))
case objabi.R_LOONG64_ADDR_LO:
out.Write64(uint64(sectoff))
out.Write64(uint64(elf.R_LARCH_PCALA_LO12) | uint64(elfsym)<<32)
out.Write64(uint64(r.Xadd))
case objabi.R_LOONG64_ADDR_HI:
out.Write64(uint64(sectoff))
out.Write64(uint64(elf.R_LARCH_PCALA_HI20) | 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)
out.Write64(uint64(r.Xadd))
case objabi.R_LOONG64_GOT_HI:
out.Write64(uint64(sectoff))
out.Write64(uint64(elf.R_LARCH_GOT_PC_HI20) | uint64(elfsym)<<32)
out.Write64(uint64(0x0))
case objabi.R_LOONG64_GOT_LO:
out.Write64(uint64(sectoff))
out.Write64(uint64(elf.R_LARCH_GOT_PC_LO12) | uint64(elfsym)<<32)
out.Write64(uint64(0x0))
}
return true
}
func machoreloc1(*sys.Arch, *ld.OutBuf, *loader.Loader, loader.Sym, loader.ExtReloc, int64) bool {
return false
}
func archreloc(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, r loader.Reloc, s loader.Sym, val int64) (o int64, nExtReloc int, ok bool) {
rs := r.Sym()
if target.IsExternal() {
switch r.Type() {
default:
return val, 0, false
case objabi.R_LOONG64_ADDR_HI,
objabi.R_LOONG64_ADDR_LO,
objabi.R_LOONG64_ADDR_PCREL20_S2:
// set up addend for eventual relocation via outer symbol.
rs, _ := ld.FoldSubSymbolOffset(ldr, rs)
rst := ldr.SymType(rs)
if rst != sym.SHOSTOBJ && rst != sym.SDYNIMPORT && ldr.SymSect(rs) == nil {
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,
objabi.R_LOONG64_CALL36,
objabi.R_JMPLOONG64,
objabi.R_LOONG64_TLS_IE_HI,
objabi.R_LOONG64_TLS_IE_LO,
objabi.R_LOONG64_GOT_HI,
objabi.R_LOONG64_GOT_LO:
return val, 1, true
}
}
const isOk = true
const noExtReloc = 0
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:
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
}
return val&0xfe00001f | (t << 5), 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()
if r.Type() == objabi.R_LOONG64_TLS_LE_LO {
return val&0xffc003ff | ((t & 0xfff) << 10), noExtReloc, isOk
}
return val&0xfe00001f | (((t) >> 12 << 5) & 0x1ffffe0), noExtReloc, isOk
case objabi.R_CALLLOONG64,
objabi.R_JMPLOONG64:
pc := ldr.SymValue(s) + int64(r.Off())
t := ldr.SymAddr(rs) + r.Add() - pc
return val&0xfc000000 | (((t >> 2) & 0xffff) << 10) | (((t >> 2) & 0x3ff0000) >> 16), noExtReloc, isOk
case objabi.R_LOONG64_CALL36:
pc := ldr.SymValue(s) + int64(r.Off())
t := (ldr.SymAddr(rs) + r.Add() - pc) >> 2
// val is pcaddu18i (lower half) + jirl (upper half)
pcaddu18i := (val & 0xfe00001f) | (((t + 0x8000) >> 16) << 5)
jirl := ((val >> 32) & 0xfc0003ff) | ((t & 0xffff) << 10)
return pcaddu18i | (jirl << 32), noExtReloc, isOk
case objabi.R_JMP16LOONG64,
objabi.R_JMP21LOONG64:
pc := ldr.SymValue(s) + int64(r.Off())
t := ldr.SymAddr(rs) + r.Add() - pc
if r.Type() == objabi.R_JMP16LOONG64 {
return val&0xfc0003ff | (((t >> 2) & 0xffff) << 10), noExtReloc, isOk
}
return val&0xfc0003e0 | (((t >> 2) & 0xffff) << 10) | (((t >> 2) & 0x1f0000) >> 16), noExtReloc, isOk
case objabi.R_LOONG64_TLS_IE_HI,
objabi.R_LOONG64_TLS_IE_LO:
if target.IsPIE() && target.IsElf() {
if !target.IsLinux() {
ldr.Errorf(s, "TLS reloc on unsupported OS %v", target.HeadType)
}
t := ldr.SymAddr(rs) + r.Add()
if r.Type() == objabi.R_LOONG64_TLS_IE_HI {
// pcalau12i -> lu12i.w
return (0x14000000 | (val & 0x1f) | ((t >> 12) << 5)), noExtReloc, isOk
}
// ld.d -> ori
return (0x03800000 | (val & 0x3ff) | ((t & 0xfff) << 10)), noExtReloc, isOk
} else {
log.Fatalf("cannot handle R_LOONG64_TLS_IE_x (sym %s) when linking internally", ldr.SymName(rs))
}
case objabi.R_LOONG64_ADD64, objabi.R_LOONG64_SUB64:
if r.Type() == objabi.R_LOONG64_ADD64 {
return val + ldr.SymAddr(rs) + r.Add(), noExtReloc, isOk
}
return val - (ldr.SymAddr(rs) + r.Add()), noExtReloc, isOk
}
return val, 0, false
}
func archrelocvariant(*ld.Target, *loader.Loader, loader.Reloc, sym.RelocVariant, loader.Sym, int64, []byte) int64 {
return -1
}
func extreloc(target *ld.Target, ldr *loader.Loader, r loader.Reloc, s loader.Sym) (loader.ExtReloc, bool) {
switch r.Type() {
case objabi.R_LOONG64_ADDR_HI,
objabi.R_LOONG64_ADDR_LO,
objabi.R_LOONG64_GOT_HI,
objabi.R_LOONG64_GOT_LO:
return ld.ExtrelocViaOuterSym(ldr, r, s), true
case objabi.R_LOONG64_TLS_LE_HI,
objabi.R_LOONG64_TLS_LE_LO,
objabi.R_CONST,
objabi.R_GOTOFF,
objabi.R_CALLLOONG64,
objabi.R_JMPLOONG64,
objabi.R_LOONG64_TLS_IE_HI,
objabi.R_LOONG64_TLS_IE_LO:
return ld.ExtrelocSimple(ldr, r), true
}
return loader.ExtReloc{}, false
}
func isRequestingLowPageBits(t objabi.RelocType) bool {
switch t {
case objabi.R_LOONG64_ADDR_LO:
return true
}
return false
}
// 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
}
pageDelta := (tgt >> 12) - (pc >> 12)
if tgt&0xfff >= 0x800 {
// adjust for sign-extended addition of the low bits
pageDelta += 1
}
// corresponding immediate field is 20 bits wide
return pageDelta & 0xfffff
}
// Convert the direct jump relocation r to refer to a trampoline if the target is too far.
func trampoline(ctxt *ld.Link, ldr *loader.Loader, ri int, rs, s loader.Sym) {
relocs := ldr.Relocs(s)
r := relocs.At(ri)
switch r.Type() {
case objabi.ElfRelocOffset + objabi.RelocType(elf.R_LARCH_B26):
// Nothing to do.
// The plt symbol has not been added. If we add tramp
// here, plt will not work.
case objabi.R_CALLLOONG64:
var t int64
// ldr.SymValue(rs) == 0 indicates a cross-package jump to a function that is not yet
// laid out. Conservatively use a trampoline. This should be rare, as we lay out packages
// in dependency order.
if ldr.SymValue(rs) != 0 {
t = ldr.SymValue(rs) + r.Add() - (ldr.SymValue(s) + int64(r.Off()))
}
if t >= 1<<27 || t < -1<<27 || ldr.SymValue(rs) == 0 || (*ld.FlagDebugTramp > 1 && (ldr.SymPkg(s) == "" || ldr.SymPkg(s) != ldr.SymPkg(rs))) {
// direct call too far need to insert trampoline.
// look up existing trampolines first. if we found one within the range
// of direct call, we can reuse it. otherwise create a new one.
var tramp loader.Sym
for i := 0; ; i++ {
oName := ldr.SymName(rs)
name := oName + fmt.Sprintf("%+x-tramp%d", r.Add(), i)
tramp = ldr.LookupOrCreateSym(name, ldr.SymVersion(rs))
ldr.SetAttrReachable(tramp, true)
if ldr.SymType(tramp) == sym.SDYNIMPORT {
// don't reuse trampoline defined in other module
continue
}
if oName == "runtime.deferreturn" {
ldr.SetIsDeferReturnTramp(tramp, true)
}
if ldr.SymValue(tramp) == 0 {
// either the trampoline does not exist -- we need to create one,
// or found one the address which is not assigned -- this will be
// laid down immediately after the current function. use this one.
break
}
t = ldr.SymValue(tramp) - (ldr.SymValue(s) + int64(r.Off()))
if t >= -1<<27 && t < 1<<27 {
// found an existing trampoline that is not too far
// we can just use it.
break
}
}
if ldr.SymType(tramp) == 0 {
// trampoline does not exist, create one
trampb := ldr.MakeSymbolUpdater(tramp)
ctxt.AddTramp(trampb, ldr.SymType(s))
if ldr.SymType(rs) == sym.SDYNIMPORT {
if r.Add() != 0 {
ctxt.Errorf(s, "nonzero addend for DYNIMPORT call: %v+%d", ldr.SymName(rs), r.Add())
}
gentrampgot(ctxt, ldr, trampb, rs)
} else {
gentramp(ctxt, ldr, trampb, rs, r.Add())
}
}
// modify reloc to point to tramp, which will be resolved later
sb := ldr.MakeSymbolUpdater(s)
relocs := sb.Relocs()
r := relocs.At(ri)
r.SetSym(tramp)
r.SetAdd(0) // clear the offset embedded in the instruction
}
default:
ctxt.Errorf(s, "trampoline called with non-jump reloc: %d (%s)", r.Type(), sym.RelocName(ctxt.Arch, r.Type()))
}
}
// generate a trampoline to target+offset.
func gentramp(ctxt *ld.Link, ldr *loader.Loader, tramp *loader.SymbolBuilder, target loader.Sym, offset int64) {
tramp.SetSize(12) // 3 instructions
P := make([]byte, tramp.Size())
o1 := uint32(0x1a00001e) // pcalau12i $r30, 0
ctxt.Arch.ByteOrder.PutUint32(P, o1)
r1, _ := tramp.AddRel(objabi.R_LOONG64_ADDR_HI)
r1.SetOff(0)
r1.SetSiz(4)
r1.SetSym(target)
r1.SetAdd(offset)
o2 := uint32(0x02c003de) // addi.d $r30, $r30, 0
ctxt.Arch.ByteOrder.PutUint32(P[4:], o2)
r2, _ := tramp.AddRel(objabi.R_LOONG64_ADDR_LO)
r2.SetOff(4)
r2.SetSiz(4)
r2.SetSym(target)
r2.SetAdd(offset)
o3 := uint32(0x4c0003c0) // jirl $r0, $r30, 0
ctxt.Arch.ByteOrder.PutUint32(P[8:], o3)
tramp.SetData(P)
}
func gentrampgot(ctxt *ld.Link, ldr *loader.Loader, tramp *loader.SymbolBuilder, target loader.Sym) {
tramp.SetSize(12) // 3 instructions
P := make([]byte, tramp.Size())
o1 := uint32(0x1a00001e) // pcalau12i $r30, 0
ctxt.Arch.ByteOrder.PutUint32(P, o1)
r1, _ := tramp.AddRel(objabi.R_LOONG64_GOT_HI)
r1.SetOff(0)
r1.SetSiz(4)
r1.SetSym(target)
o2 := uint32(0x28c003de) // ld.d $r30, $r30, 0
ctxt.Arch.ByteOrder.PutUint32(P[4:], o2)
r2, _ := tramp.AddRel(objabi.R_LOONG64_GOT_LO)
r2.SetOff(4)
r2.SetSiz(4)
r2.SetSym(target)
o3 := uint32(0x4c0003c0) // jirl $r0, $r30, 0
ctxt.Arch.ByteOrder.PutUint32(P[8:], o3)
tramp.SetData(P)
}