[lld] [LLD][RISCV][Zicfilp] Generate unlabeled landing pad-style PLT (PR #145461)

Piyou Chen via llvm-commits llvm-commits at lists.llvm.org
Wed Jul 1 23:55:55 PDT 2026


https://github.com/BeMg updated https://github.com/llvm/llvm-project/pull/145461

>From 5dd098753d19b1f3abeced6cf59b72da0b710482 Mon Sep 17 00:00:00 2001
From: Ming-Yi Lai <ming-yi.lai at mediatek.com>
Date: Fri, 15 Mar 2024 17:13:26 +0800
Subject: [PATCH 01/16] [LLD][RISCV][Zicfilp] Generate unlabeled landing
 pad-style PLT

To support dynamic linking when Zicfilp is enabled, lpad insns are inserted into
PLTs. This patch generates the unlabeled landing pad-style PLT, in which all the
lpads have label "0", when ZICFILP-unlabeled is enabled:

--- PLT Header:

```
    lpad 0
1:  auipc  t2, %pcrel_hi(.got.plt)
    sub    t1, t1, t3               # shifted .got.plt offset + hdr size + 16
    l[w|d] t3, %pcrel_lo(1b)(t2)    # _dl_runtime_resolve
    addi   t1, t1, -(hdr size + 16) # 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
```

--- PLT Entry:

```
    lpad 0
1:  auipc   t3, %pcrel_hi(function at .got.plt)
    l[w|d]  t3, %pcrel_lo(1b)(t3)
    jalr    t1, t3
```
---
 lld/ELF/Arch/RISCV.cpp                    |  72 +++++++++++-
 lld/test/ELF/riscv-plt-cfi-lp-unlabeled.s | 135 ++++++++++++++++++++++
 2 files changed, 205 insertions(+), 2 deletions(-)
 create mode 100644 lld/test/ELF/riscv-plt-cfi-lp-unlabeled.s

diff --git a/lld/ELF/Arch/RISCV.cpp b/lld/ELF/Arch/RISCV.cpp
index 72d83159ad8ac..33d3fdcc60ff8 100644
--- a/lld/ELF/Arch/RISCV.cpp
+++ b/lld/ELF/Arch/RISCV.cpp
@@ -27,7 +27,7 @@ using namespace lld::elf;
 
 namespace {
 
-class RISCV final : public TargetInfo {
+class RISCV : public TargetInfo {
 public:
   RISCV(Ctx &);
   uint32_t calcEFlags() const override;
@@ -1065,6 +1065,66 @@ void RISCV::finalizeRelax(int passes) const {
   }
 }
 
+namespace {
+
+class RISCVCfiLpUnlabeledPLT final : public RISCV {
+public:
+  RISCVCfiLpUnlabeledPLT(Ctx &ctx);
+  void writePltHeader(uint8_t *buf) const override;
+  void writePlt(uint8_t *buf, const Symbol &sym,
+                uint64_t pltEntryAddr) const override;
+};
+
+} // namespace
+
+RISCVCfiLpUnlabeledPLT::RISCVCfiLpUnlabeledPLT(Ctx &ctx) : RISCV(ctx) {
+  pltHeaderSize = 48;
+}
+
+void RISCVCfiLpUnlabeledPLT::writePltHeader(uint8_t *buf) const {
+  // lpad 0
+  // 1: auipc t2, %pcrel_hi(.got.plt)
+  // sub t1, t1, t3
+  // l[wd] t3, %pcrel_lo(1b)(t2); t3 = _dl_runtime_resolve
+  // addi t1, t1, -pltHeaderSize-16; t1 = &.plt[i] - &.plt[0]
+  // addi t0, t2, %pcrel_lo(1b)
+  // srli t1, t1, (rv64?1:2); t1 = &.got.plt[i] - &.got.plt[0]
+  // l[wd] t0, Wordsize(t0); t0 = link_map
+  // jr t3
+  // nop
+  // nop
+  // nop
+  const uint32_t offset =
+      ctx.in.gotPlt->getVA() - (ctx.in.plt->getVA() + 4 /* offset for lpad */);
+  const uint32_t load = ctx.arg.is64 ? LD : LW;
+  write32le(buf + 0, utype(AUIPC, 0, 0)); // lpad 0
+  write32le(buf + 4, utype(AUIPC, X_T2, hi20(offset)));
+  write32le(buf + 8, rtype(SUB, X_T1, X_T1, X_T3));
+  write32le(buf + 12, itype(load, X_T3, X_T2, lo12(offset)));
+  write32le(buf + 16, itype(ADDI, X_T1, X_T1, -ctx.target->pltHeaderSize - 16));
+  write32le(buf + 20, itype(ADDI, X_T0, X_T2, lo12(offset)));
+  write32le(buf + 24, itype(SRLI, X_T1, X_T1, ctx.arg.is64 ? 1 : 2));
+  write32le(buf + 28, itype(load, X_T0, X_T0, ctx.arg.wordsize));
+  write32le(buf + 32, itype(JALR, 0, X_T3, 0));
+  write32le(buf + 36, itype(ADDI, 0, 0, 0)); // nop
+  write32le(buf + 40, itype(ADDI, 0, 0, 0)); // nop
+  write32le(buf + 44, itype(ADDI, 0, 0, 0)); // nop
+}
+
+void RISCVCfiLpUnlabeledPLT::writePlt(uint8_t *buf, const Symbol &sym,
+                                      uint64_t pltEntryAddr) const {
+  // lpad 0
+  // 1: auipc t3, %pcrel_hi(f at .got.plt)
+  // l[wd] t3, %pcrel_lo(1b)(t3)
+  // jalr t1, t3
+  const uint32_t offset =
+      sym.getGotPltVA(ctx) - (pltEntryAddr + 4 /* offset for lpad */);
+  write32le(buf + 0, utype(AUIPC, 0, 0)); // lpad 0
+  write32le(buf + 4, utype(AUIPC, X_T3, hi20(offset)));
+  write32le(buf + 8, itype(ctx.arg.is64 ? LD : LW, X_T3, X_T3, lo12(offset)));
+  write32le(buf + 12, itype(JALR, X_T1, X_T3, 0));
+}
+
 namespace {
 // Representation of the merged .riscv.attributes input sections. The psABI
 // specifies merge policy for attributes. E.g. if we link an object without an
@@ -1357,4 +1417,12 @@ void elf::mergeRISCVAttributesSections(Ctx &ctx) {
                            mergeAttributesSection(ctx, sections));
 }
 
-void elf::setRISCVTargetInfo(Ctx &ctx) { ctx.target.reset(new RISCV(ctx)); }
+void elf::setRISCVTargetInfo(Ctx &ctx) {
+  RISCV *target;
+  if (ctx.arg.andFeatures & GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED)
+    target = new RISCVCfiLpUnlabeledPLT(ctx);
+  else
+    target = new RISCV(ctx);
+
+  ctx.target.reset(target);
+}
diff --git a/lld/test/ELF/riscv-plt-cfi-lp-unlabeled.s b/lld/test/ELF/riscv-plt-cfi-lp-unlabeled.s
new file mode 100644
index 0000000000000..4a80081ecc258
--- /dev/null
+++ b/lld/test/ELF/riscv-plt-cfi-lp-unlabeled.s
@@ -0,0 +1,135 @@
+# REQUIRES: riscv
+# RUN: rm -rf %t && split-file %s %t && cd %t
+
+# RUN: llvm-mc -filetype=obj -triple=riscv32 rv32-foo.s -o foo32.o
+# RUN: ld.lld -shared foo32.o -soname=libfoo32.so -z zicfilp-unlabeled-report=error --fatal-warnings -o libfoo32.so
+# RUN: llvm-mc -filetype=obj -triple=riscv32 rv32-start.s -o start32.o
+# RUN: ld.lld start32.o libfoo32.so -z zicfilp-unlabeled-report=error --fatal-warnings -o out32
+# RUN: llvm-readelf -S out32 | FileCheck --check-prefix=SEC32 %s
+# RUN: llvm-objdump -d --no-show-raw-insn --mattr=+experimental-zicfilp out32 | FileCheck --check-prefixes=DIS,DIS32 %s
+
+# RUN: llvm-mc -filetype=obj -triple=riscv64 rv64-foo.s -o foo64.o
+# RUN: ld.lld -shared foo64.o -soname=libfoo64.so -z zicfilp-unlabeled-report=error --fatal-warnings -o libfoo64.so
+# RUN: llvm-mc -filetype=obj -triple=riscv64 rv64-start.s -o start64.o
+# RUN: ld.lld start64.o libfoo64.so -z zicfilp-unlabeled-report=error --fatal-warnings -o out64
+# RUN: llvm-readelf -S out64 | FileCheck --check-prefix=SEC64 %s
+# RUN: llvm-objdump -d --no-show-raw-insn --mattr=+experimental-zicfilp out64 | FileCheck --check-prefixes=DIS,DIS64 %s
+
+# SEC32: .plt     PROGBITS {{0*}}00011210
+# SEC32: .got.plt PROGBITS {{0*}}000132b8
+
+# SEC64: .plt     PROGBITS {{0*}}00011330
+# SEC64: .got.plt PROGBITS {{0*}}00013440
+
+# DIS:      Disassembly of section .plt:
+# DIS:      <.plt>:
+# DIS-NEXT:     lpad 0x0
+# DIS-NEXT:     auipc t2, 0x2
+# DIS-NEXT:     sub t1, t1, t3
+# DIS32-NEXT:   lw t3, 0xa4(t2)
+# DIS64-NEXT:   ld t3, 0x10c(t2)
+# DIS-NEXT:     addi t1, t1, -0x40
+# DIS32-NEXT:   addi t0, t2, 0xa4
+# DIS64-NEXT:   addi t0, t2, 0x10c
+# DIS32-NEXT:   srli t1, t1, 0x2
+# DIS64-NEXT:   srli t1, t1, 0x1
+# DIS32-NEXT:   lw t0, 0x4(t0)
+# DIS64-NEXT:   ld t0, 0x8(t0)
+# DIS-NEXT:     jr t3
+# DIS-NEXT:     nop
+# DIS-NEXT:     nop
+# DIS-NEXT:     nop
+
+# DIS:          lpad 0x0
+# DIS-NEXT:     auipc t3, 0x2
+# DIS32-NEXT:   lw t3, 0x7c(t3)
+# DIS64-NEXT:   ld t3, 0xec(t3)
+# DIS-NEXT:     jalr t1, t3
+
+#--- rv32-start.s
+
+.section ".note.gnu.property", "a"
+.balign 4
+.4byte 4
+.4byte (ndesc_end - ndesc_begin)
+.4byte 0x5        // NT_GNU_PROPERTY_TYPE_0
+.asciz "GNU"
+ndesc_begin:
+.balign 4
+.4byte 0xc0000000 // GNU_PROPERTY_RISCV_FEATURE_1_AND
+.4byte 4
+.4byte 1          // GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED
+.balign 4
+ndesc_end:
+
+.text
+.global _start, foo
+
+_start:
+  call foo at plt
+
+#--- rv32-foo.s
+
+.section ".note.gnu.property", "a"
+.balign 4
+.4byte 4
+.4byte (ndesc_end - ndesc_begin)
+.4byte 0x5        // NT_GNU_PROPERTY_TYPE_0
+.asciz "GNU"
+ndesc_begin:
+.balign 4
+.4byte 0xc0000000 // GNU_PROPERTY_RISCV_FEATURE_1_AND
+.4byte 4
+.4byte 1          // GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED
+.balign 4
+ndesc_end:
+
+.text
+.global foo
+.type foo, @function
+foo:
+  ret
+
+#--- rv64-start.s
+
+.section ".note.gnu.property", "a"
+.balign 8
+.4byte 4
+.4byte (ndesc_end - ndesc_begin)
+.4byte 0x5        // NT_GNU_PROPERTY_TYPE_0
+.asciz "GNU"
+ndesc_begin:
+.balign 8
+.4byte 0xc0000000 // GNU_PROPERTY_RISCV_FEATURE_1_AND
+.4byte 4
+.4byte 1          // GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED
+.balign 8
+ndesc_end:
+
+.text
+.global _start, foo
+
+_start:
+  call foo at plt
+
+#--- rv64-foo.s
+
+.section ".note.gnu.property", "a"
+.balign 8
+.4byte 4
+.4byte (ndesc_end - ndesc_begin)
+.4byte 0x5        // NT_GNU_PROPERTY_TYPE_0
+.asciz "GNU"
+ndesc_begin:
+.balign 8
+.4byte 0xc0000000 // GNU_PROPERTY_RISCV_FEATURE_1_AND
+.4byte 4
+.4byte 1          // GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED
+.balign 8
+ndesc_end:
+
+.text
+.global foo
+.type foo, @function
+foo:
+  ret

>From 063757cd81d62f43833974bac31fb982e47c17bf Mon Sep 17 00:00:00 2001
From: Piyou Chen <piyou.chen at sifive.com>
Date: Wed, 3 Jun 2026 02:19:42 -0700
Subject: [PATCH 02/16] Align with latest PLT format

---
 lld/ELF/Arch/RISCV.cpp                    | 54 ++++++++++-------------
 lld/test/ELF/riscv-plt-cfi-lp-unlabeled.s | 26 +++++------
 2 files changed, 34 insertions(+), 46 deletions(-)

diff --git a/lld/ELF/Arch/RISCV.cpp b/lld/ELF/Arch/RISCV.cpp
index 33d3fdcc60ff8..883b3f4f39c1d 100644
--- a/lld/ELF/Arch/RISCV.cpp
+++ b/lld/ELF/Arch/RISCV.cpp
@@ -1082,47 +1082,39 @@ RISCVCfiLpUnlabeledPLT::RISCVCfiLpUnlabeledPLT(Ctx &ctx) : RISCV(ctx) {
 }
 
 void RISCVCfiLpUnlabeledPLT::writePltHeader(uint8_t *buf) const {
-  // lpad 0
-  // 1: auipc t2, %pcrel_hi(.got.plt)
-  // sub t1, t1, t3
-  // l[wd] t3, %pcrel_lo(1b)(t2); t3 = _dl_runtime_resolve
-  // addi t1, t1, -pltHeaderSize-16; t1 = &.plt[i] - &.plt[0]
-  // addi t0, t2, %pcrel_lo(1b)
-  // srli t1, t1, (rv64?1:2); t1 = &.got.plt[i] - &.got.plt[0]
-  // l[wd] t0, Wordsize(t0); t0 = link_map
-  // jr t3
-  // nop
-  // nop
-  // nop
+  // 1:  auipc  t3, %pcrel_hi(.got.plt)
+  //     sub    t1, t1, t2
+  //     l[w|d] t2, %pcrel_lo(1b)(t3)
+  //     addi   t1, t1, -(hdr size + 16)
+  //     addi   t0, t3, %pcrel_lo(1b)
+  //     srli   t1, t1, log2(16/PTRSIZE)
+  //     l[w|d] t0, PTRSIZE(t0)
+  //     jr     t2
   const uint32_t offset =
       ctx.in.gotPlt->getVA() - (ctx.in.plt->getVA() + 4 /* offset for lpad */);
   const uint32_t load = ctx.arg.is64 ? LD : LW;
-  write32le(buf + 0, utype(AUIPC, 0, 0)); // lpad 0
-  write32le(buf + 4, utype(AUIPC, X_T2, hi20(offset)));
-  write32le(buf + 8, rtype(SUB, X_T1, X_T1, X_T3));
-  write32le(buf + 12, itype(load, X_T3, X_T2, lo12(offset)));
-  write32le(buf + 16, itype(ADDI, X_T1, X_T1, -ctx.target->pltHeaderSize - 16));
-  write32le(buf + 20, itype(ADDI, X_T0, X_T2, lo12(offset)));
-  write32le(buf + 24, itype(SRLI, X_T1, X_T1, ctx.arg.is64 ? 1 : 2));
-  write32le(buf + 28, itype(load, X_T0, X_T0, ctx.arg.wordsize));
-  write32le(buf + 32, itype(JALR, 0, X_T3, 0));
-  write32le(buf + 36, itype(ADDI, 0, 0, 0)); // nop
-  write32le(buf + 40, itype(ADDI, 0, 0, 0)); // nop
-  write32le(buf + 44, itype(ADDI, 0, 0, 0)); // nop
+  write32le(buf + 0, utype(AUIPC, X_T3, hi20(offset)));
+  write32le(buf + 4, rtype(SUB, X_T1, X_T1, X_T2));
+  write32le(buf + 8, itype(load, X_T2, X_T3, lo12(offset)));
+  write32le(buf + 12, itype(ADDI, X_T1, X_T1, -ctx.target->pltHeaderSize - 16));
+  write32le(buf + 16, itype(ADDI, X_T0, X_T3, lo12(offset)));
+  write32le(buf + 20, itype(SRLI, X_T1, X_T1, ctx.arg.is64 ? 1 : 2));
+  write32le(buf + 24, itype(load, X_T0, X_T0, ctx.arg.is64 ? 8 : 4));
+  write32le(buf + 28, itype(JALR, 0, X_T2, 0));
 }
 
 void RISCVCfiLpUnlabeledPLT::writePlt(uint8_t *buf, const Symbol &sym,
                                       uint64_t pltEntryAddr) const {
-  // lpad 0
-  // 1: auipc t3, %pcrel_hi(f at .got.plt)
-  // l[wd] t3, %pcrel_lo(1b)(t3)
-  // jalr t1, t3
+  //     lpad 0
+  // 1:  auipc   t2, %pcrel_hi(function at .got.plt)
+  //     l[w|d]  t2, %pcrel_lo(1b)(t2)
+  //     jalr    t1, t2
   const uint32_t offset =
       sym.getGotPltVA(ctx) - (pltEntryAddr + 4 /* offset for lpad */);
   write32le(buf + 0, utype(AUIPC, 0, 0)); // lpad 0
-  write32le(buf + 4, utype(AUIPC, X_T3, hi20(offset)));
-  write32le(buf + 8, itype(ctx.arg.is64 ? LD : LW, X_T3, X_T3, lo12(offset)));
-  write32le(buf + 12, itype(JALR, X_T1, X_T3, 0));
+  write32le(buf + 4, utype(AUIPC, X_T2, hi20(offset)));
+  write32le(buf + 8, itype(ctx.arg.is64 ? LD : LW, X_T2, X_T2, lo12(offset)));
+  write32le(buf + 12, itype(JALR, X_T1, X_T2, 0));
 }
 
 namespace {
diff --git a/lld/test/ELF/riscv-plt-cfi-lp-unlabeled.s b/lld/test/ELF/riscv-plt-cfi-lp-unlabeled.s
index 4a80081ecc258..87317eddf48b0 100644
--- a/lld/test/ELF/riscv-plt-cfi-lp-unlabeled.s
+++ b/lld/test/ELF/riscv-plt-cfi-lp-unlabeled.s
@@ -23,28 +23,24 @@
 
 # DIS:      Disassembly of section .plt:
 # DIS:      <.plt>:
-# DIS-NEXT:     lpad 0x0
-# DIS-NEXT:     auipc t2, 0x2
-# DIS-NEXT:     sub t1, t1, t3
-# DIS32-NEXT:   lw t3, 0xa4(t2)
-# DIS64-NEXT:   ld t3, 0x10c(t2)
+# DIS-NEXT:     auipc t3, 0x2
+# DIS-NEXT:     sub t1, t1, t2
+# DIS32-NEXT:   lw t2, 0xa4(t3)
+# DIS64-NEXT:   ld t2, 0x10c(t3)
 # DIS-NEXT:     addi t1, t1, -0x40
-# DIS32-NEXT:   addi t0, t2, 0xa4
-# DIS64-NEXT:   addi t0, t2, 0x10c
+# DIS32-NEXT:   addi t0, t3, 0xa4
+# DIS64-NEXT:   addi t0, t3, 0x10c
 # DIS32-NEXT:   srli t1, t1, 0x2
 # DIS64-NEXT:   srli t1, t1, 0x1
 # DIS32-NEXT:   lw t0, 0x4(t0)
 # DIS64-NEXT:   ld t0, 0x8(t0)
-# DIS-NEXT:     jr t3
-# DIS-NEXT:     nop
-# DIS-NEXT:     nop
-# DIS-NEXT:     nop
+# DIS-NEXT:     jr t2
 
 # DIS:          lpad 0x0
-# DIS-NEXT:     auipc t3, 0x2
-# DIS32-NEXT:   lw t3, 0x7c(t3)
-# DIS64-NEXT:   ld t3, 0xec(t3)
-# DIS-NEXT:     jalr t1, t3
+# DIS-NEXT:     auipc t2, 0x2
+# DIS32-NEXT:   lw t2, 0x7c(t2)
+# DIS64-NEXT:   ld t2, 0xec(t2)
+# DIS-NEXT:     jalr t1, t2
 
 #--- rv32-start.s
 

>From b0b466e2b2959e750543d0ba07dd6ac21c123424 Mon Sep 17 00:00:00 2001
From: Piyou Chen <piyou.chen at sifive.com>
Date: Wed, 3 Jun 2026 02:39:50 -0700
Subject: [PATCH 03/16] !fixup drop the new class

---
 lld/ELF/Arch/RISCV.cpp                    | 102 +++++++++-------------
 lld/test/ELF/riscv-plt-cfi-lp-unlabeled.s |  14 +--
 2 files changed, 48 insertions(+), 68 deletions(-)

diff --git a/lld/ELF/Arch/RISCV.cpp b/lld/ELF/Arch/RISCV.cpp
index 883b3f4f39c1d..1b1c7cd0e3c31 100644
--- a/lld/ELF/Arch/RISCV.cpp
+++ b/lld/ELF/Arch/RISCV.cpp
@@ -222,6 +222,31 @@ void RISCV::writeIgotPlt(uint8_t *buf, const Symbol &s) const {
 }
 
 void RISCV::writePltHeader(uint8_t *buf) const {
+
+  if (ctx.arg.andFeatures & GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED) {
+    // 1:  auipc  t3, %pcrel_hi(.got.plt)
+    //     sub    t1, t1, t2
+    //     l[w|d] t2, %pcrel_lo(1b)(t3)
+    //     addi   t1, t1, -(hdr size + 16)
+    //     addi   t0, t3, %pcrel_lo(1b)
+    //     srli   t1, t1, log2(16/PTRSIZE)
+    //     l[w|d] t0, PTRSIZE(t0)
+    //     jr     t2
+    const uint32_t offset = ctx.in.gotPlt->getVA() -
+                            (ctx.in.plt->getVA() + 4 /* offset for lpad */);
+    const uint32_t load = ctx.arg.is64 ? LD : LW;
+    write32le(buf + 0, utype(AUIPC, X_T3, hi20(offset)));
+    write32le(buf + 4, rtype(SUB, X_T1, X_T1, X_T2));
+    write32le(buf + 8, itype(load, X_T2, X_T3, lo12(offset)));
+    write32le(buf + 12,
+              itype(ADDI, X_T1, X_T1, -ctx.target->pltHeaderSize - 16));
+    write32le(buf + 16, itype(ADDI, X_T0, X_T3, lo12(offset)));
+    write32le(buf + 20, itype(SRLI, X_T1, X_T1, ctx.arg.is64 ? 1 : 2));
+    write32le(buf + 24, itype(load, X_T0, X_T0, ctx.arg.is64 ? 8 : 4));
+    write32le(buf + 28, itype(JALR, 0, X_T2, 0));
+    return;
+  }
+
   // 1: auipc t2, %pcrel_hi(.got.plt)
   // sub t1, t1, t3
   // l[wd] t3, %pcrel_lo(1b)(t2); t3 = _dl_runtime_resolve
@@ -244,6 +269,21 @@ void RISCV::writePltHeader(uint8_t *buf) const {
 
 void RISCV::writePlt(uint8_t *buf, const Symbol &sym,
                      uint64_t pltEntryAddr) const {
+
+  if (ctx.arg.andFeatures & GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED) {
+    //     lpad 0
+    // 1:  auipc   t2, %pcrel_hi(function at .got.plt)
+    //     l[w|d]  t2, %pcrel_lo(1b)(t2)
+    //     jalr    t1, t2
+    const uint32_t offset =
+        sym.getGotPltVA(ctx) - (pltEntryAddr + 4 /* offset for lpad */);
+    write32le(buf + 0, utype(AUIPC, 0, 0)); // lpad 0
+    write32le(buf + 4, utype(AUIPC, X_T2, hi20(offset)));
+    write32le(buf + 8, itype(ctx.arg.is64 ? LD : LW, X_T2, X_T2, lo12(offset)));
+    write32le(buf + 12, itype(JALR, X_T1, X_T2, 0));
+    return;
+  }
+
   // 1: auipc t3, %pcrel_hi(f at .got.plt)
   // l[wd] t3, %pcrel_lo(1b)(t3)
   // jalr t1, t3
@@ -1065,58 +1105,6 @@ void RISCV::finalizeRelax(int passes) const {
   }
 }
 
-namespace {
-
-class RISCVCfiLpUnlabeledPLT final : public RISCV {
-public:
-  RISCVCfiLpUnlabeledPLT(Ctx &ctx);
-  void writePltHeader(uint8_t *buf) const override;
-  void writePlt(uint8_t *buf, const Symbol &sym,
-                uint64_t pltEntryAddr) const override;
-};
-
-} // namespace
-
-RISCVCfiLpUnlabeledPLT::RISCVCfiLpUnlabeledPLT(Ctx &ctx) : RISCV(ctx) {
-  pltHeaderSize = 48;
-}
-
-void RISCVCfiLpUnlabeledPLT::writePltHeader(uint8_t *buf) const {
-  // 1:  auipc  t3, %pcrel_hi(.got.plt)
-  //     sub    t1, t1, t2
-  //     l[w|d] t2, %pcrel_lo(1b)(t3)
-  //     addi   t1, t1, -(hdr size + 16)
-  //     addi   t0, t3, %pcrel_lo(1b)
-  //     srli   t1, t1, log2(16/PTRSIZE)
-  //     l[w|d] t0, PTRSIZE(t0)
-  //     jr     t2
-  const uint32_t offset =
-      ctx.in.gotPlt->getVA() - (ctx.in.plt->getVA() + 4 /* offset for lpad */);
-  const uint32_t load = ctx.arg.is64 ? LD : LW;
-  write32le(buf + 0, utype(AUIPC, X_T3, hi20(offset)));
-  write32le(buf + 4, rtype(SUB, X_T1, X_T1, X_T2));
-  write32le(buf + 8, itype(load, X_T2, X_T3, lo12(offset)));
-  write32le(buf + 12, itype(ADDI, X_T1, X_T1, -ctx.target->pltHeaderSize - 16));
-  write32le(buf + 16, itype(ADDI, X_T0, X_T3, lo12(offset)));
-  write32le(buf + 20, itype(SRLI, X_T1, X_T1, ctx.arg.is64 ? 1 : 2));
-  write32le(buf + 24, itype(load, X_T0, X_T0, ctx.arg.is64 ? 8 : 4));
-  write32le(buf + 28, itype(JALR, 0, X_T2, 0));
-}
-
-void RISCVCfiLpUnlabeledPLT::writePlt(uint8_t *buf, const Symbol &sym,
-                                      uint64_t pltEntryAddr) const {
-  //     lpad 0
-  // 1:  auipc   t2, %pcrel_hi(function at .got.plt)
-  //     l[w|d]  t2, %pcrel_lo(1b)(t2)
-  //     jalr    t1, t2
-  const uint32_t offset =
-      sym.getGotPltVA(ctx) - (pltEntryAddr + 4 /* offset for lpad */);
-  write32le(buf + 0, utype(AUIPC, 0, 0)); // lpad 0
-  write32le(buf + 4, utype(AUIPC, X_T2, hi20(offset)));
-  write32le(buf + 8, itype(ctx.arg.is64 ? LD : LW, X_T2, X_T2, lo12(offset)));
-  write32le(buf + 12, itype(JALR, X_T1, X_T2, 0));
-}
-
 namespace {
 // Representation of the merged .riscv.attributes input sections. The psABI
 // specifies merge policy for attributes. E.g. if we link an object without an
@@ -1409,12 +1397,4 @@ void elf::mergeRISCVAttributesSections(Ctx &ctx) {
                            mergeAttributesSection(ctx, sections));
 }
 
-void elf::setRISCVTargetInfo(Ctx &ctx) {
-  RISCV *target;
-  if (ctx.arg.andFeatures & GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED)
-    target = new RISCVCfiLpUnlabeledPLT(ctx);
-  else
-    target = new RISCV(ctx);
-
-  ctx.target.reset(target);
-}
+void elf::setRISCVTargetInfo(Ctx &ctx) { ctx.target.reset(new RISCV(ctx)); }
diff --git a/lld/test/ELF/riscv-plt-cfi-lp-unlabeled.s b/lld/test/ELF/riscv-plt-cfi-lp-unlabeled.s
index 87317eddf48b0..347975c81b2a2 100644
--- a/lld/test/ELF/riscv-plt-cfi-lp-unlabeled.s
+++ b/lld/test/ELF/riscv-plt-cfi-lp-unlabeled.s
@@ -16,20 +16,20 @@
 # RUN: llvm-objdump -d --no-show-raw-insn --mattr=+experimental-zicfilp out64 | FileCheck --check-prefixes=DIS,DIS64 %s
 
 # SEC32: .plt     PROGBITS {{0*}}00011210
-# SEC32: .got.plt PROGBITS {{0*}}000132b8
+# SEC32: .got.plt PROGBITS {{0*}}000132a8
 
 # SEC64: .plt     PROGBITS {{0*}}00011330
-# SEC64: .got.plt PROGBITS {{0*}}00013440
+# SEC64: .got.plt PROGBITS {{0*}}00013430
 
 # DIS:      Disassembly of section .plt:
 # DIS:      <.plt>:
 # DIS-NEXT:     auipc t3, 0x2
 # DIS-NEXT:     sub t1, t1, t2
-# DIS32-NEXT:   lw t2, 0xa4(t3)
-# DIS64-NEXT:   ld t2, 0x10c(t3)
-# DIS-NEXT:     addi t1, t1, -0x40
-# DIS32-NEXT:   addi t0, t3, 0xa4
-# DIS64-NEXT:   addi t0, t3, 0x10c
+# DIS32-NEXT:   lw t2, 0x94(t3)
+# DIS64-NEXT:   ld t2, 0xfc(t3)
+# DIS-NEXT:     addi t1, t1, -0x30
+# DIS32-NEXT:   addi t0, t3, 0x94
+# DIS64-NEXT:   addi t0, t3, 0xfc
 # DIS32-NEXT:   srli t1, t1, 0x2
 # DIS64-NEXT:   srli t1, t1, 0x1
 # DIS32-NEXT:   lw t0, 0x4(t0)

>From e80ea64cf51b565d68df33cb32e4a556fac3a82f Mon Sep 17 00:00:00 2001
From: Piyou Chen <piyou.chen at sifive.com>
Date: Wed, 3 Jun 2026 18:46:51 -0700
Subject: [PATCH 04/16] !fixup restore final

---
 lld/ELF/Arch/RISCV.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lld/ELF/Arch/RISCV.cpp b/lld/ELF/Arch/RISCV.cpp
index 1b1c7cd0e3c31..c6443b68b318a 100644
--- a/lld/ELF/Arch/RISCV.cpp
+++ b/lld/ELF/Arch/RISCV.cpp
@@ -27,7 +27,7 @@ using namespace lld::elf;
 
 namespace {
 
-class RISCV : public TargetInfo {
+class RISCV final : public TargetInfo {
 public:
   RISCV(Ctx &);
   uint32_t calcEFlags() const override;

>From 958a7b5290e58a13e9c1c76a8f34423432a1e2f6 Mon Sep 17 00:00:00 2001
From: Piyou Chen <gccbg04538 at gmail.com>
Date: Thu, 4 Jun 2026 11:51:25 +0800
Subject: [PATCH 05/16] Apply suggestion from @kito-cheng

Co-authored-by: Kito Cheng <npickito at gmail.com>
---
 lld/ELF/Arch/RISCV.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/lld/ELF/Arch/RISCV.cpp b/lld/ELF/Arch/RISCV.cpp
index c6443b68b318a..9f5af38a9a46c 100644
--- a/lld/ELF/Arch/RISCV.cpp
+++ b/lld/ELF/Arch/RISCV.cpp
@@ -233,7 +233,8 @@ void RISCV::writePltHeader(uint8_t *buf) const {
     //     l[w|d] t0, PTRSIZE(t0)
     //     jr     t2
     const uint32_t offset = ctx.in.gotPlt->getVA() -
-                            (ctx.in.plt->getVA() + 4 /* offset for lpad */);
+    const uint32_t offset = ctx.in.gotPlt->getVA() -
+                            ctx.in.plt->getVA();
     const uint32_t load = ctx.arg.is64 ? LD : LW;
     write32le(buf + 0, utype(AUIPC, X_T3, hi20(offset)));
     write32le(buf + 4, rtype(SUB, X_T1, X_T1, X_T2));

>From 7fb834a7ce218537349132ed7a85e189c646f174 Mon Sep 17 00:00:00 2001
From: Piyou Chen <piyou.chen at sifive.com>
Date: Thu, 4 Jun 2026 01:21:10 -0700
Subject: [PATCH 06/16] !fixup fix the wrong offset patch

---
 lld/ELF/Arch/RISCV.cpp                    | 4 +---
 lld/test/ELF/riscv-plt-cfi-lp-unlabeled.s | 8 ++++----
 2 files changed, 5 insertions(+), 7 deletions(-)

diff --git a/lld/ELF/Arch/RISCV.cpp b/lld/ELF/Arch/RISCV.cpp
index 9f5af38a9a46c..c4c25c772e4b6 100644
--- a/lld/ELF/Arch/RISCV.cpp
+++ b/lld/ELF/Arch/RISCV.cpp
@@ -232,9 +232,7 @@ void RISCV::writePltHeader(uint8_t *buf) const {
     //     srli   t1, t1, log2(16/PTRSIZE)
     //     l[w|d] t0, PTRSIZE(t0)
     //     jr     t2
-    const uint32_t offset = ctx.in.gotPlt->getVA() -
-    const uint32_t offset = ctx.in.gotPlt->getVA() -
-                            ctx.in.plt->getVA();
+    const uint32_t offset = ctx.in.gotPlt->getVA() - ctx.in.plt->getVA();
     const uint32_t load = ctx.arg.is64 ? LD : LW;
     write32le(buf + 0, utype(AUIPC, X_T3, hi20(offset)));
     write32le(buf + 4, rtype(SUB, X_T1, X_T1, X_T2));
diff --git a/lld/test/ELF/riscv-plt-cfi-lp-unlabeled.s b/lld/test/ELF/riscv-plt-cfi-lp-unlabeled.s
index 347975c81b2a2..cb0574cddf767 100644
--- a/lld/test/ELF/riscv-plt-cfi-lp-unlabeled.s
+++ b/lld/test/ELF/riscv-plt-cfi-lp-unlabeled.s
@@ -25,11 +25,11 @@
 # DIS:      <.plt>:
 # DIS-NEXT:     auipc t3, 0x2
 # DIS-NEXT:     sub t1, t1, t2
-# DIS32-NEXT:   lw t2, 0x94(t3)
-# DIS64-NEXT:   ld t2, 0xfc(t3)
+# DIS32-NEXT:   lw t2, 0x98(t3)
+# DIS64-NEXT:   ld t2, 0x100(t3)
 # DIS-NEXT:     addi t1, t1, -0x30
-# DIS32-NEXT:   addi t0, t3, 0x94
-# DIS64-NEXT:   addi t0, t3, 0xfc
+# DIS32-NEXT:   addi t0, t3, 0x98
+# DIS64-NEXT:   addi t0, t3, 0x100
 # DIS32-NEXT:   srli t1, t1, 0x2
 # DIS64-NEXT:   srli t1, t1, 0x1
 # DIS32-NEXT:   lw t0, 0x4(t0)

>From f73119c538987158f922615dbbbed2e65b42b7c6 Mon Sep 17 00:00:00 2001
From: Piyou Chen <piyou.chen at sifive.com>
Date: Thu, 4 Jun 2026 01:32:35 -0700
Subject: [PATCH 07/16] !fixup drop blank line

---
 lld/ELF/Arch/RISCV.cpp | 2 --
 1 file changed, 2 deletions(-)

diff --git a/lld/ELF/Arch/RISCV.cpp b/lld/ELF/Arch/RISCV.cpp
index c4c25c772e4b6..d74c7cf3404a6 100644
--- a/lld/ELF/Arch/RISCV.cpp
+++ b/lld/ELF/Arch/RISCV.cpp
@@ -222,7 +222,6 @@ void RISCV::writeIgotPlt(uint8_t *buf, const Symbol &s) const {
 }
 
 void RISCV::writePltHeader(uint8_t *buf) const {
-
   if (ctx.arg.andFeatures & GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED) {
     // 1:  auipc  t3, %pcrel_hi(.got.plt)
     //     sub    t1, t1, t2
@@ -268,7 +267,6 @@ void RISCV::writePltHeader(uint8_t *buf) const {
 
 void RISCV::writePlt(uint8_t *buf, const Symbol &sym,
                      uint64_t pltEntryAddr) const {
-
   if (ctx.arg.andFeatures & GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED) {
     //     lpad 0
     // 1:  auipc   t2, %pcrel_hi(function at .got.plt)

>From eb39d1c1ab99d790954cb18c237ebfaf6f8b5656 Mon Sep 17 00:00:00 2001
From: Piyou Chen <piyou.chen at sifive.com>
Date: Thu, 4 Jun 2026 01:33:58 -0700
Subject: [PATCH 08/16] !fixup replace // with #

---
 lld/test/ELF/riscv-plt-cfi-lp-unlabeled.s | 24 +++++++++++------------
 1 file changed, 12 insertions(+), 12 deletions(-)

diff --git a/lld/test/ELF/riscv-plt-cfi-lp-unlabeled.s b/lld/test/ELF/riscv-plt-cfi-lp-unlabeled.s
index cb0574cddf767..b7338ccff54ac 100644
--- a/lld/test/ELF/riscv-plt-cfi-lp-unlabeled.s
+++ b/lld/test/ELF/riscv-plt-cfi-lp-unlabeled.s
@@ -48,13 +48,13 @@
 .balign 4
 .4byte 4
 .4byte (ndesc_end - ndesc_begin)
-.4byte 0x5        // NT_GNU_PROPERTY_TYPE_0
+.4byte 0x5        # NT_GNU_PROPERTY_TYPE_0
 .asciz "GNU"
 ndesc_begin:
 .balign 4
-.4byte 0xc0000000 // GNU_PROPERTY_RISCV_FEATURE_1_AND
+.4byte 0xc0000000 # GNU_PROPERTY_RISCV_FEATURE_1_AND
 .4byte 4
-.4byte 1          // GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED
+.4byte 1          # GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED
 .balign 4
 ndesc_end:
 
@@ -70,13 +70,13 @@ _start:
 .balign 4
 .4byte 4
 .4byte (ndesc_end - ndesc_begin)
-.4byte 0x5        // NT_GNU_PROPERTY_TYPE_0
+.4byte 0x5        # NT_GNU_PROPERTY_TYPE_0
 .asciz "GNU"
 ndesc_begin:
 .balign 4
-.4byte 0xc0000000 // GNU_PROPERTY_RISCV_FEATURE_1_AND
+.4byte 0xc0000000 # GNU_PROPERTY_RISCV_FEATURE_1_AND
 .4byte 4
-.4byte 1          // GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED
+.4byte 1          # GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED
 .balign 4
 ndesc_end:
 
@@ -92,13 +92,13 @@ foo:
 .balign 8
 .4byte 4
 .4byte (ndesc_end - ndesc_begin)
-.4byte 0x5        // NT_GNU_PROPERTY_TYPE_0
+.4byte 0x5        # NT_GNU_PROPERTY_TYPE_0
 .asciz "GNU"
 ndesc_begin:
 .balign 8
-.4byte 0xc0000000 // GNU_PROPERTY_RISCV_FEATURE_1_AND
+.4byte 0xc0000000 # GNU_PROPERTY_RISCV_FEATURE_1_AND
 .4byte 4
-.4byte 1          // GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED
+.4byte 1          # GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED
 .balign 8
 ndesc_end:
 
@@ -114,13 +114,13 @@ _start:
 .balign 8
 .4byte 4
 .4byte (ndesc_end - ndesc_begin)
-.4byte 0x5        // NT_GNU_PROPERTY_TYPE_0
+.4byte 0x5        # NT_GNU_PROPERTY_TYPE_0
 .asciz "GNU"
 ndesc_begin:
 .balign 8
-.4byte 0xc0000000 // GNU_PROPERTY_RISCV_FEATURE_1_AND
+.4byte 0xc0000000 # GNU_PROPERTY_RISCV_FEATURE_1_AND
 .4byte 4
-.4byte 1          // GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED
+.4byte 1          # GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED
 .balign 8
 ndesc_end:
 

>From 790b227b4e58a47063835f855bd0ff205ff895d3 Mon Sep 17 00:00:00 2001
From: Piyou Chen <piyou.chen at sifive.com>
Date: Mon, 22 Jun 2026 01:33:17 -0700
Subject: [PATCH 09/16] !fixup use X_X0 instead of 0

---
 lld/ELF/Arch/RISCV.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/lld/ELF/Arch/RISCV.cpp b/lld/ELF/Arch/RISCV.cpp
index d74c7cf3404a6..da88c448b8eb6 100644
--- a/lld/ELF/Arch/RISCV.cpp
+++ b/lld/ELF/Arch/RISCV.cpp
@@ -241,7 +241,7 @@ void RISCV::writePltHeader(uint8_t *buf) const {
     write32le(buf + 16, itype(ADDI, X_T0, X_T3, lo12(offset)));
     write32le(buf + 20, itype(SRLI, X_T1, X_T1, ctx.arg.is64 ? 1 : 2));
     write32le(buf + 24, itype(load, X_T0, X_T0, ctx.arg.is64 ? 8 : 4));
-    write32le(buf + 28, itype(JALR, 0, X_T2, 0));
+    write32le(buf + 28, itype(JALR, X_X0, X_T2, 0));
     return;
   }
 
@@ -274,7 +274,7 @@ void RISCV::writePlt(uint8_t *buf, const Symbol &sym,
     //     jalr    t1, t2
     const uint32_t offset =
         sym.getGotPltVA(ctx) - (pltEntryAddr + 4 /* offset for lpad */);
-    write32le(buf + 0, utype(AUIPC, 0, 0)); // lpad 0
+    write32le(buf + 0, utype(AUIPC, X_X0, 0)); // lpad 0
     write32le(buf + 4, utype(AUIPC, X_T2, hi20(offset)));
     write32le(buf + 8, itype(ctx.arg.is64 ? LD : LW, X_T2, X_T2, lo12(offset)));
     write32le(buf + 12, itype(JALR, X_T1, X_T2, 0));

>From 839a2de5a7e2fe0a52ea0051b84a64379198f664 Mon Sep 17 00:00:00 2001
From: Piyou Chen <piyou.chen at sifive.com>
Date: Mon, 22 Jun 2026 01:35:56 -0700
Subject: [PATCH 10/16] !fixup drop the blank line after #---

---
 lld/test/ELF/riscv-plt-cfi-lp-unlabeled.s | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/lld/test/ELF/riscv-plt-cfi-lp-unlabeled.s b/lld/test/ELF/riscv-plt-cfi-lp-unlabeled.s
index b7338ccff54ac..dde4e236c4afb 100644
--- a/lld/test/ELF/riscv-plt-cfi-lp-unlabeled.s
+++ b/lld/test/ELF/riscv-plt-cfi-lp-unlabeled.s
@@ -43,7 +43,6 @@
 # DIS-NEXT:     jalr t1, t2
 
 #--- rv32-start.s
-
 .section ".note.gnu.property", "a"
 .balign 4
 .4byte 4
@@ -65,7 +64,6 @@ _start:
   call foo at plt
 
 #--- rv32-foo.s
-
 .section ".note.gnu.property", "a"
 .balign 4
 .4byte 4
@@ -87,7 +85,6 @@ foo:
   ret
 
 #--- rv64-start.s
-
 .section ".note.gnu.property", "a"
 .balign 8
 .4byte 4
@@ -109,7 +106,6 @@ _start:
   call foo at plt
 
 #--- rv64-foo.s
-
 .section ".note.gnu.property", "a"
 .balign 8
 .4byte 4

>From ff660bec36a21ff9241b387073df4e1514459508 Mon Sep 17 00:00:00 2001
From: Piyou Chen <piyou.chen at sifive.com>
Date: Mon, 22 Jun 2026 01:51:59 -0700
Subject: [PATCH 11/16] !fixup integrate with exist test file

---
 .../ELF/riscv-feature-zicfilp-unlabeled.s     | 126 +++++++++++++++++
 lld/test/ELF/riscv-plt-cfi-lp-unlabeled.s     | 127 ------------------
 2 files changed, 126 insertions(+), 127 deletions(-)
 delete mode 100644 lld/test/ELF/riscv-plt-cfi-lp-unlabeled.s

diff --git a/lld/test/ELF/riscv-feature-zicfilp-unlabeled.s b/lld/test/ELF/riscv-feature-zicfilp-unlabeled.s
index 20491f057c8ed..997f6d571ab49 100644
--- a/lld/test/ELF/riscv-feature-zicfilp-unlabeled.s
+++ b/lld/test/ELF/riscv-feature-zicfilp-unlabeled.s
@@ -17,6 +17,21 @@
 # RUN: llvm-mc --filetype=obj --triple=riscv64 f3-s.s -o f3-s.o
 # RUN: llvm-mc --filetype=obj --triple=riscv64 f3-f.s -o f3-f.o
 
+## Test PLT generation with unlabeled landing pads
+# RUN: llvm-mc -filetype=obj -triple=riscv32 rv32-plt-foo.s -o rv32-plt-foo.o
+# RUN: ld.lld -shared rv32-plt-foo.o -soname=libfoo32.so -z zicfilp-unlabeled-report=error --fatal-warnings -o libfoo32.so
+# RUN: llvm-mc -filetype=obj -triple=riscv32 rv32-plt-start.s -o rv32-plt-start.o
+# RUN: ld.lld rv32-plt-start.o libfoo32.so -z zicfilp-unlabeled-report=error --fatal-warnings -o out.plt32
+# RUN: llvm-readelf -S out.plt32 | FileCheck --check-prefix=PLT-SEC32 %s
+# RUN: llvm-objdump -d --no-show-raw-insn --mattr=+experimental-zicfilp out.plt32 | FileCheck --check-prefixes=PLT-DIS,PLT-DIS32 %s
+
+# RUN: llvm-mc -filetype=obj -triple=riscv64 rv64-plt-foo.s -o rv64-plt-foo.o
+# RUN: ld.lld -shared rv64-plt-foo.o -soname=libfoo64.so -z zicfilp-unlabeled-report=error --fatal-warnings -o libfoo64.so
+# RUN: llvm-mc -filetype=obj -triple=riscv64 rv64-plt-start.s -o rv64-plt-start.o
+# RUN: ld.lld rv64-plt-start.o libfoo64.so -z zicfilp-unlabeled-report=error --fatal-warnings -o out.plt64
+# RUN: llvm-readelf -S out.plt64 | FileCheck --check-prefix=PLT-SEC64 %s
+# RUN: llvm-objdump -d --no-show-raw-insn --mattr=+experimental-zicfilp out.plt64 | FileCheck --check-prefixes=PLT-DIS,PLT-DIS64 %s
+
 ## ZICFILP-unlabeled should be enabled when it's enabled in all inputs or when
 ## it's forced on.
 # RUN: ld.lld rv32-f1-s.o rv32-f2-s.o rv32-f3-s.o -o out.rv32 --fatal-warnings
@@ -68,6 +83,33 @@
 # RUN: llvm-readelf -n out.override | FileCheck --check-prefixes=ZICFILP,OVERRIDE %s
 # OVERRIDE-NOT: ZICFILP-func-sig
 
+# PLT-SEC32: .plt     PROGBITS {{0*}}00011210
+# PLT-SEC32: .got.plt PROGBITS {{0*}}000132a8
+
+# PLT-SEC64: .plt     PROGBITS {{0*}}00011330
+# PLT-SEC64: .got.plt PROGBITS {{0*}}00013430
+
+# PLT-DIS:      Disassembly of section .plt:
+# PLT-DIS:      <.plt>:
+# PLT-DIS-NEXT:     auipc t3, 0x2
+# PLT-DIS-NEXT:     sub t1, t1, t2
+# PLT-DIS32-NEXT:   lw t2, 0x98(t3)
+# PLT-DIS64-NEXT:   ld t2, 0x100(t3)
+# PLT-DIS-NEXT:     addi t1, t1, -0x30
+# PLT-DIS32-NEXT:   addi t0, t3, 0x98
+# PLT-DIS64-NEXT:   addi t0, t3, 0x100
+# PLT-DIS32-NEXT:   srli t1, t1, 0x2
+# PLT-DIS64-NEXT:   srli t1, t1, 0x1
+# PLT-DIS32-NEXT:   lw t0, 0x4(t0)
+# PLT-DIS64-NEXT:   ld t0, 0x8(t0)
+# PLT-DIS-NEXT:     jr t2
+
+# PLT-DIS:          lpad 0x0
+# PLT-DIS-NEXT:     auipc t2, 0x2
+# PLT-DIS32-NEXT:   lw t2, 0x7c(t2)
+# PLT-DIS64-NEXT:   ld t2, 0xec(t2)
+# PLT-DIS-NEXT:     jalr t1, t2
+
 #--- rv32-f1-s.s
 .section ".note.gnu.property", "a"
 .balign 4
@@ -261,3 +303,87 @@ ndesc_end:
 .type f3, at function
 f3:
   ret
+
+#--- rv32-plt-start.s
+.section ".note.gnu.property", "a"
+.balign 4
+.4byte 4
+.4byte (ndesc_end - ndesc_begin)
+.4byte 0x5        # NT_GNU_PROPERTY_TYPE_0
+.asciz "GNU"
+ndesc_begin:
+.balign 4
+.4byte 0xc0000000 # GNU_PROPERTY_RISCV_FEATURE_1_AND
+.4byte 4
+.4byte 1          # GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED
+.balign 4
+ndesc_end:
+
+.text
+.global _start, foo
+
+_start:
+  call foo at plt
+
+#--- rv32-plt-foo.s
+.section ".note.gnu.property", "a"
+.balign 4
+.4byte 4
+.4byte (ndesc_end - ndesc_begin)
+.4byte 0x5        # NT_GNU_PROPERTY_TYPE_0
+.asciz "GNU"
+ndesc_begin:
+.balign 4
+.4byte 0xc0000000 # GNU_PROPERTY_RISCV_FEATURE_1_AND
+.4byte 4
+.4byte 1          # GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED
+.balign 4
+ndesc_end:
+
+.text
+.global foo
+.type foo, @function
+foo:
+  ret
+
+#--- rv64-plt-start.s
+.section ".note.gnu.property", "a"
+.balign 8
+.4byte 4
+.4byte (ndesc_end - ndesc_begin)
+.4byte 0x5        # NT_GNU_PROPERTY_TYPE_0
+.asciz "GNU"
+ndesc_begin:
+.balign 8
+.4byte 0xc0000000 # GNU_PROPERTY_RISCV_FEATURE_1_AND
+.4byte 4
+.4byte 1          # GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED
+.balign 8
+ndesc_end:
+
+.text
+.global _start, foo
+
+_start:
+  call foo at plt
+
+#--- rv64-plt-foo.s
+.section ".note.gnu.property", "a"
+.balign 8
+.4byte 4
+.4byte (ndesc_end - ndesc_begin)
+.4byte 0x5        # NT_GNU_PROPERTY_TYPE_0
+.asciz "GNU"
+ndesc_begin:
+.balign 8
+.4byte 0xc0000000 # GNU_PROPERTY_RISCV_FEATURE_1_AND
+.4byte 4
+.4byte 1          # GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED
+.balign 8
+ndesc_end:
+
+.text
+.global foo
+.type foo, @function
+foo:
+  ret
diff --git a/lld/test/ELF/riscv-plt-cfi-lp-unlabeled.s b/lld/test/ELF/riscv-plt-cfi-lp-unlabeled.s
deleted file mode 100644
index dde4e236c4afb..0000000000000
--- a/lld/test/ELF/riscv-plt-cfi-lp-unlabeled.s
+++ /dev/null
@@ -1,127 +0,0 @@
-# REQUIRES: riscv
-# RUN: rm -rf %t && split-file %s %t && cd %t
-
-# RUN: llvm-mc -filetype=obj -triple=riscv32 rv32-foo.s -o foo32.o
-# RUN: ld.lld -shared foo32.o -soname=libfoo32.so -z zicfilp-unlabeled-report=error --fatal-warnings -o libfoo32.so
-# RUN: llvm-mc -filetype=obj -triple=riscv32 rv32-start.s -o start32.o
-# RUN: ld.lld start32.o libfoo32.so -z zicfilp-unlabeled-report=error --fatal-warnings -o out32
-# RUN: llvm-readelf -S out32 | FileCheck --check-prefix=SEC32 %s
-# RUN: llvm-objdump -d --no-show-raw-insn --mattr=+experimental-zicfilp out32 | FileCheck --check-prefixes=DIS,DIS32 %s
-
-# RUN: llvm-mc -filetype=obj -triple=riscv64 rv64-foo.s -o foo64.o
-# RUN: ld.lld -shared foo64.o -soname=libfoo64.so -z zicfilp-unlabeled-report=error --fatal-warnings -o libfoo64.so
-# RUN: llvm-mc -filetype=obj -triple=riscv64 rv64-start.s -o start64.o
-# RUN: ld.lld start64.o libfoo64.so -z zicfilp-unlabeled-report=error --fatal-warnings -o out64
-# RUN: llvm-readelf -S out64 | FileCheck --check-prefix=SEC64 %s
-# RUN: llvm-objdump -d --no-show-raw-insn --mattr=+experimental-zicfilp out64 | FileCheck --check-prefixes=DIS,DIS64 %s
-
-# SEC32: .plt     PROGBITS {{0*}}00011210
-# SEC32: .got.plt PROGBITS {{0*}}000132a8
-
-# SEC64: .plt     PROGBITS {{0*}}00011330
-# SEC64: .got.plt PROGBITS {{0*}}00013430
-
-# DIS:      Disassembly of section .plt:
-# DIS:      <.plt>:
-# DIS-NEXT:     auipc t3, 0x2
-# DIS-NEXT:     sub t1, t1, t2
-# DIS32-NEXT:   lw t2, 0x98(t3)
-# DIS64-NEXT:   ld t2, 0x100(t3)
-# DIS-NEXT:     addi t1, t1, -0x30
-# DIS32-NEXT:   addi t0, t3, 0x98
-# DIS64-NEXT:   addi t0, t3, 0x100
-# DIS32-NEXT:   srli t1, t1, 0x2
-# DIS64-NEXT:   srli t1, t1, 0x1
-# DIS32-NEXT:   lw t0, 0x4(t0)
-# DIS64-NEXT:   ld t0, 0x8(t0)
-# DIS-NEXT:     jr t2
-
-# DIS:          lpad 0x0
-# DIS-NEXT:     auipc t2, 0x2
-# DIS32-NEXT:   lw t2, 0x7c(t2)
-# DIS64-NEXT:   ld t2, 0xec(t2)
-# DIS-NEXT:     jalr t1, t2
-
-#--- rv32-start.s
-.section ".note.gnu.property", "a"
-.balign 4
-.4byte 4
-.4byte (ndesc_end - ndesc_begin)
-.4byte 0x5        # NT_GNU_PROPERTY_TYPE_0
-.asciz "GNU"
-ndesc_begin:
-.balign 4
-.4byte 0xc0000000 # GNU_PROPERTY_RISCV_FEATURE_1_AND
-.4byte 4
-.4byte 1          # GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED
-.balign 4
-ndesc_end:
-
-.text
-.global _start, foo
-
-_start:
-  call foo at plt
-
-#--- rv32-foo.s
-.section ".note.gnu.property", "a"
-.balign 4
-.4byte 4
-.4byte (ndesc_end - ndesc_begin)
-.4byte 0x5        # NT_GNU_PROPERTY_TYPE_0
-.asciz "GNU"
-ndesc_begin:
-.balign 4
-.4byte 0xc0000000 # GNU_PROPERTY_RISCV_FEATURE_1_AND
-.4byte 4
-.4byte 1          # GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED
-.balign 4
-ndesc_end:
-
-.text
-.global foo
-.type foo, @function
-foo:
-  ret
-
-#--- rv64-start.s
-.section ".note.gnu.property", "a"
-.balign 8
-.4byte 4
-.4byte (ndesc_end - ndesc_begin)
-.4byte 0x5        # NT_GNU_PROPERTY_TYPE_0
-.asciz "GNU"
-ndesc_begin:
-.balign 8
-.4byte 0xc0000000 # GNU_PROPERTY_RISCV_FEATURE_1_AND
-.4byte 4
-.4byte 1          # GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED
-.balign 8
-ndesc_end:
-
-.text
-.global _start, foo
-
-_start:
-  call foo at plt
-
-#--- rv64-foo.s
-.section ".note.gnu.property", "a"
-.balign 8
-.4byte 4
-.4byte (ndesc_end - ndesc_begin)
-.4byte 0x5        # NT_GNU_PROPERTY_TYPE_0
-.asciz "GNU"
-ndesc_begin:
-.balign 8
-.4byte 0xc0000000 # GNU_PROPERTY_RISCV_FEATURE_1_AND
-.4byte 4
-.4byte 1          # GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED
-.balign 8
-ndesc_end:
-
-.text
-.global foo
-.type foo, @function
-foo:
-  ret

>From a3855890b09417addbf27366ebd8a2fec6595b30 Mon Sep 17 00:00:00 2001
From: Piyou Chen <piyou.chen at sifive.com>
Date: Mon, 22 Jun 2026 23:46:50 -0700
Subject: [PATCH 12/16] Apply MaskRay's patch

from https://github.com/MaskRay/llvm-project/tree/review/145461
---
 .../ELF/riscv-feature-zicfilp-unlabeled.s     | 126 +++---------------
 1 file changed, 16 insertions(+), 110 deletions(-)

diff --git a/lld/test/ELF/riscv-feature-zicfilp-unlabeled.s b/lld/test/ELF/riscv-feature-zicfilp-unlabeled.s
index 997f6d571ab49..1ed28c1c26a3b 100644
--- a/lld/test/ELF/riscv-feature-zicfilp-unlabeled.s
+++ b/lld/test/ELF/riscv-feature-zicfilp-unlabeled.s
@@ -17,21 +17,6 @@
 # RUN: llvm-mc --filetype=obj --triple=riscv64 f3-s.s -o f3-s.o
 # RUN: llvm-mc --filetype=obj --triple=riscv64 f3-f.s -o f3-f.o
 
-## Test PLT generation with unlabeled landing pads
-# RUN: llvm-mc -filetype=obj -triple=riscv32 rv32-plt-foo.s -o rv32-plt-foo.o
-# RUN: ld.lld -shared rv32-plt-foo.o -soname=libfoo32.so -z zicfilp-unlabeled-report=error --fatal-warnings -o libfoo32.so
-# RUN: llvm-mc -filetype=obj -triple=riscv32 rv32-plt-start.s -o rv32-plt-start.o
-# RUN: ld.lld rv32-plt-start.o libfoo32.so -z zicfilp-unlabeled-report=error --fatal-warnings -o out.plt32
-# RUN: llvm-readelf -S out.plt32 | FileCheck --check-prefix=PLT-SEC32 %s
-# RUN: llvm-objdump -d --no-show-raw-insn --mattr=+experimental-zicfilp out.plt32 | FileCheck --check-prefixes=PLT-DIS,PLT-DIS32 %s
-
-# RUN: llvm-mc -filetype=obj -triple=riscv64 rv64-plt-foo.s -o rv64-plt-foo.o
-# RUN: ld.lld -shared rv64-plt-foo.o -soname=libfoo64.so -z zicfilp-unlabeled-report=error --fatal-warnings -o libfoo64.so
-# RUN: llvm-mc -filetype=obj -triple=riscv64 rv64-plt-start.s -o rv64-plt-start.o
-# RUN: ld.lld rv64-plt-start.o libfoo64.so -z zicfilp-unlabeled-report=error --fatal-warnings -o out.plt64
-# RUN: llvm-readelf -S out.plt64 | FileCheck --check-prefix=PLT-SEC64 %s
-# RUN: llvm-objdump -d --no-show-raw-insn --mattr=+experimental-zicfilp out.plt64 | FileCheck --check-prefixes=PLT-DIS,PLT-DIS64 %s
-
 ## ZICFILP-unlabeled should be enabled when it's enabled in all inputs or when
 ## it's forced on.
 # RUN: ld.lld rv32-f1-s.o rv32-f2-s.o rv32-f3-s.o -o out.rv32 --fatal-warnings
@@ -40,6 +25,12 @@
 # RUN: llvm-readelf -n out | FileCheck --check-prefix=ZICFILP %s
 # RUN: ld.lld f1-s.o f3-s.o --shared -o out.so --fatal-warnings
 # RUN: llvm-readelf -n out.so | FileCheck --check-prefix=ZICFILP %s
+
+## Check the unlabeled landing pad-style PLT. f1's `call f2` references the
+## undefined f2, which becomes a preemptible PLT entry in a shared object.
+# RUN: ld.lld rv32-f1-s.o --shared -o out.rv32.so --fatal-warnings
+# RUN: llvm-objdump -h -d --no-show-raw-insn --mattr=+experimental-zicfilp out.rv32.so | FileCheck --check-prefixes=PLT-DIS,PLT-DIS32 %s
+# RUN: llvm-objdump -h -d --no-show-raw-insn --mattr=+experimental-zicfilp out.so | FileCheck --check-prefixes=PLT-DIS,PLT-DIS64 %s
 # RUN: ld.lld f1-s.o f2.o f3-s.o -o out.force -z zicfilp=unlabeled --fatal-warnings
 # RUN: llvm-readelf -n out.force | FileCheck --check-prefix=ZICFILP %s
 # RUN: ld.lld f2-s.o f3.o --shared -o out.force.so -z zicfilp=never -z zicfilp=unlabeled --fatal-warnings
@@ -83,21 +74,20 @@
 # RUN: llvm-readelf -n out.override | FileCheck --check-prefixes=ZICFILP,OVERRIDE %s
 # OVERRIDE-NOT: ZICFILP-func-sig
 
-# PLT-SEC32: .plt     PROGBITS {{0*}}00011210
-# PLT-SEC32: .got.plt PROGBITS {{0*}}000132a8
-
-# PLT-SEC64: .plt     PROGBITS {{0*}}00011330
-# PLT-SEC64: .got.plt PROGBITS {{0*}}00013430
+# PLT-DIS32:    .plt     00000030         00001220 TEXT
+# PLT-DIS32:    .got.plt 0000000c         000032a8 DATA
+# PLT-DIS64:    .plt     00000030 0000000000001370 TEXT
+# PLT-DIS64:    .got.plt 00000018 0000000000003450 DATA
 
 # PLT-DIS:      Disassembly of section .plt:
 # PLT-DIS:      <.plt>:
 # PLT-DIS-NEXT:     auipc t3, 0x2
 # PLT-DIS-NEXT:     sub t1, t1, t2
-# PLT-DIS32-NEXT:   lw t2, 0x98(t3)
-# PLT-DIS64-NEXT:   ld t2, 0x100(t3)
+# PLT-DIS32-NEXT:   lw t2, 0x88(t3)
+# PLT-DIS64-NEXT:   ld t2, 0xe0(t3)
 # PLT-DIS-NEXT:     addi t1, t1, -0x30
-# PLT-DIS32-NEXT:   addi t0, t3, 0x98
-# PLT-DIS64-NEXT:   addi t0, t3, 0x100
+# PLT-DIS32-NEXT:   addi t0, t3, 0x88
+# PLT-DIS64-NEXT:   addi t0, t3, 0xe0
 # PLT-DIS32-NEXT:   srli t1, t1, 0x2
 # PLT-DIS64-NEXT:   srli t1, t1, 0x1
 # PLT-DIS32-NEXT:   lw t0, 0x4(t0)
@@ -106,8 +96,8 @@
 
 # PLT-DIS:          lpad 0x0
 # PLT-DIS-NEXT:     auipc t2, 0x2
-# PLT-DIS32-NEXT:   lw t2, 0x7c(t2)
-# PLT-DIS64-NEXT:   ld t2, 0xec(t2)
+# PLT-DIS32-NEXT:   lw t2, 0x6c(t2)
+# PLT-DIS64-NEXT:   ld t2, 0xcc(t2)
 # PLT-DIS-NEXT:     jalr t1, t2
 
 #--- rv32-f1-s.s
@@ -303,87 +293,3 @@ ndesc_end:
 .type f3, at function
 f3:
   ret
-
-#--- rv32-plt-start.s
-.section ".note.gnu.property", "a"
-.balign 4
-.4byte 4
-.4byte (ndesc_end - ndesc_begin)
-.4byte 0x5        # NT_GNU_PROPERTY_TYPE_0
-.asciz "GNU"
-ndesc_begin:
-.balign 4
-.4byte 0xc0000000 # GNU_PROPERTY_RISCV_FEATURE_1_AND
-.4byte 4
-.4byte 1          # GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED
-.balign 4
-ndesc_end:
-
-.text
-.global _start, foo
-
-_start:
-  call foo at plt
-
-#--- rv32-plt-foo.s
-.section ".note.gnu.property", "a"
-.balign 4
-.4byte 4
-.4byte (ndesc_end - ndesc_begin)
-.4byte 0x5        # NT_GNU_PROPERTY_TYPE_0
-.asciz "GNU"
-ndesc_begin:
-.balign 4
-.4byte 0xc0000000 # GNU_PROPERTY_RISCV_FEATURE_1_AND
-.4byte 4
-.4byte 1          # GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED
-.balign 4
-ndesc_end:
-
-.text
-.global foo
-.type foo, @function
-foo:
-  ret
-
-#--- rv64-plt-start.s
-.section ".note.gnu.property", "a"
-.balign 8
-.4byte 4
-.4byte (ndesc_end - ndesc_begin)
-.4byte 0x5        # NT_GNU_PROPERTY_TYPE_0
-.asciz "GNU"
-ndesc_begin:
-.balign 8
-.4byte 0xc0000000 # GNU_PROPERTY_RISCV_FEATURE_1_AND
-.4byte 4
-.4byte 1          # GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED
-.balign 8
-ndesc_end:
-
-.text
-.global _start, foo
-
-_start:
-  call foo at plt
-
-#--- rv64-plt-foo.s
-.section ".note.gnu.property", "a"
-.balign 8
-.4byte 4
-.4byte (ndesc_end - ndesc_begin)
-.4byte 0x5        # NT_GNU_PROPERTY_TYPE_0
-.asciz "GNU"
-ndesc_begin:
-.balign 8
-.4byte 0xc0000000 # GNU_PROPERTY_RISCV_FEATURE_1_AND
-.4byte 4
-.4byte 1          # GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED
-.balign 8
-ndesc_end:
-
-.text
-.global foo
-.type foo, @function
-foo:
-  ret

>From cb02e55228ec86fe72022fb27bfa7b0a340e7971 Mon Sep 17 00:00:00 2001
From: Piyou Chen <piyou.chen at sifive.com>
Date: Wed, 1 Jul 2026 23:00:43 -0700
Subject: [PATCH 13/16] !fixup Use ctx.arg.wordsize

---
 lld/ELF/Arch/RISCV.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lld/ELF/Arch/RISCV.cpp b/lld/ELF/Arch/RISCV.cpp
index da88c448b8eb6..5c7e3a8bbb117 100644
--- a/lld/ELF/Arch/RISCV.cpp
+++ b/lld/ELF/Arch/RISCV.cpp
@@ -240,7 +240,7 @@ void RISCV::writePltHeader(uint8_t *buf) const {
               itype(ADDI, X_T1, X_T1, -ctx.target->pltHeaderSize - 16));
     write32le(buf + 16, itype(ADDI, X_T0, X_T3, lo12(offset)));
     write32le(buf + 20, itype(SRLI, X_T1, X_T1, ctx.arg.is64 ? 1 : 2));
-    write32le(buf + 24, itype(load, X_T0, X_T0, ctx.arg.is64 ? 8 : 4));
+    write32le(buf + 24, itype(load, X_T0, X_T0, ctx.arg.wordsize));
     write32le(buf + 28, itype(JALR, X_X0, X_T2, 0));
     return;
   }

>From 9e3dfb1d173de44c01b88e94be4416f90791e448 Mon Sep 17 00:00:00 2001
From: Piyou Chen <piyou.chen at sifive.com>
Date: Wed, 1 Jul 2026 23:38:50 -0700
Subject: [PATCH 14/16] !fixup merge the non-lpad and lpad in writePltHeader
 and writePlt

---
 lld/ELF/Arch/RISCV.cpp | 95 +++++++++++++++++++++---------------------
 1 file changed, 47 insertions(+), 48 deletions(-)

diff --git a/lld/ELF/Arch/RISCV.cpp b/lld/ELF/Arch/RISCV.cpp
index 5c7e3a8bbb117..adede5a8fe8f3 100644
--- a/lld/ELF/Arch/RISCV.cpp
+++ b/lld/ELF/Arch/RISCV.cpp
@@ -222,29 +222,19 @@ void RISCV::writeIgotPlt(uint8_t *buf, const Symbol &s) const {
 }
 
 void RISCV::writePltHeader(uint8_t *buf) const {
-  if (ctx.arg.andFeatures & GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED) {
-    // 1:  auipc  t3, %pcrel_hi(.got.plt)
-    //     sub    t1, t1, t2
-    //     l[w|d] t2, %pcrel_lo(1b)(t3)
-    //     addi   t1, t1, -(hdr size + 16)
-    //     addi   t0, t3, %pcrel_lo(1b)
-    //     srli   t1, t1, log2(16/PTRSIZE)
-    //     l[w|d] t0, PTRSIZE(t0)
-    //     jr     t2
-    const uint32_t offset = ctx.in.gotPlt->getVA() - ctx.in.plt->getVA();
-    const uint32_t load = ctx.arg.is64 ? LD : LW;
-    write32le(buf + 0, utype(AUIPC, X_T3, hi20(offset)));
-    write32le(buf + 4, rtype(SUB, X_T1, X_T1, X_T2));
-    write32le(buf + 8, itype(load, X_T2, X_T3, lo12(offset)));
-    write32le(buf + 12,
-              itype(ADDI, X_T1, X_T1, -ctx.target->pltHeaderSize - 16));
-    write32le(buf + 16, itype(ADDI, X_T0, X_T3, lo12(offset)));
-    write32le(buf + 20, itype(SRLI, X_T1, X_T1, ctx.arg.is64 ? 1 : 2));
-    write32le(buf + 24, itype(load, X_T0, X_T0, ctx.arg.wordsize));
-    write32le(buf + 28, itype(JALR, X_X0, X_T2, 0));
-    return;
-  }
-
+  // If using lpad (CFI):
+  //
+  // 1:  auipc  t3, %pcrel_hi(.got.plt)
+  //     sub    t1, t1, t2
+  //     l[w|d] t2, %pcrel_lo(1b)(t3)
+  //     addi   t1, t1, -(hdr size + 16)
+  //     addi   t0, t3, %pcrel_lo(1b)
+  //     srli   t1, t1, log2(16/PTRSIZE)
+  //     l[w|d] t0, PTRSIZE(t0)
+  //     jr     t2
+  //
+  // If not using lpad:
+  //
   // 1: auipc t2, %pcrel_hi(.got.plt)
   // sub t1, t1, t3
   // l[wd] t3, %pcrel_lo(1b)(t2); t3 = _dl_runtime_resolve
@@ -253,43 +243,52 @@ void RISCV::writePltHeader(uint8_t *buf) const {
   // srli t1, t1, (rv64?1:2); t1 = &.got.plt[i] - &.got.plt[0]
   // l[wd] t0, Wordsize(t0); t0 = link_map
   // jr t3
+  bool lpad =
+      ctx.arg.andFeatures & GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED;
   uint32_t offset = ctx.in.gotPlt->getVA() - ctx.in.plt->getVA();
   uint32_t load = ctx.arg.is64 ? LD : LW;
-  write32le(buf + 0, utype(AUIPC, X_T2, hi20(offset)));
-  write32le(buf + 4, rtype(SUB, X_T1, X_T1, X_T3));
-  write32le(buf + 8, itype(load, X_T3, X_T2, lo12(offset)));
-  write32le(buf + 12, itype(ADDI, X_T1, X_T1, -ctx.target->pltHeaderSize - 12));
-  write32le(buf + 16, itype(ADDI, X_T0, X_T2, lo12(offset)));
+  uint32_t auipcReg = lpad ? X_T3 : X_T2;
+  uint32_t workReg = lpad ? X_T2 : X_T3;
+
+  write32le(buf + 0, utype(AUIPC, auipcReg, hi20(offset)));
+  write32le(buf + 4, rtype(SUB, X_T1, X_T1, workReg));
+  write32le(buf + 8, itype(load, workReg, auipcReg, lo12(offset)));
+  write32le(buf + 12, itype(ADDI, X_T1, X_T1,
+                            -ctx.target->pltHeaderSize - (lpad ? 16 : 12)));
+  write32le(buf + 16, itype(ADDI, X_T0, auipcReg, lo12(offset)));
   write32le(buf + 20, itype(SRLI, X_T1, X_T1, ctx.arg.is64 ? 1 : 2));
   write32le(buf + 24, itype(load, X_T0, X_T0, ctx.arg.wordsize));
-  write32le(buf + 28, itype(JALR, 0, X_T3, 0));
+  write32le(buf + 28, itype(JALR, lpad ? X_X0 : 0, workReg, 0));
 }
 
 void RISCV::writePlt(uint8_t *buf, const Symbol &sym,
                      uint64_t pltEntryAddr) const {
-  if (ctx.arg.andFeatures & GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED) {
-    //     lpad 0
-    // 1:  auipc   t2, %pcrel_hi(function at .got.plt)
-    //     l[w|d]  t2, %pcrel_lo(1b)(t2)
-    //     jalr    t1, t2
-    const uint32_t offset =
-        sym.getGotPltVA(ctx) - (pltEntryAddr + 4 /* offset for lpad */);
-    write32le(buf + 0, utype(AUIPC, X_X0, 0)); // lpad 0
-    write32le(buf + 4, utype(AUIPC, X_T2, hi20(offset)));
-    write32le(buf + 8, itype(ctx.arg.is64 ? LD : LW, X_T2, X_T2, lo12(offset)));
-    write32le(buf + 12, itype(JALR, X_T1, X_T2, 0));
-    return;
-  }
-
+  // If using lpad:
+  //
+  //     lpad 0
+  // 1:  auipc   t2, %pcrel_hi(function at .got.plt)
+  //     l[w|d]  t2, %pcrel_lo(1b)(t2)
+  //     jalr    t1, t2
+  //
+  // If not using lpad:
+  //
   // 1: auipc t3, %pcrel_hi(f at .got.plt)
   // l[wd] t3, %pcrel_lo(1b)(t3)
   // jalr t1, t3
   // nop
-  uint32_t offset = sym.getGotPltVA(ctx) - pltEntryAddr;
-  write32le(buf + 0, utype(AUIPC, X_T3, hi20(offset)));
-  write32le(buf + 4, itype(ctx.arg.is64 ? LD : LW, X_T3, X_T3, lo12(offset)));
-  write32le(buf + 8, itype(JALR, X_T1, X_T3, 0));
-  write32le(buf + 12, itype(ADDI, 0, 0, 0));
+  bool lpad =
+      ctx.arg.andFeatures & GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED;
+  uint32_t auipcOffset = lpad * 4;
+  uint32_t offset = sym.getGotPltVA(ctx) - pltEntryAddr - auipcOffset;
+  uint32_t rd = lpad ? X_T2 : X_T3;
+  if (lpad)
+    write32le(buf + 0, utype(AUIPC, X_X0, 0)); // lpad 0
+  write32le(buf + 0 + auipcOffset, utype(AUIPC, rd, hi20(offset)));
+  write32le(buf + 4 + auipcOffset,
+            itype(ctx.arg.is64 ? LD : LW, rd, rd, lo12(offset)));
+  write32le(buf + 8 + auipcOffset, itype(JALR, X_T1, rd, 0));
+  if (!lpad)
+    write32le(buf + 12, itype(ADDI, 0, 0, 0));
 }
 
 RelType RISCV::getDynRel(RelType type) const {

>From f8bfefe1b314a349a007dba311012fe63ff692ae Mon Sep 17 00:00:00 2001
From: Piyou Chen <piyou.chen at sifive.com>
Date: Wed, 1 Jul 2026 23:39:25 -0700
Subject: [PATCH 15/16] !fixup just use X_X0

---
 lld/ELF/Arch/RISCV.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/lld/ELF/Arch/RISCV.cpp b/lld/ELF/Arch/RISCV.cpp
index adede5a8fe8f3..7703a365b97cf 100644
--- a/lld/ELF/Arch/RISCV.cpp
+++ b/lld/ELF/Arch/RISCV.cpp
@@ -258,7 +258,7 @@ void RISCV::writePltHeader(uint8_t *buf) const {
   write32le(buf + 16, itype(ADDI, X_T0, auipcReg, lo12(offset)));
   write32le(buf + 20, itype(SRLI, X_T1, X_T1, ctx.arg.is64 ? 1 : 2));
   write32le(buf + 24, itype(load, X_T0, X_T0, ctx.arg.wordsize));
-  write32le(buf + 28, itype(JALR, lpad ? X_X0 : 0, workReg, 0));
+  write32le(buf + 28, itype(JALR, X_X0, workReg, 0));
 }
 
 void RISCV::writePlt(uint8_t *buf, const Symbol &sym,
@@ -288,7 +288,7 @@ void RISCV::writePlt(uint8_t *buf, const Symbol &sym,
             itype(ctx.arg.is64 ? LD : LW, rd, rd, lo12(offset)));
   write32le(buf + 8 + auipcOffset, itype(JALR, X_T1, rd, 0));
   if (!lpad)
-    write32le(buf + 12, itype(ADDI, 0, 0, 0));
+    write32le(buf + 12, itype(ADDI, X_X0, X_X0, 0));
 }
 
 RelType RISCV::getDynRel(RelType type) const {

>From 15b42f285cc3a5941d3fce1befcc6b441a70b5f3 Mon Sep 17 00:00:00 2001
From: Piyou Chen <piyou.chen at sifive.com>
Date: Wed, 1 Jul 2026 23:40:57 -0700
Subject: [PATCH 16/16] !fixup sync the comment format

---
 lld/ELF/Arch/RISCV.cpp | 24 ++++++++++++------------
 1 file changed, 12 insertions(+), 12 deletions(-)

diff --git a/lld/ELF/Arch/RISCV.cpp b/lld/ELF/Arch/RISCV.cpp
index 7703a365b97cf..9c75e3a794ad2 100644
--- a/lld/ELF/Arch/RISCV.cpp
+++ b/lld/ELF/Arch/RISCV.cpp
@@ -235,14 +235,14 @@ void RISCV::writePltHeader(uint8_t *buf) const {
   //
   // If not using lpad:
   //
-  // 1: auipc t2, %pcrel_hi(.got.plt)
-  // sub t1, t1, t3
-  // l[wd] t3, %pcrel_lo(1b)(t2); t3 = _dl_runtime_resolve
-  // addi t1, t1, -pltHeaderSize-12; t1 = &.plt[i] - &.plt[0]
-  // addi t0, t2, %pcrel_lo(1b)
-  // srli t1, t1, (rv64?1:2); t1 = &.got.plt[i] - &.got.plt[0]
-  // l[wd] t0, Wordsize(t0); t0 = link_map
-  // jr t3
+  // 1:  auipc  t2, %pcrel_hi(.got.plt)
+  //     sub    t1, t1, t3
+  //     l[w|d] t3, %pcrel_lo(1b)(t2)     ; t3 = _dl_runtime_resolve
+  //     addi   t1, t1, -pltHeaderSize-12 ; t1 = &.plt[i] - &.plt[0]
+  //     addi   t0, t2, %pcrel_lo(1b)
+  //     srli   t1, t1, (rv64?1:2)        ; t1 = &.got.plt[i] - &.got.plt[0]
+  //     l[w|d] t0, Wordsize(t0)          ; t0 = link_map
+  //     jr     t3
   bool lpad =
       ctx.arg.andFeatures & GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED;
   uint32_t offset = ctx.in.gotPlt->getVA() - ctx.in.plt->getVA();
@@ -272,10 +272,10 @@ void RISCV::writePlt(uint8_t *buf, const Symbol &sym,
   //
   // If not using lpad:
   //
-  // 1: auipc t3, %pcrel_hi(f at .got.plt)
-  // l[wd] t3, %pcrel_lo(1b)(t3)
-  // jalr t1, t3
-  // nop
+  // 1:  auipc   t3, %pcrel_hi(f at .got.plt)
+  //     l[w|d]  t3, %pcrel_lo(1b)(t3)
+  //     jalr    t1, t3
+  //     nop
   bool lpad =
       ctx.arg.andFeatures & GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED;
   uint32_t auipcOffset = lpad * 4;



More information about the llvm-commits mailing list