/* ******************************************************
 * Copyright (c) 2014-2021 Google, Inc.  All rights reserved.
 * ******************************************************/

/*
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * * Redistributions of source code must retain the above copyright notice,
 *   this list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 *
 * * Neither the name of VMware, Inc. nor the names of its contributors may be
 *   used to endorse or promote products derived from this software without
 *   specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL VMWARE, INC. OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 */

#ifndef _DYNAMORIO_ANNOTATIONS_ASM_H_
#define _DYNAMORIO_ANNOTATIONS_ASM_H_ 1

/* This header defines the annotation functions and code sequences. For a high-level
 * overview, see https://dynamorio.org/page_annotations.html.
 */

/* To simplify project configuration, this pragma excludes the file from GCC warnings. */
#ifdef __GNUC__
#    pragma GCC system_header
#endif

#include <stddef.h>
#ifdef _MSC_VER
#    include <intrin.h>
#    pragma warning(disable : 4100) /* disable "unreferenced formal parameter" warning \
                                     */
#endif

#ifndef DYNAMORIO_ANNOTATIONS_X86
#    if defined(__x86_64__) || defined(__i386__) || defined(_M_X64) || defined(_M_IX86)
#        define DYNAMORIO_ANNOTATIONS_X86 1
#    endif
#endif

#ifndef DYNAMORIO_ANNOTATIONS_X64
#    ifdef _MSC_VER
#        ifdef _WIN64
#            define DYNAMORIO_ANNOTATIONS_X64 1
#        endif
#    else
#        ifdef __LP64__
#            define DYNAMORIO_ANNOTATIONS_X64 1
#        endif
#    endif
#endif

#ifdef _MSC_VER
#    ifdef __cplusplus
#        define EXTERN extern "C"
#    else
#        define EXTERN extern
#    endif
#else
#    ifdef __cplusplus
#        define EXTERN_C extern "C"
#    else
#        define EXTERN_C
#    endif
#endif

#if !defined(DYNAMORIO_ANNOTATIONS_X86) || defined(__clang__)
/* TODO i#1672: Add annotation support to AArchXX.
 * For now, we provide a fallback so we can build the annotation-concurrency
 * app for use with drcachesim tests.
 * We do the same for clang, which has no "asm goto".
 */
#    define DR_ANNOTATION_OR_NATIVE(annotation, native_version, ...) /* Nothing. */
#    define DR_DECLARE_ANNOTATION(return_type, annotation, parameters) \
        return_type annotation parameters
#    define DR_DEFINE_ANNOTATION(return_type, annotation, parameters, body) \
        return_type annotation parameters                                   \
        {                                                                   \
            body;                                                           \
        }

#elif defined(_MSC_VER) /* Microsoft Visual Studio */
#    define PASTE1(x, y) x##y
#    define PASTE(x, y) PASTE1(x, y)

#    ifdef DYNAMORIO_ANNOTATIONS_X64
/* The value of this intrinsic is assumed to be non-zero. In the rare case of
 * annotations occurring within managed code (i.e., C# or VB), it may not properly
 * be the address of the return address on the stack, but it still should never be
 * zero.
 */
#        pragma intrinsic(_AddressOfReturnAddress, __debugbreak, __int2c, _m_prefetchw)
#        define GET_RETURN_PTR _AddressOfReturnAddress
#        define DR_DEFINE_ANNOTATION_LABELS(annotation, return_type)             \
            const char *annotation##_expression_label =                          \
                "dynamorio-annotation:expression:" #return_type ":" #annotation; \
            const char *annotation##_statement_label =                           \
                "dynamorio-annotation:statement:" #return_type ":" #annotation;
#        define DR_EXTERN_ANNOTATION_LABELS(annotation, return_type) \
            EXTERN const char *annotation##_expression_label;        \
            EXTERN const char *annotation##_statement_label;
/* The magic numbers for the head and tail are specially selected to establish
 * immovable "bookends" on the annotation around which the compiler and optimizers
 * will not reorder instructions. The values 0xfffffffffffffff0 and
 * 0xfffffffffffffff1 are chosen because: (1) the stack can never appear in this
 * address range in Windows x64, and (2) there is a little space below
 * 0xffffffffffffffff to prevent optimizations based on the number of possible
 * values greater than the magic number. The factor of 2 prevents optimizers from
 * sharing a single instance of the conditional expression between the tail of one
 * annotation and the head of the next when they appear on subsequent lines. The
 * compiler will always reduce the magic number expression to a single constant,
 * even in a debug build with all optimizations disabled.
 */
