ClamAV is an open source (GPLv2) anti-virus toolkit.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
clamav/libclamav/js/vmjumps.c

589 lines
13 KiB

/*
* Optimized `jumps' instruction dispatcher.
* Copyright (c) 1998 New Generation Software (NGS) Oy
*
* Author: Markku Rossi <mtr@ngs.fi>
*/
/*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA
*/
/*
* $Source: /tmp/cvsroot-15-2-2007/clamav-devel/libclamav/js/vmjumps.c,v $
* $Id: vmjumps.c,v 1.2 2006/10/28 11:27:44 njh Exp $
*/
#if HAVE_CONFIG_H
#include "clamav-config.h"
#endif
#ifdef CL_EXPERIMENTAL
#include "jsint.h"
#if __GNUC__ && !DISABLE_JUMPS
/*
* Types and definitions.
*/
#define SAVE_OP(a) \
reloc[cp - code_start - 1] = &f->code[cpos]; \
f->code[cpos++].u.ptr = (a)
#define SAVE_INT8(a) f->code[cpos++].u.i8 = (a)
#define SAVE_INT16(a) f->code[cpos++].u.i16 = (a)
#define SAVE_INT32(a) f->code[cpos++].u.i32 = (a)
#define ARG_INT32() f->code[cpos].u.i32
#if BC_OPERAND_HOOKS
#define NEXT() \
do { \
if (++vm->hook_operand_count >= vm->hook_operand_count_trigger) \
{ \
JS_CALL_HOOK (JS_VM_EVENT_OPERAND_COUNT); \
vm->hook_operand_count = 0; \
} \
goto *((pc++)->u.ptr); \
} while (0)
#else /* not BC_OPERAND_HOOKS */
#define NEXT() goto *((pc++)->u.ptr)
#endif /* not BC_OPERAND_HOOKS */
#define READ_INT8(var) (var) = (pc++)->u.i8
#define READ_INT16(var) (var) = (pc++)->u.i16
#define READ_INT32(var) (var) = (pc++)->u.i32
#define SETPC(ofs) pc = (ofs)
#define SETPC_RELATIVE(ofs) pc += (ofs)
#define CALL_USER_FUNC(f) pc = ((Function *) (f))->code
#define DONE() goto done
#define ERROR(msg) \
do { \
JS_SAVE_REGS (); \
strcpy (vm->error, (msg)); \
js_vm_error (vm); \
/* NOTREACHED */ \
} while (0)
#if PROFILING
#define OPERAND(op) vm->prof_op = (op)
#else
#define OPERAND(op)
#endif
struct compiled_st
{
union
{
void *ptr;
JSInt8 i8;
JSInt16 i16;
JSInt32 i32;
} u;
};
typedef struct compiled_st Compiled;
/* Debug information. */
struct debug_info_st
{
void *pc;
unsigned int linenum;
};
typedef struct debug_info_st DebugInfo;
struct function_st
{
JSHeapDestroyableCB destroy;
char *name;
Compiled *code;
unsigned int length;
struct
{
char *file;
unsigned int num_info;
DebugInfo *info;
} debug;
};
typedef struct function_st Function;
/*
* Static functions.
*/
static void
function_destroy (void *ptr)
{
Function *f = ptr;
int i;
/* Name. */
js_free (f->name);
/* Code. */
js_free (f->code);
/* Debug info. */
if (f->debug.file)
js_free (f->debug.file);
if (f->debug.info)
js_free (f->debug.info);
}
#endif /* not (__GNUC__ && !DISABLE_JUMPS) */
/*
* Global functions.
*/
int
js_vm_jumps_exec (JSVirtualMachine *vm, JSByteCode *bc, JSSymtabEntry *symtab,
unsigned int num_symtab_entries, unsigned int consts_offset,
unsigned int anonymous_function_offset,
unsigned char *debug_info, unsigned int debug_info_len,
JSNode *object, JSNode *func,
unsigned int argc, JSNode *argv)
{
#if __GNUC__ && !DISABLE_JUMPS
int s;
unsigned int ui;
Function *global_f = NULL;
Function *f = NULL;
unsigned char *code = NULL;
JSNode *sp = NULL;
JSNode *fp = NULL;
Compiled *pc = NULL;
char *debug_filename = "unknown";
char buf[512];
unsigned int opcount = 0;
if (bc)
{
/* Executing byte-code. */
/* Find the code section. */
for (s = 0; s < bc->num_sects; s++)
if (bc->sects[s].type == JS_BCST_CODE)
code = bc->sects[s].data;
assert (code != NULL);
/* Enter all functions to the known functions of the VM. */
for (s = 0; s < num_symtab_entries; s++)
{
/* We need one function. */
f = js_vm_alloc_destroyable (vm, sizeof (*f));
f->destroy = function_destroy;
f->name = js_strdup (vm, symtab[s].name);
if (strcmp (symtab[s].name, JS_GLOBAL_NAME) == 0)
global_f = f;
else
{
int is_anonymous = 0;
/* Check for the anonymous function. */
if (symtab[s].name[0] == '.' && symtab[s].name[1] == 'F'
&& symtab[s].name[2] == ':')
is_anonymous = 1;
if (vm->verbose > 3)
{
sprintf (buf, "VM: link: %s(): start=%d, length=%d",
symtab[s].name, symtab[s].offset,
symtab[s + 1].offset - symtab[s].offset);
if (is_anonymous)
sprintf (buf + strlen (buf),
", relocating with offset %u",
anonymous_function_offset);
strcat (buf, JS_HOST_LINE_BREAK);
js_iostream_write (vm->s_stderr, buf, strlen (buf));
}
if (is_anonymous)
{
sprintf (buf, ".F:%u",
(unsigned int) atoi (symtab[s].name + 3)
+ anonymous_function_offset);
ui = js_vm_intern (vm, buf);
}
else
ui = js_vm_intern (vm, symtab[s].name);
vm->globals[ui].type = JS_FUNC;
vm->globals[ui].u.vfunction = js_vm_make_function (vm, f);
}
/* Link the code to our environment.*/
{
unsigned char *cp;
unsigned char *code_start, *code_end;
unsigned char *fixed_code;
JSInt32 i;
unsigned int cpos;
Compiled **reloc;
unsigned int length;
length = symtab[s + 1].offset - symtab[s].offset + 1;
/*
* Allocate space for our compiled code. <length> is enought,
* but is is almost always too much. Who cares?
*/
f->code = js_malloc (vm, length * sizeof (Compiled));
reloc = js_calloc (vm, 1, length * sizeof (Compiled *));
fixed_code = js_malloc (vm, length);
memcpy (fixed_code, code + symtab[s].offset, length);
fixed_code[length - 1] = 1; /* op `done'. */
code_start = fixed_code;
code_end = code_start + length;
/* Link phase 1: constants and symbols. */
cp = code_start;
cpos = 0;
while (cp < code_end)
{
switch (*cp++)
{
/* include c1jumps.h */
#include "c1jumps.h"
/* end include c1jumps.h */
}
}
f->length = cpos;
/* Link phase 2: relative jumps. */
cp = code_start;
cpos = 0;
while (cp < code_end)
{
switch (*cp++)
{
/* include c2jumps.h */
#include "c2jumps.h"
/* end include c2jumps.h */
}
}
/* Handle debug info. */
if (debug_info)
{
unsigned int di_start = symtab[s].offset;
unsigned int di_end = symtab[s + 1].offset;
unsigned int ln;
for (; debug_info_len > 0;)
{
switch (*debug_info)
{
case JS_DI_FILENAME:
debug_info++;
debug_info_len--;
JS_BC_READ_INT32 (debug_info, ui);
debug_info += 4;
debug_info_len -= 4;
f->debug.file = js_malloc (vm, ui + 1);
memcpy (f->debug.file, debug_info, ui);
f->debug.file[ui] = '\0';
debug_filename = f->debug.file;
debug_info += ui;
debug_info_len -= ui;
break;
case JS_DI_LINENUMBER:
JS_BC_READ_INT32 (debug_info + 1, ui);
if (ui > di_end)
goto debug_info_done;
/* This belongs to us (maybe). */
debug_info += 5;
debug_info_len -= 5;
JS_BC_READ_INT32 (debug_info, ln);
debug_info += 4;
debug_info_len -= 4;
if (di_start <= ui && ui <= di_end)
{
ui -= di_start;
f->debug.info = js_realloc (vm, f->debug.info,
(f->debug.num_info + 1)
* sizeof (DebugInfo));
f->debug.info[f->debug.num_info].pc = reloc[ui];
f->debug.info[f->debug.num_info].linenum = ln;
f->debug.num_info++;
}
break;
default:
sprintf (buf,
"VM: unknown debug information type %d%s",
*debug_info, JS_HOST_LINE_BREAK);
js_iostream_write (vm->s_stderr, buf, strlen (buf));
js_iostream_flush (vm->s_stderr);
abort ();
break;
}
}
debug_info_done:
if (f->debug.file == NULL)
f->debug.file = js_strdup (vm, debug_filename);
}
js_free (reloc);
js_free (fixed_code);
}
}
}
else
{
int i;
/* Applying arguments to function. */
if (func->type != JS_FUNC)
{
sprintf (vm->error, "illegal function in apply");
return 0;
}
if (vm->verbose > 1)
{
sprintf (buf, "VM: calling function%s",
JS_HOST_LINE_BREAK);
js_iostream_write (vm->s_stderr, buf, strlen (buf));
}
f = func->u.vfunction->implementation;
/* Init stack. */
sp = vm->sp;
/*
* Save the applied function to the stack. If our script
* overwrites the function, the function will not be deleted
* under us, since it is protected from the gc in the stack.
*/
JS_COPY (JS_SP0, func);
JS_PUSH ();
/* Push arguments to the stack. */
for (i = argc - 1; i >= 0; i--)
{
JS_COPY (JS_SP0, &argv[i]);
JS_PUSH ();
}
/* This pointer. */
if (object)
JS_COPY (JS_SP0, object);
else
JS_SP0->type = JS_NULL;
JS_PUSH ();
/* Init fp and pc so our SUBROUTINE_CALL will work. */
fp = NULL;
pc = NULL;
JS_SUBROUTINE_CALL (f);
/* Run. */
NEXT ();
}
if (global_f)
{
if (vm->verbose > 1)
{
sprintf (buf, "VM: exec: %s%s", JS_GLOBAL_NAME,
JS_HOST_LINE_BREAK);
js_iostream_write (vm->s_stderr, buf, strlen (buf));
}
/* Create the initial stack frame by hand. */
sp = vm->sp;
/*
* Push the global function to the stack. There it is protected
* from the garbage collection, as long, as we are executing the
* global code. It is also removed automatically, when the
* execution ends.
*/
JS_SP0->type = JS_FUNC;
JS_SP0->u.vfunction = js_vm_make_function (vm, global_f);
JS_PUSH ();
/* Empty this pointer. */
JS_SP0->type = JS_NULL;
JS_PUSH ();
/* Init fp and pc so our JS_SUBROUTINE_CALL macro works. */
fp = NULL;
pc = NULL;
JS_SUBROUTINE_CALL (global_f);
/* Run. */
NEXT ();
}
/* The smart done label. */
done:
/*
* The return value from function calls and global evals is at JS_SP1.
* If <sp> is NULL, then we were linking byte-code that didn't have
* .global section.
*/
if (sp)
JS_COPY (&vm->exec_result, JS_SP1);
else
vm->exec_result.type = JS_UNDEFINED;
/* All done. */
return 1;
/* And finally, include the operands. */
{
JSNode builtin_result;
JSNode *function;
JSInt32 i, j;
JSInt8 i8;
/* include ejumps.h */
#include "ejumps.h"
/* end include ejumps.h */
}
#else /* not (__GNUC__ && !DISABLE_JUMPS) */
return 0;
#endif /* not (__GNUC__ && !DISABLE_JUMPS) */
}
const char *
js_vm_jumps_func_name (JSVirtualMachine *vm, void *program_counter)
{
#if __GNUC__ && !DISABLE_JUMPS
int i;
Function *f;
Compiled *pc = program_counter;
JSNode *sp = vm->sp;
/* Check the globals. */
for (i = 0; i < vm->num_globals; i++)
if (vm->globals[i].type == JS_FUNC)
{
f = (Function *) vm->globals[i].u.vfunction->implementation;
if (f->code < pc && pc < f->code + f->length)
return f->name;
}
/* No luck. Let's try the stack. */
for (sp++; sp < vm->stack + vm->stack_size; sp++)
if (sp->type == JS_FUNC)
{
f = (Function *) sp->u.vfunction->implementation;
if (f->code < pc && pc < f->code + f->length)
return f->name;
}
/* Still no matches. This shouldn't be reached... ok, who cares? */
return JS_GLOBAL_NAME;
#else /* not (__GNUC__ && !DISABLE_JUMPS) */
return "";
#endif /* not (__GNUC__ && !DISABLE_JUMPS) */
}
const char *
js_vm_jumps_debug_position (JSVirtualMachine *vm, unsigned int *linenum_return)
{
#if __GNUC__ && !DISABLE_JUMPS
int i;
Function *f;
void *program_counter = vm->pc;
Compiled *pc = vm->pc;
JSNode *sp = vm->sp;
unsigned int linenum = 0;
/* Check the globals. */
for (i = 0; i < vm->num_globals; i++)
if (vm->globals[i].type == JS_FUNC)
{
f = (Function *) vm->globals[i].u.vfunction->implementation;
if (f->code < pc && pc < f->code + f->length)
{
found:
/* Ok, found it. */
if (f->debug.file == NULL)
/* No debugging information available for this function. */
return NULL;
/* Find the correct pc position. */
for (i = 0; i < f->debug.num_info; i++)
{
if (f->debug.info[i].pc > program_counter)
break;
linenum = f->debug.info[i].linenum;
}
*linenum_return = linenum;
return f->debug.file;
}
}
/* No luck. Let's try the stack. */
for (sp++; sp < vm->stack + vm->stack_size; sp++)
if (sp->type == JS_FUNC)
{
f = (Function *) sp->u.vfunction->implementation;
if (f->code < pc && pc < f->code + f->length)
/* Found it. */
goto found;
}
/* Couldn't find the function we are executing. */
return NULL;
#else /* not (__GNUC__ && !DISABLE_JUMPS) */
return NULL;
#endif /* not (__GNUC__ && !DISABLE_JUMPS) */
}
#endif /*CL_EXPERIMENTAL*/