aboutsummaryrefslogtreecommitdiff
path: root/lib/x86
diff options
context:
space:
mode:
Diffstat (limited to 'lib/x86')
-rw-r--r--lib/x86/arch-dis.h10
-rw-r--r--lib/x86/arch-transform-dis.inc.h58
-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.h21
-rw-r--r--lib/x86/misc.h12
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) {}