M1 W^X fully supported

This commit is contained in:
lazymio 2024-02-12 00:10:44 +08:00
parent e915e18987
commit 822bb527f3
No known key found for this signature in database
GPG Key ID: DFF27E34A47CB873
8 changed files with 223 additions and 47 deletions

View File

@ -416,6 +416,9 @@ struct uc_struct {
#endif #endif
GArray *unmapped_regions; GArray *unmapped_regions;
int32_t snapshot_level; int32_t snapshot_level;
uint64_t nested; // the nested level of all exposed API
bool thread_executable_entry;
bool current_executable;
}; };
// Metadata stub for the variable-size cpu context used with uc_context_*() // Metadata stub for the variable-size cpu context used with uc_context_*()

View File

@ -38,7 +38,7 @@ void cpu_loop_exit(CPUState *cpu)
{ {
/* Unlock JIT write protect if applicable. */ /* Unlock JIT write protect if applicable. */
if (cpu->uc->nested_level == 1) { if (cpu->uc->nested_level == 1) {
tb_exec_unlock(cpu->uc->tcg_ctx); tb_exec_unlock(cpu->uc);
} }
/* Undo the setting in cpu_tb_exec. */ /* Undo the setting in cpu_tb_exec. */
cpu->can_do_io = 1; cpu->can_do_io = 1;

View File

@ -56,11 +56,11 @@ static inline tcg_target_ulong cpu_tb_exec(CPUState *cpu, TranslationBlock *itb)
uint8_t *tb_ptr = itb->tc.ptr; uint8_t *tb_ptr = itb->tc.ptr;
UC_TRACE_START(UC_TRACE_TB_EXEC); UC_TRACE_START(UC_TRACE_TB_EXEC);
tb_exec_lock(cpu->uc->tcg_ctx); tb_exec_lock(cpu->uc);
ret = tcg_qemu_tb_exec(env, tb_ptr); ret = tcg_qemu_tb_exec(env, tb_ptr);
if (cpu->uc->nested_level == 1) { if (cpu->uc->nested_level == 1) {
// Only unlock (allow writing to JIT area) if we are the outmost uc_emu_start // Only unlock (allow writing to JIT area) if we are the outmost uc_emu_start
tb_exec_unlock(cpu->uc->tcg_ctx); tb_exec_unlock(cpu->uc);
} }
UC_TRACE_END(UC_TRACE_TB_EXEC, "[uc] exec tb 0x%" PRIx64 ": ", itb->pc); UC_TRACE_END(UC_TRACE_TB_EXEC, "[uc] exec tb 0x%" PRIx64 ": ", itb->pc);

View File

@ -36,8 +36,8 @@
#include "sysemu/tcg.h" #include "sysemu/tcg.h"
#include "uc_priv.h" #include "uc_priv.h"
static bool tb_exec_is_locked(TCGContext*); static bool tb_exec_is_locked(struct uc_struct*);
static void tb_exec_change(TCGContext*, bool locked); static void tb_exec_change(struct uc_struct*, bool locked);
/* #define DEBUG_TB_INVALIDATE */ /* #define DEBUG_TB_INVALIDATE */
/* #define DEBUG_TB_FLUSH */ /* #define DEBUG_TB_FLUSH */
@ -1115,7 +1115,9 @@ static void tb_htable_init(struct uc_struct *uc)
static void uc_tb_flush(struct uc_struct *uc) { static void uc_tb_flush(struct uc_struct *uc) {
tb_exec_unlock(uc);
tb_flush(uc->cpu); tb_flush(uc->cpu);
tb_exec_lock(uc);
} }
static void uc_invalidate_tb(struct uc_struct *uc, uint64_t start_addr, size_t len) static void uc_invalidate_tb(struct uc_struct *uc, uint64_t start_addr, size_t len)
@ -1223,8 +1225,9 @@ void tcg_exec_init(struct uc_struct *uc, uint32_t tb_size)
page_init(uc); page_init(uc);
tb_htable_init(uc); tb_htable_init(uc);
code_gen_alloc(uc, tb_size); code_gen_alloc(uc, tb_size);
tb_exec_unlock(uc->tcg_ctx); tb_exec_unlock(uc);
tcg_prologue_init(uc->tcg_ctx); tcg_prologue_init(uc->tcg_ctx);
tb_exec_lock(uc);
/* cpu_interrupt_handler is not used in uc1 */ /* cpu_interrupt_handler is not used in uc1 */
uc->l1_map = g_malloc0(sizeof(void *) * V_L1_MAX_SIZE); uc->l1_map = g_malloc0(sizeof(void *) * V_L1_MAX_SIZE);
/* Invalidate / Cache TBs */ /* Invalidate / Cache TBs */
@ -1497,8 +1500,8 @@ static void do_tb_phys_invalidate(TCGContext *tcg_ctx, TranslationBlock *tb, boo
bool code_gen_locked; bool code_gen_locked;
assert_memory_lock(); assert_memory_lock();
code_gen_locked = tb_exec_is_locked(tcg_ctx); code_gen_locked = tb_exec_is_locked(uc);
tb_exec_unlock(tcg_ctx); tb_exec_unlock(uc);
/* make sure no further incoming jumps will be chained to this TB */ /* make sure no further incoming jumps will be chained to this TB */
tb->cflags = tb->cflags | CF_INVALID; tb->cflags = tb->cflags | CF_INVALID;
@ -1509,7 +1512,7 @@ static void do_tb_phys_invalidate(TCGContext *tcg_ctx, TranslationBlock *tb, boo
tb->trace_vcpu_dstate); tb->trace_vcpu_dstate);
if (!(tb->cflags & CF_NOCACHE) && if (!(tb->cflags & CF_NOCACHE) &&
!qht_remove(&tcg_ctx->tb_ctx.htable, tb, h)) { !qht_remove(&tcg_ctx->tb_ctx.htable, tb, h)) {
tb_exec_change(tcg_ctx, code_gen_locked); tb_exec_change(uc, code_gen_locked);
return; return;
} }
@ -1540,7 +1543,7 @@ static void do_tb_phys_invalidate(TCGContext *tcg_ctx, TranslationBlock *tb, boo
tcg_ctx->tb_phys_invalidate_count = tcg_ctx->tb_phys_invalidate_count + 1; tcg_ctx->tb_phys_invalidate_count = tcg_ctx->tb_phys_invalidate_count + 1;
tb_exec_change(tcg_ctx, code_gen_locked); tb_exec_change(uc, code_gen_locked);
} }
static void tb_phys_invalidate__locked(TCGContext *tcg_ctx, TranslationBlock *tb) static void tb_phys_invalidate__locked(TCGContext *tcg_ctx, TranslationBlock *tb)
@ -1710,7 +1713,7 @@ TranslationBlock *tb_gen_code(CPUState *cpu,
assert_memory_lock(); assert_memory_lock();
#ifdef HAVE_PTHREAD_JIT_PROTECT #ifdef HAVE_PTHREAD_JIT_PROTECT
tb_exec_unlock(tcg_ctx); tb_exec_unlock(cpu->uc);
#endif #endif
phys_pc = get_page_addr_code(env, pc); phys_pc = get_page_addr_code(env, pc);
@ -2169,32 +2172,35 @@ void tcg_flush_softmmu_tlb(struct uc_struct *uc)
#ifdef HAVE_PTHREAD_JIT_PROTECT #ifdef HAVE_PTHREAD_JIT_PROTECT
static bool tb_exec_is_locked(TCGContext *tcg_ctx) static bool tb_exec_is_locked(struct uc_struct *uc)
{ {
return tcg_ctx->code_gen_locked; return uc->current_executable;
} }
static void tb_exec_change(TCGContext *tcg_ctx, bool locked) static void tb_exec_change(struct uc_struct *uc, bool executable)
{ {
jit_write_protect(locked); assert(uc->current_executable == thread_executable());
tcg_ctx->code_gen_locked = locked; if (uc->current_executable != executable) {
jit_write_protect(executable);
uc->current_executable = executable;
}
} }
#else /* not needed on non-Darwin platforms */ #else /* not needed on non-Darwin platforms */
static bool tb_exec_is_locked(TCGContext *tcg_ctx) static bool tb_exec_is_locked(struct uc_struct *uc)
{ {
return false; return false;
} }
static void tb_exec_change(TCGContext *tcg_ctx, bool locked) {} static void tb_exec_change(struct uc_struct *uc, bool locked) {}
#endif #endif
void tb_exec_lock(TCGContext *tcg_ctx) void tb_exec_lock(struct uc_struct *uc)
{ {
/* assumes sys_icache_invalidate already called */ /* assumes sys_icache_invalidate already called */
tb_exec_change(tcg_ctx, true); tb_exec_change(uc, true);
} }
void tb_exec_unlock(TCGContext *tcg_ctx) void tb_exec_unlock(struct uc_struct *uc)
{ {
tb_exec_change(tcg_ctx, false); tb_exec_change(uc, false);
} }

View File

@ -388,8 +388,8 @@ TranslationBlock *tb_htable_lookup(CPUState *cpu, target_ulong pc,
target_ulong cs_base, uint32_t flags, target_ulong cs_base, uint32_t flags,
uint32_t cf_mask); uint32_t cf_mask);
void tb_set_jmp_target(TranslationBlock *tb, int n, uintptr_t addr); void tb_set_jmp_target(TranslationBlock *tb, int n, uintptr_t addr);
void tb_exec_lock(TCGContext*); void tb_exec_lock(struct uc_struct*);
void tb_exec_unlock(TCGContext*); void tb_exec_unlock(struct uc_struct*);
/* GETPC is the true target of the return instruction that we'll execute. */ /* GETPC is the true target of the return instruction that we'll execute. */
#ifdef _MSC_VER #ifdef _MSC_VER

View File

@ -33,6 +33,36 @@ static inline void jit_write_protect(int enabled)
return pthread_jit_write_protect_np(enabled); return pthread_jit_write_protect_np(enabled);
} }
// Returns the S3_6_c15_c1_5 register's value
// Taken from
// https://stackoverflow.com/questions/70019553/lldb-how-to-read-the-permissions-of-a-memory-region-for-a-thread
// https://blog.svenpeter.dev/posts/m1_sprr_gxf/
static uint64_t read_sprr_perm(void)
{
uint64_t v;
__asm__ __volatile__("isb sy\n"
"mrs %0, S3_6_c15_c1_5\n"
: "=r"(v)::"memory");
return v;
}
__attribute__((unused)) static uint8_t thread_mask()
{
uint64_t v = read_sprr_perm();
return (v >> 20) & 3;
}
__attribute__((unused)) static bool thread_writeable()
{
return thread_mask() == 3;
}
__attribute__((unused)) static bool thread_executable()
{
return thread_mask() == 1;
}
#else /* defined(__aarch64__) && defined(CONFIG_DARWIN) */ #else /* defined(__aarch64__) && defined(CONFIG_DARWIN) */
static inline void jit_write_protect(int enabled) static inline void jit_write_protect(int enabled)

View File

@ -651,15 +651,6 @@ struct TCGContext {
/* Threshold to flush the translated code buffer. */ /* Threshold to flush the translated code buffer. */
void *code_gen_highwater; void *code_gen_highwater;
#ifdef HAVE_PTHREAD_JIT_PROTECT
/*
* True for X, False for W.
*
* Source: https://developer.apple.com/documentation/apple_silicon/porting_just-in-time_compilers_to_apple_silicon?language=objc
*/
bool code_gen_locked;
#endif
size_t tb_phys_invalidate_count; size_t tb_phys_invalidate_count;
/* Track which vCPU triggers events */ /* Track which vCPU triggers events */

174
uc.c
View File

@ -27,6 +27,7 @@
#include "qemu/target/s390x/unicorn.h" #include "qemu/target/s390x/unicorn.h"
#include "qemu/target/tricore/unicorn.h" #include "qemu/target/tricore/unicorn.h"
#include "qemu/include/tcg/tcg-apple-jit.h"
#include "qemu/include/qemu/queue.h" #include "qemu/include/qemu/queue.h"
#include "qemu-common.h" #include "qemu-common.h"
@ -34,6 +35,43 @@ static void clear_deleted_hooks(uc_engine *uc);
static uc_err uc_snapshot(uc_engine *uc); static uc_err uc_snapshot(uc_engine *uc);
static uc_err uc_restore_latest_snapshot(uc_engine *uc); static uc_err uc_restore_latest_snapshot(uc_engine *uc);
#ifdef HAVE_PTHREAD_JIT_PROTECT
static void save_jit_state(uc_engine *uc)
{
if (!uc->nested) {
uc->thread_executable_entry = thread_executable();
uc->current_executable = uc->thread_executable_entry;
}
uc->nested += 1;
}
static void restore_jit_state(uc_engine *uc)
{
assert(uc->nested > 0);
if (uc->nested == 1) {
assert(uc->current_executable == thread_executable());
if (uc->current_executable != uc->thread_executable_entry) {
if (uc->thread_executable_entry) {
jit_write_protect(true);
} else {
jit_write_protect(false);
}
}
}
uc->nested -= 1;
}
#else
static void save_jit_state(uc_engine *uc)
{
(void *)uc;
}
static void restore_jit_state(uc_engine *uc)
{
(void *)uc;
}
#endif
static void *hook_insert(struct list *l, struct hook *h) static void *hook_insert(struct list *l, struct hook *h)
{ {
void *item = list_insert(l, (void *)h); void *item = list_insert(l, (void *)h);
@ -206,6 +244,7 @@ bool uc_arch_supported(uc_arch arch)
} }
#define UC_INIT(uc) \ #define UC_INIT(uc) \
save_jit_state(uc); \
if (unlikely(!(uc)->init_done)) { \ if (unlikely(!(uc)->init_done)) { \
int __init_ret = uc_init_engine(uc); \ int __init_ret = uc_init_engine(uc); \
if (unlikely(__init_ret != UC_ERR_OK)) { \ if (unlikely(__init_ret != UC_ERR_OK)) { \
@ -229,7 +268,6 @@ static gint uc_exits_cmp(gconstpointer a, gconstpointer b, gpointer user_data)
static uc_err uc_init_engine(uc_engine *uc) static uc_err uc_init_engine(uc_engine *uc)
{ {
if (uc->init_done) { if (uc->init_done) {
return UC_ERR_HANDLE; return UC_ERR_HANDLE;
} }
@ -547,10 +585,12 @@ uc_err uc_reg_read_batch(uc_engine *uc, int *regs, void **vals, int count)
size_t size = (size_t)-1; size_t size = (size_t)-1;
uc_err err = reg_read(env, mode, regid, value, &size); uc_err err = reg_read(env, mode, regid, value, &size);
if (err) { if (err) {
restore_jit_state(uc);
return err; return err;
} }
} }
restore_jit_state(uc);
return UC_ERR_OK; return UC_ERR_OK;
} }
@ -571,6 +611,7 @@ uc_err uc_reg_write_batch(uc_engine *uc, int *regs, void *const *vals,
size_t size = (size_t)-1; size_t size = (size_t)-1;
uc_err err = reg_write(env, mode, regid, value, &size, &setpc); uc_err err = reg_write(env, mode, regid, value, &size, &setpc);
if (err) { if (err) {
restore_jit_state(uc);
return err; return err;
} }
} }
@ -580,6 +621,7 @@ uc_err uc_reg_write_batch(uc_engine *uc, int *regs, void *const *vals,
break_translation_loop(uc); break_translation_loop(uc);
} }
restore_jit_state(uc);
return UC_ERR_OK; return UC_ERR_OK;
} }
@ -598,10 +640,12 @@ uc_err uc_reg_read_batch2(uc_engine *uc, int *regs, void *const *vals,
void *value = vals[i]; void *value = vals[i];
uc_err err = reg_read(env, mode, regid, value, sizes + i); uc_err err = reg_read(env, mode, regid, value, sizes + i);
if (err) { if (err) {
restore_jit_state(uc);
return err; return err;
} }
} }
restore_jit_state(uc);
return UC_ERR_OK; return UC_ERR_OK;
} }
@ -621,6 +665,7 @@ uc_err uc_reg_write_batch2(uc_engine *uc, int *regs, const void *const *vals,
const void *value = vals[i]; const void *value = vals[i];
uc_err err = reg_write(env, mode, regid, value, sizes + i, &setpc); uc_err err = reg_write(env, mode, regid, value, sizes + i, &setpc);
if (err) { if (err) {
restore_jit_state(uc);
return err; return err;
} }
} }
@ -630,6 +675,7 @@ uc_err uc_reg_write_batch2(uc_engine *uc, int *regs, const void *const *vals,
break_translation_loop(uc); break_translation_loop(uc);
} }
restore_jit_state(uc);
return UC_ERR_OK; return UC_ERR_OK;
} }
@ -638,7 +684,9 @@ uc_err uc_reg_read(uc_engine *uc, int regid, void *value)
{ {
UC_INIT(uc); UC_INIT(uc);
size_t size = (size_t)-1; size_t size = (size_t)-1;
return uc->reg_read(uc->cpu->env_ptr, uc->mode, regid, value, &size); uc_err err = uc->reg_read(uc->cpu->env_ptr, uc->mode, regid, value, &size);
restore_jit_state(uc);
return err;
} }
UNICORN_EXPORT UNICORN_EXPORT
@ -650,6 +698,7 @@ uc_err uc_reg_write(uc_engine *uc, int regid, const void *value)
uc_err err = uc_err err =
uc->reg_write(uc->cpu->env_ptr, uc->mode, regid, value, &size, &setpc); uc->reg_write(uc->cpu->env_ptr, uc->mode, regid, value, &size, &setpc);
if (err) { if (err) {
restore_jit_state(uc);
return err; return err;
} }
if (setpc) { if (setpc) {
@ -658,6 +707,7 @@ uc_err uc_reg_write(uc_engine *uc, int regid, const void *value)
break_translation_loop(uc); break_translation_loop(uc);
} }
restore_jit_state(uc);
return UC_ERR_OK; return UC_ERR_OK;
} }
@ -665,7 +715,9 @@ UNICORN_EXPORT
uc_err uc_reg_read2(uc_engine *uc, int regid, void *value, size_t *size) uc_err uc_reg_read2(uc_engine *uc, int regid, void *value, size_t *size)
{ {
UC_INIT(uc); UC_INIT(uc);
return uc->reg_read(uc->cpu->env_ptr, uc->mode, regid, value, size); uc_err err = uc->reg_read(uc->cpu->env_ptr, uc->mode, regid, value, size);
restore_jit_state(uc);
return err;
} }
UNICORN_EXPORT UNICORN_EXPORT
@ -676,6 +728,7 @@ uc_err uc_reg_write2(uc_engine *uc, int regid, const void *value, size_t *size)
uc_err err = uc_err err =
uc->reg_write(uc->cpu->env_ptr, uc->mode, regid, value, size, &setpc); uc->reg_write(uc->cpu->env_ptr, uc->mode, regid, value, size, &setpc);
if (err) { if (err) {
restore_jit_state(uc);
return err; return err;
} }
if (setpc) { if (setpc) {
@ -684,6 +737,7 @@ uc_err uc_reg_write2(uc_engine *uc, int regid, const void *value, size_t *size)
break_translation_loop(uc); break_translation_loop(uc);
} }
restore_jit_state(uc);
return UC_ERR_OK; return UC_ERR_OK;
} }
@ -727,10 +781,13 @@ uc_err uc_mem_read(uc_engine *uc, uint64_t address, void *_bytes, size_t size)
UC_INIT(uc); UC_INIT(uc);
// qemu cpu_physical_memory_rw() size is an int // qemu cpu_physical_memory_rw() size is an int
if (size > INT_MAX) if (size > INT_MAX) {
restore_jit_state(uc);
return UC_ERR_ARG; return UC_ERR_ARG;
}
if (!check_mem_area(uc, address, size)) { if (!check_mem_area(uc, address, size)) {
restore_jit_state(uc);
return UC_ERR_READ_UNMAPPED; return UC_ERR_READ_UNMAPPED;
} }
@ -752,8 +809,10 @@ uc_err uc_mem_read(uc_engine *uc, uint64_t address, void *_bytes, size_t size)
} }
if (count == size) { if (count == size) {
restore_jit_state(uc);
return UC_ERR_OK; return UC_ERR_OK;
} else { } else {
restore_jit_state(uc);
return UC_ERR_READ_UNMAPPED; return UC_ERR_READ_UNMAPPED;
} }
} }
@ -768,10 +827,13 @@ uc_err uc_mem_write(uc_engine *uc, uint64_t address, const void *_bytes,
UC_INIT(uc); UC_INIT(uc);
// qemu cpu_physical_memory_rw() size is an int // qemu cpu_physical_memory_rw() size is an int
if (size > INT_MAX) if (size > INT_MAX) {
restore_jit_state(uc);
return UC_ERR_ARG; return UC_ERR_ARG;
}
if (!check_mem_area(uc, address, size)) { if (!check_mem_area(uc, address, size)) {
restore_jit_state(uc);
return UC_ERR_WRITE_UNMAPPED; return UC_ERR_WRITE_UNMAPPED;
} }
@ -814,8 +876,10 @@ uc_err uc_mem_write(uc_engine *uc, uint64_t address, const void *_bytes,
} }
if (count == size) { if (count == size) {
restore_jit_state(uc);
return UC_ERR_OK; return UC_ERR_OK;
} else { } else {
restore_jit_state(uc);
return UC_ERR_WRITE_UNMAPPED; return UC_ERR_WRITE_UNMAPPED;
} }
} }
@ -897,7 +961,10 @@ uc_err uc_emu_start(uc_engine *uc, uint64_t begin, uint64_t until,
uc->timed_out = false; uc->timed_out = false;
uc->first_tb = true; uc->first_tb = true;
UC_INIT(uc); // Avoid nested uc_emu_start saves wrong jit states.
if (uc->nested_level == 0) {
UC_INIT(uc);
}
// Advance the nested levels. We must decrease the level count by one when // Advance the nested levels. We must decrease the level count by one when
// we return from uc_emu_start. // we return from uc_emu_start.
@ -1042,6 +1109,8 @@ uc_err uc_emu_start(uc_engine *uc, uint64_t begin, uint64_t until,
// remove hooks to delete // remove hooks to delete
// make sure we delete all hooks at the first level. // make sure we delete all hooks at the first level.
clear_deleted_hooks(uc); clear_deleted_hooks(uc);
restore_jit_state(uc);
} }
if (timeout) { if (timeout) {
@ -1061,7 +1130,9 @@ uc_err uc_emu_stop(uc_engine *uc)
{ {
UC_INIT(uc); UC_INIT(uc);
uc->stop_request = true; uc->stop_request = true;
return break_translation_loop(uc); uc_err err = break_translation_loop(uc);
restore_jit_state(uc);
return err;
} }
// return target index where a memory region at the address exists, or could be // return target index where a memory region at the address exists, or could be
@ -1196,10 +1267,13 @@ uc_err uc_mem_map(uc_engine *uc, uint64_t address, size_t size, uint32_t perms)
res = mem_map_check(uc, address, size, perms); res = mem_map_check(uc, address, size, perms);
if (res) { if (res) {
restore_jit_state(uc);
return res; return res;
} }
return mem_map(uc, uc->memory_map(uc, address, size, perms)); res = mem_map(uc, uc->memory_map(uc, address, size, perms));
restore_jit_state(uc);
return res;
} }
UNICORN_EXPORT UNICORN_EXPORT
@ -1211,15 +1285,19 @@ uc_err uc_mem_map_ptr(uc_engine *uc, uint64_t address, size_t size,
UC_INIT(uc); UC_INIT(uc);
if (ptr == NULL) { if (ptr == NULL) {
restore_jit_state(uc);
return UC_ERR_ARG; return UC_ERR_ARG;
} }
res = mem_map_check(uc, address, size, perms); res = mem_map_check(uc, address, size, perms);
if (res) { if (res) {
restore_jit_state(uc);
return res; return res;
} }
return mem_map(uc, uc->memory_map_ptr(uc, address, size, perms, ptr)); res = mem_map(uc, uc->memory_map_ptr(uc, address, size, perms, ptr));
restore_jit_state(uc);
return res;
} }
UNICORN_EXPORT UNICORN_EXPORT
@ -1232,13 +1310,17 @@ uc_err uc_mmio_map(uc_engine *uc, uint64_t address, size_t size,
UC_INIT(uc); UC_INIT(uc);
res = mem_map_check(uc, address, size, UC_PROT_ALL); res = mem_map_check(uc, address, size, UC_PROT_ALL);
if (res) if (res) {
restore_jit_state(uc);
return res; return res;
}
// The callbacks do not need to be checked for NULL here, as their presence // The callbacks do not need to be checked for NULL here, as their presence
// (or lack thereof) will determine the permissions used. // (or lack thereof) will determine the permissions used.
return mem_map(uc, uc->memory_map_io(uc, address, size, read_cb, write_cb, res = mem_map(uc, uc->memory_map_io(uc, address, size, read_cb, write_cb,
user_data_read, user_data_write)); user_data_read, user_data_write));
restore_jit_state(uc);
return res;
} }
// Create a backup copy of the indicated MemoryRegion. // Create a backup copy of the indicated MemoryRegion.
@ -1512,26 +1594,31 @@ uc_err uc_mem_protect(struct uc_struct *uc, uint64_t address, size_t size,
// snapshot and protection can't be mixed // snapshot and protection can't be mixed
if (uc->snapshot_level > 0) { if (uc->snapshot_level > 0) {
restore_jit_state(uc);
return UC_ERR_ARG; return UC_ERR_ARG;
} }
if (size == 0) { if (size == 0) {
// trivial case, no change // trivial case, no change
restore_jit_state(uc);
return UC_ERR_OK; return UC_ERR_OK;
} }
// address must be aligned to uc->target_page_size // address must be aligned to uc->target_page_size
if ((address & uc->target_page_align) != 0) { if ((address & uc->target_page_align) != 0) {
restore_jit_state(uc);
return UC_ERR_ARG; return UC_ERR_ARG;
} }
// size must be multiple of uc->target_page_size // size must be multiple of uc->target_page_size
if ((size & uc->target_page_align) != 0) { if ((size & uc->target_page_align) != 0) {
restore_jit_state(uc);
return UC_ERR_ARG; return UC_ERR_ARG;
} }
// check for only valid permissions // check for only valid permissions
if ((perms & ~UC_PROT_ALL) != 0) { if ((perms & ~UC_PROT_ALL) != 0) {
restore_jit_state(uc);
return UC_ERR_ARG; return UC_ERR_ARG;
} }
@ -1539,6 +1626,7 @@ uc_err uc_mem_protect(struct uc_struct *uc, uint64_t address, size_t size,
// TODO check if protected is possible // TODO check if protected is possible
// deny after cow // deny after cow
if (!check_mem_area(uc, address, size)) { if (!check_mem_area(uc, address, size)) {
restore_jit_state(uc);
return UC_ERR_NOMEM; return UC_ERR_NOMEM;
} }
@ -1551,6 +1639,7 @@ uc_err uc_mem_protect(struct uc_struct *uc, uint64_t address, size_t size,
len = memory_region_len(uc, mr, addr, size - count); len = memory_region_len(uc, mr, addr, size - count);
if (mr->ram) { if (mr->ram) {
if (!split_region(uc, mr, addr, len, false)) { if (!split_region(uc, mr, addr, len, false)) {
restore_jit_state(uc);
return UC_ERR_NOMEM; return UC_ERR_NOMEM;
} }
@ -1565,6 +1654,7 @@ uc_err uc_mem_protect(struct uc_struct *uc, uint64_t address, size_t size,
} else { } else {
if (!split_mmio_region(uc, mr, addr, len, false)) { if (!split_mmio_region(uc, mr, addr, len, false)) {
restore_jit_state(uc);
return UC_ERR_NOMEM; return UC_ERR_NOMEM;
} }
@ -1586,6 +1676,7 @@ uc_err uc_mem_protect(struct uc_struct *uc, uint64_t address, size_t size,
} }
} }
restore_jit_state(uc);
return UC_ERR_OK; return UC_ERR_OK;
} }
@ -1623,26 +1714,32 @@ uc_err uc_mem_unmap(struct uc_struct *uc, uint64_t address, size_t size)
if (size == 0) { if (size == 0) {
// nothing to unmap // nothing to unmap
restore_jit_state(uc);
return UC_ERR_OK; return UC_ERR_OK;
} }
// address must be aligned to uc->target_page_size // address must be aligned to uc->target_page_size
if ((address & uc->target_page_align) != 0) { if ((address & uc->target_page_align) != 0) {
restore_jit_state(uc);
return UC_ERR_ARG; return UC_ERR_ARG;
} }
// size must be multiple of uc->target_page_size // size must be multiple of uc->target_page_size
if ((size & uc->target_page_align) != 0) { if ((size & uc->target_page_align) != 0) {
restore_jit_state(uc);
return UC_ERR_ARG; return UC_ERR_ARG;
} }
// check that user's entire requested block is mapped // check that user's entire requested block is mapped
if (!check_mem_area(uc, address, size)) { if (!check_mem_area(uc, address, size)) {
restore_jit_state(uc);
return UC_ERR_NOMEM; return UC_ERR_NOMEM;
} }
if (uc->snapshot_level > 0) { if (uc->snapshot_level > 0) {
return uc_mem_unmap_snapshot(uc, address, size, NULL); uc_err res = uc_mem_unmap_snapshot(uc, address, size, NULL);
restore_jit_state(uc);
return res;
} }
// Now we know entire region is mapped, so do the unmap // Now we know entire region is mapped, so do the unmap
@ -1654,10 +1751,12 @@ uc_err uc_mem_unmap(struct uc_struct *uc, uint64_t address, size_t size)
len = memory_region_len(uc, mr, addr, size - count); len = memory_region_len(uc, mr, addr, size - count);
if (!mr->ram) { if (!mr->ram) {
if (!split_mmio_region(uc, mr, addr, len, true)) { if (!split_mmio_region(uc, mr, addr, len, true)) {
restore_jit_state(uc);
return UC_ERR_NOMEM; return UC_ERR_NOMEM;
} }
} else { } else {
if (!split_region(uc, mr, addr, len, true)) { if (!split_region(uc, mr, addr, len, true)) {
restore_jit_state(uc);
return UC_ERR_NOMEM; return UC_ERR_NOMEM;
} }
} }
@ -1672,6 +1771,7 @@ uc_err uc_mem_unmap(struct uc_struct *uc, uint64_t address, size_t size)
addr += len; addr += len;
} }
restore_jit_state(uc);
return UC_ERR_OK; return UC_ERR_OK;
} }
@ -1686,6 +1786,7 @@ uc_err uc_hook_add(uc_engine *uc, uc_hook *hh, int type, void *callback,
struct hook *hook = calloc(1, sizeof(struct hook)); struct hook *hook = calloc(1, sizeof(struct hook));
if (hook == NULL) { if (hook == NULL) {
restore_jit_state(uc);
return UC_ERR_NOMEM; return UC_ERR_NOMEM;
} }
@ -1711,6 +1812,7 @@ uc_err uc_hook_add(uc_engine *uc, uc_hook *hh, int type, void *callback,
if (uc->insn_hook_validate) { if (uc->insn_hook_validate) {
if (!uc->insn_hook_validate(hook->insn)) { if (!uc->insn_hook_validate(hook->insn)) {
free(hook); free(hook);
restore_jit_state(uc);
return UC_ERR_HOOK; return UC_ERR_HOOK;
} }
} }
@ -1718,16 +1820,19 @@ uc_err uc_hook_add(uc_engine *uc, uc_hook *hh, int type, void *callback,
if (uc->hook_insert) { if (uc->hook_insert) {
if (hook_insert(&uc->hook[UC_HOOK_INSN_IDX], hook) == NULL) { if (hook_insert(&uc->hook[UC_HOOK_INSN_IDX], hook) == NULL) {
free(hook); free(hook);
restore_jit_state(uc);
return UC_ERR_NOMEM; return UC_ERR_NOMEM;
} }
} else { } else {
if (hook_append(&uc->hook[UC_HOOK_INSN_IDX], hook) == NULL) { if (hook_append(&uc->hook[UC_HOOK_INSN_IDX], hook) == NULL) {
free(hook); free(hook);
restore_jit_state(uc);
return UC_ERR_NOMEM; return UC_ERR_NOMEM;
} }
} }
uc->hooks_count[UC_HOOK_INSN_IDX]++; uc->hooks_count[UC_HOOK_INSN_IDX]++;
restore_jit_state(uc);
return UC_ERR_OK; return UC_ERR_OK;
} }
@ -1742,6 +1847,7 @@ uc_err uc_hook_add(uc_engine *uc, uc_hook *hh, int type, void *callback,
if (uc->opcode_hook_invalidate) { if (uc->opcode_hook_invalidate) {
if (!uc->opcode_hook_invalidate(hook->op, hook->op_flags)) { if (!uc->opcode_hook_invalidate(hook->op, hook->op_flags)) {
free(hook); free(hook);
restore_jit_state(uc);
return UC_ERR_HOOK; return UC_ERR_HOOK;
} }
} }
@ -1749,11 +1855,13 @@ uc_err uc_hook_add(uc_engine *uc, uc_hook *hh, int type, void *callback,
if (uc->hook_insert) { if (uc->hook_insert) {
if (hook_insert(&uc->hook[UC_HOOK_TCG_OPCODE_IDX], hook) == NULL) { if (hook_insert(&uc->hook[UC_HOOK_TCG_OPCODE_IDX], hook) == NULL) {
free(hook); free(hook);
restore_jit_state(uc);
return UC_ERR_NOMEM; return UC_ERR_NOMEM;
} }
} else { } else {
if (hook_append(&uc->hook[UC_HOOK_TCG_OPCODE_IDX], hook) == NULL) { if (hook_append(&uc->hook[UC_HOOK_TCG_OPCODE_IDX], hook) == NULL) {
free(hook); free(hook);
restore_jit_state(uc);
return UC_ERR_NOMEM; return UC_ERR_NOMEM;
} }
} }
@ -1769,11 +1877,13 @@ uc_err uc_hook_add(uc_engine *uc, uc_hook *hh, int type, void *callback,
if (uc->hook_insert) { if (uc->hook_insert) {
if (hook_insert(&uc->hook[i], hook) == NULL) { if (hook_insert(&uc->hook[i], hook) == NULL) {
free(hook); free(hook);
restore_jit_state(uc);
return UC_ERR_NOMEM; return UC_ERR_NOMEM;
} }
} else { } else {
if (hook_append(&uc->hook[i], hook) == NULL) { if (hook_append(&uc->hook[i], hook) == NULL) {
free(hook); free(hook);
restore_jit_state(uc);
return UC_ERR_NOMEM; return UC_ERR_NOMEM;
} }
} }
@ -1789,6 +1899,7 @@ uc_err uc_hook_add(uc_engine *uc, uc_hook *hh, int type, void *callback,
free(hook); free(hook);
} }
restore_jit_state(uc);
return ret; return ret;
} }
@ -1816,6 +1927,7 @@ uc_err uc_hook_del(uc_engine *uc, uc_hook hh)
} }
} }
restore_jit_state(uc);
return UC_ERR_OK; return UC_ERR_OK;
} }
@ -1928,6 +2040,7 @@ uc_err uc_mem_regions(uc_engine *uc, uc_mem_region **regions, uint32_t *count)
r = g_malloc0(*count * sizeof(uc_mem_region)); r = g_malloc0(*count * sizeof(uc_mem_region));
if (r == NULL) { if (r == NULL) {
// out of memory // out of memory
restore_jit_state(uc);
return UC_ERR_NOMEM; return UC_ERR_NOMEM;
} }
} }
@ -1940,6 +2053,7 @@ uc_err uc_mem_regions(uc_engine *uc, uc_mem_region **regions, uint32_t *count)
*regions = r; *regions = r;
restore_jit_state(uc);
return UC_ERR_OK; return UC_ERR_OK;
} }
@ -1974,6 +2088,7 @@ uc_err uc_query(uc_engine *uc, uc_query_type type, size_t *result)
break; break;
} }
restore_jit_state(uc);
return UC_ERR_OK; return UC_ERR_OK;
} }
@ -1990,8 +2105,10 @@ uc_err uc_context_alloc(uc_engine *uc, uc_context **context)
(*_context)->context_size = size - sizeof(uc_context); (*_context)->context_size = size - sizeof(uc_context);
(*_context)->arch = uc->arch; (*_context)->arch = uc->arch;
(*_context)->mode = uc->mode; (*_context)->mode = uc->mode;
restore_jit_state(uc);
return UC_ERR_OK; return UC_ERR_OK;
} else { } else {
restore_jit_state(uc);
return UC_ERR_NOMEM; return UC_ERR_NOMEM;
} }
} }
@ -2008,6 +2125,7 @@ size_t uc_context_size(uc_engine *uc)
{ {
UC_INIT(uc); UC_INIT(uc);
restore_jit_state(uc);
if (!uc->context_size) { if (!uc->context_size) {
// return the total size of struct uc_context // return the total size of struct uc_context
return sizeof(uc_context) + uc->cpu_context_size; return sizeof(uc_context) + uc->cpu_context_size;
@ -2025,6 +2143,7 @@ uc_err uc_context_save(uc_engine *uc, uc_context *context)
if (uc->context_content & UC_CTL_CONTEXT_MEMORY) { if (uc->context_content & UC_CTL_CONTEXT_MEMORY) {
ret = uc_snapshot(uc); ret = uc_snapshot(uc);
if (ret != UC_ERR_OK) { if (ret != UC_ERR_OK) {
restore_jit_state(uc);
return ret; return ret;
} }
} }
@ -2034,11 +2153,15 @@ uc_err uc_context_save(uc_engine *uc, uc_context *context)
if (uc->context_content & UC_CTL_CONTEXT_CPU) { if (uc->context_content & UC_CTL_CONTEXT_CPU) {
if (!uc->context_save) { if (!uc->context_save) {
memcpy(context->data, uc->cpu->env_ptr, context->context_size); memcpy(context->data, uc->cpu->env_ptr, context->context_size);
restore_jit_state(uc);
return UC_ERR_OK; return UC_ERR_OK;
} else { } else {
return uc->context_save(uc, context); ret = uc->context_save(uc, context);
restore_jit_state(uc);
return ret;
} }
} }
restore_jit_state(uc);
return ret; return ret;
} }
@ -2378,6 +2501,8 @@ uc_err uc_ctl(uc_engine *uc, uc_control_type control, ...)
uint32_t *page_size = va_arg(args, uint32_t *); uint32_t *page_size = va_arg(args, uint32_t *);
*page_size = uc->target_page_size; *page_size = uc->target_page_size;
restore_jit_state(uc);
} else { } else {
uint32_t page_size = va_arg(args, uint32_t); uint32_t page_size = va_arg(args, uint32_t);
int bits = 0; int bits = 0;
@ -2406,6 +2531,7 @@ uc_err uc_ctl(uc_engine *uc, uc_control_type control, ...)
err = UC_ERR_OK; err = UC_ERR_OK;
} }
break; break;
} }
@ -2431,6 +2557,8 @@ uc_err uc_ctl(uc_engine *uc, uc_control_type control, ...)
} else { } else {
err = UC_ERR_ARG; err = UC_ERR_ARG;
} }
restore_jit_state(uc);
break; break;
} }
@ -2464,6 +2592,8 @@ uc_err uc_ctl(uc_engine *uc, uc_control_type control, ...)
} else { } else {
err = UC_ERR_ARG; err = UC_ERR_ARG;
} }
restore_jit_state(uc);
break; break;
} }
@ -2474,6 +2604,8 @@ uc_err uc_ctl(uc_engine *uc, uc_control_type control, ...)
int *model = va_arg(args, int *); int *model = va_arg(args, int *);
*model = uc->cpu_model; *model = uc->cpu_model;
save_jit_state(uc);
} else { } else {
int model = va_arg(args, int); int model = va_arg(args, int);
@ -2579,6 +2711,8 @@ uc_err uc_ctl(uc_engine *uc, uc_control_type control, ...)
} else { } else {
err = UC_ERR_ARG; err = UC_ERR_ARG;
} }
restore_jit_state(uc);
break; break;
} }
@ -2597,6 +2731,8 @@ uc_err uc_ctl(uc_engine *uc, uc_control_type control, ...)
} else { } else {
err = UC_ERR_ARG; err = UC_ERR_ARG;
} }
restore_jit_state(uc);
break; break;
} }
@ -2609,6 +2745,8 @@ uc_err uc_ctl(uc_engine *uc, uc_control_type control, ...)
} else { } else {
err = UC_ERR_ARG; err = UC_ERR_ARG;
} }
restore_jit_state(uc);
break; break;
case UC_CTL_TLB_FLUSH: case UC_CTL_TLB_FLUSH:
@ -2620,6 +2758,8 @@ uc_err uc_ctl(uc_engine *uc, uc_control_type control, ...)
} else { } else {
err = UC_ERR_ARG; err = UC_ERR_ARG;
} }
restore_jit_state(uc);
break; break;
case UC_CTL_TLB_TYPE: { case UC_CTL_TLB_TYPE: {
@ -2632,6 +2772,8 @@ uc_err uc_ctl(uc_engine *uc, uc_control_type control, ...)
} else { } else {
err = UC_ERR_ARG; err = UC_ERR_ARG;
} }
restore_jit_state(uc);
break; break;
} }
@ -2645,6 +2787,8 @@ uc_err uc_ctl(uc_engine *uc, uc_control_type control, ...)
uint32_t *size = va_arg(args, uint32_t *); uint32_t *size = va_arg(args, uint32_t *);
*size = uc->tcg_buffer_size; *size = uc->tcg_buffer_size;
restore_jit_state(uc);
} }
break; break;
} }
@ -2660,6 +2804,8 @@ uc_err uc_ctl(uc_engine *uc, uc_control_type control, ...)
} else { } else {
err = UC_ERR_ARG; err = UC_ERR_ARG;
} }
restore_jit_state(uc);
break; break;
default: default: