aboutsummaryrefslogtreecommitdiff
path: root/llvm/test/CodeGen/ARM/kcfi-cbz-range.ll
blob: 8e71cae3131d4b1e7a44c3926c2649a51f3ed336 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
; RUN: llc -mtriple=thumbv7-linux-gnueabi -filetype=obj < %s
; RUN: llc -mtriple=thumbv7-linux-gnueabi < %s | FileCheck %s

; This test verifies that KCFI instrumentation doesn't cause "out of range
; pc-relative fixup value" errors when generating object files.
;
; The test creates a scenario with enough KCFI-instrumented indirect calls
; (~32 bytes each) that would push a cbz/cbnz instruction out of its ±126 byte
; range if the KCFI_CHECK pseudo-instruction size is not properly accounted for.
;
; Without the fix (KCFI_CHECK returns size 0):
;   - Backend thinks KCFI checks take no space
;   - Generates cbz to branch over the code
;   - During assembly, cbz target is >126 bytes away
;   - Assembly fails with "error: out of range pc-relative fixup value"
;
; With the fix (KCFI_CHECK returns size 32 for Thumb2):
;   - Backend correctly accounts for KCFI check expansion
;   - Avoids cbz or uses longer-range branch instructions
;   - Assembly succeeds, object file is generated

declare void @external_function(i32)

; Test WITHOUT KCFI: should generate cbz since calls are small
; CHECK-LABEL: test_without_kcfi:
; CHECK: cbz
; CHECK-NOT: bic{{.*}}#1
define i32 @test_without_kcfi(ptr %callback, i32 %x) {
entry:
  %cmp = icmp eq i32 %x, 0
  br i1 %cmp, label %if_zero, label %if_nonzero

if_nonzero:
  ; Regular (non-KCFI) indirect calls - much smaller
  call void %callback()
  call void %callback()
  call void %callback()
  call void %callback()
  call void %callback()
  call void %callback()

  call void @external_function(i32 %x)
  %add1 = add i32 %x, 1
  ret i32 %add1

if_zero:
  call void @external_function(i32 0)
  ret i32 0
}

; Test WITH KCFI: should NOT generate cbz due to large KCFI checks
; CHECK-LABEL: test_with_kcfi:
; CHECK-NOT: cbz
; CHECK: bic{{.*}}#1
define i32 @test_with_kcfi(ptr %callback, i32 %x) !kcfi_type !1 {
entry:
  %cmp = icmp eq i32 %x, 0
  br i1 %cmp, label %if_zero, label %if_nonzero

if_nonzero:
  ; Six KCFI-instrumented indirect calls (~192 bytes total, exceeds cbz range)
  call void %callback() [ "kcfi"(i32 12345678) ]
  call void %callback() [ "kcfi"(i32 12345678) ]
  call void %callback() [ "kcfi"(i32 12345678) ]
  call void %callback() [ "kcfi"(i32 12345678) ]
  call void %callback() [ "kcfi"(i32 12345678) ]
  call void %callback() [ "kcfi"(i32 12345678) ]

  ; Regular call to prevent optimization
  call void @external_function(i32 %x)
  %add1 = add i32 %x, 1
  ret i32 %add1

if_zero:
  call void @external_function(i32 0)
  ret i32 0
}

!llvm.module.flags = !{!0}
!0 = !{i32 4, !"kcfi", i32 1}
!1 = !{i32 12345678}