diff options
author | comex | 2015-01-17 20:56:04 -0500 |
---|---|---|
committer | comex | 2015-01-17 20:56:16 -0500 |
commit | b70a6008604ae5746f50d3025404b06673db48f4 (patch) | |
tree | 0fe11003e83a244f6467425c7d6020ce865ead12 | |
parent | IT, BX LR - untested (diff) | |
download | substitute-b70a6008604ae5746f50d3025404b06673db48f4.tar.gz |
arm64 transform dis, arm fixes, move to .c since i'm dumb
-rw-r--r-- | lib/dis-arm64.inc.h | 12 | ||||
-rw-r--r-- | lib/dis.h | 2 | ||||
-rw-r--r-- | lib/transform-dis-arm-multi.inc.h | 18 | ||||
-rw-r--r-- | lib/transform-dis-arm64.inc.h | 52 | ||||
-rw-r--r-- | lib/transform-dis.c | 127 | ||||
-rw-r--r-- | lib/transform-dis.inc.h | 120 |
6 files changed, 189 insertions, 142 deletions
diff --git a/lib/dis-arm64.inc.h b/lib/dis-arm64.inc.h index 388ac19..5317b89 100644 --- a/lib/dis-arm64.inc.h +++ b/lib/dis-arm64.inc.h @@ -17,16 +17,16 @@ static INLINE void P(am_ldrlit_label_unk_Rt_6_LDRDl)(tdis_ctx ctx, struct bitsli enum pcrel_load_mode mode; if ((ctx->op >> 26) & 1) { switch (ctx->op >> 30) { - case 0b00: mode = PLM_U32_SIMD; break; - case 0b01: mode = PLM_U64_SIMD; break; - case 0b10: mode = PLM_U128_SIMD; break; + case 0: mode = PLM_U32_SIMD; break; + case 1: mode = PLM_U64_SIMD; break; + case 2: mode = PLM_U128_SIMD; break; default: __builtin_abort(); } } else { switch (ctx->op >> 30) { - case 0b00: mode = PLM_U32; break; - case 0b01: mode = PLM_U64; break; - case 0b10: mode = PLM_S32; break; + case 0: mode = PLM_U32; break; + case 1: mode = PLM_U64; break; + case 2: mode = PLM_S32; break; default: __builtin_abort(); } } @@ -68,7 +68,7 @@ enum pcrel_load_mode { PLM_U16, PLM_S16, PLM_U32, PLM_S32, PLM_U64, - PLM_U128, + PLM_U128, /* i.e. LDRD */ PLM_U32_SIMD, PLM_U64_SIMD, PLM_U128_SIMD, diff --git a/lib/transform-dis-arm-multi.inc.h b/lib/transform-dis-arm-multi.inc.h index 3c99911..5382304 100644 --- a/lib/transform-dis-arm-multi.inc.h +++ b/lib/transform-dis-arm-multi.inc.h @@ -3,7 +3,6 @@ static inline void PUSHone(struct transform_dis_ctx *ctx, int Rt) { op32(ctx, 0x0d04f84d | Rt << 28); else op32(ctx, 0xe52d0004 | Rt << 12); - } static inline void POPone(struct transform_dis_ctx *ctx, int Rt) { @@ -56,19 +55,20 @@ static inline void LDRxi(struct transform_dis_ctx *ctx, int Rt, int Rn, uint32_t } op32(ctx, 0x0000f8d0 | Rn | Rt << 28 | subop << 5 | sign << 8 | off << 16); } else { - int is_byte, subop; + int is_byte, subop, not_ldrd; switch (load_mode) { - case PLM_U8: is_byte = 1; goto type1; - case PLM_S8: subop = 13; goto type2; - case PLM_U16: subop = 11; goto type2; - case PLM_S16: subop = 15; goto type2; - case PLM_U32: is_byte = 0; goto type1; + case PLM_U8: is_byte = 1; goto type1; + case PLM_S8: subop = 13; not_ldrd = 1; goto type2; + case PLM_U16: subop = 11; not_ldrd = 1; goto type2; + case PLM_S16: subop = 15; not_ldrd = 1; goto type2; + case PLM_U32: is_byte = 0; goto type1; + case PLM_U128: subop = 13; not_ldrd = 0; goto type2; type1: op32(ctx, 0xe5900000 | Rn << 16 | Rt << 12 | off); break; type2: - op32(ctx, 0xe1d00000 | Rn << 16 | Rt << 12 | subop << 4 | - (off & 0xf) | (off & 0xf0) << 4); + op32(ctx, 0xe1c00000 | Rn << 16 | Rt << 12 | subop << 4 | + (off & 0xf) | (off & 0xf0) << 4 | not_ldrd << 20); break; default: __builtin_abort(); diff --git a/lib/transform-dis-arm64.inc.h b/lib/transform-dis-arm64.inc.h new file mode 100644 index 0000000..c47971b --- /dev/null +++ b/lib/transform-dis-arm64.inc.h @@ -0,0 +1,52 @@ +static inline void MOVi64(struct transform_dis_ctx *ctx, int Rd, uint64_t val) { + int shift_nybbles = 0; + do { + int k = shift_nybbles != 0 ? 1 : 0; + op32(ctx, 0x69400000 | k << 28 | Rd | (val & 0xffff) << 4 | shift_nybbles << 20); + shift_nybbles++; + val >>= 16; + } while(val); +} + +static inline void LDRxi(struct transform_dis_ctx *ctx, int Rt, int Rn, uint32_t off, + bool regsize_64, enum pcrel_load_mode load_mode) { + int size, opc; + bool sign, simd; + switch (load_mode) { + case PLM_U8: size = 0; sign = false; simd = false; break; + case PLM_S8: size = 0; sign = true; simd = false; break; + case PLM_U16: size = 1; sign = false; simd = false; break; + case PLM_S16: size = 1; sign = true; simd = false; break; + case PLM_U32: size = 2; sign = false; simd = false; break; + case PLM_S32: size = 2; sign = true; simd = false; break; + case PLM_U64: size = 3; sign = false; simd = false; break; + case PLM_U32_SIMD: size = 2; opc = 1; simd = true; break; + case PLM_U64_SIMD: size = 3; opc = 1; simd = true; break; + case PLM_U128_SIMD: size = 0; opc = 3; simd = true; break; + default: __builtin_abort(); + } + if (simd) { + off /= 1 << (size | (opc & 1) << 2); + } else { + off /= 1 << size; + opc = sign ? (regsize_64 ? 2 : 3) : 1; + } + op32(ctx, 0x39000000 | Rt | Rn << 5 | off << 10 | opc << 22 | simd << 26 | size << 30); +} + + +static NOINLINE UNUSED void transform_dis_pcrel(struct transform_dis_ctx *ctx, + uintptr_t dpc, unsigned reg, enum pcrel_load_mode load_mode) { + ctx->write_newop_here = NULL; + if (load_mode >= PLM_U32_SIMD) { + /* use x0 as scratch */ + op32(ctx, 0xf81f0fe0); /* str x0, [sp, #-0x10]! */ + MOVi64(ctx, 0, dpc); + LDRxi(ctx, reg, 0, 0, true, load_mode); + op32(ctx, 0xf84107e0); /* ldr x0, [sp], #0x10 */ + } else { + MOVi64(ctx, reg, dpc); + LDRxi(ctx, reg, reg, 0, true, load_mode); + } +} + diff --git a/lib/transform-dis.c b/lib/transform-dis.c index 6fc6d7f..b8df987 100644 --- a/lib/transform-dis.c +++ b/lib/transform-dis.c @@ -1,9 +1,124 @@ #include "substitute-internal.h" #ifndef TARGET_UNSUPPORTED - #include "transform-dis.inc.h" - #ifdef TARGET_arm - #include "transform-dis-arm-multi.inc.h" - #else - #error ? - #endif + +#include "substitute.h" +#include "dis.h" +#include <stdbool.h> +#include <stdint.h> + +#define P(x) transform_dis_##x + +struct transform_dis_ctx { + /* outputs */ + bool modify; + int err; + + bool pc_low_bit; + uintptr_t pc_patch_start; + uintptr_t pc_patch_end; + uintptr_t pc; + int op_size; + unsigned op; + unsigned newop; + unsigned newval[4]; + + const void *ptr; + void **rewritten_ptr_ptr; + void *write_newop_here; + + struct arch_dis_ctx arch; +}; + +#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_SET_NEWOP(ctx, new) ((ctx)->newop = (new)) + +/* 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 */ + 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) { + 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 */ +} + +static INLINE UNUSED void transform_dis_unidentified(UNUSED struct transform_dis_ctx *ctx) { + /* this isn't exhaustive, so unidentified is fine */ +} + +static INLINE UNUSED void transform_dis_bad(struct transform_dis_ctx *ctx) { + ctx->err = SUBSTITUTE_ERR_FUNC_BAD_INSN_AT_START; +} + + +static void transform_dis_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, + bool pc_low_bit, + int *offset_by_pcdiff) { + struct transform_dis_ctx ctx; + ctx.pc_patch_start = pc_patch_start; + ctx.pc_patch_end = pc_patch_end; + ctx.pc_low_bit = pc_low_bit; + ctx.pc = pc_patch_start; + /* data is written to rewritten both by this function directly and, in case + * additional scaffolding is needed, by arch-specific transform_dis_* */ + ctx.rewritten_ptr_ptr = rewritten_ptr_ptr; + void *rewritten_start = *rewritten_ptr_ptr; + int 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; + transform_dis_dis(&ctx); + + if (ctx.err) + return ctx.err; + if (ctx.write_newop_here != NULL) { + if (ctx.op_size == 4) + *(uint32_t *) ctx.write_newop_here = ctx.newop; + else if (ctx.op_size == 2) + *(uint16_t *) ctx.write_newop_here = ctx.newop; + else + __builtin_abort(); + if (*rewritten_ptr_ptr == rewritten_ptr) + *rewritten_ptr_ptr += ctx.op_size; + } + ctx.pc += ctx.op_size; + 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); + } + return SUBSTITUTE_OK; +} + +static inline void op32(struct transform_dis_ctx *ctx, uint32_t op) { + void **rpp = ctx->rewritten_ptr_ptr; + *(uint32_t *) *rpp = op; + *rpp += 4; +} + +#ifdef TARGET_arm + #include "transform-dis-arm-multi.inc.h" +#else + #include "transform-dis-arm64.inc.h" #endif +#include TARGET_DIS_HEADER + +#endif /* TARGET_UNSUPPORTED */ diff --git a/lib/transform-dis.inc.h b/lib/transform-dis.inc.h deleted file mode 100644 index 04cd7f2..0000000 --- a/lib/transform-dis.inc.h +++ /dev/null @@ -1,120 +0,0 @@ -#include "substitute.h" -#include "dis.h" -#include <stdbool.h> -#include <stdint.h> - -#undef P -#define P(x) transform_dis_##x -struct transform_dis_ctx { - /* outputs */ - bool modify; - int err; - - bool pc_low_bit; - uintptr_t pc_patch_start; - uintptr_t pc_patch_end; - uintptr_t pc; - int op_size; - unsigned op; - unsigned newop; - unsigned newval[4]; - - const void *ptr; - void **rewritten_ptr_ptr; - void *write_newop_here; - - struct arch_dis_ctx arch; -}; - -#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_SET_NEWOP(ctx, new) ((ctx)->newop = (new)) - -/* 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 */ - 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) { - 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 */ -} - -static INLINE UNUSED void transform_dis_unidentified(UNUSED struct transform_dis_ctx *ctx) { - /* this isn't exhaustive, so unidentified is fine */ -} - -static INLINE UNUSED void transform_dis_bad(struct transform_dis_ctx *ctx) { - ctx->err = SUBSTITUTE_ERR_FUNC_BAD_INSN_AT_START; -} - - -/* provide arch-specific definitions of these */ -static void transform_dis_data(struct transform_dis_ctx *ctx, unsigned o0, - unsigned o1, unsigned o2, unsigned o3, unsigned out_mask); -static void transform_dis_pcrel(struct transform_dis_ctx *ctx, uintptr_t dpc, - unsigned reg, enum pcrel_load_mode mode); - -static void transform_dis_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, - bool pc_low_bit, - int *offset_by_pcdiff) { - struct transform_dis_ctx ctx; - ctx.pc_patch_start = pc_patch_start; - ctx.pc_patch_end = pc_patch_end; - ctx.pc_low_bit = pc_low_bit; - ctx.pc = pc_patch_start; - /* data is written to rewritten both by this function directly and, in case - * additional scaffolding is needed, by arch-specific transform_dis_* */ - ctx.rewritten_ptr_ptr = rewritten_ptr_ptr; - void *rewritten_start = *rewritten_ptr_ptr; - int 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; - transform_dis_dis(&ctx); - - if (ctx.err) - return ctx.err; - if (ctx.write_newop_here != NULL) { - if (ctx.op_size == 4) - *(uint32_t *) ctx.write_newop_here = ctx.newop; - else if (ctx.op_size == 2) - *(uint16_t *) ctx.write_newop_here = ctx.newop; - else - __builtin_abort(); - if (*rewritten_ptr_ptr == rewritten_ptr) - *rewritten_ptr_ptr += ctx.op_size; - } - ctx.pc += ctx.op_size; - 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); - } - return SUBSTITUTE_OK; -} - -static inline void op32(struct transform_dis_ctx *ctx, uint32_t op){ - void **rpp = ctx->rewritten_ptr_ptr; - *(uint32_t *) *rpp = op; - *rpp += 4; -} - -#include TARGET_DIS_HEADER |