diff options
-rw-r--r-- | lib/arm/arch-transform-dis.inc.h | 3 | ||||
-rw-r--r-- | lib/arm/assemble.h | 16 | ||||
-rw-r--r-- | lib/arm/jump-patch.h | 4 | ||||
-rw-r--r-- | lib/darwin/execmem.c | 8 | ||||
-rw-r--r-- | lib/execmem.h | 7 | ||||
-rw-r--r-- | lib/hook-functions.c | 123 | ||||
-rw-r--r-- | lib/strerror.c | 2 | ||||
-rw-r--r-- | lib/substitute-internal.h | 2 | ||||
-rw-r--r-- | lib/substitute.h | 35 | ||||
-rw-r--r-- | lib/vita/execmem.c | 141 |
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; +} |