diff options
Diffstat (limited to 'lib/transform-dis.inc.h')
-rw-r--r-- | lib/transform-dis.inc.h | 130 |
1 files changed, 94 insertions, 36 deletions
diff --git a/lib/transform-dis.inc.h b/lib/transform-dis.inc.h index a229aaf..b20012a 100644 --- a/lib/transform-dis.inc.h +++ b/lib/transform-dis.inc.h @@ -1,60 +1,118 @@ -typedef struct { +#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; - bool bad; -} void; + int err; -typedef struct tc { + bool pc_low_bit; uintptr_t pc_patch_start; uintptr_t pc_patch_end; uintptr_t pc; int op_size; - uint32_t op; - uint32_t newop; - uint32_t newval[4]; + unsigned op; + unsigned newop; + unsigned newval[4]; -} *tdis_ctx; + const void *ptr; + void **rewritten_ptr_ptr; + void *write_newop_here; +}; -NOINLINE UNUSED -static void P_data(struct tc *ctx, unsigned o0, unsigned o1, unsigned o2, unsigned o3, unsigned out_mask) { - / - if (((o0 | o1 | o2 | o3) & (MAX_REGS - 1)) == (MAX_REGS - 1)) { - - __builtin_abort(); -} +#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)) -NOINLINE UNUSED -static void P_pcrel(struct tc *ctx, uintptr_t dpc, unsigned reg, bool is_load) { - __builtin_abort(); -} +/* largely similar to jump_dis */ -NOINLINE UNUSED -static void P_ret(struct tc *ctx) { +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) - return (void) {0}; - else - return (void) {.bad = true}; + if (ctx->pc + ctx->op_size < ctx->pc_patch_end) + ctx->err = SUBSTITUTE_ERR_FUNC_TOO_SHORT; } -NOINLINE UNUSED -static void P_branch(struct tc *ctx, uintptr_t dpc) { +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 */ - return (void) {.bad = true}; + ctx->err = SUBSTITUTE_ERR_FUNC_BAD_INSN_AT_START; } /* branch out of bounds is fine */ - return (void) {0}; } -NOINLINE UNUSED -static void P_unidentified(struct tc *ctx) { +static INLINE UNUSED void transform_dis_unidentified(UNUSED struct transform_dis_ctx *ctx) { /* this isn't exhaustive, so unidentified is fine */ - return (void) {0}; } -NOINLINE UNUSED -static void P_bad(struct tc *ctx) { - return (void) {.bad = true}; +static INLINE UNUSED void transform_dis_bad(struct transform_dis_ctx *ctx) { + ctx->err = SUBSTITUTE_ERR_FUNC_BAD_INSN_AT_START; } -#define P(x) transform_dis_##x + +/* 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 |