RISC-V: Add support for vendor-specific relocations

Message ID 5cb6d30110d1312a2005673a710f6d84b916db77.camel@espressif.com
State New
Headers
Series RISC-V: Add support for vendor-specific relocations |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_binutils_build--master-arm success Build passed
linaro-tcwg-bot/tcwg_binutils_build--master-aarch64 success Build passed
linaro-tcwg-bot/tcwg_binutils_check--master-arm success Test passed
linaro-tcwg-bot/tcwg_binutils_check--master-aarch64 success Test passed

Commit Message

Alexey Lapshin April 9, 2025, 8:56 a.m. UTC
  This patch adds the RISC-V vendor-specific relocation enumeration
to cover the full reserved range of custom relocation types from
R_RISCV_CUSTOM0 (192) through R_RISCV_CUSTOM63 (255),
as defined by the RISC-V ELF psABI specification.

Added Espressif's xesplp (hardware loop) extension.

    bfd/
            * bfd-in2.h: Add R_RISCV_VENDOR relocation.
              Add vendor (Espressif) relocations.
            * elfnn-riscv.c: Ditto.
            * elfxx-riscv.h: Ditto.
            * libbfd.h: Ditto.
            * reloc.c: Ditto.
            * elfxx-riscv.c: Ditto. Add xesplp extension.
    binutils/
            * readelf.c: Add riscv vendor-specific relocation support.
    gas/
            * config/tc-riscv.c: Add xesplp extension support.
    opcodes/
            * riscv-dis.c: Add xesplp extension support.
            * riscv-opc.c: Ditto.
    include/
            * elf/riscv.h: Add vednor-specific relocations support.
            * opcode/riscv.h: Add xesplp extension support.
            * opcode/riscv-opc.h: Ditto.
---
 bfd/bfd-in2.h                              |   5 +
 bfd/elfnn-riscv.c                          | 135 ++++++++++-
 bfd/elfxx-riscv.c                          | 246 ++++++++++++++++++++-
 bfd/elfxx-riscv.h                          |   6 +
 bfd/libbfd.h                               |   3 +
 bfd/reloc.c                                |   9 +
 binutils/readelf.c                         |  14 +-
 gas/config/tc-riscv.c                      | 192 ++++++++++++++++
 gas/testsuite/gas/riscv/march-help.l       |   1 +
 gas/testsuite/gas/riscv/xesplp-reloc.d     |  12 +
 gas/testsuite/gas/riscv/xesplp-reloc.s     |   7 +
 include/elf/riscv.h                        | 110 ++++++++-
 include/opcode/riscv-opc.h                 |  20 ++
 include/opcode/riscv.h                     |  24 ++
 ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp |   2 +
 ld/testsuite/ld-riscv-elf/xesplp-reloc.d   |  13 ++
 ld/testsuite/ld-riscv-elf/xesplp-reloc.s   |   7 +
 opcodes/riscv-dis.c                        |  51 +++++
 opcodes/riscv-opc.c                        |   8 +
 19 files changed, 850 insertions(+), 15 deletions(-)
 create mode 100644 gas/testsuite/gas/riscv/xesplp-reloc.d
 create mode 100644 gas/testsuite/gas/riscv/xesplp-reloc.s
 create mode 100644 ld/testsuite/ld-riscv-elf/xesplp-reloc.d
 create mode 100644 ld/testsuite/ld-riscv-elf/xesplp-reloc.s

-- 
2.43.0
  

Comments

Jan Beulich April 9, 2025, 9:35 a.m. UTC | #1
On 09.04.2025 10:56, Alexey Lapshin wrote:
> This patch adds the RISC-V vendor-specific relocation enumeration
> to cover the full reserved range of custom relocation types from
> R_RISCV_CUSTOM0 (192) through R_RISCV_CUSTOM63 (255),
> as defined by the RISC-V ELF psABI specification.

The 1.0 spec indeed mentions this range. However, ...

> Added Espressif's xesplp (hardware loop) extension.
> 
>     bfd/
>             * bfd-in2.h: Add R_RISCV_VENDOR relocation.

... I can't find any mention of this. Where's that specified in a
sufficient stable form?

Jan
  
Alexey Lapshin April 9, 2025, 9:44 a.m. UTC | #2
Thank you for the quick response!
I implemented it based on https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-elf.adoc

Regards,
Alexey

On Wed, 2025-04-09 at 11:35 +0200, Jan Beulich wrote:
> [You don't often get email from jbeulich@suse.com. Learn why this is important at https://aka.ms/LearnAboutSenderIdentification ]
> 
> [External: This email originated outside Espressif]
> 
> On 09.04.2025 10:56, Alexey Lapshin wrote:
> > This patch adds the RISC-V vendor-specific relocation enumeration
> > to cover the full reserved range of custom relocation types from
> > R_RISCV_CUSTOM0 (192) through R_RISCV_CUSTOM63 (255),
> > as defined by the RISC-V ELF psABI specification.
> 
> The 1.0 spec indeed mentions this range. However, ...
> 
> > Added Espressif's xesplp (hardware loop) extension.
> > 
> >     bfd/
> >             * bfd-in2.h: Add R_RISCV_VENDOR relocation.
> 
> ... I can't find any mention of this. Where's that specified in a
> sufficient stable form?
> 
> Jan
  
Jan Beulich April 9, 2025, 10:02 a.m. UTC | #3
On 09.04.2025 11:44, Alexey Lapshin wrote:
> Thank you for the quick response!
> I implemented it based on https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-elf.adoc

Right, I did find that. But aiui it's not stable or even just frozen, yet.
Sending such patches is certainly fine, but they should come with a
warning that they probably shouldn't go in until the spec is finalized.

Jan
  
Nelson Chu April 9, 2025, 7:25 p.m. UTC | #4
Hi Alexey,


On Wed, Apr 9, 2025 at 4:59 PM Alexey Lapshin <alexey.lapshin@espressif.com>
wrote:

> diff --git a/bfd/elfxx-riscv.c b/bfd/elfxx-riscv.c
> index ec254915c76..b9b0001f3f9 100644
> --- a/bfd/elfxx-riscv.c
> +++ b/bfd/elfxx-riscv.c
> @@ -960,6 +960,168 @@ static reloc_howto_type howto_table_internal[] =
>          false),                        /* pcrel_offset */
>  };
>
> +/* This howto table is used solely to provide relocation name strings.  */
> +static reloc_howto_type howto_table_vendor[] = {
> +  HOWTO (R_RISCV_CUSTOM0, 0, 0, 0, false, 0, complain_overflow_dont,
> +        bfd_elf_generic_reloc, "R_RISCV_CUSTOM0", false, 0, 0, false),
> +  HOWTO (R_RISCV_CUSTOM1, 0, 0, 0, false, 0, complain_overflow_dont,
> +        bfd_elf_generic_reloc, "R_RISCV_CUSTOM1", false, 0, 0, false),
> +  HOWTO (R_RISCV_CUSTOM2, 0, 0, 0, false, 0, complain_overflow_dont,
> +        bfd_elf_generic_reloc, "R_RISCV_CUSTOM2", false, 0, 0, false),
> +  HOWTO (R_RISCV_CUSTOM3, 0, 0, 0, false, 0, complain_overflow_dont,
> +        bfd_elf_generic_reloc, "R_RISCV_CUSTOM3", false, 0, 0, false),
> +  HOWTO (R_RISCV_CUSTOM4, 0, 0, 0, false, 0, complain_overflow_dont,
> +        bfd_elf_generic_reloc, "R_RISCV_CUSTOM4", false, 0, 0, false),
> +  HOWTO (R_RISCV_CUSTOM5, 0, 0, 0, false, 0, complain_overflow_dont,
> +        bfd_elf_generic_reloc, "R_RISCV_CUSTOM5", false, 0, 0, false),
> +  HOWTO (R_RISCV_CUSTOM6, 0, 0, 0, false, 0, complain_overflow_dont,
> +        bfd_elf_generic_reloc, "R_RISCV_CUSTOM6", false, 0, 0, false),
> +  HOWTO (R_RISCV_CUSTOM7, 0, 0, 0, false, 0, complain_overflow_dont,
> +        bfd_elf_generic_reloc, "R_RISCV_CUSTOM7", false, 0, 0, false),
> +  HOWTO (R_RISCV_CUSTOM8, 0, 0, 0, false, 0, complain_overflow_dont,
> +        bfd_elf_generic_reloc, "R_RISCV_CUSTOM8", false, 0, 0, false),
> +  HOWTO (R_RISCV_CUSTOM9, 0, 0, 0, false, 0, complain_overflow_dont,
> +        bfd_elf_generic_reloc, "R_RISCV_CUSTOM9", false, 0, 0, false),
> +  HOWTO (R_RISCV_CUSTOM10, 0, 0, 0, false, 0, complain_overflow_dont,
> +        bfd_elf_generic_reloc, "R_RISCV_CUSTOM10", false, 0, 0, false),
> +  HOWTO (R_RISCV_CUSTOM11, 0, 0, 0, false, 0, complain_overflow_dont,
> +        bfd_elf_generic_reloc, "R_RISCV_CUSTOM11", false, 0, 0, false),
> +  HOWTO (R_RISCV_CUSTOM12, 0, 0, 0, false, 0, complain_overflow_dont,
> +        bfd_elf_generic_reloc, "R_RISCV_CUSTOM12", false, 0, 0, false),
> +  HOWTO (R_RISCV_CUSTOM13, 0, 0, 0, false, 0, complain_overflow_dont,
> +        bfd_elf_generic_reloc, "R_RISCV_CUSTOM13", false, 0, 0, false),
> +  HOWTO (R_RISCV_CUSTOM14, 0, 0, 0, false, 0, complain_overflow_dont,
> +        bfd_elf_generic_reloc, "R_RISCV_CUSTOM14", false, 0, 0, false),
> +  HOWTO (R_RISCV_CUSTOM15, 0, 0, 0, false, 0, complain_overflow_dont,
> +        bfd_elf_generic_reloc, "R_RISCV_CUSTOM15", false, 0, 0, false),
> +  HOWTO (R_RISCV_CUSTOM16, 0, 0, 0, false, 0, complain_overflow_dont,
> +        bfd_elf_generic_reloc, "R_RISCV_CUSTOM16", false, 0, 0, false),
> +  HOWTO (R_RISCV_CUSTOM17, 0, 0, 0, false, 0, complain_overflow_dont,
> +        bfd_elf_generic_reloc, "R_RISCV_CUSTOM17", false, 0, 0, false),
> +  HOWTO (R_RISCV_CUSTOM18, 0, 0, 0, false, 0, complain_overflow_dont,
> +        bfd_elf_generic_reloc, "R_RISCV_CUSTOM18", false, 0, 0, false),
> +  HOWTO (R_RISCV_CUSTOM19, 0, 0, 0, false, 0, complain_overflow_dont,
> +        bfd_elf_generic_reloc, "R_RISCV_CUSTOM19", false, 0, 0, false),
> +  HOWTO (R_RISCV_CUSTOM20, 0, 0, 0, false, 0, complain_overflow_dont,
> +        bfd_elf_generic_reloc, "R_RISCV_CUSTOM20", false, 0, 0, false),
> +  HOWTO (R_RISCV_CUSTOM21, 0, 0, 0, false, 0, complain_overflow_dont,
> +        bfd_elf_generic_reloc, "R_RISCV_CUSTOM21", false, 0, 0, false),
> +  HOWTO (R_RISCV_CUSTOM22, 0, 0, 0, false, 0, complain_overflow_dont,
> +        bfd_elf_generic_reloc, "R_RISCV_CUSTOM22", false, 0, 0, false),
> +  HOWTO (R_RISCV_CUSTOM23, 0, 0, 0, false, 0, complain_overflow_dont,
> +        bfd_elf_generic_reloc, "R_RISCV_CUSTOM23", false, 0, 0, false),
> +  HOWTO (R_RISCV_CUSTOM24, 0, 0, 0, false, 0, complain_overflow_dont,
> +        bfd_elf_generic_reloc, "R_RISCV_CUSTOM24", false, 0, 0, false),
> +  HOWTO (R_RISCV_CUSTOM25, 0, 0, 0, false, 0, complain_overflow_dont,
> +        bfd_elf_generic_reloc, "R_RISCV_CUSTOM25", false, 0, 0, false),
> +  HOWTO (R_RISCV_CUSTOM26, 0, 0, 0, false, 0, complain_overflow_dont,
> +        bfd_elf_generic_reloc, "R_RISCV_CUSTOM26", false, 0, 0, false),
> +  HOWTO (R_RISCV_CUSTOM27, 0, 0, 0, false, 0, complain_overflow_dont,
> +        bfd_elf_generic_reloc, "R_RISCV_CUSTOM27", false, 0, 0, false),
> +  HOWTO (R_RISCV_CUSTOM28, 0, 0, 0, false, 0, complain_overflow_dont,
> +        bfd_elf_generic_reloc, "R_RISCV_CUSTOM28", false, 0, 0, false),
> +  HOWTO (R_RISCV_CUSTOM29, 0, 0, 0, false, 0, complain_overflow_dont,
> +        bfd_elf_generic_reloc, "R_RISCV_CUSTOM29", false, 0, 0, false),
> +  HOWTO (R_RISCV_CUSTOM30, 0, 0, 0, false, 0, complain_overflow_dont,
> +        bfd_elf_generic_reloc, "R_RISCV_CUSTOM30", false, 0, 0, false),
> +  HOWTO (R_RISCV_CUSTOM31, 0, 0, 0, false, 0, complain_overflow_dont,
> +        bfd_elf_generic_reloc, "R_RISCV_CUSTOM31", false, 0, 0, false),
> +  HOWTO (R_RISCV_CUSTOM32, 0, 0, 0, false, 0, complain_overflow_dont,
> +        bfd_elf_generic_reloc, "R_RISCV_CUSTOM32", false, 0, 0, false),
> +  HOWTO (R_RISCV_CUSTOM33, 0, 0, 0, false, 0, complain_overflow_dont,
> +        bfd_elf_generic_reloc, "R_RISCV_CUSTOM33", false, 0, 0, false),
> +  HOWTO (R_RISCV_CUSTOM34, 0, 0, 0, false, 0, complain_overflow_dont,
> +        bfd_elf_generic_reloc, "R_RISCV_CUSTOM34", false, 0, 0, false),
> +  HOWTO (R_RISCV_CUSTOM35, 0, 0, 0, false, 0, complain_overflow_dont,
> +        bfd_elf_generic_reloc, "R_RISCV_CUSTOM35", false, 0, 0, false),
> +  HOWTO (R_RISCV_CUSTOM36, 0, 0, 0, false, 0, complain_overflow_dont,
> +        bfd_elf_generic_reloc, "R_RISCV_CUSTOM36", false, 0, 0, false),
> +  HOWTO (R_RISCV_CUSTOM37, 0, 0, 0, false, 0, complain_overflow_dont,
> +        bfd_elf_generic_reloc, "R_RISCV_CUSTOM37", false, 0, 0, false),
> +  HOWTO (R_RISCV_CUSTOM38, 0, 0, 0, false, 0, complain_overflow_dont,
> +        bfd_elf_generic_reloc, "R_RISCV_CUSTOM38", false, 0, 0, false),
> +  HOWTO (R_RISCV_CUSTOM39, 0, 0, 0, false, 0, complain_overflow_dont,
> +        bfd_elf_generic_reloc, "R_RISCV_CUSTOM39", false, 0, 0, false),
> +  HOWTO (R_RISCV_CUSTOM40, 0, 0, 0, false, 0, complain_overflow_dont,
> +        bfd_elf_generic_reloc, "R_RISCV_CUSTOM40", false, 0, 0, false),
> +  HOWTO (R_RISCV_CUSTOM41, 0, 0, 0, false, 0, complain_overflow_dont,
> +        bfd_elf_generic_reloc, "R_RISCV_CUSTOM41", false, 0, 0, false),
> +  HOWTO (R_RISCV_CUSTOM42, 0, 0, 0, false, 0, complain_overflow_dont,
> +        bfd_elf_generic_reloc, "R_RISCV_CUSTOM42", false, 0, 0, false),
> +  HOWTO (R_RISCV_CUSTOM43, 0, 0, 0, false, 0, complain_overflow_dont,
> +        bfd_elf_generic_reloc, "R_RISCV_CUSTOM43", false, 0, 0, false),
> +  HOWTO (R_RISCV_CUSTOM44, 0, 0, 0, false, 0, complain_overflow_dont,
> +        bfd_elf_generic_reloc, "R_RISCV_CUSTOM44", false, 0, 0, false),
> +  HOWTO (R_RISCV_CUSTOM45, 0, 0, 0, false, 0, complain_overflow_dont,
> +        bfd_elf_generic_reloc, "R_RISCV_CUSTOM45", false, 0, 0, false),
> +  HOWTO (R_RISCV_CUSTOM46, 0, 0, 0, false, 0, complain_overflow_dont,
> +        bfd_elf_generic_reloc, "R_RISCV_CUSTOM46", false, 0, 0, false),
> +  HOWTO (R_RISCV_CUSTOM47, 0, 0, 0, false, 0, complain_overflow_dont,
> +        bfd_elf_generic_reloc, "R_RISCV_CUSTOM47", false, 0, 0, false),
> +  HOWTO (R_RISCV_CUSTOM48, 0, 0, 0, false, 0, complain_overflow_dont,
> +        bfd_elf_generic_reloc, "R_RISCV_CUSTOM48", false, 0, 0, false),
> +  HOWTO (R_RISCV_CUSTOM49, 0, 0, 0, false, 0, complain_overflow_dont,
> +        bfd_elf_generic_reloc, "R_RISCV_CUSTOM49", false, 0, 0, false),
> +  HOWTO (R_RISCV_CUSTOM50, 0, 0, 0, false, 0, complain_overflow_dont,
> +        bfd_elf_generic_reloc, "R_RISCV_CUSTOM50", false, 0, 0, false),
> +  HOWTO (R_RISCV_CUSTOM51, 0, 0, 0, false, 0, complain_overflow_dont,
> +        bfd_elf_generic_reloc, "R_RISCV_CUSTOM51", false, 0, 0, false),
> +  HOWTO (R_RISCV_CUSTOM52, 0, 0, 0, false, 0, complain_overflow_dont,
> +        bfd_elf_generic_reloc, "R_RISCV_CUSTOM52", false, 0, 0, false),
> +  HOWTO (R_RISCV_CUSTOM53, 0, 0, 0, false, 0, complain_overflow_dont,
> +        bfd_elf_generic_reloc, "R_RISCV_CUSTOM53", false, 0, 0, false),
> +  HOWTO (R_RISCV_CUSTOM54, 0, 0, 0, false, 0, complain_overflow_dont,
> +        bfd_elf_generic_reloc, "R_RISCV_CUSTOM54", false, 0, 0, false),
> +  HOWTO (R_RISCV_CUSTOM55, 0, 0, 0, false, 0, complain_overflow_dont,
> +        bfd_elf_generic_reloc, "R_RISCV_CUSTOM55", false, 0, 0, false),
> +  HOWTO (R_RISCV_CUSTOM56, 0, 0, 0, false, 0, complain_overflow_dont,
> +        bfd_elf_generic_reloc, "R_RISCV_CUSTOM56", false, 0, 0, false),
> +  HOWTO (R_RISCV_CUSTOM57, 0, 0, 0, false, 0, complain_overflow_dont,
> +        bfd_elf_generic_reloc, "R_RISCV_CUSTOM57", false, 0, 0, false),
> +  HOWTO (R_RISCV_CUSTOM58, 0, 0, 0, false, 0, complain_overflow_dont,
> +        bfd_elf_generic_reloc, "R_RISCV_CUSTOM58", false, 0, 0, false),
> +  HOWTO (R_RISCV_CUSTOM59, 0, 0, 0, false, 0, complain_overflow_dont,
> +        bfd_elf_generic_reloc, "R_RISCV_CUSTOM59", false, 0, 0, false),
> +  HOWTO (R_RISCV_CUSTOM60, 0, 0, 0, false, 0, complain_overflow_dont,
> +        bfd_elf_generic_reloc, "R_RISCV_CUSTOM60", false, 0, 0, false),
> +  HOWTO (R_RISCV_CUSTOM61, 0, 0, 0, false, 0, complain_overflow_dont,
> +        bfd_elf_generic_reloc, "R_RISCV_CUSTOM61", false, 0, 0, false),
> +  HOWTO (R_RISCV_CUSTOM62, 0, 0, 0, false, 0, complain_overflow_dont,
> +        bfd_elf_generic_reloc, "R_RISCV_CUSTOM62", false, 0, 0, false),
> +  HOWTO (R_RISCV_CUSTOM63, 0, 0, 0, false, 0, complain_overflow_dont,
> +        bfd_elf_generic_reloc, "R_RISCV_CUSTOM63", false, 0, 0, false)
> +};
>

