Merge branch 'master' into mem_map_ex_cse

This commit is contained in:
Chris Eagle 2015-08-29 21:22:33 -07:00
commit 4a680b9277
10 changed files with 179 additions and 14 deletions

View File

@ -9,8 +9,8 @@ void hookCode_cgo(uch handle, uint64_t addr, uint32_t size, void *user) {
hookCode(handle, addr, size, user); hookCode(handle, addr, size, user);
} }
bool hookMemInvalid_cgo(uch handle, uc_mem_type type, uint64_t addr, int64_t value, void *user) { bool hookMemInvalid_cgo(uch handle, uc_mem_type type, uint64_t addr, int size, int64_t value, void *user) {
return hookMemInvalid(handle, type, addr, value, user); return hookMemInvalid(handle, type, addr, size, value, user);
} }
void hookMemAccess_cgo(uch handle, uc_mem_type type, uint64_t addr, int size, int64_t value, void *user) { void hookMemAccess_cgo(uch handle, uc_mem_type type, uint64_t addr, int size, int64_t value, void *user) {

View File

@ -23,15 +23,15 @@ func hookCode(handle C.uch, addr C.uint64_t, size C.uint32_t, user unsafe.Pointe
} }
//export hookMemInvalid //export hookMemInvalid
func hookMemInvalid(handle C.uch, typ C.uc_mem_type, addr C.uint64_t, value C.int64_t, user unsafe.Pointer) C.bool { func hookMemInvalid(handle C.uch, typ C.uc_mem_type, addr C.uint64_t, size int, value C.int64_t, user unsafe.Pointer) C.bool {
hook := (*HookData)(user) hook := (*HookData)(user)
return C.bool(hook.Callback.(func(*Uc, int, uint64, int64) bool)(hook.Uc, int(typ), uint64(addr), int64(value))) return C.bool(hook.Callback.(func(*Uc, int, uint64, int, int64) bool)(hook.Uc, int(typ), uint64(addr), size, int64(value)))
} }
//export hookMemAccess //export hookMemAccess
func hookMemAccess(handle C.uch, typ C.uc_mem_type, addr C.uint64_t, size int, value C.int64_t, user unsafe.Pointer) { func hookMemAccess(handle C.uch, typ C.uc_mem_type, addr C.uint64_t, size int, value C.int64_t, user unsafe.Pointer) {
hook := (*HookData)(user) hook := (*HookData)(user)
hook.Callback.(func(*Uc, int, uint64, uint32, int64))(hook.Uc, int(typ), uint64(addr), uint32(size), int64(value)) hook.Callback.(func(*Uc, int, uint64, int, int64))(hook.Uc, int(typ), uint64(addr), size, int64(value))
} }
//export hookX86In //export hookX86In
@ -52,6 +52,8 @@ func hookX86Syscall(handle C.uch, user unsafe.Pointer) {
hook.Callback.(func(*Uc))(hook.Uc) hook.Callback.(func(*Uc))(hook.Uc)
} }
var hookRetain = make(map[C.uch]*HookData)
func (u *Uc) HookAdd(htype int, cb interface{}, insn ...int) (C.uch, error) { func (u *Uc) HookAdd(htype int, cb interface{}, insn ...int) (C.uch, error) {
var callback unsafe.Pointer var callback unsafe.Pointer
var extra C.int var extra C.int
@ -78,10 +80,13 @@ func (u *Uc) HookAdd(htype int, cb interface{}, insn ...int) (C.uch, error) {
return 0, errors.New("Unknown hook type.") return 0, errors.New("Unknown hook type.")
} }
var h2 C.uch var h2 C.uch
C.uc_hook_add2(u.Handle, &h2, C.uc_hook_t(htype), callback, unsafe.Pointer(&HookData{u, cb}), extra) data := &HookData{u, cb}
C.uc_hook_add2(u.Handle, &h2, C.uc_hook_t(htype), callback, unsafe.Pointer(data), extra)
hookRetain[h2] = data
return h2, nil return h2, nil
} }
func (u *Uc) HookDel(hook *C.uch) error { func (u *Uc) HookDel(hook *C.uch) error {
delete(hookRetain, *hook)
return errReturn(C.uc_hook_del(u.Handle, hook)) return errReturn(C.uc_hook_del(u.Handle, hook))
} }

