libclamav/bytecode_vm.c
e0c4fd85
 /*
  *  Execute ClamAV bytecode.
  *
e1cbc270
  *  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,
288057e9
                                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
288057e9
 #define bcfail(msg, a, b, f, l) CL_EBYTECODE
15fd260a
 #endif
82ca2ab4
 
288057e9
 #define CHECK_FUNCID(funcid)                                                                                          \
     do {                                                                                                              \
         if (funcid >= bc->num_func) return bcfail("funcid out of bounds!", funcid, bc->num_func, __FILE__, __LINE__); \
     } while (0)
 #define CHECK_APIID(funcid)                                                                                                      \
     do {                                                                                                                         \
         if (funcid >= cli_apicall_maxapi) return bcfail("APIid out of bounds!", funcid, cli_apicall_maxapi, __FILE__, __LINE__); \
     } while (0)
 #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) {}
288057e9
 #define CHECK_FUNCID(x) ;
 #define CHECK_APIID(x) ;
 #define CHECK_EQ(a, b)
 #define CHECK_GT(a, b)
e0c4fd85
 #endif
48692aff
 #if 0 /* too verbose, use #ifdef CL_DEBUG if needed */
288057e9
 #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)
43422579
 #define TRACE_INST(inst)                                                   \
     do {                                                                   \
         unsigned bbnum = 0;                                                \
         printf("LibClamAV debug: bytecode trace: executing instruction "); \
         cli_byteinst_describe(inst, &bbnum);                               \
         printf("\n");                                                      \
     } while (0)
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)
92288e16
 #define TRACE_INST(inst)
1678ef9e
 #define TRACE_API(s, dest, ty, stack)
15fd260a
 #endif
 
288057e9
 #define SIGNEXT(a, from) CLI_SRS(((int64_t)(a)) << (64 - (from)), (64 - (from)))
cfec3d90
 
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,
288057e9
                               unsigned *bb_inst)
8c0933ce
 {
     CHECK_GT(func->numBB, bbid);
288057e9
     *bb      = &func->BB[bbid];
     *inst    = (*bb)->insts;
8c0933ce
     *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 {
288057e9
     struct stack_chunk *chunk;
cfec3d90
     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;
 
288057e9
 static always_inline void *cli_stack_alloc(struct stack *stack, unsigned bytes)
cfec3d90
 {
     struct stack_chunk *chunk = stack->chunk;
     uint16_t last_size_off;
 
     /* last_size is stored after data */
     /* align bytes to pointer size */
288057e9
     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
 
288057e9
         *(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
     }
 
288057e9
     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
 
288057e9
     *(uint16_t *)&chunk->u.data[last_size_off] = stack->last_size;
     stack->last_size                           = bytes / sizeof(align_t);
cfec3d90
 
288057e9
     chunk->used  = bytes;
     chunk->prev  = stack->chunk;
cfec3d90
     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
     }
288057e9
     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",
288057e9
                     data, chunk->u.data + chunk->used - stack->last_size * sizeof(align_t));
90c0acc7
         return;
cfec3d90
     }
288057e9
     last_size = *(uint16_t *)&chunk->u.data[chunk->used - 2];
     if (chunk->used < stack->last_size * sizeof(align_t)) {
90c0acc7
         cli_warnmsg("cli_stack_free: last_size is corrupt!\n");
         return;
cfec3d90
     }
288057e9
     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;
288057e9
     struct stack_entry *entry = cli_stack_alloc(stack, sizeof(*entry) + sizeof(*values) * func->numBytes);
cfec3d90
     if (!entry)
90c0acc7
         return NULL;
288057e9
     entry->prev    = prev;
     entry->func    = func_old;
     entry->ret     = ret;
     entry->bb      = bb;
cfec3d90
     entry->bb_inst = bb_inst;
     /* we allocated room for values right after stack_entry! */
288057e9
     entry->values = values = (char *)&entry[1];
     memcpy(&values[func->numBytes - func->numConstants * 8], func->constants,
            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;
288057e9
     *func       = stack_entry->func;
     *ret        = stack_entry->ret;
     *bb         = stack_entry->bb;
     *bb_inst    = stack_entry->bb_inst;
     data        = stack_entry;
cfec3d90
     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); 
 */
