cmd/link/internal: add support for internal linking on loong64

Change-Id: Ic0d36f27481ac707d04aaf7001f26061e510dd8f
Reviewed-on: https://go-review.googlesource.com/c/go/+/533716
Reviewed-by: Qiqi Huang <huangqiqi@loongson.cn>
Reviewed-by: sophie zhao <zhaoxiaolin@loongson.cn>
Reviewed-by: Cherry Mui <cherryyz@google.com>
Reviewed-by: abner chenc <chenguoqi@loongson.cn>
Reviewed-by: Michael Knyszek <mknyszek@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
This commit is contained in:
limeidan 2023-10-09 17:31:14 +08:00 committed by abner chenc
parent 42f9ee904c
commit f212b6499a
2 changed files with 375 additions and 5 deletions

View file

@ -610,6 +610,11 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, f *bio.Reader,
// See issue 73591.
continue
}
if arch.Family == sys.Loong64 && (strings.HasPrefix(elfsym.name, ".L") || elfsym.name == "L0\001") {
// Symbols generated by the relax feature of gcc and binutils on loong64.
continue
}
}
if strings.HasPrefix(elfsym.name, ".Linfo_string") {
@ -690,6 +695,12 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, f *bio.Reader,
l.SetAttrOnList(s, true)
textp = append(textp, s)
for ss := l.SubSym(s); ss != 0; ss = l.SubSym(ss) {
if arch.Family == sys.Loong64 && (strings.HasPrefix(l.SymName(ss), ".L") || l.SymName(ss) == "L0\001") {
// Symbols generated by the relax feature of gcc and binutils on loong64.
// We ignore them here because there are too many symbols of this type,
// resulting in insufficient space in findfunctable.
continue
}
if l.AttrOnList(ss) {
return errorf("symbol %s listed multiple times",
l.SymName(ss))
@ -1026,7 +1037,14 @@ func relSize(arch *sys.Arch, pn string, elftype uint32) (uint8, uint8, error) {
MIPS64 | uint32(elf.R_MIPS_PC32)<<16:
return 4, 4, nil
// These are informational annotations to assist linker optimizations.
case LOONG64 | uint32(elf.R_LARCH_ALIGN)<<16,
LOONG64 | uint32(elf.R_LARCH_RELAX)<<16:
return 0, 0, nil
case LOONG64 | uint32(elf.R_LARCH_ADD8)<<16,
LOONG64 | uint32(elf.R_LARCH_ADD6)<<16,
LOONG64 | uint32(elf.R_LARCH_SUB6)<<16,
LOONG64 | uint32(elf.R_LARCH_SUB8)<<16:
return 1, 1, nil
@ -1040,7 +1058,13 @@ func relSize(arch *sys.Arch, pn string, elftype uint32) (uint8, uint8, error) {
LOONG64 | uint32(elf.R_LARCH_ADD32)<<16,
LOONG64 | uint32(elf.R_LARCH_SUB24)<<16,
LOONG64 | uint32(elf.R_LARCH_SUB32)<<16,
LOONG64 | uint32(elf.R_LARCH_B16)<<16,
LOONG64 | uint32(elf.R_LARCH_B21)<<16,
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_GOT_PC_HI20)<<16,
LOONG64 | uint32(elf.R_LARCH_GOT_PC_LO12)<<16,
LOONG64 | uint32(elf.R_LARCH_32_PCREL)<<16:
return 4, 4, nil

View file

@ -58,8 +58,326 @@ func gentext(ctxt *ld.Link, ldr *loader.Loader) {
}
func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym, r loader.Reloc, rIdx int) bool {
log.Fatalf("adddynrel not implemented")
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):
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))
}
su := ldr.MakeSymbolUpdater(s)
su.SetRelocType(rIdx, objabi.R_CALLLOONG64)
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):
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))
}
su := ldr.MakeSymbolUpdater(s)
if r.Type() == objabi.ElfRelocOffset+objabi.RelocType(elf.R_LARCH_PCALA_HI20) {
su.SetRelocType(rIdx, objabi.R_LOONG64_ADDR_HI)
} else {
su.SetRelocType(rIdx, objabi.R_LOONG64_ADDR_LO)
}
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, int64(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 {
@ -134,10 +452,6 @@ func elfreloc1(ctxt *ld.Link, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym,
return true
}
func elfsetupplt(ctxt *ld.Link, ldr *loader.Loader, plt, gotplt *loader.SymbolBuilder, dynamic loader.Sym) {
return
}
func machoreloc1(*sys.Arch, *ld.OutBuf, *loader.Loader, loader.Sym, loader.ExtReloc, int64) bool {
return false
}
@ -197,6 +511,38 @@ func archreloc(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, r loade
pc := ldr.SymValue(s) + int64(r.Off())
t := ldr.SymAddr(rs) + r.Add() - pc
return int64(val&0xfc000000 | (((t >> 2) & 0xffff) << 10) | (((t >> 2) & 0x3ff0000) >> 16)), 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 int64(val&0xfc0003ff | (((t >> 2) & 0xffff) << 10)), noExtReloc, isOk
}
return int64(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 int64(val + ldr.SymAddr(rs) + r.Add()), noExtReloc, isOk
}
return int64(val - (ldr.SymAddr(rs) + r.Add())), noExtReloc, isOk
}
return val, 0, false