View File

@ -1,6 +1,6 @@
uc_err uc_hook_add2(uch handle, uch *h2, uc_hook_t type, void *callback, void *user_data, int extra); uc_err uc_hook_add2(uch handle, uch *h2, uc_hook_t type, void *callback, void *user_data, int extra);
void hookCode_cgo(uch handle, uint64_t addr, uint32_t size, void *user); void hookCode_cgo(uch handle, uint64_t addr, uint32_t size, void *user);
bool hookMemInvalid_cgo(uch handle, uc_mem_type type, uint64_t addr, int64_t value, void *user); bool hookMemInvalid_cgo(uch handle, uc_mem_type type, uint64_t addr, int size, int64_t value, void *user);
void hookMemAccess_cgo(uch handle, uc_mem_type type, uint64_t addr, int size, int64_t value, void *user); void hookMemAccess_cgo(uch handle, uc_mem_type type, uint64_t addr, int size, int64_t value, void *user);
uint32_t hookX86In_cgo(uch handle, uint32_t port, uint32_t size, void *user); uint32_t hookX86In_cgo(uch handle, uint32_t port, uint32_t size, void *user);
void hookX86Out_cgo(uch handle, uint32_t port, uint32_t size, uint32_t value, void *user); void hookX86Out_cgo(uch handle, uint32_t port, uint32_t size, uint32_t value, void *user);

View File

@ -72,10 +72,16 @@ func (u *Uc) RegRead(reg int) (uint64, error) {
} }
func (u *Uc) MemWrite(addr uint64, data []byte) error { func (u *Uc) MemWrite(addr uint64, data []byte) error {
if len(data) == 0 {
return nil
}
return errReturn(C.uc_mem_write(u.Handle, C.uint64_t(addr), (*C.uint8_t)(unsafe.Pointer(&data[0])), C.size_t(len(data)))) return errReturn(C.uc_mem_write(u.Handle, C.uint64_t(addr), (*C.uint8_t)(unsafe.Pointer(&data[0])), C.size_t(len(data))))
} }
func (u *Uc) MemReadInto(dst []byte, addr uint64) error { func (u *Uc) MemReadInto(dst []byte, addr uint64) error {
if len(dst) == 0 {
return nil
}
return errReturn(C.uc_mem_read(u.Handle, C.uint64_t(addr), (*C.uint8_t)(unsafe.Pointer(&dst[0])), C.size_t(len(dst)))) return errReturn(C.uc_mem_read(u.Handle, C.uint64_t(addr), (*C.uint8_t)(unsafe.Pointer(&dst[0])), C.size_t(len(dst))))
} }

View File

@ -98,7 +98,6 @@ struct uc_struct {
void* cpu; void* cpu;
MemoryRegion *system_memory; // qemu/exec.c MemoryRegion *system_memory; // qemu/exec.c
MemoryRegion *ram;
MemoryRegion io_mem_rom; // qemu/exec.c MemoryRegion io_mem_rom; // qemu/exec.c
MemoryRegion io_mem_notdirty; // qemu/exec.c MemoryRegion io_mem_notdirty; // qemu/exec.c
MemoryRegion io_mem_unassigned; // qemu/exec.c MemoryRegion io_mem_unassigned; // qemu/exec.c

View File

@ -38,8 +38,8 @@ build_cross() {
[ "$UNAME" = Darwin ] && LIBARCHS="i386 x86_64" [ "$UNAME" = Darwin ] && LIBARCHS="i386 x86_64"
CROSS=$1 CROSS=$1
CC=$CROSS-gcc \ CC=$CROSS-gcc \
AR=$CROSS-ar \ AR=$CROSS-gcc-ar \
RANLIB=$CROSS-ranlib \ RANLIB=$CROSS-gcc-ranlib \
GLIB="-L/usr/$CROSS/lib/ -lglib-2.0" \ GLIB="-L/usr/$CROSS/lib/ -lglib-2.0" \
${MAKE} ${MAKE}
} }

View File

@ -33,16 +33,16 @@
// Unicorn engine // Unicorn engine
MemoryRegion *memory_map(struct uc_struct *uc, ram_addr_t begin, size_t size, uint32_t perms) MemoryRegion *memory_map(struct uc_struct *uc, ram_addr_t begin, size_t size, uint32_t perms)
{ {
uc->ram = g_new(MemoryRegion, 1); MemoryRegion *ram = g_new(MemoryRegion, 1);
memory_region_init_ram(uc, uc->ram, NULL, "pc.ram", size, perms, &error_abort); memory_region_init_ram(uc, ram, NULL, "pc.ram", size, perms, &error_abort);
memory_region_add_subregion(get_system_memory(uc), begin, uc->ram); memory_region_add_subregion(get_system_memory(uc), begin, ram);
if (uc->current_cpu) if (uc->current_cpu)
tlb_flush(uc->current_cpu, 1); tlb_flush(uc->current_cpu, 1);
return uc->ram; return ram;
} }
void memory_unmap(struct uc_struct *uc, MemoryRegion *mr) void memory_unmap(struct uc_struct *uc, MemoryRegion *mr)

View File

@ -5,6 +5,7 @@ TESTS = map_crash map_write
TESTS += sigill sigill2 TESTS += sigill sigill2
TESTS += block_test TESTS += block_test
TESTS += ro_mem_test nr_mem_test TESTS += ro_mem_test nr_mem_test
TESTS += timeout_segfault
all: $(TESTS) all: $(TESTS)

149
regress/timeout_segfault.c Normal file
View File

