https://github.com/phoebewang updated 
https://github.com/llvm/llvm-project/pull/206002

>From 0892425b2582788a8937655cfa739863034b978b Mon Sep 17 00:00:00 2001
From: Phoebe Wang <[email protected]>
Date: Fri, 26 Jun 2026 15:45:06 +0800
Subject: [PATCH 1/5] [X86] Add dynamic section support per psABI

Add option [no]mark-plt to enable it. Dynamic linker can change PLT
entries with JMPABS instruction on supported targets.
---
 lld/ELF/Config.h                              |  1 +
 lld/ELF/Driver.cpp                            |  1 +
 lld/ELF/Relocations.cpp                       | 10 ++++-
 lld/ELF/SyntheticSections.cpp                 |  7 ++++
 lld/test/ELF/x86-64-mark-plt.s                | 37 +++++++++++++++++++
 .../include/llvm/BinaryFormat/DynamicTags.def | 19 ++++++++++
 llvm/lib/Object/ELF.cpp                       | 22 +++++++++++
 7 files changed, 96 insertions(+), 1 deletion(-)
 create mode 100644 lld/test/ELF/x86-64-mark-plt.s

diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h
index 12b16ded61fca..d6590ab284e5b 100644
--- a/lld/ELF/Config.h
+++ b/lld/ELF/Config.h
@@ -439,6 +439,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 7ec7dfcae6bca..c41991e377832 100644
--- a/lld/ELF/Driver.cpp
+++ b/lld/ELF/Driver.cpp
@@ -1657,6 +1657,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/Relocations.cpp b/lld/ELF/Relocations.cpp
index 0d45236e6d11e..b8aac5b5b63f4 100644
--- a/lld/ELF/Relocations.cpp
+++ b/lld/ELF/Relocations.cpp
@@ -749,9 +749,17 @@ static void addPltEntry(Ctx &ctx, PltSection &plt, 
GotPltSection &gotPlt,
                         RelocationBaseSection &rel, RelType type, Symbol &sym) 
{
   plt.addEntry(sym);
   gotPlt.addEntry(sym);
+  // The x86-64 psABI requires the r_addend of R_X86_64_JUMP_SLOT to hold the
+  // link-time VA of the PLT entry so that the dynamic linker can locate the
+  // indirect branch (l->l_addr + r_addend) and rewrite it to a direct branch
+  // when -z mark-plt is active.
+  RelExpr addendExpr = R_ADDEND;
+  if (ctx.arg.emachine == EM_X86_64 && ctx.arg.zMarkPlt &&
+      type == ctx.target->pltRel)
+    addendExpr = R_PLT;
   if (sym.isPreemptible)
     rel.addReloc(
-        {type, &gotPlt, sym.getGotPltOffset(ctx), true, sym, 0, R_ADDEND});
+        {type, &gotPlt, sym.getGotPltOffset(ctx), true, sym, 0, addendExpr});
   else
     rel.addReloc(
         {type, &gotPlt, sym.getGotPltOffset(ctx), false, sym, 0, R_ABS});
diff --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp
index b9a42590dc8e7..ac417e160f31d 100644
--- a/lld/ELF/SyntheticSections.cpp
+++ b/lld/ELF/SyntheticSections.cpp
@@ -1318,6 +1318,13 @@ DynamicSection<ELFT>::computeContents() {
     addInt(DT_PLTREL, ctx.arg.isRela ? DT_RELA : DT_REL);
   }
 
