[llvm] [AArch64][PAC] Combine signing with address materialization (PR #130809)
Anatoly Trosinenko via llvm-commits
llvm-commits at lists.llvm.org
Tue Jun 3 11:41:53 PDT 2025
atrosinenko wrote:
Ping.
I have updated this PR to account for possibility of immediate modifier substitution:
```
movk x10, #1234, #48
bl callee
adrp x17, :got:sym
add x17, x17, :got_lo12:sym
pacda x17, x10
```
In the above example, the discriminator is computed as `blend(addr, 1234)` - while it is impossible to prevent substitution of `addr` in general, it is definitely possible to at least make sure that 1234 is always blended into whatever is stored in `x10` after `callee` returns (in the below code `x10` is used for clarity - with this PR applied `x16` would be used instead):
```
bl callee
adrp x17, :got:sym
add x17, x17, :got_lo12:sym
movk x10, #1234, #48
pacda x17, x10
```
In their current state, both this PR and #133788 protect both pointer-to-be-signed as well as the immediate modifier from tampering, but #133788 still has to be updated to handle GlobalISel in addition to DAGISel.
Hopefully, having a unified post-processing at the `MachineInstr` level instead of two separate implementations in DAGISel and GlobalISel could improve maintainability - though, it may be my bias towards `MachineFunction` passes vs. instruction selectors. Furthermore, it feels like with custom inserters it should be possible to extend #132857 to completely get rid of DAGIsel- and GlobalISel-specific selectors for AUT and PAC and use the selectors tablegen-erated from declarative patterns.
Another feature of custom inserters seems to be better handling of cross-basic-block analysis. If I understand it right, at finalize-isel stage virtual registers are in SSA form, and it should be safe to ignore basic blocks while following the chains of copy and increment/decrement instructions, as long as only virtual registers are traversed and no PHI instructions occur.
```llvm
@var = global i32 0
@discvar = global i64 0
define i64 @foo(i1 %cond) {
entry:
%addrdisc = load i64, ptr @discvar
%disc = call i64 @llvm.ptrauth.blend(i64 %addrdisc, i64 42)
%var.ptr.i = ptrtoint ptr @var to i64
br i1 %cond, label %next, label %exit
next:
call void asm sideeffect "nop", "r"(i64 %disc)
br label %exit
exit:
%signed = call i64 @llvm.ptrauth.sign(i64 %var.ptr.i, i32 2, i64 %disc)
ret i64 %signed
}
!llvm.module.flags = !{!0}
!0 = !{i32 8, !"ptrauth-elf-got", i32 1}
```
here the DAGISel-based patch is unable to provide a `PtrAuthGlobalAddress` node because discriminator computation is in another basic block (note the `bb.2.exit` basic block in the dump after `finalize-isel` pass):
<details>
<summary>DAGISel-based patch</summary>
```
# ./bin/clang -target aarch64-linux-pauthtest -march=armv8.3-a /tmp/repro.ll -S -o- -O2 -mllvm -print-after=codegenprepare,finalize-isel
warning: overriding the module target triple with aarch64-unknown-linux-pauthtest [-Woverride-module]
.file "repro.ll"
*** IR Dump After CodeGen Prepare (codegenprepare) ***
define i64 @foo(i1 %cond) local_unnamed_addr {
entry:
%addrdisc = load i64, ptr @discvar, align 8
%disc = tail call i64 @llvm.ptrauth.blend(i64 %addrdisc, i64 42)
br i1 %cond, label %next, label %exit
next: ; preds = %entry
tail call void asm sideeffect "nop", "r"(i64 %disc) #1
br label %exit
exit: ; preds = %next, %entry
%signed = tail call i64 @llvm.ptrauth.sign(i64 ptrtoint (ptr @var to i64), i32 2, i64 %disc)
ret i64 %signed
}
# *** IR Dump After Finalize ISel and expand pseudo-instructions (finalize-isel) ***:
# Machine code for function foo: IsSSA, TracksLiveness
Function Live Ins: $w0 in %1
bb.0.entry:
successors: %bb.1(0x40000000), %bb.2(0x40000000); %bb.1(50.00%), %bb.2(50.00%)
liveins: $w0
%1:gpr32 = COPY $w0
%2:gpr64common = LOADgotAUTH target-flags(aarch64-got) @discvar, implicit-def dead $x16, implicit-def dead $x17, implicit-def dead $nzcv
%3:gpr64 = LDRXui killed %2:gpr64common, 0 :: (dereferenceable load (s64) from @discvar)
%4:gpr64 = MOVKXi %3:gpr64(tied-def 0), 42, 48
%0:gpr64sp = COPY %4:gpr64
TBZW %1:gpr32, 0, %bb.2
B %bb.1
bb.1.next:
; predecessors: %bb.0
successors: %bb.2(0x80000000); %bb.2(100.00%)
%5:gpr64common = COPY %0:gpr64sp
INLINEASM &nop [sideeffect] [attdialect], $0:[reguse:GPR64common], %5:gpr64common
bb.2.exit:
; predecessors: %bb.0, %bb.1
%6:gpr64common = LOADgotAUTH target-flags(aarch64-got) @var, implicit-def dead $x16, implicit-def dead $x17, implicit-def dead $nzcv
%7:gpr64 = PACDA %6:gpr64common(tied-def 0), %0:gpr64sp
$x0 = COPY %7:gpr64
RET_ReallyLR implicit $x0
# End machine code for function foo.
.text
.globl foo // -- Begin function foo
.p2align 2
.type foo, at function
foo: // @foo
.cfi_startproc
// %bb.0: // %entry
adrp x17, :got_auth:discvar
add x17, x17, :got_auth_lo12:discvar
ldr x16, [x17]
autda x16, x17
mov x17, x16
xpacd x17
cmp x16, x17
b.eq .Lauth_success_0
brk #0xc472
.Lauth_success_0:
mov x8, x16
ldr x8, [x8]
movk x8, #42, lsl #48
tbz w0, #0, .LBB0_2
// %bb.1: // %next
//APP
nop
//NO_APP
.LBB0_2: // %exit
adrp x17, :got_auth:var
add x17, x17, :got_auth_lo12:var
ldr x16, [x17]
autda x16, x17
mov x17, x16
xpacd x17
cmp x16, x17
b.eq .Lauth_success_1
brk #0xc472
.Lauth_success_1:
mov x0, x16
pacda x0, x8
ret
.Lfunc_end0:
.size foo, .Lfunc_end0-foo
.cfi_endproc
// -- End function
.type var, at object // @var
.bss
.globl var
.p2align 2, 0x0
var:
.word 0 // 0x0
.size var, 4
.type discvar, at object // @discvar
.globl discvar
.p2align 3, 0x0
discvar:
.xword 0 // 0x0
.size discvar, 8
.section ".note.GNU-stack","", at progbits
.addrsig
.addrsig_sym var
1 warning generated.
```
</details>
<details>
<summary>Custom inserter-based patch</summary>
```
# ./bin/clang -target aarch64-linux-pauthtest -march=armv8.3-a /tmp/repro.ll -S -o- -O2 -mllvm -print-after=codegenprepare,finalize-isel
warning: overriding the module target triple with aarch64-unknown-linux-pauthtest [-Woverride-module]
.file "repro.ll"
*** IR Dump After CodeGen Prepare (codegenprepare) ***
define i64 @foo(i1 %cond) local_unnamed_addr {
entry:
%addrdisc = load i64, ptr @discvar, align 8
%disc = tail call i64 @llvm.ptrauth.blend(i64 %addrdisc, i64 42)
br i1 %cond, label %next, label %exit
next: ; preds = %entry
tail call void asm sideeffect "nop", "r"(i64 %disc) #1
br label %exit
exit: ; preds = %next, %entry
%signed = tail call i64 @llvm.ptrauth.sign(i64 ptrtoint (ptr @var to i64), i32 2, i64 %disc)
ret i64 %signed
}
# *** IR Dump After Finalize ISel and expand pseudo-instructions (finalize-isel) ***:
# Machine code for function foo: IsSSA, TracksLiveness
Function Live Ins: $w0 in %1
bb.0.entry:
successors: %bb.1(0x40000000), %bb.2(0x40000000); %bb.1(50.00%), %bb.2(50.00%)
liveins: $w0
%1:gpr32 = COPY $w0
%2:gpr64common = LOADgotAUTH target-flags(aarch64-got) @discvar, implicit-def dead $x16, implicit-def dead $x17, implicit-def dead $nzcv
%3:gpr64 = LDRXui killed %2:gpr64common, 0 :: (dereferenceable load (s64) from @discvar)
%4:gpr64 = MOVKXi %3:gpr64(tied-def 0), 42, 48
%0:gpr64sp = COPY %4:gpr64
TBZW %1:gpr32, 0, %bb.2
B %bb.1
bb.1.next:
; predecessors: %bb.0
successors: %bb.2(0x80000000); %bb.2(100.00%)
%5:gpr64common = COPY %0:gpr64sp
INLINEASM &nop [sideeffect] [attdialect], $0:[reguse:GPR64common], %5:gpr64common
bb.2.exit:
; predecessors: %bb.0, %bb.1
%6:gpr64common = LOADgotAUTH target-flags(aarch64-got) @var, implicit-def dead $x16, implicit-def dead $x17, implicit-def dead $nzcv
%8:gpr64noip = COPY %3:gpr64
LOADgotPAC target-flags(aarch64-got) @var, 2, %8:gpr64noip, 42, implicit-def $x16, implicit-def $x17, implicit-def $nzcv
%7:gpr64 = COPY $x16
$x0 = COPY %7:gpr64
RET_ReallyLR implicit $x0
# End machine code for function foo.
.text
.globl foo // -- Begin function foo
.p2align 2
.type foo, at function
foo: // @foo
.cfi_startproc
// %bb.0: // %entry
adrp x17, :got_auth:discvar
add x17, x17, :got_auth_lo12:discvar
ldr x16, [x17]
autda x16, x17
mov x17, x16
xpacd x17
cmp x16, x17
b.eq .Lauth_success_0
brk #0xc472
.Lauth_success_0:
mov x8, x16
ldr x8, [x8]
tbz w0, #0, .LBB0_2
// %bb.1: // %next
mov x9, x8
movk x9, #42, lsl #48
//APP
nop
//NO_APP
.LBB0_2: // %exit
adrp x17, :got_auth:var
add x17, x17, :got_auth_lo12:var
ldr x16, [x17]
autda x16, x17
mov x17, x16
xpacd x17
cmp x16, x17
b.eq .Lauth_success_1
brk #0xc472
.Lauth_success_1:
mov x17, x8
movk x17, #42, lsl #48
pacda x16, x17
mov x0, x16
ret
.Lfunc_end0:
.size foo, .Lfunc_end0-foo
.cfi_endproc
// -- End function
.type var, at object // @var
.bss
.globl var
.p2align 2, 0x0
var:
.word 0 // 0x0
.size var, 4
.type discvar, at object // @discvar
.globl discvar
.p2align 3, 0x0
discvar:
.xword 0 // 0x0
.size discvar, 8
.section ".note.GNU-stack","", at progbits
.addrsig
.addrsig_sym var
1 warning generated.
```
</details>
https://github.com/llvm/llvm-project/pull/130809
More information about the llvm-commits
mailing list