/* dhae_sdk_gnu.c - GCC / Clang / MinGW binding of the Dhaedalida RabbitStart/RabbitEnd
 * virtualization markers. Covers BOTH x86-64 (PE32+) and 32-bit x86 (PE32).
 *
 * The shipping sdk/dhae_sdk.c relies on compiler-specific tricks (an MSVC-x64
 * volatile 64-bit store compiling to `movabs`; MSVC-x86 __declspec(naked) +
 * _emit). GCC/Clang have neither, so this binding emits the magic explicitly
 * with inline asm. The 8 bytes that land in .text are identical to the MSVC
 * binding's, so the Dhaedalida GUI detects a function that calls these markers
 * exactly the same way (see sdk/dhae_marker_sig.h).
 *
 * The immediates / .byte values below MUST equal the six DHAE_SIG_* constants in
 * sdk/dhae_marker_sig.h, in that file's order.
 *
 * Build (add to your project):
 *     clang -O2 -c dhae_sdk_gnu.c                             (x64)
 *     clang --target=i686-pc-windows-msvc -O2 -c dhae_sdk_gnu.c   (x86)
 *     gcc -m32 -O2 -c dhae_sdk_gnu.c                          (x86, MinGW)
 * then link the object into your program and #include "dhae_sdk.h".
 */
#include "dhae_sdk.h"

#if defined(__x86_64__) || defined(_M_X64)

/* noinline keeps each marker a real, callable E8 target; the volatile asm is an
 * observable side effect the optimizer may not delete, so the call survives. */
__attribute__((noinline)) void RabbitStart(void) {
    __asm__ __volatile__("movabs $0xD11AE5C0DE765401, %%rax" : : : "rax");
}

__attribute__((noinline)) void RabbitEnd(void) {
    __asm__ __volatile__("movabs $0xD11AE5C0DE765402, %%rax" : : : "rax");
}

__attribute__((noinline)) void BearStart(void) {
    __asm__ __volatile__("movabs $0xD11AE5C0DE765405, %%rax" : : : "rax");
}

__attribute__((noinline)) void BearEnd(void) {
    __asm__ __volatile__("movabs $0xD11AE5C0DE765406, %%rax" : : : "rax");
}

#elif defined(__i386__) || defined(_M_IX86)

/* 32-bit x86 has no 64-bit immediate, so the magic is emitted as inline DATA
 * jumped over by a two-byte relative branch:
 *
 *     EB 08                          jmp short $+10   -> past the data
 *     01 54 76 DE C0 E5 1A D1        DHAE_SIG_RABBITSTART, little-endian
 *
 * DELIBERATELY NOT `naked`, and deliberately without a hand-written `ret`.
 * __attribute__((naked)) only exists for x86 from GCC 8; older GCC accepts the
 * attribute and SILENTLY IGNORES it ("attribute directive ignored"), which would
 * leave the compiler's own prologue in place while our `ret` skipped its
 * matching `pop %ebp` - a stack corruption that appears at run time and nowhere
 * else. Emitting only the jump-and-data is correct either way: the branch lands
 * on whatever epilogue the compiler generated, and that epilogue returns.
 *
 * Measured on this toolchain: at -O2 there is no prologue and the magic sits at
 * offset +2 (identical to the MSVC x86 binding); at -O0 the prologue is
 * `push %ebp; mov %esp,%ebp` and the magic sits at +5, with the branch landing
 * on `nop; pop %ebp; ret`. Both are far inside the scanner's 16-byte window.
 *
 * .byte is used rather than a `jmp` mnemonic and a label so the displacement
 * cannot depend on how the assembler sizes a local branch. */
__attribute__((noinline)) void RabbitStart(void) {
    __asm__ __volatile__(".byte 0xEB, 0x08, 0x01, 0x54, 0x76, 0xDE, 0xC0, 0xE5, 0x1A, 0xD1");
}

__attribute__((noinline)) void RabbitEnd(void) {
    __asm__ __volatile__(".byte 0xEB, 0x08, 0x02, 0x54, 0x76, 0xDE, 0xC0, 0xE5, 0x1A, 0xD1");
}

__attribute__((noinline)) void BearStart(void) {
    __asm__ __volatile__(".byte 0xEB, 0x08, 0x05, 0x54, 0x76, 0xDE, 0xC0, 0xE5, 0x1A, 0xD1");
}

__attribute__((noinline)) void BearEnd(void) {
    __asm__ __volatile__(".byte 0xEB, 0x08, 0x06, 0x54, 0x76, 0xDE, 0xC0, 0xE5, 0x1A, 0xD1");
}

#else
#error "Dhaedalida markers require x86-64 (PE32+) or 32-bit x86 (PE32)."
#endif
