From: limeidan Date: Mon, 9 Oct 2023 09:31:14 +0000 (+0800) Subject: cmd/link/internal: add support for internal linking on loong64 X-Git-Tag: go1.25rc1~241 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=f212b6499a9944d728d514a70cca2d91a1110b2a;p=gostls13.git 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 Reviewed-by: sophie zhao Reviewed-by: Cherry Mui Reviewed-by: abner chenc Reviewed-by: Michael Knyszek LUCI-TryBot-Result: Go LUCI --- diff --git a/src/cmd/link/internal/loadelf/ldelf.go b/src/cmd/link/internal/loadelf/ldelf.go index c91fed5088..22c5dbc007 100644 --- a/src/cmd/link/internal/loadelf/ldelf.go +++ b/src/cmd/link/internal/loadelf/ldelf.go @@ -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 diff --git a/src/cmd/link/internal/loong64/asm.go b/src/cmd/link/internal/loong64/asm.go index 8a6cce177e..6adafd38fc 100644 --- a/src/cmd/link/internal/loong64/asm.go +++ b/src/cmd/link/internal/loong64/asm.go @@ -58,10 +58,328 @@ 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 { // loong64 ELF relocation (endian neutral) // offset uint64 @@ -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