From 93052f6566f6ab849712ee80d6e2e04ce7cb8e0e Mon Sep 17 00:00:00 2001 From: Ryan Hileman Date: Sat, 16 Jan 2016 00:44:02 -0800 Subject: [PATCH] refactor to allow multiple hooks for one type --- Makefile | 4 +- hook.c | 279 --------------------------- include/hook.h | 20 -- include/list.h | 20 ++ include/uc_priv.h | 87 ++++++--- list.c | 66 +++++++ qemu/Makefile.objs | 2 +- qemu/cpu-exec.c | 10 +- qemu/ioport.c | 62 +++--- qemu/softmmu_template.h | 194 ++++++++++++------- qemu/target-arm/translate-a64.c | 19 +- qemu/target-arm/translate.c | 35 ++-- qemu/target-i386/seg_helper.c | 28 +-- qemu/target-i386/translate.c | 38 ++-- qemu/target-m68k/translate.c | 20 +- qemu/target-mips/translate.c | 50 ++--- qemu/target-sparc/translate.c | 26 +-- qemu/tcg/i386/tcg-target.c | 2 +- qemu/translate-all.c | 2 +- uc.c | 329 ++++++++++---------------------- 20 files changed, 542 insertions(+), 751 deletions(-) delete mode 100644 hook.c delete mode 100644 include/hook.h create mode 100644 include/list.h create mode 100644 list.c diff --git a/Makefile b/Makefile index 4d7e4e73..103dd531 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 6e575d83..00000000 --- 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 6fbdf509..00000000 --- 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 00000000..75ec13e2 --- /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 5cdf6557..a55c847e 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" #define ARR_SIZE(a) (sizeof(a)/sizeof(a[0])) @@ -60,16 +60,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 @@ -144,35 +190,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 00000000..695f5a24 --- /dev/null +++ b/list.c @@ -0,0 +1,66 @@ +#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; + } +} + +// 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 4e7cc899..d309d2a4 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 5a1f3798..33246009 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 338a296a..c7ac6b85 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 e3100fcd..1f636055 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/translate-a64.c b/qemu/target-arm/translate-a64.c index c76de8e0..28d57681 100644 --- a/qemu/target-arm/translate-a64.c +++ b/qemu/target-arm/translate-a64.c @@ -10970,6 +10970,7 @@ static void disas_data_proc_simd_fp(DisasContext *s, uint32_t insn) static void disas_a64_insn(CPUARMState *env, DisasContext *s) { uint32_t insn; + struct hook *hook; TCGContext *tcg_ctx = env->uc->tcg_ctx; // Unicorn: end address tells us to stop emulation @@ -10984,10 +10985,10 @@ 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); + HOOK_FOREACH(env->uc, hook, UC_HOOK_CODE) { + if (! HOOK_BOUND_CHECK(hook, s->pc - 4)) + continue; + gen_uc_tracecode(tcg_ctx, 4, hook->callback, env->uc, s->pc - 4, hook->user_data); // the callback might want to stop emulation immediately check_exit_request(tcg_ctx); } @@ -11043,6 +11044,7 @@ void gen_intermediate_code_internal_a64(ARMCPU *cpu, int max_insns; TCGContext *tcg_ctx = env->uc->tcg_ctx; bool block_full = false; + struct hook *hook; pc_start = tb->pc; @@ -11114,12 +11116,13 @@ 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) { + if (! env->uc->block_full) { + HOOK_FOREACH(env->uc, hook, UC_HOOK_BLOCK) { + if (! HOOK_BOUND_CHECK(hook, pc_start)) + continue; // 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); + gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, hook->callback, env->uc, pc_start, hook->user_data); } } diff --git a/qemu/target-arm/translate.c b/qemu/target-arm/translate.c index 3560e6ed..bf190452 100644 --- a/qemu/target-arm/translate.c +++ b/qemu/target-arm/translate.c @@ -7680,6 +7680,7 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) // qq TCGv_i32 tmp3; TCGv_i32 addr; TCGv_i64 tmp64; + struct hook *hook; /* M variants do not implement ARM mode. */ if (arm_dc_feature(s, ARM_FEATURE_M)) { @@ -7687,10 +7688,10 @@ 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); + HOOK_FOREACH(s->uc, hook, UC_HOOK_CODE) { + if (! HOOK_BOUND_CHECK(hook, s->pc - 4)) + continue; + gen_uc_tracecode(tcg_ctx, 4, hook->callback, s->uc, s->pc - 4, hook->user_data); // the callback might want to stop emulation immediately check_exit_request(tcg_ctx); } @@ -10390,6 +10391,7 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s) // qq TCGv_i32 tmp; TCGv_i32 tmp2; TCGv_i32 addr; + struct hook *hook; // Unicorn: end address tells us to stop emulation if (s->pc == s->uc->addr_end) { @@ -10408,15 +10410,12 @@ 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); - } + HOOK_FOREACH(env->uc, hook, UC_HOOK_CODE) { + if (! HOOK_BOUND_CHECK(hook, s->pc)) + continue; + gen_uc_tracecode(tcg_ctx, 2, hook->callback, env->uc, s->pc, hook->user_data); + // check to see if we need to exit immediately + check_exit_request(tcg_ctx); } insn = arm_lduw_code(env, s->pc, s->bswap_code); @@ -11148,6 +11147,7 @@ static inline void gen_intermediate_code_internal(ARMCPU *cpu, int max_insns; TCGContext *tcg_ctx = env->uc->tcg_ctx; bool block_full = false; + struct hook *hook; /* generate intermediate code */ @@ -11237,12 +11237,13 @@ 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) { + if (!env->uc->block_full) { + HOOK_FOREACH(env->uc, hook, UC_HOOK_BLOCK) { + if (! HOOK_BOUND_CHECK(hook, pc_start)) + continue; // 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); + gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, hook->callback, env->uc, pc_start, hook->user_data); } } diff --git a/qemu/target-i386/seg_helper.c b/qemu/target-i386/seg_helper.c index 42b0f37c..4cf6e41a 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 285ba32c..0d83f4bd 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,11 @@ 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; + struct hook *hook = 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,16 +4767,17 @@ 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; - } + 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; + } + HOOK_FOREACH(env->uc, hook, UC_HOOK_CODE) { + if (! HOOK_BOUND_CHECK(hook, pc_start)) + continue; // generate code to call callback - gen_uc_tracecode(tcg_ctx, 0xf1f1f1f1, trace->callback, env->uc, pc_start, trace->user_data); + gen_uc_tracecode(tcg_ctx, 0xf1f1f1f1, hook->callback, env->uc, pc_start, hook->user_data); // the callback might want to stop emulation immediately check_exit_request(tcg_ctx); } @@ -8173,7 +8173,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) { // int i; // for(i = 0; i < 20; i++) // printf("=== [%u] = %x\n", i, *(save_opparam_ptr + i)); @@ -8282,6 +8282,7 @@ static inline void gen_intermediate_code_internal(uint8_t *gen_opc_cc_op, int num_insns = 0; int max_insns; bool block_full = false; + struct hook *hook; /* generate intermediate code */ pc_start = tb->pc; @@ -8387,11 +8388,12 @@ 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) { + if (!env->uc->block_full) { + HOOK_FOREACH(env->uc, hook, UC_HOOK_BLOCK) { + if (! HOOK_BOUND_CHECK(hook, pc_start)) + continue; env->uc->block_addr = pc_start; - gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, trace->callback, env->uc, pc_start, trace->user_data); + gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, hook->callback, env->uc, pc_start, hook->user_data); } } diff --git a/qemu/target-m68k/translate.c b/qemu/target-m68k/translate.c index 8c2951d9..0f685d8e 100644 --- a/qemu/target-m68k/translate.c +++ b/qemu/target-m68k/translate.c @@ -3031,6 +3031,7 @@ static void disas_m68k_insn(CPUM68KState * env, DisasContext *s) { TCGContext *tcg_ctx = s->uc->tcg_ctx; uint16_t insn; + struct hook *hook; if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT))) { tcg_gen_debug_insn_start(tcg_ctx, s->pc); @@ -3043,11 +3044,10 @@ 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); - + HOOK_FOREACH(env->uc, hook, UC_HOOK_CODE) { + if (! HOOK_BOUND_CHECK(hook, s->pc)) + continue; + gen_uc_tracecode(tcg_ctx, 2, hook->callback, env->uc, s->pc, hook->user_data); // the callback might want to stop emulation immediately check_exit_request(tcg_ctx); } @@ -3075,6 +3075,7 @@ gen_intermediate_code_internal(M68kCPU *cpu, TranslationBlock *tb, int max_insns; TCGContext *tcg_ctx = env->uc->tcg_ctx; bool block_full = false; + struct hook *hook; /* generate intermediate code */ pc_start = tb->pc; @@ -3109,12 +3110,13 @@ 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) { + if (!env->uc->block_full) { + HOOK_FOREACH(env->uc, hook, UC_HOOK_BLOCK) { + if (! HOOK_BOUND_CHECK(hook, pc_start)) + continue; // 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); + gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, hook->callback, env->uc, pc_start, hook->user_data); } } diff --git a/qemu/target-mips/translate.c b/qemu/target-mips/translate.c index 75465915..fadbaeb0 100644 --- a/qemu/target-mips/translate.c +++ b/qemu/target-mips/translate.c @@ -11331,6 +11331,7 @@ static int decode_mips16_opc (CPUMIPSState *env, DisasContext *ctx, bool *insn_n int op, cnvt_op, op1, offset; int funct; int n_bytes; + struct hook *hook; op = (ctx->opcode >> 11) & 0x1f; sa = (ctx->opcode >> 2) & 0x7; @@ -11343,12 +11344,11 @@ 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; - } + HOOK_FOREACH(env->uc, hook, UC_HOOK_CODE) { + if (! HOOK_BOUND_CHECK(hook, ctx->pc)) + continue; + gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, hook->callback, env->uc, ctx->pc, hook->user_data); + *insn_need_patch = true; // the callback might want to stop emulation immediately check_exit_request(tcg_ctx); } @@ -13932,6 +13932,7 @@ static int decode_micromips_opc (CPUMIPSState *env, DisasContext *ctx, bool *ins TCGContext *tcg_ctx = env->uc->tcg_ctx; TCGv **cpu_gpr = (TCGv **)tcg_ctx->cpu_gpr; uint32_t op; + struct hook *hook; /* make sure instructions are on a halfword boundary */ if (ctx->pc & 0x1) { @@ -13942,12 +13943,11 @@ 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; - } + HOOK_FOREACH(env->uc, hook, UC_HOOK_CODE) { + if (! HOOK_BOUND_CHECK(hook, ctx->pc)) + continue; + gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, hook->callback, env->uc, ctx->pc, hook->user_data); + *insn_need_patch = true; // the callback might want to stop emulation immediately check_exit_request(tcg_ctx); } @@ -18504,13 +18504,13 @@ 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; + struct hook *hook; + HOOK_FOREACH(env->uc, hook, UC_HOOK_CODE) { + if (! HOOK_BOUND_CHECK(hook, ctx->pc)) + continue; + gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, hook->callback, env->uc, ctx->pc, hook->user_data); + *insn_need_patch = true; // the callback might want to stop emulation immediately check_exit_request(tcg_ctx); *insn_patch_offset = offset_value; @@ -19178,6 +19178,7 @@ gen_intermediate_code_internal(MIPSCPU *cpu, TranslationBlock *tb, TCGContext *tcg_ctx = env->uc->tcg_ctx; TCGArg *save_opparam_ptr = NULL; bool block_full = false; + struct hook *hook; if (search_pc) qemu_log("search pc %d\n", search_pc); @@ -19223,12 +19224,13 @@ 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) { + if (! env->uc->block_full) { + HOOK_FOREACH(env->uc, hook, UC_HOOK_BLOCK) { + if (! HOOK_BOUND_CHECK(hook, pc_start)) + continue; // 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); + gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, hook->callback, env->uc, pc_start, hook->user_data); } } @@ -19275,7 +19277,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/translate.c b/qemu/target-sparc/translate.c index fc71e88b..8caba2b7 100644 --- a/qemu/target-sparc/translate.c +++ b/qemu/target-sparc/translate.c @@ -2625,6 +2625,7 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn, bool hook_ins TCGv_i32 cpu_src1_32, cpu_src2_32, cpu_dst_32; TCGv_i64 cpu_src1_64, cpu_src2_64, cpu_dst_64; target_long simm; + struct hook *hook; if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT))) { tcg_gen_debug_insn_start(tcg_ctx, dc->pc); @@ -2637,13 +2638,14 @@ 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); - - // the callback might want to stop emulation immediately - check_exit_request(tcg_ctx); + if (hook_insn) { + HOOK_FOREACH(dc->uc, hook, UC_HOOK_CODE) { + if (! HOOK_BOUND_CHECK(hook, dc->pc)) + continue; + gen_uc_tracecode(tcg_ctx, 4, hook->callback, dc->uc, dc->pc, hook->user_data); + // the callback might want to stop emulation immediately + check_exit_request(tcg_ctx); + } } opc = GET_FIELD(insn, 0, 1); @@ -5388,6 +5390,7 @@ static inline void gen_intermediate_code_internal(SPARCCPU *cpu, unsigned int insn; TCGContext *tcg_ctx = env->uc->tcg_ctx; bool block_full = false; + struct hook *hook; memset(dc, 0, sizeof(DisasContext)); dc->uc = env->uc; @@ -5428,12 +5431,13 @@ 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) { + if (!env->uc->block_full) { + HOOK_FOREACH(env->uc, hook, UC_HOOK_BLOCK) { + if (! HOOK_BOUND_CHECK(hook, pc_start)) + continue; // 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); + gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, hook->callback, env->uc, pc_start, hook->user_data); } } diff --git a/qemu/tcg/i386/tcg-target.c b/qemu/tcg/i386/tcg-target.c index aa06cc18..414c4a72 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/translate-all.c b/qemu/translate-all.c index 11bbb07b..72e1b60f 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 (HOOK_EXISTS(env->uc, UC_HOOK_BLOCK) && env->uc->block_addr == tb->pc) { if (env->uc->block_full) // block size is unknown *(s->gen_opparam_buf + 1) = 0; else diff --git a/uc.c b/uc.c index b354a5fa..f7f40853 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)"; } @@ -245,9 +242,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; @@ -259,6 +253,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); @@ -284,7 +280,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); @@ -445,6 +454,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) { @@ -506,8 +524,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; @@ -546,37 +573,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) { @@ -935,208 +931,95 @@ 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, 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); } UNICORN_EXPORT