From c11b9aa5c3bd2500ce1fc8821f70d38d26987fd7 Mon Sep 17 00:00:00 2001 From: lazymio Date: Mon, 1 Nov 2021 23:27:35 +0100 Subject: [PATCH] Add a new hook type UC_HOOK_EDGE_GENERATED and corresponding sample --- include/uc_priv.h | 9 +++++++++ include/unicorn/unicorn.h | 29 ++++++++++++++++++++++------- qemu/accel/tcg/cpu-exec.c | 21 +++++++++++++++++++++ qemu/accel/tcg/translate-all.c | 4 +--- samples/sample_ctl.c | 27 +++++++++++++++++++++++++++ 5 files changed, 80 insertions(+), 10 deletions(-) diff --git a/include/uc_priv.h b/include/uc_priv.h index d4223d37..17978aaa 100644 --- a/include/uc_priv.h +++ b/include/uc_priv.h @@ -157,10 +157,19 @@ typedef enum uc_hook_idx { UC_HOOK_MEM_FETCH_IDX, UC_HOOK_MEM_READ_AFTER_IDX, UC_HOOK_INSN_INVALID_IDX, + UC_HOOK_EDGE_GENERATED_IDX, UC_HOOK_MAX, } 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. #define UC_HOOK_IDX_MASK ((1 << 6) - 1) diff --git a/include/unicorn/unicorn.h b/include/unicorn/unicorn.h index 2d29094f..c30f55a6 100644 --- a/include/unicorn/unicorn.h +++ b/include/unicorn/unicorn.h @@ -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, 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 @@ -302,6 +318,12 @@ typedef enum uc_hook_type { UC_HOOK_MEM_READ_AFTER = 1 << 13, // Hook invalid instructions exceptions. 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; // Hook type for all events of unmapped memory access @@ -391,13 +413,6 @@ typedef enum uc_query_type { // result = True) } 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 // different. // diff --git a/qemu/accel/tcg/cpu-exec.c b/qemu/accel/tcg/cpu-exec.c index 79bcdd13..99a17273 100644 --- a/qemu/accel/tcg/cpu-exec.c +++ b/qemu/accel/tcg/cpu-exec.c @@ -245,6 +245,10 @@ static inline TranslationBlock *tb_find(CPUState *cpu, TranslationBlock *tb; target_ulong cs_base, pc; 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); if (tb == NULL) { @@ -265,6 +269,23 @@ static inline TranslationBlock *tb_find(CPUState *cpu, if (last_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; } diff --git a/qemu/accel/tcg/translate-all.c b/qemu/accel/tcg/translate-all.c index c9d41e8d..b9f3c272 100644 --- a/qemu/accel/tcg/translate-all.c +++ b/qemu/accel/tcg/translate-all.c @@ -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) { - out_tb->pc = tb->pc; - out_tb->size = tb->size; - out_tb->icount = tb->icount; + UC_TB_COPY(out_tb, tb); } return UC_ERR_OK; diff --git a/samples/sample_ctl.c b/samples/sample_ctl.c index 593cd9a8..3e63c4f6 100644 --- a/samples/sample_ctl.c +++ b/samples/sample_ctl.c @@ -73,10 +73,17 @@ static void test_uc_ctl_read(void) 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() { uc_engine *uc; uc_err err; + uc_hook h; int r_eax, r_ebx; uint64_t exits[] = {ADDRESS + 6, ADDRESS + 8}; @@ -102,6 +109,14 @@ void test_uc_ctl_exits() 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. err = uc_ctl_exits_enabled(uc, true); if (err) { @@ -183,6 +198,7 @@ static void test_uc_ctl_tb_cache() uc_engine *uc; uc_err err; uc_tb tb; + uc_hook h; char code[CODE_LEN]; double standard, cached, evicted; @@ -211,6 +227,17 @@ static void test_uc_ctl_tb_cache() 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. standard = time_emulation(uc, ADDRESS, ADDRESS + sizeof(code) - 1);