288057e9
 #define WRITE8(p, x)             \
     CHECK_GT(func->numBytes, p); \
     TRACE_W(x, p, 8);            \
     *(uint8_t *)&values[p] = x
 #define WRITE16(p, x)                \
     CHECK_GT(func->numBytes, p + 1); \
     CHECK_EQ((p)&1, 0);              \
     TRACE_W(x, p, 16);               \
     *(uint16_t *)&values[p] = x
 #define WRITE32(p, x)                \
     CHECK_GT(func->numBytes, p + 3); \
     CHECK_EQ((p)&3, 0);              \
     TRACE_W(x, p, 32);               \
     *(uint32_t *)&values[p] = x
 #define WRITE64(p, x)                \
     CHECK_GT(func->numBytes, p + 7); \
     CHECK_EQ((p)&7, 0);              \
     TRACE_W(x, p, 64);               \
     *(uint64_t *)&values[p] = x
 #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
288057e9
 #define READNfrom(maxBytes, from, x, n, p)   \
     CHECK_GT((maxBytes), (p) + (n / 8) - 1); \
     CHECK_EQ((p) & (n / 8 - 1), 0);          \
     x = *(uint_type(n) *)&(from)[(p)];       \
11e86f8c
     TRACE_R(x)
df5c921d
 
288057e9
 #define READN(x, n, p)                                                    \
     do {                                                                  \
         if (p & 0x80000000) {                                             \
             uint32_t pg = p & 0x7fffffff;                                 \
             if (!pg) {                                                    \
                 x = 0;                                                    \
             } else {                                                      \
                 READNfrom(bc->numGlobalBytes, bc->globalBytes, x, n, pg); \
             }                                                             \
         } else {                                                          \
             READNfrom(func->numBytes, values, x, n, p);                   \
         }                                                                 \
     } while (0)
 
 #define READ1(x, p) \
     READN(x, 8, p); \
     x = x & 1
df5c921d
 #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)
288057e9
 #define READP(x, p, asize)                             \
     {                                                  \
         int64_t iptr__;                                \
         READN(iptr__, 64, p);                          \
         x = ptr_torealptr(&ptrinfos, iptr__, (asize)); \
         if (!x) {                                      \
             stop = CL_EBYTECODE;                       \
             break;                                     \
         }                                              \
         TRACE_R(x)                                     \
     }
 #define READPOP(x, p, asize)                 \
     {                                        \
         if ((p)&0x40000000) {                \
             unsigned ptr__ = (p)&0xbfffffff; \
             CHECK_GT(func->numBytes, ptr__); \
             TRACE_PTR(ptr__, asize);         \
             x = (void *)&values[ptr__];      \
         } else {                             \
             READP(x, p, asize)               \
         }                                    \
     }
6922903a
 
288057e9
 #define READOLD8(x, p)              \
     CHECK_GT(func->numBytes, p);    \
     x = *(uint8_t *)&old_values[p]; \
11e86f8c
     TRACE_R(x)
288057e9
 #define READOLD16(x, p)              \
     CHECK_GT(func->numBytes, p + 1); \
     CHECK_EQ((p)&1, 0);              \
     x = *(uint16_t *)&old_values[p]; \
11e86f8c
     TRACE_R(x)
288057e9
 #define READOLD32(x, p)              \
     CHECK_GT(func->numBytes, p + 3); \
     CHECK_EQ((p)&3, 0);              \
     x = *(uint32_t *)&old_values[p]; \
11e86f8c
     TRACE_R(x)
288057e9
 #define READOLD64(x, p)              \
     CHECK_GT(func->numBytes, p + 7); \
     CHECK_EQ((p)&7, 0);              \
     x = *(uint64_t *)&old_values[p]; \
11e86f8c
     TRACE_R(x)
82ca2ab4
 
 #define BINOP(i) inst->u.binop[i]
 
52dd3a6b
 #define DEFINE_BINOP_BC_HELPER(opc, OP, W0, W1, W2, W3, W4) \
288057e9
     case opc * 5: {                                         \
         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;                                              \
     }                                                       \
     case opc * 5 + 1: {                                     \
         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;                                              \
     }                                                       \
     case opc * 5 + 2: {                                     \
         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;                                              \
     }                                                       \
     case opc * 5 + 3: {                                     \
         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;                                              \
     }                                                       \
     case opc * 5 + 4: {                                     \
         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
 
288057e9
 #define CHECK_OP(cond, msg)  \
     if ((cond)) {            \
         cli_dbgmsg(msg);     \
         stop = CL_EBYTECODE; \
         break;               \
     }
 
 #define DEFINE_SCASTOP(opc, OP)   \
     case opc * 5: {               \
         uint8_t res;              \
         int8_t sres;              \
         OP;                       \
         WRITE8(inst->dest, res);  \
         break;                    \
     }                             \
     case opc * 5 + 1: {           \
         uint8_t res;              \
         int8_t sres;              \
         OP;                       \
         WRITE8(inst->dest, res);  \
         break;                    \
     }                             \
     case opc * 5 + 2: {           \
         uint16_t res;             \
         int16_t sres;             \
         OP;                       \
         WRITE16(inst->dest, res); \
         break;                    \
     }                             \
     case opc * 5 + 3: {           \
         uint32_t res;             \
         int32_t sres;             \
         OP;                       \
         WRITE32(inst->dest, res); \
         break;                    \
     }                             \
     case opc * 5 + 4: {           \
         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
 
288057e9
 #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:
82ca2ab4
 
 #define CHOOSE(OP0, OP1, OP2, OP3, OP4) \
288057e9
     switch (inst->u.cast.size) {        \
         case 0:                         \
             OP0;                        \
             break;                      \
         case 1:                         \
             OP1;                        \
             break;                      \
         case 2:                         \
             OP2;                        \
             break;                      \
         case 3:                         \
             OP3;                        \
             break;                      \
         case 4:                         \
             OP4;                        \
             break;                      \
         default:                        \
             CHECK_UNREACHABLE;          \
82ca2ab4
     }
 
288057e9
 #define DEFINE_OP_BC_RET_N(OP, T, R0, W0)                                         \
     case OP: {                                                                    \
         operand_t ret;                                                            \
         T tmp;                                                                    \
         R0(tmp, inst->u.unaryop);                                                 \
         CHECK_GT(stack_depth, 0);                                                 \
         stack_depth--;                                                            \
         stack_entry = pop_stack(&stack, stack_entry, &func, &ret, &bb,            \
                                 &bb_inst);                                        \
         values      = stack_entry ? stack_entry->values : ctx->values;            \
         CHECK_GT(func->numBytes, ret);                                            \
         W0(ret, tmp);                                                             \
         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
 {
288057e9
     unsigned n              = infos->nstacks + 1;
9cbece5c
     struct ptr_info *sinfos = cli_realloc(infos->stack_infos,
288057e9
                                           sizeof(*sinfos) * n);
9cbece5c
     if (!sinfos)
90c0acc7
         return 0;
9cbece5c
     infos->stack_infos = sinfos;
288057e9
     infos->nstacks     = n;
     sinfos             = &sinfos[n - 1];
     sinfos->base       = (uint8_t *)values + off;
     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) {
288057e9
         sinfos = cli_realloc(infos->glob_infos, sizeof(*sinfos) * n);
90c0acc7
         if (!sinfos)
             return 0;
288057e9
         memset(sinfos + infos->nglobs, 0, (n - infos->nglobs) * sizeof(*sinfos));
90c0acc7
         infos->glob_infos = sinfos;
288057e9
         infos->nglobs     = n;
52d0d8bc
     }
288057e9
     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;
288057e9
     return ptr_register_glob_fixedid(infos, values, size, infos->nglobs + 1);
52d0d8bc
 }
 
288057e9
 static inline void *ptr_torealptr(const struct ptr_infos *infos, int64_t ptr,
90c0acc7
                                   uint32_t read_size)
9cbece5c
 {
     struct ptr_info *info;
288057e9
     int32_t ptrid   = ptr >> 32;
9cbece5c
     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) {
288057e9
         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 &&
288057e9
                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__);
288057e9
     (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)
 {
288057e9
     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)
288057e9
         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:
288057e9
             return ty->numElements * globaltypesize(ty->containedTypes[0]);
90c0acc7
         case DStructType:
288057e9
         case DPackedStructType: {
             unsigned i, s = 0;
             for (i = 0; i < ty->numElements; i++)
                 s += globaltypesize(ty->containedTypes[i]);
             return s;
         }
90c0acc7
         default:
             return 0;
52d0d8bc
     }
 }
 
a15fc904
 /* TODO: fix the APIs too */
 static struct {
     cli_apicall_pointer api;
     uint32_t override_size;
 } apisize_override[] = {
288057e9
     {(void *)cli_bcapi_disasm_x86, sizeof(struct DISASM_RESULT)},
     {(void *)cli_bcapi_get_pe_section, sizeof(struct cli_exe_section)},
a15fc904
 };
 
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;
288057e9
     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;
288057e9
     struct cli_bc_bb *bb            = NULL;
     char *values                    = ctx->values;
6922903a
     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));
288057e9
     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];
288057e9
         void **apiglobal              = (void **)(((char *)ctx) + g->offset);
90c0acc7
         if (!apiglobal)
             continue;
         apiptr = *apiglobal;
288057e9
         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);
288057e9
     timeout.tv_usec = tv0.tv_usec + ctx->bytecode_timeout * 1000;
     timeout.tv_sec  = tv0.tv_sec + timeout.tv_usec / 1000000;
