diff --git a/include/unicorn/unicorn.h b/include/unicorn/unicorn.h index 4619c7c3..5f04ec10 100644 --- a/include/unicorn/unicorn.h +++ b/include/unicorn/unicorn.h @@ -624,6 +624,35 @@ uc_err uc_mem_protect(uc_engine *uc, uint64_t address, size_t size, uint32_t per UNICORN_EXPORT uc_err uc_mem_regions(uc_engine *uc, uc_mem_region **regions, uint32_t *count); +/* + Save a copy of the current state's registers + This API should be used to efficiently make or update a saved copy of the + state's registers. + + @uc: handle returned by uc_open() + @buffer: pointer to the region to store the registers in. The first call to + this function should pass NULL in this parameter, so a region of the + appropriate size for the current architecure can be allocated. Further calls + to this function may pass in the return value of previous calls. + + @return a pointer to the region the registers were saved in. If buffer was + NULL, this is a newly allocated region, otherwise it is the same as buffer. + Any allocation performed by this function must be freed by the user. +*/ +UNICORN_EXPORT +void *uc_save_regstate(uc_engine *uc, void *buffer); + +/* + Restore the current state's registers from a saved copy + This API should be used to roll the CPU register state back to a previous + state saved by uc_save_regstate(). + + @uc: handle returned by uc_open() + @buffer: pointer returned by uc_save_regstate() +*/ +UNICORN_EXPORT +void uc_restore_regstate(uc_engine *uc, void *buffer); + #ifdef __cplusplus } #endif diff --git a/qemu/target-arm/unicorn.h b/qemu/target-arm/unicorn.h index 0b3fb93d..d8a02505 100644 --- a/qemu/target-arm/unicorn.h +++ b/qemu/target-arm/unicorn.h @@ -19,4 +19,7 @@ void arm_uc_init(struct uc_struct* uc); __attribute__ ((visibility ("default"))) void arm64_uc_init(struct uc_struct* uc); +extern const int ARM_REGS_STORAGE_SIZE; +extern const int ARM64_REGS_STORAGE_SIZE; + #endif diff --git a/qemu/target-arm/unicorn_aarch64.c b/qemu/target-arm/unicorn_aarch64.c index e2c593a6..bb2610fd 100644 --- a/qemu/target-arm/unicorn_aarch64.c +++ b/qemu/target-arm/unicorn_aarch64.c @@ -10,6 +10,8 @@ #include "uc_priv.h" +const int ARM64_REGS_STORAGE_SIZE = offsetof(CPUARMState, tlb_table); + static void arm64_set_pc(struct uc_struct *uc, uint64_t address) { ((CPUARMState *)uc->current_cpu->env_ptr)->pc = address; diff --git a/qemu/target-arm/unicorn_arm.c b/qemu/target-arm/unicorn_arm.c index bdf2222c..706cf7bb 100644 --- a/qemu/target-arm/unicorn_arm.c +++ b/qemu/target-arm/unicorn_arm.c @@ -10,6 +10,8 @@ #include "uc_priv.h" +const int ARM_REGS_STORAGE_SIZE = offsetof(CPUARMState, tlb_table); + static void arm_set_pc(struct uc_struct *uc, uint64_t address) { ((CPUARMState *)uc->current_cpu->env_ptr)->pc = address; diff --git a/qemu/target-i386/unicorn.c b/qemu/target-i386/unicorn.c index 004e2a4a..a4d7a640 100644 --- a/qemu/target-i386/unicorn.c +++ b/qemu/target-i386/unicorn.c @@ -12,6 +12,8 @@ #include "uc_priv.h" +const int X86_REGS_STORAGE_SIZE = offsetof(CPUX86State, tlb_table); + static void x86_set_pc(struct uc_struct *uc, uint64_t address) { ((CPUX86State *)uc->current_cpu->env_ptr)->eip = address; diff --git a/qemu/target-i386/unicorn.h b/qemu/target-i386/unicorn.h index 37e38b4f..cb292001 100644 --- a/qemu/target-i386/unicorn.h +++ b/qemu/target-i386/unicorn.h @@ -12,4 +12,6 @@ void x86_reg_reset(struct uc_struct *uc); void x86_uc_init(struct uc_struct* uc); int x86_uc_machine_init(struct uc_struct *uc); + +extern const int X86_REGS_STORAGE_SIZE; #endif diff --git a/qemu/target-m68k/unicorn.c b/qemu/target-m68k/unicorn.c index d2818cfe..849eb08f 100644 --- a/qemu/target-m68k/unicorn.c +++ b/qemu/target-m68k/unicorn.c @@ -10,6 +10,8 @@ #include "uc_priv.h" +const int M68K_REGS_STORAGE_SIZE = offsetof(CPUM68KState, tlb_table); + static void m68k_set_pc(struct uc_struct *uc, uint64_t address) { ((CPUM68KState *)uc->current_cpu->env_ptr)->pc = address; diff --git a/qemu/target-m68k/unicorn.h b/qemu/target-m68k/unicorn.h index 7cf8e21a..59471865 100644 --- a/qemu/target-m68k/unicorn.h +++ b/qemu/target-m68k/unicorn.h @@ -12,4 +12,5 @@ void m68k_reg_reset(struct uc_struct *uc); void m68k_uc_init(struct uc_struct* uc); +extern const int M68K_REGS_STORAGE_SIZE; #endif diff --git a/qemu/target-mips/unicorn.c b/qemu/target-mips/unicorn.c index 94c1819f..6d84a97b 100644 --- a/qemu/target-mips/unicorn.c +++ b/qemu/target-mips/unicorn.c @@ -9,6 +9,14 @@ #include "unicorn_common.h" #include "uc_priv.h" +// prevent the lines from being compiled twice +#ifdef TARGET_WORDS_BIGENDIAN +#ifdef TARGET_MIPS64 +const int MIPS64_REGS_STORAGE_SIZE = offsetof(CPUMIPSState, tlb_table); +#else // MIPS32 +const int MIPS_REGS_STORAGE_SIZE = offsetof(CPUMIPSState, tlb_table); +#endif +#endif static uint64_t mips_mem_redirect(uint64_t address) { diff --git a/qemu/target-mips/unicorn.h b/qemu/target-mips/unicorn.h index c9639eb8..53b5c32a 100644 --- a/qemu/target-mips/unicorn.h +++ b/qemu/target-mips/unicorn.h @@ -15,4 +15,7 @@ void mipsel_uc_init(struct uc_struct* uc); void mips64_uc_init(struct uc_struct* uc); void mips64el_uc_init(struct uc_struct* uc); +extern const int MIPS_REGS_STORAGE_SIZE; +extern const int MIPS64_REGS_STORAGE_SIZE; + #endif diff --git a/qemu/target-sparc/unicorn.c b/qemu/target-sparc/unicorn.c index a2a3dcaa..5cfb8d2a 100644 --- a/qemu/target-sparc/unicorn.c +++ b/qemu/target-sparc/unicorn.c @@ -10,6 +10,8 @@ #include "uc_priv.h" +const int SPARC_REGS_STORAGE_SIZE = offsetof(CPUSPARCState, tlb_table); + static bool sparc_stop_interrupt(int intno) { switch(intno) { diff --git a/qemu/target-sparc/unicorn.h b/qemu/target-sparc/unicorn.h index 53bf1303..2140f286 100644 --- a/qemu/target-sparc/unicorn.h +++ b/qemu/target-sparc/unicorn.h @@ -13,4 +13,7 @@ void sparc_reg_reset(struct uc_struct *uc); void sparc_uc_init(struct uc_struct* uc); void sparc64_uc_init(struct uc_struct* uc); +extern const int SPARC_REGS_STORAGE_SIZE; +extern const int SPARC64_REGS_STORAGE_SIZE; + #endif diff --git a/qemu/target-sparc/unicorn64.c b/qemu/target-sparc/unicorn64.c index 9a748936..b8a96dda 100644 --- a/qemu/target-sparc/unicorn64.c +++ b/qemu/target-sparc/unicorn64.c @@ -10,6 +10,8 @@ #include "uc_priv.h" +const int SPARC64_REGS_STORAGE_SIZE = offsetof(CPUSPARCState, tlb_table); + static bool sparc_stop_interrupt(int intno) { switch(intno) { diff --git a/uc.c b/uc.c index d6f26ba4..d9a9282d 100644 --- a/uc.c +++ b/uc.c @@ -1160,3 +1160,36 @@ uc_err uc_query(uc_engine *uc, uc_query_type type, size_t *result) return UC_ERR_OK; } + +size_t cpu_regs_size(uc_arch arch, uc_mode mode); +size_t cpu_regs_size(uc_arch arch, uc_mode mode) { + // each of these constants is defined by offsetof(CPUXYZState, tlb_table) + // tbl_table is the first entry in the CPU_COMMON macro, so it marks the end + // of the interesting CPU registers + switch (arch) { + case UC_ARCH_M68K: return M68K_REGS_STORAGE_SIZE; + case UC_ARCH_X86: return X86_REGS_STORAGE_SIZE; + case UC_ARCH_ARM: return ARM_REGS_STORAGE_SIZE; + case UC_ARCH_ARM64: return ARM64_REGS_STORAGE_SIZE; + case UC_ARCH_MIPS: return mode & UC_MODE_MIPS64 ? MIPS64_REGS_STORAGE_SIZE : MIPS_REGS_STORAGE_SIZE; + case UC_ARCH_SPARC: return mode & UC_MODE_SPARC64 ? SPARC64_REGS_STORAGE_SIZE : SPARC_REGS_STORAGE_SIZE; + default: return 0; + } +} + +UNICORN_EXPORT +void *uc_save_regstate(uc_engine *uc, void *buffer) { + size_t sz = cpu_regs_size(uc->arch, uc->mode); + if (!buffer) { + buffer = malloc(sz); + } + + memcpy(buffer, uc->current_cpu->env_ptr, sz); + return buffer; +} + +UNICORN_EXPORT +void uc_restore_regstate(uc_engine *uc, void *buffer) { + size_t sz = cpu_regs_size(uc->arch, uc->mode); + memcpy(uc->current_cpu->env_ptr, buffer, sz); +}