//===-- sanitizer_common_interceptors_memintrinsics.inc ---------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// Memintrinsic function interceptors for tools like AddressSanitizer,
// ThreadSanitizer, MemorySanitizer, etc.
//
// These interceptors are part of the common interceptors, but separated out so
// that implementations may add them, if necessary, to a separate source file
// that should define SANITIZER_COMMON_NO_REDEFINE_BUILTINS at the top.
//
// This file should be included into the tool's memintrinsic interceptor file,
// which has to define its own macros:
//   COMMON_INTERCEPTOR_ENTER
//   COMMON_INTERCEPTOR_READ_RANGE
//   COMMON_INTERCEPTOR_WRITE_RANGE
//   COMMON_INTERCEPTOR_MEMSET_IMPL
//   COMMON_INTERCEPTOR_MEMMOVE_IMPL
//   COMMON_INTERCEPTOR_MEMCPY_IMPL
//   COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED
//===----------------------------------------------------------------------===//

#ifdef SANITIZER_REDEFINE_BUILTINS_H
#error "Define SANITIZER_COMMON_NO_REDEFINE_BUILTINS in .cpp file"
#endif

#include "interception/interception.h"
#include "sanitizer_platform_interceptors.h"

// Platform-specific options.
#if SANITIZER_APPLE
#  define PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE 0
#elif SANITIZER_WINDOWS64
#  define PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE 0
#elif SANITIZER_AIX
#  define PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE 0
#else
#  define PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE 1
#endif  // SANITIZER_APPLE

#ifndef COMMON_INTERCEPTOR_MEMSET_IMPL
#define COMMON_INTERCEPTOR_MEMSET_IMPL(ctx, dst, v, size) \
  {                                                       \
    if (COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED)        \
      return internal_memset(dst, v, size);               \
    COMMON_INTERCEPTOR_ENTER(ctx, memset, dst, v, size);  \
    if (common_flags()->intercept_intrin)                 \
      COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, size);     \
    return REAL(memset)(dst, v, size);                    \
  }
#endif

#ifndef COMMON_INTERCEPTOR_MEMMOVE_IMPL
#define COMMON_INTERCEPTOR_MEMMOVE_IMPL(ctx, dst, src, size) \
  {                                                          \
    if (COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED)           \
      return internal_memmove(dst, src, size);               \
    COMMON_INTERCEPTOR_ENTER(ctx, memmove, dst, src, size);  \
    if (common_flags()->intercept_intrin) {                  \
      COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, size);        \
      COMMON_INTERCEPTOR_READ_RANGE(ctx, src, size);         \
    }                                                        \
    return REAL(memmove)(dst, src, size);                    \
  }
#endif

#ifndef COMMON_INTERCEPTOR_MEMCPY_IMPL
#define COMMON_INTERCEPTOR_MEMCPY_IMPL(ctx, dst, src, size) \
  {                                                         \
    if (COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED) {        \
      return internal_memmove(dst, src, size);              \
    }                                                       \
    COMMON_INTERCEPTOR_ENTER(ctx, memcpy, dst, src, size);  \
    if (common_flags()->intercept_intrin) {                 \
      COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, size);       \
      COMMON_INTERCEPTOR_READ_RANGE(ctx, src, size);        \
    }                                                       \
    return REAL(memcpy)(dst, src, size);                    \
  }
#endif

#if SANITIZER_INTERCEPT_MEMSET
INTERCEPTOR(void *, memset, void *dst, int v, usize size) {
  void *ctx;
  COMMON_INTERCEPTOR_MEMSET_IMPL(ctx, dst, v, size);
}

#define INIT_MEMSET COMMON_INTERCEPT_FUNCTION(memset)
#else
#define INIT_MEMSET
#endif

#if SANITIZER_INTERCEPT_MEMMOVE
INTERCEPTOR(void *, memmove, void *dst, const void *src, usize size) {
  void *ctx;
  COMMON_INTERCEPTOR_MEMMOVE_IMPL(ctx, dst, src, size);
}

#define INIT_MEMMOVE COMMON_INTERCEPT_FUNCTION(memmove)
#else
#define INIT_MEMMOVE
#endif

#if SANITIZER_INTERCEPT_MEMCPY
INTERCEPTOR(void *, memcpy, void *dst, const void *src, usize size) {
  // On OS X, calling internal_memcpy here will cause memory corruptions,
  // because memcpy and memmove are actually aliases of the same
  // implementation.  We need to use internal_memmove here.
  // N.B.: If we switch this to internal_ we'll have to use internal_memmove
  // due to memcpy being an alias of memmove on OS X.
  void *ctx;
#if PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE
    COMMON_INTERCEPTOR_MEMCPY_IMPL(ctx, dst, src, size);
#else
    COMMON_INTERCEPTOR_MEMMOVE_IMPL(ctx, dst, src, size);
#endif
}

#define INIT_MEMCPY                                  \
  do {                                               \
    if (PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE) { \
      COMMON_INTERCEPT_FUNCTION(memcpy);             \
    } else {                                         \
      ASSIGN_REAL(memcpy, memmove);                  \
    }                                                \
    CHECK(REAL(memcpy));                             \
  } while (false)

