Skip to content

Commit e665cf3

Browse files
authored
[BOLT] Fix handling of ambiguous jump table entries (#155291)
Jump tables may contain entries that point immediately past the end of their parent function. Normally, such entries are generated by the compiler as a result of builtin_unreachable() case. We used to replace those entries with a label belonging to their parent function assuming the destination doesn't matter if it's an undefined behavior. However, if such entry is at the end of the jump table, it could be a real function pointer, not a jump table entry. We rely on heuristics to detect such cases and can drop the trailing function pointer entries from the table. The problem presents when the "unreachable" ambiguous entry is followed by another ambiguous entry corresponding to the start of the parent function. In this case we accept pointers as entries and may incorrectly update the function pointer. The solution is to keep ambiguous "unreachable" jump table entries identical to the original input, i.e. point to the same function. This change does not affect CFG, but results in the entries being updated with the new function address if it gets relocated.
1 parent dc8596d commit e665cf3

File tree

4 files changed

+102
-7
lines changed

4 files changed

+102
-7
lines changed

bolt/lib/Core/BinaryBasicBlock.cpp

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,9 +103,18 @@ bool BinaryBasicBlock::validateSuccessorInvariants() {
103103
Valid &= (Sym == Function->getFunctionEndLabel() ||
104104
Sym == Function->getFunctionEndLabel(getFragmentNum()));
105105
if (!Valid) {
106-
BC.errs() << "BOLT-WARNING: Jump table contains illegal entry: "
107-
<< Sym->getName() << "\n";
106+
const BinaryFunction *TargetBF = BC.getFunctionForSymbol(Sym);
107+
if (TargetBF) {
108+
// It's possible for another function to be in the jump table entry
109+
// as a result of built-in unreachable.
110+
Valid = true;
111+
} else {
112+
BC.errs() << "BOLT-WARNING: Jump table contains illegal entry: "
113+
<< Sym->getName() << "\n";
114+
}
108115
}
116+
if (!Valid)
117+
break;
109118
}
110119
}
111120
} else {

bolt/lib/Core/BinaryFunction.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1959,7 +1959,9 @@ void BinaryFunction::postProcessJumpTables() {
19591959
return EntryAddress == Parent->getAddress() + Parent->getSize();
19601960
});
19611961
if (IsBuiltinUnreachable) {
1962-
MCSymbol *Label = getOrCreateLocalLabel(EntryAddress, true);
1962+
BinaryFunction *TargetBF = BC.getBinaryFunctionAtAddress(EntryAddress);
1963+
MCSymbol *Label = TargetBF ? TargetBF->getSymbol()
1964+
: getOrCreateLocalLabel(EntryAddress, true);
19631965
JT.Entries.push_back(Label);
19641966
continue;
19651967
}

bolt/lib/Passes/IndirectCallPromotion.cpp

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -261,10 +261,7 @@ IndirectCallPromotion::getCallTargets(BinaryBasicBlock &BB,
261261
for (size_t I = Range.first; I < Range.second; ++I, JI += JIAdj) {
262262
MCSymbol *Entry = JT->Entries[I];
263263
const BinaryBasicBlock *ToBB = BF.getBasicBlockForLabel(Entry);
264-
assert(ToBB || Entry == BF.getFunctionEndLabel() ||
265-
Entry == BF.getFunctionEndLabel(FragmentNum::cold()));
266-
if (Entry == BF.getFunctionEndLabel() ||
267-
Entry == BF.getFunctionEndLabel(FragmentNum::cold()))
264+
if (!ToBB)
268265
continue;
269266
const Location To(Entry);
270267
const BinaryBasicBlock::BinaryBranchInfo &BI = BB.getBranchInfo(*ToBB);
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
## Check that llvm-bolt correctly updates ambiguous jump table entries that
2+
## can correspond to either builtin_unreachable() or could be a pointer to
3+
## the next function.
4+
5+
# REQUIRES: system-linux
6+
7+
# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown %s -o %t.o
8+
# RUN: %clang %cflags %t.o -o %t.exe -no-pie -Wl,-q
9+
10+
# RUN: llvm-bolt %t.exe --print-normalized --print-only=foo -o %t.out \
11+
# RUN: 2>&1 | FileCheck %s
12+
13+
14+
15+
.text
16+
.globl _start
17+
.type _start, %function
18+
_start:
19+
.cfi_startproc
20+
call foo
21+
ret
22+
.cfi_endproc
23+
.size _start, .-_start
24+
25+
.globl foo
26+
.type foo, %function
27+
foo:
28+
.cfi_startproc
29+
.LBB00:
30+
movq 0x8(%rdi), %rdi
31+
movzbl 0x1(%rdi), %eax
32+
.LBB00_br:
33+
jmpq *"JUMP_TABLE/foo.0"(,%rax,8)
34+
# CHECK: jmpq {{.*}} # JUMPTABLE
35+
# CHECK-NEXT: Successors: {{.*}}, {{.*}}, {{.*}}, {{.*}}, {{.*}}
36+
37+
.Ltmp87085:
38+
xorl %eax, %eax
39+
retq
40+
41+
.Ltmp87086:
42+
cmpb $0x0, 0x8(%rdi)
43+
setne %al
44+
retq
45+
46+
.Ltmp87088:
47+
movb $0x1, %al
48+
retq
49+
50+
.Ltmp87087:
51+
movzbl 0x14(%rdi), %eax
52+
andb $0x2, %al
53+
shrb %al
54+
retq
55+
56+
.cfi_endproc
57+
.size foo, .-foo
58+
59+
.globl bar
60+
.type bar, %function
61+
bar:
62+
.cfi_startproc
63+
ret
64+
.cfi_endproc
65+
.size bar, .-bar
66+
67+
# Jump tables
68+
.section .rodata
69+
.global jump_table
70+
jump_table:
71+
"JUMP_TABLE/foo.0":
72+
.quad bar
73+
.quad .Ltmp87085
74+
.quad bar
75+
.quad .Ltmp87086
76+
.quad .Ltmp87087
77+
.quad .LBB00
78+
.quad .Ltmp87088
79+
.quad bar
80+
.quad .LBB00
81+
82+
# CHECK: Jump table {{.*}} for function foo
83+
# CHECK-NEXT: 0x{{.*}} : bar
84+
# CHECK-NEXT: 0x{{.*}} :
85+
# CHECK-NEXT: 0x{{.*}} : bar
86+
# CHECK-NEXT: 0x{{.*}} :
87+
# CHECK-NEXT: 0x{{.*}} :

0 commit comments

Comments
 (0)