diff options
-rw-r--r-- | Makefile | 14 | ||||
-rw-r--r-- | lib/arm/misc.h | 6 | ||||
-rw-r--r-- | lib/arm/transform-dis-arm-multi.inc.h | 1 | ||||
-rw-r--r-- | lib/arm64/misc.h | 4 | ||||
-rw-r--r-- | lib/arm64/transform-dis-arm64.inc.h | 1 | ||||
-rw-r--r-- | lib/dis.h | 1 | ||||
-rw-r--r-- | lib/substitute-internal.h | 2 | ||||
-rw-r--r-- | lib/x86/dis-tables.h | 99 | ||||
-rw-r--r-- | lib/x86/dis-x86.inc.h | 315 | ||||
-rw-r--r-- | lib/x86/jump-patch.h | 32 | ||||
-rw-r--r-- | lib/x86/misc.h | 9 | ||||
-rw-r--r-- | test/test-td-simple.c | 87 |
12 files changed, 410 insertions, 161 deletions
@@ -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 @@ -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 } |