libclamav/bytecode_vm.c
e0c4fd85
 /*
  *  Execute ClamAV bytecode.
  *
c442ca9c
  *  Copyright (C) 2013-2019 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
  *  Copyright (C) 2009-2013 Sourcefire, Inc.
e0c4fd85
  *
  *  Authors: Török Edvin
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
  *  published by the Free Software Foundation.
  *
  *  This program is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  *  MA 02110-1301, USA.
  */
 #if HAVE_CONFIG_H
 #include "clamav-config.h"
 #endif
b2e7c931
 
e0c4fd85
 #include "clamav.h"
 #include "others.h"
 #include "bytecode.h"
 #include "bytecode_priv.h"
cf0cd429
 #include "type_desc.h"
e0c4fd85
 #include "readdb.h"
 #include <string.h>
145ca5cb
 #ifndef _WIN32
 #include <sys/time.h>
 #endif
a15fc904
 #include "bytecode_api_impl.h"
 #include "disasm-common.h"
e0c4fd85
 
8596e278
 /* Enable this to catch more bugs in the RC phase */
15fd260a
 #define CL_BYTECODE_SAFE
8596e278
 
15fd260a
 #ifdef CL_BYTECODE_SAFE
e0c4fd85
 /* These checks will also be done by the bytecode verifier, but for
  * debugging purposes we have explicit checks, these should never fail! */
15fd260a
 #ifdef CL_DEBUG
9cbece5c
 static int never_inline bcfail(const char *msg, long a, long b,
90c0acc7
                   const char *file, unsigned line)
e0c4fd85
 {
7f6b55a1
     cli_warnmsg("bytecode: check failed %s (%lx and %lx) at %s:%u\n", msg, a, b, file, line);
e0c4fd85
     return CL_EARG;
 }
15fd260a
 #else
 #define bcfail(msg,a,b,f,l) CL_EBYTECODE
 #endif
82ca2ab4
 
e0c4fd85
 #define CHECK_FUNCID(funcid) do { if (funcid >= bc->num_func) return \
     bcfail("funcid out of bounds!",funcid, bc->num_func,__FILE__,__LINE__); } while(0)
cf0cd429
 #define CHECK_APIID(funcid) do { if (funcid >= cli_apicall_maxapi) return \
     bcfail("APIid out of bounds!",funcid, cli_apicall_maxapi,__FILE__,__LINE__); } while(0)
