From 96e2b019e525b81a33bae5e6aeafc0e667f04f20 Mon Sep 17 00:00:00 2001 From: comex Date: Sat, 17 Jan 2015 14:56:03 -0500 Subject: reorganization --- lib/jump-dis.c | 161 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 161 insertions(+) create mode 100644 lib/jump-dis.c (limited to 'lib/jump-dis.c') diff --git a/lib/jump-dis.c b/lib/jump-dis.c new file mode 100644 index 0000000..c7c7346 --- /dev/null +++ b/lib/jump-dis.c @@ -0,0 +1,161 @@ +#include "substitute-internal.h" +#ifndef TARGET_UNSUPPORTED +#include "dis.h" +#include +#include +#include + +/* This pass tries to look through the function to find jumps back to the + * patched code at the beginning to the function. It does not deal with jump + * tables, and has a limited range, so it is only heuristic. If such jumps are + * found, the hook is aborted. In the future it might be possible to fix up + * the jumps rather than merely detect them, but that would require doing + * something weird like extending the patch region to add trampolines... */ + +enum { + JUMP_ANALYSIS_MAX_INSNS = 512, + JUMP_ANALYSIS_MAX_SIZE = JUMP_ANALYSIS_MAX_INSNS * MIN_INSN_SIZE, +}; + +struct jump_dis_ctx { + /* outputs */ + bool bad_insn; + bool continue_after_this_insn; + + uintptr_t pc; + uintptr_t pc_patch_start; + uintptr_t pc_patch_end; + bool pc_low_bit; + unsigned op; + void *ptr; + int op_size; + uint8_t seen_mask[JUMP_ANALYSIS_MAX_INSNS / 8]; + /* queue of instructions to visit */ + uintptr_t *queue; + size_t queue_write_off; + size_t queue_read_off; + size_t queue_size; + size_t queue_count; +}; + +#undef P +#define P(x) jump_dis_##x + +#define tdis_ctx struct jump_dis_ctx * +#define TDIS_CTX_MODIFY(ctx) 0 +#define TDIS_CTX_NEWVAL(ctx, n) 0 +#define TDIS_CTX_SET_NEWOP(ctx, new) ((void) 0) + +static void jump_dis_add_to_queue(struct jump_dis_ctx *ctx, uintptr_t pc) { + size_t diff = (pc - ctx->pc_patch_start) / MIN_INSN_SIZE; + if (diff >= JUMP_ANALYSIS_MAX_INSNS) { +#ifdef JUMP_DIS_VERBOSE + printf("jump-dis: not adding %llx - out of range\n", (unsigned long long) pc); +#endif + return; + } + if (ctx->seen_mask[diff / 8] & 1 << (diff % 8)) { +#ifdef JUMP_DIS_VERBOSE + printf("jump-dis: not adding %llx - already seen\n", (unsigned long long) pc); +#endif + return; + } + ctx->seen_mask[diff / 8] |= 1 << (diff % 8); + + if (ctx->queue_write_off == ctx->queue_read_off && (ctx->queue_count || !ctx->queue_size)) { + size_t new_size = ctx->queue_size * 2 + 5; + ctx->queue = realloc(ctx->queue, new_size * sizeof(*ctx->queue)); + if (!ctx->queue) + substitute_panic("%s: out of memory\n", __func__); + size_t new_read_off = new_size - (ctx->queue_size - ctx->queue_read_off); + memmove(ctx->queue + new_read_off, ctx->queue + ctx->queue_read_off, (ctx->queue_size - ctx->queue_read_off) * sizeof(*ctx->queue)); + ctx->queue_read_off = new_read_off % new_size; + ctx->queue_size = new_size; + } + ctx->queue[ctx->queue_write_off] = pc; + ctx->queue_write_off = (ctx->queue_write_off + 1) % ctx->queue_size; + ctx->queue_count++; +} + + +static INLINE inline void jump_dis_data(UNUSED struct jump_dis_ctx *ctx, UNUSED unsigned o0, UNUSED unsigned o1, UNUSED unsigned o2, UNUSED unsigned o3, UNUSED unsigned out_mask) { + /* on ARM, ignore mov PC jumps, as they're unlikely to be in the same function */ +} + +static INLINE inline void jump_dis_pcrel(struct jump_dis_ctx *ctx, uintptr_t dpc, UNUSED unsigned reg, UNUSED bool is_load) { + ctx->bad_insn = dpc >= ctx->pc_patch_start && dpc < ctx->pc_patch_end; +} + +NOINLINE UNUSED +static void jump_dis_ret(struct jump_dis_ctx *ctx) { + ctx->continue_after_this_insn = false; +} + +NOINLINE UNUSED +static void jump_dis_branch(struct jump_dis_ctx *ctx, uintptr_t dpc, bool conditional) { + if (dpc >= ctx->pc_patch_start && dpc < ctx->pc_patch_end) { + ctx->bad_insn = true; + return; + } +#ifdef JUMP_DIS_VERBOSE + printf("jump-dis: enqueueing %llx\n", (unsigned long long) dpc); +#endif + jump_dis_add_to_queue(ctx, dpc); + ctx->continue_after_this_insn = conditional; +} + +NOINLINE UNUSED +static void jump_dis_unidentified(UNUSED struct jump_dis_ctx *ctx) { +} + +NOINLINE UNUSED +static void jump_dis_bad(struct jump_dis_ctx *ctx) { + ctx->continue_after_this_insn = false; +} + +static void jump_dis_dis(tdis_ctx ctx); + +bool jump_dis_main(void *code_ptr, uintptr_t pc_patch_start, uintptr_t pc_patch_end, bool pc_low_bit) { + bool ret; + struct jump_dis_ctx ctx; + memset(&ctx, 0, sizeof(ctx)); + ctx.pc_patch_start = pc_patch_start; + ctx.pc_patch_end = pc_patch_end; + ctx.pc_low_bit = pc_low_bit; + ctx.pc = pc_patch_end; + while (1) { + ctx.bad_insn = false; + ctx.continue_after_this_insn = true; + ctx.ptr = code_ptr + (ctx.pc - pc_patch_start); + jump_dis_dis(&ctx); +#ifdef JUMP_DIS_VERBOSE + printf("jump-dis: pc=%llx op=%08x size=%x bad=%d continue_after=%d\n", + (unsigned long long) ctx.pc, + ctx.op, + ctx.op_size, + ctx.bad_insn, + ctx.continue_after_this_insn); +#endif + if (ctx.bad_insn) { + ret = true; + goto fail; + } + if (ctx.continue_after_this_insn) + jump_dis_add_to_queue(&ctx, ctx.pc + ctx.op_size); + + /* get next address */ + if (ctx.queue_read_off == ctx.queue_write_off) + break; + ctx.pc = ctx.queue[ctx.queue_read_off]; + ctx.queue_read_off = (ctx.queue_read_off + 1) % ctx.queue_size; + ctx.queue_count--; + } + /* no bad instructions! */ + ret = false; +fail: + free(ctx.queue); + return ret; +} + +#include TARGET_DIS_HEADER +#endif /* TARGET_UNSUPPORTED */ -- cgit v1.2.3