aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcomex2015-01-17 22:24:13 -0500
committercomex2015-01-17 22:24:13 -0500
commitd281e5233f304dab603d638a623ae54af8117ce7 (patch)
tree6605309de4f0e0728f7d9171528f376906af00f0
parentmisc. objc trimmings (diff)
downloadsubstitute-d281e5233f304dab603d638a623ae54af8117ce7.tar.gz
improve test; thumb2 fixes
-rw-r--r--Makefile6
-rw-r--r--lib/dis-thumb.inc.h2
-rw-r--r--lib/dis-thumb2.inc.h23
-rw-r--r--lib/jump-dis.c1
-rw-r--r--lib/transform-dis-arm-multi.inc.h49
-rw-r--r--lib/transform-dis.c11
-rw-r--r--lib/transform-dis.h10
-rw-r--r--test/insns-arm.S8
-rw-r--r--test/test-jump-dis.c1
-rw-r--r--test/test-td-simple.c1
-rw-r--r--test/test-transform-dis.c48
11 files changed, 135 insertions, 25 deletions
diff --git a/Makefile b/Makefile
index 6108db9..37ec88a 100644
--- a/Makefile
+++ b/Makefile
@@ -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);
+ }
+ }
+ }
+}