#include "asan_mapping.h"
#include "sanitizer_common/sanitizer_asm.h"

#if defined(__x86_64__)
#include "sanitizer_common/sanitizer_platform.h"

.section .text
.file "asan_rtl_x86_64.S"

#define NAME(n, reg, op, s, i) n##_##op##_##i##_##s##_##reg

#define FNAME(reg, op, s, i) NAME(__asan_check, reg, op, s, i)
#define RLABEL(reg, op, s, i) NAME(.return, reg, op, s, i)
#define CLABEL(reg, op, s, i) NAME(.check, reg, op, s, i)
#define FLABEL(reg, op, s, i) NAME(.fail, reg, op, s, i)

#define BEGINF(reg, op, s, i) \
.globl  FNAME(reg, op, s, i) ;\
.hidden  FNAME(reg, op, s, i) ;\
ASM_TYPE_FUNCTION(FNAME(reg, op, s, i)) ;\
.cfi_startproc ;\
FNAME(reg, op, s, i): ;\

#define ENDF .cfi_endproc ;\

// Access check functions for 1,2 and 4 byte types, which require extra checks.
#define ASAN_MEMORY_ACCESS_INITIAL_CHECK_ADD(reg, op, s) \
        mov    %##reg,%r10 ;\
        shr    $0x3,%r10 ;\
        movsbl ASAN_SHADOW_OFFSET_CONST(%r10),%r10d ;\
        test   %r10d,%r10d ;\
        jne    CLABEL(reg, op, s, add) ;\
RLABEL(reg, op, s, add): ;\
        retq  ;\

#define ASAN_MEMORY_ACCESS_EXTRA_CHECK_1(reg, op, i) \
CLABEL(reg, op, 1, i): ;\
        push   %rcx ;\
        mov    %##reg,%rcx ;\
        and    $0x7,%ecx ;\
        cmp    %r10d,%ecx ;\
        pop    %rcx ;\
        jl     RLABEL(reg, op, 1, i);\
        mov    %##reg,%rdi ;\
        jmp    __asan_report_##op##1_asm ;\

#define ASAN_MEMORY_ACCESS_EXTRA_CHECK_2(reg, op, i) \
CLABEL(reg, op, 2, i): ;\
        push   %rcx ;\
        mov    %##reg,%rcx ;\
        and    $0x7,%ecx ;\
        add    $0x1,%ecx ;\
        cmp    %r10d,%ecx ;\
        pop    %rcx ;\
        jl     RLABEL(reg, op, 2, i);\
        mov    %##reg,%rdi ;\
        jmp    __asan_report_##op##2_asm ;\

#define ASAN_MEMORY_ACCESS_EXTRA_CHECK_4(reg, op, i) \
CLABEL(reg, op, 4, i): ;\
        push   %rcx ;\
        mov    %##reg,%rcx ;\
        and    $0x7,%ecx ;\
        add    $0x3,%ecx ;\
        cmp    %r10d,%ecx ;\
        pop    %rcx ;\
        jl     RLABEL(reg, op, 4, i);\
        mov    %##reg,%rdi ;\
        jmp    __asan_report_##op##4_asm ;\

#define ASAN_MEMORY_ACCESS_CALLBACK_ADD_1(reg, op) \
BEGINF(reg, op, 1, add) ;\
        ASAN_MEMORY_ACCESS_INITIAL_CHECK_ADD(reg, op, 1) ;\
        ASAN_MEMORY_ACCESS_EXTRA_CHECK_1(reg, op, add) ;\
ENDF

#define ASAN_MEMORY_ACCESS_CALLBACK_ADD_2(reg, op) \
BEGINF(reg, op, 2, add) ;\
        ASAN_MEMORY_ACCESS_INITIAL_CHECK_ADD(reg, op, 2) ;\
        ASAN_MEMORY_ACCESS_EXTRA_CHECK_2(reg, op, add) ;\
ENDF

