mirror of
https://github.com/yuzu-emu/unicorn.git
synced 2025-01-03 16:35:46 +00:00
allow to change PC during callback. this solves issue #210
This commit is contained in:
parent
e750a4e97c
commit
5a04bcb115
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -135,6 +135,8 @@ rw_hookstack
|
|||
hook_extrainvoke
|
||||
sysenter_hook_x86
|
||||
test_tb_x86
|
||||
test_multihook
|
||||
test_pc_change
|
||||
|
||||
|
||||
#################
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 $@ $^
|
||||
|
|
104
tests/unit/test_pc_change.c
Normal file
104
tests/unit/test_pc_change.c
Normal file
|
@ -0,0 +1,104 @@
|
|||
// Test PC change during the callback. by Nguyen Anh Quynh, 2016
|
||||
#include "unicorn_test.h"
|
||||
#include <inttypes.h>
|
||||
|
||||
#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);
|
||||
}
|
3
uc.c
3
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) {
|
||||
|
|
Loading…
Reference in a new issue