From 96e2b019e525b81a33bae5e6aeafc0e667f04f20 Mon Sep 17 00:00:00 2001 From: comex Date: Sat, 17 Jan 2015 14:56:03 -0500 Subject: reorganization --- Makefile | 18 +++--- lib/dis.h | 14 ++++ lib/jump-dis-arm-multi.c | 6 -- lib/jump-dis.c | 161 ++++++++++++++++++++++++++++++++++++++++++++++ lib/jump-dis.h | 5 ++ lib/jump-dis.inc.h | 153 ------------------------------------------- lib/substitute-internal.h | 25 +++++++ test/test-jump-dis.c | 2 +- 8 files changed, 216 insertions(+), 168 deletions(-) delete mode 100644 lib/jump-dis-arm-multi.c create mode 100644 lib/jump-dis.c create mode 100644 lib/jump-dis.h delete mode 100644 lib/jump-dis.inc.h diff --git a/Makefile b/Makefile index 2318f7d..6c13837 100644 --- a/Makefile +++ b/Makefile @@ -30,7 +30,7 @@ out/%.o: lib/%.c Makefile $(HEADERS) $(CC) -fvisibility=hidden -std=c11 -c -o $@ $< out/%.o: lib/%.S Makefile $(HEADERS) $(CC) -fvisibility=hidden -c -o $@ $< -out/jump-dis-arm-multi.o: generated/generic-dis-arm.inc.h generated/generic-dis-thumb.inc.h generated/generic-dis-thumb2.inc.h +out/jump-dis.o: $(GENERATED) LIB_OBJS := \ out/find-syms.o \ @@ -38,7 +38,7 @@ LIB_OBJS := \ out/objc.o \ out/objc-asm.o \ out/substrate-compat.o \ - out/jump-dis-arm-multi.o + out/jump-dis.o out/libsubstitute.dylib: $(LIB_OBJS) $(CC) -o $@ $(LIB_OBJS) $(LIB_LDFLAGS) @@ -47,15 +47,17 @@ out/test-$(1): test/test-$(2).[cm]* $(HEADERS) $(GENERATED) Makefile out/libsubs $(3) -g -o $$@ $$< -Ilib -Isubstrate -Lout -lsubstitute all: out/test-$(1) endef -$(eval $(call define_test,tdarm-simple,td-simple,$(CC) -std=c11 -DHDR='"dis-arm.inc.h"' -Dxdis=dis_arm)) -$(eval $(call define_test,tdthumb-simple,td-simple,$(CC) -std=c11 -DHDR='"dis-thumb.inc.h"' -Dxdis=dis_thumb)) -$(eval $(call define_test,tdthumb2-simple,td-simple,$(CC) -std=c11 -DHDR='"dis-thumb2.inc.h"' -Dxdis=dis_thumb2)) -$(eval $(call define_test,tdarm64-simple,td-simple,$(CC) -std=c11 -DHDR='"dis-arm64.inc.h"' -Dxdis=dis)) -$(eval $(call define_test,dis,dis,$(CC) -std=c11)) +$(eval $(call define_test,tdarm-simple,td-simple,$(CC) -std=c11 -DHDR='"dis-arm.inc.h"' -Dxdis=dis_arm -DFORCE_TARGET_arm)) +$(eval $(call define_test,tdthumb-simple,td-simple,$(CC) -std=c11 -DHDR='"dis-thumb.inc.h"' -Dxdis=dis_thumb -DFORCE_TARGET_arm)) +$(eval $(call define_test,tdthumb2-simple,td-simple,$(CC) -std=c11 -DHDR='"dis-thumb2.inc.h"' -Dxdis=dis_thumb2 -DFORCE_TARGET_arm)) +$(eval $(call define_test,tdarm64-simple,td-simple,$(CC) -std=c11 -DHDR='"dis-arm64.inc.h"' -Dxdis=dis -DFORCE_TARGET_arm64)) +$(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)) +$(eval $(call define_test,jump-dis-arm64,jump-dis,$(CC) -std=c11 -DFORCE_TARGET_arm64)) $(eval $(call define_test,find-syms,find-syms,$(CC) -std=c89)) $(eval $(call define_test,find-syms-cpp,find-syms,$(CXX) -x c++ -std=c++98)) $(eval $(call define_test,substrate,substrate,$(CXX) -std=c++98)) -$(eval $(call define_test,jump-dis,jump-dis,$(CC) -std=c11)) $(eval $(call define_test,imp-forwarding,imp-forwarding,$(CC) -std=c11 -framework Foundation -lobjc)) $(eval $(call define_test,objc-hook,objc-hook,$(CC) -std=c11 -framework Foundation -lsubstitute)) $(eval $(call define_test,interpose,interpose,$(CC) -std=c11 -lsubstitute)) diff --git a/lib/dis.h b/lib/dis.h index 5a26d84..5a49723 100644 --- a/lib/dis.h +++ b/lib/dis.h @@ -104,3 +104,17 @@ static const unsigned null_op = -0x100; return; \ } while (0) +#if defined(TARGET_x86_64) + #define MIN_INSN_SIZE 1 + #error "no x86 dis yet" +#elif defined(TARGET_i386) + #define MIN_INSN_SIZE 1 + #error "no x86 dis yet" +#elif defined(TARGET_arm) + #define MIN_INSN_SIZE 2 + #define TARGET_DIS_HEADER "dis-arm-multi.inc.h" +#elif defined(TARGET_arm64) + #define MIN_INSN_SIZE 4 + #define TARGET_DIS_HEADER "dis-arm64.inc.h" +#endif + diff --git a/lib/jump-dis-arm-multi.c b/lib/jump-dis-arm-multi.c deleted file mode 100644 index b5967f5..0000000 --- a/lib/jump-dis-arm-multi.c +++ /dev/null @@ -1,6 +0,0 @@ -enum { - MIN_INSN_SIZE = 2 -}; -#define P(x) jump_dis_##x -#include "jump-dis.inc.h" -#include "dis-arm-multi.inc.h" diff --git a/lib/jump-dis.c b/lib/jump-dis.c new file mode 100644 index 0000000..c7c7346 --- /dev/null +++ b/lib/jump-dis.c @@ -0,0 +1,161 @@ +#include "substitute-internal.h" +#ifndef TARGET_UNSUPPORTED +#include "dis.h" +#include +#include +#include + +/* This pass tries to look through the function to find jumps back to the + * patched code at the beginning to the function. It does not deal with jump + * tables, and has a limited range, so it is only heuristic. If such jumps are + * found, the hook is aborted. In the future it might be possible to fix up + * the jumps rather than merely detect them, but that would require doing + * something weird like extending the patch region to add trampolines... */ + +enum { + JUMP_ANALYSIS_MAX_INSNS = 512, + JUMP_ANALYSIS_MAX_SIZE = JUMP_ANALYSIS_MAX_INSNS * MIN_INSN_SIZE, +}; + +struct jump_dis_ctx { + /* outputs */ + bool bad_insn; + bool continue_after_this_insn; + + uintptr_t pc; + uintptr_t pc_patch_start; + uintptr_t pc_patch_end; + bool pc_low_bit; + unsigned op; + void *ptr; + int op_size; + uint8_t seen_mask[JUMP_ANALYSIS_MAX_INSNS / 8]; + /* queue of instructions to visit */ + uintptr_t *queue; + size_t queue_write_off; + size_t queue_read_off; + size_t queue_size; + size_t queue_count; +}; + +#undef P +#define P(x) jump_dis_##x + +#define tdis_ctx struct jump_dis_ctx * +#define TDIS_CTX_MODIFY(ctx) 0 +#define TDIS_CTX_NEWVAL(ctx, n) 0 +#define TDIS_CTX_SET_NEWOP(ctx, new) ((void) 0) + +static void jump_dis_add_to_queue(struct jump_dis_ctx *ctx, uintptr_t pc) { + size_t diff = (pc - ctx->pc_patch_start) / MIN_INSN_SIZE; + if (diff >= JUMP_ANALYSIS_MAX_INSNS) { +#ifdef JUMP_DIS_VERBOSE + printf("jump-dis: not adding %llx - out of range\n", (unsigned long long) pc); +#endif + return; + } + if (ctx->seen_mask[diff / 8] & 1 << (diff % 8)) { +#ifdef JUMP_DIS_VERBOSE + printf("jump-dis: not adding %llx - already seen\n", (unsigned long long) pc); +#endif + return; + } + ctx->seen_mask[diff / 8] |= 1 << (diff % 8); + + if (ctx->queue_write_off == ctx->queue_read_off && (ctx->queue_count || !ctx->queue_size)) { + size_t new_size = ctx->queue_size * 2 + 5; + ctx->queue = realloc(ctx->queue, new_size * sizeof(*ctx->queue)); + if (!ctx->queue) + substitute_panic("%s: out of memory\n", __func__); + size_t new_read_off = new_size - (ctx->queue_size - ctx->queue_read_off); + memmove(ctx->queue + new_read_off, ctx->queue + ctx->queue_read_off, (ctx->queue_size - ctx->queue_read_off) * sizeof(*ctx->queue)); + ctx->queue_read_off = new_read_off % new_size; + ctx->queue_size = new_size; + } + ctx->queue[ctx->queue_write_off] = pc; + ctx->queue_write_off = (ctx->queue_write_off + 1) % ctx->queue_size; + ctx->queue_count++; +} + + +static INLINE inline void jump_dis_data(UNUSED struct jump_dis_ctx *ctx, UNUSED unsigned o0, UNUSED unsigned o1, UNUSED unsigned o2, UNUSED unsigned o3, UNUSED unsigned out_mask) { + /* on ARM, ignore mov PC jumps, as they're unlikely to be in the same function */ +} + +static INLINE inline void jump_dis_pcrel(struct jump_dis_ctx *ctx, uintptr_t dpc, UNUSED unsigned reg, UNUSED bool is_load) { + ctx->bad_insn = dpc >= ctx->pc_patch_start && dpc < ctx->pc_patch_end; +} + +NOINLINE UNUSED +static void jump_dis_ret(struct jump_dis_ctx *ctx) { + ctx->continue_after_this_insn = false; +} + +NOINLINE UNUSED +static void jump_dis_branch(struct jump_dis_ctx *ctx, uintptr_t dpc, bool conditional) { + if (dpc >= ctx->pc_patch_start && dpc < ctx->pc_patch_end) { + ctx->bad_insn = true; + return; + } +#ifdef JUMP_DIS_VERBOSE + printf("jump-dis: enqueueing %llx\n", (unsigned long long) dpc); +#endif + jump_dis_add_to_queue(ctx, dpc); + ctx->continue_after_this_insn = conditional; +} + +NOINLINE UNUSED +static void jump_dis_unidentified(UNUSED struct jump_dis_ctx *ctx) { +} + +NOINLINE UNUSED +static void jump_dis_bad(struct jump_dis_ctx *ctx) { + ctx->continue_after_this_insn = false; +} + +static void jump_dis_dis(tdis_ctx ctx); + +bool jump_dis_main(void *code_ptr, uintptr_t pc_patch_start, uintptr_t pc_patch_end, bool pc_low_bit) { + bool ret; + struct jump_dis_ctx ctx; + memset(&ctx, 0, sizeof(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_end; + while (1) { + ctx.bad_insn = false; + ctx.continue_after_this_insn = true; + ctx.ptr = code_ptr + (ctx.pc - pc_patch_start); + jump_dis_dis(&ctx); +#ifdef JUMP_DIS_VERBOSE + printf("jump-dis: pc=%llx op=%08x size=%x bad=%d continue_after=%d\n", + (unsigned long long) ctx.pc, + ctx.op, + ctx.op_size, + ctx.bad_insn, + ctx.continue_after_this_insn); +#endif + if (ctx.bad_insn) { + ret = true; + goto fail; + } + if (ctx.continue_after_this_insn) + jump_dis_add_to_queue(&ctx, ctx.pc + ctx.op_size); + + /* get next address */ + if (ctx.queue_read_off == ctx.queue_write_off) + break; + ctx.pc = ctx.queue[ctx.queue_read_off]; + ctx.queue_read_off = (ctx.queue_read_off + 1) % ctx.queue_size; + ctx.queue_count--; + } + /* no bad instructions! */ + ret = false; +fail: + free(ctx.queue); + return ret; +} + +#include TARGET_DIS_HEADER +#endif /* TARGET_UNSUPPORTED */ diff --git a/lib/jump-dis.h b/lib/jump-dis.h new file mode 100644 index 0000000..c567096 --- /dev/null +++ b/lib/jump-dis.h @@ -0,0 +1,5 @@ +#pragma once +#include +#include + +bool jump_dis_main(void *code_ptr, uintptr_t pc_patch_start, uintptr_t pc_patch_end, bool pc_low_bit); diff --git a/lib/jump-dis.inc.h b/lib/jump-dis.inc.h deleted file mode 100644 index a070a74..0000000 --- a/lib/jump-dis.inc.h +++ /dev/null @@ -1,153 +0,0 @@ -#include "dis.h" -#include -#include -#include - -/* This pass tries to look through the function to find jumps back to the - * patched code at the beginning to the function. It does not deal with jump - * tables, and has a limited range, so it is only heuristic. If such jumps are - * found, the hook is aborted. In the future it might be possible to fix up - * the jumps rather than merely detect them, but that would require doing - * something weird like extending the patch region to add trampolines... */ - -enum { - JUMP_ANALYSIS_MAX_INSNS = 512, - JUMP_ANALYSIS_MAX_SIZE = JUMP_ANALYSIS_MAX_INSNS * MIN_INSN_SIZE, -}; - -struct jump_dis_ctx { - /* outputs */ - bool bad_insn; - bool continue_after_this_insn; - - uintptr_t pc; - uintptr_t pc_patch_start; - uintptr_t pc_patch_end; - bool pc_low_bit; - unsigned op; - void *ptr; - int op_size; - uint8_t seen_mask[JUMP_ANALYSIS_MAX_INSNS / 8]; - /* queue of instructions to visit */ - uintptr_t *queue; - size_t queue_write_off; - size_t queue_read_off; - size_t queue_size; - size_t queue_count; -}; - -#define tdis_ctx struct jump_dis_ctx * -#define TDIS_CTX_MODIFY(ctx) 0 -#define TDIS_CTX_NEWVAL(ctx, n) 0 -#define TDIS_CTX_SET_NEWOP(ctx, new) ((void) 0) - -static void P(add_to_queue)(struct jump_dis_ctx *ctx, uintptr_t pc) { - size_t diff = (pc - ctx->pc_patch_start) / MIN_INSN_SIZE; - if (diff >= JUMP_ANALYSIS_MAX_INSNS) { -#ifdef JUMP_DIS_VERBOSE - printf("jump-dis: not adding %llx - out of range\n", (unsigned long long) pc); -#endif - return; - } - if (ctx->seen_mask[diff / 8] & 1 << (diff % 8)) { -#ifdef JUMP_DIS_VERBOSE - printf("jump-dis: not adding %llx - already seen\n", (unsigned long long) pc); -#endif - return; - } - ctx->seen_mask[diff / 8] |= 1 << (diff % 8); - - if (ctx->queue_write_off == ctx->queue_read_off && (ctx->queue_count || !ctx->queue_size)) { - size_t new_size = ctx->queue_size * 2 + 5; - ctx->queue = realloc(ctx->queue, new_size * sizeof(*ctx->queue)); - if (!ctx->queue) - substitute_panic("%s: out of memory\n", __func__); - size_t new_read_off = new_size - (ctx->queue_size - ctx->queue_read_off); - memmove(ctx->queue + new_read_off, ctx->queue + ctx->queue_read_off, (ctx->queue_size - ctx->queue_read_off) * sizeof(*ctx->queue)); - ctx->queue_read_off = new_read_off % new_size; - ctx->queue_size = new_size; - } - ctx->queue[ctx->queue_write_off] = pc; - ctx->queue_write_off = (ctx->queue_write_off + 1) % ctx->queue_size; - ctx->queue_count++; -} - - -static INLINE inline void P(data)(UNUSED struct jump_dis_ctx *ctx, UNUSED unsigned o0, UNUSED unsigned o1, UNUSED unsigned o2, UNUSED unsigned o3, UNUSED unsigned out_mask) { - /* on ARM, ignore mov PC jumps, as they're unlikely to be in the same function */ -} - -static INLINE inline void P(pcrel)(struct jump_dis_ctx *ctx, uintptr_t dpc, UNUSED unsigned reg, UNUSED bool is_load) { - ctx->bad_insn = dpc >= ctx->pc_patch_start && dpc < ctx->pc_patch_end; -} - -NOINLINE UNUSED -static void P(ret)(struct jump_dis_ctx *ctx) { - ctx->continue_after_this_insn = false; -} - -NOINLINE UNUSED -static void P(branch)(struct jump_dis_ctx *ctx, uintptr_t dpc, bool conditional) { - if (dpc >= ctx->pc_patch_start && dpc < ctx->pc_patch_end) { - ctx->bad_insn = true; - return; - } -#ifdef JUMP_DIS_VERBOSE - printf("jump-dis: enqueueing %llx\n", (unsigned long long) dpc); -#endif - P(add_to_queue)(ctx, dpc); - ctx->continue_after_this_insn = conditional; -} - -NOINLINE UNUSED -static void P(unidentified)(UNUSED struct jump_dis_ctx *ctx) { -} - -NOINLINE UNUSED -static void P(bad)(struct jump_dis_ctx *ctx) { - ctx->continue_after_this_insn = false; -} - -static void P(dis)(tdis_ctx ctx); - -bool P(main)(void *code_ptr, uintptr_t pc_patch_start, uintptr_t pc_patch_end, bool pc_low_bit) { - bool ret; - struct jump_dis_ctx ctx; - memset(&ctx, 0, sizeof(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_end; - while (1) { - ctx.bad_insn = false; - ctx.continue_after_this_insn = true; - ctx.ptr = code_ptr + (ctx.pc - pc_patch_start); - P(dis)(&ctx); -#ifdef JUMP_DIS_VERBOSE - printf("jump-dis: pc=%llx op=%08x size=%x bad=%d continue_after=%d\n", - (unsigned long long) ctx.pc, - ctx.op, - ctx.op_size, - ctx.bad_insn, - ctx.continue_after_this_insn); -#endif - if (ctx.bad_insn) { - ret = true; - goto fail; - } - if (ctx.continue_after_this_insn) - P(add_to_queue)(&ctx, ctx.pc + ctx.op_size); - - /* get next address */ - if (ctx.queue_read_off == ctx.queue_write_off) - break; - ctx.pc = ctx.queue[ctx.queue_read_off]; - ctx.queue_read_off = (ctx.queue_read_off + 1) % ctx.queue_size; - ctx.queue_count--; - } - /* no bad instructions! */ - ret = false; -fail: - free(ctx.queue); - return ret; -} diff --git a/lib/substitute-internal.h b/lib/substitute-internal.h index 2550d2a..cf50a99 100644 --- a/lib/substitute-internal.h +++ b/lib/substitute-internal.h @@ -26,3 +26,28 @@ typedef struct section section_x; #define LC_SEGMENT_X LC_SEGMENT #endif #endif + +/* FORCE_* are for tests */ +#if defined(FORCE_TARGET_x86_64) + #define TARGET_x86_64 +#elif defined(FORCE_TARGET_i386) + #define TARGET_i386 + #define TARGET_UNSUPPORTED +#elif defined(FORCE_TARGET_arm) + #define TARGET_arm +#elif defined(FORCE_TARGET_arm64) + #define TARGET_arm64 +#elif defined(__x86_64__) + #define TARGET_x86_64 +#elif defined(__i386__) + #define TARGET_i386 +#elif defined(__arm__) + #define TARGET_arm +#elif defined(__arm64__) + #define TARGET_arm64 +#else + #error target? +#endif +#if defined(TARGET_x86_64) || defined(TARGET_i386) + #define TARGET_UNSUPPORTED +#endif diff --git a/test/test-jump-dis.c b/test/test-jump-dis.c index 161c80c..1afa4e5 100644 --- a/test/test-jump-dis.c +++ b/test/test-jump-dis.c @@ -1,6 +1,6 @@ #define JUMP_DIS_VERBOSE #include -#include "jump-dis-arm-multi.c" +#include "jump-dis.c" #include int main(UNUSED int argc, char **argv) { static char buf[1048576]; -- cgit v1.2.3