Skip to content

Commit 3af1146

Browse files
committed
[AArch64][PAC] Combine signing with address materialization
In pauthtest ABI, it is common to store a pointer signed with address diversity to a heap-allocated object (namely, storing a signed pointer to VTable as part of new object construction). This patch tries to prevent introducing a signing oracle by combining pointer materialization and its (re)signing into a single pseudo instruction which is not expanded until AsmPrinter, if possible. One of the typical patterns is materializing an unsigned pointer with `MOVaddr` pseudo and then signing it with `PAC[ID][AB]` instruction, which can be moved far away from `MOVaddr` by one of the passes in the machine pipeline. As the storage address is not a `Constant` value, one cannot simply emit a `ptrauth` constant in the frontend, which would be selected into `MOVaddrPAC` pseudo. Another pattern is fetching a pointer to VTable from a signed GOT entry using `LOADgotAUTH` pseudo, authenticating and checking it, and then re-signing after adding an offset. This commit adds an instruction insertion hook for `PAC[ID][AB]` which detects the above patterns and replaces it either with `MOVaddrPAC` or `LOADgotPAC` instruction.
1 parent cf9b917 commit 3af1146

File tree

7 files changed

+254
-77
lines changed

7 files changed

+254
-77
lines changed

llvm/lib/Target/AArch64/AArch64ISelLowering.cpp

Lines changed: 73 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3101,10 +3101,13 @@ AArch64TargetLowering::EmitGetSMESaveSize(MachineInstr &MI,
31013101
return BB;
31023102
}
31033103