I think the above shouldn't be necessary.  We already have howto_table and
howto_table_internal for standard cases, and you will add a howto_table_esp
for esp vendor, so could we try to do everything right in the
riscv_elf_rtype_to_howto?  Which means let riscv_elf_rtype_to_howto choose
the right how_to tables, including vendors', which is the howto_table_esp
for you.  It is similar to the riscv_all_supported_ext, which collects all
tables of extensions, so we may have a howto_tables to collect howto_table,
howto_table_internal and howto_table_esp, ....  Then define an enum similar
to riscv_prefix_ext_class, which should be passed to
riscv_elf_rtype_to_howto, and then can get the right howto table from
howto_tables with the enum.  We will parse the R_RISCV_VENDOR reloc first,
and then get the vendor id, so the vendor id can be mapped to the right
enum.

       if (rtype == NULL)
>         printf (_("unrecognized: %-7lx"), (unsigned long) type &
> 0xffffffff);
>        else
> @@ -2500,6 +2510,7 @@ dump_relocations (Filedata *          filedata,
>                     sec_name = printable_section_name_from_index
>                       (filedata, psym->st_shndx, NULL);
>
> +                 vendor_id = strdup(sec_name);
>                   print_symbol_name (22, sec_name);
>                 }
>               else if (strtab == NULL)
> @@ -2512,6 +2523,7 @@ dump_relocations (Filedata *          filedata,
>                 }
>               else
>                 {
> +                 vendor_id = strdup(strtab + psym->st_name);
>                   print_symbol_name (22, strtab + psym->st_name);
>                   if (version_string)
>                     printf (sym_info == symbol_public ? "@@%s" : "@%s",
>

The other targets don't have vendor id, so probably can have a better way
to let only riscv do this.


> diff --git a/gas/config/tc-riscv.c b/gas/config/tc-riscv.c
> index 8485ad441f5..a63d4bb0f2e 100644
> --- a/gas/config/tc-riscv.c
> +++ b/gas/config/tc-riscv.c
> @@ -734,6 +734,54 @@ riscv_target_format (void)
>      return xlen == 64 ? "elf64-littleriscv" : "elf32-littleriscv";
>  }
>
> +/* Insert R_RISCV_VENDOR relocation right before the custom relocation
> + * passed in fixP. */
> +static void
> +insert_vendor_relocation (fixS *fixP, segT target_seg, const char
> *vendor_id)
> +{
> +  segT saved_seg;
> +  symbolS *sym;
> +  const flagword vendor_sym_flags = (BSF_NO_FLAGS | BSF_LOCAL);
> +
> +  /* Check if the vendor ID symbol already exists. */
> +  for (sym = symbol_rootP; sym != NULL; sym = symbol_next (sym))
> +    {
> +      /* Ensure we find a local symbol in the desired segment.
> +         This prevents R_RISCV_VENDOR relocation against a global symbol
> +         with the same name, if it exists.  */
> +      if (S_GET_SEGMENT (sym) == target_seg &&
> +         symbol_get_bfdsym (sym)->flags == vendor_sym_flags &&

+         strcmp (S_GET_NAME (sym), vendor_id) == 0)
>

if (S_GET_SEGMENT (sym) == target_seg
    && symbol_get_bfdsym (sym)->flags == vendor_sym_flags
    && strcmp (S_GET_NAME (sym), vendor_id) == 0)


> +       {
> +         break; /* Symbol found.  */
> +       }
> +    }
> +
> +  /* If symbol does not exist, create a new one.  */
> +  if (sym == NULL)
> +    {
> +      sym = symbol_new (vendor_id, target_seg, &zero_address_frag, 0);
> +      symbol_get_bfdsym (sym)->flags = vendor_sym_flags;
> +    }
> +
> +  /* Temporarily switch segment for fix_new().  */
> +  saved_seg = now_seg;
> +  now_seg = target_seg; /* Ensure fix_new_internal() uses the correct
> segment.  */
> +
> +  /* Insert the R_RISCV_VENDOR relocation.  */
> +  /* Since it's not possible to access the previous element in the fixup
> list,
> +     it worked around by calling fix_at_start when 'where == 0'.
> +     Otherwise, place the fix at 'where - 1' to ensure correct ordering of
> +     R_RISCV_VENDOR and R_RISCV_CUSTOM.  */
> +  if (fixP->fx_where == 0)
> +    fix_at_start (fixP->fx_frag, 4, sym, 0, false,
> BFD_RELOC_RISCV_VENDOR);
> +  else
> +    fix_new (fixP->fx_frag, fixP->fx_where - 1, 4, sym, 0, false,
> BFD_RELOC_RISCV_VENDOR);
>

I do not really understand the problem here.  Why not just refer to how the
R_RISCV_RELAX insert?  Just use the xmemdup to copy the fixup, and decide
the R_RISCV_VENDOR should be inserted into the current one or the copied
next one.


> diff --git a/include/elf/riscv.h b/include/elf/riscv.h
> index 2ea1ae82eb6..0f3a5c4ded6 100644
> --- a/include/elf/riscv.h
> +++ b/include/elf/riscv.h
> @@ -95,15 +95,113 @@ 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)
> +  /* update R_RISCV_internal_first if add new reloc.  */
> +
> +  /* Vendor-specific (nonstandard) relocations are defined here only to
> provide
> +     string version names (in readelf).  */
> +  RELOC_NUMBER (R_RISCV_VENDOR, 191)
> +  RELOC_NUMBER (R_RISCV_CUSTOM0, 192)
> +  RELOC_NUMBER (R_RISCV_CUSTOM1, 193)
> +  RELOC_NUMBER (R_RISCV_CUSTOM2, 194)
> +  RELOC_NUMBER (R_RISCV_CUSTOM3, 195)
> +  RELOC_NUMBER (R_RISCV_CUSTOM4, 196)
> +  RELOC_NUMBER (R_RISCV_CUSTOM5, 197)
> +  RELOC_NUMBER (R_RISCV_CUSTOM6, 198)
> +  RELOC_NUMBER (R_RISCV_CUSTOM7, 199)
> +  RELOC_NUMBER (R_RISCV_CUSTOM8, 200)
> +  RELOC_NUMBER (R_RISCV_CUSTOM9, 201)
> +  RELOC_NUMBER (R_RISCV_CUSTOM10, 202)
> +  RELOC_NUMBER (R_RISCV_CUSTOM11, 203)
> +  RELOC_NUMBER (R_RISCV_CUSTOM12, 204)
> +  RELOC_NUMBER (R_RISCV_CUSTOM13, 205)
> +  RELOC_NUMBER (R_RISCV_CUSTOM14, 206)
> +  RELOC_NUMBER (R_RISCV_CUSTOM15, 207)
> +  RELOC_NUMBER (R_RISCV_CUSTOM16, 208)
> +  RELOC_NUMBER (R_RISCV_CUSTOM17, 209)
> +  RELOC_NUMBER (R_RISCV_CUSTOM18, 210)
> +  RELOC_NUMBER (R_RISCV_CUSTOM19, 211)
> +  RELOC_NUMBER (R_RISCV_CUSTOM20, 212)
> +  RELOC_NUMBER (R_RISCV_CUSTOM21, 213)
> +  RELOC_NUMBER (R_RISCV_CUSTOM22, 214)
> +  RELOC_NUMBER (R_RISCV_CUSTOM23, 215)
> +  RELOC_NUMBER (R_RISCV_CUSTOM24, 216)
> +  RELOC_NUMBER (R_RISCV_CUSTOM25, 217)
> +  RELOC_NUMBER (R_RISCV_CUSTOM26, 218)
> +  RELOC_NUMBER (R_RISCV_CUSTOM27, 219)
> +  RELOC_NUMBER (R_RISCV_CUSTOM28, 220)
> +  RELOC_NUMBER (R_RISCV_CUSTOM29, 221)
> +  RELOC_NUMBER (R_RISCV_CUSTOM30, 222)
> +  RELOC_NUMBER (R_RISCV_CUSTOM31, 223)
> +  RELOC_NUMBER (R_RISCV_CUSTOM32, 224)
> +  RELOC_NUMBER (R_RISCV_CUSTOM33, 225)
> +  RELOC_NUMBER (R_RISCV_CUSTOM34, 226)
> +  RELOC_NUMBER (R_RISCV_CUSTOM35, 227)
> +  RELOC_NUMBER (R_RISCV_CUSTOM36, 228)
> +  RELOC_NUMBER (R_RISCV_CUSTOM37, 229)
> +  RELOC_NUMBER (R_RISCV_CUSTOM38, 230)
> +  RELOC_NUMBER (R_RISCV_CUSTOM39, 231)
> +  RELOC_NUMBER (R_RISCV_CUSTOM40, 232)
> +  RELOC_NUMBER (R_RISCV_CUSTOM41, 233)
> +  RELOC_NUMBER (R_RISCV_CUSTOM42, 234)
> +  RELOC_NUMBER (R_RISCV_CUSTOM43, 235)
> +  RELOC_NUMBER (R_RISCV_CUSTOM44, 236)
> +  RELOC_NUMBER (R_RISCV_CUSTOM45, 237)
> +  RELOC_NUMBER (R_RISCV_CUSTOM46, 238)
> +  RELOC_NUMBER (R_RISCV_CUSTOM47, 239)
> +  RELOC_NUMBER (R_RISCV_CUSTOM48, 240)
> +  RELOC_NUMBER (R_RISCV_CUSTOM49, 241)
> +  RELOC_NUMBER (R_RISCV_CUSTOM50, 242)
> +  RELOC_NUMBER (R_RISCV_CUSTOM51, 243)
> +  RELOC_NUMBER (R_RISCV_CUSTOM52, 244)
> +  RELOC_NUMBER (R_RISCV_CUSTOM53, 245)
> +  RELOC_NUMBER (R_RISCV_CUSTOM54, 246)
> +  RELOC_NUMBER (R_RISCV_CUSTOM55, 247)
> +  RELOC_NUMBER (R_RISCV_CUSTOM56, 248)
> +  RELOC_NUMBER (R_RISCV_CUSTOM57, 249)
> +  RELOC_NUMBER (R_RISCV_CUSTOM58, 250)
> +  RELOC_NUMBER (R_RISCV_CUSTOM59, 251)
> +  RELOC_NUMBER (R_RISCV_CUSTOM60, 252)
> +  RELOC_NUMBER (R_RISCV_CUSTOM61, 253)
> +  RELOC_NUMBER (R_RISCV_CUSTOM62, 254)
> +  RELOC_NUMBER (R_RISCV_CUSTOM63, 255)
>

Likewise, these should be unnecessary.


>  END_RELOC_NUMBERS (R_RISCV_max)
>
>  /* 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_internal_first (R_RISCV_TLSDESC_CALL + 1)
> +#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)


In case one day the internal relocations may be overlapped with the vendor
stuff, I think we only need to define the R_RISCV_internal_first to 256
directly, after the end of vendor relocations.


Thanks
Nelson
  
Nelson Chu April 9, 2025, 7:54 p.m. UTC | #5
On Wed, Apr 9, 2025 at 4:59 PM Alexey Lapshin <alexey.lapshin@espressif.com>
wrote:

> +struct elf_vendor_map
> +{
> +  enum elf_riscv_vendor_id id;
> +  const char *id_str;
> +};
> +
> +static const struct elf_vendor_map elf_riscv_vendor_map[] =
> +{
> +  { R_RISCV_VENDOR_ID_ESP, R_RISCV_ESPRESSIF_VENDOR_ID_STR },
> +};
> +
> +enum elf_riscv_vendor_id
> +riscv_elf_vendor_id_str_to_enum(const char *id_str)
> +{
> +  unsigned int i;
> +  for (i = 0; i < ARRAY_SIZE (elf_riscv_vendor_map); i++)
> +    if (strcmp(elf_riscv_vendor_map[i].id_str, id_str) == 0)
> +      return elf_riscv_vendor_map[i].id;
> +
> +  return R_RISCV_VENDOR_ID_NONE;
> +}
> +
> +struct elf_reloc_map_esp
> +{
> +  bfd_reloc_code_real_type bfd_val;
> +  enum elf_riscv_esp_reloc_type elf_val;
> +};
> +
> +static const struct elf_reloc_map_esp riscv_esp_reloc_map[] =
> +{
> +  { BFD_RELOC_RISCV_ESP_LP_OFFSET_9, R_RISCV_ESP_LP_OFFSET_9 },
> +  { BFD_RELOC_RISCV_ESP_LP_OFFSET_12, R_RISCV_ESP_LP_OFFSET_12 },
> +};
>

I forgot to mention this - could we just define these vendor mappings into
elf_riscv_map, so...


> diff --git a/include/elf/riscv.h b/include/elf/riscv.h
> index 2ea1ae82eb6..0f3a5c4ded6 100644
> --- a/include/elf/riscv.h
> +++ b/include/elf/riscv.h
> +
> +/* Vendor specific relocations macro and structures.  */
> +#define R_RISCV_ESPRESSIF_VENDOR_ID_STR "esp"
> +
> +/* VENDOR IDs for internal usage.  */
> +enum elf_riscv_vendor_id {
> +  R_RISCV_VENDOR_ID_NONE = -1,
> +  R_RISCV_VENDOR_ID_ESP,
> +};
> +
> +/* Vendor (Espressif) relocations.  */
> +START_RELOC_NUMBERS (elf_riscv_esp_reloc_type)
> +  RELOC_NUMBER (R_RISCV_ESP_LP_OFFSET_9, 192)
> +  RELOC_NUMBER (R_RISCV_ESP_LP_OFFSET_12, 193)
> +END_RELOC_NUMBERS (R_RISCV_ESP_max)
>

...  We probably don't need to define so many elf_riscv_xxx_reloc_type for
every vendor.  I see MIPS and ARM had defined FAKE_RELOC for
duplicate cases, not sure if that also works for risc-v vendor
relocations.  That is - could we define FAKE_RELOC (R_RISCV_vendor_begin)
and FAKE_RELOC (R_RISCV_vendor_end), then define all vendor stuff between
them?

Thanks
Nelson
  
Alexey Lapshin April 10, 2025, 5:12 a.m. UTC | #6
Nelson, thank you for the suggestions! They are really helpful and will improve the code

The only thing should be reconsidered is:

> define the R_RISCV_internal_first to 256 directly, after the end of vendor relocations.

It seems having relocations with r_type greater than 255 is bad idea,
because it will be corrupted when passed to macro (masked by 0xff):

#define ELF32_R_INFO(s,t)	(((unsigned) (s) << 8) + ((t) & 0xff))

I don't know why these 'internal' relocations were moved from elf_riscv_reloc_type
to another ID (and still have duplicate in elf_riscv_reloc_type),
but it looks like it's a 'postponed' issue to be resolved when most
relocation IDs are reserved.

Regards,
Alexey

On Thu, 2025-04-10 at 03:54 +0800, Nelson Chu wrote:
> 
>  
> You don't often get email from nelson@rivosinc.com. 
> Learn why this is important 
> 
> 
> 
> [External: This email originated outside Espressif]
> 
> 
> 
> 
> 
> 
> 
> 
> On Wed, Apr 9, 2025 at 4:59 PM Alexey Lapshin <alexey.lapshin@espressif.com> wrote:
> > +struct elf_vendor_map
> > +{
> > +  enum elf_riscv_vendor_id id;
> > +  const char *id_str;
> > +};
> > +
> > +static const struct elf_vendor_map elf_riscv_vendor_map[] =
> > +{
> > +  { R_RISCV_VENDOR_ID_ESP, R_RISCV_ESPRESSIF_VENDOR_ID_STR },
> > +};
> > +
> > +enum elf_riscv_vendor_id
> > +riscv_elf_vendor_id_str_to_enum(const char *id_str)
> > +{
> > +  unsigned int i;
> > +  for (i = 0; i < ARRAY_SIZE (elf_riscv_vendor_map); i++)
> > +    if (strcmp(elf_riscv_vendor_map[i].id_str, id_str) == 0)
> > +      return elf_riscv_vendor_map[i].id;
> > +
> > +  return R_RISCV_VENDOR_ID_NONE;
> > +}
> > +
> > +struct elf_reloc_map_esp
> > +{
> > +  bfd_reloc_code_real_type bfd_val;
> > +  enum elf_riscv_esp_reloc_type elf_val;
> > +};
> > +
> > +static const struct elf_reloc_map_esp riscv_esp_reloc_map[] =
> > +{
> > +  { BFD_RELOC_RISCV_ESP_LP_OFFSET_9, R_RISCV_ESP_LP_OFFSET_9 },
> > +  { BFD_RELOC_RISCV_ESP_LP_OFFSET_12, R_RISCV_ESP_LP_OFFSET_12 },
> > +};
> > 
> 
> 
> I forgot to mention this - could we just define these vendor mappings into elf_riscv_map, so...
>  
> > diff --git a/include/elf/riscv.h b/include/elf/riscv.h
> > index 2ea1ae82eb6..0f3a5c4ded6 100644
> > --- a/include/elf/riscv.h
> > +++ b/include/elf/riscv.h
> > +
> > +/* Vendor specific relocations macro and structures.  */
> > +#define R_RISCV_ESPRESSIF_VENDOR_ID_STR "esp"
> > +
> > +/* VENDOR IDs for internal usage.  */
> > +enum elf_riscv_vendor_id {
> > +  R_RISCV_VENDOR_ID_NONE = -1,
> > +  R_RISCV_VENDOR_ID_ESP,
> > +};
> > +
> > +/* Vendor (Espressif) relocations.  */
> > +START_RELOC_NUMBERS (elf_riscv_esp_reloc_type)
> > +  RELOC_NUMBER (R_RISCV_ESP_LP_OFFSET_9, 192)
> > +  RELOC_NUMBER (R_RISCV_ESP_LP_OFFSET_12, 193)
> > +END_RELOC_NUMBERS (R_RISCV_ESP_max)
> > 
> 
> 
> ...  We probably don't need to define so many elf_riscv_xxx_reloc_type for every vendor.  I see MIPS and ARM had defined FAKE_RELOC for duplicate cases, not sure if that also works for risc-v vendor relocations.  That is - could we define FAKE_RELOC (R_RISCV_vendor_begin) and FAKE_RELOC (R_RISCV_vendor_end), then define all vendor stuff between them?
>  
> Thanks
> Nelson
  
Nelson Chu April 11, 2025, 4:20 a.m. UTC | #7
On Thu, Apr 10, 2025 at 1:12 PM Alexey Lapshin <alexey.lapshin@espressif.com>
wrote:

> > define the R_RISCV_internal_first to 256 directly, after the end of
> vendor relocations.
>
> It seems having relocations with r_type greater than 255 is bad idea,
> because it will be corrupted when passed to macro (masked by 0xff):
>
> #define ELF32_R_INFO(s,t)       (((unsigned) (s) << 8) + ((t) & 0xff))
>
> I don't know why these 'internal' relocations were moved from
> elf_riscv_reloc_type
> to another ID (and still have duplicate in elf_riscv_reloc_type),
> but it looks like it's a 'postponed' issue to be resolved when most
> relocation IDs are reserved.
>

We can regard these internal relocation as they are not claimed to be
supported in the psABI, which means they shouldn't be generated to the
output.  Currently, all of them are used to help to do the linker
relaxations, replacing the original relocation to them, and then don't need
to be generated for the dynamic objects after resolving them, so that's why
they are internal.  So the r_type of them doesn't really matter, their
original number defined in the psABI have been freed, and some of them have
already been reused for other relocations.  Therefore, I think we just need
to give them an r_type, even if their r_type changes in every binutils
version, that should be fine.  The only thing that might be affected is
using --emit-relocs ld option, there was a patch for this, but it is
pending for some issues which I cannot remember.

Thanks
Nelson
  
Alexey Lapshin April 11, 2025, 5:38 a.m. UTC | #8
On Fri, 2025-04-11 at 12:20 +0800, Nelson Chu wrote:
> I think we just need to give them an r_type,

Yes, I don't mind. I was just pointing out that internal relocations
must not exceed 255 to ensure it works correctly.
  

Patch

diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h
index ec60f23f61b..f7511e1ffc2 100644
--- a/bfd/bfd-in2.h
+++ b/bfd/bfd-in2.h
@@ -5430,6 +5430,11 @@  enum bfd_reloc_code_real
   BFD_RELOC_RISCV_32_PCREL,
   BFD_RELOC_RISCV_SET_ULEB128,
   BFD_RELOC_RISCV_SUB_ULEB128,
+  BFD_RELOC_RISCV_VENDOR,
+
+  /* RISC-V Vendor (Espressif) relocations.  */
+  BFD_RELOC_RISCV_ESP_LP_OFFSET_9,
+  BFD_RELOC_RISCV_ESP_LP_OFFSET_12,
 
   /* Renesas RL78 Relocations.  */
   BFD_RELOC_RL78_NEG8,
diff --git a/bfd/elfnn-riscv.c b/bfd/elfnn-riscv.c
index 1c494f5f986..5f0ece560b8 100644
--- a/bfd/elfnn-riscv.c
+++ b/bfd/elfnn-riscv.c
@@ -264,7 +264,27 @@  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 = R_RISCV_VENDOR_ID_NONE;
+  unsigned int r_type = ELFNN_R_TYPE (dst->r_info);
+
+  if (r_type > R_RISCV_VENDOR)
+    {
+      cache_ptr->howto =
+	riscv_elf_rtype_to_vendor_howto (abfd, r_type, vendor_id);
+      vendor_id = R_RISCV_VENDOR_ID_NONE;
+      return cache_ptr->howto != NULL;
+    }
+  else
+    vendor_id = R_RISCV_VENDOR_ID_NONE;
+
+  if (r_type == R_RISCV_VENDOR)
+    {
+      const char *sym_name = bfd_asymbol_name (*cache_ptr->sym_ptr_ptr);
+      if (sym_name != NULL)
+	vendor_id = riscv_elf_vendor_id_str_to_enum (sym_name);
+    }
+
+  cache_ptr->howto = riscv_elf_rtype_to_howto (abfd, r_type);
   return cache_ptr->howto != NULL;
 }
 
@@ -1772,6 +1792,38 @@  riscv_global_pointer_value (struct bfd_link_info *info)
   return h->u.def.value + sec_addr (h->u.def.section);
 }
 
+static bfd_reloc_status_type
+calculate_vendor_relocaton (const Elf_Internal_Rela *rel, bfd_vma *value,
+			    enum elf_riscv_vendor_id vendor_id)
+{
+  bfd_vma v = *value;
+
+  switch (vendor_id)
+    {
+    case R_RISCV_VENDOR_ID_ESP: /* Espressif.  */
+      switch (ELFNN_R_TYPE (rel->r_info))
+	{
+	case R_RISCV_ESP_LP_OFFSET_9:
+	  if (!VALID_ESPPIE_LP_OFFSET_9 (v))
+	    return bfd_reloc_overflow;
+	  v = ENCODE_ESPPIE_LP_OFFSET_9 (v);
+	  break;
+
+	case R_RISCV_ESP_LP_OFFSET_12:
+	  if (!VALID_ESPPIE_LP_OFFSET_12 (v))
+	    return bfd_reloc_overflow;
+	  v = ENCODE_ESPPIE_LP_OFFSET_12 (v);
+	  break;
+	}
+      break;
+    default:
+      return bfd_reloc_notsupported;
+    }
+
+  *value = v;
+  return bfd_reloc_ok;
+}
+
 /* Emplace a static relocation.  */
 
 static bfd_reloc_status_type
