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