3104-
// Helper function to find the instruction that defined a virtual register.
3105-
// If unable to find such instruction, returns nullptr.
3106-
static const MachineInstr *stripVRegCopies(const MachineRegisterInfo &MRI,
3107-
Register Reg) {
3104+
// Helper function to find the instruction that defined a virtual register,
3105+
// stripping and accumulating optional offset.
3106+
// If unable to find such instruction, returns nullptr (Offset is unspecified).
3107+
static const MachineInstr *
3108+
stripAndAccumulateOffset(const MachineRegisterInfo &MRI, Register Reg,
3109+
int64_t &Offset) {
3110+
Offset = 0;
31083111
while (Reg.isVirtual()) {
31093112
MachineInstr *DefMI = MRI.getVRegDef(Reg);
31103113
assert(DefMI && "Virtual register definition not found");
@@ -3122,7 +3125,17 @@ static const MachineInstr *stripVRegCopies(const MachineRegisterInfo &MRI,
31223125
continue;
31233126
}
31243127

3125-
return DefMI;
3128+
// If this is neither a copy, nor inc/dec instruction, we are done.
3129+
if (Opcode != AArch64::ADDXri && Opcode != AArch64::SUBXri)
3130+
return DefMI;
3131+
// Inc/dec with shifted immediates are not handled.
3132+
if (DefMI->getOperand(3).getImm() != 0)
3133+
return DefMI;
3134+
3135+
int64_t Imm = DefMI->getOperand(2).getImm();
3136+
Offset += (Opcode == AArch64::ADDXri) ? Imm : -Imm;
3137+
3138+
Reg = DefMI->getOperand(1).getReg();
31263139
}
31273140
return nullptr;
31283141
}
@@ -3138,8 +3151,13 @@ void AArch64TargetLowering::fixupPtrauthDiscriminator(
31383151
int64_t IntDisc = IntDiscOp.getImm();
31393152
assert(IntDisc == 0 && "Blend components are already expanded");
31403153

3141-
const MachineInstr *DiscMI = stripVRegCopies(MRI, AddrDisc);
3142-
if (DiscMI) {
3154+
int64_t Offset = 0;
3155+
const MachineInstr *DiscMI = stripAndAccumulateOffset(MRI, AddrDisc, Offset);
3156+
3157+
// The result of any recognized discriminator computation may be copied, but
3158+
// without adding any offset. Nevertheless, perform the remaining fix-ups
3159+
// even on an opaque, pre-computed discriminator.
3160+
if (DiscMI && Offset == 0) {
31433161
switch (DiscMI->getOpcode()) {
31443162
case AArch64::MOVKXi:
31453163
// blend(addr, imm) which is lowered as "MOVK addr, #imm, #48".
@@ -3178,6 +3196,53 @@ void AArch64TargetLowering::fixupPtrauthDiscriminator(
31783196
IntDiscOp.setImm(IntDisc);
31793197
}
31803198

3199+
MachineBasicBlock *
3200+
AArch64TargetLowering::tryRewritingPAC(MachineInstr &MI,
3201+
MachineBasicBlock *BB) const {
3202+
const TargetInstrInfo *TII = Subtarget->getInstrInfo();
3203+
MachineRegisterInfo &MRI = MI.getMF()->getRegInfo();
3204+
const DebugLoc &DL = MI.getDebugLoc();
3205+
3206+
Register Val = MI.getOperand(1).getReg();
3207+
unsigned Key = MI.getOperand(2).getImm();
3208+
int64_t IntDisc = MI.getOperand(3).getImm();
3209+
Register AddrDisc = MI.getOperand(4).getReg();
3210+
3211+
// Try to find a known address-setting instruction, accumulating the offset
3212+
// along the way. If no known pattern is found, keep everything as-is.
3213+
3214+
int64_t AddrOffset = 0;
3215+
const MachineInstr *AddrDefInstr =
3216+
stripAndAccumulateOffset(MRI, Val, AddrOffset);
3217+
if (!AddrDefInstr)
3218+
return BB;
3219+
3220+
unsigned NewOpcode;
3221+
if (AddrDefInstr->getOpcode() == AArch64::LOADgotAUTH)
3222+
NewOpcode = AArch64::LOADgotPAC;
3223+
else if (AddrDefInstr->getOpcode() == AArch64::MOVaddr)
3224+
NewOpcode = AArch64::MOVaddrPAC;
3225+
else
3226+
return BB; // Unknown opcode.
3227+
3228+
const MachineOperand &AddrOp = AddrDefInstr->getOperand(1);
3229+
unsigned TargetFlags = AddrOp.getTargetFlags() & ~AArch64II::MO_PAGE;
3230+
const GlobalValue *GV = AddrOp.getGlobal();
3231+
AddrOffset += AddrOp.getOffset();
3232+
3233+
BuildMI(*BB, MI, DL, TII->get(NewOpcode))
3234+
.addGlobalAddress(GV, AddrOffset, TargetFlags)
3235+
.addImm(Key)
3236+
.addReg(AddrDisc)
3237+
.addImm(IntDisc);
3238+
3239+
BuildMI(*BB, MI, DL, TII->get(AArch64::COPY), MI.getOperand(0).getReg())
3240+
.addReg(AArch64::X16);
3241+
3242+
MI.removeFromParent();
3243+
return BB;
3244+
}
3245+
31813246
MachineBasicBlock *AArch64TargetLowering::EmitInstrWithCustomInserter(
31823247
MachineInstr &MI, MachineBasicBlock *BB) const {
31833248

@@ -3288,7 +3353,7 @@ MachineBasicBlock *AArch64TargetLowering::EmitInstrWithCustomInserter(
32883353
case AArch64::PAC:
32893354
fixupPtrauthDiscriminator(MI, BB, MI.getOperand(3), MI.getOperand(4),
32903355
&AArch64::GPR64noipRegClass);
3291-
return BB;
3356+
return tryRewritingPAC(MI, BB);
32923357
case AArch64::AUTPAC:
32933358
fixupPtrauthDiscriminator(MI, BB, MI.getOperand(1), MI.getOperand(2),
32943359
&AArch64::GPR64noipRegClass);

llvm/lib/Target/AArch64/AArch64ISelLowering.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,9 @@ class AArch64TargetLowering : public TargetLowering {
189189
MachineOperand &AddrDiscOp,
190190
const TargetRegisterClass *AddrDiscRC) const;
191191

192+
MachineBasicBlock *tryRewritingPAC(MachineInstr &MI,
193+
MachineBasicBlock *BB) const;
194+
192195
MachineBasicBlock *
193196
EmitInstrWithCustomInserter(MachineInstr &MI,
194197
MachineBasicBlock *MBB) const override;

llvm/lib/Target/AArch64/AArch64InstrInfo.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2218,6 +2218,9 @@ let Predicates = [HasPAuth] in {
22182218

22192219
def LOADgotAUTH : Pseudo<(outs GPR64common:$dst), (ins i64imm:$addr), []>,
22202220
Sched<[WriteI, ReadI]> {
2221+
// Make it possible to eliminate dead instruction after folding it
2222+
// into LOADgotPAC.
2223+
let hasSideEffects = 0;
22212224
let Defs = [X16,X17,NZCV];
22222225
let Size = 44;
22232226
}

llvm/test/CodeGen/AArch64/GlobalISel/ptrauth-constant-in-code.ll

Lines changed: 88 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -82,25 +82,50 @@ define ptr @foo() {
8282

8383
; RUN: llc < finalize-isel.ll -mtriple aarch64-elf -mattr=+pauth -global-isel=1 \
8484
; RUN: -verify-machineinstrs -global-isel-abort=1 -stop-after=finalize-isel | \
85-
; RUN: FileCheck --check-prefixes=ISEL,ISEL-ELF %s
85+
; RUN: FileCheck --check-prefixes=ISEL-MIR,ISEL-MIR-ELF %s
8686
; RUN: llc < finalize-isel.ll -mtriple arm64-apple-ios -mattr=+pauth -global-isel=1 \
8787
; RUN: -verify-machineinstrs -global-isel-abort=1 -stop-after=finalize-isel | \
88-
; RUN: FileCheck --check-prefixes=ISEL %s
88+
; RUN: FileCheck --check-prefixes=ISEL-MIR %s
89+
; RUN: llc < finalize-isel.ll -mtriple aarch64-elf -mattr=+pauth -global-isel=1 \
90+
; RUN: -verify-machineinstrs -global-isel-abort=1 -asm-verbose=0 | \
91+
; RUN: FileCheck --check-prefixes=ISEL-ASM,ISEL-ASM-ELF %s
92+
; RUN: llc < finalize-isel.ll -mtriple arm64-apple-ios -mattr=+pauth -global-isel=1 \
93+
; RUN: -verify-machineinstrs -global-isel-abort=1 -asm-verbose=0 | \
94+
; RUN: FileCheck --check-prefixes=ISEL-ASM,ISEL-ASM-MACHO %s
8995

9096
@const_table_local = dso_local constant [3 x ptr] [ptr null, ptr null, ptr null]
9197
@const_table_got = constant [3 x ptr] [ptr null, ptr null, ptr null]
9298

99+
; Test that after post-processing in finalize-isel, MOVaddrPAC (or LOADgotPAC,
100+
; respectively) has both $AddrDisc and $Disc operands set. MOVaddr (or LOADgotAUTH,
101+
; respectively) and MOVKXi are not used anymore and are dead-code-eliminated
102+
; by the later passes.
103+
93104
define void @store_signed_const_local(ptr %dest) {
94-
; ISEL-LABEL: name: store_signed_const_local
95-
; ISEL: body:
96-
; ISEL: %0:gpr64common = COPY $x0
97-
; ISEL-NEXT: %10:gpr64common = MOVaddr target-flags(aarch64-page) @const_table_local + 8, target-flags(aarch64-pageoff, aarch64-nc) @const_table_local + 8
98-
; ISEL-NEXT: %2:gpr64noip = MOVKXi %0, 1234
99-
; ISEL-NEXT: %15:gpr64noip = COPY %0
100-
; ISEL-NEXT: %4:gpr64 = PAC %10, 2, 1234, %15, implicit-def dead $x16, implicit-def dead $x17
101-
; ISEL-NEXT: %14:gpr64 = COPY %4
102-
; ISEL-NEXT: STRXui %14, %0, 0 :: (store (p0) into %ir.dest)
103-
; ISEL-NEXT: RET_ReallyLR
105+
; ISEL-MIR-LABEL: name: store_signed_const_local
106+
; ISEL-MIR: body:
107+
; ISEL-MIR: %0:gpr64common = COPY $x0
108+
; ISEL-MIR-NEXT: %10:gpr64common = MOVaddr target-flags(aarch64-page) @const_table_local + 8, target-flags(aarch64-pageoff, aarch64-nc) @const_table_local + 8
109+
; ISEL-MIR-NEXT: %2:gpr64noip = MOVKXi %0, 1234, 48
110+
; ISEL-MIR-NEXT: %15:gpr64noip = COPY %0
111+
; ISEL-MIR-NEXT: MOVaddrPAC @const_table_local + 8, 2, %15, 1234, implicit-def $x16, implicit-def $x17
112+
; ISEL-MIR-NEXT: %4:gpr64 = COPY $x16
113+
; ISEL-MIR-NEXT: %14:gpr64 = COPY %4
114+
; ISEL-MIR-NEXT: STRXui %14, %0, 0 :: (store (p0) into %ir.dest)
115+
; ISEL-MIR-NEXT: RET_ReallyLR
116+
;
117+
; ISEL-ASM-LABEL: store_signed_const_local:
118+
; ISEL-ASM-NEXT: .cfi_startproc
119+
; ISEL-ASM-ELF-NEXT: adrp x16, const_table_local
120+
; ISEL-ASM-ELF-NEXT: add x16, x16, :lo12:const_table_local
121+
; ISEL-ASM-MACHO-NEXT: adrp x16, _const_table_local@PAGE
122+
; ISEL-ASM-MACHO-NEXT: add x16, x16, _const_table_local@PAGEOFF
123+
; ISEL-ASM-NEXT: add x16, x16, #8
124+
; ISEL-ASM-NEXT: mov x17, x0
125+
; ISEL-ASM-NEXT: movk x17, #1234, lsl #48
126+
; ISEL-ASM-NEXT: pacda x16, x17
127+
; ISEL-ASM-NEXT: str x16, [x0]
128+
; ISEL-ASM-NEXT: ret
104129
%dest.i = ptrtoint ptr %dest to i64
105130
%discr = call i64 @llvm.ptrauth.blend(i64 %dest.i, i64 1234)
106131
%signed.i = call i64 @llvm.ptrauth.sign(i64 ptrtoint (ptr getelementptr ([2 x ptr], ptr @const_table_local, i32 0, i32 1) to i64), i32 2, i64 %discr)
@@ -110,17 +135,37 @@ define void @store_signed_const_local(ptr %dest) {
110135
}
111136

112137
define void @store_signed_const_got(ptr %dest) {
113-
; ISEL-ELF-LABEL: name: store_signed_const_got
114-
; ISEL-ELF: body:
115-
; ISEL-ELF: %0:gpr64common = COPY $x0
116-
; ISEL-ELF-NEXT: %7:gpr64common = LOADgotAUTH target-flags(aarch64-got) @const_table_got
117-
; ISEL-ELF-NEXT: %6:gpr64common = ADDXri %7, 8, 0
118-
; ISEL-ELF-NEXT: %2:gpr64noip = MOVKXi %0, 1234
119-
; ISEL-ELF-NEXT: %12:gpr64noip = COPY %0
120-
; ISEL-ELF-NEXT: %4:gpr64 = PAC %6, 2, 1234, %12, implicit-def dead $x16, implicit-def dead $x17
121-
; ISEL-ELF-NEXT: %10:gpr64 = COPY %4
122-
; ISEL-ELF-NEXT: STRXui %10, %0, 0 :: (store (p0) into %ir.dest)
123-
; ISEL-ELF-NEXT: RET_ReallyLR
138+
; ISEL-MIR-ELF-LABEL: name: store_signed_const_got
139+
; ISEL-MIR-ELF: body:
140+
; ISEL-MIR-ELF: %0:gpr64common = COPY $x0
141+
; ISEL-MIR-ELF-NEXT: %7:gpr64common = LOADgotAUTH target-flags(aarch64-got) @const_table_got
142+
; ISEL-MIR-ELF-NEXT: %6:gpr64common = ADDXri %7, 8, 0
143+
; ISEL-MIR-ELF-NEXT: %2:gpr64noip = MOVKXi %0, 1234, 48
144+
; ISEL-MIR-ELF-NEXT: %12:gpr64noip = COPY %0
145+
; ISEL-MIR-ELF-NEXT: LOADgotPAC target-flags(aarch64-got) @const_table_got + 8, 2, %12, 1234, implicit-def $x16, implicit-def $x17, implicit-def $nzcv
146+
; ISEL-MIR-ELF-NEXT: %4:gpr64 = COPY $x16
147+
; ISEL-MIR-ELF-NEXT: %10:gpr64 = COPY %4
148+
; ISEL-MIR-ELF-NEXT: STRXui %10, %0, 0 :: (store (p0) into %ir.dest)
149+
; ISEL-MIR-ELF-NEXT: RET_ReallyLR
150+
;
151+
; ISEL-ASM-ELF-LABEL: store_signed_const_got:
152+
; ISEL-ASM-ELF-NEXT: .cfi_startproc
153+
; ISEL-ASM-ELF-NEXT: adrp x17, :got_auth:const_table_got
154+
; ISEL-ASM-ELF-NEXT: add x17, x17, :got_auth_lo12:const_table_got
155+
; ISEL-ASM-ELF-NEXT: ldr x16, [x17]
156+
; ISEL-ASM-ELF-NEXT: autda x16, x17
157+
; ISEL-ASM-ELF-NEXT: mov x17, x16
158+
; ISEL-ASM-ELF-NEXT: xpacd x17
159+
; ISEL-ASM-ELF-NEXT: cmp x16, x17
160+
; ISEL-ASM-ELF-NEXT: b.eq .Lauth_success_0
161+
; ISEL-ASM-ELF-NEXT: brk #0xc472
162+
; ISEL-ASM-ELF-NEXT: .Lauth_success_0:
163+
; ISEL-ASM-ELF-NEXT: add x16, x16, #8
164+
; ISEL-ASM-ELF-NEXT: mov x17, x0
165+
; ISEL-ASM-ELF-NEXT: movk x17, #1234, lsl #48
166+
; ISEL-ASM-ELF-NEXT: pacda x16, x17
167+
; ISEL-ASM-ELF-NEXT: str x16, [x0]
168+
; ISEL-ASM-ELF-NEXT: ret
124169
%dest.i = ptrtoint ptr %dest to i64
125170
%discr = call i64 @llvm.ptrauth.blend(i64 %dest.i, i64 1234)
126171
%signed.i = call i64 @llvm.ptrauth.sign(i64 ptrtoint (ptr getelementptr ([2 x ptr], ptr @const_table_got, i32 0, i32 1) to i64), i32 2, i64 %discr)
@@ -130,17 +175,26 @@ define void @store_signed_const_got(ptr %dest) {
130175
}
131176

132177
define void @store_signed_arg(ptr %dest, ptr %p) {
133-
; ISEL-LABEL: name: store_signed_arg
134-
; ISEL: body:
135-
; ISEL: %0:gpr64common = COPY $x0
136-
; ISEL-NEXT: %1:gpr64common = COPY $x1
137-
; ISEL-NEXT: %3:gpr64noip = MOVKXi %0, 1234
138-
; ISEL-NEXT: %6:gpr64common = ADDXri %1, 8, 0
139-
; ISEL-NEXT: %12:gpr64noip = COPY %0
140-
; ISEL-NEXT: %8:gpr64 = PAC %6, 2, 1234, %12, implicit-def dead $x16, implicit-def dead $x17
141-
; ISEL-NEXT: %10:gpr64 = COPY %8
142-
; ISEL-NEXT: STRXui %10, %0, 0 :: (store (p0) into %ir.dest)
143-
; ISEL-NEXT: RET_ReallyLR
178+
; ISEL-MIR-LABEL: name: store_signed_arg
179+
; ISEL-MIR: body:
180+
; ISEL-MIR: %0:gpr64common = COPY $x0
181+
; ISEL-MIR-NEXT: %1:gpr64common = COPY $x1
182+
; ISEL-MIR-NEXT: %3:gpr64noip = MOVKXi %0, 1234, 48
183+
; ISEL-MIR-NEXT: %6:gpr64common = ADDXri %1, 8, 0
184+
; ISEL-MIR-NEXT: %12:gpr64noip = COPY %0
185+
; ISEL-MIR-NEXT: %8:gpr64 = PAC %6, 2, 1234, %12, implicit-def dead $x16, implicit-def dead $x17
186+
; ISEL-MIR-NEXT: %10:gpr64 = COPY %8
187+
; ISEL-MIR-NEXT: STRXui %10, %0, 0 :: (store (p0) into %ir.dest)
188+
; ISEL-MIR-NEXT: RET_ReallyLR
189+
;
190+
; ISEL-ASM-LABEL: store_signed_arg:
191+
; ISEL-ASM-NEXT: .cfi_startproc
192+
; ISEL-ASM-NEXT: add x8, x1, #8
193+
; ISEL-ASM-NEXT: mov x16, x0
194+
; ISEL-ASM-NEXT: movk x16, #1234, lsl #48
195+
; ISEL-ASM-NEXT: pacda x8, x16
196+
; ISEL-ASM-NEXT: str x8, [x0]
197+
; ISEL-ASM-NEXT: ret
144198
%dest.i = ptrtoint ptr %dest to i64
145199
%discr = call i64 @llvm.ptrauth.blend(i64 %dest.i, i64 1234)
146200
%p.offset = getelementptr [2 x ptr], ptr %p, i32 0, i32 1

0 commit comments

Comments
 (0)