[v2] RISC-V: Added DT_RELR support

Message ID 20250910100703.3783-1-nelson@rivosinc.com
State New
Headers
Series [v2] RISC-V: Added DT_RELR support |

Commit Message

Nelson Chu Sept. 10, 2025, 10:07 a.m. UTC
  Porting by refering aarch64 and loongarch implementations.

RISC-V and loongarch both have relaxations, so there is also the same thing
that it's not safe to do relxations when updating the section layouts for relr.

For relr readelf, added is_riscv_special_symbol_name to filter following
special symbols:
1. mapping symbols
2. __DATA_BEGIN__, __SDATA_BEGIN__ and __BSS_END__, which are only used for
counting gp in riscv.

=====
v2 changelog,
1. rebasing test cases
2. Only enable dt_relr for linux rather than elf toolchain in
   ld/emulparams/elf32lriscv-defs.sh
---
 bfd/elfnn-riscv.c                             | 513 +++++++++++++++++-
 binutils/readelf.c                            |  17 +
 binutils/testsuite/lib/binutils-common.exp    |   3 +-
 ld/emulparams/elf32lriscv-defs.sh             |   1 +
 ld/testsuite/ld-riscv-elf/discard-pic.d       |   2 +-
 ld/testsuite/ld-riscv-elf/discard.ld          |   2 +-
 ld/testsuite/ld-riscv-elf/discard.s           |  18 +-
 ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp    |  50 ++
 .../ld-riscv-elf/relr-align-rv32-pic.d        |  23 +
 .../ld-riscv-elf/relr-align-rv32-pic.rd       |  18 +
 .../ld-riscv-elf/relr-align-rv64-pic.d        |  41 ++
 .../ld-riscv-elf/relr-align-rv64-pic.rd       |  18 +
 ld/testsuite/ld-riscv-elf/relr-align.s        | 114 ++++
 .../ld-riscv-elf/relr-data-rv32-pic.d         |  22 +
 .../ld-riscv-elf/relr-data-rv32-pic.rd        |  14 +
 .../ld-riscv-elf/relr-data-rv32-pie.d         |  22 +
 .../ld-riscv-elf/relr-data-rv32-pie.rd        |  14 +
 .../ld-riscv-elf/relr-data-rv64-pic.d         |  22 +
 .../ld-riscv-elf/relr-data-rv64-pic.rd        |  14 +
 .../ld-riscv-elf/relr-data-rv64-pie.d         |  22 +
 .../ld-riscv-elf/relr-data-rv64-pie.rd        |  14 +
 ld/testsuite/ld-riscv-elf/relr-data.s         |  87 +++
 ld/testsuite/ld-riscv-elf/relr-discard-pic.d  |  12 +
 ld/testsuite/ld-riscv-elf/relr-discard-pie.d  |   9 +
 ld/testsuite/ld-riscv-elf/relr-got-rv32-pic.d |  41 ++
 .../ld-riscv-elf/relr-got-rv32-pic.rd         |  11 +
 ld/testsuite/ld-riscv-elf/relr-got-rv32-pie.d |  41 ++
 .../ld-riscv-elf/relr-got-rv32-pie.rd         |  11 +
 ld/testsuite/ld-riscv-elf/relr-got-rv64-pic.d |  43 ++
 .../ld-riscv-elf/relr-got-rv64-pic.rd         |  11 +
 ld/testsuite/ld-riscv-elf/relr-got-rv64-pie.d |  43 ++
 .../ld-riscv-elf/relr-got-rv64-pie.rd         |  11 +
 ld/testsuite/ld-riscv-elf/relr-got.s          |  43 ++
 ld/testsuite/ld-riscv-elf/relr-relocs.ld      |  12 +
 ld/testsuite/ld-riscv-elf/relr-textrel-pic.d  |  14 +
 ld/testsuite/ld-riscv-elf/relr-textrel-pie.d  |  14 +
 ld/testsuite/ld-riscv-elf/relr-textrel.s      |  10 +
 37 files changed, 1346 insertions(+), 31 deletions(-)
 create mode 100644 ld/testsuite/ld-riscv-elf/relr-align-rv32-pic.d
 create mode 100644 ld/testsuite/ld-riscv-elf/relr-align-rv32-pic.rd
 create mode 100644 ld/testsuite/ld-riscv-elf/relr-align-rv64-pic.d
 create mode 100644 ld/testsuite/ld-riscv-elf/relr-align-rv64-pic.rd
 create mode 100644 ld/testsuite/ld-riscv-elf/relr-align.s
 create mode 100644 ld/testsuite/ld-riscv-elf/relr-data-rv32-pic.d
 create mode 100644 ld/testsuite/ld-riscv-elf/relr-data-rv32-pic.rd
 create mode 100644 ld/testsuite/ld-riscv-elf/relr-data-rv32-pie.d
 create mode 100644 ld/testsuite/ld-riscv-elf/relr-data-rv32-pie.rd
 create mode 100644 ld/testsuite/ld-riscv-elf/relr-data-rv64-pic.d
 create mode 100644 ld/testsuite/ld-riscv-elf/relr-data-rv64-pic.rd
 create mode 100644 ld/testsuite/ld-riscv-elf/relr-data-rv64-pie.d
 create mode 100644 ld/testsuite/ld-riscv-elf/relr-data-rv64-pie.rd
 create mode 100644 ld/testsuite/ld-riscv-elf/relr-data.s
 create mode 100644 ld/testsuite/ld-riscv-elf/relr-discard-pic.d
 create mode 100644 ld/testsuite/ld-riscv-elf/relr-discard-pie.d
 create mode 100644 ld/testsuite/ld-riscv-elf/relr-got-rv32-pic.d
 create mode 100644 ld/testsuite/ld-riscv-elf/relr-got-rv32-pic.rd
 create mode 100644 ld/testsuite/ld-riscv-elf/relr-got-rv32-pie.d
 create mode 100644 ld/testsuite/ld-riscv-elf/relr-got-rv32-pie.rd
 create mode 100644 ld/testsuite/ld-riscv-elf/relr-got-rv64-pic.d
 create mode 100644 ld/testsuite/ld-riscv-elf/relr-got-rv64-pic.rd
 create mode 100644 ld/testsuite/ld-riscv-elf/relr-got-rv64-pie.d
 create mode 100644 ld/testsuite/ld-riscv-elf/relr-got-rv64-pie.rd
 create mode 100644 ld/testsuite/ld-riscv-elf/relr-got.s
 create mode 100644 ld/testsuite/ld-riscv-elf/relr-relocs.ld
 create mode 100644 ld/testsuite/ld-riscv-elf/relr-textrel-pic.d
 create mode 100644 ld/testsuite/ld-riscv-elf/relr-textrel-pie.d
 create mode 100644 ld/testsuite/ld-riscv-elf/relr-textrel.s
  

Patch

diff --git a/bfd/elfnn-riscv.c b/bfd/elfnn-riscv.c
index aac061fb43d..b9ba6248280 100644
--- a/bfd/elfnn-riscv.c
+++ b/bfd/elfnn-riscv.c
@@ -77,21 +77,27 @@ 
        && (H)->type == STT_GNU_IFUNC \
        && ((SEC)->flags & SEC_CODE) == 0))
 
-/* True if dynamic relocation should be generated.  */
-#define RISCV_GENERATE_DYNAMIC_RELOC(PCREL, INFO, H, RESOLVED_TO_ZERO) \
-  ((bfd_link_pic (INFO) \
-    && ((H) == NULL \
-	|| (ELF_ST_VISIBILITY ((H)->other) == STV_DEFAULT && !(RESOLVED_TO_ZERO)) \
-	|| (H)->root.type != bfd_link_hash_undefweak) \
-    && (!(PCREL) \
-	|| !SYMBOL_CALLS_LOCAL ((INFO), (H)))) \
-   || (!bfd_link_pic (INFO) \
-       && (H) != NULL \
-       && (H)->dynindx != -1 \
-       && !(H)->non_got_ref \
-       && (((H)->def_dynamic && !(H)->def_regular) \
-	   || (H)->root.type == bfd_link_hash_undefweak \
-	   || (H)->root.type == bfd_link_hash_undefined)))
+/* True if dynamic relocation should be generated when generating a shared
+   library or PIE.  */
+#define RISCV_GENERATE_DYNAMIC_RELOC_FOR_PIC(PCREL, INFO, H, RESOLVED_TO_ZERO) \
+  (bfd_link_pic (INFO) \
+   && ((H) == NULL \
+       || (ELF_ST_VISIBILITY ((H)->other) == STV_DEFAULT && !(RESOLVED_TO_ZERO)) \
+       || (H)->root.type != bfd_link_hash_undefweak) \
+   && (!(PCREL) \
+       || !SYMBOL_CALLS_LOCAL ((INFO), (H))))
+
+/* True if dynamic relocation should be generated when generating an
+   executable.  We may need to keep relocations for symbols satisfied
+   by a dynamic library if we manage to avoid.  */
+#define RISCV_GENERATE_DYNAMIC_RELOC_FOR_EXE(INFO, H) \
+  (!bfd_link_pic (INFO) \
+   && (H) != NULL \
+   && (H)->dynindx != -1 \
+   && !(H)->non_got_ref \
+   && (((H)->def_dynamic && !(H)->def_regular) \
+	 || (H)->root.type == bfd_link_hash_undefweak \
+	 || (H)->root.type == bfd_link_hash_undefined))
 
 /* True if this input relocation should be copied to output.  H->dynindx
    may be -1 if this symbol was marked to become local.  */
@@ -205,6 +211,39 @@  elfNN_riscv_mkobject (bfd *abfd)
 				  sizeof (struct _bfd_riscv_elf_obj_tdata));
 }
 
+struct relr_entry
+{
+  asection *sec;
+  bfd_vma off;
+};
+
+typedef struct _riscv_elf_section_data
+{
+  struct bfd_elf_section_data elf;
+
+  /* Handle interactions between DT_RELR and relaxation.  */
+  struct relr_entry *relr;
+} _riscv_elf_section_data;
+
+#define riscv_elf_section_data(sec) \
+  ((_riscv_elf_section_data *) elf_section_data (sec))
+
+/* Allocate target specific section data.  */
+
+static bool
+elfNN_riscv_new_section_hook (bfd *abfd, asection *sec)
+{
+  if (!sec->used_by_bfd)
+    {
+      struct _riscv_elf_section_data *sdata;
+      sdata = bfd_zalloc (abfd, sizeof (*sdata));
+      if (sdata == NULL)
+	return false;
+      sec->used_by_bfd = sdata;
+    }
+  return _bfd_elf_new_section_hook (abfd, sec);
+}
+
 #include "elf/common.h"
 #include "elf/internal.h"
 
@@ -246,6 +285,17 @@  struct riscv_elf_link_hash_table
   bool (*make_plt_header) (bfd *output_bfd, struct riscv_elf_link_hash_table *htab);
   bool (*make_plt_entry) (bfd *output_bfd, asection *got, bfd_vma got_offset,
 			  asection *plt, bfd_vma plt_offset);
+
+  /* Array of RELATIVE relocs to be emitted in DT_RELR format.  */
+  bfd_size_type relr_alloc;
+  bfd_size_type relr_count;
+  struct relr_entry *relr;
+  /* Sorted output addresses of above RELATIVE relocs.  */
+  bfd_vma *relr_sorted;
+  /* Layout recomputation count.  */
+  bfd_size_type relr_layout_iter;
+  /* The section layouts are updating for relr.  */
+  bool layout_mutating_for_relr;
 };
 
 /* Instruction access functions. */
@@ -1692,6 +1742,369 @@  allocate_local_ifunc_dynrelocs (void **slot, void *inf)
   return allocate_ifunc_dynrelocs (h, inf);
 }
 
+/* Record a relative relocation that will be emitted packed (DT_RELR).
+   Called after relocation sections are sized, so undo the size accounting
+   for this relocation.  */
+
+static bool
+record_relr (struct riscv_elf_link_hash_table *htab, asection *sec,
+	     bfd_vma off, asection *sreloc)
+{
+  struct relr_entry **sec_relr = &riscv_elf_section_data (sec)->relr;
+
+  /* Undo the relocation section size accounting.  */
+  BFD_ASSERT (sreloc->size >= sizeof (ElfNN_External_Rela));
+  sreloc->size -= sizeof (ElfNN_External_Rela);
+
+  /* The packing format uses the last bit of the address so that
+     must be aligned.  We don't pack relocations that may not be
+     aligned even though the final output address could end up
+     aligned, to avoid complex sizing logic for a rare case.  */
+  BFD_ASSERT (off % 2 == 0 && sec->alignment_power > 0);
+  if (htab->relr_count >= htab->relr_alloc)
+    {
+      if (htab->relr_alloc == 0)
+	htab->relr_alloc = 4096;
+      else
+	htab->relr_alloc *= 2;
+      htab->relr = bfd_realloc (htab->relr,
+				htab->relr_alloc * sizeof (*htab->relr));
+      if (htab->relr == NULL)
+	return false;
+    }
+
+  htab->relr[htab->relr_count].sec = sec;
+  htab->relr[htab->relr_count].off = off;
+  if (*sec_relr == NULL)
+    *sec_relr = &htab->relr[htab->relr_count];
+  htab->relr_count++;
+  return true;
+}
+
+/* Follow allocate_dynrelocs, but only record relative relocations against the
+   GOT and undo their previous size accounting.  */
+
+static bool
+record_relr_dyn_got_relocs (struct elf_link_hash_entry *h, void *inf)
+{
+  if (h->root.type == bfd_link_hash_indirect)
+    return true;
+  if (h->type == STT_GNU_IFUNC && h->def_regular)
+    return true;
+  if (h->got.refcount <= 0)
+    return true;
+  if (riscv_elf_hash_entry (h)->tls_type
+      & (GOT_TLS_GD | GOT_TLS_IE | GOT_TLSDESC))
+    return true;
+
+  struct bfd_link_info *info = (struct bfd_link_info *) inf;
+  struct riscv_elf_link_hash_table *htab = riscv_elf_hash_table (info);
+
+  /* Need to make sure gp is output as a dynamic symbol for pde?  */
+
+  if (bfd_link_pic (info) && !UNDEFWEAK_NO_DYNAMIC_RELOC (info, h))
+    {
+      /* Check got relocs in riscv_elf_relocate_section.  */
+      bool relative_reloc = SYMBOL_REFERENCES_LOCAL (info, h)
+			    && !bfd_is_abs_symbol (&h->root);
+      if (relative_reloc)
+	if (!record_relr (htab, htab->elf.sgot, h->got.offset,
+			  htab->elf.srelgot))
+	  return false;
+    }
+  return true;
+}
+
+/* Record packed relative relocs against the GOT for local symbols.
+   Undo the size accounting of riscv_elf_late_size_sections.  */
+
+static bool
+record_relr_local_got_relocs (bfd *input_bfd, struct bfd_link_info *info)
+{
+  bfd_signed_vma *local_got_refcounts = elf_local_got_refcounts (input_bfd);
+  bfd_vma *local_got_offsets = elf_local_got_offsets (input_bfd);
+  char *local_tls_type = _bfd_riscv_elf_local_got_tls_type (input_bfd);
+
+  if (!bfd_link_pic (info)
+      || !local_got_refcounts
+      || !local_got_offsets
+      || !local_tls_type)
+    return true;
+
+  Elf_Internal_Shdr *symtab_hdr = &elf_symtab_hdr (input_bfd);
+  struct riscv_elf_link_hash_table *htab = riscv_elf_hash_table (info);
+  for (unsigned int i = 0; i < symtab_hdr->sh_info; i++)
+    {
+      if (local_got_refcounts[i] <= 0)
+	continue;
+      if ((local_tls_type[i] & GOT_NORMAL) == 0)
+	continue;
+
+      bfd_vma off = local_got_offsets[i];
+
+      /* FIXME: If the local symbol is in SHN_ABS then emitting
+	 a relative relocation is not correct, but it seems to
+	 be wrong in riscv_elf_relocate_section too.  */
+      if (!record_relr (htab, htab->elf.sgot, off, htab->elf.srelgot))
+	return false;
+    }
+  return true;
+}
+
+/* Follows the logic of riscv_elf_relocate_section to decide which
+   relocations will become relative and possible to pack.  Ignore
+   relocations against the GOT, those are handled separately per-symbol.
+   Undo the size accounting of the packed relocations and record them
+   so the relr section can be sized later.  */
+
+static bool
+record_relr_non_got_relocs (bfd *input_bfd, struct bfd_link_info *info,
+			    asection *sec)
+{
+  if (sec->reloc_count == 0)
+    return true;
+  if ((sec->flags & (SEC_RELOC | SEC_ALLOC | SEC_DEBUGGING))
+      != (SEC_RELOC | SEC_ALLOC))
+    return true;
+  if (sec->alignment_power == 0)
+    return true;
+  if (discarded_section (sec))
+    return true;
+  asection *sreloc = elf_section_data (sec)->sreloc;
+  if (sreloc == NULL)
+    return true;
+
+  struct riscv_elf_link_hash_table *htab = riscv_elf_hash_table (info);
+  Elf_Internal_Shdr *symtab_hdr = &elf_symtab_hdr (input_bfd);
+  struct elf_link_hash_entry **sym_hashes = elf_sym_hashes (input_bfd);
+  const Elf_Internal_Rela * relocs =
+	_bfd_elf_link_info_read_relocs (input_bfd, info, sec, NULL, NULL,
+					info->keep_memory);
+  BFD_ASSERT (relocs != NULL);
+
+  const Elf_Internal_Rela *rel = relocs;
+  const Elf_Internal_Rela *rel_end = relocs + sec->reloc_count;
+  for (; rel < rel_end; rel++)
+    {
+      unsigned int r_symndx = ELFNN_R_SYM (rel->r_info);
+      unsigned int r_type = ELFNN_R_TYPE (rel->r_info);
+
+      /* Handle relocs that can become R_RISCV_RELATIVE,
+	 but not ones against the GOT as those are handled
+	 separately per-symbol.  */
+      if (r_type != R_RISCV_32 && r_type != R_RISCV_64)
+	continue;
+      /* Can only pack relocation against an aligned address.  */
+      if (rel->r_offset % 2 != 0)
+	continue;
+
+      struct elf_link_hash_entry *h = NULL;
+      asection *def_sec = NULL;
+      bool resolved_to_zero = false;
+      if (r_symndx < symtab_hdr->sh_info)
+	{
+	  /* A local symbol.  */
+	  Elf_Internal_Sym *isym;
+	  isym = bfd_sym_from_r_symndx (&htab->elf.sym_cache,
+					input_bfd, r_symndx);
+	  BFD_ASSERT (isym != NULL);
+
+	  if (ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC)
+	    continue;
+
+	  def_sec = bfd_section_from_elf_index (input_bfd, isym->st_shndx);
+	}
+      else
+	{
+	  h = sym_hashes[r_symndx - symtab_hdr->sh_info];
+	  while (h->root.type == bfd_link_hash_indirect
+		 || h->root.type == bfd_link_hash_warning)
+	    h = (struct elf_link_hash_entry *) h->root.u.i.link;
+
+	  /* Filter out symbols that cannot have a relative reloc.  */
+	  if (h->dyn_relocs == NULL)
+	    continue;
+	  if (bfd_is_abs_symbol (&h->root))
+	    continue;
+	  if (h->type == STT_GNU_IFUNC)
+	    continue;
+
+	  if (h->root.type == bfd_link_hash_defined
+	      || h->root.type == bfd_link_hash_defweak)
+	    def_sec = h->root.u.def.section;
+
+	  resolved_to_zero = UNDEFWEAK_NO_DYNAMIC_RELOC (info, h);
+	}
+
+      if (def_sec != NULL && discarded_section (def_sec))
+	continue;
+
+      /* Same logic as in riscv_elf_relocate_section, for R_RISCV_32/64.
+	 Except conditionals trimmed that cannot result a reltive reloc.  */
+      reloc_howto_type *howto = riscv_elf_rtype_to_howto (input_bfd, r_type);
+      if (RISCV_GENERATE_DYNAMIC_RELOC_FOR_PIC (howto->pc_relative, info, h,
+						resolved_to_zero))
+	{
+	  if (RISCV_COPY_INPUT_RELOC (info, h))
+	    continue;
+
+	  if (!record_relr (htab, sec, rel->r_offset, sreloc))
+	    return false;
+	}
+    }
+  return true;
+}
+
+static int
+cmp_relr_addr (const void *p, const void *q)
+{
+  const bfd_vma *a = p;
+  const bfd_vma *b = q;
+  return *a < *b ? -1 : *a > *b ? 1 : 0;
+}
+
+/* Produce a malloc'd sorted array of reloc addresses in htab->relr_sorted.
+   Returns false on allocation failure.  */
+
+static bool
+sort_relr (struct bfd_link_info *info,
+	   struct riscv_elf_link_hash_table *htab)
+{
+  if (htab->relr_count == 0)
+    return true;
+
+  bfd_vma *addr = htab->relr_sorted;
+  if (addr == NULL)
+    {
+      addr = bfd_malloc (htab->relr_count * sizeof (*addr));
+      if (addr == NULL)
+	return false;
+      htab->relr_sorted = addr;
+    }
+
+  for (bfd_size_type i = 0; i < htab->relr_count; i++)
+    {
+      bfd_vma off = _bfd_elf_section_offset (info->output_bfd, info,
+					     htab->relr[i].sec,
+					     htab->relr[i].off);
+      addr[i] = htab->relr[i].sec->output_section->vma
+		+ htab->relr[i].sec->output_offset
+		+ off;
+    }
+  qsort (addr, htab->relr_count, sizeof (*addr), cmp_relr_addr);
+  return true;
+}
+
+/* Size of a relr entry and a relocated location.  */
+#define RELR_SZ (ARCH_SIZE / 8)
+/* Number of consecutive locations a relr bitmap entry references.  */
+#define RELR_N (ARCH_SIZE - 1)
+
+/* Size .relr.dyn whenever the layout changes, the number of packed
+   relocs are unchanged but the packed representation can.  */
+
+static bool
+riscv_elf_size_relative_relocs (struct bfd_link_info *info,
+				bool *need_layout)
+{
+  struct riscv_elf_link_hash_table *htab = riscv_elf_hash_table (info);
+  asection *srelrdyn = htab->elf.srelrdyn;
+  *need_layout = false;
+
+  if (!sort_relr (info, htab))
+    return false;
+  bfd_vma *addr = htab->relr_sorted;
+
+  BFD_ASSERT (srelrdyn != NULL);
+  bfd_size_type oldsize = srelrdyn->size;
+  srelrdyn->size = 0;
+  for (bfd_size_type i = 0; i < htab->relr_count; )
+    {
+      bfd_vma base = addr[i];
+      i++;
+      srelrdyn->size += RELR_SZ;
+      base += RELR_SZ;
+      for (;;)
+	{
+	  bfd_size_type start_i = i;
+	  while (i < htab->relr_count
+		 && addr[i] - base < RELR_N * RELR_SZ
+		 && (addr[i] - base) % RELR_SZ == 0)
+	    i++;
+	  if (i == start_i)
+	    break;
+	  srelrdyn->size += RELR_SZ;
+	  base += RELR_N * RELR_SZ;
+	}
+    }
+  if (srelrdyn->size != oldsize)
+    {
+      *need_layout = true;
+      /* Stop after a few iterations in case the layout does not converge,
+	 we can do this when the size would shrink.  */
+      if (htab->relr_layout_iter++ > 5 && srelrdyn->size < oldsize)
+	{
+	  srelrdyn->size = oldsize;
+	  *need_layout = false;
+	}
+    }
+  htab->layout_mutating_for_relr = *need_layout;
+  return true;
+}
+
+/* Emit the .relr.dyn section after it is sized and the layout is fixed.  */
+
+static bool
+riscv_elf_finish_relative_relocs (struct bfd_link_info *info)
+{
+  struct riscv_elf_link_hash_table *htab = riscv_elf_hash_table (info);
+  asection *srelrdyn = htab->elf.srelrdyn;
+  bfd *dynobj = htab->elf.dynobj;
+
+  if (srelrdyn == NULL || srelrdyn->size == 0)
+    return true;
+  srelrdyn->contents = bfd_alloc (dynobj, srelrdyn->size);
+  if (srelrdyn->contents == NULL)
+    return false;
+  srelrdyn->alloced = 1;
+  bfd_vma *addr = htab->relr_sorted;
+  bfd_byte *loc = srelrdyn->contents;
+  for (bfd_size_type i = 0; i < htab->relr_count; )
+    {
+      bfd_vma base = addr[i];
+      i++;
+      bfd_put_NN (dynobj, base, loc);
+      loc += RELR_SZ;
+      base += RELR_SZ;
+      for (;;)
+	{
+	  bfd_vma bits = 0;
+	  while (i < htab->relr_count)
+	    {
+	      bfd_vma delta = addr[i] - base;
+	      if (delta >= RELR_N * RELR_SZ || delta % RELR_SZ != 0)
+		break;
+	      bits |= (bfd_vma) 1 << (delta / RELR_SZ);
+	      i++;
+	    }
+	  if (bits == 0)
+	    break;
+	  bfd_put_NN (dynobj, (bits << 1) | 1, loc);
+	  loc += RELR_SZ;
+	  base += RELR_N * RELR_SZ;
+	}
+    }
+  free (addr);
+  htab->relr_sorted = NULL;
+  /* Pad any excess with 1's, a do-nothing encoding.  */
+  while (loc < srelrdyn->contents + srelrdyn->size)
+    {
+      bfd_put_NN (dynobj, 1, loc);
+      loc += RELR_SZ;
+    }
+  return true;
+}
+
 static bool
 riscv_elf_late_size_sections (bfd *output_bfd, struct bfd_link_info *info)
 {
@@ -1841,6 +2254,27 @@  riscv_elf_late_size_sections (bfd *output_bfd, struct bfd_link_info *info)
 	htab->elf.sgotplt->size = 0;
     }
 
+  /* Record the relative relocations that will be packed and undo the
+     size allocation for them in .rela.*. The size of .relr.dyn will be
+     computed later iteratively since it depends on the final layout.  */
+  if (info->enable_dt_relr && !bfd_link_relocatable (info))
+    {
+      elf_link_hash_traverse (&htab->elf, record_relr_dyn_got_relocs, info);
+
+      for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link.next)
+	{
+	  if (!is_riscv_elf (ibfd))
+	    continue;
+
+	  for (s = ibfd->sections; s != NULL; s = s->next)
+	    if (!record_relr_non_got_relocs (ibfd, info, s))
+	      return false;
+
+	  if (!record_relr_local_got_relocs (ibfd, info))
+	    return false;
+	}
+    }
+
   /* The check_relocs and adjust_dynamic_symbol entry points have
      determined the sizes of the various dynamic sections.  Allocate
      memory for them.  */
@@ -1870,6 +2304,15 @@  riscv_elf_late_size_sections (bfd *output_bfd, struct bfd_link_info *info)
 	      s->reloc_count = 0;
 	    }
 	}
+      else if (s == htab->elf.srelrdyn)
+	{
+	  /* Remove .relr.dyn based on relr_count, not size, since
+	     it is not sized yet.  */
+	  if (htab->relr_count == 0)
+	    s->flags |= SEC_EXCLUDE;
+	  /* Allocate contents later.  */
+	  continue;
+	}
       else
 	{
 	  /* It's not one of our sections.  */
@@ -2918,7 +3361,7 @@  riscv_elf_relocate_section (bfd *output_bfd,
 	  /* We need to generate a R_RISCV_RELATIVE relocation later in the
 	     riscv_elf_finish_dynamic_symbol if h->dynindx != -1;  Otherwise,
 	     generate a R_RISCV_RELATIVE relocation here now.  */
-	  if (relative_got)
+	  if (relative_got && !info->enable_dt_relr)
 	    {
 	      asection *s = htab->elf.srelgot;
 	      BFD_ASSERT (s != NULL);
@@ -3155,8 +3598,9 @@  riscv_elf_relocate_section (bfd *output_bfd,
 	  if ((input_section->flags & SEC_ALLOC) == 0)
 	    break;
 
-	  if (RISCV_GENERATE_DYNAMIC_RELOC (howto->pc_relative, info, h,
-					    resolved_to_zero))
+	  if (RISCV_GENERATE_DYNAMIC_RELOC_FOR_PIC (howto->pc_relative, info,
+						    h, resolved_to_zero)
+	      || RISCV_GENERATE_DYNAMIC_RELOC_FOR_EXE (info, h))
 	    {
 	      Elf_Internal_Rela outrel;
 	      asection *sreloc;
@@ -3196,6 +3640,14 @@  riscv_elf_relocate_section (bfd *output_bfd,
 		  outrel.r_info = ELFNN_R_INFO (h->dynindx, r_type);
 		  outrel.r_addend = rel->r_addend;
 		}
+	      else if (info->enable_dt_relr
+		       && input_section->alignment_power != 0
+		       && rel->r_offset % 2 == 0)
+		{
+		  /* Don't emit a relative relocation that is packed, only
+		     apply the addend.  */
+		  goto do_relocation;
+		}
 	      else
 		{
 		  /* This symbol is local, or marked to become local.  */
@@ -3642,6 +4094,11 @@  riscv_elf_finish_dynamic_symbol (bfd *output_bfd,
 	     of a version file.  The entry in the global offset table will
 	     already have been initialized in the relocate_section function.  */
 	  BFD_ASSERT ((h->got.offset & 1) != 0);
+
+	  /* Don't emit relative relocs if they are packed.  */
+	  if (info->enable_dt_relr)
+	    goto skip_got_reloc;
+
 	  asection *sec = h->root.u.def.section;
 	  rela.r_info = ELFNN_R_INFO (0, R_RISCV_RELATIVE);
 	  rela.r_addend = (h->root.u.def.value
@@ -3678,6 +4135,8 @@  riscv_elf_finish_dynamic_symbol (bfd *output_bfd,
 	}
     }
 
+ skip_got_reloc:
+
   if (h->needs_copy)
     {
       Elf_Internal_Rela rela;
@@ -4762,6 +5221,16 @@  _riscv_relax_delete_bytes (bfd *abfd,
 	}
     }
 
+  /* Adjust the offsets for all record relr in this section.  */
+  struct relr_entry *relr = riscv_elf_section_data (sec)->relr;
+  struct riscv_elf_link_hash_table *htab = riscv_elf_hash_table (link_info);
+  struct relr_entry *relr_end = NULL;
+  if (htab->relr_count)
+    relr_end = htab->relr + htab->relr_count;
+  for (; relr && relr < relr_end && relr->sec == sec; relr++)
+    if (relr->off > addr && relr->off < toaddr)
+      relr->off -= count;
+
   return true;
 }
 
@@ -5398,7 +5867,10 @@  _bfd_riscv_relax_section (bfd *abfd, asection *sec,
 	  && info->relax_pass == 0)
       /* The exp_seg_relro_adjust is enum phase_enum (0x4),
 	 and defined in ld/ldexp.h.  */
-      || *(htab->data_segment_phase) == 4)
+      || *(htab->data_segment_phase) == 4
+      /* It's not safe to do relaxations when relr are updating the section
+	 layouts.  */
+      || htab->layout_mutating_for_relr)
     return true;
 
   /* Record the first relax section, so that we can reset the
@@ -6007,6 +6479,7 @@  elfNN_riscv_merge_gnu_properties (struct bfd_link_info *info, bfd *abfd,
 #define bfd_elfNN_mkobject			elfNN_riscv_mkobject
 #define bfd_elfNN_get_synthetic_symtab		\
   elfNN_riscv_get_synthetic_symtab
+#define bfd_elfNN_new_section_hook		elfNN_riscv_new_section_hook
 
 #define elf_backend_reloc_type_class		riscv_reloc_type_class
 #define elf_backend_copy_indirect_symbol	riscv_elf_copy_indirect_symbol
@@ -6035,6 +6508,8 @@  elfNN_riscv_merge_gnu_properties (struct bfd_link_info *info, bfd *abfd,
   elfNN_riscv_link_setup_gnu_properties
 #define elf_backend_merge_gnu_properties	\
   elfNN_riscv_merge_gnu_properties
+#define elf_backend_size_relative_relocs	riscv_elf_size_relative_relocs
+#define elf_backend_finish_relative_relocs	riscv_elf_finish_relative_relocs
 
 #define elf_backend_can_gc_sections		1
 #define elf_backend_can_refcount		1
diff --git a/binutils/readelf.c b/binutils/readelf.c
index 2f8dda88913..f1b8c4c5b40 100644
--- a/binutils/readelf.c
+++ b/binutils/readelf.c
@@ -1679,6 +1679,20 @@  is_aarch64_special_symbol_name (const char *name)
   return name[2] == 0 || name[2] == '.';
 }
 
+/* See riscv_elf_is_mapping_symbols.  */
+
+static bool
+is_riscv_special_symbol_name (const char *name)
+{
+  return (!strcmp (name, "$d")
+	  || !strcmp (name, "$x")
+	  || !strncmp (name, "$xrv", 4)
+	  /* Only used for counting gp in risc-v.  */
+	  || !strcmp (name, "__DATA_BEGIN__")
+	  || !strcmp (name, "__SDATA_BEGIN__")
+	  || !strcmp (name, "__BSS_END__"));
+}
+
 static bool
 is_special_symbol_name (Filedata * filedata, const char * s)
 {
@@ -1687,6 +1701,9 @@  is_special_symbol_name (Filedata * filedata, const char * s)
     case EM_AARCH64:
       return is_aarch64_special_symbol_name (s);
 
+    case EM_RISCV:
+      return is_riscv_special_symbol_name (s);
+
     default:
       return false;
     }
diff --git a/binutils/testsuite/lib/binutils-common.exp b/binutils/testsuite/lib/binutils-common.exp
index 47fe48a4919..37f4ebb4224 100644
--- a/binutils/testsuite/lib/binutils-common.exp
+++ b/binutils/testsuite/lib/binutils-common.exp
@@ -469,7 +469,8 @@  proc supports_dt_relr {} {
 	  || [istarget i?86-*-*]
 	  || [istarget powerpc64*-*-*]
 	  || [istarget aarch64*-*-*]
-	  || [istarget loongarch64*-*-*])
+	  || [istarget loongarch64*-*-*]
+	  || [istarget riscv*-*-*])
 	 && ([istarget *-*-linux*]
 	     || [istarget *-*-gnu*]) } {
 	return 1
diff --git a/ld/emulparams/elf32lriscv-defs.sh b/ld/emulparams/elf32lriscv-defs.sh
index 016556168c3..09a1f375c80 100644
--- a/ld/emulparams/elf32lriscv-defs.sh
+++ b/ld/emulparams/elf32lriscv-defs.sh
@@ -23,6 +23,7 @@  case "$target" in
   *)
     GENERATE_SHLIB_SCRIPT=yes
     GENERATE_PIE_SCRIPT=yes
+    source_sh ${srcdir}/emulparams/dt-relr.sh
     ;;
 esac
 
diff --git a/ld/testsuite/ld-riscv-elf/discard-pic.d b/ld/testsuite/ld-riscv-elf/discard-pic.d
index 9ac2cc6850f..b9cc3eb0c77 100644
--- a/ld/testsuite/ld-riscv-elf/discard-pic.d
+++ b/ld/testsuite/ld-riscv-elf/discard-pic.d
@@ -6,4 +6,4 @@ 
 Relocation section '\.rela\.dyn'.*
 [ 	]+Offset[ 	]+Info[ 	]+Type.*
 0+0[ 	]+0+0[ 	]+R_RISCV_NONE[ 	]+0
-0+(20008|20010)[ 	]+[0-9a-f]+[ 	]+R_RISCV_(32|64)[ 	]+0+10008[ 	]+sym_global \+ 0
+0+(20008|20010)[ 	]+[0-9a-f]+[ 	]+R_RISCV_(32|64)[ 	]+0+1000c[ 	]+sym_global \+ 0
diff --git a/ld/testsuite/ld-riscv-elf/discard.ld b/ld/testsuite/ld-riscv-elf/discard.ld
index 3afed216fbc..175441a53fb 100644
--- a/ld/testsuite/ld-riscv-elf/discard.ld
+++ b/ld/testsuite/ld-riscv-elf/discard.ld
@@ -8,6 +8,6 @@  SECTIONS
   .text : { *(.text) }
   . = 0x20000;
   .got  : { *(.got) *(.got.plt)}
-  . = 0x30000;
+  . = 0x12340000;
   .data : { *(.data) *(.data.*) }
 }
diff --git a/ld/testsuite/ld-riscv-elf/discard.s b/ld/testsuite/ld-riscv-elf/discard.s
index f03ea2b4590..1fe643a9006 100644
--- a/ld/testsuite/ld-riscv-elf/discard.s
+++ b/ld/testsuite/ld-riscv-elf/discard.s
@@ -9,15 +9,15 @@  _start:
 sym_local:
 	nop
 
-.global sym_global
-sym_global:
-	nop
-
 .global sym_hidden
 .hidden sym_hidden
 sym_hidden:
 	nop
 
+.global sym_global
+sym_global:
+	nop
+
 .global sym_global_abs
 .set sym_global_abs, 42
 
@@ -39,16 +39,16 @@  sym_hidden:
 discard_local:
 .quad sym_local
 
-.section .discard.global,"a"
-.p2align 1
-discard_global:
-.quad sym_global
-
 .section .discard.hidden,"a"
 .p2align 1
 discard_hidden:
 .quad sym_hidden
 
+.section .discard.global,"a"
+.p2align 1
+discard_global:
+.quad sym_global
+
 .section .discard.global_abs,"a"
 .p2align 1
 discard_global_abs:
diff --git a/ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp b/ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp
index b0d510ac8da..03959a6b638 100644
--- a/ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp
+++ b/ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp
@@ -78,6 +78,40 @@  proc run_dump_test_ifunc { name target output} {
 	      "$name-$target.$ext"]]
 }
 
+proc run_dump_test_relr { name target output} {
+    # TODO: big-endian for *.d test cases?
+    set asflags "-mlittle-endian"
+    set ldflags "-z pack-relative-relocs -T relr-relocs.ld"
+    switch -- $output {
+	pie {
+	    set ext "pie"
+	    set ldflags "$ldflags -pie"
+	}
+	pic {
+	    set ext "so"
+	    set ldflags "$ldflags -shared"
+	}
+    }
+    switch -- $target {
+	rv32 {
+	    set asflags "$asflags -march=rv32i -mabi=ilp32"
+	    set ldflags "$ldflags -m[riscv_choose_ilp32_emul]"
+	}
+	rv64 {
+	    set asflags "$asflags -march=rv64i -mabi=lp64 -defsym __64_bit__=1"
+	    set ldflags "$ldflags -m[riscv_choose_lp64_emul]"
+	}
+    }
+    run_ld_link_tests [list \
+	[list "$name ($target-$output)" \
+	      "$ldflags" "" \
+	      "$asflags" \
+	      [list "$name.s"] \
+	      [concat [list "readelf -rW  $name-$target-$output.rd"] \
+		      [list "objdump -dsw $name-$target-$output.d"]] \
+	      "$name-$target.$ext"]]
+}
+
 proc run_relax_twice_test {} {
     global as
     global ld
@@ -242,6 +276,22 @@  if [istarget "riscv*-*-*"] {
     run_dump_test "discard-pie"
     run_dump_test "discard-pic"
 
+    # RELR
+    run_dump_test_relr "relr-data" rv32 pie
+    run_dump_test_relr "relr-data" rv32 pic
+    run_dump_test_relr "relr-got" rv32 pie
+    run_dump_test_relr "relr-got" rv32 pic
+    run_dump_test_relr "relr-align" rv32 pic
+    run_dump_test_relr "relr-data" rv64 pie
+    run_dump_test_relr "relr-data" rv64 pic
+    run_dump_test_relr "relr-got" rv64 pie
+    run_dump_test_relr "relr-got" rv64 pic
+    run_dump_test_relr "relr-align" rv64 pic
+    run_dump_test "relr-textrel-pie"
+    run_dump_test "relr-textrel-pic"
+    run_dump_test "relr-discard-pie"
+    run_dump_test "relr-discard-pic"
+
     # IFUNC testcases.
     # Check IFUNC by single type relocs.
     run_dump_test_ifunc "ifunc-reloc-call-01" rv32 exe
diff --git a/ld/testsuite/ld-riscv-elf/relr-align-rv32-pic.d b/ld/testsuite/ld-riscv-elf/relr-align-rv32-pic.d
new file mode 100644
index 00000000000..31eea11c42f
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/relr-align-rv32-pic.d
@@ -0,0 +1,23 @@ 
+#...
+Contents of section .data:
+ 12340000 00000100 00000100 00000000 00000000.*
+ 12340010 00000000 01000000 01000000 00000000.*
+ 12340020 00000100 00000000 00000000 01000000.*
+ 12340030 00000000 00000100 01000000 02000000.*
+ 12340040 03000000 04000000 05000000 06000000.*
+ 12340050 07000000 08000000 09000000 0a000000.*
+ 12340060 0b000000 0c000000 0d000000 0e000000.*
+ 12340070 0f000000 10000000 11000000 12000000.*
+ 12340080 13000000 14000000 15000000 16000000.*
+ 12340090 17000000 18000000 19000000 1a000000.*
+ 123400a0 1b000000 1c000000 1d000000 1e000000.*
+ 123400b0 1f000000 20000100 21000000 22000000.*
+ 123400c0 23000000 24000000 25000000 26000000.*
+ 123400d0 27000000 28000000 29000000 2a000000.*
+ 123400e0 2b000000 2c000000 2d000000 2e000000.*
+ 123400f0 2f000000 30000000 31000000 32000000.*
+ 12340100 33000000 34000000 35000000 36000000.*
+ 12340110 37000000 38000000 39000000 3a000000.*
+ 12340120 3b000000 3c000000 3d000000 3e000000.*
+ 12340130 3f000100 40000100.*
+#...
diff --git a/ld/testsuite/ld-riscv-elf/relr-align-rv32-pic.rd b/ld/testsuite/ld-riscv-elf/relr-align-rv32-pic.rd
new file mode 100644
index 00000000000..01a08671d3c
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/relr-align-rv32-pic.rd
@@ -0,0 +1,18 @@ 
+Relocation section '\.rela\.dyn'.*contains 3 entries:
+[ 	]+Offset[ 	]+Info[ 	]+Type[ 	]+.*
+12340009[ 	]+[0-9a-f]+[ 	]+R_RISCV_RELATIVE[ 	]+10000
+1234000d[ 	]+[0-9a-f]+[ 	]+R_RISCV_RELATIVE[ 	]+10000
+12340025[ 	]+[0-9a-f]+[ 	]+R_RISCV_RELATIVE[ 	]+10000
+
+Relocation section '\.relr\.dyn'.*contains 10 entries which relocate 10 locations:
+Index:[ 	]+Entry[ 	]+Address[ 	]+Symbolic Address
+0000:[ 	]+12340000[ 	]+12340000[ 	]+double_0
+0001:[ 	]+00000003[ 	]+12340004[ 	]+double_0 \+ 0x4
+0002:[ 	]+12340012[ 	]+12340012[ 	]+double_2
+0003:[ 	]+00000003[ 	]+12340016[ 	]+double_2 \+ 0x4
+0004:[ 	]+12340020[ 	]+12340020[ 	]+single_0
+0005:[ 	]+1234002a[ 	]+1234002a[ 	]+single_2
+0006:[ 	]+12340034[ 	]+12340034[ 	]+big
+0007:[ 	]+123400b4[ 	]+123400b4[ 	]+big \+ 0x80
+0008:[ 	]+80000001[ 	]+12340130[ 	]+big \+ 0xfc
+0009:[ 	]+00000003[ 	]+12340134[ 	]+big \+ 0x100
diff --git a/ld/testsuite/ld-riscv-elf/relr-align-rv64-pic.d b/ld/testsuite/ld-riscv-elf/relr-align-rv64-pic.d
new file mode 100644
index 00000000000..38d81f8da5c
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/relr-align-rv64-pic.d
@@ -0,0 +1,41 @@ 
+#...
+Contents of section .data:
+ 12340000 00000100 00000000 00000100 00000000.*
+ 12340010 00000000 00000000 00000000 00000000.*
+ 12340020 00000000 01000000 00000000 01000000.*
+ 12340030 00000000 00000000 00000100 00000000.*
+ 12340040 00000000 00000000 00000000 01000000.*
+ 12340050 00000000 00000000 00000100 00000000.*
+ 12340060 01000000 00000000 02000000 00000000.*
+ 12340070 03000000 00000000 04000000 00000000.*
+ 12340080 05000000 00000000 06000000 00000000.*
+ 12340090 07000000 00000000 08000000 00000000.*
+ 123400a0 09000000 00000000 0a000000 00000000.*
+ 123400b0 0b000000 00000000 0c000000 00000000.*
+ 123400c0 0d000000 00000000 0e000000 00000000.*
+ 123400d0 0f000000 00000000 10000000 00000000.*
+ 123400e0 11000000 00000000 12000000 00000000.*
+ 123400f0 13000000 00000000 14000000 00000000.*
+ 12340100 15000000 00000000 16000000 00000000.*
+ 12340110 17000000 00000000 18000000 00000000.*
+ 12340120 19000000 00000000 1a000000 00000000.*
+ 12340130 1b000000 00000000 1c000000 00000000.*
+ 12340140 1d000000 00000000 1e000000 00000000.*
+ 12340150 1f000000 00000000 20000100 00000000.*
+ 12340160 21000000 00000000 22000000 00000000.*
+ 12340170 23000000 00000000 24000000 00000000.*
+ 12340180 25000000 00000000 26000000 00000000.*
+ 12340190 27000000 00000000 28000000 00000000.*
+ 123401a0 29000000 00000000 2a000000 00000000.*
+ 123401b0 2b000000 00000000 2c000000 00000000.*
+ 123401c0 2d000000 00000000 2e000000 00000000.*
+ 123401d0 2f000000 00000000 30000000 00000000.*
+ 123401e0 31000000 00000000 32000000 00000000.*
+ 123401f0 33000000 00000000 34000000 00000000.*
+ 12340200 35000000 00000000 36000000 00000000.*
+ 12340210 37000000 00000000 38000000 00000000.*
+ 12340220 39000000 00000000 3a000000 00000000.*
+ 12340230 3b000000 00000000 3c000000 00000000.*
+ 12340240 3d000000 00000000 3e000000 00000000.*
+ 12340250 3f000100 00000000 40000100 00000000.*
+#...
diff --git a/ld/testsuite/ld-riscv-elf/relr-align-rv64-pic.rd b/ld/testsuite/ld-riscv-elf/relr-align-rv64-pic.rd
new file mode 100644
index 00000000000..97a4d8c290e
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/relr-align-rv64-pic.rd
@@ -0,0 +1,18 @@ 
+Relocation section '\.rela\.dyn'.*contains 3 entries:
+[ 	]+Offset[ 	]+Info[ 	]+Type[ 	]+.*
+0000000012340011[ 	]+[0-9a-f]+[ 	]+R_RISCV_RELATIVE[ 	]+10000
+0000000012340019[ 	]+[0-9a-f]+[ 	]+R_RISCV_RELATIVE[ 	]+10000
+0000000012340041[ 	]+[0-9a-f]+[ 	]+R_RISCV_RELATIVE[ 	]+10000
+
+Relocation section '\.relr\.dyn'.*contains 9 entries which relocate 10 locations:
+Index:[ 	]+Entry[ 	]+Address[ 	]+Symbolic Address
+0000:[ 	]+0000000012340000[ 	]+0000000012340000[ 	]+double_0
+0001:[ 	]+0000000000000003[ 	]+0000000012340008[ 	]+double_0 \+ 0x8
+0002:[ 	]+0000000012340022[ 	]+0000000012340022[ 	]+double_2
+0003:[ 	]+0000000000000003[ 	]+000000001234002a[ 	]+double_2 \+ 0x8
+0004:[ 	]+0000000012340038[ 	]+0000000012340038[ 	]+single_0
+0005:[ 	]+000000001234004a[ 	]+000000001234004a[ 	]+single_2
+0006:[ 	]+0000000012340058[ 	]+0000000012340058[ 	]+big
+0007:[ 	]+8000000100000001[ 	]+0000000012340158[ 	]+big \+ 0x100
+[ 	]+0000000012340250[ 	]+big \+ 0x1f8
+0008:[ 	]+0000000000000003[ 	]+0000000012340258[ 	]+big \+ 0x200
diff --git a/ld/testsuite/ld-riscv-elf/relr-align.s b/ld/testsuite/ld-riscv-elf/relr-align.s
new file mode 100644
index 00000000000..9b19f88f5ec
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/relr-align.s
@@ -0,0 +1,114 @@ 
+# Test DT_RELR with differently aligned relative relocs.
+
+.macro data value
+.ifdef __64_bit__
+	.quad \value
+.else
+	.long \value
+.endif
+.endm
+
+.text
+.global _start
+_start:
+foo:
+
+.data
+.p2align 3
+double_0:
+data foo
+data foo
+.byte 0
+double_1:
+data foo
+data foo
+.byte 0
+double_2:
+data foo
+data foo
+.byte 0
+.byte 0
+.byte 0
+.byte 0
+.byte 0
+.byte 0
+single_0:
+data foo
+.byte 0
+single_1:
+data foo
+.byte 0
+single_2:
+data foo
+.byte 0
+.byte 0
+.byte 0
+.byte 0
+.byte 0
+.byte 0
+big:
+data foo
+data 1
+data 2
+data 3
+data 4
+data 5
+data 6
+data 7
+data 8
+data 9
+data 10
+data 11
+data 12
+data 13
+data 14
+data 15
+data 16
+data 17
+data 18
+data 19
+data 20
+data 21
+data 22
+data 23
+data 24
+data 25
+data 26
+data 27
+data 28
+data 29
+data 30
+data 31
+data foo + 32
+data 33
+data 34
+data 35
+data 36
+data 37
+data 38
+data 39
+data 40
+data 41
+data 42
+data 43
+data 44
+data 45
+data 46
+data 47
+data 48
+data 49
+data 50
+data 51
+data 52
+data 53
+data 54
+data 55
+data 56
+data 57
+data 58
+data 59
+data 60
+data 61
+data 62
+data foo + 63
+data foo + 64
diff --git a/ld/testsuite/ld-riscv-elf/relr-data-rv32-pic.d b/ld/testsuite/ld-riscv-elf/relr-data-rv32-pic.d
new file mode 100644
index 00000000000..4af2318123a
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/relr-data-rv32-pic.d
@@ -0,0 +1,22 @@ 
+#...
+Contents of section .data.unaligned_local:
+ 12340000 00000000.*
+Contents of section .data.unaligned_hidden:
+ 12340004 00000000.*
+Contents of section .data.unaligned_global:
+ 12340008 00000000.*
+Contents of section .data.unaligned_DYNAMIC:
+ 1234000c 00000000.*
+Contents of section .data.aligned_local:
+ 12340010 04000100.*
+Contents of section .data.aligned_hidden:
+ 12340014 08000100.*
+Contents of section .data.aligned_global:
+ 12340018 00000000.*
+Contents of section .data.aligned_global_abs:
+ 1234001c 2a000000.*
+Contents of section .data.aligned_weak_undef:
+ 12340020 00000000.*
+Contents of section .data.aligned_DYNAMIC:
+ 12340024 28003412.*
+#...
diff --git a/ld/testsuite/ld-riscv-elf/relr-data-rv32-pic.rd b/ld/testsuite/ld-riscv-elf/relr-data-rv32-pic.rd
new file mode 100644
index 00000000000..45da4525ba8
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/relr-data-rv32-pic.rd
@@ -0,0 +1,14 @@ 
+Relocation section '\.rela\.dyn'.*contains 6 entries:
+[ 	]+Offset[ 	]+Info[ 	]+Type[ 	]+.*
+12340000[ 	]+[0-9a-f]+[ 	]+R_RISCV_RELATIVE[ 	]+10004
+12340004[ 	]+[0-9a-f]+[ 	]+R_RISCV_RELATIVE[ 	]+10008
+1234000c[ 	]+[0-9a-f]+[ 	]+R_RISCV_RELATIVE[ 	]+12340028
+12340008[ 	]+[0-9a-f]+[ 	]+R_RISCV_32[ 	]+0001000c[ 	]+sym_global \+ 0
+12340018[ 	]+[0-9a-f]+[ 	]+R_RISCV_32[ 	]+0001000c[ 	]+sym_global \+ 0
+12340020[ 	]+[0-9a-f]+[ 	]+R_RISCV_32[ 	]+00000000[ 	]+sym_weak_undef \+ 0
+
+Relocation section '\.relr\.dyn'.*contains 2 entries which relocate 3 locations:
+Index:[ 	]+Entry[ 	]+Address[ 	]+Symbolic Address
+0000:[ 	]+12340010[ 	]+12340010[ 	]+aligned_local
+0001:[ 	]+00000023[ 	]+12340014[ 	]+aligned_hidden
+[ 	]+12340024[ 	]+aligned_DYNAMIC
diff --git a/ld/testsuite/ld-riscv-elf/relr-data-rv32-pie.d b/ld/testsuite/ld-riscv-elf/relr-data-rv32-pie.d
new file mode 100644
index 00000000000..71e081ccdca
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/relr-data-rv32-pie.d
@@ -0,0 +1,22 @@ 
+#...
+Contents of section .data.unaligned_local:
+ 12340000 00000000.*
+Contents of section .data.unaligned_hidden:
+ 12340004 00000000.*
+Contents of section .data.unaligned_global:
+ 12340008 00000000.*
+Contents of section .data.unaligned_DYNAMIC:
+ 1234000c 00000000.*
+Contents of section .data.aligned_local:
+ 12340010 04000100.*
+Contents of section .data.aligned_hidden:
+ 12340014 08000100.*
+Contents of section .data.aligned_global:
+ 12340018 0c000100.*
+Contents of section .data.aligned_global_abs:
+ 1234001c 2a000000.*
+Contents of section .data.aligned_weak_undef:
+ 12340020 00000000.*
+Contents of section .data.aligned_DYNAMIC:
+ 12340024 28003412.*
+#...
diff --git a/ld/testsuite/ld-riscv-elf/relr-data-rv32-pie.rd b/ld/testsuite/ld-riscv-elf/relr-data-rv32-pie.rd
new file mode 100644
index 00000000000..99e733f93c2
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/relr-data-rv32-pie.rd
@@ -0,0 +1,14 @@ 
+Relocation section '\.rela\.dyn'.*contains 5 entries:
+[ 	]+Offset[ 	]+Info[ 	]+Type[ 	]+.*
+12340000[ 	]+[0-9a-f]+[ 	]+R_RISCV_RELATIVE[ 	]+10004
+12340004[ 	]+[0-9a-f]+[ 	]+R_RISCV_RELATIVE[ 	]+10008
+12340008[ 	]+[0-9a-f]+[ 	]+R_RISCV_RELATIVE[ 	]+1000c
+1234000c[ 	]+[0-9a-f]+[ 	]+R_RISCV_RELATIVE[ 	]+12340028
+12340020[ 	]+[0-9a-f]+[ 	]+R_RISCV_32[ 	]+00000000[ 	]+sym_weak_undef \+ 0
+
+Relocation section '\.relr\.dyn'.*contains 2 entries which relocate 4 locations:
+Index:[ 	]+Entry[ 	]+Address[ 	]+Symbolic Address
+0000:[ 	]+12340010[ 	]+12340010[ 	]+aligned_local
+0001:[ 	]+00000027[ 	]+12340014[ 	]+aligned_hidden
+[ 	]+12340018[ 	]+aligned_global
+[ 	]+12340024[ 	]+aligned_DYNAMIC
diff --git a/ld/testsuite/ld-riscv-elf/relr-data-rv64-pic.d b/ld/testsuite/ld-riscv-elf/relr-data-rv64-pic.d
new file mode 100644
index 00000000000..0e3e1627dc5
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/relr-data-rv64-pic.d
@@ -0,0 +1,22 @@ 
+#...
+Contents of section .data.unaligned_local:
+ 12340000 00000000 00000000.*
+Contents of section .data.unaligned_hidden:
+ 12340008 00000000 00000000.*
+Contents of section .data.unaligned_global:
+ 12340010 00000000 00000000.*
+Contents of section .data.unaligned_DYNAMIC:
+ 12340018 00000000 00000000.*
+Contents of section .data.aligned_local:
+ 12340020 04000100 00000000.*
+Contents of section .data.aligned_hidden:
+ 12340028 08000100 00000000.*
+Contents of section .data.aligned_global:
+ 12340030 00000000 00000000.*
+Contents of section .data.aligned_global_abs:
+ 12340038 2a000000 00000000.*
+Contents of section .data.aligned_weak_undef:
+ 12340040 00000000 00000000.*
+Contents of section .data.aligned_DYNAMIC:
+ 12340048 50003412 00000000.*
+#...
diff --git a/ld/testsuite/ld-riscv-elf/relr-data-rv64-pic.rd b/ld/testsuite/ld-riscv-elf/relr-data-rv64-pic.rd
new file mode 100644
index 00000000000..8bbd2e45a5e
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/relr-data-rv64-pic.rd
@@ -0,0 +1,14 @@ 
+Relocation section '\.rela\.dyn'.*contains 6 entries:
+[ 	]+Offset[ 	]+Info[ 	]+Type[ 	]+.*
+0000000012340000[ 	]+[0-9a-f]+[ 	]+R_RISCV_RELATIVE[ 	]+10004
+0000000012340008[ 	]+[0-9a-f]+[ 	]+R_RISCV_RELATIVE[ 	]+10008
+0000000012340018[ 	]+[0-9a-f]+[ 	]+R_RISCV_RELATIVE[ 	]+12340050
+0000000012340010[ 	]+[0-9a-f]+[ 	]+R_RISCV_64[ 	]+000000000001000c[ 	]+sym_global \+ 0
+0000000012340030[ 	]+[0-9a-f]+[ 	]+R_RISCV_64[ 	]+000000000001000c[ 	]+sym_global \+ 0
+0000000012340040[ 	]+[0-9a-f]+[ 	]+R_RISCV_64[ 	]+0000000000000000[ 	]+sym_weak_undef \+ 0
+
+Relocation section '\.relr\.dyn'.*contains 2 entries which relocate 3 locations:
+Index:[ 	]+Entry[ 	]+Address[ 	]+Symbolic Address
+0000:[ 	]+0000000012340020[ 	]+0000000012340020[ 	]+aligned_local
+0001:[ 	]+0000000000000023[ 	]+0000000012340028[ 	]+aligned_hidden
+[ 	]+0000000012340048[ 	]+aligned_DYNAMIC
diff --git a/ld/testsuite/ld-riscv-elf/relr-data-rv64-pie.d b/ld/testsuite/ld-riscv-elf/relr-data-rv64-pie.d
new file mode 100644
index 00000000000..0f940b325a8
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/relr-data-rv64-pie.d
@@ -0,0 +1,22 @@ 
+#...
+Contents of section .data.unaligned_local:
+ 12340000 00000000 00000000.*
+Contents of section .data.unaligned_hidden:
+ 12340008 00000000 00000000.*
+Contents of section .data.unaligned_global:
+ 12340010 00000000 00000000.*
+Contents of section .data.unaligned_DYNAMIC:
+ 12340018 00000000 00000000.*
+Contents of section .data.aligned_local:
+ 12340020 04000100 00000000.*
+Contents of section .data.aligned_hidden:
+ 12340028 08000100 00000000.*
+Contents of section .data.aligned_global:
+ 12340030 0c000100 00000000.*
+Contents of section .data.aligned_global_abs:
+ 12340038 2a000000 00000000.*
+Contents of section .data.aligned_weak_undef:
+ 12340040 00000000 00000000.*
+Contents of section .data.aligned_DYNAMIC:
+ 12340048 50003412 00000000.*
+#...
diff --git a/ld/testsuite/ld-riscv-elf/relr-data-rv64-pie.rd b/ld/testsuite/ld-riscv-elf/relr-data-rv64-pie.rd
new file mode 100644
index 00000000000..6a7b8875133
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/relr-data-rv64-pie.rd
@@ -0,0 +1,14 @@ 
+Relocation section '\.rela\.dyn'.*contains 5 entries:
+[ 	]+Offset[ 	]+Info[ 	]+Type[ 	]+.*
+0000000012340000[ 	]+[0-9a-f]+[ 	]+R_RISCV_RELATIVE[ 	]+10004
+0000000012340008[ 	]+[0-9a-f]+[ 	]+R_RISCV_RELATIVE[ 	]+10008
+0000000012340010[ 	]+[0-9a-f]+[ 	]+R_RISCV_RELATIVE[ 	]+1000c
+0000000012340018[ 	]+[0-9a-f]+[ 	]+R_RISCV_RELATIVE[ 	]+12340050
+0000000012340040[ 	]+[0-9a-f]+[ 	]+R_RISCV_64[ 	]+0000000000000000[ 	]+sym_weak_undef \+ 0
+
+Relocation section '\.relr\.dyn'.*contains 2 entries which relocate 4 locations:
+Index:[ 	]+Entry[ 	]+Address[ 	]+Symbolic Address
+0000:[ 	]+0000000012340020[ 	]+0000000012340020[ 	]+aligned_local
+0001:[ 	]+0000000000000027[ 	]+0000000012340028[ 	]+aligned_hidden
+[ 	]+0000000012340030[ 	]+aligned_global
+[ 	]+0000000012340048[ 	]+aligned_DYNAMIC
diff --git a/ld/testsuite/ld-riscv-elf/relr-data.s b/ld/testsuite/ld-riscv-elf/relr-data.s
new file mode 100644
index 00000000000..a550111dd66
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/relr-data.s
@@ -0,0 +1,87 @@ 
+# Test symbol references in .data when used with DT_RELR.
+# Relocations for unaligned sections are currently not packed.
+
+.text
+.option pic
+.option norvc
+.global _start
+_start:
+	nop
+
+sym_local:
+	nop
+
+.global sym_hidden
+.hidden sym_hidden
+sym_hidden:
+	nop
+
+.global sym_global
+sym_global:
+	nop
+
+.global sym_global_abs
+.set sym_global_abs, 42
+
+.global sym_weak_undef
+.weak sym_weak_undef
+
+# data macro
+
+.macro data value
+.ifdef __64_bit__
+	.quad \value
+.else
+	.long \value
+.endif
+.endm
+
+# unaligned data
+
+.section .data.unaligned_local
+unaligned_local:
+data sym_local
+
+.section .data.unaligned_hidden
+unaligned_hidden:
+data sym_hidden
+
+.section .data.unaligned_global
+unaligned_global:
+data sym_global
+
+.section .data.unaligned_DYNAMIC
+unaligned_DYNAMIC:
+data _DYNAMIC
+
+# aligned data
+
+.section .data.aligned_local
+.p2align 1
+aligned_local:
+data sym_local
+
+.section .data.aligned_hidden
+.p2align 1
+aligned_hidden:
+data sym_hidden
+
+.section .data.aligned_global
+.p2align 1
+aligned_global:
+data sym_global
+
+.section .data.aligned_global_abs
+.p2align 1
+aligned_global_abs:
+data sym_global_abs
+
+.section .data.aligned_weak_undef
+.p2align 1
+aligned_weak_undef:
+data sym_weak_undef
+
+.section .data.aligned_DYNAMIC
+.p2align 1
+aligned_DYNAMIC:
+data _DYNAMIC
diff --git a/ld/testsuite/ld-riscv-elf/relr-discard-pic.d b/ld/testsuite/ld-riscv-elf/relr-discard-pic.d
new file mode 100644
index 00000000000..6edd4e1444f
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/relr-discard-pic.d
@@ -0,0 +1,12 @@ 
+#source: discard.s
+#as:
+#ld: -shared -Tdiscard.ld -z pack-relative-relocs
+#readelf: -rW
+
+Relocation section '\.rela\.dyn'.*
+[ 	]+Offset[ 	]+Info[ 	]+Type.*
+0+(20008|20010)[ 	]+[0-9a-f]+[ 	]+R_RISCV_(32|64)[ 	]+0+1000c[ 	]+sym_global \+ 0
+
+Relocation section '\.relr\.dyn'.*contains 1 entry which relocates 1 location:
+Index:[ 	]+Entry[ 	]+Address[ 	]+Symbolic Address
+0000:[ 	]+0+(20004|20008)[ 	]+0+(20004|20008)[ 	]+_GLOBAL_OFFSET_TABLE_ \+ (0x4|0x8)
diff --git a/ld/testsuite/ld-riscv-elf/relr-discard-pie.d b/ld/testsuite/ld-riscv-elf/relr-discard-pie.d
new file mode 100644
index 00000000000..0029dec23cc
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/relr-discard-pie.d
@@ -0,0 +1,9 @@ 
+#source: discard.s
+#as:
+#ld: -pie -Tdiscard.ld -z pack-relative-relocs
+#readelf: -rW
+
+Relocation section '\.relr\.dyn'.*contains 2 entries which relocate 2 locations:
+Index:[ 	]+Entry[ 	]+Address[ 	]+Symbolic Address
+0000:[ 	]+0+(20004|20008)[ 	]+0+(20004|20008)[ 	]+_GLOBAL_OFFSET_TABLE_ \+ (0x4|0x8)
+0001:[ 	]+0+3[ 	]+0+(20008|20010)[ 	]+_GLOBAL_OFFSET_TABLE_ \+ (0x8|0x10)
diff --git a/ld/testsuite/ld-riscv-elf/relr-got-rv32-pic.d b/ld/testsuite/ld-riscv-elf/relr-got-rv32-pic.d
new file mode 100644
index 00000000000..e221d6b8ff3
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/relr-got-rv32-pic.d
@@ -0,0 +1,41 @@ 
+#...
+Contents of section .got:
+ 20000 00003412 00000100 04000100 00003412.*
+ 20010 00000000 00000000 00000000.*
+#...
+Disassembly of section .text.got_local:
+
+0001000c <.text.got_local>:
+[ 	]+1000c:[ 	]+[0-9a-f]+[ 	]+auipc.*
+[ 	]+10010:[ 	]+[0-9a-f]+[ 	]+lw[ 	]+.*# 20004 <.*>
+
+Disassembly of section .text.got_hidden:
+
+00010014 <.text.got_hidden>:
+[ 	]+10014:[ 	]+[0-9a-f]+[ 	]+auipc.*
+[ 	]+10018:[ 	]+[0-9a-f]+[ 	]+lw[ 	]+.*# 20008 <.*>
+
+Disassembly of section .text.got_global:
+
+0001001c <.text.got_global>:
+[ 	]+1001c:[ 	]+[0-9a-f]+[ 	]+auipc.*
+[ 	]+10020:[ 	]+[0-9a-f]+[ 	]+lw[ 	]+.*# 20010 <.*>
+
+Disassembly of section .text.got_global_abs:
+
+00010024 <.text.got_global_abs>:
+[ 	]+10024:[ 	]+[0-9a-f]+[ 	]+auipc.*
+[ 	]+10028:[ 	]+[0-9a-f]+[ 	]+lw[ 	]+.*# 20014 <.*>
+
+Disassembly of section .text.got_weak_undef:
+
+0001002c <.text.got_weak_undef>:
+[ 	]+1002c:[ 	]+[0-9a-f]+[ 	]+auipc.*
+[ 	]+10030:[ 	]+[0-9a-f]+[ 	]+lw[ 	]+.*# 20018 <.*>
+
+Disassembly of section .text.got_DYNAMIC:
+
+00010034 <.text.got_DYNAMIC>:
+[ 	]+10034:[ 	]+[0-9a-f]+[ 	]+auipc.*
+[ 	]+10038:[ 	]+[0-9a-f]+[ 	]+lw[ 	]+.*# 2000c <.*>
+#...
diff --git a/ld/testsuite/ld-riscv-elf/relr-got-rv32-pic.rd b/ld/testsuite/ld-riscv-elf/relr-got-rv32-pic.rd
new file mode 100644
index 00000000000..d3d7c9462aa
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/relr-got-rv32-pic.rd
@@ -0,0 +1,11 @@ 
+Relocation section '\.rela\.dyn'.*contains 3 entries:
+[ 	]+Offset[ 	]+Info[ 	]+Type[ 	]+.*
+00020010[ 	]+[0-9a-f]+[ 	]+R_RISCV_32[ 	]+00010008[ 	]+sym_global \+ 0
+00020014[ 	]+[0-9a-f]+[ 	]+R_RISCV_32[ 	]+0000002a[ 	]+sym_global_abs \+ 0
+00020018[ 	]+[0-9a-f]+[ 	]+R_RISCV_32[ 	]+00000000[ 	]+sym_weak_undef \+ 0
+
+Relocation section '\.relr\.dyn'.*contains 2 entries which relocate 3 locations:
+Index:[ 	]+Entry[ 	]+Address[ 	]+Symbolic Address
+0000:[ 	]+00020004[ 	]+00020004[ 	]+_GLOBAL_OFFSET_TABLE_ \+ 0x4
+0001:[ 	]+00000007[ 	]+00020008[ 	]+_GLOBAL_OFFSET_TABLE_ \+ 0x8
+[ 	]+0002000c[ 	]+_GLOBAL_OFFSET_TABLE_ \+ 0xc
diff --git a/ld/testsuite/ld-riscv-elf/relr-got-rv32-pie.d b/ld/testsuite/ld-riscv-elf/relr-got-rv32-pie.d
new file mode 100644
index 00000000000..8609ce3a64a
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/relr-got-rv32-pie.d
@@ -0,0 +1,41 @@ 
+#...
+Contents of section .got:
+ 20000 00003412 00000100 04000100 00003412.*
+ 20010 08000100 2a000000 00000000.*
+#...
+Disassembly of section .text.got_local:
+
+0001000c <.text.got_local>:
+[ 	]+1000c:[ 	]+[0-9a-f]+[ 	]+auipc.*
+[ 	]+10010:[ 	]+[0-9a-f]+[ 	]+lw[ 	]+.*# 20004 <.*>
+
+Disassembly of section .text.got_hidden:
+
+00010014 <.text.got_hidden>:
+[ 	]+10014:[ 	]+[0-9a-f]+[ 	]+auipc.*
+[ 	]+10018:[ 	]+[0-9a-f]+[ 	]+lw[ 	]+.*# 20008 <.*>
+
+Disassembly of section .text.got_global:
+
+0001001c <.text.got_global>:
+[ 	]+1001c:[ 	]+[0-9a-f]+[ 	]+auipc.*
+[ 	]+10020:[ 	]+[0-9a-f]+[ 	]+lw[ 	]+.*# 20010 <.*>
+
+Disassembly of section .text.got_global_abs:
+
+00010024 <.text.got_global_abs>:
+[ 	]+10024:[ 	]+[0-9a-f]+[ 	]+auipc.*
+[ 	]+10028:[ 	]+[0-9a-f]+[ 	]+lw[ 	]+.*# 20014 <.*>
+
+Disassembly of section .text.got_weak_undef:
+
+0001002c <.text.got_weak_undef>:
+[ 	]+1002c:[ 	]+[0-9a-f]+[ 	]+auipc.*
+[ 	]+10030:[ 	]+[0-9a-f]+[ 	]+lw[ 	]+.*# 20018 <.*>
+
+Disassembly of section .text.got_DYNAMIC:
+
+00010034 <.text.got_DYNAMIC>:
+[ 	]+10034:[ 	]+[0-9a-f]+[ 	]+auipc.*
+[ 	]+10038:[ 	]+[0-9a-f]+[ 	]+lw[ 	]+.*# 2000c <.*>
+#...
diff --git a/ld/testsuite/ld-riscv-elf/relr-got-rv32-pie.rd b/ld/testsuite/ld-riscv-elf/relr-got-rv32-pie.rd
new file mode 100644
index 00000000000..b0138b16139
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/relr-got-rv32-pie.rd
@@ -0,0 +1,11 @@ 
+Relocation section '\.rela\.dyn'.*contains 2 entries:
+[ 	]+Offset[ 	]+Info[ 	]+Type[ 	]+.*
+00000000[ 	]+[0-9a-f]+[ 	]+R_RISCV_NONE[ 	]+0
+00020018[ 	]+[0-9a-f]+[ 	]+R_RISCV_32[ 	]+00000000[ 	]+sym_weak_undef \+ 0
+
+Relocation section '\.relr\.dyn'.*contains 2 entries which relocate 4 locations:
+Index:[ 	]+Entry[ 	]+Address[ 	]+Symbolic Address
+0000:[ 	]+00020004[ 	]+00020004[ 	]+_GLOBAL_OFFSET_TABLE_ \+ 0x4
+0001:[ 	]+0000000f[ 	]+00020008[ 	]+_GLOBAL_OFFSET_TABLE_ \+ 0x8
+[ 	]+0002000c[ 	]+_GLOBAL_OFFSET_TABLE_ \+ 0xc
+[ 	]+00020010[ 	]+_GLOBAL_OFFSET_TABLE_ \+ 0x10
diff --git a/ld/testsuite/ld-riscv-elf/relr-got-rv64-pic.d b/ld/testsuite/ld-riscv-elf/relr-got-rv64-pic.d
new file mode 100644
index 00000000000..7b605aaafa4
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/relr-got-rv64-pic.d
@@ -0,0 +1,43 @@ 
+#...
+Contents of section .got:
+ 20000 00003412 00000000 00000100 00000000.*
+ 20010 04000100 00000000 00003412 00000000.*
+ 20020 00000000 00000000 00000000 00000000.*
+ 20030 00000000 00000000.*
+#...
+Disassembly of section .text.got_local:
+
+000000000001000c <.text.got_local>:
+[ 	]+1000c:[ 	]+[0-9a-f]+[ 	]+auipc.*
+[ 	]+10010:[ 	]+[0-9a-f]+[ 	]+ld[ 	]+.*# 20008 <.*>
+
+Disassembly of section .text.got_hidden:
+
+0000000000010014 <.text.got_hidden>:
+[ 	]+10014:[ 	]+[0-9a-f]+[ 	]+auipc.*
+[ 	]+10018:[ 	]+[0-9a-f]+[ 	]+ld[ 	]+.*# 20010 <.*>
+
+Disassembly of section .text.got_global:
+
+000000000001001c <.text.got_global>:
+[ 	]+1001c:[ 	]+[0-9a-f]+[ 	]+auipc.*
+[ 	]+10020:[ 	]+[0-9a-f]+[ 	]+ld[ 	]+.*# 20020 <.*>
+
+Disassembly of section .text.got_global_abs:
+
+0000000000010024 <.text.got_global_abs>:
+[ 	]+10024:[ 	]+[0-9a-f]+[ 	]+auipc.*
+[ 	]+10028:[ 	]+[0-9a-f]+[ 	]+ld[ 	]+.*# 20028 <.*>
+
+Disassembly of section .text.got_weak_undef:
+
+000000000001002c <.text.got_weak_undef>:
+[ 	]+1002c:[ 	]+[0-9a-f]+[ 	]+auipc.*
+[ 	]+10030:[ 	]+[0-9a-f]+[ 	]+ld[ 	]+.*# 20030 <.*>
+
+Disassembly of section .text.got_DYNAMIC:
+
+0000000000010034 <.text.got_DYNAMIC>:
+[ 	]+10034:[ 	]+[0-9a-f]+[ 	]+auipc.*
+[ 	]+10038:[ 	]+[0-9a-f]+[ 	]+ld[ 	]+.*# 20018 <.*>
+#...
diff --git a/ld/testsuite/ld-riscv-elf/relr-got-rv64-pic.rd b/ld/testsuite/ld-riscv-elf/relr-got-rv64-pic.rd
new file mode 100644
index 00000000000..4f8b4edb95e
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/relr-got-rv64-pic.rd
@@ -0,0 +1,11 @@ 
+Relocation section '\.rela\.dyn'.*contains 3 entries:
+[ 	]+Offset[ 	]+Info[ 	]+Type[ 	]+.*
+0000000000020020[ 	]+[0-9a-f]+[ 	]+R_RISCV_64[ 	]+0000000000010008[ 	]+sym_global \+ 0
+0000000000020028[ 	]+[0-9a-f]+[ 	]+R_RISCV_64[ 	]+000000000000002a[ 	]+sym_global_abs \+ 0
+0000000000020030[ 	]+[0-9a-f]+[ 	]+R_RISCV_64[ 	]+0000000000000000[ 	]+sym_weak_undef \+ 0
+
+Relocation section '\.relr\.dyn'.*contains 2 entries which relocate 3 locations:
+Index:[ 	]+Entry[ 	]+Address[ 	]+Symbolic Address
+0000:[ 	]+0000000000020008[ 	]+0000000000020008[ 	]+_GLOBAL_OFFSET_TABLE_ \+ 0x8
+0001:[ 	]+0000000000000007[ 	]+0000000000020010[ 	]+_GLOBAL_OFFSET_TABLE_ \+ 0x10
+[ 	]+0000000000020018[ 	]+_GLOBAL_OFFSET_TABLE_ \+ 0x18
diff --git a/ld/testsuite/ld-riscv-elf/relr-got-rv64-pie.d b/ld/testsuite/ld-riscv-elf/relr-got-rv64-pie.d
new file mode 100644
index 00000000000..b4c8b435de4
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/relr-got-rv64-pie.d
@@ -0,0 +1,43 @@ 
+#...
+Contents of section .got:
+ 20000 00003412 00000000 00000100 00000000.*
+ 20010 04000100 00000000 00003412 00000000.*
+ 20020 08000100 00000000 2a000000 00000000.*
+ 20030 00000000 00000000.*
+#...
+Disassembly of section .text.got_local:
+
+000000000001000c <.text.got_local>:
+[ 	]+1000c:[ 	]+[0-9a-f]+[ 	]+auipc.*
+[ 	]+10010:[ 	]+[0-9a-f]+[ 	]+ld[ 	]+.*# 20008 <.*>
+
+Disassembly of section .text.got_hidden:
+
+0000000000010014 <.text.got_hidden>:
+[ 	]+10014:[ 	]+[0-9a-f]+[ 	]+auipc.*
+[ 	]+10018:[ 	]+[0-9a-f]+[ 	]+ld[ 	]+.*# 20010 <.*>
+
+Disassembly of section .text.got_global:
+
+000000000001001c <.text.got_global>:
+[ 	]+1001c:[ 	]+[0-9a-f]+[ 	]+auipc.*
+[ 	]+10020:[ 	]+[0-9a-f]+[ 	]+ld[ 	]+.*# 20020 <.*>
+
+Disassembly of section .text.got_global_abs:
+
+0000000000010024 <.text.got_global_abs>:
+[ 	]+10024:[ 	]+[0-9a-f]+[ 	]+auipc.*
+[ 	]+10028:[ 	]+[0-9a-f]+[ 	]+ld[ 	]+.*# 20028 <.*>
+
+Disassembly of section .text.got_weak_undef:
+
+000000000001002c <.text.got_weak_undef>:
+[ 	]+1002c:[ 	]+[0-9a-f]+[ 	]+auipc.*
+[ 	]+10030:[ 	]+[0-9a-f]+[ 	]+ld[ 	]+.*# 20030 <.*>
+
+Disassembly of section .text.got_DYNAMIC:
+
+0000000000010034 <.text.got_DYNAMIC>:
+[ 	]+10034:[ 	]+[0-9a-f]+[ 	]+auipc.*
+[ 	]+10038:[ 	]+[0-9a-f]+[ 	]+ld[ 	]+.*# 20018 <.*>
+#...
diff --git a/ld/testsuite/ld-riscv-elf/relr-got-rv64-pie.rd b/ld/testsuite/ld-riscv-elf/relr-got-rv64-pie.rd
new file mode 100644
index 00000000000..6776d438cb3
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/relr-got-rv64-pie.rd
@@ -0,0 +1,11 @@ 
+Relocation section '\.rela\.dyn'.*contains 2 entries:
+[ 	]+Offset[ 	]+Info[ 	]+Type[ 	]+.*
+0000000000000000[ 	]+[0-9a-f]+[ 	]+R_RISCV_NONE[ 	]+0
+0000000000020030[ 	]+[0-9a-f]+[ 	]+R_RISCV_64[ 	]+0000000000000000[ 	]+sym_weak_undef \+ 0
+
+Relocation section '\.relr\.dyn'.*contains 2 entries which relocate 4 locations:
+Index:[ 	]+Entry[ 	]+Address[ 	]+Symbolic Address
+0000:[ 	]+0000000000020008[ 	]+0000000000020008[ 	]+_GLOBAL_OFFSET_TABLE_ \+ 0x8
+0001:[ 	]+000000000000000f[ 	]+0000000000020010[ 	]+_GLOBAL_OFFSET_TABLE_ \+ 0x10
+[ 	]+0000000000020018[ 	]+_GLOBAL_OFFSET_TABLE_ \+ 0x18
+[ 	]+0000000000020020[ 	]+_GLOBAL_OFFSET_TABLE_ \+ 0x20
diff --git a/ld/testsuite/ld-riscv-elf/relr-got.s b/ld/testsuite/ld-riscv-elf/relr-got.s
new file mode 100644
index 00000000000..8c1e54b4cb8
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/relr-got.s
@@ -0,0 +1,43 @@ 
+# Test GOT relocations with DT_RELR.
+
+.text
+.option norelax
+.option pic
+.global _start
+_start:
+
+sym_local:
+	nop
+
+.global sym_hidden
+.hidden sym_hidden
+sym_hidden:
+	nop
+
+.global sym_global
+sym_global:
+	nop
+
+.global sym_global_abs
+.set sym_global_abs, 42
+
+.global sym_weak_undef
+.weak sym_weak_undef
+
+.section .text.got_local, "ax"
+	la      x1, sym_local
+
+.section .text.got_hidden, "ax"
+	la      x1, sym_hidden
+
+.section .text.got_global, "ax"
+	la      x1, sym_global
+
+.section .text.got_global_abs, "ax"
+	la	x1, sym_global_abs
+
+.section .text.got_weak_undef, "ax"
+	la      x1, sym_weak_undef
+
+.section .text.got_DYNAMIC, "ax"
+	la      x1, _DYNAMIC
diff --git a/ld/testsuite/ld-riscv-elf/relr-relocs.ld b/ld/testsuite/ld-riscv-elf/relr-relocs.ld
new file mode 100644
index 00000000000..ad130d4e95f
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/relr-relocs.ld
@@ -0,0 +1,12 @@ 
+OUTPUT_ARCH (riscv)
+ENTRY (_start)
+SECTIONS
+{
+  . = 0x10000;
+  .text : { *(.got_*) *(.text)}
+  . = 0x20000;
+  .got : { *(.got) *(.got.plt)}
+  . = 0x12340000;
+  .data : { *(.data) }
+  .riscv.attributes 0 : { *(.riscv.atttributes) }
+}
diff --git a/ld/testsuite/ld-riscv-elf/relr-textrel-pic.d b/ld/testsuite/ld-riscv-elf/relr-textrel-pic.d
new file mode 100644
index 00000000000..c8ec1983d61
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/relr-textrel-pic.d
@@ -0,0 +1,14 @@ 
+#source: relr-textrel.s
+#ld: -shared -z pack-relative-relocs -T relr-relocs.ld
+#readelf: -drW
+
+#...
+.*\(TEXTREL\)[ 	]+0x0
+#...
+.*\(RELR\).*
+.*\(RELRSZ\)[ 	]+(4|8) \(bytes\)
+.*\(RELRENT\)[ 	]+(4|8) \(bytes\)
+#...
+Relocation section '\.relr\.dyn' .* contains 1 entry which relocates 1 location:
+Index:[ 	]+Entry[ 	]+Address[ 	]+Symbolic Address
+0000:[ 	]+(00000000|)00010000[ 	]+(00000000|)00010000[ 	]+_start
diff --git a/ld/testsuite/ld-riscv-elf/relr-textrel-pie.d b/ld/testsuite/ld-riscv-elf/relr-textrel-pie.d
new file mode 100644
index 00000000000..fa5e8e3b5b7
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/relr-textrel-pie.d
@@ -0,0 +1,14 @@ 
+#source: relr-textrel.s
+#ld: -pie -z pack-relative-relocs -T relr-relocs.ld
+#readelf: -drW
+
+#...
+.*\(TEXTREL\)[ 	]+0x0
+#...
+.*\(RELR\).*
+.*\(RELRSZ\)[ 	]+(4|8) \(bytes\)
+.*\(RELRENT\)[ 	]+(4|8) \(bytes\)
+#...
+Relocation section '\.relr\.dyn' .* contains 1 entry which relocates 1 location:
+Index:[ 	]+Entry[ 	]+Address[ 	]+Symbolic Address
+0000:[ 	]+(00000000|)00010000[ 	]+(00000000|)00010000[ 	]+_start
diff --git a/ld/testsuite/ld-riscv-elf/relr-textrel.s b/ld/testsuite/ld-riscv-elf/relr-textrel.s
new file mode 100644
index 00000000000..7e0eecbc527
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/relr-textrel.s
@@ -0,0 +1,10 @@ 
+# Test DT_RELR with DT_TEXTREL.
+
+.text
+.p2align 3
+.global _start
+_start:
+.global foo
+.hidden foo
+foo:
+.quad foo