diff options
Diffstat (limited to 'lib/hook-functions.c')
-rw-r--r-- | lib/hook-functions.c | 123 |
1 files changed, 95 insertions, 28 deletions
diff --git a/lib/hook-functions.c b/lib/hook-functions.c index fed9e3e..674d424 100644 --- a/lib/hook-functions.c +++ b/lib/hook-functions.c @@ -5,7 +5,11 @@ #include "transform-dis.h" #include "execmem.h" #include stringify(TARGET_DIR/jump-patch.h) +#include <stdlib.h> +#include <alloca.h> +#ifndef NO_PTHREADS #include <pthread.h> +#endif struct hook_internal { int offset_by_pcdiff[MAX_EXTENDED_PATCH_SIZE + 1]; @@ -62,14 +66,17 @@ static uintptr_t pc_callback(void *ctx, uintptr_t pc) { */ static int check_intro_trampoline(void **trampoline_ptr_p, + uintptr_t *trampoline_addr_p, size_t *trampoline_size_left_p, uintptr_t pc, uintptr_t dpc, int *patch_size_p, bool *need_intro_trampoline_p, void **trampoline_page_p, - struct arch_dis_ctx arch) { + struct arch_dis_ctx arch, + void *opt) { void *trampoline_ptr = *trampoline_ptr_p; + uintptr_t trampoline_addr = *trampoline_addr_p; size_t trampoline_size_left = *trampoline_size_left_p; /* Try direct */ @@ -82,7 +89,7 @@ static int check_intro_trampoline(void **trampoline_ptr_p, if (trampoline_ptr) { /* Try existing trampoline */ - *patch_size_p = jump_patch_size(pc, (uintptr_t) trampoline_ptr, arch, + *patch_size_p = jump_patch_size(pc, trampoline_addr, arch, false); if (*patch_size_p != -1 && (size_t) *patch_size_p @@ -92,38 +99,36 @@ static int check_intro_trampoline(void **trampoline_ptr_p, /* Allocate new trampoline - try after pc. If this fails, we can try * before pc before giving up. */ - int ret = execmem_alloc_unsealed(pc, &trampoline_ptr, &trampoline_size_left); + int ret = execmem_alloc_unsealed(pc, &trampoline_ptr, &trampoline_addr, + &trampoline_size_left, opt); if (!ret) { - *patch_size_p = jump_patch_size(pc, (uintptr_t) trampoline_ptr, arch, - false); + *patch_size_p = jump_patch_size(pc, trampoline_addr, arch, false); if (*patch_size_p != -1) { ret = SUBSTITUTE_OK; goto end; } - execmem_free(trampoline_ptr); + execmem_free(trampoline_ptr, opt); } /* Allocate new trampoline - try before pc (xxx only meaningful on arm64) */ uintptr_t start_address = pc - 0x80000000; - ret = execmem_alloc_unsealed(start_address, - &trampoline_ptr, &trampoline_size_left); + ret = execmem_alloc_unsealed(start_address, &trampoline_ptr, &trampoline_addr, + &trampoline_size_left, opt); if (!ret) { - *patch_size_p = jump_patch_size(pc, (uintptr_t) trampoline_ptr, arch, - false); + *patch_size_p = jump_patch_size(pc, trampoline_addr, arch, false); if (*patch_size_p != -1) { - *trampoline_ptr_p = trampoline_ptr; - *trampoline_size_left_p = trampoline_size_left; - *trampoline_page_p = trampoline_ptr; - return SUBSTITUTE_OK; + ret = SUBSTITUTE_OK; + goto end; } - execmem_free(trampoline_ptr); + execmem_free(trampoline_ptr, opt); ret = SUBSTITUTE_ERR_OUT_OF_RANGE; } end: *trampoline_ptr_p = trampoline_ptr; + *trampoline_addr_p = trampoline_addr; *trampoline_size_left_p = trampoline_size_left; *trampoline_page_p = trampoline_ptr; return ret; @@ -135,15 +140,19 @@ int substitute_hook_functions(const struct substitute_function_hook *hooks, size_t nhooks, struct substitute_function_hook_record **recordp, int options) { +#ifndef NO_PTHREADS bool thread_safe = !(options & SUBSTITUTE_NO_THREAD_SAFETY); if (thread_safe && !pthread_main_np()) return SUBSTITUTE_ERR_NOT_ON_MAIN_THREAD; +#else + bool thread_safe = false; +#endif if (recordp) *recordp = NULL; struct execmem_foreign_write *fws; - struct hook_internal *his = malloc(nhooks * sizeof(*his) + + struct hook_internal *his = alloca(nhooks * sizeof(*his) + nhooks * sizeof(*fws)); if (!his) return SUBSTITUTE_ERR_OOM; @@ -154,7 +163,9 @@ int substitute_hook_functions(const struct substitute_function_hook *hooks, int ret = SUBSTITUTE_OK; + void *trampoline_prev = NULL; void *trampoline_ptr = NULL; + uintptr_t trampoline_addr = 0; size_t trampoline_size_left = 0; /* First run through and (a) ensure all the functions are OK to hook, (b) @@ -176,19 +187,23 @@ int substitute_hook_functions(const struct substitute_function_hook *hooks, uintptr_t pc_patch_start = (uintptr_t) code; int patch_size; bool need_intro_trampoline; - if ((ret = check_intro_trampoline(&trampoline_ptr, &trampoline_size_left, - pc_patch_start, + if ((ret = check_intro_trampoline(&trampoline_ptr, &trampoline_addr, + &trampoline_size_left, pc_patch_start, (uintptr_t) hook->replacement, &patch_size, &need_intro_trampoline, - &hi->trampoline_page, arch))) + &hi->trampoline_page, arch, + hook->opt))) goto end; uint_tptr pc_patch_end = pc_patch_start + patch_size; uintptr_t initial_target; if (need_intro_trampoline) { - initial_target = (uintptr_t) trampoline_ptr; + initial_target = trampoline_addr; + trampoline_prev = trampoline_ptr; make_jump_patch(&trampoline_ptr, (uintptr_t) trampoline_ptr, (uintptr_t) hook->replacement, arch); + trampoline_size_left -= patch_size; + trampoline_addr += (trampoline_ptr - trampoline_prev); } else { initial_target = (uintptr_t) hook->replacement; } @@ -202,9 +217,16 @@ int substitute_hook_functions(const struct substitute_function_hook *hooks, if (outro_est > trampoline_size_left) { /* Not enough space left in our existing block... */ - if ((ret = execmem_alloc_unsealed(0, &trampoline_ptr, - &trampoline_size_left))) + if ((ret = execmem_alloc_unsealed(0, &trampoline_ptr, + &trampoline_addr, + &trampoline_size_left, + hook->opt))) goto end; + /* NOTE: We assume that each page is large enough (min + * TD_MAX_REWRITTEN_SIZE + 2 * MAX_JUMP_PATCH_SIZE) so we don't lose + * a reference by having one hook allocate two pages. Also must + * ensure this size is aligned to ARCH_MAX_CODE_ALIGNMENT otherwise + * MAX_JUMP_PATCH_SIZE might be wrong. */ hi->trampoline_page = trampoline_ptr; } @@ -215,17 +237,20 @@ int substitute_hook_functions(const struct substitute_function_hook *hooks, hi->outro_trampoline++; #endif if (hook->old_ptr) - *(void **) hook->old_ptr = hi->outro_trampoline; + *(uintptr_t *) hook->old_ptr = trampoline_addr + + (uintptr_t)(hi->outro_trampoline - outro_trampoline_real); /* Generate the rewritten start of the function for the outro * trampoline (complaining if any bad instructions are found) * (on arm64, this modifies arch.regs_possibly_written, which is used * by the later make_jump_patch call) */ + trampoline_prev = trampoline_ptr; if ((ret = transform_dis_main(code, &trampoline_ptr, pc_patch_start, - &pc_patch_end, (uintptr_t) trampoline_ptr, + &pc_patch_end, trampoline_addr, &arch, hi->offset_by_pcdiff, thread_safe ? TRANSFORM_DIS_BAN_CALLS : 0))) goto end; + trampoline_addr += (trampoline_ptr - trampoline_prev); uintptr_t dpc = pc_patch_end; #ifdef __arm__ @@ -239,21 +264,42 @@ int substitute_hook_functions(const struct substitute_function_hook *hooks, if ((ret = jump_dis_main(code, pc_patch_start, pc_patch_end, arch))) goto end; /* Okay, continue with the outro. */ - make_jump_patch(&trampoline_ptr, (uintptr_t) trampoline_ptr, dpc, arch); + trampoline_prev = trampoline_ptr; + make_jump_patch(&trampoline_ptr, trampoline_addr, dpc, arch); + trampoline_addr += (trampoline_ptr - trampoline_prev); + trampoline_ptr += -(uintptr_t) trampoline_ptr % ARCH_MAX_CODE_ALIGNMENT; + trampoline_addr += -trampoline_addr % ARCH_MAX_CODE_ALIGNMENT; trampoline_size_left -= (uint8_t *) trampoline_ptr - (uint8_t *) outro_trampoline_real; } + /* room to save records */ + struct substitute_function_hook_record *records = NULL; + + if (recordp) { + records = malloc(nhooks * (sizeof(struct substitute_function_hook_record) + + MAX_JUMP_PATCH_SIZE)); + *recordp = records; + } + /* Now commit. */ for (size_t i = 0; i < nhooks; i++) { struct hook_internal *hi = &his[i]; void *page = hi->trampoline_page; if (page) - execmem_seal(page); + execmem_seal(page, hooks[i].opt); fws[i].dst = hi->code; fws[i].src = hi->jump_patch; fws[i].len = hi->jump_patch_size; + fws[i].opt = hooks[i].opt; + if (records) { + records->function = hi->code; + records->opt = hooks[i].opt; + records->buffer_size = hi->jump_patch_size; + memcpy(records->saved_buffer, hi->code, hi->jump_patch_size); + records = (struct substitute_function_hook_record *)((char *)&records->saved_buffer + records->buffer_size); + } } struct pc_callback_info info = {his, nhooks, false}; @@ -273,10 +319,31 @@ end: for (size_t i = 0; i < nhooks; i++) { void *page = his[i].trampoline_page; if (page) - execmem_free(page); + execmem_free(page, hooks[i].opt); } + /* free records */ + if (recordp && *recordp) + free(*recordp); end_dont_free: - free(his); + return ret; +} + +EXPORT +int substitute_free_hooks(struct substitute_function_hook_record *records, + size_t nhooks) { + int ret; + struct substitute_function_hook_record *cur = records; + struct execmem_foreign_write *fws = alloca(nhooks * sizeof(*fws)); + for (int i = 0; i < nhooks; i++) { + fws[i].dst = cur->function; + fws[i].src = cur->saved_buffer; + fws[i].len = cur->buffer_size; + fws[i].opt = cur->opt; + cur = (struct substitute_function_hook_record *)((char *)&cur->saved_buffer + cur->buffer_size); + } + /* TODO: Fix the case when thread is inside a patch/trampoline. */ + ret = execmem_foreign_write_with_pc_patch(fws, nhooks, NULL, NULL); + free(records); return ret; } |