Skip to content

Commit 8d06c89

Browse files
committed
Exclude JT pattern matching under assert failure
There are several patterns to implement jump table. Need to exclude the following patterns under assert during disassemble stage: (1) nop (2) sub (3) adrp adr ldr ldr ldrh ldrh ldrh adr adr adr add add add br br br Full jump table processing for AArch64 is still disabled.
1 parent 81ae668 commit 8d06c89

File tree

3 files changed

+296
-55
lines changed

3 files changed

+296
-55
lines changed

bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp

Lines changed: 169 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "bolt/Core/BinaryBasicBlock.h"
1919
#include "bolt/Core/BinaryFunction.h"
2020
#include "bolt/Core/MCPlusBuilder.h"
21+
#include "bolt/Utils/CommandLineOpts.h"
2122
#include "llvm/BinaryFormat/ELF.h"
2223
#include "llvm/MC/MCContext.h"
2324
#include "llvm/MC/MCFixupKindInfo.h"
@@ -213,6 +214,34 @@ class AArch64MCPlusBuilder : public MCPlusBuilder {
213214
Inst.getOpcode() == AArch64::ADDXrx64);
214215
}
215216

217+
bool isSUB(const MCInst &Inst) const override {
218+
const unsigned opcode = Inst.getOpcode();
219+
switch (opcode) {
220+
case AArch64::SUBSWri:
221+
case AArch64::SUBSWrr:
222+
case AArch64::SUBSWrs:
223+
case AArch64::SUBSWrx:
224+
case AArch64::SUBSXri:
225+
case AArch64::SUBSXrr:
226+
case AArch64::SUBSXrs:
227+
case AArch64::SUBSXrx:
228+
case AArch64::SUBSXrx64:
229+
case AArch64::SUBWri:
230+
case AArch64::SUBWrr:
231+
case AArch64::SUBWrs:
232+
case AArch64::SUBWrx:
233+
case AArch64::SUBXri:
234+
case AArch64::SUBXrr:
235+
case AArch64::SUBXrs:
236+
case AArch64::SUBXrx:
237+
case AArch64::SUBXrx64:
238+
return true;
239+
default:
240+
return false;
241+
}
242+
llvm_unreachable("");
243+
}
244+
216245
bool isLDRB(const MCInst &Inst) const {
217246
return (Inst.getOpcode() == AArch64::LDRBBpost ||
218247
Inst.getOpcode() == AArch64::LDRBBpre ||
@@ -652,13 +681,45 @@ class AArch64MCPlusBuilder : public MCPlusBuilder {
652681
/// # of this BB)
653682
/// br x0 # Indirect jump instruction
654683
///
684+
/// adrp + ldr pair instructions of JT
685+
/// adrp x3, :got:jump_table
686+
/// ldr x1, [x1, #value]
687+
/// ldrh w1, [x1, w3, uxtw #1]
688+
/// adr x3, 573ae4
689+
/// add x1, x3, w1, sxth #2
690+
/// br x1
691+
///
692+
/// lld/test/ELF/aarch64-adrp-ldr-got.s
693+
/// if .rodata and .text are sufficiently (<1M)
694+
/// close to each other so that the adrp + ldr pair can be relaxed to
695+
/// nop + adr.
696+
/// nop #
697+
/// adr x0, #6d8f8 # Get JT table address
698+
/// ldrh w0, [x0, w4, uxtw #1] # Loads JT entry
699+
/// adr x2, 1479b0 # Get PC first instruction for next BB
700+
/// add x0, x2, w0, sxth #2 # Finish building branch target
701+
/// # (entries in JT are relative to the end
702+
/// # of this BB)
703+
/// br x0 # Indirect jump instruction
704+
///
705+
/// sub + ldr pair instructions of JT, JT address on the stack and other BB
706+
/// sub x1, x29, #0x4, lsl #12
707+
/// ldr x1, [x1, #14352]
708+
/// ldrh w1, [x1, w3, uxtw #1]
709+
/// adr x3, 573ae4
710+
/// add x1, x3, w1, sxth #2
711+
/// br x1
655712
bool analyzeIndirectBranchFragment(
656713
const MCInst &Inst,
657714
DenseMap<const MCInst *, SmallVector<MCInst *, 4>> &UDChain,
658715
const MCExpr *&JumpTable, int64_t &Offset, int64_t &ScaleValue,
659716
MCInst *&PCRelBase) const {
660-
// Expect AArch64 BR
661-
assert(Inst.getOpcode() == AArch64::BR && "Unexpected opcode");
717+
718+
if (Inst.getOpcode() != AArch64::BR) {
719+
if (opts::Verbosity > 1)
720+
outs() << "BOLT-WARNING: Unexpected opcode\n";
721+
return false;
722+
}
662723

663724
// Match the indirect branch pattern for aarch64
664725
SmallVector<MCInst *, 4> &UsesRoot = UDChain[&Inst];
@@ -697,8 +758,12 @@ class AArch64MCPlusBuilder : public MCPlusBuilder {
697758
// Parsed as ADDXrs reg:x8 reg:x8 reg:x12 imm:0
698759
return false;
699760
}
700-
assert(DefAdd->getOpcode() == AArch64::ADDXrx &&
701-
"Failed to match indirect branch!");
761+
762+
if (DefAdd->getOpcode() != AArch64::ADDXrx) {
763+
if (opts::Verbosity > 1)
764+
outs() << "BOLT-WARNING: Failed to match indirect branch!\n";
765+
return false;
766+
}
702767

703768
// Validate ADD operands
704769
int64_t OperandExtension = DefAdd->getOperand(3).getImm();
@@ -737,61 +802,129 @@ class AArch64MCPlusBuilder : public MCPlusBuilder {
737802
// This was observed in the wild in HHVM code (dispatchImpl).
738803
return false;
739804
}
805+
740806
MCInst *DefBaseAddr = UsesAdd[1];
741-
assert(DefBaseAddr->getOpcode() == AArch64::ADR &&
742-
"Failed to match indirect branch pattern! (fragment 3)");
807+
if (DefBaseAddr->getOpcode() != AArch64::ADR) {
808+
if (opts::Verbosity > 1)
809+
outs() << "BOLT-WARNING: "
810+
<< "Failed to match indirect branch pattern! (fragment 3)\n";
811+
return false;
812+
}
743813

744814
PCRelBase = DefBaseAddr;
745815
// Match LOAD to load the jump table (relative) target
746816
const MCInst *DefLoad = UsesAdd[2];
747-
assert(mayLoad(*DefLoad) &&
748-
"Failed to match indirect branch load pattern! (1)");
749-
assert((ScaleValue != 1LL || isLDRB(*DefLoad)) &&
750-
"Failed to match indirect branch load pattern! (2)");
751-
assert((ScaleValue != 2LL || isLDRH(*DefLoad)) &&
752-
"Failed to match indirect branch load pattern! (3)");
817+
if (!mayLoad(*DefLoad)) {
818+
if (opts::Verbosity > 1)
819+
outs() << "BOLT-WARNING: "
820+
<< "Failed to match indirect branch load pattern! (1)\n";
821+
return false;
822+
}
823+
824+
if (isLDRB(*DefLoad) && ScaleValue != 1LL) {
825+
if (opts::Verbosity > 1)
826+
outs() << "BOLT-WARNING: "
827+
<< "Failed to match indirect branch load pattern! (2)\n";
828+
return false;
829+
} else if (isLDRH(*DefLoad) && ScaleValue != 2LL) {
830+
if (opts::Verbosity > 1)
831+
outs() << "BOLT-WARNING: "
832+
<< "Failed to match indirect branch load pattern! (3)\n";
833+
return false;
834+
} else if (isLDRW(*DefLoad) && ScaleValue != 4LL) {
835+
if (opts::Verbosity > 1)
836+
outs() << "BOLT-WARNING: "
837+
<< "Failed to match indirect branch load pattern! (4)\n";
838+
return false;
839+
}
753840

754841
// Match ADD that calculates the JumpTable Base Address (not the offset)
755842
SmallVector<MCInst *, 4> &UsesLoad = UDChain[DefLoad];
756-
const MCInst *DefJTBaseAdd = UsesLoad[1];
843+
const MCInst *DefJTPageBias = UsesLoad[1];
757844
MCPhysReg From, To;
758-
if (DefJTBaseAdd == nullptr || isLoadFromStack(*DefJTBaseAdd) ||
759-
isRegToRegMove(*DefJTBaseAdd, From, To)) {
845+
JumpTable = nullptr;
846+
if (DefJTPageBias == nullptr || isLoadFromStack(*DefJTPageBias) ||
847+
isRegToRegMove(*DefJTPageBias, From, To)) {
760848
// Sometimes base address may have been defined in another basic block
761849
// (hoisted). Return with no jump table info.
762-
JumpTable = nullptr;
763850
return true;
764851
}
765852

766-
if (DefJTBaseAdd->getOpcode() == AArch64::ADR) {
853+
if (isAddXri(*DefJTPageBias)) {
854+
if (DefJTPageBias->getOperand(2).isImm())
855+
Offset = DefJTPageBias->getOperand(2).getImm();
856+
SmallVector<MCInst *, 4> &UsesJTBaseAdd = UDChain[DefJTPageBias];
857+
const MCInst *DefJTBasePage = UsesJTBaseAdd[1];
858+
if (DefJTBasePage == nullptr || isLoadFromStack(*DefJTBasePage)) {
859+
return true;
860+
}
861+
862+
if (DefJTBasePage->getOpcode() != AArch64::ADRP) {
863+
if (opts::Verbosity > 1)
864+
outs() << "BOLT-WARNING: "
865+
<< "Failed to match jump table base page pattern! (2)\n";
866+
return false;
867+
}
868+
869+
if (DefJTBasePage->getOperand(1).isExpr())
870+
JumpTable = DefJTBasePage->getOperand(1).getExpr();
871+
return true;
872+
} else if (isADR(*DefJTPageBias)) {
767873
// TODO: Handle the pattern where there is no adrp/add pair.
768874
// It also occurs when the binary is static.
769-
// adr x13, 0x215a18 <_nl_value_type_LC_COLLATE+0x50>
875+
// nop
876+
// *adr x13, 0x215a18 <_nl_value_type_LC_COLLATE+0x50>
770877
// ldrh w13, [x13, w12, uxtw #1]
771878
// adr x12, 0x247b30 <__gettextparse+0x5b0>
772879
// add x13, x12, w13, sxth #2
773880
// br x13
774-
errs() << "BOLT-WARNING: Failed to match indirect branch: "
775-
"nop/adr instead of adrp/add \n";
776-
return false;
777-
}
881+
SmallVector<MCInst *, 4> &UsesJTNop = UDChain[DefJTPageBias];
882+
if (UsesJTNop.size() == 1 && UsesJTNop[0] != nullptr) {
883+
if (opts::Verbosity > 1)
884+
outs() << "BOLT-WARNING: "
885+
<< "Failed to match jump table pattern! (2)\n";
886+
return false;
887+
}
778888

779-
assert(DefJTBaseAdd->getOpcode() == AArch64::ADDXri &&
780-
"Failed to match jump table base address pattern! (1)");
889+
if (DefJTPageBias->getOperand(1).isExpr()) {
890+
JumpTable = DefJTPageBias->getOperand(1).getExpr();
891+
return true;
892+
}
893+
} else if (mayLoad(*DefJTPageBias)) {
894+
if (isLoadFromStack(*DefJTPageBias))
895+
return true;
781896

782-
if (DefJTBaseAdd->getOperand(2).isImm())
783-
Offset = DefJTBaseAdd->getOperand(2).getImm();
784-
SmallVector<MCInst *, 4> &UsesJTBaseAdd = UDChain[DefJTBaseAdd];
785-
const MCInst *DefJTBasePage = UsesJTBaseAdd[1];
786-
if (DefJTBasePage == nullptr || isLoadFromStack(*DefJTBasePage)) {
787-
JumpTable = nullptr;
788-
return true;
897+
SmallVector<MCInst *, 4> &UsesJTBase = UDChain[DefJTPageBias];
898+
const MCInst *DefJTBasePage = UsesJTBase[1];
899+
if (DefJTBasePage == nullptr)
900+
return true;
901+
if (DefJTBasePage->getOpcode() == AArch64::ADRP) {
902+
// test jmp-table-pattern-matching.s (3)
903+
if (DefJTBasePage->getOperand(1).isExpr())
904+
JumpTable = DefJTBasePage->getOperand(1).getExpr();
905+
return true;
906+
} else {
907+
// Base address may have been defined in another basic block
908+
// and sub instruction can be used to get base page address
909+
// jmp-table-pattern-matching.s (2)
910+
if (isSUB(*DefJTBasePage)) {
911+
for (const MCOperand &Operand : useOperands(*DefJTBasePage)) {
912+
if (!Operand.isReg())
913+
continue;
914+
const unsigned Reg = Operand.getReg();
915+
if (Reg == AArch64::SP || Reg == AArch64::WSP ||
916+
Reg == AArch64::FP || Reg == AArch64::W29)
917+
return true;
918+
}
919+
}
920+
}
789921
}
790-
assert(DefJTBasePage->getOpcode() == AArch64::ADRP &&
791-
"Failed to match jump table base page pattern! (2)");
792-
if (DefJTBasePage->getOperand(1).isExpr())
793-
JumpTable = DefJTBasePage->getOperand(1).getExpr();
794-
return true;
922+
923+
if (opts::Verbosity > 1)
924+
outs() << "BOLT-WARNING: "
925+
<< "Failed to match jump table pattern! (4)\n";
926+
927+
return false;
795928
}
796929

797930
DenseMap<const MCInst *, SmallVector<MCInst *, 4>>
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
## This test checks that disassemble stage works properly
2+
## JT with indirect branch
3+
## 1) nop + adr pair instructions
4+
## 2) sub + ldr pair instructions
5+
## 3) adrp + ldr pair instructions
6+
7+
# REQUIRES: system-linux
8+
9+
# RUN: rm -rf %t && split-file %s %t
10+
11+
## Prepare binary (1)
12+
# RUN: llvm-mc -filetype=obj -triple aarch64-unknown-unknown %t/jt_nop_adr.s \
13+
# RUN: -o %t/jt_nop_adr.o
14+
# RUN: %clang %cflags --target=aarch64-unknown-linux %t/jt_nop_adr.o \
15+
# RUN: -Wl,-q -Wl,-z,now, -Wl,-T,%t/within-adr-range.t -o %t/jt_nop_adr.exe
16+
# RUN: llvm-objdump --no-show-raw-insn -d %t/jt_nop_adr.exe | FileCheck \
17+
# RUN: --check-prefix=JT-RELAXED %s
18+
19+
# JT-RELAXED: <_start>:
20+
# JT-RELAXED-NEXT: nop
21+
# JT-RELAXED-NEXT: adr {{.*}}x3
22+
23+
# RUN: llvm-bolt %t/jt_nop_adr.exe -o %t/jt_nop_adr.bolt -v 2 2>&1 | FileCheck %s
24+
# CHECK-NOT: Failed to match
25+
26+
## This linker script ensures that .rodata and .text are sufficiently (<1M)
27+
## close to each other so that the adrp + ldr pair can be relaxed to nop + adr.
28+
#--- within-adr-range.t
29+
SECTIONS {
30+
.rodata 0x1000: { *(.rodata) }
31+
.text 0x2000: { *(.text) }
32+
.rela.rodata : { *(.rela.rodata) }
33+
}
34+
35+
## Prepare binary (2)
36+
# RUN: llvm-mc -filetype=obj -triple aarch64-unknown-unknown %t/jt_sub_ldr.s \
37+
# RUN: -o %t/jt_sub_ldr.o
38+
# RUN: %clang %cflags --target=aarch64-unknown-linux %t/jt_sub_ldr.o \
39+
# RUN: -Wl,-q -Wl,-z,now -o %t/jt_sub_ldr.exe
40+
# RUN: llvm-objdump --no-show-raw-insn -d %t/jt_sub_ldr.exe | FileCheck \
41+
# RUN: --check-prefix=JT-SUB-LDR %s
42+
43+
# JT-SUB-LDR: <_start>:
44+
# JT-SUB-LDR-NEXT: sub
45+
# JT-SUB-LDR-NEXT: ldr
46+
47+
# RUN: llvm-bolt %t/jt_sub_ldr.exe -o %t/jt_sub_ldr.bolt -v 2 2>&1 | FileCheck \
48+
# RUN: --check-prefix=JT-BOLT-SUBLDR %s
49+
# JT-BOLT-SUBLDR-NOT: Failed to match
50+
51+
## Prepare binary (3)
52+
# RUN: llvm-mc -filetype=obj -triple aarch64-unknown-unknown %t/jt_adrp_ldr.s \
53+
# RUN: -o %t/jt_adrp_ldr.o
54+
# RUN: %clang %cflags --target=aarch64-unknown-linux %t/jt_adrp_ldr.o \
55+
# RUN: -Wl,-q -Wl,-z,now -Wl,--no-relax -o %t/jt_adrp_ldr.exe
56+
# RUN: llvm-objdump --no-show-raw-insn -d %t/jt_adrp_ldr.exe | FileCheck \
57+
# RUN: --check-prefix=JT-ADRP-LDR %s
58+
59+
# JT-ADRP-LDR: <_start>:
60+
# JT-ADRP-LDR-NEXT: adrp
61+
# JT-ADRP-LDR-NEXT: ldr
62+
63+
# RUN: llvm-bolt %t/jt_adrp_ldr.exe -o %t/jt_adrp_ldr.bolt -v 2 2>&1 | FileCheck \
64+
# RUN: --check-prefix=JT-BOLT-ADRP-LDR %s
65+
# JT-BOLT-ADRP-LDR-NOT: Failed to match
66+
67+
#--- jt_nop_adr.s
68+
.globl _start
69+
.type _start, %function
70+
_start:
71+
adrp x3, :got:jump_table
72+
ldr x3, [x3, #:got_lo12:jump_table]
73+
ldrh w3, [x3, x1, lsl #1]
74+
adr x1, test2_0
75+
add x3, x1, w3, sxth #2
76+
br x3
77+
test2_0:
78+
ret
79+
test2_1:
80+
ret
81+
82+
.section .rodata,"a",@progbits
83+
jump_table:
84+
.hword (test2_0-test2_0)>>2
85+
.hword (test2_1-test2_0)>>2
86+
87+
88+
#--- jt_sub_ldr.s
89+
.globl _start
90+
.type _start, %function
91+
_start:
92+
sub x1, x29, #0x4, lsl #12
93+
ldr x1, [x1, #14352]
94+
ldrh w1, [x1, w3, uxtw #1]
95+
adr x3, test2_0
96+
add x1, x3, w1, sxth #2
97+
br x1
98+
test2_0:
99+
ret
100+
test2_1:
101+
ret
102+
103+
.section .rodata,"a",@progbits
104+
jump_table:
105+
.hword (test2_0-test2_0)>>2
106+
.hword (test2_1-test2_0)>>2
107+
108+
109+
#--- jt_adrp_ldr.s
110+
.globl _start
111+
.type _start, %function
112+
_start:
113+
adrp x3, :got:jump_table
114+
ldr x3, [x3, #:got_lo12:jump_table]
115+
ldrh w3, [x3, x1, lsl #1]
116+
adr x1, test2_0
117+
add x3, x1, w3, sxth #2
118+
br x3
119+
test2_0:
120+
ret
121+
test2_1:
122+
ret
123+
124+
.section .rodata,"a",@progbits
125+
jump_table:
126+
.hword (test2_0-test2_0)>>2
127+
.hword (test2_1-test2_0)>>2

0 commit comments

Comments
 (0)