ec5cccc7
 #define CHECK_EQ(a, b) do { if ((a) != (b)) return \
     bcfail("Values "#a" and "#b" don't match!",(a),(b),__FILE__,__LINE__); } while(0)
 #define CHECK_GT(a, b) do {if ((a) <= (b)) return \
     bcfail("Condition failed "#a" > "#b,(a),(b), __FILE__, __LINE__); } while(0)
15fd260a
 
e0c4fd85
 #else
9cbece5c
 static inline int bcfail(const char *msg, long a, long b,
90c0acc7
                          const char *file, unsigned line) {}
82ca2ab4
 #define CHECK_FUNCID(x);
cf0cd429
 #define CHECK_APIID(x);
e0c4fd85
 #define CHECK_EQ(a,b)
 #define CHECK_GT(a,b)
 #endif
48692aff
 #if 0 /* too verbose, use #ifdef CL_DEBUG if needed */
15fd260a
 #define CHECK_UNREACHABLE do { cli_dbgmsg("bytecode: unreachable executed!\n"); return CL_EBYTECODE; } while(0)
a15fc904
 #define TRACE_PTR(ptr, s) cli_dbgmsg("bytecode trace: ptr %llx, +%x\n", ptr, s);
15fd260a
 #define TRACE_R(x) cli_dbgmsg("bytecode trace: %u, read %llx\n", pc, (long long)x);
 #define TRACE_W(x, w, p) cli_dbgmsg("bytecode trace: %u, write%d @%u %llx\n", pc, p, w, (long long)(x));
 #define TRACE_EXEC(id, dest, ty, stack) cli_dbgmsg("bytecode trace: executing %d, -> %u (%u); %u\n", id, dest, ty, stack)
1678ef9e
 #define TRACE_API(s, dest, ty, stack) cli_dbgmsg("bytecode trace: executing %s, -> %u (%u); %u\n", s, dest, ty, stack)
15fd260a
 #else
 #define CHECK_UNREACHABLE return CL_EBYTECODE
a15fc904
 #define TRACE_PTR(ptr, s)
15fd260a
 #define TRACE_R(x)
 #define TRACE_W(x, w, p)
 #define TRACE_EXEC(id, dest, ty, stack)
1678ef9e
 #define TRACE_API(s, dest, ty, stack)
15fd260a
 #endif
 
cfec3d90
 #define SIGNEXT(a, from) CLI_SRS(((int64_t)(a)) << (64-(from)), (64-(from)))
 
6922903a
 #ifdef CL_DEBUG
 #undef always_inline
 #define always_inline
 #endif
 
cfec3d90
 static always_inline int jump(const struct cli_bc_func *func, uint16_t bbid, struct cli_bc_bb **bb, const struct cli_bc_inst **inst,
90c0acc7
                 unsigned *bb_inst)
8c0933ce
 {
     CHECK_GT(func->numBB, bbid);
     *bb = &func->BB[bbid];
     *inst = (*bb)->insts;
     *bb_inst = 0;
ec5cccc7
     return 0;
8c0933ce
 }
 
349e6e11
 #define STACK_CHUNKSIZE 65536
cfec3d90
 
 struct stack_chunk {
     struct stack_chunk *prev;
     unsigned used;
     union {
90c0acc7
         void *align;
         char data[STACK_CHUNKSIZE];
cfec3d90
     } u;
 };
 
 struct stack {
     struct stack_chunk* chunk;
     uint16_t last_size;
 };
 
ac17fc6c
 /* type with largest alignment that we use (in general it is a long double, but
  * thats too big alignment for us) */
 typedef uint64_t align_t;
 
cfec3d90
 static always_inline void* cli_stack_alloc(struct stack *stack, unsigned bytes)
 {
     struct stack_chunk *chunk = stack->chunk;
     uint16_t last_size_off;
 
     /* last_size is stored after data */
     /* align bytes to pointer size */
ac17fc6c
     bytes = (bytes + sizeof(uint16_t) + sizeof(align_t)) & ~(sizeof(align_t)-1);
cfec3d90
     last_size_off = bytes - 2;
 
     if (chunk && (chunk->used + bytes <= STACK_CHUNKSIZE)) {
90c0acc7
         /* there is still room in this chunk */
         void *ret;
cfec3d90
 
90c0acc7
         *(uint16_t*)&chunk->u.data[chunk->used + last_size_off] = stack->last_size;
         stack->last_size = bytes/sizeof(align_t);
cfec3d90
 
90c0acc7
         ret = chunk->u.data + chunk->used;
         chunk->used += bytes;
         return ret;
cfec3d90
     }
 
     if(bytes >= STACK_CHUNKSIZE) {
90c0acc7
         cli_warnmsg("cli_stack_alloc: Attempt to allocate more than STACK_CHUNKSIZE bytes: %u!\n", bytes);
         return NULL;
cfec3d90
     }
     /* not enough room here, allocate new chunk */
     chunk = cli_malloc(sizeof(*stack->chunk));
241e7eb1
     if (!chunk) {
48692aff
         cli_warnmsg("cli_stack_alloc: Unable to allocate memory for stack-chunk: bytes: %zu!\n", sizeof(*stack->chunk));
241e7eb1
         return NULL;
     }
cfec3d90
 
     *(uint16_t*)&chunk->u.data[last_size_off] = stack->last_size;
ac17fc6c
     stack->last_size = bytes/sizeof(align_t);
cfec3d90
 
     chunk->used = bytes;
     chunk->prev = stack->chunk;
     stack->chunk = chunk;
     return chunk->u.data;
 }
 
 static always_inline void cli_stack_free(struct stack *stack, void *data)
 {
     uint16_t last_size;
     struct stack_chunk *chunk = stack->chunk;
     if (!chunk) {
90c0acc7
         cli_warnmsg("cli_stack_free: stack empty!\n");
         return;
cfec3d90
     }
ac17fc6c
     if ((chunk->u.data + chunk->used) != ((char*)data + stack->last_size*sizeof(align_t))) {
90c0acc7
         cli_warnmsg("cli_stack_free: wrong free order: %p, expected %p\n",
                    data, chunk->u.data + chunk->used - stack->last_size*sizeof(align_t));
         return;
cfec3d90
     }
     last_size = *(uint16_t*)&chunk->u.data[chunk->used-2];
ac17fc6c
     if (chunk->used < stack->last_size*sizeof(align_t)) {
90c0acc7
         cli_warnmsg("cli_stack_free: last_size is corrupt!\n");
         return;
cfec3d90
     }
ac17fc6c
     chunk->used -= stack->last_size*sizeof(align_t);
cfec3d90
     stack->last_size = last_size;
     if (!chunk->used) {
90c0acc7
         stack->chunk = chunk->prev;
         free(chunk);
cfec3d90
     }
 }
 
 static void cli_stack_destroy(struct stack *stack)
 {
     struct stack_chunk *chunk = stack->chunk;
     while (chunk) {
90c0acc7
         stack->chunk = chunk->prev;
         free(chunk);
         chunk = stack->chunk;
cfec3d90
     }
 }
 
 struct stack_entry {
     struct stack_entry *prev;
     const struct cli_bc_func *func;
6922903a
     operand_t ret;
cfec3d90
     unsigned bb_inst;
2545f976
     struct cli_bc_bb *bb;
6922903a
     char *values;
cfec3d90
 };
 
 static always_inline struct stack_entry *allocate_stack(struct stack *stack,
90c0acc7
                                                         struct stack_entry *prev,
                                                         const struct cli_bc_func *func,
                                                         const struct cli_bc_func *func_old,
                                                         operand_t ret,
                                                         struct cli_bc_bb *bb,
                                                         unsigned bb_inst)
ec5cccc7
 {
6922903a
     char *values;
     struct stack_entry *entry = cli_stack_alloc(stack, sizeof(*entry) + sizeof(*values)*func->numBytes);
cfec3d90
     if (!entry)
90c0acc7
         return NULL;
cfec3d90
     entry->prev = prev;
     entry->func = func_old;
     entry->ret = ret;
     entry->bb = bb;
     entry->bb_inst = bb_inst;
     /* we allocated room for values right after stack_entry! */
6922903a
     entry->values = values = (char*)&entry[1];
     memcpy(&values[func->numBytes - func->numConstants*8], func->constants,
90c0acc7
            sizeof(*values)*func->numConstants*8);
cfec3d90
     return entry;
 }
 
 static always_inline struct stack_entry *pop_stack(struct stack *stack,
90c0acc7
                                                    struct stack_entry *stack_entry,
                                                    const struct cli_bc_func **func,
                                                    operand_t *ret,
                                                    struct cli_bc_bb **bb,
                                                    unsigned *bb_inst)
cfec3d90
 {
     void *data;
     *func = stack_entry->func;
     *ret = stack_entry->ret;
     *bb = stack_entry->bb;
     *bb_inst = stack_entry->bb_inst;
     data = stack_entry;
     stack_entry = stack_entry->prev;
     cli_stack_free(stack, data);
     return stack_entry;
ec5cccc7
 }
 
82ca2ab4
 
 /*
  *
  * p, p+1, p+2, p+3 <- gt
     CHECK_EQ((p)&1, 0); 
     CHECK_EQ((p)&3, 0); 
     CHECK_EQ((p)&7, 0); 
 */
6922903a
 #define WRITE8(p, x) CHECK_GT(func->numBytes, p);\
11e86f8c
     TRACE_W(x, p, 8);\
6922903a
     *(uint8_t*)&values[p] = x
 #define WRITE16(p, x) CHECK_GT(func->numBytes, p+1);\
     CHECK_EQ((p)&1, 0);\
11e86f8c
     TRACE_W(x, p, 16);\
6922903a
     *(uint16_t*)&values[p] = x
 #define WRITE32(p, x) CHECK_GT(func->numBytes, p+3);\
     CHECK_EQ((p)&3, 0);\
11e86f8c
     TRACE_W(x, p, 32);\
6922903a
     *(uint32_t*)&values[p] = x
 #define WRITE64(p, x) CHECK_GT(func->numBytes, p+7);\
     CHECK_EQ((p)&7, 0);\
11e86f8c
     TRACE_W(x, p, 64);\
     *(uint64_t*)&values[p] = x
72617ba2
 #define WRITEP(x, p) CHECK_GT(func->numBytes, p+PSIZE-1);\
     CHECK_EQ((p)&(PSIZE-1), 0);\
     TRACE_W(x, p, PSIZE*8);\
     *(void**)&values[p] = x
6922903a
 
df5c921d
 #define uint_type(n) uint##n##_t
18204a67
 #define READNfrom(maxBytes, from, x, n, p)\
     CHECK_GT((maxBytes), (p)+(n/8)-1);\
c6c464e5
     CHECK_EQ((p)&(n/8-1), 0);\
18204a67
     x = *(uint_type(n)*)&(from)[(p)];\
11e86f8c
     TRACE_R(x)
df5c921d
 
bc157ce3
 #define READN(x, n, p)\
  do {\
      if (p&0x80000000) {\
90c0acc7
          uint32_t pg = p&0x7fffffff;\
          if (!pg) {\
          x = 0;\
          } else {\
          READNfrom(bc->numGlobalBytes, bc->globalBytes, x, n, pg);\
          }\
bc157ce3
      } else {\
90c0acc7
          READNfrom(func->numBytes, values, x, n, p);\
bc157ce3
      }\
  } while (0)
18204a67
 
df5c921d
 #define READ1(x, p) READN(x, 8, p);\
     x = x&1
 #define READ8(x, p) READN(x, 8, p)
 #define READ16(x, p) READN(x, 16, p)
 #define READ32(x, p) READN(x, 32, p)
 #define READ64(x, p) READN(x, 64, p)
 
9cbece5c
 #define PSIZE sizeof(int64_t)
 #define READP(x, p, asize) { int64_t iptr__;\
52d0d8bc
     READN(iptr__, 64, p);\
9cbece5c
     x = ptr_torealptr(&ptrinfos, iptr__, (asize));\
     if (!x) {\
90c0acc7
         stop = CL_EBYTECODE;\
         break;\
9cbece5c
     }\
     TRACE_R(x)\
 }
0d9b99f4
 #define READPOP(x, p, asize) {\
     if ((p)&0x40000000) {\
90c0acc7
         unsigned ptr__ = (p)&0xbfffffff;\
         CHECK_GT(func->numBytes, ptr__);\
         TRACE_PTR(ptr__, asize);\
         x = (void*)&values[ptr__];\
0d9b99f4
     } else {\
90c0acc7
         READP(x, p, asize)\
0d9b99f4
     }\
 }
