mirror of
https://github.com/yuzu-emu/unicorn.git
synced 2025-02-25 23:16:50 +00:00
Merge branch 'cseagle-mem_map_ex'
This commit is contained in:
commit
220faed3e8
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -84,3 +84,6 @@ regress/map_crash
|
||||||
regress/sigill
|
regress/sigill
|
||||||
regress/sigill2
|
regress/sigill2
|
||||||
regress/block_test
|
regress/block_test
|
||||||
|
regress/map_write
|
||||||
|
regress/ro_mem_test
|
||||||
|
|
||||||
|
|
|
@ -15,12 +15,6 @@
|
||||||
|
|
||||||
QTAILQ_HEAD(CPUTailQ, CPUState);
|
QTAILQ_HEAD(CPUTailQ, CPUState);
|
||||||
|
|
||||||
typedef struct MemoryBlock {
|
|
||||||
uint64_t begin; //inclusive
|
|
||||||
uint64_t end; //exclusive
|
|
||||||
uint32_t perms;
|
|
||||||
} MemoryBlock;
|
|
||||||
|
|
||||||
typedef struct ModuleEntry {
|
typedef struct ModuleEntry {
|
||||||
void (*init)(void);
|
void (*init)(void);
|
||||||
QTAILQ_ENTRY(ModuleEntry) node;
|
QTAILQ_ENTRY(ModuleEntry) node;
|
||||||
|
@ -51,7 +45,9 @@ typedef void (*uc_args_uc_long_t)(struct uc_struct*, unsigned long);
|
||||||
|
|
||||||
typedef void (*uc_args_uc_u64_t)(struct uc_struct *, uint64_t addr);
|
typedef void (*uc_args_uc_u64_t)(struct uc_struct *, uint64_t addr);
|
||||||
|
|
||||||
typedef int (*uc_args_uc_ram_size_t)(struct uc_struct*, ram_addr_t begin, size_t size);
|
typedef MemoryRegion* (*uc_args_uc_ram_size_t)(struct uc_struct*, ram_addr_t begin, size_t size, uint32_t perms);
|
||||||
|
|
||||||
|
typedef void (*uc_readonly_mem_t)(MemoryRegion *mr, bool readonly);
|
||||||
|
|
||||||
// which interrupt should make emulation stop?
|
// which interrupt should make emulation stop?
|
||||||
typedef bool (*uc_args_int_t)(int intno);
|
typedef bool (*uc_args_int_t)(int intno);
|
||||||
|
@ -94,6 +90,7 @@ struct uc_struct {
|
||||||
uc_args_tcg_enable_t tcg_enabled;
|
uc_args_tcg_enable_t tcg_enabled;
|
||||||
uc_args_uc_long_t tcg_exec_init;
|
uc_args_uc_long_t tcg_exec_init;
|
||||||
uc_args_uc_ram_size_t memory_map;
|
uc_args_uc_ram_size_t memory_map;
|
||||||
|
uc_readonly_mem_t readonly_mem;
|
||||||
// list of cpu
|
// list of cpu
|
||||||
void* cpu;
|
void* cpu;
|
||||||
|
|
||||||
|
@ -174,13 +171,13 @@ struct uc_struct {
|
||||||
int thumb; // thumb mode for ARM
|
int thumb; // thumb mode for ARM
|
||||||
// full TCG cache leads to middle-block break in the last translation?
|
// full TCG cache leads to middle-block break in the last translation?
|
||||||
bool block_full;
|
bool block_full;
|
||||||
MemoryBlock *mapped_blocks;
|
MemoryRegion **mapped_blocks;
|
||||||
uint32_t mapped_block_count;
|
uint32_t mapped_block_count;
|
||||||
};
|
};
|
||||||
|
|
||||||
#include "qemu_macro.h"
|
#include "qemu_macro.h"
|
||||||
|
|
||||||
// check if this address is mapped in (via uc_mem_map())
|
// check if this address is mapped in (via uc_mem_map())
|
||||||
bool memory_mapping(struct uc_struct* uc, uint64_t address);
|
MemoryRegion *memory_mapping(struct uc_struct* uc, uint64_t address);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -116,6 +116,8 @@ typedef enum uc_err {
|
||||||
UC_ERR_HOOK, // Invalid hook type: uc_hook_add()
|
UC_ERR_HOOK, // Invalid hook type: uc_hook_add()
|
||||||
UC_ERR_INSN_INVALID, // Quit emulation due to invalid instruction: uc_emu_start()
|
UC_ERR_INSN_INVALID, // Quit emulation due to invalid instruction: uc_emu_start()
|
||||||
UC_ERR_MAP, // Invalid memory mapping: uc_mem_map()
|
UC_ERR_MAP, // Invalid memory mapping: uc_mem_map()
|
||||||
|
UC_ERR_MEM_WRITE_NW, // Quit emulation due to write to non-writable: uc_emu_start()
|
||||||
|
UC_ERR_MEM_READ_NR, // Quit emulation due to read from non-readable: uc_emu_start()
|
||||||
} uc_err;
|
} uc_err;
|
||||||
|
|
||||||
|
|
||||||
|
@ -147,6 +149,8 @@ typedef enum uc_mem_type {
|
||||||
UC_MEM_READ = 16, // Memory is read from
|
UC_MEM_READ = 16, // Memory is read from
|
||||||
UC_MEM_WRITE, // Memory is written to
|
UC_MEM_WRITE, // Memory is written to
|
||||||
UC_MEM_READ_WRITE, // Memory is accessed (either READ or WRITE)
|
UC_MEM_READ_WRITE, // Memory is accessed (either READ or WRITE)
|
||||||
|
UC_MEM_WRITE_NW, // write to non-writable
|
||||||
|
UC_MEM_READ_NR, // read from non-readable
|
||||||
} uc_mem_type;
|
} uc_mem_type;
|
||||||
|
|
||||||
// All type of hooks for uc_hook_add() API.
|
// All type of hooks for uc_hook_add() API.
|
||||||
|
@ -387,12 +391,12 @@ uc_err uc_hook_del(uch handle, uch *h2);
|
||||||
typedef enum uc_prot {
|
typedef enum uc_prot {
|
||||||
UC_PROT_READ = 1,
|
UC_PROT_READ = 1,
|
||||||
UC_PROT_WRITE = 2,
|
UC_PROT_WRITE = 2,
|
||||||
UC_PROT_EXEC = 4
|
|
||||||
} uc_prot;
|
} uc_prot;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Map memory in for emulation.
|
Map memory in for emulation.
|
||||||
This API adds a memory region that can be used by emulation.
|
This API adds a memory region that can be used by emulation. The region is mapped
|
||||||
|
with permissions UC_PROT_READ | UC_PROT_WRITE.
|
||||||
|
|
||||||
@handle: handle returned by uc_open()
|
@handle: handle returned by uc_open()
|
||||||
@address: starting address of the new memory region to be mapped in.
|
@address: starting address of the new memory region to be mapped in.
|
||||||
|
@ -406,6 +410,25 @@ typedef enum uc_prot {
|
||||||
UNICORN_EXPORT
|
UNICORN_EXPORT
|
||||||
uc_err uc_mem_map(uch handle, uint64_t address, size_t size);
|
uc_err uc_mem_map(uch handle, uint64_t address, size_t size);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Map memory in for emulation.
|
||||||
|
This API adds a memory region that can be used by emulation.
|
||||||
|
|
||||||
|
@handle: handle returned by uc_open()
|
||||||
|
@address: starting address of the new memory region to be mapped in.
|
||||||
|
This address must be aligned to 4KB, or this will return with UC_ERR_MAP error.
|
||||||
|
@size: size of the new memory region to be mapped in.
|
||||||
|
This size must be multiple of 4KB, or this will return with UC_ERR_MAP error.
|
||||||
|
@perms: Permissions for the newly mapped region.
|
||||||
|
This must be some combination of UC_PROT_READ | UC_PROT_WRITE,
|
||||||
|
or this will return with UC_ERR_MAP error.
|
||||||
|
|
||||||
|
@return UC_ERR_OK on success, or other value on failure (refer to uc_err enum
|
||||||
|
for detailed error).
|
||||||
|
*/
|
||||||
|
UNICORN_EXPORT
|
||||||
|
uc_err uc_mem_map_ex(uch handle, uint64_t address, size_t size, uint32_t perms);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -170,6 +170,8 @@ struct MemoryRegion {
|
||||||
MemoryRegionIoeventfd *ioeventfds;
|
MemoryRegionIoeventfd *ioeventfds;
|
||||||
NotifierList iommu_notify;
|
NotifierList iommu_notify;
|
||||||
struct uc_struct *uc;
|
struct uc_struct *uc;
|
||||||
|
uint32_t perms; //all perms, partially redundant with readonly
|
||||||
|
uint64_t end;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -315,12 +317,14 @@ void memory_region_init_io(struct uc_struct *uc, MemoryRegion *mr,
|
||||||
* @owner: the object that tracks the region's reference count
|
* @owner: the object that tracks the region's reference count
|
||||||
* @name: the name of the region.
|
* @name: the name of the region.
|
||||||
* @size: size of the region.
|
* @size: size of the region.
|
||||||
|
* @perms: permissions on the region (UC_PROT_READ, UC_PROT_WRITE, UC_PROT_EXEC).
|
||||||
* @errp: pointer to Error*, to store an error if it happens.
|
* @errp: pointer to Error*, to store an error if it happens.
|
||||||
*/
|
*/
|
||||||
void memory_region_init_ram(struct uc_struct *uc, MemoryRegion *mr,
|
void memory_region_init_ram(struct uc_struct *uc, MemoryRegion *mr,
|
||||||
struct Object *owner,
|
struct Object *owner,
|
||||||
const char *name,
|
const char *name,
|
||||||
uint64_t size,
|
uint64_t size,
|
||||||
|
uint32_t perms,
|
||||||
Error **errp);
|
Error **errp);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -934,7 +938,7 @@ void address_space_unmap(AddressSpace *as, void *buffer, hwaddr len,
|
||||||
|
|
||||||
void memory_register_types(struct uc_struct *uc);
|
void memory_register_types(struct uc_struct *uc);
|
||||||
|
|
||||||
int memory_map(struct uc_struct *uc, ram_addr_t begin, size_t size);
|
MemoryRegion *memory_map(struct uc_struct *uc, ram_addr_t begin, size_t size, uint32_t perms);
|
||||||
int memory_free(struct uc_struct *uc);
|
int memory_free(struct uc_struct *uc);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -31,27 +31,28 @@
|
||||||
|
|
||||||
|
|
||||||
// Unicorn engine
|
// Unicorn engine
|
||||||
int memory_map(struct uc_struct *uc, ram_addr_t begin, size_t size)
|
MemoryRegion *memory_map(struct uc_struct *uc, ram_addr_t begin, size_t size, uint32_t perms)
|
||||||
{
|
{
|
||||||
uc->ram = g_new(MemoryRegion, 1);
|
uc->ram = g_new(MemoryRegion, 1);
|
||||||
|
|
||||||
memory_region_init_ram(uc, uc->ram, NULL, "pc.ram", size, &error_abort);
|
memory_region_init_ram(uc, uc->ram, NULL, "pc.ram", size, perms, &error_abort);
|
||||||
|
|
||||||
memory_region_add_subregion(get_system_memory(uc), begin, uc->ram);
|
memory_region_add_subregion(get_system_memory(uc), begin, uc->ram);
|
||||||
|
|
||||||
if (uc->current_cpu)
|
if (uc->current_cpu)
|
||||||
tlb_flush(uc->current_cpu, 1);
|
tlb_flush(uc->current_cpu, 1);
|
||||||
|
|
||||||
return 0;
|
return uc->ram;
|
||||||
}
|
}
|
||||||
|
|
||||||
int memory_free(struct uc_struct *uc)
|
int memory_free(struct uc_struct *uc)
|
||||||
{
|
{
|
||||||
|
int i;
|
||||||
get_system_memory(uc)->enabled = false;
|
get_system_memory(uc)->enabled = false;
|
||||||
if (uc->ram) {
|
for (i = 0; i < uc->mapped_block_count; i++) {
|
||||||
uc->ram->enabled = false;
|
uc->mapped_blocks[i]->enabled = false;
|
||||||
memory_region_del_subregion(get_system_memory(uc), uc->ram);
|
memory_region_del_subregion(get_system_memory(uc), uc->mapped_blocks[i]);
|
||||||
g_free(uc->ram);
|
g_free(uc->mapped_blocks[i]);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1151,10 +1152,15 @@ void memory_region_init_ram(struct uc_struct *uc, MemoryRegion *mr,
|
||||||
Object *owner,
|
Object *owner,
|
||||||
const char *name,
|
const char *name,
|
||||||
uint64_t size,
|
uint64_t size,
|
||||||
|
uint32_t perms,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
memory_region_init(uc, mr, owner, name, size);
|
memory_region_init(uc, mr, owner, name, size);
|
||||||
mr->ram = true;
|
mr->ram = true;
|
||||||
|
if (!(perms & UC_PROT_WRITE)) {
|
||||||
|
mr->readonly = true;
|
||||||
|
}
|
||||||
|
mr->perms = perms;
|
||||||
mr->terminates = true;
|
mr->terminates = true;
|
||||||
mr->destructor = memory_region_destructor_ram;
|
mr->destructor = memory_region_destructor_ram;
|
||||||
mr->ram_addr = qemu_ram_alloc(size, mr, errp);
|
mr->ram_addr = qemu_ram_alloc(size, mr, errp);
|
||||||
|
@ -1309,6 +1315,12 @@ void memory_region_set_readonly(MemoryRegion *mr, bool readonly)
|
||||||
if (mr->readonly != readonly) {
|
if (mr->readonly != readonly) {
|
||||||
memory_region_transaction_begin(mr->uc);
|
memory_region_transaction_begin(mr->uc);
|
||||||
mr->readonly = readonly;
|
mr->readonly = readonly;
|
||||||
|
if (readonly) {
|
||||||
|
mr->perms &= ~UC_PROT_WRITE;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
mr->perms |= UC_PROT_WRITE;
|
||||||
|
}
|
||||||
mr->uc->memory_region_update_pending |= mr->enabled;
|
mr->uc->memory_region_update_pending |= mr->enabled;
|
||||||
memory_region_transaction_commit(mr->uc);
|
memory_region_transaction_commit(mr->uc);
|
||||||
}
|
}
|
||||||
|
@ -1528,6 +1540,7 @@ static void memory_region_add_subregion_common(MemoryRegion *mr,
|
||||||
assert(!subregion->container);
|
assert(!subregion->container);
|
||||||
subregion->container = mr;
|
subregion->container = mr;
|
||||||
subregion->addr = offset;
|
subregion->addr = offset;
|
||||||
|
subregion->end = offset + int128_get64(subregion->size);
|
||||||
memory_region_update_container_subregions(subregion);
|
memory_region_update_container_subregions(subregion);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -178,6 +178,9 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx,
|
||||||
uintptr_t haddr;
|
uintptr_t haddr;
|
||||||
DATA_TYPE res;
|
DATA_TYPE res;
|
||||||
|
|
||||||
|
struct uc_struct *uc = env->uc;
|
||||||
|
MemoryRegion *mr = memory_mapping(uc, addr);
|
||||||
|
|
||||||
// Unicorn: callback on memory read
|
// Unicorn: callback on memory read
|
||||||
if (env->uc->hook_mem_read && READ_ACCESS_TYPE == MMU_DATA_LOAD) {
|
if (env->uc->hook_mem_read && READ_ACCESS_TYPE == MMU_DATA_LOAD) {
|
||||||
struct hook_struct *trace = hook_find((uch)env->uc, UC_MEM_READ, addr);
|
struct hook_struct *trace = hook_find((uch)env->uc, UC_MEM_READ, addr);
|
||||||
|
@ -188,7 +191,7 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unicorn: callback on invalid memory
|
// Unicorn: callback on invalid memory
|
||||||
if (env->uc->hook_mem_idx && !memory_mapping(env->uc, addr)) {
|
if (env->uc->hook_mem_idx && mr == NULL) {
|
||||||
if (!((uc_cb_eventmem_t)env->uc->hook_callbacks[env->uc->hook_mem_idx].callback)(
|
if (!((uc_cb_eventmem_t)env->uc->hook_callbacks[env->uc->hook_mem_idx].callback)(
|
||||||
(uch)env->uc, UC_MEM_READ, addr, DATA_SIZE, 0,
|
(uch)env->uc, UC_MEM_READ, addr, DATA_SIZE, 0,
|
||||||
env->uc->hook_callbacks[env->uc->hook_mem_idx].user_data)) {
|
env->uc->hook_callbacks[env->uc->hook_mem_idx].user_data)) {
|
||||||
|
@ -203,6 +206,26 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Unicorn: callback on read only memory
|
||||||
|
if (mr != NULL && !(mr->perms & UC_PROT_READ)) { //non-readable
|
||||||
|
bool result = false;
|
||||||
|
if (uc->hook_mem_idx) {
|
||||||
|
result = ((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_idx].callback)(
|
||||||
|
(uch)uc, UC_MEM_READ_NR, addr, DATA_SIZE, 0,
|
||||||
|
uc->hook_callbacks[uc->hook_mem_idx].user_data);
|
||||||
|
}
|
||||||
|
if (result) {
|
||||||
|
env->invalid_error = UC_ERR_OK;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
env->invalid_addr = addr;
|
||||||
|
env->invalid_error = UC_ERR_MEM_READ_NR;
|
||||||
|
// printf("***** Invalid memory read (non-readable) at " TARGET_FMT_lx "\n", addr);
|
||||||
|
cpu_exit(uc->current_cpu);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Adjust the given return address. */
|
/* Adjust the given return address. */
|
||||||
retaddr -= GETPC_ADJ;
|
retaddr -= GETPC_ADJ;
|
||||||
|
|
||||||
|
@ -300,6 +323,9 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx,
|
||||||
uintptr_t haddr;
|
uintptr_t haddr;
|
||||||
DATA_TYPE res;
|
DATA_TYPE res;
|
||||||
|
|
||||||
|
struct uc_struct *uc = env->uc;
|
||||||
|
MemoryRegion *mr = memory_mapping(uc, addr);
|
||||||
|
|
||||||
// Unicorn: callback on memory read
|
// Unicorn: callback on memory read
|
||||||
if (env->uc->hook_mem_read && READ_ACCESS_TYPE == MMU_DATA_LOAD) {
|
if (env->uc->hook_mem_read && READ_ACCESS_TYPE == MMU_DATA_LOAD) {
|
||||||
struct hook_struct *trace = hook_find((uch)env->uc, UC_MEM_READ, addr);
|
struct hook_struct *trace = hook_find((uch)env->uc, UC_MEM_READ, addr);
|
||||||
|
@ -310,7 +336,7 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unicorn: callback on invalid memory
|
// Unicorn: callback on invalid memory
|
||||||
if (env->uc->hook_mem_idx && !memory_mapping(env->uc, addr)) {
|
if (env->uc->hook_mem_idx && mr == NULL) {
|
||||||
if (!((uc_cb_eventmem_t)env->uc->hook_callbacks[env->uc->hook_mem_idx].callback)(
|
if (!((uc_cb_eventmem_t)env->uc->hook_callbacks[env->uc->hook_mem_idx].callback)(
|
||||||
(uch)env->uc, UC_MEM_READ, addr, DATA_SIZE, 0,
|
(uch)env->uc, UC_MEM_READ, addr, DATA_SIZE, 0,
|
||||||
env->uc->hook_callbacks[env->uc->hook_mem_idx].user_data)) {
|
env->uc->hook_callbacks[env->uc->hook_mem_idx].user_data)) {
|
||||||
|
@ -325,6 +351,26 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Unicorn: callback on read only memory
|
||||||
|
if (mr != NULL && !(mr->perms & UC_PROT_READ)) { //non-readable
|
||||||
|
bool result = false;
|
||||||
|
if (uc->hook_mem_idx) {
|
||||||
|
result = ((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_idx].callback)(
|
||||||
|
(uch)uc, UC_MEM_READ_NR, addr, DATA_SIZE, 0,
|
||||||
|
uc->hook_callbacks[uc->hook_mem_idx].user_data);
|
||||||
|
}
|
||||||
|
if (result) {
|
||||||
|
env->invalid_error = UC_ERR_OK;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
env->invalid_addr = addr;
|
||||||
|
env->invalid_error = UC_ERR_MEM_READ_NR;
|
||||||
|
// printf("***** Invalid memory read (non-readable) at " TARGET_FMT_lx "\n", addr);
|
||||||
|
cpu_exit(uc->current_cpu);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Adjust the given return address. */
|
/* Adjust the given return address. */
|
||||||
retaddr -= GETPC_ADJ;
|
retaddr -= GETPC_ADJ;
|
||||||
|
|
||||||
|
@ -460,31 +506,53 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
|
||||||
target_ulong tlb_addr = env->tlb_table[mmu_idx][index].addr_write;
|
target_ulong tlb_addr = env->tlb_table[mmu_idx][index].addr_write;
|
||||||
uintptr_t haddr;
|
uintptr_t haddr;
|
||||||
|
|
||||||
|
struct uc_struct *uc = env->uc;
|
||||||
|
MemoryRegion *mr = memory_mapping(uc, addr);
|
||||||
|
|
||||||
// Unicorn: callback on memory write
|
// Unicorn: callback on memory write
|
||||||
if (env->uc->hook_mem_write) {
|
if (uc->hook_mem_write) {
|
||||||
struct hook_struct *trace = hook_find((uch)env->uc, UC_MEM_WRITE, addr);
|
struct hook_struct *trace = hook_find((uch)uc, UC_MEM_WRITE, addr);
|
||||||
if (trace) {
|
if (trace) {
|
||||||
((uc_cb_hookmem_t)trace->callback)((uch)env->uc, UC_MEM_WRITE,
|
((uc_cb_hookmem_t)trace->callback)((uch)uc, UC_MEM_WRITE,
|
||||||
(uint64_t)addr, (int)DATA_SIZE, (int64_t)val, trace->user_data);
|
(uint64_t)addr, (int)DATA_SIZE, (int64_t)val, trace->user_data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unicorn: callback on invalid memory
|
// Unicorn: callback on invalid memory
|
||||||
if (env->uc->hook_mem_idx && !memory_mapping(env->uc, addr)) {
|
if (uc->hook_mem_idx && mr == NULL) {
|
||||||
if (!((uc_cb_eventmem_t)env->uc->hook_callbacks[env->uc->hook_mem_idx].callback)(
|
if (!((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_idx].callback)(
|
||||||
(uch)env->uc, UC_MEM_WRITE, addr, DATA_SIZE, (int64_t)val,
|
(uch)uc, UC_MEM_WRITE, addr, DATA_SIZE, (int64_t)val,
|
||||||
env->uc->hook_callbacks[env->uc->hook_mem_idx].user_data)) {
|
uc->hook_callbacks[uc->hook_mem_idx].user_data)) {
|
||||||
// save error & quit
|
// save error & quit
|
||||||
env->invalid_addr = addr;
|
env->invalid_addr = addr;
|
||||||
env->invalid_error = UC_ERR_MEM_WRITE;
|
env->invalid_error = UC_ERR_MEM_WRITE;
|
||||||
// printf("***** Invalid memory write at " TARGET_FMT_lx "\n", addr);
|
// printf("***** Invalid memory write at " TARGET_FMT_lx "\n", addr);
|
||||||
cpu_exit(env->uc->current_cpu);
|
cpu_exit(uc->current_cpu);
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
env->invalid_error = UC_ERR_OK;
|
env->invalid_error = UC_ERR_OK;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Unicorn: callback on read only memory
|
||||||
|
if (mr != NULL && !(mr->perms & UC_PROT_WRITE)) { //read only memory
|
||||||
|
bool result = false;
|
||||||
|
if (uc->hook_mem_idx) {
|
||||||
|
result = ((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_idx].callback)(
|
||||||
|
(uch)uc, UC_MEM_WRITE_NW, addr, DATA_SIZE, (int64_t)val,
|
||||||
|
uc->hook_callbacks[uc->hook_mem_idx].user_data);
|
||||||
|
}
|
||||||
|
if (result) {
|
||||||
|
env->invalid_error = UC_ERR_OK;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
env->invalid_addr = addr;
|
||||||
|
env->invalid_error = UC_ERR_MEM_WRITE_NW;
|
||||||
|
// printf("***** Invalid memory write (ro) at " TARGET_FMT_lx "\n", addr);
|
||||||
|
cpu_exit(uc->current_cpu);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Adjust the given return address. */
|
/* Adjust the given return address. */
|
||||||
retaddr -= GETPC_ADJ;
|
retaddr -= GETPC_ADJ;
|
||||||
|
@ -546,6 +614,8 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
|
||||||
Undo that for the recursion. */
|
Undo that for the recursion. */
|
||||||
glue(helper_ret_stb, MMUSUFFIX)(env, addr + i, val8,
|
glue(helper_ret_stb, MMUSUFFIX)(env, addr + i, val8,
|
||||||
mmu_idx, retaddr + GETPC_ADJ);
|
mmu_idx, retaddr + GETPC_ADJ);
|
||||||
|
if (env->invalid_error != UC_ERR_OK)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -574,31 +644,54 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
|
||||||
target_ulong tlb_addr = env->tlb_table[mmu_idx][index].addr_write;
|
target_ulong tlb_addr = env->tlb_table[mmu_idx][index].addr_write;
|
||||||
uintptr_t haddr;
|
uintptr_t haddr;
|
||||||
|
|
||||||
|
struct uc_struct *uc = env->uc;
|
||||||
|
MemoryRegion *mr = memory_mapping(uc, addr);
|
||||||
|
|
||||||
// Unicorn: callback on memory write
|
// Unicorn: callback on memory write
|
||||||
if (env->uc->hook_mem_write) {
|
if (uc->hook_mem_write) {
|
||||||
struct hook_struct *trace = hook_find((uch)env->uc, UC_MEM_WRITE, addr);
|
struct hook_struct *trace = hook_find((uch)uc, UC_MEM_WRITE, addr);
|
||||||
if (trace) {
|
if (trace) {
|
||||||
((uc_cb_hookmem_t)trace->callback)((uch)env->uc, UC_MEM_WRITE,
|
((uc_cb_hookmem_t)trace->callback)((uch)uc, UC_MEM_WRITE,
|
||||||
(uint64_t)addr, (int)DATA_SIZE, (int64_t)val, trace->user_data);
|
(uint64_t)addr, (int)DATA_SIZE, (int64_t)val, trace->user_data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unicorn: callback on invalid memory
|
// Unicorn: callback on invalid memory
|
||||||
if (env->uc->hook_mem_idx && !memory_mapping(env->uc, addr)) {
|
if (uc->hook_mem_idx && mr == NULL) {
|
||||||
if (!((uc_cb_eventmem_t)env->uc->hook_callbacks[env->uc->hook_mem_idx].callback)(
|
if (!((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_idx].callback)(
|
||||||
(uch)env->uc, UC_MEM_WRITE, addr, DATA_SIZE, (int64_t)val,
|
(uch)uc, UC_MEM_WRITE, addr, DATA_SIZE, (int64_t)val,
|
||||||
env->uc->hook_callbacks[env->uc->hook_mem_idx].user_data)) {
|
uc->hook_callbacks[uc->hook_mem_idx].user_data)) {
|
||||||
// save error & quit
|
// save error & quit
|
||||||
env->invalid_addr = addr;
|
env->invalid_addr = addr;
|
||||||
env->invalid_error = UC_ERR_MEM_WRITE;
|
env->invalid_error = UC_ERR_MEM_WRITE;
|
||||||
// printf("***** Invalid memory write at " TARGET_FMT_lx "\n", addr);
|
// printf("***** Invalid memory write at " TARGET_FMT_lx "\n", addr);
|
||||||
cpu_exit(env->uc->current_cpu);
|
cpu_exit(uc->current_cpu);
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
env->invalid_error = UC_ERR_OK;
|
env->invalid_error = UC_ERR_OK;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Unicorn: callback on read only memory
|
||||||
|
if (mr != NULL && !(mr->perms & UC_PROT_WRITE)) { //read only memory
|
||||||
|
bool result = false;
|
||||||
|
if (uc->hook_mem_idx) {
|
||||||
|
result = ((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_idx].callback)(
|
||||||
|
(uch)uc, UC_MEM_WRITE_NW, addr, DATA_SIZE, (int64_t)val,
|
||||||
|
uc->hook_callbacks[uc->hook_mem_idx].user_data);
|
||||||
|
}
|
||||||
|
if (result) {
|
||||||
|
env->invalid_error = UC_ERR_OK;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
env->invalid_addr = addr;
|
||||||
|
env->invalid_error = UC_ERR_MEM_WRITE_NW;
|
||||||
|
// printf("***** Invalid memory write (ro) at " TARGET_FMT_lx "\n", addr);
|
||||||
|
cpu_exit(uc->current_cpu);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Adjust the given return address. */
|
/* Adjust the given return address. */
|
||||||
retaddr -= GETPC_ADJ;
|
retaddr -= GETPC_ADJ;
|
||||||
|
|
||||||
|
@ -659,6 +752,8 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
|
||||||
Undo that for the recursion. */
|
Undo that for the recursion. */
|
||||||
glue(helper_ret_stb, MMUSUFFIX)(env, addr + i, val8,
|
glue(helper_ret_stb, MMUSUFFIX)(env, addr + i, val8,
|
||||||
mmu_idx, retaddr + GETPC_ADJ);
|
mmu_idx, retaddr + GETPC_ADJ);
|
||||||
|
if (env->invalid_error != UC_ERR_OK)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,6 +73,7 @@ static inline void uc_common_init(struct uc_struct* uc)
|
||||||
uc->pause_all_vcpus = pause_all_vcpus;
|
uc->pause_all_vcpus = pause_all_vcpus;
|
||||||
uc->vm_start = vm_start;
|
uc->vm_start = vm_start;
|
||||||
uc->memory_map = memory_map;
|
uc->memory_map = memory_map;
|
||||||
|
uc->readonly_mem = memory_region_set_readonly;
|
||||||
|
|
||||||
if (!uc->release)
|
if (!uc->release)
|
||||||
uc->release = release_common;
|
uc->release = release_common;
|
||||||
|
|
|
@ -4,6 +4,7 @@ LDFLAGS = -L.. -lunicorn
|
||||||
TESTS = map_crash map_write
|
TESTS = map_crash map_write
|
||||||
TESTS += sigill sigill2
|
TESTS += sigill sigill2
|
||||||
TESTS += block_test
|
TESTS += block_test
|
||||||
|
TESTS += ro_mem_test nr_mem_test
|
||||||
|
|
||||||
all: $(TESTS)
|
all: $(TESTS)
|
||||||
|
|
||||||
|
|
|
@ -4,9 +4,10 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#define UC_BUG_WRITE_SIZE 13000
|
#define UC_BUG_WRITE_SIZE 13000
|
||||||
#define UC_BUG_WRITE_ADDR 0x1000 // fix this by change this to 0x2000
|
#define UC_BUG_WRITE_ADDR 0x1000
|
||||||
|
|
||||||
int main() {
|
int main()
|
||||||
|
{
|
||||||
int size;
|
int size;
|
||||||
uint8_t *buf;
|
uint8_t *buf;
|
||||||
uch uh;
|
uch uh;
|
||||||
|
|
|
@ -6,9 +6,10 @@
|
||||||
#define SIZE 1024*64
|
#define SIZE 1024*64
|
||||||
#define OVERFLOW 1
|
#define OVERFLOW 1
|
||||||
|
|
||||||
int main() {
|
int main()
|
||||||
|
{
|
||||||
uch uh;
|
uch uh;
|
||||||
char *buf, *buf2;
|
uint8_t *buf, *buf2;
|
||||||
int i;
|
int i;
|
||||||
uc_err err;
|
uc_err err;
|
||||||
|
|
||||||
|
|
217
regress/ro_mem_test.c
Normal file
217
regress/ro_mem_test.c
Normal file
|
@ -0,0 +1,217 @@
|
||||||
|
/*
|
||||||
|
|
||||||
|
non-writable memory test case
|
||||||
|
|
||||||
|
Copyright(c) 2015 Chris Eagle
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License
|
||||||
|
version 2 as published by the Free Software Foundation.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <unicorn/unicorn.h>
|
||||||
|
|
||||||
|
const uint8_t PROGRAM[] =
|
||||||
|
"\xeb\x1a\x58\x83\xc0\x04\x83\xe0\xfc\x83\xc0\x01\xc7\x00\x78\x56"
|
||||||
|
"\x34\x12\x83\xc0\x07\xc7\x00\x21\x43\x65\x87\x90\xe8\xe1\xff\xff"
|
||||||
|
"\xff" "xxxxAAAAxxxBBBB";
|
||||||
|
// total size: 33 bytes
|
||||||
|
|
||||||
|
/*
|
||||||
|
jmp short bottom
|
||||||
|
top:
|
||||||
|
pop eax
|
||||||
|
add eax, 4
|
||||||
|
and eax, 0xfffffffc
|
||||||
|
add eax, 1 ; unaligned
|
||||||
|
mov dword [eax], 0x12345678 ; try to write into code section
|
||||||
|
add eax, 7 ; aligned
|
||||||
|
mov dword [eax], 0x87654321 ; try to write into code section
|
||||||
|
nop
|
||||||
|
bottom:
|
||||||
|
call top
|
||||||
|
*/
|
||||||
|
|
||||||
|
// callback for tracing instruction
|
||||||
|
static void hook_code(uch handle, uint64_t address, uint32_t size, void *user_data)
|
||||||
|
{
|
||||||
|
uint32_t esp;
|
||||||
|
printf(">>> Tracing instruction at 0x%"PRIx64 ", instruction size = 0x%x\n", address, size);
|
||||||
|
|
||||||
|
uc_reg_read(handle, UC_X86_REG_ESP, &esp);
|
||||||
|
printf(">>> --- ESP is 0x%x\n", esp);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// callback for tracing memory access (READ or WRITE)
|
||||||
|
static bool hook_mem_invalid(uch handle, uc_mem_type type,
|
||||||
|
uint64_t address, int size, int64_t value, void *user_data)
|
||||||
|
{
|
||||||
|
uint32_t esp;
|
||||||
|
uc_reg_read(handle, UC_X86_REG_ESP, &esp);
|
||||||
|
|
||||||
|
switch(type) {
|
||||||
|
default:
|
||||||
|
// return false to indicate we want to stop emulation
|
||||||
|
return false;
|
||||||
|
case UC_MEM_WRITE:
|
||||||
|
//if this is a push, esp has not been adjusted yet
|
||||||
|
if (esp == (address + size)) {
|
||||||
|
uint32_t upper;
|
||||||
|
upper = (esp + 0xfff) & ~0xfff;
|
||||||
|
printf(">>> Stack appears to be missing at 0x%"PRIx64 ", allocating now\n", address);
|
||||||
|
// map this memory in with 2MB in size
|
||||||
|
uc_mem_map_ex(handle, upper - 0x8000, 0x8000, UC_PROT_READ | UC_PROT_WRITE);
|
||||||
|
// return true to indicate we want to continue
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
printf(">>> Missing memory is being WRITTEN at 0x%"PRIx64 ", data size = %u, data value = 0x%"PRIx64 "\n",
|
||||||
|
address, size, value);
|
||||||
|
return false;
|
||||||
|
case UC_MEM_WRITE_NR:
|
||||||
|
printf(">>> RO memory is being WRITTEN at 0x%"PRIx64 ", data size = %u, data value = 0x%"PRIx64 "\n",
|
||||||
|
address, size, value);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#define STACK 0x500000
|
||||||
|
#define STACK_SIZE 0x5000
|
||||||
|
|
||||||
|
int main(int argc, char **argv, char **envp)
|
||||||
|
{
|
||||||
|
uch handle, trace1, trace2;
|
||||||
|
uc_err err;
|
||||||
|
uint8_t bytes[8];
|
||||||
|
uint32_t esp;
|
||||||
|
int result;
|
||||||
|
int map_stack = 0;
|
||||||
|
|
||||||
|
if (argc == 2 && strcmp(argv[1], "--map-stack") == 0) {
|
||||||
|
map_stack = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Memory mapping test\n");
|
||||||
|
|
||||||
|
// Initialize emulator in X86-32bit mode
|
||||||
|
err = uc_open(UC_ARCH_X86, UC_MODE_32, &handle);
|
||||||
|
if (err) {
|
||||||
|
printf("Failed on uc_open() with error returned: %u\n", err);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uc_mem_map(handle, 0x100000, 0x1000);
|
||||||
|
uc_mem_map(handle, 0x200000, 0x2000);
|
||||||
|
uc_mem_map(handle, 0x300000, 0x3000);
|
||||||
|
uc_mem_map_ex(handle, 0x400000, 0x4000, UC_PROT_READ);
|
||||||
|
|
||||||
|
if (map_stack) {
|
||||||
|
printf("Pre-mapping stack\n");
|
||||||
|
uc_mem_map_ex(handle, STACK, STACK_SIZE, UC_PROT_READ | UC_PROT_WRITE);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
printf("Mapping stack on first invalid memory access\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
esp = STACK + STACK_SIZE;
|
||||||
|
|
||||||
|
uc_reg_write(handle, UC_X86_REG_ESP, &esp);
|
||||||
|
|
||||||
|
// write machine code to be emulated to memory
|
||||||
|
if (uc_mem_write(handle, 0x400000, PROGRAM, sizeof(PROGRAM))) {
|
||||||
|
printf("Failed to write emulation code to memory, quit!\n");
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
printf("Allowed to write to read only memory via uc_mem_write\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
//uc_hook_add(handle, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)0x400000, (uint64_t)0x400fff);
|
||||||
|
|
||||||
|
// intercept invalid memory events
|
||||||
|
uc_hook_add(handle, &trace1, UC_HOOK_MEM_INVALID, hook_mem_invalid, NULL);
|
||||||
|
|
||||||
|
// emulate machine code in infinite time
|
||||||
|
printf("BEGIN execution - 1\n");
|
||||||
|
err = uc_emu_start(handle, 0x400000, 0x400000 + sizeof(PROGRAM), 0, 10);
|
||||||
|
if (err) {
|
||||||
|
printf("Expected failue on uc_emu_start() with error returned %u: %s\n",
|
||||||
|
err, uc_strerror(err));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
printf("UNEXPECTED uc_emu_start returned UC_ERR_OK\n");
|
||||||
|
}
|
||||||
|
printf("END execution - 1\n");
|
||||||
|
|
||||||
|
// emulate machine code in infinite time
|
||||||
|
printf("BEGIN execution - 2\n");
|
||||||
|
uint32_t eax = 0x40002C;
|
||||||
|
uc_reg_write(handle, UC_X86_REG_EAX, &eax);
|
||||||
|
err = uc_emu_start(handle, 0x400015, 0x400000 + sizeof(PROGRAM), 0, 2);
|
||||||
|
if (err) {
|
||||||
|
printf("Expected failure on uc_emu_start() with error returned %u: %s\n",
|
||||||
|
err, uc_strerror(err));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
printf("UNEXPECTED uc_emu_start returned UC_ERR_OK\n");
|
||||||
|
}
|
||||||
|
printf("END execution - 2\n");
|
||||||
|
|
||||||
|
printf("Verifying content at 0x400025 is unchanged\n");
|
||||||
|
if (!uc_mem_read(handle, 0x400025, bytes, 4)) {
|
||||||
|
printf(">>> Read 4 bytes from [0x%x] = 0x%x\n", (uint32_t)0x400025, *(uint32_t*) bytes);
|
||||||
|
if (0x41414141 != *(uint32_t*) bytes) {
|
||||||
|
printf("ERROR content in read only memory changed\n");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
printf("SUCCESS content in read only memory unchanged\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
printf(">>> Failed to read 4 bytes from [0x%x]\n", (uint32_t)(esp - 4));
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Verifying content at 0x40002C is unchanged\n");
|
||||||
|
if (!uc_mem_read(handle, 0x40002C, bytes, 4)) {
|
||||||
|
printf(">>> Read 4 bytes from [0x%x] = 0x%x\n", (uint32_t)0x40002C, *(uint32_t*) bytes);
|
||||||
|
if (0x42424242 != *(uint32_t*) bytes) {
|
||||||
|
printf("ERROR content in read only memory changed\n");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
printf("SUCCESS content in read only memory unchanged\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
printf(">>> Failed to read 4 bytes from [0x%x]\n", (uint32_t)(esp - 4));
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Verifying content at bottom of stack is readable and correct\n");
|
||||||
|
if (!uc_mem_read(handle, esp - 4, bytes, 4)) {
|
||||||
|
printf(">>> Read 4 bytes from [0x%x] = 0x%x\n", (uint32_t)(esp - 4), *(uint32_t*) bytes);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
printf(">>> Failed to read 4 bytes from [0x%x]\n", (uint32_t)(esp - 4));
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
uc_close(&handle);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -8,14 +8,16 @@
|
||||||
|
|
||||||
int got_sigill = 0;
|
int got_sigill = 0;
|
||||||
|
|
||||||
void _interrupt(uch handle, uint32_t intno, void *user_data) {
|
void _interrupt(uch handle, uint32_t intno, void *user_data)
|
||||||
|
{
|
||||||
if (intno == 6) {
|
if (intno == 6) {
|
||||||
uc_emu_stop (handle);
|
uc_emu_stop (handle);
|
||||||
got_sigill = 1;
|
got_sigill = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int main() {
|
int main()
|
||||||
|
{
|
||||||
int size;
|
int size;
|
||||||
uint8_t *buf;
|
uint8_t *buf;
|
||||||
uch uh;
|
uch uh;
|
||||||
|
|
55
uc.c
55
uc.c
|
@ -89,6 +89,10 @@ const char *uc_strerror(uc_err code)
|
||||||
return "Invalid hook type (UC_ERR_HOOK)";
|
return "Invalid hook type (UC_ERR_HOOK)";
|
||||||
case UC_ERR_MAP:
|
case UC_ERR_MAP:
|
||||||
return "Invalid memory mapping (UC_ERR_MAP)";
|
return "Invalid memory mapping (UC_ERR_MAP)";
|
||||||
|
case UC_ERR_MEM_WRITE_NW:
|
||||||
|
return "Write to non-writable (UC_ERR_MEM_WRITE_NW)";
|
||||||
|
case UC_ERR_MEM_READ_NR:
|
||||||
|
return "Read from non-readable (UC_ERR_MEM_READ_NR)";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -351,19 +355,32 @@ uc_err uc_mem_read(uch handle, uint64_t address, uint8_t *bytes, size_t size)
|
||||||
return UC_ERR_OK;
|
return UC_ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
UNICORN_EXPORT
|
UNICORN_EXPORT
|
||||||
uc_err uc_mem_write(uch handle, uint64_t address, const uint8_t *bytes, size_t size)
|
uc_err uc_mem_write(uch handle, uint64_t address, const uint8_t *bytes, size_t size)
|
||||||
{
|
{
|
||||||
struct uc_struct *uc = (struct uc_struct *)(uintptr_t)handle;
|
struct uc_struct *uc = (struct uc_struct *)(uintptr_t)handle;
|
||||||
|
uint32_t operms;
|
||||||
|
|
||||||
if (handle == 0)
|
if (handle == 0)
|
||||||
// invalid handle
|
// invalid handle
|
||||||
return UC_ERR_UCH;
|
return UC_ERR_UCH;
|
||||||
|
|
||||||
|
MemoryRegion *mr = memory_mapping(uc, address);
|
||||||
|
if (mr == NULL)
|
||||||
|
return UC_ERR_MEM_WRITE;
|
||||||
|
|
||||||
|
operms = mr->perms;
|
||||||
|
if (!(operms & UC_PROT_WRITE)) //write protected
|
||||||
|
//but this is not the program accessing memory, so temporarily mark writable
|
||||||
|
uc->readonly_mem(mr, false);
|
||||||
|
|
||||||
if (uc->write_mem(&uc->as, address, bytes, size) == false)
|
if (uc->write_mem(&uc->as, address, bytes, size) == false)
|
||||||
return UC_ERR_MEM_WRITE;
|
return UC_ERR_MEM_WRITE;
|
||||||
|
|
||||||
|
if (!(operms & UC_PROT_WRITE)) //write protected
|
||||||
|
//now write protect it again
|
||||||
|
uc->readonly_mem(mr, true);
|
||||||
|
|
||||||
return UC_ERR_OK;
|
return UC_ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -529,9 +546,9 @@ static uc_err _hook_mem_access(uch handle, uc_mem_type type,
|
||||||
}
|
}
|
||||||
|
|
||||||
UNICORN_EXPORT
|
UNICORN_EXPORT
|
||||||
uc_err uc_mem_map(uch handle, uint64_t address, size_t size)
|
uc_err uc_mem_map_ex(uch handle, uint64_t address, size_t size, uint32_t perms)
|
||||||
{
|
{
|
||||||
MemoryBlock *blocks;
|
MemoryRegion **regions;
|
||||||
struct uc_struct* uc = (struct uc_struct *)handle;
|
struct uc_struct* uc = (struct uc_struct *)handle;
|
||||||
|
|
||||||
if (handle == 0)
|
if (handle == 0)
|
||||||
|
@ -550,34 +567,41 @@ uc_err uc_mem_map(uch handle, uint64_t address, size_t size)
|
||||||
if ((size & (4*1024 - 1)) != 0)
|
if ((size & (4*1024 - 1)) != 0)
|
||||||
return UC_ERR_MAP;
|
return UC_ERR_MAP;
|
||||||
|
|
||||||
|
// check for only valid permissions
|
||||||
|
if ((perms & ~(UC_PROT_READ | UC_PROT_WRITE)) != 0)
|
||||||
|
return UC_ERR_MAP;
|
||||||
|
|
||||||
if ((uc->mapped_block_count & (MEM_BLOCK_INCR - 1)) == 0) { //time to grow
|
if ((uc->mapped_block_count & (MEM_BLOCK_INCR - 1)) == 0) { //time to grow
|
||||||
blocks = realloc(uc->mapped_blocks, sizeof(MemoryBlock) * (uc->mapped_block_count + MEM_BLOCK_INCR));
|
regions = (MemoryRegion**)realloc(uc->mapped_blocks, sizeof(MemoryRegion*) * (uc->mapped_block_count + MEM_BLOCK_INCR));
|
||||||
if (blocks == NULL) {
|
if (regions == NULL) {
|
||||||
return UC_ERR_OOM;
|
return UC_ERR_OOM;
|
||||||
}
|
}
|
||||||
uc->mapped_blocks = blocks;
|
uc->mapped_blocks = regions;
|
||||||
}
|
}
|
||||||
uc->mapped_blocks[uc->mapped_block_count].begin = address;
|
uc->mapped_blocks[uc->mapped_block_count] = uc->memory_map(uc, address, size, perms);
|
||||||
uc->mapped_blocks[uc->mapped_block_count].end = address + size;
|
|
||||||
//TODO extend uc_mem_map to accept permissions, figure out how to pass this down to qemu
|
|
||||||
uc->mapped_blocks[uc->mapped_block_count].perms = UC_PROT_READ | UC_PROT_WRITE | UC_PROT_EXEC;
|
|
||||||
uc->memory_map(uc, address, size);
|
|
||||||
uc->mapped_block_count++;
|
uc->mapped_block_count++;
|
||||||
|
|
||||||
return UC_ERR_OK;
|
return UC_ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool memory_mapping(struct uc_struct* uc, uint64_t address)
|
UNICORN_EXPORT
|
||||||
|
uc_err uc_mem_map(uch handle, uint64_t address, size_t size)
|
||||||
|
{
|
||||||
|
//old api, maps RW by default
|
||||||
|
return uc_mem_map_ex(handle, address, size, UC_PROT_READ | UC_PROT_WRITE);
|
||||||
|
}
|
||||||
|
|
||||||
|
MemoryRegion *memory_mapping(struct uc_struct* uc, uint64_t address)
|
||||||
{
|
{
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
|
||||||
for(i = 0; i < uc->mapped_block_count; i++) {
|
for(i = 0; i < uc->mapped_block_count; i++) {
|
||||||
if (address >= uc->mapped_blocks[i].begin && address < uc->mapped_blocks[i].end)
|
if (address >= uc->mapped_blocks[i]->addr && address < uc->mapped_blocks[i]->end)
|
||||||
return true;
|
return uc->mapped_blocks[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
// not found
|
// not found
|
||||||
return false;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uc_err _hook_mem_invalid(struct uc_struct* uc, uc_cb_eventmem_t callback,
|
static uc_err _hook_mem_invalid(struct uc_struct* uc, uc_cb_eventmem_t callback,
|
||||||
|
@ -744,4 +768,3 @@ uc_err uc_hook_del(uch handle, uch *h2)
|
||||||
|
|
||||||
return hook_del(handle, h2);
|
return hook_del(handle, h2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue