[PATCH 1/3] RISC-V: Add support for vendor-specific relocations

Ethan Y. C. Liang ycl669@andestech.com
Wed Nov 26 08:47:53 GMT 2025
Support vendor-specific relocations in RISC-V ELF according to the
specification [1], allowing relocations in the 192–255 range to be
independently defined and used by multiple vendors.

The patch is adapted from Alexey's patch [2].

Contributors:
  Ethan Y. C. Liang <ycl669@andestech.com>
  Yu-Qi Liu <yuqiliu@andestech.com>

bfd/ChangeLog

	* bfd-in2.h: Add BFD_RELOC_RISCV_VENDOR.
	* libbfd.h: Likewise.
	* reloc.c: Likewise.
	* elfnn-riscv.c: Add support for creating and parsing vendor
	  relocation pairs.
	* elfxx-riscv.c: Likewise.
	* elfxx-riscv.h: Likewise.

binutils/ChangeLog

	* readelf.c: Add support for parsing vendor relocation pairs.

gas/ChangeLog

	* config/tc-riscv.c: Add support for creating vendor relocation
	  pairs.

include/ChangeLog

	* elf/riscv.h: Add support for vendor enum, vendor id, and vendor
	  string.

[1] https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-elf.adoc#nonstandard-relocations-aka-vendor-specific-relocations
[2] https://sourceware.org/pipermail/binutils/2025-April/140484.html
---
 bfd/bfd-in2.h         |   1 +
 bfd/elfnn-riscv.c     | 104 +++++++++++++++++---
 bfd/elfxx-riscv.c     | 221 +++++++++++++++++++++++++++++++++++++++---
 bfd/elfxx-riscv.h     |  15 ++-
 bfd/libbfd.h          |   1 +
 bfd/reloc.c           |   2 +
 binutils/readelf.c    |  22 ++++-
 gas/config/tc-riscv.c |  64 ++++++++++++
 include/elf/riscv.h   |  61 ++++++++++--
 9 files changed, 455 insertions(+), 36 deletions(-)

diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h
index 5e7c6ddf1ee..06e78a30a2b 100644
--- a/bfd/bfd-in2.h
+++ b/bfd/bfd-in2.h
@@ -5449,6 +5449,7 @@ enum bfd_reloc_code_real
   BFD_RELOC_RISCV_32_PCREL,
   BFD_RELOC_RISCV_SET_ULEB128,
   BFD_RELOC_RISCV_SUB_ULEB128,
+  BFD_RELOC_RISCV_VENDOR,
 
   /* Renesas RL78 Relocations.  */
   BFD_RELOC_RL78_NEG8,
diff --git a/bfd/elfnn-riscv.c b/bfd/elfnn-riscv.c
index 09cf7076733..f9e2f3a7597 100644
--- a/bfd/elfnn-riscv.c
+++ b/bfd/elfnn-riscv.c
@@ -284,7 +284,28 @@ riscv_info_to_howto_rela (bfd *abfd,
 			  arelent *cache_ptr,
 			  Elf_Internal_Rela *dst)
 {
-  cache_ptr->howto = riscv_elf_rtype_to_howto (abfd, ELFNN_R_TYPE (dst->r_info));
+  static enum elf_riscv_vendor_id vendor_id = RISCV_VENDOR_ID_NONE;
+  unsigned int r_type = ELFNN_R_TYPE (dst->r_info);
+
+  cache_ptr->howto
+      = riscv_elf_rtype_to_howto (abfd, ELFNN_R_TYPE (dst->r_info), vendor_id);
+  vendor_id = RISCV_VENDOR_ID_NONE;
+
+  /* Record the vendor id for use by the next relocation.  */
+  if (r_type == R_RISCV_VENDOR)
+    {
+      const char *vendor_str = bfd_asymbol_name (*cache_ptr->sym_ptr_ptr);
+      vendor_id = riscv_vendor_str_to_id (vendor_str);
+      if (vendor_id == RISCV_VENDOR_ID_NONE)
+	{
+	  _bfd_error_handler (_("error: %pB: vendor-specific (%s) "
+				 "relocations are not supported"),
+			      abfd, vendor_str);
+	  bfd_set_error (bfd_error_bad_value);
+	  return false;
+	}
+    }
+
   return cache_ptr->howto != NULL;
 }
 