@@ -1780,7 +1832,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;
@@ -1789,6 +1842,15 @@  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 != R_RISCV_VENDOR_ID_NONE)
+    {
+      bfd_reloc_status_type status =
+	calculate_vendor_relocaton (rel, &value, vendor_id);
+      if (status != bfd_reloc_ok)
+	return status;
+      goto finish_relocation;
+    }
+
   switch (ELFNN_R_TYPE (rel->r_info))
     {
     case R_RISCV_HI20:
@@ -1944,6 +2006,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);
@@ -2166,7 +2230,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, R_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
@@ -2241,6 +2305,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))
@@ -2260,10 +2325,45 @@  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;
+      enum elf_riscv_vendor_id vendor_id = R_RISCV_VENDOR_ID_NONE;
+
+      if (vendor_rel == NULL)
+	howto = riscv_elf_rtype_to_howto (input_bfd, r_type);
+      else
+	{
+	  if (r_type <= R_RISCV_VENDOR)
+	    {
+	      _bfd_error_handler
+		(_
+		 ("error: %pB: expected vendor-specific relocation (r_type 192..255) after R_RISCV_VENDOR(191); Got r_type %d"),
+		 input_bfd, r_type);
+	      bfd_set_error (bfd_error_bad_value);
+	      return false;
+	    }
+
+	  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_elf_vendor_id_str_to_enum (name);
+	  if (vendor_id == R_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;
+	    }
+	  howto =
+	    riscv_elf_rtype_to_vendor_howto (input_bfd, r_type, vendor_id);
+	  name = NULL;
+	  vendor_rel = NULL;
+	}
 
       if (howto == NULL)
 	continue;
@@ -2572,6 +2672,26 @@  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)
+	{
+      case R_RISCV_VENDOR_ID_ESP:
+      switch (r_type)
+	{
+	case R_RISCV_ESP_LP_OFFSET_9:
+	case R_RISCV_ESP_LP_OFFSET_12:
+	  /* These require no special handling beyond perform_relocation.  */
+	  break;
+	default:
+	  /* Unreachable: vendor_id was was validated earlier.  */
+	  BFD_ASSERT (false);
+	}
+      goto do_relocation;
+	default:
+	  break;
+	}
+
       switch (r_type)
 	{
 	case R_RISCV_NONE:
@@ -2599,6 +2719,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)
 	    {
@@ -3169,8 +3293,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
 	 or we don't set it before, then we should set the default message
diff --git a/bfd/elfxx-riscv.c b/bfd/elfxx-riscv.c
index ec254915c76..b9b0001f3f9 100644
--- a/bfd/elfxx-riscv.c
+++ b/bfd/elfxx-riscv.c
@@ -960,6 +960,168 @@  static reloc_howto_type howto_table_internal[] =
 	 false),			/* pcrel_offset */
 };
 
