aboutsummaryrefslogtreecommitdiff
path: root/clang/test/CodeGen/cfi-unchecked-callee-attribute.cpp
blob: feb7c9c30a2199d8ef2740d9bb1a4e8f79fd4385 (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
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -fsanitize=cfi-icall -o - %s | FileCheck %s

#define CFI_UNCHECKED_CALLEE __attribute__((cfi_unchecked_callee))

void unchecked(void) CFI_UNCHECKED_CALLEE {}

/// All references to unchecked function with `cfi_unchecked_callee` should have the `cfi_unchecked_callee` wrapper.
// CHECK: @checked = global ptr no_cfi @_Z9uncheckedv
void (*checked)(void) = unchecked;

// CHECK: @unchecked2 = global ptr no_cfi @_Z9uncheckedv
void (CFI_UNCHECKED_CALLEE *unchecked2)(void) = unchecked;

// CHECK: @checked2 = global ptr no_cfi @_Z9uncheckedv
constexpr void (CFI_UNCHECKED_CALLEE *unchecked_constexpr)(void) = unchecked;
void (*checked2)(void) = unchecked_constexpr;

/// Note we still reference the `no_cfi` function rather than the jump table entry.
/// The explicit cast will only silence the warning.
// CHECK: @checked_explicit_cast = global ptr no_cfi @_Z9uncheckedv
void (*checked_explicit_cast)(void) = (void (*)(void))unchecked;

// CHECK: @checked_array = global [3 x ptr] [ptr no_cfi @_Z9uncheckedv, ptr no_cfi @_Z9uncheckedv, ptr no_cfi @_Z9uncheckedv]
void (*checked_array[])(void) = {
  unchecked,
  (void (*)(void))unchecked,
  reinterpret_cast<void (*)(void)>(unchecked),
};

void func_accepting_checked(void (*p)(void)) {}

// CHECK-LABEL: _Z9InvokeCFIv
void InvokeCFI() {
  // CHECK: %0 = load ptr, ptr @checked, align 8
  // CHECK: %1 = call i1 @llvm.type.test(ptr %0, metadata !"_ZTSFvvE")
  checked();
}

// CHECK-LABEL: _Z11InvokeNoCFIv
void InvokeNoCFI() {
  // CHECK:  %0 = load ptr, ptr @unchecked2, align 8
  // CHECK:  call void %0()
  unchecked2();
}

struct A {
  void unchecked_method() CFI_UNCHECKED_CALLEE {}
  virtual void unchecked_virtual_method() CFI_UNCHECKED_CALLEE {}
  static void unchecked_static_method() CFI_UNCHECKED_CALLEE {}
  int unchecked_const_method() const CFI_UNCHECKED_CALLEE { return 0; }
  int unchecked_const_method_int_arg(int n) const CFI_UNCHECKED_CALLEE { return 0; }
};

void h(void) {
  // CHECK: store ptr no_cfi @_Z9uncheckedv, ptr %unchecked_local
  void (*unchecked_local)(void) = unchecked;

  // CHECK: call void @_Z22func_accepting_checkedPFvvE(ptr noundef no_cfi @_Z9uncheckedv)
  func_accepting_checked(unchecked);

  // CHECK:      [[B:%.*]] = load ptr, ptr @checked
  // CHECK-NEXT: call void @_Z22func_accepting_checkedPFvvE(ptr noundef [[B]]) 
  func_accepting_checked(checked);

  // CHECK: store { i64, i64 } { i64 ptrtoint (ptr no_cfi @_ZN1A16unchecked_methodEv to i64), i64 0 }, ptr %A1
  auto A1 = &A::unchecked_method;
  /// Storing unchecked virtual function pointer stores an offset instead. This is part of the
  /// normal Itanium C++ ABI, but let's make sure we don't change anything.
  // CHECK: store { i64, i64 } { i64 1, i64 0 }, ptr %A2
  auto A2 = &A::unchecked_virtual_method;
  // CHECK: store ptr no_cfi @_ZN1A23unchecked_static_methodEv, ptr %A3
  auto A3 = &A::unchecked_static_method;
  // CHECK: store { i64, i64 } { i64 ptrtoint (ptr no_cfi @_ZNK1A22unchecked_const_methodEv to i64), i64 0 }, ptr %A4
  auto A4 = (int(CFI_UNCHECKED_CALLEE A::*)() const)(&A::unchecked_const_method);
  // CHECK: store { i64, i64 } { i64 ptrtoint (ptr no_cfi @_ZNK1A30unchecked_const_method_int_argEi to i64), i64 0 }, ptr %A5
  auto A5 = (int(CFI_UNCHECKED_CALLEE A::*)(int) const)(&A::unchecked_const_method_int_arg);
}