llvm/clang/test/CodeGen/memtag-globals-asm.cpp

// REQUIRES: aarch64-registered-target

// RUN: %clang_cc1 -S -x c++ -std=c++11 -triple aarch64-linux-android31 \
// RUN:   -fsanitize=memtag-globals -o %t.out %s
// RUN: FileCheck %s --input-file=%t.out
// RUN: FileCheck %s --input-file=%t.out --check-prefix=CHECK-A
// RUN: FileCheck %s --input-file=%t.out --check-prefix=CHECK-B
// RUN: FileCheck %s --input-file=%t.out --check-prefix=CHECK-C
// RUN: FileCheck %s --input-file=%t.out --check-prefix=CHECK-D
// RUN: FileCheck %s --input-file=%t.out --check-prefix=CHECK-E
// RUN: FileCheck %s --input-file=%t.out --check-prefix=CHECK-F
// RUN: FileCheck %s --input-file=%t.out --check-prefix=CHECK-G
// RUN: FileCheck %s --input-file=%t.out --check-prefix=CHECK-H
// RUN: FileCheck %s --input-file=%t.out --check-prefix=CHECK-I
// RUN: FileCheck %s --input-file=%t.out --check-prefix=CHECK-J
// RUN: FileCheck %s --input-file=%t.out --check-prefix=CHECK-K
// RUN: FileCheck %s --input-file=%t.out --check-prefix=CHECK-L
// RUN: FileCheck %s --input-file=%t.out --check-prefix=CHECK-M
// RUN: FileCheck %s --input-file=%t.out --check-prefix=CHECK-N
// RUN: FileCheck %s --input-file=%t.out --check-prefix=CHECK-O
// RUN: FileCheck %s --input-file=%t.out --check-prefix=CHECK-P
// RUN: FileCheck %s --input-file=%t.out --check-prefix=CHECK-Q
// RUN: FileCheck %s --input-file=%t.out --check-prefix=CHECK-R

// RUN: %clang_cc1 -O3 -S -x c++ -std=c++11 -triple aarch64-linux-android31 \
// RUN:   -fsanitize=memtag-globals -o %t.out %s
// RUN: FileCheck %s --input-file=%t.out
// RUN: FileCheck %s --input-file=%t.out --check-prefix=CHECK-A
// RUN: FileCheck %s --input-file=%t.out --check-prefix=CHECK-B
// RUN: FileCheck %s --input-file=%t.out --check-prefix=CHECK-C
// RUN: FileCheck %s --input-file=%t.out --check-prefix=CHECK-D
// RUN: FileCheck %s --input-file=%t.out --check-prefix=CHECK-E
// RUN: FileCheck %s --input-file=%t.out --check-prefix=CHECK-F
// RUN: FileCheck %s --input-file=%t.out --check-prefix=CHECK-G
// RUN: FileCheck %s --input-file=%t.out --check-prefix=CHECK-H
// RUN: FileCheck %s --input-file=%t.out --check-prefix=CHECK-I
// RUN: FileCheck %s --input-file=%t.out --check-prefix=CHECK-J
// RUN: FileCheck %s --input-file=%t.out --check-prefix=CHECK-K
// RUN: FileCheck %s --input-file=%t.out --check-prefix=CHECK-L
// RUN: FileCheck %s --input-file=%t.out --check-prefix=CHECK-M
// RUN: FileCheck %s --input-file=%t.out --check-prefix=CHECK-N
// RUN: FileCheck %s --input-file=%t.out --check-prefix=CHECK-O
// RUN: FileCheck %s --input-file=%t.out --check-prefix=CHECK-P
// RUN: FileCheck %s --input-file=%t.out --check-prefix=CHECK-Q
// RUN: FileCheck %s --input-file=%t.out --check-prefix=CHECK-R

/// Ensure that emulated TLS also doesn't get sanitized.
// RUN: %clang_cc1 -S -x c++ -std=c++11 -triple aarch64-linux-android31 \
// RUN:   -fsanitize=memtag-globals -o - %s | FileCheck %s

// CHECK-A: .memtag global_int
// CHECK-A: .globl global_int
// CHECK-A: .p2align 4, 0x0
// CHECK-A: .size global_int, 16
int global_int;
// CHECK-B: .memtag _ZL9local_int
// CHECK-B: .local _ZL9local_int
// CHECK-B: .comm _ZL9local_int,16,16
static int local_int;

