cmd/link,cmd/internal/obj/riscv: implement archreloc for riscv64

Based on the riscv-go port.

Updates #27532

Change-Id: I478254306441c253d3a2c09c10932ad1ac0be3c6
Reviewed-on: https://go-review.googlesource.com/c/go/+/204625
Reviewed-by: Cherry Zhang <cherryyz@google.com>
Run-TryBot: Cherry Zhang <cherryyz@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
diff --git a/src/cmd/internal/obj/riscv/obj.go b/src/cmd/internal/obj/riscv/obj.go
index 9a3930f..b90be07 100644
--- a/src/cmd/internal/obj/riscv/obj.go
+++ b/src/cmd/internal/obj/riscv/obj.go
@@ -324,7 +324,7 @@
 		off := p.From.Offset
 		to := p.To
 
-		low, high, err := split32BitImmediate(off)
+		low, high, err := Split32BitImmediate(off)
 		if err != nil {
 			ctxt.Diag("%v: constant %d too large: %v", p, off, err)
 		}
@@ -512,11 +512,11 @@
 	return val << (64 - bit) >> (64 - bit)
 }
 
-// split32BitImmediate splits a signed 32-bit immediate into a signed 20-bit
+// Split32BitImmediate splits a signed 32-bit immediate into a signed 20-bit
 // upper immediate and a signed 12-bit lower immediate to be added to the upper
 // result. For example, high may be used in LUI and low in a following ADDI to
 // generate a full 32-bit constant.
-func split32BitImmediate(imm int64) (low, high int64, err error) {
+func Split32BitImmediate(imm int64) (low, high int64, err error) {
 	if !immIFits(imm, 32) {
 		return 0, 0, fmt.Errorf("immediate does not fit in 32-bits: %d", imm)
 	}
@@ -909,6 +909,27 @@
 	return uint32(a.Offset)
 }
 
+func EncodeIImmediate(imm int64) (int64, error) {
+	if !immIFits(imm, 12) {
+		return 0, fmt.Errorf("immediate %#x does not fit in 12 bits", imm)
+	}
+	return imm << 20, nil
+}
+
+func EncodeSImmediate(imm int64) (int64, error) {
+	if !immIFits(imm, 12) {
+		return 0, fmt.Errorf("immediate %#x does not fit in 12 bits", imm)
+	}
+	return ((imm >> 5) << 25) | ((imm & 0x1f) << 7), nil
+}
+
+func EncodeUImmediate(imm int64) (int64, error) {
+	if !immUFits(imm, 20) {
+		return 0, fmt.Errorf("immediate %#x does not fit in 20 bits", imm)
+	}
+	return imm << 12, nil
+}
+
 type encoding struct {
 	encode   func(*obj.Prog) uint32 // encode returns the machine code for an *obj.Prog
 	validate func(*obj.Prog)        // validate validates an *obj.Prog, calling ctxt.Diag for any issues
diff --git a/src/cmd/link/internal/riscv64/asm.go b/src/cmd/link/internal/riscv64/asm.go
index 111ff9d..b089728 100644
--- a/src/cmd/link/internal/riscv64/asm.go
+++ b/src/cmd/link/internal/riscv64/asm.go
@@ -5,6 +5,7 @@
 package riscv64
 
 import (
+	"cmd/internal/obj/riscv"
 	"cmd/internal/objabi"
 	"cmd/internal/sys"
 	"cmd/link/internal/ld"
@@ -40,8 +41,53 @@
 }
 
 func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bool) {
-	// TODO(jsing): Implement.
-	log.Fatalf("archreloc not implemented")
+	switch r.Type {
+	case objabi.R_CALLRISCV:
+		// Nothing to do.
+		return val, true
+
+	case objabi.R_RISCV_PCREL_ITYPE, objabi.R_RISCV_PCREL_STYPE:
+		pc := s.Value + int64(r.Off)
+		off := ld.Symaddr(r.Sym) + r.Add - pc
+
+		// Generate AUIPC and second instruction immediates.
+		low, high, err := riscv.Split32BitImmediate(off)
+		if err != nil {
+			ld.Errorf(s, "R_RISCV_PCREL_ relocation does not fit in 32-bits: %d", off)
+		}
+
+		auipcImm, err := riscv.EncodeUImmediate(high)
+		if err != nil {
+			ld.Errorf(s, "cannot encode R_RISCV_PCREL_ AUIPC relocation offset for %s: %v", r.Sym.Name, err)
+		}
+
+		var secondImm, secondImmMask int64
+		switch r.Type {
+		case objabi.R_RISCV_PCREL_ITYPE:
+			secondImmMask = riscv.ITypeImmMask
+			secondImm, err = riscv.EncodeIImmediate(low)
+			if err != nil {
+				ld.Errorf(s, "cannot encode R_RISCV_PCREL_ITYPE I-type instruction relocation offset for %s: %v", r.Sym.Name, err)
+			}
+		case objabi.R_RISCV_PCREL_STYPE:
+			secondImmMask = riscv.STypeImmMask
+			secondImm, err = riscv.EncodeSImmediate(low)
+			if err != nil {
+				ld.Errorf(s, "cannot encode R_RISCV_PCREL_STYPE S-type instruction relocation offset for %s: %v", r.Sym.Name, err)
+			}
+		default:
+			panic(fmt.Sprintf("Unknown relocation type: %v", r.Type))
+		}
+
+		auipc := int64(uint32(val))
+		second := int64(uint32(val >> 32))
+
+		auipc = (auipc &^ riscv.UTypeImmMask) | int64(uint32(auipcImm))
+		second = (second &^ secondImmMask) | int64(uint32(secondImm))
+
+		return second<<32 | auipc, true
+	}
+
 	return val, false
 }