diff options
author | comex | 2015-01-17 22:24:13 -0500 |
---|---|---|
committer | comex | 2015-01-17 22:24:13 -0500 |
commit | d281e5233f304dab603d638a623ae54af8117ce7 (patch) | |
tree | 6605309de4f0e0728f7d9171528f376906af00f0 /lib | |
parent | misc. objc trimmings (diff) | |
download | substitute-d281e5233f304dab603d638a623ae54af8117ce7.tar.gz |
improve test; thumb2 fixes
Diffstat (limited to 'lib')
-rw-r--r-- | lib/dis-thumb.inc.h | 2 | ||||
-rw-r--r-- | lib/dis-thumb2.inc.h | 23 | ||||
-rw-r--r-- | lib/jump-dis.c | 1 | ||||
-rw-r--r-- | lib/transform-dis-arm-multi.inc.h | 49 | ||||
-rw-r--r-- | lib/transform-dis.c | 11 | ||||
-rw-r--r-- | lib/transform-dis.h | 10 |
6 files changed, 77 insertions, 19 deletions
diff --git a/lib/dis-thumb.inc.h b/lib/dis-thumb.inc.h index 6d9683e..4e6d106 100644 --- a/lib/dis-thumb.inc.h +++ b/lib/dis-thumb.inc.h @@ -71,9 +71,9 @@ static INLINE void P(it_pred_cc_it_mask_mask_1_t2IT)(tdis_ctx ctx, struct bitsli static INLINE void P(dis_thumb)(tdis_ctx ctx) { uint16_t op = ctx->op = *(uint16_t *) ctx->ptr; + ctx->op_size = 2; if (ctx->arch.thumb_it_length) ctx->arch.thumb_it_length--; - ctx->op_size = 2; #include "../generated/generic-dis-thumb.inc.h" __builtin_abort(); } diff --git a/lib/dis-thumb2.inc.h b/lib/dis-thumb2.inc.h index 632e2a2..fa744bc 100644 --- a/lib/dis-thumb2.inc.h +++ b/lib/dis-thumb2.inc.h @@ -1,8 +1,11 @@ #include "dis.h" -/* TODO: handle 'it' for conditional br/ret!! */ +static inline unsigned flip16(unsigned op) { + return op >> 16 | op << 16; +} static inline enum pcrel_load_mode get_thumb2_load_mode(unsigned op) { + op = flip16(op); bool sign = (op >> 8) & 1; switch ((op >> 5) & 3) { case 0: return sign ? PLM_S8 : PLM_U8; @@ -162,11 +165,21 @@ static INLINE void P(unk_Rt_13_VMOVRRD)(tdis_ctx ctx, UNUSED struct bitslice Rt) return P(unidentified)(ctx); } +static INLINE void do_it(tdis_ctx ctx) { + uint32_t op = ctx->op; + #include "../generated/generic-dis-thumb2.inc.h" + __builtin_abort(); +} + static INLINE void P(dis_thumb2)(tdis_ctx ctx) { - uint32_t op = ctx->op = *(uint32_t *) ctx->ptr; + ctx->op = *(uint32_t *) ctx->ptr; + ctx->op_size = 4; if (ctx->arch.thumb_it_length) ctx->arch.thumb_it_length--; - ctx->op_size = 4; - #include "../generated/generic-dis-thumb2.inc.h" - __builtin_abort(); + /* LLVM likes to think about Thumb2 instructions the way the ARM manual + * does - 15..0 15..0 rather than 31..0 as actually laid out in memory... */ + ctx->op = flip16(ctx->op); + do_it(ctx); + TDIS_CTX_SET_NEWOP(ctx, flip16(TDIS_CTX_NEWOP(ctx))); + ctx->op = flip16(ctx->op); } diff --git a/lib/jump-dis.c b/lib/jump-dis.c index beb6ffc..355ac0f 100644 --- a/lib/jump-dis.c +++ b/lib/jump-dis.c @@ -46,6 +46,7 @@ struct jump_dis_ctx { #define tdis_ctx struct jump_dis_ctx * #define TDIS_CTX_MODIFY(ctx) 0 #define TDIS_CTX_NEWVAL(ctx, n) 0 +#define TDIS_CTX_NEWOP(ctx) 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) { diff --git a/lib/transform-dis-arm-multi.inc.h b/lib/transform-dis-arm-multi.inc.h index 5382304..dc066ff 100644 --- a/lib/transform-dis-arm-multi.inc.h +++ b/lib/transform-dis-arm-multi.inc.h @@ -46,14 +46,14 @@ static inline void LDRxi(struct transform_dis_ctx *ctx, int Rt, int Rn, uint32_t if (ctx->pc_low_bit) { int subop, sign; switch (load_mode) { - case PLM_U8: subop = 0; sign = 0; - case PLM_S8: subop = 0; sign = 1; - case PLM_U16: subop = 1; sign = 0; - case PLM_S16: subop = 1; sign = 1; - case PLM_U32: subop = 2; sign = 0; + case PLM_U8: subop = 0; sign = 0; break; + case PLM_S8: subop = 0; sign = 1; break; + case PLM_U16: subop = 1; sign = 0; break; + case PLM_S16: subop = 1; sign = 1; break; + case PLM_U32: subop = 2; sign = 0; break; default: __builtin_abort(); } - op32(ctx, 0x0000f8d0 | Rn | Rt << 28 | subop << 5 | sign << 8 | off << 16); + op32(ctx, 0x0000f890 | Rn | Rt << 28 | subop << 5 | sign << 8 | off << 16); } else { int is_byte, subop, not_ldrd; switch (load_mode) { @@ -78,6 +78,10 @@ static inline void LDRxi(struct transform_dis_ctx *ctx, int Rt, int Rn, uint32_t static NOINLINE UNUSED void transform_dis_data(struct transform_dis_ctx *ctx, unsigned o0, unsigned o1, unsigned o2, unsigned o3, unsigned out_mask) { +#ifdef TRANSFORM_DIS_VERBOSE + printf("transform_dis_data: (%p) %x %x %x %x out_mask=%x\n", (void *) ctx->pc, + o0, o1, o2, o3, out_mask); +#endif /* We only care if at least one op is PC, so quickly test that. */ if (((o0 | o1 | o2 | o3) & 15) != 15) return; @@ -108,12 +112,18 @@ static NOINLINE UNUSED void transform_dis_data(struct transform_dis_ctx *ctx, int out_reg = -1; for (int i = 0; i < 4; i++) { if (out_mask & 1 << i) - out_reg = i; + out_reg = newval[i]; else in_regs |= 1 << newval[i]; } uint32_t pc = ctx->pc + (ctx->pc_low_bit ? 4 : 8); int scratch = __builtin_ctz(~(in_regs | (1 << out_reg))); + +#ifdef TRANSFORM_DIS_VERBOSE + printf("transform_dis_data: in_regs=%x out_reg=%d pc=%x scratch=%d\n", + in_regs, out_reg, pc, scratch); +#endif + if (out_reg == 15) { if (in_regs & 1 << 15) return; /* case 1 */ @@ -131,7 +141,7 @@ static NOINLINE UNUSED void transform_dis_data(struct transform_dis_ctx *ctx, if (out_reg != -1 && !(in_regs & 1 << out_reg)) { /* case 3 - ignore scratch */ MOVW_MOVT(ctx, out_reg, pc); - for (int i = 1; i < 4; i++) + for (int i = 0; i < 4; i++) if (newval[i] == 15) newval[i] = out_reg; ctx->write_newop_here = *rpp; *rpp += ctx->op_size; @@ -143,7 +153,7 @@ static NOINLINE UNUSED void transform_dis_data(struct transform_dis_ctx *ctx, if (newval[i] == 15) newval[i] = scratch; ctx->write_newop_here = *rpp; *rpp += ctx->op_size; - POPone(ctx, 1 << scratch); + POPone(ctx, scratch); } } ctx->modify = true; @@ -151,8 +161,23 @@ static NOINLINE UNUSED void transform_dis_data(struct transform_dis_ctx *ctx, static NOINLINE UNUSED void transform_dis_pcrel(struct transform_dis_ctx *ctx, uintptr_t dpc, unsigned reg, enum pcrel_load_mode load_mode) { +#ifdef TRANSFORM_DIS_VERBOSE + printf("transform_dis_pcrel: (%p) dpc=%p reg=%x mode=%d\n", (void *) ctx->pc, + (void *) dpc, reg, load_mode); +#endif ctx->write_newop_here = NULL; - MOVW_MOVT(ctx, reg, dpc); - if (load_mode != PLM_ADR) - LDRxi(ctx, reg, reg, 0, load_mode); + if (reg == 15) { + int scratch = 0; + PUSHone(ctx, scratch); + PUSHone(ctx, scratch); + MOVW_MOVT(ctx, scratch, dpc); + if (load_mode != PLM_ADR) + LDRxi(ctx, scratch, scratch, 0, load_mode); + STRri(ctx, scratch, 13, 4); + POPmulti(ctx, 1 << scratch | 1 << 15); + } else { + MOVW_MOVT(ctx, reg, dpc); + if (load_mode != PLM_ADR) + LDRxi(ctx, reg, reg, 0, load_mode); + } } diff --git a/lib/transform-dis.c b/lib/transform-dis.c index 32c012b..6d5fc66 100644 --- a/lib/transform-dis.c +++ b/lib/transform-dis.c @@ -32,6 +32,7 @@ struct transform_dis_ctx { #define tdis_ctx struct transform_dis_ctx * #define TDIS_CTX_MODIFY(ctx) ((ctx)->modify) #define TDIS_CTX_NEWVAL(ctx, n) ((ctx)->newval[n]) +#define TDIS_CTX_NEWOP(ctx) ((ctx)->newop) #define TDIS_CTX_SET_NEWOP(ctx, new) ((ctx)->newop = (new)) /* largely similar to jump_dis */ @@ -44,6 +45,9 @@ static INLINE UNUSED void transform_dis_ret(struct transform_dis_ctx *ctx) { 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; @@ -52,6 +56,9 @@ static INLINE UNUSED void transform_dis_branch(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 */ } @@ -78,10 +85,10 @@ int transform_dis_main(const void *restrict code_ptr, ctx.rewritten_ptr_ptr = rewritten_ptr_ptr; void *rewritten_start = *rewritten_ptr_ptr; int written_pcdiff = 0; + offset_by_pcdiff[written_pcdiff++] = 0; while (ctx.pc < ctx.pc_patch_end) { ctx.modify = false; ctx.err = 0; - ctx.newop = ctx.op; ctx.ptr = code_ptr + (ctx.pc - pc_patch_start); void *rewritten_ptr = *rewritten_ptr_ptr; ctx.write_newop_here = rewritten_ptr; @@ -90,6 +97,8 @@ int transform_dis_main(const void *restrict code_ptr, if (ctx.err) return ctx.err; if (ctx.write_newop_here != NULL) { + if (!ctx.modify) + ctx.newop = ctx.op; if (ctx.op_size == 4) *(uint32_t *) ctx.write_newop_here = ctx.newop; else if (ctx.op_size == 2) diff --git a/lib/transform-dis.h b/lib/transform-dis.h new file mode 100644 index 0000000..28e5d72 --- /dev/null +++ b/lib/transform-dis.h @@ -0,0 +1,10 @@ +#pragma once +#include <stdint.h> +#include <stdbool.h> + +int transform_dis_main(const void *restrict code_ptr, + void **restrict rewritten_ptr_ptr, + uintptr_t pc_patch_start, + uintptr_t pc_patch_end, + bool pc_low_bit, + int *offset_by_pcdiff); |