diff --git a/.gitignore b/.gitignore index e0d13af9..d3ddf440 100644 --- a/.gitignore +++ b/.gitignore @@ -135,6 +135,8 @@ rw_hookstack hook_extrainvoke sysenter_hook_x86 test_tb_x86 +test_multihook +test_pc_change ################# diff --git a/qemu/cpu-exec.c b/qemu/cpu-exec.c index 33246009..6fd497bf 100644 --- a/qemu/cpu-exec.c +++ b/qemu/cpu-exec.c @@ -66,6 +66,7 @@ int cpu_exec(struct uc_struct *uc, CPUArchState *env) // qq uintptr_t next_tb; struct hook *hook; + /* This must be volatile so it is not trashed by longjmp() */ volatile bool have_tb_lock = false; @@ -100,6 +101,7 @@ int cpu_exec(struct uc_struct *uc, CPUArchState *env) // qq if (sigsetjmp(cpu->jmp_env, 0) == 0) { if (uc->stop_request || uc->invalid_error) break; + /* if an exception is pending, we execute it here */ if (cpu->exception_index >= 0) { //printf(">>> GOT INTERRUPT. exception idx = %x\n", cpu->exception_index); // qq diff --git a/qemu/target-arm/unicorn_aarch64.c b/qemu/target-arm/unicorn_aarch64.c index 0123b5cb..1ce9d6eb 100644 --- a/qemu/target-arm/unicorn_aarch64.c +++ b/qemu/target-arm/unicorn_aarch64.c @@ -82,6 +82,9 @@ int arm64_reg_write(struct uc_struct *uc, unsigned int regid, const void *value) break; case UC_ARM64_REG_PC: ARM_CPU(uc, mycpu)->env.pc = *(uint64_t *)value; + // force to quit execution and flush TB + uc->quit_request = true; + uc_emu_stop(uc); break; case UC_ARM64_REG_SP: ARM_CPU(uc, mycpu)->env.xregs[31] = *(uint64_t *)value; diff --git a/qemu/target-arm/unicorn_arm.c b/qemu/target-arm/unicorn_arm.c index 15906deb..63f2c3f5 100644 --- a/qemu/target-arm/unicorn_arm.c +++ b/qemu/target-arm/unicorn_arm.c @@ -91,6 +91,9 @@ int arm_reg_write(struct uc_struct *uc, unsigned int regid, const void *value) //case UC_ARM_REG_PC: case UC_ARM_REG_R15: ARM_CPU(uc, mycpu)->env.regs[15] = *(uint32_t *)value; + // force to quit execution and flush TB + uc->quit_request = true; + uc_emu_stop(uc); break; } } diff --git a/qemu/target-i386/unicorn.c b/qemu/target-i386/unicorn.c index 11bb8232..0af5763e 100644 --- a/qemu/target-i386/unicorn.c +++ b/qemu/target-i386/unicorn.c @@ -656,9 +656,15 @@ int x86_reg_write(struct uc_struct *uc, unsigned int regid, const void *value) break; case UC_X86_REG_EIP: X86_CPU(uc, mycpu)->env.eip = *(uint32_t *)value; + // force to quit execution and flush TB + uc->quit_request = true; + uc_emu_stop(uc); break; case UC_X86_REG_IP: WRITE_WORD(X86_CPU(uc, mycpu)->env.eip, *(uint16_t *)value); + // force to quit execution and flush TB + uc->quit_request = true; + uc_emu_stop(uc); break; case UC_X86_REG_CS: X86_CPU(uc, mycpu)->env.segs[R_CS].base = *(uint32_t *)value; @@ -806,12 +812,21 @@ int x86_reg_write(struct uc_struct *uc, unsigned int regid, const void *value) break; case UC_X86_REG_RIP: X86_CPU(uc, mycpu)->env.eip = *(uint64_t *)value; + // force to quit execution and flush TB + uc->quit_request = true; + uc_emu_stop(uc); break; case UC_X86_REG_EIP: WRITE_DWORD(X86_CPU(uc, mycpu)->env.eip, *(uint32_t *)value); + // force to quit execution and flush TB + uc->quit_request = true; + uc_emu_stop(uc); break; case UC_X86_REG_IP: WRITE_WORD(X86_CPU(uc, mycpu)->env.eip, *(uint16_t *)value); + // force to quit execution and flush TB + uc->quit_request = true; + uc_emu_stop(uc); break; case UC_X86_REG_CS: X86_CPU(uc, mycpu)->env.segs[R_CS].base = *(uint64_t *)value; diff --git a/qemu/target-m68k/unicorn.c b/qemu/target-m68k/unicorn.c index df732405..0055d4a1 100644 --- a/qemu/target-m68k/unicorn.c +++ b/qemu/target-m68k/unicorn.c @@ -70,6 +70,9 @@ int m68k_reg_write(struct uc_struct *uc, unsigned int regid, const void *value) default: break; case UC_M68K_REG_PC: M68K_CPU(uc, mycpu)->env.pc = *(uint32_t *)value; + // force to quit execution and flush TB + uc->quit_request = true; + uc_emu_stop(uc); break; } } diff --git a/qemu/target-mips/unicorn.c b/qemu/target-mips/unicorn.c index 3885d02d..6af98dce 100644 --- a/qemu/target-mips/unicorn.c +++ b/qemu/target-mips/unicorn.c @@ -81,6 +81,9 @@ int mips_reg_write(struct uc_struct *uc, unsigned int regid, const void *value) default: break; case UC_MIPS_REG_PC: MIPS_CPU(uc, mycpu)->env.active_tc.PC = *(uint32_t *)value; + // force to quit execution and flush TB + uc->quit_request = true; + uc_emu_stop(uc); break; } } diff --git a/qemu/target-sparc/unicorn.c b/qemu/target-sparc/unicorn.c index d86497b5..e612457b 100644 --- a/qemu/target-sparc/unicorn.c +++ b/qemu/target-sparc/unicorn.c @@ -87,6 +87,9 @@ int sparc_reg_write(struct uc_struct *uc, unsigned int regid, const void *value) case UC_SPARC_REG_PC: SPARC_CPU(uc, mycpu)->env.pc = *(uint32_t *)value; SPARC_CPU(uc, mycpu)->env.npc = *(uint32_t *)value + 4; + // force to quit execution and flush TB + uc->quit_request = true; + uc_emu_stop(uc); break; } } diff --git a/tests/unit/Makefile b/tests/unit/Makefile index cd0e46eb..67376fd2 100644 --- a/tests/unit/Makefile +++ b/tests/unit/Makefile @@ -5,7 +5,7 @@ CFLAGS += -lcmocka -lunicorn CFLAGS += -I ../../include ALL_TESTS = test_sanity test_x86 test_mem_map test_mem_high test_mem_map_ptr \ - test_tb_x86 test_multihook + test_tb_x86 test_multihook test_pc_change .PHONY: all all: ${ALL_TESTS} @@ -24,6 +24,7 @@ test: ${ALL_TESTS} ./test_mem_high ./test_tb_x86 ./test_multihook + ./test_pc_change test_sanity: test_sanity.c test_x86: test_x86.c @@ -32,6 +33,7 @@ test_mem_map_ptr: test_mem_map_ptr.c test_mem_high: test_mem_high.c test_tb_x86: test_tb_x86.c test_multihook: test_multihook.c +test_pc_change: test_pc_change.c ${ALL_TESTS}: ${CC} ${CFLAGS} -o $@ $^ diff --git a/tests/unit/test_pc_change.c b/tests/unit/test_pc_change.c new file mode 100644 index 00000000..7994e772 --- /dev/null +++ b/tests/unit/test_pc_change.c @@ -0,0 +1,104 @@ +// Test PC change during the callback. by Nguyen Anh Quynh, 2016 +#include "unicorn_test.h" +#include + +#define OK(x) uc_assert_success(x) + +/* Called before every test to set up a new instance */ +static int setup32(void **state) +{ + uc_engine *uc; + + OK(uc_open(UC_ARCH_X86, UC_MODE_32, &uc)); + + *state = uc; + return 0; +} + +/* Called after every test to clean up */ +static int teardown(void **state) +{ + uc_engine *uc = *state; + + OK(uc_close(uc)); + + *state = NULL; + return 0; +} + +/******************************************************************************/ + +static void test_code_hook(uc_engine *uc, uint64_t address, uint32_t size, void *user_data) +{ + uint8_t tmp[256]; + int32_t r_eip = 0x1000006; + printf("instruction at 0x%"PRIx64": ", address); + + if (!uc_mem_read(uc, address, tmp, size)) { + uint32_t i; + + for (i = 0; i < size; i++) { + printf("0x%x ", tmp[i]); + } + printf("\n"); + } + + if (address == 0x1000003) { + // change the PC to "inc EDX" + uc_reg_write(uc, UC_X86_REG_EIP, &r_eip); + } +} + +static void test_pc_change(void **state) +{ + uc_engine *uc = *state; + uc_hook trace1; + int32_t r_ecx = 3, r_edx = 15; + +#define BASEADDR 0x1000000 + + uint64_t address = BASEADDR; + const uint8_t code[] = { + 0x41, // inc ECX @0x1000000 + 0x41, // inc ECX + 0x41, // inc ECX + 0x41, // inc ECX @0x1000003 + 0x41, // inc ECX + 0x41, // inc ECX + + 0x42, // inc EDX @0x1000006 + 0x42, // inc EDX + }; + +#undef BASEADDR + + // map 2MB memory for this emulation + OK(uc_mem_map(uc, address, 2 * 1024 * 1024, UC_PROT_ALL)); + + // write machine code to be emulated to memory + OK(uc_mem_write(uc, address, code, sizeof(code))); + + uc_reg_write(uc, UC_X86_REG_ECX, &r_ecx); + uc_reg_write(uc, UC_X86_REG_EDX, &r_edx); + printf("ECX = %u, EDX = %u\n", r_ecx, r_edx); + + // trace all instructions + OK(uc_hook_add(uc, &trace1, UC_HOOK_CODE, test_code_hook, NULL, (uint64_t)1, (uint64_t)0)); + + OK(uc_emu_start(uc, address, address+sizeof(code), 0, 0)); + + uc_reg_read(uc, UC_X86_REG_ECX, &r_ecx); + uc_reg_read(uc, UC_X86_REG_EDX, &r_edx); + + printf("ECX = %u, EDX = %u\n", r_ecx, r_edx); + assert_int_equal(r_ecx, 6); + assert_int_equal(r_edx, 17); +} + +int main(void) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(test_pc_change, setup32, teardown), + }; + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/uc.c b/uc.c index 06b08fca..ab466601 100644 --- a/uc.c +++ b/uc.c @@ -494,7 +494,6 @@ uc_err uc_emu_start(uc_engine* uc, uint64_t begin, uint64_t until, uint64_t time { // reset the counter uc->emu_counter = 0; - uc->stop_request = false; uc->invalid_error = UC_ERR_OK; uc->block_full = false; uc->emulation_done = false; @@ -542,6 +541,8 @@ uc_err uc_emu_start(uc_engine* uc, uint64_t begin, uint64_t until, uint64_t time break; } + uc->stop_request = false; + uc->emu_count = count; // remove count hook if counting isn't necessary if (count <= 0 && uc->count_hook != 0) {