@@ -906,7 +927,8 @@ riscv_elf_record_got_reference (bfd *abfd, struct bfd_link_info *info,
 static bool
 bad_static_reloc (bfd *abfd, unsigned r_type, struct elf_link_hash_entry *h)
 {
-  reloc_howto_type * r = riscv_elf_rtype_to_howto (abfd, r_type);
+  reloc_howto_type *r
+      = riscv_elf_rtype_to_howto (abfd, r_type, RISCV_VENDOR_ID_NONE);
 
   /* We propably can improve the information to tell users that they
      should be recompile the code with -fPIC or -fPIE, just like what
@@ -1114,8 +1136,8 @@ riscv_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 		      name = bfd_elf_sym_name (abfd, symtab_hdr, sym, NULL);
 		    }
 
-		  reloc_howto_type *r_t =
-			riscv_elf_rtype_to_howto (abfd, r_type);
+		  reloc_howto_type *r_t = riscv_elf_rtype_to_howto (
+		      abfd, r_type, RISCV_VENDOR_ID_NONE);
 		  _bfd_error_handler
 		    (_("%pB: relocation %s against absolute symbol `%s' can "
 		       "not be used when making a shared object"),
@@ -1157,7 +1179,8 @@ riscv_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 	      if (is_abs_symbol)
 		break;
 
-	      reloc_howto_type *r_t = riscv_elf_rtype_to_howto (abfd, r_type);
+	      reloc_howto_type *r_t = riscv_elf_rtype_to_howto (
+		  abfd, r_type, RISCV_VENDOR_ID_NONE);
 	      _bfd_error_handler
 		(_("%pB: relocation %s against non-absolute symbol `%s' can "
 		   "not be used in RVNN when making a shared object"),
@@ -1194,7 +1217,8 @@ riscv_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 		}
 	    }
 
-	  reloc_howto_type *r = riscv_elf_rtype_to_howto (abfd, r_type);
+	  reloc_howto_type *r = riscv_elf_rtype_to_howto (
+	      abfd, r_type, RISCV_VENDOR_ID_NONE);
 	  if (RISCV_NEED_DYNAMIC_RELOC (r->pc_relative, info, h, sec))
 	    {
 	      struct elf_dyn_relocs *p;
@@ -1975,7 +1999,8 @@ perform_relocation (const reloc_howto_type *howto,
 		    bfd_vma value,
 		    asection *input_section,
 		    bfd *input_bfd,
-		    bfd_byte *contents)
+		    bfd_byte *contents,
+		    enum elf_riscv_vendor_id vendor_id)
 {
   if (howto->pc_relative)
     value -= sec_addr (input_section) + rel->r_offset;
@@ -1984,6 +2009,19 @@ perform_relocation (const reloc_howto_type *howto,
   if (ELFNN_R_TYPE (rel->r_info) != R_RISCV_SUB_ULEB128)
     value += rel->r_addend;
 
+  if (vendor_id != RISCV_VENDOR_ID_NONE)
+    {
+      bfd_reloc_status_type status = bfd_reloc_ok;
+      switch (vendor_id)
+	{
+	default:
+	  return bfd_reloc_notsupported;
+	}
+      if (status != bfd_reloc_ok)
+	return status;
+      goto finish_relocation;
+    }
+
   switch (ELFNN_R_TYPE (rel->r_info))
     {
     case R_RISCV_HI20:
@@ -2139,6 +2177,8 @@ perform_relocation (const reloc_howto_type *howto,
       return bfd_reloc_notsupported;
     }
 
+finish_relocation:
+
   bfd_vma word;
   if (riscv_is_insn_reloc (howto))
     word = riscv_get_insn (howto->bitsize, contents + rel->r_offset);
@@ -2361,7 +2401,7 @@ riscv_resolve_pcrel_lo_relocs (riscv_pcrel_relocs *p)
 	}
 
       perform_relocation (r->howto, r->reloc, entry->value, r->input_section,
-			  input_bfd, r->contents);
+			  input_bfd, r->contents, RISCV_VENDOR_ID_NONE);
 
       /* The corresponding R_RISCV_GOT_PCREL_HI20 and R_RISCV_PCREL_HI20 are
 	 converted to R_RISCV_HI20, so try to convert R_RISCV_PCREL_LO12_I/S
@@ -2436,6 +2476,7 @@ riscv_elf_relocate_section (bfd *output_bfd,
   bfd_vma *local_got_offsets = elf_local_got_offsets (input_bfd);
   bfd_vma uleb128_set_vma = 0;
   Elf_Internal_Rela *uleb128_set_rel = NULL;
+  Elf_Internal_Rela *vendor_rel = NULL;
   bool absolute;
 
   if (!riscv_init_pcrel_relocs (&pcrel_relocs))
@@ -2455,11 +2496,34 @@ riscv_elf_relocate_section (bfd *output_bfd,
       bool unresolved_reloc, is_ie = false, is_desc = false;
       bfd_vma pc = sec_addr (input_section) + rel->r_offset;
       int r_type = ELFNN_R_TYPE (rel->r_info), tls_type;
-      reloc_howto_type *howto = riscv_elf_rtype_to_howto (input_bfd, r_type);
+      reloc_howto_type *howto;
       const char *msg = NULL;
       bool resolved_to_zero;
       bool via_plt = false;
       bool relative_got = false;
+      enum elf_riscv_vendor_id vendor_id = RISCV_VENDOR_ID_NONE;
+
+      if (vendor_rel != NULL)
+	{
+	  r_symndx = ELFNN_R_SYM (vendor_rel->r_info);
+	  sym = bfd_sym_from_r_symndx (&htab->elf.sym_cache, input_bfd,
+				       r_symndx);
+	  name = bfd_elf_sym_name (input_bfd, symtab_hdr, sym, NULL);
+	  vendor_id = riscv_vendor_str_to_id (name);
+	  if (vendor_id == RISCV_VENDOR_ID_NONE)
+	    {
+	      _bfd_error_handler
+		(_("error: %pB: vendor-specific (%s) relocations are "
+		   "not supported"),
+		 input_bfd, name);
+	      bfd_set_error (bfd_error_bad_value);
+	      return false;
+	    }
+	  name = NULL;
+	  vendor_rel = NULL;
+	}
+
+      howto = riscv_elf_rtype_to_howto (input_bfd, r_type, vendor_id);
 
       if (howto == NULL)
 	continue;
@@ -2769,6 +2833,14 @@ riscv_elf_relocate_section (bfd *output_bfd,
 		 && h != NULL
 		 && h->plt.offset != MINUS_ONE);
 
+      /* Expected vendor-specific relocation if previous
+	 type was R_RISCV_VENDOR.  */
+      switch (vendor_id)
+	{
+	default:
+	  break;
+	}
+
       switch (r_type)
 	{
 	case R_RISCV_NONE:
@@ -2796,6 +2868,10 @@ riscv_elf_relocate_section (bfd *output_bfd,
 	  /* These require no special handling beyond perform_relocation.  */
 	  break;
 
+	case R_RISCV_VENDOR:
+	  vendor_rel = rel;
+	  continue;
+
 	case R_RISCV_SET_ULEB128:
 	  if (uleb128_set_rel == NULL)
 	    {
@@ -2943,8 +3019,8 @@ riscv_elf_relocate_section (bfd *output_bfd,
 						    &relocation, contents,
 						    howto);
 	      /* Update howto if relocation is changed.  */
-	      howto = riscv_elf_rtype_to_howto (input_bfd,
-						ELFNN_R_TYPE (rel->r_info));
+	      howto = riscv_elf_rtype_to_howto (
+		  input_bfd, ELFNN_R_TYPE (rel->r_info), RISCV_VENDOR_ID_NONE);
 	      if (howto == NULL)
 		r = bfd_reloc_notsupported;
 	      else if (!riscv_record_pcrel_hi_reloc (&pcrel_relocs, pc,
@@ -3091,8 +3167,8 @@ riscv_elf_relocate_section (bfd *output_bfd,
 	  absolute = riscv_zero_pcrel_hi_reloc (rel, info, pc, &relocation,
 						contents, howto);
 	  /* Update howto if relocation is changed.  */
-	  howto = riscv_elf_rtype_to_howto (input_bfd,
-					    ELFNN_R_TYPE (rel->r_info));
+	  howto = riscv_elf_rtype_to_howto (
+	      input_bfd, ELFNN_R_TYPE (rel->r_info), RISCV_VENDOR_ID_NONE);
 	  if (howto == NULL)
 	    r = bfd_reloc_notsupported;
 	  else if (!riscv_record_pcrel_hi_reloc (&pcrel_relocs, pc,
@@ -3376,7 +3452,7 @@ riscv_elf_relocate_section (bfd *output_bfd,
  do_relocation:
       if (r == bfd_reloc_ok)
 	r = perform_relocation (howto, rel, relocation, input_section,
-				input_bfd, contents);
+				input_bfd, contents, vendor_id);
 
       /* We should have already detected the error and set message before.
 	 If the error message isn't set since the linker runs out of memory
diff --git a/bfd/elfxx-riscv.c b/bfd/elfxx-riscv.c
index 085d77923a0..1a391c51f2b 100644
--- a/bfd/elfxx-riscv.c
+++ b/bfd/elfxx-riscv.c
@@ -876,7 +876,56 @@ 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 */
+
+  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), EMPTY_HOWTO (77),
+  EMPTY_HOWTO (78), EMPTY_HOWTO (79), EMPTY_HOWTO (80), EMPTY_HOWTO (81),
+  EMPTY_HOWTO (82), EMPTY_HOWTO (83), EMPTY_HOWTO (84), EMPTY_HOWTO (85),
+  EMPTY_HOWTO (86), EMPTY_HOWTO (87), EMPTY_HOWTO (88), EMPTY_HOWTO (89),
+  EMPTY_HOWTO (90), EMPTY_HOWTO (91), EMPTY_HOWTO (92), EMPTY_HOWTO (93),
+  EMPTY_HOWTO (94), EMPTY_HOWTO (95), EMPTY_HOWTO (96), EMPTY_HOWTO (97),
+  EMPTY_HOWTO (98), EMPTY_HOWTO (99), EMPTY_HOWTO (100), EMPTY_HOWTO (101),
+  EMPTY_HOWTO (102), EMPTY_HOWTO (103), EMPTY_HOWTO (104), EMPTY_HOWTO (105),
+  EMPTY_HOWTO (106), EMPTY_HOWTO (107), EMPTY_HOWTO (108), EMPTY_HOWTO (109),
+  EMPTY_HOWTO (110), EMPTY_HOWTO (111), EMPTY_HOWTO (112), EMPTY_HOWTO (113),
+  EMPTY_HOWTO (114), EMPTY_HOWTO (115), EMPTY_HOWTO (116), EMPTY_HOWTO (117),
+  EMPTY_HOWTO (118), EMPTY_HOWTO (119), EMPTY_HOWTO (120), EMPTY_HOWTO (121),
+  EMPTY_HOWTO (122), EMPTY_HOWTO (123), EMPTY_HOWTO (124), EMPTY_HOWTO (125),
+  EMPTY_HOWTO (126), EMPTY_HOWTO (127), EMPTY_HOWTO (128), EMPTY_HOWTO (129),
+  EMPTY_HOWTO (130), EMPTY_HOWTO (131), EMPTY_HOWTO (132), EMPTY_HOWTO (133),
+  EMPTY_HOWTO (134), EMPTY_HOWTO (135), EMPTY_HOWTO (136), EMPTY_HOWTO (137),
+  EMPTY_HOWTO (138), EMPTY_HOWTO (139), EMPTY_HOWTO (140), EMPTY_HOWTO (141),
+  EMPTY_HOWTO (142), EMPTY_HOWTO (143), EMPTY_HOWTO (144), EMPTY_HOWTO (145),
+  EMPTY_HOWTO (146), EMPTY_HOWTO (147), EMPTY_HOWTO (148), EMPTY_HOWTO (149),
+  EMPTY_HOWTO (150), EMPTY_HOWTO (151), EMPTY_HOWTO (152), EMPTY_HOWTO (153),
+  EMPTY_HOWTO (154), EMPTY_HOWTO (155), EMPTY_HOWTO (156), EMPTY_HOWTO (157),
+  EMPTY_HOWTO (158), EMPTY_HOWTO (159), EMPTY_HOWTO (160), EMPTY_HOWTO (161),
+  EMPTY_HOWTO (162), EMPTY_HOWTO (163), EMPTY_HOWTO (164), EMPTY_HOWTO (165),
+  EMPTY_HOWTO (166), EMPTY_HOWTO (167), EMPTY_HOWTO (168), EMPTY_HOWTO (169),
+  EMPTY_HOWTO (170), EMPTY_HOWTO (171), EMPTY_HOWTO (172), EMPTY_HOWTO (173),
+  EMPTY_HOWTO (174), EMPTY_HOWTO (175), EMPTY_HOWTO (176), EMPTY_HOWTO (177),
+  EMPTY_HOWTO (178), EMPTY_HOWTO (179), EMPTY_HOWTO (180), EMPTY_HOWTO (181),
+  EMPTY_HOWTO (182), EMPTY_HOWTO (183), EMPTY_HOWTO (184), EMPTY_HOWTO (185),
+  EMPTY_HOWTO (186), EMPTY_HOWTO (187), EMPTY_HOWTO (188), EMPTY_HOWTO (189),
+  EMPTY_HOWTO (190),
+
+  /* Paired with a vendor-specific relocation and must be placed immediately
+     before it. Its symbol indicates which vendor owns the relocation.  */
+  HOWTO (R_RISCV_VENDOR,		/* type */
+	 0,				/* rightshift */
+	 0,				/* size */
+	 0,				/* bitsize */
+	 false,				/* pc_relative */
+	 0,				/* bitpos */
+	 complain_overflow_dont,	/* complain_on_overflow */
+	 bfd_elf_generic_reloc,		/* special_function */
+	 "R_RISCV_VENDOR",		/* name */
+	 false,				/* partial_inplace */
+	 0,				/* src_mask */
+	 0,				/* dst_mask */
+	 false)				/* pcrel_offset */
 };
 
 static const reloc_howto_type howto_table_internal[] =
@@ -960,11 +1009,25 @@ static const reloc_howto_type howto_table_internal[] =
 	 false),			/* pcrel_offset */
 };
 
+static const reloc_howto_type *const howto_table_vendors[] =
+{
+  /* Register vendor howto tables here. The order must match the order of
+     strings in the vendor string table.  */
+  NULL
+};
+
+static const size_t howto_table_vendor_sizes[] =
+{
+  /* Register vendor howto table sizes here. The order must match the order of
+     strings in the vendor string table.  */
+  0
+};
+
 /* A mapping from BFD reloc types to RISC-V ELF reloc types.  */
 struct elf_reloc_map
 {
   bfd_reloc_code_real_type bfd_val;
-  enum elf_riscv_reloc_type elf_val;
+  uint32_t elf_val;
 };
 
 static const struct elf_reloc_map riscv_reloc_map[] =
@@ -1020,8 +1083,40 @@ 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_VENDOR, R_RISCV_VENDOR },
+};
+
+static const struct elf_reloc_map *const riscv_vendor_reloc_maps[] =
+{
+  /* Register vendor relocation maps here. The order must match the order of
+     strings in the vendor string table.  */
+  NULL
+};
+
+static const size_t riscv_vendor_reloc_map_sizes[] =
+{
+  /* Register vendor relocation map sizes here. The order must match the order
+     of strings in the vendor string table.  */
+  0
 };
 
+static const char *const *const riscv_vendor_ext_with_reloc_tables[] =
+{
+  /* Register vendor extension-with-relocation tables here. The order must
+     match the order of strings in the vendor string table.  */
+  NULL
+};
+
+static const size_t riscv_vendor_ext_with_reloc_table_sizes[] =
+{
+  /* Register vendor extension-with-relocation table sizes here. The order must
+     match the order of strings in the vendor string table.  */
+  0
+};
+
+static const char *const riscv_vendor_str_table[] =
+RISCV_VENDOR_STR_TABLE_INITIALIZER;
+
 struct riscv_profiles
 {
   const char *profile_name;
@@ -1034,42 +1129,121 @@ reloc_howto_type *
 riscv_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED,
 			 bfd_reloc_code_real_type code)
 {
-  unsigned int i;
+  unsigned int i, j;
 
   for (i = 0; i < ARRAY_SIZE (riscv_reloc_map); i++)
     if (riscv_reloc_map[i].bfd_val == code)
       return &howto_table[(int) riscv_reloc_map[i].elf_val];
 
+  for (i = 0; i < ARRAY_SIZE (riscv_vendor_reloc_maps); i++)
+    for (j = 0; j < riscv_vendor_reloc_map_sizes[i]; j++)
+      if (riscv_vendor_reloc_maps[i][j].bfd_val == code
+	  && riscv_vendor_reloc_maps[i][j].elf_val > R_RISCV_VENDOR
+	  && riscv_vendor_reloc_maps[i][j].elf_val
+		 <= R_RISCV_VENDOR + howto_table_vendor_sizes[i])
+	return &howto_table_vendors[i][(
+	    int)(riscv_vendor_reloc_maps[i][j].elf_val - R_RISCV_VENDOR - 1)];
+
   bfd_set_error (bfd_error_bad_value);
   return NULL;
 }
 
+/* Given a BFD reloc type, return a vendor string.  */
+
+const char *
+riscv_reloc_type_to_vendor_str (bfd_reloc_code_real_type code)
+{
+  unsigned int i, j;
+
+  for (i = 0; i < ARRAY_SIZE (riscv_reloc_map); i++)
+    if (riscv_reloc_map[i].bfd_val == code)
+      return NULL;
+
+  for (i = 0; i < ARRAY_SIZE (riscv_vendor_reloc_maps); i++)
+    for (j = 0; j < riscv_vendor_reloc_map_sizes[i]; j++)
+      if (riscv_vendor_reloc_maps[i][j].bfd_val == code)
+	return riscv_vendor_str_table[i];
+
+  return NULL;
+}
+
 reloc_howto_type *
 riscv_reloc_name_lookup (bfd *abfd ATTRIBUTE_UNUSED, const char *r_name)
 {
-  unsigned int i;
+  unsigned int i, j;
 
   for (i = 0; i < ARRAY_SIZE (howto_table); i++)
     if (howto_table[i].name && strcasecmp (howto_table[i].name, r_name) == 0)
       return &howto_table[i];
 
+  for (i = 0; i < ARRAY_SIZE (howto_table_vendors); i++)
+    for (j = 0; j < howto_table_vendor_sizes[i]; j++)
+      if (howto_table_vendors[i][j].name
+	  && strcasecmp (howto_table_vendors[i][j].name, r_name) == 0)
+	return &howto_table_vendors[i][j];
+
   return NULL;
 }
 
 reloc_howto_type *
-riscv_elf_rtype_to_howto (bfd *abfd, unsigned int r_type)
+riscv_elf_rtype_to_howto (bfd *abfd, unsigned int r_type,
+			  enum elf_riscv_vendor_id vendor_id)
 {
-  if (r_type < ARRAY_SIZE (howto_table))
-    return &howto_table[r_type];
-  else if (r_type < R_RISCV_max + ARRAY_SIZE (howto_table_internal))
-    return &howto_table_internal[r_type - R_RISCV_max];
+  if (vendor_id == RISCV_VENDOR_ID_NONE)
+    {
+      if (r_type >= R_RISCV_internal_first
+	  && r_type < (R_RISCV_internal_first
+		       + ARRAY_SIZE (howto_table_internal)))
+	return &howto_table_internal[r_type - R_RISCV_internal_first];
+      else if (r_type < ARRAY_SIZE (howto_table))
+	return &howto_table[r_type];
+    }
   else
     {
-      (*_bfd_error_handler) (_("%pB: unsupported relocation type %#x"),
-			     abfd, r_type);
-      bfd_set_error (bfd_error_bad_value);
-      return NULL;
+      if (vendor_id > RISCV_VENDOR_ID_NONE
+	  && vendor_id < RISCV_VENDOR_ID_COUNT
+	  && r_type > R_RISCV_VENDOR
+	  && r_type <= R_RISCV_VENDOR + howto_table_vendor_sizes[vendor_id])
+	return &howto_table_vendors[vendor_id][r_type - R_RISCV_VENDOR - 1];
     }
+
+  if (vendor_id == RISCV_VENDOR_ID_NONE)
+    (*_bfd_error_handler) (_("%pB: unsupported relocation type %#x"), abfd,
+			   r_type);
+  else
+    (*_bfd_error_handler) (
+	_("%pB: unsupported relocation type %#x for vendor id %d"), abfd,
+	r_type, (int)vendor_id);
+
+  bfd_set_error (bfd_error_bad_value);
+  return NULL;
+}
+
+/* Given a vendor id, return a vendor string.  */
+
+const char *
+riscv_vendor_id_to_str (enum elf_riscv_vendor_id vendor_id)
+{
+  if (vendor_id <= RISCV_VENDOR_ID_NONE
+      || vendor_id >= RISCV_VENDOR_ID_COUNT)
+    return NULL;
+
+  return riscv_vendor_str_table[vendor_id];
+}
+
+/* Given a vendor string, return a vendor id.  */
+
+enum elf_riscv_vendor_id
+riscv_vendor_str_to_id (const char *vendor_str)
+{
+  unsigned int i;
+
+  for (i = 0; i < ARRAY_SIZE (riscv_vendor_str_table); i++)
+    if (riscv_vendor_str_table[i] && vendor_str
+	&& strcmp (riscv_vendor_str_table[i], vendor_str) == 0)
+      return i;
+
+  return RISCV_VENDOR_ID_NONE;
 }
 
 /* Special_function of RISCV_ADD and RISCV_SUB relocations.  */
@@ -2790,6 +2964,27 @@ riscv_subset_supports (riscv_parse_subset_t *rps,
   return riscv_lookup_subset (rps->subset_list, feature, &subset);
 }
 
+/* Return true if any extension of the given vendor that uses relocation is
+   supported in the subset list.  */
+
+bool
+riscv_subset_supports_vendor_ext_with_reloc (riscv_parse_subset_t *rps,
+					     enum elf_riscv_vendor_id vendor_id)
+{
+  if (vendor_id <= RISCV_VENDOR_ID_NONE
+      || vendor_id >= RISCV_VENDOR_ID_COUNT)
+    return false;
+
+  unsigned int i;
+
+  for (i = 0; i < riscv_vendor_ext_with_reloc_table_sizes[vendor_id]; i++)
+    if (riscv_subset_supports (
+	    rps, riscv_vendor_ext_with_reloc_tables[vendor_id][i]))
+      return true;
+
+  return false;
+}
+
 /* Each instuction is belonged to an instruction class INSN_CLASS_*.
    Call riscv_subset_supports to make sure if the instuction is valid.  */
 
diff --git a/bfd/elfxx-riscv.h b/bfd/elfxx-riscv.h
index fbbefb5ac18..41631ad4465 100644
--- a/bfd/elfxx-riscv.h
+++ b/bfd/elfxx-riscv.h
@@ -33,6 +33,8 @@ typedef enum
     PLT_ZICFILP_UNLABELED = 0x1   /* Landing pad unlabeled plts.  */
 } riscv_plt_type;
 
+enum elf_riscv_vendor_id;
+
 struct riscv_elf_params
 {
   /* Whether to relax code sequences to GP-relative addressing.  */
@@ -52,8 +54,16 @@ riscv_reloc_name_lookup (bfd *, const char *);
 extern reloc_howto_type *
 riscv_reloc_type_lookup (bfd *, bfd_reloc_code_real_type);
 
+extern const char *
+riscv_reloc_type_to_vendor_str (bfd_reloc_code_real_type);
+
 extern reloc_howto_type *
-riscv_elf_rtype_to_howto (bfd *, unsigned int r_type);
+riscv_elf_rtype_to_howto (bfd *, unsigned int r_type,
+			  enum elf_riscv_vendor_id);
+
+extern const char * riscv_vendor_id_to_str (enum elf_riscv_vendor_id);
+extern enum elf_riscv_vendor_id riscv_vendor_str_to_id (const char *);
+
 
 /* The information of architecture attribute.  */
 struct riscv_subset_t
@@ -124,6 +134,9 @@ riscv_update_subset_norvc (riscv_parse_subset_t *);
 extern bool
 riscv_subset_supports (riscv_parse_subset_t *, const char *);
 
+extern bool
+riscv_subset_supports_vendor_ext_with_reloc (riscv_parse_subset_t *,
+					     enum elf_riscv_vendor_id);
 extern bool
 riscv_multi_subset_supports (riscv_parse_subset_t *, enum riscv_insn_class);
 
diff --git a/bfd/libbfd.h b/bfd/libbfd.h
index f2485d99078..9da65b4ec9d 100644
--- a/bfd/libbfd.h
+++ b/bfd/libbfd.h
@@ -2464,6 +2464,7 @@ static const char *const bfd_reloc_code_real_names[] = { "@@uninitialized@@",
   "BFD_RELOC_RISCV_32_PCREL",
   "BFD_RELOC_RISCV_SET_ULEB128",
   "BFD_RELOC_RISCV_SUB_ULEB128",
+  "BFD_RELOC_RISCV_VENDOR",
   "BFD_RELOC_RL78_NEG8",
   "BFD_RELOC_RL78_NEG16",
   "BFD_RELOC_RL78_NEG24",
diff --git a/bfd/reloc.c b/bfd/reloc.c
index c9d53bb9e11..0ef5e550403 100644
--- a/bfd/reloc.c
+++ b/bfd/reloc.c
@@ -5036,6 +5036,8 @@ ENUMX
   BFD_RELOC_RISCV_SET_ULEB128
 ENUMX
   BFD_RELOC_RISCV_SUB_ULEB128
+ENUMX
+  BFD_RELOC_RISCV_VENDOR
 ENUMDOC
   RISC-V relocations.
 
diff --git a/binutils/readelf.c b/binutils/readelf.c
index fd9722c8afc..6217aded055 100644
--- a/binutils/readelf.c
+++ b/binutils/readelf.c
@@ -2216,6 +2216,7 @@ dump_relocations (Filedata *          filedata,
   size_t i;
   Elf_Internal_Rela * rels;
   bool res = true;
+  const char *riscv_vendor_str = NULL;
 
   if (rel_type == reltype_unknown)
     rel_type = guess_is_rela (filedata->file_header.e_machine) ? reltype_rela : reltype_rel;
@@ -2444,7 +2445,15 @@ dump_relocations (Filedata *          filedata,
 	  break;
 
 	case EM_RISCV:
-	  rtype = elf_riscv_reloc_type (type);
+	  if (riscv_vendor_str)
+	    {
+	      /* Use special function for vendor-specific relocations.  */
+	      rtype = elf_riscv_vendor_reloc_type (riscv_vendor_str, type);
+	      free ((void *)riscv_vendor_str);
+	      riscv_vendor_str = NULL;
+	    }
+	  else
+	    rtype = elf_riscv_reloc_type (type);
 	  break;
 
 	case EM_ALPHA:
@@ -2798,6 +2807,11 @@ dump_relocations (Filedata *          filedata,
 		}
 	      else
 		{
+		  /* Record RISC-V vendor string for use by the next
+		     relocation.  */
+		  if (filedata->file_header.e_machine == EM_RISCV
+		      && rtype && strcmp (rtype, "R_RISCV_VENDOR") == 0)
+		    riscv_vendor_str = strdup (strtab + psym->st_name);
 		  if (dump_reloc)
 		    {
 		      print_symbol_name (22, strtab + psym->st_name);
@@ -2887,6 +2901,12 @@ dump_relocations (Filedata *          filedata,
 	}
     }
 
+  if (riscv_vendor_str)
+    {
+      free ((void *)riscv_vendor_str);
+      riscv_vendor_str = NULL;
+    }
+
   free (rels);
 
   return res;
diff --git a/gas/config/tc-riscv.c b/gas/config/tc-riscv.c
index df60c206b82..1893e4ce11d 100644
--- a/gas/config/tc-riscv.c
+++ b/gas/config/tc-riscv.c
@@ -196,6 +196,9 @@ static bool start_assemble = false;
 
 static bool probing_insn_operands;
 
+/* Used for vendor symbol relocation.  */
+segT vendor_section;
+
 /* Set the default_isa_spec.  Return 0 if the spec isn't supported.
    Otherwise, return 1.  */
 
@@ -1963,6 +1966,25 @@ riscv_apply_const_reloc (bfd_reloc_code_real_type reloc_type, bfd_vma value)
     }
 }
 
+/* Add a vendor symbol relocation before any vendor-specific relocation.  */
+
+static void
+add_vendor_symbol_relocation (fragS *frag, long where, long size,
+			      const char *vendor_str)
+{
+  if (!vendor_str)
+    return;
+
+  symbolS *sym;
+
+  /* Find the vendor symbol created in md_assemble ().  */
+  sym = symbol_find (vendor_str);
+  if (!sym)
+    abort ();
+
+  fix_new (frag, where, size, sym, 0, false, BFD_RELOC_RISCV_VENDOR);
+}
+
 /* Output an instruction.  IP is the instruction information.
    ADDRESS_EXPR is an operand of the instruction to be used with
    RELOC_TYPE.  */
@@ -2004,6 +2026,10 @@ append_insn (struct riscv_cl_insn *ip, expressionS *address_expr,
 	    as_bad (_("internal: unsupported RISC-V relocation number %d"),
 		    reloc_type);
 
+	  add_vendor_symbol_relocation (
+	      frag_now, frag_more (0) - frag_now->fr_literal, insn_length (ip),
+	      riscv_reloc_type_to_vendor_str (reloc_type));
+
 	  ip->fixp = fix_new_exp (ip->frag, ip->where,
 				  bfd_get_reloc_size (howto),
 				  address_expr, false, reloc_type);
@@ -4407,6 +4433,34 @@ md_assemble (char *str)
     {
       start_assemble = true;
 
+      /* Check if any vendor extension with relocation is supported; if so, a
+	 corresponding vendor symbol must be created.  */
+      unsigned int i;
+
+      for (i = 0; i < RISCV_VENDOR_ID_COUNT; i++)
+	{
+	  if (riscv_subset_supports_vendor_ext_with_reloc (&riscv_rps_as, i))
+	    {
+	      symbolS *sym;
+
+	      /* Create a vendor section to hold the vendor symbol.  */
+	      if (!vendor_section)
+		{
+		  segT saved_now_seg = now_seg;
+		  subsegT saved_now_subseg = now_subseg;
+		  vendor_section = subseg_new (".vendor", 0);
+		  subseg_set (saved_now_seg, saved_now_subseg);
+		}
+
+	      /* Create a corresponding vendor symbol and mark it as used in
+		 reloc to ensure it is not removed later.  */
+	      sym = symbol_new (riscv_vendor_id_to_str (i), vendor_section,
+				&zero_address_frag, 0);
+	      symbol_table_insert (sym);
+	      symbol_mark_used_in_reloc (sym);
+	    }
+	}
+
       riscv_set_abi_by_arch ();
       if (!riscv_set_default_priv_spec (NULL))
        return;
@@ -5000,6 +5054,9 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg)
     case BFD_RELOC_RISCV_ALIGN:
       break;
 
+    case BFD_RELOC_RISCV_VENDOR:
+      break;
+
     default:
       /* We ignore generic BFD relocations we don't know about.  */
       if (bfd_reloc_type_lookup (stdoutput, fixP->fx_r_type) != NULL)
@@ -5442,6 +5499,10 @@ md_convert_frag_branch (fragS *fragp)
 	    /* Just keep the RVC branch.  */
 	    reloc = RELAX_BRANCH_UNCOND (fragp->fr_subtype)
 		    ? BFD_RELOC_RISCV_RVC_JUMP : BFD_RELOC_RISCV_RVC_BRANCH;
+	    /* Maybe it is a vendor-specific relocation in the future.  */
+	    add_vendor_symbol_relocation (
+		fragp, buf - (bfd_byte *)fragp->fr_literal, 2,
+		riscv_reloc_type_to_vendor_str (reloc));
 	    fixp = fix_new_exp (fragp, buf - (bfd_byte *)fragp->fr_literal,
 				2, &exp, false, reloc);
 	    buf += 2;
@@ -5475,6 +5536,9 @@ md_convert_frag_branch (fragS *fragp)
     case 4:
       reloc = RELAX_BRANCH_UNCOND (fragp->fr_subtype)
 	      ? BFD_RELOC_RISCV_JMP : BFD_RELOC_12_PCREL;
+      /* Maybe it is a vendor-specific relocation in the future.  */
+      add_vendor_symbol_relocation (fragp, buf - (bfd_byte *)fragp->fr_literal,
+				    4, riscv_reloc_type_to_vendor_str (reloc));
       fixp = fix_new_exp (fragp, buf - (bfd_byte *)fragp->fr_literal,
 			  4, &exp, false, reloc);
       buf += 4;
diff --git a/include/elf/riscv.h b/include/elf/riscv.h
index 2ea1ae82eb6..8ce9fc98c43 100644
--- a/include/elf/riscv.h
+++ b/include/elf/riscv.h
@@ -95,15 +95,62 @@ 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)
-END_RELOC_NUMBERS (R_RISCV_max)
+  EMPTY_RELOC (R_RISCV_internal_first)
+  RELOC_NUMBER (R_RISCV_VENDOR, 191)
+END_RELOC_NUMBERS ()
 
 /* Internal relocations used exclusively by the relaxation pass.  */
-#define R_RISCV_DELETE  (R_RISCV_max)
-#define R_RISCV_RVC_LUI (R_RISCV_max + 1)
-#define R_RISCV_GPREL_I (R_RISCV_max + 2)
-#define R_RISCV_GPREL_S (R_RISCV_max + 3)
-#define R_RISCV_TPREL_I (R_RISCV_max + 4)
-#define R_RISCV_TPREL_S (R_RISCV_max + 5)
+#define R_RISCV_DELETE  (R_RISCV_internal_first)
+#define R_RISCV_RVC_LUI (R_RISCV_internal_first + 1)
+#define R_RISCV_GPREL_I (R_RISCV_internal_first + 2)
+#define R_RISCV_GPREL_S (R_RISCV_internal_first + 3)
+#define R_RISCV_TPREL_I (R_RISCV_internal_first + 4)
+#define R_RISCV_TPREL_S (R_RISCV_internal_first + 5)
+
+enum elf_riscv_vendor_id
+{
+  RISCV_VENDOR_ID_NONE = -1,
+  /* Register vendor id here. The order must match the order of strings in the
+     vendor string table.  */
+  RISCV_VENDOR_ID_COUNT
+};
+
+#define RISCV_VENDOR_STR_TABLE_INITIALIZER				\
+{									\
+  /* Register vendor strings (defined as macros) here. Remember to	\
+     append a space to each string to avoid symbol conflicts.  */	\
+  NULL									\
+}
+
+/* Used for readelf.  */
+#ifdef RELOC_MACROS_GEN_FUNC
+static const char *const elf_riscv_vendor_str_table[] =
+RISCV_VENDOR_STR_TABLE_INITIALIZER;
+
+static const char *(*elf_riscv_vendor_reloc_type_funcs[]) (unsigned long) =
+{
+  /* Register vendor-defined functions (given a relocation number, return a
+     relocation name string) here. The order must match the order of strings
+     in the vendor string table.  */
+  NULL
+};
+
+/* Given a vendor string and a relocation number, return a relocation name
+   string.  */
+static const char *
+elf_riscv_vendor_reloc_type (const char *vendor_str, unsigned long rtype)
+{
+  unsigned int i;
+
+  /* Choose a vendor-defined function according to the vendor string. */
+  for (i = 0; i < ARRAY_SIZE (elf_riscv_vendor_str_table); i++)
+    if (elf_riscv_vendor_str_table[i] && vendor_str
+	&& strcmp (elf_riscv_vendor_str_table[i], vendor_str) == 0)
+      return elf_riscv_vendor_reloc_type_funcs[i](rtype);
+
+  return NULL;
+}
+#endif
 
 /* Processor specific flags for the ELF header e_flags field.  */
 
-- 
2.49.0



More information about the Binutils mailing list