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 | |
parent | misc. objc trimmings (diff) | |
download | substitute-d281e5233f304dab603d638a623ae54af8117ce7.tar.gz |
improve test; thumb2 fixes
Diffstat (limited to '')
-rw-r--r-- | Makefile | 6 | ||||
-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 | ||||
-rw-r--r-- | test/insns-arm.S | 8 | ||||
-rw-r--r-- | test/test-jump-dis.c | 1 | ||||
-rw-r--r-- | test/test-td-simple.c | 1 | ||||
-rw-r--r-- | test/test-transform-dis.c | 48 |
11 files changed, 135 insertions, 25 deletions
@@ -55,8 +55,10 @@ $(eval $(call define_test,tdthumb2-simple,td-simple,$(CC) -std=c11 -DHDR='"dis-t $(eval $(call define_test,tdarm64-simple,td-simple,$(CC) -std=c11 -DHDR='"dis-arm64.inc.h"' -Dxdis=dis -DFORCE_TARGET_arm64)) $(eval $(call define_test,dis-arm,dis,$(CC) -std=c11 -DFORCE_TARGET_arm)) $(eval $(call define_test,dis-arm64,dis,$(CC) -std=c11 -DFORCE_TARGET_arm64)) -$(eval $(call define_test,jump-dis-arm,jump-dis,$(CC) -std=c11 -DFORCE_TARGET_arm)) -$(eval $(call define_test,jump-dis-arm64,jump-dis,$(CC) -std=c11 -DFORCE_TARGET_arm64)) +$(eval $(call define_test,jump-dis-arm,jump-dis,$(CC) -std=c11 -DFORCE_TARGET_arm -O0)) +$(eval $(call define_test,jump-dis-arm64,jump-dis,$(CC) -std=c11 -DFORCE_TARGET_arm64 -O0)) +$(eval $(call define_test,transform-dis-arm,transform-dis,$(CC) -std=c11 -DFORCE_TARGET_arm -O0)) +$(eval $(call define_test,transform-dis-arm64,transform-dis,$(CC) -std=c11 -DFORCE_TARGET_arm64 -O0)) $(eval $(call define_test,find-syms,find-syms,$(CC) -std=c89)) $(eval $(call define_test,find-syms-cpp,find-syms,$(CXX) -x c++ -std=c++98)) $(eval $(call define_test,substrate,substrate,$(CXX) -std=c++98)) 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); diff --git a/test/insns-arm.S b/test/insns-arm.S index 62b4b48..b5a2cce 100644 --- a/test/insns-arm.S +++ b/test/insns-arm.S @@ -17,7 +17,7 @@ add r0, pc, #123 mcr p15,0,pc,c14,c3,0 str r3, [pc, #5] str pc, [pc, #5] -#ifdef THUMB2 // it shouldn't be, though +#ifdef THUMB2 /* it shouldn't be, though */ strht r0, [r3] #endif ldr r0, [pc] @@ -35,13 +35,13 @@ ldrsb r1, [pc] ldrh r1, [pc] ldrsh r1, [pc] ldr r1, [pc] -ldrd r1, r2, [pc] +ldrd r0, r1, [pc] str r1, [pc] -strd r1, r2, [pc] +strd r0, r1, [pc] push {r0-r3, pc} push {r0-r3, lr} -pop {r0-r3, pc} +/* pop {r0-r3, pc} */ 1: .long 0xdeadbeef diff --git a/test/test-jump-dis.c b/test/test-jump-dis.c index 1afa4e5..1a34bc0 100644 --- a/test/test-jump-dis.c +++ b/test/test-jump-dis.c @@ -5,6 +5,7 @@ int main(UNUSED int argc, char **argv) { static char buf[1048576]; UNUSED size_t size = fread(buf, 1, sizeof(buf), stdin); + printf("size=%zd\n", size); int patch_size = atoi(argv[1]); int thumb = atoi(argv[2]); bool bad = P(main)(buf, 0x10000, 0x10000 + patch_size, thumb); diff --git a/test/test-td-simple.c b/test/test-td-simple.c index 4464091..6347359 100644 --- a/test/test-td-simple.c +++ b/test/test-td-simple.c @@ -17,6 +17,7 @@ typedef struct tc { #define P(x) P_##x #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)) NOINLINE UNUSED diff --git a/test/test-transform-dis.c b/test/test-transform-dis.c new file mode 100644 index 0000000..d7c44d6 --- /dev/null +++ b/test/test-transform-dis.c @@ -0,0 +1,48 @@ +#include <stdio.h> +#define TRANSFORM_DIS_VERBOSE 1 +#include "transform-dis.c" +#include <stdlib.h> +int main(UNUSED int argc, char **argv) { + static uint8_t in[1048576]; + UNUSED size_t size = fread(in, 1, sizeof(in), stdin); + int patch_size = atoi(argv[1]); + int thumb = atoi(argv[2]); + uint8_t out[patch_size * 10]; + int offsets[patch_size + 1]; + void *rewritten_ptr = out; + printf("\n#if 0\n"); + int ret = transform_dis_main( + in, + &rewritten_ptr, + 0x10000, + 0x10000 + patch_size, + thumb, + offsets); + printf("=> %d\n", ret); + printf("#endif\n"); + int print_out_idx = 0; + int print_in_idx = 0; + if (!ret) { + printf("// total length: %zd\n", (uint8_t *) rewritten_ptr - out); + for(int ii = 0; ii <= patch_size; ii++) { + int oi = offsets[ii]; + if(oi != -1) { + int in_size = ii - print_in_idx; + int out_size = oi - print_out_idx; + if (in_size != out_size || memcmp(out + print_out_idx, in + print_in_idx, in_size)) { + printf("at_%x: nop; nop; nop\n", print_in_idx); + printf(" .byte "); + while(print_in_idx++ < ii) + printf("0x%02x%s", in[print_in_idx-1], print_in_idx == ii ? "" : ", "); + printf("\nnop // -->\n .byte "); + while(print_out_idx++ < oi) + printf("0x%02x%s", out[print_out_idx-1], print_out_idx == oi ? "" : ", "); + printf("\n"); + } + print_in_idx = ii; + print_out_idx = oi; + printf("/* 0x%x: 0x%x */\n", ii, oi); + } + } + } +} |