mirror of
https://github.com/yuzu-emu/unicorn.git
synced 2024-12-23 05:45:36 +00:00
Change save/restore API to have explicit alloc/free functions. Python has explicit save/update functions.
This commit is contained in:
parent
6c54f9ce48
commit
1ab2154fe5
|
@ -80,6 +80,7 @@ def _setup_prototype(lib, fname, restype, *argtypes):
|
||||||
|
|
||||||
ucerr = ctypes.c_int
|
ucerr = ctypes.c_int
|
||||||
uc_engine = ctypes.c_void_p
|
uc_engine = ctypes.c_void_p
|
||||||
|
uc_context = ctypes.c_void_p
|
||||||
uc_hook_h = ctypes.c_size_t
|
uc_hook_h = ctypes.c_size_t
|
||||||
|
|
||||||
_setup_prototype(_uc, "uc_version", ctypes.c_uint, ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_int))
|
_setup_prototype(_uc, "uc_version", ctypes.c_uint, ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_int))
|
||||||
|
@ -100,8 +101,10 @@ _setup_prototype(_uc, "uc_mem_map_ptr", ucerr, uc_engine, ctypes.c_uint64, ctype
|
||||||
_setup_prototype(_uc, "uc_mem_unmap", ucerr, uc_engine, ctypes.c_uint64, ctypes.c_size_t)
|
_setup_prototype(_uc, "uc_mem_unmap", ucerr, uc_engine, ctypes.c_uint64, ctypes.c_size_t)
|
||||||
_setup_prototype(_uc, "uc_mem_protect", ucerr, uc_engine, ctypes.c_uint64, ctypes.c_size_t, ctypes.c_uint32)
|
_setup_prototype(_uc, "uc_mem_protect", ucerr, uc_engine, ctypes.c_uint64, ctypes.c_size_t, ctypes.c_uint32)
|
||||||
_setup_prototype(_uc, "uc_query", ucerr, uc_engine, ctypes.c_uint32, ctypes.POINTER(ctypes.c_size_t))
|
_setup_prototype(_uc, "uc_query", ucerr, uc_engine, ctypes.c_uint32, ctypes.POINTER(ctypes.c_size_t))
|
||||||
_setup_prototype(_uc, "uc_context_save", ctypes.c_voidp, uc_engine, ctypes.c_voidp)
|
_setup_prototype(_uc, "uc_context_alloc", ucerr, uc_engine, ctypes.POINTER(uc_context))
|
||||||
_setup_prototype(_uc, "uc_context_restore", None, uc_engine, ctypes.c_voidp)
|
_setup_prototype(_uc, "uc_context_free", ucerr, uc_context)
|
||||||
|
_setup_prototype(_uc, "uc_context_save", ucerr, uc_engine, uc_context)
|
||||||
|
_setup_prototype(_uc, "uc_context_restore", ucerr, uc_engine, uc_context)
|
||||||
_setup_prototype(_uc, "free", None, ctypes.c_voidp)
|
_setup_prototype(_uc, "free", None, ctypes.c_voidp)
|
||||||
|
|
||||||
# uc_hook_add is special due to variable number of arguments
|
# uc_hook_add is special due to variable number of arguments
|
||||||
|
@ -443,27 +446,36 @@ class Uc(object):
|
||||||
raise UcError(status)
|
raise UcError(status)
|
||||||
h = 0
|
h = 0
|
||||||
|
|
||||||
def context_save(self, store=None):
|
def context_save(self):
|
||||||
if store is None:
|
ptr = ctypes.cast(0, ctypes.c_voidp)
|
||||||
ptr = ctypes.cast(0, ctypes.c_voidp)
|
status = _uc.uc_context_alloc(self._uch, ctypes.byref(ptr))
|
||||||
return _ActivePointer(_uc.uc_context_save(self._uch, ptr))
|
if status != uc.UC_ERR_OK:
|
||||||
elif type(store) is _ActivePointer:
|
raise UcError(status)
|
||||||
_uc.uc_context_save(self._uch, store.pointer)
|
|
||||||
return store
|
|
||||||
else:
|
|
||||||
raise TypeError("Bad register store %s" % repr(store))
|
|
||||||
|
|
||||||
def context_restore(self, store):
|
status = _uc.uc_context_save(self._uch, ptr)
|
||||||
if type(store) is not _ActivePointer:
|
if status != uc.UC_ERR_OK:
|
||||||
raise TYpeError("Bad register store %s" % repr(store))
|
raise UcError(status)
|
||||||
_uc.uc_context_restore(self._uch, store.pointer)
|
|
||||||
|
|
||||||
class _ActivePointer(object):
|
return SavedContext(ptr)
|
||||||
|
|
||||||
|
def context_update(self, context):
|
||||||
|
status = _uc.uc_context_save(self._uch, context.pointer)
|
||||||
|
if status != uc.UC_ERR_OK:
|
||||||
|
raise UcError(status)
|
||||||
|
|
||||||
|
def context_restore(self, context):
|
||||||
|
status = _uc.uc_context_restore(self._uch, context.pointer)
|
||||||
|
if status != uc.UC_ERR_OK:
|
||||||
|
raise UcError(status)
|
||||||
|
|
||||||
|
class SavedContext(object):
|
||||||
def __init__(self, pointer):
|
def __init__(self, pointer):
|
||||||
self.pointer = pointer
|
self.pointer = pointer
|
||||||
|
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
_uc.free(self.pointer)
|
status = _uc.uc_context_free(self.pointer)
|
||||||
|
if status != uc.UC_ERR_OK:
|
||||||
|
raise UcError(status)
|
||||||
|
|
||||||
# print out debugging info
|
# print out debugging info
|
||||||
def debug():
|
def debug():
|
||||||
|
|
|
@ -273,6 +273,15 @@ typedef enum uc_query_type {
|
||||||
UC_QUERY_PAGE_SIZE,
|
UC_QUERY_PAGE_SIZE,
|
||||||
} uc_query_type;
|
} uc_query_type;
|
||||||
|
|
||||||
|
// Metadata stub for the variable-size cpu context used with uc_context_*()
|
||||||
|
typedef struct uc_context {
|
||||||
|
uc_arch arch;
|
||||||
|
uc_mode mode;
|
||||||
|
size_t size;
|
||||||
|
bool used;
|
||||||
|
char data[0];
|
||||||
|
} uc_context;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Return combined API version & major and minor version numbers.
|
Return combined API version & major and minor version numbers.
|
||||||
|
|
||||||
|
@ -624,23 +633,46 @@ uc_err uc_mem_protect(uc_engine *uc, uint64_t address, size_t size, uint32_t per
|
||||||
UNICORN_EXPORT
|
UNICORN_EXPORT
|
||||||
uc_err uc_mem_regions(uc_engine *uc, uc_mem_region **regions, uint32_t *count);
|
uc_err uc_mem_regions(uc_engine *uc, uc_mem_region **regions, uint32_t *count);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Allocate a region that can be used with uc_context_{save,restore} to perform
|
||||||
|
quick save/rollback of the CPU context, which includes registers and some
|
||||||
|
internal metadata. Contexts may not be shared across engine instances with
|
||||||
|
differing arches or modes.
|
||||||
|
|
||||||
|
@uc: handle returned by uc_open()
|
||||||
|
@context: pointer to a uc_engine*. This will be updated with the pointer to
|
||||||
|
the new context on successful return of this function.
|
||||||
|
|
||||||
|
@return UC_ERR_OK on success, or other value on failure (refer to uc_err enum
|
||||||
|
for detailed error).
|
||||||
|
*/
|
||||||
|
UNICORN_EXPORT
|
||||||
|
uc_err uc_context_alloc(uc_engine *uc, uc_context **context);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Free the resource allocated by uc_context_alloc.
|
||||||
|
|
||||||
|
@context: handle returned by uc_context_alloc()
|
||||||
|
|
||||||
|
@return UC_ERR_OK on success, or other value on failure (refer to uc_err enum
|
||||||
|
for detailed error).
|
||||||
|
*/
|
||||||
|
UNICORN_EXPORT
|
||||||
|
uc_err uc_context_free(uc_context *context);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Save a copy of the internal CPU context.
|
Save a copy of the internal CPU context.
|
||||||
This API should be used to efficiently make or update a saved copy of the
|
This API should be used to efficiently make or update a saved copy of the
|
||||||
internal CPU state.
|
internal CPU state.
|
||||||
|
|
||||||
@uc: handle returned by uc_open()
|
@uc: handle returned by uc_open()
|
||||||
@buffer: pointer to the region to store the context in. The first call to
|
@context: handle returned by uc_context_alloc()
|
||||||
this function should pass NULL in this parameter, so a region of the
|
|
||||||
appropriate size for the current architecture can be allocated. Further calls
|
|
||||||
to this function may pass in the return value of previous calls.
|
|
||||||
|
|
||||||
@return a pointer to the region the context was saved in. If buffer was
|
@return UC_ERR_OK on success, or other value on failure (refer to uc_err enum
|
||||||
NULL, this is a newly allocated region, otherwise it is the same as buffer.
|
for detailed error).
|
||||||
Any allocation performed by this function must be freed by the user.
|
|
||||||
*/
|
*/
|
||||||
UNICORN_EXPORT
|
UNICORN_EXPORT
|
||||||
void *uc_context_save(uc_engine *uc, void *buffer);
|
uc_err uc_context_save(uc_engine *uc, uc_context *context);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Restore the current CPU context from a saved copy.
|
Restore the current CPU context from a saved copy.
|
||||||
|
@ -648,10 +680,13 @@ void *uc_context_save(uc_engine *uc, void *buffer);
|
||||||
state saved by uc_context_save().
|
state saved by uc_context_save().
|
||||||
|
|
||||||
@uc: handle returned by uc_open()
|
@uc: handle returned by uc_open()
|
||||||
@buffer: pointer returned by uc_context_save()
|
@buffer: handle returned by uc_context_alloc that has been used with uc_context_save
|
||||||
|
|
||||||
|
@return UC_ERR_OK on success, or other value on failure (refer to uc_err enum
|
||||||
|
for detailed error).
|
||||||
*/
|
*/
|
||||||
UNICORN_EXPORT
|
UNICORN_EXPORT
|
||||||
void uc_context_restore(uc_engine *uc, void *buffer);
|
uc_err uc_context_restore(uc_engine *uc, uc_context *context);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|
|
@ -742,6 +742,7 @@ static void test_x86_16(void **state)
|
||||||
static void test_i386_reg_save(void **state)
|
static void test_i386_reg_save(void **state)
|
||||||
{
|
{
|
||||||
uc_engine *uc;
|
uc_engine *uc;
|
||||||
|
uc_context *saved_regs;
|
||||||
|
|
||||||
static const uint64_t address = 0;
|
static const uint64_t address = 0;
|
||||||
static const uint8_t code[] = {
|
static const uint8_t code[] = {
|
||||||
|
@ -764,8 +765,11 @@ static void test_i386_reg_save(void **state)
|
||||||
// step one instruction
|
// step one instruction
|
||||||
uc_assert_success(uc_emu_start(uc, address, address+1, 0, 0));
|
uc_assert_success(uc_emu_start(uc, address, address+1, 0, 0));
|
||||||
|
|
||||||
|
// grab a buffer to use for state saving
|
||||||
|
uc_assert_success(uc_context_alloc(uc, &saved_regs));
|
||||||
|
|
||||||
// save the state
|
// save the state
|
||||||
void *saved_regs = uc_context_save(uc, NULL);
|
uc_assert_success(uc_context_save(uc, saved_regs));
|
||||||
|
|
||||||
// step one instruction
|
// step one instruction
|
||||||
uc_assert_success(uc_emu_start(uc, address, address+1, 0, 0));
|
uc_assert_success(uc_emu_start(uc, address, address+1, 0, 0));
|
||||||
|
@ -796,7 +800,7 @@ static void test_i386_reg_save(void **state)
|
||||||
assert_int_equal(eax, 2);
|
assert_int_equal(eax, 2);
|
||||||
|
|
||||||
// clean up;
|
// clean up;
|
||||||
free(saved_regs);
|
uc_context_free(saved_regs);
|
||||||
uc_assert_success(uc_close(uc));
|
uc_assert_success(uc_close(uc));
|
||||||
}
|
}
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
47
uc.c
47
uc.c
|
@ -1174,20 +1174,47 @@ size_t cpu_regs_size(uc_arch arch, uc_mode mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
UNICORN_EXPORT
|
UNICORN_EXPORT
|
||||||
void *uc_context_save(uc_engine *uc, void *buffer)
|
uc_err uc_context_alloc(uc_engine *uc, uc_context **context)
|
||||||
{
|
{
|
||||||
size_t sz = cpu_regs_size(uc->arch, uc->mode);
|
size_t size = cpu_regs_size(uc->arch, uc->mode);
|
||||||
if (!buffer) {
|
*context = malloc(size + sizeof(uc_context));
|
||||||
buffer = malloc(sz);
|
if (*context) {
|
||||||
|
(*context)->size = size;
|
||||||
|
(*context)->arch = uc->arch;
|
||||||
|
(*context)->mode = uc->mode;
|
||||||
|
(*context)->used = false;
|
||||||
|
return UC_ERR_OK;
|
||||||
|
} else {
|
||||||
|
return UC_ERR_NOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(buffer, uc->cpu->env_ptr, sz);
|
|
||||||
return buffer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
UNICORN_EXPORT
|
UNICORN_EXPORT
|
||||||
void uc_context_restore(uc_engine *uc, void *buffer)
|
uc_err uc_context_free(uc_context *context)
|
||||||
{
|
{
|
||||||
size_t sz = cpu_regs_size(uc->arch, uc->mode);
|
free(context);
|
||||||
memcpy(uc->cpu->env_ptr, buffer, sz);
|
return UC_ERR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
UNICORN_EXPORT
|
||||||
|
uc_err uc_context_save(uc_engine *uc, uc_context *context)
|
||||||
|
{
|
||||||
|
if (context->arch != uc->arch || context->mode != uc->mode) {
|
||||||
|
return UC_ERR_ARG;
|
||||||
|
} else {
|
||||||
|
memcpy(context->data, uc->cpu->env_ptr, context->size);
|
||||||
|
context->used = true;
|
||||||
|
return UC_ERR_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UNICORN_EXPORT
|
||||||
|
uc_err uc_context_restore(uc_engine *uc, uc_context *context)
|
||||||
|
{
|
||||||
|
if (context->arch != uc->arch || context->mode != uc->mode || !context->used) {
|
||||||
|
return UC_ERR_ARG;
|
||||||
|
} else {
|
||||||
|
memcpy(uc->cpu->env_ptr, context->data, context->size);
|
||||||
|
return UC_ERR_OK;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue