[binutils-gdb] RISC-V: Support for unlabeled landing pad PLT generation

Nelson Chu nelsonc1225@sourceware.org
Tue Jun 24 10:16:28 GMT 2025


https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=9b4b518eceaa1f59acfb88013406b4203f6a3016

commit 9b4b518eceaa1f59acfb88013406b4203f6a3016
Author: Kito Cheng <kito.cheng@sifive.com>
Date:   Wed Jun 11 16:33:49 2025 +0800

    RISC-V: Support for unlabeled landing pad PLT generation
    
    This patch adds support for generating unlabeled landing pad PLT entries
    for the RISC-V architecture. Unlabeled landing pad will place a LPAD
    instruction at the PLT entry and PLT header, also PLT header will have
    few changes due to the offset is different from the original one.
    
    Ref: https://github.com/riscv-non-isa/riscv-elf-psabi-doc/pull/417

Diff:
---
 bfd/elfnn-riscv.c                                 | 141 ++++++++++++++++++++++
 bfd/elfxx-riscv.h                                 |   3 +-
 include/opcode/riscv.h                            |   1 +
 ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp        |   2 +
 ld/testsuite/ld-riscv-elf/zicfilp-unlabeled-plt.d |  35 ++++++
 ld/testsuite/ld-riscv-elf/zicfilp-unlabeled-plt.s |  21 ++++
 6 files changed, 202 insertions(+), 1 deletion(-)

diff --git a/bfd/elfnn-riscv.c b/bfd/elfnn-riscv.c
index 49e812ea99c..790f0397cf5 100644
--- a/bfd/elfnn-riscv.c
+++ b/bfd/elfnn-riscv.c
@@ -317,6 +317,12 @@ riscv_is_insn_reloc (const reloc_howto_type *howto)
 #define PLT_ENTRY_INSNS 4
 #define PLT_HEADER_SIZE (PLT_HEADER_INSNS * 4)
 #define PLT_ENTRY_SIZE (PLT_ENTRY_INSNS * 4)
+
+#define PLT_ZICFILP_UNLABELED_HEADER_INSNS 12
+#define PLT_ZICFILP_UNLABELED_ENTRY_INSNS 4
+#define PLT_ZICFILP_UNLABELED_HEADER_SIZE (PLT_ZICFILP_UNLABELED_HEADER_INSNS * 4)
+#define PLT_ZICFILP_UNLABELED_ENTRY_SIZE (PLT_ZICFILP_UNLABELED_ENTRY_INSNS * 4)
+
 #define GOT_ENTRY_SIZE RISCV_ELF_WORD_BYTES
 #define TLS_GD_GOT_ENTRY_SIZE (RISCV_ELF_WORD_BYTES * 2)
 #define TLS_IE_GOT_ENTRY_SIZE RISCV_ELF_WORD_BYTES
@@ -334,6 +340,31 @@ riscv_is_insn_reloc (const reloc_howto_type *howto)
 # define MATCH_LREG MATCH_LD
 #endif
 
+
+/* Check whether the compact PLT is used in this object.  Tools need this
+   to dump the correct PLT header contents.  */
+
+static long
+elfNN_riscv_get_synthetic_symtab (bfd *abfd,
+				  long symcount,
+				  asymbol **syms,
+				  long dynsymcount,
+				  asymbol **dynsyms,
+				  asymbol **ret)
+{
+  /* Check Zicfilp PLT.  */
+  elf_property *prop;
+  prop = _bfd_elf_get_property (abfd, GNU_PROPERTY_RISCV_FEATURE_1_AND, 4);
+  if (prop)
+    {
+      if (prop->u.number & GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED)
+	 _bfd_riscv_elf_tdata (abfd)->plt_type |= PLT_ZICFILP_UNLABELED;
+    }
+
+  return _bfd_elf_get_synthetic_symtab (abfd, symcount, syms,
+					dynsymcount, dynsyms, ret);
+}
+
 /* Generate a PLT header.  */
 
 static bool
@@ -381,6 +412,64 @@ riscv_make_plt_header (bfd *output_bfd, struct riscv_elf_link_hash_table *htab)
   return true;
 }
 
