Add a new hook type UC_HOOK_EDGE_GENERATED and corresponding sample
This commit is contained in:
parent
b7e82d460c
commit
c11b9aa5c3
|
@ -157,10 +157,19 @@ typedef enum uc_hook_idx {
|
||||||
UC_HOOK_MEM_FETCH_IDX,
|
UC_HOOK_MEM_FETCH_IDX,
|
||||||
UC_HOOK_MEM_READ_AFTER_IDX,
|
UC_HOOK_MEM_READ_AFTER_IDX,
|
||||||
UC_HOOK_INSN_INVALID_IDX,
|
UC_HOOK_INSN_INVALID_IDX,
|
||||||
|
UC_HOOK_EDGE_GENERATED_IDX,
|
||||||
|
|
||||||
UC_HOOK_MAX,
|
UC_HOOK_MAX,
|
||||||
} uc_hook_idx;
|
} uc_hook_idx;
|
||||||
|
|
||||||
|
// Copy the essential information from TranslationBlock
|
||||||
|
#define UC_TB_COPY(uc_tb, tb) \
|
||||||
|
do { \
|
||||||
|
(uc_tb)->pc = tb->pc; \
|
||||||
|
(uc_tb)->icount = tb->icount; \
|
||||||
|
(uc_tb)->size = tb->size; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
// The lowest 6 bits are used for hook type index.
|
// The lowest 6 bits are used for hook type index.
|
||||||
#define UC_HOOK_IDX_MASK ((1 << 6) - 1)
|
#define UC_HOOK_IDX_MASK ((1 << 6) - 1)
|
||||||
|
|
||||||
|
|
|
@ -232,6 +232,22 @@ typedef uint32_t (*uc_cb_insn_in_t)(uc_engine *uc, uint32_t port, int size,
|
||||||
typedef void (*uc_cb_insn_out_t)(uc_engine *uc, uint32_t port, int size,
|
typedef void (*uc_cb_insn_out_t)(uc_engine *uc, uint32_t port, int size,
|
||||||
uint32_t value, void *user_data);
|
uint32_t value, void *user_data);
|
||||||
|
|
||||||
|
// Represent a TranslationBlock.
|
||||||
|
typedef struct uc_tb {
|
||||||
|
uint64_t pc;
|
||||||
|
uint16_t icount;
|
||||||
|
uint16_t size;
|
||||||
|
} uc_tb;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Callback function for new edges between translation blocks.
|
||||||
|
|
||||||
|
@cur_tb: Current TB which is to be generated.
|
||||||
|
@prev_tb: The previous TB.
|
||||||
|
*/
|
||||||
|
typedef void (*uc_hook_edge_gen_t)(uc_engine *uc, uc_tb *cur_tb, uc_tb *prev_tb,
|
||||||
|
void *user_data);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Callback function for MMIO read
|
Callback function for MMIO read
|
||||||
|
|
||||||
|
@ -302,6 +318,12 @@ typedef enum uc_hook_type {
|
||||||
UC_HOOK_MEM_READ_AFTER = 1 << 13,
|
UC_HOOK_MEM_READ_AFTER = 1 << 13,
|
||||||
// Hook invalid instructions exceptions.
|
// Hook invalid instructions exceptions.
|
||||||
UC_HOOK_INSN_INVALID = 1 << 14,
|
UC_HOOK_INSN_INVALID = 1 << 14,
|
||||||
|
// Hook on new edge generation. Could be useful in program analysis.
|
||||||
|
//
|
||||||
|
// NOTE: This is different from UC_HOOK_BLOCK in 2 ways:
|
||||||
|
// 1. The hook is called before executing code.
|
||||||
|
// 2. The hook is only called when generation is triggered.
|
||||||
|
UC_HOOK_EDGE_GENERATED = 1 << 15
|
||||||
} uc_hook_type;
|
} uc_hook_type;
|
||||||
|
|
||||||
// Hook type for all events of unmapped memory access
|
// Hook type for all events of unmapped memory access
|
||||||
|
@ -391,13 +413,6 @@ typedef enum uc_query_type {
|
||||||
// result = True)
|
// result = True)
|
||||||
} uc_query_type;
|
} uc_query_type;
|
||||||
|
|
||||||
// Represent a TranslationBlock.
|
|
||||||
typedef struct uc_tb {
|
|
||||||
uint64_t pc;
|
|
||||||
uint16_t icount;
|
|
||||||
uint16_t size;
|
|
||||||
} uc_tb;
|
|
||||||
|
|
||||||
// The implementation of uc_ctl is like what Linux ioctl does but slightly
|
// The implementation of uc_ctl is like what Linux ioctl does but slightly
|
||||||
// different.
|
// different.
|
||||||
//
|
//
|
||||||
|
|
|
@ -245,6 +245,10 @@ static inline TranslationBlock *tb_find(CPUState *cpu,
|
||||||
TranslationBlock *tb;
|
TranslationBlock *tb;
|
||||||
target_ulong cs_base, pc;
|
target_ulong cs_base, pc;
|
||||||
uint32_t flags;
|
uint32_t flags;
|
||||||
|
uc_tb cur_tb, prev_tb;
|
||||||
|
uc_engine *uc = cpu->uc;
|
||||||
|
struct list_item *cur;
|
||||||
|
struct hook *hook;
|
||||||
|
|
||||||
tb = tb_lookup__cpu_state(cpu, &pc, &cs_base, &flags, cf_mask);
|
tb = tb_lookup__cpu_state(cpu, &pc, &cs_base, &flags, cf_mask);
|
||||||
if (tb == NULL) {
|
if (tb == NULL) {
|
||||||
|
@ -265,6 +269,23 @@ static inline TranslationBlock *tb_find(CPUState *cpu,
|
||||||
if (last_tb) {
|
if (last_tb) {
|
||||||
tb_add_jump(last_tb, tb_exit, tb);
|
tb_add_jump(last_tb, tb_exit, tb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UC_TB_COPY(&cur_tb, tb);
|
||||||
|
|
||||||
|
if (last_tb) {
|
||||||
|
UC_TB_COPY(&prev_tb, last_tb);
|
||||||
|
for (cur = uc->hook[UC_HOOK_EDGE_GENERATED_IDX].head;
|
||||||
|
cur != NULL && (hook = (struct hook *)cur->data); cur = cur->next) {
|
||||||
|
if (hook->to_delete) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (HOOK_BOUND_CHECK(hook, (uint64_t)tb->pc)) {
|
||||||
|
((uc_hook_edge_gen_t)hook->callback)(uc, &cur_tb, &prev_tb, hook->user_data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return tb;
|
return tb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1049,9 +1049,7 @@ static uc_err uc_gen_tb(struct uc_struct *uc, uint64_t addr, uc_tb *out_tb)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (out_tb != NULL) {
|
if (out_tb != NULL) {
|
||||||
out_tb->pc = tb->pc;
|
UC_TB_COPY(out_tb, tb);
|
||||||
out_tb->size = tb->size;
|
|
||||||
out_tb->icount = tb->icount;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return UC_ERR_OK;
|
return UC_ERR_OK;
|
||||||
|
|
|
@ -73,10 +73,17 @@ static void test_uc_ctl_read(void)
|
||||||
uc_close(uc);
|
uc_close(uc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void trace_new_edge(uc_engine *uc, uc_tb *cur, uc_tb *prev, void *data)
|
||||||
|
{
|
||||||
|
printf(">>> Getting a new edge from 0x%" PRIx64 " to 0x%" PRIx64 ".\n",
|
||||||
|
prev->pc + prev->size - 1, cur->pc);
|
||||||
|
}
|
||||||
|
|
||||||
void test_uc_ctl_exits()
|
void test_uc_ctl_exits()
|
||||||
{
|
{
|
||||||
uc_engine *uc;
|
uc_engine *uc;
|
||||||
uc_err err;
|
uc_err err;
|
||||||
|
uc_hook h;
|
||||||
int r_eax, r_ebx;
|
int r_eax, r_ebx;
|
||||||
uint64_t exits[] = {ADDRESS + 6, ADDRESS + 8};
|
uint64_t exits[] = {ADDRESS + 6, ADDRESS + 8};
|
||||||
|
|
||||||
|
@ -102,6 +109,14 @@ void test_uc_ctl_exits()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We trace if any new edge is generated.
|
||||||
|
err = uc_hook_add(uc, &h, UC_HOOK_EDGE_GENERATED, trace_new_edge, NULL, 0,
|
||||||
|
-1);
|
||||||
|
if (err) {
|
||||||
|
printf("Failed on uc_hook_add() with error returned: %u\n", err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Enable multiple exits.
|
// Enable multiple exits.
|
||||||
err = uc_ctl_exits_enabled(uc, true);
|
err = uc_ctl_exits_enabled(uc, true);
|
||||||
if (err) {
|
if (err) {
|
||||||
|
@ -183,6 +198,7 @@ static void test_uc_ctl_tb_cache()
|
||||||
uc_engine *uc;
|
uc_engine *uc;
|
||||||
uc_err err;
|
uc_err err;
|
||||||
uc_tb tb;
|
uc_tb tb;
|
||||||
|
uc_hook h;
|
||||||
char code[CODE_LEN];
|
char code[CODE_LEN];
|
||||||
double standard, cached, evicted;
|
double standard, cached, evicted;
|
||||||
|
|
||||||
|
@ -211,6 +227,17 @@ static void test_uc_ctl_tb_cache()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We trace if any new edge is generated.
|
||||||
|
// Note: In this sample, there is only **one** basic block while muliple
|
||||||
|
// translation blocks is generated due to QEMU tcg buffer limit. In this
|
||||||
|
// case, we don't consider it as a new edge.
|
||||||
|
err = uc_hook_add(uc, &h, UC_HOOK_EDGE_GENERATED, trace_new_edge, NULL, 0,
|
||||||
|
-1);
|
||||||
|
if (err) {
|
||||||
|
printf("Failed on uc_hook_add() with error returned: %u\n", err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Do emulation without any cache.
|
// Do emulation without any cache.
|
||||||
standard = time_emulation(uc, ADDRESS, ADDRESS + sizeof(code) - 1);
|
standard = time_emulation(uc, ADDRESS, ADDRESS + sizeof(code) - 1);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue