aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-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
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);