#        define DR_ANNOTATION_STATEMENT_HEAD (0xfffffffffffffff1 - (2 * __LINE__))
#        define DR_ANNOTATION_STATEMENT_TAIL (0xfffffffffffffff0 - (2 * __LINE__))
#        define DR_ANNOTATION_OR_NATIVE(annotation, native_version, ...)                 \
            do {                                                                         \
                if ((unsigned __int64)GET_RETURN_PTR() > DR_ANNOTATION_STATEMENT_HEAD) { \
                    __int2c();                                                           \
                    _m_prefetchw(annotation##_statement_label);                          \
                    __debugbreak();                                                      \
                    annotation(__VA_ARGS__);                                             \
                } else {                                                                 \
                    native_version;                                                      \
                }                                                                        \
            } while ((unsigned __int64)GET_RETURN_PTR() > DR_ANNOTATION_STATEMENT_TAIL)
#        define DR_ANNOTATION_FUNCTION(annotation, body)     \
            if (GET_RETURN_PTR() == (void *)0) {             \
                __int2c();                                   \
                _m_prefetchw(annotation##_expression_label); \
                __debugbreak();                              \
            } else {                                         \
                body;                                        \
            }
#    else
/* The assembly sequence for this platform begins with absolute bytes "0xeb 0x06"
 * (jmp +6) to facilitate an optimized detection algorithm in the DR interpreter.
 * The target of this jump is always the following jump, i.e. (mov=5 + pop/nop=1)
 * => 6.
 */
#        define DR_DEFINE_ANNOTATION_LABELS(annotation, return_type) \
            const char *annotation##_label =                         \
                "dynamorio-annotation:" #return_type ":" #annotation;
#        define DR_EXTERN_ANNOTATION_LABELS(annotation, return_type) \
            EXTERN const char *annotation##_label;
#        define DR_ANNOTATION_OR_NATIVE_INSTANCE(unique_id, annotation, native_version, \
                                                 ...)                                   \
            {                                                                           \
                __asm { \
            __asm _emit 0xeb \
            __asm _emit 0x06 \
            __asm mov eax, annotation##_label \
            __asm pop eax \
            __asm jmp PASTE(native_run, unique_id) }   \
                annotation(__VA_ARGS__);                                                \
                goto PASTE(native_end_marker, unique_id);                               \
                PASTE(native_run, unique_id)                                            \
                    : native_version;                                                   \
                PASTE(native_end_marker, unique_id)                                     \
                    :;                                                                  \
            }
#        define DR_ANNOTATION_OR_NATIVE(annotation, native_version, ...)              \
            DR_ANNOTATION_OR_NATIVE_INSTANCE(__COUNTER__, annotation, native_version, \
                                             __VA_ARGS__)
#        define DR_ANNOTATION_FUNCTION_INSTANCE(unique_id, annotation, body)  \
            __asm { \
        __asm _emit 0xeb \
        __asm _emit 0x06 \
        __asm mov eax, annotation##_label \
        __asm nop \
        __asm jmp PASTE(native_run, unique_id) } \
            goto PASTE(native_end_marker, unique_id);                         \
            PASTE(native_run, unique_id)                                      \
                : body;                                                       \
            PASTE(native_end_marker, unique_id)                               \
                :;
#        define DR_ANNOTATION_FUNCTION(annotation, body) \
            DR_ANNOTATION_FUNCTION_INSTANCE(__COUNTER__, annotation, body)
#    endif
#    define DR_DECLARE_ANNOTATION(return_type, annotation, parameters) \
        DR_EXTERN_ANNOTATION_LABELS(annotation, return_type)           \
        return_type __fastcall annotation parameters
#    define DR_DEFINE_ANNOTATION(return_type, annotation, parameters, body) \
        DR_DEFINE_ANNOTATION_LABELS(annotation, return_type)                \
        return_type __fastcall annotation parameters                        \
        {                                                                   \
            DR_ANNOTATION_FUNCTION(annotation, body)                        \
        }
#else /* GCC or Intel (Unix or Windows) */
/* *************************************************************************************
 * The assembly sequence for this platform begins with absolute bytes "0xeb 0xNN"
 * (jmp +LABEL_REFERENCE_LENGTH) to facilitate an optimized detection algorithm in
 * the DR interpreter. The target of this jump is always the following jump.
 * Each reference to _GLOBAL_OFFSET_TABLE_ is adjusted by the linker to be
 * XIP-relative, and no relocations are generated for the operand.
 */
#    ifdef DYNAMORIO_ANNOTATIONS_X64
/* (mov=8 + bsf/bsr=9) => 0x11 */
#        define LABEL_REFERENCE_LENGTH "0x11"
#        define LABEL_REFERENCE_REGISTER "%rax"
/* Defensive clobber list: only rax is actually clobbered, but all
 * argument-passing registers are included to discourage optimizers from using
 * them prior to the annotation. Note that optimizations should be disabled for
 * files that define annotation functions, so this is just an extra precaution.
 */
#        define ANNOTATION_FUNCTION_CLOBBER_LIST \
            "%rax", "%rcx", "%rdx", "%rsi", "%rdi", "%r8", "%r9"
#        define _CALL_TYPE
#        define ANNOT_LBL(annot, base) #        annot "_label@GOT"
#    else
/* (mov=5 + bsf/bsr=7) => 0xc */
#        define LABEL_REFERENCE_LENGTH "0xc"
#        define LABEL_REFERENCE_REGISTER "%eax"
/* Defensive clobber list: only rax is actually clobbered, but all
 * argument-passing registers are included to discourage optimizers from using
 * them prior to the annotation. Note that optimizations should be disabled for
 * files that define annotation functions, so this is just an extra precaution.
 */
#        define ANNOTATION_FUNCTION_CLOBBER_LIST "%rax", "%rcx", "%rdx"
#        define _CALL_TYPE , fastcall
/* i#2050: i386 ABI forces us to use the base register as an offset into GOT, which is
 * not the case for 64-bit.
 */
#        define ANNOT_LBL(annot, base) #        annot "_label@GOT(%" base ")"
#    endif
#    define DR_ANNOTATION_ATTRIBUTES \
        __attribute__((noinline, visibility("hidden") _CALL_TYPE))
#    define DR_WEAK_DECLARATION __attribute__((weak))
/* GCC extension "__label__" confines the named label to the block scope. */
#    define DR_ANNOTATION_OR_NATIVE(annotation, native_version, ...) \
        ({                                                           \
            __label__ native_run, native_end_marker;                 \
            extern const char *annotation##_label;                   \
            __asm__ volatile goto(".byte 0xeb; .byte " LABEL_REFERENCE_LENGTH "; \
                            mov _GLOBAL_OFFSET_TABLE_,%" LABEL_REFERENCE_REGISTER "; \
                            bsf " ANNOT_LBL(annotation, LABEL_REFERENCE_REGISTER) ", \
                               %" LABEL_REFERENCE_REGISTER "; \
                            jmp %l0;" ::                             \
                                      : LABEL_REFERENCE_REGISTER     \
                                  : native_run);                     \
            annotation(__VA_ARGS__);                                 \
            goto native_end_marker;                                  \
        native_run:                                                  \
            native_version;                                          \
        native_end_marker:;                                          \
        })

#    define DR_DECLARE_ANNOTATION(return_type, annotation, parameters) \
        DR_ANNOTATION_ATTRIBUTES return_type annotation parameters DR_WEAK_DECLARATION
#    define DR_DEFINE_ANNOTATION(return_type, annotation, parameters, body)          \
        EXTERN_C const char *annotation##_label =                                    \
            "dynamorio-annotation:" #return_type ":" #annotation;                    \
        DR_ANNOTATION_ATTRIBUTES return_type annotation parameters                   \
        {                                                                            \
            __label__ native_run, native_end_marker;                                 \
            extern const char *annotation##_label;                                   \
            __asm__ volatile goto(".byte 0xeb; .byte " LABEL_REFERENCE_LENGTH "; \
                               mov _GLOBAL_OFFSET_TABLE_,%" LABEL_REFERENCE_REGISTER \
                                  "; \
                               bsr " ANNOT_LBL(annotation,                           \
                                               LABEL_REFERENCE_REGISTER) ", \
                                  %" LABEL_REFERENCE_REGISTER "; \
                               jmp %l0;" ::                                          \
                                      : ANNOTATION_FUNCTION_CLOBBER_LIST             \
                                  : native_run);                                     \
            goto native_end_marker;                                                  \
        native_run:                                                                  \
            body;                                                                    \
        native_end_marker:;                                                          \
        }
#endif

#define DR_ANNOTATION(annotation, ...) DR_ANNOTATION_OR_NATIVE(annotation, , __VA_ARGS__)

#endif
