aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/arm/misc.h2
-rw-r--r--lib/arm64/assemble.h9
-rw-r--r--lib/arm64/jump-patch.h8
-rw-r--r--lib/execmem.h4
-rw-r--r--lib/hook-functions.c29
-rw-r--r--lib/substitute.h8
6 files changed, 39 insertions, 21 deletions
diff --git a/lib/arm/misc.h b/lib/arm/misc.h
index 7ce1c05..f8d593e 100644
--- a/lib/arm/misc.h
+++ b/lib/arm/misc.h
@@ -10,5 +10,3 @@ struct arch_dis_ctx {
};
enum { IS_LDRD_STRD = 1 << 16 };
-#define JUMP_PATCH_SIZE 8
-#define MAX_REWRITTEN_SIZE (12 * 4) /* actually should be less */
diff --git a/lib/arm64/assemble.h b/lib/arm64/assemble.h
index c20c219..8a98b7b 100644
--- a/lib/arm64/assemble.h
+++ b/lib/arm64/assemble.h
@@ -4,8 +4,8 @@ static inline void MOVi64(void **codep, int Rd, uint64_t val) {
int shift_nybbles = 0;
do {
int k = shift_nybbles != 0 ? 1 : 0;
- op32(codep, 0x69400000 | k << 28 | Rd | (val & 0xffff) << 4 |
- shift_nybbles << 20);
+ op32(codep, 0xd2800000 | k << 29 | Rd | (val & 0xffff) << 5 |
+ shift_nybbles << 21);
shift_nybbles++;
val >>= 16;
} while(val);
@@ -16,6 +16,7 @@ static inline void LDRxi(void **codep, int Rt, int Rn, uint32_t off,
int size, opc;
bool sign, simd;
switch (load_mode) {
+ case PLM_ADR: return;
case PLM_U8: size = 0; sign = false; simd = false; break;
case PLM_S8: size = 0; sign = true; simd = false; break;
case PLM_U16: size = 1; sign = false; simd = false; break;
@@ -41,8 +42,8 @@ static inline void LDRxi(void **codep, int Rt, int Rn, uint32_t off,
static inline void ADRP_ADD(void **codep, int reg, uint64_t pc, uint64_t dpc) {
uintptr_t diff = (dpc & ~0xfff) - (pc & ~0xfff);
/* ADRP reg, dpc */
- op32(codep, 0x90000000 | reg | (diff & 0x3000) << 17 | (diff & 0xffffc000) >> 8);
- uint32_t lo = pc & 0xfff;
+ op32(codep, 0x90000000 | reg | (diff & 0x3000) << 17 | (diff & 0x1ffffc000) >> 9);
+ uint32_t lo = dpc & 0xfff;
if (lo) {
/* ADD reg, reg, #lo */
op32(codep, 0x91000000 | reg | reg << 5 | lo << 10);
diff --git a/lib/arm64/jump-patch.h b/lib/arm64/jump-patch.h
index c42c730..aa818d3 100644
--- a/lib/arm64/jump-patch.h
+++ b/lib/arm64/jump-patch.h
@@ -3,20 +3,20 @@
#define MAX_JUMP_PATCH_SIZE 12
#define MAX_REWRITTEN_SIZE (7 * 2 * 4) /* also conservative */
static inline int jump_patch_size(uintptr_t pc, uintptr_t dpc,
- struct arch_dis_ctx arch,
+ UNUSED struct arch_dis_ctx arch,
bool force) {
intptr_t diff = (dpc & ~0xfff) - (pc & ~0xfff);
if (!(diff >= -0x100000000 && diff < 0x100000000))
return force ? 16 : -1;
- else if (pc & 0xfff)
+ else if (!(dpc & 0xfff))
return 8;
else
return 12;
}
static inline void make_jump_patch(void **codep, uintptr_t pc, uintptr_t dpc,
- struct arch_dis_ctx arch) {
- int reg = 12; /* XXX */
+ UNUSED struct arch_dis_ctx arch) {
+ int reg = 15;
intptr_t diff = (dpc & ~0xfff) - (pc & ~0xfff);
if (!(diff >= -0x100000000 && diff < 0x100000000))
MOVi64(codep, reg, dpc);
diff --git a/lib/execmem.h b/lib/execmem.h
index a1cd47e..b4860e9 100644
--- a/lib/execmem.h
+++ b/lib/execmem.h
@@ -1,9 +1,9 @@
#pragma once
#include <stdlib.h>
-/* write to a foreign page which is already RX / with unknown permissions */
+/* Write to a foreign page which is already RX / with unknown permissions. */
int execmem_write(void *dest, const void *src, size_t len);
-/* for allocating trampolines */
+/* 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);
diff --git a/lib/hook-functions.c b/lib/hook-functions.c
index 38d2a80..cc8bd3e 100644
--- a/lib/hook-functions.c
+++ b/lib/hook-functions.c
@@ -8,7 +8,7 @@
#include TARGET_JUMP_PATCH_HDR
struct hook_internal {
- int offset_by_pcdiff[JUMP_PATCH_SIZE + 1];
+ int offset_by_pcdiff[MAX_JUMP_PATCH_SIZE + 1];
uint8_t jump_patch[MAX_JUMP_PATCH_SIZE];
size_t jump_patch_size;
void *code;
@@ -21,6 +21,7 @@ struct hook_internal {
struct pc_callback_info {
struct hook_internal *his;
size_t nhooks;
+ bool encountered_bad_pc;
};
static uintptr_t pc_callback(void *ctx, uintptr_t pc) {
@@ -32,8 +33,14 @@ static uintptr_t pc_callback(void *ctx, uintptr_t pc) {
for (size_t i = 0; i < info->nhooks; i++) {
struct hook_internal *hi = &info->his[i];
uintptr_t diff = real_pc - (uintptr_t) hi->code;
- if (diff < hi->jump_patch_size)
- return (uintptr_t) hi->outro_trampoline + hi->offset_by_pcdiff[diff];
+ if (diff < hi->jump_patch_size) {
+ int offset = hi->offset_by_pcdiff[diff];
+ if (offset == -1) {
+ info->encountered_bad_pc = true;
+ return pc;
+ }
+ return (uintptr_t) hi->outro_trampoline + offset;
+ }
}
return pc;
}
@@ -76,9 +83,9 @@ static int check_intro_trampoline(void **trampoline_ptr_p,
if (*patch_size_p != -1 && (size_t) *patch_size_p <= *trampoline_size_left_p)
return SUBSTITUTE_OK;
- /* Allocate new trampoline - try after dpc. If this fails, we can try
- * before dpc before giving up. */
- int ret = execmem_alloc_unsealed(dpc, &trampoline_ptr, &trampoline_size_left);
+ /* 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);
if (ret)
goto skip_after;
@@ -93,8 +100,8 @@ static int check_intro_trampoline(void **trampoline_ptr_p,
execmem_free(trampoline_ptr);
skip_after:;
- /* Allocate new trampoline - try before dpc (xxx only meaningful on arm64) */
- uintptr_t start_address = dpc - 0xffff0000;
+ /* Allocate new trampoline - try before pc (xxx only meaningful on arm64) */
+ uintptr_t start_address = pc - 0xffff0000;
ret = execmem_alloc_unsealed(start_address, &trampoline_ptr, &trampoline_size_left);
if (ret)
return ret;
@@ -234,9 +241,13 @@ int substitute_hook_functions(const struct substitute_function_hook *hooks,
/* *sigh of relief* now we can rewrite the PCs. */
if (stopped) {
- struct pc_callback_info info = {his, nhooks};
+ struct pc_callback_info info = {his, nhooks, false};
if ((ret = apply_pc_patch_callback(stop_token, pc_callback, &info)))
goto end;
+ if (info.encountered_bad_pc) {
+ ret = SUBSTITURE_ERR_UNEXPECTED_PC_ON_OTHER_THREAD;
+ goto end;
+ }
}
end:
diff --git a/lib/substitute.h b/lib/substitute.h
index 250106b..2aa7ed8 100644
--- a/lib/substitute.h
+++ b/lib/substitute.h
@@ -51,6 +51,14 @@ enum {
* SUBSTITUTE_DONT_STOP_THREADS */
SUBSTITUTE_ERR_NOT_ON_MAIN_THREAD,
+ /* substitute_hook_functions: when trying to patch the PC of other threads
+ * (in case they were inside the patched prolog when they were suspended),
+ * found a PC that was in the patch region but seemingly not at an
+ * instruction boundary
+ * The hooks were otherwise completed, but the thread in question will
+ * probably crash now that its code has changed under it. */
+ SUBSTITURE_ERR_UNEXPECTED_PC_ON_OTHER_THREAD,
+
/* substitute_hook_functions: destination was out of range, and mmap
* wouldn't give us a trampoline in range */
SUBSTITUTE_ERR_OUT_OF_RANGE,