[PATCH v5] RISC-V: Add new relocation types for global array accesses with non-constant indices

Liqin Weng <[email protected]> Tue, 4 Aug 2026 15:52:02 +0800
Newsgroups gmane.comp.gnu.binutils
Message-ID <[email protected]>
From: wengliqin <[email protected]>

This patch introduces support for new RISC-V relocation types
(R_RISCV_BASE_IDX_*) to enable more efficient code generation for
global array accesses with non-constant array subscripts.

New Reloc includes :
R_RISCV_BASE_IDX_LO12_I/R_RISCV_BASE_IDX_LO12_S/R_RISCV_BASE_IDX_ADD

RISC-V PSABI specification: https://github.com/riscv-non-isa/riscv-elf-psabi-doc/pull/489

bfd/ChangeLog:

	* bfd-in2.h : Add new relocation type.
	* elfnn-riscv.c :  Define relocation information for new
          relocation type.  Convert the internal
          R_RISCV_BASE_IDX_GPREL_* relocations produced by relaxation
          back to their public equivalents before they can be written
          out, e.g. via --emit-relocs/-r.
	* elfxx-riscv.c : Add HOWTOs for new relocation type.  Add
          riscv_internal_reloc_map and riscv_elf_reloc_type_lookup to
          map each internal-only relaxed relocation type to the
          elf type that should be emitted in its place.
	* libbfd.h: Regenerate.
	* reloc.c: Document BFD_RELOC_RISCV_BASE_IDX_* type.

gas/ChangeLog:

	* config/tc-riscv.c: Add support for RISC-V Base IDX relocations
	* testsuite/gas/riscv/base-idx-add.s: New test for BASE_IDX*.
	* testsuite/gas/riscv/base-idx-add.d: New test.
	* testsuite/gas/riscv/base-idx-add.l: New test.

include/ChangeLog:

	* elf/riscv.h: Update relocation definitions for new BASE_IDX*
          Type.

ld/ChangeLog:

	* testsuite/ld-riscv-elf/base-idx-relax-gp.s: New test source
	  exercising base-idx relaxation to gp.
	* testsuite/ld-riscv-elf/base-idx-relax-x0.s: New test source
	  exercising base-idx relaxation to x0 only, covering both a
	  defined symbol (symL) and an undefined weak symbol referenced
	  with a non-zero addend (weakL+0x8).
	* testsuite/ld-riscv-elf/base-idx-relax-near.ld: New linker
	  script placing __global_pointer$ within gp-relaxable range.
	* testsuite/ld-riscv-elf/base-idx-relax-far.ld: New linker
	  script keeping data out of gp/x0 range so relaxation cannot
	  fire.
	* testsuite/ld-riscv-elf/base-idx-relax-x0.ld: New linker
	  script exercising the x0-only relaxation path.
	* testsuite/ld-riscv-elf/base-idx-relax-medlow-near.d,
	  testsuite/ld-riscv-elf/base-idx-relax-medlow-near-emit-relocs.d,
	  testsuite/ld-riscv-elf/base-idx-relax-medlow-far.d,
	  testsuite/ld-riscv-elf/base-idx-relax-medlow-far-emit-relocs.d,
	  testsuite/ld-riscv-elf/base-idx-relax-x0.d,
	  testsuite/ld-riscv-elf/base-idx-relax-x0-emit-relocs.d: New
	  dump tests covering base-idx relaxation to gp and x0 for both
	  defined and weak symbols, with and without --emit-relocs;
	  confirm the internal R_RISCV_BASE_IDX_GPREL_* relocation types
	  never leak into --emit-relocs output.
	* testsuite/ld-riscv-elf/ld-riscv-elf.exp: Register the new
	  base-idx-relax dump tests.

opcodes/ChangeLog:

	* riscv-opc.c : add new pattern for add/shaxadd/add.uw/shxadd.uw
	* riscv-dis.c (print_insn_args): Add a no-op case for the '1'
	relaxation-marker operand, so that disassembling an instruction
	whose only matching opcode-table row now includes it (e.g. add,
	shNadd) no longer hits "undefined modifier" -- the marker
	carries no encoding bits, so nothing is printed for it.
---
This Patch update:
1. clang-format
2. add the weak symbol tests && remove the implement add x0, x0, zero(weak symbol
may relax gp, so remove 'add a0, a0, zero' will be implemented in the new patch)
3. Convert the internal 'R_RISCV_BASE_IDX_GPREL_*' relocations produced
by relaxation back to their elf equivalents before they can be written
out, eg. via --emit-relocs/-r.

 bfd/bfd-in2.h                                 |   3 +
 bfd/elfnn-riscv.c                             |  92 +++++++++++-
 bfd/elfxx-riscv.c                             | 138 +++++++++++++++++-
 bfd/libbfd.h                                  |   3 +
 bfd/reloc.c                                   |   6 +
 gas/config/tc-riscv.c                         |  34 ++++-
 gas/testsuite/gas/riscv/base-idx-add.d        |   3 +
 gas/testsuite/gas/riscv/base-idx-add.l        |  10 ++
 gas/testsuite/gas/riscv/base-idx-add.s        |  19 +++
 include/elf/riscv.h                           |   6 +
 .../ld-riscv-elf/base-idx-relax-far.ld        |  21 +++
 ld/testsuite/ld-riscv-elf/base-idx-relax-gp.s |  36 +++++
 .../base-idx-relax-medlow-far-emit-relocs.d   |  65 +++++++++
 .../ld-riscv-elf/base-idx-relax-medlow-far.d  |  29 ++++
 .../base-idx-relax-medlow-near-emit-relocs.d  |  63 ++++++++
 .../ld-riscv-elf/base-idx-relax-medlow-near.d |  27 ++++
 .../ld-riscv-elf/base-idx-relax-near.ld       |  22 +++
 .../base-idx-relax-x0-emit-relocs.d           |  63 ++++++++
 ld/testsuite/ld-riscv-elf/base-idx-relax-x0.d |  27 ++++
 .../ld-riscv-elf/base-idx-relax-x0.ld         |  28 ++++
 ld/testsuite/ld-riscv-elf/base-idx-relax-x0.s |  34 +++++
 ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp    |   6 +
 opcodes/riscv-dis.c                           |   6 +
 opcodes/riscv-opc.c                           |  20 +--
 24 files changed, 743 insertions(+), 18 deletions(-)
 create mode 100644 gas/testsuite/gas/riscv/base-idx-add.d
 create mode 100644 gas/testsuite/gas/riscv/base-idx-add.l
 create mode 100644 gas/testsuite/gas/riscv/base-idx-add.s
 create mode 100644 ld/testsuite/ld-riscv-elf/base-idx-relax-far.ld
 create mode 100644 ld/testsuite/ld-riscv-elf/base-idx-relax-gp.s
 create mode 100644 ld/testsuite/ld-riscv-elf/base-idx-relax-medlow-far-emit-relocs.d
 create mode 100644 ld/testsuite/ld-riscv-elf/base-idx-relax-medlow-far.d
 create mode 100644 ld/testsuite/ld-riscv-elf/base-idx-relax-medlow-near-emit-relocs.d
 create mode 100644 ld/testsuite/ld-riscv-elf/base-idx-relax-medlow-near.d
 create mode 100644 ld/testsuite/ld-riscv-elf/base-idx-relax-near.ld
 create mode 100644 ld/testsuite/ld-riscv-elf/base-idx-relax-x0-emit-relocs.d
 create mode 100644 ld/testsuite/ld-riscv-elf/base-idx-relax-x0.d
 create mode 100644 ld/testsuite/ld-riscv-elf/base-idx-relax-x0.ld
 create mode 100644 ld/testsuite/ld-riscv-elf/base-idx-relax-x0.s

diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h
index d4e4f8e4375..71044bcd3f1 100644
--- a/bfd/bfd-in2.h
+++ b/bfd/bfd-in2.h
@@ -5351,6 +5351,9 @@ enum bfd_reloc_code_real
   BFD_RELOC_RISCV_TLSDESC_LOAD_LO12,
   BFD_RELOC_RISCV_TLSDESC_ADD_LO12,
   BFD_RELOC_RISCV_TLSDESC_CALL,
+  BFD_RELOC_RISCV_BASE_IDX_LO12_I,
+  BFD_RELOC_RISCV_BASE_IDX_LO12_S,
+  BFD_RELOC_RISCV_BASE_IDX_ADD,
   BFD_RELOC_RISCV_ALIGN,
   BFD_RELOC_RISCV_RVC_BRANCH,
   BFD_RELOC_RISCV_RVC_JUMP,