+/* This howto table is used solely to provide relocation name strings.  */
+static reloc_howto_type howto_table_vendor[] = {
+  HOWTO (R_RISCV_CUSTOM0, 0, 0, 0, false, 0, complain_overflow_dont,
+	 bfd_elf_generic_reloc, "R_RISCV_CUSTOM0", false, 0, 0, false),
+  HOWTO (R_RISCV_CUSTOM1, 0, 0, 0, false, 0, complain_overflow_dont,
+	 bfd_elf_generic_reloc, "R_RISCV_CUSTOM1", false, 0, 0, false),
+  HOWTO (R_RISCV_CUSTOM2, 0, 0, 0, false, 0, complain_overflow_dont,
+	 bfd_elf_generic_reloc, "R_RISCV_CUSTOM2", false, 0, 0, false),
+  HOWTO (R_RISCV_CUSTOM3, 0, 0, 0, false, 0, complain_overflow_dont,
+	 bfd_elf_generic_reloc, "R_RISCV_CUSTOM3", false, 0, 0, false),
+  HOWTO (R_RISCV_CUSTOM4, 0, 0, 0, false, 0, complain_overflow_dont,
+	 bfd_elf_generic_reloc, "R_RISCV_CUSTOM4", false, 0, 0, false),
+  HOWTO (R_RISCV_CUSTOM5, 0, 0, 0, false, 0, complain_overflow_dont,
+	 bfd_elf_generic_reloc, "R_RISCV_CUSTOM5", false, 0, 0, false),
+  HOWTO (R_RISCV_CUSTOM6, 0, 0, 0, false, 0, complain_overflow_dont,
+	 bfd_elf_generic_reloc, "R_RISCV_CUSTOM6", false, 0, 0, false),
+  HOWTO (R_RISCV_CUSTOM7, 0, 0, 0, false, 0, complain_overflow_dont,
+	 bfd_elf_generic_reloc, "R_RISCV_CUSTOM7", false, 0, 0, false),
+  HOWTO (R_RISCV_CUSTOM8, 0, 0, 0, false, 0, complain_overflow_dont,
+	 bfd_elf_generic_reloc, "R_RISCV_CUSTOM8", false, 0, 0, false),
+  HOWTO (R_RISCV_CUSTOM9, 0, 0, 0, false, 0, complain_overflow_dont,
+	 bfd_elf_generic_reloc, "R_RISCV_CUSTOM9", false, 0, 0, false),
+  HOWTO (R_RISCV_CUSTOM10, 0, 0, 0, false, 0, complain_overflow_dont,
+	 bfd_elf_generic_reloc, "R_RISCV_CUSTOM10", false, 0, 0, false),
+  HOWTO (R_RISCV_CUSTOM11, 0, 0, 0, false, 0, complain_overflow_dont,
+	 bfd_elf_generic_reloc, "R_RISCV_CUSTOM11", false, 0, 0, false),
+  HOWTO (R_RISCV_CUSTOM12, 0, 0, 0, false, 0, complain_overflow_dont,
+	 bfd_elf_generic_reloc, "R_RISCV_CUSTOM12", false, 0, 0, false),
+  HOWTO (R_RISCV_CUSTOM13, 0, 0, 0, false, 0, complain_overflow_dont,
+	 bfd_elf_generic_reloc, "R_RISCV_CUSTOM13", false, 0, 0, false),
+  HOWTO (R_RISCV_CUSTOM14, 0, 0, 0, false, 0, complain_overflow_dont,
+	 bfd_elf_generic_reloc, "R_RISCV_CUSTOM14", false, 0, 0, false),
+  HOWTO (R_RISCV_CUSTOM15, 0, 0, 0, false, 0, complain_overflow_dont,
+	 bfd_elf_generic_reloc, "R_RISCV_CUSTOM15", false, 0, 0, false),
+  HOWTO (R_RISCV_CUSTOM16, 0, 0, 0, false, 0, complain_overflow_dont,
+	 bfd_elf_generic_reloc, "R_RISCV_CUSTOM16", false, 0, 0, false),
+  HOWTO (R_RISCV_CUSTOM17, 0, 0, 0, false, 0, complain_overflow_dont,
+	 bfd_elf_generic_reloc, "R_RISCV_CUSTOM17", false, 0, 0, false),
+  HOWTO (R_RISCV_CUSTOM18, 0, 0, 0, false, 0, complain_overflow_dont,
+	 bfd_elf_generic_reloc, "R_RISCV_CUSTOM18", false, 0, 0, false),
+  HOWTO (R_RISCV_CUSTOM19, 0, 0, 0, false, 0, complain_overflow_dont,
+	 bfd_elf_generic_reloc, "R_RISCV_CUSTOM19", false, 0, 0, false),
+  HOWTO (R_RISCV_CUSTOM20, 0, 0, 0, false, 0, complain_overflow_dont,
+	 bfd_elf_generic_reloc, "R_RISCV_CUSTOM20", false, 0, 0, false),
+  HOWTO (R_RISCV_CUSTOM21, 0, 0, 0, false, 0, complain_overflow_dont,
+	 bfd_elf_generic_reloc, "R_RISCV_CUSTOM21", false, 0, 0, false),
+  HOWTO (R_RISCV_CUSTOM22, 0, 0, 0, false, 0, complain_overflow_dont,
+	 bfd_elf_generic_reloc, "R_RISCV_CUSTOM22", false, 0, 0, false),
+  HOWTO (R_RISCV_CUSTOM23, 0, 0, 0, false, 0, complain_overflow_dont,
+	 bfd_elf_generic_reloc, "R_RISCV_CUSTOM23", false, 0, 0, false),
+  HOWTO (R_RISCV_CUSTOM24, 0, 0, 0, false, 0, complain_overflow_dont,
+	 bfd_elf_generic_reloc, "R_RISCV_CUSTOM24", false, 0, 0, false),
+  HOWTO (R_RISCV_CUSTOM25, 0, 0, 0, false, 0, complain_overflow_dont,
+	 bfd_elf_generic_reloc, "R_RISCV_CUSTOM25", false, 0, 0, false),
+  HOWTO (R_RISCV_CUSTOM26, 0, 0, 0, false, 0, complain_overflow_dont,
+	 bfd_elf_generic_reloc, "R_RISCV_CUSTOM26", false, 0, 0, false),
+  HOWTO (R_RISCV_CUSTOM27, 0, 0, 0, false, 0, complain_overflow_dont,
+	 bfd_elf_generic_reloc, "R_RISCV_CUSTOM27", false, 0, 0, false),
+  HOWTO (R_RISCV_CUSTOM28, 0, 0, 0, false, 0, complain_overflow_dont,
+	 bfd_elf_generic_reloc, "R_RISCV_CUSTOM28", false, 0, 0, false),
+  HOWTO (R_RISCV_CUSTOM29, 0, 0, 0, false, 0, complain_overflow_dont,
+	 bfd_elf_generic_reloc, "R_RISCV_CUSTOM29", false, 0, 0, false),
+  HOWTO (R_RISCV_CUSTOM30, 0, 0, 0, false, 0, complain_overflow_dont,
+	 bfd_elf_generic_reloc, "R_RISCV_CUSTOM30", false, 0, 0, false),
+  HOWTO (R_RISCV_CUSTOM31, 0, 0, 0, false, 0, complain_overflow_dont,
+	 bfd_elf_generic_reloc, "R_RISCV_CUSTOM31", false, 0, 0, false),
+  HOWTO (R_RISCV_CUSTOM32, 0, 0, 0, false, 0, complain_overflow_dont,
+	 bfd_elf_generic_reloc, "R_RISCV_CUSTOM32", false, 0, 0, false),
+  HOWTO (R_RISCV_CUSTOM33, 0, 0, 0, false, 0, complain_overflow_dont,
+	 bfd_elf_generic_reloc, "R_RISCV_CUSTOM33", false, 0, 0, false),
+  HOWTO (R_RISCV_CUSTOM34, 0, 0, 0, false, 0, complain_overflow_dont,
+	 bfd_elf_generic_reloc, "R_RISCV_CUSTOM34", false, 0, 0, false),
+  HOWTO (R_RISCV_CUSTOM35, 0, 0, 0, false, 0, complain_overflow_dont,
+	 bfd_elf_generic_reloc, "R_RISCV_CUSTOM35", false, 0, 0, false),
+  HOWTO (R_RISCV_CUSTOM36, 0, 0, 0, false, 0, complain_overflow_dont,
+	 bfd_elf_generic_reloc, "R_RISCV_CUSTOM36", false, 0, 0, false),
+  HOWTO (R_RISCV_CUSTOM37, 0, 0, 0, false, 0, complain_overflow_dont,
+	 bfd_elf_generic_reloc, "R_RISCV_CUSTOM37", false, 0, 0, false),
+  HOWTO (R_RISCV_CUSTOM38, 0, 0, 0, false, 0, complain_overflow_dont,
+	 bfd_elf_generic_reloc, "R_RISCV_CUSTOM38", false, 0, 0, false),
+  HOWTO (R_RISCV_CUSTOM39, 0, 0, 0, false, 0, complain_overflow_dont,
+	 bfd_elf_generic_reloc, "R_RISCV_CUSTOM39", false, 0, 0, false),
+  HOWTO (R_RISCV_CUSTOM40, 0, 0, 0, false, 0, complain_overflow_dont,
+	 bfd_elf_generic_reloc, "R_RISCV_CUSTOM40", false, 0, 0, false),
+  HOWTO (R_RISCV_CUSTOM41, 0, 0, 0, false, 0, complain_overflow_dont,
+	 bfd_elf_generic_reloc, "R_RISCV_CUSTOM41", false, 0, 0, false),
+  HOWTO (R_RISCV_CUSTOM42, 0, 0, 0, false, 0, complain_overflow_dont,
+	 bfd_elf_generic_reloc, "R_RISCV_CUSTOM42", false, 0, 0, false),
+  HOWTO (R_RISCV_CUSTOM43, 0, 0, 0, false, 0, complain_overflow_dont,
+	 bfd_elf_generic_reloc, "R_RISCV_CUSTOM43", false, 0, 0, false),
+  HOWTO (R_RISCV_CUSTOM44, 0, 0, 0, false, 0, complain_overflow_dont,
+	 bfd_elf_generic_reloc, "R_RISCV_CUSTOM44", false, 0, 0, false),
+  HOWTO (R_RISCV_CUSTOM45, 0, 0, 0, false, 0, complain_overflow_dont,
+	 bfd_elf_generic_reloc, "R_RISCV_CUSTOM45", false, 0, 0, false),
+  HOWTO (R_RISCV_CUSTOM46, 0, 0, 0, false, 0, complain_overflow_dont,
+	 bfd_elf_generic_reloc, "R_RISCV_CUSTOM46", false, 0, 0, false),
+  HOWTO (R_RISCV_CUSTOM47, 0, 0, 0, false, 0, complain_overflow_dont,
+	 bfd_elf_generic_reloc, "R_RISCV_CUSTOM47", false, 0, 0, false),
+  HOWTO (R_RISCV_CUSTOM48, 0, 0, 0, false, 0, complain_overflow_dont,
+	 bfd_elf_generic_reloc, "R_RISCV_CUSTOM48", false, 0, 0, false),
+  HOWTO (R_RISCV_CUSTOM49, 0, 0, 0, false, 0, complain_overflow_dont,
+	 bfd_elf_generic_reloc, "R_RISCV_CUSTOM49", false, 0, 0, false),
+  HOWTO (R_RISCV_CUSTOM50, 0, 0, 0, false, 0, complain_overflow_dont,
+	 bfd_elf_generic_reloc, "R_RISCV_CUSTOM50", false, 0, 0, false),
+  HOWTO (R_RISCV_CUSTOM51, 0, 0, 0, false, 0, complain_overflow_dont,
+	 bfd_elf_generic_reloc, "R_RISCV_CUSTOM51", false, 0, 0, false),
+  HOWTO (R_RISCV_CUSTOM52, 0, 0, 0, false, 0, complain_overflow_dont,
+	 bfd_elf_generic_reloc, "R_RISCV_CUSTOM52", false, 0, 0, false),
+  HOWTO (R_RISCV_CUSTOM53, 0, 0, 0, false, 0, complain_overflow_dont,
+	 bfd_elf_generic_reloc, "R_RISCV_CUSTOM53", false, 0, 0, false),
+  HOWTO (R_RISCV_CUSTOM54, 0, 0, 0, false, 0, complain_overflow_dont,
+	 bfd_elf_generic_reloc, "R_RISCV_CUSTOM54", false, 0, 0, false),
+  HOWTO (R_RISCV_CUSTOM55, 0, 0, 0, false, 0, complain_overflow_dont,
+	 bfd_elf_generic_reloc, "R_RISCV_CUSTOM55", false, 0, 0, false),
+  HOWTO (R_RISCV_CUSTOM56, 0, 0, 0, false, 0, complain_overflow_dont,
+	 bfd_elf_generic_reloc, "R_RISCV_CUSTOM56", false, 0, 0, false),
+  HOWTO (R_RISCV_CUSTOM57, 0, 0, 0, false, 0, complain_overflow_dont,
+	 bfd_elf_generic_reloc, "R_RISCV_CUSTOM57", false, 0, 0, false),
+  HOWTO (R_RISCV_CUSTOM58, 0, 0, 0, false, 0, complain_overflow_dont,
+	 bfd_elf_generic_reloc, "R_RISCV_CUSTOM58", false, 0, 0, false),
+  HOWTO (R_RISCV_CUSTOM59, 0, 0, 0, false, 0, complain_overflow_dont,
+	 bfd_elf_generic_reloc, "R_RISCV_CUSTOM59", false, 0, 0, false),
+  HOWTO (R_RISCV_CUSTOM60, 0, 0, 0, false, 0, complain_overflow_dont,
+	 bfd_elf_generic_reloc, "R_RISCV_CUSTOM60", false, 0, 0, false),
+  HOWTO (R_RISCV_CUSTOM61, 0, 0, 0, false, 0, complain_overflow_dont,
+	 bfd_elf_generic_reloc, "R_RISCV_CUSTOM61", false, 0, 0, false),
+  HOWTO (R_RISCV_CUSTOM62, 0, 0, 0, false, 0, complain_overflow_dont,
+	 bfd_elf_generic_reloc, "R_RISCV_CUSTOM62", false, 0, 0, false),
+  HOWTO (R_RISCV_CUSTOM63, 0, 0, 0, false, 0, complain_overflow_dont,
+	 bfd_elf_generic_reloc, "R_RISCV_CUSTOM63", false, 0, 0, false)
+};
+
+static reloc_howto_type howto_table_esp[] =
+{
+  HOWTO (R_RISCV_ESP_LP_OFFSET_9,	/* type */
+	 0,				/* rightshift */
+	 4,				/* size */
+	 32,				/* bitsize */
+	 true,				/* pc_relative */
+	 0,				/* bitpos */
+	 complain_overflow_dont,	/* complain_on_overflow */
+	 bfd_elf_generic_reloc,		/* special_function */
+	 "R_RISCV_ESP_LP_OFFSET_9",	/* name */
+	 false,				/* partial_inplace */
+	 0,				/* src_mask */
+	 ENCODE_ESPPIE_LP_OFFSET_9 (-1U),	/* dst_mask */
+	 true),				/* pcrel_offset */
+  HOWTO (R_RISCV_ESP_LP_OFFSET_12,	/* type */
+	 0,				/* rightshift */
+	 4,				/* size */
+	 32,				/* bitsize */
+	 true,				/* pc_relative */
+	 0,				/* bitpos */
+	 complain_overflow_dont,	/* complain_on_overflow */
+	 bfd_elf_generic_reloc,		/* special_function */
+	 "R_RISCV_ESP_LP_OFFSET_12",	/* name */
+	 false,				/* partial_inplace */
+	 0,				/* src_mask */
+	 ENCODE_ESPPIE_LP_OFFSET_12 (-1U),	/* dst_mask */
+	 true),				/* pcrel_offset */
+};
+
 /* A mapping from BFD reloc types to RISC-V ELF reloc types.  */
 struct elf_reloc_map
 {
@@ -1022,6 +1184,54 @@  static const struct elf_reloc_map riscv_reloc_map[] =
   { BFD_RELOC_RISCV_SUB_ULEB128, R_RISCV_SUB_ULEB128 },
 };
 
+static const reloc_howto_type howto_vendor = 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);
+
+struct elf_vendor_map
+{
+  enum elf_riscv_vendor_id id;
+  const char *id_str;
+};
+
+static const struct elf_vendor_map elf_riscv_vendor_map[] =
+{
+  { R_RISCV_VENDOR_ID_ESP, R_RISCV_ESPRESSIF_VENDOR_ID_STR },
+};
+
+enum elf_riscv_vendor_id
+riscv_elf_vendor_id_str_to_enum(const char *id_str)
+{
+  unsigned int i;
+  for (i = 0; i < ARRAY_SIZE (elf_riscv_vendor_map); i++)
+    if (strcmp(elf_riscv_vendor_map[i].id_str, id_str) == 0)
+      return elf_riscv_vendor_map[i].id;
+
+  return R_RISCV_VENDOR_ID_NONE;
+}
+
+struct elf_reloc_map_esp
+{
+  bfd_reloc_code_real_type bfd_val;
+  enum elf_riscv_esp_reloc_type elf_val;
+};
+
+static const struct elf_reloc_map_esp riscv_esp_reloc_map[] =
+{
+  { BFD_RELOC_RISCV_ESP_LP_OFFSET_9, R_RISCV_ESP_LP_OFFSET_9 },
+  { BFD_RELOC_RISCV_ESP_LP_OFFSET_12, R_RISCV_ESP_LP_OFFSET_12 },
+};
+
 /* Given a BFD reloc type, return a howto structure.  */
 
 reloc_howto_type *
@@ -1034,6 +1244,14 @@  riscv_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED,
     if (riscv_reloc_map[i].bfd_val == code)
       return &howto_table[(int) riscv_reloc_map[i].elf_val];
 
+  if (code == BFD_RELOC_RISCV_VENDOR)
+    return &howto_vendor;
+
+  /* Vendor-specific (Espressif) relocations.  */
+  for (i = 0; i < ARRAY_SIZE (riscv_esp_reloc_map); i++)
+    if (riscv_esp_reloc_map[i].bfd_val == code)
+      return &howto_table_esp[(int) riscv_esp_reloc_map[i].elf_val - R_RISCV_CUSTOM0];
+
   bfd_set_error (bfd_error_bad_value);
   return NULL;
 }
@@ -1047,6 +1265,10 @@  riscv_reloc_name_lookup (bfd *abfd ATTRIBUTE_UNUSED, const char *r_name)
     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_esp); i++)
+    if (howto_table_esp[i].name && strcasecmp (howto_table_esp[i].name, r_name) == 0)
+      return &howto_table_esp[i];
+
   return NULL;
 }
 
