aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile14
-rw-r--r--lib/arm/misc.h6
-rw-r--r--lib/arm/transform-dis-arm-multi.inc.h1
-rw-r--r--lib/arm64/misc.h4
-rw-r--r--lib/arm64/transform-dis-arm64.inc.h1
-rw-r--r--lib/dis.h1
-rw-r--r--lib/substitute-internal.h2
-rw-r--r--lib/x86/dis-tables.h99
-rw-r--r--lib/x86/dis-x86.inc.h315
-rw-r--r--lib/x86/jump-patch.h32
-rw-r--r--lib/x86/misc.h9
-rw-r--r--test/test-td-simple.c87
12 files changed, 410 insertions, 161 deletions
diff --git a/Makefile b/Makefile
index 1ebe805..91b8d90 100644
--- a/Makefile
+++ b/Makefile
@@ -105,18 +105,20 @@ out/%.bin: out/%.o Makefile
segedit -extract __TEXT __text $@ $<
define define_test
-out/test-$(1): test/test-$(2).[cm]* $(HEADERS) $(GENERATED) Makefile out/libsubstitute.dylib
- $(3) -o $$@ $$< -Ilib -Isubstrate -Lout -lsubstitute -dead_strip
+out/test-$(1): test/test-$(2).[cm]* $(HEADERS) $(GENERATED) Makefile # out/libsubstitute.dylib
+ $(3) -o $$@ $$< -Ilib -Isubstrate -Lout -dead_strip #-lsubstitute
install_name_tool -change /usr/lib/libsubstitute.0.dylib '@executable_path/libsubstitute.dylib' $$@
ifneq (,$(IS_IOS))
ldid -Sent.plist $$@
endif
tests: out/test-$(1)
endef
-$(eval $(call define_test,tdarm-simple,td-simple,$(CC) -std=c11 -DHDR='"arm/dis-arm.inc.h"' -Dxdis=dis_arm -DFORCE_TARGET_arm))
-$(eval $(call define_test,tdthumb-simple,td-simple,$(CC) -std=c11 -DHDR='"arm/dis-thumb.inc.h"' -Dxdis=dis_thumb -DFORCE_TARGET_arm))
-$(eval $(call define_test,tdthumb2-simple,td-simple,$(CC) -std=c11 -DHDR='"arm/dis-thumb2.inc.h"' -Dxdis=dis_thumb2 -DFORCE_TARGET_arm))
-$(eval $(call define_test,tdarm64-simple,td-simple,$(CC) -std=c11 -DHDR='"arm64/dis-arm64.inc.h"' -Dxdis=dis -DFORCE_TARGET_arm64))
+$(eval $(call define_test,td-simple-arm,td-simple,$(CC) -std=c11 -DHDR='"arm/dis-arm.inc.h"' -Dxdis=dis_arm -DFORCE_TARGET_arm))
+$(eval $(call define_test,td-simple-thumb,td-simple,$(CC) -std=c11 -DHDR='"arm/dis-thumb.inc.h"' -Dxdis=dis_thumb -DFORCE_TARGET_arm))
+$(eval $(call define_test,td-simple-thumb2,td-simple,$(CC) -std=c11 -DHDR='"arm/dis-thumb2.inc.h"' -Dxdis=dis_thumb2 -DFORCE_TARGET_arm))
+$(eval $(call define_test,td-simple-arm64,td-simple,$(CC) -std=c11 -DHDR='"arm64/dis-arm64.inc.h"' -Dxdis=dis -DFORCE_TARGET_arm64))
+$(eval $(call define_test,td-simple-i386,td-simple,$(CC) -std=c11 -DHDR='"x86/dis-x86.inc.h"' -Dxdis=dis -DFORCE_TARGET_i386))
+$(eval $(call define_test,td-simple-x86-64,td-simple,$(CC) -std=c11 -DHDR='"x86/dis-x86.inc.h"' -Dxdis=dis -DFORCE_TARGET_x86_64))
$(eval $(call define_test,dis-arm,dis,$(CC) -std=c11 -DFORCE_TARGET_arm))
$(eval $(call define_test,dis-arm64,dis,$(CC) -std=c11 -DFORCE_TARGET_arm64))
$(eval $(call define_test,jump-dis-arm,jump-dis,$(CC) -std=c11 -DFORCE_TARGET_arm -O0))
diff --git a/lib/arm/misc.h b/lib/arm/misc.h
index 02b06fe..ef11a05 100644
--- a/lib/arm/misc.h
+++ b/lib/arm/misc.h
@@ -50,10 +50,10 @@ static inline void advance_it_cond(struct arch_dis_ctx *ctx) {
/* Types of conditionals for 'branch' */
/* a regular old branch-with-condition */
-#define CC_ARMCC (CC_CONDITIONAL | 0x200)
+#define CC_ARMCC (CC_CONDITIONAL | 0x400)
/* already in an IT block - in transform_dis this will be rewritten to a branch
* anyway, so it can be treated as unconditional; in jump_dis we have to know
* to keep going */
-#define CC_ALREADY_IN_IT (CC_CONDITIONAL | 0x400)
+#define CC_ALREADY_IN_IT (CC_CONDITIONAL | 0x800)
/* CBZ/CBNZ is rewritten */
-#define CC_CBXZ (CC_CONDITIONAL | 0x800)
+#define CC_CBXZ (CC_CONDITIONAL | 0xc00)
diff --git a/lib/arm/transform-dis-arm-multi.inc.h b/lib/arm/transform-dis-arm-multi.inc.h
index e080866..6e91ff5 100644
--- a/lib/arm/transform-dis-arm-multi.inc.h
+++ b/lib/arm/transform-dis-arm-multi.inc.h
@@ -1,3 +1,4 @@
+/* TODO fix BL incl MOV LR, PC */
#include "arm/assemble.h"
static struct assemble_ctx tdctx_to_actx(const struct transform_dis_ctx *ctx) {
diff --git a/lib/arm64/misc.h b/lib/arm64/misc.h
index c21bc0b..f5a6154 100644
--- a/lib/arm64/misc.h
+++ b/lib/arm64/misc.h
@@ -32,5 +32,5 @@ static inline int arm64_get_unwritten_temp_reg(struct arch_dis_ctx *ctx) {
return 31 - __builtin_clz(avail);
}
-#define CC_ARMCC (CC_CONDITIONAL | 0x200)
-#define CC_XBXZ (CC_CONDITIONAL | 0x400)
+#define CC_ARMCC (CC_CONDITIONAL | 0x400)
+#define CC_XBXZ (CC_CONDITIONAL | 0x800)
diff --git a/lib/arm64/transform-dis-arm64.inc.h b/lib/arm64/transform-dis-arm64.inc.h
index a98932d..792b835 100644
--- a/lib/arm64/transform-dis-arm64.inc.h
+++ b/lib/arm64/transform-dis-arm64.inc.h
@@ -17,6 +17,7 @@ void transform_dis_pcrel(struct transform_dis_ctx *ctx, uintptr_t dpc, unsigned
static NOINLINE UNUSED
void transform_dis_branch(struct transform_dis_ctx *ctx, uintptr_t dpc, int cc) {
+ /* TODO fix BL */
#ifdef TRANSFORM_DIS_VERBOSE
printf("transform_dis (%p): branch => %p\n", (void *) ctx->pc, (void *) dpc);
#endif
diff --git a/lib/dis.h b/lib/dis.h
index a5af29a..6b189e0 100644
--- a/lib/dis.h
+++ b/lib/dis.h
@@ -122,3 +122,4 @@ static inline void op16(void **codep, uint16_t op) {
}
#define CC_CONDITIONAL 0x100
+#define CC_CALL 0x200
diff --git a/lib/substitute-internal.h b/lib/substitute-internal.h
index f93d1eb..17ad6ec 100644
--- a/lib/substitute-internal.h
+++ b/lib/substitute-internal.h
@@ -52,6 +52,8 @@ typedef struct section section_x;
#include "arm/misc.h"
#elif defined(TARGET_arm64)
#include "arm64/misc.h"
+#elif defined(TARGET_x86_64) || defined(TARGET_i386)
+ #include "x86/misc.h"
#endif
#ifdef __APPLE__
diff --git a/lib/x86/dis-tables.h b/lib/x86/dis-tables.h
deleted file mode 100644
index 6d24b16..0000000
--- a/lib/x86/dis-tables.h
+++ /dev/null
@@ -1,99 +0,0 @@
-#pragma once
-#include <stdlib.h>
-/*
-prefixes REX opc ModR/M SIB displacement immediate
-
-1A/C: modrm stuff
-i64: 32 only
-o64: 64 only
-
-CDEGMNPQRSUVW: modrm
-EMQW: modrm w/ address
-IJO: immediate
-L: 8-bit immediate
-
-a Two one-word operands in memory or two double-word operands in memory, depending on operand-size attribute (used only by the BOUND instruction).
-b Byte, regardless of operand-size attribute.
-c Byte or word, depending on operand-size attribute.
-d Doubleword, regardless of operand-size attribute.
-dq Double-quadword, regardless of operand-size attribute.
-p 32-bit, 48-bit, or 80-bit pointer, depending on operand-size attribute.
-pd 128-bit or 256-bit packed double-precision floating-point data. pi Quadword MMX technology register (for example: mm0).
-ps 128-bit or 256-bit packed single-precision floating-point data. q Quadword, regardless of operand-size attribute.
-qq Quad-Quadword (256-bits), regardless of operand-size attribute. s 6-byte or 10-byte pseudo-descriptor.
-sd Scalar element of a 128-bit double-precision floating data.
-ss Scalar element of a 128-bit single-precision floating data.
-si Doubleword integer register (for example: eax).
-v Word, doubleword or quadword (in 64-bit mode), depending on operand-size attribute.
-w Word, regardless of operand-size attribute.
-x dq or qq based on the operand-size attribute.
-y Doubleword or quadword (in 64-bit mode), depending on operand-size attribute.
-z Word for 16-bit operand-size or doubleword for 32 or 64-bit operand-size.
-
-*/
-#define REP4(x) x, x, x, x
-#define REP8(x) REP4(x), REP4(x)
-#define REP16(x) REP8(x), REP8(x)
-#define I_8 0x01
-#define I_16 0x02
-#define I_24 0x04
-#define I_v 0x04
-#define I_z 0x05
-#define I_p 0x06
-#define I_MOD 0x08
-#define I_ADDR 0x10
-#define I_MODA (I_MOD|I_ADDR)
-#define I_PFX 0x20
-#define I_BAD 0x80
-#define I_SPEC 0x00
-#ifdef TARGET_x86_64
-#define if64(_64, _32) _64
-#else
-#define if64(_64, _32) _32
-#endif
-#define i64(x) if64(I_BAD, x)
-#define o64(x) if64(x, I_BAD)
-
-static const uint8_t onebyte_bits[] = {
-/*0x*/ REP4(I_MODA), I_8, I_v, i64(0), i64(0), REP4(I_MODA), I_8, I_z, i64(0), I_SPEC,
-/*1x*/ REP4(I_MODA), I_8, I_v, i64(0), i64(0), REP4(I_MODA), I_8, I_z, i64(0), i64(0),
-/*2x*/ REP4(I_MODA), I_8, I_v, I_PFX, i64(0), REP4(I_MODA), I_8, I_z, I_PFX, i64(0),
-/*3x*/ REP4(I_MODA), I_8, I_v, I_PFX, i64(0), REP4(I_MODA), I_8, I_z, I_PFX, i64(0),
-/*4x*/ REP16(if64(PFX, 0)),
-/*5x*/ REP16(0),
-/*6x*/ i64(0), i64(0), i64(I_MOD), I_MOD|I_ADD, I_PFX, I_PFX, I_PFX, I_PFX,
- I_z, I_MODA|I_z, I_8, I_MODA|I_8, REP4(0),
-/*7x*/ REP16(I_8),
-/*8x*/ I_MODA|I_8, I_MODA|I_v, i64(I_MODA|I_8), I_MODA|I_8, I_MODA|I_8, I_MODA|I_v, I_MODA|I_8, I_MODA|I_v,
- REP4(I_MODA), I_MOD, I_MODA, I_MOD, I_MODA,
-/*9x*/ REP8(0), 0, 0, i64(0), 0, 0, 0, 0, 0,
-/*Ax*/ I_8, I_v, I_8, I_v, REP4(0), I_8, I_z, 0, 0, 0, 0, 0, 0,
-/*Bx*/ REP8(I_8), REP8(I_v),
-/*Cx*/ I_MODA|I_8, I_MODA|I_8, I_16, 0, i64(I_MODA), i64(I_MODA), I_MODA|I_8, I_MODA|I_8,
- I_24, 0, I_16, 0, 0, I_8, i64(0), 0,
-/*Dx*/ REP4(I_MODA), i64(I_8), i64(I_8), I_BAD, 0, REP8(I_SPEC),
-/*Ex*/ REP8(I_8), I_z, I_z, I_p, I_8, 0, 0, 0, 0,
-/*Fx*/ I_PFX, I_BAD, I_PFX, I_PFX, 0, 0, I_MODA, I_MODA, 0, 0, 0, 0, 0, 0, I_MODA, I_MODA,
-};
-_Static_assert(sizeof(onebyte_bits) == 256, "onebyte_bits");
-
-static const uint8_t _0f_bits[] = {
-/*0x*/ I_MODA, I_MODA, 0, 0, I_BAD, o64(0), 0, o64(0), 0, 0, I_BAD, 0, 0, I_MODA, 0, 0,
-/*1x*/ REP8(I_MODA), I_MODA, I_BAD, I_BAD, I_BAD, I_BAD, I_BAD, I_BAD, I_MODA,
-/*2x*/ REP4(I_MOD), REP4(I_BAD), REP8(I_MODA),
-/*3x*/ 0, 0, 0, 0, 0, 0, I_BAD, 0, I_SPEC, I_BAD, I_SPEC, I_BAD, REP4(I_BAD),
-/*4x*/ REP16(I_MODA),
-/*5x*/ I_MOD, I_MODA, I_MODA, I_MODA, REP4(I_MODA), REP8(I_MODA),
-/*6x*/ REP16(I_MODA),
-/*7x*/ I_MODA, I_MOD|I_8, I_MOD|I_8, I_MOD|I_8, I_MODA, I_MODA, I_MODA, 0,
- I_MODA, I_MODA, I_BAD, I_BAD, REP4(I_MODA),
-/*8x*/ REP16(I_z),
-/*9x*/ REP16(I_MODA),
-/*Ax*/ 0, 0, 0, 0, 0, 0, I_BAD, I_BAD, 0, 0, 0, I_MODA, I_MODA|I_8, I_MODA, I_MODA, I_MODA,
-/*Bx*/ REP8(I_MODA), I_MODA, 0, I_MODA|I_8, I_MODA, REP4(I_MODA),
-/*Cx*/ I_MODA, I_MODA, I_MODA|I_8, I_MODA, I_MODA|I_8, I_MOD|I_8, I_MODA|I_8, I_MODA|I_z, REP8(0),
-/*Dx*/ REP4(I_MODA), I_MODA, I_MODA, I_MODA, I_MOD, REP8(I_MODA),
-/*Ex*/ REP16(I_MODA),
-/*Fx*/ REP4(I_MODA), I_MODA, I_MODA, I_MODA, I_MOD, REP4(I_MODA), I_MODA, I_MODA, I_MODA, I_BAD,
-};
-_Static_assert(sizeof(_0f_bits) == 256, "_0f_bits");
diff --git a/lib/x86/dis-x86.inc.h b/lib/x86/dis-x86.inc.h
index 83645ec..e0259ea 100644
--- a/lib/x86/dis-x86.inc.h
+++ b/lib/x86/dis-x86.inc.h
@@ -1,40 +1,305 @@
-static void P(dis_onebyte)(tdis_ctx ctx) {
- uint8_t *ptr = ctx->ptr;
+/*
+random notes:
+
+REX: 0100wrxb
+
+prefixes REX opc ModR/M SIB displacement immediate
+
+1A/C: modrm stuff
+i64: 32 only
+o64: 64 only
+
+CDEGMNPQRSUVW: modrm
+EMQW: modrm w/ address
+IJO: immediate
+L: 8-bit immediate
+
+VEX last byte 1:0: {none, 66, f3, f2}
+
+*/
+
+
+/* This is probably not the most efficient implementation, but hopefully good
+ * enough... */
+
+#define REP4(x) x, x, x, x
+#define REP8(x) REP4(x), REP4(x)
+#define REP16(x) REP8(x), REP8(x)
+#define I_8 0x01
+#define I_16 0x02
+#define I_24 0x03
+#define I_32 0x04
+#define I_v 0x05
+#define I_z 0x06
+#define I_p 0x07
+#define I_IMM_MASK 0x07
+#define I_MOD 0x08
+#define I_ADDR 0x10
+#define I_MODA (I_MOD|I_ADDR)
+/* mutually exclusive types */
+#define I_PFX 0x20 /* prefix */
+#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_BAD 0x80
+#ifdef TARGET_x86_64
+#define if64(_64, _32) _64
+#else
+#define if64(_64, _32) _32
+#endif
+#define i64(x) if64(I_BAD, x)
+#define o64(x) if64(x, I_BAD)
+
+static const uint8_t onebyte_bits[] = {
+/*00*/ REP4(I_MODA), I_8, I_z, i64(0), i64(0), REP4(I_MODA), I_8, I_z, i64(0), I_SPEC,
+/*10*/ REP4(I_MODA), I_8, I_z, i64(0), i64(0), REP4(I_MODA), I_8, I_z, i64(0), i64(0),
+/*20*/ REP4(I_MODA), I_8, I_z, I_PFX, i64(0), REP4(I_MODA), I_8, I_z, I_PFX, i64(0),
+/*30*/ REP4(I_MODA), I_8, I_z, I_PFX, i64(0), REP4(I_MODA), I_8, I_z, I_PFX, i64(0),
+/*40*/ REP16(if64(I_PFX, 0)),
+/*50*/ REP16(0),
+/*60*/ i64(0), i64(0), i64(I_MOD), I_MODA, I_PFX, I_PFX, I_PFX, I_PFX,
+ /*68*/ I_z, I_MODA|I_z, I_8, I_MODA|I_8, REP4(0),
+/*70*/ REP16(I_8|I_JIMM),
+/*80*/ I_MODA|I_8, I_MODA|I_v, i64(I_MODA|I_8), I_MODA, I_MODA, I_MODA, I_MODA, I_MODA,
+ /*88*/ REP4(I_MODA), I_MODA, I_MOD, I_MODA, if64(I_PFX, I_MODA),
+/*90*/ REP8(0), 0, 0, i64(I_p), 0, 0, 0, 0, 0,
+/*A0*/ I_8, I_v, I_8, I_v, REP4(0), I_8, I_z, 0, 0, 0, 0, 0, 0,
+/*B0*/ REP8(I_8), REP8(I_v),
+/*C0*/ I_MODA|I_8, I_MODA|I_8, I_16|I_JMP, I_JMP,
+ /*C4*/ if64(I_PFX, I_MODA), if64(I_PFX, I_MODA), I_MODA|I_8, I_MODA|I_8,
+ /*C8*/ I_24, 0, I_16|I_JMP, I_JMP, 0, I_8, i64(0), I_JMP,
+/*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,
+/*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,
+};
+_Static_assert(sizeof(onebyte_bits) == 256, "onebyte_bits");
+
+/* Note:
+ *All* currently defined 0f 38 opcodes are I_MODA. Assuming that any
+ unknown such opcodes are also I_MODA is probably better than generic
+ unknown.
+ Similarly, all defined 0f 3a opcodes are I_MODA|I_8.
+*/
+
+static const uint8_t _0f_bits[] = {
+/*00*/ I_MODA, I_MODA, 0, 0, I_BAD, o64(0), 0, o64(0),
+ /*08*/ 0, 0, I_BAD, 0, 0, I_MODA, 0, 0,
+/*10*/ REP8(I_MODA), I_MODA, I_BAD, I_BAD, I_BAD, I_BAD, I_BAD, I_BAD, I_MODA,
+/*20*/ REP4(I_MOD), REP4(I_BAD), REP8(I_MODA),
+/*30*/ 0, 0, 0, 0, 0, 0, I_BAD, 0, I_MODA, I_BAD, I_MODA|I_8, I_BAD, REP4(I_BAD),
+/*40*/ REP16(I_MODA),
+/*50*/ I_MOD, I_MODA, I_MODA, I_MODA, REP4(I_MODA), REP8(I_MODA),
+/*60*/ REP16(I_MODA),
+/*70*/ I_MODA, I_MOD|I_8, I_MOD|I_8, I_MOD|I_8, I_MODA, I_MODA, I_MODA, 0,
+ /*78*/ I_MODA, I_MODA, I_BAD, I_BAD, REP4(I_MODA),
+/*80*/ REP16(I_z),
+/*90*/ REP16(I_MODA),
+/*Ax*/ 0, 0, 0, 0, 0, 0, I_BAD, I_BAD,
+ /*A8*/ 0, 0, 0, I_MODA, I_MODA|I_8, I_MODA, I_MODA, I_MODA,
+/*B0*/ REP8(I_MODA), I_MODA, 0, I_MODA|I_8, I_MODA, REP4(I_MODA),
+/*C0*/ I_MODA, I_MODA, I_MODA|I_8, I_MODA, I_MODA|I_8, I_MOD|I_8, I_MODA|I_8, I_MODA|I_z,
+ /*C8*/ REP8(0),
+/*D0*/ REP4(I_MODA), I_MODA, I_MODA, I_MODA, I_MOD, REP8(I_MODA),
+/*E0*/ REP16(I_MODA),
+/*F0*/ REP4(I_MODA), I_MODA, I_MODA, I_MODA, I_MOD,
+ /*F8*/ REP4(I_MODA), I_MODA, I_MODA, I_MODA, I_BAD,
+};
+_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;
+
+ int opnd_size = 4;
+ int mod, rm = 0;
+restart:;
uint8_t byte1 = *ptr++;
uint8_t bits = onebyte_bits[byte1];
- uint8_t byte2 = 0;
- if (byte1 == 0x0f) {
- byte2 = *ptr++;
- bits = _0f_bits[byte2];
- if (byte2 == 0x39) {
- XXX
- } else if (byte2 == 0x3b) {
- XXX
+ /* printf("b1=%x bytes=%x\n", byte1, bits); */
+ if ((bits & I_TYPE_MASK) == I_SPEC) {
+ if (byte1 == 0x0f) {
+ uint8_t byte2 = *ptr++;
+ bits = _0f_bits[byte2];
+ } else if ((byte1 & 0xf8) == 0xd8) {
+ /* ESC */
+ ptr++;
+ bits = I_MODA;
+ } else if (byte1 == 0xff) {
+ uint8_t modrm = *ptr;
+ if (modrm >> 6 == 3) {
+ int subop = modrm >> 3 & 7;
+ if (subop == 4 || subop == 5) /* JMP */
+ bits = I_JMP | I_MODA;
+ else
+ bits = I_MODA;
+ }
+ } else {
+ __builtin_abort();
+ }
+ }
+got_bits: UNUSED
+ if (bits == I_BAD)
+ return P(bad)(ctx);
+ if ((bits & I_TYPE_MASK) == I_PFX) {
+ if (byte1 == 0x66) {
+ opnd_size = 2;
+ goto restart;
+#ifdef TARGET_x86_64
+ } else if ((byte1 & 0xf0) == 0x40) { /* REX */
+ if (byte1 & 8) /* W */
+ opnd_size = 8;
+ if (byte1 & 1) /* B */
+ rm = 8;
+ goto restart;
+ } else if (byte1 == 0xc4) { /* VEX 3 */
+ uint8_t byte2 = *ptr++;
+ if (!(byte2 & 0x20)) /* VEX.~B */
+ rm = 8;
+ UNUSED uint8_t byte3 = *ptr++;
+ ptr++;
+ int map = byte2 & 0x1f;
+ switch (map) {
+ case 1:
+ bits = _0f_bits[byte2];
+ break;
+ case 2:
+ bits = _0f_bits[0x38];
+ break;
+ case 3:
+ bits = _0f_bits[0x3a];
+ break;
+ default:
+ bits = I_BAD;
+ break;
+ }
+ goto got_bits;
+ } else if (byte1 == 0xc5) { /* VEX 2 */
+ uint8_t byte2 = *ptr++;
+ bits = _0f_bits[byte2];
+ goto got_bits;
+ } else if (byte1 == 0x8f) { /* XOP (AMD only) */
+ uint8_t byte2 = *ptr;
+ /* could be modrm */
+ if ((byte2 >> 3 & 7) == 0)
+ goto modrm;
+ ptr++; /* ok, definitely XOP */
+ if (!(byte2 & 0x20)) /* VEX.~B */
+ rm = 8;
+ int map = byte2 & 0x1f;
+ switch (map) {
+ case 8:
+ bits = I_MODA|I_8;
+ break;
+ case 9:
+ bits = I_MODA;
+ break;
+ case 10:
+ bits = I_MODA|I_32;
+ break;
+ default:
+ bits = I_BAD;
+ break;
+ }
+ goto got_bits;
+#endif
+ } else {
+ /* other prefix we don't care about */
+ goto restart;
}
- } else if ((byte1 & 0xd8) == 0xd8) {
- *ptr++;
- bits = I_MODA;
}
- // get modrm
- int mod, rm, mrlow;
+ UNUSED int modrm_off = ptr - orig;
if (bits & I_MOD) {
+ modrm: UNUSED;
uint8_t modrm = *ptr++;
mod = modrm >> 6;
- rm = modrm >> 3 & 7;
- mrlow = modrm & 7;
+ rm |= modrm & 7;
if (rm == 4) {
/* sib */
ptr++;
}
+ /* displacement */
+#ifdef TARGET_x86_64
+ if (mod == 0 && rm == 5)
+ ptr += 4;
+#endif
+ else if (mod == 1)
+ ptr++;
+ else if (mod == 2)
+ ptr += 4;
}
- if (bits & I_PFX) {
- // this could affect opcode size etc... then we restart
- }
-};
+ int imm_off = ptr - orig;
+
+ /* disp */
+ int imm_bits = bits & I_IMM_MASK;
+ int imm_size;
+ if (imm_bits <= I_32)
+ imm_size = imm_bits;
+ else if (imm_bits == I_v)
+ imm_size = opnd_size;
+ else if (imm_bits == I_z)
+ imm_size = opnd_size == 2 ? 2 : 4;
+ else if (imm_bits == I_p)
+ imm_size = opnd_size == 2 ? 4 : 6;
+ else /* because GCC is stupid */
+ __builtin_abort();
+ ptr += imm_size;
-static void P(dis_x86)(tdis_ctx ctx) {
- void *orig = ctx->ptr;
- P(dis_onebyte)(ctx);
- ctx->op_size = ctx->ptr - orig;
+ ctx->ptr = ptr;
+ ctx->op_size = ptr - orig;
+ /* printf("bits=%x\n", bits); */
+
+ if ((bits & I_JIMM) == I_JIMM) {
+ int32_t imm;
+ const void *imm_ptr = orig + imm_off;
+ switch (imm_size) {
+ case 1: imm = *(int8_t *) imm_ptr; break;
+ case 2: imm = *(int16_t *) imm_ptr; break;
+ case 4: imm = *(int32_t *) imm_ptr; break;
+ default: __builtin_abort();
+ }
+
+ bool cond = (byte1 & 0xf0) != 0xe0;
+ bool call = !(bits & I_JMP);
+ P(branch)(ctx, ctx->pc + ctx->op_size + imm,
+ cond * CC_CONDITIONAL | call * CC_CALL);
+ if (TDIS_CTX_MODIFY(ctx)) {
+ /* 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);
+ uint8_t *new_imm_ptr = new_op + imm_off;
+ switch (imm_size) {
+ case 1: *(int8_t *) new_imm_ptr = new_imm; break;
+ case 2: *(int16_t *) new_imm_ptr = new_imm; break;
+ case 4: *(int32_t *) new_imm_ptr = new_imm; break;
+ }
+ }
+#ifdef TARGET_x86_64
+ } else if ((bits & I_MODA) == I_MODA && mod == 0 && rm == 5) {
+ 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);
+ /* 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 */
+ 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);
+ }
+#endif
+ } else if ((bits & I_TYPE_MASK) == I_JMP) {
+ P(ret)(ctx);
+ } else {
+ P(unidentified)(ctx);
+ }
}
diff --git a/lib/x86/jump-patch.h b/lib/x86/jump-patch.h
new file mode 100644
index 0000000..efd4825
--- /dev/null
+++ b/lib/x86/jump-patch.h
@@ -0,0 +1,32 @@
+#pragma once
+#define MAX_JUMP_PATCH_SIZE 5
+
+static inline int jump_patch_size(uintptr_t pc, uintptr_t dpc,
+ UNUSED struct arch_dis_ctx arch,
+ bool force) {
+ uintptr_t diff = pc - (dpc + 5);
+ /* fits in 32? */
+ if (diff == (uintptr_t) (int32_t) diff)
+ return 5;
+ else
+ return force ? (2+4+8) : -1;
+}
+
+static inline void make_jump_patch(void **codep, UNUSED uintptr_t pc,
+ uintptr_t dpc,
+ UNUSED struct arch_dis_ctx arch) {
+ uintptr_t diff = pc - (dpc + 5);
+ uint8_t *code = *codep;
+ if (diff == (uintptr_t) (int32_t) diff) {
+ *(uint8_t *) code = 0xe9;
+ *(uint32_t *) (code + 1) = diff;
+ *codep = code + 5;
+ } else {
+ /* jmpq *(%rip) */
+ *code++ = 0xff;
+ *code++ = 0x25;
+ *(uint32_t *) code = 0; code += 4;
+ *(uint64_t *) code = dpc; code += 8;
+ *codep = code;
+ }
+}
diff --git a/lib/x86/misc.h b/lib/x86/misc.h
new file mode 100644
index 0000000..c8eee19
--- /dev/null
+++ b/lib/x86/misc.h
@@ -0,0 +1,9 @@
+#pragma once
+#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) {}
diff --git a/test/test-td-simple.c b/test/test-td-simple.c
index 9e02ba2..4768177 100644
--- a/test/test-td-simple.c
+++ b/test/test-td-simple.c
@@ -6,9 +6,13 @@
typedef struct tc {
uint32_t pc;
- void *ptr;
+ const void *ptr;
+#if defined(TARGET_x86_64) || defined(TARGET_i386)
+ uint8_t newop[16];
+#else
uint32_t op;
uint32_t newop;
+#endif
uint32_t newval[4];
bool modify;
int op_size;
@@ -20,9 +24,17 @@ typedef struct tc {
#define TDIS_CTX_NEWOP(ctx) ((ctx)->newop)
#define TDIS_CTX_SET_NEWOP(ctx, new) ((ctx)->newop = (new))
+
+#if defined(TARGET_x86_64) || defined(TARGET_i386)
+NOINLINE UNUSED
+static void P_pcrel(UNUSED struct tc *ctx, uint32_t dpc) {
+ printf("adr => %08x\n", dpc);
+}
+#else
NOINLINE UNUSED
-static void P_data(struct tc *ctx, unsigned o0, unsigned o1, unsigned o2, unsigned o3, unsigned out_mask) {
- printf("data: %08x\n", ctx->op);
+static void P_data(UNUSED struct tc *ctx, unsigned o0, unsigned o1, unsigned o2,
+ unsigned o3, unsigned out_mask) {
+ printf("data\n", ctx->op);
unsigned os[] = {o0, o1, o2, o3};
for(size_t i = 0; i < 4; i++) {
unsigned val = os[i];
@@ -33,43 +45,38 @@ static void P_data(struct tc *ctx, unsigned o0, unsigned o1, unsigned o2, unsign
}
ctx->modify = true;
}
-
NOINLINE UNUSED
-static void P_pcrel(struct tc *ctx, uint32_t dpc, unsigned reg, enum pcrel_load_mode lm) {
- printf("adr: %08x => %08x r%u lm:%d\n", ctx->op, dpc, reg, lm);
- ctx->modify = false;
+static void P_pcrel(UNUSED struct tc *ctx, uint32_t dpc,
+ unsigned reg, enum pcrel_load_mode lm) {
+ printf("adr => %08x r%u lm:%d\n", dpc, reg, lm);
}
-
NOINLINE UNUSED
-static void P_ret(struct tc *ctx) {
- printf("ret: %08x\n", ctx->op);
- ctx->modify = false;
+static void P_thumb_it(UNUSED struct tc *ctx) {
+ printf("thumb_it\n");
}
+#endif
NOINLINE UNUSED
-static void P_branch(struct tc *ctx, uint32_t dpc, int cc) {
- printf("branch(%s): %08x => %08x\n",
- (cc & CC_CONDITIONAL) ? "cond" : "uncond",
- ctx->op, dpc);
- ctx->modify = false;
+static void P_ret(UNUSED struct tc *ctx) {
+ printf("ret\n");
}
NOINLINE UNUSED
-static void P_unidentified(struct tc *ctx) {
- printf("unidentified: %08x\n", ctx->op);
- ctx->modify = false;
+static void P_branch(UNUSED struct tc *ctx, uint64_t dpc, int cc) {
+ printf("branch(%s,%s) => %08llx\n",
+ (cc & CC_CONDITIONAL) ? "cond" : "uncond",
+ (cc & CC_CALL) ? "call" : "!call",
+ (unsigned long long) dpc);
}
NOINLINE UNUSED
-static void P_bad(struct tc *ctx) {
- printf("bad: %08x\n", ctx->op);
- ctx->modify = false;
+static void P_unidentified(UNUSED struct tc *ctx) {
+ printf("unidentified\n");
}
NOINLINE UNUSED
-static void P_thumb_it(struct tc *ctx) {
- printf("thumb_it: %08x\n", ctx->op);
- ctx->modify = false;
+static void P_bad(UNUSED struct tc *ctx) {
+ printf("bad\n");
}
#include HDR
@@ -79,10 +86,38 @@ static void P_thumb_it(struct tc *ctx) {
int main(UNUSED int argc, char **argv) {
struct tc ctx;
ctx.pc = 0xdead0000;
- uint32_t op = strtoll(argv[1] ? argv[1] : "deadbeef", NULL, 16);
+ const char *op_str = argv[1];
+#if defined(TARGET_x86_64) || defined(TARGET_i386)
+ uint8_t op[20] = {0};
+ if (!op_str)
+ op_str = "deadbeef";
+ size_t len = strlen(op_str);
+ if (len % 1 || len > 32) {
+ printf("bad op_str len\n");
+ return 1;
+ }
+ for (size_t i = 0; i < len; i += 2) {
+ char str[3] = {op_str[i], op_str[i+1], 0};
+ char *end;
+ uint8_t byte = strtol(str, &end, 16);
+ if (*end) {
+ printf("bad op_str byte\n");
+ return 1;
+ }
+ op[i/2] = byte;
+ }
+ ctx.ptr = op;
+ ctx.modify = false;
+ P_(xdis)(&ctx);
+ printf("(size=%d/%zd)\n", ctx.op_size, len / 2);
+#else
+ uint32_t op = strtoll(op_str ? op_str : "deadbeef", NULL, 16);
ctx.ptr = &op;
ctx.newop = 0;
+ ctx.modify = false;
+ printf("%08x: ", op);
P_(xdis)(&ctx);
printf("==> %x (size=%d)\n", ctx.newop, ctx.op_size);
+#endif
}