diff --git a/bfd/elfnn-riscv.c b/bfd/elfnn-riscv.c
index d3963ec9a58..f8ed892edf3 100644
--- a/bfd/elfnn-riscv.c
+++ b/bfd/elfnn-riscv.c
@@ -1999,7 +1999,9 @@ perform_relocation (const reloc_howto_type *howto,
       break;
 
     case R_RISCV_LO12_I:
+    case R_RISCV_BASE_IDX_LO12_I:
     case R_RISCV_GPREL_I:
+    case R_RISCV_BASE_IDX_GPREL_I:
     case R_RISCV_TPREL_LO12_I:
     case R_RISCV_TPREL_I:
     case R_RISCV_PCREL_LO12_I:
@@ -2009,7 +2011,9 @@ perform_relocation (const reloc_howto_type *howto,
       break;
 
     case R_RISCV_LO12_S:
+    case R_RISCV_BASE_IDX_LO12_S:
     case R_RISCV_GPREL_S:
+    case R_RISCV_BASE_IDX_GPREL_S:
     case R_RISCV_TPREL_LO12_S:
     case R_RISCV_TPREL_S:
     case R_RISCV_PCREL_LO12_S:
@@ -2130,6 +2134,8 @@ perform_relocation (const reloc_howto_type *howto,
     case R_RISCV_32_PCREL:
     case R_RISCV_TLS_DTPREL32:
     case R_RISCV_TLS_DTPREL64:
+    case R_RISCV_BASE_IDX_ADD:
+    case R_RISCV_BASE_IDX_GPREL_ADD:
       break;
 
     case R_RISCV_DELETE:
@@ -2389,6 +2395,21 @@ riscv_resolve_pcrel_lo_relocs (riscv_pcrel_relocs *p)
   return true;
 }
 
+static void
+change_internal_relocs_to_elf_relocs (Elf_Internal_Rela *relocs,
+				      Elf_Internal_Rela *relend)
+{
+  Elf_Internal_Rela *rel;
+
+  for (rel = relocs; rel < relend; rel++)
+    {
+      unsigned int r_type = ELFNN_R_TYPE (rel->r_info);
+      unsigned int elf_type = riscv_elf_reloc_type_lookup (r_type);
+      if (elf_type != r_type)
+	rel->r_info = ELFNN_R_INFO (ELFNN_R_SYM (rel->r_info), elf_type);
+    }
+}
+
 /* Relocate a RISC-V ELF section.
 
    The RELOCATE_SECTION function is called by the new ELF backend linker
@@ -2722,6 +2743,9 @@ riscv_elf_relocate_section (bfd *output_bfd,
 	      case R_RISCV_HI20:
 	      case R_RISCV_LO12_I:
 	      case R_RISCV_LO12_S:
+	      case R_RISCV_BASE_IDX_LO12_I:
+	      case R_RISCV_BASE_IDX_LO12_S:
+	      case R_RISCV_BASE_IDX_ADD:
 		goto do_relocation;
 
 	      case R_RISCV_PCREL_HI20:
@@ -2788,6 +2812,9 @@ riscv_elf_relocate_section (bfd *output_bfd,
 	case R_RISCV_RVC_LUI:
 	case R_RISCV_LO12_I:
 	case R_RISCV_LO12_S:
+	case R_RISCV_BASE_IDX_LO12_I:
+	case R_RISCV_BASE_IDX_LO12_S:
+	case R_RISCV_BASE_IDX_ADD:
 	case R_RISCV_SET6:
 	case R_RISCV_SET8:
 	case R_RISCV_SET16:
@@ -3089,6 +3116,48 @@ riscv_elf_relocate_section (bfd *output_bfd,
 	    break;
 	  }
 
+	case R_RISCV_BASE_IDX_GPREL_I:
+	case R_RISCV_BASE_IDX_GPREL_S:
+	  {
+	    /* After relaxation, the ld/st uses (gp + index) as its base
+	       register, so the immediate encodes the symbol's displacement
+	       from gp.  perform_relocation will encode the final value
+	       from (relocation + r_addend).  */
+	    bfd_vma gp = riscv_global_pointer_value (info);
+	    bfd_vma disp = relocation + rel->r_addend - gp;
+	    bool x0_base = VALID_ITYPE_IMM (relocation + rel->r_addend);
+	    bool gp_base = (r_type == R_RISCV_BASE_IDX_GPREL_I)
+			     ? VALID_ITYPE_IMM (disp)
+			     : VALID_STYPE_IMM (disp);
+	    if (!x0_base)
+	      {
+		/* Only when gp is actually used as the base do we need to
+		   express the immediate as a displacement from gp.  */
+		if (gp_base)
+		  rel->r_addend -= gp;
+		else
+		  r = bfd_reloc_overflow;
+	      }
+	    break;
+	  }
+
+	case R_RISCV_BASE_IDX_GPREL_ADD:
+	  {
+	    /* Rewrite rs2 of the add/shXadd instruction to use gp or x0 as the
+	       base register after relaxation.  The same encoding change
+	       applies to both add and shXadd — the opcode itself is left
+	       intact.  */
+	    bfd_vma insn = bfd_getl32 (contents + rel->r_offset);
+	    bool x0_base = VALID_ITYPE_IMM (relocation + rel->r_addend);
+	    insn &= ~(OP_MASK_RS2 << OP_SH_RS2);
+	    if (!x0_base)
+	      {
+		insn |= X_GP << OP_SH_RS2;
+	      }
+	    bfd_putl32 (insn, contents + rel->r_offset);
+	    break;
+	  }
+
 	case R_RISCV_PCREL_HI20:
 	  absolute = riscv_zero_pcrel_hi_reloc (rel, info, pc, &relocation,
 						contents, howto);
@@ -3434,6 +3503,8 @@ riscv_elf_relocate_section (bfd *output_bfd,
       goto out;
     }
 
+  change_internal_relocs_to_elf_relocs (relocs, relend);
+
   ret = riscv_resolve_pcrel_lo_relocs (&pcrel_relocs);
  out:
   riscv_free_pcrel_relocs (&pcrel_relocs);
@@ -5148,6 +5219,18 @@ _bfd_riscv_relax_lui (bfd *abfd,
 	  rel->r_info = ELFNN_R_INFO (sym, R_RISCV_GPREL_S);
 	  return true;
 
+	case R_RISCV_BASE_IDX_LO12_I:
+	  rel->r_info = ELFNN_R_INFO (sym, R_RISCV_BASE_IDX_GPREL_I);
+	  return true;
+
+	case R_RISCV_BASE_IDX_LO12_S:
+	  rel->r_info = ELFNN_R_INFO (sym, R_RISCV_BASE_IDX_GPREL_S);
+	  return true;
+
+	case R_RISCV_BASE_IDX_ADD:
+	  rel->r_info = ELFNN_R_INFO (sym, R_RISCV_BASE_IDX_GPREL_ADD);
+	  return true;
+
 	case R_RISCV_HI20:
 	  /* Delete unnecessary LUI and reuse the reloc.  */
 	  *again = true;
@@ -5160,7 +5243,7 @@ _bfd_riscv_relax_lui (bfd *abfd,
     }
 
   /* Can we relax LUI to C.LUI?  Alignment might move the section forward;
-     account for this assuming page alignment at worst. In the presence of 
+     account for this assuming page alignment at worst. In the presence of
      RELRO segment the linker aligns it by one page size, therefore sections
      after the segment can be moved more than one page. */
 
@@ -5537,9 +5620,10 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec,
 	  if (type == R_RISCV_CALL
 	      || type == R_RISCV_CALL_PLT)
 	    relax_func = _bfd_riscv_relax_call;
-	  else if (type == R_RISCV_HI20
-		   || type == R_RISCV_LO12_I
-		   || type == R_RISCV_LO12_S)
+	  else if (type == R_RISCV_HI20 || type == R_RISCV_LO12_I
+		   || type == R_RISCV_LO12_S || type == R_RISCV_BASE_IDX_LO12_I
+		   || type == R_RISCV_BASE_IDX_LO12_S
+		   || type == R_RISCV_BASE_IDX_ADD)
 	    relax_func = _bfd_riscv_relax_lui;
 	  else if (type == R_RISCV_TPREL_HI20
 		   || type == R_RISCV_TPREL_ADD
diff --git a/bfd/elfxx-riscv.c b/bfd/elfxx-riscv.c
index ee962b4f6f1..0db219b5521 100644
--- a/bfd/elfxx-riscv.c
+++ b/bfd/elfxx-riscv.c
@@ -876,7 +876,63 @@ static const reloc_howto_type howto_table[] =
 	 false,				/* partial_inplace */
 	 0,				/* src_mask */
 	 ENCODE_ITYPE_IMM (-1U),	/* dst_mask */
-	 false) 			/* pcrel_offset */
+	 false),			/* pcrel_offset */
+
+  /* Reserved slots 66-76.  */
+  EMPTY_HOWTO (66),
+  EMPTY_HOWTO (67),
+  EMPTY_HOWTO (68),
+  EMPTY_HOWTO (69),
+  EMPTY_HOWTO (70),
+  EMPTY_HOWTO (71),
+  EMPTY_HOWTO (72),
+  EMPTY_HOWTO (73),
+  EMPTY_HOWTO (74),
+  EMPTY_HOWTO (75),
+  EMPTY_HOWTO (76),
+
+  /* Global array accesses with non-constant subscript (base + index).  */
+  HOWTO (R_RISCV_BASE_IDX_LO12_I,   /* type */
+	 0,			    /* rightshift */
+	 4,			    /* size */
+	 32,			    /* bitsize */
+	 false,			    /* pc_relative */
+	 0,			    /* bitpos */
+	 complain_overflow_dont,    /* complain_on_overflow */
+	 bfd_elf_generic_reloc,	    /* special_function */
+	 "R_RISCV_BASE_IDX_LO12_I", /* name */
+	 false,			    /* partial_inplace */
+	 0,			    /* src_mask */
+	 ENCODE_ITYPE_IMM (-1U),    /* dst_mask */
+	 false),		    /* pcrel_offset */
+
+  HOWTO (R_RISCV_BASE_IDX_LO12_S,   /* type */
+	 0,			    /* rightshift */
+	 4,			    /* size */
+	 32,			    /* bitsize */
+	 false,			    /* pc_relative */
+	 0,			    /* bitpos */
+	 complain_overflow_dont,    /* complain_on_overflow */
+	 bfd_elf_generic_reloc,	    /* special_function */
+	 "R_RISCV_BASE_IDX_LO12_S", /* name */
+	 false,			    /* partial_inplace */
+	 0,			    /* src_mask */
+	 ENCODE_STYPE_IMM (-1U),    /* dst_mask */
+	 false),		    /* pcrel_offset */
+
+  HOWTO (R_RISCV_BASE_IDX_ADD,	 /* type */
+	 0,			 /* rightshift */
+	 4,			 /* size */
+	 32,			 /* bitsize */
+	 false,			 /* pc_relative */
+	 0,			 /* bitpos */
+	 complain_overflow_dont, /* complain_on_overflow */
+	 bfd_elf_generic_reloc,	 /* special_function */
+	 "R_RISCV_BASE_IDX_ADD", /* name */
+	 false,			 /* partial_inplace */
+	 0,			 /* src_mask */
+	 0,			 /* dst_mask */
+	 false),		 /* pcrel_offset */
 };
 
 static const reloc_howto_type howto_table_internal[] =
@@ -961,6 +1017,50 @@ static const reloc_howto_type howto_table_internal[] =
 	 0,				/* src_mask */
 	 ENCODE_STYPE_IMM (-1U),	/* dst_mask */
 	 false),			/* pcrel_offset */
+
+  /* Global array accesses with non-constant subscript (base + index),
+     after relaxation.  */
+  HOWTO (R_RISCV_BASE_IDX_GPREL_I,   /* type */
+	 0,			     /* rightshift */
+	 4,			     /* size */
+	 32,			     /* bitsize */
+	 false,			     /* pc_relative */
+	 0,			     /* bitpos */
+	 complain_overflow_dont,     /* complain_on_overflow */
+	 bfd_elf_generic_reloc,	     /* special_function */
+	 "R_RISCV_BASE_IDX_GPREL_I", /* name */
+	 false,			     /* partial_inplace */
+	 0,			     /* src_mask */
+	 ENCODE_ITYPE_IMM (-1U),     /* dst_mask */
+	 false),		     /* pcrel_offset */
+
+  HOWTO (R_RISCV_BASE_IDX_GPREL_S,   /* type */
+	 0,			     /* rightshift */
+	 4,			     /* size */
+	 32,			     /* bitsize */
+	 false,			     /* pc_relative */
+	 0,			     /* bitpos */
+	 complain_overflow_dont,     /* complain_on_overflow */
+	 bfd_elf_generic_reloc,	     /* special_function */
+	 "R_RISCV_BASE_IDX_GPREL_S", /* name */
+	 false,			     /* partial_inplace */
+	 0,			     /* src_mask */
+	 ENCODE_STYPE_IMM (-1U),     /* dst_mask */
+	 false),		     /* pcrel_offset */
+
+  HOWTO (R_RISCV_BASE_IDX_GPREL_ADD,   /* type */
+	 0,			       /* rightshift */
+	 4,			       /* size */
+	 32,			       /* bitsize */
+	 false,			       /* pc_relative */
+	 0,			       /* bitpos */
+	 complain_overflow_dont,       /* complain_on_overflow */
+	 bfd_elf_generic_reloc,	       /* special_function */
+	 "R_RISCV_BASE_IDX_GPREL_ADD", /* name */
+	 false,			       /* partial_inplace */
+	 0,			       /* src_mask */
+	 0,			       /* dst_mask */
+	 false)
 };
 
 /* A mapping from BFD reloc types to RISC-V ELF reloc types.  */
@@ -1023,6 +1123,26 @@ static const struct elf_reloc_map riscv_reloc_map[] =
   { BFD_RELOC_RISCV_32_PCREL, R_RISCV_32_PCREL },
   { BFD_RELOC_RISCV_SET_ULEB128, R_RISCV_SET_ULEB128 },
   { BFD_RELOC_RISCV_SUB_ULEB128, R_RISCV_SUB_ULEB128 },
+  { BFD_RELOC_RISCV_BASE_IDX_LO12_I, R_RISCV_BASE_IDX_LO12_I },
+  { BFD_RELOC_RISCV_BASE_IDX_LO12_S, R_RISCV_BASE_IDX_LO12_S },
+  { BFD_RELOC_RISCV_BASE_IDX_ADD, R_RISCV_BASE_IDX_ADD },
+};
+
+/* A mapping from internal-only relocation types (produced by the linker's
+   relaxation passes; numbered after R_RISCV_max and never valid in an
+   object file) to the elf relocation type that should be substituted
+   before the type is written out, e.g. for --emit-relocs/-r output.  */
+struct internal_reloc_map
+{
+  unsigned int internal_type;
+  enum elf_riscv_reloc_type elf_val;
+};
+
+/* TODO: add another internal relocation type  */
+static const struct internal_reloc_map riscv_internal_reloc_map[] = {
+    { R_RISCV_BASE_IDX_GPREL_I, R_RISCV_BASE_IDX_LO12_I },
+    { R_RISCV_BASE_IDX_GPREL_S, R_RISCV_BASE_IDX_LO12_S },
+    { R_RISCV_BASE_IDX_GPREL_ADD, R_RISCV_BASE_IDX_ADD },
 };
 
 struct riscv_profiles
@@ -1059,6 +1179,22 @@ riscv_reloc_name_lookup (bfd *abfd ATTRIBUTE_UNUSED, const char *r_name)
   return NULL;
 }
 
+/* If R_TYPE is one of the internal-only relocation types produced by
+   relaxation, return the elf relocation type that should be emitted
+   in its place.  Otherwise return R_TYPE unchanged.  */
+
+unsigned int
+riscv_elf_reloc_type_lookup (unsigned int r_type)
+{
+  unsigned int i;
+
+  for (i = 0; i < ARRAY_SIZE (riscv_internal_reloc_map); i++)
+    if (riscv_internal_reloc_map[i].internal_type == r_type)
+      return riscv_internal_reloc_map[i].elf_val;
+
+  return r_type;
+}
+
 reloc_howto_type *
 riscv_elf_rtype_to_howto (bfd *abfd, unsigned int r_type)
 {
diff --git a/bfd/libbfd.h b/bfd/libbfd.h
index a27134dcd78..ef4f96a1747 100644
--- a/bfd/libbfd.h
+++ b/bfd/libbfd.h
@@ -2362,6 +2362,9 @@ static const char *const bfd_reloc_code_real_names[] = { "@@uninitialized@@",
   "BFD_RELOC_RISCV_TLSDESC_LOAD_LO12",
   "BFD_RELOC_RISCV_TLSDESC_ADD_LO12",
   "BFD_RELOC_RISCV_TLSDESC_CALL",
+  "BFD_RELOC_RISCV_BASE_IDX_LO12_I",
+  "BFD_RELOC_RISCV_BASE_IDX_LO12_S",
+  "BFD_RELOC_RISCV_BASE_IDX_ADD",
   "BFD_RELOC_RISCV_ALIGN",
   "BFD_RELOC_RISCV_RVC_BRANCH",
   "BFD_RELOC_RISCV_RVC_JUMP",
diff --git a/bfd/reloc.c b/bfd/reloc.c
index 98343696a33..6f6ff98c4a5 100644
--- a/bfd/reloc.c
+++ b/bfd/reloc.c
@@ -4861,6 +4861,12 @@ ENUMX
   BFD_RELOC_RISCV_TLSDESC_ADD_LO12
 ENUMX
   BFD_RELOC_RISCV_TLSDESC_CALL
+ENUMX
+  BFD_RELOC_RISCV_BASE_IDX_LO12_I
+ENUMX
+  BFD_RELOC_RISCV_BASE_IDX_LO12_S
+ENUMX
+  BFD_RELOC_RISCV_BASE_IDX_ADD
 ENUMX
   BFD_RELOC_RISCV_ALIGN
 ENUMX
diff --git a/gas/config/tc-riscv.c b/gas/config/tc-riscv.c
index 6bcf53832a0..11d9db0f977 100644
--- a/gas/config/tc-riscv.c
+++ b/gas/config/tc-riscv.c
@@ -1953,9 +1953,11 @@ riscv_apply_const_reloc (bfd_reloc_code_real_type reloc_type, bfd_vma value)
       return ENCODE_UTYPE_IMM (RISCV_CONST_HIGH_PART (value));
 
     case BFD_RELOC_RISCV_LO12_S:
+    case BFD_RELOC_RISCV_BASE_IDX_LO12_S:
       return ENCODE_STYPE_IMM (value);
 
     case BFD_RELOC_RISCV_LO12_I:
+    case BFD_RELOC_RISCV_BASE_IDX_LO12_I:
       return ENCODE_ITYPE_IMM (value);
 
     default:
@@ -2022,7 +2024,8 @@ append_insn (struct riscv_cl_insn *ip, expressionS *address_expr,
   if (reloc_type == BFD_RELOC_RISCV_HI20
       || reloc_type == BFD_RELOC_RISCV_PCREL_HI20
       || reloc_type == BFD_RELOC_RISCV_TPREL_HI20
-      || reloc_type == BFD_RELOC_RISCV_TPREL_ADD)
+      || reloc_type == BFD_RELOC_RISCV_TPREL_ADD
+      || reloc_type == BFD_RELOC_RISCV_BASE_IDX_ADD)
     {
       frag_wane (frag_now);
       frag_new (0);
@@ -2455,6 +2458,7 @@ static const struct percent_op_match percent_op_utype[] =
 static const struct percent_op_match percent_op_itype[] =
 {
   {"lo", BFD_RELOC_RISCV_LO12_I},
+  {"base_idx_lo", BFD_RELOC_RISCV_BASE_IDX_LO12_I},
   {"tprel_lo", BFD_RELOC_RISCV_TPREL_LO12_I},
   {"pcrel_lo", BFD_RELOC_RISCV_PCREL_LO12_I},
   {"tlsdesc_load_lo", BFD_RELOC_RISCV_TLSDESC_LOAD_LO12},
@@ -2465,6 +2469,7 @@ static const struct percent_op_match percent_op_itype[] =
 static const struct percent_op_match percent_op_stype[] =
 {
   {"lo", BFD_RELOC_RISCV_LO12_S},
+  {"base_idx_lo", BFD_RELOC_RISCV_BASE_IDX_LO12_S},
   {"tprel_lo", BFD_RELOC_RISCV_TPREL_LO12_S},
   {"pcrel_lo", BFD_RELOC_RISCV_PCREL_LO12_S},
   {0, 0}
@@ -2474,6 +2479,7 @@ static const struct percent_op_match percent_op_relax_only[] =
 {
   {"tlsdesc_call", BFD_RELOC_RISCV_TLSDESC_CALL},
   {"tprel_add", BFD_RELOC_RISCV_TPREL_ADD},
+  {"base_idx_add", BFD_RELOC_RISCV_BASE_IDX_ADD},
   {0, 0}
 };
 
@@ -3644,9 +3650,23 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr,
 	      *imm_reloc = BFD_RELOC_RISCV_LO12_I;
 	      goto load_store;
 	    case '1':
-	      /* This is used for TLS relocations that acts as relaxation
-		 markers and do not change the instruction encoding,
-		 i.e. %tprel_add and %tlsdesc_call.  */
+	      /* This operand is a relaxation marker that does not change
+		 the instruction encoding, e.g. %tprel_add, %tlsdesc_call
+		 and %base_idx_add.  It is mandatory when a literal ','
+		 precedes it in the operand string (e.g. "d,s,1" for
+		 jalr's %tlsdesc_call), in which case that ',' is already
+		 consumed by the general ',' handling above and *asarg
+		 points straight at the marker text.  It is optional when
+		 it directly follows another operand letter with no
+		 separating ',' in the operand string (e.g. "d,s,t1" on
+		 add/shNadd/add.uw for %base_idx_add), in which case we
+		 must recognise here whether it was actually given --
+		 mirrors the "optional vector mask" handling of 'Vm'
+		 above.  */
+	      if (*asarg == '\0')
+		continue;
+	      if (*asarg == ',')
+		++asarg;
 	      p = percent_op_relax_only;
 	      goto alu_op;
 	    case '0': /* AMO displacement, which must be zero.  */
@@ -4687,6 +4707,8 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg)
     case BFD_RELOC_RISCV_HI20:
     case BFD_RELOC_RISCV_LO12_I:
     case BFD_RELOC_RISCV_LO12_S:
+    case BFD_RELOC_RISCV_BASE_IDX_LO12_I:
+    case BFD_RELOC_RISCV_BASE_IDX_LO12_S:
       bfd_putl32 (riscv_apply_const_reloc (fixP->fx_r_type, *valP)
 		  | bfd_getl32 (buf), buf);
       if (fixP->fx_addsy == NULL)
@@ -4694,6 +4716,10 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg)
       relaxable = true;
       break;
 
+    case BFD_RELOC_RISCV_BASE_IDX_ADD:
+      relaxable = true;
+      break;
+
     case BFD_RELOC_RISCV_GOT_HI20:
       /* R_RISCV_GOT_HI20 and the following R_RISCV_LO12_I are relaxable
 	 only if it is created as a result of la or lga assembler macros. */
diff --git a/gas/testsuite/gas/riscv/base-idx-add.d b/gas/testsuite/gas/riscv/base-idx-add.d
new file mode 100644
index 00000000000..488ffbc1956
--- /dev/null
+++ b/gas/testsuite/gas/riscv/base-idx-add.d
@@ -0,0 +1,3 @@
+#as: -march=rv64imafdc_zbb_zba
+#source: base-idx-add.s
+#error_output: base-idx-add.l
diff --git a/gas/testsuite/gas/riscv/base-idx-add.l b/gas/testsuite/gas/riscv/base-idx-add.l
new file mode 100644
index 00000000000..8321b40ccc6
--- /dev/null
+++ b/gas/testsuite/gas/riscv/base-idx-add.l
@@ -0,0 +1,10 @@
+.*: Assembler messages:
+.*: Error: illegal operands `amoadd.w x8,x9,%base_idx_add\(sym\)\(x10\)'
+.*: Error: illegal operands `add a5,a5,a0,0'
+.*: Error: illegal operands `sh1add a0,a0,a5,0'
+.*: Error: illegal operands `sh1add.uw a0,a0,a5,0'
+.*: Error: illegal operands `sh2add a0,a0,a5,0'
+.*: Error: illegal operands `sh2add.uw a0,a0,a5,0'
+.*: Error: illegal operands `sh3add a0,a0,a5,0'
+.*: Error: illegal operands `sh3add.uw a0,a0,a5,0'
+.*: Error: illegal operands `add.uw a0,a0,a5,0'
diff --git a/gas/testsuite/gas/riscv/base-idx-add.s b/gas/testsuite/gas/riscv/base-idx-add.s
new file mode 100644
index 00000000000..47d1b3b5aa3
--- /dev/null
+++ b/gas/testsuite/gas/riscv/base-idx-add.s
@@ -0,0 +1,19 @@
+.option arch, +a
+    # Don't allow base_idx_add in amoadd.
+	amoadd.w x8,x9,%base_idx_add(sym)(x10)
+	# Do require base_idx_add in 4-operand add.
+	add		a5,a5,a0,0
+	sh1add		a0,a0,a5,0
+	sh1add.uw	a0,a0,a5,0
+	sh2add		a0,a0,a5,0
+	sh2add.uw	a0,a0,a5,0
+	sh3add		a0,a0,a5,0
+	sh3add.uw	a0,a0,a5,0
+	add.uw		a0,a0,a5,0
+	.globl	sym
+	.section	.tbss,"awT",@nobits
+	.align	2
+	.type	sym, @object
+	.size	sym, 4
+sym:
+	.zero	4
diff --git a/include/elf/riscv.h b/include/elf/riscv.h
index f6ca3d4acd8..a19bee4b684 100644
--- a/include/elf/riscv.h
+++ b/include/elf/riscv.h
@@ -95,6 +95,9 @@ START_RELOC_NUMBERS (elf_riscv_reloc_type)
   RELOC_NUMBER (R_RISCV_TLSDESC_LOAD_LO12, 63)
   RELOC_NUMBER (R_RISCV_TLSDESC_ADD_LO12, 64)
   RELOC_NUMBER (R_RISCV_TLSDESC_CALL, 65)
+  RELOC_NUMBER (R_RISCV_BASE_IDX_LO12_I, 77)
+  RELOC_NUMBER (R_RISCV_BASE_IDX_LO12_S, 78)
+  RELOC_NUMBER (R_RISCV_BASE_IDX_ADD, 79)
 END_RELOC_NUMBERS (R_RISCV_max)
 
 /* Internal relocations used exclusively by the relaxation pass.  */
@@ -105,6 +108,9 @@ END_RELOC_NUMBERS (R_RISCV_max)
 #define R_RISCV_GPREL_S           (R_RISCV_max + 4)
 #define R_RISCV_TPREL_I           (R_RISCV_max + 5)
 #define R_RISCV_TPREL_S           (R_RISCV_max + 6)
+#define R_RISCV_BASE_IDX_GPREL_I (R_RISCV_max + 7)
+#define R_RISCV_BASE_IDX_GPREL_S (R_RISCV_max + 8)
+#define R_RISCV_BASE_IDX_GPREL_ADD (R_RISCV_max + 9)
 
 /* Processor specific flags for the ELF header e_flags field.  */
 
diff --git a/ld/testsuite/ld-riscv-elf/base-idx-relax-far.ld b/ld/testsuite/ld-riscv-elf/base-idx-relax-far.ld
new file mode 100644
index 00000000000..123f9c4cbe9
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/base-idx-relax-far.ld
@@ -0,0 +1,21 @@
+/* Data symbols reachable by lui/auipc+addi but far from __global_pointer$,
+   so BASE_IDX gp-relaxation cannot fire.  The original instruction sequence
+   is preserved.  */
+
+ENTRY(_start)
+MEMORY
+{
+	rom (rx)  : ORIGIN = 0x10000, LENGTH = 0x1000
+	ram (!rx) : ORIGIN = 0x100000, LENGTH = 0x1000
+}
+SECTIONS {
+	.text : {
+		*(.text*)
+	} >rom
+
+	.data : {
+		*(.data*)
+	} >ram
+
+	__global_pointer$ = 0x20000;
+}
diff --git a/ld/testsuite/ld-riscv-elf/base-idx-relax-gp.s b/ld/testsuite/ld-riscv-elf/base-idx-relax-gp.s
new file mode 100644
index 00000000000..b157b0b79bb
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/base-idx-relax-gp.s
@@ -0,0 +1,36 @@
+	.text
+	.global _start
+_start:
+
+.ifdef __medlow__
+	lui	        a1, %hi(symL)
+	add	        a2, a0, a1, %base_idx_add(symL)
+	add.uw	        a2, a0, a1, %base_idx_add(symL)
+	sh1add	        a2, a0, a1, %base_idx_add(symL)
+	sh2add	        a2, a0, a1, %base_idx_add(symL)
+	sh3add	        a2, a0, a1, %base_idx_add(symL)
+	sh3add.uw	a2, a0, a1, %base_idx_add(symL)
+	lbu     	a3, %base_idx_lo(symL)(a2)
+	sb      	a3, %base_idx_lo(symL)(a2)
+
+	lui	        a1, %hi(weakL)
+	add	        a2, a0, a1, %base_idx_add(weakL)
+	add.uw	        a2, a0, a1, %base_idx_add(weakL)
+	sh1add	        a2, a0, a1, %base_idx_add(weakL)
+	sh2add	        a2, a0, a1, %base_idx_add(weakL)
+	sh3add	        a2, a0, a1, %base_idx_add(weakL)
+	sh3add.uw	a2, a0, a1, %base_idx_add(weakL)
+	lbu     	a3, %base_idx_lo(weakL)(a2)
+	sb      	a3, %base_idx_lo(weakL)(a2)
+.endif
+
+	.size	_start, .-_start
+
+	.data
+	.global symL
+symL:
+	.dword	0x1111222233334444
+
+	.weak	weakL
+weakL:
+	.dword	0x5555666677778888
diff --git a/ld/testsuite/ld-riscv-elf/base-idx-relax-medlow-far-emit-relocs.d b/ld/testsuite/ld-riscv-elf/base-idx-relax-medlow-far-emit-relocs.d
new file mode 100644
index 00000000000..64a2aeb65cc
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/base-idx-relax-medlow-far-emit-relocs.d
@@ -0,0 +1,65 @@
+#source: base-idx-relax-gp.s
+#as: -march=rv64i_zba -mabi=lp64 --defsym __medlow__=1
+#ld: -Tbase-idx-relax-far.ld -melf64lriscv --relax --emit-relocs
+#objdump: -dr -Mno-aliases
+
+.*:[ 	]+file format .*
+
+
+Disassembly of section .text:
+
+0+10000 <_start>:
+[ 	]+[0-9a-f]+:[ 	]+001005b7[ 	]+lui[ 	]+a1,0x100
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_HI20[ 	]+symL
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_RELAX[ 	]+\*ABS\*
+[ 	]+[0-9a-f]+:[ 	]+00b50633[ 	]+add[ 	]+a2,a0,a1
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_BASE_IDX_ADD[ 	]+symL
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_RELAX[ 	]+\*ABS\*
+[ 	]+[0-9a-f]+:[ 	]+08b5063b[ 	]+add\.uw[ 	]+a2,a0,a1
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_BASE_IDX_ADD[ 	]+symL
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_RELAX[ 	]+\*ABS\*
+[ 	]+[0-9a-f]+:[ 	]+20b52633[ 	]+sh1add[ 	]+a2,a0,a1
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_BASE_IDX_ADD[ 	]+symL
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_RELAX[ 	]+\*ABS\*
+[ 	]+[0-9a-f]+:[ 	]+20b54633[ 	]+sh2add[ 	]+a2,a0,a1
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_BASE_IDX_ADD[ 	]+symL
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_RELAX[ 	]+\*ABS\*
+[ 	]+[0-9a-f]+:[ 	]+20b56633[ 	]+sh3add[ 	]+a2,a0,a1
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_BASE_IDX_ADD[ 	]+symL
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_RELAX[ 	]+\*ABS\*
+[ 	]+[0-9a-f]+:[ 	]+20b5663b[ 	]+sh3add\.uw[ 	]+a2,a0,a1
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_BASE_IDX_ADD[ 	]+symL
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_RELAX[ 	]+\*ABS\*
+[ 	]+[0-9a-f]+:[ 	]+00064683[ 	]+lbu[ 	]+a3,0\(a2\)
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_BASE_IDX_LO12_I[ 	]+symL
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_RELAX[ 	]+\*ABS\*
+[ 	]+[0-9a-f]+:[ 	]+00d60023[ 	]+sb[ 	]+a3,0\(a2\)
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_BASE_IDX_LO12_S[ 	]+symL
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_RELAX[ 	]+\*ABS\*
+[ 	]+[0-9a-f]+:[ 	]+001005b7[ 	]+lui[ 	]+a1,0x100
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_HI20[ 	]+weakL
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_RELAX[ 	]+\*ABS\*
+[ 	]+[0-9a-f]+:[ 	]+00b50633[ 	]+add[ 	]+a2,a0,a1
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_BASE_IDX_ADD[ 	]+weakL
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_RELAX[ 	]+\*ABS\*
+[ 	]+[0-9a-f]+:[ 	]+08b5063b[ 	]+add\.uw[ 	]+a2,a0,a1
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_BASE_IDX_ADD[ 	]+weakL
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_RELAX[ 	]+\*ABS\*
+[ 	]+[0-9a-f]+:[ 	]+20b52633[ 	]+sh1add[ 	]+a2,a0,a1
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_BASE_IDX_ADD[ 	]+weakL
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_RELAX[ 	]+\*ABS\*
+[ 	]+[0-9a-f]+:[ 	]+20b54633[ 	]+sh2add[ 	]+a2,a0,a1
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_BASE_IDX_ADD[ 	]+weakL
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_RELAX[ 	]+\*ABS\*
+[ 	]+[0-9a-f]+:[ 	]+20b56633[ 	]+sh3add[ 	]+a2,a0,a1
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_BASE_IDX_ADD[ 	]+weakL
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_RELAX[ 	]+\*ABS\*
+[ 	]+[0-9a-f]+:[ 	]+20b5663b[ 	]+sh3add\.uw[ 	]+a2,a0,a1
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_BASE_IDX_ADD[ 	]+weakL
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_RELAX[ 	]+\*ABS\*
+[ 	]+[0-9a-f]+:[ 	]+00864683[ 	]+lbu[ 	]+a3,8\(a2\)
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_BASE_IDX_LO12_I[ 	]+weakL
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_RELAX[ 	]+\*ABS\*
+[ 	]+[0-9a-f]+:[ 	]+00d60423[ 	]+sb[ 	]+a3,8\(a2\)
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_BASE_IDX_LO12_S[ 	]+weakL
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_RELAX[ 	]+\*ABS\*
diff --git a/ld/testsuite/ld-riscv-elf/base-idx-relax-medlow-far.d b/ld/testsuite/ld-riscv-elf/base-idx-relax-medlow-far.d
new file mode 100644
index 00000000000..753518183fe
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/base-idx-relax-medlow-far.d
@@ -0,0 +1,29 @@
+#source: base-idx-relax-gp.s
+#as: -march=rv64i_zba -mabi=lp64 --defsym __medlow__=1
+#ld: -Tbase-idx-relax-far.ld -melf64lriscv --relax
+#objdump: -d -Mno-aliases
+
+.*:[ 	]+file format .*
+
+
+Disassembly of section .text:
+
+0+10000 <_start>:
+[ 	]+[0-9a-f]+:[ 	]+001005b7[ 	]+lui[ 	]+a1,0x100
+[ 	]+[0-9a-f]+:[ 	]+00b50633[ 	]+add[ 	]+a2,a0,a1
+[ 	]+[0-9a-f]+:[ 	]+08b5063b[ 	]+add\.uw[ 	]+a2,a0,a1
+[ 	]+[0-9a-f]+:[ 	]+20b52633[ 	]+sh1add[ 	]+a2,a0,a1
+[ 	]+[0-9a-f]+:[ 	]+20b54633[ 	]+sh2add[ 	]+a2,a0,a1
+[ 	]+[0-9a-f]+:[ 	]+20b56633[ 	]+sh3add[ 	]+a2,a0,a1
+[ 	]+[0-9a-f]+:[ 	]+20b5663b[ 	]+sh3add\.uw[ 	]+a2,a0,a1
+[ 	]+[0-9a-f]+:[ 	]+00064683[ 	]+lbu[ 	]+a3,0\(a2\)
+[ 	]+[0-9a-f]+:[ 	]+00d60023[ 	]+sb[ 	]+a3,0\(a2\)
+[ 	]+[0-9a-f]+:[ 	]+001005b7[ 	]+lui[ 	]+a1,0x100
+[ 	]+[0-9a-f]+:[ 	]+00b50633[ 	]+add[ 	]+a2,a0,a1
+[ 	]+[0-9a-f]+:[ 	]+08b5063b[ 	]+add\.uw[ 	]+a2,a0,a1
+[ 	]+[0-9a-f]+:[ 	]+20b52633[ 	]+sh1add[ 	]+a2,a0,a1
+[ 	]+[0-9a-f]+:[ 	]+20b54633[ 	]+sh2add[ 	]+a2,a0,a1
+[ 	]+[0-9a-f]+:[ 	]+20b56633[ 	]+sh3add[ 	]+a2,a0,a1
+[ 	]+[0-9a-f]+:[ 	]+20b5663b[ 	]+sh3add\.uw[ 	]+a2,a0,a1
+[ 	]+[0-9a-f]+:[ 	]+00864683[ 	]+lbu[ 	]+a3,8\(a2\)
+[ 	]+[0-9a-f]+:[ 	]+00d60423[ 	]+sb[ 	]+a3,8\(a2\)
diff --git a/ld/testsuite/ld-riscv-elf/base-idx-relax-medlow-near-emit-relocs.d b/ld/testsuite/ld-riscv-elf/base-idx-relax-medlow-near-emit-relocs.d
new file mode 100644
index 00000000000..71caba35bb1
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/base-idx-relax-medlow-near-emit-relocs.d
@@ -0,0 +1,63 @@
+#source: base-idx-relax-gp.s
+#as: -march=rv64i_zba -mabi=lp64 --defsym __medlow__=1
+#ld: -Tbase-idx-relax-near.ld -melf64lriscv --relax --emit-relocs
+#objdump: -dr -Mno-aliases
+
+.*:[ 	]+file format .*
+
+
+Disassembly of section .text:
+
+0+10000 <_start>:
+[ 	]+[0-9a-f]+:[ 	]+00350633[ 	]+add[ 	]+a2,a0,gp
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_NONE[ 	]+\*ABS\*\+0x4
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_RELAX[ 	]+\*ABS\*
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_BASE_IDX_ADD[ 	]+symL
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_RELAX[ 	]+\*ABS\*
+[ 	]+[0-9a-f]+:[ 	]+0835063b[ 	]+add\.uw[ 	]+a2,a0,gp
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_BASE_IDX_ADD[ 	]+symL
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_RELAX[ 	]+\*ABS\*
+[ 	]+[0-9a-f]+:[ 	]+20352633[ 	]+sh1add[ 	]+a2,a0,gp
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_BASE_IDX_ADD[ 	]+symL
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_RELAX[ 	]+\*ABS\*
+[ 	]+[0-9a-f]+:[ 	]+20354633[ 	]+sh2add[ 	]+a2,a0,gp
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_BASE_IDX_ADD[ 	]+symL
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_RELAX[ 	]+\*ABS\*
+[ 	]+[0-9a-f]+:[ 	]+20356633[ 	]+sh3add[ 	]+a2,a0,gp
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_BASE_IDX_ADD[ 	]+symL
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_RELAX[ 	]+\*ABS\*
+[ 	]+[0-9a-f]+:[ 	]+2035663b[ 	]+sh3add\.uw[ 	]+a2,a0,gp
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_BASE_IDX_ADD[ 	]+symL
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_RELAX[ 	]+\*ABS\*
+[ 	]+[0-9a-f]+:[ 	]+10064683[ 	]+lbu[ 	]+a3,256\(a2\)
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_BASE_IDX_LO12_I[ 	]+symL-0x20000
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_RELAX[ 	]+\*ABS\*
+[ 	]+[0-9a-f]+:[ 	]+10d60023[ 	]+sb[ 	]+a3,256\(a2\)
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_BASE_IDX_LO12_S[ 	]+symL-0x20000
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_RELAX[ 	]+\*ABS\*
+[ 	]+[0-9a-f]+:[ 	]+00350633[ 	]+add[ 	]+a2,a0,gp
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_NONE[ 	]+\*ABS\*\+0x4
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_RELAX[ 	]+\*ABS\*
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_BASE_IDX_ADD[ 	]+weakL
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_RELAX[ 	]+\*ABS\*
+[ 	]+[0-9a-f]+:[ 	]+0835063b[ 	]+add\.uw[ 	]+a2,a0,gp
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_BASE_IDX_ADD[ 	]+weakL
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_RELAX[ 	]+\*ABS\*
+[ 	]+[0-9a-f]+:[ 	]+20352633[ 	]+sh1add[ 	]+a2,a0,gp
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_BASE_IDX_ADD[ 	]+weakL
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_RELAX[ 	]+\*ABS\*
+[ 	]+[0-9a-f]+:[ 	]+20354633[ 	]+sh2add[ 	]+a2,a0,gp
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_BASE_IDX_ADD[ 	]+weakL
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_RELAX[ 	]+\*ABS\*
+[ 	]+[0-9a-f]+:[ 	]+20356633[ 	]+sh3add[ 	]+a2,a0,gp
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_BASE_IDX_ADD[ 	]+weakL
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_RELAX[ 	]+\*ABS\*
+[ 	]+[0-9a-f]+:[ 	]+2035663b[ 	]+sh3add\.uw[ 	]+a2,a0,gp
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_BASE_IDX_ADD[ 	]+weakL
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_RELAX[ 	]+\*ABS\*
+[ 	]+[0-9a-f]+:[ 	]+10864683[ 	]+lbu[ 	]+a3,264\(a2\)
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_BASE_IDX_LO12_I[ 	]+weakL-0x20000
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_RELAX[ 	]+\*ABS\*
+[ 	]+[0-9a-f]+:[ 	]+10d60423[ 	]+sb[ 	]+a3,264\(a2\)
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_BASE_IDX_LO12_S[ 	]+weakL-0x20000
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_RELAX[ 	]+\*ABS\*
diff --git a/ld/testsuite/ld-riscv-elf/base-idx-relax-medlow-near.d b/ld/testsuite/ld-riscv-elf/base-idx-relax-medlow-near.d
new file mode 100644
index 00000000000..2249dcde471
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/base-idx-relax-medlow-near.d
@@ -0,0 +1,27 @@
+#source: base-idx-relax-gp.s
+#as: -march=rv64i_zba -mabi=lp64 --defsym __medlow__=1
+#ld: -Tbase-idx-relax-near.ld -melf64lriscv --relax
+#objdump: -d -Mno-aliases
+
+.*:[ 	]+file format .*
+
+
+Disassembly of section .text:
+
+0+10000 <_start>:
+[ 	]+[0-9a-f]+:[ 	]+00350633[ 	]+add[ 	]+a2,a0,gp
+[ 	]+[0-9a-f]+:[ 	]+0835063b[ 	]+add\.uw[ 	]+a2,a0,gp
+[ 	]+[0-9a-f]+:[ 	]+20352633[ 	]+sh1add[ 	]+a2,a0,gp
+[ 	]+[0-9a-f]+:[ 	]+20354633[ 	]+sh2add[ 	]+a2,a0,gp
+[ 	]+[0-9a-f]+:[ 	]+20356633[ 	]+sh3add[ 	]+a2,a0,gp
+[ 	]+[0-9a-f]+:[ 	]+2035663b[ 	]+sh3add\.uw[ 	]+a2,a0,gp
+[ 	]+[0-9a-f]+:[ 	]+10064683[ 	]+lbu[ 	]+a3,256\(a2\)
+[ 	]+[0-9a-f]+:[ 	]+10d60023[ 	]+sb[ 	]+a3,256\(a2\)
+[ 	]+[0-9a-f]+:[ 	]+00350633[ 	]+add[ 	]+a2,a0,gp
+[ 	]+[0-9a-f]+:[ 	]+0835063b[ 	]+add\.uw[ 	]+a2,a0,gp
+[ 	]+[0-9a-f]+:[ 	]+20352633[ 	]+sh1add[ 	]+a2,a0,gp
+[ 	]+[0-9a-f]+:[ 	]+20354633[ 	]+sh2add[ 	]+a2,a0,gp
+[ 	]+[0-9a-f]+:[ 	]+20356633[ 	]+sh3add[ 	]+a2,a0,gp
+[ 	]+[0-9a-f]+:[ 	]+2035663b[ 	]+sh3add\.uw[ 	]+a2,a0,gp
+[ 	]+[0-9a-f]+:[ 	]+10864683[ 	]+lbu[ 	]+a3,264\(a2\)
+[ 	]+[0-9a-f]+:[ 	]+10d60423[ 	]+sb[ 	]+a3,264\(a2\)
diff --git a/ld/testsuite/ld-riscv-elf/base-idx-relax-near.ld b/ld/testsuite/ld-riscv-elf/base-idx-relax-near.ld
new file mode 100644
index 00000000000..b04e15d2fb8
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/base-idx-relax-near.ld
@@ -0,0 +1,22 @@
+/* Data symbols sit right next to __global_pointer$ so the gp-relaxable
+   range is satisfied and R_RISCV_BASE_IDX_* are fully relaxed.  */
+
+ENTRY(_start)
+MEMORY
+{
+	rom (rx)  : ORIGIN = 0x10000, LENGTH = 0x1000
+	ram (!rx) : ORIGIN = 0x20000, LENGTH = 0x1000
+}
+SECTIONS {
+	.text : {
+		*(.text*)
+	} >rom
+
+	.data : {
+		__global_pointer$ = .;
+		/* Pad so that symL sits at gp + 0x100, giving the relaxed
+		   lbu/sb a non-zero gp-relative immediate.  */
+		. = . + 0x100;
+		*(.data*)
+	} >ram
+}
diff --git a/ld/testsuite/ld-riscv-elf/base-idx-relax-x0-emit-relocs.d b/ld/testsuite/ld-riscv-elf/base-idx-relax-x0-emit-relocs.d
new file mode 100644
index 00000000000..59ca62578c7
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/base-idx-relax-x0-emit-relocs.d
@@ -0,0 +1,63 @@
+#source: base-idx-relax-x0.s
+#as: -march=rv64i_zba -mabi=lp64 --defsym __medlow__=1
+#ld: -Tbase-idx-relax-x0.ld -melf64lriscv --relax --emit-relocs
+#objdump: -dr -Mno-aliases
+
+.*:[ 	]+file format .*
+
+
+Disassembly of section .text:
+
+0+10000 <_start>:
+[ 	]+[0-9a-f]+:[ 	]+00050533[ 	]+add[ 	]+a0,a0,zero
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_NONE[ 	]+\*ABS\*\+0x4
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_RELAX[ 	]+\*ABS\*
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_BASE_IDX_ADD[ 	]+symL
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_RELAX[ 	]+\*ABS\*
+[ 	]+[0-9a-f]+:[ 	]+0805053b[ 	]+add\.uw[ 	]+a0,a0,zero
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_BASE_IDX_ADD[ 	]+symL
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_RELAX[ 	]+\*ABS\*
+[ 	]+[0-9a-f]+:[ 	]+20052533[ 	]+sh1add[ 	]+a0,a0,zero
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_BASE_IDX_ADD[ 	]+symL
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_RELAX[ 	]+\*ABS\*
+[ 	]+[0-9a-f]+:[ 	]+20054533[ 	]+sh2add[ 	]+a0,a0,zero
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_BASE_IDX_ADD[ 	]+symL
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_RELAX[ 	]+\*ABS\*
+[ 	]+[0-9a-f]+:[ 	]+20056533[ 	]+sh3add[ 	]+a0,a0,zero
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_BASE_IDX_ADD[ 	]+symL
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_RELAX[ 	]+\*ABS\*
+[ 	]+[0-9a-f]+:[ 	]+2005653b[ 	]+sh3add\.uw[ 	]+a0,a0,zero
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_BASE_IDX_ADD[ 	]+symL
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_RELAX[ 	]+\*ABS\*
+[ 	]+[0-9a-f]+:[ 	]+10054603[ 	]+lbu[ 	]+a2,256\(a0\)
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_BASE_IDX_LO12_I[ 	]+symL
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_RELAX[ 	]+\*ABS\*
+[ 	]+[0-9a-f]+:[ 	]+10c50023[ 	]+sb[ 	]+a2,256\(a0\)
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_BASE_IDX_LO12_S[ 	]+symL
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_RELAX[ 	]+\*ABS\*
+[ 	]+[0-9a-f]+:[ 	]+00050533[ 	]+add[ 	]+a0,a0,zero
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_NONE[ 	]+\*ABS\*\+0x4
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_RELAX[ 	]+\*ABS\*\+0x8
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_BASE_IDX_ADD[ 	]+weakL\+0x8
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_RELAX[ 	]+\*ABS\*\+0x8
+[ 	]+[0-9a-f]+:[ 	]+0805053b[ 	]+add\.uw[ 	]+a0,a0,zero
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_BASE_IDX_ADD[ 	]+weakL\+0x8
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_RELAX[ 	]+\*ABS\*\+0x8
+[ 	]+[0-9a-f]+:[ 	]+20052533[ 	]+sh1add[ 	]+a0,a0,zero
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_BASE_IDX_ADD[ 	]+weakL\+0x8
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_RELAX[ 	]+\*ABS\*\+0x8
+[ 	]+[0-9a-f]+:[ 	]+20054533[ 	]+sh2add[ 	]+a0,a0,zero
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_BASE_IDX_ADD[ 	]+weakL\+0x8
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_RELAX[ 	]+\*ABS\*\+0x8
+[ 	]+[0-9a-f]+:[ 	]+20056533[ 	]+sh3add[ 	]+a0,a0,zero
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_BASE_IDX_ADD[ 	]+weakL\+0x8
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_RELAX[ 	]+\*ABS\*\+0x8
+[ 	]+[0-9a-f]+:[ 	]+2005653b[ 	]+sh3add\.uw[ 	]+a0,a0,zero
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_BASE_IDX_ADD[ 	]+weakL\+0x8
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_RELAX[ 	]+\*ABS\*\+0x8
+[ 	]+[0-9a-f]+:[ 	]+00854603[ 	]+lbu[ 	]+a2,8\(a0\)
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_BASE_IDX_LO12_I[ 	]+weakL\+0x8
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_RELAX[ 	]+\*ABS\*\+0x8
+[ 	]+[0-9a-f]+:[ 	]+00c50423[ 	]+sb[ 	]+a2,8\(a0\)
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_BASE_IDX_LO12_S[ 	]+weakL\+0x8
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_RELAX[ 	]+\*ABS\*\+0x8
diff --git a/ld/testsuite/ld-riscv-elf/base-idx-relax-x0.d b/ld/testsuite/ld-riscv-elf/base-idx-relax-x0.d
new file mode 100644
index 00000000000..c1687ee0699
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/base-idx-relax-x0.d
@@ -0,0 +1,27 @@
+#source: base-idx-relax-x0.s
+#as: -march=rv64i_zba -mabi=lp64 --defsym __medlow__=1
+#ld: -Tbase-idx-relax-x0.ld -melf64lriscv --relax
+#objdump: -d -Mno-aliases
+
+.*:[ 	]+file format .*
+
+
+Disassembly of section .text:
+
+0+10000 <_start>:
+[ 	]+[0-9a-f]+:[ 	]+00050533[ 	]+add[ 	]+a0,a0,zero
+[ 	]+[0-9a-f]+:[ 	]+0805053b[ 	]+add\.uw[ 	]+a0,a0,zero
+[ 	]+[0-9a-f]+:[ 	]+20052533[ 	]+sh1add[ 	]+a0,a0,zero
+[ 	]+[0-9a-f]+:[ 	]+20054533[ 	]+sh2add[ 	]+a0,a0,zero
+[ 	]+[0-9a-f]+:[ 	]+20056533[ 	]+sh3add[ 	]+a0,a0,zero
+[ 	]+[0-9a-f]+:[ 	]+2005653b[ 	]+sh3add\.uw[ 	]+a0,a0,zero
+[ 	]+[0-9a-f]+:[ 	]+10054603[ 	]+lbu[ 	]+a2,256\(a0\)
+[ 	]+[0-9a-f]+:[ 	]+10c50023[ 	]+sb[ 	]+a2,256\(a0\)
+[ 	]+[0-9a-f]+:[ 	]+00050533[ 	]+add[ 	]+a0,a0,zero
+[ 	]+[0-9a-f]+:[ 	]+0805053b[ 	]+add\.uw[ 	]+a0,a0,zero
+[ 	]+[0-9a-f]+:[ 	]+20052533[ 	]+sh1add[ 	]+a0,a0,zero
+[ 	]+[0-9a-f]+:[ 	]+20054533[ 	]+sh2add[ 	]+a0,a0,zero
+[ 	]+[0-9a-f]+:[ 	]+20056533[ 	]+sh3add[ 	]+a0,a0,zero
+[ 	]+[0-9a-f]+:[ 	]+2005653b[ 	]+sh3add\.uw[ 	]+a0,a0,zero
+[ 	]+[0-9a-f]+:[ 	]+00854603[ 	]+lbu[ 	]+a2,8\(a0\)
+[ 	]+[0-9a-f]+:[ 	]+00c50423[ 	]+sb[ 	]+a2,8\(a0\)
diff --git a/ld/testsuite/ld-riscv-elf/base-idx-relax-x0.ld b/ld/testsuite/ld-riscv-elf/base-idx-relax-x0.ld
new file mode 100644
index 00000000000..883ebeae167
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/base-idx-relax-x0.ld
@@ -0,0 +1,28 @@
+/* Three base-idx-relax cases sharing one gp placement (__global_pointer$ =
+   0x1000):
+
+   - symL is a defined symbol whose address (0x100) fits a 12-bit signed
+     immediate directly, while its displacement from gp does not, so x0 is
+     selected both at relaxation time and at final relocation time.
+
+   - weakL is an undefined weak symbol referenced with a +0x8 addend, so
+     both relaxation time (undefined_weak forces x0) and final relocation
+     time (0 + 0x8 fits a 12-bit immediate) agree on x0.  */
+
+ENTRY(_start)
+MEMORY
+{
+	rom (rx)  : ORIGIN = 0x10000, LENGTH = 0x1000
+	ram (!rx) : ORIGIN = 0x100, LENGTH = 0x1000
+}
+SECTIONS {
+	.text : {
+		*(.text*)
+	} >rom
+
+	.data : {
+		*(.data*)
+	} >ram
+
+	__global_pointer$ = 0x1000;
+}
diff --git a/ld/testsuite/ld-riscv-elf/base-idx-relax-x0.s b/ld/testsuite/ld-riscv-elf/base-idx-relax-x0.s
new file mode 100644
index 00000000000..caa2fe7124f
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/base-idx-relax-x0.s
@@ -0,0 +1,34 @@
+	.text
+	.global _start
+_start:
+
+.ifdef __medlow__
+	lui             a1, %hi(symL)
+	add	        a0, a0, a1, %base_idx_add(symL)
+	add.uw	        a0, a0, a1, %base_idx_add(symL)
+	sh1add	        a0, a0, a1, %base_idx_add(symL)
+	sh2add	        a0, a0, a1, %base_idx_add(symL)
+	sh3add	        a0, a0, a1, %base_idx_add(symL)
+	sh3add.uw	a0, a0, a1, %base_idx_add(symL)
+	lbu	        a2, %base_idx_lo(symL)(a0)
+	sb      	a2, %base_idx_lo(symL)(a0)
+
+	lui             a1, %hi(weakL+0x8)
+	add	        a0, a0, a1, %base_idx_add(weakL+0x8)
+	add.uw	        a0, a0, a1, %base_idx_add(weakL+0x8)
+	sh1add	        a0, a0, a1, %base_idx_add(weakL+0x8)
+	sh2add	        a0, a0, a1, %base_idx_add(weakL+0x8)
+	sh3add	        a0, a0, a1, %base_idx_add(weakL+0x8)
+	sh3add.uw       a0, a0, a1, %base_idx_add(weakL+0x8)
+	lbu	        a2, %base_idx_lo(weakL+0x8)(a0)
+	sb      	a2, %base_idx_lo(weakL+0x8)(a0)
+.endif
+
+	.size	_start, .-_start
+
+	.data
+	.global symL
+symL:
+	.dword	0x1111222233334444
+
+        .weak	weakL
diff --git a/ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp b/ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp
index 8e26ccff10a..82e906503d4 100644
--- a/ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp
+++ b/ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp
@@ -154,6 +154,12 @@ if [istarget "riscv*-*-*"] {
     run_dump_test "code-model-relax-medany-02"
     run_dump_test "code-model-relax-medany-weakref-01"
     run_dump_test "code-model-relax-medany-weakref-02"
+    run_dump_test "base-idx-relax-medlow-near"
+    run_dump_test "base-idx-relax-medlow-near-emit-relocs"
+    run_dump_test "base-idx-relax-medlow-far"
+    run_dump_test "base-idx-relax-medlow-far-emit-relocs"
+    run_dump_test "base-idx-relax-x0"
+    run_dump_test "base-idx-relax-x0-emit-relocs"
     run_dump_test "attr-merge-arch-01"
     run_dump_test "attr-merge-arch-02"
     run_dump_test "attr-merge-arch-03"
diff --git a/opcodes/riscv-dis.c b/opcodes/riscv-dis.c
index 03c8cf1e344..5a53367fdd6 100644
--- a/opcodes/riscv-dis.c
+++ b/opcodes/riscv-dis.c
@@ -513,6 +513,12 @@ print_insn_args (const char *oparg, insn_t l, bfd_vma pc, disassemble_info *info
 	    print (info->stream, dis_style_immediate, "0");
 	  break;
 
+	case '1':
+	  /* This is a relaxation marker operand (e.g. %tprel_add,
+	     %tlsdesc_call, %base_idx_add) that does not correspond to
+	     any encoding bits, so there is nothing to print for it.  */
+	  break;
+
 	case 'r':
 	  print (info->stream, dis_style_register, "%s",
 		 pd->riscv_gpr_names[EXTRACT_OPERAND (RS3, l)]);
diff --git a/opcodes/riscv-opc.c b/opcodes/riscv-opc.c
index 54887c97880..86084dbe091 100644
--- a/opcodes/riscv-opc.c
+++ b/opcodes/riscv-opc.c
@@ -556,8 +556,8 @@ const struct riscv_opcode riscv_opcodes[] =
 {"add",         0, INSN_CLASS_ZCA, "Ct,Cc,CK",  MATCH_C_ADDI4SPN, MASK_C_ADDI4SPN, match_c_addi4spn, INSN_ALIAS },
 {"add",         0, INSN_CLASS_ZCA, "Cc,Cc,CL",  MATCH_C_ADDI16SP, MASK_C_ADDI16SP, match_c_addi16sp, INSN_ALIAS },
 {"add",         0, INSN_CLASS_ZCA, "d,Cz,CV",   MATCH_C_MV, MASK_C_MV, match_c_add, INSN_ALIAS },
-{"add",         0, INSN_CLASS_I, "d,s,t",     MATCH_ADD, MASK_ADD, match_opcode, 0 },
-{"add",         0, INSN_CLASS_I, "d,s,t,1",   MATCH_ADD, MASK_ADD, match_opcode, 0 },
+/* make fourth operand (tprel_add/base_idx_add) of add an optional operand.  */
+{"add",         0, INSN_CLASS_I, "d,s,t1",    MATCH_ADD, MASK_ADD, match_opcode, 0 },
 {"add",         0, INSN_CLASS_I, "d,s,j",     MATCH_ADDI, MASK_ADDI, match_opcode, INSN_ALIAS },
 {"la",          0, INSN_CLASS_I, "d,B",       0, (int) M_LA, match_rd_nonzero, INSN_MACRO },
 {"lla",         0, INSN_CLASS_I, "d,B",       0, (int) M_LLA, NULL, INSN_MACRO },
@@ -1378,16 +1378,18 @@ const struct riscv_opcode riscv_opcodes[] =
 {"rorw",      64, INSN_CLASS_ZBB_OR_ZBKB,  "d,s,<", MATCH_RORIW, MASK_RORIW, match_opcode, INSN_ALIAS },
 
 /* Zba instructions.  */
-{"sh1add",     0, INSN_CLASS_ZBA,  "d,s,t", MATCH_SH1ADD, MASK_SH1ADD, match_opcode, 0 },
-{"sh2add",     0, INSN_CLASS_ZBA,  "d,s,t", MATCH_SH2ADD, MASK_SH2ADD, match_opcode, 0 },
-{"sh3add",     0, INSN_CLASS_ZBA,  "d,s,t", MATCH_SH3ADD, MASK_SH3ADD, match_opcode, 0 },
-{"sh1add.uw", 64, INSN_CLASS_ZBA,  "d,s,t", MATCH_SH1ADD_UW, MASK_SH1ADD_UW, match_opcode, 0 },
-{"sh2add.uw", 64, INSN_CLASS_ZBA,  "d,s,t", MATCH_SH2ADD_UW, MASK_SH2ADD_UW, match_opcode, 0 },
-{"sh3add.uw", 64, INSN_CLASS_ZBA,  "d,s,t", MATCH_SH3ADD_UW, MASK_SH3ADD_UW, match_opcode, 0 },
+/*  make fourth operand (base_idx_add) of shxadd/shxadd.uw an optional operand.  */
+{"sh1add",     0, INSN_CLASS_ZBA,  "d,s,t1",  MATCH_SH1ADD, MASK_SH1ADD, match_opcode, 0 },
+{"sh2add",     0, INSN_CLASS_ZBA,  "d,s,t1",  MATCH_SH2ADD, MASK_SH2ADD, match_opcode, 0 },
+{"sh3add",     0, INSN_CLASS_ZBA,  "d,s,t1",  MATCH_SH3ADD, MASK_SH3ADD, match_opcode, 0 },
+{"sh1add.uw", 64, INSN_CLASS_ZBA,  "d,s,t1",  MATCH_SH1ADD_UW, MASK_SH1ADD_UW, match_opcode, 0 },
+{"sh2add.uw", 64, INSN_CLASS_ZBA,  "d,s,t1",  MATCH_SH2ADD_UW, MASK_SH2ADD_UW, match_opcode, 0 },
+{"sh3add.uw", 64, INSN_CLASS_ZBA,  "d,s,t1",  MATCH_SH3ADD_UW, MASK_SH3ADD_UW, match_opcode, 0 },
 {"zext.w",    64, INSN_CLASS_ZCB_AND_ZBA,  "Cs,Cw", MATCH_C_ZEXT_W, MASK_C_ZEXT_W, match_opcode, INSN_ALIAS },
 {"zext.w",    64, INSN_CLASS_ZBA,  "d,s",   MATCH_ADD_UW, MASK_ADD_UW | MASK_RS2, match_opcode, INSN_ALIAS },
 {"zext.w",    64, INSN_CLASS_I, "d,s",       0, (int) M_ZEXTW, NULL, INSN_MACRO },
-{"add.uw",    64, INSN_CLASS_ZBA,  "d,s,t", MATCH_ADD_UW, MASK_ADD_UW, match_opcode, 0 },
+/* make fourth operand (base_idx_add) of add.uw an optional operand.  */
+{"add.uw",    64, INSN_CLASS_ZBA,  "d,s,t1", MATCH_ADD_UW, MASK_ADD_UW, match_opcode, 0 },
 {"slli.uw",   64, INSN_CLASS_ZBA,  "d,s,>", MATCH_SLLI_UW, MASK_SLLI_UW, match_opcode, 0 },
 
 /* Zbc or zbkc instructions.  */
-- 
2.43.0