## dhaemarker.nim - Dhaedalida RabbitStart / RabbitEnd virtualization markers for Nim.
## Covers BOTH x86-64 (PE32+) and 32-bit x86 (PE32).
##
## The markers are emitted as C functions with GCC/Clang inline asm, so the 8
## magic bytes land verbatim in .text and the Dhaedalida GUI detects a caller
## exactly like the C binding (see sdk/dhae_marker_sig.h). The values below MUST
## equal DHAE_SIG_RABBITSTART / DHAE_SIG_RABBITEND there.
##
##     x64:  movabs rax, <magic>       -> 48 B8 <magic>
##     x86:  .byte 0xEB, 0x08, <magic> -> jmp short $+10 ; <data>
##
## On 32-bit there is no 64-bit immediate to hide the magic in, so it is emitted
## as inline DATA and jumped over. No `ret` of our own is emitted: the branch
## lands on whatever epilogue the C compiler generated, which returns correctly
## with or without a prologue.
##
## Usage - bracket the proc you want protected:
##
##     import dhaemarker
##     proc validateLicense(key: string): bool =
##       RabbitStart()
##       # ... sensitive code ...
##       RabbitEnd()
##       result = true
##
## Backend: use the gcc or clang C backend (Nim's default on Windows via MinGW,
## or `nim c --cc:clang`). The inline asm is not valid under the MSVC (vcc)
## backend - for that toolchain, link sdk/dhae_sdk.c and `importc` the two
## symbols instead; it covers both bitnesses natively.
##
## Build optimized: `nim c -d:release yourapp.nim`, adding `--cpu:i386` (plus
## `--passC:-m32 --passL:-m32` on a 64-bit MinGW) for a 32-bit binary.

{.emit: """
#if defined(__x86_64__) || defined(_M_X64)
__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)
__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
""".}

proc RabbitStart*() {.importc, cdecl.}
  ## Marks the start of a region to virtualize (the enclosing proc is protected).
proc RabbitEnd*() {.importc, cdecl.}
  ## Marks the end of a region to virtualize.
proc BearStart*() {.importc, cdecl.}
  ## Marks the start of a region to protect with the STACK VM: the strongest of the
  ## three modes and by far the slowest (a bytecode interpreter -- budget roughly
  ## 5x the cost of Rabbit, so this is for a licence check, never a hot loop).
  ## 64-bit only in this version -- a 32-bit target virtualizes such a proc instead.
  ## A proc carrying two marker pairs is REFUSED, not resolved.
proc BearEnd*() {.importc, cdecl.}
  ## Marks the end of a region to protect with Bear.