// CHECK-C: .memtag _ZL12local_buffer
// CHECK-C: .local _ZL12local_buffer
// CHECK-C: .comm _ZL12local_buffer,16,16
static char local_buffer[16];
// CHECK-D: .memtag _ZL22local_buffer_local_end
// CHECK-D: .p2align 4, 0x0
// CHECK-D: _ZL22local_buffer_local_end:
// CHECK-D: .xword _ZL12local_buffer+16
// CHECK-D: .size _ZL22local_buffer_local_end, 16
static char* local_buffer_local_end = &local_buffer[16];
// CHECK-E: .memtag local_buffer_global_end
// CHECK-E: .globl local_buffer_global_end
// CHECK-E  .p2align 4, 0x0
// CHECK-E: local_buffer_global_end:
// CHECK-E: .xword _ZL12local_buffer+16
// CHECK-E: .size local_buffer_global_end, 16
char* local_buffer_global_end = &local_buffer[16];

// CHECK-F: .memtag global_buffer
// CHECK-F: .globl global_buffer
// CHECK-F: .p2align 4, 0x0
// CHECK-F: .size global_buffer, 16
char global_buffer[16];
// CHECK-G: .memtag _ZL23global_buffer_local_end
// CHECK-G: .p2align 4, 0x0
// CHECK-G: _ZL23global_buffer_local_end:
// CHECK-G: .xword global_buffer+16
// CHECK-G: .size _ZL23global_buffer_local_end, 16
static char* global_buffer_local_end = &global_buffer[16];
// CHECK-H: .memtag global_buffer_global_end
// CHECK-H: .p2align 4, 0x0
// CHECK-H: global_buffer_global_end:
// CHECK-H: .xword global_buffer+16
// CHECK-H: .size global_buffer_global_end, 16
char* global_buffer_global_end = &global_buffer[16];

class MyClass {
 public:
  virtual ~MyClass() {}
  static int my_class_int;
  static const int my_class_const_int;
  virtual int virtual_func() { return 1; }
};
// CHECK-I: .memtag _ZN7MyClass12my_class_intE
// CHECK-I: .globl _ZN7MyClass12my_class_intE
// CHECK-I: .p2align 4, 0x0
// CHECK-I: .size _ZN7MyClass12my_class_intE, 16
int MyClass::my_class_int;
// CHECK-NOT: .memtag _ZN7MyClass18my_class_const_intE
const int MyClass::my_class_const_int = 1;

// CHECK-J: .memtag global_my_class
// CHECK-J: .globl global_my_class
// CHECK-J: .p2align 4, 0x0
// CHECK-J: .size global_my_class, 16
MyClass global_my_class;
// CHECK-K: .memtag _ZL14local_my_class
// CHECK-K: .p2align 4, 0x0
// CHECK-K: .size _ZL14local_my_class, 16
static MyClass local_my_class;

// CHECK-NOT: .memtag _ZL18local_const_string
static const char local_const_string[] = "this is a local string";
// CHECK-L: .memtag _ZL12local_string
// CHECK-L: .p2align 4, 0x0
// CHECK-L: .size _ZL12local_string, 32
static char local_string[] = "this is a local string";

// CHECK-M: .memtag global_atomic_int
// CHECK-M: .globl global_atomic_int
// CHECK-M: .p2align 4, 0x0
// CHECK-M: .size global_atomic_int, 16
_Atomic(int) global_atomic_int;
// CHECK-N: .memtag _ZL16local_atomic_int
// CHECK-N: .local _ZL16local_atomic_int
// CHECK-N: .comm _ZL16local_atomic_int,16,16
static _Atomic(int) local_atomic_int;

union MyUnion {
  int i;
  char c;
};

// CHECK-O: .memtag global_union
// CHECK-O: .globl global_union
// CHECK-O: .p2align 4, 0x0
// CHECK-O: .size global_union, 16
MyUnion global_union;
// CHECK-P: .memtag _ZL11local_union
// CHECK-P: .local _ZL11local_union
// CHECK-P: .comm _ZL11local_union,16,16
static MyUnion local_union;

// CHECK-NOT: .memtag {{.*}}global_tls
thread_local int global_tls;
// CHECK-NOT: .memtag {{.*}}local_tls
static thread_local int local_tls;

/// Prevent the compiler from realising that non-const local variables are not
/// modified, and constant inlining into f().
const void* export_pointers(int c) {
  switch (c) {
    case 0:  return &local_int;
    case 1:  return &local_buffer;
    case 2:  return &local_buffer_local_end;
    case 3:  return &global_buffer_local_end;
    case 4:  return &MyClass::my_class_int;
    case 6:  return &local_my_class;
    case 8:  return &local_string;
    case 9:  return &local_atomic_int;
    case 10: return &local_union;
    case 11: return &local_tls;
  }
  return nullptr;
}