+static bool
+riscv_make_plt_zicfilp_unlabeled_header (bfd *output_bfd,
+					 struct riscv_elf_link_hash_table *htab)
+{
+  /*
+      lpad   0  # disable label checking
+      auipc  t2, %hi(.got.plt)          # Rewrite this to using
+      sub    t1, t1, t3                 # shifted .got.plt offset + hdr size + 16
+      l[w|d] t3, %lo(1b)(t2)            # _dl_runtime_resolve
+      addi   t1, t1, -(hdr size + 12)   # shifted .got.plt offset
+      addi   t0, t2, %pcrel_lo(1b)      # &.got.plt
+      srli   t1, t1, log2(16/PTRSIZE)   # .got.plt offset
+      l[w|d] t0, PTRSIZE(t0)            # link map
+      jr     t3
+      nop
+      nop
+      nop  */
+
+  /* RVE has no t3 register, so this won't work, and is not supported.  */
+  if (elf_elfheader (output_bfd)->e_flags & EF_RISCV_RVE)
+    {
+      _bfd_error_handler (_("%pB: warning: RVE PLT generation not supported"),
+			  output_bfd);
+      return false;
+    }
+
+  asection *gotplt = htab->elf.sgotplt;
+  bfd_vma gotplt_addr = sec_addr (gotplt);
+
+  asection *splt = htab->elf.splt;
+  bfd_vma plt_header_addr = sec_addr (splt);
+
+  bfd_vma auipc_addr = plt_header_addr + 4;
+  /* Add INSN_BYTES to skip the lpad instruction.  */
+  bfd_vma gotplt_offset_high = RISCV_PCREL_HIGH_PART (gotplt_addr, auipc_addr);
+  bfd_vma gotplt_offset_low = RISCV_PCREL_LOW_PART (gotplt_addr, auipc_addr);
+
+  uint32_t header[PLT_ZICFILP_UNLABELED_HEADER_INSNS];
+  header[0] = RISCV_UTYPE (LPAD, X_ZERO, 0);
+  header[1] = RISCV_UTYPE (AUIPC, X_T2, gotplt_offset_high);
+  header[2] = RISCV_RTYPE (SUB, X_T1, X_T1, X_T3);
+  header[3] = RISCV_ITYPE (LREG, X_T3, X_T2, gotplt_offset_low);
+  header[4] = RISCV_ITYPE (ADDI, X_T1, X_T1,
+			   (uint32_t) -(PLT_ZICFILP_UNLABELED_HEADER_SIZE + 16));
+  header[5] = RISCV_ITYPE (ADDI, X_T0, X_T2, gotplt_offset_low);
+  header[6] = RISCV_ITYPE (SRLI, X_T1, X_T1, 4 - RISCV_ELF_LOG_WORD_BYTES);
+  header[7] = RISCV_ITYPE (LREG, X_T0, X_T0, RISCV_ELF_WORD_BYTES);
+  header[8] = RISCV_ITYPE (JALR, 0, X_T3, 0);
+  header[9] = RISCV_NOP;
+  header[10] = RISCV_NOP;
+  header[11] = RISCV_NOP;
+
+  for (int i = 0; i < PLT_ZICFILP_UNLABELED_HEADER_INSNS; i++)
+    bfd_putl32 (header[i], splt->contents + 4 * i);
+
+  return true;
+}
+
 /* Generate a PLT entry.  */
 
 static bool
@@ -415,6 +504,40 @@ riscv_make_plt_entry (bfd *output_bfd, asection *gotsec, bfd_vma got_offset,
   return true;
 }
 
+static bool
+riscv_make_plt_zicfilp_unlabeled_entry (bfd *output_bfd, asection *got,
+					bfd_vma got_offset, asection *plt,
+					bfd_vma plt_offset)
+{
+  /*    lpad    0
+    1:  auipc   t3, %pcrel_hi(function@.got.plt)
+	l[w|d]  t3, %pcrel_lo(1b)(t3)
+	jalr    t1, t3 */
+
+  /* RVE has no t3 register, so this won't work, and is not supported.  */
+  if (elf_elfheader (output_bfd)->e_flags & EF_RISCV_RVE)
+    {
+      _bfd_error_handler (_("%pB: warning: RVE PLT generation not supported"),
+			  output_bfd);
+      return false;
+    }
+
+  bfd_vma got_entry_addr = sec_addr(got) + got_offset;
+  bfd_vma plt_entry_addr = sec_addr(plt) + plt_offset;
+  bfd_vma auipc_addr = plt_entry_addr + 4;
+  uint32_t entry[PLT_ZICFILP_UNLABELED_ENTRY_INSNS];
+  entry[0] = RISCV_UTYPE (LPAD, X_ZERO, 0);
+  entry[1] = RISCV_UTYPE (AUIPC, X_T3, RISCV_PCREL_HIGH_PART (got_entry_addr, auipc_addr));
+  entry[2] = RISCV_ITYPE (LREG,  X_T3, X_T3, RISCV_PCREL_LOW_PART (got_entry_addr, auipc_addr));
+  entry[3] = RISCV_ITYPE (JALR, X_T1, X_T3, 0);
+
+  bfd_byte *loc = plt->contents + plt_offset;
+  for (int i = 0; i < PLT_ZICFILP_UNLABELED_ENTRY_INSNS; i++)
+    bfd_putl32 (entry[i], loc + 4 * i);
+
+  return true;
+}
+
 /* Create an entry in an RISC-V ELF linker hash table.  */
 
 static struct bfd_hash_entry *
@@ -541,6 +664,13 @@ setup_plt_values (struct bfd *output_bfd,
       htab->make_plt_entry = riscv_make_plt_entry;
       break;
 
+    case PLT_ZICFILP_UNLABELED:
+      htab->plt_header_size = PLT_ZICFILP_UNLABELED_HEADER_SIZE;
+      htab->plt_entry_size = PLT_ZICFILP_UNLABELED_ENTRY_SIZE;
+      htab->make_plt_header = riscv_make_plt_zicfilp_unlabeled_header;
+      htab->make_plt_entry = riscv_make_plt_zicfilp_unlabeled_entry;
+      break;
+
     default:
       _bfd_error_handler (_("%pB: error: unsupported PLT type: %u"),
 			  output_bfd,
@@ -3717,6 +3847,9 @@ riscv_elf_plt_sym_val (bfd_vma i, const asection *plt,
     case PLT_NORMAL:
       return plt->vma + (PLT_HEADER_SIZE) + (i * PLT_ENTRY_SIZE);
 
+    case PLT_ZICFILP_UNLABELED:
+      return plt->vma + PLT_ZICFILP_UNLABELED_HEADER_SIZE + (i * PLT_ZICFILP_UNLABELED_ENTRY_SIZE);
+
     default:
       abort ();
     }
@@ -5823,6 +5956,13 @@ elfNN_riscv_link_setup_gnu_properties (struct bfd_link_info *info)
   bfd *pbfd = _bfd_riscv_elf_link_setup_gnu_properties (info, &and_prop);
 
   _bfd_riscv_elf_tdata (info->output_bfd)->gnu_and_prop = and_prop;
+
+  if (and_prop & GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED)
+    _bfd_riscv_elf_tdata (info->output_bfd)->plt_type = PLT_ZICFILP_UNLABELED;
+
+  setup_plt_values (info->output_bfd, riscv_elf_hash_table (info),
+		    _bfd_riscv_elf_tdata (info->output_bfd)->plt_type);
+
   return pbfd;
 }
 
@@ -5873,6 +6013,7 @@ elfNN_riscv_merge_gnu_properties (struct bfd_link_info *info, bfd *abfd,
 #define elf_info_to_howto			riscv_info_to_howto_rela
 #define bfd_elfNN_bfd_relax_section		_bfd_riscv_relax_section
 #define bfd_elfNN_mkobject			elfNN_riscv_mkobject
+#define bfd_elfNN_get_synthetic_symtab		elfNN_riscv_get_synthetic_symtab
 #define elf_backend_additional_program_headers \
   riscv_elf_additional_program_headers
 #define elf_backend_modify_segment_map		riscv_elf_modify_segment_map
diff --git a/bfd/elfxx-riscv.h b/bfd/elfxx-riscv.h
index d8845afb666..db494d08a0c 100644
--- a/bfd/elfxx-riscv.h
+++ b/bfd/elfxx-riscv.h
@@ -29,7 +29,8 @@
 
 typedef enum
 {
-    PLT_NORMAL    = 0x0,  /* Normal plts.  */
+    PLT_NORMAL            = 0x0,  /* Normal plts.  */
+    PLT_ZICFILP_UNLABELED = 0x1   /* Landing pad unlabeled plts.  */
 } riscv_plt_type;
 
 struct riscv_elf_params
diff --git a/include/opcode/riscv.h b/include/opcode/riscv.h
index 7fca806b643..2b146afd868 100644
--- a/include/opcode/riscv.h
+++ b/include/opcode/riscv.h
@@ -421,6 +421,7 @@ static inline unsigned int riscv_insn_length (insn_t insn)
 
 /* ABI names for selected x-registers.  */
 
+#define X_ZERO 0
 #define X_RA 1
 #define X_SP 2
 #define X_GP 3
diff --git a/ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp b/ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp
index e03e44ae4a2..e103df66e11 100644
--- a/ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp
+++ b/ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp
@@ -233,6 +233,8 @@ if [istarget "riscv*-*-*"] {
     run_dump_test "property-combine-and-2"
     run_dump_test "property-combine-and-3"
 
+    run_dump_test "zicfilp-unlabeled-plt"
+
     # IFUNC testcases.
     # Check IFUNC by single type relocs.
     run_dump_test_ifunc "ifunc-reloc-call-01" rv32 exe
diff --git a/ld/testsuite/ld-riscv-elf/zicfilp-unlabeled-plt.d b/ld/testsuite/ld-riscv-elf/zicfilp-unlabeled-plt.d
new file mode 100644
index 00000000000..2181fc49786
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/zicfilp-unlabeled-plt.d
@@ -0,0 +1,35 @@
+#name: Unlabled landing pad PLT
+#source: zicfilp-unlabeled-plt.s
+#ld: -shared
+#objdump: -dr -j .plt
+#as: -march=rv64gc_zicfilp
+
+[^:]*: *file format elf64-.*riscv
+
+Disassembly of section \.plt:
+
+[0-9a-f]+ <\.plt>:
+.*:[ 	]+[0-9a-f]+[ 	]+lpad[ 	]+0x0
+.*:[ 	]+[0-9a-f]+[ 	]+auipc[ 	]+t2,0x[0-9a-f]+
+.*:[ 	]+[0-9a-f]+[ 	]+sub[ 	]+t1,t1,t3
+.*:[ 	]+[0-9a-f]+[ 	]+ld[ 	]+t3,[0-9]+\(t2\) # [0-9a-f]+ <\.got\.plt>
+.*:[ 	]+[0-9a-f]+[ 	]+addi[ 	]+t1,t1,-64
+.*:[ 	]+[0-9a-f]+[ 	]+addi[ 	]+t0,t2,[0-9]+
+.*:[ 	]+[0-9a-f]+[ 	]+srli[ 	]+t1,t1,0x1
+.*:[ 	]+[0-9a-f]+[ 	]+ld[ 	]+t0,8\(t0\)
+.*:[ 	]+[0-9a-f]+[ 	]+jr[ 	]+t3
+.*:[ 	]+[0-9a-f]+[ 	]+nop
+.*:[ 	]+[0-9a-f]+[ 	]+nop
+.*:[ 	]+[0-9a-f]+[ 	]+nop
+
+[0-9a-f]+ <foo@plt>:
+.*:[ 	]+[0-9a-f]+[ 	]+lpad[ 	]+0x0
+.*:[ 	]+[0-9a-f]+[ 	]+auipc[ 	]+t3,0x[0-9a-f]+
+.*:[ 	]+[0-9a-f]+[ 	]+ld[ 	]+t3,[0-9]+\(t3\) # [0-9a-f]+ <foo>
+.*:[ 	]+[0-9a-f]+[ 	]+jalr[ 	]+t1,t3
+
+[0-9a-f]+ <bar@plt>:
+.*:[ 	]+[0-9a-f]+[ 	]+lpad[ 	]+0x0
+.*:[ 	]+[0-9a-f]+[ 	]+auipc[ 	]+t3,0x1
+.*:[ 	]+[0-9a-f]+[ 	]+ld[ 	]+t3,[0-9]+\(t3\) # [0-9a-f]+ <bar>
+.*:[ 	]+[0-9a-f]+[ 	]+jalr[ 	]+t1,t3
diff --git a/ld/testsuite/ld-riscv-elf/zicfilp-unlabeled-plt.s b/ld/testsuite/ld-riscv-elf/zicfilp-unlabeled-plt.s
new file mode 100644
index 00000000000..628fca85aa0
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/zicfilp-unlabeled-plt.s
@@ -0,0 +1,21 @@
+	.text
+	.globl _start
+	.type _start,@function
+_start:
+	call foo
+	call bar
+	.section ".note.gnu.property", "a"
+	.p2align 3
+	.long 1f - 0f		/* name length */
+	.long 5f - 2f		/* data length */
+	.long 5			/* note type */
+0:	.asciz "GNU"		/* vendor name */
+1:
+	.p2align 3
+2:	.long 0xc0000000	/* pr_type.  */
+	.long 4f - 3f		/* pr_datasz.  */
+3:
+	.long 0x1		/* CFI_LP.  */
+4:
+	.p2align 3
+5:


More information about the Binutils-cvs mailing list