aboutsummaryrefslogtreecommitdiff
path: root/lib/hook-functions.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/hook-functions.c')
-rw-r--r--lib/hook-functions.c123
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;
}