6922903a
 
 #define READOLD8(x, p) CHECK_GT(func->numBytes, p);\
11e86f8c
     x = *(uint8_t*)&old_values[p];\
     TRACE_R(x)
6922903a
 #define READOLD16(x, p) CHECK_GT(func->numBytes, p+1);\
     CHECK_EQ((p)&1, 0);\
11e86f8c
     x = *(uint16_t*)&old_values[p];\
     TRACE_R(x)
6922903a
 #define READOLD32(x, p) CHECK_GT(func->numBytes, p+3);\
     CHECK_EQ((p)&3, 0);\
11e86f8c
     x = *(uint32_t*)&old_values[p];\
     TRACE_R(x)
6922903a
 #define READOLD64(x, p) CHECK_GT(func->numBytes, p+7);\
     CHECK_EQ((p)&7, 0);\
11e86f8c
     x = *(uint64_t*)&old_values[p];\
     TRACE_R(x)
82ca2ab4
 
 #define BINOP(i) inst->u.binop[i]
 
52dd3a6b
 #define DEFINE_BINOP_BC_HELPER(opc, OP, W0, W1, W2, W3, W4) \
82ca2ab4
     case opc*5: {\
90c0acc7
                     uint8_t op0, op1, res;\
                     int8_t sop0, sop1;\
                     READ1(op0, BINOP(0));\
                     READ1(op1, BINOP(1));\
                     sop0 = op0; sop1 = op1;\
                     OP;\
                     W0(inst->dest, res);\
                     break;\
                 }\
82ca2ab4
     case opc*5+1: {\
90c0acc7
                     uint8_t op0, op1, res;\
                     int8_t sop0, sop1;\
                     READ8(op0, BINOP(0));\
                     READ8(op1, BINOP(1));\
                     sop0 = op0; sop1 = op1;\
                     OP;\
                     W1(inst->dest, res);\
                     break;\
                 }\
82ca2ab4
     case opc*5+2: {\
90c0acc7
                     uint16_t op0, op1, res;\
                     int16_t sop0, sop1;\
                     READ16(op0, BINOP(0));\
                     READ16(op1, BINOP(1));\
                     sop0 = op0; sop1 = op1;\
                     OP;\
                     W2(inst->dest, res);\
                     break;\
                 }\
82ca2ab4
     case opc*5+3: {\
90c0acc7
                     uint32_t op0, op1, res;\
                     int32_t sop0, sop1;\
                     READ32(op0, BINOP(0));\
                     READ32(op1, BINOP(1));\
                     sop0 = op0; sop1 = op1;\
                     OP;\
                     W3(inst->dest, res);\
                     break;\
                 }\
82ca2ab4
     case opc*5+4: {\
90c0acc7
                     uint64_t op0, op1, res;\
                     int64_t sop0, sop1;\
                     READ64(op0, BINOP(0));\
                     READ64(op1, BINOP(1));\
                     sop0 = op0; sop1 = op1;\
                     OP;\
                     W4(inst->dest, res);\
                     break;\
                 }
82ca2ab4
 
52dd3a6b
 #define DEFINE_BINOP(opc, OP) DEFINE_BINOP_BC_HELPER(opc, OP, WRITE8, WRITE8, WRITE16, WRITE32, WRITE64)
 #define DEFINE_ICMPOP(opc, OP) DEFINE_BINOP_BC_HELPER(opc, OP, WRITE8, WRITE8, WRITE8, WRITE8, WRITE8)
82ca2ab4
 
4e2959f2
 #define CHECK_OP(cond, msg) if((cond)) { cli_dbgmsg(msg); stop = CL_EBYTECODE; break;}
82ca2ab4
 
e4a0f2c9
 #define DEFINE_SCASTOP(opc, OP) \
82ca2ab4
     case opc*5: {\
90c0acc7
                     uint8_t res;\
                     int8_t sres;\
                     OP;\
                     WRITE8(inst->dest, res);\
                     break;\
                 }\
82ca2ab4
     case opc*5+1: {\
90c0acc7
                     uint8_t res;\
                     int8_t sres;\
                     OP;\
                     WRITE8(inst->dest, res);\
                     break;\
                 }\
82ca2ab4
     case opc*5+2: {\
90c0acc7
                     uint16_t res;\
                     int16_t sres;\
                     OP;\
                     WRITE16(inst->dest, res);\
                     break;\
                 }\
82ca2ab4
     case opc*5+3: {\
90c0acc7
                     uint32_t res;\
                     int32_t sres;\
                     OP;\
                     WRITE32(inst->dest, res);\
                     break;\
                 }\
82ca2ab4
     case opc*5+4: {\
90c0acc7
                     uint64_t res;\
                     int64_t sres;\
                     OP;\
                     WRITE64(inst->dest, res);\
                     break;\
                 }
e4a0f2c9
 #define DEFINE_CASTOP(opc, OP) DEFINE_SCASTOP(opc, OP; (void)sres)
82ca2ab4
 
 #define DEFINE_OP(opc) \
     case opc*5: /* fall-through */\
     case opc*5+1: /* fall-through */\
     case opc*5+2: /* fall-through */\
     case opc*5+3: /* fall-through */\
     case opc*5+4:
 
 #define CHOOSE(OP0, OP1, OP2, OP3, OP4) \
     switch (inst->u.cast.size) {\
90c0acc7
         case 0: OP0; break;\
         case 1: OP1; break;\
         case 2: OP2; break;\
         case 3: OP3; break;\
         case 4: OP4; break;\
         default: CHECK_UNREACHABLE;\
82ca2ab4
     }
 
52dd3a6b
 #define DEFINE_OP_BC_RET_N(OP, T, R0, W0) \