74f5816c
     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
         }
92288e16
 
         TRACE_INST(inst);
 
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");
288057e9
                          res = op0 / op1);
90c0acc7
             DEFINE_BINOP(OP_BC_SDIV, CHECK_OP(check_sdivops(sop0, sop1), "bytecode attempted to execute sdiv#0\n");
288057e9
                          res = sop0 / sop1);
90c0acc7
             DEFINE_BINOP(OP_BC_UREM, CHECK_OP(op1 == 0, "bytecode attempted to execute urem#0\n");
288057e9
                          res = op0 % op1);
             DEFINE_BINOP(OP_BC_SREM, CHECK_OP(check_sdivops(sop0, sop1), "bytecode attempted to execute urem#0\n");
                          res = sop0 % sop1);
90c0acc7
 
             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);
 
288057e9
             // clang-format off
90c0acc7
             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)));
288057e9
             // clang-format on
90c0acc7
             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)
288057e9
             stop = jump(func, (values[inst->u.branch.condition] & 1) ? inst->u.branch.br_true : inst->u.branch.br_false,
                         &bb, &inst, &bb_inst);
             continue;
90c0acc7
 
             DEFINE_OP(OP_BC_JMP)
288057e9
             stop = jump(func, inst->u.jump, &bb, &inst, &bb_inst);
             continue;
90c0acc7
 
288057e9
             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);
90c0acc7
 
288057e9
             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));
90c0acc7
 
             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));
 
288057e9
             case OP_BC_SELECT * 5: {
90c0acc7
                 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;
             }
288057e9
             case OP_BC_SELECT * 5 + 1: {
90c0acc7
                 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;
             }
288057e9
             case OP_BC_SELECT * 5 + 2: {
90c0acc7
                 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;
             }
288057e9
             case OP_BC_SELECT * 5 + 3: {
90c0acc7
                 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;
             }
288057e9
             case OP_BC_SELECT * 5 + 4: {
90c0acc7
                 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;
             }
 
288057e9
                 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;
                                 }
90c0acc7
                             }
288057e9
                             READPOP(arg1, inst->u.ops.ops[0], arg1size);
                             res32 = cli_apicalls1[api->idx](ctx, arg1, arg2);
                             WRITE32(inst->dest, res32);
                             break;
90c0acc7
                         }
288057e9
                         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;
90c0acc7
                     }
288057e9
                     break;
90c0acc7
                 }
 
288057e9
                 DEFINE_OP(OP_BC_CALL_DIRECT)
90c0acc7
                 CHECK_FUNCID(inst->u.ops.funcid);
                 func2 = &bc->funcs[inst->u.ops.funcid];
                 CHECK_EQ(func2->numArgs, inst->u.ops.numOps);
288057e9
                 old_values  = values;
90c0acc7
                 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;
288057e9
                 for (i = 0; i < func2->numArgs; i++) {
90c0acc7
                     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]);
288057e9
                             j = (j + 1) & ~1;
90c0acc7
                             CHECK_GT(func2->numBytes, j);
288057e9
                             *(uint16_t *)&values[j] = v;
90c0acc7
                             j += 2;
                             break;
                         }
                         case 4: {
                             uint32_t v;
                             READOLD32(v, inst->u.ops.ops[i]);
288057e9
                             j = (j + 3) & ~3;
90c0acc7
                             CHECK_GT(func2->numBytes, j);
288057e9
                             *(uint32_t *)&values[j] = v;
90c0acc7
                             j += 4;
                             break;
                         }
                         case 8: {
                             uint64_t v;
                             READOLD64(v, inst->u.ops.ops[i]);
288057e9
                             j = (j + 7) & ~7;
90c0acc7
                             CHECK_GT(func2->numBytes, j);
288057e9
                             *(uint64_t *)&values[j] = v;
90c0acc7
                             j += 8;
                             break;
                         }
                     }
                 }
288057e9
                 func    = func2;
                 stackid = ptr_register_stack(&ptrinfos, values, 0, func->numBytes) >> 32;
90c0acc7
                 CHECK_GT(func->numBB, 0);
                 stop = jump(func, 0, &bb, &inst, &bb_inst);
                 stack_depth++;
                 continue;
 
288057e9
             case OP_BC_COPY * 5: {
90c0acc7
                 uint8_t op;
                 READ1(op, BINOP(0));
                 WRITE8(BINOP(1), op);
                 break;
             }
288057e9
             case OP_BC_COPY * 5 + 1: {
90c0acc7
                 uint8_t op;
                 READ8(op, BINOP(0));
                 WRITE8(BINOP(1), op);
                 break;
             }
