aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/dis-arm64.inc.h12
-rw-r--r--lib/dis.h2
-rw-r--r--lib/transform-dis-arm-multi.inc.h18
-rw-r--r--lib/transform-dis-arm64.inc.h52
-rw-r--r--lib/transform-dis.c127
-rw-r--r--lib/transform-dis.inc.h120
6 files changed, 189 insertions, 142 deletions
diff --git a/lib/dis-arm64.inc.h b/lib/dis-arm64.inc.h
index 388ac19..5317b89 100644
--- a/lib/dis-arm64.inc.h
+++ b/lib/dis-arm64.inc.h
@@ -17,16 +17,16 @@ static INLINE void P(am_ldrlit_label_unk_Rt_6_LDRDl)(tdis_ctx ctx, struct bitsli
enum pcrel_load_mode mode;
if ((ctx->op >> 26) & 1) {
switch (ctx->op >> 30) {
- case 0b00: mode = PLM_U32_SIMD; break;
- case 0b01: mode = PLM_U64_SIMD; break;
- case 0b10: mode = PLM_U128_SIMD; break;
+ case 0: mode = PLM_U32_SIMD; break;
+ case 1: mode = PLM_U64_SIMD; break;
+ case 2: mode = PLM_U128_SIMD; break;
default: __builtin_abort();
}
} else {
switch (ctx->op >> 30) {
- case 0b00: mode = PLM_U32; break;
- case 0b01: mode = PLM_U64; break;
- case 0b10: mode = PLM_S32; break;
+ case 0: mode = PLM_U32; break;
+ case 1: mode = PLM_U64; break;
+ case 2: mode = PLM_S32; break;
default: __builtin_abort();
}
}
diff --git a/lib/dis.h b/lib/dis.h
index 37c0889..e0cbc4f 100644
--- a/lib/dis.h
+++ b/lib/dis.h
@@ -68,7 +68,7 @@ enum pcrel_load_mode {
PLM_U16, PLM_S16,
PLM_U32, PLM_S32,
PLM_U64,
- PLM_U128,
+ PLM_U128, /* i.e. LDRD */
PLM_U32_SIMD,
PLM_U64_SIMD,
PLM_U128_SIMD,
diff --git a/lib/transform-dis-arm-multi.inc.h b/lib/transform-dis-arm-multi.inc.h
index 3c99911..5382304 100644
--- a/lib/transform-dis-arm-multi.inc.h
+++ b/lib/transform-dis-arm-multi.inc.h
@@ -3,7 +3,6 @@ static inline void PUSHone(struct transform_dis_ctx *ctx, int Rt) {
op32(ctx, 0x0d04f84d | Rt << 28);
else
op32(ctx, 0xe52d0004 | Rt << 12);
-
}
static inline void POPone(struct transform_dis_ctx *ctx, int Rt) {
@@ -56,19 +55,20 @@ static inline void LDRxi(struct transform_dis_ctx *ctx, int Rt, int Rn, uint32_t
}
op32(ctx, 0x0000f8d0 | Rn | Rt << 28 | subop << 5 | sign << 8 | off << 16);
} else {
- int is_byte, subop;
+ int is_byte, subop, not_ldrd;
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;
+ case PLM_U8: is_byte = 1; goto type1;
+ case PLM_S8: subop = 13; not_ldrd = 1; goto type2;
+ case PLM_U16: subop = 11; not_ldrd = 1; goto type2;
+ case PLM_S16: subop = 15; not_ldrd = 1; goto type2;
+ case PLM_U32: is_byte = 0; goto type1;
+ case PLM_U128: subop = 13; not_ldrd = 0; goto type2;
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);
+ op32(ctx, 0xe1c00000 | Rn << 16 | Rt << 12 | subop << 4 |
+ (off & 0xf) | (off & 0xf0) << 4 | not_ldrd << 20);
break;
default:
__builtin_abort();
diff --git a/lib/transform-dis-arm64.inc.h b/lib/transform-dis-arm64.inc.h
new file mode 100644
index 0000000..c47971b
--- /dev/null
+++ b/lib/transform-dis-arm64.inc.h
@@ -0,0 +1,52 @@
+static inline void MOVi64(struct transform_dis_ctx *ctx, int Rd, uint64_t val) {
+ int shift_nybbles = 0;
+ do {
+ int k = shift_nybbles != 0 ? 1 : 0;
+ op32(ctx, 0x69400000 | k << 28 | Rd | (val & 0xffff) << 4 | shift_nybbles << 20);
+ shift_nybbles++;
+ val >>= 16;
+ } while(val);
+}
+
+static inline void LDRxi(struct transform_dis_ctx *ctx, int Rt, int Rn, uint32_t off,
+ bool regsize_64, enum pcrel_load_mode load_mode) {
+ int size, opc;
+ bool sign, simd;
+ switch (load_mode) {
+ case PLM_U8: size = 0; sign = false; simd = false; break;
+ case PLM_S8: size = 0; sign = true; simd = false; break;
+ case PLM_U16: size = 1; sign = false; simd = false; break;
+ case PLM_S16: size = 1; sign = true; simd = false; break;
+ case PLM_U32: size = 2; sign = false; simd = false; break;
+ case PLM_S32: size = 2; sign = true; simd = false; break;
+ case PLM_U64: size = 3; sign = false; simd = false; break;
+ case PLM_U32_SIMD: size = 2; opc = 1; simd = true; break;
+ case PLM_U64_SIMD: size = 3; opc = 1; simd = true; break;
+ case PLM_U128_SIMD: size = 0; opc = 3; simd = true; break;
+ default: __builtin_abort();
+ }
+ if (simd) {
+ off /= 1 << (size | (opc & 1) << 2);
+ } else {
+ off /= 1 << size;
+ opc = sign ? (regsize_64 ? 2 : 3) : 1;
+ }
+ op32(ctx, 0x39000000 | Rt | Rn << 5 | off << 10 | opc << 22 | simd << 26 | size << 30);
+}
+
+
+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;
+ if (load_mode >= PLM_U32_SIMD) {
+ /* use x0 as scratch */
+ op32(ctx, 0xf81f0fe0); /* str x0, [sp, #-0x10]! */
+ MOVi64(ctx, 0, dpc);
+ LDRxi(ctx, reg, 0, 0, true, load_mode);
+ op32(ctx, 0xf84107e0); /* ldr x0, [sp], #0x10 */
+ } else {
+ MOVi64(ctx, reg, dpc);
+ LDRxi(ctx, reg, reg, 0, true, load_mode);
+ }
+}
+
diff --git a/lib/transform-dis.c b/lib/transform-dis.c
index 6fc6d7f..b8df987 100644
--- a/lib/transform-dis.c
+++ b/lib/transform-dis.c
@@ -1,9 +1,124 @@
#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
+
+#include "substitute.h"
+#include "dis.h"
+#include <stdbool.h>
+#include <stdint.h>
+
+#define P(x) transform_dis_##x
+
+struct transform_dis_ctx {
+ /* outputs */
+ bool modify;
+ int err;
+
+ bool pc_low_bit;
+ uintptr_t pc_patch_start;
+ uintptr_t pc_patch_end;
+ uintptr_t pc;
+ int op_size;
+ unsigned op;
+ unsigned newop;
+ unsigned newval[4];
+
+ const void *ptr;
+ void **rewritten_ptr_ptr;
+ void *write_newop_here;
+
+ struct arch_dis_ctx arch;
+};
+
+#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))
+
+/* largely similar to jump_dis */
+
+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)
+ ctx->err = SUBSTITUTE_ERR_FUNC_TOO_SHORT;
+}
+
+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 */
+ ctx->err = SUBSTITUTE_ERR_FUNC_BAD_INSN_AT_START;
+ }
+ /* branch out of bounds is fine */
+}
+
+static INLINE UNUSED void transform_dis_unidentified(UNUSED struct transform_dis_ctx *ctx) {
+ /* this isn't exhaustive, so unidentified is fine */
+}
+
+static INLINE UNUSED void transform_dis_bad(struct transform_dis_ctx *ctx) {
+ ctx->err = SUBSTITUTE_ERR_FUNC_BAD_INSN_AT_START;
+}
+
+
+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;
+}
+
+#ifdef TARGET_arm
+ #include "transform-dis-arm-multi.inc.h"
+#else
+ #include "transform-dis-arm64.inc.h"
#endif
+#include TARGET_DIS_HEADER
+
+#endif /* TARGET_UNSUPPORTED */
diff --git a/lib/transform-dis.inc.h b/lib/transform-dis.inc.h
deleted file mode 100644
index 04cd7f2..0000000
--- a/lib/transform-dis.inc.h
+++ /dev/null
@@ -1,120 +0,0 @@
-#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;
- int err;
-
- bool pc_low_bit;
- uintptr_t pc_patch_start;
- uintptr_t pc_patch_end;
- uintptr_t pc;
- int op_size;
- unsigned op;
- unsigned newop;
- unsigned newval[4];
-
- const void *ptr;
- void **rewritten_ptr_ptr;
- void *write_newop_here;
-
- struct arch_dis_ctx arch;
-};
-
-#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))
-
-/* largely similar to jump_dis */
-
-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)
- ctx->err = SUBSTITUTE_ERR_FUNC_TOO_SHORT;
-}
-
-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 */
- ctx->err = SUBSTITUTE_ERR_FUNC_BAD_INSN_AT_START;
- }
- /* branch out of bounds is fine */
-}
-
-static INLINE UNUSED void transform_dis_unidentified(UNUSED struct transform_dis_ctx *ctx) {
- /* this isn't exhaustive, so unidentified is fine */
-}
-
-static INLINE UNUSED void transform_dis_bad(struct transform_dis_ctx *ctx) {
- ctx->err = SUBSTITUTE_ERR_FUNC_BAD_INSN_AT_START;
-}
-
-
-/* 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