9703c2bb75
may be unexpected. svn path=/trunk/; revision=24232
570 lines
14 KiB
C
570 lines
14 KiB
C
/*
|
|
* $Id$
|
|
*
|
|
* Wireshark - Network traffic analyzer
|
|
* By Gerald Combs <gerald@wireshark.org>
|
|
* Copyright 2001 Gerald Combs
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include "dfilter-int.h"
|
|
#include "gencode.h"
|
|
#include "dfvm.h"
|
|
#include "syntax-tree.h"
|
|
#include "sttype-range.h"
|
|
#include "sttype-test.h"
|
|
#include "sttype-function.h"
|
|
#include "ftypes/ftypes.h"
|
|
|
|
static void
|
|
gencode(dfwork_t *dfw, stnode_t *st_node);
|
|
|
|
static int
|
|
gen_entity(dfwork_t *dfw, stnode_t *st_arg, dfvm_value_t **p_jmp);
|
|
|
|
static void
|
|
dfw_append_insn(dfwork_t *dfw, dfvm_insn_t *insn)
|
|
{
|
|
insn->id = dfw->next_insn_id;
|
|
dfw->next_insn_id++;
|
|
g_ptr_array_add(dfw->insns, insn);
|
|
}
|
|
|
|
static void
|
|
dfw_append_const(dfwork_t *dfw, dfvm_insn_t *insn)
|
|
{
|
|
insn->id = dfw->next_const_id;
|
|
dfw->next_const_id++;
|
|
g_ptr_array_add(dfw->consts, insn);
|
|
}
|
|
|
|
/* returns register number */
|
|
static int
|
|
dfw_append_read_tree(dfwork_t *dfw, header_field_info *hfinfo)
|
|
{
|
|
dfvm_insn_t *insn;
|
|
dfvm_value_t *val1, *val2;
|
|
int reg = -1;
|
|
gboolean added_new_hfinfo = FALSE;
|
|
|
|
/* Rewind to find the first field of this name. */
|
|
while (hfinfo->same_name_prev) {
|
|
hfinfo = hfinfo->same_name_prev;
|
|
}
|
|
|
|
/* Keep track of which registers
|
|
* were used for which hfinfo's so that we
|
|
* can re-use registers. */
|
|
reg = GPOINTER_TO_UINT(
|
|
g_hash_table_lookup(dfw->loaded_fields, hfinfo));
|
|
if (reg) {
|
|
/* Reg's are stored in has as reg+1, so
|
|
* that the non-existence of a hfinfo in
|
|
* the hash, or 0, can be differentiated from
|
|
* a hfinfo being loaded into register #0. */
|
|
reg--;
|
|
}
|
|
else {
|
|
reg = dfw->next_register++;
|
|
g_hash_table_insert(dfw->loaded_fields,
|
|
hfinfo, GUINT_TO_POINTER(reg + 1));
|
|
|
|
added_new_hfinfo = TRUE;
|
|
}
|
|
|
|
insn = dfvm_insn_new(READ_TREE);
|
|
val1 = dfvm_value_new(HFINFO);
|
|
val1->value.hfinfo = hfinfo;
|
|
val2 = dfvm_value_new(REGISTER);
|
|
val2->value.numeric = reg;
|
|
|
|
insn->arg1 = val1;
|
|
insn->arg2 = val2;
|
|
dfw_append_insn(dfw, insn);
|
|
|
|
if (added_new_hfinfo) {
|
|
while (hfinfo) {
|
|
/* Record the FIELD_ID in hash of interesting fields. */
|
|
g_hash_table_insert(dfw->interesting_fields,
|
|
GINT_TO_POINTER(hfinfo->id),
|
|
GUINT_TO_POINTER(TRUE));
|
|
hfinfo = hfinfo->same_name_next;
|
|
}
|
|
}
|
|
|
|
return reg;
|
|
}
|
|
|
|
/* returns register number */
|
|
static int
|
|
dfw_append_put_fvalue(dfwork_t *dfw, fvalue_t *fv)
|
|
{
|
|
dfvm_insn_t *insn;
|
|
dfvm_value_t *val1, *val2;
|
|
int reg;
|
|
|
|
insn = dfvm_insn_new(PUT_FVALUE);
|
|
val1 = dfvm_value_new(FVALUE);
|
|
val1->value.fvalue = fv;
|
|
val2 = dfvm_value_new(REGISTER);
|
|
reg = dfw->first_constant--;
|
|
val2->value.numeric = reg;
|
|
insn->arg1 = val1;
|
|
insn->arg2 = val2;
|
|
dfw_append_const(dfw, insn);
|
|
|
|
return reg;
|
|
}
|
|
|
|
/* returns register number */
|
|
static int
|
|
dfw_append_mk_range(dfwork_t *dfw, stnode_t *node)
|
|
{
|
|
int hf_reg, reg;
|
|
header_field_info *hfinfo;
|
|
dfvm_insn_t *insn;
|
|
dfvm_value_t *val;
|
|
|
|
hfinfo = sttype_range_hfinfo(node);
|
|
hf_reg = dfw_append_read_tree(dfw, hfinfo);
|
|
|
|
insn = dfvm_insn_new(MK_RANGE);
|
|
|
|
val = dfvm_value_new(REGISTER);
|
|
val->value.numeric = hf_reg;
|
|
insn->arg1 = val;
|
|
|
|
val = dfvm_value_new(REGISTER);
|
|
reg =dfw->next_register++;
|
|
val->value.numeric = reg;
|
|
insn->arg2 = val;
|
|
|
|
val = dfvm_value_new(DRANGE);
|
|
val->value.drange = sttype_range_drange(node);
|
|
insn->arg3 = val;
|
|
|
|
sttype_range_remove_drange(node);
|
|
|
|
dfw_append_insn(dfw, insn);
|
|
|
|
return reg;
|
|
}
|
|
|
|
/* returns register number that the functions's result will be in. */
|
|
static int
|
|
dfw_append_function(dfwork_t *dfw, stnode_t *node, dfvm_value_t **p_jmp)
|
|
{
|
|
GSList *params;
|
|
int i, num_params, reg;
|
|
dfvm_value_t **jmps;
|
|
dfvm_insn_t *insn;
|
|
dfvm_value_t *val1, *val2, *val;
|
|
|
|
params = sttype_function_params(node);
|
|
num_params = g_slist_length(params);
|
|
|
|
/* Array to hold the instructions that need to jump to
|
|
* an instruction if they fail. */
|
|
jmps = g_malloc(num_params * sizeof(dfvm_value_t*));
|
|
|
|
/* Create the new DFVM instruction */
|
|
insn = dfvm_insn_new(CALL_FUNCTION);
|
|
|
|
val1 = dfvm_value_new(FUNCTION_DEF);
|
|
val1->value.funcdef = sttype_function_funcdef(node);
|
|
insn->arg1 = val1;
|
|
val2 = dfvm_value_new(REGISTER);
|
|
val2->value.numeric = dfw->next_register++;
|
|
insn->arg2 = val2;
|
|
insn->arg3 = NULL;
|
|
insn->arg4 = NULL;
|
|
|
|
i = 0;
|
|
while (params) {
|
|
jmps[i] = NULL;
|
|
reg = gen_entity(dfw, params->data, &jmps[i]);
|
|
|
|
val = dfvm_value_new(REGISTER);
|
|
val->value.numeric = reg;
|
|
|
|
switch(i) {
|
|
case 0:
|
|
insn->arg3 = val;
|
|
break;
|
|
case 1:
|
|
insn->arg4 = val;
|
|
break;
|
|
default:
|
|
g_assert_not_reached();
|
|
}
|
|
|
|
params = params->next;
|
|
i++;
|
|
}
|
|
|
|
dfw_append_insn(dfw, insn);
|
|
|
|
/* If any of our parameters failed, send them to
|
|
* our own failure instruction. This *has* to be done
|
|
* after we caled dfw_append_insn above so that
|
|
* we know what the next DFVM insruction is, via
|
|
* dfw->next_insn_id */
|
|
for (i = 0; i < num_params; i++) {
|
|
if (jmps[i]) {
|
|
jmps[i]->value.numeric = dfw->next_insn_id;
|
|
}
|
|
}
|
|
|
|
/* We need another instruction to jump to another exit
|
|
* place, if the call() of our function failed for some reaosn */
|
|
insn = dfvm_insn_new(IF_FALSE_GOTO);
|
|
g_assert(p_jmp);
|
|
*p_jmp = dfvm_value_new(INSN_NUMBER);
|
|
insn->arg1 = *p_jmp;
|
|
dfw_append_insn(dfw, insn);
|
|
|
|
g_free(jmps);
|
|
|
|
return val2->value.numeric;
|
|
}
|
|
|
|
|
|
static void
|
|
gen_relation(dfwork_t *dfw, dfvm_opcode_t op, stnode_t *st_arg1, stnode_t *st_arg2)
|
|
{
|
|
dfvm_insn_t *insn;
|
|
dfvm_value_t *val1, *val2;
|
|
dfvm_value_t *jmp1 = NULL, *jmp2 = NULL;
|
|
int reg1 = -1, reg2 = -1;
|
|
|
|
/* Create code for the LHS and RHS of the relation */
|
|
reg1 = gen_entity(dfw, st_arg1, &jmp1);
|
|
reg2 = gen_entity(dfw, st_arg2, &jmp2);
|
|
|
|
/* Then combine them in a DFVM insruction */
|
|
insn = dfvm_insn_new(op);
|
|
val1 = dfvm_value_new(REGISTER);
|
|
val1->value.numeric = reg1;
|
|
val2 = dfvm_value_new(REGISTER);
|
|
val2->value.numeric = reg2;
|
|
insn->arg1 = val1;
|
|
insn->arg2 = val2;
|
|
dfw_append_insn(dfw, insn);
|
|
|
|
/* If either of the relation argumnents need an "exit" instruction
|
|
* to jump to (on failure), mark them */
|
|
if (jmp1) {
|
|
jmp1->value.numeric = dfw->next_insn_id;
|
|
}
|
|
|
|
if (jmp2) {
|
|
jmp2->value.numeric = dfw->next_insn_id;
|
|
}
|
|
}
|
|
|
|
/* Parse an entity, returning the reg that it gets put into.
|
|
* p_jmp will be set if it has to be set by the calling code; it should
|
|
* be set to the place to jump to, to return to the calling code,
|
|
* if the load of a field from the proto_tree fails. */
|
|
static int
|
|
gen_entity(dfwork_t *dfw, stnode_t *st_arg, dfvm_value_t **p_jmp)
|
|
{
|
|
sttype_id_t e_type;
|
|
dfvm_insn_t *insn;
|
|
header_field_info *hfinfo;
|
|
int reg = -1;
|
|
e_type = stnode_type_id(st_arg);
|
|
|
|
if (e_type == STTYPE_FIELD) {
|
|
hfinfo = stnode_data(st_arg);
|
|
reg = dfw_append_read_tree(dfw, hfinfo);
|
|
|
|
insn = dfvm_insn_new(IF_FALSE_GOTO);
|
|
g_assert(p_jmp);
|
|
*p_jmp = dfvm_value_new(INSN_NUMBER);
|
|
insn->arg1 = *p_jmp;
|
|
dfw_append_insn(dfw, insn);
|
|
}
|
|
else if (e_type == STTYPE_FVALUE) {
|
|
reg = dfw_append_put_fvalue(dfw, stnode_data(st_arg));
|
|
}
|
|
else if (e_type == STTYPE_RANGE) {
|
|
reg = dfw_append_mk_range(dfw, st_arg);
|
|
}
|
|
else if (e_type == STTYPE_FUNCTION) {
|
|
reg = dfw_append_function(dfw, st_arg, p_jmp);
|
|
}
|
|
else {
|
|
printf("sttype_id is %u\n", e_type);
|
|
g_assert_not_reached();
|
|
}
|
|
return reg;
|
|
}
|
|
|
|
|
|
static void
|
|
gen_test(dfwork_t *dfw, stnode_t *st_node)
|
|
{
|
|
test_op_t st_op;
|
|
stnode_t *st_arg1, *st_arg2;
|
|
dfvm_value_t *val1;
|
|
dfvm_insn_t *insn;
|
|
|
|
header_field_info *hfinfo;
|
|
|
|
sttype_test_get(st_node, &st_op, &st_arg1, &st_arg2);
|
|
|
|
switch (st_op) {
|
|
case TEST_OP_UNINITIALIZED:
|
|
g_assert_not_reached();
|
|
break;
|
|
|
|
case TEST_OP_EXISTS:
|
|
val1 = dfvm_value_new(HFINFO);
|
|
hfinfo = stnode_data(st_arg1);
|
|
|
|
/* Rewind to find the first field of this name. */
|
|
while (hfinfo->same_name_prev) {
|
|
hfinfo = hfinfo->same_name_prev;
|
|
}
|
|
val1->value.hfinfo = hfinfo;
|
|
insn = dfvm_insn_new(CHECK_EXISTS);
|
|
insn->arg1 = val1;
|
|
dfw_append_insn(dfw, insn);
|
|
|
|
/* Record the FIELD_ID in hash of interesting fields. */
|
|
while (hfinfo) {
|
|
g_hash_table_insert(dfw->interesting_fields,
|
|
GINT_TO_POINTER(hfinfo->id),
|
|
GUINT_TO_POINTER(TRUE));
|
|
hfinfo = hfinfo->same_name_next;
|
|
}
|
|
|
|
break;
|
|
|
|
case TEST_OP_NOT:
|
|
gencode(dfw, st_arg1);
|
|
insn = dfvm_insn_new(NOT);
|
|
dfw_append_insn(dfw, insn);
|
|
break;
|
|
|
|
case TEST_OP_AND:
|
|
gencode(dfw, st_arg1);
|
|
|
|
insn = dfvm_insn_new(IF_FALSE_GOTO);
|
|
val1 = dfvm_value_new(INSN_NUMBER);
|
|
insn->arg1 = val1;
|
|
dfw_append_insn(dfw, insn);
|
|
|
|
gencode(dfw, st_arg2);
|
|
val1->value.numeric = dfw->next_insn_id;
|
|
break;
|
|
|
|
case TEST_OP_OR:
|
|
gencode(dfw, st_arg1);
|
|
|
|
insn = dfvm_insn_new(IF_TRUE_GOTO);
|
|
val1 = dfvm_value_new(INSN_NUMBER);
|
|
insn->arg1 = val1;
|
|
dfw_append_insn(dfw, insn);
|
|
|
|
gencode(dfw, st_arg2);
|
|
val1->value.numeric = dfw->next_insn_id;
|
|
break;
|
|
|
|
case TEST_OP_EQ:
|
|
gen_relation(dfw, ANY_EQ, st_arg1, st_arg2);
|
|
break;
|
|
|
|
case TEST_OP_NE:
|
|
gen_relation(dfw, ANY_NE, st_arg1, st_arg2);
|
|
break;
|
|
|
|
case TEST_OP_GT:
|
|
gen_relation(dfw, ANY_GT, st_arg1, st_arg2);
|
|
break;
|
|
|
|
case TEST_OP_GE:
|
|
gen_relation(dfw, ANY_GE, st_arg1, st_arg2);
|
|
break;
|
|
|
|
case TEST_OP_LT:
|
|
gen_relation(dfw, ANY_LT, st_arg1, st_arg2);
|
|
break;
|
|
|
|
case TEST_OP_LE:
|
|
gen_relation(dfw, ANY_LE, st_arg1, st_arg2);
|
|
break;
|
|
|
|
case TEST_OP_BITWISE_AND:
|
|
gen_relation(dfw, ANY_BITWISE_AND, st_arg1, st_arg2);
|
|
break;
|
|
|
|
case TEST_OP_CONTAINS:
|
|
gen_relation(dfw, ANY_CONTAINS, st_arg1, st_arg2);
|
|
break;
|
|
|
|
case TEST_OP_MATCHES:
|
|
gen_relation(dfw, ANY_MATCHES, st_arg1, st_arg2);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gencode(dfwork_t *dfw, stnode_t *st_node)
|
|
{
|
|
const char *name;
|
|
|
|
name = stnode_type_name(st_node);
|
|
|
|
switch (stnode_type_id(st_node)) {
|
|
case STTYPE_TEST:
|
|
gen_test(dfw, st_node);
|
|
break;
|
|
default:
|
|
g_assert_not_reached();
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
dfw_gencode(dfwork_t *dfw)
|
|
{
|
|
int id, id1, length;
|
|
dfvm_insn_t *insn, *insn1, *prev;
|
|
dfvm_value_t *arg1;
|
|
|
|
dfw->insns = g_ptr_array_new();
|
|
dfw->consts = g_ptr_array_new();
|
|
dfw->loaded_fields = g_hash_table_new(g_direct_hash, g_direct_equal);
|
|
dfw->interesting_fields = g_hash_table_new(g_direct_hash, g_direct_equal);
|
|
gencode(dfw, dfw->st_root);
|
|
dfw_append_insn(dfw, dfvm_insn_new(RETURN));
|
|
|
|
/* fixup goto */
|
|
length = dfw->insns->len;
|
|
|
|
for (id = 0, prev = NULL; id < length; prev = insn, id++) {
|
|
insn = g_ptr_array_index(dfw->insns, id);
|
|
arg1 = insn->arg1;
|
|
if (insn->op == IF_TRUE_GOTO || insn->op == IF_FALSE_GOTO) {
|
|
dfvm_opcode_t revert = (insn->op == IF_FALSE_GOTO)?IF_TRUE_GOTO:IF_FALSE_GOTO;
|
|
id1 = arg1->value.numeric;
|
|
do {
|
|
insn1 = g_ptr_array_index(dfw->insns, id1);
|
|
if (insn1->op == revert) {
|
|
/* this one is always false and the branch is not taken*/
|
|
id1 = id1 +1;
|
|
continue;
|
|
}
|
|
else if (insn1->op == READ_TREE && prev && prev->op == READ_TREE &&
|
|
prev->arg2->value.numeric == insn1->arg2->value.numeric) {
|
|
/* hack if it's the same register it's the same field
|
|
* and it returns the same value
|
|
*/
|
|
id1 = id1 +1;
|
|
continue;
|
|
}
|
|
else if (insn1->op != insn->op) {
|
|
/* bail out */
|
|
arg1 = insn->arg1;
|
|
arg1->value.numeric = id1;
|
|
break;
|
|
}
|
|
arg1 = insn1->arg1;
|
|
id1 = arg1->value.numeric;
|
|
} while (1);
|
|
}
|
|
}
|
|
|
|
/* move constants after registers*/
|
|
if (dfw->first_constant == -1) {
|
|
/* NONE */
|
|
dfw->first_constant = 0;
|
|
return;
|
|
}
|
|
|
|
id = -dfw->first_constant -1;
|
|
dfw->first_constant = dfw->next_register;
|
|
dfw->next_register += id;
|
|
|
|
length = dfw->consts->len;
|
|
for (id = 0; id < length; id++) {
|
|
insn = g_ptr_array_index(dfw->consts, id);
|
|
if (insn->arg2 && insn->arg2->type == REGISTER && (int)insn->arg2->value.numeric < 0 )
|
|
insn->arg2->value.numeric = dfw->first_constant - insn->arg2->value.numeric -1;
|
|
}
|
|
|
|
length = dfw->insns->len;
|
|
for (id = 0; id < length; id++) {
|
|
insn = g_ptr_array_index(dfw->insns, id);
|
|
if (insn->arg1 && insn->arg1->type == REGISTER && (int)insn->arg1->value.numeric < 0 )
|
|
insn->arg1->value.numeric = dfw->first_constant - insn->arg1->value.numeric -1;
|
|
|
|
if (insn->arg2 && insn->arg2->type == REGISTER && (int)insn->arg2->value.numeric < 0 )
|
|
insn->arg2->value.numeric = dfw->first_constant - insn->arg2->value.numeric -1;
|
|
|
|
if (insn->arg3 && insn->arg3->type == REGISTER && (int)insn->arg3->value.numeric < 0 )
|
|
insn->arg3->value.numeric = dfw->first_constant - insn->arg3->value.numeric -1;
|
|
|
|
if (insn->arg4 && insn->arg4->type == REGISTER && (int)insn->arg4->value.numeric < 0 )
|
|
insn->arg4->value.numeric = dfw->first_constant - insn->arg4->value.numeric -1;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
typedef struct {
|
|
int i;
|
|
int *fields;
|
|
} hash_key_iterator;
|
|
|
|
static void
|
|
get_hash_key(gpointer key, gpointer value _U_, gpointer user_data)
|
|
{
|
|
int field_id = GPOINTER_TO_INT(key);
|
|
hash_key_iterator *hki = user_data;
|
|
|
|
hki->fields[hki->i] = field_id;
|
|
hki->i++;
|
|
}
|
|
|
|
int*
|
|
dfw_interesting_fields(dfwork_t *dfw, int *caller_num_fields)
|
|
{
|
|
int num_fields = g_hash_table_size(dfw->interesting_fields);
|
|
|
|
hash_key_iterator hki;
|
|
|
|
if (num_fields == 0) {
|
|
*caller_num_fields = 0;
|
|
return NULL;
|
|
}
|
|
|
|
hki.fields = g_new(int, num_fields);
|
|
hki.i = 0;
|
|
|
|
g_hash_table_foreach(dfw->interesting_fields, get_hash_key, &hki);
|
|
*caller_num_fields = num_fields;
|
|
return hki.fields;
|
|
}
|