Add IL lifting testing to rz-test
This commit is contained in:
parent
cd938d2083
commit
7e36f4bd38
8 changed files with 115 additions and 8 deletions
|
|
@ -266,6 +266,7 @@ RZ_API void rz_test_asm_test_free(RzAsmTest *test) {
|
||||||
}
|
}
|
||||||
free(test->disasm);
|
free(test->disasm);
|
||||||
free(test->bytes);
|
free(test->bytes);
|
||||||
|
free(test->il);
|
||||||
free(test);
|
free(test);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -343,6 +344,7 @@ RZ_API RzPVector *rz_test_load_asm_test_file(RzStrConstPool *strpool, const char
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// mode flags
|
||||||
int mode = 0;
|
int mode = 0;
|
||||||
while (*line && *line != ' ') {
|
while (*line && *line != ' ') {
|
||||||
switch (*line) {
|
switch (*line) {
|
||||||
|
|
@ -369,6 +371,7 @@ RZ_API RzPVector *rz_test_load_asm_test_file(RzStrConstPool *strpool, const char
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// disasm
|
||||||
char *disasm = strchr(line, '"');
|
char *disasm = strchr(line, '"');
|
||||||
if (!disasm) {
|
if (!disasm) {
|
||||||
eprintf(LINEFMT "Error: Expected \" to begin disassembly.\n", file, linenum);
|
eprintf(LINEFMT "Error: Expected \" to begin disassembly.\n", file, linenum);
|
||||||
|
|
@ -384,14 +387,30 @@ RZ_API RzPVector *rz_test_load_asm_test_file(RzStrConstPool *strpool, const char
|
||||||
hex++;
|
hex++;
|
||||||
rz_str_trim(disasm);
|
rz_str_trim(disasm);
|
||||||
|
|
||||||
|
// hex
|
||||||
while (*hex && *hex == ' ') {
|
while (*hex && *hex == ' ') {
|
||||||
hex++;
|
hex++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// remove comment at the end of the line
|
||||||
|
char *latecmt = strchr(hex, '#');
|
||||||
|
if (latecmt) {
|
||||||
|
*latecmt = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
// offset (optional)
|
||||||
char *offset = strchr(hex, ' ');
|
char *offset = strchr(hex, ' ');
|
||||||
|
char *il = NULL;
|
||||||
if (offset) {
|
if (offset) {
|
||||||
*offset = '\0';
|
*offset = '\0';
|
||||||
offset++;
|
offset++;
|
||||||
|
|
||||||
|
// IL (optional, the entire rest)
|
||||||
|
il = strchr(offset, ' ');
|
||||||
|
if (il) {
|
||||||
|
*il = '\0';
|
||||||
|
il++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t hexlen = strlen(hex);
|
size_t hexlen = strlen(hex);
|
||||||
|
|
@ -425,10 +444,17 @@ RZ_API RzPVector *rz_test_load_asm_test_file(RzStrConstPool *strpool, const char
|
||||||
test->arch = arch;
|
test->arch = arch;
|
||||||
test->cpu = cpu;
|
test->cpu = cpu;
|
||||||
test->mode = mode;
|
test->mode = mode;
|
||||||
test->offset = offset ? (ut64)strtoull(offset, NULL, 0) : 0;
|
char *endptr = NULL;
|
||||||
|
test->offset = offset ? (ut64)strtoull(offset, &endptr, 0) : 0;
|
||||||
|
if (endptr && *endptr) {
|
||||||
|
eprintf(LINEFMT "Error: Invalid offset string: \"%s\"\n", file, linenum, offset);
|
||||||
|
free(bytes);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
test->disasm = strdup(disasm);
|
test->disasm = strdup(disasm);
|
||||||
test->bytes = bytes;
|
test->bytes = bytes;
|
||||||
test->bytes_size = (size_t)bytesz;
|
test->bytes_size = (size_t)bytesz;
|
||||||
|
test->il = il ? strdup(il) : NULL;
|
||||||
rz_pvector_push(ret, test);
|
rz_pvector_push(ret, test);
|
||||||
} while ((line = nextline));
|
} while ((line = nextline));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -353,6 +353,31 @@ RZ_API RzAsmTestOutput *rz_test_run_asm_test(RzTestRunConfig *config, RzAsmTest
|
||||||
rz_pvector_pop(&args);
|
rz_pvector_pop(&args);
|
||||||
rz_subprocess_free(proc);
|
rz_subprocess_free(proc);
|
||||||
}
|
}
|
||||||
|
if (test->il) {
|
||||||
|
char *hex = rz_hex_bin2strdup(test->bytes, test->bytes_size);
|
||||||
|
if (!hex) {
|
||||||
|
goto beach;
|
||||||
|
}
|
||||||
|
rz_pvector_push(&args, "-I");
|
||||||
|
rz_pvector_push(&args, hex);
|
||||||
|
RzSubprocess *proc = rz_subprocess_start(config->rz_asm_cmd, args.v.a, rz_pvector_len(&args), NULL, NULL, 0);
|
||||||
|
if (rz_subprocess_wait(proc, config->timeout_ms) == RZ_SUBPROCESS_TIMEDOUT) {
|
||||||
|
rz_subprocess_kill(proc);
|
||||||
|
out->il_timeout = true;
|
||||||
|
} else {
|
||||||
|
char *il = (char *)remove_cr(rz_subprocess_out(proc, NULL));
|
||||||
|
rz_str_trim(il);
|
||||||
|
char *il_err = (char *)remove_cr(rz_subprocess_err(proc, NULL));
|
||||||
|
rz_str_trim(il_err);
|
||||||
|
out->il = il;
|
||||||
|
out->il_report = il_err;
|
||||||
|
out->il_failed = rz_subprocess_ret(proc) != 0;
|
||||||
|
}
|
||||||
|
free(hex);
|
||||||
|
rz_pvector_pop(&args);
|
||||||
|
rz_pvector_pop(&args);
|
||||||
|
rz_subprocess_free(proc);
|
||||||
|
}
|
||||||
|
|
||||||
beach:
|
beach:
|
||||||
rz_pvector_clear(&args);
|
rz_pvector_clear(&args);
|
||||||
|
|
@ -379,6 +404,16 @@ RZ_API bool rz_test_check_asm_test(RzAsmTestOutput *out, RzAsmTest *test) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (test->il) {
|
||||||
|
// expect some IL, no failure, no report and no timeout
|
||||||
|
if (!out->il || out->il_failed || (out->il_report && *out->il_report) || out->il_timeout) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// IL must also be correct
|
||||||
|
if (strcmp(out->il, test->il) != 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -388,6 +423,8 @@ RZ_API void rz_test_asm_test_output_free(RzAsmTestOutput *out) {
|
||||||
}
|
}
|
||||||
free(out->disasm);
|
free(out->disasm);
|
||||||
free(out->bytes);
|
free(out->bytes);
|
||||||
|
free(out->il);
|
||||||
|
free(out->il_report);
|
||||||
free(out);
|
free(out);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -464,7 +501,7 @@ RZ_API RzTestResultInfo *rz_test_run_test(RzTestRunConfig *config, RzTest *test)
|
||||||
success = rz_test_check_asm_test(out, asm_test);
|
success = rz_test_check_asm_test(out, asm_test);
|
||||||
ret->asm_out = out;
|
ret->asm_out = out;
|
||||||
if (out) {
|
if (out) {
|
||||||
ret->timeout = out->as_timeout || out->disas_timeout;
|
ret->timeout = out->as_timeout || out->disas_timeout || out->il_timeout;
|
||||||
}
|
}
|
||||||
ret->run_failed = !out;
|
ret->run_failed = !out;
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -735,6 +735,27 @@ static void print_result_diff(RzTestRunConfig *config, RzTestResultInfo *result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO: assembly
|
// TODO: assembly
|
||||||
|
if (result->test->asm_test->il) {
|
||||||
|
const char *expect = result->test->asm_test->il;
|
||||||
|
const char *actual = result->asm_out->il;
|
||||||
|
const char *report = result->asm_out->il_report;
|
||||||
|
bool il_printed = false;
|
||||||
|
const char *hdr = "-- IL\n";
|
||||||
|
if (expect && actual && strcmp(actual, expect)) {
|
||||||
|
printf("%s", hdr);
|
||||||
|
il_printed = true;
|
||||||
|
print_diff(actual, expect, NULL);
|
||||||
|
}
|
||||||
|
if (report) {
|
||||||
|
if (!il_printed) {
|
||||||
|
printf("%s", hdr);
|
||||||
|
if (actual) {
|
||||||
|
printf("%s\n", actual);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf(Color_RED "%s" Color_RESET "\n", report);
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case RZ_TEST_TYPE_JSON:
|
case RZ_TEST_TYPE_JSON:
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -99,9 +99,10 @@ typedef struct rz_test_asm_test_t {
|
||||||
int bits;
|
int bits;
|
||||||
int mode;
|
int mode;
|
||||||
ut64 offset;
|
ut64 offset;
|
||||||
char *disasm;
|
RZ_NONNULL char *disasm;
|
||||||
ut8 *bytes;
|
RZ_NONNULL ut8 *bytes;
|
||||||
size_t bytes_size;
|
size_t bytes_size;
|
||||||
|
RZ_NULLABLE char *il;
|
||||||
} RzAsmTest;
|
} RzAsmTest;
|
||||||
|
|
||||||
typedef struct rz_test_json_test_t {
|
typedef struct rz_test_json_test_t {
|
||||||
|
|
@ -149,8 +150,12 @@ typedef struct rz_test_asm_test_output_t {
|
||||||
char *disasm;
|
char *disasm;
|
||||||
ut8 *bytes;
|
ut8 *bytes;
|
||||||
size_t bytes_size;
|
size_t bytes_size;
|
||||||
|
char *il;
|
||||||
|
char *il_report;
|
||||||
|
bool il_failed;
|
||||||
bool as_timeout;
|
bool as_timeout;
|
||||||
bool disas_timeout;
|
bool disas_timeout;
|
||||||
|
bool il_timeout;
|
||||||
} RzAsmTestOutput;
|
} RzAsmTestOutput;
|
||||||
|
|
||||||
typedef enum rz_test_test_result_t {
|
typedef enum rz_test_test_result_t {
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,7 @@ A test can have one of the following results:
|
||||||
Tests for the assembly and disassembly (in `db/asm/*`) have a different format:
|
Tests for the assembly and disassembly (in `db/asm/*`) have a different format:
|
||||||
General format:
|
General format:
|
||||||
```
|
```
|
||||||
type "assembly" opcode [offset]
|
type "assembly" opcode [offset] [IL]
|
||||||
```
|
```
|
||||||
where type can be any of:
|
where type can be any of:
|
||||||
* **a** meaning "assemble"
|
* **a** meaning "assemble"
|
||||||
|
|
@ -79,6 +79,21 @@ a "nop" 90 # Assembly is correct
|
||||||
dB "nopppp" 90 # Disassembly test is broken
|
dB "nopppp" 90 # Disassembly test is broken
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### IL
|
||||||
|
|
||||||
|
To also test lifting an instruction to RzIL, you can append the readable IL
|
||||||
|
representation like so:
|
||||||
|
```
|
||||||
|
d "inc ptr" 3e 0 set(v:ptr, x:add(x:var(v:ptr), y:bitv(bits:0x0000000000000001, len:64)))
|
||||||
|
```
|
||||||
|
|
||||||
|
This means that rz-test will also perform the lifting from bytes to RzIL,
|
||||||
|
run the validation pass on the result and compare it against the given string.
|
||||||
|
|
||||||
|
In this case, passing an offset is mandatory, otherwise the argument would be ambiguous.
|
||||||
|
|
||||||
|
#### General hints
|
||||||
|
|
||||||
You can merge lines:
|
You can merge lines:
|
||||||
```
|
```
|
||||||
adB "nop" 90
|
adB "nop" 90
|
||||||
|
|
|
||||||
3
test/db/asm/bf
Normal file
3
test/db/asm/bf
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
d "inc ptr" 3e 0 set(v:ptr, x:add(x:var(v:ptr), y:bitv(bits:0x0000000000000001, len:64)))
|
||||||
|
d "dec ptr" 3c 0 set(v:ptr, x:sub(x:var(v:ptr), y:bitv(bits:0x0000000000000001, len:64)))
|
||||||
|
d "out [ptr]" 2e 0 goto(lbl:write)
|
||||||
|
|
@ -6,7 +6,7 @@ d "? R0 = ##0x101" 20e00078 0x0
|
||||||
d "? R5 = add(clb(R31),#0xffffffff)" 05ff3f8c 0x0
|
d "? R5 = add(clb(R31),#0xffffffff)" 05ff3f8c 0x0
|
||||||
d "? R4 = add(R19,##0x33)" 64c613b0 0x0
|
d "? R4 = add(R19,##0x33)" 64c613b0 0x0
|
||||||
|
|
||||||
d "? nop" 00c0007f 0x10000000 0x0
|
d "? nop" 00c0007f 0x10000000
|
||||||
d "? R31 = add(R0,##0x2)" 5f4000b0 0x0
|
d "? R31 = add(R0,##0x2)" 5f4000b0 0x0
|
||||||
|
|
||||||
d "? P0 = tstbit(R6,#0); if (!P0.new) jump:t 0x14" 04e3c611 0xc
|
d "? P0 = tstbit(R6,#0); if (!P0.new) jump:t 0x14" 04e3c611 0xc
|
||||||
|
|
@ -52,4 +52,4 @@ d "? R1:0 = S79:78" 00404e6f 0x0
|
||||||
|
|
||||||
d "? R1:0 = combine(##0x1b,#0x3)" 6063017c 0x0
|
d "? R1:0 = combine(##0x1b,#0x3)" 6063017c 0x0
|
||||||
d "? if (!P0) R5:4 = memd(R0+R2<<#0x0)" 04c2c031 0x0
|
d "? if (!P0) R5:4 = memd(R0+R2<<#0x0)" 04c2c031 0x0
|
||||||
d "? if (!P0.new) R2 = add(R2,##0x8)" 02618274 0x0
|
d "? if (!P0.new) R2 = add(R2,##0x8)" 02618274 0x0
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ a "jne 0x14" 7512
|
||||||
a "jnz 0x94" 660f858d000000
|
a "jnz 0x94" 660f858d000000
|
||||||
a "jnz -0x94" 660f8565ffffff
|
a "jnz -0x94" 660f8565ffffff
|
||||||
a "jno -0x34" 71ca
|
a "jno -0x34" 71ca
|
||||||
dB "jmp 0xfec50" e95bec f000:fff2
|
dB "jmp 0xfec50" e95bec
|
||||||
d "jmp 0x1fec50" e95bec 0x001ffff2
|
d "jmp 0x1fec50" e95bec 0x001ffff2
|
||||||
a "mov al, [0xbeef]" a0efbe
|
a "mov al, [0xbeef]" a0efbe
|
||||||
a "mov ax, [0xbeef]" a1efbe
|
a "mov ax, [0xbeef]" a1efbe
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue