//===-- asan_malloc_win_thunk.cpp
//-----------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// This file is a part of AddressSanitizer, an address sanity checker.
//
// Windows-specific malloc interception.
// This is included statically for projects statically linking
// with the C Runtime (/MT, /MTd) in order to provide ASAN-aware
// versions of the C allocation functions.
//===----------------------------------------------------------------------===//

#ifdef SANITIZER_STATIC_RUNTIME_THUNK
#  include "..\sanitizer_common\sanitizer_allocator_interface.h"
// #include "asan_win_thunk_common.h"

// Preserve stack traces with noinline.
#  define STATIC_MALLOC_INTERFACE __declspec(noinline)

extern "C" {
__declspec(dllimport) size_t __cdecl __asan_msize(void *ptr);
__declspec(dllimport) void __cdecl __asan_free(void *const ptr);
__declspec(dllimport) void *__cdecl __asan_malloc(const size_t size);
__declspec(dllimport) void *__cdecl __asan_calloc(const size_t nmemb,
                                                  const size_t size);
__declspec(dllimport) void *__cdecl __asan_realloc(void *const ptr,
                                                   const size_t size);
__declspec(dllimport) void *__cdecl __asan_recalloc(void *const ptr,
                                                    const size_t nmemb,
                                                    const size_t size);

// Avoid tailcall optimization to preserve stack frames.
#  pragma optimize("", off)

// _msize
STATIC_MALLOC_INTERFACE size_t _msize(void *ptr) { return __asan_msize(ptr); }

STATIC_MALLOC_INTERFACE size_t _msize_base(void *ptr) {
  return __asan_msize(ptr);
}

STATIC_MALLOC_INTERFACE size_t _msize_dbg(void *ptr) {
  return __asan_msize(ptr);
}

// free
STATIC_MALLOC_INTERFACE void free(void *const ptr) { return __asan_free(ptr); }

STATIC_MALLOC_INTERFACE void _free_base(void *const ptr) {
  return __asan_free(ptr);
}

STATIC_MALLOC_INTERFACE void _free_dbg(void *const ptr) {
  return __asan_free(ptr);
}

// malloc
STATIC_MALLOC_INTERFACE void *malloc(const size_t size) {
  return __asan_malloc(size);
}

STATIC_MALLOC_INTERFACE void *_malloc_base(const size_t size) {
  return __asan_malloc(size);
}

STATIC_MALLOC_INTERFACE void *_malloc_dbg(const size_t size) {
  return __asan_malloc(size);
}

// calloc
STATIC_MALLOC_INTERFACE void *calloc(const size_t nmemb, const size_t size) {
  return __asan_calloc(nmemb, size);
}

STATIC_MALLOC_INTERFACE void *_calloc_base(const size_t nmemb,
                                           const size_t size) {
  return __asan_calloc(nmemb, size);
}

STATIC_MALLOC_INTERFACE void *_calloc_impl(const size_t nmemb,
                                           const size_t size,
                                           int *const errno_tmp) {
  // Provided by legacy msvcrt.
  (void)errno_tmp;

  return __asan_calloc(nmemb, size);
}

STATIC_MALLOC_INTERFACE void *_calloc_dbg(const size_t nmemb, const size_t size,
                                          int, const char *, int) {
  return __asan_calloc(nmemb, size);
}

// realloc
STATIC_MALLOC_INTERFACE void *realloc(void *const ptr, const size_t size) {
  return __asan_realloc(ptr, size);
}

STATIC_MALLOC_INTERFACE void *_realloc_base(void *const ptr,
                                            const size_t size) {
  return __asan_realloc(ptr, size);
}

STATIC_MALLOC_INTERFACE void *_realloc_dbg(void *const ptr, const size_t size,
                                           int, const char *, int) {
  return __asan_realloc(ptr, size);
}

// recalloc
STATIC_MALLOC_INTERFACE void *_recalloc(void *const ptr, const size_t nmemb,
                                        const size_t size) {
  return __asan_recalloc(ptr, nmemb, size);
}

STATIC_MALLOC_INTERFACE void *_recalloc_base(void *const ptr,
                                             const size_t nmemb,
                                             const size_t size) {
  return __asan_recalloc(ptr, nmemb, size);
}

STATIC_MALLOC_INTERFACE void *_recalloc_dbg(void *const ptr, const size_t nmemb,
                                            const size_t size, int,
                                            const char *, int) {
  return __asan_recalloc(ptr, nmemb, size);
}

// expand
STATIC_MALLOC_INTERFACE void *_expand(void *, size_t) {
  // _expand is used in realloc-like functions to resize the buffer if possible.
  // We don't want memory to stand still while resizing buffers, so return 0.
  return nullptr;
}

STATIC_MALLOC_INTERFACE void *_expand_dbg(void *, size_t, int, const char *,
                                          int) {
  return nullptr;
}

// We need to provide symbols for all the debug CRT functions if we decide to
// provide any. Most of these functions make no sense under ASan and so we
// make them no-ops.
long _CrtSetBreakAlloc(long const) { return ~0; }

void _CrtSetDbgBlockType(void *const, int const) { return; }

typedef int(__cdecl *CRT_ALLOC_HOOK)(int, void *, size_t, int, long,
                                     const unsigned char *, int);

CRT_ALLOC_HOOK _CrtGetAllocHook() { return nullptr; }

CRT_ALLOC_HOOK _CrtSetAllocHook(CRT_ALLOC_HOOK const hook) { return hook; }

int _CrtCheckMemory() { return 1; }

int _CrtSetDbgFlag(int const new_bits) { return new_bits; }

typedef void (*CrtDoForAllClientObjectsCallback)(void *, void *);

void _CrtDoForAllClientObjects(CrtDoForAllClientObjectsCallback const,
                               void *const) {
  return;
}

int _CrtIsValidPointer(void const *const p, unsigned int const, int const) {
  return p != nullptr;
}

int _CrtIsValidHeapPointer(void const *const block) {
  if (!block) {
    return 0;
  }

  return __sanitizer_get_ownership(block);
}

int _CrtIsMemoryBlock(void const *const, unsigned const, long *const,
                      char **const, int *const) {
  return 0;
}

int _CrtReportBlockType(void const *const) { return -1; }

typedef void(__cdecl *CRT_DUMP_CLIENT)(void *, size_t);

CRT_DUMP_CLIENT _CrtGetDumpClient() { return nullptr; }

CRT_DUMP_CLIENT _CrtSetDumpClient(CRT_DUMP_CLIENT new_client) {
  return new_client;
}

void _CrtMemCheckpoint(void *const) { return; }

int _CrtMemDifference(void *const, void const *const, void const *const) {
  return 0;
}

void _CrtMemDumpAllObjectsSince(void const *const) { return; }

int _CrtDumpMemoryLeaks() { return 0; }

void _CrtMemDumpStatistics(void const *const) { return; }

int _crtDbgFlag{0};
long _crtBreakAlloc{-1};
CRT_DUMP_CLIENT _pfnDumpClient{nullptr};

int *__p__crtDbgFlag() { return &_crtDbgFlag; }

long *__p__crtBreakAlloc() { return &_crtBreakAlloc; }

// TODO: These were added upstream but conflict with definitions in ucrtbased.
// int _CrtDbgReport(int, const char *, int, const char *, const char *, ...) {
//   ShowStatsAndAbort();
// }
//
// int _CrtDbgReportW(int reportType, const wchar_t *, int, const wchar_t *,
//                    const wchar_t *, ...) {
//   ShowStatsAndAbort();
// }
//
// int _CrtSetReportMode(int, int) { return 0; }

}  // extern "C"
#endif  // SANITIZER_STATIC_RUNTIME_THUNK
