Skip to content

Commit ad23391

Browse files
committed
[BOLT] Extend Inliner to work on functions with Pointer Autentication
The inliner uses DirectSP to check if a function has instructions that modify the SP. Exceptions are stack Push and Pop instructions. We can also allow pointer signing and authentication instructions. The inliner removes the Return instructions from the inlined functions. If it is a fused pointer-authentication-and-return (e.g. RETAA), we have to generate a new authentication instruction in place of the Return.
1 parent 96688d4 commit ad23391

File tree

4 files changed

+98
-0
lines changed

4 files changed

+98
-0
lines changed

bolt/include/bolt/Core/MCPlusBuilder.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -632,6 +632,12 @@ class MCPlusBuilder {
632632
return false;
633633
}
634634

635+
/// Generate the matching pointer authentication instruction from a fused
636+
/// pauth-and-return instruction.
637+
virtual void createMatchingAuth(const MCInst &AuthAndRet, MCInst &Auth) {
638+
llvm_unreachable("not implemented");
639+
}
640+
635641
/// Returns the register used as a return address. Returns std::nullopt if
636642
/// not applicable, such as reading the return address from a system register
637643
/// or from the stack.

bolt/lib/Passes/Inliner.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,13 @@ InliningInfo getInliningInfo(const BinaryFunction &BF) {
195195
if (BC.MIB->isPush(Inst) || BC.MIB->isPop(Inst))
196196
continue;
197197

198+
// Pointer signing and authenticatin instructions are used around
199+
// Push and Pop. These are also straightforward to handle.
200+
if (BC.isAArch64() &&
201+
(BC.MIB->isPSignOnLR(Inst) || BC.MIB->isPAuthOnLR(Inst) ||
202+
BC.MIB->isPAuthAndRet(Inst)))
203+
continue;
204+
198205
DirectSP |= BC.MIB->hasDefOfPhysReg(Inst, SPReg) ||
199206
BC.MIB->hasUseOfPhysReg(Inst, SPReg);
200207
}
@@ -338,6 +345,17 @@ Inliner::inlineCall(BinaryBasicBlock &CallerBB,
338345
BC.Ctx.get());
339346
}
340347

348+
// Handling fused authentication and return instructions (Armv8.3-A):
349+
// if the Return here is RETA(A|B), we have to keep the authentication
350+
// part.
351+
// RETAA -> AUTIASP + RET
352+
// RETAB -> AUTIBSP + RET
353+
if (BC.isAArch64() && BC.MIB->isPAuthAndRet(Inst)) {
354+
MCInst Auth;
355+
BC.MIB->createMatchingAuth(Inst, Auth);
356+
InsertII =
357+
std::next(InlinedBB->insertInstruction(InsertII, std::move(Auth)));
358+
}
341359
if (CSIsTailCall || (!MIB.isCall(Inst) && !MIB.isReturn(Inst))) {
342360
InsertII =
343361
std::next(InlinedBB->insertInstruction(InsertII, std::move(Inst)));

bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,35 @@ class AArch64MCPlusBuilder : public MCPlusBuilder {
266266
Inst.getOpcode() == AArch64::RETABSPPCr;
267267
}
268268

269+
void createMatchingAuth(const MCInst &AuthAndRet, MCInst &Auth) override {
270+
assert(isPAuthAndRet(AuthAndRet) &&
271+
"Not a fused pauth-and-return instruction");
272+
273+
Auth.clear();
274+
switch (AuthAndRet.getOpcode()) {
275+
case AArch64::RETAA:
276+
Auth.setOpcode(AArch64::AUTIASP);
277+
break;
278+
case AArch64::RETAB:
279+
Auth.setOpcode(AArch64::AUTIBSP);
280+
break;
281+
case AArch64::RETAASPPCi:
282+
Auth.setOpcode(AArch64::AUTIASPPCi);
283+
break;
284+
case AArch64::RETABSPPCi:
285+
Auth.setOpcode(AArch64::AUTIBSPPCi);
286+
break;
287+
case AArch64::RETAASPPCr:
288+
Auth.setOpcode(AArch64::AUTIASPPCr);
289+
break;
290+
case AArch64::RETABSPPCr:
291+
Auth.setOpcode(AArch64::AUTIBSPPCr);
292+
break;
293+
default:
294+
llvm_unreachable("Unhandled fused pauth-and-return instruction");
295+
}
296+
}
297+
269298
std::optional<MCPhysReg> getSignedReg(const MCInst &Inst) const override {
270299
switch (Inst.getOpcode()) {
271300
case AArch64::PACIA:
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# This test checks that inlining functions with fused pointer-auth-and-return
2+
# instructions is properly handled by BOLT.
3+
4+
# REQUIRES: system-linux
5+
6+
# RUN: llvm-mc -filetype=obj -triple aarch64-unknown-unknown -mattr=+v8.3a %s -o %t.o
7+
# RUN: %clang %cflags -O0 %t.o -o %t.exe -Wl,-q
8+
# RUN: llvm-bolt --inline-all --print-inline --print-only=_Z3barP1A \
9+
# RUN: %t.exe -o %t.bolt | FileCheck %s
10+
11+
# CHECK: BOLT-INFO: inlined 0 calls at 1 call sites in 2 iteration(s). Change in binary size: 8 bytes.
12+
# CHECK: Binary Function "_Z3barP1A" after inlining {
13+
# CHECK-NOT: bl _Z3fooP1A
14+
# CHECK: ldr x8, [x0]
15+
# CHECK-NEXT: ldr w0, [x8]
16+
# CHECK-NEXT: autiasp
17+
18+
.text
19+
.globl _Z3fooP1A
20+
.type _Z3fooP1A,@function
21+
_Z3fooP1A:
22+
paciasp
23+
ldr x8, [x0]
24+
ldr w0, [x8]
25+
retaa
26+
.size _Z3fooP1A, .-_Z3fooP1A
27+
28+
.globl _Z3barP1A
29+
.type _Z3barP1A,@function
30+
_Z3barP1A:
31+
stp x29, x30, [sp, #-16]!
32+
mov x29, sp
33+
bl _Z3fooP1A
34+
mul w0, w0, w0
35+
ldp x29, x30, [sp], #16
36+
ret
37+
.size _Z3barP1A, .-_Z3barP1A
38+
39+
.globl main
40+
.p2align 2
41+
.type main,@function
42+
main:
43+
mov w0, wzr
44+
ret
45+
.size main, .-main

0 commit comments

Comments
 (0)