/// Ensure that all tagged globals are loaded/referenced via. the GOT.
// CHECK-NOT:      .memtag _Z1fi
// CHECK-Q:        _Z1fi:
int f(int x) {
  // CHECK-R: .memtag _ZZ1fiE12function_int
  // CHECK-R: .local _ZZ1fiE12function_int
  // CHECK-R: .comm _ZZ1fiE12function_int,16,16
  static int function_int = 0;
  /// Prevent non-const `f` from being promoted to a constant and inlined.
  function_int += x;

  return
  // CHECK-Q-DAG: adrp [[REG_A:x[0-9]+]], :got:global_int
  // CHECK-Q-DAG: ldr  [[REG_A2:x[0-9]+]], [[[REG_A]], :got_lo12:global_int]
  // CHECK-Q-DAG: ldr  {{.*}}, [[[REG_A2]]]
      global_int +
  // CHECK-Q-DAG: adrp [[REG_B:x[0-9]+]], :got:_ZL9local_int
  // CHECK-Q-DAG: ldr  [[REG_B2:x[0-9]+]], [[[REG_B]], :got_lo12:_ZL9local_int]
  // CHECK-Q-DAG: ldr  {{.*}}, [[[REG_B2]]]
      local_int +
  // CHECK-Q-DAG: adrp  [[REG_C:x[0-9]+]], :got:_ZL12local_buffer
  // CHECK-Q-DAG: ldr   [[REG_C2:x[0-9]+]], [[[REG_C]], :got_lo12:_ZL12local_buffer]
  // CHECK-Q-DAG: ldrsb {{.*}}, [[[REG_C2]]]
      local_buffer[0] +
  // CHECK-Q-DAG: adrp   [[REG_D:x[0-9]+]], :got:_ZL22local_buffer_local_end
  // CHECK-Q-DAG: ldr    [[REG_D2:x[0-9]+]], [[[REG_D]], :got_lo12:_ZL22local_buffer_local_end]
  // CHECK-Q-DAG: ldr    [[REG_D3:x[0-9]+]], [[[REG_D2]]]
  // CHECK-Q-DAG: ldursb {{.*}}, [[[REG_D3]], #-16]
      local_buffer_local_end[-16] +
  // CHECK-Q-DAG: adrp   [[REG_E:x[0-9]+]], :got:local_buffer_global_end
  // CHECK-Q-DAG: ldr    [[REG_E2:x[0-9]+]], [[[REG_E]], :got_lo12:local_buffer_global_end]
  // CHECK-Q-DAG: ldr    [[REG_E3:x[0-9]+]], [[[REG_E2]]]
  // CHECK-Q-DAG: ldursb {{.*}}, [[[REG_E3]], #-16]
      local_buffer_global_end[-16] +
  // CHECK-Q-DAG: adrp  [[REG_F:x[0-9]+]], :got:global_buffer{{$}}
  // CHECK-Q-DAG: ldr   [[REG_F2:x[0-9]+]], [[[REG_F]], :got_lo12:global_buffer]
  // CHECK-Q-DAG: ldrsb {{.*}}, [[[REG_F2]]]
      global_buffer[0] +
  // CHECK-Q-DAG: adrp   [[REG_G:x[0-9]+]], :got:_ZL23global_buffer_local_end
  // CHECK-Q-DAG: ldr    [[REG_G2:x[0-9]+]], [[[REG_G]], :got_lo12:_ZL23global_buffer_local_end]
  // CHECK-Q-DAG: ldr    [[REG_G3:x[0-9]+]], [[[REG_G2]]]
  // CHECK-Q-DAG: ldursb {{.*}}, [[[REG_G3]], #-16]
      global_buffer_local_end[-16] +
  // CHECK-Q-DAG: adrp   [[REG_H:x[0-9]+]], :got:global_buffer_global_end
  // CHECK-Q-DAG: ldr    [[REG_H2:x[0-9]+]], [[[REG_H]], :got_lo12:global_buffer_global_end]
  // CHECK-Q-DAG: ldr    [[REG_H3:x[0-9]+]], [[[REG_H2]]]
  // CHECK-Q-DAG: ldursb {{.*}}, [[[REG_H3]], #-16]
      global_buffer_global_end[-16] +
  // CHECK-Q-DAG: adrp [[REG_I:x[0-9]+]], :got:_ZN7MyClass12my_class_intE
  // CHECK-Q-DAG: ldr  [[REG_I2:x[0-9]+]], [[[REG_I]], :got_lo12:_ZN7MyClass12my_class_intE]
  // CHECK-Q-DAG: ldr  {{.*}}, [[[REG_I2]]]
      MyClass::my_class_int +
  /// Constant values - ignore.
      MyClass::my_class_const_int +
      global_my_class.virtual_func() +
      local_my_class.virtual_func() +
      local_const_string[0] +
  // CHECK-Q-DAG: adrp  [[REG_J:x[0-9]+]], :got:_ZL12local_string
  // CHECK-Q-DAG: ldr   [[REG_J2:x[0-9]+]], [[[REG_J]], :got_lo12:_ZL12local_string]
  // CHECK-Q-DAG: ldrsb {{.*}}, [[[REG_J2]]]
      local_string[0] +
  // CHECK-Q-DAG: adrp  [[REG_K:x[0-9]+]], :got:_ZL16local_atomic_int
  // CHECK-Q-DAG: ldr   [[REG_K2:x[0-9]+]], [[[REG_K]], :got_lo12:_ZL16local_atomic_int]
  // CHECK-Q-DAG: ldar {{.*}}, [[[REG_K2]]]
      local_atomic_int +
  // CHECK-Q-DAG: adrp [[REG_L:x[0-9]+]], :got:global_atomic_int
  // CHECK-Q-DAG: ldr  [[REG_L2:x[0-9]+]], [[[REG_L]], :got_lo12:global_atomic_int]
  // CHECK-Q-DAG: ldar {{.*}}, [[[REG_L2]]]
      global_atomic_int +
  // CHECK-Q-DAG: adrp [[REG_M:x[0-9]+]], :got:global_union
  // CHECK-Q-DAG: ldr  [[REG_M2:x[0-9]+]], [[[REG_M]], :got_lo12:global_union]
  // CHECK-Q-DAG: ldr  {{.*}}, [[[REG_M2]]]
      global_union.i +
  // CHECK-Q-DAG: adrp  [[REG_N:x[0-9]+]], :got:_ZL11local_union
  // CHECK-Q-DAG: ldr   [[REG_N2:x[0-9]+]], [[[REG_N]], :got_lo12:_ZL11local_union]
  // CHECK-Q-DAG: ldrsb {{.*}}, [[[REG_N2]]]
      local_union.c +
  /// Global variables - ignore.
      global_tls +
      local_tls +
  // CHECK-Q-DAG: adrp  [[REG_O:x[0-9]+]], :got:_ZZ1fiE12function_int
  // CHECK-Q-DAG: ldr   [[REG_O2:x[0-9]+]], [[[REG_O]], :got_lo12:_ZZ1fiE12function_int]
  // CHECK-Q-DAG: ldr   {{.*}}, [[[REG_O2]]]
      function_int;
}

