https://github.com/pcc updated https://github.com/llvm/llvm-project/pull/147424
>From 5bce06b0d8db161a2e09709bcfe15b4623e43d01 Mon Sep 17 00:00:00 2001 From: Peter Collingbourne <pe...@pcc.me.uk> Date: Mon, 7 Jul 2025 16:41:10 -0700 Subject: [PATCH 1/2] =?UTF-8?q?[=F0=9D=98=80=F0=9D=97=BD=F0=9D=97=BF]=20in?= =?UTF-8?q?itial=20version?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Created using spr 1.3.6-beta.1 --- lld/ELF/Arch/X86_64.cpp | 95 +++++++++++++++++++++++++++++++++++++++++ lld/ELF/Relocations.cpp | 2 +- lld/ELF/Target.h | 1 + 3 files changed, 97 insertions(+), 1 deletion(-) diff --git a/lld/ELF/Arch/X86_64.cpp b/lld/ELF/Arch/X86_64.cpp index 488f4803b2cb4..04ca79befdc4a 100644 --- a/lld/ELF/Arch/X86_64.cpp +++ b/lld/ELF/Arch/X86_64.cpp @@ -318,6 +318,9 @@ bool X86_64::deleteFallThruJmpInsn(InputSection &is, InputFile *file, } bool X86_64::relaxOnce(int pass) const { + if (pass == 0) + relaxJumpTables(ctx); + uint64_t minVA = UINT64_MAX, maxVA = 0; for (OutputSection *osec : ctx.outputSections) { if (!(osec->flags & SHF_ALLOC)) @@ -1231,6 +1234,98 @@ void X86_64::applyBranchToBranchOpt() const { redirectControlTransferRelocations); } +void elf::relaxJumpTables(Ctx &ctx) { + // Relax CFI jump tables. + // - Split jump table into pieces and place target functions inside the jump + // table if small enough. + // - Move jump table before last called function and delete last branch + // instruction. + std::map<InputSection *, std::vector<InputSection *>> sectionReplacements; + SmallVector<InputSection *, 0> storage; + for (OutputSection *osec : ctx.outputSections) { + if (!(osec->flags & SHF_EXECINSTR)) + continue; + for (InputSection *sec : getInputSections(*osec, storage)) { + if (!sec->name.starts_with(".text..L.cfi.jumptable")) + continue; + std::vector<InputSection *> replacements; + replacements.push_back(sec); + auto addSectionSlice = [&](size_t begin, size_t end, Relocation *rbegin, + Relocation *rend) { + if (begin == end) + return; + auto *slice = make<InputSection>( + sec->file, sec->name, sec->type, sec->flags, 1, sec->entsize, + sec->contentMaybeDecompress().slice(begin, end - begin)); + for (const Relocation &r : ArrayRef<Relocation>(rbegin, rend)) { + slice->relocations.push_back( + Relocation{r.expr, r.type, r.offset - begin, r.addend, r.sym}); + } + replacements.push_back(slice); + }; + auto getMovableSection = [&](Relocation &r) -> InputSection * { + auto *sym = dyn_cast_or_null<Defined>(r.sym); + if (!sym || sym->isPreemptible || sym->isGnuIFunc() || sym->value != 0) + return nullptr; + auto *sec = dyn_cast_or_null<InputSection>(sym->section); + if (!sec || sectionReplacements.count(sec)) + return nullptr; + return sec; + }; + size_t begin = 0; + Relocation *rbegin = sec->relocs().begin(); + for (auto &r : sec->relocs().slice(0, sec->relocs().size() - 1)) { + auto entrySize = (&r + 1)->offset - r.offset; + InputSection *target = getMovableSection(r); + if (!target || target->size > entrySize) + continue; + target->addralign = 1; + addSectionSlice(begin, r.offset - 1, rbegin, &r); + replacements.push_back(target); + sectionReplacements[target] = {}; + begin = r.offset - 1 + target->size; + rbegin = &r + 1; + } + InputSection *lastSec = getMovableSection(sec->relocs().back()); + if (lastSec) { + lastSec->addralign = 1; + addSectionSlice(begin, sec->relocs().back().offset - 1, rbegin, + &sec->relocs().back()); + replacements.push_back(lastSec); + sectionReplacements[sec] = {}; + sectionReplacements[lastSec] = replacements; + for (auto *s : replacements) + s->parent = lastSec->parent; + } else { + addSectionSlice(begin, sec->size, rbegin, sec->relocs().end()); + sectionReplacements[sec] = replacements; + for (auto *s : replacements) + s->parent = sec->parent; + } + sec->relocations.clear(); + sec->size = 0; + } + } + for (OutputSection *osec : ctx.outputSections) { + if (!(osec->flags & SHF_EXECINSTR)) + continue; + for (SectionCommand *cmd : osec->commands) { + auto *isd = dyn_cast<InputSectionDescription>(cmd); + if (!isd) + continue; + SmallVector<InputSection *> newSections; + for (auto *sec : isd->sections) { + auto i = sectionReplacements.find(sec); + if (i == sectionReplacements.end()) + newSections.push_back(sec); + else + newSections.append(i->second.begin(), i->second.end()); + } + isd->sections = std::move(newSections); + } + } +} + // If Intel Indirect Branch Tracking is enabled, we have to emit special PLT // entries containing endbr64 instructions. A PLT entry will be split into two // parts, one in .plt.sec (writePlt), and the other in .plt (writeIBTPlt). diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp index cebd564036b2c..f7e3d54878395 100644 --- a/lld/ELF/Relocations.cpp +++ b/lld/ELF/Relocations.cpp @@ -1674,7 +1674,7 @@ void RelocationScanner::scan(Relocs<RelTy> rels) { // R_RISCV_PCREL_HI20, R_PPC64_ADDR64 and the branch-to-branch optimization. if (ctx.arg.emachine == EM_RISCV || (ctx.arg.emachine == EM_PPC64 && sec->name == ".toc") || - ctx.arg.branchToBranch) + ctx.arg.branchToBranch || sec->name.starts_with(".text..L.cfi.jumptable")) llvm::stable_sort(sec->relocs(), [](const Relocation &lhs, const Relocation &rhs) { return lhs.offset < rhs.offset; diff --git a/lld/ELF/Target.h b/lld/ELF/Target.h index 6dd20b2f0cbaa..e6eb33fa5338c 100644 --- a/lld/ELF/Target.h +++ b/lld/ELF/Target.h @@ -195,6 +195,7 @@ void setSPARCV9TargetInfo(Ctx &); void setSystemZTargetInfo(Ctx &); void setX86TargetInfo(Ctx &); void setX86_64TargetInfo(Ctx &); +void relaxJumpTables(Ctx &); struct ErrorPlace { InputSectionBase *isec; >From afa726a766603c29393a6c3f0d3500a11c85d9e9 Mon Sep 17 00:00:00 2001 From: Peter Collingbourne <pe...@pcc.me.uk> Date: Thu, 28 Aug 2025 15:34:34 -0700 Subject: [PATCH 2/2] Fix bug where overaligned jump tables are laid out incorrectly Created using spr 1.3.6-beta.1 --- lld/ELF/Arch/X86_64.cpp | 7 +++++++ lld/test/ELF/x86_64-relax-jump-tables.s | 27 +++++++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/lld/ELF/Arch/X86_64.cpp b/lld/ELF/Arch/X86_64.cpp index 2ced135a4d0c3..50026dabfeee5 100644 --- a/lld/ELF/Arch/X86_64.cpp +++ b/lld/ELF/Arch/X86_64.cpp @@ -376,6 +376,13 @@ void X86_64::relaxCFIJumpTables() const { // because the last entry controls which output section the jump table is // placed into, which affects move eligibility for other sections. auto *lastSec = [&]() -> InputSection * { + // If the jump table section is more aligned than the entry size, skip + // this because there's no guarantee that we'll be able to emit a + // padding section that places the last entry at a correctly aligned + // address. + if (sec->addralign > sec->entsize) + return nullptr; + Relocation *lastReloc = sec->relocs().end(); while (lastReloc != sec->relocs().begin() && (lastReloc - 1)->offset >= sec->size - sec->entsize) diff --git a/lld/test/ELF/x86_64-relax-jump-tables.s b/lld/test/ELF/x86_64-relax-jump-tables.s index 8c2394003bacb..50edf4c240ecc 100644 --- a/lld/test/ELF/x86_64-relax-jump-tables.s +++ b/lld/test/ELF/x86_64-relax-jump-tables.s @@ -109,6 +109,23 @@ jmp f12.cfi f13: jmp f13.cfi +// Jumptable alignment > entsize prevents it from being moved before last +// function, but moving non-last functions into the jumptable should work. +// CHECK: <f14>: +// CHECK-NEXT: <f14.cfi>: +// CHECK-NEXT: retq $0xe +.section .text.jt5,"ax",@llvm_cfi_jump_table,8 +.balign 16 +f14: +jmp f14.cfi +.balign 8, 0xcc + +// CHECK: <f15>: +// CHECK-NEXT: jmp {{.*}} <f15.cfi> +f15: +jmp f15.cfi +.balign 8, 0xcc + // CHECK: <f1>: // CHECK-NEXT: <f1.cfi>: // CHECK-NEXT: retq $0x1 @@ -180,5 +197,15 @@ ret $12 f13.cfi: ret $13 +.section .text.f14,"ax",@progbits +f14.cfi: +ret $14 + +.section .text.f15,"ax",@progbits +.balign 64 +f15.cfi: +ret $15 +.zero 16 + // CHECK: <.iplt>: // CHECK-NEXT: [[IPLT]]: _______________________________________________ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits