mirror of
https://github.com/yuzu-emu/unicorn.git
synced 2025-04-18 04:51:44 +00:00
Merge branch 'hook'
This commit is contained in:
commit
48ab148d1c
4
Makefile
4
Makefile
|
@ -228,7 +228,7 @@ compile_lib: config qemu/config-host.h-timestamp
|
||||||
|
|
||||||
unicorn: $(LIBRARY) $(ARCHIVE)
|
unicorn: $(LIBRARY) $(ARCHIVE)
|
||||||
|
|
||||||
$(LIBRARY): $(UC_TARGET_OBJ) uc.o hook.o
|
$(LIBRARY): $(UC_TARGET_OBJ) uc.o list.o
|
||||||
ifeq ($(UNICORN_SHARED),yes)
|
ifeq ($(UNICORN_SHARED),yes)
|
||||||
ifeq ($(V),0)
|
ifeq ($(V),0)
|
||||||
$(call log,GEN,$(LIBRARY))
|
$(call log,GEN,$(LIBRARY))
|
||||||
|
@ -241,7 +241,7 @@ ifneq (,$(LIBRARY_SYMLINK))
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
$(ARCHIVE): $(UC_TARGET_OBJ) uc.o hook.o
|
$(ARCHIVE): $(UC_TARGET_OBJ) uc.o list.o
|
||||||
ifeq ($(UNICORN_STATIC),yes)
|
ifeq ($(UNICORN_STATIC),yes)
|
||||||
ifeq ($(V),0)
|
ifeq ($(V),0)
|
||||||
$(call log,GEN,$(ARCHIVE))
|
$(call log,GEN,$(ARCHIVE))
|
||||||
|
|
279
hook.c
279
hook.c
|
@ -1,279 +0,0 @@
|
||||||
/* Unicorn Emulator Engine */
|
|
||||||
/* By Nguyen Anh Quynh <aquynh@gmail.com>, 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);
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
/* Unicorn Emulator Engine */
|
|
||||||
/* By Nguyen Anh Quynh <aquynh@gmail.com>, 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
|
|
20
include/list.h
Normal file
20
include/list.h
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
#ifndef UC_LLIST_H
|
||||||
|
#define UC_LLIST_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
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
|
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
#include "qemu.h"
|
#include "qemu.h"
|
||||||
#include "unicorn/unicorn.h"
|
#include "unicorn/unicorn.h"
|
||||||
#include "hook.h"
|
#include "list.h"
|
||||||
|
|
||||||
// These are masks of supported modes for each cpu/arch.
|
// These are masks of supported modes for each cpu/arch.
|
||||||
// They should be updated when changes are made to the uc_mode enum typedef.
|
// 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
|
// some architecture redirect virtual memory to physical memory like Mips
|
||||||
typedef uint64_t (*uc_mem_redirect_t)(uint64_t address);
|
typedef uint64_t (*uc_mem_redirect_t)(uint64_t address);
|
||||||
|
|
||||||
|
struct hook {
|
||||||
struct hook_struct {
|
int type; // UC_HOOK_*
|
||||||
int hook_type; // uc_tracecode_type & uc_tracemem_type
|
int insn; // instruction for HOOK_INSN
|
||||||
uint64_t begin, end; // range of address to be monitored
|
int refs; // reference count to free hook stored in multiple lists
|
||||||
void *callback; // either uc_cb_tracecode_t or uc_cb_tracemem_t
|
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;
|
void *user_data;
|
||||||
};
|
};
|
||||||
|
|
||||||
// extend memory to keep 32 more hooks each time
|
// hook list offsets
|
||||||
#define HOOK_SIZE 32
|
// 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!
|
//relloc increment, KEEP THIS A POWER OF 2!
|
||||||
#define MEM_BLOCK_INCR 32
|
#define MEM_BLOCK_INCR 32
|
||||||
|
@ -156,35 +202,16 @@ struct uc_struct {
|
||||||
bool apic_report_tpr_access;
|
bool apic_report_tpr_access;
|
||||||
CPUState *current_cpu;
|
CPUState *current_cpu;
|
||||||
|
|
||||||
// all the hook callbacks
|
// linked lists containing hooks per type
|
||||||
size_t hook_size;
|
struct list hook[UC_HOOK_MAX];
|
||||||
struct hook_struct *hook_callbacks;
|
|
||||||
|
|
||||||
// hook to count number of instructions for uc_emu_start()
|
// hook to count number of instructions for uc_emu_start()
|
||||||
struct hook_struct hook_count;
|
uc_hook count_hook;
|
||||||
uc_cb_hookcode_t hook_count_callback;
|
|
||||||
|
|
||||||
size_t emu_counter; // current counter of uc_emu_start()
|
size_t emu_counter; // current counter of uc_emu_start()
|
||||||
size_t emu_count; // save 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
|
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 init_tcg; // already initialized local TCGv variables?
|
||||||
bool stop_request; // request to immediately stop emulation - for uc_emu_stop()
|
bool stop_request; // request to immediately stop emulation - for uc_emu_stop()
|
||||||
|
|
68
list.c
Normal file
68
list.c
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#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;
|
||||||
|
}
|
|
@ -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 is code used by both qemu system emulation and qemu-img
|
||||||
|
|
||||||
block-obj-y =
|
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_POSIX) += aio-posix.o
|
||||||
#block-obj-$(CONFIG_WIN32) += aio-win32.o
|
#block-obj-$(CONFIG_WIN32) += aio-win32.o
|
||||||
|
|
||||||
|
|
|
@ -64,6 +64,7 @@ int cpu_exec(struct uc_struct *uc, CPUArchState *env) // qq
|
||||||
TranslationBlock *tb;
|
TranslationBlock *tb;
|
||||||
uint8_t *tc_ptr;
|
uint8_t *tc_ptr;
|
||||||
uintptr_t next_tb;
|
uintptr_t next_tb;
|
||||||
|
struct hook *hook;
|
||||||
|
|
||||||
/* This must be volatile so it is not trashed by longjmp() */
|
/* This must be volatile so it is not trashed by longjmp() */
|
||||||
volatile bool have_tb_lock = false;
|
volatile bool have_tb_lock = false;
|
||||||
|
@ -127,11 +128,10 @@ int cpu_exec(struct uc_struct *uc, CPUArchState *env) // qq
|
||||||
ret = cpu->exception_index;
|
ret = cpu->exception_index;
|
||||||
break;
|
break;
|
||||||
#else
|
#else
|
||||||
// Unicorn: call interrupt callback if registered
|
// Unicorn: call registered interrupt callbacks
|
||||||
if (uc->hook_intr_idx)
|
HOOK_FOREACH(uc, hook, UC_HOOK_INTR) {
|
||||||
((uc_cb_hookintr_t)uc->hook_callbacks[uc->hook_intr_idx].callback)(
|
((uc_cb_hookintr_t)hook->callback)(uc, cpu->exception_index, hook->user_data);
|
||||||
uc, cpu->exception_index,
|
}
|
||||||
uc->hook_callbacks[uc->hook_intr_idx].user_data);
|
|
||||||
cpu->exception_index = -1;
|
cpu->exception_index = -1;
|
||||||
#if defined(TARGET_X86_64)
|
#if defined(TARGET_X86_64)
|
||||||
// point EIP to the next instruction after INT
|
// point EIP to the next instruction after INT
|
||||||
|
|
|
@ -66,39 +66,45 @@ const MemoryRegionOps unassigned_io_ops = {
|
||||||
void cpu_outb(struct uc_struct *uc, pio_addr_t addr, uint8_t val)
|
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);
|
//LOG_IOPORT("outb: %04"FMT_pioaddr" %02"PRIx8"\n", addr, val);
|
||||||
// Unicorn: call interrupt callback if registered
|
// Unicorn: call registered OUT callbacks
|
||||||
if (uc->hook_out_idx)
|
struct hook *hook;
|
||||||
((uc_cb_insn_out_t)uc->hook_callbacks[uc->hook_out_idx].callback)(
|
HOOK_FOREACH(uc, hook, UC_HOOK_INSN) {
|
||||||
uc, addr, 1, val,
|
if (hook->insn == UC_X86_INS_OUT)
|
||||||
uc->hook_callbacks[uc->hook_out_idx].user_data);
|
((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)
|
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);
|
//LOG_IOPORT("outw: %04"FMT_pioaddr" %04"PRIx16"\n", addr, val);
|
||||||
// Unicorn: call interrupt callback if registered
|
// Unicorn: call registered OUT callbacks
|
||||||
if (uc->hook_out_idx)
|
struct hook *hook;
|
||||||
((uc_cb_insn_out_t)uc->hook_callbacks[uc->hook_out_idx].callback)(
|
HOOK_FOREACH(uc, hook, UC_HOOK_INSN) {
|
||||||
uc, addr, 2, val,
|
if (hook->insn == UC_X86_INS_OUT)
|
||||||
uc->hook_callbacks[uc->hook_out_idx].user_data);
|
((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)
|
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);
|
//LOG_IOPORT("outl: %04"FMT_pioaddr" %08"PRIx32"\n", addr, val);
|
||||||
if (uc->hook_out_idx)
|
// Unicorn: call registered OUT callbacks
|
||||||
((uc_cb_insn_out_t)uc->hook_callbacks[uc->hook_out_idx].callback)(
|
struct hook *hook;
|
||||||
uc, addr, 4, val,
|
HOOK_FOREACH(uc, hook, UC_HOOK_INSN) {
|
||||||
uc->hook_callbacks[uc->hook_out_idx].user_data);
|
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)
|
uint8_t cpu_inb(struct uc_struct *uc, pio_addr_t addr)
|
||||||
{
|
{
|
||||||
//LOG_IOPORT("inb : %04"FMT_pioaddr" %02"PRIx8"\n", addr, val);
|
//LOG_IOPORT("inb : %04"FMT_pioaddr" %02"PRIx8"\n", addr, val);
|
||||||
if (uc->hook_in_idx)
|
// Unicorn: call registered IN callbacks
|
||||||
return ((uc_cb_insn_in_t)uc->hook_callbacks[uc->hook_in_idx].callback)(
|
struct hook *hook;
|
||||||
uc, addr, 1,
|
HOOK_FOREACH(uc, hook, UC_HOOK_INSN) {
|
||||||
uc->hook_callbacks[uc->hook_in_idx].user_data);
|
if (hook->insn == UC_X86_INS_IN)
|
||||||
|
return ((uc_cb_insn_in_t)hook->callback)(uc, addr, 1, hook->user_data);
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
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)
|
uint16_t cpu_inw(struct uc_struct *uc, pio_addr_t addr)
|
||||||
{
|
{
|
||||||
//LOG_IOPORT("inw : %04"FMT_pioaddr" %04"PRIx16"\n", addr, val);
|
//LOG_IOPORT("inw : %04"FMT_pioaddr" %04"PRIx16"\n", addr, val);
|
||||||
if (uc->hook_in_idx)
|
// Unicorn: call registered IN callbacks
|
||||||
return ((uc_cb_insn_in_t)uc->hook_callbacks[uc->hook_in_idx].callback)(
|
struct hook *hook;
|
||||||
uc, addr, 2,
|
HOOK_FOREACH(uc, hook, UC_HOOK_INSN) {
|
||||||
uc->hook_callbacks[uc->hook_in_idx].user_data);
|
if (hook->insn == UC_X86_INS_IN)
|
||||||
|
return ((uc_cb_insn_in_t)hook->callback)(uc, addr, 2, hook->user_data);
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
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)
|
uint32_t cpu_inl(struct uc_struct *uc, pio_addr_t addr)
|
||||||
{
|
{
|
||||||
//LOG_IOPORT("inl : %04"FMT_pioaddr" %08"PRIx32"\n", addr, val);
|
//LOG_IOPORT("inl : %04"FMT_pioaddr" %08"PRIx32"\n", addr, val);
|
||||||
if (uc->hook_in_idx)
|
// Unicorn: call registered IN callbacks
|
||||||
return ((uc_cb_insn_in_t)uc->hook_callbacks[uc->hook_in_idx].callback)(
|
struct hook *hook;
|
||||||
uc, addr, 4,
|
HOOK_FOREACH(uc, hook, UC_HOOK_INSN) {
|
||||||
uc->hook_callbacks[uc->hook_in_idx].user_data);
|
if (hook->insn == UC_X86_INS_IN)
|
||||||
|
return ((uc_cb_insn_in_t)hook->callback)(uc, addr, 4, hook->user_data);
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -178,23 +178,33 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx,
|
||||||
uintptr_t haddr;
|
uintptr_t haddr;
|
||||||
DATA_TYPE res;
|
DATA_TYPE res;
|
||||||
int error_code;
|
int error_code;
|
||||||
|
struct hook *hook;
|
||||||
|
bool handled;
|
||||||
|
|
||||||
struct uc_struct *uc = env->uc;
|
struct uc_struct *uc = env->uc;
|
||||||
MemoryRegion *mr = memory_mapping(uc, addr);
|
MemoryRegion *mr = memory_mapping(uc, addr);
|
||||||
|
|
||||||
// memory might be still unmapped while reading or fetching
|
// memory might be still unmapped while reading or fetching
|
||||||
if (mr == NULL) {
|
if (mr == NULL) {
|
||||||
|
handled = false;
|
||||||
#if defined(SOFTMMU_CODE_ACCESS)
|
#if defined(SOFTMMU_CODE_ACCESS)
|
||||||
error_code = UC_ERR_FETCH_UNMAPPED;
|
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)(
|
HOOK_FOREACH(uc, hook, UC_HOOK_MEM_FETCH_UNMAPPED) {
|
||||||
uc, UC_MEM_FETCH_UNMAPPED, addr, DATA_SIZE, 0,
|
if (!HOOK_BOUND_CHECK(hook, addr))
|
||||||
uc->hook_callbacks[uc->hook_mem_fetch_idx].user_data)) {
|
continue;
|
||||||
|
if ((handled = ((uc_cb_eventmem_t)hook->callback)(uc, UC_MEM_FETCH_UNMAPPED, addr, DATA_SIZE, 0, hook->user_data)))
|
||||||
|
break;
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
error_code = UC_ERR_READ_UNMAPPED;
|
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)(
|
HOOK_FOREACH(uc, hook, UC_HOOK_MEM_READ_UNMAPPED) {
|
||||||
uc, UC_MEM_READ_UNMAPPED, addr, DATA_SIZE, 0,
|
if (!HOOK_BOUND_CHECK(hook, addr))
|
||||||
uc->hook_callbacks[uc->hook_mem_read_idx].user_data)) {
|
continue;
|
||||||
|
if ((handled = ((uc_cb_eventmem_t)hook->callback)(uc, UC_MEM_READ_UNMAPPED, addr, DATA_SIZE, 0, hook->user_data)))
|
||||||
|
break;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
if (handled) {
|
||||||
env->invalid_error = UC_ERR_OK;
|
env->invalid_error = UC_ERR_OK;
|
||||||
mr = memory_mapping(uc, addr); // FIXME: what if mr is still NULL at this time?
|
mr = memory_mapping(uc, addr); // FIXME: what if mr is still NULL at this time?
|
||||||
} else {
|
} else {
|
||||||
|
@ -209,9 +219,15 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx,
|
||||||
#if defined(SOFTMMU_CODE_ACCESS)
|
#if defined(SOFTMMU_CODE_ACCESS)
|
||||||
// Unicorn: callback on fetch from NX
|
// Unicorn: callback on fetch from NX
|
||||||
if (mr != NULL && !(mr->perms & UC_PROT_EXEC)) { // non-executable
|
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)(
|
handled = false;
|
||||||
uc, UC_MEM_FETCH_PROT, addr, DATA_SIZE, 0,
|
HOOK_FOREACH(uc, hook, UC_HOOK_MEM_FETCH_PROT) {
|
||||||
uc->hook_callbacks[uc->hook_mem_fetch_prot_idx].user_data)) {
|
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;
|
env->invalid_error = UC_ERR_OK;
|
||||||
} else {
|
} else {
|
||||||
env->invalid_addr = addr;
|
env->invalid_addr = addr;
|
||||||
|
@ -224,19 +240,25 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx,
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Unicorn: callback on memory read
|
// Unicorn: callback on memory read
|
||||||
if (READ_ACCESS_TYPE == MMU_DATA_LOAD && env->uc->hook_mem_read) {
|
if (READ_ACCESS_TYPE == MMU_DATA_LOAD) {
|
||||||
struct hook_struct *trace = hook_find(env->uc, UC_HOOK_MEM_READ, addr);
|
HOOK_FOREACH(uc, hook, UC_HOOK_MEM_READ) {
|
||||||
if (trace) {
|
if (!HOOK_BOUND_CHECK(hook, addr))
|
||||||
((uc_cb_hookmem_t)trace->callback)(env->uc, UC_MEM_READ,
|
continue;
|
||||||
(uint64_t)addr, (int)DATA_SIZE, (int64_t)0, trace->user_data);
|
((uc_cb_hookmem_t)hook->callback)(env->uc, UC_MEM_READ, addr, DATA_SIZE, 0, hook->user_data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unicorn: callback on non-readable memory
|
// Unicorn: callback on non-readable memory
|
||||||
if (READ_ACCESS_TYPE == MMU_DATA_LOAD && mr != NULL && !(mr->perms & UC_PROT_READ)) { //non-readable
|
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)(
|
handled = false;
|
||||||
uc, UC_MEM_READ_PROT, addr, DATA_SIZE, 0,
|
HOOK_FOREACH(uc, hook, UC_HOOK_MEM_READ_PROT) {
|
||||||
uc->hook_callbacks[uc->hook_mem_read_prot_idx].user_data)) {
|
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;
|
env->invalid_error = UC_ERR_OK;
|
||||||
} else {
|
} else {
|
||||||
env->invalid_addr = addr;
|
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;
|
uintptr_t haddr;
|
||||||
DATA_TYPE res;
|
DATA_TYPE res;
|
||||||
int error_code;
|
int error_code;
|
||||||
|
struct hook *hook;
|
||||||
|
bool handled;
|
||||||
|
|
||||||
struct uc_struct *uc = env->uc;
|
struct uc_struct *uc = env->uc;
|
||||||
MemoryRegion *mr = memory_mapping(uc, addr);
|
MemoryRegion *mr = memory_mapping(uc, addr);
|
||||||
|
|
||||||
// memory can be unmapped while reading or fetching
|
// memory can be unmapped while reading or fetching
|
||||||
if (mr == NULL) {
|
if (mr == NULL) {
|
||||||
|
handled = false;
|
||||||
#if defined(SOFTMMU_CODE_ACCESS)
|
#if defined(SOFTMMU_CODE_ACCESS)
|
||||||
error_code = UC_ERR_FETCH_UNMAPPED;
|
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)(
|
HOOK_FOREACH(uc, hook, UC_HOOK_MEM_FETCH_UNMAPPED) {
|
||||||
uc, UC_MEM_FETCH_UNMAPPED, addr, DATA_SIZE, 0,
|
if (!HOOK_BOUND_CHECK(hook, addr))
|
||||||
uc->hook_callbacks[uc->hook_mem_fetch_idx].user_data)) {
|
continue;
|
||||||
|
if ((handled = ((uc_cb_eventmem_t)hook->callback)(uc, UC_MEM_FETCH_UNMAPPED, addr, DATA_SIZE, 0, hook->user_data)))
|
||||||
|
break;
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
error_code = UC_ERR_READ_UNMAPPED;
|
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)(
|
HOOK_FOREACH(uc, hook, UC_HOOK_MEM_READ_UNMAPPED) {
|
||||||
uc, UC_MEM_READ_UNMAPPED, addr, DATA_SIZE, 0,
|
if (!HOOK_BOUND_CHECK(hook, addr))
|
||||||
uc->hook_callbacks[uc->hook_mem_read_idx].user_data)) {
|
continue;
|
||||||
|
if ((handled = ((uc_cb_eventmem_t)hook->callback)(uc, UC_MEM_READ_UNMAPPED, addr, DATA_SIZE, 0, hook->user_data)))
|
||||||
|
break;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
if (handled) {
|
||||||
env->invalid_error = UC_ERR_OK;
|
env->invalid_error = UC_ERR_OK;
|
||||||
mr = memory_mapping(uc, addr); // FIXME: what if mr is still NULL at this time?
|
mr = memory_mapping(uc, addr); // FIXME: what if mr is still NULL at this time?
|
||||||
} else {
|
} else {
|
||||||
|
@ -399,9 +431,15 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx,
|
||||||
#if defined(SOFTMMU_CODE_ACCESS)
|
#if defined(SOFTMMU_CODE_ACCESS)
|
||||||
// Unicorn: callback on fetch from NX
|
// Unicorn: callback on fetch from NX
|
||||||
if (mr != NULL && !(mr->perms & UC_PROT_EXEC)) { // non-executable
|
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)(
|
handled = false;
|
||||||
uc, UC_MEM_FETCH_PROT, addr, DATA_SIZE, 0,
|
HOOK_FOREACH(uc, hook, UC_HOOK_MEM_FETCH_PROT) {
|
||||||
uc->hook_callbacks[uc->hook_mem_fetch_prot_idx].user_data)) {
|
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;
|
env->invalid_error = UC_ERR_OK;
|
||||||
} else {
|
} else {
|
||||||
env->invalid_addr = addr;
|
env->invalid_addr = addr;
|
||||||
|
@ -414,19 +452,25 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx,
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Unicorn: callback on memory read
|
// Unicorn: callback on memory read
|
||||||
if (READ_ACCESS_TYPE == MMU_DATA_LOAD && env->uc->hook_mem_read) {
|
if (READ_ACCESS_TYPE == MMU_DATA_LOAD) {
|
||||||
struct hook_struct *trace = hook_find(env->uc, UC_HOOK_MEM_READ, addr);
|
HOOK_FOREACH(uc, hook, UC_HOOK_MEM_READ) {
|
||||||
if (trace) {
|
if (!HOOK_BOUND_CHECK(hook, addr))
|
||||||
((uc_cb_hookmem_t)trace->callback)(env->uc, UC_MEM_READ,
|
continue;
|
||||||
(uint64_t)addr, (int)DATA_SIZE, (int64_t)0, trace->user_data);
|
((uc_cb_hookmem_t)hook->callback)(env->uc, UC_MEM_READ, addr, DATA_SIZE, 0, hook->user_data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unicorn: callback on non-readable memory
|
// Unicorn: callback on non-readable memory
|
||||||
if (READ_ACCESS_TYPE == MMU_DATA_LOAD && mr != NULL && !(mr->perms & UC_PROT_READ)) { //non-readable
|
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)(
|
handled = false;
|
||||||
uc, UC_MEM_READ_PROT, addr, DATA_SIZE, 0,
|
HOOK_FOREACH(uc, hook, UC_HOOK_MEM_READ_PROT) {
|
||||||
uc->hook_callbacks[uc->hook_mem_read_prot_idx].user_data)) {
|
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;
|
env->invalid_error = UC_ERR_OK;
|
||||||
} else {
|
} else {
|
||||||
env->invalid_addr = addr;
|
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);
|
int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
|
||||||
target_ulong tlb_addr = env->tlb_table[mmu_idx][index].addr_write;
|
target_ulong tlb_addr = env->tlb_table[mmu_idx][index].addr_write;
|
||||||
uintptr_t haddr;
|
uintptr_t haddr;
|
||||||
|
struct hook *hook;
|
||||||
|
bool handled;
|
||||||
|
|
||||||
struct uc_struct *uc = env->uc;
|
struct uc_struct *uc = env->uc;
|
||||||
MemoryRegion *mr = memory_mapping(uc, addr);
|
MemoryRegion *mr = memory_mapping(uc, addr);
|
||||||
|
|
||||||
// Unicorn: callback on memory write
|
// Unicorn: callback on memory write
|
||||||
if (uc->hook_mem_write) {
|
HOOK_FOREACH(uc, hook, UC_HOOK_MEM_WRITE) {
|
||||||
struct hook_struct *trace = hook_find(uc, UC_HOOK_MEM_WRITE, addr);
|
if (!HOOK_BOUND_CHECK(hook, addr))
|
||||||
if (trace) {
|
continue;
|
||||||
((uc_cb_hookmem_t)trace->callback)(uc, UC_MEM_WRITE,
|
((uc_cb_hookmem_t)hook->callback)(uc, UC_MEM_WRITE, addr, DATA_SIZE, val, hook->user_data);
|
||||||
(uint64_t)addr, (int)DATA_SIZE, (int64_t)val, trace->user_data);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unicorn: callback on invalid memory
|
// Unicorn: callback on invalid memory
|
||||||
if (uc->hook_mem_write_idx && mr == NULL) {
|
if (mr == NULL) {
|
||||||
if (!((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_write_idx].callback)(
|
handled = false;
|
||||||
uc, UC_MEM_WRITE_UNMAPPED, addr, DATA_SIZE, (int64_t)val,
|
HOOK_FOREACH(uc, hook, UC_HOOK_MEM_WRITE_UNMAPPED) {
|
||||||
uc->hook_callbacks[uc->hook_mem_write_idx].user_data)) {
|
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
|
// save error & quit
|
||||||
env->invalid_addr = addr;
|
env->invalid_addr = addr;
|
||||||
env->invalid_error = UC_ERR_WRITE_UNMAPPED;
|
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
|
// Unicorn: callback on non-writable memory
|
||||||
if (mr != NULL && !(mr->perms & UC_PROT_WRITE)) { //non-writable
|
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)(
|
handled = false;
|
||||||
uc, UC_MEM_WRITE_PROT, addr, DATA_SIZE, (int64_t)val,
|
HOOK_FOREACH(uc, hook, UC_HOOK_MEM_WRITE_PROT) {
|
||||||
uc->hook_callbacks[uc->hook_mem_write_prot_idx].user_data)) {
|
if (!HOOK_BOUND_CHECK(hook, addr))
|
||||||
env->invalid_error = UC_ERR_OK;
|
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_addr = addr;
|
||||||
env->invalid_error = UC_ERR_WRITE_PROT;
|
env->invalid_error = UC_ERR_WRITE_PROT;
|
||||||
// printf("***** Invalid memory write (ro) at " TARGET_FMT_lx "\n", addr);
|
// 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);
|
int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
|
||||||
target_ulong tlb_addr = env->tlb_table[mmu_idx][index].addr_write;
|
target_ulong tlb_addr = env->tlb_table[mmu_idx][index].addr_write;
|
||||||
uintptr_t haddr;
|
uintptr_t haddr;
|
||||||
|
struct hook *hook;
|
||||||
|
bool handled;
|
||||||
|
|
||||||
struct uc_struct *uc = env->uc;
|
struct uc_struct *uc = env->uc;
|
||||||
MemoryRegion *mr = memory_mapping(uc, addr);
|
MemoryRegion *mr = memory_mapping(uc, addr);
|
||||||
|
|
||||||
// Unicorn: callback on memory write
|
// Unicorn: callback on memory write
|
||||||
if (uc->hook_mem_write) {
|
HOOK_FOREACH(uc, hook, UC_HOOK_MEM_WRITE) {
|
||||||
struct hook_struct *trace = hook_find(uc, UC_HOOK_MEM_WRITE, addr);
|
if (!HOOK_BOUND_CHECK(hook, addr))
|
||||||
if (trace) {
|
continue;
|
||||||
((uc_cb_hookmem_t)trace->callback)(uc, UC_MEM_WRITE,
|
((uc_cb_hookmem_t)hook->callback)(uc, UC_MEM_WRITE, addr, DATA_SIZE, val, hook->user_data);
|
||||||
(uint64_t)addr, (int)DATA_SIZE, (int64_t)val, trace->user_data);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unicorn: callback on invalid memory
|
// Unicorn: callback on invalid memory
|
||||||
if (uc->hook_mem_write_idx && mr == NULL) {
|
if (mr == NULL) {
|
||||||
if (!((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_write_idx].callback)(
|
handled = false;
|
||||||
uc, UC_MEM_WRITE_UNMAPPED, addr, DATA_SIZE, (int64_t)val,
|
HOOK_FOREACH(uc, hook, UC_HOOK_MEM_WRITE_UNMAPPED) {
|
||||||
uc->hook_callbacks[uc->hook_mem_write_idx].user_data)) {
|
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
|
// save error & quit
|
||||||
env->invalid_addr = addr;
|
env->invalid_addr = addr;
|
||||||
env->invalid_error = UC_ERR_WRITE_UNMAPPED;
|
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
|
// Unicorn: callback on non-writable memory
|
||||||
if (mr != NULL && !(mr->perms & UC_PROT_WRITE)) { //non-writable
|
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)(
|
handled = false;
|
||||||
uc, UC_MEM_WRITE_PROT, addr, DATA_SIZE, (int64_t)val,
|
HOOK_FOREACH(uc, hook, UC_HOOK_MEM_WRITE_PROT) {
|
||||||
uc->hook_callbacks[uc->hook_mem_write_prot_idx].user_data)) {
|
if (!HOOK_BOUND_CHECK(hook, addr))
|
||||||
env->invalid_error = UC_ERR_OK;
|
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_addr = addr;
|
||||||
env->invalid_error = UC_ERR_WRITE_PROT;
|
env->invalid_error = UC_ERR_WRITE_PROT;
|
||||||
// printf("***** Invalid memory write (ro) at " TARGET_FMT_lx "\n", addr);
|
// printf("***** Invalid memory write (ro) at " TARGET_FMT_lx "\n", addr);
|
||||||
|
|
|
@ -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)
|
DEF_HELPER_FLAGS_1(clz_arm, TCG_CALL_NO_RWG_SE, i32, i32)
|
||||||
|
|
||||||
|
|
|
@ -10984,10 +10984,8 @@ static void disas_a64_insn(CPUARMState *env, DisasContext *s)
|
||||||
s->pc += 4;
|
s->pc += 4;
|
||||||
|
|
||||||
// Unicorn: trace this instruction on request
|
// Unicorn: trace this instruction on request
|
||||||
if (env->uc->hook_insn) {
|
if (HOOK_EXISTS_BOUNDED(env->uc, UC_HOOK_CODE, s->pc - 4)) {
|
||||||
struct hook_struct *trace = hook_find(s->uc, UC_HOOK_CODE, s->pc - 4);
|
gen_uc_tracecode(tcg_ctx, 4, UC_HOOK_CODE_IDX, env->uc, s->pc - 4);
|
||||||
if (trace)
|
|
||||||
gen_uc_tracecode(tcg_ctx, 4, trace->callback, env->uc, s->pc - 4, trace->user_data);
|
|
||||||
// the callback might want to stop emulation immediately
|
// the callback might want to stop emulation immediately
|
||||||
check_exit_request(tcg_ctx);
|
check_exit_request(tcg_ctx);
|
||||||
}
|
}
|
||||||
|
@ -11114,13 +11112,10 @@ void gen_intermediate_code_internal_a64(ARMCPU *cpu,
|
||||||
// Unicorn: trace this block on request
|
// Unicorn: trace this block on request
|
||||||
// Only hook this block if it is not broken from previous translation due to
|
// Only hook this block if it is not broken from previous translation due to
|
||||||
// full translation cache
|
// full translation cache
|
||||||
if (env->uc->hook_block && !env->uc->block_full) {
|
if (!env->uc->block_full && HOOK_EXISTS_BOUNDED(env->uc, UC_HOOK_BLOCK, pc_start)) {
|
||||||
struct hook_struct *trace = hook_find(env->uc, UC_HOOK_BLOCK, pc_start);
|
// save block address to see if we need to patch block size later
|
||||||
if (trace) {
|
env->uc->block_addr = pc_start;
|
||||||
// save block address to see if we need to patch block size later
|
gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, UC_HOOK_BLOCK_IDX, env->uc, pc_start);
|
||||||
env->uc->block_addr = pc_start;
|
|
||||||
gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, trace->callback, env->uc, pc_start, trace->user_data);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
gen_tb_start(tcg_ctx);
|
gen_tb_start(tcg_ctx);
|
||||||
|
|
|
@ -7687,10 +7687,8 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) // qq
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unicorn: trace this instruction on request
|
// Unicorn: trace this instruction on request
|
||||||
if (s->uc->hook_insn) {
|
if (HOOK_EXISTS_BOUNDED(s->uc, UC_HOOK_CODE, s->pc - 4)) {
|
||||||
struct hook_struct *trace = hook_find(s->uc, UC_HOOK_CODE, s->pc - 4);
|
gen_uc_tracecode(tcg_ctx, 4, UC_HOOK_CODE_IDX, s->uc, s->pc - 4);
|
||||||
if (trace)
|
|
||||||
gen_uc_tracecode(tcg_ctx, 4, trace->callback, s->uc, s->pc - 4, trace->user_data);
|
|
||||||
// the callback might want to stop emulation immediately
|
// the callback might want to stop emulation immediately
|
||||||
check_exit_request(tcg_ctx);
|
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
|
// Unicorn: trace this instruction on request
|
||||||
if (env->uc->hook_insn) {
|
if (HOOK_EXISTS_BOUNDED(s->uc, UC_HOOK_CODE, s->pc)) {
|
||||||
struct hook_struct *trace = hook_find(s->uc, UC_HOOK_CODE, s->pc);
|
gen_uc_tracecode(tcg_ctx, 2, UC_HOOK_CODE_IDX, s->uc, s->pc);
|
||||||
if (trace)
|
// the callback might want to stop emulation immediately
|
||||||
gen_uc_tracecode(tcg_ctx, 2, trace->callback, env->uc, s->pc, trace->user_data);
|
check_exit_request(tcg_ctx);
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
insn = arm_lduw_code(env, s->pc, s->bswap_code);
|
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
|
// Unicorn: trace this block on request
|
||||||
// Only hook this block if it is not broken from previous translation due to
|
// Only hook this block if it is not broken from previous translation due to
|
||||||
// full translation cache
|
// full translation cache
|
||||||
if (env->uc->hook_block && !env->uc->block_full) {
|
if (!env->uc->block_full && HOOK_EXISTS_BOUNDED(env->uc, UC_HOOK_BLOCK, pc_start)) {
|
||||||
struct hook_struct *trace = hook_find(env->uc, UC_HOOK_BLOCK, pc_start);
|
// save block address to see if we need to patch block size later
|
||||||
if (trace) {
|
env->uc->block_addr = pc_start;
|
||||||
// save block address to see if we need to patch block size later
|
gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, UC_HOOK_BLOCK_IDX, env->uc, pc_start);
|
||||||
env->uc->block_addr = pc_start;
|
|
||||||
gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, trace->callback, env->uc, pc_start, trace->user_data);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
gen_tb_start(tcg_ctx);
|
gen_tb_start(tcg_ctx);
|
||||||
|
|
|
@ -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_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)
|
DEF_HELPER_FLAGS_4(cc_compute_c, TCG_CALL_NO_RWG_SE, tl, tl, tl, tl, int)
|
||||||
|
|
|
@ -945,14 +945,16 @@ void helper_syscall(CPUX86State *env, int next_eip_addend)
|
||||||
#else
|
#else
|
||||||
void helper_syscall(CPUX86State *env, int next_eip_addend)
|
void helper_syscall(CPUX86State *env, int next_eip_addend)
|
||||||
{
|
{
|
||||||
// Unicorn: call interrupt callback if registered
|
// Unicorn: call registered syscall hooks
|
||||||
struct uc_struct *uc = env->uc;
|
struct hook *hook;
|
||||||
if (uc->hook_syscall_idx) {
|
HOOK_FOREACH(env->uc, hook, UC_HOOK_INSN) {
|
||||||
((uc_cb_insn_syscall_t)uc->hook_callbacks[uc->hook_syscall_idx].callback)(
|
if (!HOOK_BOUND_CHECK(hook, env->eip))
|
||||||
uc, uc->hook_callbacks[uc->hook_syscall_idx].user_data);
|
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;
|
return;
|
||||||
|
|
||||||
int selector;
|
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)
|
void helper_sysenter(CPUX86State *env, int next_eip_addend)
|
||||||
{
|
{
|
||||||
// Unicorn: call interrupt callback if registered
|
// Unicorn: call registered SYSENTER hooks
|
||||||
struct uc_struct *uc = env->uc;
|
struct hook *hook;
|
||||||
if (uc->hook_syscall_idx) {
|
HOOK_FOREACH(env->uc, hook, UC_HOOK_INSN) {
|
||||||
((uc_cb_insn_syscall_t)uc->hook_callbacks[uc->hook_syscall_idx].callback)(
|
if (!HOOK_BOUND_CHECK(hook, env->eip))
|
||||||
uc, uc->hook_callbacks[uc->hook_syscall_idx].user_data);
|
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;
|
return;
|
||||||
|
|
||||||
if (env->sysenter_cs == 0) {
|
if (env->sysenter_cs == 0) {
|
||||||
|
|
|
@ -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)
|
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
|
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);
|
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)
|
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
|
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);
|
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_tmp4 = *(TCGv *)tcg_ctx->cpu_tmp4;
|
||||||
TCGv **cpu_T = (TCGv **)tcg_ctx->cpu_T;
|
TCGv **cpu_T = (TCGv **)tcg_ctx->cpu_T;
|
||||||
TCGv **cpu_regs = (TCGv **)tcg_ctx->cpu_regs;
|
TCGv **cpu_regs = (TCGv **)tcg_ctx->cpu_regs;
|
||||||
struct hook_struct *trace = NULL;
|
|
||||||
TCGArg *save_opparam_ptr = tcg_ctx->gen_opparam_ptr;
|
TCGArg *save_opparam_ptr = tcg_ctx->gen_opparam_ptr;
|
||||||
bool cc_op_dirty = s->cc_op_dirty;
|
bool cc_op_dirty = s->cc_op_dirty;
|
||||||
bool changed_cc_op = false;
|
bool changed_cc_op = false;
|
||||||
|
|
||||||
|
|
||||||
s->pc = pc_start;
|
s->pc = pc_start;
|
||||||
|
|
||||||
// end address tells us to stop emulation
|
// 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
|
// Unicorn: trace this instruction on request
|
||||||
if (env->uc->hook_insn) {
|
if (HOOK_EXISTS_BOUNDED(env->uc, UC_HOOK_CODE, pc_start)) {
|
||||||
trace = hook_find(env->uc, UC_HOOK_CODE, pc_start);
|
if (s->last_cc_op != s->cc_op) {
|
||||||
if (trace) {
|
sync_eflags(s, tcg_ctx);
|
||||||
if (s->last_cc_op != s->cc_op) {
|
s->last_cc_op = s->cc_op;
|
||||||
sync_eflags(s, tcg_ctx);
|
changed_cc_op = true;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
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;
|
prefixes = 0;
|
||||||
|
@ -8173,7 +8167,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
|
||||||
gen_helper_unlock(tcg_ctx, cpu_env);
|
gen_helper_unlock(tcg_ctx, cpu_env);
|
||||||
|
|
||||||
// Unicorn: patch the callback for the instruction size
|
// Unicorn: patch the callback for the instruction size
|
||||||
if (trace) {
|
if (HOOK_EXISTS_BOUNDED(env->uc, UC_HOOK_CODE, pc_start)) {
|
||||||
// int i;
|
// int i;
|
||||||
// for(i = 0; i < 20; i++)
|
// for(i = 0; i < 20; i++)
|
||||||
// printf("=== [%u] = %x\n", i, *(save_opparam_ptr + 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
|
// Unicorn: trace this block on request
|
||||||
// Only hook this block if it is not broken from previous translation due to
|
// Only hook this block if it is not broken from previous translation due to
|
||||||
// full translation cache
|
// full translation cache
|
||||||
if (env->uc->hook_block && !env->uc->block_full) {
|
if (!env->uc->block_full && HOOK_EXISTS_BOUNDED(env->uc, UC_HOOK_BLOCK, pc_start)) {
|
||||||
struct hook_struct *trace = hook_find(env->uc, UC_HOOK_BLOCK, pc_start);
|
env->uc->block_addr = pc_start;
|
||||||
if (trace) {
|
gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, UC_HOOK_BLOCK_IDX, env->uc, pc_start);
|
||||||
env->uc->block_addr = pc_start;
|
|
||||||
gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, trace->callback, env->uc, pc_start, trace->user_data);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
gen_tb_start(tcg_ctx);
|
gen_tb_start(tcg_ctx);
|
||||||
|
|
|
@ -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(bitrev, i32, i32)
|
||||||
DEF_HELPER_1(ff1, i32, i32)
|
DEF_HELPER_1(ff1, i32, i32)
|
||||||
|
|
|
@ -3043,11 +3043,8 @@ static void disas_m68k_insn(CPUM68KState * env, DisasContext *s)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unicorn: trace this instruction on request
|
// Unicorn: trace this instruction on request
|
||||||
if (env->uc->hook_insn) {
|
if (HOOK_EXISTS_BOUNDED(env->uc, UC_HOOK_CODE, s->pc)) {
|
||||||
struct hook_struct *trace = hook_find(env->uc, UC_HOOK_CODE, s->pc);
|
gen_uc_tracecode(tcg_ctx, 2, UC_HOOK_CODE_IDX, env->uc, s->pc);
|
||||||
if (trace)
|
|
||||||
gen_uc_tracecode(tcg_ctx, 2, trace->callback, env->uc, s->pc, trace->user_data);
|
|
||||||
|
|
||||||
// the callback might want to stop emulation immediately
|
// the callback might want to stop emulation immediately
|
||||||
check_exit_request(tcg_ctx);
|
check_exit_request(tcg_ctx);
|
||||||
}
|
}
|
||||||
|
@ -3109,13 +3106,10 @@ gen_intermediate_code_internal(M68kCPU *cpu, TranslationBlock *tb,
|
||||||
// Unicorn: trace this block on request
|
// Unicorn: trace this block on request
|
||||||
// Only hook this block if it is not broken from previous translation due to
|
// Only hook this block if it is not broken from previous translation due to
|
||||||
// full translation cache
|
// full translation cache
|
||||||
if (env->uc->hook_block && !env->uc->block_full) {
|
if (!env->uc->block_full && HOOK_EXISTS_BOUNDED(env->uc, UC_HOOK_BLOCK, pc_start)) {
|
||||||
struct hook_struct *trace = hook_find(env->uc, UC_HOOK_BLOCK, pc_start);
|
// save block address to see if we need to patch block size later
|
||||||
if (trace) {
|
env->uc->block_addr = pc_start;
|
||||||
// save block address to see if we need to patch block size later
|
gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, UC_HOOK_BLOCK_IDX, env->uc, pc_start);
|
||||||
env->uc->block_addr = pc_start;
|
|
||||||
gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, trace->callback, env->uc, pc_start, trace->user_data);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
gen_tb_start(tcg_ctx);
|
gen_tb_start(tcg_ctx);
|
||||||
|
|
|
@ -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_3(raise_exception_err, noreturn, env, i32, int)
|
||||||
DEF_HELPER_2(raise_exception, noreturn, env, i32)
|
DEF_HELPER_2(raise_exception, noreturn, env, i32)
|
||||||
|
|
|
@ -11343,12 +11343,9 @@ static int decode_mips16_opc (CPUMIPSState *env, DisasContext *ctx, bool *insn_n
|
||||||
n_bytes = 2;
|
n_bytes = 2;
|
||||||
|
|
||||||
// Unicorn: trace this instruction on request
|
// Unicorn: trace this instruction on request
|
||||||
if (env->uc->hook_insn) {
|
if (HOOK_EXISTS_BOUNDED(env->uc, UC_HOOK_CODE, ctx->pc)) {
|
||||||
struct hook_struct *trace = hook_find(env->uc, UC_HOOK_CODE, ctx->pc);
|
gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, UC_HOOK_CODE_IDX, env->uc, ctx->pc);
|
||||||
if (trace) {
|
*insn_need_patch = true;
|
||||||
gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, trace->callback, env->uc, ctx->pc, trace->user_data);
|
|
||||||
*insn_need_patch = true;
|
|
||||||
}
|
|
||||||
// the callback might want to stop emulation immediately
|
// the callback might want to stop emulation immediately
|
||||||
check_exit_request(tcg_ctx);
|
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
|
// Unicorn: trace this instruction on request
|
||||||
if (env->uc->hook_insn) {
|
if (HOOK_EXISTS_BOUNDED(env->uc, UC_HOOK_CODE, ctx->pc)) {
|
||||||
struct hook_struct *trace = hook_find(env->uc, UC_HOOK_CODE, ctx->pc);
|
gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, UC_HOOK_CODE_IDX, env->uc, ctx->pc);
|
||||||
if (trace) {
|
*insn_need_patch = true;
|
||||||
gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, trace->callback, env->uc, ctx->pc, trace->user_data);
|
|
||||||
*insn_need_patch = true;
|
|
||||||
}
|
|
||||||
// the callback might want to stop emulation immediately
|
// the callback might want to stop emulation immediately
|
||||||
check_exit_request(tcg_ctx);
|
check_exit_request(tcg_ctx);
|
||||||
}
|
}
|
||||||
|
@ -18504,13 +18498,10 @@ static void gen_msa(CPUMIPSState *env, DisasContext *ctx)
|
||||||
// Unicorn: trace this instruction on request
|
// 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)
|
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;
|
||||||
TCGContext *tcg_ctx = ctx->uc->tcg_ctx;
|
if (HOOK_EXISTS_BOUNDED(env->uc, UC_HOOK_CODE, ctx->pc)) {
|
||||||
struct hook_struct *trace = hook_find(env->uc, UC_HOOK_CODE, ctx->pc);
|
gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, UC_HOOK_CODE_IDX, env->uc, ctx->pc);
|
||||||
if (trace) {
|
*insn_need_patch = true;
|
||||||
gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, trace->callback, env->uc, ctx->pc, trace->user_data);
|
|
||||||
*insn_need_patch = true;
|
|
||||||
}
|
|
||||||
// the callback might want to stop emulation immediately
|
// the callback might want to stop emulation immediately
|
||||||
check_exit_request(tcg_ctx);
|
check_exit_request(tcg_ctx);
|
||||||
*insn_patch_offset = offset_value;
|
*insn_patch_offset = offset_value;
|
||||||
|
@ -19223,13 +19214,10 @@ gen_intermediate_code_internal(MIPSCPU *cpu, TranslationBlock *tb,
|
||||||
// Unicorn: trace this block on request
|
// Unicorn: trace this block on request
|
||||||
// Only hook this block if it is not broken from previous translation due to
|
// Only hook this block if it is not broken from previous translation due to
|
||||||
// full translation cache
|
// full translation cache
|
||||||
if (env->uc->hook_block && !env->uc->block_full) {
|
if (!env->uc->block_full && HOOK_EXISTS_BOUNDED(env->uc, UC_HOOK_BLOCK, pc_start)) {
|
||||||
struct hook_struct *trace = hook_find(env->uc, UC_HOOK_BLOCK, pc_start);
|
// save block address to see if we need to patch block size later
|
||||||
if (trace) {
|
env->uc->block_addr = pc_start;
|
||||||
// save block address to see if we need to patch block size later
|
gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, UC_HOOK_BLOCK_IDX, env->uc, pc_start);
|
||||||
env->uc->block_addr = pc_start;
|
|
||||||
gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, trace->callback, env->uc, pc_start, trace->user_data);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
gen_tb_start(tcg_ctx);
|
gen_tb_start(tcg_ctx);
|
||||||
|
@ -19275,7 +19263,7 @@ gen_intermediate_code_internal(MIPSCPU *cpu, TranslationBlock *tb,
|
||||||
int insn_patch_offset = 1;
|
int insn_patch_offset = 1;
|
||||||
|
|
||||||
// Unicorn: save param buffer
|
// 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;
|
save_opparam_ptr = tcg_ctx->gen_opparam_ptr;
|
||||||
|
|
||||||
is_slot = ctx.hflags & MIPS_HFLAG_BMASK;
|
is_slot = ctx.hflags & MIPS_HFLAG_BMASK;
|
||||||
|
|
|
@ -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)
|
DEF_HELPER_1(power_down, void, env)
|
||||||
|
|
||||||
#ifndef TARGET_SPARC64
|
#ifndef TARGET_SPARC64
|
||||||
|
|
|
@ -2637,11 +2637,8 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn, bool hook_ins
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unicorn: trace this instruction on request
|
// Unicorn: trace this instruction on request
|
||||||
if (hook_insn && dc->uc->hook_insn) {
|
if (hook_insn && HOOK_EXISTS_BOUNDED(dc->uc, UC_HOOK_CODE, dc->pc)) {
|
||||||
struct hook_struct *trace = hook_find(dc->uc, UC_HOOK_CODE, dc->pc);
|
gen_uc_tracecode(tcg_ctx, 4, UC_HOOK_CODE_IDX, dc->uc, 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
|
// the callback might want to stop emulation immediately
|
||||||
check_exit_request(tcg_ctx);
|
check_exit_request(tcg_ctx);
|
||||||
}
|
}
|
||||||
|
@ -5428,13 +5425,10 @@ static inline void gen_intermediate_code_internal(SPARCCPU *cpu,
|
||||||
// Unicorn: trace this block on request
|
// Unicorn: trace this block on request
|
||||||
// Only hook this block if it is not broken from previous translation due to
|
// Only hook this block if it is not broken from previous translation due to
|
||||||
// full translation cache
|
// full translation cache
|
||||||
if (env->uc->hook_block && !env->uc->block_full) {
|
if (!env->uc->block_full && HOOK_EXISTS_BOUNDED(env->uc, UC_HOOK_BLOCK, pc_start)) {
|
||||||
struct hook_struct *trace = hook_find(env->uc, UC_HOOK_BLOCK, pc_start);
|
// save block address to see if we need to patch block size later
|
||||||
if (trace) {
|
env->uc->block_addr = pc_start;
|
||||||
// save block address to see if we need to patch block size later
|
gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, UC_HOOK_BLOCK_IDX, env->uc, pc_start);
|
||||||
env->uc->block_addr = pc_start;
|
|
||||||
gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, trace->callback, env->uc, pc_start, trace->user_data);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
gen_tb_start(tcg_ctx);
|
gen_tb_start(tcg_ctx);
|
||||||
|
|
|
@ -1209,7 +1209,7 @@ static inline void tcg_out_tlb_load(TCGContext *s, TCGReg addrlo, TCGReg addrhi,
|
||||||
tcg_out_mov(s, ttype, r1, addrlo);
|
tcg_out_mov(s, ttype, r1, addrlo);
|
||||||
|
|
||||||
// Unicorn: fast path if hookmem is not enable
|
// 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);
|
tcg_out_opc(s, OPC_JCC_long + JCC_JNE, 0, 0, 0);
|
||||||
else
|
else
|
||||||
tcg_out_opc(s, OPC_JMP_long, 0, 0, 0); /* slow_path */
|
tcg_out_opc(s, OPC_JMP_long, 0, 0, 0); /* slow_path */
|
||||||
|
|
|
@ -27,14 +27,13 @@
|
||||||
|
|
||||||
int gen_new_label(TCGContext *);
|
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_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_ptr tuc = tcg_const_ptr(tcg_ctx, uc);
|
||||||
TCGv_i64 tpc = tcg_const_i64(tcg_ctx, pc);
|
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, ttype, tuc, tpc);
|
||||||
gen_helper_uc_tracecode(tcg_ctx, tsize, tcallback, tuc, tpc, tdata);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void tcg_gen_op0(TCGContext *s, TCGOpcode opc)
|
static inline void tcg_gen_op0(TCGContext *s, TCGOpcode opc)
|
||||||
|
|
|
@ -179,7 +179,7 @@ static int cpu_gen_code(CPUArchState *env, TranslationBlock *tb, int *gen_code_s
|
||||||
gen_intermediate_code(env, tb);
|
gen_intermediate_code(env, tb);
|
||||||
|
|
||||||
// Unicorn: when tracing block, patch 1st operand for block size
|
// 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
|
if (env->uc->block_full) // block size is unknown
|
||||||
*(s->gen_opparam_buf + 1) = 0;
|
*(s->gen_opparam_buf + 1) = 0;
|
||||||
else
|
else
|
||||||
|
|
|
@ -142,13 +142,13 @@ static void do_nx_demo(bool cause_fault)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
bits 32
|
bits 32
|
||||||
page0:
|
page0: @0
|
||||||
times 4091 inc eax
|
times 4091 inc eax
|
||||||
jmp page2
|
jmp page2
|
||||||
page1:
|
page1: @1000
|
||||||
times 4095 inc eax
|
times 4095 inc eax (or INC ECX)
|
||||||
hlt
|
hlt
|
||||||
page2:
|
page2: @2000
|
||||||
jmp page1
|
jmp page1
|
||||||
*/
|
*/
|
||||||
memset(code_buf, 0x40, sizeof(code_buf)); // fill with inc eax
|
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
|
// 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 ||
|
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,
|
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");
|
printf("not ok - Failed to install hooks\n");
|
||||||
return;
|
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 ||
|
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_add(uc, &trace1,
|
||||||
UC_HOOK_MEM_INVALID,
|
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");
|
printf("not ok - Failed to install hooks\n");
|
||||||
return;
|
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 ||
|
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_add(uc, &trace1,
|
||||||
UC_HOOK_MEM_INVALID,
|
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");
|
printf("not ok - Failed to install hooks\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ CFLAGS += -lcmocka -lunicorn
|
||||||
CFLAGS += -I ../../include
|
CFLAGS += -I ../../include
|
||||||
|
|
||||||
ALL_TESTS = test_sanity test_x86 test_mem_map test_mem_high test_mem_map_ptr \
|
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
|
.PHONY: all
|
||||||
all: ${ALL_TESTS}
|
all: ${ALL_TESTS}
|
||||||
|
@ -23,6 +23,7 @@ test: ${ALL_TESTS}
|
||||||
./test_mem_map_ptr
|
./test_mem_map_ptr
|
||||||
./test_mem_high
|
./test_mem_high
|
||||||
./test_tb_x86
|
./test_tb_x86
|
||||||
|
./test_multihook
|
||||||
|
|
||||||
test_sanity: test_sanity.c
|
test_sanity: test_sanity.c
|
||||||
test_x86: test_x86.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_map_ptr: test_mem_map_ptr.c
|
||||||
test_mem_high: test_mem_high.c
|
test_mem_high: test_mem_high.c
|
||||||
test_tb_x86: test_tb_x86.c
|
test_tb_x86: test_tb_x86.c
|
||||||
|
test_multihook: test_multihook.c
|
||||||
|
|
||||||
${ALL_TESTS}:
|
${ALL_TESTS}:
|
||||||
${CC} ${CFLAGS} -o $@ $^
|
${CC} ${CFLAGS} -o $@ $^
|
||||||
|
|
111
tests/unit/test_multihook.c
Normal file
111
tests/unit/test_multihook.c
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
#include "unicorn_test.h"
|
||||||
|
#include <inttypes.h>
|
||||||
|
|
||||||
|
#define OK(x) uc_assert_success(x)
|
||||||
|
|
||||||
|
/* Called before every test to set up a new instance */
|
||||||
|
static int setup32(void **state)
|
||||||
|
{
|
||||||
|
uc_engine *uc;
|
||||||
|
|
||||||
|
OK(uc_open(UC_ARCH_X86, UC_MODE_32, &uc));
|
||||||
|
|
||||||
|
*state = uc;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Called after every test to clean up */
|
||||||
|
static int teardown(void **state)
|
||||||
|
{
|
||||||
|
uc_engine *uc = *state;
|
||||||
|
|
||||||
|
OK(uc_close(uc));
|
||||||
|
|
||||||
|
*state = NULL;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
337
uc.c
337
uc.c
|
@ -20,7 +20,6 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "uc_priv.h"
|
#include "uc_priv.h"
|
||||||
#include "hook.h"
|
|
||||||
|
|
||||||
// target specific headers
|
// target specific headers
|
||||||
#include "qemu/target-m68k/unicorn.h"
|
#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)";
|
return "Write to unaligned memory (UC_ERR_WRITE_UNALIGNED)";
|
||||||
case UC_ERR_FETCH_UNALIGNED:
|
case UC_ERR_FETCH_UNALIGNED:
|
||||||
return "Fetch from unaligned memory (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:
|
case UC_ERR_RESOURCE:
|
||||||
return "Insufficient resource (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)
|
if (uc->reg_reset)
|
||||||
uc->reg_reset(uc);
|
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;
|
return UC_ERR_OK;
|
||||||
} else {
|
} else {
|
||||||
return UC_ERR_ARCH;
|
return UC_ERR_ARCH;
|
||||||
|
@ -285,6 +279,8 @@ UNICORN_EXPORT
|
||||||
uc_err uc_close(uc_engine *uc)
|
uc_err uc_close(uc_engine *uc)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
struct list_item *cur;
|
||||||
|
struct hook *hook;
|
||||||
|
|
||||||
if (uc->release)
|
if (uc->release)
|
||||||
uc->release(uc->tcg_ctx);
|
uc->release(uc->tcg_ctx);
|
||||||
|
@ -310,7 +306,20 @@ uc_err uc_close(uc_engine *uc)
|
||||||
// TODO: remove uc->root (created with object_new())
|
// TODO: remove uc->root (created with object_new())
|
||||||
uc->root->free(uc->root);
|
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);
|
free(uc->mapped_blocks);
|
||||||
|
|
||||||
|
@ -471,6 +480,15 @@ static void enable_emu_timer(uc_engine *uc, uint64_t timeout)
|
||||||
uc, QEMU_THREAD_JOINABLE);
|
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
|
UNICORN_EXPORT
|
||||||
uc_err uc_emu_start(uc_engine* uc, uint64_t begin, uint64_t until, uint64_t timeout, size_t count)
|
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;
|
uc->emu_count = count;
|
||||||
if (count > 0) {
|
// remove count hook if counting isn't necessary
|
||||||
uc->hook_insn = true;
|
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;
|
uc->addr_end = until;
|
||||||
|
@ -565,37 +592,6 @@ uc_err uc_emu_stop(uc_engine *uc)
|
||||||
return UC_ERR_OK;
|
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
|
// find if a memory range overlaps with existing mapped regions
|
||||||
static bool memory_overlap(struct uc_struct *uc, uint64_t begin, size_t size)
|
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;
|
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
|
UNICORN_EXPORT
|
||||||
uc_err uc_hook_add(uc_engine *uc, uc_hook *hh, int type, void *callback, void *user_data, ...)
|
uc_err uc_hook_add(uc_engine *uc, uc_hook *hh, int type, void *callback, void *user_data, ...)
|
||||||
{
|
{
|
||||||
va_list valist;
|
va_list valist;
|
||||||
int ret = UC_ERR_OK;
|
int ret = UC_ERR_OK;
|
||||||
int id;
|
|
||||||
uint64_t begin, end;
|
|
||||||
|
|
||||||
va_start(valist, user_data);
|
va_start(valist, user_data);
|
||||||
|
|
||||||
if (type & UC_HOOK_MEM_READ_UNMAPPED)
|
struct hook *hook = calloc(1, sizeof(struct hook));
|
||||||
ret = _hook_mem_invalid(uc, UC_HOOK_MEM_READ_UNMAPPED, callback, user_data, hh);
|
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)
|
// everybody but HOOK_INSN gets begin/end, so exit early here.
|
||||||
ret = _hook_mem_invalid(uc, UC_HOOK_MEM_WRITE_UNMAPPED, callback, user_data, hh);
|
if (type & UC_HOOK_INSN) {
|
||||||
|
hook->insn = va_arg(valist, int);
|
||||||
if (type & UC_HOOK_MEM_FETCH_UNMAPPED)
|
hook->begin = 1;
|
||||||
ret = _hook_mem_invalid(uc, UC_HOOK_MEM_FETCH_UNMAPPED, callback, user_data, hh);
|
hook->end = 0;
|
||||||
|
if (list_append(&uc->hook[UC_HOOK_INSN_IDX], hook) == NULL) {
|
||||||
if (type & UC_HOOK_MEM_READ_PROT)
|
free(hook);
|
||||||
ret = _hook_mem_invalid(uc, UC_HOOK_MEM_READ_PROT, callback, user_data, hh);
|
return UC_ERR_NOMEM;
|
||||||
|
}
|
||||||
if (type & UC_HOOK_MEM_WRITE_PROT)
|
hook->refs++;
|
||||||
ret = _hook_mem_invalid(uc, UC_HOOK_MEM_WRITE_PROT, callback, user_data, hh);
|
return UC_ERR_OK;
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hook->begin = va_arg(valist, uint64_t);
|
||||||
|
hook->end = va_arg(valist, uint64_t);
|
||||||
va_end(valist);
|
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;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
UNICORN_EXPORT
|
UNICORN_EXPORT
|
||||||
uc_err uc_hook_del(uc_engine *uc, uc_hook hh)
|
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
|
UNICORN_EXPORT
|
||||||
|
|
Loading…
Reference in a new issue