@@ -1055,8 +1277,10 @@  riscv_elf_rtype_to_howto (bfd *abfd, unsigned int r_type)
 {
   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];
+  else if (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 == R_RISCV_VENDOR)
+    return &howto_vendor;
   else
     {
       (*_bfd_error_handler) (_("%pB: unsupported relocation type %#x"),
@@ -1066,6 +1290,19 @@  riscv_elf_rtype_to_howto (bfd *abfd, unsigned int r_type)
     }
 }
 
+reloc_howto_type *
+riscv_elf_rtype_to_vendor_howto (bfd *abfd ATTRIBUTE_UNUSED, unsigned int r_type,
+				 enum elf_riscv_vendor_id vendor_id)
+{
+  switch (vendor_id)
+    {
+    case R_RISCV_VENDOR_ID_ESP:
+      return &howto_table_esp[r_type - R_RISCV_CUSTOM0];
+    default:
+      return &howto_table_vendor[r_type - R_RISCV_CUSTOM0];
+    }
+}
+
 /* Special_function of RISCV_ADD and RISCV_SUB relocations.  */
 
 static bfd_reloc_status_type
@@ -1523,6 +1760,7 @@  static struct riscv_supported_ext riscv_supported_vendor_x_ext[] =
   {"xsfvqmaccqoq",	ISA_SPEC_CLASS_DRAFT,	1, 0, 0},
   {"xsfvqmaccdod",	ISA_SPEC_CLASS_DRAFT,	1, 0, 0},
   {"xsfvfnrclipxfqf",	ISA_SPEC_CLASS_DRAFT,	1, 0, 0},
+  {"xesplp",		ISA_SPEC_CLASS_DRAFT,	1, 0, 0},
   {NULL, 0, 0, 0, 0}
 };
 
