[lld] 7f8640b - [lld] Add -z mark-plt support for X86_64 (#206002)

via llvm-commits llvm-commits at lists.llvm.org
Tue Sep 22 20:48:57 PDT 2026


Author: Phoebe Wang
Date: 2026-09-23T11:48:50+08:00
New Revision: 7f8640b7bdab5a79ffc02e1a039c09217459f1f9

URL: https://github.com/llvm/llvm-project/commit/7f8640b7bdab5a79ffc02e1a039c09217459f1f9
DIFF: https://github.com/llvm/llvm-project/commit/7f8640b7bdab5a79ffc02e1a039c09217459f1f9.diff

LOG: [lld] Add -z mark-plt support for X86_64 (#206002)

Add option [no]mark-plt to enable it. Dynamic linker can change PLT
entries with JMPABS instruction on supported targets.

Ref.:
https://maskray.me/blog/2021-09-19-all-about-procedure-linkage-table#x86-plt-rewriting

Assisted-by: Claude Sonnet 4.6

---------

Co-authored-by: Claude Opus 4.8 <noreply at anthropic.com>

Added: 
    

Modified: 
    lld/ELF/Config.h
    lld/ELF/Driver.cpp
    lld/ELF/OutputSections.cpp
    lld/ELF/Relocations.cpp
    lld/ELF/SyntheticSections.cpp
    lld/test/ELF/x86-64-plt.s

Removed: 
    


################################################################################
diff  --git a/lld/ELF/Config.h b/lld/ELF/Config.h
index 0d091b42a9862..a43d5d509a80c 100644
--- a/lld/ELF/Config.h
+++ b/lld/ELF/Config.h
@@ -441,6 +441,7 @@ struct Config {
   bool zKeepDataSectionPrefix;
   bool zKeepTextSectionPrefix;
   bool zLrodataAfterBss;
+  bool zMarkPlt;
   bool zNoBtCfi;
   bool zNodefaultlib;
   bool zNodelete;

diff  --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp
index 63e758d33ed1e..4e5a5eb8664d7 100644
--- a/lld/ELF/Driver.cpp
+++ b/lld/ELF/Driver.cpp
@@ -405,6 +405,17 @@ static void checkOptions(Ctx &ctx) {
       ctx.arg.zCetReport != ReportPolicy::None)
     ErrAlways(ctx) << "-z cet-report only supported on X86 and X86_64";
 
+  if (ctx.arg.zMarkPlt) {
+    if (ctx.arg.emachine != EM_X86_64)
+      ErrAlways(ctx) << "-z mark-plt only supported on X86_64";
+    // The PLT entry address is stored in the JUMP_SLOT relocation's addend, so
+    // -z mark-plt requires RELA relocations. REL relocations have no addend
+    // field and the .got.plt entry is already occupied by the lazy-binding
+    // address, so the information would be silently lost.
+    else if (!ctx.arg.isRela)
+      ErrAlways(ctx) << "-z mark-plt requires -z rela";
+  }
+
   if (ctx.arg.pie && ctx.arg.shared)
     ErrAlways(ctx) << "-shared and -pie may not be used together";
 
@@ -1671,6 +1682,7 @@ static void readConfigs(Ctx &ctx, opt::InputArgList &args) {
       args, "keep-text-section-prefix", "nokeep-text-section-prefix", false);
   ctx.arg.zLrodataAfterBss =
       getZFlag(args, "lrodata-after-bss", "nolrodata-after-bss", false);
+  ctx.arg.zMarkPlt = getZFlag(args, "mark-plt", "nomark-plt", false);
   ctx.arg.zNoBtCfi = hasZOption(args, "nobtcfi");
   ctx.arg.zNodefaultlib = hasZOption(args, "nodefaultlib");
   ctx.arg.zNodelete = hasZOption(args, "nodelete");

diff  --git a/lld/ELF/OutputSections.cpp b/lld/ELF/OutputSections.cpp
index 86c5f66c8c0ae..7ae024e47a7e8 100644
--- a/lld/ELF/OutputSections.cpp
+++ b/lld/ELF/OutputSections.cpp
@@ -924,6 +924,11 @@ void OutputSection::checkDynRelAddends(Ctx &ctx) {
           (rel.inputSec == ctx.in.ppc64LongBranchTarget.get() ||
            rel.inputSec == ctx.in.igotPlt.get()))
         continue;
+      // With -z mark-plt, the JUMP_SLOT relocation's addend is the PLT entry
+      // address, but the .got.plt entry it targets holds the lazy-binding
+      // address instead, so the written value intentionally 
diff ers.
+      if (ctx.arg.zMarkPlt && rel.type == ctx.target->pltRel)
+        continue;
       const uint8_t *relocTarget =
           ctx.bufferStart + relOsec->offset + (rel.r_offset - relOsec->addr);
       // For SHT_NOBITS the written addend is always zero.

diff  --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp
index 0bd594b224b73..b6540a943bd18 100644
--- a/lld/ELF/Relocations.cpp
+++ b/lld/ELF/Relocations.cpp
@@ -762,6 +762,8 @@ static void addPltEntry(Ctx &ctx, PltSection &plt, GotPltSection &gotPlt,
     return;
   }
   gotPlt.addEntry(sym);
+  if (sym.isPreemptible && ctx.arg.zMarkPlt && type == ctx.target->pltRel)
+    expr = R_PLT;
   rel.addReloc(
       {type, &gotPlt, sym.getGotPltOffset(ctx), isPreemptible, sym, 0, expr});
 }

diff  --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp
index dd20ca35d2d5c..2e7e898a55c63 100644
--- a/lld/ELF/SyntheticSections.cpp
+++ b/lld/ELF/SyntheticSections.cpp
@@ -1332,6 +1332,12 @@ DynamicSection<ELFT>::computeContents() {
     addInt(DT_PLTREL, ctx.arg.isRela ? DT_RELA : DT_REL);
   }
 
+  if (ctx.arg.zMarkPlt && ctx.in.plt->isNeeded()) {
+    addInSec(DT_X86_64_PLT, *ctx.in.plt);
+    addInt(DT_X86_64_PLTSZ, ctx.in.plt->getSize());
+    addInt(DT_X86_64_PLTENT, ctx.target->pltEntrySize);
+  }
+
   if (ctx.arg.emachine == EM_AARCH64) {
     if (ctx.arg.andFeatures & GNU_PROPERTY_AARCH64_FEATURE_1_BTI)
       addInt(DT_AARCH64_BTI_PLT, 0);

diff  --git a/lld/test/ELF/x86-64-plt.s b/lld/test/ELF/x86-64-plt.s
index f36ba69146db3..78823a041169f 100644
--- a/lld/test/ELF/x86-64-plt.s
+++ b/lld/test/ELF/x86-64-plt.s
@@ -10,6 +10,24 @@
 # RUN: llvm-readelf -S -r %t.so | FileCheck %s --check-prefix=CHECK2
 # RUN: llvm-objdump --no-print-imm-hex -d --no-show-raw-insn %t.so | FileCheck %s --check-prefixes=DISASM,DISASM2
 
+# RUN: ld.lld %t.o %t2.so -z mark-plt -z now -o %t.mark
+# RUN: llvm-readelf -S --dynamic-table -r %t.mark | FileCheck %s --check-prefix=MARK
+# RUN: llvm-objdump --no-print-imm-hex -d --no-show-raw-insn %t.mark | FileCheck %s --check-prefix=DISASM-MARK
+
+## --apply-dynamic-relocs must not trip the dynamic relocation addend check:
+## the JUMP_SLOT addend is the PLT entry address while the .got.plt entry holds
+## the lazy-binding address.
+# RUN: ld.lld %t.o %t2.so -z mark-plt -z now --apply-dynamic-relocs -o %t.mark2
+# RUN: llvm-readelf -r %t.mark2 | FileCheck %s --check-prefix=MARK-RELA
+
+# MARK-RELA:      Relocation section '.rela.plt' at offset {{.*}} contains 2 entries:
+# MARK-RELA:      {{.*}} R_X86_64_JUMP_SLOT 0000000000000000 weak + 2012c0
+# MARK-RELA-NEXT: {{.*}} R_X86_64_JUMP_SLOT 0000000000000000 bar + 2012d0
+
+## -z mark-plt requires RELA relocations to carry the PLT entry address addend.
+# RUN: not ld.lld %t.o %t2.so -z mark-plt -z rel -o /dev/null 2>&1 | FileCheck %s --check-prefix=ERR-REL
+# ERR-REL: error: -z mark-plt requires -z rela
+
 # CHECK1:      Name      Type     Address          Off    Size   ES Flg Lk Inf Al
 # CHECK1:      .plt      PROGBITS 00000000002012e0 0002e0 000030 00 AX   0   0 16
 # CHECK1:      .got.plt  PROGBITS 00000000002033e0 0003e0 000028 00 WA   0   0  8
@@ -24,6 +42,15 @@
 # CHECK2:      0000000000003418 {{.*}} R_X86_64_JUMP_SLOT 0000000000000000 weak + 0
 # CHECK2-NEXT: 0000000000003420 {{.*}} R_X86_64_JUMP_SLOT 0000000000000000 bar + 0
 
+# MARK:      Name      Type     Address          Off    Size   ES Flg Lk Inf Al
+# MARK:      .plt      PROGBITS 00000000002012b0 0002b0 000030 00 AX   0   0 16
+# MARK:      0x0000000070000000 (X86_64_PLT)    0x2012b0
+# MARK-NEXT: 0x0000000070000001 (X86_64_PLTSZ)  0x30
+# MARK-NEXT: 0x0000000070000003 (X86_64_PLTENT) 0x10
+# MARK:      Relocation section '.rela.plt' at offset {{.*}} contains 2 entries:
+# MARK:      {{.*}} R_X86_64_JUMP_SLOT 0000000000000000 weak + 2012c0
+# MARK-NEXT: {{.*}} R_X86_64_JUMP_SLOT 0000000000000000 bar + 2012d0
+
 # DISASM:       <_start>:
 # DISASM-NEXT:    callq {{.*}} <local>
 # DISASM-NEXT:    callq {{.*}} <bar at plt>
@@ -66,6 +93,23 @@
 # DISASM2-NEXT:             jmp 0x1310 <.plt>
 # DISASM2-NOT:  {{.}}
 
+# DISASM-MARK:      Disassembly of section .plt:
+# DISASM-MARK-EMPTY:
+# DISASM-MARK-NEXT: <.plt>:
+# DISASM-MARK-NEXT: 2012b0:      pushq 4434(%rip)  # 0x202408
+# DISASM-MARK-NEXT:              jmpq *4436(%rip)  # 0x202410
+# DISASM-MARK-NEXT:              nopl (%rax)
+# DISASM-MARK-EMPTY:
+# DISASM-MARK: <weak at plt>:
+# DISASM-MARK-NEXT: 2012c0:      jmpq *4434(%rip)  # 0x202418
+# DISASM-MARK-NEXT:              pushq $0
+# DISASM-MARK-NEXT:              jmp 0x2012b0 <.plt>
+# DISASM-MARK-EMPTY:
+# DISASM-MARK: <bar at plt>:
+# DISASM-MARK-NEXT: 2012d0:      jmpq *4426(%rip)  # 0x202420
+# DISASM-MARK-NEXT:              pushq $1
+# DISASM-MARK-NEXT:              jmp 0x2012b0 <.plt>
+
 .global _start
 .weak weak
 


        


More information about the llvm-commits mailing list