Skip to content

Commit 3f8f25b

Browse files
committed
[𝘀𝗽𝗿] changes to main this commit is based on
Created using spr 1.3.6-beta.1 [skip ci]
1 parent 6fea3da commit 3f8f25b

File tree

11 files changed

+419
-26
lines changed

11 files changed

+419
-26
lines changed

lld/ELF/Arch/X86_64.cpp

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,174 @@ bool X86_64::deleteFallThruJmpInsn(InputSection &is, InputFile *file,
317317
return true;
318318
}
319319

320+
static void relaxJumpTables(Ctx &ctx) {
321+
// Relax CFI jump tables.
322+
// - Split jump table into pieces and place target functions inside the jump
323+
// table if small enough.
324+
// - Move jump table before last called function and delete last branch
325+
// instruction.
326+
std::map<InputSection *, std::vector<InputSection *>> sectionReplacements;
327+
SmallVector<InputSection *, 0> storage;
328+
for (OutputSection *osec : ctx.outputSections) {
329+
if (!(osec->flags & SHF_EXECINSTR))
330+
continue;
331+
for (InputSection *sec : getInputSections(*osec, storage)) {
332+
if (sec->type != SHT_LLVM_CFI_JUMP_TABLE || sec->entsize == 0 ||
333+
sec->size % sec->entsize != 0)
334+
continue;
335+
336+
// We're going to replace the jump table with this list of sections. This
337+
// list will be made up of slices of the original section and function
338+
// bodies that were moved into the jump table.
339+
std::vector<InputSection *> replacements;
340+
341+
// First, push the original jump table section. This is only so that it
342+
// can act as a relocation target. Later on, we will set the size of the
343+
// jump table section to 0 so that the slices and moved function bodies
344+
// become the actual relocation targets.
345+
replacements.push_back(sec);
346+
347+
// Add the slice [begin, end) of the original section to the replacement
348+
// list. [rbegin, rend) is the slice of the relocation list that covers
349+
// [begin, end).
350+
auto addSectionSlice = [&](size_t begin, size_t end, Relocation *rbegin,
351+
Relocation *rend) {
352+
auto *slice = make<InputSection>(
353+
sec->file, sec->name, sec->type, sec->flags, sec->entsize,
354+
sec->entsize,
355+
sec->contentMaybeDecompress().slice(begin, end - begin));
356+
for (const Relocation &r : ArrayRef<Relocation>(rbegin, rend)) {
357+
slice->relocations.push_back(
358+
Relocation{r.expr, r.type, r.offset - begin, r.addend, r.sym});
359+
}
360+
replacements.push_back(slice);
361+
};
362+
363+
// r is the only relocation in a jump table entry. Figure out whether it
364+
// is a branch pointing to the start of a statically known section that
365+
// hasn't already been moved while processing a different jump table
366+
// section, and if so return it.
367+
auto getMovableSection = [&](Relocation &r) -> InputSection * {
368+
if (r.type != R_X86_64_PC32 && r.type != R_X86_64_PLT32)
369+
return nullptr;
370+
auto *sym = dyn_cast_or_null<Defined>(r.sym);
371+
if (!sym || sym->isPreemptible || sym->isGnuIFunc() ||
372+
sym->value + r.addend != -4ull)
373+
return nullptr;
374+
auto *target = dyn_cast_or_null<InputSection>(sym->section);
375+
if (!target || target->addralign > sec->entsize ||
376+
sectionReplacements.count(target))
377+
return nullptr;
378+
return target;
379+
};
380+
381+
// Figure out the movable section for the last entry. We do this first
382+
// because the last entry controls which output section the jump table is
383+
// placed into, which affects move eligibility for other sections.
384+
auto *lastSec = [&]() -> InputSection * {
385+
Relocation *lastReloc = sec->relocs().end();
386+
while (lastReloc != sec->relocs().begin() &&
387+
(lastReloc - 1)->offset >= sec->size - sec->entsize)
388+
--lastReloc;
389+
if (lastReloc + 1 != sec->relocs().end())
390+
return nullptr;
391+
return getMovableSection(*lastReloc);
392+
}();
393+
OutputSection *targetOutputSec;
394+
if (lastSec) {
395+
// We've already decided to move the output section so make sure that we
396+
// don't try to move it again.
397+
sectionReplacements[lastSec] = replacements;
398+
targetOutputSec = lastSec->getParent();
399+
} else {
400+
targetOutputSec = sec->getParent();
401+
}
402+
403+
// Walk the jump table entries other than the last one looking for sections
404+
// that are small enough to be moved into the jump table and in the same
405+
// section as the jump table's destination.
406+
size_t begin = 0;
407+
Relocation *rbegin = sec->relocs().begin();
408+
size_t cur = begin;
409+
Relocation *rcur = rbegin;
410+
while (cur != sec->size - sec->entsize) {
411+
size_t next = cur + sec->entsize;
412+
Relocation *rnext = rcur;
413+
while (rnext != sec->relocs().end() && rnext->offset < next)
414+
++rnext;
415+
if (rcur + 1 == rnext) {
416+
InputSection *target = getMovableSection(*rcur);
417+
if (target && target->size <= sec->entsize &&
418+
target->getParent() == targetOutputSec) {
419+
// Okay, we found a small enough section. Move it into the jump
420+
// table. First add a slice for the unmodified jump table entries
421+
// before this one.
422+
addSectionSlice(begin, cur, rbegin, rcur);
423+
// Add the target to our replacement list, and set the target's
424+
// replacement list to the empty list. This removes it from its
425+
// original position and adds it here, as well as causing
426+
// future getMovableSection() queries to return nullptr.
427+
replacements.push_back(target);
428+
sectionReplacements[target] = {};
429+
begin = next;
430+
rbegin = rnext;
431+
}
432+
}
433+
cur = next;
434+
rcur = rnext;
435+
}
436+
437+
// Finally, process the last entry. If it is movable, move the entire
438+
// jump table behind it and delete the last entry (so that the last
439+
// function's body acts as the last jump table entry), otherwise leave the
440+
// jump table where it is and keep the last entry.
441+
if (lastSec) {
442+
addSectionSlice(begin, cur, rbegin, rcur);
443+
replacements.push_back(lastSec);
444+
sectionReplacements[sec] = {};
445+
sectionReplacements[lastSec] = replacements;
446+
for (auto *s : replacements)
447+
s->parent = lastSec->parent;
448+
} else {
449+
addSectionSlice(begin, sec->size, rbegin, sec->relocs().end());
450+
sectionReplacements[sec] = replacements;
451+
for (auto *s : replacements)
452+
s->parent = sec->parent;
453+
}
454+
455+
// Everything from the original section has been recreated, so delete the
456+
// original contents.
457+
sec->relocations.clear();
458+
sec->size = 0;
459+
}
460+
}
461+
462+
// Now that we have the complete mapping of replacements, go through the input
463+
// section lists and apply the replacements.
464+
for (OutputSection *osec : ctx.outputSections) {
465+
if (!(osec->flags & SHF_EXECINSTR))
466+
continue;
467+
for (SectionCommand *cmd : osec->commands) {
468+
auto *isd = dyn_cast<InputSectionDescription>(cmd);
469+
if (!isd)
470+
continue;
471+
SmallVector<InputSection *> newSections;
472+
for (auto *sec : isd->sections) {
473+
auto i = sectionReplacements.find(sec);
474+
if (i == sectionReplacements.end())
475+
newSections.push_back(sec);
476+
else
477+
newSections.append(i->second.begin(), i->second.end());
478+
}
479+
isd->sections = std::move(newSections);
480+
}
481+
}
482+
}
483+
320484
bool X86_64::relaxOnce(int pass) const {
485+
if (pass == 0)
486+
relaxJumpTables(ctx);
487+
321488
uint64_t minVA = UINT64_MAX, maxVA = 0;
322489
for (OutputSection *osec : ctx.outputSections) {
323490
if (!(osec->flags & SHF_ALLOC))

lld/ELF/OutputSections.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,8 @@ static bool canMergeToProgbits(Ctx &ctx, unsigned type) {
9191
return type == SHT_NOBITS || type == SHT_PROGBITS || type == SHT_INIT_ARRAY ||
9292
type == SHT_PREINIT_ARRAY || type == SHT_FINI_ARRAY ||
9393
type == SHT_NOTE ||
94-
(type == SHT_X86_64_UNWIND && ctx.arg.emachine == EM_X86_64);
94+
(type == SHT_X86_64_UNWIND && ctx.arg.emachine == EM_X86_64) ||
95+
type == SHT_LLVM_CFI_JUMP_TABLE;
9596
}
9697

9798
// Record that isec will be placed in the OutputSection. isec does not become

lld/ELF/Relocations.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1674,7 +1674,7 @@ void RelocationScanner::scan(Relocs<RelTy> rels) {
16741674
// R_RISCV_PCREL_HI20, R_PPC64_ADDR64 and the branch-to-branch optimization.
16751675
if (ctx.arg.emachine == EM_RISCV ||
16761676
(ctx.arg.emachine == EM_PPC64 && sec->name == ".toc") ||
1677-
ctx.arg.branchToBranch)
1677+
ctx.arg.branchToBranch || sec->type == SHT_LLVM_CFI_JUMP_TABLE)
16781678
llvm::stable_sort(sec->relocs(),
16791679
[](const Relocation &lhs, const Relocation &rhs) {
16801680
return lhs.offset < rhs.offset;
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
// REQUIRES: x86
2+
// RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t.o
3+
// RUN: ld.lld %t.o -shared -o %t
4+
// RUN: llvm-objdump -d --show-all-symbols %t | FileCheck %s
5+
6+
// Mostly positive cases, except for f2.
7+
.section .text.jt1,"ax",@llvm_cfi_jump_table,8
8+
// Function fits.
9+
f1:
10+
jmp f1.cfi
11+
.balign 8, 0xcc
12+
13+
// Function too large.
14+
f2:
15+
jmp f2.cfi
16+
.balign 8, 0xcc
17+
18+
// Function too large, but may be placed at the end.
19+
// Because this causes the jump table to move, it is tested below.
20+
f3:
21+
jmp f3.cfi
22+
.balign 8, 0xcc
23+
24+
// Mostly negative cases, except for f4.
25+
.section .text.jt2,"ax",@llvm_cfi_jump_table,16
26+
// Function already moved into jt1.
27+
// CHECK: <f1a>:
28+
// CHECK-NEXT: jmp {{.*}} <f1.cfi>
29+
f1a:
30+
jmp f1.cfi
31+
.balign 16, 0xcc
32+
33+
// Function already moved into jt1.
34+
// CHECK: <f3a>:
35+
// CHECK-NEXT: jmp {{.*}} <f3.cfi>
36+
f3a:
37+
jmp f3.cfi
38+
.balign 16, 0xcc
39+
40+
// Function too large for jt1 but small enough for jt2.
41+
// CHECK: <f4>:
42+
// CHECK-NEXT: <f4.cfi>:
43+
// CHECK-NEXT: retq $0x4
44+
f4:
45+
jmp f4.cfi
46+
.balign 16, 0xcc
47+
48+
// Function too large for jt2.
49+
// CHECK: <f5>:
50+
// CHECK-NEXT: jmp {{.*}} <f5.cfi>
51+
f5:
52+
jmp f5.cfi
53+
.balign 16, 0xcc
54+
55+
// Branch target not at start of section.
56+
// CHECK: <f6>:
57+
// CHECK-NEXT: jmp {{.*}} <f6.cfi>
58+
f6:
59+
jmp f6.cfi
60+
.balign 16, 0xcc
61+
62+
// Overaligned section.
63+
// CHECK: <f7>:
64+
// CHECK-NEXT: jmp {{.*}} <f7.cfi>
65+
f7:
66+
jmp f7.cfi
67+
.balign 16, 0xcc
68+
69+
// Branch to IFUNC.
70+
// CHECK: <f8>:
71+
// CHECK-NEXT: jmp 0x[[IPLT:[0-9a-f]*]]
72+
f8:
73+
jmp f8.cfi
74+
.balign 16, 0xcc
75+
76+
// Unexpected number of relocations in entry.
77+
// CHECK: <f9>:
78+
// CHECK-NEXT: jmp {{.*}} <f9.cfi>
79+
// CHECK-NEXT: jmp {{.*}} <f9.cfi>
80+
f9:
81+
jmp f9.cfi
82+
jmp f9.cfi
83+
.balign 16, 0xcc
84+
85+
// Branch to different output section.
86+
f10:
87+
jmp f10.cfi
88+
.balign 16, 0xcc
89+
90+
// Branch via PLT to STB_GLOBAL symbol.
91+
// CHECK: <f11>:
92+
// CHECK-NEXT: jmp {{.*}} <f11.cfi@plt>
93+
f11:
94+
jmp f11.cfi
95+
.balign 16, 0xcc
96+
97+
// Invalid jumptable: entsize unset.
98+
// CHECK: <f12>:
99+
// CHECK-NEXT: jmp {{.*}} <f12.cfi>
100+
.section .text.jt3,"ax",@0x6fff4c0e
101+
f12:
102+
jmp f12.cfi
103+
.balign 8, 0xcc
104+
105+
// Invalid jumptable: size not a multiple of entsize.
106+
// CHECK: <f13>:
107+
// CHECK-NEXT: jmp {{.*}} <f13.cfi>
108+
.section .text.jt4,"ax",@llvm_cfi_jump_table,8
109+
f13:
110+
jmp f13.cfi
111+
112+
// CHECK: <f1>:
113+
// CHECK-NEXT: <f1.cfi>:
114+
// CHECK-NEXT: retq $0x1
115+
.section .text.f1,"ax",@progbits
116+
f1.cfi:
117+
ret $1
118+
119+
// CHECK: <f2>:
120+
// CHECK-NEXT: jmp {{.*}} <f2.cfi>
121+
.section .text.f2,"ax",@progbits
122+
f2.cfi:
123+
ret $2
124+
.zero 16
125+
126+
// CHECK: <f3>:
127+
// CHECK-NEXT: <f3.cfi>:
128+
// CHECK-NEXT: retq $0x3
129+
.section .text.f3,"ax",@progbits
130+
f3.cfi:
131+
ret $3
132+
.zero 16
133+
134+
.section .text.f4,"ax",@progbits
135+
f4.cfi:
136+
ret $4
137+
.zero 13
138+
139+
.section .text.f5,"ax",@progbits
140+
f5.cfi:
141+
ret $5
142+
.zero 14
143+
144+
.section .text.f6,"ax",@progbits
145+
nop
146+
f6.cfi:
147+
ret $6
148+
149+
.section .text.f7,"ax",@progbits
150+
.balign 32
151+
f7.cfi:
152+
ret $7
153+
154+
.section .text.f8,"ax",@progbits
155+
.type f8.cfi,@gnu_indirect_function
156+
f8.cfi:
157+
ret $8
158+
159+
.section .text.f9,"ax",@progbits
160+
f9.cfi:
161+
ret $9
162+
163+
.section foo,"ax",@progbits
164+
f10.cfi:
165+
ret $10
166+
167+
.section .text.f11,"ax",@progbits
168+
.globl f11.cfi
169+
f11.cfi:
170+
ret $11
171+
172+
.section .text.f12,"ax",@progbits
173+
f12.cfi:
174+
ret $12
175+
176+
.section .text.f13,"ax",@progbits
177+
f13.cfi:
178+
ret $13
179+
180+
// CHECK: <.iplt>:
181+
// CHECK-NEXT: [[IPLT]]:

0 commit comments

Comments
 (0)