aboutsummaryrefslogtreecommitdiff
path: root/lib/transform-dis.c
diff options
context:
space:
mode:
authorcomex2015-02-01 01:56:29 -0500
committercomex2015-02-01 01:56:42 -0500
commita23ef990492cd0384de1a924c44805587d5b5aed (patch)
treeaa3a28446fc1a7ca1d799c8f3ad3acc6afdea0f2 /lib/transform-dis.c
parenttrivial wording tweak (diff)
downloadsubstitute-a23ef990492cd0384de1a924c44805587d5b5aed.tar.gz
fix my utter failure to handle branches/conditionals correctly (on ARM)
Diffstat (limited to 'lib/transform-dis.c')
-rw-r--r--lib/transform-dis.c55
1 files changed, 33 insertions, 22 deletions
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 <stdbool.h>
#include <stdint.h>
@@ -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;
}