From 4a1829b65a5c6b62c3aadeccc0822882e1bc4e46 Mon Sep 17 00:00:00 2001 From: Joel Sing Date: Tue, 7 Jun 2022 06:08:51 +1000 Subject: [PATCH] cmd/link: add internal linking support for calling SDYNIMPORT on mips64 Add internal linking support for calling SDYNIMPORT symbols on mips64. This adds code to generate appropriate PLT and GOT entries, along with the various dynamic entries needed for the dynamic loader. Updates #36435, #46178 Change-Id: I783e0d028510ca2bca82bcbc745f2375770813fe Reviewed-on: https://go-review.googlesource.com/c/go/+/415815 Reviewed-by: Rong Zhang Run-TryBot: Ian Lance Taylor Run-TryBot: Joel Sing Reviewed-by: Ian Lance Taylor Reviewed-by: Than McIntosh TryBot-Result: Gopher Robot Auto-Submit: Ian Lance Taylor --- src/cmd/link/internal/ld/elf.go | 1 + src/cmd/link/internal/mips64/asm.go | 177 +++++++++++++++++++++++++++- src/cmd/link/internal/mips64/obj.go | 9 +- 3 files changed, 179 insertions(+), 8 deletions(-) diff --git a/src/cmd/link/internal/ld/elf.go b/src/cmd/link/internal/ld/elf.go index 954aaaff16..5eeb4a9993 100644 --- a/src/cmd/link/internal/ld/elf.go +++ b/src/cmd/link/internal/ld/elf.go @@ -1623,6 +1623,7 @@ func (ctxt *Link) doelf() { // DT_JMPREL is emitted so we have to defer generation of elf.DT_PLTREL, // DT_PLTRELSZ, and elf.DT_JMPREL dynamic entries until after we know the // size of .rel(a).plt section. + Elfwritedynent(ctxt.Arch, dynamic, elf.DT_DEBUG, 0) } diff --git a/src/cmd/link/internal/mips64/asm.go b/src/cmd/link/internal/mips64/asm.go index 0e64af3e6a..bd0e0191bc 100644 --- a/src/cmd/link/internal/mips64/asm.go +++ b/src/cmd/link/internal/mips64/asm.go @@ -39,7 +39,97 @@ import ( "debug/elf" ) -func gentext(ctxt *ld.Link, ldr *loader.Loader) {} +var ( + // dtOffsets contains offsets for entries within the .dynamic section. + // These are used to fix up symbol values once they are known. + dtOffsets map[elf.DynTag]int64 + + // dynSymCount contains the number of entries in the .dynsym section. + // This is used to populate the DT_MIPS_SYMTABNO entry in the .dynamic + // section. + dynSymCount uint64 + + // gotLocalCount contains the number of local global offset table + // entries. This is used to populate the DT_MIPS_LOCAL_GOTNO entry in + // the .dynamic section. + gotLocalCount uint64 + + // gotSymIndex contains the index of the first dynamic symbol table + // entry that corresponds to an entry in the global offset table. + // This is used to populate the DT_MIPS_GOTSYM entry in the .dynamic + // section. + gotSymIndex uint64 +) + +func gentext(ctxt *ld.Link, ldr *loader.Loader) { + if *ld.FlagD || ctxt.Target.IsExternal() { + return + } + + dynamic := ldr.MakeSymbolUpdater(ctxt.ArchSyms.Dynamic) + + ld.Elfwritedynent(ctxt.Arch, dynamic, elf.DT_MIPS_RLD_VERSION, 1) + ld.Elfwritedynent(ctxt.Arch, dynamic, elf.DT_MIPS_BASE_ADDRESS, 0) + + // elfsetupplt should have been called and gotLocalCount should now + // have its correct value. + if gotLocalCount == 0 { + ctxt.Errorf(0, "internal error: elfsetupplt has not been called") + } + ld.Elfwritedynent(ctxt.Arch, dynamic, elf.DT_MIPS_LOCAL_GOTNO, gotLocalCount) + + // DT_* entries have to exist prior to elfdynhash(), which finalises the + // table by adding DT_NULL. However, the values for the following entries + // are not know until after dynreloc() has completed. Add the symbols now, + // then update their values prior to code generation. + dts := []elf.DynTag{ + elf.DT_MIPS_SYMTABNO, + elf.DT_MIPS_GOTSYM, + } + dtOffsets = make(map[elf.DynTag]int64) + for _, dt := range dts { + ld.Elfwritedynent(ctxt.Arch, dynamic, dt, 0) + dtOffsets[dt] = dynamic.Size() - 8 + } +} + +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) + } + + if r.Type() >= objabi.ElfRelocOffset { + ldr.Errorf(s, "unexpected relocation type %d (%s)", r.Type(), sym.RelocName(target.Arch, r.Type())) + return false + } + + switch r.Type() { + case objabi.R_CALLMIPS, objabi.R_JMPMIPS: + if targType != sym.SDYNIMPORT { + // Nothing to do, the relocation will be laid out in reloc + return true + } + if target.IsExternal() { + // External linker will do this relocation. + return true + } + + // Internal linking, build a PLT entry and change the relocation + // target to that entry. + if r.Add() != 0 { + ldr.Errorf(s, "PLT call with non-zero addend (%v)", r.Add()) + } + addpltsym(target, ldr, syms, targ) + su := ldr.MakeSymbolUpdater(s) + su.SetRelocSym(rIdx, syms.PLT) + su.SetRelocAdd(rIdx, int64(ldr.SymPlt(targ))) + return true + } + + return false +} func elfreloc1(ctxt *ld.Link, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym, r loader.ExtReloc, ri int, sectoff int64) bool { @@ -95,7 +185,90 @@ func elfreloc1(ctxt *ld.Link, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym, } func elfsetupplt(ctxt *ld.Link, plt, gotplt *loader.SymbolBuilder, dynamic loader.Sym) { - return + if plt.Size() != 0 { + return + } + + // Load resolver address from got[0] into r25. + plt.AddSymRef(ctxt.Arch, gotplt.Sym(), 0, objabi.R_ADDRMIPSU, 4) + plt.SetUint32(ctxt.Arch, plt.Size()-4, 0x3c0e0000) // lui $14, %hi(&GOTPLT[0]) + plt.AddSymRef(ctxt.Arch, gotplt.Sym(), 0, objabi.R_ADDRMIPS, 4) + plt.SetUint32(ctxt.Arch, plt.Size()-4, 0xddd90000) // ld $25, %lo(&GOTPLT[0])($14) + + // Load return address into r15, the index of the got.plt entry into r24, then + // JALR to the resolver. The address of the got.plt entry is currently in r24, + // which we have to turn into an index. + plt.AddSymRef(ctxt.Arch, gotplt.Sym(), 0, objabi.R_ADDRMIPS, 4) + plt.SetUint32(ctxt.Arch, plt.Size()-4, 0x25ce0000) // addiu $14, $14, %lo(&GOTPLT[0]) + plt.AddUint32(ctxt.Arch, 0x030ec023) // subu $24, $24, $14 + plt.AddUint32(ctxt.Arch, 0x03e07825) // move $15, $31 + plt.AddUint32(ctxt.Arch, 0x0018c0c2) // srl $24, $24, 3 + plt.AddUint32(ctxt.Arch, 0x0320f809) // jalr $25 + plt.AddUint32(ctxt.Arch, 0x2718fffe) // subu $24, $24, 2 + + if gotplt.Size() != 0 { + ctxt.Errorf(gotplt.Sym(), "got.plt is not empty") + } + + // Reserve got[0] for resolver address (populated by dynamic loader). + gotplt.AddUint32(ctxt.Arch, 0) + gotplt.AddUint32(ctxt.Arch, 0) + gotLocalCount++ + + // Reserve got[1] for ELF object pointer (populated by dynamic loader). + gotplt.AddUint32(ctxt.Arch, 0) + gotplt.AddUint32(ctxt.Arch, 0) + gotLocalCount++ +} + +func addpltsym(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym) { + if ldr.SymPlt(s) >= 0 { + return + } + + dynamic := ldr.MakeSymbolUpdater(syms.Dynamic) + + const dynSymEntrySize = 20 + if gotSymIndex == 0 { + // Compute and update GOT symbol index. + gotSymIndex = uint64(ldr.SymSize(syms.DynSym) / dynSymEntrySize) + dynamic.SetUint(target.Arch, dtOffsets[elf.DT_MIPS_GOTSYM], gotSymIndex) + } + if dynSymCount == 0 { + dynSymCount = uint64(ldr.SymSize(syms.DynSym) / dynSymEntrySize) + } + + ld.Adddynsym(ldr, target, syms, s) + dynSymCount++ + + if !target.IsElf() { + ldr.Errorf(s, "addpltsym: unsupported binary format") + } + + plt := ldr.MakeSymbolUpdater(syms.PLT) + gotplt := ldr.MakeSymbolUpdater(syms.GOTPLT) + if plt.Size() == 0 { + panic("plt is not set up") + } + + // Load got.plt entry into r25. + plt.AddSymRef(target.Arch, gotplt.Sym(), gotplt.Size(), objabi.R_ADDRMIPSU, 4) + plt.SetUint32(target.Arch, plt.Size()-4, 0x3c0f0000) // lui $15, %hi(.got.plt entry) + plt.AddSymRef(target.Arch, gotplt.Sym(), gotplt.Size(), objabi.R_ADDRMIPS, 4) + plt.SetUint32(target.Arch, plt.Size()-4, 0xddf90000) // ld $25, %lo(.got.plt entry)($15) + + // Load address of got.plt entry into r24 and JALR to address in r25. + plt.AddUint32(target.Arch, 0x03200008) // jr $25 + plt.AddSymRef(target.Arch, gotplt.Sym(), gotplt.Size(), objabi.R_ADDRMIPS, 4) + plt.SetUint32(target.Arch, plt.Size()-4, 0x65f80000) // daddiu $24, $15, %lo(.got.plt entry) + + // Add pointer to plt[0] to got.plt + gotplt.AddAddrPlus(target.Arch, plt.Sym(), 0) + + ldr.SetPlt(s, int32(plt.Size()-16)) + + // Update dynamic symbol count. + dynamic.SetUint(target.Arch, dtOffsets[elf.DT_MIPS_SYMTABNO], dynSymCount) } func machoreloc1(*sys.Arch, *ld.OutBuf, *loader.Loader, loader.Sym, loader.ExtReloc, int64) bool { diff --git a/src/cmd/link/internal/mips64/obj.go b/src/cmd/link/internal/mips64/obj.go index a06e6f7981..7fb19e92ac 100644 --- a/src/cmd/link/internal/mips64/obj.go +++ b/src/cmd/link/internal/mips64/obj.go @@ -34,7 +34,6 @@ import ( "cmd/internal/objabi" "cmd/internal/sys" "cmd/link/internal/ld" - "cmd/link/internal/loader" "internal/buildcfg" ) @@ -108,10 +107,8 @@ func archinit(ctxt *ld.Link) { *ld.FlagRound = 0x10000 } } -} - -func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym, r loader.Reloc, rIdx int) bool { - ld.Exitf("adddynrel currently unimplemented for MIPS64") - return false + dynSymCount = 0 + gotLocalCount = 0 + gotSymIndex = 0 } -- 2.50.0