From a23ef990492cd0384de1a924c44805587d5b5aed Mon Sep 17 00:00:00 2001 From: comex Date: Sun, 1 Feb 2015 01:56:29 -0500 Subject: fix my utter failure to handle branches/conditionals correctly (on ARM) --- lib/transform-dis.c | 55 ++++++++++++++++++++++++++++++++--------------------- 1 file changed, 33 insertions(+), 22 deletions(-) (limited to 'lib/transform-dis.c') diff --git a/lib/transform-dis.c b/lib/transform-dis.c index 1e66bd1..3210333 100644 --- a/lib/transform-dis.c +++ b/lib/transform-dis.c @@ -3,6 +3,7 @@ #include "substitute.h" #include "dis.h" +#include "transform-dis.h" #include #include @@ -14,6 +15,8 @@ struct transform_dis_ctx { int err; uintptr_t pc_patch_start; + /* this is only tentative - it will be updated to include parts of + * instructions poking out, and instructions forced to be transformed by IT */ uintptr_t pc_patch_end; uintptr_t pc; int op_size; @@ -21,6 +24,9 @@ struct transform_dis_ctx { unsigned newop; unsigned newval[4]; + /* for IT - eww */ + bool force_keep_transforming; + const void *ptr; void **rewritten_ptr_ptr; void *write_newop_here; @@ -36,50 +42,46 @@ struct transform_dis_ctx { /* largely similar to jump_dis */ -static INLINE UNUSED void transform_dis_ret(struct transform_dis_ctx *ctx) { - /* ret is okay if it's at the end of the patch */ +static INLINE UNUSED +void transform_dis_ret(struct transform_dis_ctx *ctx) { + /* ret is okay if it's at the end of the required patch (past the original + * patch size is good too) */ if (ctx->pc + ctx->op_size < ctx->pc_patch_end) ctx->err = SUBSTITUTE_ERR_FUNC_TOO_SHORT; } -static INLINE UNUSED void transform_dis_branch(struct transform_dis_ctx *ctx, - uintptr_t dpc, UNUSED bool conditional) { -#ifdef TRANSFORM_DIS_VERBOSE - printf("transform_dis (%p): branch => %p\n", (void *) ctx->pc, (void *) dpc); -#endif - if (dpc >= ctx->pc_patch_start && dpc < ctx->pc_patch_end) { - /* don't support this for now */ - ctx->err = SUBSTITUTE_ERR_FUNC_BAD_INSN_AT_START; - } - /* branch out of bounds is fine */ - /* XXX just kidding, the instruction needs to be rewritten obviously. what - * was I thinking? */ -} - -static INLINE UNUSED void transform_dis_unidentified(UNUSED struct transform_dis_ctx *ctx) { +static INLINE UNUSED +void transform_dis_unidentified(UNUSED struct transform_dis_ctx *ctx) { #ifdef TRANSFORM_DIS_VERBOSE printf("transform_dis (%p): unidentified\n", (void *) ctx->pc); #endif /* this isn't exhaustive, so unidentified is fine */ } -static INLINE UNUSED void transform_dis_bad(struct transform_dis_ctx *ctx) { +static INLINE UNUSED +void transform_dis_bad(struct transform_dis_ctx *ctx) { ctx->err = SUBSTITUTE_ERR_FUNC_BAD_INSN_AT_START; } +static INLINE UNUSED +void transform_dis_thumb_it(UNUSED struct transform_dis_ctx *ctx) { + /* ignore, since it was turned into B */ +} static void transform_dis_dis(struct transform_dis_ctx *ctx); +static void transform_dis_pre_dis(struct transform_dis_ctx *ctx); +static void transform_dis_post_dis(struct transform_dis_ctx *ctx); int transform_dis_main(const void *restrict code_ptr, void **restrict rewritten_ptr_ptr, uintptr_t pc_patch_start, - uintptr_t pc_patch_end, + uintptr_t *pc_patch_end_p, struct arch_dis_ctx initial_arch_ctx, int *offset_by_pcdiff) { struct transform_dis_ctx ctx; memset(&ctx, 0, sizeof(ctx)); ctx.pc_patch_start = pc_patch_start; - ctx.pc_patch_end = pc_patch_end; + ctx.pc_patch_end = *pc_patch_end_p; ctx.pc = pc_patch_start; ctx.arch = initial_arch_ctx; /* data is written to rewritten both by this function directly and, in case @@ -88,12 +90,16 @@ int transform_dis_main(const void *restrict code_ptr, void *rewritten_start = *rewritten_ptr_ptr; int written_pcdiff = 0; offset_by_pcdiff[written_pcdiff++] = 0; - while (ctx.pc < ctx.pc_patch_end) { + while (ctx.pc < ctx.pc_patch_end && !ctx.force_keep_transforming) { ctx.modify = false; ctx.err = 0; ctx.ptr = code_ptr + (ctx.pc - pc_patch_start); + + transform_dis_pre_dis(&ctx); + void *rewritten_ptr = *rewritten_ptr_ptr; ctx.write_newop_here = rewritten_ptr; + transform_dis_dis(&ctx); if (ctx.err) @@ -111,11 +117,16 @@ int transform_dis_main(const void *restrict code_ptr, *rewritten_ptr_ptr += ctx.op_size; } ctx.pc += ctx.op_size; + + transform_dis_post_dis(&ctx); + int pcdiff = ctx.pc - ctx.pc_patch_start; while (written_pcdiff < pcdiff) offset_by_pcdiff[written_pcdiff++] = -1; - offset_by_pcdiff[written_pcdiff++] = (int) (*rewritten_ptr_ptr - rewritten_start); + offset_by_pcdiff[written_pcdiff++] = + (int) (*rewritten_ptr_ptr - rewritten_start); } + *pc_patch_end_p = ctx.pc; return SUBSTITUTE_OK; } -- cgit v1.2.3