diff --git a/Makefile b/Makefile index c4cb31c..5bd94a9 100644 --- a/Makefile +++ b/Makefile @@ -228,7 +228,7 @@ compile_lib: config qemu/config-host.h-timestamp unicorn: $(LIBRARY) $(ARCHIVE) -$(LIBRARY): $(UC_TARGET_OBJ) uc.o hook.o +$(LIBRARY): $(UC_TARGET_OBJ) uc.o list.o ifeq ($(UNICORN_SHARED),yes) ifeq ($(V),0) $(call log,GEN,$(LIBRARY)) @@ -241,7 +241,7 @@ ifneq (,$(LIBRARY_SYMLINK)) endif endif -$(ARCHIVE): $(UC_TARGET_OBJ) uc.o hook.o +$(ARCHIVE): $(UC_TARGET_OBJ) uc.o list.o ifeq ($(UNICORN_STATIC),yes) ifeq ($(V),0) $(call log,GEN,$(ARCHIVE)) diff --git a/hook.c b/hook.c deleted file mode 100644 index 6e575d8..0000000 --- a/hook.c +++ /dev/null @@ -1,279 +0,0 @@ -/* Unicorn Emulator Engine */ -/* By Nguyen Anh Quynh , 2015 */ - -#include "uc_priv.h" -#include "hook.h" - - -// return index for a new hook entry in hook_callbacks[] array. -// this realloc memory if needed. -size_t hook_find_new(struct uc_struct *uc) -{ - size_t i; - struct hook_struct *new; - - // find the first free slot. skip slot 0, so index > 0 - for(i = 1; i < uc->hook_size; i++) { - if (uc->hook_callbacks[i].callback == NULL) { - return i; - } - } - - // not found, so the array is full. - // we have to realloc hook_callbacks[] to contain new hooks - new = realloc(uc->hook_callbacks, - (uc->hook_size + HOOK_SIZE) * sizeof(uc->hook_callbacks[0])); - if (!new) // OOM ? - return 0; - - // reset the newly added slots - memset(new + uc->hook_size, 0, HOOK_SIZE * sizeof(uc->hook_callbacks[0])); - - uc->hook_callbacks = new; - uc->hook_size += HOOK_SIZE; - - // return the first newly allocated slot - return uc->hook_size - HOOK_SIZE; -} - -// return -1 on failure, index to hook_callbacks[] on success. -size_t hook_add(struct uc_struct *uc, int type, uint64_t begin, uint64_t end, void *callback, void *user_data) -{ - int i; - - // find the first free slot. skip slot 0, so index > 0 - i = hook_find_new(uc); - if (i) { - uc->hook_callbacks[i].hook_type = type; - uc->hook_callbacks[i].begin = begin; - uc->hook_callbacks[i].end = end; - uc->hook_callbacks[i].callback = callback; - uc->hook_callbacks[i].user_data = user_data; - - switch(type) { - default: break; - case UC_HOOK_BLOCK: - uc->hook_block = true; - if (begin > end) - uc->hook_block_idx = i; - break; - case UC_HOOK_CODE: - uc->hook_insn = true; - if (begin > end) - uc->hook_insn_idx = i; - break; - case UC_HOOK_MEM_READ: - uc->hook_mem_read = true; - if (begin > end) - uc->hook_read_idx = i; - break; - case UC_HOOK_MEM_WRITE: - uc->hook_mem_write = true; - if (begin > end) - uc->hook_write_idx = i; - break; - case UC_HOOK_MEM_READ | UC_HOOK_MEM_WRITE: - uc->hook_mem_read = true; - uc->hook_mem_write = true; - if (begin > end) { - uc->hook_read_idx = i; - uc->hook_write_idx = i; - } - break; - } - - return i; - } - - // not found - return 0; -} - -// return 0 on success, -1 on failure -uc_err hook_del(struct uc_struct *uc, uc_hook hh) -{ - if (hh == uc->hook_block_idx) { - uc->hook_block_idx = 0; - } - - if (hh == uc->hook_insn_idx) { - uc->hook_insn_idx = 0; - } - - if (hh == uc->hook_read_idx) { - uc->hook_read_idx = 0; - } - - if (hh == uc->hook_write_idx) { - uc->hook_write_idx = 0; - } - - if (hh == uc->hook_mem_read_idx) { - uc->hook_mem_read_idx = 0; - } - - if (hh == uc->hook_mem_write_idx) { - uc->hook_mem_write_idx = 0; - } - - if (hh == uc->hook_mem_fetch_idx) { - uc->hook_mem_fetch_idx = 0; - } - - if (hh == uc->hook_mem_read_prot_idx) { - uc->hook_mem_read_prot_idx = 0; - } - - if (hh == uc->hook_mem_write_prot_idx) { - uc->hook_mem_write_prot_idx = 0; - } - - if (hh == uc->hook_mem_fetch_prot_idx) { - uc->hook_mem_fetch_prot_idx = 0; - } - - if (hh == uc->hook_intr_idx) { - uc->hook_intr_idx = 0; - } - - if (hh == uc->hook_out_idx) { - uc->hook_out_idx = 0; - } - - if (hh == uc->hook_in_idx) { - uc->hook_in_idx = 0; - } - - if(hh == uc->hook_syscall_idx) { - uc->hook_syscall_idx = 0; - } - - uc->hook_callbacks[hh].callback = NULL; - uc->hook_callbacks[hh].user_data = NULL; - uc->hook_callbacks[hh].hook_type = 0; - uc->hook_callbacks[hh].begin = 0; - uc->hook_callbacks[hh].end = 0; - - return UC_ERR_OK; -} - -// return NULL on failure -static struct hook_struct *_hook_find(struct uc_struct *uc, int type, uint64_t address) -{ - int i; - - switch(type) { - default: break; - case UC_HOOK_BLOCK: - // already hooked all blocks? - if (uc->hook_block_idx) - return &uc->hook_callbacks[uc->hook_block_idx]; - break; - case UC_HOOK_CODE: - // already hooked all the code? - if (uc->hook_insn_idx) - return &uc->hook_callbacks[uc->hook_insn_idx]; - break; - case UC_HOOK_MEM_READ: - // already hooked all memory read? - if (uc->hook_read_idx) { - return &uc->hook_callbacks[uc->hook_read_idx]; - } - break; - case UC_HOOK_MEM_WRITE: - // already hooked all memory write? - if (uc->hook_write_idx) - return &uc->hook_callbacks[uc->hook_write_idx]; - break; - } - - // no trace-all callback - for(i = 1; i < uc->hook_size; i++) { - switch(type) { - default: break; - case UC_HOOK_BLOCK: - case UC_HOOK_CODE: - if (uc->hook_callbacks[i].hook_type == type) { - if (uc->hook_callbacks[i].begin <= address && address <= uc->hook_callbacks[i].end) - return &uc->hook_callbacks[i]; - } - break; - case UC_HOOK_MEM_READ: - if (uc->hook_callbacks[i].hook_type & UC_HOOK_MEM_READ) { - if (uc->hook_callbacks[i].begin <= address && address <= uc->hook_callbacks[i].end) - return &uc->hook_callbacks[i]; - } - break; - case UC_HOOK_MEM_WRITE: - if (uc->hook_callbacks[i].hook_type & UC_HOOK_MEM_WRITE) { - if (uc->hook_callbacks[i].begin <= address && address <= uc->hook_callbacks[i].end) - return &uc->hook_callbacks[i]; - } - break; - } - } - - // not found - return NULL; -} - - -static void hook_count_cb(struct uc_struct *uc, uint64_t address, uint32_t size, void *user_data) -{ - // count this instruction - uc->emu_counter++; - - if (uc->emu_counter > uc->emu_count) - uc_emu_stop(uc); - else if (uc->hook_count_callback) - uc->hook_count_callback(uc, address, size, user_data); -} - -struct hook_struct *hook_find(struct uc_struct *uc, int type, uint64_t address) -{ - // stop executing callbacks if we already got stop request - if (uc->stop_request) - return NULL; - - // UC_HOOK_CODE is special because we may need to count instructions - if (type == UC_HOOK_CODE && uc->emu_count > 0) { - struct hook_struct *st = _hook_find(uc, type, address); - if (st) { - // prepare this struct to pass back to caller - uc->hook_count.hook_type = UC_HOOK_CODE; - uc->hook_count.begin = st->begin; - uc->hook_count.end = st->end; - uc->hook_count.callback = hook_count_cb; - uc->hook_count.user_data = st->user_data; - // save this hook callback so we can call it later - uc->hook_count_callback = st->callback; - } else { - // there is no callback, but we still need to - // handle instruction count - uc->hook_count.hook_type = UC_HOOK_CODE; - uc->hook_count.begin = 1; - uc->hook_count.end = 0; - uc->hook_count.callback = hook_count_cb; - uc->hook_count.user_data = NULL; - uc->hook_count_callback = NULL; // no callback - } - - return &(uc->hook_count); - } else - return _hook_find(uc, type, address); -} - - -// TCG helper -void helper_uc_tracecode(int32_t size, void *callback, void *handle, int64_t address, void *user_data); -void helper_uc_tracecode(int32_t size, void *callback, void *handle, int64_t address, void *user_data) -{ - struct uc_struct *uc = handle; - - // sync PC in CPUArchState with address - if (uc->set_pc) { - uc->set_pc(uc, address); - } - - ((uc_cb_hookcode_t)callback)(uc, address, size, user_data); -} diff --git a/include/hook.h b/include/hook.h deleted file mode 100644 index 6fbdf50..0000000 --- a/include/hook.h +++ /dev/null @@ -1,20 +0,0 @@ -/* Unicorn Emulator Engine */ -/* By Nguyen Anh Quynh , 2015 */ - -#ifndef UC_HOOK_H -#define UC_HOOK_H - -// return -1 on failure, index to traces[] on success. -size_t hook_add(struct uc_struct *uc, int type, uint64_t begin, uint64_t end, void *callback, void *user_data); - -// return 0 on success, -1 on failure -uc_err hook_del(struct uc_struct *uc, uc_hook hh); - -// return NULL on failure -struct hook_struct *hook_find(struct uc_struct *uc, int type, uint64_t address); - -// return index of an free hook entry in hook_callbacks[] array. -// this realloc memory if needed. -size_t hook_find_new(struct uc_struct *uc); - -#endif diff --git a/include/list.h b/include/list.h new file mode 100644 index 0000000..75ec13e --- /dev/null +++ b/include/list.h @@ -0,0 +1,20 @@ +#ifndef UC_LLIST_H +#define UC_LLIST_H + +#include + +struct list_item { + struct list_item *next; + void *data; +}; + +struct list { + struct list_item *head, *tail; +}; + +struct list *list_new(void); +void list_clear(struct list *list); +void *list_append(struct list *list, void *data); +bool list_remove(struct list *list, void *data); + +#endif diff --git a/include/uc_priv.h b/include/uc_priv.h index c4e0e17..44c116e 100644 --- a/include/uc_priv.h +++ b/include/uc_priv.h @@ -9,7 +9,7 @@ #include "qemu.h" #include "unicorn/unicorn.h" -#include "hook.h" +#include "list.h" // These are masks of supported modes for each cpu/arch. // They should be updated when changes are made to the uc_mode enum typedef. @@ -71,16 +71,62 @@ typedef bool (*uc_args_int_t)(int intno); // some architecture redirect virtual memory to physical memory like Mips typedef uint64_t (*uc_mem_redirect_t)(uint64_t address); - -struct hook_struct { - int hook_type; // uc_tracecode_type & uc_tracemem_type - uint64_t begin, end; // range of address to be monitored - void *callback; // either uc_cb_tracecode_t or uc_cb_tracemem_t +struct hook { + int type; // UC_HOOK_* + int insn; // instruction for HOOK_INSN + int refs; // reference count to free hook stored in multiple lists + uint64_t begin, end; // only trigger if PC or memory access is in this address (depends on hook type) + void *callback; // a uc_cb_* type void *user_data; }; -// extend memory to keep 32 more hooks each time -#define HOOK_SIZE 32 +// hook list offsets +// mirrors the order of uc_hook_type from include/unicorn/unicorn.h +enum uc_hook_idx { + UC_HOOK_INTR_IDX, + UC_HOOK_INSN_IDX, + UC_HOOK_CODE_IDX, + UC_HOOK_BLOCK_IDX, + UC_HOOK_MEM_READ_UNMAPPED_IDX, + UC_HOOK_MEM_WRITE_UNMAPPED_IDX, + UC_HOOK_MEM_FETCH_UNMAPPED_IDX, + UC_HOOK_MEM_READ_PROT_IDX, + UC_HOOK_MEM_WRITE_PROT_IDX, + UC_HOOK_MEM_FETCH_PROT_IDX, + UC_HOOK_MEM_READ_IDX, + UC_HOOK_MEM_WRITE_IDX, + UC_HOOK_MEM_FETCH_IDX, + + UC_HOOK_MAX, +}; + +// for loop macro to loop over hook lists +#define HOOK_FOREACH(uc, hh, idx) \ + struct list_item *cur; \ + for ( \ + cur = (uc)->hook[idx##_IDX].head; \ + cur != NULL && ((hh) = (struct hook *)cur->data) \ + /* stop excuting callbacks on stop request */ \ + && !uc->stop_request; \ + cur = cur->next) + +// if statement to check hook bounds +#define HOOK_BOUND_CHECK(hh, addr) \ + ((((addr) >= (hh)->begin && (addr) <= (hh)->end) \ + || (hh)->begin > (hh)->end)) + +#define HOOK_EXISTS(uc, idx) ((uc)->hook[idx##_IDX].head != NULL) +#define HOOK_EXISTS_BOUNDED(uc, idx, addr) _hook_exists_bounded((uc)->hook[idx##_IDX].head, addr) + +static inline bool _hook_exists_bounded(struct list_item *cur, uint64_t addr) +{ + while (cur != NULL) { + if (HOOK_BOUND_CHECK((struct hook *)cur->data, addr)) + return true; + cur = cur->next; + } + return false; +} //relloc increment, KEEP THIS A POWER OF 2! #define MEM_BLOCK_INCR 32 @@ -156,35 +202,16 @@ struct uc_struct { bool apic_report_tpr_access; CPUState *current_cpu; - // all the hook callbacks - size_t hook_size; - struct hook_struct *hook_callbacks; + // linked lists containing hooks per type + struct list hook[UC_HOOK_MAX]; // hook to count number of instructions for uc_emu_start() - struct hook_struct hook_count; - uc_cb_hookcode_t hook_count_callback; + uc_hook count_hook; size_t emu_counter; // current counter of uc_emu_start() size_t emu_count; // save counter of uc_emu_start() - // indexes if hooking ALL block/code/read/write events - unsigned int hook_block_idx, hook_insn_idx, hook_read_idx, hook_write_idx; - // boolean variables for quick check on hooking block, code, memory accesses - bool hook_block, hook_insn, hook_mem_read, hook_mem_write; uint64_t block_addr; // save the last block address we hooked - // indexes to event callbacks - int hook_mem_read_idx; // for handling invalid memory read access on unmapped memory - int hook_mem_write_idx; // for handling invalid memory write access on unmapped memory - int hook_mem_fetch_idx; // for handling invalid memory fetch access on unmapped memory - int hook_mem_read_prot_idx; // for handling invalid memory read access on read-protected memory - int hook_mem_write_prot_idx; // for handling invalid memory write access on write-protected memory - int hook_mem_fetch_prot_idx; // for handling invalid memory fetch access on non-executable memory - - int hook_intr_idx; // for handling interrupt - int hook_out_idx; // for handling OUT instruction (X86) - int hook_in_idx; // for handling IN instruction (X86) - int hook_syscall_idx; // for handling SYSCALL/SYSENTER (X86) - bool init_tcg; // already initialized local TCGv variables? bool stop_request; // request to immediately stop emulation - for uc_emu_stop() diff --git a/list.c b/list.c new file mode 100644 index 0000000..6dbe478 --- /dev/null +++ b/list.c @@ -0,0 +1,68 @@ +#include +#include +#include "list.h" + +// simple linked list implementation + +struct list *list_new(void) +{ + return calloc(1, sizeof(struct list)); +} + +// removed linked list nodes but does not free their content +void list_clear(struct list *list) +{ + struct list_item *next, *cur = list->head; + while (cur != NULL) { + next = cur->next; + free(cur); + cur = next; + } + list->head = NULL; + list->tail = NULL; +} + +// returns generated linked list node, or NULL on failure +void *list_append(struct list *list, void *data) +{ + struct list_item *item = malloc(sizeof(struct list_item)); + if (item == NULL) { + return NULL; + } + item->next = NULL; + item->data = data; + if (list->head == NULL) { + list->head = item; + } else { + list->tail->next = item; + } + list->tail = item; + return item; +} + +// returns true if entry was removed, false otherwise +bool list_remove(struct list *list, void *data) +{ + struct list_item *next, *cur, *prev = NULL; + // is list empty? + if (list->head == NULL) { + return false; + } + cur = list->head; + while (cur != NULL) { + next = cur->next; + if (cur->data == data) { + if (cur == list->head) { + list->head = next; + } + if (cur == list->tail) { + list->tail = prev; + } + free(cur); + return true; + } + prev = cur; + cur = next; + } + return false; +} diff --git a/qemu/Makefile.objs b/qemu/Makefile.objs index 4e7cc89..d309d2a 100644 --- a/qemu/Makefile.objs +++ b/qemu/Makefile.objs @@ -6,7 +6,7 @@ util-obj-y = util/ qobject/ qapi/ qapi-types.o qapi-visit.o # block-obj-y is code used by both qemu system emulation and qemu-img block-obj-y = -block-obj-y += ../uc.o ../hook.o +block-obj-y += ../uc.o ../list.o #block-obj-$(CONFIG_POSIX) += aio-posix.o #block-obj-$(CONFIG_WIN32) += aio-win32.o diff --git a/qemu/cpu-exec.c b/qemu/cpu-exec.c index 5a1f379..3324600 100644 --- a/qemu/cpu-exec.c +++ b/qemu/cpu-exec.c @@ -64,6 +64,7 @@ int cpu_exec(struct uc_struct *uc, CPUArchState *env) // qq TranslationBlock *tb; uint8_t *tc_ptr; uintptr_t next_tb; + struct hook *hook; /* This must be volatile so it is not trashed by longjmp() */ volatile bool have_tb_lock = false; @@ -127,11 +128,10 @@ int cpu_exec(struct uc_struct *uc, CPUArchState *env) // qq ret = cpu->exception_index; break; #else - // Unicorn: call interrupt callback if registered - if (uc->hook_intr_idx) - ((uc_cb_hookintr_t)uc->hook_callbacks[uc->hook_intr_idx].callback)( - uc, cpu->exception_index, - uc->hook_callbacks[uc->hook_intr_idx].user_data); + // Unicorn: call registered interrupt callbacks + HOOK_FOREACH(uc, hook, UC_HOOK_INTR) { + ((uc_cb_hookintr_t)hook->callback)(uc, cpu->exception_index, hook->user_data); + } cpu->exception_index = -1; #if defined(TARGET_X86_64) // point EIP to the next instruction after INT diff --git a/qemu/ioport.c b/qemu/ioport.c index 338a296..c7ac6b8 100644 --- a/qemu/ioport.c +++ b/qemu/ioport.c @@ -66,39 +66,45 @@ const MemoryRegionOps unassigned_io_ops = { void cpu_outb(struct uc_struct *uc, pio_addr_t addr, uint8_t val) { //LOG_IOPORT("outb: %04"FMT_pioaddr" %02"PRIx8"\n", addr, val); - // Unicorn: call interrupt callback if registered - if (uc->hook_out_idx) - ((uc_cb_insn_out_t)uc->hook_callbacks[uc->hook_out_idx].callback)( - uc, addr, 1, val, - uc->hook_callbacks[uc->hook_out_idx].user_data); + // Unicorn: call registered OUT callbacks + struct hook *hook; + HOOK_FOREACH(uc, hook, UC_HOOK_INSN) { + if (hook->insn == UC_X86_INS_OUT) + ((uc_cb_insn_out_t)hook->callback)(uc, addr, 1, val, hook->user_data); + } } void cpu_outw(struct uc_struct *uc, pio_addr_t addr, uint16_t val) { //LOG_IOPORT("outw: %04"FMT_pioaddr" %04"PRIx16"\n", addr, val); - // Unicorn: call interrupt callback if registered - if (uc->hook_out_idx) - ((uc_cb_insn_out_t)uc->hook_callbacks[uc->hook_out_idx].callback)( - uc, addr, 2, val, - uc->hook_callbacks[uc->hook_out_idx].user_data); + // Unicorn: call registered OUT callbacks + struct hook *hook; + HOOK_FOREACH(uc, hook, UC_HOOK_INSN) { + if (hook->insn == UC_X86_INS_OUT) + ((uc_cb_insn_out_t)hook->callback)(uc, addr, 2, val, hook->user_data); + } } void cpu_outl(struct uc_struct *uc, pio_addr_t addr, uint32_t val) { //LOG_IOPORT("outl: %04"FMT_pioaddr" %08"PRIx32"\n", addr, val); - if (uc->hook_out_idx) - ((uc_cb_insn_out_t)uc->hook_callbacks[uc->hook_out_idx].callback)( - uc, addr, 4, val, - uc->hook_callbacks[uc->hook_out_idx].user_data); + // Unicorn: call registered OUT callbacks + struct hook *hook; + HOOK_FOREACH(uc, hook, UC_HOOK_INSN) { + if (hook->insn == UC_X86_INS_OUT) + ((uc_cb_insn_out_t)hook->callback)(uc, addr, 4, val, hook->user_data); + } } uint8_t cpu_inb(struct uc_struct *uc, pio_addr_t addr) { //LOG_IOPORT("inb : %04"FMT_pioaddr" %02"PRIx8"\n", addr, val); - if (uc->hook_in_idx) - return ((uc_cb_insn_in_t)uc->hook_callbacks[uc->hook_in_idx].callback)( - uc, addr, 1, - uc->hook_callbacks[uc->hook_in_idx].user_data); + // Unicorn: call registered IN callbacks + struct hook *hook; + HOOK_FOREACH(uc, hook, UC_HOOK_INSN) { + if (hook->insn == UC_X86_INS_IN) + return ((uc_cb_insn_in_t)hook->callback)(uc, addr, 1, hook->user_data); + } return 0; } @@ -106,10 +112,12 @@ uint8_t cpu_inb(struct uc_struct *uc, pio_addr_t addr) uint16_t cpu_inw(struct uc_struct *uc, pio_addr_t addr) { //LOG_IOPORT("inw : %04"FMT_pioaddr" %04"PRIx16"\n", addr, val); - if (uc->hook_in_idx) - return ((uc_cb_insn_in_t)uc->hook_callbacks[uc->hook_in_idx].callback)( - uc, addr, 2, - uc->hook_callbacks[uc->hook_in_idx].user_data); + // Unicorn: call registered IN callbacks + struct hook *hook; + HOOK_FOREACH(uc, hook, UC_HOOK_INSN) { + if (hook->insn == UC_X86_INS_IN) + return ((uc_cb_insn_in_t)hook->callback)(uc, addr, 2, hook->user_data); + } return 0; } @@ -117,10 +125,12 @@ uint16_t cpu_inw(struct uc_struct *uc, pio_addr_t addr) uint32_t cpu_inl(struct uc_struct *uc, pio_addr_t addr) { //LOG_IOPORT("inl : %04"FMT_pioaddr" %08"PRIx32"\n", addr, val); - if (uc->hook_in_idx) - return ((uc_cb_insn_in_t)uc->hook_callbacks[uc->hook_in_idx].callback)( - uc, addr, 4, - uc->hook_callbacks[uc->hook_in_idx].user_data); + // Unicorn: call registered IN callbacks + struct hook *hook; + HOOK_FOREACH(uc, hook, UC_HOOK_INSN) { + if (hook->insn == UC_X86_INS_IN) + return ((uc_cb_insn_in_t)hook->callback)(uc, addr, 4, hook->user_data); + } return 0; } diff --git a/qemu/softmmu_template.h b/qemu/softmmu_template.h index e3100fc..f58065b 100644 --- a/qemu/softmmu_template.h +++ b/qemu/softmmu_template.h @@ -178,23 +178,33 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx, uintptr_t haddr; DATA_TYPE res; int error_code; + struct hook *hook; + bool handled; struct uc_struct *uc = env->uc; MemoryRegion *mr = memory_mapping(uc, addr); // memory might be still unmapped while reading or fetching if (mr == NULL) { + handled = false; #if defined(SOFTMMU_CODE_ACCESS) error_code = UC_ERR_FETCH_UNMAPPED; - if (uc->hook_mem_fetch_idx != 0 && ((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_fetch_idx].callback)( - uc, UC_MEM_FETCH_UNMAPPED, addr, DATA_SIZE, 0, - uc->hook_callbacks[uc->hook_mem_fetch_idx].user_data)) { + HOOK_FOREACH(uc, hook, UC_HOOK_MEM_FETCH_UNMAPPED) { + if (!HOOK_BOUND_CHECK(hook, addr)) + continue; + if ((handled = ((uc_cb_eventmem_t)hook->callback)(uc, UC_MEM_FETCH_UNMAPPED, addr, DATA_SIZE, 0, hook->user_data))) + break; + } #else error_code = UC_ERR_READ_UNMAPPED; - if (uc->hook_mem_read_idx != 0 && ((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_read_idx].callback)( - uc, UC_MEM_READ_UNMAPPED, addr, DATA_SIZE, 0, - uc->hook_callbacks[uc->hook_mem_read_idx].user_data)) { + HOOK_FOREACH(uc, hook, UC_HOOK_MEM_READ_UNMAPPED) { + if (!HOOK_BOUND_CHECK(hook, addr)) + continue; + if ((handled = ((uc_cb_eventmem_t)hook->callback)(uc, UC_MEM_READ_UNMAPPED, addr, DATA_SIZE, 0, hook->user_data))) + break; + } #endif + if (handled) { env->invalid_error = UC_ERR_OK; mr = memory_mapping(uc, addr); // FIXME: what if mr is still NULL at this time? } else { @@ -209,9 +219,15 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx, #if defined(SOFTMMU_CODE_ACCESS) // Unicorn: callback on fetch from NX if (mr != NULL && !(mr->perms & UC_PROT_EXEC)) { // non-executable - if (uc->hook_mem_fetch_prot_idx != 0 && ((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_fetch_prot_idx].callback)( - uc, UC_MEM_FETCH_PROT, addr, DATA_SIZE, 0, - uc->hook_callbacks[uc->hook_mem_fetch_prot_idx].user_data)) { + handled = false; + HOOK_FOREACH(uc, hook, UC_HOOK_MEM_FETCH_PROT) { + if (!HOOK_BOUND_CHECK(hook, addr)) + continue; + if ((handled = ((uc_cb_eventmem_t)hook->callback)(uc, UC_MEM_FETCH_PROT, addr, DATA_SIZE, 0, hook->user_data))) + break; + } + + if (handled) { env->invalid_error = UC_ERR_OK; } else { env->invalid_addr = addr; @@ -224,19 +240,25 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx, #endif // Unicorn: callback on memory read - if (READ_ACCESS_TYPE == MMU_DATA_LOAD && env->uc->hook_mem_read) { - struct hook_struct *trace = hook_find(env->uc, UC_HOOK_MEM_READ, addr); - if (trace) { - ((uc_cb_hookmem_t)trace->callback)(env->uc, UC_MEM_READ, - (uint64_t)addr, (int)DATA_SIZE, (int64_t)0, trace->user_data); + if (READ_ACCESS_TYPE == MMU_DATA_LOAD) { + HOOK_FOREACH(uc, hook, UC_HOOK_MEM_READ) { + if (!HOOK_BOUND_CHECK(hook, addr)) + continue; + ((uc_cb_hookmem_t)hook->callback)(env->uc, UC_MEM_READ, addr, DATA_SIZE, 0, hook->user_data); } } // Unicorn: callback on non-readable memory if (READ_ACCESS_TYPE == MMU_DATA_LOAD && mr != NULL && !(mr->perms & UC_PROT_READ)) { //non-readable - if (uc->hook_mem_read_prot_idx != 0 && ((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_read_prot_idx].callback)( - uc, UC_MEM_READ_PROT, addr, DATA_SIZE, 0, - uc->hook_callbacks[uc->hook_mem_read_prot_idx].user_data)) { + handled = false; + HOOK_FOREACH(uc, hook, UC_HOOK_MEM_READ_PROT) { + if (!HOOK_BOUND_CHECK(hook, addr)) + continue; + if ((handled = ((uc_cb_eventmem_t)hook->callback)(uc, UC_MEM_READ_PROT, addr, DATA_SIZE, 0, hook->user_data))) + break; + } + + if (handled) { env->invalid_error = UC_ERR_OK; } else { env->invalid_addr = addr; @@ -368,23 +390,33 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx, uintptr_t haddr; DATA_TYPE res; int error_code; + struct hook *hook; + bool handled; struct uc_struct *uc = env->uc; MemoryRegion *mr = memory_mapping(uc, addr); // memory can be unmapped while reading or fetching if (mr == NULL) { + handled = false; #if defined(SOFTMMU_CODE_ACCESS) error_code = UC_ERR_FETCH_UNMAPPED; - if (uc->hook_mem_fetch_idx != 0 && ((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_fetch_idx].callback)( - uc, UC_MEM_FETCH_UNMAPPED, addr, DATA_SIZE, 0, - uc->hook_callbacks[uc->hook_mem_fetch_idx].user_data)) { + HOOK_FOREACH(uc, hook, UC_HOOK_MEM_FETCH_UNMAPPED) { + if (!HOOK_BOUND_CHECK(hook, addr)) + continue; + if ((handled = ((uc_cb_eventmem_t)hook->callback)(uc, UC_MEM_FETCH_UNMAPPED, addr, DATA_SIZE, 0, hook->user_data))) + break; + } #else error_code = UC_ERR_READ_UNMAPPED; - if (uc->hook_mem_read_idx != 0 && ((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_read_idx].callback)( - uc, UC_MEM_READ_UNMAPPED, addr, DATA_SIZE, 0, - uc->hook_callbacks[uc->hook_mem_read_idx].user_data)) { + HOOK_FOREACH(uc, hook, UC_HOOK_MEM_READ_UNMAPPED) { + if (!HOOK_BOUND_CHECK(hook, addr)) + continue; + if ((handled = ((uc_cb_eventmem_t)hook->callback)(uc, UC_MEM_READ_UNMAPPED, addr, DATA_SIZE, 0, hook->user_data))) + break; + } #endif + if (handled) { env->invalid_error = UC_ERR_OK; mr = memory_mapping(uc, addr); // FIXME: what if mr is still NULL at this time? } else { @@ -399,9 +431,15 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx, #if defined(SOFTMMU_CODE_ACCESS) // Unicorn: callback on fetch from NX if (mr != NULL && !(mr->perms & UC_PROT_EXEC)) { // non-executable - if (uc->hook_mem_fetch_prot_idx != 0 && ((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_fetch_prot_idx].callback)( - uc, UC_MEM_FETCH_PROT, addr, DATA_SIZE, 0, - uc->hook_callbacks[uc->hook_mem_fetch_prot_idx].user_data)) { + handled = false; + HOOK_FOREACH(uc, hook, UC_HOOK_MEM_FETCH_PROT) { + if (!HOOK_BOUND_CHECK(hook, addr)) + continue; + if ((handled = ((uc_cb_eventmem_t)hook->callback)(uc, UC_MEM_FETCH_PROT, addr, DATA_SIZE, 0, hook->user_data))) + break; + } + + if (handled) { env->invalid_error = UC_ERR_OK; } else { env->invalid_addr = addr; @@ -414,19 +452,25 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx, #endif // Unicorn: callback on memory read - if (READ_ACCESS_TYPE == MMU_DATA_LOAD && env->uc->hook_mem_read) { - struct hook_struct *trace = hook_find(env->uc, UC_HOOK_MEM_READ, addr); - if (trace) { - ((uc_cb_hookmem_t)trace->callback)(env->uc, UC_MEM_READ, - (uint64_t)addr, (int)DATA_SIZE, (int64_t)0, trace->user_data); + if (READ_ACCESS_TYPE == MMU_DATA_LOAD) { + HOOK_FOREACH(uc, hook, UC_HOOK_MEM_READ) { + if (!HOOK_BOUND_CHECK(hook, addr)) + continue; + ((uc_cb_hookmem_t)hook->callback)(env->uc, UC_MEM_READ, addr, DATA_SIZE, 0, hook->user_data); } } // Unicorn: callback on non-readable memory if (READ_ACCESS_TYPE == MMU_DATA_LOAD && mr != NULL && !(mr->perms & UC_PROT_READ)) { //non-readable - if (uc->hook_mem_read_prot_idx != 0 && ((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_read_prot_idx].callback)( - uc, UC_MEM_READ_PROT, addr, DATA_SIZE, 0, - uc->hook_callbacks[uc->hook_mem_read_prot_idx].user_data)) { + handled = false; + HOOK_FOREACH(uc, hook, UC_HOOK_MEM_READ_PROT) { + if (!HOOK_BOUND_CHECK(hook, addr)) + continue; + if ((handled = ((uc_cb_eventmem_t)hook->callback)(uc, UC_MEM_READ_PROT, addr, DATA_SIZE, 0, hook->user_data))) + break; + } + + if (handled) { env->invalid_error = UC_ERR_OK; } else { env->invalid_addr = addr; @@ -595,24 +639,30 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); target_ulong tlb_addr = env->tlb_table[mmu_idx][index].addr_write; uintptr_t haddr; + struct hook *hook; + bool handled; struct uc_struct *uc = env->uc; MemoryRegion *mr = memory_mapping(uc, addr); // Unicorn: callback on memory write - if (uc->hook_mem_write) { - struct hook_struct *trace = hook_find(uc, UC_HOOK_MEM_WRITE, addr); - if (trace) { - ((uc_cb_hookmem_t)trace->callback)(uc, UC_MEM_WRITE, - (uint64_t)addr, (int)DATA_SIZE, (int64_t)val, trace->user_data); - } + HOOK_FOREACH(uc, hook, UC_HOOK_MEM_WRITE) { + if (!HOOK_BOUND_CHECK(hook, addr)) + continue; + ((uc_cb_hookmem_t)hook->callback)(uc, UC_MEM_WRITE, addr, DATA_SIZE, val, hook->user_data); } // Unicorn: callback on invalid memory - if (uc->hook_mem_write_idx && mr == NULL) { - if (!((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_write_idx].callback)( - uc, UC_MEM_WRITE_UNMAPPED, addr, DATA_SIZE, (int64_t)val, - uc->hook_callbacks[uc->hook_mem_write_idx].user_data)) { + if (mr == NULL) { + handled = false; + HOOK_FOREACH(uc, hook, UC_HOOK_MEM_WRITE_UNMAPPED) { + if (!HOOK_BOUND_CHECK(hook, addr)) + continue; + if ((handled = ((uc_cb_eventmem_t)hook->callback)(uc, UC_MEM_WRITE_UNMAPPED, addr, DATA_SIZE, val, hook->user_data))) + break; + } + + if (!handled) { // save error & quit env->invalid_addr = addr; env->invalid_error = UC_ERR_WRITE_UNMAPPED; @@ -627,12 +677,17 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, // Unicorn: callback on non-writable memory if (mr != NULL && !(mr->perms & UC_PROT_WRITE)) { //non-writable - if (uc->hook_mem_write_prot_idx != 0 && ((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_write_prot_idx].callback)( - uc, UC_MEM_WRITE_PROT, addr, DATA_SIZE, (int64_t)val, - uc->hook_callbacks[uc->hook_mem_write_prot_idx].user_data)) { - env->invalid_error = UC_ERR_OK; + handled = false; + HOOK_FOREACH(uc, hook, UC_HOOK_MEM_WRITE_PROT) { + if (!HOOK_BOUND_CHECK(hook, addr)) + continue; + if ((handled = ((uc_cb_eventmem_t)hook->callback)(uc, UC_MEM_WRITE_PROT, addr, DATA_SIZE, val, hook->user_data))) + break; } - else { + + if (handled) { + env->invalid_error = UC_ERR_OK; + } else { env->invalid_addr = addr; env->invalid_error = UC_ERR_WRITE_PROT; // printf("***** Invalid memory write (ro) at " TARGET_FMT_lx "\n", addr); @@ -742,24 +797,30 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); target_ulong tlb_addr = env->tlb_table[mmu_idx][index].addr_write; uintptr_t haddr; + struct hook *hook; + bool handled; struct uc_struct *uc = env->uc; MemoryRegion *mr = memory_mapping(uc, addr); // Unicorn: callback on memory write - if (uc->hook_mem_write) { - struct hook_struct *trace = hook_find(uc, UC_HOOK_MEM_WRITE, addr); - if (trace) { - ((uc_cb_hookmem_t)trace->callback)(uc, UC_MEM_WRITE, - (uint64_t)addr, (int)DATA_SIZE, (int64_t)val, trace->user_data); - } + HOOK_FOREACH(uc, hook, UC_HOOK_MEM_WRITE) { + if (!HOOK_BOUND_CHECK(hook, addr)) + continue; + ((uc_cb_hookmem_t)hook->callback)(uc, UC_MEM_WRITE, addr, DATA_SIZE, val, hook->user_data); } // Unicorn: callback on invalid memory - if (uc->hook_mem_write_idx && mr == NULL) { - if (!((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_write_idx].callback)( - uc, UC_MEM_WRITE_UNMAPPED, addr, DATA_SIZE, (int64_t)val, - uc->hook_callbacks[uc->hook_mem_write_idx].user_data)) { + if (mr == NULL) { + handled = false; + HOOK_FOREACH(uc, hook, UC_HOOK_MEM_WRITE_UNMAPPED) { + if (!HOOK_BOUND_CHECK(hook, addr)) + continue; + if ((handled = ((uc_cb_eventmem_t)hook->callback)(uc, UC_MEM_WRITE_UNMAPPED, addr, DATA_SIZE, val, hook->user_data))) + break; + } + + if (!handled) { // save error & quit env->invalid_addr = addr; env->invalid_error = UC_ERR_WRITE_UNMAPPED; @@ -774,12 +835,17 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, // Unicorn: callback on non-writable memory if (mr != NULL && !(mr->perms & UC_PROT_WRITE)) { //non-writable - if (uc->hook_mem_write_prot_idx != 0 && ((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_write_prot_idx].callback)( - uc, UC_MEM_WRITE_PROT, addr, DATA_SIZE, (int64_t)val, - uc->hook_callbacks[uc->hook_mem_write_prot_idx].user_data)) { - env->invalid_error = UC_ERR_OK; + handled = false; + HOOK_FOREACH(uc, hook, UC_HOOK_MEM_WRITE_PROT) { + if (!HOOK_BOUND_CHECK(hook, addr)) + continue; + if ((handled = ((uc_cb_eventmem_t)hook->callback)(uc, UC_MEM_WRITE_PROT, addr, DATA_SIZE, val, hook->user_data))) + break; } - else { + + if (handled) { + env->invalid_error = UC_ERR_OK; + } else { env->invalid_addr = addr; env->invalid_error = UC_ERR_WRITE_PROT; // printf("***** Invalid memory write (ro) at " TARGET_FMT_lx "\n", addr); diff --git a/qemu/target-arm/helper.h b/qemu/target-arm/helper.h index d2de58d..6427c18 100644 --- a/qemu/target-arm/helper.h +++ b/qemu/target-arm/helper.h @@ -1,4 +1,4 @@ -DEF_HELPER_5(uc_tracecode, void, i32, ptr, ptr, i64, ptr) +DEF_HELPER_4(uc_tracecode, void, i32, i32, ptr, i64) DEF_HELPER_FLAGS_1(clz_arm, TCG_CALL_NO_RWG_SE, i32, i32) diff --git a/qemu/target-arm/translate-a64.c b/qemu/target-arm/translate-a64.c index c76de8e..e869da0 100644 --- a/qemu/target-arm/translate-a64.c +++ b/qemu/target-arm/translate-a64.c @@ -10984,10 +10984,8 @@ static void disas_a64_insn(CPUARMState *env, DisasContext *s) s->pc += 4; // Unicorn: trace this instruction on request - if (env->uc->hook_insn) { - struct hook_struct *trace = hook_find(s->uc, UC_HOOK_CODE, s->pc - 4); - if (trace) - gen_uc_tracecode(tcg_ctx, 4, trace->callback, env->uc, s->pc - 4, trace->user_data); + if (HOOK_EXISTS_BOUNDED(env->uc, UC_HOOK_CODE, s->pc - 4)) { + gen_uc_tracecode(tcg_ctx, 4, UC_HOOK_CODE_IDX, env->uc, s->pc - 4); // the callback might want to stop emulation immediately check_exit_request(tcg_ctx); } @@ -11114,13 +11112,10 @@ void gen_intermediate_code_internal_a64(ARMCPU *cpu, // Unicorn: trace this block on request // Only hook this block if it is not broken from previous translation due to // full translation cache - if (env->uc->hook_block && !env->uc->block_full) { - struct hook_struct *trace = hook_find(env->uc, UC_HOOK_BLOCK, pc_start); - if (trace) { - // save block address to see if we need to patch block size later - env->uc->block_addr = pc_start; - gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, trace->callback, env->uc, pc_start, trace->user_data); - } + if (!env->uc->block_full && HOOK_EXISTS_BOUNDED(env->uc, UC_HOOK_BLOCK, pc_start)) { + // save block address to see if we need to patch block size later + env->uc->block_addr = pc_start; + gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, UC_HOOK_BLOCK_IDX, env->uc, pc_start); } gen_tb_start(tcg_ctx); diff --git a/qemu/target-arm/translate.c b/qemu/target-arm/translate.c index 3560e6e..d097c23 100644 --- a/qemu/target-arm/translate.c +++ b/qemu/target-arm/translate.c @@ -7687,10 +7687,8 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) // qq } // Unicorn: trace this instruction on request - if (s->uc->hook_insn) { - struct hook_struct *trace = hook_find(s->uc, UC_HOOK_CODE, s->pc - 4); - if (trace) - gen_uc_tracecode(tcg_ctx, 4, trace->callback, s->uc, s->pc - 4, trace->user_data); + if (HOOK_EXISTS_BOUNDED(s->uc, UC_HOOK_CODE, s->pc - 4)) { + gen_uc_tracecode(tcg_ctx, 4, UC_HOOK_CODE_IDX, s->uc, s->pc - 4); // the callback might want to stop emulation immediately check_exit_request(tcg_ctx); } @@ -10408,15 +10406,10 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s) // qq } // Unicorn: trace this instruction on request - if (env->uc->hook_insn) { - struct hook_struct *trace = hook_find(s->uc, UC_HOOK_CODE, s->pc); - if (trace) - gen_uc_tracecode(tcg_ctx, 2, trace->callback, env->uc, s->pc, trace->user_data); - // if requested to emulate only some instructions, check to see - // if we need to exit immediately - if (env->uc->emu_count > 0) { - check_exit_request(tcg_ctx); - } + if (HOOK_EXISTS_BOUNDED(s->uc, UC_HOOK_CODE, s->pc)) { + gen_uc_tracecode(tcg_ctx, 2, UC_HOOK_CODE_IDX, s->uc, s->pc); + // the callback might want to stop emulation immediately + check_exit_request(tcg_ctx); } insn = arm_lduw_code(env, s->pc, s->bswap_code); @@ -11237,13 +11230,10 @@ static inline void gen_intermediate_code_internal(ARMCPU *cpu, // Unicorn: trace this block on request // Only hook this block if it is not broken from previous translation due to // full translation cache - if (env->uc->hook_block && !env->uc->block_full) { - struct hook_struct *trace = hook_find(env->uc, UC_HOOK_BLOCK, pc_start); - if (trace) { - // save block address to see if we need to patch block size later - env->uc->block_addr = pc_start; - gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, trace->callback, env->uc, pc_start, trace->user_data); - } + if (!env->uc->block_full && HOOK_EXISTS_BOUNDED(env->uc, UC_HOOK_BLOCK, pc_start)) { + // save block address to see if we need to patch block size later + env->uc->block_addr = pc_start; + gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, UC_HOOK_BLOCK_IDX, env->uc, pc_start); } gen_tb_start(tcg_ctx); diff --git a/qemu/target-i386/helper.h b/qemu/target-i386/helper.h index 8ab196a..d3b52d1 100644 --- a/qemu/target-i386/helper.h +++ b/qemu/target-i386/helper.h @@ -1,4 +1,4 @@ -DEF_HELPER_5(uc_tracecode, void, i32, ptr, ptr, i64, ptr) +DEF_HELPER_4(uc_tracecode, void, i32, i32, ptr, i64) DEF_HELPER_FLAGS_4(cc_compute_all, TCG_CALL_NO_RWG_SE, tl, tl, tl, tl, int) DEF_HELPER_FLAGS_4(cc_compute_c, TCG_CALL_NO_RWG_SE, tl, tl, tl, tl, int) diff --git a/qemu/target-i386/seg_helper.c b/qemu/target-i386/seg_helper.c index 42b0f37..4702dfc 100644 --- a/qemu/target-i386/seg_helper.c +++ b/qemu/target-i386/seg_helper.c @@ -945,14 +945,16 @@ void helper_syscall(CPUX86State *env, int next_eip_addend) #else void helper_syscall(CPUX86State *env, int next_eip_addend) { - // Unicorn: call interrupt callback if registered - struct uc_struct *uc = env->uc; - if (uc->hook_syscall_idx) { - ((uc_cb_insn_syscall_t)uc->hook_callbacks[uc->hook_syscall_idx].callback)( - uc, uc->hook_callbacks[uc->hook_syscall_idx].user_data); + // Unicorn: call registered syscall hooks + struct hook *hook; + HOOK_FOREACH(env->uc, hook, UC_HOOK_INSN) { + if (!HOOK_BOUND_CHECK(hook, env->eip)) + continue; + if (hook->insn == UC_X86_INS_SYSCALL) + ((uc_cb_insn_syscall_t)hook->callback)(env->uc, hook->user_data); } - env->eip += next_eip_addend; + env->eip += next_eip_addend; return; int selector; @@ -2303,14 +2305,16 @@ void helper_lret_protected(CPUX86State *env, int shift, int addend) void helper_sysenter(CPUX86State *env, int next_eip_addend) { - // Unicorn: call interrupt callback if registered - struct uc_struct *uc = env->uc; - if (uc->hook_syscall_idx) { - ((uc_cb_insn_syscall_t)uc->hook_callbacks[uc->hook_syscall_idx].callback)( - uc, uc->hook_callbacks[uc->hook_syscall_idx].user_data); + // Unicorn: call registered SYSENTER hooks + struct hook *hook; + HOOK_FOREACH(env->uc, hook, UC_HOOK_INSN) { + if (!HOOK_BOUND_CHECK(hook, env->eip)) + continue; + if (hook->insn == UC_X86_INS_SYSENTER) + ((uc_cb_insn_syscall_t)hook->callback)(env->uc, hook->user_data); } - env->eip += next_eip_addend; + env->eip += next_eip_addend; return; if (env->sysenter_cs == 0) { diff --git a/qemu/target-i386/translate.c b/qemu/target-i386/translate.c index 285ba32..d68cb51 100644 --- a/qemu/target-i386/translate.c +++ b/qemu/target-i386/translate.c @@ -516,14 +516,14 @@ static inline void gen_op_addq_A0_reg_sN(TCGContext *s, int shift, int reg) static inline void gen_op_ld_v(DisasContext *s, int idx, TCGv t0, TCGv a0) { - if (s->uc->hook_mem_read) + if (HOOK_EXISTS(s->uc, UC_HOOK_MEM_READ)) gen_jmp_im(s, s->prev_pc); // Unicorn: sync EIP tcg_gen_qemu_ld_tl(s->uc, t0, a0, s->mem_index, idx | MO_LE); } static inline void gen_op_st_v(DisasContext *s, int idx, TCGv t0, TCGv a0) { - if (s->uc->hook_mem_write) + if (HOOK_EXISTS(s->uc, UC_HOOK_MEM_WRITE)) gen_jmp_im(s, s->prev_pc); // Unicorn: sync EIP tcg_gen_qemu_st_tl(s->uc, t0, a0, s->mem_index, idx | MO_LE); } @@ -4745,12 +4745,10 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, TCGv cpu_tmp4 = *(TCGv *)tcg_ctx->cpu_tmp4; TCGv **cpu_T = (TCGv **)tcg_ctx->cpu_T; TCGv **cpu_regs = (TCGv **)tcg_ctx->cpu_regs; - struct hook_struct *trace = NULL; TCGArg *save_opparam_ptr = tcg_ctx->gen_opparam_ptr; bool cc_op_dirty = s->cc_op_dirty; bool changed_cc_op = false; - s->pc = pc_start; // end address tells us to stop emulation @@ -4768,19 +4766,15 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, } // Unicorn: trace this instruction on request - if (env->uc->hook_insn) { - trace = hook_find(env->uc, UC_HOOK_CODE, pc_start); - if (trace) { - if (s->last_cc_op != s->cc_op) { - sync_eflags(s, tcg_ctx); - s->last_cc_op = s->cc_op; - changed_cc_op = true; - } - // generate code to call callback - gen_uc_tracecode(tcg_ctx, 0xf1f1f1f1, trace->callback, env->uc, pc_start, trace->user_data); - // the callback might want to stop emulation immediately - check_exit_request(tcg_ctx); + if (HOOK_EXISTS_BOUNDED(env->uc, UC_HOOK_CODE, pc_start)) { + if (s->last_cc_op != s->cc_op) { + sync_eflags(s, tcg_ctx); + s->last_cc_op = s->cc_op; + changed_cc_op = true; } + gen_uc_tracecode(tcg_ctx, 0xf1f1f1f1, UC_HOOK_CODE_IDX, env->uc, pc_start); + // the callback might want to stop emulation immediately + check_exit_request(tcg_ctx); } prefixes = 0; @@ -8173,7 +8167,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, gen_helper_unlock(tcg_ctx, cpu_env); // Unicorn: patch the callback for the instruction size - if (trace) { + if (HOOK_EXISTS_BOUNDED(env->uc, UC_HOOK_CODE, pc_start)) { // int i; // for(i = 0; i < 20; i++) // printf("=== [%u] = %x\n", i, *(save_opparam_ptr + i)); @@ -8387,12 +8381,9 @@ static inline void gen_intermediate_code_internal(uint8_t *gen_opc_cc_op, // Unicorn: trace this block on request // Only hook this block if it is not broken from previous translation due to // full translation cache - if (env->uc->hook_block && !env->uc->block_full) { - struct hook_struct *trace = hook_find(env->uc, UC_HOOK_BLOCK, pc_start); - if (trace) { - env->uc->block_addr = pc_start; - gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, trace->callback, env->uc, pc_start, trace->user_data); - } + if (!env->uc->block_full && HOOK_EXISTS_BOUNDED(env->uc, UC_HOOK_BLOCK, pc_start)) { + env->uc->block_addr = pc_start; + gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, UC_HOOK_BLOCK_IDX, env->uc, pc_start); } gen_tb_start(tcg_ctx); diff --git a/qemu/target-m68k/helper.h b/qemu/target-m68k/helper.h index 865cf95..caaadb3 100644 --- a/qemu/target-m68k/helper.h +++ b/qemu/target-m68k/helper.h @@ -1,4 +1,4 @@ -DEF_HELPER_5(uc_tracecode, void, i32, ptr, ptr, i64, ptr) +DEF_HELPER_4(uc_tracecode, void, i32, i32, ptr, i64) DEF_HELPER_1(bitrev, i32, i32) DEF_HELPER_1(ff1, i32, i32) diff --git a/qemu/target-m68k/translate.c b/qemu/target-m68k/translate.c index 8c2951d..bf45a66 100644 --- a/qemu/target-m68k/translate.c +++ b/qemu/target-m68k/translate.c @@ -3043,11 +3043,8 @@ static void disas_m68k_insn(CPUM68KState * env, DisasContext *s) } // Unicorn: trace this instruction on request - if (env->uc->hook_insn) { - struct hook_struct *trace = hook_find(env->uc, UC_HOOK_CODE, s->pc); - if (trace) - gen_uc_tracecode(tcg_ctx, 2, trace->callback, env->uc, s->pc, trace->user_data); - + if (HOOK_EXISTS_BOUNDED(env->uc, UC_HOOK_CODE, s->pc)) { + gen_uc_tracecode(tcg_ctx, 2, UC_HOOK_CODE_IDX, env->uc, s->pc); // the callback might want to stop emulation immediately check_exit_request(tcg_ctx); } @@ -3109,13 +3106,10 @@ gen_intermediate_code_internal(M68kCPU *cpu, TranslationBlock *tb, // Unicorn: trace this block on request // Only hook this block if it is not broken from previous translation due to // full translation cache - if (env->uc->hook_block && !env->uc->block_full) { - struct hook_struct *trace = hook_find(env->uc, UC_HOOK_BLOCK, pc_start); - if (trace) { - // save block address to see if we need to patch block size later - env->uc->block_addr = pc_start; - gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, trace->callback, env->uc, pc_start, trace->user_data); - } + if (!env->uc->block_full && HOOK_EXISTS_BOUNDED(env->uc, UC_HOOK_BLOCK, pc_start)) { + // save block address to see if we need to patch block size later + env->uc->block_addr = pc_start; + gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, UC_HOOK_BLOCK_IDX, env->uc, pc_start); } gen_tb_start(tcg_ctx); diff --git a/qemu/target-mips/helper.h b/qemu/target-mips/helper.h index 81f733c..1924bf6 100644 --- a/qemu/target-mips/helper.h +++ b/qemu/target-mips/helper.h @@ -1,4 +1,4 @@ -DEF_HELPER_5(uc_tracecode, void, i32, ptr, ptr, i64, ptr) +DEF_HELPER_4(uc_tracecode, void, i32, i32, ptr, i64) DEF_HELPER_3(raise_exception_err, noreturn, env, i32, int) DEF_HELPER_2(raise_exception, noreturn, env, i32) diff --git a/qemu/target-mips/translate.c b/qemu/target-mips/translate.c index 7546591..ea9aa45 100644 --- a/qemu/target-mips/translate.c +++ b/qemu/target-mips/translate.c @@ -11343,12 +11343,9 @@ static int decode_mips16_opc (CPUMIPSState *env, DisasContext *ctx, bool *insn_n n_bytes = 2; // Unicorn: trace this instruction on request - if (env->uc->hook_insn) { - struct hook_struct *trace = hook_find(env->uc, UC_HOOK_CODE, ctx->pc); - if (trace) { - gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, trace->callback, env->uc, ctx->pc, trace->user_data); - *insn_need_patch = true; - } + if (HOOK_EXISTS_BOUNDED(env->uc, UC_HOOK_CODE, ctx->pc)) { + gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, UC_HOOK_CODE_IDX, env->uc, ctx->pc); + *insn_need_patch = true; // the callback might want to stop emulation immediately check_exit_request(tcg_ctx); } @@ -13942,12 +13939,9 @@ static int decode_micromips_opc (CPUMIPSState *env, DisasContext *ctx, bool *ins } // Unicorn: trace this instruction on request - if (env->uc->hook_insn) { - struct hook_struct *trace = hook_find(env->uc, UC_HOOK_CODE, ctx->pc); - if (trace) { - gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, trace->callback, env->uc, ctx->pc, trace->user_data); - *insn_need_patch = true; - } + if (HOOK_EXISTS_BOUNDED(env->uc, UC_HOOK_CODE, ctx->pc)) { + gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, UC_HOOK_CODE_IDX, env->uc, ctx->pc); + *insn_need_patch = true; // the callback might want to stop emulation immediately check_exit_request(tcg_ctx); } @@ -18504,13 +18498,10 @@ static void gen_msa(CPUMIPSState *env, DisasContext *ctx) // Unicorn: trace this instruction on request static void hook_insn(CPUMIPSState *env, DisasContext *ctx, bool *insn_need_patch, int *insn_patch_offset, int offset_value) { - if (env->uc->hook_insn) { - TCGContext *tcg_ctx = ctx->uc->tcg_ctx; - struct hook_struct *trace = hook_find(env->uc, UC_HOOK_CODE, ctx->pc); - if (trace) { - gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, trace->callback, env->uc, ctx->pc, trace->user_data); - *insn_need_patch = true; - } + TCGContext *tcg_ctx = ctx->uc->tcg_ctx; + if (HOOK_EXISTS_BOUNDED(env->uc, UC_HOOK_CODE, ctx->pc)) { + gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, UC_HOOK_CODE_IDX, env->uc, ctx->pc); + *insn_need_patch = true; // the callback might want to stop emulation immediately check_exit_request(tcg_ctx); *insn_patch_offset = offset_value; @@ -19223,13 +19214,10 @@ gen_intermediate_code_internal(MIPSCPU *cpu, TranslationBlock *tb, // Unicorn: trace this block on request // Only hook this block if it is not broken from previous translation due to // full translation cache - if (env->uc->hook_block && !env->uc->block_full) { - struct hook_struct *trace = hook_find(env->uc, UC_HOOK_BLOCK, pc_start); - if (trace) { - // save block address to see if we need to patch block size later - env->uc->block_addr = pc_start; - gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, trace->callback, env->uc, pc_start, trace->user_data); - } + if (!env->uc->block_full && HOOK_EXISTS_BOUNDED(env->uc, UC_HOOK_BLOCK, pc_start)) { + // save block address to see if we need to patch block size later + env->uc->block_addr = pc_start; + gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, UC_HOOK_BLOCK_IDX, env->uc, pc_start); } gen_tb_start(tcg_ctx); @@ -19275,7 +19263,7 @@ gen_intermediate_code_internal(MIPSCPU *cpu, TranslationBlock *tb, int insn_patch_offset = 1; // Unicorn: save param buffer - if (env->uc->hook_insn) + if (HOOK_EXISTS(env->uc, UC_HOOK_CODE)) save_opparam_ptr = tcg_ctx->gen_opparam_ptr; is_slot = ctx.hflags & MIPS_HFLAG_BMASK; diff --git a/qemu/target-sparc/helper.h b/qemu/target-sparc/helper.h index 2557dd0..503e1e5 100644 --- a/qemu/target-sparc/helper.h +++ b/qemu/target-sparc/helper.h @@ -1,4 +1,4 @@ -DEF_HELPER_5(uc_tracecode, void, i32, ptr, ptr, i64, ptr) +DEF_HELPER_4(uc_tracecode, void, i32, i32, ptr, i64) DEF_HELPER_1(power_down, void, env) #ifndef TARGET_SPARC64 diff --git a/qemu/target-sparc/translate.c b/qemu/target-sparc/translate.c index fc71e88..a7f067c 100644 --- a/qemu/target-sparc/translate.c +++ b/qemu/target-sparc/translate.c @@ -2637,11 +2637,8 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn, bool hook_ins } // Unicorn: trace this instruction on request - if (hook_insn && dc->uc->hook_insn) { - struct hook_struct *trace = hook_find(dc->uc, UC_HOOK_CODE, dc->pc); - if (trace) - gen_uc_tracecode(tcg_ctx, 4, trace->callback, dc->uc, dc->pc, trace->user_data); - + if (hook_insn && HOOK_EXISTS_BOUNDED(dc->uc, UC_HOOK_CODE, dc->pc)) { + gen_uc_tracecode(tcg_ctx, 4, UC_HOOK_CODE_IDX, dc->uc, dc->pc); // the callback might want to stop emulation immediately check_exit_request(tcg_ctx); } @@ -5428,13 +5425,10 @@ static inline void gen_intermediate_code_internal(SPARCCPU *cpu, // Unicorn: trace this block on request // Only hook this block if it is not broken from previous translation due to // full translation cache - if (env->uc->hook_block && !env->uc->block_full) { - struct hook_struct *trace = hook_find(env->uc, UC_HOOK_BLOCK, pc_start); - if (trace) { - // save block address to see if we need to patch block size later - env->uc->block_addr = pc_start; - gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, trace->callback, env->uc, pc_start, trace->user_data); - } + if (!env->uc->block_full && HOOK_EXISTS_BOUNDED(env->uc, UC_HOOK_BLOCK, pc_start)) { + // save block address to see if we need to patch block size later + env->uc->block_addr = pc_start; + gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, UC_HOOK_BLOCK_IDX, env->uc, pc_start); } gen_tb_start(tcg_ctx); diff --git a/qemu/tcg/i386/tcg-target.c b/qemu/tcg/i386/tcg-target.c index aa06cc1..414c4a7 100644 --- a/qemu/tcg/i386/tcg-target.c +++ b/qemu/tcg/i386/tcg-target.c @@ -1209,7 +1209,7 @@ static inline void tcg_out_tlb_load(TCGContext *s, TCGReg addrlo, TCGReg addrhi, tcg_out_mov(s, ttype, r1, addrlo); // Unicorn: fast path if hookmem is not enable - if (!s->uc->hook_mem_read && !s->uc->hook_mem_write) + if (!HOOK_EXISTS(s->uc, UC_HOOK_MEM_READ) && !HOOK_EXISTS(s->uc, UC_HOOK_MEM_WRITE)) tcg_out_opc(s, OPC_JCC_long + JCC_JNE, 0, 0, 0); else tcg_out_opc(s, OPC_JMP_long, 0, 0, 0); /* slow_path */ diff --git a/qemu/tcg/tcg-op.h b/qemu/tcg/tcg-op.h index 9f096a3..8735824 100644 --- a/qemu/tcg/tcg-op.h +++ b/qemu/tcg/tcg-op.h @@ -27,14 +27,13 @@ int gen_new_label(TCGContext *); -static inline void gen_uc_tracecode(TCGContext *tcg_ctx, int32_t size, void *callback, void *uc, uint64_t pc, void *data) +static inline void gen_uc_tracecode(TCGContext *tcg_ctx, int32_t size, int32_t type, void *uc, uint64_t pc) { TCGv_i32 tsize = tcg_const_i32(tcg_ctx, size); - TCGv_ptr tcallback = tcg_const_ptr(tcg_ctx, callback); + TCGv_i32 ttype = tcg_const_i32(tcg_ctx, type); TCGv_ptr tuc = tcg_const_ptr(tcg_ctx, uc); TCGv_i64 tpc = tcg_const_i64(tcg_ctx, pc); - TCGv_ptr tdata = tcg_const_ptr(tcg_ctx, data); - gen_helper_uc_tracecode(tcg_ctx, tsize, tcallback, tuc, tpc, tdata); + gen_helper_uc_tracecode(tcg_ctx, tsize, ttype, tuc, tpc); } static inline void tcg_gen_op0(TCGContext *s, TCGOpcode opc) diff --git a/qemu/translate-all.c b/qemu/translate-all.c index 11bbb07..aec38c7 100644 --- a/qemu/translate-all.c +++ b/qemu/translate-all.c @@ -179,7 +179,7 @@ static int cpu_gen_code(CPUArchState *env, TranslationBlock *tb, int *gen_code_s gen_intermediate_code(env, tb); // Unicorn: when tracing block, patch 1st operand for block size - if (env->uc->hook_block && env->uc->block_addr == tb->pc) { + if (env->uc->block_addr == tb->pc && HOOK_EXISTS_BOUNDED(env->uc, UC_HOOK_BLOCK, tb->pc)) { if (env->uc->block_full) // block size is unknown *(s->gen_opparam_buf + 1) = 0; else diff --git a/samples/mem_apis.c b/samples/mem_apis.c index 50554ee..008f077 100644 --- a/samples/mem_apis.c +++ b/samples/mem_apis.c @@ -142,13 +142,13 @@ static void do_nx_demo(bool cause_fault) /* bits 32 - page0: + page0: @0 times 4091 inc eax jmp page2 - page1: - times 4095 inc eax + page1: @1000 + times 4095 inc eax (or INC ECX) hlt - page2: + page2: @2000 jmp page1 */ memset(code_buf, 0x40, sizeof(code_buf)); // fill with inc eax @@ -170,7 +170,7 @@ static void do_nx_demo(bool cause_fault) // intercept code and invalid memory events if (uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)1, (uint64_t)0) != UC_ERR_OK || uc_hook_add(uc, &trace1, UC_HOOK_MEM_INVALID, - hook_mem_invalid, NULL) != UC_ERR_OK) { + hook_mem_invalid, NULL, (uint64_t)1, (uint64_t)0) != UC_ERR_OK) { printf("not ok - Failed to install hooks\n"); return; } @@ -251,7 +251,7 @@ static void do_perms_demo(bool change_perms) if (uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)1, (uint64_t)0) != UC_ERR_OK || uc_hook_add(uc, &trace1, UC_HOOK_MEM_INVALID, - hook_mem_invalid, NULL) != UC_ERR_OK) { + hook_mem_invalid, NULL, (uint64_t)1, (uint64_t)0) != UC_ERR_OK) { printf("not ok - Failed to install hooks\n"); return; } @@ -329,7 +329,7 @@ static void do_unmap_demo(bool do_unmap) if (uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)1, (uint64_t)0) != UC_ERR_OK || uc_hook_add(uc, &trace1, UC_HOOK_MEM_INVALID, - hook_mem_invalid, NULL) != UC_ERR_OK) { + hook_mem_invalid, NULL, (uint64_t)1, (uint64_t)0) != UC_ERR_OK) { printf("not ok - Failed to install hooks\n"); return; } diff --git a/tests/unit/Makefile b/tests/unit/Makefile index b261da4..cd0e46e 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_tb_x86 test_multihook .PHONY: all all: ${ALL_TESTS} @@ -23,6 +23,7 @@ test: ${ALL_TESTS} ./test_mem_map_ptr ./test_mem_high ./test_tb_x86 + ./test_multihook test_sanity: test_sanity.c test_x86: test_x86.c @@ -30,6 +31,7 @@ test_mem_map: test_mem_map.c 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 ${ALL_TESTS}: ${CC} ${CFLAGS} -o $@ $^ diff --git a/tests/unit/test_multihook.c b/tests/unit/test_multihook.c new file mode 100644 index 0000000..eb83186 --- /dev/null +++ b/tests/unit/test_multihook.c @@ -0,0 +1,111 @@ +#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; +} + +/******************************************************************************/ + +struct bb { + uint64_t addr; + size_t size; +}; + +struct bbtest { + const struct bb *blocks; + unsigned int blocknum; +}; + + +static void test_basic_blocks_hook(uc_engine *uc, uint64_t address, uint32_t size, void *user_data) +{ + struct bbtest *bbtest = user_data; + const struct bb *bb = &bbtest->blocks[bbtest->blocknum]; + + printf("block hook 1: %d == %zu\n", size, bb->size); + assert_int_equal(address, bb->addr); + assert_int_equal((size_t)size, bb->size); +} + +static void test_basic_blocks_hook2(uc_engine *uc, uint64_t address, uint32_t size, void *user_data) +{ + struct bbtest *bbtest = user_data; + const struct bb *bb = &bbtest->blocks[bbtest->blocknum++]; + + printf("block hook 2: %d == %zu\n", size, bb->size); + assert_int_equal(address, bb->addr); + assert_int_equal((size_t)size, bb->size); +} + +static void test_basic_blocks(void **state) +{ + uc_engine *uc = *state; + uc_hook trace1, trace2; + +#define BASEADDR 0x1000000 + + uint64_t address = BASEADDR; + const uint8_t code[] = { + 0x33, 0xC0, // xor eax, eax + 0x90, // nop + 0x90, // nop + 0xEB, 0x00, // jmp $+2 + 0x90, // nop + 0x90, // nop + 0x90, // nop + }; + + static const struct bb blocks[] = { + {BASEADDR, 6}, + {BASEADDR+ 6, 3}, + }; + + struct bbtest bbtest = { + .blocks = blocks, + .blocknum = 0, + }; + + +#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))); + + // trace all basic blocks + OK(uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, test_basic_blocks_hook, &bbtest, (uint64_t)1, (uint64_t)0)); + OK(uc_hook_add(uc, &trace2, UC_HOOK_BLOCK, test_basic_blocks_hook2, &bbtest, (uint64_t)1, (uint64_t)0)); + + OK(uc_emu_start(uc, address, address+sizeof(code), 0, 0)); +} + +int main(void) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(test_basic_blocks, setup32, teardown), + }; + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/uc.c b/uc.c index 06c15f7..e66b9f8 100644 --- a/uc.c +++ b/uc.c @@ -20,7 +20,6 @@ #endif #include "uc_priv.h" -#include "hook.h" // target specific headers #include "qemu/target-m68k/unicorn.h" @@ -95,8 +94,6 @@ const char *uc_strerror(uc_err code) return "Write to unaligned memory (UC_ERR_WRITE_UNALIGNED)"; case UC_ERR_FETCH_UNALIGNED: return "Fetch from unaligned memory (UC_ERR_FETCH_UNALIGNED)"; - case UC_ERR_HOOK_EXIST: - return "Hook for this type event already exists (UC_ERR_HOOK_EXIST)"; case UC_ERR_RESOURCE: return "Insufficient resource (UC_ERR_RESOURCE)"; } @@ -271,9 +268,6 @@ uc_err uc_open(uc_arch arch, uc_mode mode, uc_engine **result) if (uc->reg_reset) uc->reg_reset(uc); - uc->hook_size = HOOK_SIZE; - uc->hook_callbacks = calloc(1, sizeof(uc->hook_callbacks[0]) * HOOK_SIZE); - return UC_ERR_OK; } else { return UC_ERR_ARCH; @@ -285,6 +279,8 @@ UNICORN_EXPORT uc_err uc_close(uc_engine *uc) { int i; + struct list_item *cur; + struct hook *hook; if (uc->release) uc->release(uc->tcg_ctx); @@ -310,7 +306,20 @@ uc_err uc_close(uc_engine *uc) // TODO: remove uc->root (created with object_new()) uc->root->free(uc->root); - free(uc->hook_callbacks); + // free hooks and hook lists + for (i = 0; i < UC_HOOK_MAX; i++) { + cur = uc->hook[i].head; + // hook can be in more than one list + // so we refcount to know when to free + while (cur) { + hook = (struct hook *)cur->data; + if (--hook->refs == 0) { + free(hook); + } + cur = cur->next; + } + list_clear(&uc->hook[i]); + } free(uc->mapped_blocks); @@ -471,6 +480,15 @@ static void enable_emu_timer(uc_engine *uc, uint64_t timeout) uc, QEMU_THREAD_JOINABLE); } +static void hook_count_cb(struct uc_struct *uc, uint64_t address, uint32_t size, void *user_data) +{ + // count this instruction. ah ah ah. + uc->emu_counter++; + + if (uc->emu_counter > uc->emu_count) + uc_emu_stop(uc); +} + UNICORN_EXPORT uc_err uc_emu_start(uc_engine* uc, uint64_t begin, uint64_t until, uint64_t timeout, size_t count) { @@ -525,8 +543,17 @@ uc_err uc_emu_start(uc_engine* uc, uint64_t begin, uint64_t until, uint64_t time } uc->emu_count = count; - if (count > 0) { - uc->hook_insn = true; + // remove count hook if counting isn't necessary + if (count <= 0 && uc->count_hook != 0) { + uc_hook_del(uc, uc->count_hook); + uc->count_hook = 0; + } + // set up count hook to count instructions. + if (count > 0 && uc->count_hook == 0) { + uc_err err = uc_hook_add(uc, &uc->count_hook, UC_HOOK_CODE, hook_count_cb, NULL); + if (err != UC_ERR_OK) { + return err; + } } uc->addr_end = until; @@ -565,37 +592,6 @@ uc_err uc_emu_stop(uc_engine *uc) return UC_ERR_OK; } - -static int _hook_code(uc_engine *uc, int type, uint64_t begin, uint64_t end, - void *callback, void *user_data, uc_hook *hh) -{ - int i; - - i = hook_add(uc, type, begin, end, callback, user_data); - if (i == 0) - return UC_ERR_NOMEM; - - *hh = i; - - return UC_ERR_OK; -} - - -static uc_err _hook_mem_access(uc_engine *uc, uc_hook_type type, - uint64_t begin, uint64_t end, - void *callback, void *user_data, uc_hook *hh) -{ - int i; - - i = hook_add(uc, type, begin, end, callback, user_data); - if (i == 0) - return UC_ERR_NOMEM; - - *hh = i; - - return UC_ERR_OK; -} - // find if a memory range overlaps with existing mapped regions static bool memory_overlap(struct uc_struct *uc, uint64_t begin, size_t size) { @@ -954,208 +950,103 @@ MemoryRegion *memory_mapping(struct uc_struct* uc, uint64_t address) return NULL; } -static uc_err _hook_mem_invalid(struct uc_struct* uc, int type, uc_cb_eventmem_t callback, - void *user_data, uc_hook *evh) -{ - size_t i; - - // only one event handler at the same time - if ((type & UC_HOOK_MEM_READ_UNMAPPED) != 0 && (uc->hook_mem_read_idx != 0)) - return UC_ERR_HOOK_EXIST; - - if ((type & UC_HOOK_MEM_READ_PROT) != 0 && (uc->hook_mem_read_prot_idx != 0)) - return UC_ERR_HOOK_EXIST; - - if ((type & UC_HOOK_MEM_WRITE_UNMAPPED) != 0 && (uc->hook_mem_write_idx != 0)) - return UC_ERR_HOOK_EXIST; - - if ((type & UC_HOOK_MEM_WRITE_PROT) != 0 && (uc->hook_mem_write_prot_idx != 0)) - return UC_ERR_HOOK_EXIST; - - if ((type & UC_HOOK_MEM_FETCH_UNMAPPED) != 0 && (uc->hook_mem_fetch_idx != 0)) - return UC_ERR_HOOK_EXIST; - - if ((type & UC_HOOK_MEM_FETCH_PROT) != 0 && (uc->hook_mem_fetch_prot_idx != 0)) - return UC_ERR_HOOK_EXIST; - - i = hook_find_new(uc); - if (i) { - uc->hook_callbacks[i].callback = callback; - uc->hook_callbacks[i].user_data = user_data; - *evh = i; - if (type & UC_HOOK_MEM_READ_UNMAPPED) - uc->hook_mem_read_idx = i; - if (type & UC_HOOK_MEM_READ_PROT) - uc->hook_mem_read_prot_idx = i; - if (type & UC_HOOK_MEM_WRITE_UNMAPPED) - uc->hook_mem_write_idx = i; - if (type & UC_HOOK_MEM_WRITE_PROT) - uc->hook_mem_write_prot_idx = i; - if (type & UC_HOOK_MEM_FETCH_UNMAPPED) - uc->hook_mem_fetch_idx = i; - if (type & UC_HOOK_MEM_FETCH_PROT) - uc->hook_mem_fetch_prot_idx = i; - return UC_ERR_OK; - } else - return UC_ERR_NOMEM; -} - - -static uc_err _hook_intr(struct uc_struct* uc, void *callback, - void *user_data, uc_hook *evh) -{ - size_t i; - - // only one event handler at the same time - if (uc->hook_intr_idx) - return UC_ERR_HOOK_EXIST; - - i = hook_find_new(uc); - if (i) { - uc->hook_callbacks[i].callback = callback; - uc->hook_callbacks[i].user_data = user_data; - *evh = i; - uc->hook_intr_idx = i; - return UC_ERR_OK; - } else - return UC_ERR_NOMEM; -} - - -static uc_err _hook_insn(struct uc_struct *uc, unsigned int insn_id, void *callback, - void *user_data, uc_hook *evh) -{ - size_t i; - - switch(uc->arch) { - default: break; - case UC_ARCH_X86: - switch(insn_id) { - default: break; - case UC_X86_INS_OUT: - // only one event handler at the same time - if (uc->hook_out_idx) - return UC_ERR_HOOK_EXIST; - - i = hook_find_new(uc); - if (i) { - uc->hook_callbacks[i].callback = callback; - uc->hook_callbacks[i].user_data = user_data; - *evh = i; - uc->hook_out_idx = i; - return UC_ERR_OK; - } else - return UC_ERR_NOMEM; - case UC_X86_INS_IN: - // only one event handler at the same time - if (uc->hook_in_idx) - return UC_ERR_HOOK_EXIST; - - i = hook_find_new(uc); - if (i) { - uc->hook_callbacks[i].callback = callback; - uc->hook_callbacks[i].user_data = user_data; - *evh = i; - uc->hook_in_idx = i; - return UC_ERR_OK; - } else - return UC_ERR_NOMEM; - case UC_X86_INS_SYSCALL: - case UC_X86_INS_SYSENTER: - // only one event handler at the same time - if (uc->hook_syscall_idx) - return UC_ERR_HOOK_EXIST; - - i = hook_find_new(uc); - if (i) { - uc->hook_callbacks[i].callback = callback; - uc->hook_callbacks[i].user_data = user_data; - *evh = i; - uc->hook_syscall_idx = i; - return UC_ERR_OK; - } else - return UC_ERR_NOMEM; - } - break; - } - - return UC_ERR_OK; -} - UNICORN_EXPORT uc_err uc_hook_add(uc_engine *uc, uc_hook *hh, int type, void *callback, void *user_data, ...) { va_list valist; int ret = UC_ERR_OK; - int id; - uint64_t begin, end; va_start(valist, user_data); - if (type & UC_HOOK_MEM_READ_UNMAPPED) - ret = _hook_mem_invalid(uc, UC_HOOK_MEM_READ_UNMAPPED, callback, user_data, hh); + struct hook *hook = calloc(1, sizeof(struct hook)); + if (hook == NULL) { + return UC_ERR_NOMEM; + } + hook->type = type; + hook->callback = callback; + hook->user_data = user_data; + hook->refs = 0; + *hh = (uc_hook)hook; - if (type & UC_HOOK_MEM_WRITE_UNMAPPED) - ret = _hook_mem_invalid(uc, UC_HOOK_MEM_WRITE_UNMAPPED, callback, user_data, hh); - - if (type & UC_HOOK_MEM_FETCH_UNMAPPED) - ret = _hook_mem_invalid(uc, UC_HOOK_MEM_FETCH_UNMAPPED, callback, user_data, hh); - - if (type & UC_HOOK_MEM_READ_PROT) - ret = _hook_mem_invalid(uc, UC_HOOK_MEM_READ_PROT, callback, user_data, hh); - - if (type & UC_HOOK_MEM_WRITE_PROT) - ret = _hook_mem_invalid(uc, UC_HOOK_MEM_WRITE_PROT, callback, user_data, hh); - - if (type & UC_HOOK_MEM_FETCH_PROT) - ret = _hook_mem_invalid(uc, UC_HOOK_MEM_FETCH_PROT, callback, user_data, hh); - - switch(type) { - default: - break; - case UC_HOOK_INTR: - ret = _hook_intr(uc, callback, user_data, hh); - break; - case UC_HOOK_INSN: - id = va_arg(valist, int); - ret = _hook_insn(uc, id, callback, user_data, hh); - break; - case UC_HOOK_CODE: - begin = va_arg(valist, uint64_t); - end = va_arg(valist, uint64_t); - ret = _hook_code(uc, UC_HOOK_CODE, begin, end, callback, user_data, hh); - break; - case UC_HOOK_BLOCK: - begin = va_arg(valist, uint64_t); - end = va_arg(valist, uint64_t); - ret = _hook_code(uc, UC_HOOK_BLOCK, begin, end, callback, user_data, hh); - break; - case UC_HOOK_MEM_READ: - begin = va_arg(valist, uint64_t); - end = va_arg(valist, uint64_t); - ret = _hook_mem_access(uc, UC_HOOK_MEM_READ, begin, end, callback, user_data, hh); - break; - case UC_HOOK_MEM_WRITE: - begin = va_arg(valist, uint64_t); - end = va_arg(valist, uint64_t); - ret = _hook_mem_access(uc, UC_HOOK_MEM_WRITE, begin, end, callback, user_data, hh); - break; - case UC_HOOK_MEM_READ | UC_HOOK_MEM_WRITE: - begin = va_arg(valist, uint64_t); - end = va_arg(valist, uint64_t); - ret = _hook_mem_access(uc, UC_HOOK_MEM_READ | UC_HOOK_MEM_WRITE, begin, end, callback, user_data, hh); - break; + // everybody but HOOK_INSN gets begin/end, so exit early here. + if (type & UC_HOOK_INSN) { + hook->insn = va_arg(valist, int); + hook->begin = 1; + hook->end = 0; + if (list_append(&uc->hook[UC_HOOK_INSN_IDX], hook) == NULL) { + free(hook); + return UC_ERR_NOMEM; + } + hook->refs++; + return UC_ERR_OK; } + hook->begin = va_arg(valist, uint64_t); + hook->end = va_arg(valist, uint64_t); va_end(valist); + int i = 0; + while ((type >> i) > 0) { + if ((type >> i) & 1) { + // TODO: invalid hook error? + if (i < UC_HOOK_MAX) { + if (list_append(&uc->hook[i], hook) == NULL) { + if (hook->refs == 0) { + free(hook); + } + return UC_ERR_NOMEM; + } + hook->refs++; + } + } + i++; + } + + // we didn't use the hook + // TODO: return an error? + if (hook->refs == 0) { + free(hook); + } + return ret; } UNICORN_EXPORT uc_err uc_hook_del(uc_engine *uc, uc_hook hh) { - return hook_del(uc, hh); + int i; + struct hook *hook; + for (i = 0; i < UC_HOOK_MAX; i++) { + if (list_remove(&uc->hook[i], (void *)hh)) { + hook = (struct hook *)hh; + if (--hook->refs == 0) { + free(hook); + } + } + } + return UC_ERR_OK; +} + +// TCG helper +void helper_uc_tracecode(int32_t size, uc_hook_type type, void *handle, int64_t address); +void helper_uc_tracecode(int32_t size, uc_hook_type type, void *handle, int64_t address) +{ + struct uc_struct *uc = handle; + struct list_item *cur = uc->hook[type].head; + struct hook *hook; + + // sync PC in CPUArchState with address + if (uc->set_pc) { + uc->set_pc(uc, address); + } + + while (cur != NULL && !uc->stop_request) { + hook = (struct hook *)cur->data; + if (HOOK_BOUND_CHECK(hook, address)) { + ((uc_cb_hookcode_t)hook->callback)(uc, address, size, hook->user_data); + } + cur = cur->next; + } } UNICORN_EXPORT