@ -0,0 +1,149 @@
/*
timeout_segfault.c
This program shows a case where the emulation timer keeps running after
emulation has ended. It triggers an intermittent segfault when _timeout_fn()
tries to call uc_emu_stop() after emulation has already been cleaned up. This
code is the same as samples/sample_arm.c, except that it adds a timeout on each
call to uc_emu_start(). See issue #78 for more details:
https://github.com/unicorn-engine/unicorn/issues/78
*/
#include <inttypes.h>
#include <unicorn/unicorn.h>
// code to be emulated
#define ARM_CODE "\x37\x00\xa0\xe3\x03\x10\x42\xe0" // mov r0, #0x37; sub r1, r2, r3
#define THUMB_CODE "\x83\xb0" // sub sp, #0xc
// memory address where emulation starts
#define ADDRESS 0x10000
// number of seconds to wait before timeout
#define TIMEOUT 5
static void hook_block(uch handle, uint64_t address, uint32_t size, void *user_data)
{
printf(">>> Tracing basic block at 0x%"PRIx64 ", block size = 0x%x\n", address, size);
}
static void hook_code(uch handle, uint64_t address, uint32_t size, void *user_data)
{
printf(">>> Tracing instruction at 0x%"PRIx64 ", instruction size = 0x%x\n", address, size);
}
static void test_arm(void)
{
uch handle;
uc_err err;
uch trace1, trace2;
int r0 = 0x1234; // R0 register
int r2 = 0x6789; // R1 register
int r3 = 0x3333; // R2 register
int r1; // R1 register
printf("Emulate ARM code\n");
// Initialize emulator in ARM mode
err = uc_open(UC_ARCH_ARM, UC_MODE_ARM, &handle);
if (err) {
printf("Failed on uc_open() with error returned: %u (%s)\n",
err, uc_strerror(err));
return;
}
// map 2MB memory for this emulation
uc_mem_map(handle, ADDRESS, 2 * 1024 * 1024, UC_PROT_ALL);
// write machine code to be emulated to memory
uc_mem_write(handle, ADDRESS, (uint8_t *)ARM_CODE, sizeof(ARM_CODE) - 1);
// initialize machine registers
uc_reg_write(handle, UC_ARM_REG_R0, &r0);
uc_reg_write(handle, UC_ARM_REG_R2, &r2);
uc_reg_write(handle, UC_ARM_REG_R3, &r3);
// tracing all basic blocks with customized callback
uc_hook_add(handle, &trace1, UC_HOOK_BLOCK, hook_block, NULL, (uint64_t)1, (uint64_t)0);
// tracing one instruction at ADDRESS with customized callback
uc_hook_add(handle, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)ADDRESS, (uint64_t)ADDRESS);
// emulate machine code in infinite time (last param = 0), or when
// finishing all the code.
err = uc_emu_start(handle, ADDRESS, ADDRESS + sizeof(ARM_CODE) -1, UC_SECOND_SCALE * TIMEOUT, 0);
if (err) {
printf("Failed on uc_emu_start() with error returned: %u\n", err);
}
// now print out some registers
printf(">>> Emulation done. Below is the CPU context\n");
uc_reg_read(handle, UC_ARM_REG_R0, &r0);
uc_reg_read(handle, UC_ARM_REG_R1, &r1);
printf(">>> R0 = 0x%x\n", r0);
printf(">>> R1 = 0x%x\n", r1);
uc_close(&handle);
}
static void test_thumb(void)
{
uch handle;
uc_err err;
uch trace1, trace2;
int sp = 0x1234; // R0 register
printf("Emulate THUMB code\n");
// Initialize emulator in ARM mode
err = uc_open(UC_ARCH_ARM, UC_MODE_THUMB, &handle);
if (err) {
printf("Failed on uc_open() with error returned: %u (%s)\n",
err, uc_strerror(err));
return;
}
// map 2MB memory for this emulation
uc_mem_map(handle, ADDRESS, 2 * 1024 * 1024, UC_PROT_ALL);
// write machine code to be emulated to memory
uc_mem_write(handle, ADDRESS, (uint8_t *)THUMB_CODE, sizeof(THUMB_CODE) - 1);
// initialize machine registers
uc_reg_write(handle, UC_ARM_REG_SP, &sp);
// tracing all basic blocks with customized callback
uc_hook_add(handle, &trace1, UC_HOOK_BLOCK, hook_block, NULL, (uint64_t)1, (uint64_t)0);
// tracing one instruction at ADDRESS with customized callback
uc_hook_add(handle, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)ADDRESS, (uint64_t)ADDRESS);
// emulate machine code in infinite time (last param = 0), or when
// finishing all the code.
err = uc_emu_start(handle, ADDRESS, ADDRESS + sizeof(THUMB_CODE) -1, UC_SECOND_SCALE * TIMEOUT, 0);
if (err) {
printf("Failed on uc_emu_start() with error returned: %u\n", err);
}
// now print out some registers
printf(">>> Emulation done. Below is the CPU context\n");
uc_reg_read(handle, UC_ARM_REG_SP, &sp);
printf(">>> SP = 0x%x\n", sp);
uc_close(&handle);
}
int main(int argc, char **argv, char **envp)
{
test_arm();
printf("==========================\n");
test_thumb();
return 0;
}

5
uc.c
View File

@ -549,6 +549,11 @@ uc_err uc_emu_start(uch handle, uint64_t begin, uint64_t until, uint64_t timeout
// emulation is done // emulation is done
uc->emulation_done = true; uc->emulation_done = true;
if (timeout) {
// wait for the timer to finish
qemu_thread_join(&uc->timer);
}
return uc->invalid_error; return uc->invalid_error;
} }