]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
RISC-V: Support for unlabeled landing pad PLT generation
authorKito Cheng <kito.cheng@sifive.com>
Wed, 11 Jun 2025 08:33:49 +0000 (16:33 +0800)
committerNelson Chu <nelson@rivosinc.com>
Tue, 24 Jun 2025 10:14:45 +0000 (18:14 +0800)
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

bfd/elfnn-riscv.c
bfd/elfxx-riscv.h
include/opcode/riscv.h
ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp
ld/testsuite/ld-riscv-elf/zicfilp-unlabeled-plt.d [new file with mode: 0644]
ld/testsuite/ld-riscv-elf/zicfilp-unlabeled-plt.s [new file with mode: 0644]

index 49e812ea99ca8a794b8e83b8df0218c35831eade..790f0397cf5a7590e9be10c5c915cdee3d596486 100644 (file)
@@ -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
index d8845afb666030e60af4a3dd57b91f94ffd3064e..db494d08a0c7c5fb2499d4b4526bfdc4078a1242 100644 (file)
@@ -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
index 7fca806b643ec843bb2ebe007013fe9f204e77e3..2b146afd86883cceacc069b03f9891ea5d3bb110 100644 (file)
@@ -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
index e03e44ae4a25147cdf876abc54df993bacb74158..e103df66e11336a9f34deaab6785f34adc264c0a 100644 (file)
@@ -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 (file)
index 0000000..2181fc4
--- /dev/null
@@ -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 (file)
index 0000000..628fca8
--- /dev/null
@@ -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: