$OpenBSD: patch-src_cmd_link_internal_mips64_asm_go,v 1.2 2021/08/27 18:33:27 jsing Exp $

Index: src/cmd/link/internal/mips64/asm.go
--- src/cmd/link/internal/mips64/asm.go.orig
+++ src/cmd/link/internal/mips64/asm.go
@@ -37,6 +37,7 @@ import (
 	"cmd/link/internal/loader"
 	"cmd/link/internal/sym"
 	"debug/elf"
+	"fmt"
 )
 
 func gentext(ctxt *ld.Link, ldr *loader.Loader) {}
@@ -59,8 +60,10 @@ func elfreloc1(ctxt *ld.Link, out *ld.OutBuf, ldr *loa
 	elfsym := ld.ElfSymForReloc(ctxt, r.Xsym)
 	out.Write32(uint32(elfsym))
 	out.Write8(0)
-	out.Write8(0)
-	out.Write8(0)
+	if r.Type != objabi.R_MIPSGPRELHI && r.Type != objabi.R_MIPSGPRELLO {
+		out.Write8(0)
+		out.Write8(0)
+	}
 	switch r.Type {
 	default:
 		return false
@@ -85,9 +88,20 @@ func elfreloc1(ctxt *ld.Link, out *ld.OutBuf, ldr *loa
 			// via the external linker.
 			addend += 0x7000
 		}
-	case objabi.R_CALLMIPS,
-		objabi.R_JMPMIPS:
+	case objabi.R_CALLMIPS, objabi.R_JMPMIPS:
 		out.Write8(uint8(elf.R_MIPS_26))
+	case objabi.R_MIPSGPRELHI:
+		out.Write8(uint8(elf.R_MIPS_HI16))
+		out.Write8(uint8(elf.R_MIPS_SUB))
+		out.Write8(uint8(elf.R_MIPS_GPREL16))
+	case objabi.R_MIPSGPRELLO:
+		out.Write8(uint8(elf.R_MIPS_LO16))
+		out.Write8(uint8(elf.R_MIPS_SUB))
+		out.Write8(uint8(elf.R_MIPS_GPREL16))
+	case objabi.R_MIPSCALL16:
+		out.Write8(uint8(elf.R_MIPS_CALL16))
+	case objabi.R_MIPSJALR:
+		out.Write8(uint8(elf.R_MIPS_JALR))
 	}
 	out.Write64(uint64(addend))
 
@@ -102,6 +116,120 @@ func machoreloc1(*sys.Arch, *ld.OutBuf, *loader.Loader
 	return false
 }
 
+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.R_CALLIND:
+		if rs != 0 || ldr.SymType(rs) == sym.SDYNIMPORT {
+			ctxt.Errorf(s, "unsupported indirect call to SDYNIMPORT symbol")
+		}
+	case objabi.R_CALLMIPS, objabi.R_JMPMIPS:
+		if rs == 0 || ldr.SymType(rs) != sym.SDYNIMPORT {
+			break
+		}
+
+		// In the case of SDYNIMPORT symbols, add a trampoline that provides
+		// the necessary calling convention and relocations.
+
+		// 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
+			if r.Add() == 0 {
+				name += fmt.Sprintf("-tramp%d", i)
+			} else {
+				name += fmt.Sprintf("%+x-tramp%d", r.Add(), i)
+			}
+			tramp = ldr.LookupOrCreateSym(name, int(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)
+			}
+			break
+		}
+		if ldr.SymType(tramp) == 0 {
+			// trampoline does not exist, create one
+			trampb := ldr.MakeSymbolUpdater(tramp)
+			ctxt.AddTramp(trampb)
+			gentramp(ctxt.Arch, ctxt.LinkMode, ldr, trampb, rs, int64(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)
+	default:
+		ctxt.Errorf(s, "trampoline called with non-jump reloc: %d (%s)", r.Type(), sym.RelocName(ctxt.Arch, r.Type()))
+	}
+}
+
+func gentramp(arch *sys.Arch, linkmode ld.LinkMode, ldr *loader.Loader, tramp *loader.SymbolBuilder, target loader.Sym, offset int64) {
+	// Generate a trampoline that loads register 25 (t9) and jumps to that address.
+	// The JALR is needed for the relocation and PIC code requires that t9 contain
+	// the function address when called. The offset is based off the value that gp
+	// was initialised to via the dynamic linker, now stored in runtime.libc_gp.
+
+	ops := []uint32{
+		// Determine address of trampoline (our t9), preserving RA.
+		// The bal instruction gives us the address three instructions
+		// or 12 bytes into the trampoline.
+		0x03e0b825, // move   s7,ra
+		0x04110001, // bal    1(pc)
+		0x00000000, // nop
+		0x03e0c825, // move   t9,ra
+		0x02e0f825, // move   ra,s7
+		0x6339fff4, // daddi  t9,t9,-12
+
+		// Load R23 (aka REGTMP aka s7) with gp address.
+		0x3c170000, // lui    s7,0x0	<- R_MIPS_GPREL16/SUB/HI16
+		0x02f9b82d, // daddu  s7,s7,t9
+		0x66f70000, // daddiu s7,s7,0   <- R_MIPS_GPREL16/SUB/LO16
+		0x02e0e025, // move   gp,s7
+
+		// Load R25 (aka t9) with function address and indirect call.
+		0xdef90000, // ld     t9,0(s7)  <- R_MIPS_CALL16
+		0x03200009, // jalr   zero,t9   <- R_MIPS_JALR
+		0x00000000, // nop
+	}
+
+	tramp.SetSize(int64(len(ops) * 4))
+	ibs := make([]byte, tramp.Size())
+	for i, op := range ops {
+		arch.ByteOrder.PutUint32(ibs[i*4:], op)
+	}
+	tramp.SetData(ibs)
+
+	r1, _ := tramp.AddRel(objabi.R_MIPSGPRELHI)
+	r1.SetSiz(4)
+	r1.SetOff(6 * 4)
+	r1.SetSym(tramp.Sym())
+
+	r2, _ := tramp.AddRel(objabi.R_MIPSGPRELLO)
+	r2.SetSiz(4)
+	r2.SetOff(8 * 4)
+	r2.SetSym(tramp.Sym())
+
+	r3, _ := tramp.AddRel(objabi.R_MIPSCALL16)
+	r3.SetSiz(4)
+	r3.SetOff(10 * 4)
+	r3.SetSym(target)
+	r3.SetAdd(offset)
+
+	r4, _ := tramp.AddRel(objabi.R_MIPSJALR)
+	r4.SetSiz(4)
+	r4.SetOff(11 * 4)
+	r4.SetSym(target)
+	r4.SetAdd(offset)
+}
+
 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) {
 	if target.IsExternal() {
 		switch r.Type() {
@@ -112,7 +240,11 @@ func archreloc(target *ld.Target, ldr *loader.Loader, 
 			objabi.R_ADDRMIPSU,
 			objabi.R_ADDRMIPSTLS,
 			objabi.R_CALLMIPS,
-			objabi.R_JMPMIPS:
+			objabi.R_JMPMIPS,
+			objabi.R_MIPSGPRELHI,
+			objabi.R_MIPSGPRELLO,
+			objabi.R_MIPSCALL16,
+			objabi.R_MIPSJALR:
 			return val, 1, true
 		}
 	}
@@ -163,7 +295,11 @@ func extreloc(target *ld.Target, ldr *loader.Loader, r
 
 	case objabi.R_ADDRMIPSTLS,
 		objabi.R_CALLMIPS,
-		objabi.R_JMPMIPS:
+		objabi.R_JMPMIPS,
+		objabi.R_MIPSGPRELHI,
+		objabi.R_MIPSGPRELLO,
+		objabi.R_MIPSCALL16,
+		objabi.R_MIPSJALR:
 		return ld.ExtrelocSimple(ldr, r), true
 	}
 	return loader.ExtReloc{}, false
