|
|
|
/* $IdPath$
|
|
|
|
*
|
|
|
|
* Copyright (C) 2001 Peter Johnson
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND OTHER CONTRIBUTORS ``AS IS''
|
|
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS BE
|
|
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
#include "util.h"
|
|
|
|
|
|
|
|
#include "check.h"
|
|
|
|
|
|
|
|
#include "bitvect.h"
|
|
|
|
|
|
|
|
#include "errwarn.h"
|
|
|
|
|
|
|
|
#include "expr.h"
|
|
|
|
#include "intnum.h"
|
|
|
|
#include "floatnum.h"
|
|
|
|
|
|
|
|
#include "bytecode.h"
|
|
|
|
#include "arch.h"
|
|
|
|
#include "x86arch.h"
|
|
|
|
|
|
|
|
typedef enum {
|
|
|
|
REG_AX = 0,
|
|
|
|
REG_CX = 1,
|
|
|
|
REG_DX = 2,
|
|
|
|
REG_BX = 3,
|
|
|
|
REG_SP = 4,
|
|
|
|
REG_BP = 5,
|
|
|
|
REG_SI = 6,
|
|
|
|
REG_DI = 7
|
|
|
|
} reg16type;
|
|
|
|
|
|
|
|
/* Memory expression building functions.
|
|
|
|
* These try to exactly match how a parser will build up the expr for _in,
|
|
|
|
* and exactly what the output expr should be for _out.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* [5] */
|
|
|
|
static expr *
|
|
|
|
gen_5_in(void)
|
|
|
|
{
|
|
|
|
return expr_new_ident(ExprInt(intnum_new_uint(5)));
|
|
|
|
}
|
|
|
|
#define gen_5_out gen_5_in
|
|
|
|
/* [1.2] */
|
|
|
|
static expr *
|
|
|
|
gen_1pt2_in(void)
|
|
|
|
{
|
|
|
|
return expr_new_ident(ExprFloat(floatnum_new("1.2")));
|
|
|
|
}
|
|
|
|
/* No _out, it's invalid */
|
|
|
|
/* [ecx] */
|
|
|
|
static expr *
|
|
|
|
gen_ecx_in(void)
|
|
|
|
{
|
|
|
|
return expr_new_ident(ExprReg(REG_CX, 32));
|
|
|
|
}
|
|
|
|
#define gen_ecx_out NULL
|
|
|
|
/* [di] */
|
|
|
|
static expr *
|
|
|
|
gen_di_in(void)
|
|
|
|
{
|
|
|
|
return expr_new_ident(ExprReg(REG_DI, 16));
|
|
|
|
}
|
|
|
|
#define gen_di_out NULL
|
|
|
|
/* [di-si+si+126] */
|
|
|
|
static expr *
|
|
|
|
gen_dimsipsip126_in(void)
|
|
|
|
{
|
|
|
|
return expr_new_tree(
|
|
|
|
expr_new_tree(
|
|
|
|
expr_new_tree(
|
|
|
|
expr_new_ident(ExprReg(REG_DI, 16)),
|
|
|
|
EXPR_SUB,
|
|
|
|
expr_new_ident(ExprReg(REG_SI, 16))),
|
|
|
|
EXPR_ADD,
|
|
|
|
expr_new_ident(ExprReg(REG_SI, 16))),
|
|
|
|
EXPR_ADD,
|
|
|
|
expr_new_ident(ExprInt(intnum_new_uint(126))));
|
|
|
|
}
|
|
|
|
#define gen_dimsipsip126_out NULL
|
|
|
|
/* [bx-(bx-di)+bx-2] */
|
|
|
|
static expr *
|
|
|
|
gen_bxmqbxmdiqpbxm2_in(void)
|
|
|
|
{
|
|
|
|
return expr_new_tree(
|
|
|
|
expr_new_tree(
|
|
|
|
expr_new_tree(
|
|
|
|
expr_new_ident(ExprReg(REG_BX, 16)),
|
|
|
|
EXPR_SUB,
|
|
|
|
expr_new_tree(
|
|
|
|
expr_new_ident(ExprReg(REG_BX, 16)),
|
|
|
|
EXPR_SUB,
|
|
|
|
expr_new_ident(ExprReg(REG_DI, 16)))),
|
|
|
|
EXPR_ADD,
|
|
|
|
expr_new_ident(ExprReg(REG_BX, 16))),
|
|
|
|
EXPR_SUB,
|
|
|
|
expr_new_ident(ExprInt(intnum_new_uint(2))));
|
|
|
|
}
|
|
|
|
static expr *
|
|
|
|
gen_bxmqbxmdiqpbxm2_out(void)
|
|
|
|
{
|
|
|
|
return expr_new_ident(ExprInt(intnum_new_int(-2)));
|
|
|
|
}
|
|
|
|
/* [bp] */
|
|
|
|
static expr *
|
|
|
|
gen_bp_in(void)
|
|
|
|
{
|
|
|
|
return expr_new_ident(ExprReg(REG_BP, 16));
|
|
|
|
}
|
|
|
|
#define gen_bp_out NULL
|
|
|
|
/* [bp*1+500] */
|
|
|
|
static expr *
|
|
|
|
gen_bpx1p500_in(void)
|
|
|
|
{
|
|
|
|
return expr_new_tree(
|
|
|
|
expr_new_tree(
|
|
|
|
expr_new_ident(ExprReg(REG_BP, 16)),
|
|
|
|
EXPR_MUL,
|
|
|
|
expr_new_ident(ExprInt(intnum_new_uint(1)))),
|
|
|
|
EXPR_ADD,
|
|
|
|
expr_new_ident(ExprInt(intnum_new_uint(500))));
|
|
|
|
}
|
|
|
|
static expr *
|
|
|
|
gen_bpx1p500_out(void)
|
|
|
|
{
|
|
|
|
return expr_new_ident(ExprInt(intnum_new_uint(500)));
|
|
|
|
}
|
|
|
|
|
|
|
|
typedef struct CheckEA_InOut {
|
|
|
|
/* Function to generate input/output expr. */
|
|
|
|
expr *(*expr_gen)(void);
|
|
|
|
unsigned char addrsize;
|
|
|
|
unsigned char bits;
|
|
|
|
unsigned char nosplit;
|
|
|
|
unsigned char displen;
|
|
|
|
unsigned char modrm;
|
|
|
|
unsigned char v_modrm;
|
|
|
|
unsigned char n_modrm;
|
|
|
|
unsigned char sib;
|
|
|
|
unsigned char v_sib;
|
|
|
|
unsigned char n_sib;
|
|
|
|
} CheckEA_InOut;
|
|
|
|
|
|
|
|
typedef struct CheckEA_Entry {
|
|
|
|
const char *ascii; /* Text description of input */
|
|
|
|
CheckEA_InOut in; /* Input Parameter Values */
|
|
|
|
int retval; /* Return value */
|
|
|
|
CheckEA_InOut out; /* Correct output Parameter Values
|
|
|
|
(N/A if retval=0) */
|
|
|
|
} CheckEA_Entry;
|
|
|
|
|
|
|
|
/* Values used for tests */
|
|
|
|
static CheckEA_Entry bits16_vals[] = {
|
|
|
|
{
|
|
|
|
"[5]",
|
|
|
|
{gen_5_in , 0, 16, 0, 0, 0, 0, 1, 0, 0, 0xff},
|
|
|
|
1,
|
|
|
|
{gen_5_out, 16, 16, 0, 2, 0x06, 1, 1, 0, 0, 0}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"a16 [5]",
|
|
|
|
{gen_5_in , 16, 16, 0, 0, 0, 0, 1, 0, 0, 0xff},
|
|
|
|
1,
|
|
|
|
{gen_5_out, 16, 16, 0, 2, 0x06, 1, 1, 0, 0, 0}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"a32 [5]",
|
|
|
|
{gen_5_in , 32, 16, 0, 0, 0, 0, 1, 0, 0, 0xff},
|
|
|
|
1,
|
|
|
|
{gen_5_out, 32, 16, 0, 4, 0x05, 1, 1, 0x25, 1, 1}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"[word 5]",
|
|
|
|
{gen_5_in , 0, 16, 0, 2, 0, 0, 1, 0, 0, 0xff},
|
|
|
|
1,
|
|
|
|
{gen_5_out, 16, 16, 0, 2, 0x06, 1, 1, 0, 0, 0}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"[dword 5]",
|
|
|
|
{gen_5_in , 0, 16, 0, 4, 0, 0, 1, 0, 0, 0xff},
|
|
|
|
1,
|
|
|
|
{gen_5_out, 32, 16, 0, 4, 0x05, 1, 1, 0x25, 1, 1}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"a16 [dword 5]",
|
|
|
|
{gen_5_in, 16, 16, 0, 4, 0, 0, 1, 0, 0, 0xff},
|
|
|
|
0,
|
|
|
|
{NULL , 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
|
|
|
},
|
|
|
|
/* should error */
|
|
|
|
{
|
|
|
|
"[di+1.2]",
|
|
|
|
{gen_1pt2_in, 0, 16, 0, 0, 0, 0, 1, 0, 0, 0xff},
|
|
|
|
0,
|
|
|
|
{NULL , 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"[ecx]",
|
|
|
|
{gen_ecx_in , 0, 16, 0, 0, 0, 0, 1, 0, 0, 0xff},
|
|
|
|
1,
|
|
|
|
{gen_ecx_out, 32, 16, 0, 0, 0x01, 1, 1, 0, 0, 0}
|
|
|
|
},
|
|
|
|
/* should error */
|
|
|
|
{
|
|
|
|
"a16 [ecx]",
|
|
|
|
{gen_ecx_in, 16, 16, 0, 0, 0, 0, 1, 0, 0, 0xff},
|
|
|
|
0,
|
|
|
|
{NULL , 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"[di]",
|
|
|
|
{gen_di_in , 0, 16, 0, 0, 0, 0, 1, 0, 0, 0xff},
|
|
|
|
1,
|
|
|
|
{gen_di_out, 16, 16, 0, 0, 0x05, 1, 1, 0, 0, 0}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"[di-si+si+126]",
|
|
|
|
{gen_dimsipsip126_in , 0, 16, 0, 0, 0, 0, 1, 0, 0, 0xff},
|
|
|
|
1,
|
|
|
|
{gen_dimsipsip126_out, 16, 16, 0, 1, 0x45, 1, 1, 0, 0, 0}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"[bx-(bx-di)+bx-2]",
|
|
|
|
{gen_bxmqbxmdiqpbxm2_in , 0, 16, 0, 0, 0, 0, 1, 0, 0, 0xff},
|
|
|
|
1,
|
|
|
|
{gen_bxmqbxmdiqpbxm2_out, 16, 16, 0, 1, 0x41, 1, 1, 0, 0, 0}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"[bp]",
|
|
|
|
{gen_bp_in , 0, 16, 0, 0, 0, 0, 1, 0, 0, 0xff},
|
|
|
|
1,
|
|
|
|
{gen_bp_out, 16, 16, 0, 1, 0x46, 1, 1, 0, 0, 0}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"[bp*1+500]",
|
|
|
|
{gen_bpx1p500_in , 0, 16, 0, 0, 0, 0, 1, 0, 0, 0xff},
|
|
|
|
1,
|
|
|
|
{gen_bpx1p500_out, 16, 16, 0, 2, 0x86, 1, 1, 0, 0, 0}
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
/* input expression */
|
|
|
|
expr *expn;
|
|
|
|
|
|
|
|
/* failure messages */
|
|
|
|
static char result_msg[1024];
|
|
|
|
|
|
|
|
int error_triggered;
|
|
|
|
|
|
|
|
/* Replace errwarn functions */
|
|
|
|
void InternalError_(const char *file, unsigned int line, const char *msg)
|
|
|
|
{
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Fatal(fatal_num num)
|
|
|
|
{
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Error(const char *msg, ...)
|
|
|
|
{
|
|
|
|
error_triggered = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Warning(const char *msg, ...)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
ErrorAt(const char *filename, unsigned long line, const char *fmt, ...)
|
|
|
|
{
|
|
|
|
error_triggered = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
WarningAt(const char *filename, unsigned long line, const char *fmt, ...)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
x86_checkea_check(CheckEA_Entry *val)
|
|
|
|
{
|
|
|
|
CheckEA_InOut chk = val->in; /* local structure copy of inputs */
|
|
|
|
int retval;
|
|
|
|
|
|
|
|
error_triggered = 0;
|
|
|
|
|
|
|
|
/* execute function and check return value */
|
|
|
|
retval = x86_expr_checkea(&expn, &chk.addrsize, chk.bits, chk.nosplit,
|
|
|
|
&chk.displen, &chk.modrm, &chk.v_modrm,
|
|
|
|
&chk.n_modrm, &chk.sib, &chk.v_sib, &chk.n_sib);
|
|
|
|
if (retval != val->retval) {
|
|
|
|
sprintf(result_msg, "%s: incorrect %s (expected %d, got %d)",
|
|
|
|
val->ascii, "return value", val->retval, retval);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If returned 0 (failure), check to see if ErrorAt() was called */
|
|
|
|
if (retval == 0) {
|
|
|
|
if (error_triggered == 0) {
|
|
|
|
sprintf(result_msg, "%s: didn't call ErrorAt() and returned 0",
|
|
|
|
val->ascii);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0; /* don't check other return values */
|
|
|
|
}
|
|
|
|
|
|
|
|
/* check expr result */
|
|
|
|
/* TODO */
|
|
|
|
|
|
|
|
/* Check other outputs */
|
|
|
|
if (chk.addrsize != val->out.addrsize) {
|
|
|
|
sprintf(result_msg, "%s: incorrect %s (expected %d, got %d)",
|
|
|
|
val->ascii, "addrsize", (int)val->out.addrsize,
|
|
|
|
(int)chk.addrsize);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
if (chk.displen != val->out.displen) {
|
|
|
|
sprintf(result_msg, "%s: incorrect %s (expected %d, got %d)",
|
|
|
|
val->ascii, "displen", (int)val->out.displen,
|
|
|
|
(int)chk.displen);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
if (chk.modrm != val->out.modrm) {
|
|
|
|
sprintf(result_msg, "%s: incorrect %s (expected %03o, got %03o)",
|
|
|
|
val->ascii, "modrm", (int)val->out.modrm, (int)chk.modrm);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
if (chk.v_modrm != val->out.v_modrm) {
|
|
|
|
sprintf(result_msg, "%s: incorrect %s (expected %d, got %d)",
|
|
|
|
val->ascii, "v_modrm", (int)val->out.v_modrm,
|
|
|
|
(int)chk.v_modrm);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
if (chk.n_modrm != val->out.n_modrm) {
|
|
|
|
sprintf(result_msg, "%s: incorrect %s (expected %d, got %d)",
|
|
|
|
val->ascii, "n_modrm", (int)val->out.n_modrm,
|
|
|
|
(int)chk.n_modrm);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
if (chk.sib != val->out.sib) {
|
|
|
|
sprintf(result_msg, "%s: incorrect %s (expected %03o, got %03o)",
|
|
|
|
val->ascii, "sib", (int)val->out.sib, (int)chk.sib);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
if (chk.v_sib != val->out.v_sib) {
|
|
|
|
sprintf(result_msg, "%s: incorrect %s (expected %d, got %d)",
|
|
|
|
val->ascii, "v_sib", (int)val->out.v_sib, (int)chk.v_sib);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
if (chk.n_sib != val->out.n_sib) {
|
|
|
|
sprintf(result_msg, "%s: incorrect %s (expected %x, got %x)",
|
|
|
|
val->ascii, "n_sib", (int)val->out.n_sib, (int)chk.n_sib);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
START_TEST(test_x86_checkea_bits16)
|
|
|
|
{
|
|
|
|
CheckEA_Entry *vals = bits16_vals;
|
|
|
|
int i, num = sizeof(bits16_vals)/sizeof(CheckEA_Entry);
|
|
|
|
|
|
|
|
for (i=0; i<num; i++) {
|
|
|
|
expn = vals[i].in.expr_gen();
|
|
|
|
fail_unless(x86_checkea_check(&vals[i]) == 0, result_msg);
|
|
|
|
expr_delete(expn);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
END_TEST
|
|
|
|
|
|
|
|
static Suite *
|
|
|
|
memexpr_suite(void)
|
|
|
|
{
|
|
|
|
Suite *s = suite_create("memexpr");
|
|
|
|
TCase *tc_x86_checkea = tcase_create("x86_checkea");
|
|
|
|
|
|
|
|
suite_add_tcase(s, tc_x86_checkea);
|
|
|
|
tcase_add_test(tc_x86_checkea, test_x86_checkea_bits16);
|
|
|
|
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
main(void)
|
|
|
|
{
|
|
|
|
int nf;
|
|
|
|
Suite *s = memexpr_suite();
|
|
|
|
SRunner *sr = srunner_create(s);
|
|
|
|
BitVector_Boot();
|
|
|
|
srunner_run_all(sr, CRNORMAL);
|
|
|
|
nf = srunner_ntests_failed(sr);
|
|
|
|
srunner_free(sr);
|
|
|
|
suite_free(s);
|
|
|
|
return (nf == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
|
|
|
|
}
|