typedef void (*func_t)(void);
#define CONSTRUCTOR(section_name) \
  __attribute__((used)) __attribute__((section(section_name)))

__attribute__((constructor(0))) void func_constructor() {}
CONSTRUCTOR(".init") func_t func_init = func_constructor;
CONSTRUCTOR(".fini") func_t func_fini = func_constructor;
CONSTRUCTOR(".ctors") func_t func_ctors = func_constructor;
CONSTRUCTOR(".dtors") func_t func_dtors = func_constructor;
CONSTRUCTOR(".init_array") func_t func_init_array = func_constructor;
CONSTRUCTOR(".fini_array") func_t func_fini_array = func_constructor;
CONSTRUCTOR(".preinit_array") func_t preinit_array = func_constructor;
CONSTRUCTOR("array_of_globals") int global1;
CONSTRUCTOR("array_of_globals") int global2;
CONSTRUCTOR("array_of_globals") int global_string;

// CHECK-NOT: .memtag func_constructor
// CHECK-NOT: .memtag func_init
// CHECK-NOT: .memtag func_fini
// CHECK-NOT: .memtag func_ctors
// CHECK-NOT: .memtag func_dtors
// CHECK-NOT: .memtag func_init_array
// CHECK-NOT: .memtag func_fini_array
// CHECK-NOT: .memtag preinit_array
// CHECK-NOT: .memtag global1
// CHECK-NOT: .memtag global2
// CHECK-NOT: .memtag global_string