6922903a
     case OP: {\
ce2dcb53
                 operand_t ret;\
90c0acc7
                 T tmp;\
                 R0(tmp, inst->u.unaryop);\
                 CHECK_GT(stack_depth, 0);\
                 stack_depth--;\
ce2dcb53
                 stack_entry = pop_stack(&stack, stack_entry, &func, &ret, &bb,\
90c0acc7
                                         &bb_inst);\
                 values = stack_entry ? stack_entry->values : ctx->values;\
ce2dcb53
                 CHECK_GT(func->numBytes, ret);\
                 W0(ret, tmp);\
90c0acc7
                 if (!bb) {\
                     stop = CL_BREAK;\
                     continue;\
                 }\
                 stackid = ptr_register_stack(&ptrinfos, values, 0, func->numBytes)>>32;\
                 inst = &bb->insts[bb_inst];\
                 break;\
             }
6922903a
 
9cbece5c
 struct ptr_info {
     uint8_t *base;
     uint32_t size;
 };
 
 struct ptr_infos {
     struct ptr_info *stack_infos;
     struct ptr_info *glob_infos;
     unsigned nstacks, nglobs;
 };
 
 static inline int64_t ptr_compose(int32_t id, uint32_t offset)
 {
     uint64_t i = id;
     return (i << 32) | offset;
 }
 
44e13431
 static inline int32_t ptr_diff32(int64_t ptr1, int64_t ptr2)
 {
     int32_t ptrid1 = ptr1 >> 32;
     int32_t ptrid2 = ptr2 >> 32;
     if (ptrid1 != ptrid2) {
90c0acc7
         (void)bcfail("difference of pointers not pointing to same object!", ptrid1, ptrid2, __FILE__, __LINE__);
         /* invalid diff */
         return 0x40000000;
44e13431
     }
     return (uint32_t)ptr1 - (uint32_t)ptr2;
 }
 
4993d6cf
 static inline int64_t ptr_register_stack(struct ptr_infos *infos,
90c0acc7
                                          char *values,
                                          uint32_t off, uint32_t size)
9cbece5c
 {
     unsigned n = infos->nstacks + 1;
     struct ptr_info *sinfos = cli_realloc(infos->stack_infos,
90c0acc7
                                           sizeof(*sinfos)*n);
9cbece5c
     if (!sinfos)
90c0acc7
         return 0;
9cbece5c
     infos->stack_infos = sinfos;
     infos->nstacks = n;
     sinfos = &sinfos[n-1];
e4a0f2c9
     sinfos->base = (uint8_t*)values + off;
9cbece5c
     sinfos->size = size;
4993d6cf
     return ptr_compose(-n, 0);
9cbece5c
 }
 
52d0d8bc
 static inline int64_t ptr_register_glob_fixedid(struct ptr_infos *infos,
90c0acc7
                                                 void *values, uint32_t size, unsigned n)
9cbece5c
 {
52d0d8bc
     struct ptr_info *sinfos;
     if (n > infos->nglobs) {
90c0acc7
         sinfos = cli_realloc(infos->glob_infos, sizeof(*sinfos)*n);
         if (!sinfos)
             return 0;
598d7eda
         memset(sinfos + infos->nglobs, 0, (n - infos->nglobs)*sizeof(*sinfos));
90c0acc7
         infos->glob_infos = sinfos;
         infos->nglobs = n;
52d0d8bc
     }
     sinfos = &infos->glob_infos[n-1];
74f5816c
     if (!values)
90c0acc7
         size = 0;
9cbece5c
     sinfos->base = values;
     sinfos->size = size;
74f5816c
     cli_dbgmsg("bytecode: registered ctx variable at %p (+%u) id %u\n", values,
90c0acc7
                size, n);
9cbece5c
     return ptr_compose(n, 0);
 }
 
52d0d8bc
 static inline int64_t ptr_register_glob(struct ptr_infos *infos,
90c0acc7
                                         void *values, uint32_t size)
52d0d8bc
 {
9d39dc93
     if (!values)
90c0acc7
         return 0;
52d0d8bc
     return ptr_register_glob_fixedid(infos, values, size, infos->nglobs+1);
 }
 
9cbece5c
 static inline void* ptr_torealptr(const struct ptr_infos *infos, int64_t ptr,
90c0acc7
                                   uint32_t read_size)
9cbece5c
 {
     struct ptr_info *info;
     int32_t ptrid = ptr >> 32;
     uint32_t ptroff = (uint32_t)ptr;
a15fc904
     TRACE_PTR(ptr, read_size);
9cbece5c
     if (UNLIKELY(!ptrid)) {
90c0acc7
         (void)bcfail("nullptr", ptrid, 0, __FILE__, __LINE__);
         return NULL;
9cbece5c
     }
     if (ptrid < 0) {
90c0acc7
         ptrid = -ptrid-1;
cd94be7a
         if (UNLIKELY((const unsigned int)ptrid >= infos->nstacks)) {
90c0acc7
             (void)bcfail("ptr", ptrid, infos->nstacks, __FILE__, __LINE__);
             return NULL;
         }
         info = &infos->stack_infos[ptrid];
9cbece5c
     } else {
90c0acc7
         ptrid--;
cd94be7a
         if (UNLIKELY((const unsigned int)ptrid >= infos->nglobs)) {
90c0acc7
             (void)bcfail("ptr", ptrid, infos->nglobs, __FILE__, __LINE__);
             return NULL;
         }
         info = &infos->glob_infos[ptrid];
9cbece5c
     }
     if (LIKELY(ptroff < info->size &&
90c0acc7
         read_size <= info->size &&
         ptroff + read_size <= info->size)) {
         return info->base+ptroff;
9cbece5c
     }
 
e4a0f2c9
     (void)bcfail("ptr1", ptroff, info->size, __FILE__, __LINE__);
     (void)bcfail("ptr2", read_size, info->size, __FILE__, __LINE__);
     (void)bcfail("ptr3", ptroff+read_size, info->size, __FILE__, __LINE__);
9cbece5c
     return NULL;
 }
 
82ca2ab4
 static always_inline int check_sdivops(int64_t op0, int64_t op1)
 {
e0cab690
     return op1 == 0 || (op1 == -1 && op0 ==  (-9223372036854775807LL-1LL));
82ca2ab4
 }
 
52d0d8bc
 static unsigned globaltypesize(uint16_t id)
 {
     const struct cli_bc_type *ty;
     if (id <= 64)
90c0acc7
         return (id + 7)/8;
50829fbf
     if (id < 69)
90c0acc7
         return 8; /* ptr */
52d0d8bc
     ty = &cli_apicall_types[id - 69];
     switch (ty->kind) {
90c0acc7
         case DArrayType:
             return ty->numElements*globaltypesize(ty->containedTypes[0]);
         case DStructType:
         case DPackedStructType:
             {
                 unsigned i, s = 0;
                 for (i=0;i<ty->numElements;i++)
                     s += globaltypesize(ty->containedTypes[i]);
                 return s;
             }
         default:
             return 0;
52d0d8bc
     }
 }
 