@@ -2863,6 +3101,8 @@  riscv_multi_subset_supports (riscv_parse_subset_t *rps,
       return riscv_subset_supports (rps, "xsfvqmaccdod");
     case INSN_CLASS_XSFVFNRCLIPXFQF:
       return riscv_subset_supports (rps, "xsfvfnrclipxfqf");
+    case INSN_CLASS_XESPLP:
+      return riscv_subset_supports (rps, "xesplp");
     default:
       rps->error_handler
         (_("internal: unreachable INSN_CLASS_*"));
@@ -3162,6 +3402,8 @@  riscv_multi_subset_supports_ext (riscv_parse_subset_t *rps,
       return "xtheadzvamo";
     case INSN_CLASS_XSFCEASE:
       return "xsfcease";
+    case INSN_CLASS_XESPLP:
+      return "xesplp";
     default:
       rps->error_handler
         (_("internal: unreachable INSN_CLASS_*"));
diff --git a/bfd/elfxx-riscv.h b/bfd/elfxx-riscv.h
index 1ce682a97ac..2e5e225669b 100644
--- a/bfd/elfxx-riscv.h
+++ b/bfd/elfxx-riscv.h
@@ -40,6 +40,9 @@  extern void riscv_elf32_set_options (struct bfd_link_info *,
 extern void riscv_elf64_set_options (struct bfd_link_info *,
 				     struct riscv_elf_params *);
 
+extern enum elf_riscv_vendor_id
+riscv_elf_vendor_id_str_to_enum(const char *id_str);
+
 extern reloc_howto_type *
 riscv_reloc_name_lookup (bfd *, const char *);
 
@@ -49,6 +52,9 @@  riscv_reloc_type_lookup (bfd *, bfd_reloc_code_real_type);
 extern reloc_howto_type *
 riscv_elf_rtype_to_howto (bfd *, unsigned int r_type);
 
+extern reloc_howto_type *
+riscv_elf_rtype_to_vendor_howto (bfd *, unsigned int, enum elf_riscv_vendor_id);
+
 /* The information of architecture attribute.  */
 struct riscv_subset_t
 {
diff --git a/bfd/libbfd.h b/bfd/libbfd.h
index d367fea33d3..605db395945 100644
--- a/bfd/libbfd.h
+++ b/bfd/libbfd.h
@@ -2467,6 +2467,9 @@  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_RISCV_ESP_LP_OFFSET_9",
+  "BFD_RELOC_RISCV_ESP_LP_OFFSET_12",
   "BFD_RELOC_RL78_NEG8",
   "BFD_RELOC_RL78_NEG16",
   "BFD_RELOC_RL78_NEG24",
diff --git a/bfd/reloc.c b/bfd/reloc.c
index d3ddafb7305..f44156694a7 100644
--- a/bfd/reloc.c
+++ b/bfd/reloc.c
@@ -5036,9 +5036,18 @@  ENUMX
   BFD_RELOC_RISCV_SET_ULEB128
 ENUMX
   BFD_RELOC_RISCV_SUB_ULEB128
+ENUMX
+  BFD_RELOC_RISCV_VENDOR
 ENUMDOC
   RISC-V relocations.
 
+ENUM
+  BFD_RELOC_RISCV_ESP_LP_OFFSET_9
+ENUMX
+  BFD_RELOC_RISCV_ESP_LP_OFFSET_12
+ENUMDOC
+  RISC-V Vendor (Espressif) relocations.
+
 ENUM
   BFD_RELOC_RL78_NEG8
 ENUMX
diff --git a/binutils/readelf.c b/binutils/readelf.c
index dd1871d8c75..92e4d7111dc 100644
--- a/binutils/readelf.c
+++ b/binutils/readelf.c
@@ -1967,6 +1967,7 @@  dump_relocations (Filedata *          filedata,
   size_t i;
   Elf_Internal_Rela * rels;
   bool res = true;
+  char *vendor_id = NULL;
 
   if (rel_type == reltype_unknown)
     rel_type = guess_is_rela (filedata->file_header.e_machine) ? reltype_rela : reltype_rel;
@@ -2197,7 +2198,10 @@  dump_relocations (Filedata *          filedata,
 	  break;
 
 	case EM_RISCV:
-	  rtype = elf_riscv_reloc_type (type);
+	  if (vendor_id)
+	    rtype = elf_riscv_vendor_reloc_type (vendor_id, type);
+	  else
+	    rtype = elf_riscv_reloc_type (type);
 	  break;
 
 	case EM_ALPHA:
@@ -2397,6 +2401,12 @@  dump_relocations (Filedata *          filedata,
 	  break;
 	}
 
+      if (vendor_id)
+	{
+	  free(vendor_id);
+	  vendor_id = NULL;
+	}
+
       if (rtype == NULL)
 	printf (_("unrecognized: %-7lx"), (unsigned long) type & 0xffffffff);
       else
@@ -2500,6 +2510,7 @@  dump_relocations (Filedata *          filedata,
 		    sec_name = printable_section_name_from_index
 		      (filedata, psym->st_shndx, NULL);
 
+		  vendor_id = strdup(sec_name);
 		  print_symbol_name (22, sec_name);
 		}
 	      else if (strtab == NULL)
@@ -2512,6 +2523,7 @@  dump_relocations (Filedata *          filedata,
 		}
 	      else
 		{
+		  vendor_id = strdup(strtab + psym->st_name);
 		  print_symbol_name (22, strtab + psym->st_name);
 		  if (version_string)
 		    printf (sym_info == symbol_public ? "@@%s" : "@%s",
diff --git a/gas/config/tc-riscv.c b/gas/config/tc-riscv.c
index 8485ad441f5..a63d4bb0f2e 100644
--- a/gas/config/tc-riscv.c
+++ b/gas/config/tc-riscv.c
@@ -734,6 +734,54 @@  riscv_target_format (void)
     return xlen == 64 ? "elf64-littleriscv" : "elf32-littleriscv";
 }
 
+/* Insert R_RISCV_VENDOR relocation right before the custom relocation
+ * passed in fixP. */
+static void
+insert_vendor_relocation (fixS *fixP, segT target_seg, const char *vendor_id)
+{
+  segT saved_seg;
+  symbolS *sym;
+  const flagword vendor_sym_flags = (BSF_NO_FLAGS | BSF_LOCAL);
+
+  /* Check if the vendor ID symbol already exists. */
+  for (sym = symbol_rootP; sym != NULL; sym = symbol_next (sym))
+    {
+      /* Ensure we find a local symbol in the desired segment.
+         This prevents R_RISCV_VENDOR relocation against a global symbol
+         with the same name, if it exists.  */
+      if (S_GET_SEGMENT (sym) == target_seg &&
+	  symbol_get_bfdsym (sym)->flags == vendor_sym_flags &&
+	  strcmp (S_GET_NAME (sym), vendor_id) == 0)
+	{
+	  break; /* Symbol found.  */
+	}
+    }
+
+  /* If symbol does not exist, create a new one.  */
+  if (sym == NULL)
+    {
+      sym = symbol_new (vendor_id, target_seg, &zero_address_frag, 0);
+      symbol_get_bfdsym (sym)->flags = vendor_sym_flags;
+    }
+
+  /* Temporarily switch segment for fix_new().  */
+  saved_seg = now_seg;
+  now_seg = target_seg; /* Ensure fix_new_internal() uses the correct segment.  */
+
+  /* Insert the R_RISCV_VENDOR relocation.  */
+  /* Since it's not possible to access the previous element in the fixup list,
+     it worked around by calling fix_at_start when 'where == 0'.
+     Otherwise, place the fix at 'where - 1' to ensure correct ordering of
+     R_RISCV_VENDOR and R_RISCV_CUSTOM.  */
+  if (fixP->fx_where == 0)
+    fix_at_start (fixP->fx_frag, 4, sym, 0, false, BFD_RELOC_RISCV_VENDOR);
+  else
+    fix_new (fixP->fx_frag, fixP->fx_where - 1, 4, sym, 0, false, BFD_RELOC_RISCV_VENDOR);
+
+  /* Restore the previous segment.  */
+  now_seg = saved_seg;
+}
+
 /* Return the length of instruction INSN.  */
 
 static inline unsigned int
@@ -1752,6 +1800,39 @@  validate_riscv_insn (const struct riscv_opcode *opc, int length)
 		    goto unknown_validate_operand;
 		}
 		break;
+	    case 'e':
+	      switch (*++oparg)
+		{
+		case 'l':
+		  switch (*++oparg)
+		    {
+		    case 'c': used_bits |= ENCODE_ESPPIE_LP_COUNT (-1U); break;	/* Xelc */
+		    case 'i': used_bits |= ENCODE_ESPPIE_LP_ID (-1U); break;	/* Xeli */
+		    case 'o':
+		      switch (*++oparg)
+			{
+			case '1':
+			  switch (*++oparg)
+			    {
+			    case '2': used_bits |= ENCODE_ESPPIE_LP_OFFSET_12 (-1U); break;	/* Xelo12 */
+			    default:	/* Xelo1[.] */
+			      goto unknown_validate_operand;
+			    }
+			  break;
+			case '9': used_bits |= ENCODE_ESPPIE_LP_OFFSET_9 (-1U); break;	/* Xelo9 */
+			default:	/* Xelo[.] */
+			  goto unknown_validate_operand;
+			}
+		      break;
+		    default:	/* Xel[.] */
+		      goto unknown_validate_operand;
+		    }
+		  break;
+		default:	/* Xe[.] */
+		  goto unknown_validate_operand;
+		}
+	      break;
+
 	    default:
 	      goto unknown_validate_operand;
 	    }
@@ -4173,6 +4254,80 @@  riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr,
 #undef ENCODE_UIMM_BIT_FIELD
 		  break;
 
+		case 'e': /* Vendor-specific (Espressif) operands.  */
+		  switch (*++oparg)
+		    {
+		    case 'l': /* Xesplp extension operands.  */
+		      switch (*++oparg)
+			{
+			case 'c':	/* Xelc */
+			  if (my_getSmallExpression
+			      (imm_expr, imm_reloc, asarg, p)
+			      || imm_expr->X_op != O_constant
+			      || imm_expr->X_add_number > 4095
+			      || imm_expr->X_add_number < 0
+			      || !VALID_ESPPIE_LP_COUNT (imm_expr->
+							 X_add_number))
+			    {
+			      as_bad (_("bad value for lp_count, "
+					"must be in range 0..4095 with step 1"));
+			      break;
+			    }
+			  ip->insn_opcode |=
+			    ENCODE_ESPPIE_LP_COUNT (imm_expr->X_add_number);
+			esp_imm_done:
+			  asarg = expr_parse_end;
+			  imm_expr->X_op = O_absent;
+			  continue;
+			case 'i':	/* Xeli */
+			  if (my_getSmallExpression
+			      (imm_expr, imm_reloc, asarg, p)
+			      || imm_expr->X_op != O_constant
+			      || imm_expr->X_add_number > 1
+			      || imm_expr->X_add_number < 0
+			      || !VALID_ESPPIE_LP_ID (imm_expr->X_add_number))
+			    {
+			      as_bad (_("bad value for lp_id, "
+					"must be in range 0..1 with step 1"));
+			      break;
+			    }
+			  ip->insn_opcode |=
+			    ENCODE_ESPPIE_LP_ID (imm_expr->X_add_number);
+			  goto esp_imm_done;
+			case 'o':
+			  switch (*++oparg)
+			    {
+			    case '1':
+			      switch (*++oparg)
+				{
+				case '2':	/* Xelo12 */
+				  my_getExpression (imm_expr, asarg);
+				  *imm_reloc =
+				    BFD_RELOC_RISCV_ESP_LP_OFFSET_12;
+				  asarg = expr_parse_end;
+				  continue;
+				default:	/* Xelo1[.] */
+				  goto unknown_riscv_ip_operand;
+				}
+			      break;
+			    case '9':	/* Xelo9 */
+			      my_getExpression (imm_expr, asarg);
+			      *imm_reloc = BFD_RELOC_RISCV_ESP_LP_OFFSET_9;
+			      asarg = expr_parse_end;
+			      continue;
+			    default:	/* Xelo[.] */
+			      goto unknown_riscv_ip_operand;
+			    }
+			  break;
+			default:	/* Xel[.] */
+			  goto unknown_riscv_ip_operand;
+			}
+		      break;
+		    default:	/* Xe[.] */
+		      goto unknown_riscv_ip_operand;
+		    }
+		  break;
+
 		default:
 		  goto unknown_riscv_ip_operand;
 		}
@@ -4855,6 +5010,43 @@  md_apply_fix (fixS *fixP, valueT *valP, segT seg)
     case BFD_RELOC_RISCV_ALIGN:
       break;
 
+    case BFD_RELOC_RISCV_VENDOR:
+      break;
+
+    case BFD_RELOC_RISCV_ESP_LP_OFFSET_9:
+	if (fixP->fx_addsy)
+	  {
+	    /* Fill in a tentative value to improve objdump readability.  */
+	    bfd_vma target = S_GET_VALUE (fixP->fx_addsy) + *valP;
+	    bfd_vma delta = target - md_pcrel_from (fixP);
+	    if (!VALID_ESPPIE_LP_OFFSET_9 (delta))
+	      as_bad_where (fixP->fx_file, fixP->fx_line,
+	      	      _("bad value for lp_offset_9, must be in range 0..1022 with step 2"));
+	    bfd_putl32 (bfd_getl32 (buf) | ENCODE_ESPPIE_LP_OFFSET_9 (delta), buf);
+	    if (!riscv_opts.relax)
+	      fixP->fx_done = 1;
+	  }
+	if (!fixP->fx_done)
+	  insert_vendor_relocation(fixP, seg, R_RISCV_ESPRESSIF_VENDOR_ID_STR);
+      break;
+
+    case BFD_RELOC_RISCV_ESP_LP_OFFSET_12:
+	if (fixP->fx_addsy)
+	  {
+	    /* Fill in a tentative value to improve objdump readability.  */
+	    bfd_vma target = S_GET_VALUE (fixP->fx_addsy) + *valP;
+	    bfd_vma delta = target - md_pcrel_from (fixP);
+	    if (!VALID_ESPPIE_LP_OFFSET_12 (delta))
+	      as_bad_where (fixP->fx_file, fixP->fx_line,
+	      	      _("bad value for lp_offset_12, must be in range 0..8190 with step 2"));
+	    bfd_putl32 (bfd_getl32 (buf) | ENCODE_ESPPIE_LP_OFFSET_12 (delta), buf);
+	    if (!riscv_opts.relax)
+	      fixP->fx_done = 1;
+	  }
+	if (!fixP->fx_done)
+	  insert_vendor_relocation(fixP, seg, R_RISCV_ESPRESSIF_VENDOR_ID_STR);
+      break;
+
     default:
       /* We ignore generic BFD relocations we don't know about.  */
       if (bfd_reloc_type_lookup (stdoutput, fixP->fx_r_type) != NULL)
diff --git a/gas/testsuite/gas/riscv/march-help.l b/gas/testsuite/gas/riscv/march-help.l
index d77472fda9c..78753c5562c 100644
--- a/gas/testsuite/gas/riscv/march-help.l
+++ b/gas/testsuite/gas/riscv/march-help.l
@@ -173,3 +173,4 @@  All available -march extensions for RISC-V:
 	xsfvqmaccqoq                            1.0
 	xsfvqmaccdod                            1.0
 	xsfvfnrclipxfqf                         1.0
+	xesplp                                  1.0
diff --git a/gas/testsuite/gas/riscv/xesplp-reloc.d b/gas/testsuite/gas/riscv/xesplp-reloc.d
new file mode 100644
index 00000000000..6d83ff59e30
--- /dev/null
+++ b/gas/testsuite/gas/riscv/xesplp-reloc.d
@@ -0,0 +1,12 @@ 
+#as: -march=rv32ic_xesplp10
+#source: xesplp-reloc.s
+#objdump: -r
+
+.*:[ 	]+file format .*
+
+RELOCATION RECORDS FOR .*
+.*
+0+0 R_RISCV_VENDOR[ 	]+esp
+0+0 R_RISCV_ESP_LP_OFFSET_12.*
+0+3 R_RISCV_VENDOR[ 	]+esp
+0+4 R_RISCV_ESP_LP_OFFSET_9.*
diff --git a/gas/testsuite/gas/riscv/xesplp-reloc.s b/gas/testsuite/gas/riscv/xesplp-reloc.s
new file mode 100644
index 00000000000..643b478df41
--- /dev/null
+++ b/gas/testsuite/gas/riscv/xesplp-reloc.s
@@ -0,0 +1,7 @@ 
+    .global _start
+    .type _start, @function
+_start:
+    esp.lp.setup    0,t0,last_inst + 4
+    esp.lp.setupi	0,4,last_inst - 4
+last_inst:
+    nop
diff --git a/include/elf/riscv.h b/include/elf/riscv.h
index 2ea1ae82eb6..0f3a5c4ded6 100644
--- a/include/elf/riscv.h
+++ b/include/elf/riscv.h
@@ -95,15 +95,113 @@  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)
+  /* update R_RISCV_internal_first if add new reloc.  */
+
+  /* Vendor-specific (nonstandard) relocations are defined here only to provide
+     string version names (in readelf).  */
+  RELOC_NUMBER (R_RISCV_VENDOR, 191)
+  RELOC_NUMBER (R_RISCV_CUSTOM0, 192)
+  RELOC_NUMBER (R_RISCV_CUSTOM1, 193)
+  RELOC_NUMBER (R_RISCV_CUSTOM2, 194)
+  RELOC_NUMBER (R_RISCV_CUSTOM3, 195)
+  RELOC_NUMBER (R_RISCV_CUSTOM4, 196)
+  RELOC_NUMBER (R_RISCV_CUSTOM5, 197)
+  RELOC_NUMBER (R_RISCV_CUSTOM6, 198)
+  RELOC_NUMBER (R_RISCV_CUSTOM7, 199)
+  RELOC_NUMBER (R_RISCV_CUSTOM8, 200)
+  RELOC_NUMBER (R_RISCV_CUSTOM9, 201)
+  RELOC_NUMBER (R_RISCV_CUSTOM10, 202)
+  RELOC_NUMBER (R_RISCV_CUSTOM11, 203)
+  RELOC_NUMBER (R_RISCV_CUSTOM12, 204)
+  RELOC_NUMBER (R_RISCV_CUSTOM13, 205)
+  RELOC_NUMBER (R_RISCV_CUSTOM14, 206)
+  RELOC_NUMBER (R_RISCV_CUSTOM15, 207)
+  RELOC_NUMBER (R_RISCV_CUSTOM16, 208)
+  RELOC_NUMBER (R_RISCV_CUSTOM17, 209)
+  RELOC_NUMBER (R_RISCV_CUSTOM18, 210)
+  RELOC_NUMBER (R_RISCV_CUSTOM19, 211)
+  RELOC_NUMBER (R_RISCV_CUSTOM20, 212)
+  RELOC_NUMBER (R_RISCV_CUSTOM21, 213)
+  RELOC_NUMBER (R_RISCV_CUSTOM22, 214)
+  RELOC_NUMBER (R_RISCV_CUSTOM23, 215)
+  RELOC_NUMBER (R_RISCV_CUSTOM24, 216)
+  RELOC_NUMBER (R_RISCV_CUSTOM25, 217)
+  RELOC_NUMBER (R_RISCV_CUSTOM26, 218)
+  RELOC_NUMBER (R_RISCV_CUSTOM27, 219)
+  RELOC_NUMBER (R_RISCV_CUSTOM28, 220)
+  RELOC_NUMBER (R_RISCV_CUSTOM29, 221)
+  RELOC_NUMBER (R_RISCV_CUSTOM30, 222)
+  RELOC_NUMBER (R_RISCV_CUSTOM31, 223)
+  RELOC_NUMBER (R_RISCV_CUSTOM32, 224)
+  RELOC_NUMBER (R_RISCV_CUSTOM33, 225)
+  RELOC_NUMBER (R_RISCV_CUSTOM34, 226)
+  RELOC_NUMBER (R_RISCV_CUSTOM35, 227)
+  RELOC_NUMBER (R_RISCV_CUSTOM36, 228)
+  RELOC_NUMBER (R_RISCV_CUSTOM37, 229)
+  RELOC_NUMBER (R_RISCV_CUSTOM38, 230)
+  RELOC_NUMBER (R_RISCV_CUSTOM39, 231)
+  RELOC_NUMBER (R_RISCV_CUSTOM40, 232)
+  RELOC_NUMBER (R_RISCV_CUSTOM41, 233)
+  RELOC_NUMBER (R_RISCV_CUSTOM42, 234)
+  RELOC_NUMBER (R_RISCV_CUSTOM43, 235)
+  RELOC_NUMBER (R_RISCV_CUSTOM44, 236)
+  RELOC_NUMBER (R_RISCV_CUSTOM45, 237)
+  RELOC_NUMBER (R_RISCV_CUSTOM46, 238)
+  RELOC_NUMBER (R_RISCV_CUSTOM47, 239)
+  RELOC_NUMBER (R_RISCV_CUSTOM48, 240)
+  RELOC_NUMBER (R_RISCV_CUSTOM49, 241)
+  RELOC_NUMBER (R_RISCV_CUSTOM50, 242)
+  RELOC_NUMBER (R_RISCV_CUSTOM51, 243)
+  RELOC_NUMBER (R_RISCV_CUSTOM52, 244)
+  RELOC_NUMBER (R_RISCV_CUSTOM53, 245)
+  RELOC_NUMBER (R_RISCV_CUSTOM54, 246)
+  RELOC_NUMBER (R_RISCV_CUSTOM55, 247)
+  RELOC_NUMBER (R_RISCV_CUSTOM56, 248)
+  RELOC_NUMBER (R_RISCV_CUSTOM57, 249)
+  RELOC_NUMBER (R_RISCV_CUSTOM58, 250)
+  RELOC_NUMBER (R_RISCV_CUSTOM59, 251)
+  RELOC_NUMBER (R_RISCV_CUSTOM60, 252)
+  RELOC_NUMBER (R_RISCV_CUSTOM61, 253)
+  RELOC_NUMBER (R_RISCV_CUSTOM62, 254)
+  RELOC_NUMBER (R_RISCV_CUSTOM63, 255)
 END_RELOC_NUMBERS (R_RISCV_max)
 
 /* 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_internal_first (R_RISCV_TLSDESC_CALL + 1)
+#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)
+
+/* Vendor specific relocations macro and structures.  */
+#define R_RISCV_ESPRESSIF_VENDOR_ID_STR "esp"
+
+/* VENDOR IDs for internal usage.  */
+enum elf_riscv_vendor_id {
+  R_RISCV_VENDOR_ID_NONE = -1,
+  R_RISCV_VENDOR_ID_ESP,
+};
+
+/* Vendor (Espressif) relocations.  */
+START_RELOC_NUMBERS (elf_riscv_esp_reloc_type)
+  RELOC_NUMBER (R_RISCV_ESP_LP_OFFSET_9, 192)
+  RELOC_NUMBER (R_RISCV_ESP_LP_OFFSET_12, 193)
+END_RELOC_NUMBERS (R_RISCV_ESP_max)
+
+#ifdef RELOC_MACROS_GEN_FUNC
+static const char *elf_riscv_vendor_reloc_type (const char *vendor, unsigned long rtype);
+static const char *
+elf_riscv_vendor_reloc_type (const char *vendor, unsigned long rtype)
+{
+  if (strcmp(vendor, R_RISCV_ESPRESSIF_VENDOR_ID_STR) == 0)
+  {
+    return elf_riscv_esp_reloc_type(rtype);
+  }
+  return elf_riscv_reloc_type(rtype);
+}
+#endif /* RELOC_MACROS_GEN_FUNC */
 
 /* Processor specific flags for the ELF header e_flags field.  */
 
diff --git a/include/opcode/riscv-opc.h b/include/opcode/riscv-opc.h
index fea49481498..717f53b7db2 100644
--- a/include/opcode/riscv-opc.h
+++ b/include/opcode/riscv-opc.h
@@ -3804,6 +3804,19 @@ 
 #define MASK_SFVFNRCLIPXUFQF 0xfe00707f
 #define MATCH_SFVFNRCLIPXFQF 0x8e00505b
 #define MASK_SFVFNRCLIPXFQF 0xfe00707f
+/* Vendor-specific (Espressif) hardware loop instructions.  */
+#define MATCH_ESP_LP_COUNT 0x202b
+#define MASK_ESP_LP_COUNT 0xfff07f7f
+#define MATCH_ESP_LP_COUNTI 0x302b
+#define MASK_ESP_LP_COUNTI 0xfff7f
+#define MATCH_ESP_LP_ENDI 0x102b
+#define MASK_ESP_LP_ENDI 0xfff7f
+#define MATCH_ESP_LP_SETUP 0x402b
+#define MASK_ESP_LP_SETUP 0x7f7f
+#define MATCH_ESP_LP_SETUPI 0x502b
+#define MASK_ESP_LP_SETUPI 0x707f
+#define MATCH_ESP_LP_STARTI 0x2b
+#define MASK_ESP_LP_STARTI 0xfff7f
 /* Unprivileged Counter/Timers CSR addresses.  */
 #define CSR_CYCLE 0xc00
 #define CSR_TIME 0xc01
@@ -4934,6 +4947,13 @@  DECLARE_INSN(th_sync_s, MATCH_TH_SYNC_S, MASK_TH_SYNC_S)
 /* XVentanaCondOps instructions. */
 DECLARE_INSN(vt_maskc, MATCH_VT_MASKC, MASK_VT_MASKC)
 DECLARE_INSN(vt_maskcn, MATCH_VT_MASKCN, MASK_VT_MASKCN)
+/* Vendor-specific (Espressif) Xesplp instructions.  */
+DECLARE_INSN(esp_lp_count, MATCH_ESP_LP_COUNT, MASK_ESP_LP_COUNT)
+DECLARE_INSN(esp_lp_counti, MATCH_ESP_LP_COUNTI, MASK_ESP_LP_COUNTI)
+DECLARE_INSN(esp_lp_endi, MATCH_ESP_LP_ENDI, MASK_ESP_LP_ENDI)
+DECLARE_INSN(esp_lp_setup, MATCH_ESP_LP_SETUP, MASK_ESP_LP_SETUP)
+DECLARE_INSN(esp_lp_setupi, MATCH_ESP_LP_SETUPI, MASK_ESP_LP_SETUPI)
+DECLARE_INSN(esp_lp_starti, MATCH_ESP_LP_STARTI, MASK_ESP_LP_STARTI)
 
 /* Zicfiss instructions.  */
 DECLARE_INSN(sspush, MATCH_SSPUSH, MASK_SSPUSH)
diff --git a/include/opcode/riscv.h b/include/opcode/riscv.h
index d76bcdb295c..26d05a463b0 100644
--- a/include/opcode/riscv.h
+++ b/include/opcode/riscv.h
@@ -132,6 +132,15 @@  static inline unsigned int riscv_insn_length (insn_t insn)
   ((RV_X(x, 25, 1)) | (RV_X(x, 20, 5) << 1) | (RV_IMM_SIGN_N(x, 20, 5) << 5))
 #define EXTRACT_CV_SIMD_UIMM6(x) \
   ((RV_X(x, 25, 1)) | (RV_X(x, 20, 5) << 1))
+/* Vendor-specific (Espressif) extract macros.  */
+#define EXTRACT_ESPPIE_LP_COUNT(x) \
+  (RV_X(x, 20, 12))
+#define EXTRACT_ESPPIE_LP_ID(x) \
+  (RV_X(x, 7, 1))
+#define EXTRACT_ESPPIE_LP_OFFSET_12(x) \
+  (RV_X(x, 20, 12) << 1)
+#define EXTRACT_ESPPIE_LP_OFFSET_9(x) \
+  ((RV_X(x, 15, 5) << 1) | (RV_X(x, 8, 4) << 6))
 
 #define ENCODE_ITYPE_IMM(x) \
   (RV_X(x, 0, 12) << 20)
@@ -200,6 +209,15 @@  static inline unsigned int riscv_insn_length (insn_t insn)
   ((RV_X(x, 0, 1) << 25) | (RV_X(x, 1, 5) << 20))
 #define ENCODE_CV_SIMD_UIMM6(x) \
   ((RV_X(x, 0, 1) << 25) | (RV_X(x, 1, 5) << 20))
+/* Vendor-specific (Espressif) encode macros.  */
+#define ENCODE_ESPPIE_LP_COUNT(x) \
+  (RV_X(x, 0, 12) << 20)
+#define ENCODE_ESPPIE_LP_ID(x) \
+  (RV_X(x, 0, 1) << 7)
+#define ENCODE_ESPPIE_LP_OFFSET_12(x) \
+  (RV_X(x, 1, 12) << 20)
+#define ENCODE_ESPPIE_LP_OFFSET_9(x) \
+  ((RV_X(x, 1, 5) << 15) | (RV_X(x, 6, 4) << 8))
 
 #define VALID_ITYPE_IMM(x) (EXTRACT_ITYPE_IMM(ENCODE_ITYPE_IMM(x)) == (x))
 #define VALID_STYPE_IMM(x) (EXTRACT_STYPE_IMM(ENCODE_STYPE_IMM(x)) == (x))
@@ -228,6 +246,11 @@  static inline unsigned int riscv_insn_length (insn_t insn)
 #define VALID_ZCB_BYTE_UIMM(x) (EXTRACT_ZCB_BYTE_UIMM(ENCODE_ZCB_BYTE_UIMM(x)) == (x))
 #define VALID_ZCB_HALFWORD_UIMM(x) (EXTRACT_ZCB_HALFWORD_UIMM(ENCODE_ZCB_HALFWORD_UIMM(x)) == (x))
 #define VALID_ZCMP_SPIMM(x) (EXTRACT_ZCMP_SPIMM(ENCODE_ZCMP_SPIMM(x)) == (x))
+/* Vendor-specific (Espressif) validation macros.  */
+#define VALID_ESPPIE_LP_COUNT(x) (EXTRACT_ESPPIE_LP_COUNT(ENCODE_ESPPIE_LP_COUNT(x)) == (x))
+#define VALID_ESPPIE_LP_ID(x) (EXTRACT_ESPPIE_LP_ID(ENCODE_ESPPIE_LP_ID(x)) == (x))
+#define VALID_ESPPIE_LP_OFFSET_12(x) (EXTRACT_ESPPIE_LP_OFFSET_12(ENCODE_ESPPIE_LP_OFFSET_12(x)) == (x))
+#define VALID_ESPPIE_LP_OFFSET_9(x) (EXTRACT_ESPPIE_LP_OFFSET_9(ENCODE_ESPPIE_LP_OFFSET_9(x)) == (x))
 
 #define RISCV_RTYPE(insn, rd, rs1, rs2) \
   ((MATCH_ ## insn) | ((rd) << OP_SH_RD) | ((rs1) << OP_SH_RS1) | ((rs2) << OP_SH_RS2))
@@ -563,6 +586,7 @@  enum riscv_insn_class
   INSN_CLASS_XSFVQMACCQOQ,
   INSN_CLASS_XSFVQMACCDOD,
   INSN_CLASS_XSFVFNRCLIPXFQF,
+  INSN_CLASS_XESPLP,
 };
 
 /* This structure holds information for a particular instruction.  */
diff --git a/ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp b/ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp
index 9cb847d3d8a..0b4a90456bf 100644
--- a/ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp
+++ b/ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp
@@ -179,6 +179,8 @@  if [istarget "riscv*-*-*"] {
 	    "-march=rv64i -mabi=lp64" {weakref64.s} \
 	    {{objdump -d weakref64.d}} "weakref64"]]
 
+    run_dump_test "xesplp-reloc"
+
     # The following tests require shared library support.
     if ![check_shared_lib_support] {
 	return
diff --git a/ld/testsuite/ld-riscv-elf/xesplp-reloc.d b/ld/testsuite/ld-riscv-elf/xesplp-reloc.d
new file mode 100644
index 00000000000..723de71200a
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/xesplp-reloc.d
@@ -0,0 +1,13 @@ 
+#source: xesplp-reloc.s
+#as: -march=rv32i_xesplp
+#ld:
+#objdump: -d
+
+#...
+Disassembly of section .text:
+#...
+0+[0-9a-f]+ <_start>:
+.*:[ 	]+[0-9a-f]+[ 	]+esp.lp.setup[ 	]+.*<last_inst\+0x4>
+.*:[ 	]+[0-9a-f]+[ 	]+esp.lp.setupi[ 	]+.*<_start\+0x4>
+0+[0-9a-f]+ <last_inst>:
+#...
diff --git a/ld/testsuite/ld-riscv-elf/xesplp-reloc.s b/ld/testsuite/ld-riscv-elf/xesplp-reloc.s
new file mode 100644
index 00000000000..643b478df41
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/xesplp-reloc.s
@@ -0,0 +1,7 @@ 
+    .global _start
+    .type _start, @function
+_start:
+    esp.lp.setup    0,t0,last_inst + 4
+    esp.lp.setupi	0,4,last_inst - 4
+last_inst:
+    nop
diff --git a/opcodes/riscv-dis.c b/opcodes/riscv-dis.c
index f862ef28a16..c7949b40702 100644
--- a/opcodes/riscv-dis.c
+++ b/opcodes/riscv-dis.c
@@ -880,6 +880,57 @@  print_insn_args (const char *oparg, insn_t l, bfd_vma pc, disassemble_info *info
 		  break;
 		}
 	      break;
+	    case 'e': /* Vendor-specific (Espressif) operands.  */
+	      switch (*++oparg)
+		{
+		case 'l': /* Xesplp extension operands.  */
+		  switch (*++oparg)
+		    {
+		    case 'c':	/* Xelc */
+		      print (info->stream, dis_style_immediate, "%d",
+			     (int) EXTRACT_ESPPIE_LP_COUNT (l));
+		      break;	/* Xelc */
+		    case 'i':	/* Xeli */
+		      print (info->stream, dis_style_immediate, "%d",
+			     (int) EXTRACT_ESPPIE_LP_ID (l));
+		      break;	/* Xeli */
+		    case 'o':
+		      switch (*++oparg)
+			{
+			case '1':
+			  switch (*++oparg)
+			    {
+			    case '2':	/* Xelo12 */
+			      maybe_print_address (pd, 0,
+						   EXTRACT_ESPPIE_LP_OFFSET_12
+						   (l) + pc, 0);
+			      print (info->stream, dis_style_address_offset,
+				     "%d",
+				     (int) EXTRACT_ESPPIE_LP_OFFSET_12 (l));
+			      break;	/* Xelo12 */
+			    default:	/* Xelo1[.] */
+			      goto undefined_modifier;
+			    }
+			  break;
+			case '9':	/* Xelo9 */
+			  maybe_print_address (pd, 0,
+					       EXTRACT_ESPPIE_LP_OFFSET_9 (l)
+					       + pc, 0);
+			  print (info->stream, dis_style_address_offset, "%d",
+				 (int) EXTRACT_ESPPIE_LP_OFFSET_9 (l));
+			  break;	/* Xelo9 */
+			default:	/* Xelo[.] */
+			  goto undefined_modifier;
+			}
+		      break;
+		    default:	/* Xel[.] */
+		      goto undefined_modifier;
+		    }
+		  break;
+		default:	/* Xe[.] */
+		  goto undefined_modifier;
+		}
+	      break;
 	    default:
 	      goto undefined_modifier;
 	    }
diff --git a/opcodes/riscv-opc.c b/opcodes/riscv-opc.c
index e07f72b89f2..0c84acf81ad 100644
--- a/opcodes/riscv-opc.c
+++ b/opcodes/riscv-opc.c
@@ -3522,6 +3522,14 @@  const struct riscv_opcode riscv_opcodes[] =
 {"sf.vfnrclip.xu.f.qf", 0, INSN_CLASS_XSFVFNRCLIPXFQF, "Vd,Vt,S", MATCH_SFVFNRCLIPXUFQF, MASK_SFVFNRCLIPXUFQF, match_opcode, 0},
 {"sf.vfnrclip.x.f.qf",  0, INSN_CLASS_XSFVFNRCLIPXFQF, "Vd,Vt,S", MATCH_SFVFNRCLIPXFQF, MASK_SFVFNRCLIPXFQF, match_opcode, 0},
 
+/* Vendor-specific (Espressif) hardware loop instructions (Xesplp).  */
+{"esp.lp.setupi", 32, INSN_CLASS_XESPLP, "Xeli,Xelc,Xelo9", MATCH_ESP_LP_SETUPI, MASK_ESP_LP_SETUPI, match_opcode, 0 },
+{"esp.lp.setup",  32, INSN_CLASS_XESPLP, "Xeli,s,Xelo12",   MATCH_ESP_LP_SETUP,  MASK_ESP_LP_SETUP,  match_opcode, 0 },
+{"esp.lp.starti", 32, INSN_CLASS_XESPLP, "Xeli,Xelo12",     MATCH_ESP_LP_STARTI, MASK_ESP_LP_STARTI, match_opcode, 0 },
+{"esp.lp.endi",   32, INSN_CLASS_XESPLP, "Xeli,Xelo12",     MATCH_ESP_LP_ENDI,   MASK_ESP_LP_ENDI,   match_opcode, 0 },
+{"esp.lp.counti", 32, INSN_CLASS_XESPLP, "Xeli,Xelc",       MATCH_ESP_LP_COUNTI, MASK_ESP_LP_COUNTI, match_opcode, 0 },
+{"esp.lp.count",  32, INSN_CLASS_XESPLP, "Xeli,s",          MATCH_ESP_LP_COUNT,  MASK_ESP_LP_COUNT,  match_opcode, 0 },
+
 /* Terminate the list.  */
 {0, 0, INSN_CLASS_NONE, 0, 0, 0, 0, 0}
 };