#else
#define INIT_MEMCPY
#endif

#if SANITIZER_INTERCEPT_AEABI_MEM
INTERCEPTOR(void *, __aeabi_memmove, void *to, const void *from, usize size) {
  void *ctx;
  COMMON_INTERCEPTOR_MEMMOVE_IMPL(ctx, to, from, size);
}

INTERCEPTOR(void *, __aeabi_memmove4, void *to, const void *from, usize size) {
  void *ctx;
  COMMON_INTERCEPTOR_MEMMOVE_IMPL(ctx, to, from, size);
}

INTERCEPTOR(void *, __aeabi_memmove8, void *to, const void *from, usize size) {
  void *ctx;
  COMMON_INTERCEPTOR_MEMMOVE_IMPL(ctx, to, from, size);
}

INTERCEPTOR(void *, __aeabi_memcpy, void *to, const void *from, usize size) {
  void *ctx;
  COMMON_INTERCEPTOR_MEMCPY_IMPL(ctx, to, from, size);
}

INTERCEPTOR(void *, __aeabi_memcpy4, void *to, const void *from, usize size) {
  void *ctx;
  COMMON_INTERCEPTOR_MEMCPY_IMPL(ctx, to, from, size);
}

INTERCEPTOR(void *, __aeabi_memcpy8, void *to, const void *from, usize size) {
  void *ctx;
  COMMON_INTERCEPTOR_MEMCPY_IMPL(ctx, to, from, size);
}

// Note the argument order.
INTERCEPTOR(void *, __aeabi_memset, void *block, usize size, int c) {
  void *ctx;
  COMMON_INTERCEPTOR_MEMSET_IMPL(ctx, block, c, size);
}

INTERCEPTOR(void *, __aeabi_memset4, void *block, usize size, int c) {
  void *ctx;
  COMMON_INTERCEPTOR_MEMSET_IMPL(ctx, block, c, size);
}

INTERCEPTOR(void *, __aeabi_memset8, void *block, usize size, int c) {
  void *ctx;
  COMMON_INTERCEPTOR_MEMSET_IMPL(ctx, block, c, size);
}

INTERCEPTOR(void *, __aeabi_memclr, void *block, usize size) {
  void *ctx;
  COMMON_INTERCEPTOR_MEMSET_IMPL(ctx, block, 0, size);
}

INTERCEPTOR(void *, __aeabi_memclr4, void *block, usize size) {
  void *ctx;
  COMMON_INTERCEPTOR_MEMSET_IMPL(ctx, block, 0, size);
}

INTERCEPTOR(void *, __aeabi_memclr8, void *block, usize size) {
  void *ctx;
  COMMON_INTERCEPTOR_MEMSET_IMPL(ctx, block, 0, size);
}

#define INIT_AEABI_MEM                         \
  COMMON_INTERCEPT_FUNCTION(__aeabi_memmove);  \
  COMMON_INTERCEPT_FUNCTION(__aeabi_memmove4); \
  COMMON_INTERCEPT_FUNCTION(__aeabi_memmove8); \
  COMMON_INTERCEPT_FUNCTION(__aeabi_memcpy);   \
  COMMON_INTERCEPT_FUNCTION(__aeabi_memcpy4);  \
  COMMON_INTERCEPT_FUNCTION(__aeabi_memcpy8);  \
  COMMON_INTERCEPT_FUNCTION(__aeabi_memset);   \
  COMMON_INTERCEPT_FUNCTION(__aeabi_memset4);  \
  COMMON_INTERCEPT_FUNCTION(__aeabi_memset8);  \
  COMMON_INTERCEPT_FUNCTION(__aeabi_memclr);   \
  COMMON_INTERCEPT_FUNCTION(__aeabi_memclr4);  \
  COMMON_INTERCEPT_FUNCTION(__aeabi_memclr8);
#else
#define INIT_AEABI_MEM
#endif  // SANITIZER_INTERCEPT_AEABI_MEM

#if SANITIZER_INTERCEPT___BZERO
INTERCEPTOR(void *, __bzero, void *block, usize size) {
  void *ctx;
  COMMON_INTERCEPTOR_MEMSET_IMPL(ctx, block, 0, size);
}
#define INIT___BZERO COMMON_INTERCEPT_FUNCTION(__bzero);
#else
#define INIT___BZERO
#endif  // SANITIZER_INTERCEPT___BZERO

#if SANITIZER_INTERCEPT_BZERO
INTERCEPTOR(void *, bzero, void *block, usize size) {
  void *ctx;
  COMMON_INTERCEPTOR_MEMSET_IMPL(ctx, block, 0, size);
}
#define INIT_BZERO COMMON_INTERCEPT_FUNCTION(bzero);
#else
#define INIT_BZERO
#endif  // SANITIZER_INTERCEPT_BZERO

namespace __sanitizer {
// This does not need to be called if InitializeCommonInterceptors() is called.
void InitializeMemintrinsicInterceptors() {
  INIT_MEMSET;
  INIT_MEMMOVE;
  INIT_MEMCPY;
  INIT_AEABI_MEM;
  INIT___BZERO;
  INIT_BZERO;
}
}  // namespace __sanitizer
