diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/dis-arm.inc.h | 28 | ||||
-rw-r--r-- | lib/dis-thumb2.inc.h | 10 | ||||
-rw-r--r-- | lib/jump-dis.c | 32 | ||||
-rw-r--r-- | lib/transform-dis-arm-multi.inc.h | 158 | ||||
-rw-r--r-- | lib/transform-dis.c | 9 | ||||
-rw-r--r-- | lib/transform-dis.inc.h | 130 |
6 files changed, 297 insertions, 70 deletions
diff --git a/lib/dis-arm.inc.h b/lib/dis-arm.inc.h index b537442..c95e450 100644 --- a/lib/dis-arm.inc.h +++ b/lib/dis-arm.inc.h @@ -44,10 +44,10 @@ static INLINE void P(GPR_Rn_GPR_Rm_unk_Rd_1_ADDrr)(tdis_ctx ctx, struct bitslice data(rout(Rd), r(Rm), r(Rn)); } static INLINE void P(GPR_Rn_so_reg_imm_shift_unk_Rd_1_ADDrsi)(tdis_ctx ctx, struct bitslice shift, struct bitslice Rd, struct bitslice Rn) { - data(rs(shift, 0, 4), r(Rn), rout(Rd)); + data(rout(Rd), rs(shift, 0, 4), r(Rn)); } static INLINE void P(GPR_Rn_so_reg_reg_shift_unk_Rd_1_ADDrsr)(tdis_ctx ctx, struct bitslice shift, struct bitslice Rd, struct bitslice Rn) { - data(rs(shift, 0, 4), rs(shift, 8, 4), r(Rn), rout(Rd)); + data(rout(Rd), rs(shift, 0, 4), rs(shift, 8, 4), r(Rn)); } static INLINE void P(GPR_Rn_unk_Rd_1_ADDri)(tdis_ctx ctx, struct bitslice Rd, struct bitslice Rn) { data(rout(Rd), r(Rn)); @@ -56,7 +56,7 @@ static INLINE void P(GPR_Rt_4_MCR)(tdis_ctx ctx, struct bitslice Rt) { data(r(Rt)); } static INLINE void P(GPR_Rt_addr_offset_none_addr_S_3_STL)(tdis_ctx ctx, struct bitslice Rt, struct bitslice addr) { - data(r(addr), rout(Rt)); + data(rout(Rt), r(addr)); } static INLINE void P(GPR_Rt_addr_offset_none_addr_am2offset_imm_offset_S_4_STRBT_POST_IMM)(tdis_ctx ctx, UNUSED struct bitslice offset, struct bitslice Rt, struct bitslice addr) { data(r(addr), r(Rt)); @@ -105,16 +105,16 @@ static INLINE void P(addr_offset_none_addr_S_4_STC2L_OPTION)(tdis_ctx ctx, struc data(r(addr)); } static INLINE void P(addr_offset_none_addr_am2offset_imm_offset_unk_Rt_4_LDRBT_POST_IMM)(tdis_ctx ctx, UNUSED struct bitslice offset, struct bitslice Rt, struct bitslice addr) { - data(r(addr), rout(Rt)); + data(rout(Rt), r(addr)); } static INLINE void P(addr_offset_none_addr_am2offset_reg_offset_unk_Rt_4_LDRBT_POST_REG)(tdis_ctx ctx, struct bitslice offset, struct bitslice Rt, struct bitslice addr) { - data(r(addr), rs(offset, 0, 4), rout(Rt)); + data(rout(Rt), r(addr), rs(offset, 0, 4)); } static INLINE void P(addr_offset_none_addr_am3offset_offset_unk_Rt_4_LDRD_POST)(tdis_ctx ctx, struct bitslice offset, struct bitslice Rt, struct bitslice addr) { - data(r(addr), rs(offset, 0, 4), rout(Rt)); + data(rout(Rt), r(addr), rs(offset, 0, 4)); } static INLINE void P(addr_offset_none_addr_postidx_imm8_offset_unk_Rt_3_LDRHTi)(tdis_ctx ctx, UNUSED struct bitslice offset, struct bitslice Rt, struct bitslice addr) { - data(r(addr), rout(Rt)); + data(rout(Rt), r(addr)); } static INLINE void P(addr_offset_none_addr_postidx_imm8s4_offset_4_LDC2L_POST)(tdis_ctx ctx, UNUSED struct bitslice offset, struct bitslice addr) { data(r(addr)); @@ -123,7 +123,7 @@ static INLINE void P(addr_offset_none_addr_postidx_imm8s4_offset_S_4_STC2L_POST) data(r(addr)); } static INLINE void P(addr_offset_none_addr_unk_Rt_13_LDA)(tdis_ctx ctx, struct bitslice Rt, struct bitslice addr) { - data(r(addr), rout(Rt)); + data(rout(Rt), r(addr)); } static INLINE void P(addrmode3_addr_unk_Rt_4_LDRD)(tdis_ctx ctx, struct bitslice addr, UNUSED struct bitslice Rt) { /* ignoring Rt2 = Rt + 1, but it isn't supposed to load PC anyway */ @@ -133,7 +133,7 @@ static INLINE void P(addrmode3_pre_addr_unk_Rt_4_LDRD_PRE)(tdis_ctx ctx, struct data(rs(addr, 9, 4), rs(addr, 0, 4)); } static INLINE void P(addrmode5_addr_8_LDC2L_OFFSET)(tdis_ctx ctx, struct bitslice addr) { - data(rsout(addr, 9, 4)); + data(rs(addr, 9, 4)); } static INLINE void P(addrmode5_addr_S_4_STC2L_OFFSET)(tdis_ctx ctx, struct bitslice addr) { data(rs(addr, 9, 4)); @@ -145,10 +145,10 @@ static INLINE void P(addrmode5_pre_addr_S_4_STC2L_PRE)(tdis_ctx ctx, struct bits data(rs(addr, 9, 4)); } static INLINE void P(addrmode_imm12_addr_unk_Rt_2_LDRBi12)(tdis_ctx ctx, struct bitslice addr, struct bitslice Rt) { - data(rs(addr, 13, 4), rout(Rt)); + data(rout(Rt), rs(addr, 13, 4)); } static INLINE void P(addrmode_imm12_pre_addr_unk_Rt_2_LDRB_PRE_IMM)(tdis_ctx ctx, struct bitslice addr, struct bitslice Rt) { - data(rs(addr, 13, 4), rout(Rt)); + data(rout(Rt), rs(addr, 13, 4)); } static INLINE void P(adrlabel_label_unk_Rd_1_ADR)(tdis_ctx ctx, struct bitslice label, struct bitslice Rd) { return P(pcrel)(ctx, ctx->pc + 8 + bs_get(label, ctx->op), bs_get(Rd, ctx->op), PLM_ADR); @@ -158,10 +158,10 @@ static INLINE void P(br_target_target_B_1_Bcc)(tdis_ctx ctx, struct bitslice tar return P(branch)(ctx, ctx->pc + 8 + sext(bs_get(target, ctx->op), 24), /*cond*/ cond); } static INLINE void P(ldst_so_reg_addr_unk_Rt_2_LDRB_PRE_REG)(tdis_ctx ctx, struct bitslice addr, struct bitslice Rt) { - data(rs(addr, 0, 4), rs(addr, 13, 4), rout(Rt)); + data(rout(Rt), rs(addr, 0, 4), rs(addr, 13, 4)); } static INLINE void P(ldst_so_reg_shift_unk_Rt_2_LDRBrs)(tdis_ctx ctx, struct bitslice shift, struct bitslice Rt) { - data(rs(shift, 0, 4), rs(shift, 13, 4), rout(Rt)); + data(rout(Rt), rs(shift, 0, 4), rs(shift, 13, 4)); } static INLINE void P(tcGPR_Rm_unk_Rd_1_MOVr_TC)(tdis_ctx ctx, struct bitslice Rm, struct bitslice Rd) { data(rout(Rd), r(Rm)); @@ -188,7 +188,7 @@ static INLINE void P(GPR_Rt_addr_offset_none_addr_unk_Rd_S_6_STLEX)(tdis_ctx ctx data(r(addr), r(Rt), r(Rd)); } static INLINE void P(addr_offset_none_addr_postidx_reg_Rm_unk_Rt_3_LDRHTr)(tdis_ctx ctx, struct bitslice Rm, struct bitslice Rt, struct bitslice addr) { - data(r(addr), rout(Rt), r(Rm)); + data(rout(Rt), r(addr), r(Rm)); } static INLINE void P(GPR_Rt_addr_offset_none_addr_postidx_reg_Rm_S_1_STRHTr)(tdis_ctx ctx, struct bitslice Rm, struct bitslice Rt, struct bitslice addr) { data(r(addr), r(Rt), r(Rm)); diff --git a/lib/dis-thumb2.inc.h b/lib/dis-thumb2.inc.h index 81b293d..36d341e 100644 --- a/lib/dis-thumb2.inc.h +++ b/lib/dis-thumb2.inc.h @@ -2,12 +2,12 @@ /* TODO: handle 'it' for conditional br/ret!! */ -static inline enum pcrel_load_mode get_thumb_load_mode(unsigned op) { +static inline enum pcrel_load_mode get_thumb2_load_mode(unsigned op) { bool sign = (op >> 8) & 1; switch ((op >> 5) & 3) { case 0: return sign ? PLM_S8 : PLM_U8; case 1: return sign ? PLM_S16 : PLM_U16; - case 2: return sign ? PLM_S32 : PLM_U32; + case 2: return PLM_U32; default: __builtin_abort(); } } @@ -55,7 +55,7 @@ static INLINE void P(rGPR_Rt_addr_offset_none_addr_S_4_t2STL)(tdis_ctx ctx, stru data(rout(Rt), r(addr)); } static INLINE void P(rGPR_Rt_addr_offset_none_addr_unk_Rd_S_7_t2STLEX)(tdis_ctx ctx, struct bitslice Rd, struct bitslice Rt, struct bitslice addr) { - data(rout(Rd), rout(Rt), r(addr)); + data(rout(Rd), r(Rt), r(addr)); } static INLINE void P(addr_offset_none_addr_4_t2LDC2L_OPTION)(tdis_ctx ctx, struct bitslice addr) { data(r(addr)); @@ -142,10 +142,10 @@ static INLINE void P(t2addrmode_so_reg_addr_unk_Rt_5_t2LDRBs)(tdis_ctx ctx, stru data(rout(Rt), rs(addr, 6, 4), rs(addr, 2, 4)); } static INLINE void P(t2adrlabel_addr_unk_Rd_1_t2ADR)(tdis_ctx ctx, struct bitslice addr, struct bitslice Rd) { - return P(pcrel)(ctx, ((ctx->pc + 4) & ~2) + (bs_get(addr, ctx->op) & ((1 << 12) - 1)), bs_get(Rd, ctx->op), false); + return P(pcrel)(ctx, ((ctx->pc + 4) & ~2) + (bs_get(addr, ctx->op) & ((1 << 12) - 1)), bs_get(Rd, ctx->op), PLM_ADR); } static INLINE void P(t2ldrlabel_addr_unk_Rt_5_t2LDRBpci)(tdis_ctx ctx, struct bitslice addr, struct bitslice Rt) { - return P(pcrel)(ctx, ((ctx->pc + 4) & ~2) + (bs_get(addr, ctx->op) & ((1 << 12) - 1)), bs_get(Rt, ctx->op), true); + return P(pcrel)(ctx, ((ctx->pc + 4) & ~2) + (bs_get(addr, ctx->op) & ((1 << 12) - 1)), bs_get(Rt, ctx->op), get_thumb2_load_mode(ctx->op)); } static INLINE void P(uncondbrtarget_target_B_1_t2B)(tdis_ctx ctx, struct bitslice target) { return P(branch)(ctx, ctx->pc + 4 + 2 * sext(bs_get(target, ctx->op), 24), /*cond*/ false); diff --git a/lib/jump-dis.c b/lib/jump-dis.c index c7c7346..aa3b5f3 100644 --- a/lib/jump-dis.c +++ b/lib/jump-dis.c @@ -27,7 +27,7 @@ struct jump_dis_ctx { uintptr_t pc_patch_end; bool pc_low_bit; unsigned op; - void *ptr; + const void *ptr; int op_size; uint8_t seen_mask[JUMP_ANALYSIS_MAX_INSNS / 8]; /* queue of instructions to visit */ @@ -68,7 +68,9 @@ static void jump_dis_add_to_queue(struct jump_dis_ctx *ctx, uintptr_t pc) { if (!ctx->queue) substitute_panic("%s: out of memory\n", __func__); size_t new_read_off = new_size - (ctx->queue_size - ctx->queue_read_off); - memmove(ctx->queue + new_read_off, ctx->queue + ctx->queue_read_off, (ctx->queue_size - ctx->queue_read_off) * sizeof(*ctx->queue)); + memmove(ctx->queue + new_read_off, + ctx->queue + ctx->queue_read_off, + (ctx->queue_size - ctx->queue_read_off) * sizeof(*ctx->queue)); ctx->queue_read_off = new_read_off % new_size; ctx->queue_size = new_size; } @@ -77,22 +79,23 @@ static void jump_dis_add_to_queue(struct jump_dis_ctx *ctx, uintptr_t pc) { ctx->queue_count++; } - -static INLINE inline void jump_dis_data(UNUSED struct jump_dis_ctx *ctx, UNUSED unsigned o0, UNUSED unsigned o1, UNUSED unsigned o2, UNUSED unsigned o3, UNUSED unsigned out_mask) { +static INLINE UNUSED void jump_dis_data(UNUSED struct jump_dis_ctx *ctx, + UNUSED unsigned o0, UNUSED unsigned o1, UNUSED unsigned o2, + UNUSED unsigned o3, UNUSED unsigned out_mask) { /* on ARM, ignore mov PC jumps, as they're unlikely to be in the same function */ } -static INLINE inline void jump_dis_pcrel(struct jump_dis_ctx *ctx, uintptr_t dpc, UNUSED unsigned reg, UNUSED bool is_load) { +static INLINE UNUSED void jump_dis_pcrel(struct jump_dis_ctx *ctx, uintptr_t dpc, + UNUSED unsigned reg, UNUSED bool is_load) { ctx->bad_insn = dpc >= ctx->pc_patch_start && dpc < ctx->pc_patch_end; } -NOINLINE UNUSED -static void jump_dis_ret(struct jump_dis_ctx *ctx) { +static INLINE UNUSED void jump_dis_ret(struct jump_dis_ctx *ctx) { ctx->continue_after_this_insn = false; } -NOINLINE UNUSED -static void jump_dis_branch(struct jump_dis_ctx *ctx, uintptr_t dpc, bool conditional) { +static NOINLINE UNUSED void jump_dis_branch(struct jump_dis_ctx *ctx, uintptr_t dpc, + bool conditional) { if (dpc >= ctx->pc_patch_start && dpc < ctx->pc_patch_end) { ctx->bad_insn = true; return; @@ -104,18 +107,17 @@ static void jump_dis_branch(struct jump_dis_ctx *ctx, uintptr_t dpc, bool condit ctx->continue_after_this_insn = conditional; } -NOINLINE UNUSED -static void jump_dis_unidentified(UNUSED struct jump_dis_ctx *ctx) { +static INLINE UNUSED void jump_dis_unidentified(UNUSED struct jump_dis_ctx *ctx) { } -NOINLINE UNUSED -static void jump_dis_bad(struct jump_dis_ctx *ctx) { +static INLINE UNUSED void jump_dis_bad(struct jump_dis_ctx *ctx) { ctx->continue_after_this_insn = false; } -static void jump_dis_dis(tdis_ctx ctx); +static void jump_dis_dis(struct jump_dis_ctx *ctx); -bool jump_dis_main(void *code_ptr, uintptr_t pc_patch_start, uintptr_t pc_patch_end, bool pc_low_bit) { +bool jump_dis_main(const void *code_ptr, uintptr_t pc_patch_start, + uintptr_t pc_patch_end, bool pc_low_bit) { bool ret; struct jump_dis_ctx ctx; memset(&ctx, 0, sizeof(ctx)); diff --git a/lib/transform-dis-arm-multi.inc.h b/lib/transform-dis-arm-multi.inc.h new file mode 100644 index 0000000..3c99911 --- /dev/null +++ b/lib/transform-dis-arm-multi.inc.h @@ -0,0 +1,158 @@ +static inline void PUSHone(struct transform_dis_ctx *ctx, int Rt) { + if (ctx->pc_low_bit) + op32(ctx, 0x0d04f84d | Rt << 28); + else + op32(ctx, 0xe52d0004 | Rt << 12); + +} + +static inline void POPone(struct transform_dis_ctx *ctx, int Rt) { + if (ctx->pc_low_bit) + op32(ctx, 0x0b04f85d | Rt << 28); + else + op32(ctx, 0xe49d0004 | Rt << 12); +} + +static inline void POPmulti(struct transform_dis_ctx *ctx, uint16_t mask) { + if (ctx->pc_low_bit) + op32(ctx, 0x0000e8bd | mask << 16); + else + op32(ctx, 0xe8bd0000 | mask); +} + +static inline void MOVW_MOVT(struct transform_dis_ctx *ctx, int Rd, uint32_t val) { + uint16_t hi = val >> 16, lo = (uint16_t) val; + if (ctx->pc_low_bit) { + op32(ctx, 0x0000f240 | Rd << 24 | lo >> 12 | (lo >> 11 & 1) << 10 | + (lo >> 8 & 7) << 28 | (lo & 0xff) << 16); + op32(ctx, 0x0000f2c0 | Rd << 24 | hi >> 12 | (hi >> 11 & 1) << 10 | + (hi >> 8 & 7) << 28 | (hi & 0xff) << 16); + + } else { + op32(ctx, 0xe3000000 | Rd << 12 | (lo >> 12) << 16 | (lo & 0xfff)); + op32(ctx, 0xe3400000 | Rd << 12 | (hi >> 12) << 16 | (hi & 0xfff)); + } + +} + +static inline void STRri(struct transform_dis_ctx *ctx, int Rt, int Rn, uint32_t off) { + if (ctx->pc_low_bit) + op32(ctx, 0x0000f8c0 | Rn | Rt << 28 | off << 16); + else + op32(ctx, 0xe4800000 | Rn << 16 | Rt << 12 | off); +} + +static inline void LDRxi(struct transform_dis_ctx *ctx, int Rt, int Rn, uint32_t off, + enum pcrel_load_mode load_mode) { + 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; + default: __builtin_abort(); + } + op32(ctx, 0x0000f8d0 | Rn | Rt << 28 | subop << 5 | sign << 8 | off << 16); + } else { + int is_byte, subop; + 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; + 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); + break; + default: + __builtin_abort(); + } + } +} + +static NOINLINE UNUSED void transform_dis_data(struct transform_dis_ctx *ctx, + unsigned o0, unsigned o1, unsigned o2, unsigned o3, unsigned out_mask) { + /* We only care if at least one op is PC, so quickly test that. */ + if (((o0 | o1 | o2 | o3) & 15) != 15) + return; + unsigned *newval = ctx->newval; + newval[0] = o0; + newval[1] = o1; + newval[2] = o2; + newval[3] = o3; + + void **rpp = ctx->rewritten_ptr_ptr; + + /* A few cases: + * 1. Move to PC that does not read PC. Probably fine. + * 2. Move to PC that does read PC, e.g. 'ldrls pc, [pc, r0, lsl #2]'. + * This is different from #4 mainly in that we can't need to do + * something like pop {temp, pc}. Not terribly plausible (only likely + * in non-position-independent code in ARM mode, and I can't get it to + * happen in the first 8 bytes then), but we may as well handle it. + * 3. Read of PC that does not read the register(s) it writes, e.g. adr r3, + * X. In this case we can use that register as a temporary. + * 4. Read of PC that does, or doesn't have any output register, e.g. add + * r3, pc. In this case we use the stack because reliably finding a + * free register would be work, and might not even be possible (thumb + * mov r9, r0; mov r12, r1; <some PC using insn(s)>) + * the out register is always first. + */ + uint16_t in_regs = 0; + int out_reg = -1; + for (int i = 0; i < 4; i++) { + if (out_mask & 1 << i) + out_reg = 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))); + if (out_reg == 15) { + if (in_regs & 1 << 15) + return; /* case 1 */ + /* case 2 */ + PUSHone(ctx, scratch); + PUSHone(ctx, scratch); + MOVW_MOVT(ctx, scratch, pc); + for (int i = 0; i < 4; i++) + if (newval[i] == 15) + newval[i] = scratch; + ctx->write_newop_here = *rpp; *rpp += ctx->op_size; + STRri(ctx, scratch, 13, 4); + POPmulti(ctx, 1 << scratch | 1 << 15); + } else { + 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++) + if (newval[i] == 15) + newval[i] = out_reg; + ctx->write_newop_here = *rpp; *rpp += ctx->op_size; + } else { + /* case 4 */ + PUSHone(ctx, scratch); + MOVW_MOVT(ctx, scratch, pc); + for (int i = 1; i < 4; i++) + if (newval[i] == 15) + newval[i] = scratch; + ctx->write_newop_here = *rpp; *rpp += ctx->op_size; + POPone(ctx, 1 << scratch); + } + } + ctx->modify = true; +} + +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; + 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 new file mode 100644 index 0000000..6fc6d7f --- /dev/null +++ b/lib/transform-dis.c @@ -0,0 +1,9 @@ +#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 +#endif 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 |