diff options
Diffstat (limited to 'lib/x86')
-rw-r--r-- | lib/x86/arch-dis.h | 10 | ||||
-rw-r--r-- | lib/x86/arch-transform-dis.inc.h | 58 | ||||
-rw-r--r-- | lib/x86/dis-main.inc.h (renamed from lib/x86/dis-x86.inc.h) | 47 | ||||
-rw-r--r-- | lib/x86/jump-patch.h | 21 | ||||
-rw-r--r-- | lib/x86/misc.h | 12 |
5 files changed, 110 insertions, 38 deletions
diff --git a/lib/x86/arch-dis.h b/lib/x86/arch-dis.h new file mode 100644 index 0000000..6447f38 --- /dev/null +++ b/lib/x86/arch-dis.h @@ -0,0 +1,10 @@ +#pragma once +#define MIN_INSN_SIZE 1 +#define TD_MAX_REWRITTEN_SIZE 100 /* XXX */ + +struct arch_pcrel_info { + int reg; +}; + +struct arch_dis_ctx {}; +static inline void arch_dis_ctx_init(UNUSED struct arch_dis_ctx *ctx) {} diff --git a/lib/x86/arch-transform-dis.inc.h b/lib/x86/arch-transform-dis.inc.h new file mode 100644 index 0000000..bb86cf9 --- /dev/null +++ b/lib/x86/arch-transform-dis.inc.h @@ -0,0 +1,58 @@ +/* Pretty trivial, but in its own file to match the other architectures. */ +#include "x86/jump-patch.h" + +static void transform_dis_pcrel(struct transform_dis_ctx *ctx, uint64_t dpc, + struct arch_pcrel_info info) { + /* push %reg; mov $dpc, %reg; <orig but with reg instead>; pop %reg */ + /* reg is rcx, or rax if the instruction might be using rcx. */ + int rax = info.reg == 1; + void *code = *ctx->rewritten_ptr_ptr; + /* push */ + op8(&code, rax ? 0x50 : 0x51); + /* mov */ +#ifdef TARGET_x86_64 + op8(&code, 0x48); + op8(&code, rax ? 0xb8 : 0xb9); + op64(&code, dpc); +#else + op8(&code, rax ? 0xb8 : 0xb9); + op32(&code, dpc); +#endif + ctx->write_newop_here = code; + code += ctx->base.op_size; + /* pop */ + op8(&code, rax ? 0x58 : 0x59); + *ctx->rewritten_ptr_ptr = code; + ctx->base.newop[0] = rax ? 0 : 1; + ctx->base.modify = true; +} + +static void transform_dis_branch(struct transform_dis_ctx *ctx, uint_tptr dpc, + int cc) { + if (dpc >= ctx->pc_patch_start && dpc < ctx->pc_patch_end) { + ctx->err = SUBSTITUTE_ERR_FUNC_BAD_INSN_AT_START; + return; + } + void *code = *ctx->rewritten_ptr_ptr; + + ctx->write_newop_here = code; + code += ctx->base.op_size; + + struct arch_dis_ctx arch; + uintptr_t source = (uintptr_t) code + 2; + int size = jump_patch_size(source, dpc, arch, true); + /* if not taken, jmp past the big jump - this is a bit suboptimal but not that bad */ + op8(&code, 0xeb); + op8(&code, size); + make_jump_patch(&code, source, dpc, arch); + + *ctx->rewritten_ptr_ptr = code; + ctx->base.newop[0] = 2; + ctx->base.modify = true; + + if (!cc) + transform_dis_ret(ctx); +} + +static void transform_dis_pre_dis(UNUSED struct transform_dis_ctx *ctx) {} +static void transform_dis_post_dis(UNUSED struct transform_dis_ctx *ctx) {} diff --git a/lib/x86/dis-x86.inc.h b/lib/x86/dis-main.inc.h index e0259ea..45a0947 100644 --- a/lib/x86/dis-x86.inc.h +++ b/lib/x86/dis-main.inc.h @@ -41,7 +41,8 @@ VEX last byte 1:0: {none, 66, f3, f2} #define I_JMP 0x40 /* execution does not continue after this */ #define I_SPEC 0x60 /* special case */ #define I_TYPE_MASK 0x60 -#define I_JIMM (0x80|I_JMP) /* imm is jump offset */ +#define I_JIMM_ONLY 0x80 /* imm is jump offset */ +#define I_JIMM (0x80|I_JMP) #define I_BAD 0x80 #ifdef TARGET_x86_64 #define if64(_64, _32) _64 @@ -72,7 +73,7 @@ static const uint8_t onebyte_bits[] = { /*D0*/ REP4(I_MODA), i64(I_8), i64(I_8), I_BAD, 0, REP8(I_SPEC), /* don't treat ljmp as a jump for now */ /*E0*/ REP4(I_8|I_JIMM), REP4(I_8), - /*E8*/ (I_z|I_JIMM)&~I_JMP, I_z|I_JIMM, i64(I_p), I_8|I_JIMM, 0, 0, 0, 0, + /*E8*/ I_z|I_JIMM_ONLY, I_z|I_JIMM, i64(I_p), I_8|I_JIMM, 0, 0, 0, 0, /*F0*/ I_PFX, I_BAD, I_PFX, I_PFX, 0, 0, I_MODA, I_MODA, /*F8*/ 0, 0, 0, 0, 0, 0, I_MODA, I_SPEC, }; @@ -111,8 +112,8 @@ static const uint8_t _0f_bits[] = { _Static_assert(sizeof(_0f_bits) == 256, "_0f_bits"); static void P(dis)(tdis_ctx ctx) { - const uint8_t *orig = ctx->ptr; - const uint8_t *ptr = ctx->ptr; + const uint8_t *orig = ctx->base.ptr; + const uint8_t *ptr = ctx->base.ptr; int opnd_size = 4; int mod, rm = 0; @@ -212,9 +213,10 @@ got_bits: UNUSED } } UNUSED int modrm_off = ptr - orig; + UNUSED uint8_t modrm; if (bits & I_MOD) { modrm: UNUSED; - uint8_t modrm = *ptr++; + modrm = *ptr++; mod = modrm >> 6; rm |= modrm & 7; if (rm == 4) { @@ -249,11 +251,11 @@ got_bits: UNUSED __builtin_abort(); ptr += imm_size; - ctx->ptr = ptr; - ctx->op_size = ptr - orig; + ctx->base.ptr = ptr; + ctx->base.newop_size = ctx->base.op_size = ptr - orig; /* printf("bits=%x\n", bits); */ - if ((bits & I_JIMM) == I_JIMM) { + if (bits & I_JIMM_ONLY) { int32_t imm; const void *imm_ptr = orig + imm_off; switch (imm_size) { @@ -265,13 +267,13 @@ got_bits: UNUSED bool cond = (byte1 & 0xf0) != 0xe0; bool call = !(bits & I_JMP); - P(branch)(ctx, ctx->pc + ctx->op_size + imm, + P(branch)(ctx, ctx->base.pc + ctx->base.op_size + imm, cond * CC_CONDITIONAL | call * CC_CALL); - if (TDIS_CTX_MODIFY(ctx)) { + if (DIS_MAY_MODIFY && ctx->base.modify) { /* newval[0] should be the new immediate */ - int32_t new_imm = TDIS_CTX_NEWVAL(ctx, 0); - uint8_t *new_op = TDIS_CTX_NEWOP(ctx); - memcpy(new_op, orig, ctx->op_size); + int32_t new_imm = ctx->base.newval[0]; + uint8_t *new_op = ctx->base.newop; + memcpy(new_op, orig, ctx->base.op_size); uint8_t *new_imm_ptr = new_op + imm_off; switch (imm_size) { case 1: *(int8_t *) new_imm_ptr = new_imm; break; @@ -284,17 +286,22 @@ got_bits: UNUSED int32_t disp = *(int32_t *) (orig + modrm_off + 1); /* unlike ARM, we can always switch to non-pcrel without making the * instruction from scratch, so we don't have 'reg' and 'lm' */ - P(pcrel)(ctx, ctx->pc + ctx->op_size + disp); - if (TDIS_CTX_MODIFY(ctx)) { - uint8_t *new_op = TDIS_CTX_NEWOP(ctx); - memcpy(new_op, orig, ctx->op_size); + struct arch_pcrel_info info = {modrm >> 3 & 7}; + P(pcrel)(ctx, ctx->base.pc + ctx->base.op_size + disp, info); + if (DIS_MAY_MODIFY && ctx->base.modify) { + uint8_t *new_op = ctx->base.newop; + memcpy(new_op, orig, ctx->base.op_size); /* newval[0] should be the new register, which should be one that * fits in r/m directly since that's all I need; - * newval[1] should be the new displacement */ + * displacement is removed */ uint8_t *new_modrm_ptr = new_op + modrm_off; - *new_modrm_ptr = (*new_modrm_ptr & ~0xc7) | 4 << 6 | TDIS_CTX_NEWVAL(ctx, 0); - *(uint32_t *) (new_modrm_ptr + 1) = TDIS_CTX_NEWVAL(ctx, 1); + *new_modrm_ptr = (*new_modrm_ptr & ~0xc7) | + 0 << 6 | + ctx->base.newval[0]; + memmove(new_modrm_ptr + 1, new_modrm_ptr + 5, + ctx->base.op_size - modrm_off - 1); + ctx->base.newop_size -= 4; } #endif } else if ((bits & I_TYPE_MASK) == I_JMP) { diff --git a/lib/x86/jump-patch.h b/lib/x86/jump-patch.h index efd4825..4c0172d 100644 --- a/lib/x86/jump-patch.h +++ b/lib/x86/jump-patch.h @@ -1,5 +1,6 @@ #pragma once #define MAX_JUMP_PATCH_SIZE 5 +#include "dis.h" static inline int jump_patch_size(uintptr_t pc, uintptr_t dpc, UNUSED struct arch_dis_ctx arch, @@ -12,21 +13,19 @@ static inline int jump_patch_size(uintptr_t pc, uintptr_t dpc, return force ? (2+4+8) : -1; } -static inline void make_jump_patch(void **codep, UNUSED uintptr_t pc, - uintptr_t dpc, +static inline void make_jump_patch(void **codep, uintptr_t pc, uintptr_t dpc, UNUSED struct arch_dis_ctx arch) { uintptr_t diff = pc - (dpc + 5); - uint8_t *code = *codep; + void *code = *codep; if (diff == (uintptr_t) (int32_t) diff) { - *(uint8_t *) code = 0xe9; - *(uint32_t *) (code + 1) = diff; - *codep = code + 5; + op8(&code, 0xe9); + op32(&code, diff); } else { /* jmpq *(%rip) */ - *code++ = 0xff; - *code++ = 0x25; - *(uint32_t *) code = 0; code += 4; - *(uint64_t *) code = dpc; code += 8; - *codep = code; + op8(&code, 0xff); + op8(&code, 0x25); + op32(&code, 0); + op64(&code, dpc); } + *codep = code; } diff --git a/lib/x86/misc.h b/lib/x86/misc.h index c8eee19..e04f1f4 100644 --- a/lib/x86/misc.h +++ b/lib/x86/misc.h @@ -1,9 +1,7 @@ #pragma once +#ifdef TARGET_x86_64 +#define TARGET_POINTER_SIZE 8 +#else +#define TARGET_POINTER_SIZE 4 +#endif #define TARGET_DIS_SUPPORTED -#define TARGET_DIS_HEADER "x86/dis-x86.inc.h" -#define TARGET_JUMP_PATCH_HDR "x86/jump-patch.h" -#define MIN_INSN_SIZE 1 -#define TD_MAX_REWRITTEN_SIZE 100 /* XXX */ - -struct arch_dis_ctx {}; -static inline void arch_dis_ctx_init(UNUSED struct arch_dis_ctx *ctx) {} |