a15fc904
 /* TODO: fix the APIs too */
 static struct {
     cli_apicall_pointer api;
     uint32_t override_size;
 } apisize_override[] = {
     {(void*)cli_bcapi_disasm_x86, sizeof(struct DISASM_RESULT)},
     {(void*)cli_bcapi_get_pe_section, sizeof(struct cli_exe_section)},
 };
 
ec5cccc7
 int cli_vm_execute(const struct cli_bc *bc, struct cli_bc_ctx *ctx, const struct cli_bc_func *func, const struct cli_bc_inst *inst)
e0c4fd85
 {
d18d7221
     size_t i;
     uint32_t j;
     unsigned stack_depth=0, bb_inst=0, stop=0, pc=0;
e0c4fd85
     struct cli_bc_func *func2;
cfec3d90
     struct stack stack;
     struct stack_entry *stack_entry = NULL;
e0c4fd85
     struct cli_bc_bb *bb = NULL;
6922903a
     char *values = ctx->values;
     char *old_values;
9cbece5c
     struct ptr_infos ptrinfos;
74f5816c
     struct timeval tv0, tv1, timeout;
4993d6cf
     int stackid = 0;
e0c4fd85
 
9cbece5c
     memset(&ptrinfos, 0, sizeof(ptrinfos));
cfec3d90
     memset(&stack, 0, sizeof(stack));
d18d7221
     for (i=0; i < (size_t)cli_apicall_maxglobal - _FIRST_GLOBAL; i++) {
90c0acc7
         void *apiptr;
         uint32_t size;
         const struct cli_apiglobal *g = &cli_globals[i];
         void **apiglobal = (void**)(((char*)ctx) + g->offset);
         if (!apiglobal)
             continue;
         apiptr = *apiglobal;
         size = globaltypesize(g->type);
         ptr_register_glob_fixedid(&ptrinfos, apiptr, size, g->globalid - _FIRST_GLOBAL+1);
52d0d8bc
     }
74f5816c
     ptr_register_glob_fixedid(&ptrinfos, bc->globalBytes, bc->numGlobalBytes,
90c0acc7
                               cli_apicall_maxglobal - _FIRST_GLOBAL + 2);
74f5816c
 
     gettimeofday(&tv0, NULL);
     timeout.tv_usec = tv0.tv_usec + ctx->bytecode_timeout*1000;
     timeout.tv_sec = tv0.tv_sec + timeout.tv_usec/1000000;
     timeout.tv_usec %= 1000000;
52d0d8bc
 
e0c4fd85
     do {
90c0acc7
         pc++;
         if (!(pc % 5000)) {
             gettimeofday(&tv1, NULL);
             if (tv1.tv_sec > timeout.tv_sec ||
                 (tv1.tv_sec == timeout.tv_sec &&
                  tv1.tv_usec > timeout.tv_usec)) {
                 cli_warnmsg("Bytecode run timed out in interpreter after %u opcodes\n", pc);
                 stop = CL_ETIMEOUT;
                 break;
             }
1aa8768d
         }
90c0acc7
         switch (inst->interp_op) {
             DEFINE_BINOP(OP_BC_ADD, res = op0 + op1);
             DEFINE_BINOP(OP_BC_SUB, res = op0 - op1);
             DEFINE_BINOP(OP_BC_MUL, res = op0 * op1);
 
             DEFINE_BINOP(OP_BC_UDIV, CHECK_OP(op1 == 0, "bytecode attempted to execute udiv#0\n");
                          res=op0/op1);
             DEFINE_BINOP(OP_BC_SDIV, CHECK_OP(check_sdivops(sop0, sop1), "bytecode attempted to execute sdiv#0\n");
                          res=sop0/sop1);
             DEFINE_BINOP(OP_BC_UREM, CHECK_OP(op1 == 0, "bytecode attempted to execute urem#0\n");
                          res=op0 % op1);
             DEFINE_BINOP(OP_BC_SREM, CHECK_OP(check_sdivops(sop0,sop1), "bytecode attempted to execute urem#0\n");
                          res=sop0 % sop1);
 
             DEFINE_BINOP(OP_BC_SHL, CHECK_OP(op1 > inst->type, "bytecode attempted to execute shl greater than bitwidth\n");
                          res = op0 << op1);
             DEFINE_BINOP(OP_BC_LSHR, CHECK_OP(op1 > inst->type, "bytecode attempted to execute lshr greater than bitwidth\n");
                          res = op0 >> op1);
             DEFINE_BINOP(OP_BC_ASHR, CHECK_OP(op1 > inst->type, "bytecode attempted to execute ashr greater than bitwidth\n");
                          res = CLI_SRS(sop0, op1));
 
             DEFINE_BINOP(OP_BC_AND, res = op0 & op1);
             DEFINE_BINOP(OP_BC_OR, res = op0 | op1);
             DEFINE_BINOP(OP_BC_XOR, res = op0 ^ op1);
 
             DEFINE_SCASTOP(OP_BC_SEXT,
ce2dcb53
                           CHOOSE(READ1(sres, inst->u.cast.source); res = sres ? ~0 : 0,
90c0acc7
                                  READ8(sres, inst->u.cast.source); res=sres=SIGNEXT(sres, inst->u.cast.mask),
                                  READ16(sres, inst->u.cast.source); res=sres=SIGNEXT(sres, inst->u.cast.mask),
                                  READ32(sres, inst->u.cast.source); res=sres=SIGNEXT(sres, inst->u.cast.mask),
                                  READ64(sres, inst->u.cast.source); res=sres=SIGNEXT(sres, inst->u.cast.mask)));
             DEFINE_CASTOP(OP_BC_ZEXT,
                           CHOOSE(READ1(res, inst->u.cast.source),
                                  READ8(res, inst->u.cast.source),
                                  READ16(res, inst->u.cast.source),
                                  READ32(res, inst->u.cast.source),
                                  READ64(res, inst->u.cast.source)));
             DEFINE_CASTOP(OP_BC_TRUNC,
                           CHOOSE(READ1(res, inst->u.cast.source),
                                  READ8(res, inst->u.cast.source),
                                  READ16(res, inst->u.cast.source),
                                  READ32(res, inst->u.cast.source),
                                  READ64(res, inst->u.cast.source)));
 
             DEFINE_OP(OP_BC_BRANCH)
                 stop = jump(func, (values[inst->u.branch.condition]&1) ?
                           inst->u.branch.br_true : inst->u.branch.br_false,
                           &bb, &inst, &bb_inst);
                 continue;
 
             DEFINE_OP(OP_BC_JMP)
                 stop = jump(func, inst->u.jump, &bb, &inst, &bb_inst);
                 continue;
 
             DEFINE_OP_BC_RET_N(OP_BC_RET*5, uint8_t, READ1, WRITE8);
             DEFINE_OP_BC_RET_N(OP_BC_RET*5+1, uint8_t, READ8, WRITE8);
             DEFINE_OP_BC_RET_N(OP_BC_RET*5+2, uint16_t, READ16, WRITE16);
             DEFINE_OP_BC_RET_N(OP_BC_RET*5+3, uint32_t, READ32, WRITE32);
             DEFINE_OP_BC_RET_N(OP_BC_RET*5+4, uint64_t, READ64, WRITE64);
 
             DEFINE_OP_BC_RET_N(OP_BC_RET_VOID*5, uint8_t, (void), (void));
             DEFINE_OP_BC_RET_N(OP_BC_RET_VOID*5+1, uint8_t, (void), (void));
             DEFINE_OP_BC_RET_N(OP_BC_RET_VOID*5+2, uint8_t, (void), (void));
             DEFINE_OP_BC_RET_N(OP_BC_RET_VOID*5+3, uint8_t, (void), (void));
             DEFINE_OP_BC_RET_N(OP_BC_RET_VOID*5+4, uint8_t, (void), (void));
 
             DEFINE_ICMPOP(OP_BC_ICMP_EQ, res = (op0 == op1));
             DEFINE_ICMPOP(OP_BC_ICMP_NE, res = (op0 != op1));
             DEFINE_ICMPOP(OP_BC_ICMP_UGT, res = (op0 > op1));
             DEFINE_ICMPOP(OP_BC_ICMP_UGE, res = (op0 >= op1));
             DEFINE_ICMPOP(OP_BC_ICMP_ULT, res = (op0 < op1));
             DEFINE_ICMPOP(OP_BC_ICMP_ULE, res = (op0 <= op1));
             DEFINE_ICMPOP(OP_BC_ICMP_SGT, res = (sop0 > sop1));
             DEFINE_ICMPOP(OP_BC_ICMP_SGE, res = (sop0 >= sop1));
             DEFINE_ICMPOP(OP_BC_ICMP_SLE, res = (sop0 <= sop1));
             DEFINE_ICMPOP(OP_BC_ICMP_SLT, res = (sop0 < sop1));
 
             case OP_BC_SELECT*5:
             {
                 uint8_t t0, t1, t2;
                 READ1(t0, inst->u.three[0]);
                 READ1(t1, inst->u.three[1]);
                 READ1(t2, inst->u.three[2]);
                 WRITE8(inst->dest, t0 ? t1 : t2);
                 break;
             }
             case OP_BC_SELECT*5+1:
             {
                 uint8_t t0, t1, t2;
                 READ1(t0, inst->u.three[0]);
                 READ8(t1, inst->u.three[1]);
                 READ8(t2, inst->u.three[2]);
                 WRITE8(inst->dest, t0 ? t1 : t2);
                 break;
             }
             case OP_BC_SELECT*5+2:
             {
                 uint8_t t0;
                 uint16_t t1, t2;
                 READ1(t0, inst->u.three[0]);
                 READ16(t1, inst->u.three[1]);
                 READ16(t2, inst->u.three[2]);
                 WRITE16(inst->dest, t0 ? t1 : t2);
                 break;
             }
             case OP_BC_SELECT*5+3:
             {
                 uint8_t t0;
                 uint32_t t1, t2;
                 READ1(t0, inst->u.three[0]);
                 READ32(t1, inst->u.three[1]);
                 READ32(t2, inst->u.three[2]);
                 WRITE32(inst->dest, t0 ? t1 : t2);
                 break;
             }
             case OP_BC_SELECT*5+4:
             {
                 uint8_t t0;
                 uint64_t t1, t2;
                 READ1(t0, inst->u.three[0]);
                 READ64(t1, inst->u.three[1]);
                 READ64(t2, inst->u.three[2]);
                 WRITE64(inst->dest, t0 ? t1 : t2);
                 break;
             }
 
             DEFINE_OP(OP_BC_CALL_API) {
                 const struct cli_apicall *api = &cli_apicalls[inst->u.ops.funcid];
                 int32_t res32;
                 int64_t res64;
                 CHECK_APIID(inst->u.ops.funcid);
                 TRACE_API(api->name, inst->dest, inst->type, stack_depth);
                 switch (api->kind) {
                     case 0: {
                         int32_t a, b;
                         READ32(a, inst->u.ops.ops[0]);
                         READ32(b, inst->u.ops.ops[1]);
                         res32 = cli_apicalls0[api->idx](ctx, a, b);
                         WRITE32(inst->dest, res32);
                         break;
                     }
                     case 1: {
                         void* arg1;
                         unsigned arg2, arg1size;
                         READ32(arg2, inst->u.ops.ops[1]);
                         /* check that arg2 is size of arg1 */
                         arg1size = arg2;
                         for (i=0;i<sizeof(apisize_override)/sizeof(apisize_override[0]);i++) {
                             if (cli_apicalls1[api->idx] == apisize_override[i].api) {
                                 arg1size = apisize_override[i].override_size;
                                 break;
                             }
                         }
                         READPOP(arg1, inst->u.ops.ops[0], arg1size);
                         res32 = cli_apicalls1[api->idx](ctx, arg1, arg2);
                         WRITE32(inst->dest, res32);
                         break;
                     }
                     case 2: {
                         int32_t a;
                         READ32(a, inst->u.ops.ops[0]);
                         res32 = cli_apicalls2[api->idx](ctx, a);
                         WRITE32(inst->dest, res32);
                         break;
                     }
                     case 3: {
                         int32_t a;
                         void *resp;
                         READ32(a, inst->u.ops.ops[0]);
                         resp = cli_apicalls3[api->idx](ctx, a);
                         res64 = ptr_register_glob(&ptrinfos, resp, a);
                         WRITE64(inst->dest, res64);
                         break;
                     }
                     case 4: {
                         int32_t arg2, arg3, arg4, arg5;
                         void *arg1;
                         READ32(arg2, inst->u.ops.ops[1]);
                         /* check that arg2 is size of arg1 */
                         READP(arg1, inst->u.ops.ops[0], arg2);
                         READ32(arg3, inst->u.ops.ops[2]);
                         READ32(arg4, inst->u.ops.ops[3]);
                         READ32(arg5, inst->u.ops.ops[4]);
                         res32 = cli_apicalls4[api->idx](ctx, arg1, arg2, arg3, arg4, arg5);
                         WRITE32(inst->dest, res32);
                         break;
                     }
                     case 5: {
                         res32 = cli_apicalls5[api->idx](ctx);
                         WRITE32(inst->dest, res32);
                         break;
                     }
                     case 6: {
                         int32_t arg1, arg2;
                         void *resp;
                         READ32(arg1, inst->u.ops.ops[0]);
                         READ32(arg2, inst->u.ops.ops[1]);
                         resp = cli_apicalls6[api->idx](ctx, arg1, arg2);
                         res64 = ptr_register_glob(&ptrinfos, resp, arg2);
                         WRITE64(inst->dest, res64);
                         break;
                     }
                     case 7: {
                         int32_t arg1,arg2,arg3;
                         READ32(arg1, inst->u.ops.ops[0]);
                         READ32(arg2, inst->u.ops.ops[1]);
                         READ32(arg3, inst->u.ops.ops[2]);
                         res32 = cli_apicalls7[api->idx](ctx, arg1, arg2, arg3);
                         WRITE32(inst->dest, res32);
                         break;
                     }
                     case 8: {
                         int32_t arg2, arg4;
                         void *arg1, *arg3;
                         int32_t resp;
                         READ32(arg2, inst->u.ops.ops[1]);
                         /* check that arg2 is size of arg1 */
                         READP(arg1, inst->u.ops.ops[0], arg2);
                         READ32(arg4, inst->u.ops.ops[3]);
                         READP(arg3, inst->u.ops.ops[2], arg4);
                         resp = cli_apicalls8[api->idx](ctx, arg1, arg2, arg3, arg4);
                         WRITE32(inst->dest, resp);
                         break;
                     }
                     case 9: {
                         int32_t arg2, arg3;
                         void *arg1;
                         int32_t resp;
                         READ32(arg2, inst->u.ops.ops[1]);
                         /* check that arg2 is size of arg1 */
                         READP(arg1, inst->u.ops.ops[0], arg2);
                         READ32(arg3, inst->u.ops.ops[2]);
                         resp = cli_apicalls9[api->idx](ctx, arg1, arg2, arg3);
                         WRITE32(inst->dest, resp);
                         break;
                     };
                     default:
                         cli_warnmsg("bytecode: type %u apicalls not yet implemented!\n", api->kind);
                         stop = CL_EBYTECODE;
                 }
                 break;
             }
 
             DEFINE_OP(OP_BC_CALL_DIRECT)
                 CHECK_FUNCID(inst->u.ops.funcid);
                 func2 = &bc->funcs[inst->u.ops.funcid];
                 CHECK_EQ(func2->numArgs, inst->u.ops.numOps);
                 old_values = values;
                 stack_entry = allocate_stack(&stack, stack_entry, func2, func, inst->dest,
                                              bb, bb_inst);
                 if (!stack_entry) {
                     stop = CL_EMEM;
                     break;
                 }
                 values = stack_entry->values;
                 /* TODO: unregister on ret */
                 TRACE_EXEC(inst->u.ops.funcid, inst->dest, inst->type, stack_depth);
                 if (stack_depth > 10000) {
                     cli_warnmsg("bytecode: stack depth exceeded\n");
                     stop = CL_EBYTECODE;
                     break;
                 }
                 j = 0;
                 for (i=0;i<func2->numArgs;i++) {
                     switch (inst->u.ops.opsizes[i]) {
                         case 1: {
                             uint8_t v;
                             READOLD8(v, inst->u.ops.ops[i]);
                             CHECK_GT(func2->numBytes, j);
                             values[j++] = v;
                             break;
                         }
                         case 2: {
                             uint16_t v;
                             READOLD16(v, inst->u.ops.ops[i]);
                             j = (j+1)&~1;
                             CHECK_GT(func2->numBytes, j);
                             *(uint16_t*)&values[j] = v;
                             j += 2;
                             break;
                         }
                         case 4: {
                             uint32_t v;
                             READOLD32(v, inst->u.ops.ops[i]);
                             j = (j+3)&~3;
                             CHECK_GT(func2->numBytes, j);
                             *(uint32_t*)&values[j] = v;
                             j += 4;
                             break;
                         }
                         case 8: {
                             uint64_t v;
                             READOLD64(v, inst->u.ops.ops[i]);
                             j = (j+7)&~7;
                             CHECK_GT(func2->numBytes, j);
                             *(uint64_t*)&values[j] = v;
                             j += 8;
                             break;
                         }
                     }
                 }
                 func = func2;
                 stackid = ptr_register_stack(&ptrinfos, values, 0, func->numBytes)>>32;
                 CHECK_GT(func->numBB, 0);
                 stop = jump(func, 0, &bb, &inst, &bb_inst);
                 stack_depth++;
                 continue;
 
             case OP_BC_COPY*5:
             {
                 uint8_t op;
                 READ1(op, BINOP(0));
                 WRITE8(BINOP(1), op);
                 break;
             }
             case OP_BC_COPY*5+1:
             {
                 uint8_t op;
                 READ8(op, BINOP(0));
                 WRITE8(BINOP(1), op);
                 break;
             }
             case OP_BC_COPY*5+2:
             {
                 uint16_t op;
                 READ16(op, BINOP(0));
                 WRITE16(BINOP(1), op);
                 break;
             }
             case OP_BC_COPY*5+3:
             {
                 uint32_t op;
                 READ32(op, BINOP(0));
                 WRITE32(BINOP(1), op);
                 break;
             }
             case OP_BC_COPY*5+4:
             {
                 uint64_t op;
                 READ64(op, BINOP(0));
                 WRITE64(BINOP(1), op);
                 break;
             }
 
             case OP_BC_LOAD*5:
             case OP_BC_LOAD*5+1:
             {
                 uint8_t *ptr;
                 READPOP(ptr, inst->u.unaryop, 1);
                 WRITE8(inst->dest, (*ptr));
                 break;
             }
             case OP_BC_LOAD*5+2:
             {
                 const union unaligned_16 *ptr;
                 READPOP(ptr, inst->u.unaryop, 2);
                 WRITE16(inst->dest, (ptr->una_u16));
                 break;
             }
             case OP_BC_LOAD*5+3:
             {
                 const union unaligned_32 *ptr;
                 READPOP(ptr, inst->u.unaryop, 4);
                 WRITE32(inst->dest, (ptr->una_u32));
                 break;
             }
             case OP_BC_LOAD*5+4:
             {
                 const union unaligned_64 *ptr;
                 READPOP(ptr, inst->u.unaryop, 8);
                 WRITE64(inst->dest, (ptr->una_u64));
                 break;
             }
 
             case OP_BC_STORE*5:
             {
                 uint8_t *ptr;
                 uint8_t v;
                 READP(ptr, BINOP(1), 1);
                 READ1(v, BINOP(0));
                 *ptr = v;
                 break;
             }
             case OP_BC_STORE*5+1:
             {
                 uint8_t *ptr;
                 uint8_t v;
                 READP(ptr, BINOP(1), 1);
                 READ8(v, BINOP(0));
                 *ptr = v;
                 break;
             }
             case OP_BC_STORE*5+2:
             {
                 union unaligned_16 *ptr;
                 uint16_t v;
                 READP(ptr, BINOP(1), 2);
                 READ16(v, BINOP(0));
                 ptr->una_s16 = v;
                 break;
             }
             case OP_BC_STORE*5+3:
             {
                 union unaligned_32 *ptr;
                 uint32_t v;
                 READP(ptr, BINOP(1), 4);
                 READ32(v, BINOP(0));
                 ptr->una_u32 = v;
                 break;
             }
             case OP_BC_STORE*5+4:
             {
                 union unaligned_64 *ptr;
                 uint64_t v;
                 READP(ptr, BINOP(1), 8);
                 READ64(v, BINOP(0));
                 ptr->una_u64 = v;
                 break;
             }
             DEFINE_OP(OP_BC_ISBIGENDIAN) {
                 WRITE8(inst->dest, WORDS_BIGENDIAN);
                 break;
             }
             DEFINE_OP(OP_BC_GEPZ) {
                 int64_t ptr, iptr;
                 int32_t off;
                 READ32(off, inst->u.three[2]);
 
                 // negative values checking, valid for intermediate GEP calculations
                 if (off < 0) {
                     cli_dbgmsg("bytecode warning: found GEP with negative offset %d!\n", off);
                 }
 
                 if (!(inst->interp_op%5)) {
7cd9337a
                     // how do negative offsets affect pointer initialization?
90c0acc7
                     WRITE64(inst->dest, ptr_compose(stackid,
                                                     inst->u.three[1]+off));
                 } else {
                     READ64(ptr, inst->u.three[1]);
08fef61f
                     off += (ptr & 0x00000000ffffffffULL);
                     iptr = (ptr & 0xffffffff00000000ULL) + (uint64_t)(off);
90c0acc7
                     WRITE64(inst->dest, ptr+off);
                 }
                 break;
             }
             DEFINE_OP(OP_BC_MEMCMP) {
                 int32_t arg3;
                 void *arg1, *arg2;
                 READ32(arg3, inst->u.three[2]);
                 READPOP(arg1, inst->u.three[0], arg3);
                 READPOP(arg2, inst->u.three[1], arg3);
                 WRITE32(inst->dest, memcmp(arg1, arg2, arg3));
                 break;
             }
             DEFINE_OP(OP_BC_MEMCPY) {
                 int64_t arg3;
                 void *arg1, *arg2;
 
                 READ32(arg3, inst->u.three[2]);
                 READPOP(arg1, inst->u.three[0], arg3);
                 READPOP(arg2, inst->u.three[1], arg3);
                 memcpy(arg1, arg2, (int32_t)arg3);
                 break;
             }
             DEFINE_OP(OP_BC_MEMMOVE) {
                 int64_t arg3;
                 void *arg1, *arg2;
 
                 READ64(arg3, inst->u.three[2]);
                 READPOP(arg1, inst->u.three[0], arg3);
                 READPOP(arg2, inst->u.three[1], arg3);
                 memmove(arg1, arg2, (int32_t)arg3);
                 break;
             }
             DEFINE_OP(OP_BC_MEMSET) {
                 int64_t arg3;
                 int32_t arg2;
                 void *arg1;
 
                 READ64(arg3, inst->u.three[2]);
                 READPOP(arg1, inst->u.three[0], arg3);
                 READ32(arg2, inst->u.three[1]);
                 memset(arg1, arg2, (int32_t)arg3);
                 break;
             }
             DEFINE_OP(OP_BC_BSWAP16) {
                 int16_t arg1;
                 READ16(arg1, inst->u.unaryop);
                 WRITE16(inst->dest, cbswap16(arg1));
                 break;
             }
             DEFINE_OP(OP_BC_BSWAP32) {
                 int32_t arg1;
                 READ32(arg1, inst->u.unaryop);
                 WRITE32(inst->dest, cbswap32(arg1));
                 break;
             }
             DEFINE_OP(OP_BC_BSWAP64) {
                 int64_t arg1;
                 READ64(arg1, inst->u.unaryop);
                 WRITE64(inst->dest, cbswap64(arg1));
                 break;
             }
             DEFINE_OP(OP_BC_PTRDIFF32) {
                 int64_t ptr1, ptr2;
                 if (BINOP(0)&0x40000000)
                     ptr1 = ptr_compose(stackid, BINOP(0)&0xbfffffff);
                 else
                     READ64(ptr1, BINOP(0));
                 if (BINOP(1)&0x40000000)
                     ptr2 = ptr_compose(stackid, BINOP(1)&0xbfffffff);
                 else
                     READ64(ptr2, BINOP(1));
                 WRITE32(inst->dest, ptr_diff32(ptr1, ptr2));
                 break;
             }
             DEFINE_OP(OP_BC_PTRTOINT64) {
                 int64_t ptr;
                 if (inst->u.unaryop&0x40000000)
                     ptr = ptr_compose(stackid, inst->u.unaryop&0xbfffffff);
                 else
                     READ64(ptr, BINOP(0));
                 WRITE64(inst->dest, ptr);
                 break;
             }
             DEFINE_OP(OP_BC_GEP1) {
                 int64_t ptr, iptr;
                 int32_t off;
                 READ32(off, inst->u.three[2]);
 
                 // negative values checking, valid for intermediate GEP calculations
                 if (off < 0) {
                     cli_dbgmsg("bytecode warning: GEP with negative offset %d!\n", off);
                 }
 
                 if (!(inst->interp_op%5)) {
7cd9337a
                     // how do negative offsets affect pointer initialization?
90c0acc7
                     cli_dbgmsg("bytecode warning: untested case for GEP1\n");
                     off *= inst->u.three[0];
                     WRITE64(inst->dest, ptr_compose(stackid,
                                                     inst->u.three[1]+off));
                 } else {
                     READ64(ptr, inst->u.three[1]);
                     off *= inst->u.three[0];
                     off += (ptr & 0x00000000ffffffff);
                     iptr = (ptr & 0xffffffff00000000) + (uint64_t)(off);
                     WRITE64(inst->dest, iptr);
                 }
                 break;
             }
             /* TODO: implement OP_BC_GEP1, OP_BC_GEP2, OP_BC_GEPN */
             default:
                 cli_errmsg("Opcode %u of type %u is not implemented yet!\n",
                            inst->interp_op/5, inst->interp_op%5);
                 stop = CL_EARG;
                 continue;
1aa8768d
         }
90c0acc7
         bb_inst++;
         inst++;
         if (bb) {
             CHECK_GT(bb->numInsts, bb_inst);
1aa8768d
         }
e0c4fd85
     } while (stop == CL_SUCCESS);
74f5816c
     if (cli_debug_flag) {
90c0acc7
         gettimeofday(&tv1, NULL);
         tv1.tv_sec -= tv0.tv_sec;
         tv1.tv_usec -= tv0.tv_usec;
7cd9337a
         cli_dbgmsg("interpreter bytecode run finished in %luus, after executing %u opcodes\n",
90c0acc7
                    tv1.tv_sec*1000000 + tv1.tv_usec, pc);
74f5816c
     }
1c00d0ac
     if (stop == CL_EBYTECODE) {
90c0acc7
         cli_event_error_str(ctx->bc_events, "interpreter finished with error\n");
7cd9337a
         cli_dbgmsg("interpreter finished with error\n");
1c00d0ac
     }
e0c4fd85
 
cfec3d90
     cli_stack_destroy(&stack);
48fc8b98
     free(ptrinfos.stack_infos);
     free(ptrinfos.glob_infos);
e0c4fd85
     return stop == CL_BREAK ? CL_SUCCESS : stop;
 }