text: #ifndef __STUB_H__
  #define __STUB_H__
  
  // Copyright (c) 2019 coolxv
  //
  // Distributed under the MIT Software License
  // See accompanying file LICENSE.txt or copy at
  // https://opensource.org/licenses/MIT
  
  #include "qd_asan_interface.h"
  
  #ifdef _WIN32
  //windows
  #include <windows.h>
  #include <processthreadsapi.h>
  #else
  //linux
  #include <unistd.h>
  #include <sys/mman.h>
  #endif
  //c
  #include <cstddef>
  #include <cstring>
  #include <cstdint>
  //c++
  #include <map>
  
  
  #define ADDR(CLASS_NAME,MEMBER_NAME) (&CLASS_NAME::MEMBER_NAME)
  
  /**********************************************************
                    replace function
  **********************************************************/
  #ifdef _WIN32
  #define CACHEFLUSH(addr, size) FlushInstructionCache(GetCurrentProcess(), addr, size)
  #else
  #define CACHEFLUSH(addr, size) __builtin___clear_cache(addr, addr + size)
  #endif
  
  #if defined(__aarch64__) || defined(_M_ARM64)
      #define CODESIZE 16U
      #define CODESIZE_MIN 16U
      #define CODESIZE_MAX CODESIZE
      // ldr x9, +8
      // br x9
      // addr
      #define REPLACE_FAR(t, fn, fn_stub)\
          ((uint32_t*)fn)[0] = 0x58000040 | 9;\
          ((uint32_t*)fn)[1] = 0xd61f0120 | (9 << 5);\
          memcpy((long long *)(fn + 8), &fn_stub, sizeof(long long));\
          CACHEFLUSH((char *)fn, CODESIZE);
      #define REPLACE_NEAR(t, fn, fn_stub) REPLACE_FAR(t, fn, fn_stub)
  #elif defined(__arm__) || defined(_M_ARM)
      #define CODESIZE 8U
      #define CODESIZE_MIN 8U
      #define CODESIZE_MAX CODESIZE
      // ldr pc, [pc, #-4]
      #define REPLACE_FAR(t, fn, fn_stub)\
          ((uint32_t*)fn)[0] = 0xe51ff004;\
          ((uint32_t*)fn)[1] = (uint32_t)fn_stub;\
          CACHEFLUSH((char *)fn, CODESIZE);
      #define REPLACE_NEAR(t, fn, fn_stub) REPLACE_FAR(t, fn, fn_stub)
  #elif defined(__mips64)
      #define CODESIZE 80U
      #define CODESIZE_MIN 80U
      #define CODESIZE_MAX CODESIZE
      //MIPS has no PC pointer, so you need to manually enter and exit the stack
      //120000ce0:  67bdffe0    daddiu  sp, sp, -32  //enter the stack
      //120000ce4:  ffbf0018    sd  ra, 24(sp)
      //120000ce8:  ffbe0010    sd  s8, 16(sp)
      //120000cec:  ffbc0008    sd  gp, 8(sp)
      //120000cf0:  03a0f025    move    s8, sp
  
      //120000d2c:  03c0e825    move    sp, s8  //exit the stack
      //120000d30:  dfbf0018    ld  ra, 24(sp)
      //120000d34:  dfbe0010    ld  s8, 16(sp)
      //120000d38:  dfbc0008    ld  gp, 8(sp)
      //120000d3c:  67bd0020    daddiu  sp, sp, 32
      //120000d40:  03e00008    jr  ra
  
      #define REPLACE_FAR(t, fn, fn_stub)\
          ((uint32_t *)fn)[0] = 0x67bdffe0;\
          ((uint32_t *)fn)[1] = 0xffbf0018;\
          ((uint32_t *)fn)[2] = 0xffbe0010;\
          ((uint32_t *)fn)[3] = 0xffbc0008;\
          ((uint32_t *)fn)[4] = 0x03a0f025;\
          *(uint16_t *)(fn + 20) = (long long)fn_stub >> 32;\
          *(fn + 22) = 0x19;\
          *(fn + 23) = 0x24;\
          ((uint32_t *)fn)[6] = 0x0019cc38;\
          *(uint16_t *)(fn + 28) = (long long)fn_stub >> 16;\
          *(fn + 30) = 0x39;\
          *(fn + 31) = 0x37;\
          ((uint32_t *)fn)[8] = 0x0019cc38;\
          *(uint16_t *)(fn + 36) = (long long)fn_stub;\
          *(fn + 38) = 0x39;\
          *(fn + 39) = 0x37;\
          ((uint32_t *)fn)[10] = 0x0320f809;\
          ((uint32_t *)fn)[11] = 0x00000000;\
          ((uint32_t *)fn)[12] = 0x00000000;\
          ((uint32_t *)fn)[13] = 0x03c0e825;\
          ((uint32_t *)fn)[14] = 0xdfbf0018;\
          ((uint32_t *)fn)[15] = 0xdfbe0010;\
          ((uint32_t *)fn)[16] = 0xdfbc0008;\
          ((uint32_t *)fn)[17] = 0x67bd0020;\
          ((uint32_t *)fn)[18] = 0x03e00008;\
          ((uint32_t *)fn)[19] = 0x00000000;\
          CACHEFLUSH((char *)fn, CODESIZE);
      #define REPLACE_NEAR(t, fn, fn_stub) REPLACE_FAR(t, fn, fn_stub)
  #elif defined(__thumb__) || defined(_M_THUMB)
      #error "Thumb is not supported"
  #else //__i386__ _x86_64__  _M_IX86 _M_X64
      #define CODESIZE 13U
      #define CODESIZE_MIN 5U
      #define CODESIZE_MAX CODESIZE
      //13 byte(jmp m16:64)
      //movabs $0x102030405060708,%r11
      //jmpq   *%r11
      #define REPLACE_FAR(t, fn, fn_stub) \
          do{\
          *fn = 0x49;\
          *(fn + 1) = 0xbb;\
          memcpy((int *)(fn + 2), &fn_stub, sizeof(long long));\
          *(fn + 10) = 0x41;\
          *(fn + 11) = 0xff;\
          *(fn + 12) = 0xe3;\
          }while(false)
          //CACHEFLUSH((char *)fn, CODESIZE);
  
      //5 byte(jmp rel32)
      #define REPLACE_NEAR(t, fn, fn_stub)\
          do{\
          const int addr = fn_stub - fn - CODESIZE_MIN;\
          *fn = 0xE9;\
          memcpy((int *)(fn + 1), &addr, sizeof(int));\
          }while(false)
          //CACHEFLUSH((char *)fn, CODESIZE);
  #endif
  
  struct func_stub
  {
      unsigned char *fn;
      unsigned char code_buf[CODESIZE];
      bool far_jmp;
  };
  
  class Stub
  {
  public:
      Stub()
      {
  #ifdef _WIN32
          SYSTEM_INFO sys_info;  
          GetSystemInfo(&sys_info);
          m_pagesize = sys_info.dwPageSize;
  #else
          m_pagesize = sysconf(_SC_PAGE_SIZE);
  #endif
  
          if (m_pagesize < 0)
          {
              m_pagesize = 4096;
          }
      }
      ~Stub()
      {
          std::map<unsigned char*,func_stub*>::iterator iter;
          struct func_stub *pstub;
          for(iter=m_result.begin(); iter != m_result.end(); iter++)
          {
              pstub = iter->second;
  #ifdef _WIN32
              DWORD lpflOldProtect;
              if(0 != VirtualProtect(pageof(pstub->fn), m_pagesize * 2, PAGE_EXECUTE_READWRITE, &lpflOldProtect))
  #else
              if (0 == mprotect(pageof(pstub->fn), m_pagesize * 2, PROT_READ | PROT_WRITE | PROT_EXEC))
  #endif
              {
  
                  if(pstub->far_jmp)
                  {
                      std::memcpy(pstub->fn, pstub->code_buf, CODESIZE_MAX);
                  }
                  else
                  {
                      std::memcpy(pstub->fn, pstub->code_buf, CODESIZE_MIN);
                  }
  
  #if defined(__aarch64__) || defined(_M_ARM64)
                  CACHEFLUSH((char *)pstub->fn, CODESIZE);
  #elif defined(__arm__) || defined(_M_ARM)
                  CACHEFLUSH((char *)pstub->fn, CODESIZE);
  #elif defined(__mips64)
                  CACHEFLUSH((char *)pstub->fn, CODESIZE);
  #else //__i386__ _x86_64__  _M_IX86 _M_X64
                  //CACHEFLUSH((char *)pstub->fn, CODESIZE);
  #endif
  
  #ifdef _WIN32
                  VirtualProtect(pageof(pstub->fn), m_pagesize * 2, PAGE_EXECUTE_READ, &lpflOldProtect);
  #else
                  mprotect(pageof(pstub->fn), m_pagesize * 2, PROT_READ | PROT_EXEC);
  #endif
              }
  
              iter->second  = NULL;
              delete pstub;
          }
  
          return;
      }
      template<typename T,typename S>
      ATTRIBUTE_NO_SANITIZE_THREAD
      void set(T addr, S addr_stub)
      {
          unsigned char * fn;
          unsigned char * fn_stub;
          fn = addrof(addr);
          fn_stub = addrof(addr_stub);
          struct func_stub *pstub;
          pstub = new func_stub;
          //start
          reset(fn); // 
          pstub->fn = fn;
  
          if(distanceof(fn, fn_stub))
          {
              pstub->far_jmp = true;
              std::memcpy(pstub->code_buf, fn, CODESIZE_MAX);
          }
          else
          {
              pstub->far_jmp = false;
              std::memcpy(pstub->code_buf, fn, CODESIZE_MIN);
          }
  
  #ifdef _WIN32
          DWORD lpflOldProtect;
          if(0 == VirtualProtect(pageof(pstub->fn), m_pagesize * 2, PAGE_EXECUTE_READWRITE, &lpflOldProtect))
  #else
          if (-1 == mprotect(pageof(pstub->fn), m_pagesize * 2, PROT_READ | PROT_WRITE | PROT_EXEC))
  #endif
          {
              throw("stub set memory protect to w+r+x failed");
          }
  
          if(pstub->far_jmp)
          {
              REPLACE_FAR(this, fn, fn_stub);
          }
          else
          {
              REPLACE_NEAR(this, fn, fn_stub);
          }
  
  
  #ifdef _WIN32
          if(0 == VirtualProtect(pageof(pstub->fn), m_pagesize * 2, PAGE_EXECUTE_READ, &lpflOldProtect))
  #else
          if (-1 == mprotect(pageof(pstub->fn), m_pagesize * 2, PROT_READ | PROT_EXEC))
  #endif
          {
              throw("stub set memory protect to r+x failed");
          }
          m_result.insert(std::pair<unsigned char*,func_stub*>(fn,pstub));
          return;
      }
  
      template<typename T>
      void reset(T addr)
      {
          unsigned char * fn;
          fn = addrof(addr);
  
          std::map<unsigned char*,func_stub*>::iterator iter = m_result.find(fn);
  
          if (iter == m_result.end())
          {
              return;
          }
          struct func_stub *pstub;
          pstub = iter->second;
  
  #ifdef _WIN32
          DWORD lpflOldProtect;
          if(0 == VirtualProtect(pageof(pstub->fn), m_pagesize * 2, PAGE_EXECUTE_READWRITE, &lpflOldProtect))
  #else
          if (-1 == mprotect(pageof(pstub->fn), m_pagesize * 2, PROT_READ | PROT_WRITE | PROT_EXEC))
  #endif
          {
              throw("stub reset memory protect to w+r+x failed");
          }
  
          if(pstub->far_jmp)
          {
              std::memcpy(pstub->fn, pstub->code_buf, CODESIZE_MAX);
          }
          else
          {
              std::memcpy(pstub->fn, pstub->code_buf, CODESIZE_MIN);
          }
  
  #if defined(__aarch64__) || defined(_M_ARM64)
                  CACHEFLUSH((char *)pstub->fn, CODESIZE);
  #elif defined(__arm__) || defined(_M_ARM)
                  CACHEFLUSH((char *)pstub->fn, CODESIZE);
  #elif defined(__mips64)
                  CACHEFLUSH((char *)pstub->fn, CODESIZE);
  #else //__i386__ _x86_64__  _M_IX86 _M_X64
                  //CACHEFLUSH((char *)pstub->fn, CODESIZE);
  #endif
  
  #ifdef _WIN32
          if(0 == VirtualProtect(pageof(pstub->fn), m_pagesize * 2, PAGE_EXECUTE_READ, &lpflOldProtect))
  #else
          if (-1 == mprotect(pageof(pstub->fn), m_pagesize * 2, PROT_READ | PROT_EXEC))
  #endif
          {
              throw("stub reset memory protect to r+x failed");
          }
          m_result.erase(iter);
          delete pstub;
  
          return;
      }
  private:
      char *pageof(unsigned char* addr)
      {
  #ifdef _WIN32
          return (char *)((unsigned long long)addr & ~(m_pagesize - 1));
  #else
          return (char *)((unsigned long)addr & ~(m_pagesize - 1));
  #endif
      }
  
      template<typename T>
      unsigned char* addrof(T addr)
      {
          union
          {
            T _s;
            unsigned char* _d;
          }ut;
          ut._s = addr;
          return ut._d;
      }
  
      bool distanceof(unsigned char* addr, unsigned char* addr_stub)
      {
          std::ptrdiff_t diff = addr_stub >= addr ? addr_stub - addr : addr - addr_stub;
          if((sizeof(addr) > 4) && (((diff >> 31) - 1) > 0))
          {
              return true;
          }
          return false;
      }
  
  private:
  #ifdef _WIN32
      //LLP64
      long long m_pagesize;
  #else
      //LP64
      long m_pagesize;
  #endif
      std::map<unsigned char*, func_stub*> m_result;
  
  };
  
  
  #endif
  