#define ASAN_MEMORY_ACCESS_CALLBACK_ADD_4(reg, op) \
BEGINF(reg, op, 4, add) ;\
        ASAN_MEMORY_ACCESS_INITIAL_CHECK_ADD(reg, op, 4) ;\
        ASAN_MEMORY_ACCESS_EXTRA_CHECK_4(reg, op, add) ;\
ENDF

// Access check functions for 8 and 16 byte types: no extra checks required.
#define ASAN_MEMORY_ACCESS_CHECK_ADD(reg, op, s, c) \
        mov    %##reg,%r10 ;\
        shr    $0x3,%r10 ;\
        ##c    $0x0,ASAN_SHADOW_OFFSET_CONST(%r10) ;\
        jne    FLABEL(reg, op, s, add) ;\
        retq  ;\

#define ASAN_MEMORY_ACCESS_FAIL(reg, op, s, i) \
FLABEL(reg, op, s, i): ;\
        mov    %##reg,%rdi ;\
        jmp    __asan_report_##op##s##_asm;\

#define ASAN_MEMORY_ACCESS_CALLBACK_ADD_8(reg, op) \
BEGINF(reg, op, 8, add) ;\
        ASAN_MEMORY_ACCESS_CHECK_ADD(reg, op, 8, cmpb) ;\
        ASAN_MEMORY_ACCESS_FAIL(reg, op, 8, add) ;\
ENDF

#define ASAN_MEMORY_ACCESS_CALLBACK_ADD_16(reg, op) \
BEGINF(reg, op, 16, add) ;\
        ASAN_MEMORY_ACCESS_CHECK_ADD(reg, op, 16, cmpw) ;\
        ASAN_MEMORY_ACCESS_FAIL(reg, op, 16, add) ;\
ENDF

#define ASAN_MEMORY_ACCESS_CALLBACKS_ADD(reg) \
ASAN_MEMORY_ACCESS_CALLBACK_ADD_1(reg, load) \
ASAN_MEMORY_ACCESS_CALLBACK_ADD_1(reg, store) \
ASAN_MEMORY_ACCESS_CALLBACK_ADD_2(reg, load) \
ASAN_MEMORY_ACCESS_CALLBACK_ADD_2(reg, store) \
ASAN_MEMORY_ACCESS_CALLBACK_ADD_4(reg, load) \
ASAN_MEMORY_ACCESS_CALLBACK_ADD_4(reg, store) \
ASAN_MEMORY_ACCESS_CALLBACK_ADD_8(reg, load) \
ASAN_MEMORY_ACCESS_CALLBACK_ADD_8(reg, store) \
ASAN_MEMORY_ACCESS_CALLBACK_ADD_16(reg, load) \
ASAN_MEMORY_ACCESS_CALLBACK_ADD_16(reg, store) \


// Instantiate all but R10 and R11 callbacks. We are using PLTSafe class with
// the intrinsic, which guarantees that the code generation will never emit
// R10 or R11 callback.
ASAN_MEMORY_ACCESS_CALLBACKS_ADD(RAX)
ASAN_MEMORY_ACCESS_CALLBACKS_ADD(RBX)
ASAN_MEMORY_ACCESS_CALLBACKS_ADD(RCX)
ASAN_MEMORY_ACCESS_CALLBACKS_ADD(RDX)
ASAN_MEMORY_ACCESS_CALLBACKS_ADD(RSI)
ASAN_MEMORY_ACCESS_CALLBACKS_ADD(RDI)
ASAN_MEMORY_ACCESS_CALLBACKS_ADD(RBP)
ASAN_MEMORY_ACCESS_CALLBACKS_ADD(R8)
ASAN_MEMORY_ACCESS_CALLBACKS_ADD(R9)
ASAN_MEMORY_ACCESS_CALLBACKS_ADD(R12)
ASAN_MEMORY_ACCESS_CALLBACKS_ADD(R13)
ASAN_MEMORY_ACCESS_CALLBACKS_ADD(R14)
ASAN_MEMORY_ACCESS_CALLBACKS_ADD(R15)

#endif

NO_EXEC_STACK_DIRECTIVE
