aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/arm/arch-transform-dis.inc.h3
-rw-r--r--lib/arm/assemble.h16
-rw-r--r--lib/arm/jump-patch.h4
-rw-r--r--lib/darwin/execmem.c8
-rw-r--r--lib/execmem.h7
-rw-r--r--lib/hook-functions.c123
-rw-r--r--lib/strerror.c2
-rw-r--r--lib/substitute-internal.h2
-rw-r--r--lib/substitute.h35
-rw-r--r--lib/vita/execmem.c141
10 files changed, 299 insertions, 42 deletions
diff --git a/lib/arm/arch-transform-dis.inc.h b/lib/arm/arch-transform-dis.inc.h
index d74a06f..1e9579d 100644
--- a/lib/arm/arch-transform-dis.inc.h
+++ b/lib/arm/arch-transform-dis.inc.h
@@ -36,7 +36,7 @@ void transform_dis_data(struct transform_dis_ctx *ctx, unsigned o0, unsigned o1,
/* We only care if at least one op is PC, so quickly approximate that. */
if (((o0 | o1 | o2 | o3) & 15) != 15)
return;
- unsigned *newval = ctx->base.newval;
+ uint32_t *newval = ctx->base.newval;
newval[0] = o0;
newval[1] = o1;
newval[2] = o2;
@@ -178,6 +178,7 @@ void transform_dis_branch(struct transform_dis_ctx *ctx, uint_tptr dpc, int cc)
void **codep = ctx->rewritten_ptr_ptr;
ctx->write_newop_here = *codep; *codep += 2;
}
+ /* If it's a call, we should jump back after the call */
actx.cond = 0xe;
MOVW_MOVT(actx, 14, dpc | ctx->arch.pc_low_bit);
BLXr(actx, 14);
diff --git a/lib/arm/assemble.h b/lib/arm/assemble.h
index 1d4250b..3066802 100644
--- a/lib/arm/assemble.h
+++ b/lib/arm/assemble.h
@@ -20,6 +20,13 @@ static inline void PUSHone(struct assemble_ctx ctx, int Rt) {
op32(ctx.codep, 0x052d0004 | Rt << 12 | ctx.cond << 28);
}
+static inline void PUSHmulti(struct assemble_ctx ctx, uint16_t mask) {
+ if (ctx.thumb)
+ op32(ctx.codep, 0x0000e92d | mask << 16);
+ else
+ op32(ctx.codep, 0x092d0000 | mask | ctx.cond << 28);
+}
+
static inline void POPone(struct assemble_ctx ctx, int Rt) {
if (ctx.thumb)
op32(ctx.codep, 0x0b04f85d | Rt << 28);
@@ -120,4 +127,13 @@ static inline void LDR_PC(struct assemble_ctx ctx, uint32_t dpc) {
else
op32(ctx.codep, 0x051ff004 | ctx.cond << 28);
op32(ctx.codep, (uint32_t) dpc);
+ if (ctx.pc & 2) // for alignment
+ op16(ctx.codep, 0xbf00);
+}
+
+static inline void ADD_PC(struct assemble_ctx ctx, uint32_t Rd, uint32_t imm12) {
+ if (ctx.thumb)
+ op32(ctx.codep, 0x0000f20f | ((imm12 >> 11) << 10) | (((imm12 & 0x700) >> 8) << 28) | Rd << 24 | (imm12 & 0xff) << 16);
+ else
+ op32(ctx.codep, 0x028f0000 | Rd << 12 | imm12 << 0 | ctx.cond << 28);
}
diff --git a/lib/arm/jump-patch.h b/lib/arm/jump-patch.h
index 8787070..8ba4c3a 100644
--- a/lib/arm/jump-patch.h
+++ b/lib/arm/jump-patch.h
@@ -1,14 +1,14 @@
#pragma once
#include "dis.h"
#include "arm/assemble.h"
-#define MAX_JUMP_PATCH_SIZE 8
+#define MAX_JUMP_PATCH_SIZE 12
#define MAX_EXTENDED_PATCH_SIZE (MAX_JUMP_PATCH_SIZE+14)
static inline int jump_patch_size(uint_tptr pc,
UNUSED uint_tptr dpc,
UNUSED struct arch_dis_ctx arch,
UNUSED bool force) {
- return (pc & 2) ? 10 : 8;
+ return (pc & 2) ? 12 : 8;
}
static inline void make_jump_patch(void **codep, uint_tptr pc,
diff --git a/lib/darwin/execmem.c b/lib/darwin/execmem.c
index c7068f3..44b26ae 100644
--- a/lib/darwin/execmem.c
+++ b/lib/darwin/execmem.c
@@ -63,22 +63,24 @@ static execmem_pc_patch_callback g_pc_patch_callback;
static void *g_pc_patch_callback_ctx;
static mach_port_t g_suspending_thread;
-int execmem_alloc_unsealed(uintptr_t hint, void **page_p, size_t *size_p) {
+int execmem_alloc_unsealed(uintptr_t hint, void **page_p, uintptr_t *vma_p,
+ size_t *size_p, UNUSED void *opt) {
*size_p = PAGE_SIZE;
*page_p = mmap((void *) hint, *size_p, PROT_READ | PROT_WRITE,
MAP_ANON | MAP_SHARED, -1, 0);
+ *vma_p = (uintptr_t)*page_p;
if (*page_p == MAP_FAILED)
return SUBSTITUTE_ERR_VM;
return SUBSTITUTE_OK;
}
-int execmem_seal(void *page) {
+int execmem_seal(void *page, UNUSED void *opt) {
if (mprotect(page, PAGE_SIZE, PROT_READ | PROT_EXEC))
return SUBSTITUTE_ERR_VM;
return SUBSTITUTE_OK;
}
-void execmem_free(void *page) {
+void execmem_free(void *page, UNUSED void *opt) {
munmap(page, PAGE_SIZE);
}
diff --git a/lib/execmem.h b/lib/execmem.h
index 87ca461..07e9fa4 100644
--- a/lib/execmem.h
+++ b/lib/execmem.h
@@ -1,9 +1,9 @@
#pragma once
#include <sys/types.h>
/* For allocating trampolines - this is just a mmap wrapper. */
-int execmem_alloc_unsealed(uintptr_t hint, void **page_p, size_t *size_p);
-int execmem_seal(void *page);
-void execmem_free(void *page);
+int execmem_alloc_unsealed(uintptr_t hint, void **page_p, uintptr_t *vma_p, size_t *size_p, void *opt);
+int execmem_seal(void *page, void *opt);
+void execmem_free(void *page, void *opt);
/* Write to "foreign" (i.e. owned by another library, not out-of-process) pages
* which are already RX or have unknown permissions.
@@ -15,6 +15,7 @@ struct execmem_foreign_write {
void *dst;
const void *src;
size_t len;
+ void *opt;
};
typedef uintptr_t (*execmem_pc_patch_callback)(void *ctx, uintptr_t pc);
int execmem_foreign_write_with_pc_patch(struct execmem_foreign_write *writes,
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;
}
diff --git a/lib/strerror.c b/lib/strerror.c
index 3720f57..db11944 100644
--- a/lib/strerror.c
+++ b/lib/strerror.c
@@ -24,8 +24,10 @@ const char *substitute_strerror(int err) {
_SUBSTITUTE_CURRENT_MAX_ERR_PLUS_ONE + 1,
"not all errors named in strerror.c");
/* substitute-internal.h */
+#ifdef __APPLE__
CASE(SUBSTITUTE_ERR_TASK_FOR_PID);
CASE(SUBSTITUTE_ERR_MISC);
+#endif /* __APPLE__ */
default:
return "(unknown libsubstitute error)";
}
diff --git a/lib/substitute-internal.h b/lib/substitute-internal.h
index 2d26e66..8859436 100644
--- a/lib/substitute-internal.h
+++ b/lib/substitute-internal.h
@@ -104,7 +104,7 @@ int substitute_dlopen_in_pid(int pid, const char *filename, int options,
int substitute_ios_unrestrict(task_t task, char **error);
#endif
-static const char *xbasename(const char *path) {
+static UNUSED const char *xbasename(const char *path) {
const char *slash = strrchr(path, '/');
return slash ? slash + 1 : path;
}
diff --git a/lib/substitute.h b/lib/substitute.h
index 72c3919..8cc2a4d 100644
--- a/lib/substitute.h
+++ b/lib/substitute.h
@@ -98,12 +98,29 @@ struct substitute_function_hook {
/* Currently unused; pass 0. (Protip: When using C {} struct initializer
* syntax, you can just omit this.) */
int options;
+ /* Any platform specific auxiliary data. The data pointed to must remain
+ * valid until after the hook is freed! */
+ void *opt;
+};
+
+struct substitute_function_hook_record {
+ /* Function that was originally hooked. */
+ void *function;
+ /* Any platform specific auxiliary data copied from the hook */
+ void *opt;
+ /** Should at least be MAX_JUMP_PATCH_SIZE for your platform */
+ size_t buffer_size;
+ /** Store the original code. Must be large enough to hold MAX_JUMP_PATCH_SIZE
+ * for whatever platform your are targeting! */
+ char saved_buffer[];
};
/* substitute_hook_functions options */
+#ifndef NO_PTHREADS
enum {
SUBSTITUTE_NO_THREAD_SAFETY = 1,
};
+#endif
/* Patch the machine code of the specified functions to redirect them to the
* specified replacements.
@@ -143,17 +160,27 @@ enum {
* @hooks see struct substitute_function_hook
* @nhooks number of hooks
* @recordp if non-NULL, on success receives a pointer that can be used to
- * cleanly undo the hooks; currently unimplemented, so pass NULL
+ * cleanly undo the hooks
* @options options - see above
* @return SUBSTITUTE_OK, or any of most of the SUBSTITUTE_ERR_*
*/
-struct substitute_function_hook_record;
int substitute_hook_functions(const struct substitute_function_hook *hooks,
size_t nhooks,
struct substitute_function_hook_record **recordp,
int options);
-#if 1 /* declare dynamic linker-related stuff? */
+/**
+ * @brief Frees hooks and restores original code.
+ *
+ * @param records from `substitute_hook_functions`
+ * @param[in] nhooks Number of hooks to free
+ *
+ * @return SUBSTITUTE_OK, or any of most of the SUBSTITUTE_ERR_*
+ */
+int substitute_free_hooks(struct substitute_function_hook_record *records,
+ size_t nhooks);
+
+#ifndef NO_DYNAMIC_LINKER_STUFF /* declare dynamic linker-related stuff? */
#ifdef __APPLE__
#include <mach-o/nlist.h>
@@ -271,7 +298,7 @@ int substitute_interpose_imports(const struct substitute_image *handle,
int options);
-#endif /* 1 */
+#endif /* NO_DYNAMIC_LINKER_STUFF */
#if defined(__APPLE__)
#include <objc/runtime.h>
diff --git a/lib/vita/execmem.c b/lib/vita/execmem.c
new file mode 100644
index 0000000..ba772ae
--- /dev/null
+++ b/lib/vita/execmem.c
@@ -0,0 +1,141 @@
+#include "substitute.h"
+#include "dis.h"
+#include "execmem.h"
+#include stringify(TARGET_DIR/jump-patch.h)
+#include <psp2kern/kernel/cpu.h>
+#include <psp2kern/kernel/sysmem.h>
+#include <psp2kern/kernel/threadmgr.h>
+#include "../../../patches.h"
+#include "../../../slab.h"
+#include "../../../taihen_internal.h"
+
+/** The size of each trampoline allocation. We use it for outro and optional
+ * intro. Realistically, we do not use an intro.
+ */
+#define PATCH_ITEM_SIZE (TD_MAX_REWRITTEN_SIZE + 2 * MAX_JUMP_PATCH_SIZE)
+#if (PATCH_ITEM_SIZE % ARCH_MAX_CODE_ALIGNMENT != 0)
+// if not aligned then substitute_hook_functions breaks!
+#error PATCH_ITEM_SIZE Must be aligned to ARCH_MAX_CODE_ALIGNMENT
+#endif
+
+/** We use the slab allocator for both these things. Choose the larger of the two as size. */
+const int g_exe_slab_item_size = PATCH_ITEM_SIZE > sizeof(tai_hook_t) ? PATCH_ITEM_SIZE : sizeof(tai_hook_t);
+
+/**
+ * The reason we use the same slab allocator for patches (sized 216 max) and
+ * tai_hook_t (size 16) is because in both cases, we need to allocate memory in
+ * the user's memory space. One option is to use two different slabs and that
+ * would make more sense. However my prediction is that there is not a large
+ * number of hooks per process, so the minimum size for the slab (0x1000 bytes)
+ * is already too much. Better to waste 200 bytes per allocation than 2000 bytes
+ * per process. If usage dictates a need for change, it is easy enough to put
+ * them in different slabs.
+ */
+
+/**
+ * @file execmem.c
+ *
+ * @brief Functions for allocating executable memory and writing to RO
+ * memory.
+ *
+ * We only consider two arenas for allocating trampoline executable
+ * memory. One is shared in user space across all processes. The
+ * other is in kernel space for kernel hooks.
+ */
+
+/**
+ * @brief Allocate a slab of executable memory.
+ *
+ * Two pointers will be returned: the executable ro pointer and a
+ * writable pointer.
+ *
+ * @param[in] hint Unused
+ * @param ptr_p The writable pointer
+ * @param vma_p The executable pointer address
+ * @param size_p The size of the allocation. Always `SLAB_ITEM_SIZE`.
+ * @param opt A `tai_substitute_args_t` structure
+ * @param[in] hint Unused
+ *
+ * @return `SUBSTITUTE_OK` or `SUBSTITUTE_ERR_VM` if out of memory
+ */
+int execmem_alloc_unsealed(UNUSED uintptr_t hint, void **ptr_p, uintptr_t *vma_p,
+ size_t *size_p, void *opt) {
+ struct slab_chain *slab = (struct slab_chain *)opt;
+
+ LOG("Allocating exec ptr for pid %d", slab->pid);
+ *ptr_p = slab_alloc(slab, vma_p);
+ *size_p = PATCH_ITEM_SIZE;
+ LOG("Got %p, sized %x", *ptr_p, *size_p);
+ if (*ptr_p == NULL) {
+ return SUBSTITUTE_ERR_VM;
+ } else {
+ return SUBSTITUTE_OK;
+ }
+}
+
+/**
+ * @brief Flushes icache
+ *
+ * @param ptr Unused
+ * @param opt A `tai_substitute_args_t` structure
+ *
+ * @return `SUBSTITUTE_OK`
+ */
+int execmem_seal(void *ptr, void *opt) {
+ uintptr_t vma;
+ struct slab_chain *slab = (struct slab_chain *)opt;
+
+ LOG("Sealing exec ptr %p", ptr);
+ vma = slab_getmirror(slab, ptr);
+ LOG("mirror addr %p", vma);
+
+ cache_flush(KERNEL_PID, (uintptr_t)ptr, PATCH_ITEM_SIZE);
+ cache_flush(slab->pid, vma, PATCH_ITEM_SIZE);
+
+ return SUBSTITUTE_OK;
+}
+
+/**
+ * @brief Frees executable memory from slab allocator
+ *
+ * @param ptr The writable pointer
+ * @param opt A `tai_substitute_args_t` structure
+ */
+void execmem_free(void *ptr, void *opt) {
+ struct slab_chain *slab = (struct slab_chain *)opt;
+ LOG("Freeing exec ptr %p", ptr);
+ slab_free(slab, ptr);
+}
+
+/**
+ * @brief Write to executable process memory
+ *
+ * @param writes List of writes
+ * @param[in] nwrites Number of writes
+ * @param[in] callback Unused
+ * @param callback_ctx Unused
+ *
+ * @return `SUBSTITUTE_OK` or `SUBSTITUTE_ERR_VM` on failure
+ */
+int execmem_foreign_write_with_pc_patch(struct execmem_foreign_write *writes,
+ size_t nwrites,
+ UNUSED execmem_pc_patch_callback callback,
+ UNUSED void *callback_ctx) {
+ LOG("Patching exec memory: %d", nwrites);
+ for (int i = 0; i < nwrites; i++) {
+ struct slab_chain *slab = (struct slab_chain *)writes[i].opt;
+ SceUID pid = slab->pid;
+ if (pid == SHARED_PID) {
+ pid = sceKernelGetProcessId();
+ LOG("sceKernelGetProcessId: %x", pid);
+ }
+ LOG("PID:%x, dst:%p, src:%p, len:%x", pid, writes[i].dst, writes[i].src, writes[i].len);
+ if (pid == KERNEL_PID) {
+ sceKernelCpuUnrestrictedMemcpy(writes[i].dst, writes[i].src, writes[i].len);
+ } else {
+ sceKernelRxMemcpyKernelToUserForPid(pid, (uintptr_t)writes[i].dst, writes[i].src, writes[i].len);
+ }
+ cache_flush(pid, (uintptr_t)writes[i].dst, writes[i].len);
+ }
+ return SUBSTITUTE_OK;
+}