288057e9
             case OP_BC_COPY * 5 + 2: {
90c0acc7
                 uint16_t op;
                 READ16(op, BINOP(0));
                 WRITE16(BINOP(1), op);
                 break;
             }
288057e9
             case OP_BC_COPY * 5 + 3: {
90c0acc7
                 uint32_t op;
                 READ32(op, BINOP(0));
                 WRITE32(BINOP(1), op);
                 break;
             }
288057e9
             case OP_BC_COPY * 5 + 4: {
90c0acc7
                 uint64_t op;
                 READ64(op, BINOP(0));
                 WRITE64(BINOP(1), op);
                 break;
             }
 
288057e9
             case OP_BC_LOAD * 5:
             case OP_BC_LOAD * 5 + 1: {
90c0acc7
                 uint8_t *ptr;
                 READPOP(ptr, inst->u.unaryop, 1);
                 WRITE8(inst->dest, (*ptr));
                 break;
             }
288057e9
             case OP_BC_LOAD * 5 + 2: {
90c0acc7
                 const union unaligned_16 *ptr;
                 READPOP(ptr, inst->u.unaryop, 2);
                 WRITE16(inst->dest, (ptr->una_u16));
                 break;
             }
288057e9
             case OP_BC_LOAD * 5 + 3: {
90c0acc7
                 const union unaligned_32 *ptr;
                 READPOP(ptr, inst->u.unaryop, 4);
                 WRITE32(inst->dest, (ptr->una_u32));
                 break;
             }
288057e9
             case OP_BC_LOAD * 5 + 4: {
90c0acc7
                 const union unaligned_64 *ptr;
                 READPOP(ptr, inst->u.unaryop, 8);
                 WRITE64(inst->dest, (ptr->una_u64));
                 break;
             }
 
288057e9
             case OP_BC_STORE * 5: {
90c0acc7
                 uint8_t *ptr;
                 uint8_t v;
                 READP(ptr, BINOP(1), 1);
                 READ1(v, BINOP(0));
                 *ptr = v;
                 break;
             }
288057e9
             case OP_BC_STORE * 5 + 1: {
90c0acc7
                 uint8_t *ptr;
                 uint8_t v;
                 READP(ptr, BINOP(1), 1);
                 READ8(v, BINOP(0));
                 *ptr = v;
                 break;
             }
288057e9
             case OP_BC_STORE * 5 + 2: {
90c0acc7
                 union unaligned_16 *ptr;
                 uint16_t v;
                 READP(ptr, BINOP(1), 2);
                 READ16(v, BINOP(0));
                 ptr->una_s16 = v;
                 break;
             }
288057e9
             case OP_BC_STORE * 5 + 3: {
90c0acc7
                 union unaligned_32 *ptr;
                 uint32_t v;
                 READP(ptr, BINOP(1), 4);
                 READ32(v, BINOP(0));
                 ptr->una_u32 = v;
                 break;
             }
288057e9
             case OP_BC_STORE * 5 + 4: {
90c0acc7
                 union unaligned_64 *ptr;
                 uint64_t v;
                 READP(ptr, BINOP(1), 8);
                 READ64(v, BINOP(0));
                 ptr->una_u64 = v;
                 break;
             }
288057e9
                 DEFINE_OP(OP_BC_ISBIGENDIAN)
                 {
                     WRITE8(inst->dest, WORDS_BIGENDIAN);
                     break;
90c0acc7
                 }
288057e9
                 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);
                     }
90c0acc7
 
288057e9
                     if (!(inst->interp_op % 5)) {
                         // how do negative offsets affect pointer initialization?
                         WRITE64(inst->dest, ptr_compose(stackid,
                                                         inst->u.three[1] + off));
                     } else {
                         READ64(ptr, inst->u.three[1]);
                         off += (ptr & 0x00000000ffffffffULL);
                         iptr = (ptr & 0xffffffff00000000ULL) + (uint64_t)(off);
                         WRITE64(inst->dest, ptr + off);
                     }
                     break;
90c0acc7
                 }
288057e9
                 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;
90c0acc7
                 }
288057e9
                 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);
                     }
90c0acc7
 
288057e9
                     if (!(inst->interp_op % 5)) {
                         // how do negative offsets affect pointer initialization?
                         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;
90c0acc7
                 }
             /* TODO: implement OP_BC_GEP1, OP_BC_GEP2, OP_BC_GEPN */
             default:
                 cli_errmsg("Opcode %u of type %u is not implemented yet!\n",
288057e9
                            inst->interp_op / 5, inst->interp_op % 5);
90c0acc7
                 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",
288057e9
                    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;
 }