Skip to content

Commit b125532

Browse files
committed
[BOLT][BTI] Add needed BTIs in LongJmp or refuse to optimize binary
This patch adds BTI landing pads to ShortJmp/LongJmp targets in the LongJmp pass when optimizing BTI binaries. BOLT does not have the ability to add BTI to all types of functions. This patch aims to insert the landing pad where possible, and emit an error where it currently is not. BOLT cannot insert BTIs into several function "types", including: - ignored functions, - PLT functions, - other functions without a CFG. Additional context: In #161206, BOLT gained the ability to decode the .note.gnu.property section, and warn about lack of BTI support for BOLT. However, this warning is misleading: the emitted binary may not need extra BTI landing pads. With this patch, the emitted binary will be "BTI-safe".
1 parent 43eecd1 commit b125532

File tree

7 files changed

+138
-9
lines changed

7 files changed

+138
-9
lines changed

bolt/include/bolt/Core/BinaryBasicBlock.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -890,6 +890,8 @@ class BinaryBasicBlock {
890890
/// Needed by graph traits.
891891
BinaryFunction *getParent() const { return getFunction(); }
892892

893+
bool hasParent() const { return getFunction() != nullptr; }
894+
893895
/// Return true if the containing function is in CFG state.
894896
bool hasCFG() const;
895897

bolt/lib/Passes/LongJmp.cpp

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -469,8 +469,8 @@ uint64_t LongJmpPass::getSymbolAddress(const BinaryContext &BC,
469469
}
470470

471471
Error LongJmpPass::relaxStub(BinaryBasicBlock &StubBB, bool &Modified) {
472-
const BinaryFunction &Func = *StubBB.getFunction();
473-
const BinaryContext &BC = Func.getBinaryContext();
472+
BinaryFunction &Func = *StubBB.getFunction();
473+
BinaryContext &BC = Func.getBinaryContext();
474474
const int Bits = StubBits[&StubBB];
475475
// Already working with the largest range?
476476
if (Bits == static_cast<int>(BC.AsmInfo->getCodePointerSize() * 8))
@@ -483,11 +483,54 @@ Error LongJmpPass::relaxStub(BinaryBasicBlock &StubBB, bool &Modified) {
483483
~((1ULL << (RangeSingleInstr - 1)) - 1);
484484

485485
const MCSymbol *RealTargetSym = BC.MIB->getTargetSymbol(*StubBB.begin());
486-
const BinaryBasicBlock *TgtBB = Func.getBasicBlockForLabel(RealTargetSym);
486+
BinaryBasicBlock *TgtBB = Func.getBasicBlockForLabel(RealTargetSym);
487+
BinaryFunction *TargetFunction = BC.getFunctionForSymbol(RealTargetSym);
487488
uint64_t TgtAddress = getSymbolAddress(BC, RealTargetSym, TgtBB);
488489
uint64_t DotAddress = BBAddresses[&StubBB];
489490
uint64_t PCRelTgtAddress = DotAddress > TgtAddress ? DotAddress - TgtAddress
490491
: TgtAddress - DotAddress;
492+
493+
auto applyBTIFixup = [&](BinaryFunction *TargetFunction,
494+
BinaryBasicBlock *RealTgtBB) {
495+
// TODO: add support for editing each type, and remove errors.
496+
if (!TargetFunction && !RealTgtBB) {
497+
BC.errs() << "BOLT-ERROR: Cannot add BTI to function with symbol "
498+
<< RealTargetSym->getName() << "\n";
499+
exit(1);
500+
}
501+
if (TargetFunction && TargetFunction->isIgnored()) {
502+
BC.errs() << "BOLT-ERROR: Cannot add BTI landing pad to ignored function "
503+
<< TargetFunction->getPrintName() << "\n";
504+
exit(1);
505+
}
506+
if (TargetFunction && !TargetFunction->hasCFG()) {
507+
auto FirstII = TargetFunction->instrs().begin();
508+
MCInst FirstInst = FirstII->second;
509+
if (BC.MIB->isBTIVariantCoveringCall(FirstInst,
510+
*StubBB.getLastNonPseudoInstr()))
511+
return;
512+
BC.errs()
513+
<< "BOLT-ERROR: Cannot add BTI landing pad to function without CFG: "
514+
<< TargetFunction->getPrintName() << "\n";
515+
exit(1);
516+
}
517+
if (!RealTgtBB)
518+
// !RealTgtBB -> TargetFunction is not a nullptr
519+
RealTgtBB = &*TargetFunction->begin();
520+
if (RealTgtBB) {
521+
if (!RealTgtBB->hasParent()) {
522+
BC.errs() << "BOLT-ERROR: Cannot add BTI to block with no parent "
523+
"function. Targeted symbol: "
524+
<< RealTargetSym->getName() << "\n";
525+
exit(1);
526+
}
527+
// The BR is the last inst of the StubBB.
528+
BC.MIB->insertBTI(*RealTgtBB, *StubBB.getLastNonPseudoInstr());
529+
return;
530+
}
531+
BC.errs() << "BOLT-ERROR: unhandled case when applying BTI fixup\n";
532+
exit(1);
533+
};
491534
// If it fits in one instruction, do not relax
492535
if (!(PCRelTgtAddress & SingleInstrMask))
493536
return Error::success();
@@ -502,6 +545,8 @@ Error LongJmpPass::relaxStub(BinaryBasicBlock &StubBB, bool &Modified) {
502545
<< " RealTargetSym = " << RealTargetSym->getName()
503546
<< "\n");
504547
relaxStubToShortJmp(StubBB, RealTargetSym);
548+
if (BC.usesBTI())
549+
applyBTIFixup(TargetFunction, TgtBB);
505550
StubBits[&StubBB] = RangeShortJmp;
506551
Modified = true;
507552
return Error::success();
@@ -517,6 +562,8 @@ Error LongJmpPass::relaxStub(BinaryBasicBlock &StubBB, bool &Modified) {
517562
<< Twine::utohexstr(PCRelTgtAddress)
518563
<< " RealTargetSym = " << RealTargetSym->getName() << "\n");
519564
relaxStubToLongJmp(StubBB, RealTargetSym);
565+
if (BC.usesBTI())
566+
applyBTIFixup(TargetFunction, TgtBB);
520567
StubBits[&StubBB] = static_cast<int>(BC.AsmInfo->getCodePointerSize() * 8);
521568
Modified = true;
522569
return Error::success();

bolt/lib/Rewrite/GNUPropertyRewriter.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,7 @@ Error GNUPropertyRewriter::sectionInitializer() {
7575
if (BC.isAArch64()) {
7676
BC.setUsesBTI(FeaturesAcc & llvm::ELF::GNU_PROPERTY_AARCH64_FEATURE_1_BTI);
7777
if (BC.usesBTI())
78-
BC.outs() << "BOLT-WARNING: binary is using BTI. Optimized binary may be "
79-
"corrupted\n";
78+
BC.outs() << "BOLT-INFO: binary is using BTI.\n";
8079
}
8180

8281
return Error::success();

bolt/test/AArch64/bti-note.test

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
// This test checks that the GNUPropertyRewriter can decode the BTI feature flag.
2-
// It decodes an executable with BTI, and checks for the warning.
2+
// It decodes an executable with BTI, and checks for the message.
33

44
RUN: yaml2obj %p/Inputs/property-note-bti.yaml &> %t.exe
55

66
RUN: llvm-readelf -n %t.exe | FileCheck %s
77
CHECK: BTI
88

99
RUN: llvm-bolt %t.exe -o %t.exe.bolt | FileCheck %s -check-prefix=CHECK-BOLT
10-
CHECK-BOLT: BOLT-WARNING: binary is using BTI. Optimized binary may be corrupted
10+
CHECK-BOLT: BOLT-INFO: binary is using BTI.
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# This test checks the situation where LongJmp adds a stub targeting an ignored (skipped) function.
2+
# The problem is that by default BOLT cannot modify ignored functions, so it cannot add the needed BTI.
3+
4+
# Current behaviour is to emit an error.
5+
6+
# REQUIRES: system-linux, asserts
7+
8+
# RUN: llvm-mc -filetype=obj -triple aarch64-unknown-unknown \
9+
# RUN: -mattr=+bti -aarch64-mark-bti-property %s -o %t.o
10+
# RUN: %clang %cflags -O0 %t.o -o %t.exe -Wl,-q -Wl,-z,force-bti
11+
# RUN: not llvm-bolt %t.exe -o %t.bolt \
12+
# RUN: --align-text=0x10000000 --skip-funcs=far_away_func 2>&1 | FileCheck %s
13+
14+
# CHECK: BOLT-ERROR: Cannot add BTI landing pad to ignored function far_away_func
15+
16+
.section .text
17+
.global _start
18+
.global far_away_func
19+
20+
.align 4
21+
.global _start
22+
.type _start, %function
23+
_start:
24+
bti c
25+
bl far_away_func
26+
ret
27+
28+
.global far_away_func
29+
.type far_away_func, %function
30+
far_away_func:
31+
add x0, x0, #1
32+
ret
33+
34+
.reloc 0, R_AARCH64_NONE
35+

bolt/test/AArch64/long-jmp-bti.s

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# This test checks that BOLT can generate BTI landing pads for targets of stubs inserted in LongJmp.
2+
3+
# REQUIRES: system-linux
4+
5+
# RUN: %clang %s %cflags -Wl,-q -o %t -mbranch-protection=bti -Wl,-z,force-bti
6+
# RUN: link_fdata --no-lbr %s %t %t.fdata
7+
# RUN: llvm-bolt %t -o %t.bolt --data %t.fdata -split-functions \
8+
# RUN: --print-split --print-only foo --print-longjmp 2>&1 | FileCheck %s
9+
10+
#CHECK: BOLT-INFO: Starting stub-insertion pass
11+
#CHECK: Binary Function "foo" after long-jmp
12+
13+
#CHECK: cmp x0, #0x0
14+
#CHECK-NEXT: Successors: .LStub0
15+
16+
#CHECK: adrp x16, .Ltmp0
17+
#CHECK-NEXT: add x16, x16, :lo12:.Ltmp0
18+
#CHECK-NEXT: br x16 # UNKNOWN CONTROL FLOW
19+
20+
#CHECK: ------- HOT-COLD SPLIT POINT -------
21+
22+
#CHECK: bti c
23+
#CHECK-NEXT: mov x0, #0x2
24+
#CHECK-NEXT: ret
25+
26+
.text
27+
.globl foo
28+
.type foo, %function
29+
foo:
30+
.cfi_startproc
31+
.entry_bb:
32+
# FDATA: 1 foo #.entry_bb# 10
33+
cmp x0, #0
34+
b .Lcold_bb1
35+
.Lcold_bb1:
36+
mov x0, #2
37+
ret
38+
.cfi_endproc
39+
.size foo, .-foo
40+
41+
# empty space, so the splitting needs short stubs
42+
.data
43+
.space 0x8000000
44+
45+
## Force relocation mode.
46+
.reloc 0, R_AARCH64_NONE

bolt/test/AArch64/no-bti-note.test

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
// This test checks that the GNUPropertyRewriter can decode the BTI feature flag.
2-
// It decodes an executable without BTI, and checks for the warning.
2+
// It decodes an executable without BTI, and checks that the BTI message is not emitted.
33

44
RUN: yaml2obj %p/Inputs/property-note-nobti.yaml &> %t.exe
55

66
RUN: llvm-readelf -n %t.exe | FileCheck %s
77
CHECK-NOT: BTI
88

99
RUN: llvm-bolt %t.exe -o %t.exe.bolt | FileCheck %s -check-prefix=CHECK-BOLT
10-
CHECK-BOLT-NOT: BOLT-WARNING: binary is using BTI. Optimized binary may be corrupted
10+
CHECK-BOLT-NOT: BOLT-INFO: binary is using BTI.

0 commit comments

Comments
 (0)