+  if (ctx.arg.emachine == EM_X86_64 && 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-mark-plt.s b/lld/test/ELF/x86-64-mark-plt.s
new file mode 100644
index 0000000000000..e177c64ee418f
--- /dev/null
+++ b/lld/test/ELF/x86-64-mark-plt.s
@@ -0,0 +1,37 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t.o
+# RUN: llvm-mc -filetype=obj -triple=x86_64 %p/Inputs/shared.s -o %t2.o
+# RUN: ld.lld -shared -soname=t2 %t2.o -o %t2.so
+
+## -z mark-plt emits DT_X86_64_PLT, DT_X86_64_PLTSZ, DT_X86_64_PLTENT.
+## PLT layout: 16-byte header + 1 entry * 16 bytes = 0x20 bytes total.
+# RUN: ld.lld %t.o %t2.so -z mark-plt -z now -o %t
+# RUN: llvm-readelf --dynamic-table %t | FileCheck %s --check-prefix=MARK
+# RUN: llvm-readelf --relocs %t | FileCheck %s --check-prefix=RELA
+
+## Without -z mark-plt: no tags and zero addend on JUMP_SLOT.
+# RUN: ld.lld %t.o %t2.so -z now -o %t.nomark
+# RUN: llvm-readelf --dynamic-table %t.nomark | FileCheck %s 
--check-prefix=NOMARK
+# RUN: llvm-readelf --relocs %t.nomark | FileCheck %s --check-prefix=NORELA
+
+## -z mark-plt also works for shared libraries.
+# RUN: ld.lld -shared %t.o %t2.so -z mark-plt -z now -o %t.so
+# RUN: llvm-readelf --dynamic-table %t.so | FileCheck %s --check-prefix=MARK
+
+# MARK:     0x0000000070000000 (X86_64_PLT)   {{0x[0-9a-f]+}}
+# MARK:     0x0000000070000001 (X86_64_PLTSZ)  0x20
+# MARK:     0x0000000070000003 (X86_64_PLTENT) 0x10
+
+# NOMARK-NOT: (X86_64_PLT)
+# NOMARK-NOT: (X86_64_PLTSZ)
+# NOMARK-NOT: (X86_64_PLTENT)
+
+## With -z mark-plt, R_X86_64_JUMP_SLOT addend is the PLT entry VA (non-zero).
+# RELA:     R_X86_64_JUMP_SLOT {{.*}} bar + {{[1-9a-f][0-9a-f]+}}
+
+## Without -z mark-plt, R_X86_64_JUMP_SLOT addend is 0.
+# NORELA:   R_X86_64_JUMP_SLOT {{.*}} bar + 0
+
+.globl _start
+_start:
+  call bar@plt
diff --git a/llvm/include/llvm/BinaryFormat/DynamicTags.def 
b/llvm/include/llvm/BinaryFormat/DynamicTags.def
index 1e9b87e1e58a7..c700044920575 100644
--- a/llvm/include/llvm/BinaryFormat/DynamicTags.def
+++ b/llvm/include/llvm/BinaryFormat/DynamicTags.def
@@ -41,6 +41,11 @@
 #define SPARC_DYNAMIC_TAG_DEFINED
 #endif
 
+#ifndef X86_64_DYNAMIC_TAG
+#define X86_64_DYNAMIC_TAG(name, value) DYNAMIC_TAG(name, value)
+#define X86_64_DYNAMIC_TAG_DEFINED
+#endif
+
 #ifndef DYNAMIC_TAG_MARKER
 #define DYNAMIC_TAG_MARKER(name, value) DYNAMIC_TAG(name, value)
 #define DYNAMIC_TAG_MARKER_DEFINED
@@ -248,6 +253,12 @@ RISCV_DYNAMIC_TAG(RISCV_VARIANT_CC, 0x70000001)
 
 SPARC_DYNAMIC_TAG(SPARC_REGISTER, 0x70000001)
 
+// x86-64 specific dynamic table entry tags.
+
+X86_64_DYNAMIC_TAG(X86_64_PLT, 0x70000000)    // Address of PLT table.
+X86_64_DYNAMIC_TAG(X86_64_PLTSZ, 0x70000001)  // Size of PLT table in bytes.
+X86_64_DYNAMIC_TAG(X86_64_PLTENT, 0x70000003) // Size of a PLT entry in bytes.
+
 // Sun machine-independent extensions.
 DYNAMIC_TAG(AUXILIARY, 0x7FFFFFFD) // Shared object to load before self
 DYNAMIC_TAG(USED, 0x7FFFFFFE)      // Same as DT_NEEDED
@@ -282,3 +293,11 @@ DYNAMIC_TAG(FILTER, 0x7FFFFFFF)    // Shared object to get 
values from
 #undef RISCV_DYNAMIC_TAG
 #undef RISCV_DYNAMIC_TAG_DEFINED
 #endif
+#ifdef SPARC_DYNAMIC_TAG_DEFINED
+#undef SPARC_DYNAMIC_TAG
+#undef SPARC_DYNAMIC_TAG_DEFINED
+#endif
+#ifdef X86_64_DYNAMIC_TAG_DEFINED
+#undef X86_64_DYNAMIC_TAG
+#undef X86_64_DYNAMIC_TAG_DEFINED
+#endif
diff --git a/llvm/lib/Object/ELF.cpp b/llvm/lib/Object/ELF.cpp
index f159fd82e7117..48dd225932ba8 100644
--- a/llvm/lib/Object/ELF.cpp
+++ b/llvm/lib/Object/ELF.cpp
@@ -588,6 +588,24 @@ std::string ELFFile<ELFT>::getDynamicTagAsString(unsigned 
Arch,
 #undef RISCV_DYNAMIC_TAG
     }
     break;
+
+  case ELF::EM_SPARC:
+  case ELF::EM_SPARC32PLUS:
+  case ELF::EM_SPARCV9:
+    switch (Type) {
+#define SPARC_DYNAMIC_TAG(name, value) DYNAMIC_STRINGIFY_ENUM(name, value)
+#include "llvm/BinaryFormat/DynamicTags.def"
+#undef SPARC_DYNAMIC_TAG
+    }
+    break;
+
+  case ELF::EM_X86_64:
+    switch (Type) {
+#define X86_64_DYNAMIC_TAG(name, value) DYNAMIC_STRINGIFY_ENUM(name, value)
+#include "llvm/BinaryFormat/DynamicTags.def"
+#undef X86_64_DYNAMIC_TAG
+    }
+    break;
   }
 #undef DYNAMIC_TAG
   switch (Type) {
@@ -598,6 +616,8 @@ std::string ELFFile<ELFT>::getDynamicTagAsString(unsigned 
Arch,
 #define PPC_DYNAMIC_TAG(name, value)
 #define PPC64_DYNAMIC_TAG(name, value)
 #define RISCV_DYNAMIC_TAG(name, value)
+#define SPARC_DYNAMIC_TAG(name, value)
+#define X86_64_DYNAMIC_TAG(name, value)
 // Also ignore marker tags such as DT_HIOS (maps to DT_VERNEEDNUM), etc.
 #define DYNAMIC_TAG_MARKER(name, value)
 #define DYNAMIC_TAG(name, value) case value: return #name;
@@ -609,6 +629,8 @@ std::string ELFFile<ELFT>::getDynamicTagAsString(unsigned 
Arch,
 #undef PPC_DYNAMIC_TAG
 #undef PPC64_DYNAMIC_TAG
 #undef RISCV_DYNAMIC_TAG
+#undef SPARC_DYNAMIC_TAG
+#undef X86_64_DYNAMIC_TAG
 #undef DYNAMIC_TAG_MARKER
 #undef DYNAMIC_STRINGIFY_ENUM
   default:

>From d16048a10dbca3492448bff3052734ae2f9bfe1e Mon Sep 17 00:00:00 2001
From: Phoebe Wang <[email protected]>
Date: Fri, 26 Jun 2026 16:35:01 +0800
Subject: [PATCH 2/5] Fix lldb failure

---
 .../Plugins/ObjectFile/ELF/ObjectFileELF.cpp  | 22 +++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp 
b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp
index 16dd2fc122906..f343939ad115f 100644
--- a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp
+++ b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp
@@ -3800,6 +3800,24 @@ std::string static getDynamicTagAsString(uint16_t Arch, 
uint64_t Type) {
 #undef RISCV_DYNAMIC_TAG
     }
     break;
+
+  case llvm::ELF::EM_SPARC:
+  case llvm::ELF::EM_SPARC32PLUS:
+  case llvm::ELF::EM_SPARCV9:
+    switch (Type) {
+#define SPARC_DYNAMIC_TAG(name, value) DYNAMIC_STRINGIFY_ENUM(name, value)
+#include "llvm/BinaryFormat/DynamicTags.def"
+#undef SPARC_DYNAMIC_TAG
+    }
+    break;
+
+  case llvm::ELF::EM_X86_64:
+    switch (Type) {
+#define X86_64_DYNAMIC_TAG(name, value) DYNAMIC_STRINGIFY_ENUM(name, value)
+#include "llvm/BinaryFormat/DynamicTags.def"
+#undef X86_64_DYNAMIC_TAG
+    }
+    break;
   }
 #undef DYNAMIC_TAG
   switch (Type) {
@@ -3810,6 +3828,8 @@ std::string static getDynamicTagAsString(uint16_t Arch, 
uint64_t Type) {
 #define PPC_DYNAMIC_TAG(name, value)
 #define PPC64_DYNAMIC_TAG(name, value)
 #define RISCV_DYNAMIC_TAG(name, value)
+#define SPARC_DYNAMIC_TAG(name, value)
+#define X86_64_DYNAMIC_TAG(name, value)
 // Also ignore marker tags such as DT_HIOS (maps to DT_VERNEEDNUM), etc.
 #define DYNAMIC_TAG_MARKER(name, value)
 #define DYNAMIC_TAG(name, value)                                               
\
@@ -3823,6 +3843,8 @@ std::string static getDynamicTagAsString(uint16_t Arch, 
uint64_t Type) {
 #undef PPC_DYNAMIC_TAG
 #undef PPC64_DYNAMIC_TAG
 #undef RISCV_DYNAMIC_TAG
+#undef SPARC_DYNAMIC_TAG
+#undef X86_64_DYNAMIC_TAG
 #undef DYNAMIC_TAG_MARKER
 #undef DYNAMIC_STRINGIFY_ENUM
   default:

>From 832a36f14668dfcb24659f26c5d630e2a5796eef Mon Sep 17 00:00:00 2001
From: Phoebe Wang <[email protected]>
Date: Sat, 27 Jun 2026 21:00:28 +0800
Subject: [PATCH 3/5] Add llvm-readelf and llvm-objdump testing

---
 .../ELF/dynamic-tags-machine-specific.test    | 46 +++++++++++++++++++
 1 file changed, 46 insertions(+)

diff --git 
a/llvm/test/tools/llvm-readobj/ELF/dynamic-tags-machine-specific.test 
b/llvm/test/tools/llvm-readobj/ELF/dynamic-tags-machine-specific.test
index 9a338a852ffe7..fcc365e528dea 100644
--- a/llvm/test/tools/llvm-readobj/ELF/dynamic-tags-machine-specific.test
+++ b/llvm/test/tools/llvm-readobj/ELF/dynamic-tags-machine-specific.test
@@ -483,3 +483,49 @@ ProgramHeaders:
   - Type:     PT_DYNAMIC
     FirstSec: .dynamic
     LastSec:  .dynamic
+
+## Case 8: Test that x86-64 machine-specific tags can be dumped.
+# RUN: yaml2obj --docnum=8 %s -o %t.x86_64
+# RUN: llvm-readobj --dynamic-table %t.x86_64 | FileCheck %s 
--check-prefix=LLVM-X86_64
+# RUN: llvm-readelf --dynamic-table %t.x86_64 | FileCheck %s 
--check-prefix=GNU-X86_64
+
+# LLVM-X86_64:      DynamicSection [ (4 entries)
+# LLVM-X86_64-NEXT:   Tag                Type          Name/Value
+# LLVM-X86_64-NEXT:   0x0000000070000000 X86_64_PLT    0x401020
+# LLVM-X86_64-NEXT:   0x0000000070000001 X86_64_PLTSZ  0x20
+# LLVM-X86_64-NEXT:   0x0000000070000003 X86_64_PLTENT 0x10
+# LLVM-X86_64-NEXT:   0x0000000000000000 NULL          0x0
+# LLVM-X86_64-NEXT: ]
+
+# GNU-X86_64:      Dynamic section at offset {{.*}} contains 4 entries:
+# GNU-X86_64-NEXT:   Tag                Type            Name/Value
+# GNU-X86_64-NEXT:   0x0000000070000000 (X86_64_PLT)    0x401020
+# GNU-X86_64-NEXT:   0x0000000070000001 (X86_64_PLTSZ)  0x20
+# GNU-X86_64-NEXT:   0x0000000070000003 (X86_64_PLTENT) 0x10
+# GNU-X86_64-NEXT:   0x0000000000000000 (NULL)          0x0
+
+--- !ELF
+FileHeader:
+  Class:   ELFCLASS64
+  Data:    ELFDATA2LSB
+  Type:    ET_EXEC
+  Machine: EM_X86_64
+Sections:
+  - Name: .dynamic
+    Type: SHT_DYNAMIC
+    Entries:
+      - Tag:   DT_X86_64_PLT
+        Value: 0x401020
+      - Tag:   DT_X86_64_PLTSZ
+        Value: 0x20
+      - Tag:   DT_X86_64_PLTENT
+        Value: 0x10
+      - Tag:   DT_NULL
+        Value: 0
+ProgramHeaders:
+  - Type:     PT_LOAD
+    FirstSec: .dynamic
+    LastSec:  .dynamic
+  - Type:     PT_DYNAMIC
+    FirstSec: .dynamic
+    LastSec:  .dynamic

>From 7762b8aed71160b5b0117146fb16cee77614d55f Mon Sep 17 00:00:00 2001
From: Phoebe Wang <[email protected]>
Date: Mon, 27 Jul 2026 16:14:22 +0800
Subject: [PATCH 4/5] Address review comments

---
 lld/ELF/Driver.cpp             |  3 +++
 lld/ELF/SyntheticSections.cpp  |  3 +--
 lld/test/ELF/x86-64-mark-plt.s | 37 ----------------------------------
 lld/test/ELF/x86-64-plt.s      | 30 +++++++++++++++++++++++++++
 4 files changed, 34 insertions(+), 39 deletions(-)
 delete mode 100644 lld/test/ELF/x86-64-mark-plt.s

diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp
index 7e5723998d118..155169b87c70e 100644
--- a/lld/ELF/Driver.cpp
+++ b/lld/ELF/Driver.cpp
@@ -399,6 +399,9 @@ 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.emachine != EM_X86_64 && ctx.arg.zMarkPlt)
+    ErrAlways(ctx) << "-z mark-plt only supported on X86_64";
+
   if (ctx.arg.pie && ctx.arg.shared)
     ErrAlways(ctx) << "-shared and -pie may not be used together";
 
diff --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp
index 38c7b54c4474b..b47af62b9cf67 100644
--- a/lld/ELF/SyntheticSections.cpp
+++ b/lld/ELF/SyntheticSections.cpp
@@ -1316,8 +1316,7 @@ DynamicSection<ELFT>::computeContents() {
     addInt(DT_PLTREL, ctx.arg.isRela ? DT_RELA : DT_REL);
   }
 
-  if (ctx.arg.emachine == EM_X86_64 && ctx.arg.zMarkPlt &&
-      ctx.in.plt->isNeeded()) {
+  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);
diff --git a/lld/test/ELF/x86-64-mark-plt.s b/lld/test/ELF/x86-64-mark-plt.s
deleted file mode 100644
index e177c64ee418f..0000000000000
--- a/lld/test/ELF/x86-64-mark-plt.s
+++ /dev/null
@@ -1,37 +0,0 @@
-# REQUIRES: x86
-# RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t.o
-# RUN: llvm-mc -filetype=obj -triple=x86_64 %p/Inputs/shared.s -o %t2.o
-# RUN: ld.lld -shared -soname=t2 %t2.o -o %t2.so
-
-## -z mark-plt emits DT_X86_64_PLT, DT_X86_64_PLTSZ, DT_X86_64_PLTENT.
-## PLT layout: 16-byte header + 1 entry * 16 bytes = 0x20 bytes total.
-# RUN: ld.lld %t.o %t2.so -z mark-plt -z now -o %t
-# RUN: llvm-readelf --dynamic-table %t | FileCheck %s --check-prefix=MARK
-# RUN: llvm-readelf --relocs %t | FileCheck %s --check-prefix=RELA
-
-## Without -z mark-plt: no tags and zero addend on JUMP_SLOT.
-# RUN: ld.lld %t.o %t2.so -z now -o %t.nomark
-# RUN: llvm-readelf --dynamic-table %t.nomark | FileCheck %s 
--check-prefix=NOMARK
-# RUN: llvm-readelf --relocs %t.nomark | FileCheck %s --check-prefix=NORELA
-
-## -z mark-plt also works for shared libraries.
-# RUN: ld.lld -shared %t.o %t2.so -z mark-plt -z now -o %t.so
-# RUN: llvm-readelf --dynamic-table %t.so | FileCheck %s --check-prefix=MARK
-
-# MARK:     0x0000000070000000 (X86_64_PLT)   {{0x[0-9a-f]+}}
-# MARK:     0x0000000070000001 (X86_64_PLTSZ)  0x20
-# MARK:     0x0000000070000003 (X86_64_PLTENT) 0x10
-
-# NOMARK-NOT: (X86_64_PLT)
-# NOMARK-NOT: (X86_64_PLTSZ)
-# NOMARK-NOT: (X86_64_PLTENT)
-
-## With -z mark-plt, R_X86_64_JUMP_SLOT addend is the PLT entry VA (non-zero).
-# RELA:     R_X86_64_JUMP_SLOT {{.*}} bar + {{[1-9a-f][0-9a-f]+}}
-
-## Without -z mark-plt, R_X86_64_JUMP_SLOT addend is 0.
-# NORELA:   R_X86_64_JUMP_SLOT {{.*}} bar + 0
-
-.globl _start
-_start:
-  call bar@plt
diff --git a/lld/test/ELF/x86-64-plt.s b/lld/test/ELF/x86-64-plt.s
index f36ba69146db3..025552dae70bf 100644
--- a/lld/test/ELF/x86-64-plt.s
+++ b/lld/test/ELF/x86-64-plt.s
@@ -10,6 +10,10 @@
 # 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
+
 # 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 +28,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@plt>
@@ -66,6 +79,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@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@plt>:
+# DISASM-MARK-NEXT: 2012d0:      jmpq *4426(%rip)  # 0x202420
+# DISASM-MARK-NEXT:              pushq $1
+# DISASM-MARK-NEXT:              jmp 0x2012b0 <.plt>
+
 .global _start
 .weak weak
 

>From 200a437b72ec2cd44211e7bb4358e40e4812637f Mon Sep 17 00:00:00 2001
From: Phoebe Wang <[email protected]>
Date: Mon, 24 Aug 2026 00:40:57 -0700
Subject: [PATCH 5/5] [lld] Address review: error on -z mark-plt with REL, skip
 addend check

-z mark-plt stores the PLT entry address in the JUMP_SLOT relocation's
addend. REL relocations have no addend field and the .got.plt entry is
already occupied by the lazy-binding address, so the addend would be
silently lost; error out instead.

With --apply-dynamic-relocs, OutputSection::checkDynRelAddends compared
the relocation addend (PLT entry address) against the value written to
the .got.plt entry (the lazy-binding address). These intentionally
differ for mark-plt, so skip the check for these relocations.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
---
 lld/ELF/Driver.cpp         | 12 ++++++++++--
 lld/ELF/OutputSections.cpp |  5 +++++
 lld/test/ELF/x86-64-plt.s  | 14 ++++++++++++++
 3 files changed, 29 insertions(+), 2 deletions(-)

diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp
index bba8d39207204..83903db765675 100644
--- a/lld/ELF/Driver.cpp
+++ b/lld/ELF/Driver.cpp
@@ -399,8 +399,16 @@ 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.emachine != EM_X86_64 && ctx.arg.zMarkPlt)
-    ErrAlways(ctx) << "-z mark-plt only supported on 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";
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 differs.
+      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/test/ELF/x86-64-plt.s b/lld/test/ELF/x86-64-plt.s
index 025552dae70bf..78823a041169f 100644
--- a/lld/test/ELF/x86-64-plt.s
+++ b/lld/test/ELF/x86-64-plt.s
@@ -14,6 +14,20 @@
 # 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

_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to