mirror of
https://github.com/yuzu-emu/unicorn
synced 2024-11-24 12:58:09 +00:00
In response to issue #364, a unit test case has been created
for exercising proper flushing of the instruction translation cache.
This commit is contained in:
parent
f5fa84d985
commit
f0dac63b69
2 changed files with 305 additions and 1 deletions
|
@ -4,7 +4,8 @@ CFLAGS += -L ../../
|
||||||
CFLAGS += -lcmocka -lunicorn
|
CFLAGS += -lcmocka -lunicorn
|
||||||
CFLAGS += -I ../../include
|
CFLAGS += -I ../../include
|
||||||
|
|
||||||
ALL_TESTS = test_sanity test_x86 test_mem_map test_mem_high test_mem_map_ptr
|
ALL_TESTS = test_sanity test_x86 test_mem_map test_mem_high test_mem_map_ptr \
|
||||||
|
test_tb_x86
|
||||||
|
|
||||||
.PHONY: all
|
.PHONY: all
|
||||||
all: ${ALL_TESTS}
|
all: ${ALL_TESTS}
|
||||||
|
@ -21,12 +22,14 @@ test: ${ALL_TESTS}
|
||||||
./test_mem_map
|
./test_mem_map
|
||||||
./test_mem_map_ptr
|
./test_mem_map_ptr
|
||||||
./test_mem_high
|
./test_mem_high
|
||||||
|
./test_tb_x86
|
||||||
|
|
||||||
test_sanity: test_sanity.c
|
test_sanity: test_sanity.c
|
||||||
test_x86: test_x86.c
|
test_x86: test_x86.c
|
||||||
test_mem_map: test_mem_map.c
|
test_mem_map: test_mem_map.c
|
||||||
test_mem_map_ptr: test_mem_map_ptr.c
|
test_mem_map_ptr: test_mem_map_ptr.c
|
||||||
test_mem_high: test_mem_high.c
|
test_mem_high: test_mem_high.c
|
||||||
|
test_tb_x86: test_tb_x86.c
|
||||||
|
|
||||||
${ALL_TESTS}:
|
${ALL_TESTS}:
|
||||||
${CC} ${CFLAGS} -o $@ $^
|
${CC} ${CFLAGS} -o $@ $^
|
||||||
|
|
301
tests/unit/test_tb_x86.c
Normal file
301
tests/unit/test_tb_x86.c
Normal file
|
@ -0,0 +1,301 @@
|
||||||
|
/**
|
||||||
|
* Unicorn x86_32 self-modifying unit test
|
||||||
|
*
|
||||||
|
* This test demonstrates the flushing of instruction translation cache
|
||||||
|
* after a self-modification of Intel's x8's "IMUL Gv,Ev,Ib" instruction.
|
||||||
|
*/
|
||||||
|
#include "unicorn_test.h"
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
|
||||||
|
// Demostration of a self-modifying "IMUL eax,mem,Ib" opcode
|
||||||
|
// And the QEMU's ability to flush the translation buffer properly
|
||||||
|
|
||||||
|
#define MIN(a, b) (a < b? a: b)
|
||||||
|
|
||||||
|
#define CODE_SPACE (2 * 1024 * 1024)
|
||||||
|
#define PHY_STACK_REGION (0x60000000)
|
||||||
|
|
||||||
|
#define X86_CODE32_ALPHA_MIXED \
|
||||||
|
"\x89\xe1\xd9\xcd\xd9\x71\xf4\x5d\x55\x59\x49\x49\x49\x49\x49\x49" \
|
||||||
|
"\x49\x49\x49\x49\x43\x43\x43\x43\x43\x43\x37\x51\x5a\x6a\x41\x58" \
|
||||||
|
"\x50\x30\x41\x30\x41\x6b\x41\x41\x51\x32\x41\x42\x32\x42\x42\x30" \
|
||||||
|
"\x42\x42\x41\x42\x58\x50\x38\x41\x42\x75\x4a\x49\x51\x51\x51\x52" \
|
||||||
|
"\x47\x33\x47\x34\x51\x55\x51\x56\x50\x47\x47\x38\x47\x39\x50\x4a" \
|
||||||
|
"\x50\x4b\x50\x4c\x50\x4d\x50\x4e\x50\x4f\x50\x50\x50\x31\x47\x42" \
|
||||||
|
"\x47\x42\x50\x34\x50\x5a\x50\x45\x51\x52\x46\x32\x47\x31\x50\x4d" \
|
||||||
|
"\x51\x51\x50\x4e\x41\x41"
|
||||||
|
|
||||||
|
|
||||||
|
/* Called before every test to set up a new instance */
|
||||||
|
static int setup(void **state)
|
||||||
|
{
|
||||||
|
uc_engine *uc;
|
||||||
|
|
||||||
|
uc_assert_success(uc_open(UC_ARCH_X86, UC_MODE_64, &uc));
|
||||||
|
|
||||||
|
*state = uc;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Called after every test to clean up */
|
||||||
|
static int teardown(void **state)
|
||||||
|
{
|
||||||
|
uc_engine *uc = *state;
|
||||||
|
|
||||||
|
uc_assert_success(uc_close(uc));
|
||||||
|
|
||||||
|
*state = NULL;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static void dump_stack_mem(uc_engine *uc)
|
||||||
|
{
|
||||||
|
uint8_t tmp[256];
|
||||||
|
uint32_t size;
|
||||||
|
|
||||||
|
size = sizeof(X86_CODE32_ALPHA_MIXED);
|
||||||
|
if (size > 255) size = 255;
|
||||||
|
if (!uc_mem_read(uc, PHY_STACK_REGION, tmp, size))
|
||||||
|
{
|
||||||
|
uint32_t i;
|
||||||
|
|
||||||
|
printf("Stack region dump");
|
||||||
|
for (i=0; i<size; i++) {
|
||||||
|
if ((i % 16) == 0) printf("\n%x: ", PHY_STACK_REGION+i);
|
||||||
|
printf("%x ", tmp[i]);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_registers(uc_engine *uc)
|
||||||
|
{
|
||||||
|
int32_t eax, ecx, edx, ebx;
|
||||||
|
int32_t esp, ebp, esi, edi;
|
||||||
|
uc_reg_read(uc, UC_X86_REG_EAX, &eax);
|
||||||
|
uc_reg_read(uc, UC_X86_REG_ECX, &ecx);
|
||||||
|
uc_reg_read(uc, UC_X86_REG_EDX, &edx);
|
||||||
|
uc_reg_read(uc, UC_X86_REG_EBX, &ebx);
|
||||||
|
uc_reg_read(uc, UC_X86_REG_ESP, &esp);
|
||||||
|
uc_reg_read(uc, UC_X86_REG_EBP, &ebp);
|
||||||
|
uc_reg_read(uc, UC_X86_REG_ESI, &esi);
|
||||||
|
uc_reg_read(uc, UC_X86_REG_EDI, &edi);
|
||||||
|
|
||||||
|
printf("Register dump:\n");
|
||||||
|
printf("eax %8.8x ", eax);
|
||||||
|
printf("ecx %8.8x ", ecx);
|
||||||
|
printf("edx %8.8x ", edx);
|
||||||
|
printf("ebx %8.8x\n", ebx);
|
||||||
|
printf("esp %8.8x ", esp);
|
||||||
|
printf("ebp %8.8x ", ebp);
|
||||||
|
printf("esi %8.8x ", esi);
|
||||||
|
printf("edi %8.8x ", edi);
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void hook_code32(uc_engine *uc,
|
||||||
|
uint64_t address,
|
||||||
|
uint32_t size,
|
||||||
|
void *user_data)
|
||||||
|
{
|
||||||
|
//uint8_t opcode[256];
|
||||||
|
uint8_t tmp[16];
|
||||||
|
uint32_t tmp4[1];
|
||||||
|
uint32_t ecx;
|
||||||
|
|
||||||
|
printf("\nhook_code32: Address: %lx, Opcode Size: %d\n", address, size);
|
||||||
|
size = MIN(sizeof(tmp), size);
|
||||||
|
if (!uc_mem_read(uc, address, tmp, size))
|
||||||
|
{
|
||||||
|
uint32_t i;
|
||||||
|
|
||||||
|
printf("Opcode: ");
|
||||||
|
for (i=0; i<size; i++) {
|
||||||
|
printf("%x ", tmp[i]);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (address == 0x60000025)
|
||||||
|
{
|
||||||
|
// double-check that opcode is
|
||||||
|
// IMUL aex,[eax+0x41],0x10
|
||||||
|
if ((tmp[0] != 0x6b) ||
|
||||||
|
(tmp[1] != 0x41) ||
|
||||||
|
(tmp[2] != 0x41) ||
|
||||||
|
(tmp[3] != 0x10))
|
||||||
|
{
|
||||||
|
printf("FAILED set-up of opcode\n");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
printf("IMUL eax,[ecx+0x41],0x10\n");
|
||||||
|
|
||||||
|
// double-check that memory operand points to 0x6000003a
|
||||||
|
uc_reg_read(uc, UC_X86_REG_ECX, &ecx);
|
||||||
|
if (ecx != 0x5ffffff9)
|
||||||
|
{
|
||||||
|
printf("FAILED EAX register not having 0x5ffffff9\n");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
printf("EAX = %8.8x\n", ecx);
|
||||||
|
|
||||||
|
printf("%8.8x + 0x41 = %8.8x\n", 0x5ffffff9, 0x5ffffff9 + 0x41);
|
||||||
|
|
||||||
|
// double-check that memory location 0x60000039
|
||||||
|
// contains 0x5151494a
|
||||||
|
if (!uc_mem_read(uc, 0x6000003a, tmp4, 4))
|
||||||
|
{
|
||||||
|
if (tmp4[0] != 0x5151494a)
|
||||||
|
{
|
||||||
|
printf("FAILED set-up\n");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
printf("Proved that 0x6000003a contains the proper 0x5151494a\n");
|
||||||
|
}
|
||||||
|
dump_stack_mem(uc);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop after 'imul eax,[ecx+0x41],0x10
|
||||||
|
if (address == 0x60000029)
|
||||||
|
{
|
||||||
|
uint32_t eax;
|
||||||
|
// IMUL eax,mem,Ib
|
||||||
|
// mem = [ecx+0x41]
|
||||||
|
// ecx = 0x5ffffff9
|
||||||
|
// [6000003A] = 0x5151494a
|
||||||
|
// Stop after 'imul eax,[ecx+0x41],0x10
|
||||||
|
// This step basically shifts left 8-bit...elaborately.
|
||||||
|
// multiplying 0x5151494a x 0x10 = 0x151494a0
|
||||||
|
uc_reg_read(uc, UC_X86_REG_EAX, &eax);
|
||||||
|
if (eax != 0x151494a0)
|
||||||
|
{
|
||||||
|
fail_msg("FAIL: TB did not flush; eax is not the expected 0x151494a0\n");
|
||||||
|
print_registers(uc);
|
||||||
|
//dump_stack_mem(uc);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
printf("PASS\n");
|
||||||
|
}
|
||||||
|
print_registers(uc);
|
||||||
|
// dump_stack_mem(uc);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void hook_mem32(uc_engine *uc,
|
||||||
|
uc_mem_type type,
|
||||||
|
uint64_t address,
|
||||||
|
int size,
|
||||||
|
uint64_t value,
|
||||||
|
void *user_data)
|
||||||
|
{
|
||||||
|
char ctype;
|
||||||
|
//uint32_t tmp[1];
|
||||||
|
|
||||||
|
ctype = '?';
|
||||||
|
if (type == 16) ctype = 'R';
|
||||||
|
if (type == 17) ctype = 'W';
|
||||||
|
printf("hook_mem32(%c): Address: 0x%lx, Size: %d, Value:0x%lx\n", ctype, address, size, value);
|
||||||
|
|
||||||
|
// if (!uc_mem_read(uc, 0x6000003a, tmp, 4))
|
||||||
|
// {
|
||||||
|
// printf(" hook_mem32 0x6000003a: %8.8x\n", tmp[0]);
|
||||||
|
// }
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void test_tb_x86_64_32_imul_Gv_Ev_Ib(void **state)
|
||||||
|
{
|
||||||
|
uc_engine *uc = *state;
|
||||||
|
uc_hook trace1, trace2, trace3, trace4;
|
||||||
|
void *mem;
|
||||||
|
int64_t eax = 0x73952c43;
|
||||||
|
int64_t ecx = 0x6010229a;
|
||||||
|
int64_t edx = 0x2a500e50;
|
||||||
|
int64_t ebx = 0x034a1295;
|
||||||
|
int64_t esp = 0x6010229a;
|
||||||
|
int64_t ebp = 0x60000000;
|
||||||
|
int64_t esi = 0x1f350211;
|
||||||
|
int64_t edi = 0x488ac239;
|
||||||
|
|
||||||
|
mem = calloc(1, CODE_SPACE);
|
||||||
|
assert_int_not_equal(0, mem);
|
||||||
|
|
||||||
|
uc_assert_success(uc_open(UC_ARCH_X86,
|
||||||
|
UC_MODE_32,
|
||||||
|
&uc));
|
||||||
|
uc_assert_success(uc_mem_map(uc,
|
||||||
|
PHY_STACK_REGION,
|
||||||
|
CODE_SPACE,
|
||||||
|
UC_PROT_ALL));
|
||||||
|
uc_assert_success(uc_mem_write(uc,
|
||||||
|
PHY_STACK_REGION,
|
||||||
|
X86_CODE32_ALPHA_MIXED,
|
||||||
|
sizeof(X86_CODE32_ALPHA_MIXED) - 1));
|
||||||
|
uc_assert_success(uc_reg_write(uc, UC_X86_REG_EAX, &eax));
|
||||||
|
uc_assert_success(uc_reg_write(uc, UC_X86_REG_ECX, &ecx));
|
||||||
|
uc_assert_success(uc_reg_write(uc, UC_X86_REG_EDX, &edx));
|
||||||
|
uc_assert_success(uc_reg_write(uc, UC_X86_REG_EBX, &ebx));
|
||||||
|
uc_assert_success(uc_reg_write(uc, UC_X86_REG_ESP, &esp));
|
||||||
|
uc_assert_success(uc_reg_write(uc, UC_X86_REG_EBP, &ebp));
|
||||||
|
uc_assert_success(uc_reg_write(uc, UC_X86_REG_ESI, &esi));
|
||||||
|
uc_assert_success(uc_reg_write(uc, UC_X86_REG_EDI, &edi));
|
||||||
|
|
||||||
|
uc_assert_success(uc_hook_add(uc,
|
||||||
|
&trace1,
|
||||||
|
UC_HOOK_CODE,
|
||||||
|
hook_code32,
|
||||||
|
NULL,
|
||||||
|
(uint64_t)1,
|
||||||
|
(uint64_t)0));
|
||||||
|
|
||||||
|
uc_assert_success(uc_hook_add(uc,
|
||||||
|
&trace2,
|
||||||
|
UC_HOOK_MEM_READ,
|
||||||
|
hook_mem32,
|
||||||
|
NULL,
|
||||||
|
(uint64_t)1,
|
||||||
|
(uint64_t)0));
|
||||||
|
|
||||||
|
uc_assert_success(uc_hook_add(uc,
|
||||||
|
&trace3,
|
||||||
|
UC_HOOK_MEM_WRITE,
|
||||||
|
hook_mem32,
|
||||||
|
NULL,
|
||||||
|
(uint64_t)1,
|
||||||
|
(uint64_t)0));
|
||||||
|
|
||||||
|
uc_assert_success(uc_hook_add(uc,
|
||||||
|
&trace4,
|
||||||
|
UC_HOOK_MEM_FETCH,
|
||||||
|
hook_mem32,
|
||||||
|
NULL,
|
||||||
|
(uint64_t)1,
|
||||||
|
(uint64_t)0));
|
||||||
|
|
||||||
|
uc_assert_success(uc_emu_start(uc,
|
||||||
|
PHY_STACK_REGION,
|
||||||
|
PHY_STACK_REGION+sizeof(X86_CODE32_ALPHA_MIXED) - 1,
|
||||||
|
0, 0));
|
||||||
|
|
||||||
|
uc_assert_success(uc_close(uc));
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main(void)
|
||||||
|
{
|
||||||
|
#define test(x) cmocka_unit_test_setup_teardown(x, setup, teardown)
|
||||||
|
const struct CMUnitTest tests[] = {
|
||||||
|
test(test_tb_x86_64_32_imul_Gv_Ev_Ib)
|
||||||
|
};
|
||||||
|
#undef test
|
||||||
|
return cmocka_run_group_tests(tests, NULL, NULL);
|
||||||
|
}
|
Loading…
Reference in a new issue