From 91122b6a68e5bdaefef3f41e6822a75ec454e576 Mon Sep 17 00:00:00 2001 From: mio Date: Thu, 26 Sep 2024 23:02:03 +0800 Subject: [PATCH 01/72] Fix compatibility of 3.8 --- bindings/python/unicorn/unicorn_py3/unicorn.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bindings/python/unicorn/unicorn_py3/unicorn.py b/bindings/python/unicorn/unicorn_py3/unicorn.py index 55bc20b104..c52046b56b 100644 --- a/bindings/python/unicorn/unicorn_py3/unicorn.py +++ b/bindings/python/unicorn/unicorn_py3/unicorn.py @@ -98,13 +98,13 @@ def _load_lib(path: Path, lib_name: str): canonicals.append( resources.files("unicorn") / 'lib' ) - except ImportError: + except: try: import pkg_resources canonicals.append( pkg_resources.resource_filename("unicorn", 'lib') ) - except ImportError: + except: # maybe importlib_resources, but ignore for now pass From 433bb163d9aaba72e4f5a29cecec052ca199a47a Mon Sep 17 00:00:00 2001 From: elicn Date: Sat, 28 Sep 2024 22:11:01 +0300 Subject: [PATCH 02/72] Add read and write cases for i386 CR8 --- qemu/target/i386/cpu.h | 2 +- qemu/target/i386/unicorn.c | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/qemu/target/i386/cpu.h b/qemu/target/i386/cpu.h index b16ebd291a..4ddb7a5155 100644 --- a/qemu/target/i386/cpu.h +++ b/qemu/target/i386/cpu.h @@ -1397,7 +1397,7 @@ typedef struct CPUX86State { SegmentCache gdt; /* only base and limit are used */ SegmentCache idt; /* only base and limit are used */ - target_ulong cr[5]; /* NOTE: cr1 is unused */ + target_ulong cr[9]; /* NOTE: cr1, cr5-cr7 are not used */ int32_t a20_mask; BNDReg bnd_regs[4]; diff --git a/qemu/target/i386/unicorn.c b/qemu/target/i386/unicorn.c index ceddb55d2a..0172999be2 100644 --- a/qemu/target/i386/unicorn.c +++ b/qemu/target/i386/unicorn.c @@ -594,6 +594,7 @@ uc_err reg_read(void *_env, int mode, unsigned int regid, void *value, case UC_X86_REG_CR2: case UC_X86_REG_CR3: case UC_X86_REG_CR4: + case UC_X86_REG_CR8: CHECK_REG_TYPE(int64_t); *(int64_t *)value = env->cr[regid - UC_X86_REG_CR0]; break; @@ -1402,6 +1403,9 @@ uc_err reg_write(void *_env, int mode, unsigned int regid, const void *value, case UC_X86_REG_CR4: CHECK_REG_TYPE(uint64_t); cpu_x86_update_cr4(env, *(uint32_t *)value); + goto write_cr64; + case UC_X86_REG_CR8: + CHECK_REG_TYPE(uint64_t); write_cr64: env->cr[regid - UC_X86_REG_CR0] = *(uint64_t *)value; break; From 6cb9bfe5c04e59ea7ae94f7c6a33a8109282361a Mon Sep 17 00:00:00 2001 From: elicn Date: Sat, 28 Sep 2024 22:11:54 +0300 Subject: [PATCH 03/72] Avoid updating CR3 when writing CR1 and CR2 --- qemu/target/i386/unicorn.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/qemu/target/i386/unicorn.c b/qemu/target/i386/unicorn.c index 0172999be2..449da90759 100644 --- a/qemu/target/i386/unicorn.c +++ b/qemu/target/i386/unicorn.c @@ -1156,6 +1156,8 @@ uc_err reg_write(void *_env, int mode, unsigned int regid, const void *value, goto write_cr; case UC_X86_REG_CR1: case UC_X86_REG_CR2: + CHECK_REG_TYPE(uint32_t); + goto write_cr; case UC_X86_REG_CR3: CHECK_REG_TYPE(uint32_t); cpu_x86_update_cr3(env, *(uint32_t *)value); @@ -1396,6 +1398,8 @@ uc_err reg_write(void *_env, int mode, unsigned int regid, const void *value, goto write_cr64; case UC_X86_REG_CR1: case UC_X86_REG_CR2: + CHECK_REG_TYPE(uint64_t); + goto write_cr64; case UC_X86_REG_CR3: CHECK_REG_TYPE(uint64_t); cpu_x86_update_cr3(env, *(uint32_t *)value); From e3675162ba22c343edf5ab6173f33f6758513a5e Mon Sep 17 00:00:00 2001 From: elicn Date: Sat, 28 Sep 2024 22:15:00 +0300 Subject: [PATCH 04/72] Add read and write support for i386 XMM16-31 --- qemu/target/i386/cpu.h | 10 +++++-- qemu/target/i386/unicorn.c | 60 +++++++++++++++++++++++++++++--------- 2 files changed, 53 insertions(+), 17 deletions(-) diff --git a/qemu/target/i386/cpu.h b/qemu/target/i386/cpu.h index 4ddb7a5155..10d93b89ac 100644 --- a/qemu/target/i386/cpu.h +++ b/qemu/target/i386/cpu.h @@ -1432,11 +1432,15 @@ typedef struct CPUX86State { ZMMReg xmm_t0; MMXReg mmx_t0; - XMMReg ymmh_regs[CPU_NB_REGS]; + /* + * YMM is not supported by QEMU at all + * As of qemu 5.0.1, ymmh_regs is nowhere used. + */ + XMMReg ymmh_regs[CPU_NB_REGS]; /* currently not in use */ uint64_t opmask_regs[NB_OPMASK_REGS]; - YMMReg zmmh_regs[CPU_NB_REGS]; - ZMMReg hi16_zmm_regs[CPU_NB_REGS]; + YMMReg zmmh_regs[CPU_NB_REGS]; /* currently not in use */ + ZMMReg hi16_zmm_regs[CPU_NB_REGS]; /* currently not in use */ /* sysenter registers */ uint32_t sysenter_cs; diff --git a/qemu/target/i386/unicorn.c b/qemu/target/i386/unicorn.c index 449da90759..6482a91f4d 100644 --- a/qemu/target/i386/unicorn.c +++ b/qemu/target/i386/unicorn.c @@ -285,9 +285,9 @@ uc_err reg_read(void *_env, int mode, unsigned int regid, void *value, case UC_X86_REG_XMM5: case UC_X86_REG_XMM6: case UC_X86_REG_XMM7: { - CHECK_REG_TYPE(float64[2]); - float64 *dst = (float64 *)value; - ZMMReg *reg = (ZMMReg *)&env->xmm_regs[regid - UC_X86_REG_XMM0]; + CHECK_REG_TYPE(uint64_t[2]); + uint64_t *dst = (uint64_t *)value; + const ZMMReg* const reg = &env->xmm_regs[regid - UC_X86_REG_XMM0]; dst[0] = reg->ZMM_Q(0); dst[1] = reg->ZMM_Q(1); return ret; @@ -968,10 +968,26 @@ uc_err reg_read(void *_env, int mode, unsigned int regid, void *value, case UC_X86_REG_XMM12: case UC_X86_REG_XMM13: case UC_X86_REG_XMM14: - case UC_X86_REG_XMM15: { - CHECK_REG_TYPE(float64[2]); - float64 *dst = (float64 *)value; - ZMMReg *reg = (ZMMReg *)&env->xmm_regs[regid - UC_X86_REG_XMM0]; + case UC_X86_REG_XMM15: + case UC_X86_REG_XMM16: + case UC_X86_REG_XMM17: + case UC_X86_REG_XMM18: + case UC_X86_REG_XMM19: + case UC_X86_REG_XMM20: + case UC_X86_REG_XMM21: + case UC_X86_REG_XMM22: + case UC_X86_REG_XMM23: + case UC_X86_REG_XMM24: + case UC_X86_REG_XMM25: + case UC_X86_REG_XMM26: + case UC_X86_REG_XMM27: + case UC_X86_REG_XMM28: + case UC_X86_REG_XMM29: + case UC_X86_REG_XMM30: + case UC_X86_REG_XMM31: { + CHECK_REG_TYPE(uint64_t[2]); + uint64_t *dst = (uint64_t *)value; + const ZMMReg* const reg = &env->xmm_regs[regid - UC_X86_REG_XMM0]; dst[0] = reg->ZMM_Q(0); dst[1] = reg->ZMM_Q(1); break; @@ -1046,9 +1062,9 @@ uc_err reg_write(void *_env, int mode, unsigned int regid, const void *value, case UC_X86_REG_XMM5: case UC_X86_REG_XMM6: case UC_X86_REG_XMM7: { - CHECK_REG_TYPE(float64[2]); - float64 *src = (float64 *)value; - ZMMReg *reg = (ZMMReg *)&env->xmm_regs[regid - UC_X86_REG_XMM0]; + CHECK_REG_TYPE(uint64_t[2]); + const uint64_t *src = (const uint64_t *)value; + ZMMReg *reg = &env->xmm_regs[regid - UC_X86_REG_XMM0]; reg->ZMM_Q(0) = src[0]; reg->ZMM_Q(1) = src[1]; return ret; @@ -1794,10 +1810,26 @@ uc_err reg_write(void *_env, int mode, unsigned int regid, const void *value, case UC_X86_REG_XMM12: case UC_X86_REG_XMM13: case UC_X86_REG_XMM14: - case UC_X86_REG_XMM15: { - CHECK_REG_TYPE(float64[2]); - float64 *src = (float64 *)value; - ZMMReg *reg = (ZMMReg *)&env->xmm_regs[regid - UC_X86_REG_XMM0]; + case UC_X86_REG_XMM15: + case UC_X86_REG_XMM16: + case UC_X86_REG_XMM17: + case UC_X86_REG_XMM18: + case UC_X86_REG_XMM19: + case UC_X86_REG_XMM20: + case UC_X86_REG_XMM21: + case UC_X86_REG_XMM22: + case UC_X86_REG_XMM23: + case UC_X86_REG_XMM24: + case UC_X86_REG_XMM25: + case UC_X86_REG_XMM26: + case UC_X86_REG_XMM27: + case UC_X86_REG_XMM28: + case UC_X86_REG_XMM29: + case UC_X86_REG_XMM30: + case UC_X86_REG_XMM31: { + CHECK_REG_TYPE(uint64_t[2]); + const uint64_t *src = (const uint64_t *)value; + ZMMReg *reg = &env->xmm_regs[regid - UC_X86_REG_XMM0]; reg->ZMM_Q(0) = src[0]; reg->ZMM_Q(1) = src[1]; break; From 5164dbd609748ce104ec1103caba0a680a25fc90 Mon Sep 17 00:00:00 2001 From: elicn Date: Sat, 28 Sep 2024 22:16:45 +0300 Subject: [PATCH 05/72] Add read and write support for i386 YMM8-31 --- qemu/target/i386/unicorn.c | 118 ++++++++++++++++++++++++++----------- 1 file changed, 82 insertions(+), 36 deletions(-) diff --git a/qemu/target/i386/unicorn.c b/qemu/target/i386/unicorn.c index 6482a91f4d..f5c5980c28 100644 --- a/qemu/target/i386/unicorn.c +++ b/qemu/target/i386/unicorn.c @@ -311,23 +311,14 @@ uc_err reg_read(void *_env, int mode, unsigned int regid, void *value, case UC_X86_REG_YMM4: case UC_X86_REG_YMM5: case UC_X86_REG_YMM6: - case UC_X86_REG_YMM7: - case UC_X86_REG_YMM8: - case UC_X86_REG_YMM9: - case UC_X86_REG_YMM10: - case UC_X86_REG_YMM11: - case UC_X86_REG_YMM12: - case UC_X86_REG_YMM13: - case UC_X86_REG_YMM14: - case UC_X86_REG_YMM15: { - CHECK_REG_TYPE(float64[4]); - float64 *dst = (float64 *)value; - ZMMReg *lo_reg = (ZMMReg *)&env->xmm_regs[regid - UC_X86_REG_YMM0]; - XMMReg *hi_reg = &env->ymmh_regs[regid - UC_X86_REG_YMM0]; - dst[0] = lo_reg->ZMM_Q(0); - dst[1] = lo_reg->ZMM_Q(1); - dst[2] = hi_reg->_d[0]; - dst[3] = hi_reg->_d[1]; + case UC_X86_REG_YMM7: { + CHECK_REG_TYPE(uint64_t[4]); + uint64_t *dst = (uint64_t *)value; + const ZMMReg* const reg = &env->xmm_regs[regid - UC_X86_REG_YMM0]; + dst[0] = reg->ZMM_Q(0); + dst[1] = reg->ZMM_Q(1); + dst[2] = reg->ZMM_Q(2); + dst[3] = reg->ZMM_Q(3); return ret; } @@ -992,6 +983,39 @@ uc_err reg_read(void *_env, int mode, unsigned int regid, void *value, dst[1] = reg->ZMM_Q(1); break; } + case UC_X86_REG_YMM8: + case UC_X86_REG_YMM9: + case UC_X86_REG_YMM10: + case UC_X86_REG_YMM11: + case UC_X86_REG_YMM12: + case UC_X86_REG_YMM13: + case UC_X86_REG_YMM14: + case UC_X86_REG_YMM15: + case UC_X86_REG_YMM16: + case UC_X86_REG_YMM17: + case UC_X86_REG_YMM18: + case UC_X86_REG_YMM19: + case UC_X86_REG_YMM20: + case UC_X86_REG_YMM21: + case UC_X86_REG_YMM22: + case UC_X86_REG_YMM23: + case UC_X86_REG_YMM24: + case UC_X86_REG_YMM25: + case UC_X86_REG_YMM26: + case UC_X86_REG_YMM27: + case UC_X86_REG_YMM28: + case UC_X86_REG_YMM29: + case UC_X86_REG_YMM30: + case UC_X86_REG_YMM31: { + CHECK_REG_TYPE(uint64_t[4]); + uint64_t *dst = (uint64_t *)value; + const ZMMReg* const reg = &env->xmm_regs[regid - UC_X86_REG_YMM0]; + dst[0] = reg->ZMM_Q(0); + dst[1] = reg->ZMM_Q(1); + dst[2] = reg->ZMM_Q(2); + dst[3] = reg->ZMM_Q(3); + break; + } case UC_X86_REG_FS_BASE: CHECK_REG_TYPE(uint64_t); *(uint64_t *)value = (uint64_t)env->segs[R_FS].base; @@ -1088,25 +1112,14 @@ uc_err reg_write(void *_env, int mode, unsigned int regid, const void *value, case UC_X86_REG_YMM4: case UC_X86_REG_YMM5: case UC_X86_REG_YMM6: - case UC_X86_REG_YMM7: - case UC_X86_REG_YMM8: - case UC_X86_REG_YMM9: - case UC_X86_REG_YMM10: - case UC_X86_REG_YMM11: - case UC_X86_REG_YMM12: - case UC_X86_REG_YMM13: - case UC_X86_REG_YMM14: - case UC_X86_REG_YMM15: { - CHECK_REG_TYPE(float64[4]); - float64 *src = (float64 *)value; - ZMMReg *lo_reg = (ZMMReg *)&env->xmm_regs[regid - UC_X86_REG_YMM0]; - XMMReg *hi_reg = &env->ymmh_regs[regid - UC_X86_REG_YMM0]; - lo_reg->ZMM_Q(0) = src[0]; - lo_reg->ZMM_Q(1) = src[1]; - // YMM is not supported by QEMU at all - // As of qemu 5.0.1, ymmh_regs is nowhere used. - hi_reg->_d[0] = src[2]; - hi_reg->_d[1] = src[3]; + case UC_X86_REG_YMM7: { + CHECK_REG_TYPE(uint64_t[4]); + const uint64_t *src = (const uint64_t *)value; + ZMMReg *reg = &env->xmm_regs[regid - UC_X86_REG_YMM0]; + reg->ZMM_Q(0) = src[0]; + reg->ZMM_Q(1) = src[1]; + reg->ZMM_Q(2) = src[2]; + reg->ZMM_Q(3) = src[3]; return ret; } @@ -1834,6 +1847,39 @@ uc_err reg_write(void *_env, int mode, unsigned int regid, const void *value, reg->ZMM_Q(1) = src[1]; break; } + case UC_X86_REG_YMM8: + case UC_X86_REG_YMM9: + case UC_X86_REG_YMM10: + case UC_X86_REG_YMM11: + case UC_X86_REG_YMM12: + case UC_X86_REG_YMM13: + case UC_X86_REG_YMM14: + case UC_X86_REG_YMM15: + case UC_X86_REG_YMM16: + case UC_X86_REG_YMM17: + case UC_X86_REG_YMM18: + case UC_X86_REG_YMM19: + case UC_X86_REG_YMM20: + case UC_X86_REG_YMM21: + case UC_X86_REG_YMM22: + case UC_X86_REG_YMM23: + case UC_X86_REG_YMM24: + case UC_X86_REG_YMM25: + case UC_X86_REG_YMM26: + case UC_X86_REG_YMM27: + case UC_X86_REG_YMM28: + case UC_X86_REG_YMM29: + case UC_X86_REG_YMM30: + case UC_X86_REG_YMM31: { + CHECK_REG_TYPE(uint64_t[4]); + const uint64_t *src = (const uint64_t *)value; + ZMMReg *reg = &env->xmm_regs[regid - UC_X86_REG_YMM0]; + reg->ZMM_Q(0) = src[0]; + reg->ZMM_Q(1) = src[1]; + reg->ZMM_Q(2) = src[2]; + reg->ZMM_Q(3) = src[3]; + break; + } case UC_X86_REG_FS_BASE: CHECK_REG_TYPE(uint64_t); env->segs[R_FS].base = *(uint64_t *)value; From 9af1df6543dc6ea67fd36406e3a20602ee49fa11 Mon Sep 17 00:00:00 2001 From: elicn Date: Sat, 28 Sep 2024 22:17:06 +0300 Subject: [PATCH 06/72] Add read and write support for i386 ZMM0-31 --- qemu/target/i386/unicorn.c | 90 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/qemu/target/i386/unicorn.c b/qemu/target/i386/unicorn.c index f5c5980c28..1a073f865e 100644 --- a/qemu/target/i386/unicorn.c +++ b/qemu/target/i386/unicorn.c @@ -1016,6 +1016,51 @@ uc_err reg_read(void *_env, int mode, unsigned int regid, void *value, dst[3] = reg->ZMM_Q(3); break; } + case UC_X86_REG_ZMM0: + case UC_X86_REG_ZMM1: + case UC_X86_REG_ZMM2: + case UC_X86_REG_ZMM3: + case UC_X86_REG_ZMM4: + case UC_X86_REG_ZMM5: + case UC_X86_REG_ZMM6: + case UC_X86_REG_ZMM7: + case UC_X86_REG_ZMM8: + case UC_X86_REG_ZMM9: + case UC_X86_REG_ZMM10: + case UC_X86_REG_ZMM11: + case UC_X86_REG_ZMM12: + case UC_X86_REG_ZMM13: + case UC_X86_REG_ZMM14: + case UC_X86_REG_ZMM15: + case UC_X86_REG_ZMM16: + case UC_X86_REG_ZMM17: + case UC_X86_REG_ZMM18: + case UC_X86_REG_ZMM19: + case UC_X86_REG_ZMM20: + case UC_X86_REG_ZMM21: + case UC_X86_REG_ZMM22: + case UC_X86_REG_ZMM23: + case UC_X86_REG_ZMM24: + case UC_X86_REG_ZMM25: + case UC_X86_REG_ZMM26: + case UC_X86_REG_ZMM27: + case UC_X86_REG_ZMM28: + case UC_X86_REG_ZMM29: + case UC_X86_REG_ZMM30: + case UC_X86_REG_ZMM31: { + CHECK_REG_TYPE(uint64_t[8]); + uint64_t *dst = (uint64_t *)value; + const ZMMReg* const reg = &env->xmm_regs[regid - UC_X86_REG_ZMM0]; + dst[0] = reg->ZMM_Q(0); + dst[1] = reg->ZMM_Q(1); + dst[2] = reg->ZMM_Q(2); + dst[3] = reg->ZMM_Q(3); + dst[4] = reg->ZMM_Q(4); + dst[5] = reg->ZMM_Q(5); + dst[6] = reg->ZMM_Q(6); + dst[7] = reg->ZMM_Q(7); + break; + } case UC_X86_REG_FS_BASE: CHECK_REG_TYPE(uint64_t); *(uint64_t *)value = (uint64_t)env->segs[R_FS].base; @@ -1880,6 +1925,51 @@ uc_err reg_write(void *_env, int mode, unsigned int regid, const void *value, reg->ZMM_Q(3) = src[3]; break; } + case UC_X86_REG_ZMM0: + case UC_X86_REG_ZMM1: + case UC_X86_REG_ZMM2: + case UC_X86_REG_ZMM3: + case UC_X86_REG_ZMM4: + case UC_X86_REG_ZMM5: + case UC_X86_REG_ZMM6: + case UC_X86_REG_ZMM7: + case UC_X86_REG_ZMM8: + case UC_X86_REG_ZMM9: + case UC_X86_REG_ZMM10: + case UC_X86_REG_ZMM11: + case UC_X86_REG_ZMM12: + case UC_X86_REG_ZMM13: + case UC_X86_REG_ZMM14: + case UC_X86_REG_ZMM15: + case UC_X86_REG_ZMM16: + case UC_X86_REG_ZMM17: + case UC_X86_REG_ZMM18: + case UC_X86_REG_ZMM19: + case UC_X86_REG_ZMM20: + case UC_X86_REG_ZMM21: + case UC_X86_REG_ZMM22: + case UC_X86_REG_ZMM23: + case UC_X86_REG_ZMM24: + case UC_X86_REG_ZMM25: + case UC_X86_REG_ZMM26: + case UC_X86_REG_ZMM27: + case UC_X86_REG_ZMM28: + case UC_X86_REG_ZMM29: + case UC_X86_REG_ZMM30: + case UC_X86_REG_ZMM31: { + CHECK_REG_TYPE(uint64_t[8]); + const uint64_t *src = (const uint64_t *)value; + ZMMReg *reg = &env->xmm_regs[regid - UC_X86_REG_ZMM0]; + reg->ZMM_Q(0) = src[0]; + reg->ZMM_Q(1) = src[1]; + reg->ZMM_Q(2) = src[2]; + reg->ZMM_Q(3) = src[3]; + reg->ZMM_Q(4) = src[4]; + reg->ZMM_Q(5) = src[5]; + reg->ZMM_Q(6) = src[6]; + reg->ZMM_Q(7) = src[7]; + break; + } case UC_X86_REG_FS_BASE: CHECK_REG_TYPE(uint64_t); env->segs[R_FS].base = *(uint64_t *)value; From ff06b145e1e0817b3010edf4d38793422868b00a Mon Sep 17 00:00:00 2001 From: elicn Date: Mon, 30 Sep 2024 18:52:37 +0300 Subject: [PATCH 07/72] Fix I0-7 registers write --- qemu/target/sparc/unicorn.c | 1 + 1 file changed, 1 insertion(+) diff --git a/qemu/target/sparc/unicorn.c b/qemu/target/sparc/unicorn.c index 39c78b6887..acccd7befb 100644 --- a/qemu/target/sparc/unicorn.c +++ b/qemu/target/sparc/unicorn.c @@ -78,6 +78,7 @@ uc_err reg_read(void *_env, int mode, unsigned int regid, void *value, CHECK_REG_TYPE(uint32_t); *(uint32_t *)value = env->regwptr[8 + regid - UC_SPARC_REG_L0]; } else if (regid >= UC_SPARC_REG_I0 && regid <= UC_SPARC_REG_I7) { + CHECK_REG_TYPE(uint32_t); *(uint32_t *)value = env->regwptr[16 + regid - UC_SPARC_REG_I0]; } else { switch (regid) { From 07e67c657077d6e121ed8002db9f4a5d8f20d056 Mon Sep 17 00:00:00 2001 From: mio Date: Wed, 2 Oct 2024 15:47:59 +0800 Subject: [PATCH 08/72] Remove gdb history --- bindings/python/.gdb_history | 107 ----------------------------------- 1 file changed, 107 deletions(-) delete mode 100644 bindings/python/.gdb_history diff --git a/bindings/python/.gdb_history b/bindings/python/.gdb_history deleted file mode 100644 index 1a96055207..0000000000 --- a/bindings/python/.gdb_history +++ /dev/null @@ -1,107 +0,0 @@ -q -rr -r -bt -b tcg.c:3075 -r -p ts -p *ts -bt -q -b translate.c:4810 -r -q -r -b translate.c:4810 -frame level 5 -frame level 7 -q -r -bt -q -r -bt -q -r -bt -b translate.c:4810 -r -p/x s->pc -n -n -n -n -n -n -n -n -p/x aflag -n -n -n -p/x dflag -n -n -n -n -p/x f -n -s -n -n -n -n -n -n -n -n -n -n -n -n -n -n -n -q -r -bt -q -r -bt -frame level 7 -p/x op -p op -p *op -q -b tcg_optimize -r -b tcg_optimize_x86_64 -r -n -s -s -s -q -r -exit -b tcg_dump_ops -r -b tcg_dump_ops -b tcg_dump_ops_x86_64 -r -b tcg_optimize_x86_64 -r -b tcg_optimize_x86_64 -r -b tcg_optimize_x86_64 -r -b print_log -r -b print_log -r -f fprintf -q -b write -r -bt -q From a637368122bd060dfa24076247a50fe95e38b13f Mon Sep 17 00:00:00 2001 From: mio Date: Wed, 2 Oct 2024 15:48:47 +0800 Subject: [PATCH 09/72] Add .gdb_history --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index e3f290a789..98fa46f04e 100644 --- a/.gitignore +++ b/.gitignore @@ -37,7 +37,7 @@ unicorn_*.lib unicorn_*.exp unicorn_*.dll - +.gdb_history *.tgz *.zip *.pyc From 9f935f505ecdc93c195d6cb05d47745a514ce175 Mon Sep 17 00:00:00 2001 From: mio Date: Wed, 2 Oct 2024 15:49:26 +0800 Subject: [PATCH 10/72] Revert previous break changes that return UC_ERR_ARG for non-existing registers But print a warning instead --- qemu/target/arm/unicorn_aarch64.c | 2 ++ qemu/target/arm/unicorn_arm.c | 4 ++++ qemu/target/i386/unicorn.c | 2 ++ qemu/target/m68k/unicorn.c | 2 ++ qemu/target/mips/unicorn.c | 2 ++ qemu/target/ppc/unicorn.c | 2 ++ qemu/target/riscv/unicorn.c | 2 ++ qemu/target/s390x/unicorn.c | 3 ++- qemu/target/sparc/unicorn.c | 2 ++ qemu/target/tricore/unicorn.c | 4 +++- qemu/unicorn_common.h | 12 ++++++++++++ 11 files changed, 35 insertions(+), 2 deletions(-) diff --git a/qemu/target/arm/unicorn_aarch64.c b/qemu/target/arm/unicorn_aarch64.c index c246395781..ae1572a145 100644 --- a/qemu/target/arm/unicorn_aarch64.c +++ b/qemu/target/arm/unicorn_aarch64.c @@ -266,6 +266,7 @@ uc_err reg_read(void *_env, int mode, unsigned int regid, void *value, } } + CHECK_RET_DEPRECATE(ret, regid); return ret; } @@ -399,6 +400,7 @@ uc_err reg_write(void *_env, int mode, unsigned int regid, const void *value, } } + CHECK_RET_DEPRECATE(ret, regid); return ret; } diff --git a/qemu/target/arm/unicorn_arm.c b/qemu/target/arm/unicorn_arm.c index eda70af595..d8417e1273 100644 --- a/qemu/target/arm/unicorn_arm.c +++ b/qemu/target/arm/unicorn_arm.c @@ -10,6 +10,8 @@ #include "uc_priv.h" #include "unicorn_common.h" #include "unicorn.h" +#include "stdio.h" +#include "inttypes.h" ARMCPU *cpu_arm_init(struct uc_struct *uc); @@ -353,6 +355,7 @@ uc_err reg_read(void *_env, int mode, unsigned int regid, void *value, } } + CHECK_RET_DEPRECATE(ret, regid); return ret; } @@ -553,6 +556,7 @@ uc_err reg_write(void *_env, int mode, unsigned int regid, const void *value, } } + CHECK_RET_DEPRECATE(ret, regid); return ret; } diff --git a/qemu/target/i386/unicorn.c b/qemu/target/i386/unicorn.c index 1a073f865e..2ed4ca336f 100644 --- a/qemu/target/i386/unicorn.c +++ b/qemu/target/i386/unicorn.c @@ -1074,6 +1074,7 @@ uc_err reg_read(void *_env, int mode, unsigned int regid, void *value, #endif } + CHECK_RET_DEPRECATE(ret, regid); return ret; } @@ -1983,6 +1984,7 @@ uc_err reg_write(void *_env, int mode, unsigned int regid, const void *value, #endif } + CHECK_RET_DEPRECATE(ret, regid); return ret; } diff --git a/qemu/target/m68k/unicorn.c b/qemu/target/m68k/unicorn.c index f1efd66e3b..56d4791e7c 100644 --- a/qemu/target/m68k/unicorn.c +++ b/qemu/target/m68k/unicorn.c @@ -77,6 +77,7 @@ uc_err reg_read(void *_env, int mode, unsigned int regid, void *value, } } + CHECK_RET_DEPRECATE(ret, regid); return ret; } @@ -109,6 +110,7 @@ uc_err reg_write(void *_env, int mode, unsigned int regid, const void *value, } } + CHECK_RET_DEPRECATE(ret, regid); return ret; } diff --git a/qemu/target/mips/unicorn.c b/qemu/target/mips/unicorn.c index 77a7d48d92..ab922e6f4c 100644 --- a/qemu/target/mips/unicorn.c +++ b/qemu/target/mips/unicorn.c @@ -100,6 +100,7 @@ uc_err reg_read(void *_env, int mode, unsigned int regid, void *value, } } + CHECK_RET_DEPRECATE(ret, regid); return ret; } @@ -149,6 +150,7 @@ uc_err reg_write(void *_env, int mode, unsigned int regid, const void *value, } } + CHECK_RET_DEPRECATE(ret, regid); return ret; } diff --git a/qemu/target/ppc/unicorn.c b/qemu/target/ppc/unicorn.c index ce86cf93eb..75841a2767 100644 --- a/qemu/target/ppc/unicorn.c +++ b/qemu/target/ppc/unicorn.c @@ -204,6 +204,7 @@ uc_err reg_read(void *_env, int mode, unsigned int regid, void *value, } } + CHECK_RET_DEPRECATE(ret, regid); return ret; } @@ -265,6 +266,7 @@ uc_err reg_write(void *_env, int mode, unsigned int regid, const void *value, } } + CHECK_RET_DEPRECATE(ret, regid); return ret; } diff --git a/qemu/target/riscv/unicorn.c b/qemu/target/riscv/unicorn.c index 8b55282fa3..69e782d4f0 100644 --- a/qemu/target/riscv/unicorn.c +++ b/qemu/target/riscv/unicorn.c @@ -126,6 +126,7 @@ uc_err reg_read(void *_env, int mode, unsigned int regid, void *value, } } + CHECK_RET_DEPRECATE(ret, regid); return ret; } @@ -176,6 +177,7 @@ uc_err reg_write(void *_env, int mode, unsigned int regid, const void *value, } } + CHECK_RET_DEPRECATE(ret, regid); return ret; } diff --git a/qemu/target/s390x/unicorn.c b/qemu/target/s390x/unicorn.c index 972bf9e36f..fed0e98731 100644 --- a/qemu/target/s390x/unicorn.c +++ b/qemu/target/s390x/unicorn.c @@ -80,7 +80,7 @@ uc_err reg_read(void *_env, int mode, unsigned int regid, void *value, break; } } - + CHECK_RET_DEPRECATE(ret, regid); return ret; } @@ -113,6 +113,7 @@ uc_err reg_write(void *_env, int mode, unsigned int regid, const void *value, break; } } + CHECK_RET_DEPRECATE(ret, regid); return ret; } diff --git a/qemu/target/sparc/unicorn.c b/qemu/target/sparc/unicorn.c index acccd7befb..33ed8def5b 100644 --- a/qemu/target/sparc/unicorn.c +++ b/qemu/target/sparc/unicorn.c @@ -91,6 +91,7 @@ uc_err reg_read(void *_env, int mode, unsigned int regid, void *value, } } + CHECK_RET_DEPRECATE(ret, regid); return ret; } @@ -126,6 +127,7 @@ uc_err reg_write(void *_env, int mode, unsigned int regid, const void *value, } } + CHECK_RET_DEPRECATE(ret, regid); return ret; } diff --git a/qemu/target/tricore/unicorn.c b/qemu/target/tricore/unicorn.c index 9f47a7dbab..43a31f5482 100644 --- a/qemu/target/tricore/unicorn.c +++ b/qemu/target/tricore/unicorn.c @@ -138,6 +138,7 @@ uc_err reg_read(void *_env, int mode, unsigned int regid, void *value, } } + CHECK_RET_DEPRECATE(ret, regid); return ret; } @@ -240,7 +241,8 @@ uc_err reg_write(void *_env, int mode, unsigned int regid, const void *value, break; } } - + + CHECK_RET_DEPRECATE(ret, regid); return ret; } diff --git a/qemu/unicorn_common.h b/qemu/unicorn_common.h index def70a9e8a..e70f50a8fd 100644 --- a/qemu/unicorn_common.h +++ b/qemu/unicorn_common.h @@ -154,4 +154,16 @@ static inline void uc_common_init(struct uc_struct* uc) ret = UC_ERR_OK; \ } while(0) +#define CHECK_RET_DEPRECATE(ret, regid) do { \ + if (ret == UC_ERR_ARG && !getenv("UC_IGNORE_REG_BREAK")) { \ + fprintf(stderr, \ + "WARNING: Your register accessing on id %"PRIu32" is deprecated" \ + " and will get UC_ERR_ARG in the future release (2.2.0) because" \ + " the accessing is either no-op or not defined." \ + " Set UC_IGNORE_REG_BREAK=1 to ignore this warning.\n", \ + regid); \ + ret = UC_ERR_OK; \ + } \ +} while (0) + #endif From d30ba77c712ac926a4359d2a68a087051393d089 Mon Sep 17 00:00:00 2001 From: mio Date: Wed, 2 Oct 2024 15:56:34 +0800 Subject: [PATCH 11/72] Refine warning --- qemu/unicorn_common.h | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/qemu/unicorn_common.h b/qemu/unicorn_common.h index e70f50a8fd..9bf24c7a6b 100644 --- a/qemu/unicorn_common.h +++ b/qemu/unicorn_common.h @@ -154,13 +154,15 @@ static inline void uc_common_init(struct uc_struct* uc) ret = UC_ERR_OK; \ } while(0) -#define CHECK_RET_DEPRECATE(ret, regid) do { \ +#define CHECK_RET_DEPRECATE(ret, regid) do { \ if (ret == UC_ERR_ARG && !getenv("UC_IGNORE_REG_BREAK")) { \ fprintf(stderr, \ - "WARNING: Your register accessing on id %"PRIu32" is deprecated" \ - " and will get UC_ERR_ARG in the future release (2.2.0) because" \ - " the accessing is either no-op or not defined." \ - " Set UC_IGNORE_REG_BREAK=1 to ignore this warning.\n", \ + "WARNING: Your register accessing on id %"PRIu32" is deprecated " \ + "and will get UC_ERR_ARG in the future release (2.2.0) because " \ + "the accessing is either no-op or not defined. If you believe " \ + "the register should be implemented or there is a bug, please " \ + "submit an issue to https://github.com/unicorn-engine/unicorn. " \ + "Set UC_IGNORE_REG_BREAK=1 to ignore this warning.\n", \ regid); \ ret = UC_ERR_OK; \ } \ From 05e29b4507c06655e815f6cc970c9f2eb98f8ead Mon Sep 17 00:00:00 2001 From: mio Date: Wed, 2 Oct 2024 16:03:36 +0800 Subject: [PATCH 12/72] Implement UC_ARM64_REG_WSP --- qemu/target/arm/unicorn_aarch64.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/qemu/target/arm/unicorn_aarch64.c b/qemu/target/arm/unicorn_aarch64.c index ae1572a145..8a01c56520 100644 --- a/qemu/target/arm/unicorn_aarch64.c +++ b/qemu/target/arm/unicorn_aarch64.c @@ -223,6 +223,10 @@ uc_err reg_read(void *_env, int mode, unsigned int regid, void *value, CHECK_REG_TYPE(uint64_t); *(uint64_t *)value = env->pc; break; + case UC_ARM64_REG_WSP: + CHECK_REG_TYPE(uint32_t); + *(uint32_t *)value = READ_DWORD(env->xregs[31]); + break; case UC_ARM64_REG_SP: CHECK_REG_TYPE(uint64_t); *(uint64_t *)value = env->xregs[31]; @@ -263,6 +267,14 @@ uc_err reg_read(void *_env, int mode, unsigned int regid, void *value, CHECK_REG_TYPE(uint32_t); *(uint32_t *)value = vfp_get_fpsr(env); break; + case UC_ARM64_REG_XZR: + CHECK_REG_TYPE(uint64_t); + *(uint64_t *)value = 0; + break; + case UC_ARM64_REG_WZR: + CHECK_REG_TYPE(uint32_t); + *(uint32_t *)value = 0; + break; } } @@ -356,6 +368,10 @@ uc_err reg_write(void *_env, int mode, unsigned int regid, const void *value, env->pc = *(uint64_t *)value; *setpc = 1; break; + case UC_ARM64_REG_WSP: + CHECK_REG_TYPE(uint32_t); + WRITE_DWORD(env->xregs[31], (*(uint32_t *)value)); + break; case UC_ARM64_REG_SP: CHECK_REG_TYPE(uint64_t); env->xregs[31] = *(uint64_t *)value; @@ -397,6 +413,14 @@ uc_err reg_write(void *_env, int mode, unsigned int regid, const void *value, CHECK_REG_TYPE(uint32_t); vfp_set_fpsr(env, *(uint32_t *)value); break; + case UC_ARM64_REG_XZR: + CHECK_REG_TYPE(uint64_t); + // no-ops actually + break; + case UC_ARM64_REG_WZR: + CHECK_REG_TYPE(uint32_t); + // no-ops actually + break; } } From ac4872be4c27c78666106509a2779be4e4069699 Mon Sep 17 00:00:00 2001 From: Eli Date: Sun, 6 Oct 2024 18:14:03 +0300 Subject: [PATCH 13/72] Support additional API on Python 3 bindings (#2016) * Styling and commets fixes * Add errno API support * Improve OOP approach by adjusting the way reg types are selected * Leverage new approach to deduplicate reg_read and reg_write code * Adjust reg_read_batch * Add support for reg_write_batch * Adjust x86 MSR accessors * Turn asserts into descriptive exceptions * Improve comments and styling * Fix ARM memcpy neon regression test * Modify canonicals import * Introduce ARM CP reg accessors --- .../python/unicorn/unicorn_py3/arch/arm.py | 81 +++-- .../python/unicorn/unicorn_py3/arch/arm64.py | 77 +++-- .../python/unicorn/unicorn_py3/arch/intel.py | 65 ++-- .../python/unicorn/unicorn_py3/arch/types.py | 15 +- .../python/unicorn/unicorn_py3/unicorn.py | 279 +++++++++++++----- tests/regress/arm_memcpy_neon.py | 106 ++++--- 6 files changed, 401 insertions(+), 222 deletions(-) diff --git a/bindings/python/unicorn/unicorn_py3/arch/arm.py b/bindings/python/unicorn/unicorn_py3/arch/arm.py index 6df9ec98f4..a31930389a 100644 --- a/bindings/python/unicorn/unicorn_py3/arch/arm.py +++ b/bindings/python/unicorn/unicorn_py3/arch/arm.py @@ -2,7 +2,7 @@ """ # @author elicn -from typing import Any, Tuple +from typing import Tuple, Type import ctypes @@ -10,7 +10,7 @@ from unicorn import arm_const as const # newly introduced unicorn imports -from ..unicorn import Uc +from ..unicorn import Uc, check_maxbits from .types import UcTupledReg, UcReg128 ARMCPReg = Tuple[int, int, int, int, int, int, int, int] @@ -40,46 +40,71 @@ class UcAArch32(Uc): """Unicorn subclass for ARM architecture. """ + REG_RANGE_CP = (const.UC_ARM_REG_CP_REG,) + REG_RANGE_Q = range(const.UC_ARM_REG_Q0, const.UC_ARM_REG_Q15 + 1) - @staticmethod - def __select_reg_class(reg_id: int): - """Select class for special architectural registers. + @classmethod + def _select_reg_class(cls, reg_id: int) -> Type: + """Select the appropriate class for the specified architectural register. """ reg_class = ( - (UcAArch32.REG_RANGE_Q, UcReg128), + (UcAArch32.REG_RANGE_CP, UcRegCP), + (UcAArch32.REG_RANGE_Q, UcReg128) ) - return next((cls for rng, cls in reg_class if reg_id in rng), None) + return next((c for rng, c in reg_class if reg_id in rng), cls._DEFAULT_REGTYPE) - def reg_read(self, reg_id: int, aux: Any = None): - # select register class for special cases - reg_cls = UcAArch32.__select_reg_class(reg_id) + # to learn more about accessing aarch32 coprocessor registers, refer to: + # https://developer.arm.com/documentation/ddi0601/latest/AArch32-Registers - if reg_cls is None: - if reg_id == const.UC_ARM_REG_CP_REG: - return self._reg_read(reg_id, UcRegCP, *aux) + def cpr_read(self, coproc: int, opc1: int, crn: int, crm: int, opc2: int, el: int, is_64: bool) -> int: + """Read a coprocessor register value. - else: - # fallback to default reading method - return super().reg_read(reg_id, aux) + Args: + coproc : coprocessor to access, value varies between 0 and 15 + opc1 : opcode 1, value varies between 0 and 7 + crn : coprocessor register to access (CRn), value varies between 0 and 15 + crm : additional coprocessor register to access (CRm), value varies between 0 and 15 + opc2 : opcode 2, value varies between 0 and 7 + el : the exception level the coprocessor register belongs to, value varies between 0 and 3 + is_64 : indicates whether this is a 64-bit register - return self._reg_read(reg_id, reg_cls) + Returns: value of coprocessor register + """ - def reg_write(self, reg_id: int, value) -> None: - # select register class for special cases - reg_cls = UcAArch32.__select_reg_class(reg_id) + assert check_maxbits(coproc, 4) + assert check_maxbits(opc1, 3) + assert check_maxbits(crn, 4) + assert check_maxbits(crm, 4) + assert check_maxbits(opc2, 3) + assert check_maxbits(el, 2) # note that unicorn currently supports only EL0 and EL1 + + return self.reg_read(const.UC_ARM_REG_CP_REG, (coproc, int(is_64), el, crn, crm, opc1, opc2)) + + def cpr_write(self, coproc: int, opc1: int, crn: int, crm: int, opc2: int, el: int, is_64: bool, value: int) -> None: + """Write a coprocessor register value. + + Args: + coproc : coprocessor to access, value varies between 0 and 15 + opc1 : opcode 1, value varies between 0 and 7 + crn : coprocessor register to access (CRn), value varies between 0 and 15 + crm : additional coprocessor register to access (CRm), value varies between 0 and 15 + opc2 : opcode 2, value varies between 0 and 7 + el : the exception level the coprocessor register belongs to, value varies between 0 and 3 + is_64 : indicates whether this is a 64-bit register + value : value to write + """ - if reg_cls is None: - if reg_id == const.UC_ARM_REG_CP_REG: - self._reg_write(reg_id, UcRegCP, value) + assert check_maxbits(coproc, 4) + assert check_maxbits(opc1, 3) + assert check_maxbits(crn, 4) + assert check_maxbits(crm, 4) + assert check_maxbits(opc2, 3) + assert check_maxbits(el, 2) # note that unicorn currently supports only EL0 and EL1 - else: - # fallback to default writing method - super().reg_write(reg_id, value) + self.reg_write(const.UC_ARM_REG_CP_REG, (coproc, int(is_64), el, crn, crm, opc1, opc2, value)) - else: - self._reg_write(reg_id, reg_cls, value) __all__ = ['UcRegCP', 'UcAArch32'] diff --git a/bindings/python/unicorn/unicorn_py3/arch/arm64.py b/bindings/python/unicorn/unicorn_py3/arch/arm64.py index 166d6483e5..34d617f225 100644 --- a/bindings/python/unicorn/unicorn_py3/arch/arm64.py +++ b/bindings/python/unicorn/unicorn_py3/arch/arm64.py @@ -2,7 +2,7 @@ """ # @author elicn -from typing import Any, Callable, NamedTuple, Tuple +from typing import Any, Callable, NamedTuple, Tuple, Type import ctypes @@ -11,7 +11,7 @@ from unicorn.unicorn_const import UC_ERR_ARG, UC_HOOK_INSN # newly introduced unicorn imports -from ..unicorn import Uc, UcError, uccallback +from ..unicorn import Uc, UcError, uccallback, check_maxbits from .types import uc_engine, UcTupledReg, UcReg128 ARM64CPReg = Tuple[int, int, int, int, int, int] @@ -41,6 +41,8 @@ class UcAArch64(Uc): """Unicorn subclass for ARM64 architecture. """ + REG_RANGE_CP = (const.UC_ARM64_REG_CP_REG,) + REG_RANGE_Q = range(const.UC_ARM64_REG_Q0, const.UC_ARM64_REG_Q31 + 1) REG_RANGE_V = range(const.UC_ARM64_REG_V0, const.UC_ARM64_REG_V31 + 1) @@ -85,45 +87,62 @@ class CpReg(NamedTuple): return getattr(self, '_Uc__do_hook_add')(htype, fptr, begin, end, insn) - @staticmethod - def __select_reg_class(reg_id: int): - """Select class for special architectural registers. + @classmethod + def _select_reg_class(cls, reg_id: int) -> Type: + """Select the appropriate class for the specified architectural register. """ reg_class = ( - (UcAArch64.REG_RANGE_Q, UcReg128), - (UcAArch64.REG_RANGE_V, UcReg128) + (UcAArch64.REG_RANGE_CP, UcRegCP64), + (UcAArch64.REG_RANGE_Q, UcReg128), + (UcAArch64.REG_RANGE_V, UcReg128) ) - return next((cls for rng, cls in reg_class if reg_id in rng), None) + return next((c for rng, c in reg_class if reg_id in rng), cls._DEFAULT_REGTYPE) + + # to learn more about accessing aarch64 coprocessor registers, refer to: + # https://developer.arm.com/documentation/ddi0601/latest/AArch64-Registers + + def cpr_read(self, op0: int, op1: int, crn: int, crm: int, op2: int) -> int: + """Read a coprocessor register value. - def reg_read(self, reg_id: int, aux: Any = None): - # select register class for special cases - reg_cls = UcAArch64.__select_reg_class(reg_id) + Args: + op0 : opcode 0, value varies between 0 and 3 + op1 : opcode 1, value varies between 0 and 7 + crn : coprocessor register to access (CRn), value varies between 0 and 15 + crm : additional coprocessor register to access (CRm), value varies between 0 and 15 + op2 : opcode 2, value varies between 0 and 7 - if reg_cls is None: - if reg_id == const.UC_ARM64_REG_CP_REG: - return self._reg_read(reg_id, UcRegCP64, *aux) + Returns: value of coprocessor register + """ + + assert check_maxbits(op0, 2) + assert check_maxbits(op1, 3) + assert check_maxbits(crn, 4) + assert check_maxbits(crm, 4) + assert check_maxbits(op2, 3) - else: - # fallback to default reading method - return super().reg_read(reg_id, aux) + return self.reg_read(const.UC_ARM64_REG_CP_REG, (crn, crm, op0, op1, op2)) - return self._reg_read(reg_id, reg_cls) + def cpr_write(self, op0: int, op1: int, crn: int, crm: int, op2: int, value: int) -> None: + """Write a coprocessor register value. - def reg_write(self, reg_id: int, value) -> None: - # select register class for special cases - reg_cls = UcAArch64.__select_reg_class(reg_id) + Args: + op0 : opcode 0, value varies between 0 and 3 + op1 : opcode 1, value varies between 0 and 7 + crn : coprocessor register to access (CRn), value varies between 0 and 15 + crm : additional coprocessor register to access (CRm), value varies between 0 and 15 + op2 : opcode 2, value varies between 0 and 7 + value : value to write + """ - if reg_cls is None: - if reg_id == const.UC_ARM64_REG_CP_REG: - self._reg_write(reg_id, UcRegCP64, value) + assert check_maxbits(op0, 2) + assert check_maxbits(op1, 3) + assert check_maxbits(crn, 4) + assert check_maxbits(crm, 4) + assert check_maxbits(op2, 3) - else: - # fallback to default writing method - super().reg_write(reg_id, value) + self.reg_write(const.UC_ARM64_REG_CP_REG, (crn, crm, op0, op1, op2, value)) - else: - self._reg_write(reg_id, reg_cls, value) __all__ = ['UcRegCP64', 'UcAArch64'] diff --git a/bindings/python/unicorn/unicorn_py3/arch/intel.py b/bindings/python/unicorn/unicorn_py3/arch/intel.py index 4f1318d181..cc0e110f5b 100644 --- a/bindings/python/unicorn/unicorn_py3/arch/intel.py +++ b/bindings/python/unicorn/unicorn_py3/arch/intel.py @@ -2,7 +2,7 @@ """ # @author elicn -from typing import Any, Callable, Sequence, Tuple +from typing import Any, Callable, Tuple, Type import ctypes @@ -64,6 +64,8 @@ class UcIntel(Uc): """Unicorn subclass for Intel architecture. """ + REG_RANGE_MSR = (const.UC_X86_REG_MSR,) + REG_RANGE_MMR = ( const.UC_X86_REG_IDTR, const.UC_X86_REG_GDTR, @@ -127,12 +129,13 @@ def __hook_insn_cpuid_cb(uc: Uc, key: int) -> int: return getattr(self, '_Uc__do_hook_add')(htype, fptr, begin, end, insn) - @staticmethod - def __select_reg_class(reg_id: int): - """Select class for special architectural registers. + @classmethod + def _select_reg_class(cls, reg_id: int) -> Type: + """Select the appropriate class for the specified architectural register. """ reg_class = ( + (UcIntel.REG_RANGE_MSR, UcRegMSR), (UcIntel.REG_RANGE_MMR, UcRegMMR), (UcIntel.REG_RANGE_FP, UcRegFPR), (UcIntel.REG_RANGE_XMM, UcReg128), @@ -140,54 +143,28 @@ def __select_reg_class(reg_id: int): (UcIntel.REG_RANGE_ZMM, UcReg512) ) - return next((cls for rng, cls in reg_class if reg_id in rng), None) - - def reg_read(self, reg_id: int, aux: Any = None): - # select register class for special cases - reg_cls = UcIntel.__select_reg_class(reg_id) - - if reg_cls is None: - # backward compatibility: msr read through reg_read - if reg_id == const.UC_X86_REG_MSR: - if type(aux) is not int: - raise UcError(UC_ERR_ARG) - - value = self.msr_read(aux) - - else: - value = super().reg_read(reg_id, aux) - else: - value = self._reg_read(reg_id, reg_cls) - - return value + return next((c for rng, c in reg_class if reg_id in rng), cls._DEFAULT_REGTYPE) - def reg_write(self, reg_id: int, value) -> None: - # select register class for special cases - reg_cls = UcIntel.__select_reg_class(reg_id) + def msr_read(self, msr_id: int) -> int: + """Read a model-specific register. - if reg_cls is None: - # backward compatibility: msr write through reg_write - if reg_id == const.UC_X86_REG_MSR: - if type(value) is not tuple or len(value) != 2: - raise UcError(UC_ERR_ARG) + Args: + msr_id: MSR index - self.msr_write(*value) - return + Returns: MSR value + """ - super().reg_write(reg_id, value) - else: - self._reg_write(reg_id, reg_cls, value) - - def msr_read(self, msr_id: int) -> int: - return self._reg_read(const.UC_X86_REG_MSR, UcRegMSR, msr_id) + return self.reg_read(const.UC_X86_REG_MSR, msr_id) def msr_write(self, msr_id: int, value: int) -> None: - self._reg_write(const.UC_X86_REG_MSR, UcRegMSR, (msr_id, value)) + """Write to a model-specific register. - def reg_read_batch(self, reg_ids: Sequence[int]) -> Tuple: - reg_types = [UcIntel.__select_reg_class(rid) or self._DEFAULT_REGTYPE for rid in reg_ids] + Args: + msr_id: MSR index + value: new MSR value + """ - return self._reg_read_batch(reg_ids, reg_types) + self.reg_write(const.UC_X86_REG_MSR, (msr_id, value)) __all__ = ['UcRegMMR', 'UcRegMSR', 'UcRegFPR', 'UcIntel'] diff --git a/bindings/python/unicorn/unicorn_py3/arch/types.py b/bindings/python/unicorn/unicorn_py3/arch/types.py index 8e7114c697..b98ed95e6c 100644 --- a/bindings/python/unicorn/unicorn_py3/arch/types.py +++ b/bindings/python/unicorn/unicorn_py3/arch/types.py @@ -3,7 +3,7 @@ # @author elicn from abc import abstractmethod -from typing import Generic, Tuple, TypeVar +from typing import Any, Generic, Tuple, TypeVar import ctypes @@ -24,7 +24,7 @@ class UcReg(ctypes.Structure): @property @abstractmethod - def value(self): + def value(self) -> Any: """Get register value. """ @@ -32,7 +32,7 @@ def value(self): @classmethod @abstractmethod - def from_value(cls, value): + def from_value(cls, value) -> 'UcReg': """Create a register instance from a given value. """ @@ -52,7 +52,11 @@ def value(self) -> VT: @classmethod def from_value(cls, value: VT): - assert type(value) is tuple and len(value) == len(cls._fields_) + if not isinstance(value, tuple): + raise TypeError(f'got {type(value).__name__} while expecting a tuple') + + if len(value) != len(cls._fields_): + raise TypeError(f'got {len(value)} elements while expecting {len(cls._fields_)}') return cls(*value) @@ -72,7 +76,8 @@ def value(self) -> int: @classmethod def from_value(cls, value: int): - assert type(value) is int + if not isinstance(value, int): + raise TypeError(f'got {type(value).__name__} while expecting an integer') mask = (1 << 64) - 1 size = cls._fields_[0][1]._length_ diff --git a/bindings/python/unicorn/unicorn_py3/unicorn.py b/bindings/python/unicorn/unicorn_py3/unicorn.py index c52046b56b..526f06b097 100644 --- a/bindings/python/unicorn/unicorn_py3/unicorn.py +++ b/bindings/python/unicorn/unicorn_py3/unicorn.py @@ -3,7 +3,7 @@ """ from __future__ import annotations -from typing import TYPE_CHECKING, Any, Callable, Iterable, Iterator, Mapping, MutableMapping, Optional, Sequence, Tuple, Type, TypeVar +from typing import TYPE_CHECKING, Any, Callable, Dict, Iterable, Iterator, Optional, Sequence, Tuple, Type, TypeVar, Union import ctypes import functools @@ -43,7 +43,6 @@ def __load_uc_lib() -> ctypes.CDLL: import inspect import os - import sys loaded_dlls = set() @@ -91,26 +90,19 @@ def _load_lib(path: Path, lib_name: str): # - we can get the path to the local libraries by parsing our filename # - global load # - python's lib directory - canonicals = [] - - try: + + if sys.version_info.minor >= 12: from importlib import resources - canonicals.append( - resources.files("unicorn") / 'lib' - ) - except: - try: - import pkg_resources - canonicals.append( - pkg_resources.resource_filename("unicorn", 'lib') - ) - except: - # maybe importlib_resources, but ignore for now - pass - + + canonicals = resources.files('unicorn') / 'lib' + else: + import pkg_resources + + canonicals = pkg_resources.resource_filename('unicorn', 'lib') + lib_locations = [ os.getenv('LIBUNICORN_PATH'), - ] + canonicals + [ + canonicals, PurePath(inspect.getfile(__load_uc_lib)).parent / 'lib', '', r'/usr/local/lib' if sys.platform == 'darwin' else r'/usr/lib64', @@ -171,6 +163,7 @@ def __set_prototype(fname: str, restype: Type[ctypes._CData], *argtypes: Type[ct __set_prototype('uc_reg_read', uc_err, uc_engine, ctypes.c_int, ctypes.c_void_p) __set_prototype('uc_reg_write', uc_err, uc_engine, ctypes.c_int, ctypes.c_void_p) __set_prototype('uc_reg_read_batch', uc_err, uc_engine, ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_void_p), ctypes.c_int) + __set_prototype('uc_reg_write_batch', uc_err, uc_engine, ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_void_p), ctypes.c_int) __set_prototype('uc_mem_read', uc_err, uc_engine, ctypes.c_uint64, ctypes.POINTER(ctypes.c_char), ctypes.c_size_t) __set_prototype('uc_mem_write', uc_err, uc_engine, ctypes.c_uint64, ctypes.POINTER(ctypes.c_char), ctypes.c_size_t) __set_prototype('uc_emu_start', uc_err, uc_engine, ctypes.c_uint64, ctypes.c_uint64, ctypes.c_uint64, ctypes.c_size_t) @@ -215,6 +208,19 @@ def __set_prototype(fname: str, restype: Type[ctypes._CData], *argtypes: Type[ct MMIO_WRITE_CFUNC = ctypes.CFUNCTYPE(None, uc_engine, ctypes.c_uint64, ctypes.c_uint, ctypes.c_uint64, ctypes.c_void_p) +def check_maxbits(value: int, nbits: int) -> bool: + """Verify that a certain value may be represented with at most `nbits` bits. + + Args: + value : numeric value to check + nbits : max number of bits allowed + + Returns: `True` if `value` is represented by at most `nbits` bits, `False` otherwise + """ + + return value & ~((1 << nbits) - 1) == 0 + + class UcError(Exception): """Unicorn base exception. @@ -310,7 +316,7 @@ def uccallback(uc: Uc, functype: Type[_CFP]): def decorate(func) -> _CFP: @functools.wraps(func) - def wrapper(handle: Uc, *args, **kwargs): + def wrapper(handle: int, *args, **kwargs): try: return func(uc, *args, **kwargs) except Exception as e: @@ -334,47 +340,62 @@ class RegStateManager: _DEFAULT_REGTYPE = ctypes.c_uint64 + @classmethod + def _select_reg_class(cls, reg_id: int) -> Type: + """An architecture-specific method that returns the appropriate + value type for a specified reg identifier. All rich `Uc` subclasses + are expected to implement their own + """ + + return cls._DEFAULT_REGTYPE + def _do_reg_read(self, reg_id: int, reg_obj) -> int: - """Private register read implementation. + """Low level register read implementation. Must be implemented by the mixin object """ raise NotImplementedError def _do_reg_write(self, reg_id: int, reg_obj) -> int: - """Private register write implementation. + """Low level register write implementation. Must be implemented by the mixin object """ raise NotImplementedError def _do_reg_read_batch(self, reglist, vallist, count) -> int: - """Private batch register read implementation. + """Low level batch register read implementation. Must be implemented by the mixin object """ raise NotImplementedError - def _do_reg_write_batch(self, reglist, count) -> int: - """Private batch register write implementation. + def _do_reg_write_batch(self, reglist, vallist, count) -> int: + """Low level batch register write implementation. Must be implemented by the mixin object """ raise NotImplementedError @staticmethod - def __get_reg_read_arg(regtype: Type, *args): - return regtype(*args) + def __get_reg_read_arg(reg_type: Type, aux: Any): + if aux is None: + return reg_type() + + if isinstance(aux, tuple): + return reg_type(*aux) + + return reg_type(aux) @staticmethod - def __get_reg_write_arg(regtype: Type, value): - return regtype.from_value(value) if issubclass(regtype, UcReg) else regtype(value) + def __get_reg_write_arg(reg_type: Type, value: Any): + return reg_type.from_value(value) if issubclass(reg_type, UcReg) else reg_type(value) - def _reg_read(self, reg_id: int, regtype: Type, *args): + def _reg_read(self, reg_id: int, reg_type: Type, aux: Any): """Register read helper method. """ - reg = self.__get_reg_read_arg(regtype, *args) + reg = self.__get_reg_read_arg(reg_type, aux) status = self._do_reg_read(reg_id, ctypes.byref(reg)) if status != uc.UC_ERR_OK: @@ -382,25 +403,32 @@ def _reg_read(self, reg_id: int, regtype: Type, *args): return reg.value - def _reg_write(self, reg_id: int, regtype: Type, value) -> None: + def _reg_write(self, reg_id: int, reg_type: Type, value: Any) -> None: """Register write helper method. """ - reg = self.__get_reg_write_arg(regtype, value) + reg = self.__get_reg_write_arg(reg_type, value) status = self._do_reg_write(reg_id, ctypes.byref(reg)) if status != uc.UC_ERR_OK: raise UcError(status, reg_id) - def _reg_read_batch(self, reg_ids: Sequence[int], reg_types: Sequence[Type]) -> Tuple: + def _reg_read_batch(self, read_seq: Sequence[Tuple[int, Type, Any]]) -> Tuple: """Batch register read helper method. + + Args: + read_seq: a sequence of 3-tuples containing register identifier, returned + value type and auxiliary data (or `None` if aux is not required) + + Returns: a tuple of values """ - assert len(reg_ids) == len(reg_types) + count = len(read_seq) + reg_ids = (rid for rid, _, _ in read_seq) + reg_info = ((rtype, aux) for _, rtype, aux in read_seq) - count = len(reg_ids) reg_list = (ctypes.c_int * count)(*reg_ids) - val_list = [rtype() for rtype in reg_types] + val_list = [self.__get_reg_read_arg(rtype, aux) for rtype, aux in reg_info] ptr_list = (ctypes.c_void_p * count)(*(ctypes.c_void_p(ctypes.addressof(elem)) for elem in val_list)) status = self._do_reg_read_batch(reg_list, ptr_list, ctypes.c_int(count)) @@ -410,6 +438,30 @@ def _reg_read_batch(self, reg_ids: Sequence[int], reg_types: Sequence[Type]) -> return tuple(v.value for v in val_list) + def _reg_write_batch(self, write_seq: Sequence[Tuple[int, Type, Any]]) -> None: + """Batch register write helper method. + + Args: + write_seq: a sequence of 3-tuples containing register identifier, value type and value + """ + + count = len(write_seq) + reg_ids = (rid for rid, _, _ in write_seq) + reg_info = ((rtype, aux) for _, rtype, aux in write_seq) + + reg_list = (ctypes.c_int * count)(*reg_ids) + val_list = [self.__get_reg_write_arg(rtype, rval) for rtype, rval in reg_info] + ptr_list = (ctypes.c_void_p * count)(*(ctypes.c_void_p(ctypes.addressof(elem)) for elem in val_list)) + + status = self._do_reg_write_batch(reg_list, ptr_list, ctypes.c_int(count)) + + if status != uc.UC_ERR_OK: + raise UcError(status) + + ######################### + # External facing API # + ######################### + def reg_read(self, reg_id: int, aux: Any = None): """Read architectural register value. @@ -422,7 +474,9 @@ def reg_read(self, reg_id: int, aux: Any = None): Raises: `UcError` in case of invalid register id or auxiliary data """ - return self._reg_read(reg_id, self._DEFAULT_REGTYPE) + reg_type = self._select_reg_class(reg_id) + + return self._reg_read(reg_id, reg_type, aux) def reg_write(self, reg_id: int, value) -> None: """Write to architectural register. @@ -434,34 +488,50 @@ def reg_write(self, reg_id: int, value) -> None: Raises: `UcError` in case of invalid register id or value format """ - self._reg_write(reg_id, self._DEFAULT_REGTYPE, value) + reg_type = self._select_reg_class(reg_id) + + self._reg_write(reg_id, reg_type, value) - def reg_read_batch(self, reg_ids: Sequence[int]) -> Tuple: - """Read a sequence of architectural registers. + def reg_read_batch(self, reg_data: Sequence[Union[int, Tuple[int, Any]]]) -> Tuple: + """Read a sequence of architectural registers. This provides with faster means to + read multiple registers. Args: - reg_ids: a sequence of register identifiers (architecture-specific enumeration) + reg_ids : a sequence of register identifiers (architecture-specific enumeration) + aux : a mapping of reg identifiers and auxiliary data, in case it is required Returns: a tuple of registers values (register-specific format) - Raises: `UcError` in case of invalid register id + Raises: `UcError` in case of an invalid register id, or an invalid aux data for a + register that requires it """ - reg_types = [self._DEFAULT_REGTYPE for _ in range(len(reg_ids))] + def __seq_tuple(elem: Union[int, Tuple[int, Any]]) -> Tuple[int, Type, Any]: + reg_id, reg_aux = elem if isinstance(elem, tuple) else (elem, None) + reg_type = self._select_reg_class(reg_id) - return self._reg_read_batch(reg_ids, reg_types) + return (reg_id, reg_type, reg_aux) - def reg_write_batch(self, reg_info: Sequence[Tuple[int, Any]]) -> None: - """Write a sequece of architectural registers. + return self._reg_read_batch([__seq_tuple(elem) for elem in reg_data]) + + def reg_write_batch(self, reg_data: Sequence[Tuple[int, Any]]) -> None: + """Write a sequece of architectural registers. This provides with faster means to + write multiple registers. Args: - regs_info: a sequence of tuples consisting of register identifiers and values + reg_data: a sequence of register identifiers matched with their designated values Raises: `UcError` in case of invalid register id or value format """ - # TODO - ... + def __seq_tuple(elem: Tuple[int, Any]) -> Tuple[int, Type, Any]: + reg_id, reg_val = elem + reg_type = self._select_reg_class(reg_id) + + return (reg_id, reg_type, reg_val) + + self._reg_write_batch([__seq_tuple(elem) for elem in reg_data]) + def ucsubclass(cls): """Uc subclass decorator. @@ -591,8 +661,8 @@ def __init__(self, arch: int, mode: int, cpu: Optional[int] = None) -> None: # we have to keep a reference to the callbacks so they do not get gc-ed # see: https://docs.python.org/3/library/ctypes.html#callback-functions - self._callbacks: MutableMapping[int, ctypes._FuncPointer] = {} - self._mmio_callbacks: MutableMapping[Tuple[int, int], Tuple[Optional[MMIO_READ_CFUNC], Optional[MMIO_WRITE_CFUNC]]] = {} + self._callbacks: Dict[int, ctypes._FuncPointer] = {} + self._mmio_callbacks: Dict[Tuple[int, int], Tuple[Optional[MMIO_READ_CFUNC], Optional[MMIO_WRITE_CFUNC]]] = {} self._hook_exception: Optional[Exception] = None @@ -617,6 +687,15 @@ def release_handle(uch: uc_engine) -> None: if status != uc.UC_ERR_OK: raise UcError(status) + @property + def errno(self) -> int: + """Get last error number. + + Returns: error number (see: UC_ERR_*) + """ + + return uclib.uc_errno(self._uch) + ########################### # Emulation controllers # ########################### @@ -680,6 +759,13 @@ def _do_reg_read_batch(self, reglist, vallist, count) -> int: return uclib.uc_reg_read_batch(self._uch, reglist, vallist, count) + def _do_reg_write_batch(self, reglist, vallist, count) -> int: + """Private batch register write implementation. + Do not call directly. + """ + + return uclib.uc_reg_write_batch(self._uch, reglist, vallist, count) + ########################### # Memory management # ########################### @@ -767,20 +853,33 @@ def mem_protect(self, address: int, size: int, perms: int = uc.UC_PROT_ALL) -> N raise UcError(status) def mmio_map(self, address: int, size: int, - read_cb: Optional[UC_MMIO_READ_TYPE], user_data_read: Any, - write_cb: Optional[UC_MMIO_WRITE_TYPE], user_data_write: Any) -> None: + read_cb: Optional[UC_MMIO_READ_TYPE], read_ud: Any, + write_cb: Optional[UC_MMIO_WRITE_TYPE], write_ud: Any) -> None: + """Map an MMIO range. This method binds a memory range to read and write accessors + to simulate a hardware device. Unicorn does not allocate memory to back this range. + + Args: + address : range base address + size : range size (in bytes) + read_cb : read callback to invoke upon read access. if not specified, reads \ + from the mmio range will be silently dropped + read_ud : optinal context object to pass on to the read callback + write_cb : write callback to invoke unpon a write access. if not specified, writes \ + to the mmio range will be silently dropped + write_ud : optinal context object to pass on to the write callback + """ @uccallback(self, MMIO_READ_CFUNC) def __mmio_map_read_cb(uc: Uc, offset: int, size: int, key: int) -> int: assert read_cb is not None - return read_cb(uc, offset, size, user_data_read) + return read_cb(uc, offset, size, read_ud) @uccallback(self, MMIO_WRITE_CFUNC) def __mmio_map_write_cb(uc: Uc, offset: int, size: int, value: int, key: int) -> None: assert write_cb is not None - write_cb(uc, offset, size, value, user_data_write) + write_cb(uc, offset, size, value, write_ud) read_cb_fptr = read_cb and __mmio_map_read_cb write_cb_fptr = write_cb and __mmio_map_write_cb @@ -860,7 +959,7 @@ def mem_write(self, address: int, data: bytes) -> None: def __do_hook_add(self, htype: int, fptr: ctypes._FuncPointer, begin: int, end: int, *args: ctypes.c_int) -> int: handle = uc_hook_h() - # TODO: we do not need a callback counter to reference the callback and user data anymore, + # we do not need a callback counter to reference the callback and user data anymore, # so just pass a dummy value. that value will become the unused 'key' argument dummy = 0 @@ -905,7 +1004,7 @@ def __hook_intr(): def __hook_intr_cb(uc: Uc, intno: int, key: int): callback(uc, intno, user_data) - return __hook_intr_cb, + return (__hook_intr_cb,) def __hook_insn(): # each arch is expected to overload hook_add and implement this handler on their own. @@ -918,35 +1017,35 @@ def __hook_code(): def __hook_code_cb(uc: Uc, address: int, size: int, key: int): callback(uc, address, size, user_data) - return __hook_code_cb, + return (__hook_code_cb,) def __hook_invalid_mem(): @uccallback(self, HOOK_MEM_INVALID_CFUNC) def __hook_mem_invalid_cb(uc: Uc, access: int, address: int, size: int, value: int, key: int) -> bool: return callback(uc, access, address, size, value, user_data) - return __hook_mem_invalid_cb, + return (__hook_mem_invalid_cb,) def __hook_mem(): @uccallback(self, HOOK_MEM_ACCESS_CFUNC) def __hook_mem_access_cb(uc: Uc, access: int, address: int, size: int, value: int, key: int) -> None: callback(uc, access, address, size, value, user_data) - return __hook_mem_access_cb, + return (__hook_mem_access_cb,) def __hook_invalid_insn(): @uccallback(self, HOOK_INSN_INVALID_CFUNC) def __hook_insn_invalid_cb(uc: Uc, key: int) -> bool: return callback(uc, user_data) - return __hook_insn_invalid_cb, + return (__hook_insn_invalid_cb,) def __hook_edge_gen(): @uccallback(self, HOOK_EDGE_GEN_CFUNC) def __hook_edge_gen_cb(uc: Uc, cur: ctypes._Pointer[uc_tb], prev: ctypes._Pointer[uc_tb], key: int): callback(uc, cur.contents, prev.contents, user_data) - return __hook_edge_gen_cb, + return (__hook_edge_gen_cb,) def __hook_tcg_opcode(): @uccallback(self, HOOK_TCG_OPCODE_CFUNC) @@ -956,9 +1055,9 @@ def __hook_tcg_op_cb(uc: Uc, address: int, arg1: int, arg2: int, size: int, key: opcode = ctypes.c_uint64(aux1) flags = ctypes.c_uint64(aux2) - return __hook_tcg_op_cb, opcode, flags + return (__hook_tcg_op_cb, opcode, flags) - handlers: Mapping[int, Callable[[], Tuple]] = { + handlers: Dict[int, Callable[[], Tuple]] = { uc.UC_HOOK_INTR : __hook_intr, uc.UC_HOOK_INSN : __hook_insn, uc.UC_HOOK_CODE : __hook_code, @@ -1034,6 +1133,11 @@ def query(self, prop: int) -> int: return result.value def context_save(self) -> UcContext: + """Save Unicorn instance internal context. + + Returns: unicorn context instance + """ + context = UcContext(self._uch, self._arch, self._mode) status = uclib.uc_context_save(self._uch, context.context) @@ -1043,12 +1147,24 @@ def context_save(self) -> UcContext: return context def context_update(self, context: UcContext) -> None: + """Update Unicorn instance internal context. + + Args: + context : unicorn context instance to copy data from + """ + status = uclib.uc_context_save(self._uch, context.context) if status != uc.UC_ERR_OK: raise UcError(status) def context_restore(self, context: UcContext) -> None: + """Overwrite Unicorn instance internal context. + + Args: + context : unicorn context instance to copy data from + """ + status = uclib.uc_context_restore(self._uch, context.context) if status != uc.UC_ERR_OK: @@ -1056,8 +1172,8 @@ def context_restore(self, context: UcContext) -> None: @staticmethod def __ctl_encode(ctl: int, op: int, nargs: int) -> int: - assert nargs and (nargs & ~0b1111) == 0, f'nargs must not exceed value of 15 (got {nargs})' - assert op and (op & ~0b11) == 0, f'op must not exceed value of 3 (got {op})' + assert nargs and check_maxbits(nargs, 4), f'nargs must not exceed value of 15 (got {nargs})' + assert op and check_maxbits(op, 2), f'op must not exceed value of 3 (got {op})' return (op << 30) | (nargs << 26) | ctl @@ -1178,6 +1294,9 @@ def ctl_tlb_mode(self, mode: int) -> None: class UcContext(RegStateManager): + """Unicorn internal context. + """ + def __init__(self, h, arch: int, mode: int): self._context = uc_context() self._size = uclib.uc_context_size(h) @@ -1193,18 +1312,31 @@ def __init__(self, h, arch: int, mode: int): @property def context(self): + """Underlying context data. + Normally this property should not be accessed directly. + """ + return self._context @property def size(self) -> int: + """Underlying context data size. + """ + return self._size @property def arch(self) -> int: + """Get emulated architecture (see UC_ARCH_* constants). + """ + return self._arch @property def mode(self) -> int: + """Get emulated processor mode (see UC_MODE_* constants). + """ + return self._mode # RegStateManager mixin method implementation @@ -1227,11 +1359,17 @@ def _do_reg_read_batch(self, reglist, vallist, count) -> int: return uclib.uc_context_reg_read_batch(self._context, reglist, vallist, count) + def _do_reg_write_batch(self, reglist, vallist, count) -> int: + """Private batch register write implementation. + """ + + return uclib.uc_context_reg_write_batch(self._context, reglist, vallist, count) + # Make UcContext picklable - def __getstate__(self): + def __getstate__(self) -> Tuple[bytes, int, int, int]: return bytes(self), self.size, self.arch, self.mode - def __setstate__(self, state) -> None: + def __setstate__(self, state: Tuple[bytes, int, int, int]) -> None: context, size, arch, mode = state self._context = ctypes.cast(ctypes.create_string_buffer(context, size), uc_context) @@ -1243,7 +1381,7 @@ def __setstate__(self, state) -> None: self._to_free = False def __bytes__(self) -> bytes: - return ctypes.string_at(self.context, self.size) + return ctypes.string_at(self._context, self._size) def __del__(self) -> None: # We need this property since we shouldn't free it if the object is constructed from pickled bytes. @@ -1257,4 +1395,3 @@ def __del__(self) -> None: __all__ = ['Uc', 'UcContext', 'ucsubclass', 'UcError', 'uc_version', 'version_bind', 'uc_arch_supported', 'debug'] - diff --git a/tests/regress/arm_memcpy_neon.py b/tests/regress/arm_memcpy_neon.py index a764ff3d12..56fdd80ff3 100644 --- a/tests/regress/arm_memcpy_neon.py +++ b/tests/regress/arm_memcpy_neon.py @@ -1,52 +1,68 @@ from unicorn import * from unicorn.arm_const import * -# .text:0001F894 ADD PC, PC, R3 -# .text:0001F898 ; --------------------------------------------------------------------------- -# .text:0001F898 VLD1.8 {D0}, [R1]! -# .text:0001F89C VST1.8 {D0}, [R12]! -# .text:0001F8A0 VLD1.8 {D0}, [R1]! -# .text:0001F8A4 VST1.8 {D0}, [R12]! -# .text:0001F8A8 VLD1.8 {D0}, [R1]! -# .text:0001F8AC VST1.8 {D0}, [R12]! -# .text:0001F8B0 VLD1.8 {D0}, [R1]! -# .text:0001F8B4 VST1.8 {D0}, [R12]! -# .text:0001F8B8 VLD1.8 {D0}, [R1]! -# .text:0001F8BC VST1.8 {D0}, [R12]! -# .text:0001F8C0 VLD1.8 {D0}, [R1]! -# .text:0001F8C4 VST1.8 {D0}, [R12]! -# .text:0001F8C8 VLD1.8 {D0}, [R1]! -# .text:0001F8CC VST1.8 {D0}, [R12]! -# .text:0001F8D0 TST R2, #4 -# .text:0001F8D4 LDRNE R3, [R1],#4 -# .text:0001F8D8 STRNE R3, [R12],#4 -# .text:0001F8DC MOVS R2, R2,LSL#31 -# .text:0001F8E0 LDRHCS R3, [R1],#2 -# .text:0001F8E4 LDRBNE R1, [R1] -# .text:0001F8E8 STRHCS R3, [R12],#2 -# .text:0001F8EC STRBNE R1, [R12] -shellcode = [0x3, 0xf0, 0x8f, 0xe0, 0xd, 0x7, 0x21, 0xf4, 0xd, 0x7, 0xc, 0xf4, 0xd, 0x7, 0x21, 0xf4, 0xd, 0x7, 0xc, 0xf4, 0xd, 0x7, 0x21, 0xf4, 0xd, 0x7, 0xc, 0xf4, 0xd, 0x7, 0x21, 0xf4, 0xd, 0x7, 0xc, 0xf4, 0xd, 0x7, 0x21, 0xf4, 0xd, 0x7, 0xc, 0xf4, 0xd, 0x7, 0x21, 0xf4, 0xd, 0x7, 0xc, 0xf4, 0xd, 0x7, 0x21, 0xf4, 0xd, 0x7, 0xc, 0xf4, 0x4, 0x0, 0x12, 0xe3, 0x4, 0x30, 0x91, 0x14, 0x4, 0x30, 0x8c, 0x14, 0x82, 0x2f, 0xb0, 0xe1, 0xb2, 0x30, 0xd1, 0x20, 0x0, 0x10, 0xd1, 0x15, 0xb2, 0x30, 0xcc, 0x20, 0x0, 0x10, 0xcc, 0x15] -base = 0x1F894 -from_address = 0x1000 -to_address = 0x2000 -cplen = 8 -bs = b"c8"*cplen + +SHELLCODE = bytes.fromhex( + '03 f0 8f e0' # 0001F894 ADD PC, PC, R3 + '0d 07 21 f4' # 0001F898 VLD1.8 {D0}, [R1]! + '0d 07 0c f4' # 0001F89C VST1.8 {D0}, [R12]! + '0d 07 21 f4' # 0001F8A0 VLD1.8 {D0}, [R1]! + '0d 07 0c f4' # 0001F8A4 VST1.8 {D0}, [R12]! + '0d 07 21 f4' # 0001F8A8 VLD1.8 {D0}, [R1]! + '0d 07 0c f4' # 0001F8AC VST1.8 {D0}, [R12]! + '0d 07 21 f4' # 0001F8B0 VLD1.8 {D0}, [R1]! + '0d 07 0c f4' # 0001F8B4 VST1.8 {D0}, [R12]! + '0d 07 21 f4' # 0001F8B8 VLD1.8 {D0}, [R1]! + '0d 07 0c f4' # 0001F8BC VST1.8 {D0}, [R12]! + '0d 07 21 f4' # 0001F8C0 VLD1.8 {D0}, [R1]! + '0d 07 0c f4' # 0001F8C4 VST1.8 {D0}, [R12]! + '0d 07 21 f4' # 0001F8C8 VLD1.8 {D0}, [R1]! + '0d 07 0c f4' # 0001F8CC VST1.8 {D0}, [R12]! + '04 00 12 e3' # 0001F8D0 TST R2, #4 + '04 30 91 14' # 0001F8D4 LDRNE R3, [R1],#4 + '04 30 8c 14' # 0001F8D8 STRNE R3, [R12],#4 + '82 2f b0 e1' # 0001F8DC MOVS R2, R2,LSL#31 + 'b2 30 d1 20' # 0001F8E0 LDRHCS R3, [R1],#2 + '00 10 d1 15' # 0001F8E4 LDRBNE R1, [R1] + 'b2 30 cc 20' # 0001F8E8 STRHCS R3, [R12],#2 + '00 10 cc 15' # 0001F8EC STRBNE R1, [R12] +) + +BASE = 0x1F894 +COPY_SRC = 0x1000 +COPY_DST = 0x2000 +COPY_LEN = 8 +bs = b'c8' * COPY_LEN uc = Uc(UC_ARCH_ARM, UC_MODE_ARM) -uc.mem_map(from_address, 0x1000) -uc.mem_map(to_address, 0x1000) -uc.mem_map(0x1F000, 0x1000) -uc.mem_write(from_address, bs) -uc.mem_write(base, bytes(shellcode)) -uc.reg_write(UC_ARM_REG_R12, to_address) -uc.reg_write(UC_ARM_REG_R1, from_address) -uc.reg_write(UC_ARM_REG_R2, cplen) -uc.reg_write(UC_ARM_REG_R3, 0x24) + +uc.mem_map(COPY_SRC, 0x1000) +uc.mem_map(COPY_DST, 0x1000) +uc.mem_map(BASE & ~(0x1000 - 1), 0x1000) +uc.mem_write(COPY_SRC, bs) +uc.mem_write(BASE, bytes(SHELLCODE)) + +uc.reg_write_batch(( + (UC_ARM_REG_R12, COPY_DST), + (UC_ARM_REG_R1, COPY_SRC), + (UC_ARM_REG_R2, COPY_LEN), + (UC_ARM_REG_R3, 0x24) +)) + # enable_vfp -uc.reg_write(UC_ARM_REG_C1_C0_2, uc.reg_read(UC_ARM_REG_C1_C0_2) | (0xf << 20)) -uc.reg_write(UC_ARM_REG_FPEXC, 0x40000000) -uc.emu_start(base, base+len(shellcode)) -fr = uc.mem_read(from_address, len(bs)) -to = uc.mem_read(to_address, len(bs)) -print(f"memcpy result:\nfrom: {bytes(fr)}\nto: {bytes(to)}") \ No newline at end of file +# coproc=15, is64=0, sec=0, CRn=1, CRm=0, opc1=0, opc2=2 +CPACR = (15, 0, 0, 1, 0, 0, 2) + +cpacr = uc.reg_read(UC_ARM_REG_CP_REG, CPACR) +uc.reg_write(UC_ARM_REG_CP_REG, CPACR + (cpacr | (0b11 << 20) | (0b11 << 22),)) +uc.reg_write(UC_ARM_REG_FPEXC, (0b1 << 30)) + +uc.emu_start(BASE, BASE + len(SHELLCODE)) +src = uc.mem_read(COPY_SRC, len(bs)) +dst = uc.mem_read(COPY_DST, len(bs)) + +print(f'''memcpy result: + from: {bytes(src)} + to: {bytes(dst)} +''') From 851914c8d013d9e7e84424b54386dce8eceb74ee Mon Sep 17 00:00:00 2001 From: lazymio Date: Sun, 6 Oct 2024 23:30:32 +0800 Subject: [PATCH 14/72] Fix segfault if tlb is flushed in the hooks --- qemu/accel/tcg/cputlb.c | 15 ++++++++++++++- tests/unit/test_x86.c | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/qemu/accel/tcg/cputlb.c b/qemu/accel/tcg/cputlb.c index 16ba4ed29b..f3f89aefc3 100644 --- a/qemu/accel/tcg/cputlb.c +++ b/qemu/accel/tcg/cputlb.c @@ -1600,12 +1600,25 @@ load_helper(CPUArchState *env, target_ulong addr, TCGMemOpIdx oi, if (!HOOK_BOUND_CHECK(hook, paddr)) continue; JIT_CALLBACK_GUARD(((uc_cb_hookmem_t)hook->callback)(env->uc, UC_MEM_READ, paddr, size, 0, hook->user_data)); - // the last callback may already asked to stop emulation if (uc->stop_request) break; } + /* Unicorn: Previous callbacks may invalidate TLB, reload everything. + This may have impact on performance but generally fine. + A better approach is not always invalidating tlb but this + might cause more chaos regarding re-entry (nested uc_emu_start). + */ + if (tlb_entry_is_empty(entry)) { + tlb_fill(env_cpu(env), addr, size, + access_type, mmu_idx, retaddr); + index = tlb_index(env, mmu_idx, addr); + entry = tlb_entry(env, mmu_idx, addr); + tlb_addr = code_read ? entry->addr_code : entry->addr_read; + tlb_addr &= ~TLB_INVALID_MASK; + } + // callback on non-readable memory if (mr != NULL && !(mr->perms & UC_PROT_READ)) { //non-readable handled = false; diff --git a/tests/unit/test_x86.c b/tests/unit/test_x86.c index a95259e6e0..a7d3909aab 100644 --- a/tests/unit/test_x86.c +++ b/tests/unit/test_x86.c @@ -1798,6 +1798,39 @@ static void test_rex_x64(void) } } +static bool test_x86_ro_segfault_cb(uc_engine *uc, uc_mem_type type, + uint64_t address, int size, + uint64_t value, void *user_data) +{ + const char code[] = + "\xA1\x00\x10\x00\x00\xA1\x00\x10\x00\x00"; + OK(uc_mem_write(uc, address, code, sizeof(code) - 1)); + return true; +} + +static void test_x86_ro_segfault(void) +{ + uc_engine* uc; + // mov eax, [0x1000] + // mov eax, [0x1000] + const char code[] = + "\xA1\x00\x10\x00\x00\xA1\x00\x10\x00\x00"; + uint32_t out; + uc_hook hh; + + OK(uc_open(UC_ARCH_X86, UC_MODE_32, &uc)); + OK(uc_mem_map(uc, 0, 0x1000, UC_PROT_ALL)); + OK(uc_mem_write(uc, 0, code, sizeof(code) - 1)); + OK(uc_mem_map(uc, 0x1000, 0x1000, UC_PROT_READ)); + + OK(uc_hook_add(uc, &hh, UC_HOOK_MEM_READ, test_x86_ro_segfault_cb, NULL, 1, 0)); + OK(uc_emu_start(uc, 0, sizeof(code) - 1, 0, 0)); + + OK(uc_reg_read(uc, UC_X86_REG_EAX, (void*)&out)); + TEST_CHECK(out == 0x001000a1); + OK(uc_close(uc)); +} + TEST_LIST = { {"test_x86_in", test_x86_in}, {"test_x86_out", test_x86_out}, @@ -1851,4 +1884,5 @@ TEST_LIST = { {"test_fxsave_fpip_x64", test_fxsave_fpip_x64}, {"test_bswap_x64", test_bswap_ax}, {"test_rex_x64", test_rex_x64}, + {"test_x86_ro_segfault", test_x86_ro_segfault}, {NULL, NULL}}; From 0886e53572524f79f9a8ad77a70c7a40e20e791a Mon Sep 17 00:00:00 2001 From: lazymio Date: Sun, 6 Oct 2024 23:32:16 +0800 Subject: [PATCH 15/72] Format code --- include/unicorn/unicorn.h | 2 +- qemu/target/i386/unicorn.c | 10 +++++----- qemu/target/tricore/unicorn.c | 2 +- tests/unit/test_x86.c | 19 +++++++++---------- 4 files changed, 16 insertions(+), 17 deletions(-) diff --git a/include/unicorn/unicorn.h b/include/unicorn/unicorn.h index 7bee9b802f..791cfa6df9 100644 --- a/include/unicorn/unicorn.h +++ b/include/unicorn/unicorn.h @@ -65,7 +65,7 @@ typedef size_t uc_hook; #define UNICORN_DEPRECATED __declspec(deprecated) #else #pragma message( \ - "WARNING: You need to implement UNICORN_DEPRECATED for this compiler") + "WARNING: You need to implement UNICORN_DEPRECATED for this compiler") #define UNICORN_DEPRECATED #endif diff --git a/qemu/target/i386/unicorn.c b/qemu/target/i386/unicorn.c index 2ed4ca336f..3d6968b9b4 100644 --- a/qemu/target/i386/unicorn.c +++ b/qemu/target/i386/unicorn.c @@ -287,7 +287,7 @@ uc_err reg_read(void *_env, int mode, unsigned int regid, void *value, case UC_X86_REG_XMM7: { CHECK_REG_TYPE(uint64_t[2]); uint64_t *dst = (uint64_t *)value; - const ZMMReg* const reg = &env->xmm_regs[regid - UC_X86_REG_XMM0]; + const ZMMReg *const reg = &env->xmm_regs[regid - UC_X86_REG_XMM0]; dst[0] = reg->ZMM_Q(0); dst[1] = reg->ZMM_Q(1); return ret; @@ -314,7 +314,7 @@ uc_err reg_read(void *_env, int mode, unsigned int regid, void *value, case UC_X86_REG_YMM7: { CHECK_REG_TYPE(uint64_t[4]); uint64_t *dst = (uint64_t *)value; - const ZMMReg* const reg = &env->xmm_regs[regid - UC_X86_REG_YMM0]; + const ZMMReg *const reg = &env->xmm_regs[regid - UC_X86_REG_YMM0]; dst[0] = reg->ZMM_Q(0); dst[1] = reg->ZMM_Q(1); dst[2] = reg->ZMM_Q(2); @@ -978,7 +978,7 @@ uc_err reg_read(void *_env, int mode, unsigned int regid, void *value, case UC_X86_REG_XMM31: { CHECK_REG_TYPE(uint64_t[2]); uint64_t *dst = (uint64_t *)value; - const ZMMReg* const reg = &env->xmm_regs[regid - UC_X86_REG_XMM0]; + const ZMMReg *const reg = &env->xmm_regs[regid - UC_X86_REG_XMM0]; dst[0] = reg->ZMM_Q(0); dst[1] = reg->ZMM_Q(1); break; @@ -1009,7 +1009,7 @@ uc_err reg_read(void *_env, int mode, unsigned int regid, void *value, case UC_X86_REG_YMM31: { CHECK_REG_TYPE(uint64_t[4]); uint64_t *dst = (uint64_t *)value; - const ZMMReg* const reg = &env->xmm_regs[regid - UC_X86_REG_YMM0]; + const ZMMReg *const reg = &env->xmm_regs[regid - UC_X86_REG_YMM0]; dst[0] = reg->ZMM_Q(0); dst[1] = reg->ZMM_Q(1); dst[2] = reg->ZMM_Q(2); @@ -1050,7 +1050,7 @@ uc_err reg_read(void *_env, int mode, unsigned int regid, void *value, case UC_X86_REG_ZMM31: { CHECK_REG_TYPE(uint64_t[8]); uint64_t *dst = (uint64_t *)value; - const ZMMReg* const reg = &env->xmm_regs[regid - UC_X86_REG_ZMM0]; + const ZMMReg *const reg = &env->xmm_regs[regid - UC_X86_REG_ZMM0]; dst[0] = reg->ZMM_Q(0); dst[1] = reg->ZMM_Q(1); dst[2] = reg->ZMM_Q(2); diff --git a/qemu/target/tricore/unicorn.c b/qemu/target/tricore/unicorn.c index 43a31f5482..ba27a26481 100644 --- a/qemu/target/tricore/unicorn.c +++ b/qemu/target/tricore/unicorn.c @@ -241,7 +241,7 @@ uc_err reg_write(void *_env, int mode, unsigned int regid, const void *value, break; } } - + CHECK_RET_DEPRECATE(ret, regid); return ret; } diff --git a/tests/unit/test_x86.c b/tests/unit/test_x86.c index a7d3909aab..c3d0b15776 100644 --- a/tests/unit/test_x86.c +++ b/tests/unit/test_x86.c @@ -1799,22 +1799,20 @@ static void test_rex_x64(void) } static bool test_x86_ro_segfault_cb(uc_engine *uc, uc_mem_type type, - uint64_t address, int size, - uint64_t value, void *user_data) + uint64_t address, int size, uint64_t value, + void *user_data) { - const char code[] = - "\xA1\x00\x10\x00\x00\xA1\x00\x10\x00\x00"; + const char code[] = "\xA1\x00\x10\x00\x00\xA1\x00\x10\x00\x00"; OK(uc_mem_write(uc, address, code, sizeof(code) - 1)); return true; } static void test_x86_ro_segfault(void) { - uc_engine* uc; + uc_engine *uc; // mov eax, [0x1000] // mov eax, [0x1000] - const char code[] = - "\xA1\x00\x10\x00\x00\xA1\x00\x10\x00\x00"; + const char code[] = "\xA1\x00\x10\x00\x00\xA1\x00\x10\x00\x00"; uint32_t out; uc_hook hh; @@ -1822,11 +1820,12 @@ static void test_x86_ro_segfault(void) OK(uc_mem_map(uc, 0, 0x1000, UC_PROT_ALL)); OK(uc_mem_write(uc, 0, code, sizeof(code) - 1)); OK(uc_mem_map(uc, 0x1000, 0x1000, UC_PROT_READ)); - - OK(uc_hook_add(uc, &hh, UC_HOOK_MEM_READ, test_x86_ro_segfault_cb, NULL, 1, 0)); + + OK(uc_hook_add(uc, &hh, UC_HOOK_MEM_READ, test_x86_ro_segfault_cb, NULL, 1, + 0)); OK(uc_emu_start(uc, 0, sizeof(code) - 1, 0, 0)); - OK(uc_reg_read(uc, UC_X86_REG_EAX, (void*)&out)); + OK(uc_reg_read(uc, UC_X86_REG_EAX, (void *)&out)); TEST_CHECK(out == 0x001000a1); OK(uc_close(uc)); } From 26268e69af4609d87b7c5f6b8f4d3a2f0be1f0c9 Mon Sep 17 00:00:00 2001 From: Daniel Roethlisberger Date: Tue, 8 Oct 2024 06:48:05 +0200 Subject: [PATCH 16/72] python: Fix assertion failure on ctl_flush_tb() (#2023) --- bindings/python/unicorn/unicorn_py3/unicorn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindings/python/unicorn/unicorn_py3/unicorn.py b/bindings/python/unicorn/unicorn_py3/unicorn.py index 526f06b097..f576542974 100644 --- a/bindings/python/unicorn/unicorn_py3/unicorn.py +++ b/bindings/python/unicorn/unicorn_py3/unicorn.py @@ -1172,7 +1172,7 @@ def context_restore(self, context: UcContext) -> None: @staticmethod def __ctl_encode(ctl: int, op: int, nargs: int) -> int: - assert nargs and check_maxbits(nargs, 4), f'nargs must not exceed value of 15 (got {nargs})' + assert check_maxbits(nargs, 4), f'nargs must not exceed value of 15 (got {nargs})' assert op and check_maxbits(op, 2), f'op must not exceed value of 3 (got {op})' return (op << 30) | (nargs << 26) | ctl From 78580ca8f91027cafc741f65c78c7650103b6683 Mon Sep 17 00:00:00 2001 From: Eli Date: Wed, 9 Oct 2024 09:13:42 +0300 Subject: [PATCH 17/72] Python3 bindings improvements (#2024) * Allow Uc subclasses to use additional constructor args * Add missing conext reg write batch prorotype * Sort uc prototypes for better readability * Redefine internal C API structures * Add ctypes alises to improve readability * Added documentation for ctl methods * Added ctl tcg buffer size accessors * Fix tcg buffer size return type --- .../python/unicorn/unicorn_py3/unicorn.py | 268 ++++++++++++++---- 1 file changed, 211 insertions(+), 57 deletions(-) diff --git a/bindings/python/unicorn/unicorn_py3/unicorn.py b/bindings/python/unicorn/unicorn_py3/unicorn.py index f576542974..5e747005bf 100644 --- a/bindings/python/unicorn/unicorn_py3/unicorn.py +++ b/bindings/python/unicorn/unicorn_py3/unicorn.py @@ -3,32 +3,45 @@ """ from __future__ import annotations -from typing import TYPE_CHECKING, Any, Callable, Dict, Iterable, Iterator, Optional, Sequence, Tuple, Type, TypeVar, Union +from typing import TYPE_CHECKING, Any, Callable, Dict, Generic, Iterable, Iterator, Optional, Sequence, Tuple, Type, TypeVar, Union import ctypes import functools import weakref from unicorn import unicorn_const as uc -from .arch.types import uc_err, uc_engine, uc_context, uc_hook_h, UcReg +from .arch.types import uc_err, uc_engine, uc_context, uc_hook_h, UcReg, VT # __version__ = f'{uc.UC_VERSION_MAJOR}.{uc.UC_VERSION_MINOR}.{uc.UC_VERSION_PATCH}' +MemRegionStruct = Tuple[int, int, int] +TBStruct = Tuple[int, int, int] -class _uc_mem_region(ctypes.Structure): + +class UcTupledStruct(ctypes.Structure, Generic[VT]): + """A base class for structures that may be converted to tuples representing + their fields values. + """ + + @property + def value(self) -> VT: + """Convert structure into a tuple containing its fields values. This method + name is used to maintain consistency with other ctypes types. + """ + + return tuple(getattr(self, fname) for fname, *_ in self.__class__._fields_) # type: ignore + + +class _uc_mem_region(UcTupledStruct[MemRegionStruct]): _fields_ = ( ('begin', ctypes.c_uint64), ('end', ctypes.c_uint64), ('perms', ctypes.c_uint32), ) - @property - def value(self) -> Tuple[int, int, int]: - return tuple(getattr(self, fname) for fname, *_ in self._fields_) - -class uc_tb(ctypes.Structure): - """"TranslationBlock +class uc_tb(UcTupledStruct[TBStruct]): + """"Translation Block """ _fields_ = ( @@ -154,40 +167,59 @@ def __set_prototype(fname: str, restype: Type[ctypes._CData], *argtypes: Type[ct func.restype = restype func.argtypes = argtypes - __set_prototype('uc_version', ctypes.c_uint, ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_int)) - __set_prototype('uc_arch_supported', ctypes.c_bool, ctypes.c_int) - __set_prototype('uc_open', uc_err, ctypes.c_uint, ctypes.c_uint, ctypes.POINTER(uc_engine)) + # declare a few ctypes aliases for brevity + char = ctypes.c_char + s32 = ctypes.c_int32 + u32 = ctypes.c_uint32 + u64 = ctypes.c_uint64 + size_t = ctypes.c_size_t + void_p = ctypes.c_void_p + PTR = ctypes.POINTER + + __set_prototype('uc_arch_supported', ctypes.c_bool, s32) __set_prototype('uc_close', uc_err, uc_engine) - __set_prototype('uc_strerror', ctypes.c_char_p, uc_err) - __set_prototype('uc_errno', uc_err, uc_engine) - __set_prototype('uc_reg_read', uc_err, uc_engine, ctypes.c_int, ctypes.c_void_p) - __set_prototype('uc_reg_write', uc_err, uc_engine, ctypes.c_int, ctypes.c_void_p) - __set_prototype('uc_reg_read_batch', uc_err, uc_engine, ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_void_p), ctypes.c_int) - __set_prototype('uc_reg_write_batch', uc_err, uc_engine, ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_void_p), ctypes.c_int) - __set_prototype('uc_mem_read', uc_err, uc_engine, ctypes.c_uint64, ctypes.POINTER(ctypes.c_char), ctypes.c_size_t) - __set_prototype('uc_mem_write', uc_err, uc_engine, ctypes.c_uint64, ctypes.POINTER(ctypes.c_char), ctypes.c_size_t) - __set_prototype('uc_emu_start', uc_err, uc_engine, ctypes.c_uint64, ctypes.c_uint64, ctypes.c_uint64, ctypes.c_size_t) + __set_prototype('uc_context_alloc', uc_err, uc_engine, PTR(uc_context)) + __set_prototype('uc_context_free', uc_err, uc_context) + __set_prototype('uc_context_reg_read', uc_err, uc_context, s32, void_p) + __set_prototype('uc_context_reg_read_batch', uc_err, uc_context, PTR(s32), PTR(void_p), s32) + __set_prototype('uc_context_reg_write', uc_err, uc_context, s32, void_p) + __set_prototype('uc_context_reg_write_batch', uc_err, uc_context, PTR(s32), void_p, s32) + __set_prototype('uc_context_restore', uc_err, uc_engine, uc_context) + __set_prototype('uc_context_save', uc_err, uc_engine, uc_context) + __set_prototype('uc_context_size', size_t, uc_engine) + __set_prototype('uc_ctl', uc_err, uc_engine, s32) + __set_prototype('uc_emu_start', uc_err, uc_engine, u64, u64, u64, size_t) __set_prototype('uc_emu_stop', uc_err, uc_engine) + __set_prototype('uc_errno', uc_err, uc_engine) + __set_prototype('uc_free', uc_err, void_p) + __set_prototype('uc_hook_add', uc_err, uc_engine, PTR(uc_hook_h), s32, void_p, void_p, u64, u64) __set_prototype('uc_hook_del', uc_err, uc_engine, uc_hook_h) - __set_prototype('uc_mmio_map', uc_err, uc_engine, ctypes.c_uint64, ctypes.c_size_t, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p) - __set_prototype('uc_mem_map', uc_err, uc_engine, ctypes.c_uint64, ctypes.c_size_t, ctypes.c_uint32) - __set_prototype('uc_mem_map_ptr', uc_err, uc_engine, ctypes.c_uint64, ctypes.c_size_t, ctypes.c_uint32, ctypes.c_void_p) - __set_prototype('uc_mem_unmap', uc_err, uc_engine, ctypes.c_uint64, ctypes.c_size_t) - __set_prototype('uc_mem_protect', uc_err, uc_engine, ctypes.c_uint64, ctypes.c_size_t, ctypes.c_uint32) - __set_prototype('uc_query', uc_err, uc_engine, ctypes.c_uint32, ctypes.POINTER(ctypes.c_size_t)) - __set_prototype('uc_context_alloc', uc_err, uc_engine, ctypes.POINTER(uc_context)) - __set_prototype('uc_free', uc_err, ctypes.c_void_p) - __set_prototype('uc_context_save', uc_err, uc_engine, uc_context) - __set_prototype('uc_context_restore', uc_err, uc_engine, uc_context) - __set_prototype('uc_context_size', ctypes.c_size_t, uc_engine) - __set_prototype('uc_context_reg_read', uc_err, uc_context, ctypes.c_int, ctypes.c_void_p) - __set_prototype('uc_context_reg_write', uc_err, uc_context, ctypes.c_int, ctypes.c_void_p) - __set_prototype('uc_context_reg_read_batch', uc_err, uc_context, ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_void_p), ctypes.c_int) - __set_prototype('uc_context_free', uc_err, uc_context) - __set_prototype('uc_mem_regions', uc_err, uc_engine, ctypes.POINTER(ctypes.POINTER(_uc_mem_region)), ctypes.POINTER(ctypes.c_uint32)) - # https://bugs.python.org/issue42880 - __set_prototype('uc_hook_add', uc_err, uc_engine, ctypes.POINTER(uc_hook_h), ctypes.c_int, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_uint64, ctypes.c_uint64) - __set_prototype('uc_ctl', uc_err, uc_engine, ctypes.c_int) + __set_prototype('uc_mem_map', uc_err, uc_engine, u64, size_t, u32) + __set_prototype('uc_mem_map_ptr', uc_err, uc_engine, u64, size_t, u32, void_p) + __set_prototype('uc_mem_protect', uc_err, uc_engine, u64, size_t, u32) + __set_prototype('uc_mem_read', uc_err, uc_engine, u64, PTR(char), size_t) + __set_prototype('uc_mem_regions', uc_err, uc_engine, PTR(PTR(_uc_mem_region)), PTR(u32)) + __set_prototype('uc_mem_unmap', uc_err, uc_engine, u64, size_t) + __set_prototype('uc_mem_write', uc_err, uc_engine, u64, PTR(char), size_t) + __set_prototype('uc_mmio_map', uc_err, uc_engine, u64, size_t, void_p, void_p, void_p, void_p) + __set_prototype('uc_open', uc_err, u32, u32, PTR(uc_engine)) + __set_prototype('uc_query', uc_err, uc_engine, u32, PTR(size_t)) + __set_prototype('uc_reg_read', uc_err, uc_engine, s32, void_p) + __set_prototype('uc_reg_read_batch', uc_err, uc_engine, PTR(s32), PTR(void_p), s32) + __set_prototype('uc_reg_write', uc_err, uc_engine, s32, void_p) + __set_prototype('uc_reg_write_batch', uc_err, uc_engine, PTR(s32), PTR(void_p), s32) + __set_prototype('uc_strerror', ctypes.c_char_p, uc_err) + __set_prototype('uc_version', u32, PTR(s32), PTR(s32)) + + # TODO: + # __set_prototype('uc_context_reg_read2', uc_err, uc_context, s32, void_p, PTR(size_t)) + # __set_prototype('uc_context_reg_read_batch2', uc_err, uc_context, PTR(s32), PTR(void_p), PTR(size_t), s32) + # __set_prototype('uc_context_reg_write2', uc_err, uc_context, s32, void_p, PTR(size_t)) + # __set_prototype('uc_context_reg_write_batch2', uc_err, uc_context, PTR(s32), PTR(void_p), PTR(size_t), s32) + # __set_prototype('uc_reg_read2', uc_err, uc_engine, s32, void_p, PTR(size_t)) + # __set_prototype('uc_reg_read_batch2', uc_err, uc_engine, PTR(s32), PTR(void_p), PTR(size_t), s32) + # __set_prototype('uc_reg_write2', uc_err, uc_engine, s32, void_p, PTR(size_t)) + # __set_prototype('uc_reg_write_batch2', uc_err, uc_engine, PTR(s32), PTR(void_p), PTR(size_t), s32) uclib = __load_uc_lib() @@ -566,7 +598,10 @@ def __replace(seq: Tuple, item, repl) -> Tuple: return seq[:i] + tuple([repl]) + seq[i + 1:] - def __new_uc_subclass(cls, arch: int, mode: int): + def __new_uc_subclass(cls, arch: int, mode: int, *args, **kwargs): + # this method has to contain *args and **kwargs to allow Uc subclasses + # to use additional arguments in their constructors + # resolve the appropriate Uc subclass subcls = Uc.__new__(cls, arch, mode) @@ -1209,89 +1244,209 @@ def __ctl_wr(self, ctl: int, arg0: Arg, arg1: Arg): self.ctl(ctl, uc.UC_CTL_IO_READ_WRITE, carg0, ctypes.byref(carg1)) - return carg1 + return carg1.value def ctl_get_mode(self) -> int: + """Retrieve current processor mode. + + Returns: current mode (see UC_MODE_* constants) + """ + return self.__ctl_r(uc.UC_CTL_UC_MODE, (ctypes.c_int, None) ) def ctl_get_page_size(self) -> int: + """Retrieve target page size. + + Returns: page size in bytes + """ + return self.__ctl_r(uc.UC_CTL_UC_PAGE_SIZE, (ctypes.c_uint32, None) ) def ctl_set_page_size(self, val: int) -> None: + """Set target page size. + + Args: + val: page size to set (in bytes) + + Raises: `UcError` in any of the following cases: + - Unicorn architecture is not ARM + - Unicorn has already completed its initialization + - Page size is not a power of 2 + """ + self.__ctl_w(uc.UC_CTL_UC_PAGE_SIZE, (ctypes.c_uint32, val) ) def ctl_get_arch(self) -> int: + """Retrieve target architecture. + + Returns: current architecture (see UC_ARCH_* constants) + """ + return self.__ctl_r(uc.UC_CTL_UC_ARCH, (ctypes.c_int, None) ) def ctl_get_timeout(self) -> int: + """Retrieve emulation timeout. + + Returns: timeout value set on emulation start + """ + return self.__ctl_r(uc.UC_CTL_UC_TIMEOUT, (ctypes.c_uint64, None) ) - def ctl_exits_enabled(self, val: bool) -> None: + def ctl_exits_enabled(self, enable: bool) -> None: + """Instruct Unicorn whether to respect emulation exit points or ignore them. + + Args: + enable: `True` to enable exit points, `False` to ignore them + """ + self.__ctl_w(uc.UC_CTL_UC_USE_EXITS, - (ctypes.c_int, val) + (ctypes.c_int, enable) ) def ctl_get_exits_cnt(self) -> int: + """Retrieve emulation exit points count. + + Returns: number of emulation exit points + + Raises: `UcErro` if Unicorn is set to ignore exits + """ + return self.__ctl_r(uc.UC_CTL_UC_EXITS_CNT, (ctypes.c_size_t, None) ) def ctl_get_exits(self) -> Sequence[int]: + """Retrieve emulation exit points. + + Returns: a tuple of all emulation exit points + + Raises: `UcErro` if Unicorn is set to ignore exits + """ + count = self.ctl_get_exits_cnt() arr = (ctypes.c_uint64 * count)() self.ctl(uc.UC_CTL_UC_EXITS, uc.UC_CTL_IO_READ, ctypes.cast(arr, ctypes.c_void_p), ctypes.c_size_t(count)) - return tuple(i for i in arr) + return tuple(arr) def ctl_set_exits(self, exits: Sequence[int]) -> None: - arr = (ctypes.c_uint64 * len(exits))() + """Set emulation exit points. + + Args: + exits: a list of emulation exit points to set + + Raises: `UcErro` if Unicorn is set to ignore exits + """ - for idx, exit in enumerate(exits): - arr[idx] = exit + arr = (ctypes.c_uint64 * len(exits))(*exits) - self.ctl(uc.UC_CTL_UC_EXITS, uc.UC_CTL_IO_WRITE, ctypes.cast(arr, ctypes.c_void_p), ctypes.c_size_t(len(exits))) + self.ctl(uc.UC_CTL_UC_EXITS, uc.UC_CTL_IO_WRITE, ctypes.cast(arr, ctypes.c_void_p), ctypes.c_size_t(len(arr))) def ctl_get_cpu_model(self) -> int: + """Retrieve target processor model. + + Returns: target cpu model (see UC_CPU_* constants) + """ + return self.__ctl_r(uc.UC_CTL_CPU_MODEL, (ctypes.c_int, None) ) - def ctl_set_cpu_model(self, val: int) -> None: + def ctl_set_cpu_model(self, model: int) -> None: + """Set target processor model. + + Args: + model: cpu model to set (see UC_CPU_* constants) + + Raises: `UcError` in any of the following cases: + - `model` is not a valid cpu model + - Requested cpu model is incompatible with current mode + - Unicorn has already completed its initialization + """ + self.__ctl_w(uc.UC_CTL_CPU_MODEL, - (ctypes.c_int, val) + (ctypes.c_int, model) ) - def ctl_remove_cache(self, addr: int, end: int) -> None: + def ctl_remove_cache(self, lbound: int, ubound: int) -> None: + """Invalidate translation cache for a specified region. + + Args: + lbound: region lower bound + ubound: region upper bound + + Raises: `UcError` in case the provided range bounds are invalid + """ + self.__ctl_w(uc.UC_CTL_TB_REMOVE_CACHE, - (ctypes.c_uint64, addr), - (ctypes.c_uint64, end) + (ctypes.c_uint64, lbound), + (ctypes.c_uint64, ubound) ) - def ctl_request_cache(self, addr: int): + def ctl_request_cache(self, addr: int) -> TBStruct: + """Get translation cache info for a specified address. + + Args: + addr: address to get its translation cache info + + Returns: a 3-tuple containing the base address, instructions count and + size of the translation block containing the specified address + """ + return self.__ctl_wr(uc.UC_CTL_TB_REQUEST_CACHE, (ctypes.c_uint64, addr), (uc_tb, None) ) def ctl_flush_tb(self) -> None: + """Flush the entire translation cache. + """ + self.__ctl_w(uc.UC_CTL_TB_FLUSH) - def ctl_tlb_mode(self, mode: int) -> None: + def ctl_set_tlb_mode(self, mode: int) -> None: + """Set TLB mode. + + Args: + mode: tlb mode to use (see UC_TLB_* constants) + """ + self.__ctl_w(uc.UC_CTL_TLB_TYPE, (ctypes.c_uint, mode) ) + def ctl_get_tcg_buffer_size(self) -> int: + """Retrieve TCG buffer size. + + Returns: buffer size (in bytes) + """ + + return self.__ctl_r(uc.UC_CTL_TCG_BUFFER_SIZE, + (ctypes.c_uint32, None) + ) + + def ctl_set_tcg_buffer_size(self, size: int) -> None: + """Set TCG buffer size. + + Args: + size: new size to set + """ + + self.__ctl_w(uc.UC_CTL_TCG_BUFFER_SIZE, + (ctypes.c_uint32, size) + ) + class UcContext(RegStateManager): """Unicorn internal context. @@ -1394,4 +1549,3 @@ def __del__(self) -> None: __all__ = ['Uc', 'UcContext', 'ucsubclass', 'UcError', 'uc_version', 'version_bind', 'uc_arch_supported', 'debug'] - From 9f578946d5b5997cc07de1b66781b4288f75c804 Mon Sep 17 00:00:00 2001 From: Eli Date: Sun, 13 Oct 2024 08:14:10 +0300 Subject: [PATCH 18/72] Revamp Python regression tests suite (#2022) * Fix Python regression test suite (partial) * Fix Python regression test suite * Add a test for mapping at high addresses * Add ctl tests --- tests/regress/arm_bx_unmapped.py | 172 +++++----- tests/regress/arm_bxeq_hang.py | 12 +- tests/regress/arm_fp_vfp_disabled.py | 49 ++- tests/regress/arm_init_input_crash.py | 56 ++-- tests/regress/arm_memcpy_neon.py | 60 ++-- tests/regress/arm_movr12_hang.py | 14 +- tests/regress/arm_vldr_invalid.py | 14 +- tests/regress/arm_wfi_first_insn_of_tb.py | 33 +- tests/regress/bad_ram.py | 22 +- tests/regress/callback-pc.py | 105 +++--- tests/regress/core_ctl.py | 177 +++++++++++ tests/regress/emu_clear_errors.py | 67 ++-- .../ensure_typedef_consts_generated.py | 18 +- tests/regress/fpu_ip.py | 83 +++-- tests/regress/fpu_mem_write.py | 40 +-- tests/regress/hang.py | 77 +++-- tests/regress/high_mem.py | 66 ++++ tests/regress/hook_add_crash.py | 20 +- tests/regress/hook_code_add_del.py | 63 ++-- tests/regress/hook_code_stop_emu.py | 111 +++---- tests/regress/hook_readonly_write_local.py | 33 +- tests/regress/init.py | 48 ++- tests/regress/jmp_ebx_hang.py | 22 +- tests/regress/jumping.py | 222 ++++++------- tests/regress/leaked_refs.py | 75 +++-- tests/regress/memmap.py | 27 +- tests/regress/memmap_segfault.py | 35 +- tests/regress/mips_branch_delay.py | 38 ++- tests/regress/mips_cp1.py | 30 +- tests/regress/mips_except.py | 48 ++- tests/regress/mips_kernel_mmu.py | 21 +- tests/regress/mips_single_step_sp.py | 53 ++-- tests/regress/mips_syscall_pc.py | 20 +- tests/regress/mov_gs_eax.py | 24 +- tests/regress/movsd.py | 38 ++- tests/regress/osx_qemu_thread_create_crash.py | 10 +- tests/regress/potential_memory_leak.py | 19 +- tests/regress/pshufb.py | 12 +- tests/regress/reg_write_sign_extension.py | 17 +- tests/regress/regress.py | 70 ++-- tests/regress/rep_hook.py | 34 +- tests/regress/run_across_bb.py | 72 +++-- tests/regress/sparc64.py | 37 ++- tests/regress/sparc_reg.py | 300 ++++++------------ .../tcg_liveness_analysis_bug_issue-287.py | 282 ++++++++-------- tests/regress/translator_buffer.py | 90 +++--- tests/regress/vld.py | 182 +++++------ tests/regress/write_before_map.py | 11 +- tests/regress/wrong_rip_arm.py | 45 +-- tests/regress/x86_64_conditional_jump.py | 28 +- tests/regress/x86_64_eflags.py | 19 +- tests/regress/x86_64_msr.py | 19 +- tests/regress/x86_eflags.py | 19 +- tests/regress/x86_fldt_fsqrt.py | 25 +- tests/regress/x86_gdt.py | 42 ++- tests/regress/x86_ld_crash.py | 54 ++-- tests/regress/x86_self_modifying.py | 13 +- tests/regress/x86_set_ip.py | 47 ++- 58 files changed, 1944 insertions(+), 1496 deletions(-) create mode 100644 tests/regress/core_ctl.py create mode 100644 tests/regress/high_mem.py diff --git a/tests/regress/arm_bx_unmapped.py b/tests/regress/arm_bx_unmapped.py index e18d345985..26dab3693f 100644 --- a/tests/regress/arm_bx_unmapped.py +++ b/tests/regress/arm_bx_unmapped.py @@ -1,93 +1,111 @@ -from __future__ import print_function + +import regress + from unicorn import * from unicorn.arm_const import * -import regress -# code to be emulated -''' -ins = { - 0x00008cd4: """ - push {r11} - add r11, sp, #0 - mov r3, pc - mov r0, r3 - sub sp, r11, #0 - pop {r11} - bx lr - """, - 0x00008cf0: """ - push {r11} - add r11, sp, #0 - push {r6} - add r6, pc, $1 - bx r6 - .code 16 - mov r3, pc - add r3, $0x4 - push {r3} - pop {pc} - .code 32 - pop {r6} - mov r0, r3 - sub sp, r11, #0 - pop {r11} - bx lr - """, - 0x00008d20: """ - push {r11} - add r11, sp, #0 - mov r3, lr - mov r0, r3 - sub sp, r11, #0 - pop {r11} - bx lr - """, - 0x00008d68: "bl 0x8cd4\n" - "mov r4, r0\n" - "bl 0x8cf0\n" - "mov r3, r0\n" - "add r4, r4, r3\n" - "bl 0x8d20\n" - "mov r3, r0\n" - "add r2, r4, r3", -} -''' + +MAIN_ADDRESS = 0x8d68 +ADDRESS = MAIN_ADDRESS & ~(0x1000 - 1) +STACK_ADDR = ADDRESS + 0x1000 + class BxTwiceTest(regress.RegressTest): def runTest(self): - ADDRESS = 0x8000 - MAIN_ADDRESS = 0x8d68 - STACK_ADDR = ADDRESS + 0x1000 - + # code to be emulated code = { - 0x8cf0: '\x04\xb0-\xe5\x00\xb0\x8d\xe2\x04`-\xe5\x01`\x8f\xe2\x16\xff/\xe1{F\x03\xf1\x04\x03\x08\xb4\x00\xbd\x00\x00\x04`\x9d\xe4\x03\x00\xa0\xe1\x00\xd0K\xe2\x04\xb0\x9d\xe4\x1e\xff/\xe1', - 0x8d20: '\x04\xb0-\xe5\x00\xb0\x8d\xe2\x0e0\xa0\xe1\x03\x00\xa0\xe1\x00\xd0K\xe2\x04\xb0\x9d\xe4\x1e\xff/\xe1', - 0x8cd4: '\x04\xb0-\xe5\x00\xb0\x8d\xe2\x0f0\xa0\xe1\x03\x00\xa0\xe1\x00\xd0K\xe2\x04\xb0\x9d\xe4\x1e\xff/\xe1', - 0x8d68: '\xd9\xff\xff\xeb\x00@\xa0\xe1\xde\xff\xff\xeb\x000\xa0\xe1\x03@\x84\xe0\xe7\xff\xff\xeb\x000\xa0\xe1\x03 \x84\xe0' + 0x8cd4: ( + b'\x04\xb0\x2d\xe5' # 8cd4 push {r11} + b'\x00\xb0\x8d\xe2' # 8cd8 add r11, sp, #0 + b'\x0f\x30\xa0\xe1' # 8cdc mov r3, pc + b'\x03\x00\xa0\xe1' # 8ce0 mov r0, r3 + b'\x00\xd0\x4b\xe2' # 8ce4 sub sp, r11, #0 + b'\x04\xb0\x9d\xe4' # 8ce8 pop {r11} + b'\x1e\xff\x2f\xe1' # 8cec bx lr + ), + 0x8cf0: ( + b'\x04\xb0\x2d\xe5' # 8cf0 push {r11} + b'\x00\xb0\x8d\xe2' # 8cf4 add r11, sp, #0 + b'\x04\x60\x2d\xe5' # 8cf8 push {r6} + b'\x01\x60\x8f\xe2' # 8cfc add r6, pc, $1 + b'\x16\xff\x2f\xe1' # 8d00 bx r6 + # .thumb + b'\x7b\x46' # 8d04 mov r3, pc + b'\x03\xf1\x08\x03' # 8d06 add r3, $0x8 # elicn: used to be $0x4 but it kept failing + b'\x08\xb4' # 8d0a push {r3} + b'\x00\xbd' # 8d0c pop {pc} + b'\x00\x00' # 8d0e (alignment) + # .arm + b'\x04\x60\x9d\xe4' # 8d10 pop {r6} + b'\x03\x00\xa0\xe1' # 8d14 mov r0, r3 + b'\x00\xd0\x4b\xe2' # 8d18 sub sp, r11, #0 + b'\x04\xb0\x9d\xe4' # 8d1c pop {r11} + b'\x1e\xff\x2f\xe1' # 8d20 bx lr + ), + 0x8d24: ( # elicn: used to be 0x8d20 but it caused this block to overlap with the previous one + b'\x04\xb0\x2d\xe5' # 8d24 push {r11} + b'\x00\xb0\x8d\xe2' # 8d28 add r11, sp, #0 + b'\x0e\x30\xa0\xe1' # 8d2c mov r3, lr + b'\x03\x00\xa0\xe1' # 8d20 mov r0, r3 + b'\x00\xd0\x4b\xe2' # 8d34 sub sp, r11, #0 + b'\x04\xb0\x9d\xe4' # 8d38 pop {r11} + b'\x1e\xff\x2f\xe1' # 8d3c bx lr + ), + 0x8d68: ( + b'\xd9\xff\xff\xeb' # 8d68 bl 0x8cd4 <-- MAIN_ADDRESS + b'\x00\x40\xa0\xe1' # 8d6c mov r4, r0 + b'\xde\xff\xff\xeb' # 8d70 bl 0x8cf0 + b'\x00\x30\xa0\xe1' # 8d74 mov r3, r0 + b'\x03\x40\x84\xe0' # 8d78 add r4, r4, r3 + b'\xe8\xff\xff\xeb' # 8d7c bl 0x8d24 + b'\x00\x30\xa0\xe1' # 8d80 mov r3, r0 + b'\x03\x20\x84\xe0' # 8d84 add r2, r4, r3 + ) } - try: - mu = Uc(UC_ARCH_ARM, UC_MODE_ARM) - # map 2MB memory for this emulation - mu.mem_map(ADDRESS, 2 * 1024 * 1024) + mu = Uc(UC_ARCH_ARM, UC_MODE_ARM) + + mu.mem_map(ADDRESS, 0x1000) + + # write machine code to be emulated to memory + for addr, c in code.items(): + regress.logger.debug("Writing %d bytes to %#x", len(c), addr) + mu.mem_write(addr, c) + + # initialize machine registers + mu.reg_write(UC_ARM_REG_PC, MAIN_ADDRESS) + mu.reg_write(UC_ARM_REG_SP, STACK_ADDR) + + regress.logger.debug("Starting emulation") + + # trace code only if we are debugging it + if regress.logger.isEnabledFor(regress.logging.DEBUG): + def __hook_code(uc, addr, size, _): + cpsr, r0, r3, r4, r6 = uc.reg_read_batch(( + UC_ARM_REG_CPSR, + UC_ARM_REG_R0, + UC_ARM_REG_R3, + UC_ARM_REG_R4, + UC_ARM_REG_R6 + )) + + is_thumb = (cpsr >> 5) & 0b1 + + opcode = uc.mem_read(addr, size).hex() - # write machine code to be emulated to memory - for addr, c in code.items(): - print("Writing chunk to 0x{:x}".format(addr)) - mu.mem_write(addr, c) + regress.logger.debug( + "%-2s PC = %#06x | opcode = %-8s [R0 = %#06x, R3 = %#06x, R4 = %#07x, R6 = %#06x]", + "T" if is_thumb else "", addr, opcode, r0, r3, r4, r6 + ) - # initialize machine registers - mu.reg_write(UC_ARM_REG_SP, STACK_ADDR) + mu.hook_add(UC_HOOK_CODE, __hook_code) - print("Starting emulation") + mu.emu_start(MAIN_ADDRESS, MAIN_ADDRESS + len(code[MAIN_ADDRESS])) - # emulate code in infinite time & unlimited instructions - mu.emu_start(MAIN_ADDRESS, MAIN_ADDRESS + len(code[MAIN_ADDRESS])) + regress.logger.debug("Emulation done") - print("Emulation done") + self.assertEqual(0x8ce4 + 0x8d10 + 0x8d80, mu.reg_read(UC_ARM_REG_R2)) - r2 = mu.reg_read(UC_ARM_REG_R2) - print(">>> r2: 0x{:08x}".format(r2)) - except UcError as e: - self.fail("ERROR: %s" % e) +if __name__ == '__main__': + regress.main() diff --git a/tests/regress/arm_bxeq_hang.py b/tests/regress/arm_bxeq_hang.py index 5040d72581..1eedf4b6eb 100755 --- a/tests/regress/arm_bxeq_hang.py +++ b/tests/regress/arm_bxeq_hang.py @@ -1,28 +1,30 @@ #!/usr/bin/python +import regress + from unicorn import * from unicorn.arm_const import * -import regress class BxHang(regress.RegressTest): - def runTest(self): uc = Uc(UC_ARCH_ARM, UC_MODE_ARM) uc.mem_map(0x1000, 0x1000) - uc.mem_write(0x1000, '1eff2f010000a0e1'.decode('hex')) # bxeq lr; mov r0, r0 + uc.mem_write(0x1000, b'\x1e\xff\x2f\x01\x00\x00\xa0\xe1') # bxeq lr; mov r0, r0 uc.count = 0 def hook_block(uc, addr, *args): - print 'enter block 0x%04x' % addr + regress.logger.debug('enter block %#06x', addr) uc.count += 1 uc.reg_write(UC_ARM_REG_LR, 0x1004) uc.hook_add(UC_HOOK_BLOCK, hook_block) - print 'block should only run once' + + regress.logger.debug('block should only run once') uc.emu_start(0x1000, 0x1004) self.assertEqual(uc.count, 1) + if __name__ == '__main__': regress.main() diff --git a/tests/regress/arm_fp_vfp_disabled.py b/tests/regress/arm_fp_vfp_disabled.py index b8003a3648..9bd2245c2d 100755 --- a/tests/regress/arm_fp_vfp_disabled.py +++ b/tests/regress/arm_fp_vfp_disabled.py @@ -1,44 +1,41 @@ #!/usr/bin/python -# coding=utf8 # Added by Peter Mackay, relating to issue 571 # "ARM NEON/VFP support seems to exist but is disabled by default" # https://github.com/unicorn-engine/unicorn/issues/571 +import regress + from unicorn import * from unicorn.arm_const import * -import regress + +CODE = ( + b'\x11\xEE\x50\x1F' # MRC p15, #0, r1, c1, c0, #2 + b'\x41\xF4\x70\x01' # ORR r1, r1, #(0xf << 20) + b'\x01\xEE\x50\x1F' # MCR p15, #0, r1, c1, c0, #2 + b'\x4F\xF0\x00\x01' # MOV r1, #0 + b'\x07\xEE\x95\x1F' # MCR p15, #0, r1, c7, c5, #4 + b'\x4F\xF0\x80\x40' # MOV r0,#0x40000000 + b'\xE8\xEE\x10\x0A' # FMXR FPEXC, r0 + b'\x2d\xed\x02\x8b' # vpush {d8} +) + +BASE = 0x1000 class FpVfpDisabled(regress.RegressTest): def runTest(self): - # MRC p15, #0, r1, c1, c0, #2 - # ORR r1, r1, #(0xf << 20) - # MCR p15, #0, r1, c1, c0, #2 - # MOV r1, #0 - # MCR p15, #0, r1, c7, c5, #4 - # MOV r0,#0x40000000 - # FMXR FPEXC, r0 - code = '11EE501F' - code += '41F47001' - code += '01EE501F' - code += '4FF00001' - code += '07EE951F' - code += '4FF08040' - code += 'E8EE100A' - # vpush {d8} - code += '2ded028b' - - address = 0x1000 mem_size = 0x1000 - code_bytes = code.decode('hex') - + uc = Uc(UC_ARCH_ARM, UC_MODE_THUMB) - uc.mem_map(address, mem_size) - uc.mem_write(address, code_bytes) - uc.reg_write(UC_ARM_REG_SP, address + mem_size) - uc.emu_start(address + 1, address + len(code_bytes)) + + uc.mem_map(BASE, mem_size) + uc.mem_write(BASE, CODE) + uc.reg_write(UC_ARM_REG_SP, BASE + mem_size - 4) + + uc.emu_start(BASE + 1, BASE + len(CODE)) + if __name__ == '__main__': regress.main() diff --git a/tests/regress/arm_init_input_crash.py b/tests/regress/arm_init_input_crash.py index ecafbfd29e..aa79a19cf4 100755 --- a/tests/regress/arm_init_input_crash.py +++ b/tests/regress/arm_init_input_crash.py @@ -3,39 +3,44 @@ # Python sample ported by Loi Anh Tuan # +import regress -from __future__ import print_function from unicorn import * from unicorn.arm_const import * # code to be emulated -ARM_CODE = "\x37\x00\xa0\xe3\x03\x10\x42\xe0" # mov r0, #0x37; sub r1, r2, r3 -THUMB_CODE = "\x83\xb0" # sub sp, #0xc +ARM_CODE = ( + b"\x37\x00\xa0\xe3" # mov r0, #0x37 + b"\x03\x10\x42\xe0" # sub r1, r2, r3 +) + +THUMB_CODE = b"\x83\xb0" # sub sp, #0xc + # memory address where emulation starts -ADDRESS = 0xF0000000 +ADDRESS = 0xF0000000 # callback for tracing basic blocks def hook_block(uc, address, size, user_data): - print(">>> Tracing basic block at 0x%x, block size = 0x%x" %(address, size)) + regress.logger.debug(">>> Tracing basic block at %#x, block size = %#x", address, size) # callback for tracing instructions def hook_code(uc, address, size, user_data): - print(">>> Tracing instruction at 0x%x, instruction size = %u" %(address, size)) + regress.logger.debug(">>> Tracing instruction at %#x, instruction size = %u", address, size) -# Test ARM -def test_arm(): - print("Emulate ARM code") - try: +class TestInitInputCrash(regress.RegressTest): + def test_arm(self): + regress.logger.debug("Emulate ARM code") + # Initialize emulator in ARM mode mu = Uc(UC_ARCH_ARM, UC_MODE_ARM) - + mem_size = 2 * (1024 * 1024) mu.mem_map(ADDRESS, mem_size) - + stack_address = ADDRESS + mem_size stack_size = stack_address # >>> here huge memory size mu.mem_map(stack_address, stack_size) @@ -58,20 +63,16 @@ def test_arm(): mu.emu_start(ADDRESS, ADDRESS + len(ARM_CODE)) # now print out some registers - print(">>> Emulation done. Below is the CPU context") + regress.logger.debug(">>> Emulation done. Below is the CPU context") r0 = mu.reg_read(UC_ARM_REG_R0) r1 = mu.reg_read(UC_ARM_REG_R1) - print(">>> R0 = 0x%x" %r0) - print(">>> R1 = 0x%x" %r1) - - except UcError as e: - print("ERROR: %s" % e) + regress.logger.debug(">>> R0 = %#x", r0) + regress.logger.debug(">>> R1 = %#x", r1) + def test_thumb(self): + regress.logger.debug("Emulate THUMB code") -def test_thumb(): - print("Emulate THUMB code") - try: # Initialize emulator in thumb mode mu = Uc(UC_ARCH_ARM, UC_MODE_THUMB) @@ -91,19 +92,14 @@ def test_thumb(): mu.hook_add(UC_HOOK_CODE, hook_code) # emulate machine code in infinite time - mu.emu_start(ADDRESS, ADDRESS + len(THUMB_CODE)) + mu.emu_start(ADDRESS | 0b1, ADDRESS + len(THUMB_CODE)) # now print out some registers - print(">>> Emulation done. Below is the CPU context") + regress.logger.debug(">>> Emulation done. Below is the CPU context") sp = mu.reg_read(UC_ARM_REG_SP) - print(">>> SP = 0x%x" %sp) - - except UcError as e: - print("ERROR: %s" % e) + regress.logger.debug(">>> SP = %#x", sp) if __name__ == '__main__': - test_arm() - print("=" * 20) - test_thumb() + regress.main() diff --git a/tests/regress/arm_memcpy_neon.py b/tests/regress/arm_memcpy_neon.py index 56fdd80ff3..c8fc8a6029 100644 --- a/tests/regress/arm_memcpy_neon.py +++ b/tests/regress/arm_memcpy_neon.py @@ -1,3 +1,6 @@ + +import regress + from unicorn import * from unicorn.arm_const import * @@ -32,37 +35,42 @@ COPY_SRC = 0x1000 COPY_DST = 0x2000 COPY_LEN = 8 -bs = b'c8' * COPY_LEN +DATA = b'c8' * COPY_LEN + +class ArmMemcpy(regress.RegressTest): + + def test_arm_memcpy(self): + uc = Uc(UC_ARCH_ARM, UC_MODE_ARM) + + uc.mem_map(COPY_SRC, 0x1000) + uc.mem_map(COPY_DST, 0x1000) + uc.mem_map(BASE & ~(0x1000 - 1), 0x1000) + uc.mem_write(COPY_SRC, DATA) + uc.mem_write(BASE, bytes(SHELLCODE)) -uc = Uc(UC_ARCH_ARM, UC_MODE_ARM) + uc.reg_write_batch(( + (UC_ARM_REG_R12, COPY_DST), + (UC_ARM_REG_R1, COPY_SRC), + (UC_ARM_REG_R2, COPY_LEN), + (UC_ARM_REG_R3, 0x24) + )) -uc.mem_map(COPY_SRC, 0x1000) -uc.mem_map(COPY_DST, 0x1000) -uc.mem_map(BASE & ~(0x1000 - 1), 0x1000) -uc.mem_write(COPY_SRC, bs) -uc.mem_write(BASE, bytes(SHELLCODE)) + # enable_vfp -uc.reg_write_batch(( - (UC_ARM_REG_R12, COPY_DST), - (UC_ARM_REG_R1, COPY_SRC), - (UC_ARM_REG_R2, COPY_LEN), - (UC_ARM_REG_R3, 0x24) -)) + # coproc=15, is64=0, sec=0, CRn=1, CRm=0, opc1=0, opc2=2 + CPACR = (15, 0, 0, 1, 0, 0, 2) -# enable_vfp + cpacr = uc.reg_read(UC_ARM_REG_CP_REG, CPACR) + uc.reg_write(UC_ARM_REG_CP_REG, CPACR + (cpacr | (0b11 << 20) | (0b11 << 22),)) + uc.reg_write(UC_ARM_REG_FPEXC, (0b1 << 30)) -# coproc=15, is64=0, sec=0, CRn=1, CRm=0, opc1=0, opc2=2 -CPACR = (15, 0, 0, 1, 0, 0, 2) + uc.emu_start(BASE, BASE + len(SHELLCODE)) + src = uc.mem_read(COPY_SRC, len(DATA)) + dst = uc.mem_read(COPY_DST, len(DATA)) -cpacr = uc.reg_read(UC_ARM_REG_CP_REG, CPACR) -uc.reg_write(UC_ARM_REG_CP_REG, CPACR + (cpacr | (0b11 << 20) | (0b11 << 22),)) -uc.reg_write(UC_ARM_REG_FPEXC, (0b1 << 30)) + self.assertEqual(DATA, src) + self.assertEqual(DATA, dst) -uc.emu_start(BASE, BASE + len(SHELLCODE)) -src = uc.mem_read(COPY_SRC, len(bs)) -dst = uc.mem_read(COPY_DST, len(bs)) -print(f'''memcpy result: - from: {bytes(src)} - to: {bytes(dst)} -''') +if __name__ == '__main__': + regress.main() diff --git a/tests/regress/arm_movr12_hang.py b/tests/regress/arm_movr12_hang.py index 1bb276e02b..ac779ce48a 100755 --- a/tests/regress/arm_movr12_hang.py +++ b/tests/regress/arm_movr12_hang.py @@ -1,23 +1,24 @@ #!/usr/bin/python +import regress + from unicorn import * from unicorn.arm_const import * -import regress class MovHang(regress.RegressTest): def runTest(self): uc = Uc(UC_ARCH_ARM, UC_MODE_ARM) uc.mem_map(0x1000, 0x1000) - uc.mem_write(0x1000, '00c000e3'.decode('hex')) # movw r12, #0 + uc.mem_write(0x1000, b'\x00\xc0\x00\xe3') # movw r12, #0 def hook_block(uc, addr, *args): - print 'enter block 0x%04x' % addr + regress.logger.debug('enter block 0x%#06x', addr) uc.count += 1 uc.reg_write(UC_ARM_REG_R12, 0x123) - self.assertEquals(uc.reg_read(UC_ARM_REG_R12), 0x123) + self.assertEqual(0x123, uc.reg_read(UC_ARM_REG_R12)) uc.hook_add(UC_HOOK_BLOCK, hook_block) uc.count = 0 @@ -25,8 +26,9 @@ def hook_block(uc, addr, *args): #print 'block should only run once' uc.emu_start(0x1000, 0x1004, timeout=500) - self.assertEquals(uc.reg_read(UC_ARM_REG_R12), 0x0) - self.assertEquals(uc.count, 1) + self.assertEqual(0x0, uc.reg_read(UC_ARM_REG_R12)) + self.assertEqual(uc.count, 1) + if __name__ == '__main__': regress.main() diff --git a/tests/regress/arm_vldr_invalid.py b/tests/regress/arm_vldr_invalid.py index febf93e733..eeaaac0d93 100755 --- a/tests/regress/arm_vldr_invalid.py +++ b/tests/regress/arm_vldr_invalid.py @@ -1,18 +1,24 @@ #!/usr/bin/python +import regress + from unicorn import * from unicorn.arm_const import * -import regress class VldrPcInsn(regress.RegressTest): def runTest(self): uc = Uc(UC_ARCH_ARM, UC_MODE_ARM) + uc.mem_map(0x1000, 0x1000) - uc.mem_write(0x1000, 'ed9f8a3d'.decode('hex')) # vldr s16, [pc, #244] - # this will raise invalid insn - uc.emu_start(0x1000, 0x1004) + uc.mem_write(0x1000, b'\xed\x9f\x8a\x3d') # vldr s16, [pc, #244] + + with self.assertRaises(UcError) as ex: + uc.emu_start(0x1000, 0x1004) + + self.assertEqual(UC_ERR_INSN_INVALID, ex.exception.errno) + if __name__ == '__main__': regress.main() diff --git a/tests/regress/arm_wfi_first_insn_of_tb.py b/tests/regress/arm_wfi_first_insn_of_tb.py index 65fe92a31c..7db9ed7139 100644 --- a/tests/regress/arm_wfi_first_insn_of_tb.py +++ b/tests/regress/arm_wfi_first_insn_of_tb.py @@ -1,15 +1,28 @@ + +import regress + from unicorn import * from unicorn.arm_const import * -# ADD R0, R10, R0; -# B L0; -# L0: -# ADD R0, R10, R0; <--- we stop at here, the first instruction of the next TB. -code = b'\x00\x00\x8a\xe0\xff\xff\xff\xea\x00\x00\x8a\xe0' -address = 0x1000 +CODE = ( + b'\x00\x00\x8a\xe0' # ADD R0, R10, R0 + b'\xff\xff\xff\xea' # B L0 + b'\x00\x00\x8a\xe0' # L0: ADD R0, R10, R0 <-- we stop here, the first instruction of the next TB +) + +BASE = 0x1000 + + +class TestARMFirstInsn(regress.RegressTest): + def runTest(self): + mu = Uc(UC_ARCH_ARM, UC_MODE_ARM) + + mu.mem_map(BASE, 0x1000) + mu.mem_write(BASE, CODE) + + mu.emu_start(BASE, BASE + len(CODE) - 4) + -mu = Uc(UC_ARCH_ARM, UC_MODE_ARM) -mu.mem_map(address, 0x1000) -mu.mem_write(address, code) -mu.emu_start(address, address + len(code) - 4) \ No newline at end of file +if __name__ == '__main__': + regress.main() diff --git a/tests/regress/bad_ram.py b/tests/regress/bad_ram.py index a74fa495a1..041da81d1f 100755 --- a/tests/regress/bad_ram.py +++ b/tests/regress/bad_ram.py @@ -1,29 +1,33 @@ #!/usr/bin/python +import regress + from unicorn import * from unicorn.x86_const import * -import regress - class Hang(regress.RegressTest): - def runTest(self): PAGE_SIZE = 0x5000 CODE_ADDR = 0x400000 RSP_ADDR = 0x200000 - binary1 = "\xCA\x24\x5D" # retf 0x5d24 + + CODE = b"\xCA\x24\x5D" # retf 0x5d24 + mu = Uc(UC_ARCH_X86, UC_MODE_64) mu.mem_map(CODE_ADDR, PAGE_SIZE) mu.mem_map(RSP_ADDR, PAGE_SIZE) - mu.mem_write(CODE_ADDR, binary1) + mu.mem_write(CODE_ADDR, CODE) mu.reg_write(UC_X86_REG_RSP, RSP_ADDR) - try: - self.assertEqual(mu.emu_start(CODE_ADDR, CODE_ADDR + PAGE_SIZE, 0), UC_ERR_FETCH_INVALID) - except UcError as e: - print("ERROR: %s" % e) + + # make sure we bump into an exception + with self.assertRaises(UcError) as raisedEx: + mu.emu_start(CODE_ADDR, CODE_ADDR + PAGE_SIZE) + + # make sure it is an exception with the errno we expect + self.assertEqual(raisedEx.exception.errno, UC_ERR_READ_UNMAPPED) if __name__ == '__main__': diff --git a/tests/regress/callback-pc.py b/tests/regress/callback-pc.py index 77f4e5ab7e..640d694218 100755 --- a/tests/regress/callback-pc.py +++ b/tests/regress/callback-pc.py @@ -3,88 +3,85 @@ # reg_write() can't modify PC from within trace callbacks # issue #210 -from __future__ import print_function +import regress + from unicorn import * from unicorn.arm_const import * -import regress BASE_ADDRESS = 0x10000000 +THUMB_CODE = b"\x83\xb0" * 5 # sub sp, #0xc +TARGET_PC = 0xffffffff -# sub sp, #0xc -THUMB_CODE = "\x83\xb0" * 5 - -# callback for tracing instructions -def hook_code(uc, address, size, user_data): - print(">>> Tracing instruction at 0x%x, instruction size = %u" % (address, size)) - mu = user_data - print(">>> Setting PC to 0xffffffff") - mu.reg_write(UC_ARM_REG_PC, 0xffffffff) - -# callback for tracing basic blocks -def hook_block(uc, address, size, user_data): - print(">>> Tracing basic block at 0x%x, block size = 0x%x" %(address, size)) - mu = user_data - print(">>> Setting PC to 0xffffffff") - mu.reg_write(UC_ARM_REG_PC, 0xffffffff) class CallBackPCTest(regress.RegressTest): def test_instruction_trace(self): - try: - # initialize emulator in ARM's Thumb mode - mu = Uc(UC_ARCH_ARM, UC_MODE_THUMB) + # initialize emulator in ARM's Thumb mode + mu = Uc(UC_ARCH_ARM, UC_MODE_THUMB) - # map some memory - mu.mem_map(BASE_ADDRESS, 2 * 1024 * 1024) + # map some memory + mu.mem_map(BASE_ADDRESS, 2 * 1024 * 1024) - # write machine code to be emulated to memory - mu.mem_write(BASE_ADDRESS, THUMB_CODE) + # write machine code to be emulated to memory + mu.mem_write(BASE_ADDRESS, THUMB_CODE) - # setup stack - mu.reg_write(UC_ARM_REG_SP, BASE_ADDRESS + 2 * 1024 * 1024) + # setup stack + mu.reg_write(UC_ARM_REG_SP, BASE_ADDRESS + 2 * 1024 * 1024) - # tracing all instructions with customized callback - mu.hook_add(UC_HOOK_CODE, hook_code, user_data=mu) + def __hook_callback(uc, address, size, user_data): + regress.logger.debug(">>> Tracing instruction at 0x%x, instruction size = %u", address, size) + regress.logger.debug(">>> Setting PC to %#x", TARGET_PC) - # emulate one instruction + uc.reg_write(UC_ARM_REG_PC, TARGET_PC) + + # tracing all instructions with customized callback + mu.hook_add(UC_HOOK_CODE, __hook_callback) + + # emulate one instruction. since pc was modified to point to an unmapped + # area, this is expected to fail + with self.assertRaises(UcError) as raisedEx: mu.emu_start(BASE_ADDRESS, BASE_ADDRESS + len(THUMB_CODE), count=1) - # the instruction trace callback set PC to 0xffffffff, so at this - # point, the PC value should be 0xffffffff. - pc = mu.reg_read(UC_ARM_REG_PC) - self.assertEqual(pc, 0xffffffff, "PC not set to 0xffffffff by instruction trace callback") + self.assertEqual(UC_ERR_FETCH_UNMAPPED, raisedEx.exception.errno) - except UcError as e: - self.assertFalse(0, "ERROR: %s" % e) + # the block callback set PC to 0xffffffff, so at this point, the PC + # value should be 0xffffffff. + self.assertEqual(TARGET_PC, mu.reg_read(UC_ARM_REG_PC) | 0b1) def test_block_trace(self): - try: - # initialize emulator in ARM's Thumb mode - mu = Uc(UC_ARCH_ARM, UC_MODE_THUMB) + # initialize emulator in ARM's Thumb mode + mu = Uc(UC_ARCH_ARM, UC_MODE_THUMB) + + # map some memory + mu.mem_map(BASE_ADDRESS, 2 * 1024 * 1024) - # map some memory - mu.mem_map(BASE_ADDRESS, 2 * 1024 * 1024) + # write machine code to be emulated to memory + mu.mem_write(BASE_ADDRESS, THUMB_CODE) - # write machine code to be emulated to memory - mu.mem_write(BASE_ADDRESS, THUMB_CODE) + # setup stack + mu.reg_write(UC_ARM_REG_SP, BASE_ADDRESS + 2 * 1024 * 1024) - # setup stack - mu.reg_write(UC_ARM_REG_SP, BASE_ADDRESS + 2 * 1024 * 1024) + def __hook_callback(uc, address, size, user_data): + regress.logger.debug(">>> Tracing basic block at 0x%x, block size = 0x%x", address, size) + regress.logger.debug(">>> Setting PC to %#x", TARGET_PC) - # trace blocks with customized callback - mu.hook_add(UC_HOOK_BLOCK, hook_block, user_data=mu) + uc.reg_write(UC_ARM_REG_PC, TARGET_PC) - # emulate one instruction + # trace blocks with customized callback + mu.hook_add(UC_HOOK_BLOCK, __hook_callback) + + # emulate one instruction. since pc was modified to point to an unmapped + # area, this is expected to fail + with self.assertRaises(UcError) as raisedEx: mu.emu_start(BASE_ADDRESS, BASE_ADDRESS + len(THUMB_CODE), count=1) - # the block callback set PC to 0xffffffff, so at this point, the PC - # value should be 0xffffffff. - pc = mu.reg_read(UC_ARM_REG_PC) - self.assertEqual(pc, 0xffffffff, "PC not set to 0xffffffff by block callback") + self.assertEqual(UC_ERR_FETCH_UNMAPPED, raisedEx.exception.errno) + + # the block callback set PC to 0xffffffff, so at this point, the PC + # value should be 0xffffffff. + self.assertEqual(TARGET_PC, mu.reg_read(UC_ARM_REG_PC) | 0b1) - except UcError as e: - self.assertFalse(0, "ERROR: %s" % e) if __name__ == '__main__': regress.main() diff --git a/tests/regress/core_ctl.py b/tests/regress/core_ctl.py new file mode 100644 index 0000000000..9cf1e1c2c3 --- /dev/null +++ b/tests/regress/core_ctl.py @@ -0,0 +1,177 @@ +#!/usr/bin/env python + +import regress + +from unicorn import * +from unicorn.arm_const import * +from unicorn.x86_const import * + + +# count down from maxint to zero +_VALID_CODE = ( + b'\x31\xc9' # xor ecx, ecx + b'\x49' # _top: dec ecx + b'\xf3\x90' # pause + b'\xe3\xfb' # jecxz _top + b'\xf4' # _end: hlt +) + +_INVALID_CODE = ( + b'\xff\xff' # (invalid) +) + +CODE = _VALID_CODE + _INVALID_CODE + +BASE = 0x100000 + + +class TestCtl(regress.RegressTest): + + def test_mode(self): + uc = Uc(UC_ARCH_ARM, UC_MODE_BIG_ENDIAN) + + # changing cpu model to one that supports EB + uc.ctl_set_cpu_model(UC_CPU_ARM_CORTEX_M0) + + # changing cpu model to one that does not support EB; this is expected to fail + with self.assertRaises(UcError) as ex: + uc.ctl_set_cpu_model(UC_CPU_ARM_CORTEX_A8) + + self.assertEqual(UC_ERR_ARG, ex.exception.errno) + + # make sure we stay with the configuration we set beforehand + self.assertEqual(UC_ARCH_ARM, uc.ctl_get_arch()) + self.assertEqual(UC_MODE_BIG_ENDIAN, uc.ctl_get_mode()) + self.assertEqual(UC_CPU_ARM_CORTEX_M0, uc.ctl_get_cpu_model()) + + def test_page_size(self): + SIZE_4KB = 4 * 1024 ** 1 + SIZE_2MB = 2 * 1024 ** 2 + + uc = Uc(UC_ARCH_ARM, UC_MODE_ARM) + + # set page size to 2 MiB; this should work + uc.ctl_set_page_size(SIZE_2MB) + + # BUG! was it set properly? + # self.assertEqual(SIZE_2MB, uc.ctl_get_page_size()) + + # set a page size which is not a power of 2 + with self.assertRaises(UcError) as ex: + uc.ctl_set_page_size(SIZE_2MB + 0xbad) + + self.assertEqual(UC_ERR_ARG, ex.exception.errno) + + # BUG! are we still with the valid value? + # self.assertEqual(SIZE_2MB, uc.ctl_get_page_size()) + + # force uc to complete its initialization by triggering a random api + uc.ctl_flush_tb() + + # set a page size after uc has completed its initialization + with self.assertRaises(UcError) as ex: + uc.ctl_set_page_size(SIZE_4KB) + + self.assertEqual(UC_ERR_ARG, ex.exception.errno) + + # BUG! are we still with the valid value? + # self.assertEqual(SIZE_2MB, uc.ctl_get_page_size()) + + def test_timeout(self): + MILLIS_1S = 1000 + + uc = Uc(UC_ARCH_X86, UC_MODE_32) + + uc.mem_map(BASE, 0x1000) + uc.mem_write(BASE, CODE) + + # start a long emulation bound by timeout + uc.emu_start(BASE, BASE + len(CODE), timeout=MILLIS_1S) + + # was timeout set properly? uc returns timeout in nanoseconds + self.assertEqual(MILLIS_1S * 1000, uc.ctl_get_timeout()) + + # no way we made it all the way down to zero + self.assertNotEqual(0, uc.reg_read(UC_X86_REG_ECX)) + + def test_exits(self): + WRONG_EXIT = BASE + len(CODE) + GOOD_EXIT = BASE + len(_VALID_CODE) + + uc = Uc(UC_ARCH_X86, UC_MODE_32) + + uc.mem_map(BASE, 0x1000) + uc.mem_write(BASE, CODE) + + def __hook_code(uc, *args): + ecx = uc.reg_read(UC_X86_REG_ECX) + + # 16 down to the count + if ecx == 0xfffffff0: + # fast-forward the counter and let it run for another 16 iterations + uc.reg_write(UC_X86_REG_ECX, 0x10) + + uc.hook_add(UC_HOOK_CODE, __hook_code) + + # enable exits + uc.ctl_exits_enabled(True) + + # fix the exit point so we don't reach invalid code + uc.ctl_set_exits([GOOD_EXIT]) + + # start emulation, setting a wrong stopping point that will get us to invalid code + # since we enabled exits, the stopping point should be ignored + uc.emu_start(BASE, WRONG_EXIT, count=1) + + # only one exit point was set + self.assertEqual(1, uc.ctl_get_exits_cnt()) + + # only one exit point, and it is the wrong one + self.assertSequenceEqual([GOOD_EXIT], uc.ctl_get_exits(), int) + + # resume execution; rely on code hook to fast-forward the emulation + # stopping point is ignored due to enabled exits + uc.emu_start(BASE, WRONG_EXIT) + + # did we exit on the right place? + # not failing on an invalid instruction is another good indication for that + self.assertEqual(GOOD_EXIT, uc.reg_read(UC_X86_REG_EIP)) + + def test_tlb_mode(self): + MAPPING_LO = 0x0000000001000000 + MAPPING_HI = 0x0010000000000000 + NOPSLED = b'\x90' * 8 + + uc = Uc(UC_ARCH_X86, UC_MODE_64) + + uc.mem_map(MAPPING_LO, 0x1000) + uc.mem_write(MAPPING_LO, NOPSLED) + + uc.emu_start(MAPPING_LO, MAPPING_LO + len(NOPSLED)) + + # # we should be fine here + # uc.emu_start(BASE, BASE + len(_VALID_CODE), count=1) + + uc.mem_map(MAPPING_HI, 0x1000) + uc.mem_write(MAPPING_HI, NOPSLED) + + # this should prevents us from mapping to high addresses + uc.ctl_tlb_mode(UC_TLB_CPU) + + # this should fail + with self.assertRaises(UcError) as ex: + uc.emu_start(MAPPING_HI, MAPPING_HI + len(NOPSLED)) + + self.assertEqual(UC_ERR_FETCH_UNMAPPED, ex.exception.errno) + + # ------------------------------------------------------ + + # this should allow us mapping to high addresses + uc.ctl_tlb_mode(UC_TLB_VIRTUAL) + + # this should ok now + uc.emu_start(MAPPING_HI, MAPPING_HI + len(NOPSLED)) + + +if __name__ == '__main__': + regress.main() diff --git a/tests/regress/emu_clear_errors.py b/tests/regress/emu_clear_errors.py index 4fb6b1df02..e6c094dd34 100755 --- a/tests/regress/emu_clear_errors.py +++ b/tests/regress/emu_clear_errors.py @@ -1,6 +1,5 @@ #!/usr/bin/python -from __future__ import print_function import binascii import regress @@ -8,21 +7,18 @@ from unicorn.x86_const import * -CODE = binascii.unhexlify(b"".join([ - b"8B 74 01 28", # mov esi, dword ptr [ecx + eax + 0x28] mapped: 0x1000 - b"03 F0", # add esi, eax 0x1004 - b"8D 45 FC", # lea eax, dword ptr [ebp - 4] 0x1006 - b"50", # push eax 0x1009 - b"6A 40", # push 0x40 0x100A - b"6A 10", # push 0x10 0x100C - b"56", # push esi 0x100E - b"FF 15 20 20 00 10" # call some address 0x100F - ]).replace(" ", "")) +CODE = binascii.unhexlify(( + "8B 74 01 28" # mov esi, dword ptr [ecx + eax + 0x28] mapped: 0x1000 + "03 F0" # add esi, eax 0x1004 + "8D 45 FC" # lea eax, dword ptr [ebp - 4] 0x1006 + "50" # push eax 0x1009 + "6A 40" # push 0x40 0x100A + "6A 10" # push 0x10 0x100C + "56" # push esi 0x100E +).replace(' ', '')) - -def showpc(mu): - pc = mu.reg_read(UC_X86_REG_EIP) - print("pc: 0x%x" % (pc)) +BASE = 0x1000 +STACK = 0x4000 class HookCodeStopEmuTest(regress.RegressTest): @@ -30,38 +26,32 @@ def test_hook_code_stop_emu(self): mu = Uc(UC_ARCH_X86, UC_MODE_32) # base of CODE - mu.mem_map(0x1000, 0x1000) - mu.mem_write(0x1000, CODE) - mu.reg_write(UC_X86_REG_EIP, 0x1000) + mu.mem_map(BASE, 0x1000) + mu.mem_write(BASE, CODE) # base of STACK - mu.mem_map(0x4000, 0x4000) - mu.mem_write(0x4000, "\x00" * 0x4000) - mu.reg_write(UC_X86_REG_ESP, 0x6000) - mu.reg_write(UC_X86_REG_EBP, 0x6000) + mu.mem_map(STACK, 0x1000) + mu.mem_write(STACK, b"\x00" * 0x1000) + mu.reg_write(UC_X86_REG_EIP, BASE) + mu.reg_write(UC_X86_REG_ESP, STACK + 0x1000 - 8) + mu.reg_write(UC_X86_REG_EBP, STACK + 0x1000 - 8) mu.reg_write(UC_X86_REG_ECX, 0x0) mu.reg_write(UC_X86_REG_EAX, 0x0) - def _hook(_, access, address, length, value, context): - pc = mu.reg_read(UC_X86_REG_EIP) - print("mem unmapped: pc: %x access: %x address: %x length: %x value: %x" % ( - pc, access, address, length, value)) - mu.emu_stop() - return True - - mu.hook_add(UC_HOOK_MEM_UNMAPPED, _hook) - # we only expect the following instruction to execute, # and it will fail, because it accesses unmapped memory. # mov esi, dword ptr [ecx + eax + 0x28] mapped: 0x1000 - mu.emu_start(0x1000, 0x100F) - showpc(mu) + + with self.assertRaises(UcError) as ex: + mu.emu_start(BASE, BASE + len(CODE), count=1) + + self.assertEqual(UC_ERR_READ_UNMAPPED, ex.exception.errno) + + regress.logger.debug("pc: %#x", mu.reg_read(UC_X86_REG_EIP)) # now, we want to reuse the emulator, and keep executing # from the next instruction - mu.reg_write(UC_X86_REG_EIP, 0x1004) - self.assertEqual(0x1004, mu.reg_read(UC_X86_REG_EIP)) # we expect the following instructions to execute # add esi, eax 0x1004 @@ -70,10 +60,9 @@ def _hook(_, access, address, length, value, context): # push 0x40 0x100A # push 0x10 0x100C # push esi 0x100E - # - # currently, a UC_ERR_READ_UNMAPPED exception is raised here - mu.emu_start(0x1004, 0x100F) - showpc(mu) + mu.emu_start(BASE + 0x4, BASE + len(CODE)) + + regress.logger.debug("pc: %#x", mu.reg_read(UC_X86_REG_EIP)) if __name__ == '__main__': diff --git a/tests/regress/ensure_typedef_consts_generated.py b/tests/regress/ensure_typedef_consts_generated.py index 04948a5af8..ebe7939731 100755 --- a/tests/regress/ensure_typedef_consts_generated.py +++ b/tests/regress/ensure_typedef_consts_generated.py @@ -7,9 +7,19 @@ constants. """ +import regress import unicorn -try: - unicorn.UC_HOOK_MEM_UNMAPPED -except AttributeError: - assert(False and "Definition for UC_HOOK_MEM_UNMAPPED not generated") + +class TestTypedefs(regress.RegressTest): + def runTest(self): + prop = 'UC_HOOK_MEM_UNMAPPED' + + try: + getattr(unicorn, prop) + except AttributeError: + self.fail("Definition for %s not generated" % prop) + + +if __name__ == '__main__': + regress.main() diff --git a/tests/regress/fpu_ip.py b/tests/regress/fpu_ip.py index 7264be7a38..3c7a139559 100755 --- a/tests/regress/fpu_ip.py +++ b/tests/regress/fpu_ip.py @@ -1,69 +1,62 @@ #!/usr/bin/python -from unicorn import * -from unicorn.x86_const import * -from capstone import * + import regress -ESP = 0x2000 -PAGE_SIZE = 2 * 1024 * 1024 +from unicorn import * +from unicorn.x86_const import * +from capstone import Cs, CS_ARCH_X86, CS_ARCH_X86, CS_MODE_64, CS_MODE_32 -# mov [esp], DWORD 0x37f -# fldcw [esp] -# fnop -# fnstenv [esp + 8] -# pop ecx -CODE = b'\xc7\x04\x24\x7f\x03\x00\x00\xd9\x2c\x24\xd9\xd0\xd9\x74\x24\x08\x59' -class SimpleEngine: - def __init__(self): - self.capmd = Cs(CS_ARCH_X86, CS_MODE_32) +CODE = ( + b'\xc7\x04\x24\x7f\x03\x00\x00' # mov DWORD PTR [rsp],0x37f + b'\xd9\x2c\x24' # fldcw WORD PTR [rsp] + b'\xd9\xd0' # fnop + b'\xd9\x74\x24\x08' # fnstenv [rsp+0x8] + b'\x59' # pop rcx +) - def disas_single(self, data): - for i in self.capmd.disasm(data, 16): - print("\t%s\t%s" % (i.mnemonic, i.op_str)) - break +BASE = 0x00000000 +STACK = 0x00000f00 -disasm = SimpleEngine() def hook_code(uc, addr, size, user_data): - mem = uc.mem_read(addr, size) - print(" 0x%X:" % (addr)), - disasm.disas_single(bytes(mem)) + cs = user_data + data = uc.mem_read(addr, size) + mnem, ops = next((insn.mnemonic, insn.op_str) for insn in cs.disasm(data, addr)) -class FpuIP(regress.RegressTest): + regress.logger.debug("0x%x: %-12s %-24s", addr, mnem, ops) - def mem_reader(self, mu, addr, size, expected): - tmp = mu.mem_read(addr, size) - for out, exp in zip(tmp, expected): - self.assertEqual(exp, out) + +class FpuIP(regress.RegressTest): def test_32(self): mu = Uc(UC_ARCH_X86, UC_MODE_32) + cs = Cs(CS_ARCH_X86, CS_MODE_32) + + mu.mem_map(BASE, 0x1000) + mu.mem_write(BASE, CODE) + mu.reg_write(UC_X86_REG_ESP, STACK) + mu.hook_add(UC_HOOK_CODE, hook_code, cs) - mu.mem_map(0x0, PAGE_SIZE) - mu.mem_write(0x4000, CODE) - mu.reg_write(UC_X86_REG_ESP, ESP) - mu.hook_add(UC_HOOK_CODE, hook_code) + mu.emu_start(BASE, BASE + len(CODE), count=5) - mu.emu_start(0x4000, 0, 0, 5) - esp = mu.reg_read(UC_X86_REG_ESP) - self.assertEqual(0x2004, esp) - expected = [0x0, 0x0, 0xa, 0x40] - self.mem_reader(mu, esp + 14, 4, expected) + self.assertSequenceEqual(b'\x7f\x03\x00\x00\x00\x00\x00\x00', mu.mem_read(STACK + 8, 8)) + self.assertSequenceEqual(b'\x55\x55\x00\x00\x00\x00\x00\x00', mu.mem_read(STACK + 16, 8)) def test_64(self): mu = Uc(UC_ARCH_X86, UC_MODE_64) + cs = Cs(CS_ARCH_X86, CS_MODE_64) + + mu.mem_map(BASE, 0x1000) + mu.mem_write(BASE, CODE) + mu.reg_write(UC_X86_REG_RSP, STACK) + mu.hook_add(UC_HOOK_CODE, hook_code, cs) + + mu.emu_start(BASE, BASE + len(CODE), count=5) - mu.mem_map(0x0, PAGE_SIZE) - mu.mem_write(0x4000, CODE) - mu.reg_write(UC_X86_REG_ESP, ESP) - mu.hook_add(UC_HOOK_CODE, hook_code) + self.assertSequenceEqual(b'\x7f\x03\x00\x00\x00\x00\x00\x00', mu.mem_read(STACK + 8, 8)) + self.assertSequenceEqual(b'\x55\x55\x00\x00\x00\x00\x00\x00', mu.mem_read(STACK + 16, 8)) - mu.emu_start(0x4000, 0, 0, 5) - rsp = mu.reg_read(UC_X86_REG_RSP) - self.assertEqual(0x2012, rsp + 10) - expected = [0x0, 0x0, 0xa, 0x40, 0x0, 0x0, 0x0, 0x0] - self.mem_reader(mu, rsp + 10, 4, expected) if __name__ == '__main__': regress.main() diff --git a/tests/regress/fpu_mem_write.py b/tests/regress/fpu_mem_write.py index 4017decbcb..19dd89173e 100755 --- a/tests/regress/fpu_mem_write.py +++ b/tests/regress/fpu_mem_write.py @@ -1,38 +1,40 @@ #!/usr/bin/python + +import regress + from unicorn import * from unicorn.x86_const import * -import regress -ESP = 0x2000 -PAGE_SIZE = 1 * 1024 * 1024 +CODE = ( + b'\x9b\xd9\x3c\x24' # fstcw WORD PTR [esp] + b'\x59' # pop ecx +) + +BASE = 0x00000000 +STACK = 0x00000f00 -# wait -# fnstcw word ptr [esp] -# pop ecx -CODE = b'\x9B\xD9\x3C\x24\x59' def hook_mem_write(uc, access, address, size, value, user_data): - print("mem WRITE: 0x%x, data size = %u, data value = 0x%x" % (address, size, value)) + regress.logger.debug("mem WRITE to: %#x, size = %u, value = %#x", address, size, value) + return True -class FpuWrite(regress.RegressTest): - def mem_reader(self, mu, addr, size, expected): - tmp = mu.mem_read(addr, size) - for i, e in zip(tmp, expected): - self.assertEquals(e, i) +class FpuWrite(regress.RegressTest): def runTest(self): mu = Uc(UC_ARCH_X86, UC_MODE_32) - mu.mem_map(0, PAGE_SIZE) - mu.mem_write(0, CODE) - mu.reg_write(UC_X86_REG_ESP, ESP) + + mu.mem_map(BASE, 0x1000) + mu.mem_write(BASE, CODE) + mu.reg_write(UC_X86_REG_ESP, STACK) mu.hook_add(UC_HOOK_MEM_WRITE, hook_mem_write) - mu.emu_start(0x0, 5, 0, 2) - esp = mu.reg_read(UC_X86_REG_ESP) - self.mem_reader(mu, esp, 10, [0] * 10) + mu.emu_start(BASE, BASE + len(CODE), count=2) + + self.assertSequenceEqual(b'\x00' * 2, mu.mem_read(STACK, 2)) + if __name__ == '__main__': regress.main() diff --git a/tests/regress/hang.py b/tests/regress/hang.py index e38b1eb8dd..6cfb25403d 100755 --- a/tests/regress/hang.py +++ b/tests/regress/hang.py @@ -1,39 +1,71 @@ #!/usr/bin/python -from __future__ import print_function +import binascii +import regress + from unicorn import * from unicorn.x86_const import * -import regress # callback for tracing instructions def hook_code(uc, address, size, user_data): - tmp = uc.mem_read(address, size) - print("[0x%x] =" %(address), end="") - for i in tmp: - print(" %02x" %i, end="") - print("") + # invalid instruction? + if size == 0xf1f1f1f1: + return + + regress.logger.debug("[%#x] = %s" , address, binascii.hexlify(uc.mem_read(address, size))) + # callback for tracing Linux interrupt def hook_intr(uc, intno, user_data): # only handle Linux syscall rip = uc.reg_read(UC_X86_REG_RIP) - if intno != 0x80: - print("=== 0x%x: got interrupt %x, quit" %(rip, intno)) - uc.emu_stop() - return - eax = uc.reg_read(UC_X86_REG_EAX) - print(">>> 0x%x: interrupt 0x%x, EAX = 0x%x" %(rip, intno, eax)) + regress.logger.debug("[%#x]: got interrupt %#x", rip, intno) + regress.logger.debug(" EAX = %#010x", uc.reg_read(UC_X86_REG_EAX)) + regress.logger.debug(" EBX = %#010x", uc.reg_read(UC_X86_REG_EBX)) + regress.logger.debug(" ECX = %#010x", uc.reg_read(UC_X86_REG_ECX)) + regress.logger.debug(" EDX = %#010x", uc.reg_read(UC_X86_REG_EDX)) + + uc.emu_stop() -class Hang(regress.RegressTest): +class Hang(regress.RegressTest): def runTest(self): - binary1 = b'\xeb\x1c\x5a\x89\xd6\x8b\x02\x66\x3d\xca\x7d\x75\x06\x66\x05\x03\x03\x89\x02\xfe\xc2\x3d\x41\x41\x41\x41\x75\xe9\xff\xe6\xe8\xdf\xff\xff\xff\x31\xd2\x6a\x0b\x58\x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xca\x7d\x41\x41\x41\x41\x41\x41\x41\x41' + # self modifying shellcode execve('/bin/sh') + shellcode = ( + b'\xeb\x1c' # 00: jmp 0x1e + b'\x5a' # 02: pop rdx + b'\x89\xd6' # 03: mov esi, edx + b'\x8b\x02' # 05: mov eax, [rdx] + b'\x66\x3d\xca\x7d' # 07: cmp ax, 0x7dca + b'\x75\x06' # 0b: jne 0x13 + b'\x66\x05\x03\x03' # 0d: add ax,0x303 + b'\x89\x02' # 11: mov [rdx], eax + b'\xfe\xc2' # 13: inc dl + b'\x3d\x41\x41\x41\x41' # 15: cmp eax, 0x41414141 + b'\x75\xe9' # 1a: jne 0x5 + b'\xff\xe6' # 1c: jmp rsi + b'\xe8\xdf\xff\xff\xff' # 1e: call 0x2 + b'\x31\xd2' # 23: xor edx, edx + b'\x6a\x0b' # 25: push 0xb + b'\x58' # 27: pop rax + b'\x99' # 28: cdq + b'\x52' # 29: push rdx + b'\x68\x2f\x2f\x73\x68' # 2a: push 0x68732f2f + b'\x68\x2f\x62\x69\x6e' # 2f: push 0x6e69622f + b'\x89\xe3' # 34: mov ebx, esp + b'\x52' # 36: push rdx + b'\x53' # 37: push rbx + b'\x89\xe1' # 38: mov ecx, esp + b'\xca\x7d\x41\x41\x41\x41\x41\x41\x41\x41' # 3a: .db ca 7d 41 41 41 41 41 41 41 41 + ) + + address = 0x00000000 mu = Uc(UC_ARCH_X86, UC_MODE_64) - mu.mem_map(0, 2 * 1024 * 1024) + mu.mem_map(address, 0x1000) # tracing all instructions with customized callback mu.hook_add(UC_HOOK_CODE, hook_code) @@ -42,16 +74,15 @@ def runTest(self): mu.hook_add(UC_HOOK_INTR, hook_intr) # setup stack - mu.reg_write(UC_X86_REG_RSP, 1024 * 1024) - - # fill in memory with 0xCC (software breakpoint int 3) - for i in xrange(1 * 1024): - mu.mem_write(0 + i, b'\xcc') + mu.reg_write(UC_X86_REG_RSP, 0x1000 - 8) # write machine code to be emulated to memory - mu.mem_write(0, binary1) + mu.mem_write(address, shellcode) + + regress.logger.debug('Starting emulation') + + mu.emu_start(address, address + len(shellcode)) - self.assertEqual(mu.emu_start(0, len(binary1)), None) if __name__ == '__main__': regress.main() diff --git a/tests/regress/high_mem.py b/tests/regress/high_mem.py new file mode 100644 index 0000000000..7c46c71df1 --- /dev/null +++ b/tests/regress/high_mem.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 + +import regress + +from unicorn import Uc, UcError, UC_ARCH_X86, UC_MODE_64 +from unicorn.unicorn_const import UC_TLB_VIRTUAL, UC_TLB_CPU, UC_ERR_FETCH_UNMAPPED + + +MAX_INTEL_INSN_SIZE = 15 + + +class TestMem(regress.RegressTest): + + # 0x0008fffffffff000 : mappings till this point work ok + # 0x0010000000000000 : mappings beyond this point will fail if tlb is not set to virtual + + def setUp(self): + self.uc = Uc(UC_ARCH_X86, UC_MODE_64) + + def map_code_page(self, address, payload): + regress.logger.debug('attempting to map code at %#018x', address) + + self.uc.mem_map(address, 0x1000) + self.uc.mem_write(address, payload) + + def test_virt_high_mapping(self): + """Mapping memory at high addresses should work when TLB mode + is set to VIRTUAL. + """ + + base = 0x0010000000000000 + + self.uc.ctl_tlb_mode(UC_TLB_VIRTUAL) + + for i in range(12): + code = base << i + + self.map_code_page(code, b'\xf4') + + try: + self.uc.emu_start(code, code + MAX_INTEL_INSN_SIZE, count=1) + except UcError: + self.fail('high mapping failed at %#018x' % code) + + def test_cpu_high_mapping(self): + """Mapping memory at high addresses should work fail TLB mode + is set to CPU (default). + """ + + base = 0x0010000000000000 + + self.uc.ctl_tlb_mode(UC_TLB_CPU) + + for i in range(12): + code = base << i + + self.map_code_page(code, b'\xf4') + + with self.assertRaises(UcError) as ex: + self.uc.emu_start(code, code + MAX_INTEL_INSN_SIZE, count=1) + + self.assertEqual(UC_ERR_FETCH_UNMAPPED, ex.exception.errno) + + +if __name__ == '__main__': + regress.main() diff --git a/tests/regress/hook_add_crash.py b/tests/regress/hook_add_crash.py index 342e7faa63..f24933d500 100755 --- a/tests/regress/hook_add_crash.py +++ b/tests/regress/hook_add_crash.py @@ -2,15 +2,21 @@ """https://github.com/unicorn-engine/unicorn/issues/165""" -import unicorn +import regress + +from unicorn import * + def hook_mem_read_unmapped(mu, access, address, size, value, user_data): pass -mu = unicorn.Uc(unicorn.UC_ARCH_X86, unicorn.UC_MODE_32) +class TestHook(regress.RegressTest): + def test_excessive_hooks(self): + mu = Uc(UC_ARCH_X86, UC_MODE_32) + + for _ in range(1337): + mu.hook_add(UC_HOOK_MEM_READ_UNMAPPED, hook_mem_read_unmapped) + -try: - for x in range(0, 1000): - mu.hook_add(unicorn.UC_HOOK_MEM_READ_UNMAPPED, hook_mem_read_unmapped, None) -except unicorn.UcError as e: - print("ERROR: %s" % e) +if __name__ == '__main__': + regress.main() diff --git a/tests/regress/hook_code_add_del.py b/tests/regress/hook_code_add_del.py index ef071ca232..67cdf66fd3 100755 --- a/tests/regress/hook_code_add_del.py +++ b/tests/regress/hook_code_add_del.py @@ -11,38 +11,41 @@ ADDRESS = 0x8048000 STACK_ADDRESS = 0xffff000 STACK_SIZE = 4096 -''' -31 DB xor ebx, ebx -53 push ebx -43 inc ebx -53 push ebx -6A 02 push 2 -6A 66 push 66h -58 pop eax -89 E1 mov ecx, esp -CD 80 int 80h -''' -CODE = "\x31\xDB\x53\x43\x53\x6A\x02\x6A\x66\x58\x89\xE1\xCD\x80" + +CODE = ( + b'\x31\xDB' # xor ebx, ebx + b'\x53' # push ebx + b'\x43' # inc ebx + b'\x53' # push ebx + b'\x6A\x02' # push 2 + b'\x6A\x66' # push 66h + b'\x58' # pop eax + b'\x89\xE1' # mov ecx, esp + b'\xCD\x80' # int 80h +) + EP = ADDRESS + 0x54 def hook_code(mu, address, size, user_data): - print(">>> Tracing instruction at 0x%x, instruction size = %u" %(address, size)) - + regress.logger.debug(">>> Tracing instruction at %#x, instruction size = %u", address, size) + + class HookCodeAddDelTest(regress.RegressTest): - def runTest(self): - emu = Uc(UC_ARCH_X86, UC_MODE_32) - emu.mem_map(ADDRESS, 0x1000) - emu.mem_write(EP, CODE) - - emu.mem_map(STACK_ADDRESS, STACK_SIZE) - emu.reg_write(UC_X86_REG_ESP, STACK_ADDRESS + STACK_SIZE) - - # UC_HOOK_CODE hook will work even after deletion - i = emu.hook_add(UC_HOOK_CODE, hook_code, None) - emu.hook_del(i) - - emu.emu_start(EP, EP + len(CODE), count = 3) - print("EIP: 0x%x" % emu.reg_read(UC_X86_REG_EIP)) - + def runTest(self): + emu = Uc(UC_ARCH_X86, UC_MODE_32) + emu.mem_map(ADDRESS, 0x1000) + emu.mem_write(EP, CODE) + + emu.mem_map(STACK_ADDRESS, STACK_SIZE) + emu.reg_write(UC_X86_REG_ESP, STACK_ADDRESS + STACK_SIZE) + + # UC_HOOK_CODE hook will work even after deletion + i = emu.hook_add(UC_HOOK_CODE, hook_code, None) + emu.hook_del(i) + + emu.emu_start(EP, EP + len(CODE), count = 3) + regress.logger.debug("EIP: %#x", emu.reg_read(UC_X86_REG_EIP)) + + if __name__ == '__main__': - regress.main() + regress.main() diff --git a/tests/regress/hook_code_stop_emu.py b/tests/regress/hook_code_stop_emu.py index 33f2f8fa4a..bfcaa0d8a1 100755 --- a/tests/regress/hook_code_stop_emu.py +++ b/tests/regress/hook_code_stop_emu.py @@ -1,90 +1,77 @@ #!/usr/bin/python -from __future__ import print_function -import binascii import regress from unicorn import * from unicorn.x86_const import * -CODE = binascii.unhexlify(b"".join([ - b"48c7c003000000", # mov rax, 3 mapped: 0x1000 - b"0f05", # syscall mapped: 0x1007 - b"48c7c700400000", # mov rdi, 0x4000 mapped: 0x1009 - b"488907", # mov [rdi], rdx mapped: 0x1010 - b"488b07", # mov rdx, [rdi] mapped: 0x1013 - b"4883c201", # add rdx, 1 mapped: 0x1016 - ])) +CODE = ( + b'\x48\xc7\xc0\x03\x00\x00\x00' # 0x1000: mov rax, 3 + b'\x0f\x05' # 0x1007: syscall + b'\x48\xc7\xc7\x00\x40\x00\x00' # 0x1009: mov rdi, 0x4000 + b'\x48\x89\x07' # 0x1010: mov [rdi], rdx + b'\x48\x8b\x07' # 0x1013: mov rdx, [rdi] + b'\x48\x83\xc2\x01' # 0x1016: add rdx, 1 +) + +BASE = 0x00001000 +SCRATCH = 0x00004000 class SingleStepper: - def __init__(self, emu, test): - self._emu = emu - self._hit_count = 0 - self._test = test + def __init__(self, uc, test): + self.uc = uc + self.hits = 0 + self.test = test def _stop_hook(self, uc, address, *args, **kwargs): - if self._hit_count == 0: - self._hit_count += 1 - else: - self._test.assertEqual(1, self._hit_count, "HOOK_CODE invoked too many times") + self.hits += 1 + + if self.hits > 1: + self.test.assertEqual(2, self.hits, "HOOK_CODE invoked too many times") uc.emu_stop() def step(self): - self._hit_count = 0 - h = self._emu.hook_add(UC_HOOK_CODE, self._stop_hook) + self.hits = 0 + h = self.uc.hook_add(UC_HOOK_CODE, self._stop_hook) + try: - pc = self._emu.reg_read(UC_X86_REG_RIP) - self._emu.emu_start(pc, pc+0x20) + pc = self.uc.reg_read(UC_X86_REG_RIP) + self.uc.emu_start(pc, pc + 0x20) finally: - self._emu.hook_del(h) + self.uc.hook_del(h) def showpc(mu): - pc = mu.reg_read(UC_X86_REG_RIP) - print("pc: 0x%x" % (pc)) + regress.logger.debug("pc: %#x", mu.reg_read(UC_X86_REG_RIP)) class HookCodeStopEmuTest(regress.RegressTest): def test_hook_code_stop_emu(self): - try: - mu = Uc(UC_ARCH_X86, UC_MODE_64) - - # base of CODE - mu.mem_map(0x1000, 0x1000) - mu.mem_write(0x1000, CODE) - - # scratch, used by CODE - mu.mem_map(0x4000, 0x1000) - - mu.reg_write(UC_X86_REG_RDX, 0x1) - mu.reg_write(UC_X86_REG_RIP, 0x1000) - - # 0x1000: 48c7c003000000 mov rax, 3 - # 0x1007: 0f05 syscall - # 0x1009: 48c7c700400000 mov rdi, 0x4000 - # 0x1010: 488907 mov [rdi], rdx - # 0x1013: 488b07 mov rdx, [rdi] - # 0x1016: 4883c201 add rdx, 1 - - stepper = SingleStepper(mu, self) - showpc(mu) - self.assertEqual(0x1000, mu.reg_read(UC_X86_REG_RIP), "Unexpected PC") - - - stepper.step() - showpc(mu) - self.assertEqual(0x1007, mu.reg_read(UC_X86_REG_RIP), - "Emulator failed to stop after one instruction") - - stepper.step() - showpc(mu) - self.assertEqual(0x1009, mu.reg_read(UC_X86_REG_RIP), - "Emulator failed to stop after one instruction") - - except UcError as e: - self.assertFalse(0, "ERROR: %s" % e) + mu = Uc(UC_ARCH_X86, UC_MODE_64) + + # base of CODE + mu.mem_map(BASE, 0x1000) + mu.mem_write(BASE, CODE) + + # scratch, used by CODE + mu.mem_map(SCRATCH, 0x1000) + + mu.reg_write(UC_X86_REG_RDX, 0x1) + mu.reg_write(UC_X86_REG_RIP, BASE) + + stepper = SingleStepper(mu, self) + showpc(mu) + self.assertEqual(BASE + 0x0, mu.reg_read(UC_X86_REG_RIP), "Unexpected starting PC") + + stepper.step() + showpc(mu) + self.assertEqual(BASE + 0x7, mu.reg_read(UC_X86_REG_RIP), "Emulator failed to stop after one instruction") + + stepper.step() + showpc(mu) + self.assertEqual(BASE + 0x9, mu.reg_read(UC_X86_REG_RIP), "Emulator failed to stop after one instruction") if __name__ == '__main__': diff --git a/tests/regress/hook_readonly_write_local.py b/tests/regress/hook_readonly_write_local.py index da9b92e510..60e6de456b 100755 --- a/tests/regress/hook_readonly_write_local.py +++ b/tests/regress/hook_readonly_write_local.py @@ -1,30 +1,41 @@ #!/usr/bin/python + +import regress + from unicorn import * from unicorn.x86_const import * -import regress -PAGE_SIZE = 4 * 1024 + +PAGE_SIZE = 0x1000 ACCESS_ADDR = 0x1000 -# mov eax, [0x1000] -# mov eax, [0x1000] -CODE = b'\xA1\x00\x10\x00\x00\xA1\x00\x10\x00\x00' +CODE = ( + b'\xA1\x00\x10\x00\x00' # mov eax, [0x1000] + b'\xA1\x00\x10\x00\x00' # mov eax, [0x1000] +) + +BASE = 0x00000000 def hook_mem_read(uc, access, address, size, value, data): - print("Reading at " + str(address)) + regress.logger.debug("Reading at %#x", address) + # BUG: unicorn will segfault when calling "uc.mem_write" to write to a location that was mapped only as UC_PROT_READ uc.mem_write(address, CODE) -class REP(regress.RegressTest): - def test_rep(self): +class REP(regress.RegressTest): + @regress.unittest.skip('writing to a UC_PROT_READ area will segfault Unicorn') + def runTest(self): mu = Uc(UC_ARCH_X86, UC_MODE_32) - mu.mem_map(0, PAGE_SIZE) - mu.mem_write(0, CODE) + mu.mem_map(BASE, PAGE_SIZE) + mu.mem_write(BASE, CODE) mu.mem_map(ACCESS_ADDR, PAGE_SIZE, UC_PROT_READ) mu.hook_add(UC_HOOK_MEM_READ, hook_mem_read, begin = ACCESS_ADDR, end = ACCESS_ADDR + PAGE_SIZE) - mu.emu_start(0, len(CODE)) + mu.emu_start(BASE, BASE + len(CODE)) + + self.assertEqual(0x001000a1, mu.reg_read(UC_X86_REG_EAX)) + if __name__ == '__main__': regress.main() diff --git a/tests/regress/init.py b/tests/regress/init.py index 024ea39713..a7391ccb69 100755 --- a/tests/regress/init.py +++ b/tests/regress/init.py @@ -1,10 +1,17 @@ #!/usr/bin/python # By Mariano Graziano +import regress +import struct +import sys + from unicorn import * from unicorn.x86_const import * -import regress, struct + +if sys.version_info.major == 2: + range = xrange + mu = 0 @@ -15,54 +22,61 @@ def init_unicorn(self, ip, sp, counter): #print "[+] Emulating IP: %x SP: %x - Counter: %x" % (ip, sp, counter) mu = Uc(UC_ARCH_X86, UC_MODE_64) mu.mem_map(0x1000000, 2 * 1024 * 1024) - mu.mem_write(0x1000000, "\x90") + mu.mem_write(0x1000000, b"\x90") mu.mem_map(0x8000000, 8 * 1024 * 1024) mu.reg_write(UC_X86_REG_RSP, sp) content = self.generate_value(counter) mu.mem_write(sp, content) self.set_hooks() - + def generate_value(self, counter): start = 0xffff880026f02000 offset = counter * 8 address = start + offset return struct.pack(">> Missing memory is being WRITE at 0x%x, data size = %u, data value = 0x%x" %(address, size, value) + regress.logger.debug(">>> Missing memory is being WRITE at 0x%x, data size = %u, data value = 0x%x", address, size, value) + address_page = address & 0xFFFFFFFFFFFFF000 mu.mem_map(address_page, 2 * 1024 * 1024) mu.mem_write(address, str(value)) + return True else: return False def hook_mem_fetch_unmapped(self, uc, access, address, size, value, user_data): global mu - print "[ HOOK_MEM_FETCH - Address: %s ]" % hex(address).strip("L") - print "[ mem_fetch_unmapped: faulting address at %s ]" % hex(address).strip("L") - mu.mem_write(0x1000003, "\x90") + + regress.logger.debug("[ HOOK_MEM_FETCH - Address: 0x%x ]", address) + regress.logger.debug("[ mem_fetch_unmapped: faulting address at 0x%x ]", address) + + mu.mem_write(0x1000003, b"\x90") mu.reg_write(UC_X86_REG_RIP, 0x1000001) return True def runTest(self): global mu - ips = list(xrange(0x1000000, 0x1001000, 0x1)) - sps = list(xrange(0x8000000, 0x8001000, 0x1)) - j = 0 - for i in ips: - j += 1 - index = ips.index(i) - self.init_unicorn(i, sps[index], j) + + ips = range(0x1000000, 0x1001000) + sps = range(0x8000000, 0x8001000) + + for i, (ip, sp) in enumerate(zip(ips, sps)): + self.init_unicorn(ip, sp, i) + mu.emu_start(0x1000000, 0x1000000 + 0x1) + if __name__ == '__main__': regress.main() diff --git a/tests/regress/jmp_ebx_hang.py b/tests/regress/jmp_ebx_hang.py index 8d654d4308..a4c5346e05 100755 --- a/tests/regress/jmp_ebx_hang.py +++ b/tests/regress/jmp_ebx_hang.py @@ -2,38 +2,42 @@ """See https://github.com/unicorn-engine/unicorn/issues/82""" -import unicorn -from unicorn import * import regress +from unicorn import * +from unicorn.x86_const import * + + CODE_ADDR = 0x10101000 CODE = b'\xff\xe3' # jmp ebx class JumEbxHang(regress.RegressTest): - def runTest(self): - mu = unicorn.Uc(UC_ARCH_X86, UC_MODE_32) + mu = Uc(UC_ARCH_X86, UC_MODE_32) + mu.mem_map(CODE_ADDR, 1024 * 4) mu.mem_write(CODE_ADDR, CODE) # If EBX is zero then an exception is raised, as expected - mu.reg_write(unicorn.x86_const.UC_X86_REG_EBX, 0x0) + mu.reg_write(UC_X86_REG_EBX, 0x0) - print(">>> jmp ebx (ebx = 0)") + regress.logger.debug(">>> jmp ebx (ebx = 0)") with self.assertRaises(UcError) as m: mu.emu_start(CODE_ADDR, CODE_ADDR + 2, count=1) self.assertEqual(m.exception.errno, UC_ERR_FETCH_UNMAPPED) - print(">>> jmp ebx (ebx = 0xaa96a47f)") - mu = unicorn.Uc(UC_ARCH_X86, UC_MODE_32) + regress.logger.debug(">>> jmp ebx (ebx = 0xaa96a47f)") + mu = Uc(UC_ARCH_X86, UC_MODE_32) mu.mem_map(CODE_ADDR, 1024 * 4) # If we write this address to EBX then the emulator hangs on emu_start - mu.reg_write(unicorn.x86_const.UC_X86_REG_EBX, 0xaa96a47f) + mu.reg_write(UC_X86_REG_EBX, 0xaa96a47f) mu.mem_write(CODE_ADDR, CODE) + with self.assertRaises(UcError) as m: mu.emu_start(CODE_ADDR, CODE_ADDR + 2, count=1) self.assertEqual(m.exception.errno, UC_ERR_FETCH_UNMAPPED) + if __name__ == '__main__': regress.main() diff --git a/tests/regress/jumping.py b/tests/regress/jumping.py index 265ec075df..b7bb109058 100755 --- a/tests/regress/jumping.py +++ b/tests/regress/jumping.py @@ -1,166 +1,126 @@ #!/usr/bin/env python # Mariano Graziano +import binascii +import regress + from unicorn import * from unicorn.x86_const import * -import regress - -#echo -ne "\x48\x31\xc0\x48\xb8\x04\x00\x00\x00\x00\x00\x00\x00\x48\x3d\x05\x00\x00\x00\x74\x05\xe9\x0f\x00\x00\x00\x48\xba\xbe\xba\x00\x00\x00\x00\x00\x00\xe9\x0f\x00\x00\x00\x48\xba\xca\xc0\x00\x00\x00\x00\x00\x00\xe9\x00\x00\x00\x00\x90" | ndisasm - -b64 -#00000000 4831C0 xor rax,rax -#00000003 48B8040000000000 mov rax,0x4 -# -0000 -#0000000D 483D05000000 cmp rax,0x5 -#00000013 7405 jz 0x1a -#00000015 E90F000000 jmp qword 0x29 -#0000001A 48BABEBA00000000 mov rdx,0xbabe -# -0000 -#00000024 E90F000000 jmp qword 0x38 -#00000029 48BACAC000000000 mov rdx,0xc0ca -# -0000 -#00000033 E900000000 jmp qword 0x38 -#00000038 90 nop +# set rdx to either 0xbabe or 0xc0ca, based on a comparison. +# rdx would never be set to 0xbabe unless we set zf to 1 +CODE = ( + b"\x48\x31\xc0" # xor rax, rax + b"\x48\xb8\x04\x00\x00\x00\x00\x00\x00\x00" # movabs rax, 0x4 + b"\x48\x3d\x05\x00\x00\x00" # cmp rax, 0x5 <-- never true, zf is cleared + b"\x74\x05" # je 0x1a + b"\xe9\x0f\x00\x00\x00" # jmp 0x29 + b"\x48\xba\xbe\xba\x00\x00\x00\x00\x00\x00" # 1a: movabs rdx, 0xbabe <-- never reached unless we set zf + b"\xe9\x0f\x00\x00\x00" # jmp 0x38 + b"\x48\xba\xca\xc0\x00\x00\x00\x00\x00\x00" # 29: movabs rdx, 0xc0ca + b"\xe9\x00\x00\x00\x00" # jmp 0x38 + b"\xf4" # 38: hlt +) -mu = 0 -zf = 1 # (0:clear, 1:set) +BASE = 0x1000000 -class Init(regress.RegressTest): +class Jumping(regress.RegressTest): def clear_zf(self): - eflags_cur = mu.reg_read(UC_X86_REG_EFLAGS) - eflags = eflags_cur & ~(1 << 6) - #eflags = 0x0 - print "[clear_zf] - eflags from %x to %x" % (eflags_cur, eflags) - if eflags != eflags_cur: - print "[clear_zf] - writing new eflags..." - mu.reg_write(UC_X86_REG_EFLAGS, eflags) + eflags = self.uc.reg_read(UC_X86_REG_EFLAGS) + + if (eflags >> 6) & 0b1 == 0b1: + eflags &= ~(0b1 << 6) + + regress.logger.debug("[clear_zf] clearing zero flag") + self.uc.reg_write(UC_X86_REG_EFLAGS, eflags) + + else: + regress.logger.debug("[clear_zf] no change needed") def set_zf(self): - eflags_cur = mu.reg_read(UC_X86_REG_EFLAGS) - eflags = eflags_cur | (1 << 6) - #eflags = 0xFFFFFFFF - print "[set_zf] - eflags from %x to %x" % (eflags_cur, eflags) - if eflags != eflags_cur: - print "[set_zf] - writing new eflags..." - mu.reg_write(UC_X86_REG_EFLAGS, eflags) - - def handle_zf(self, zf): - print "[handle_zf] - eflags " , zf - if zf == 0: self.clear_zf() - else: self.set_zf() + eflags = self.uc.reg_read(UC_X86_REG_EFLAGS) + + if (eflags >> 6) & 0b1 == 0b0: + eflags |= (0b1 << 6) + + regress.logger.debug("[set_zf] setting zero flag") + self.uc.reg_write(UC_X86_REG_EFLAGS, eflags) + + else: + regress.logger.debug("[set_zf] no change needed") def multipath(self): - print "[multipath] - handling ZF (%s) - default" % zf - self.handle_zf(zf) + regress.logger.debug("[multipath] - handling ZF (%s) - default", self.fixed_zf) + + if self.fixed_zf: + self.set_zf() + else: + self.clear_zf() + + # BUG: eflags changes do not get reflected unless re-writing eip # callback for tracing basic blocks - def hook_block(self, uc, address, size, user_data): - print(">>> Tracing basic block at 0x%x, block size = 0x%x" %(address, size)) + def hook_block(self, uc, address, size, _): + regress.logger.debug("Reached a new basic block at %#x (%d bytes in size)", address, size) # callback for tracing instructions - def hook_code(self, uc, address, size, user_data): - print(">>> Tracing instruction at 0x%x, instruction size = %u" %(address, size)) - rax = mu.reg_read(UC_X86_REG_RAX) - rbx = mu.reg_read(UC_X86_REG_RBX) - rcx = mu.reg_read(UC_X86_REG_RCX) - rdx = mu.reg_read(UC_X86_REG_RDX) - rsi = mu.reg_read(UC_X86_REG_RSI) - rdi = mu.reg_read(UC_X86_REG_RDI) - r8 = mu.reg_read(UC_X86_REG_R8) - r9 = mu.reg_read(UC_X86_REG_R9) - r10 = mu.reg_read(UC_X86_REG_R10) - r11 = mu.reg_read(UC_X86_REG_R11) - r12 = mu.reg_read(UC_X86_REG_R12) - r13 = mu.reg_read(UC_X86_REG_R13) - r14 = mu.reg_read(UC_X86_REG_R14) - r15 = mu.reg_read(UC_X86_REG_R15) - eflags = mu.reg_read(UC_X86_REG_EFLAGS) - - print(">>> RAX = %x" %rax) - print(">>> RBX = %x" %rbx) - print(">>> RCX = %x" %rcx) - print(">>> RDX = %x" %rdx) - print(">>> RSI = %x" %rsi) - print(">>> RDI = %x" %rdi) - print(">>> R8 = %x" %r8) - print(">>> R9 = %x" %r9) - print(">>> R10 = %x" %r10) - print(">>> R11 = %x" %r11) - print(">>> R12 = %x" %r12) - print(">>> R13 = %x" %r13) - print(">>> R14 = %x" %r14) - print(">>> R15 = %x" %r15) - print(">>> ELAGS = %x" %eflags) - print "-"*11 + def hook_code(self, uc, address, size, _): + insn = uc.mem_read(address, size) + regress.logger.debug(">>> Tracing instruction at %#x : %s", address, binascii.hexlify(insn)) + + regs = uc.reg_read_batch(( + UC_X86_REG_RAX, UC_X86_REG_RBX, UC_X86_REG_RCX, UC_X86_REG_RDX, + UC_X86_REG_RSI, UC_X86_REG_RDI, UC_X86_REG_RBP, UC_X86_REG_RSP, + UC_X86_REG_R8, UC_X86_REG_R9, UC_X86_REG_R10, UC_X86_REG_R11, + UC_X86_REG_R12, UC_X86_REG_R13, UC_X86_REG_R14, UC_X86_REG_R15, + UC_X86_REG_EFLAGS + )) + + zf = (regs[16] >> 6) & 0b1 + + regress.logger.debug(" RAX = %08x, R8 = %08x", regs[0], regs[ 8]) + regress.logger.debug(" RBX = %08x, R9 = %08x", regs[1], regs[ 9]) + regress.logger.debug(" RCX = %08x, R10 = %08x", regs[2], regs[10]) + regress.logger.debug(" RDX = %08x, R11 = %08x", regs[3], regs[11]) + regress.logger.debug(" RSI = %08x, R12 = %08x", regs[4], regs[12]) + regress.logger.debug(" RDI = %08x, R13 = %08x", regs[5], regs[13]) + regress.logger.debug(" RBP = %08x, R14 = %08x", regs[6], regs[14]) + regress.logger.debug(" RSP = %08x, R15 = %08x", regs[7], regs[15]) + regress.logger.debug(" EFLAGS = %08x (ZF = %d)", regs[16], zf) + + regress.logger.debug("-" * 32) self.multipath() - print "-"*11 - - # callback for tracing memory access (READ or WRITE) - def hook_mem_access(self, uc, access, address, size, value, user_data): - if access == UC_MEM_WRITE: - print(">>> Memory is being WRITE at 0x%x, data size = %u, data value = 0x%x" \ - %(address, size, value)) - else: # READ - print(">>> Memory is being READ at 0x%x, data size = %u" \ - %(address, size)) - - # callback for tracing invalid memory access (READ or WRITE) - def hook_mem_invalid(self, uc, access, address, size, value, user_data): - print("[ HOOK_MEM_INVALID - Address: %s ]" % hex(address)) - if access == UC_MEM_WRITE_UNMAPPED: - print(">>> Missing memory is being WRITE at 0x%x, data size = %u, data value = 0x%x" %(address, size, value)) - return True - else: - print(">>> Missing memory is being READ at 0x%x, data size = %u, data value = 0x%x" %(address, size, value)) - return True + regress.logger.debug("-" * 32) - def hook_mem_fetch_unmapped(self, uc, access, address, size, value, user_data): - print("[ HOOK_MEM_FETCH - Address: %s ]" % hex(address)) - print("[ mem_fetch_unmapped: faulting address at %s ]" % hex(address).strip("L")) - return True + def setUp(self): + # decide how to fixate zf value: 0 to clear, 1 to set + self.fixed_zf = 1 - def runTest(self): - global mu - - JUMP = "\x48\x31\xc0\x48\xb8\x04\x00\x00\x00\x00\x00\x00\x00\x48\x3d\x05\x00\x00\x00\x74\x05\xe9\x0f\x00\x00\x00\x48\xba\xbe\xba\x00\x00\x00\x00\x00\x00\xe9\x0f\x00\x00\x00\x48\xba\xca\xc0\x00\x00\x00\x00\x00\x00\xe9\x00\x00\x00\x00\x90" - - ADDRESS = 0x1000000 - - print("Emulate x86_64 code") # Initialize emulator in X86-64bit mode - mu = Uc(UC_ARCH_X86, UC_MODE_64) + uc = Uc(UC_ARCH_X86, UC_MODE_64) - # map 2MB memory for this emulation - mu.mem_map(ADDRESS, 2 * 1024 * 1024) + # map one page for this emulation + uc.mem_map(BASE, 0x1000) # write machine code to be emulated to memory - mu.mem_write(ADDRESS, JUMP) + uc.mem_write(BASE, CODE) - # setup stack - mu.reg_write(UC_X86_REG_RSP, ADDRESS + 0x200000) + self.uc = uc + def runTest(self): # tracing all basic blocks with customized callback - mu.hook_add(UC_HOOK_BLOCK, self.hook_block) + self.uc.hook_add(UC_HOOK_BLOCK, self.hook_block) # tracing all instructions in range [ADDRESS, ADDRESS+0x60] - mu.hook_add(UC_HOOK_CODE, self.hook_code, None, ADDRESS, ADDRESS+0x60) - - # tracing all memory READ & WRITE access - mu.hook_add(UC_HOOK_MEM_WRITE, self.hook_mem_access) - mu.hook_add(UC_HOOK_MEM_READ, self.hook_mem_access) - mu.hook_add(UC_HOOK_MEM_FETCH_UNMAPPED, self.hook_mem_fetch_unmapped) - mu.hook_add(UC_HOOK_MEM_READ_UNMAPPED | UC_HOOK_MEM_WRITE_UNMAPPED, self.hook_mem_invalid) - - try: - # emulate machine code in infinite time - mu.emu_start(ADDRESS, ADDRESS + len(JUMP)) - except UcError as e: - print("ERROR: %s" % e) - - rdx = mu.reg_read(UC_X86_REG_RDX) - self.assertEqual(rdx, 0xbabe, "RDX contains the wrong value. Eflags modification failed.") + self.uc.hook_add(UC_HOOK_CODE, self.hook_code, begin=BASE, end=BASE + 0x60) + + # emulate machine code in infinite time + self.uc.emu_start(BASE, BASE + len(CODE)) + + self.assertEqual(self.uc.reg_read(UC_X86_REG_RDX), 0xbabe, "rdx contains the wrong value. eflags modification failed") if __name__ == '__main__': diff --git a/tests/regress/leaked_refs.py b/tests/regress/leaked_refs.py index 263345d582..c8091cf4a3 100644 --- a/tests/regress/leaked_refs.py +++ b/tests/regress/leaked_refs.py @@ -1,63 +1,74 @@ #!/usr/bin/python -from __future__ import print_function - -import time +import gc +import regress +import weakref from unicorn import * from unicorn.x86_const import * -import objgraph - -import regress ADDRESS = 0x8048000 STACK_ADDRESS = 0xffff000 -STACK_SIZE = 4096 -''' -31 DB xor ebx, ebx -53 push ebx -43 inc ebx -53 push ebx -6A 02 push 2 -6A 66 push 66h -58 pop eax -89 E1 mov ecx, esp -CD 80 int 80h -''' -CODE = "\x31\xDB\x53\x43\x53\x6A\x02\x6A\x66\x58\x89\xE1\xCD\x80" +STACK_SIZE = 0x1000 + +CODE = ( + b"\x31\xDB" # xor ebx, ebx + b"\x53" # push ebx + b"\x43" # inc ebx + b"\x53" # push ebx + b"\x6A\x02" # push 2 + b"\x6A\x66" # push 66h + b"\x58" # pop eax + b"\x89\xE1" # mov ecx, esp + b"\xCD\x80" # int 80h +) + EP = ADDRESS + 0x54 + +# Dictionary to keep weak references to instances +instances = weakref.WeakValueDictionary() + +def create_instance(key, *args, **kwargs): + obj = Uc(*args, **kwargs) + instances[key] = obj + + return obj + + def hook_code(mu, address, size, user_data): - print(">>> Tracing instruction at 0x%x, instruction size = %u" %(address, size)) - -def emu_loop(): - emu = Uc(UC_ARCH_X86, UC_MODE_32) + regress.logger.debug(">>> Tracing instruction at %#x, instruction size = %u", address, size) + + +def emu_loop(key): + emu = create_instance(key, UC_ARCH_X86, UC_MODE_32) emu.mem_map(ADDRESS, 0x1000) emu.mem_write(EP, CODE) emu.mem_map(STACK_ADDRESS, STACK_SIZE) emu.reg_write(UC_X86_REG_ESP, STACK_ADDRESS + STACK_SIZE) - + i = emu.hook_add(UC_HOOK_CODE, hook_code, None) emu.hook_del(i) - + emu.emu_start(EP, EP + len(CODE), count = 3) - print("EIP: 0x%x" % emu.reg_read(UC_X86_REG_EIP)) + regress.logger.debug("EIP: %#x", emu.reg_read(UC_X86_REG_EIP)) + def debugMem(): - import gc gc.collect() # don't care about stuff that would be garbage collected properly - #print("Orphaned objects in gc.garbage:", gc.garbage) - assert(len(objgraph.by_type("Uc")) == 0) - #assert(len(objgraph.get_leaking_objects()) == 0) + + assert(len(instances) == 0) + class EmuLoopReferenceTest(regress.RegressTest): def runTest(self): for i in range(5): - emu_loop() + emu_loop('obj%d' % i) + debugMem() if __name__ == '__main__': - regress.main() \ No newline at end of file + regress.main() diff --git a/tests/regress/memmap.py b/tests/regress/memmap.py index 3252d51f85..305c99eba6 100755 --- a/tests/regress/memmap.py +++ b/tests/regress/memmap.py @@ -3,38 +3,45 @@ # this prints out 2 lines and the contents must be the same -from unicorn import * import regress +from unicorn import * +from unicorn.x86_const import * + + class MemMap(regress.RegressTest): def test_mmap_write(self): uc = Uc(UC_ARCH_X86, UC_MODE_64) uc.mem_map(0x8048000, 0x2000) - uc.mem_write(0x8048000, 'test') - s1 = str(uc.mem_read(0x8048000, 4)).encode('hex') + uc.mem_write(0x8048000, b'test') + s1 = uc.mem_read(0x8048000, 4) - self.assertEqual('test'.encode('hex'), s1) + self.assertEqual(b'test', s1) uc.mem_map(0x804a000, 0x8000) - s2 = str(uc.mem_read(0x8048000, 4)).encode('hex') + s2 = uc.mem_read(0x8048000, 4) + self.assertEqual(s1, s2) def test_mmap_invalid(self): - u = unicorn.Uc(unicorn.UC_ARCH_X86, unicorn.UC_MODE_32) + u = Uc(UC_ARCH_X86, UC_MODE_32) + with self.assertRaises(UcError): u.mem_map(0x2000, 0) + with self.assertRaises(UcError): u.mem_map(0x4000, 1) def test_mmap_weird(self): - u = unicorn.Uc(unicorn.UC_ARCH_X86, unicorn.UC_MODE_32) + u = Uc(UC_ARCH_X86, UC_MODE_32) - for i in xrange(20): + for i in range(20): with self.assertRaises(UcError): - u.mem_map(i*0x1000, 5) - u.mem_read(i*0x1000+6, 1) + u.mem_map(i * 0x1000, 5) + u.mem_read(i * 0x1000+6, 1) + if __name__ == '__main__': regress.main() diff --git a/tests/regress/memmap_segfault.py b/tests/regress/memmap_segfault.py index 5d012b8b7e..5d217e62fe 100755 --- a/tests/regress/memmap_segfault.py +++ b/tests/regress/memmap_segfault.py @@ -1,35 +1,38 @@ #!/usr/bin/env python -import unicorn -from unicorn import * - import regress -class MmapSeg(regress.RegressTest): +from unicorn import * +from unicorn.x86_const import * + - def test_seg1(self): - u = unicorn.Uc(unicorn.UC_ARCH_X86, unicorn.UC_MODE_32) +class MmapSeg1(regress.RegressTest): + def runTest(self): + u = Uc(UC_ARCH_X86, UC_MODE_32) u.mem_map(0x2000, 0x1000) u.mem_read(0x2000, 1) for i in range(50): - u = unicorn.Uc(unicorn.UC_ARCH_X86, unicorn.UC_MODE_32) - u.mem_map(i*0x1000, 0x1000) - u.mem_read(i*0x1000, 1) + u = Uc(UC_ARCH_X86, UC_MODE_32) + u.mem_map(i * 0x1000, 0x1000) + u.mem_read(i * 0x1000, 1) for i in range(20): with self.assertRaises(UcError): - u = unicorn.Uc(unicorn.UC_ARCH_X86, unicorn.UC_MODE_32) - u.mem_map(i*0x1000, 5) - u.mem_read(i*0x1000, 1) + u = Uc(UC_ARCH_X86, UC_MODE_32) + u.mem_map(i * 0x1000, 5) + u.mem_read(i * 0x1000, 1) - def test_seg2(self): + +class MmapSeg2(regress.RegressTest): + def runTest(self): uc = Uc(UC_ARCH_X86, UC_MODE_32) uc.mem_map(0x0000, 0x2000) uc.mem_map(0x2000, 0x4000) - uc.mem_write(0x1000, 0x1004 * ' ') - self.assertTrue(1, - 'If not reached, then we have BUG (crash on x86_64 Linux).') + uc.mem_write(0x1000, b' ' * 0x1004) + + self.assertTrue(True, 'If not reached, then we have BUG (crash on x86_64 Linux).') + if __name__ == '__main__': regress.main() diff --git a/tests/regress/mips_branch_delay.py b/tests/regress/mips_branch_delay.py index f710890c22..959a7f58d9 100755 --- a/tests/regress/mips_branch_delay.py +++ b/tests/regress/mips_branch_delay.py @@ -1,8 +1,19 @@ #!/usr/bin/python + +import regress + from capstone import * from unicorn import * -import regress + +CODE = ( + b'\x00\x00\xa4\x12' # beq $a0, $s5, 0x4008a0 + b'\x6a\x00\x82\x28' # slti $v0, $a0, 0x6a + b'\x00\x00\x00\x00' # nop +) + +BASE = 0x400000 + class MipsBranchDelay(regress.RegressTest): @@ -10,27 +21,24 @@ def runTest(self): md = Cs(CS_ARCH_MIPS, CS_MODE_MIPS32 + CS_MODE_LITTLE_ENDIAN) def disas(code, addr): - for i in md.disasm(code, addr): - print '0x%x: %s %-6s %s' % (i.address, str(i.bytes).encode('hex'), i.mnemonic, i.op_str) + for insn in md.disasm(code, addr): + regress.logger.debug('%#x: %-8s %s', insn.address, insn.mnemonic, insn.op_str) def hook_code(uc, addr, size, _): - mem = str(uc.mem_read(addr, size)) - disas(mem, addr) - - CODE = 0x400000 - asm = '0000a4126a00822800000000'.decode('hex') # beq $a0, $s5, 0x4008a0; slti $v0, $a0, 0x6a; nop + disas(uc.mem_read(addr, size), addr) - print 'Input instructions:' - disas(asm, CODE) - print + regress.logger.debug('Input instructions:') + disas(CODE, BASE) - print 'Hooked instructions:' + regress.logger.debug('Hooked instructions:') uc = Uc(UC_ARCH_MIPS, UC_MODE_MIPS32 + UC_MODE_LITTLE_ENDIAN) uc.hook_add(UC_HOOK_CODE, hook_code) - uc.mem_map(CODE, 0x1000) - uc.mem_write(CODE, asm) - self.assertEqual(None, uc.emu_start(CODE, CODE + len(asm))) + uc.mem_map(BASE, 0x1000) + uc.mem_write(BASE, CODE) + + self.assertEqual(None, uc.emu_start(BASE, BASE + len(CODE))) + if __name__ == '__main__': regress.main() diff --git a/tests/regress/mips_cp1.py b/tests/regress/mips_cp1.py index 221bb24a72..9f8956f2c9 100644 --- a/tests/regress/mips_cp1.py +++ b/tests/regress/mips_cp1.py @@ -1,13 +1,29 @@ + +import regress + from unicorn import * from unicorn.mips_const import * +CODE = b'\x44\x43\xF8\x00' # cfc1 $v1, FCSR +BASE = 0x416CB0 + + +class TestMipsCp1(regress.RegressTest): + def runTest(self): + uc = Uc(UC_ARCH_MIPS, UC_MODE_MIPS32 + UC_MODE_BIG_ENDIAN) + + uc.mem_map(BASE & ~(0x1000 - 1), 0x1000) + uc.mem_write(BASE, CODE) + + # set a wrong value in v1 + uc.reg_write(UC_MIPS_REG_V1, 0x0badc0de) + + uc.emu_start(BASE, BASE + len(CODE)) + + # default FCSR value should be 0 + self.assertEqual(0x0000, uc.reg_read(UC_MIPS_REG_V1)) -# .text:00416CB0 cfc1 $v1, FCSR -shellcode = [0x44, 0x43, 0xF8, 0x00] -base = 0x416CB0 -uc = Uc(UC_ARCH_MIPS, UC_MODE_MIPS32 + UC_MODE_BIG_ENDIAN) -uc.mem_map(0x416000, 0x1000) -uc.mem_write(base, bytes(shellcode)) -uc.emu_start(base, base + len(shellcode)) \ No newline at end of file +if __name__ == '__main__': + regress.main() diff --git a/tests/regress/mips_except.py b/tests/regress/mips_except.py index b400efe6fe..48f01d66f9 100755 --- a/tests/regress/mips_except.py +++ b/tests/regress/mips_except.py @@ -1,41 +1,59 @@ #!/usr/bin/python + +import regress + from unicorn import * from unicorn.mips_const import * -import regress -def hook_intr(uc, intno, _): - print 'interrupt', intno +CODE = ( + b'\x00\x00\x00\x00' # nop + b'\x00\x00\xa4\x8f' # lw $a0, 0($sp) +) + +BASE = 0x20000000 -CODE = 0x400000 -asm = '0000a48f'.decode('hex') # lw $a0, ($sp) class MipsExcept(regress.RegressTest): def runTest(self): uc = Uc(UC_ARCH_MIPS, UC_MODE_MIPS32 + UC_MODE_LITTLE_ENDIAN) - uc.hook_add(UC_HOOK_INTR, hook_intr) - uc.mem_map(CODE, 0x1000) - uc.mem_write(CODE, asm) + + uc.mem_map(BASE, 0x1000) + uc.mem_write(BASE, CODE) + + # execute nop. we should be ok + uc.emu_start(BASE, BASE + len(CODE), count=1) + + # ---------------------------------------- + + # set sp to a mapped but unaligned address to read from + uc.reg_write(UC_MIPS_REG_SP, BASE + 0x801) with self.assertRaises(UcError) as m: - uc.reg_write(UC_MIPS_REG_SP, 0x400001) - uc.emu_start(CODE, CODE + len(asm), 300) + uc.emu_start(BASE + 4, BASE + len(CODE), count=1) self.assertEqual(UC_ERR_READ_UNALIGNED, m.exception.errno) + # ---------------------------------------- + + # set sp to an umapped address to read from + uc.reg_write(UC_MIPS_REG_SP, 0xfffffff0) + with self.assertRaises(UcError) as m: - uc.reg_write(UC_MIPS_REG_SP, 0xFFFFFFF0) - uc.emu_start(CODE, CODE + len(asm), 200) + uc.emu_start(BASE + 4, BASE + len(CODE), count=1) self.assertEqual(UC_ERR_READ_UNMAPPED, m.exception.errno) + # ---------------------------------------- + + uc.reg_write(UC_MIPS_REG_SP, 0x40000000) + with self.assertRaises(UcError) as m: - uc.reg_write(UC_MIPS_REG_SP, 0x80000000) - uc.emu_start(CODE, CODE + len(asm), 100) + uc.emu_start(BASE + 4, BASE + len(CODE), count=1) self.assertEqual(UC_ERR_READ_UNMAPPED, m.exception.errno) + if __name__ == '__main__': regress.main() - diff --git a/tests/regress/mips_kernel_mmu.py b/tests/regress/mips_kernel_mmu.py index 51602fa34b..584af41d61 100755 --- a/tests/regress/mips_kernel_mmu.py +++ b/tests/regress/mips_kernel_mmu.py @@ -1,23 +1,26 @@ #!/usr/bin/python +import regress + from unicorn import * from unicorn.mips_const import * -import regress -class MipsSyscall(regress.RegressTest): - def test(self): - addr = 0x80000000 - code = '34213456'.decode('hex') # ori $at, $at, 0x3456 +CODE = b'\x34\x21\x34\x56' # ori $at, $at, 0x3456 +BASE = 0x10000000 + +class MipsSyscall(regress.RegressTest): + def test_syscall(self): uc = Uc(UC_ARCH_MIPS, UC_MODE_MIPS32 + UC_MODE_BIG_ENDIAN) - uc.mem_map(addr, 0x1000) - uc.mem_write(addr, code) + + uc.mem_map(BASE, 0x1000) + uc.mem_write(BASE, CODE) uc.reg_write(UC_MIPS_REG_AT, 0) - uc.emu_start(addr, addr + len(code)) + uc.emu_start(BASE, BASE + len(CODE)) - self.assertEqual(uc.reg_read(UC_MIPS_REG_AT), 0x3456) + self.assertEqual(0x3456, uc.reg_read(UC_MIPS_REG_AT)) if __name__ == '__main__': diff --git a/tests/regress/mips_single_step_sp.py b/tests/regress/mips_single_step_sp.py index b3c7840069..d4ceea9f35 100755 --- a/tests/regress/mips_single_step_sp.py +++ b/tests/regress/mips_single_step_sp.py @@ -1,51 +1,56 @@ #!/usr/bin/python +import regress + from unicorn import * from unicorn.mips_const import * -import regress -def code_hook(uc, addr, size, user_data): - print 'code hook: pc=%08x sp=%08x' % (addr, uc.reg_read(UC_MIPS_REG_SP)) +CODE = ( + b'\xf8\xff\x01\x24' # addiu $at, $zero, -8 + b'\x24\xe8\xa1\x03' # and $sp, $sp, $at + b'\x09\xf8\x20\x03' # jalr $t9 + b'\xe8\xff\xbd\x23' # addi $sp, $sp, -0x18 + b'\xb8\xff\xbd\x27' # addiu $sp, $sp, -0x48 + b'\x00\x00\x00\x00' # nop +) + +BASE = 0x4010dc -def run(step=False): - addr = 0x4010dc +def code_hook(uc, addr, size, user_data): + regress.logger.debug('code hook: pc=%08x sp=%08x', addr, uc.reg_read(UC_MIPS_REG_SP)) - code = ( - 'f8ff0124' # addiu $at, $zero, -8 - '24e8a103' # and $sp, $sp, $at - '09f82003' # jalr $t9 - 'e8ffbd23' # addi $sp, $sp, -0x18 - 'b8ffbd27' # addiu $sp, $sp, -0x48 - '00000000' # nop - ).decode('hex') +def run(step) -> int: uc = Uc(UC_ARCH_MIPS, UC_MODE_MIPS32 + UC_MODE_LITTLE_ENDIAN) + if step: uc.hook_add(UC_HOOK_CODE, code_hook) uc.reg_write(UC_MIPS_REG_SP, 0x60800000) - uc.reg_write(UC_MIPS_REG_T9, addr + len(code) - 8) + uc.reg_write(UC_MIPS_REG_T9, BASE + len(CODE) - 8) + + regress.logger.debug('sp = %08x', uc.reg_read(UC_MIPS_REG_SP)) + regress.logger.debug('at = %08x', uc.reg_read(UC_MIPS_REG_AT)) + regress.logger.debug(' (single step: %s)', str(step)) - print 'sp =', hex(uc.reg_read(UC_MIPS_REG_SP)) - print 'at =', hex(uc.reg_read(UC_MIPS_REG_AT)) - print ' (single step: %s)' % (str(step)) + uc.mem_map(BASE & ~(0x1000 - 1), 0x2000) + uc.mem_write(BASE, CODE) + uc.emu_start(BASE, BASE + len(CODE)) - uc.mem_map(addr & ~(0x1000 - 1), 0x2000) - uc.mem_write(addr, code) - uc.emu_start(addr, addr + len(code)) + regress.logger.debug('sp = %08x', uc.reg_read(UC_MIPS_REG_SP)) + regress.logger.debug('at = %08x', uc.reg_read(UC_MIPS_REG_AT)) - print 'sp =', hex(uc.reg_read(UC_MIPS_REG_SP)) - print 'at =', hex(uc.reg_read(UC_MIPS_REG_AT)) - print return uc.reg_read(UC_MIPS_REG_SP) class MipsSingleStep(regress.RegressTest): - def test(self): + def runTest(self): sp1 = run(step=False) sp2 = run(step=True) + self.assertEqual(sp1, sp2) + if __name__ == '__main__': regress.main() diff --git a/tests/regress/mips_syscall_pc.py b/tests/regress/mips_syscall_pc.py index 995b154f63..09586bb44c 100755 --- a/tests/regress/mips_syscall_pc.py +++ b/tests/regress/mips_syscall_pc.py @@ -1,25 +1,29 @@ #!/usr/bin/python +import regress + from unicorn import * from unicorn.mips_const import * -import regress + +CODE = b'\x0c\x00\x00\x00' # syscall +BASE = 0x40000 def intr_hook(uc, intno, data): - print 'interrupt=%d, v0=%d, pc=0x%08x' % (intno, uc.reg_read(UC_MIPS_REG_V0), uc.reg_read(UC_MIPS_REG_PC)) + regress.logger.debug('interrupt=%d, v0=%d, pc=%#010x', intno, uc.reg_read(UC_MIPS_REG_V0), uc.reg_read(UC_MIPS_REG_PC)) + class MipsSyscall(regress.RegressTest): def test(self): - addr = 0x40000 - code = '0c000000'.decode('hex') # syscall - uc = Uc(UC_ARCH_MIPS, UC_MODE_MIPS32 + UC_MODE_LITTLE_ENDIAN) - uc.mem_map(addr, 0x1000) - uc.mem_write(addr, code) + + uc.mem_map(BASE, 0x1000) + uc.mem_write(BASE, CODE) uc.reg_write(UC_MIPS_REG_V0, 100) uc.hook_add(UC_HOOK_INTR, intr_hook) - uc.emu_start(addr, addr+len(code)) + uc.emu_start(BASE, BASE+len(CODE)) + self.assertEqual(0x40004, uc.reg_read(UC_MIPS_REG_PC)) diff --git a/tests/regress/mov_gs_eax.py b/tests/regress/mov_gs_eax.py index fe381eb2ef..9237f5786c 100755 --- a/tests/regress/mov_gs_eax.py +++ b/tests/regress/mov_gs_eax.py @@ -1,24 +1,34 @@ #!/usr/bin/python +import regress + from unicorn import * from unicorn.x86_const import * -import regress + +CODE = ( + b'\x8e\xe8' # mov gs, eax + b'\xb8\x01\x00\x00\x00' # mov eax, 1 +) + +BASE = 0x1000 + class VldrPcInsn(regress.RegressTest): def runTest(self): uc = Uc(UC_ARCH_X86, UC_MODE_32) - uc.mem_map(0x1000, 0x1000) - # mov gs, eax; mov eax, 1 - code = '8ee8b801000000'.decode('hex') - uc.mem_write(0x1000, code) + + uc.mem_map(BASE, 0x1000) + + uc.mem_write(BASE, CODE) uc.reg_write(UC_X86_REG_EAX, 0xFFFFFFFF) with self.assertRaises(UcError) as ex_ctx: - uc.emu_start(0x1000, 0x1000 + len(code)) + uc.emu_start(BASE, BASE + len(CODE)) + + self.assertEqual(UC_ERR_EXCEPTION, ex_ctx.exception.errno) - self.assertEquals(ex_ctx.exception.errno, UC_ERR_EXCEPTION) if __name__ == '__main__': regress.main() diff --git a/tests/regress/movsd.py b/tests/regress/movsd.py index 28766139d7..34ea93cf64 100755 --- a/tests/regress/movsd.py +++ b/tests/regress/movsd.py @@ -1,35 +1,39 @@ #!/usr/bin/python # By Ryan Hileman, issue #3 -from capstone import * +import regress + +from capstone import Cs, CS_ARCH_X86, CS_MODE_64 from unicorn import * from unicorn.x86_const import * -import regress -code = 'f20f1005aa120000'.decode('hex') -def dis(mem, addr): - md = Cs(CS_ARCH_X86, CS_MODE_64) - return '\n'.join([ - '%s %s' % (i.mnemonic, i.op_str) - for i in md.disasm(str(mem), addr) - ]) +CODE = b'\xf2\x0f\x10\x05\xaa\x12\x00\x00' + + +def dis(md, mem, addr): + return '\n'.join(('%-16s %s' % (insn.mnemonic, insn.op_str) for insn in md.disasm(mem, addr))) + -def hook_code(uc, addr, size, user_data): +def hook_code(uc, addr, size, md): mem = uc.mem_read(addr, size) - print 'instruction size:', size - print 'instruction:', str(mem).encode('hex'), dis(mem, addr) - print 'reference: ', code.encode('hex'), dis(code, addr) -class Movsd(regress.RegressTest): + regress.logger.debug('instruction size: %d', size) + regress.logger.debug('instruction: %s %s', mem, dis(md, mem, addr)) + regress.logger.debug('reference: %s %s', CODE, dis(md, CODE, addr)) + +class Movsd(regress.RegressTest): def runTest(self): addr = 0x400000 mu = Uc(UC_ARCH_X86, UC_MODE_64) - mu.hook_add(UC_HOOK_CODE, hook_code) + md = Cs(CS_ARCH_X86, CS_MODE_64) + + mu.hook_add(UC_HOOK_CODE, hook_code, md) mu.mem_map(addr, 8 * 1024 * 1024) - mu.mem_write(addr, code) - mu.emu_start(addr, addr + len(code)) + mu.mem_write(addr, CODE) + mu.emu_start(addr, addr + len(CODE)) + if __name__ == '__main__': regress.main() diff --git a/tests/regress/osx_qemu_thread_create_crash.py b/tests/regress/osx_qemu_thread_create_crash.py index b34b319b64..b2abf097e7 100755 --- a/tests/regress/osx_qemu_thread_create_crash.py +++ b/tests/regress/osx_qemu_thread_create_crash.py @@ -1,20 +1,18 @@ #!/usr/bin/env python -import platform -import resource +import regress from unicorn import * -import regress - # OS X: OK with 2047 iterations. # OS X: Crashes at 2048:th iteration ("qemu: qemu_thread_create: Resource temporarily unavailable"). # Linux: No crashes observed. + class ThreadCreateCrash(regress.RegressTest): def test(self): - for i in xrange(2048): + for _ in range(2048): Uc(UC_ARCH_X86, UC_MODE_64) - self.assertTrue(True, "If not reached, then we have a crashing bug.") + if __name__ == '__main__': regress.main() diff --git a/tests/regress/potential_memory_leak.py b/tests/regress/potential_memory_leak.py index 29a29987e2..a37d0bdc8e 100755 --- a/tests/regress/potential_memory_leak.py +++ b/tests/regress/potential_memory_leak.py @@ -2,10 +2,12 @@ import platform import resource +import regress from unicorn import * -import regress + +ITERATIONS = 10000 class MemoryLeak(regress.RegressTest): def test(self): @@ -15,15 +17,20 @@ def test(self): rusage_multiplier = 1024 else: # resource.getrusage(...) is platform dependent. Only tested under OS X and Linux. - return + self.skipTest('not OSx neither Linux') + max_rss_before = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss * rusage_multiplier - for i in xrange(10000): + + for _ in range(ITERATIONS): mu = Uc(UC_ARCH_X86, UC_MODE_64) - mu.mem_map(0, 4096) + mu.mem_map(0, 0x1000) mu.emu_start(0, 0) + max_rss_after = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss * rusage_multiplier - rss_increase_per_iteration = (max_rss_after - max_rss_before) / i - self.assertLess(rss_increase_per_iteration, 8000) + rss_increase_per_iteration = (max_rss_after - max_rss_before) / ITERATIONS + + self.assertLess(rss_increase_per_iteration, 8000.0) + if __name__ == '__main__': regress.main() diff --git a/tests/regress/pshufb.py b/tests/regress/pshufb.py index 4e60b7dc85..24eba5e5f9 100755 --- a/tests/regress/pshufb.py +++ b/tests/regress/pshufb.py @@ -1,21 +1,25 @@ #!/usr/bin/python # By Ryan Hileman, issue #91 -# Invalid instruction = test failed +import regress from unicorn import * from unicorn.x86_const import * -import regress class Pshufb(regress.RegressTest): def runTest(self): uc = Uc(UC_ARCH_X86, UC_MODE_64) + uc.ctl_set_cpu_model(UC_CPU_X86_HASWELL) + uc.mem_map(0x2000, 0x1000) - # pshufb xmm0, xmm1 - uc.mem_write(0x2000, '660f3800c1'.decode('hex')) + + uc.mem_write(0x2000, b'\x66\x0f\x38\x00\xc1') # pshufb xmm0, xmm1 + + # Invalid instruction -> test failed uc.emu_start(0x2000, 0x2005) + if __name__ == '__main__': regress.main() diff --git a/tests/regress/reg_write_sign_extension.py b/tests/regress/reg_write_sign_extension.py index b7d3be5540..813c5a00cc 100755 --- a/tests/regress/reg_write_sign_extension.py +++ b/tests/regress/reg_write_sign_extension.py @@ -2,30 +2,37 @@ """See https://github.com/unicorn-engine/unicorn/issues/98""" -import unicorn import regress +from unicorn import * + ADDR = 0xffaabbcc + def hook_mem_invalid(mu, access, address, size, value, user_data): - print ">>> Access type: %u, expected value: 0x%x, actual value: 0x%x" % (access, ADDR, address) + regress.logger.debug(">>> Access type: %u, expected value: 0x%x, actual value: 0x%x", access, ADDR, address) + assert(address == ADDR) + mu.mem_map(address & 0xfffff000, 4 * 1024) mu.mem_write(address, b'\xcc') + return True + class RegWriteSignExt(regress.RegressTest): def runTest(self): - mu = unicorn.Uc(unicorn.UC_ARCH_X86, unicorn.UC_MODE_32) - mu.reg_write(unicorn.x86_const.UC_X86_REG_EBX, ADDR) + mu = Uc(UC_ARCH_X86, UC_MODE_32) + mu.reg_write(x86_const.UC_X86_REG_EBX, ADDR) mu.mem_map(0x10000000, 1024 * 4) # jmp ebx mu.mem_write(0x10000000, b'\xff\xe3') - mu.hook_add(unicorn.UC_HOOK_MEM_FETCH_UNMAPPED | unicorn.UC_HOOK_MEM_FETCH_PROT, hook_mem_invalid) + mu.hook_add(UC_HOOK_MEM_FETCH_UNMAPPED | UC_HOOK_MEM_FETCH_PROT, hook_mem_invalid) mu.emu_start(0x10000000, 0x10000000 + 2, count=1) + if __name__ == '__main__': regress.main() diff --git a/tests/regress/regress.py b/tests/regress/regress.py index 2e4f2536bc..e0f873eabd 100755 --- a/tests/regress/regress.py +++ b/tests/regress/regress.py @@ -1,34 +1,64 @@ -#!/usr/bin/python +#!/usr/bin/env python -import unittest - -from os.path import dirname, basename, isfile import glob +import logging +import os +import unittest -# Find all unittest type in this directory and run it. class RegressTest(unittest.TestCase): - pass + """Regress test case dummy class. + """ + + +def __setup_logger(name): + """Set up a unified logger for all tests. + """ + + instance = logging.getLogger(name) + instance.propagate = False + + handler = logging.StreamHandler() + formatter = logging.Formatter('[%(levelname)s] %(message)s') + + if not instance.hasHandlers(): + handler.setFormatter(formatter) + instance.addHandler(handler) + + return instance + + +logger = __setup_logger('UnicornRegress') +logger.setLevel(logging.INFO) + def main(): unittest.main() + if __name__ == '__main__': - directory = dirname(__file__) - if directory == '': - directory = '.' - modules = glob.glob(directory+"/*.py") - __all__ = [ basename(f)[:-3] for f in modules if isfile(f)] suite = unittest.TestSuite() - for module in __all__: - m = __import__(module) - for cl in dir(m): - try: - realcl = getattr(m,cl) - if issubclass(realcl, unittest.TestCase): - suite.addTest(realcl()) - except Exception as e: - pass + logger.info('starting discovery') + + # Find all unittest type in this directory and run it. + directory = os.path.dirname(__file__) or '.' + pyfiles = glob.glob(directory + '/*.py') + modules = [os.path.splitext(os.path.basename(f))[0] for f in pyfiles if os.path.isfile(f) and f != __file__] + + logger.info('%d test modules found', len(modules)) + + for mname in modules: + try: + module = __import__(mname) + except ImportError as ex: + logger.error('could not load %s: %s is missing', mname, ex.name) + else: + tests = unittest.defaultTestLoader.loadTestsFromModule(module) + suite.addTests(tests) + + logger.debug('found %d test cases in %s', tests.countTestCases(), mname) + + logger.info('%d test cases were added', suite.countTestCases()) unittest.TextTestRunner().run(suite) diff --git a/tests/regress/rep_hook.py b/tests/regress/rep_hook.py index 493c0d1dd7..cbcb9c1456 100755 --- a/tests/regress/rep_hook.py +++ b/tests/regress/rep_hook.py @@ -1,28 +1,36 @@ #!/usr/bin/python + +import regress + from unicorn import * from unicorn.x86_const import * -import regress -PAGE_SIZE = 4 * 1024 -CODE = b'\xf3\xaa' # rep stosb +PAGE_SIZE = 0x1000 +CODE = b'\xf3\xaa' # rep stosb +BASE = 0x00000000 -def hook_code(uc, addr, size, user_data): - print("hook called at %x" %addr) -class REP(regress.RegressTest): +class TestRep(regress.RegressTest): - def test_rep(self): + def runTest(self): mu = Uc(UC_ARCH_X86, UC_MODE_32) - mu.mem_map(0, PAGE_SIZE) - mu.mem_write(0, CODE) - mu.reg_write(UC_X86_REG_ECX, 3) - mu.reg_write(UC_X86_REG_EDI, 0x100) - mu.hook_add(UC_HOOK_CODE, hook_code) + mu.mem_map(BASE, PAGE_SIZE) + mu.mem_write(BASE, CODE) + + mu.reg_write(UC_X86_REG_ECX, 8) + mu.reg_write(UC_X86_REG_ESI, 0x10) + mu.reg_write(UC_X86_REG_EDI, 0x20) + + def __hook_code(uc, addr, size, ud): + regress.logger.debug('iterations remaining: %d', uc.reg_read(UC_X86_REG_ECX)) + + mu.hook_add(UC_HOOK_CODE, __hook_code) + + mu.emu_start(BASE, len(CODE)) - mu.emu_start(0, len(CODE)) self.assertEqual(0, mu.reg_read(UC_X86_REG_ECX)) diff --git a/tests/regress/run_across_bb.py b/tests/regress/run_across_bb.py index 074db4d4dd..62326751c1 100755 --- a/tests/regress/run_across_bb.py +++ b/tests/regress/run_across_bb.py @@ -3,33 +3,31 @@ # This test demonstrates emulation behavior within and across # basic blocks. -from __future__ import print_function import binascii -import regress import struct +import regress from unicorn import * from unicorn.x86_const import * -CODE = binascii.unhexlify(b"".join([ - b"b800000000", # 1000: b8 00 00 00 00 mov eax,0x0 - b"40", # 1005: 40 inc eax - b"40", # 1006: 40 inc eax - b"6810100000", # 1007: 68 10 10 00 00 push 0x1010 - b"c3", # 100c: c3 ret - b"cc", # 100d: cc int3 - b"cc", # 100e: cc int3 - b"cc", # 100f: cc int3 - b"b800000000", # 1010: b8 00 00 00 00 mov eax,0x0 - b"40", # 1015: 40 inc eax - b"40", # 1016: 40 inc eax - ])) +CODE = ( + b"\xb8\x00\x00\x00\x00" # 1000: mov eax,0x0 + b"\x40" # 1005: inc eax + b"\x40" # 1006: inc eax + b"\x68\x10\x10\x00\x00" # 1007: push 0x1010 + b"\xc3" # 100c: ret + b"\xcc" # 100d: int3 + b"\xcc" # 100e: int3 + b"\xcc" # 100f: int3 + b"\xb8\x00\x00\x00\x00" # 1010: mov eax,0x0 + b"\x40" # 1015: inc eax + b"\x40" # 1016: inc eax +) def showpc(mu): - pc = mu.reg_read(UC_X86_REG_EIP) - print("pc: 0x%x" % (pc)) + regress.logger.debug("pc: 0x%x", mu.reg_read(UC_X86_REG_EIP)) class RunAcrossBBTest(regress.RegressTest): @@ -38,13 +36,13 @@ def test_run_all(self): ####################################################################### # emu SETUP ####################################################################### - print("\n---- test: run_all ----") - + regress.logger.debug("\n---- test: run_all ----") mu = Uc(UC_ARCH_X86, UC_MODE_32) def hook_code(uc, address, size, user_data): - print(">>> Tracing instruction at 0x%x, instruction size = %u" %(address, size)) + regress.logger.debug(">>> Tracing instruction at 0x%x, instruction size = %u", address, size) + mu.hook_add(UC_HOOK_CODE, hook_code) # base of CODE @@ -73,33 +71,34 @@ def hook_code(uc, address, size, user_data): # 1010: b8 00 00 00 00 mov eax,0x0 <-+ # 1015: 40 inc eax < # 1016: 40 inc eax < + self.assertEqual(0x1016, mu.reg_read(UC_X86_REG_EIP), "unexpected PC (2)") self.assertEqual(0x2800, mu.reg_read(UC_X86_REG_ESP), "unexpected SP (2)") + showpc(mu) except UcError as e: - if e.errno == UC_ERR_FETCH_UNMAPPED: - # during initial test dev, bad fetch at 0x1010, but the data is there, - # and this proves it - print("!!! about to bail due to bad fetch... here's the data at PC:") - print(binascii.hexlify(mu.mem_read(mu.reg_read(UC_X86_REG_EIP), 0x8))) - - self.assertFalse(True, "ERROR: %s @ 0x%x" % (e, mu.reg_read(UC_X86_REG_EIP))) + eip = mu.reg_read(UC_X86_REG_EIP) + if e.errno == UC_ERR_FETCH_UNMAPPED: + # during initial test dev, bad fetch at 0x1010, but the data is there, and this proves it + regress.logger.error("!!! about to bail due to bad fetch... here's the data at PC:") + regress.logger.error(binascii.hexlify(mu.mem_read(eip, 8))) + self.fail("ERROR: %s @ 0x%x" % (e, eip)) def test_run_across_bb(self): try: ####################################################################### # emu SETUP ####################################################################### - print("\n---- test: run_across_bb ----") - + regress.logger.debug("\n---- test: run_across_bb ----") mu = Uc(UC_ARCH_X86, UC_MODE_32) def hook_code(uc, address, size, user_data): - print(">>> Tracing instruction at 0x%x, instruction size = %u" %(address, size)) + regress.logger.debug(">>> Tracing instruction at 0x%x, instruction size = %u", address, size) + mu.hook_add(UC_HOOK_CODE, hook_code) # base of CODE @@ -111,8 +110,10 @@ def hook_code(uc, address, size, user_data): mu.reg_write(UC_X86_REG_EIP, 0x1000) mu.reg_write(UC_X86_REG_ESP, 0x2800) + self.assertEqual(0x1000, mu.reg_read(UC_X86_REG_EIP), "unexpected PC") self.assertEqual(0x2800, mu.reg_read(UC_X86_REG_ESP), "unexpected SP") + showpc(mu) @@ -182,13 +183,14 @@ def hook_code(uc, address, size, user_data): showpc(mu) except UcError as e: + eip = mu.reg_read(UC_X86_REG_EIP) + if e.errno == UC_ERR_FETCH_UNMAPPED: - # during initial test dev, bad fetch at 0x1010, but the data is there, - # and this proves it - print("!!! about to bail due to bad fetch... here's the data at PC:") - print(binascii.hexlify(mu.mem_read(mu.reg_read(UC_X86_REG_EIP), 0x8))) + # during initial test dev, bad fetch at 0x1010, but the data is there, and this proves it + regress.logger.error("!!! about to bail due to bad fetch... here's the data at PC:") + regress.logger.error(binascii.hexlify(mu.mem_read(eip, 8))) - self.assertFalse(True, "ERROR: %s @ 0x%x" % (e, mu.reg_read(UC_X86_REG_EIP))) + self.fail("ERROR: %s @ 0x%x" % (e, eip)) if __name__ == '__main__': diff --git a/tests/regress/sparc64.py b/tests/regress/sparc64.py index 343076399b..25766b221c 100755 --- a/tests/regress/sparc64.py +++ b/tests/regress/sparc64.py @@ -1,24 +1,33 @@ #!/usr/bin/python +import regress + from unicorn import * from unicorn.sparc_const import * -PAGE_SIZE = 1 * 1024 * 1024 -uc = Uc(UC_ARCH_SPARC, UC_MODE_SPARC64|UC_MODE_BIG_ENDIAN) -uc.reg_write(UC_SPARC_REG_SP, 100) -print 'writing sp = 100' +CODE = ( + b"\xb0\x06\x20\x01" # 0: b0 06 20 01 inc %i0 + b"\xb2\x06\x60\x01" # 4: b2 06 60 01 inc %i1 +) + +BASE = 0x00000000 + +class TestSparcRegRead(regress.RegressTest): + def runTest(self): + uc = Uc(UC_ARCH_SPARC, UC_MODE_SPARC64|UC_MODE_BIG_ENDIAN) + + uc.mem_map(BASE, 0x1000 ** 2) + uc.mem_write(BASE, CODE) + + uc.reg_write(UC_SPARC_REG_SP, 100) - # 0: b0 06 20 01 inc %i0 - # 4: b2 06 60 01 inc %i1 + uc.emu_start(BASE, BASE + len(CODE), count=2) -CODE = "\xb0\x06\x20\x01" \ - "\xb2\x06\x60\x01" + regress.logger.debug('sp = %#x', uc.reg_read(UC_SPARC_REG_SP)) + regress.logger.debug('i0 = %#x', uc.reg_read(UC_SPARC_REG_I0)) + regress.logger.debug('i1 = %#x', uc.reg_read(UC_SPARC_REG_I1)) -uc.mem_map(0, PAGE_SIZE) -uc.mem_write(0, CODE) -uc.emu_start(0, len(CODE), 0, 2) -print 'sp =', uc.reg_read(UC_SPARC_REG_SP) -print 'i0 =', uc.reg_read(UC_SPARC_REG_I0) -print 'i1 =', uc.reg_read(UC_SPARC_REG_I1) +if __name__ == '__main__': + regress.main() diff --git a/tests/regress/sparc_reg.py b/tests/regress/sparc_reg.py index 3d55065f69..abf4313351 100755 --- a/tests/regress/sparc_reg.py +++ b/tests/regress/sparc_reg.py @@ -1,205 +1,109 @@ #!/usr/bin/python +import regress + from unicorn import * from unicorn.sparc_const import * + PAGE_SIZE = 1 * 1024 * 1024 -uc = Uc(UC_ARCH_SPARC, UC_MODE_SPARC32|UC_MODE_BIG_ENDIAN) -uc.reg_write(UC_SPARC_REG_SP, 100) -uc.reg_write(UC_SPARC_REG_FP, 200) - - # 0x0: \x80\x00\x20\x01 add %g0, 1, %g0 - # 0x4: \x82\x00\x60\x01 add %g1, 1, %g1 - # 0x8: \x84\x00\xA0\x01 add %g2, 1, %g2 - # 0xc: \x86\x00\xE0\x01 add %g3, 1, %g3 - # 0x10: \x88\x01\x20\x01 add %g4, 1, %g4 - # 0x14: \x8A\x01\x60\x01 add %g5, 1, %g5 - # 0x18: \x8C\x01\xA0\x01 add %g6, 1, %g6 - # 0x1c: \x8E\x01\xE0\x01 add %g7, 1, %g7 - # 0x20: \x90\x02\x20\x01 add %o0, 1, %o0 - # 0x24: \x92\x02\x60\x01 add %o1, 1, %o1 - # 0x28: \x94\x02\xA0\x01 add %o2, 1, %o2 - # 0x2c: \x96\x02\xE0\x01 add %o3, 1, %o3 - # 0x30: \x98\x03\x20\x01 add %o4, 1, %o4 - # 0x34: \x9A\x03\x60\x01 add %o5, 1, %o5 - # 0x38: \x9C\x03\xA0\x01 add %sp, 1, %sp - # 0x3c: \x9E\x03\xE0\x01 add %o7, 1, %o7 - # 0x40: \xA0\x04\x20\x01 add %l0, 1, %l0 - # 0x44: \xA2\x04\x60\x01 add %l1, 1, %l1 - # 0x48: \xA4\x04\xA0\x01 add %l2, 1, %l2 - # 0x4c: \xA6\x04\xE0\x01 add %l3, 1, %l3 - # 0x50: \xA8\x05\x20\x01 add %l4, 1, %l4 - # 0x54: \xAA\x05\x60\x01 add %l5, 1, %l5 - # 0x58: \xAC\x05\xA0\x01 add %l6, 1, %l6 - # 0x5c: \xAE\x05\xE0\x01 add %l7, 1, %l7 - # 0x0: \xB0\x06\x20\x01 add %i0, 1, %i0 - # 0x4: \xB2\x06\x60\x01 add %i1, 1, %i1 - # 0x8: \xB4\x06\xA0\x01 add %i2, 1, %i2 - # 0xc: \xB6\x06\xE0\x01 add %i3, 1, %i3 - # 0x10: \xB8\x07\x20\x01 add %i4, 1, %i4 - # 0x14: \xBA\x07\x60\x01 add %i5, 1, %i5 - # 0x18: \xBC\x07\xA0\x01 add %fp, 1, %fp - # 0x1c: \xBE\x07\xE0\x01 add %i7, 1, %i7 - - -CODE = "\x80\x00\x20\x01" \ - "\x82\x00\x60\x01" \ - "\x84\x00\xA0\x01" \ - "\x86\x00\xE0\x01" \ - "\x88\x01\x20\x01" \ - "\x8A\x01\x60\x01" \ - "\x8C\x01\xA0\x01" \ - "\x8E\x01\xE0\x01" \ - "\x90\x02\x20\x01" \ - "\x92\x02\x60\x01" \ - "\x94\x02\xA0\x01" \ - "\x96\x02\xE0\x01" \ - "\x98\x03\x20\x01" \ - "\x9A\x03\x60\x01" \ - "\x9C\x03\xA0\x01" \ - "\x9E\x03\xE0\x01" \ - "\xA0\x04\x20\x01" \ - "\xA2\x04\x60\x01" \ - "\xA4\x04\xA0\x01" \ - "\xA6\x04\xE0\x01" \ - "\xA8\x05\x20\x01" \ - "\xAA\x05\x60\x01" \ - "\xAC\x05\xA0\x01" \ - "\xAE\x05\xE0\x01" \ - "\xB0\x06\x20\x01" \ - "\xB2\x06\x60\x01" \ - "\xB4\x06\xA0\x01" \ - "\xB6\x06\xE0\x01" \ - "\xB8\x07\x20\x01" \ - "\xBA\x07\x60\x01" \ - "\xBC\x07\xA0\x01" \ - "\xBE\x07\xE0\x01" - - -uc.mem_map(0, PAGE_SIZE) -uc.mem_write(0, CODE) -uc.emu_start(0, len(CODE), 0, 32) - -def print_registers(mu): - g0 = mu.reg_read(UC_SPARC_REG_G0) - g1 = mu.reg_read(UC_SPARC_REG_G1) - g2 = mu.reg_read(UC_SPARC_REG_G2) - g3 = mu.reg_read(UC_SPARC_REG_G3) - g4 = mu.reg_read(UC_SPARC_REG_G4) - g5 = mu.reg_read(UC_SPARC_REG_G5) - g6 = mu.reg_read(UC_SPARC_REG_G6) - g7 = mu.reg_read(UC_SPARC_REG_G7) - - o0 = mu.reg_read(UC_SPARC_REG_O0) - o1 = mu.reg_read(UC_SPARC_REG_O1) - o2 = mu.reg_read(UC_SPARC_REG_O2) - o3 = mu.reg_read(UC_SPARC_REG_O3) - o4 = mu.reg_read(UC_SPARC_REG_O4) - o5 = mu.reg_read(UC_SPARC_REG_O5) - o6 = mu.reg_read(UC_SPARC_REG_O6) - o7 = mu.reg_read(UC_SPARC_REG_O7) - - l0 = mu.reg_read(UC_SPARC_REG_L0) - l1 = mu.reg_read(UC_SPARC_REG_L1) - l2 = mu.reg_read(UC_SPARC_REG_L2) - l3 = mu.reg_read(UC_SPARC_REG_L3) - l4 = mu.reg_read(UC_SPARC_REG_L4) - l5 = mu.reg_read(UC_SPARC_REG_L5) - l6 = mu.reg_read(UC_SPARC_REG_L6) - l7 = mu.reg_read(UC_SPARC_REG_L7) - - i0 = mu.reg_read(UC_SPARC_REG_I0) - i1 = mu.reg_read(UC_SPARC_REG_I1) - i2 = mu.reg_read(UC_SPARC_REG_I2) - i3 = mu.reg_read(UC_SPARC_REG_I3) - i4 = mu.reg_read(UC_SPARC_REG_I4) - i5 = mu.reg_read(UC_SPARC_REG_I5) - i6 = mu.reg_read(UC_SPARC_REG_I6) - i7 = mu.reg_read(UC_SPARC_REG_I7) - - pc = mu.reg_read(UC_SPARC_REG_PC) - sp = mu.reg_read(UC_SPARC_REG_SP) - fp = mu.reg_read(UC_SPARC_REG_FP) - print(" G0 = %d" % g0) - print(" G1 = %d" % g1) - print(" G2 = %d" % g2) - print(" G3 = %d" % g3) - print(" G4 = %d" % g4) - print(" G5 = %d" % g5) - print(" G6 = %d" % g6) - print(" G7 = %d" % g7) - print("") - print(" O0 = %d" % o0) - print(" O1 = %d" % o1) - print(" O2 = %d" % o2) - print(" O3 = %d" % o3) - print(" O4 = %d" % o4) - print(" O5 = %d" % o5) - print(" O6 = %d" % o6) - print(" O7 = %d" % o7) - print("") - print(" L0 = %d" % l0) - print(" L1 = %d" % l1) - print(" L2 = %d" % l2) - print(" L3 = %d" % l3) - print(" L4 = %d" % l4) - print(" L5 = %d" % l5) - print(" L6 = %d" % l6) - print(" L7 = %d" % l7) - print("") - print(" I0 = %d" % i0) - print(" I1 = %d" % i1) - print(" I2 = %d" % i2) - print(" I3 = %d" % i3) - print(" I4 = %d" % i4) - print(" I5 = %d" % i5) - print(" I6 = %d" % i6) - print(" I7 = %d" % i7) - print("") - print(" PC = %d" % pc) - print(" SP = %d" % sp) - print(" FP = %d" % fp) - print("") - -print_registers(uc) - -assert uc.reg_read(UC_SPARC_REG_PC) == 132 # make sure we executed all instructions -assert uc.reg_read(UC_SPARC_REG_SP) == 101 -assert uc.reg_read(UC_SPARC_REG_FP) == 201 - -assert uc.reg_read(UC_SPARC_REG_G0) == 0 # G0 is always zero -assert uc.reg_read(UC_SPARC_REG_G1) == 1 -assert uc.reg_read(UC_SPARC_REG_G2) == 1 -assert uc.reg_read(UC_SPARC_REG_G3) == 1 -assert uc.reg_read(UC_SPARC_REG_G4) == 1 -assert uc.reg_read(UC_SPARC_REG_G5) == 1 -assert uc.reg_read(UC_SPARC_REG_G6) == 1 -assert uc.reg_read(UC_SPARC_REG_G7) == 1 - -assert uc.reg_read(UC_SPARC_REG_O0) == 1 -assert uc.reg_read(UC_SPARC_REG_O1) == 1 -assert uc.reg_read(UC_SPARC_REG_O2) == 1 -assert uc.reg_read(UC_SPARC_REG_O3) == 1 -assert uc.reg_read(UC_SPARC_REG_O4) == 1 -assert uc.reg_read(UC_SPARC_REG_O5) == 1 -assert uc.reg_read(UC_SPARC_REG_O6) == 101 -assert uc.reg_read(UC_SPARC_REG_O7) == 1 - -assert uc.reg_read(UC_SPARC_REG_L0) == 1 -assert uc.reg_read(UC_SPARC_REG_L1) == 1 -assert uc.reg_read(UC_SPARC_REG_L2) == 1 -assert uc.reg_read(UC_SPARC_REG_L3) == 1 -assert uc.reg_read(UC_SPARC_REG_L4) == 1 -assert uc.reg_read(UC_SPARC_REG_L5) == 1 -assert uc.reg_read(UC_SPARC_REG_L6) == 1 -assert uc.reg_read(UC_SPARC_REG_L7) == 1 - -assert uc.reg_read(UC_SPARC_REG_I0) == 1 -assert uc.reg_read(UC_SPARC_REG_I1) == 1 -assert uc.reg_read(UC_SPARC_REG_I2) == 1 -assert uc.reg_read(UC_SPARC_REG_I3) == 1 -assert uc.reg_read(UC_SPARC_REG_I4) == 1 -assert uc.reg_read(UC_SPARC_REG_I5) == 1 -assert uc.reg_read(UC_SPARC_REG_I6) == 201 -assert uc.reg_read(UC_SPARC_REG_I7) == 1 +CODE =( + b"\x80\x00\x20\x01" # add %g0, 1, %g0 + b"\x82\x00\x60\x01" # add %g1, 1, %g1 + b"\x84\x00\xA0\x01" # add %g2, 1, %g2 + b"\x86\x00\xE0\x01" # add %g3, 1, %g3 + b"\x88\x01\x20\x01" # add %g4, 1, %g4 + b"\x8A\x01\x60\x01" # add %g5, 1, %g5 + b"\x8C\x01\xA0\x01" # add %g6, 1, %g6 + b"\x8E\x01\xE0\x01" # add %g7, 1, %g7 + b"\x90\x02\x20\x01" # add %o0, 1, %o0 + b"\x92\x02\x60\x01" # add %o1, 1, %o1 + b"\x94\x02\xA0\x01" # add %o2, 1, %o2 + b"\x96\x02\xE0\x01" # add %o3, 1, %o3 + b"\x98\x03\x20\x01" # add %o4, 1, %o4 + b"\x9A\x03\x60\x01" # add %o5, 1, %o5 + b"\x9C\x03\xA0\x01" # add %sp, 1, %sp + b"\x9E\x03\xE0\x01" # add %o7, 1, %o7 + b"\xA0\x04\x20\x01" # add %l0, 1, %l0 + b"\xA2\x04\x60\x01" # add %l1, 1, %l1 + b"\xA4\x04\xA0\x01" # add %l2, 1, %l2 + b"\xA6\x04\xE0\x01" # add %l3, 1, %l3 + b"\xA8\x05\x20\x01" # add %l4, 1, %l4 + b"\xAA\x05\x60\x01" # add %l5, 1, %l5 + b"\xAC\x05\xA0\x01" # add %l6, 1, %l6 + b"\xAE\x05\xE0\x01" # add %l7, 1, %l7 + b"\xB0\x06\x20\x01" # add %i0, 1, %i0 + b"\xB2\x06\x60\x01" # add %i1, 1, %i1 + b"\xB4\x06\xA0\x01" # add %i2, 1, %i2 + b"\xB6\x06\xE0\x01" # add %i3, 1, %i3 + b"\xB8\x07\x20\x01" # add %i4, 1, %i4 + b"\xBA\x07\x60\x01" # add %i5, 1, %i5 + b"\xBC\x07\xA0\x01" # add %fp, 1, %fp + b"\xBE\x07\xE0\x01" # add %i7, 1, %i7 +) + +BASE = 0x00000000 + + +def hook_code(uc, addr, size, ud): + regress.logger.debug("executing at 0x%04x", uc.reg_read(UC_SPARC_REG_PC)) + + +class TestSparcRegRead(regress.RegressTest): + def runTest(self): + uc = Uc(UC_ARCH_SPARC, UC_MODE_SPARC32 | UC_MODE_BIG_ENDIAN) + + uc.reg_write(UC_SPARC_REG_SP, 100) + uc.reg_write(UC_SPARC_REG_FP, 200) + + uc.mem_map(BASE, PAGE_SIZE) + uc.mem_write(BASE, CODE) + + uc.hook_add(UC_HOOK_CODE, hook_code) + uc.emu_start(BASE, len(CODE)) + + self.assertEqual(0, uc.reg_read(UC_SPARC_REG_G0)) # G0 is always zero + self.assertEqual(1, uc.reg_read(UC_SPARC_REG_G1)) + self.assertEqual(1, uc.reg_read(UC_SPARC_REG_G2)) + self.assertEqual(1, uc.reg_read(UC_SPARC_REG_G3)) + self.assertEqual(1, uc.reg_read(UC_SPARC_REG_G4)) + self.assertEqual(1, uc.reg_read(UC_SPARC_REG_G5)) + self.assertEqual(1, uc.reg_read(UC_SPARC_REG_G6)) + self.assertEqual(1, uc.reg_read(UC_SPARC_REG_G7)) + + self.assertEqual(1, uc.reg_read(UC_SPARC_REG_O0)) + self.assertEqual(1, uc.reg_read(UC_SPARC_REG_O1)) + self.assertEqual(1, uc.reg_read(UC_SPARC_REG_O2)) + self.assertEqual(1, uc.reg_read(UC_SPARC_REG_O3)) + self.assertEqual(1, uc.reg_read(UC_SPARC_REG_O4)) + self.assertEqual(1, uc.reg_read(UC_SPARC_REG_O5)) + self.assertEqual(101, uc.reg_read(UC_SPARC_REG_O6)) + self.assertEqual(1, uc.reg_read(UC_SPARC_REG_O7)) + + self.assertEqual(1, uc.reg_read(UC_SPARC_REG_L0)) + self.assertEqual(1, uc.reg_read(UC_SPARC_REG_L1)) + self.assertEqual(1, uc.reg_read(UC_SPARC_REG_L2)) + self.assertEqual(1, uc.reg_read(UC_SPARC_REG_L3)) + self.assertEqual(1, uc.reg_read(UC_SPARC_REG_L4)) + self.assertEqual(1, uc.reg_read(UC_SPARC_REG_L5)) + self.assertEqual(1, uc.reg_read(UC_SPARC_REG_L6)) + self.assertEqual(1, uc.reg_read(UC_SPARC_REG_L7)) + + self.assertEqual(1, uc.reg_read(UC_SPARC_REG_I0)) + self.assertEqual(1, uc.reg_read(UC_SPARC_REG_I1)) + self.assertEqual(1, uc.reg_read(UC_SPARC_REG_I2)) + self.assertEqual(1, uc.reg_read(UC_SPARC_REG_I3)) + self.assertEqual(1, uc.reg_read(UC_SPARC_REG_I4)) + self.assertEqual(1, uc.reg_read(UC_SPARC_REG_I5)) + self.assertEqual(201, uc.reg_read(UC_SPARC_REG_I6)) + self.assertEqual(1, uc.reg_read(UC_SPARC_REG_I7)) + + # BUG: PC seems to get reset to 4 when done executing + # self.assertEqual(4 * 32, uc.reg_read(UC_SPARC_REG_PC)) # make sure we executed all instructions + self.assertEqual(101, uc.reg_read(UC_SPARC_REG_SP)) + self.assertEqual(201, uc.reg_read(UC_SPARC_REG_FP)) + + +if __name__ == '__main__': + regress.main() diff --git a/tests/regress/tcg_liveness_analysis_bug_issue-287.py b/tests/regress/tcg_liveness_analysis_bug_issue-287.py index 4310a8cb27..25c5b251ec 100755 --- a/tests/regress/tcg_liveness_analysis_bug_issue-287.py +++ b/tests/regress/tcg_liveness_analysis_bug_issue-287.py @@ -1,134 +1,150 @@ -from unicorn import * + +import regress + +from unicorn import * from unicorn.arm_const import * -import binascii - -MB = 1024 * 1024 -PAGE = 4 * 1024 - -def PrintArmRegisters(uc_emu): - print 'R0 : '+hex(uc_emu.reg_read(UC_ARM_REG_R0)) - print 'R1 : '+hex(uc_emu.reg_read(UC_ARM_REG_R1)) - print 'R2 : '+hex(uc_emu.reg_read(UC_ARM_REG_R2)) - print 'R3 : '+hex(uc_emu.reg_read(UC_ARM_REG_R3)) - print 'R4 : '+hex(uc_emu.reg_read(UC_ARM_REG_R4)) - print 'R5 : '+hex(uc_emu.reg_read(UC_ARM_REG_R5)) - print 'R6 : '+hex(uc_emu.reg_read(UC_ARM_REG_R6)) - print 'R7 : '+hex(uc_emu.reg_read(UC_ARM_REG_R7)) - print 'R8 : '+hex(uc_emu.reg_read(UC_ARM_REG_R8)) - print 'R9 : '+hex(uc_emu.reg_read(UC_ARM_REG_R9)) - print 'R10 : '+hex(uc_emu.reg_read(UC_ARM_REG_R10)) - print 'R11 : '+hex(uc_emu.reg_read(UC_ARM_REG_R11)) - print 'R12 : '+hex(uc_emu.reg_read(UC_ARM_REG_R12)) - print 'SP : '+hex(uc_emu.reg_read(UC_ARM_REG_SP)) - print 'LR : '+hex(uc_emu.reg_read(UC_ARM_REG_LR)) - print 'PC : '+hex(uc_emu.reg_read(UC_ARM_REG_PC)) - flags = uc_emu.reg_read(UC_ARM_REG_CPSR) - print 'carry : '+str(flags >> 29 & 0x1) - print 'overflow : '+str(flags >> 28 & 0x1) - print 'negative : '+str(flags >> 31 & 0x1) - print 'zero : '+str(flags >> 30 & 0x1) - -''' - issue #287 - Initial Register States: R0=3, R1=24, R2=16, R3=0 - ----- code start ----- - CMP R0,R1,LSR#3 - SUBCS R0,R0,R1,LSR#3 # CPU flags got changed in these two instructions, and *REMEMBERED*, now NF == VF == 0 - CMP R0,#1 # CPU flags changed again, now NF == 1, VF == 0, but they are not properly *REMEMBERED* - MOV R1,R1,LSR#4 - SUBGES R2,R2,#4 # according to the result of CMP, we should skip this op - - MOVGE R3,#100 # since changed flags are not *REMEMBERED* in CMP, now NF == VF == 0, which result in wrong branch - # at the end of this code block, should R3 == 0 - ----- code end ------ - - # TCG ops are correct, plain op translation is done correctly, - # but there're In-Memory bits invisible from ops that control the host code generation. - # all these codes are in one TCG translation-block, so wrong things could happen. - # detail explanation is given on the right side. - # remember, both set_label and brcond are point to refresh the dead_temps and mem_temps states in TCG - ----- TCG ops ------ - ld_i32 tmp5,env,$0xfffffffffffffff4 - movi_i32 tmp6,$0x0 - brcond_i32 tmp5,tmp6,ne,$0x0 - mov_i32 tmp5,r1 ------------------------- - movi_i32 tmp6,$0x3 | - shr_i32 tmp5,r1,tmp6 | - mov_i32 tmp6,r0 | - sub_i32 NF,r0,tmp5 | - mov_i32 ZF,NF | - setcond_i32 CF,r0,tmp5,geu | # This part is "CMP R0,R1,LSR#3" - xor_i32 VF,NF,r0 |-----> # and "SUBCS R0,R0,R1,LSR#3" - xor_i32 tmp7,r0,tmp5 | # the last op in this block, set_label get a chance to refresh the TCG globals memory states, - and_i32 VF,VF,tmp7 | # so things get back to normal states - mov_i32 tmp6,NF | # these codes are not affected by the bug. Let's called this Part-D - movi_i32 tmp5,$0x0 | - brcond_i32 CF,tmp5,eq,$0x1 | - mov_i32 tmp5,r1 | - movi_i32 tmp6,$0x3 | - shr_i32 tmp5,r1,tmp6 | - mov_i32 tmp6,r0 | - sub_i32 tmp6,r0,tmp5 | - mov_i32 r0,tmp6 | - set_label $0x1 ------------------------- - movi_i32 tmp5,$0x1 ----------------- # Let's called this Part-C - mov_i32 tmp6,r0 | # NF is used as output operand again! - sub_i32 NF,r0,tmp5 ----------------|-----> # but it is stated as Not-In-Memory, - mov_i32 ZF,NF | # no need to sync it after calculation. - setcond_i32 CF,r0,tmp5,geu | # the generated host code does not write NF - xor_i32 VF,NF,r0 | # back to its memory location, hence forgot. And the CPU flags after this calculation is not changed. - xor_i32 tmp7,r0,tmp5 | # Caution: the following SUBGES's condition check is right, even though the generated host code does not *REMEMBER* NF, it will cache the calculated result and serve SUBGES correctly - and_i32 VF,VF,tmp7 | - mov_i32 tmp6,NF | - mov_i32 tmp5,r1 | # this part is "CMP R0,#1" - movi_i32 tmp6,$0x4 | # and "MOV R1,R1,LSR#4" - shr_i32 tmp5,r1,tmp6 | # and "SUBGES R2,R2,#4" - mov_i32 r1,tmp5 |-----> # This is the part where problem start to arise - xor_i32 tmp5,VF,NF | - movi_i32 tmp6,$0x0 | - brcond_i32 tmp5,tmp6,lt,$0x2 --------|-----> # QEMU will refresh the InMemory bit for TCG globals here, but Unicorn won't - movi_i32 tmp5,$0x4 | - mov_i32 tmp6,r2 | # this is the 1st bug-related op get analyzed. - sub_i32 NF,r2,tmp5 ----------------|-----> # here, NF is an output operand, it's flagged dead - mov_i32 ZF,NF | # and the InMemory bit is clear, tell the previous(above) ops - setcond_i32 CF,r2,tmp5,geu | # if it is used as output operand again, do not sync it - xor_i32 VF,NF,r2 | # so the generated host-code for previous ops will not write it back to Memory - xor_i32 tmp7,r2,tmp5 | # Caution: the CPU flags after this calculation is also right, because the set_label is a point of refresh, make them *REMEMBERED* - and_i32 VF,VF,tmp7 | # Let's call this Part-B - mov_i32 tmp6,NF | - mov_i32 r2,ZF | - set_label $0x2 ----------------- - xor_i32 tmp5,VF,NF ----------------- - movi_i32 tmp6,$0x0 | - brcond_i32 tmp5,tmp6,lt,$0x3 | # Let's call this Part-A - movi_i32 tmp5,$0x64 | # if Part-B is not skipped, this part won't go wrong, because we'll check the CPU flags as the result of Part-B, it's *REMEMBERED* - movi_i32 r3,$0x64 |-----> # but if Part-B is skipped, - set_label $0x3 | # what should we expected? we will check the condition based on the result of Part-D!!! - call wfi,$0x0,$0,env | # because result of Part-C is lost. this is why things go wrong. - set_label $0x0 | - exit_tb $0x7f6401714013 ----------------- - ########### - ----- TCG ends ------ -''' - -TestCode = b'\xa1\x01\x50\xe1\xa1\x01\x40\x20\x01\x00\x50\xe3\x21\x12\xa0\xe1\x04\x20\x52\xa2\x64\x30\xa0\xa3' - -def UseUcToEmulate(): - try: - uc_emu = Uc(UC_ARCH_ARM, UC_MODE_ARM) - #if LoadCode(uc_emu, 2*MB, 0x9004): - uc_emu.mem_map(0, 2*MB) - uc_emu.reg_write(UC_ARM_REG_SP, 0x40000) - uc_emu.reg_write(UC_ARM_REG_R0, 3) - uc_emu.reg_write(UC_ARM_REG_R1, 24) - uc_emu.reg_write(UC_ARM_REG_R2, 16) - uc_emu.mem_write(0, TestCode) - uc_emu.emu_start(0, 24) - PrintArmRegisters(uc_emu) - - except UcError as e: - print("ERROR: %s" % e) - PrintArmRegisters(uc_emu) - - -UseUcToEmulate() + + +# issue #287 +# Initial Register States: R0=3, R1=24, R2=16, R3=0 +# +# ----- code start ----- +# CMP R0,R1,LSR#3 +# SUBCS R0,R0,R1,LSR#3 # CPU flags got changed in these two instructions, and *REMEMBERED*, now NF == VF == 0 +# CMP R0,#1 # CPU flags changed again, now NF == 1, VF == 0, but they are not properly *REMEMBERED* +# MOV R1,R1,LSR#4 +# SUBGES R2,R2,#4 # according to the result of CMP, we should skip this op +# +# MOVGE R3,#100 # since changed flags are not *REMEMBERED* in CMP, now NF == VF == 0, which result in wrong branch +# # at the end of this code block, should R3 == 0 +# ----- code end ------ +# +# TCG ops are correct, plain op translation is done correctly, +# but there're In-Memory bits invisible from ops that control the host code generation. +# all these codes are in one TCG translation-block, so wrong things could happen. +# detail explanation is given on the right side. +# remember, both set_label and brcond are point to refresh the dead_temps and mem_temps states in TCG +# +# ----- TCG ops ------ +# ld_i32 tmp5,env,$0xfffffffffffffff4 +# movi_i32 tmp6,$0x0 +# brcond_i32 tmp5,tmp6,ne,$0x0 +# mov_i32 tmp5,r1 ------------------------- +# movi_i32 tmp6,$0x3 | +# shr_i32 tmp5,r1,tmp6 | +# mov_i32 tmp6,r0 | +# sub_i32 NF,r0,tmp5 | +# mov_i32 ZF,NF | +# setcond_i32 CF,r0,tmp5,geu | # This part is "CMP R0,R1,LSR#3" +# xor_i32 VF,NF,r0 |-----> # and "SUBCS R0,R0,R1,LSR#3" +# xor_i32 tmp7,r0,tmp5 | # the last op in this block, set_label get a chance to refresh the TCG globals memory states, +# and_i32 VF,VF,tmp7 | # so things get back to normal states +# mov_i32 tmp6,NF | # these codes are not affected by the bug. Let's called this Part-D +# movi_i32 tmp5,$0x0 | +# brcond_i32 CF,tmp5,eq,$0x1 | +# mov_i32 tmp5,r1 | +# movi_i32 tmp6,$0x3 | +# shr_i32 tmp5,r1,tmp6 | +# mov_i32 tmp6,r0 | +# sub_i32 tmp6,r0,tmp5 | +# mov_i32 r0,tmp6 | +# set_label $0x1 ------------------------- +# movi_i32 tmp5,$0x1 ----------------- # Let's called this Part-C +# mov_i32 tmp6,r0 | # NF is used as output operand again! +# sub_i32 NF,r0,tmp5 ----------------|-----> # but it is stated as Not-In-Memory, +# mov_i32 ZF,NF | # no need to sync it after calculation. +# setcond_i32 CF,r0,tmp5,geu | # the generated host code does not write NF +# xor_i32 VF,NF,r0 | # back to its memory location, hence forgot. And the CPU flags after this calculation is not changed. +# xor_i32 tmp7,r0,tmp5 | # Caution: the following SUBGES's condition check is right, even though the generated host code does not *REMEMBER* NF, it will cache the calculated result and serve SUBGES correctly +# and_i32 VF,VF,tmp7 | +# mov_i32 tmp6,NF | +# mov_i32 tmp5,r1 | # this part is "CMP R0,#1" +# movi_i32 tmp6,$0x4 | # and "MOV R1,R1,LSR#4" +# shr_i32 tmp5,r1,tmp6 | # and "SUBGES R2,R2,#4" +# mov_i32 r1,tmp5 |-----> # This is the part where problem start to arise +# xor_i32 tmp5,VF,NF | +# movi_i32 tmp6,$0x0 | +# brcond_i32 tmp5,tmp6,lt,$0x2 --------|-----> # QEMU will refresh the InMemory bit for TCG globals here, but Unicorn won't +# movi_i32 tmp5,$0x4 | +# mov_i32 tmp6,r2 | # this is the 1st bug-related op get analyzed. +# sub_i32 NF,r2,tmp5 ----------------|-----> # here, NF is an output operand, it's flagged dead +# mov_i32 ZF,NF | # and the InMemory bit is clear, tell the previous(above) ops +# setcond_i32 CF,r2,tmp5,geu | # if it is used as output operand again, do not sync it +# xor_i32 VF,NF,r2 | # so the generated host-code for previous ops will not write it back to Memory +# xor_i32 tmp7,r2,tmp5 | # Caution: the CPU flags after this calculation is also right, because the set_label is a point of refresh, make them *REMEMBERED* +# and_i32 VF,VF,tmp7 | # Let's call this Part-B +# mov_i32 tmp6,NF | +# mov_i32 r2,ZF | +# set_label $0x2 ----------------- +# xor_i32 tmp5,VF,NF ----------------- +# movi_i32 tmp6,$0x0 | +# brcond_i32 tmp5,tmp6,lt,$0x3 | # Let's call this Part-A +# movi_i32 tmp5,$0x64 | # if Part-B is not skipped, this part won't go wrong, because we'll check the CPU flags as the result of Part-B, it's *REMEMBERED* +# movi_i32 r3,$0x64 |-----> # but if Part-B is skipped, +# set_label $0x3 | # what should we expected? we will check the condition based on the result of Part-D!!! +# call wfi,$0x0,$0,env | # because result of Part-C is lost. this is why things go wrong. +# set_label $0x0 | +# exit_tb $0x7f6401714013 ----------------- +# ########### +# ----- TCG ends ------ + + +CODE = ( + b'\xa1\x01\x50\xe1' # cmp r0, r1, lsr #3 + b'\xa1\x01\x40\x20' # subhs r0, r0, r1, lsr #3 + b'\x01\x00\x50\xe3' # cmp r0, #1 + b'\x21\x12\xa0\xe1' # lsr r1, r1, #4 + b'\x04\x20\x52\xa2' # subsge r2, r2, #4 + b'\x64\x30\xa0\xa3' # movge r3, #0x64 +) + +BASE = 0x00000000 + +def show_regs(uc): + regress.logger.debug('R0 : %08x', uc.reg_read(UC_ARM_REG_R0)) + regress.logger.debug('R1 : %08x', uc.reg_read(UC_ARM_REG_R1)) + regress.logger.debug('R2 : %08x', uc.reg_read(UC_ARM_REG_R2)) + regress.logger.debug('R3 : %08x', uc.reg_read(UC_ARM_REG_R3)) + regress.logger.debug('R4 : %08x', uc.reg_read(UC_ARM_REG_R4)) + regress.logger.debug('R5 : %08x', uc.reg_read(UC_ARM_REG_R5)) + regress.logger.debug('R6 : %08x', uc.reg_read(UC_ARM_REG_R6)) + regress.logger.debug('R7 : %08x', uc.reg_read(UC_ARM_REG_R7)) + regress.logger.debug('R8 : %08x', uc.reg_read(UC_ARM_REG_R8)) + regress.logger.debug('R9 : %08x', uc.reg_read(UC_ARM_REG_R9)) + regress.logger.debug('R10 : %08x', uc.reg_read(UC_ARM_REG_R10)) + regress.logger.debug('R11 : %08x', uc.reg_read(UC_ARM_REG_R11)) + regress.logger.debug('R12 : %08x', uc.reg_read(UC_ARM_REG_R12)) + regress.logger.debug('SP : %08x', uc.reg_read(UC_ARM_REG_SP)) + regress.logger.debug('LR : %08x', uc.reg_read(UC_ARM_REG_LR)) + regress.logger.debug('PC : %08x', uc.reg_read(UC_ARM_REG_PC)) + + flags = uc.reg_read(UC_ARM_REG_CPSR) + + regress.logger.debug('carry : %d', (flags >> 29) & 0b1) + regress.logger.debug('overflow : %d', (flags >> 28) & 0b1) + regress.logger.debug('negative : %d', (flags >> 31) & 0b1) + regress.logger.debug('zero : %d', (flags >> 30) & 0b1) + + +class TestReadMem(regress.RegressTest): + def runTest(self): + uc = Uc(UC_ARCH_ARM, UC_MODE_ARM) + + uc.mem_map(BASE, 0x1000) + + uc.reg_write(UC_ARM_REG_SP, 0x40000) + uc.reg_write(UC_ARM_REG_R0, 3) + uc.reg_write(UC_ARM_REG_R1, 24) + uc.reg_write(UC_ARM_REG_R2, 16) + uc.reg_write(UC_ARM_REG_R3, 0) + uc.mem_write(BASE, CODE) + + uc.emu_start(BASE, len(CODE)) + + show_regs(uc) + + self.assertEqual(0, uc.reg_read(UC_ARM_REG_R3)) + + +if __name__ == '__main__': + regress.main() diff --git a/tests/regress/translator_buffer.py b/tests/regress/translator_buffer.py index 1f8e548212..74dbfafb6e 100755 --- a/tests/regress/translator_buffer.py +++ b/tests/regress/translator_buffer.py @@ -1,22 +1,32 @@ #!/usr/bin/python # By Mariano Graziano +import struct +import regress + from unicorn import * from unicorn.x86_const import * -import regress, struct - class Emulator: def __init__(self, code, stack): - self.mask = 0xFFFFFFFFFFFFF000 + def __page_aligned(address): + return address & ~(0x1000 - 1) + self.unicorn_code = code self.unicorn_stack = stack + self.mu = Uc(UC_ARCH_X86, UC_MODE_64) - size = 1 * 4096 - self.mu.mem_map(code & self.mask, size) - size = 1 * 4096 - self.mu.mem_map(stack & self.mask, size) + + regress.logger.debug("mapping code : %#x", __page_aligned(code)) + regress.logger.debug("mapping stack : %#x", __page_aligned(stack)) + + self.mu.mem_map(__page_aligned(code), 0x1000) + self.mu.mem_map(__page_aligned(stack), 0x1000) + + self.mu.reg_write(UC_X86_REG_RSP, stack) + self.mu.reg_write(UC_X86_REG_RIP, code) + self.set_hooks() def set_hooks(self): @@ -26,53 +36,55 @@ def set_hooks(self): def hook_mem_fetch_unmapped(self, uc, access, address, size, value, user_data): next_ip = self.unicorn_code + size - self.mu.reg_write(UC_X86_REG_RIP, next_ip) - self.mu.mem_write(next_ip, "\x90") - self.mu.reg_write(UC_X86_REG_RIP, address) + + self.write_reg(UC_X86_REG_RIP, next_ip) + self.write_data(next_ip, b"\x90") + # self.write_reg(UC_X86_REG_RIP, address) # ??? + return True def hook_mem_invalid(self, uc, access, address, size, value, user_data): + regress.logger.debug("invalid mem access: access type = %d, to = %#x, size = %u, value = %#x", access, address, size, value) + return True - + def hook_mem_access(self, uc, access, address, size, value, user_data): return True - def emu(self, size): + def emu(self, steps): ip = self.mu.reg_read(UC_X86_REG_RIP) - try: - self.mu.emu_start(ip, ip + size, timeout=10000, count=1) - except UcError as e: - print("Error %s" % e) + max_intel_insn_size = 15 + + regress.logger.debug("starting at : %#x", ip) + self.mu.emu_start(ip, ip + max_intel_insn_size, count=steps) def write_data(self, address, content): self.mu.mem_write(address, content) + def write_reg(self, reg, value): + self.mu.reg_write(reg, value) + class Init(regress.RegressTest): - def init_unicorn(self, ip, sp, counter): - #print "[+] Emulating IP: %x SP: %x - Counter: %x" % (ip, sp, counter) - E = Emulator(ip, sp) - E.write_data(ip, "\x90") - E.write_data(sp, self.generate_value(counter)) - E.mu.reg_write(UC_X86_REG_RSP, sp) - E.mu.reg_write(UC_X86_REG_RIP, ip) - E.emu(1) - - def generate_value(self, counter): - start = 0xffff880026f02000 - offset = counter * 8 - address = start + offset - return struct.pack(">> Before emulation ") - print("\tD6 = 0x%x" % mu.reg_read(UC_ARM_REG_D6)) - print("\tD7 = 0x%x" % mu.reg_read(UC_ARM_REG_D7)) - for i in range(UC_ARM_REG_R0, UC_ARM_REG_R12): - val = mu.reg_read(i) - print("\t %s = 0x%x" % ("R" + str(i-UC_ARM_REG_R0),val)) - - self.assertEqual(UC_ARM_REG_D6, mu.reg_read(UC_ARM_REG_D6)) - self.assertEqual(UC_ARM_REG_D7, mu.reg_read(UC_ARM_REG_D7)) - - try: - content = mu.mem_read(SCRATCH_ADDRESS, 100) - print("Memory at addr 0x%X %s" % (SCRATCH_ADDRESS, binascii.hexlify(content))) - content = mu.mem_read(SCRATCH_ADDRESS+0x100, 100) - print("Memory at addr 0x%X %s" % (SCRATCH_ADDRESS+0x100, binascii.hexlify(content))) - except Exception, errtxt: - print (errtxt) - - - # emulate machine code in infinite time - mu.emu_start(ADDRESS, ADDRESS + len(code)) - - # now print out some registers - print(">>> Emulation done. Below is the CPU context") - - sp = mu.reg_read(UC_ARM_REG_SP) - print(">>> SP = 0x%x" %sp) - val = mu.reg_read(UC_ARM_REG_PC) - print(">>> PC = 0x%x" %val) - for i in range(UC_ARM_REG_R0, UC_ARM_REG_R12): - val = mu.reg_read(i) - print(">>> %s = 0x%x" % ("R" + str(i-UC_ARM_REG_R0),val)) - - print("\tD6 = 0x%x" % mu.reg_read(UC_ARM_REG_D6)) - print("\tD7 = 0x%x" % mu.reg_read(UC_ARM_REG_D7)) - - try: - content = mu.mem_read(SCRATCH_ADDRESS, 100) - print("Memory at addr 0x%X %s" % (SCRATCH_ADDRESS, binascii.hexlify(content))) - content = mu.mem_read(SCRATCH_ADDRESS+0x100, 100) - print("Memory at addr 0x%X %s" % (SCRATCH_ADDRESS+0x100, binascii.hexlify(content))) - except Exception, errtxt: - print (errtxt) - - self.assertEqual(mu.reg_read(UC_ARM_REG_D6), 0x0101010101010101) - self.assertEqual(mu.reg_read(UC_ARM_REG_D7), 0x0101010101010101) - - except UcError as e: - print("ERROR: %s" % e) + regress.logger.debug("Emulate THUMB code") + + # Initialize emulator in thumb mode + mu = Uc(UC_ARCH_ARM, UC_MODE_THUMB) + + # map 2MB memory for this emulation + mu.mem_map(ADDRESS, 2 * 1024 * 1024) + + # write machine code to be emulated to memory + mu.mem_write(ADDRESS, code) + + # map 10K scratch memory for this emulation + mu.mem_map(SCRATCH_ADDRESS, 10 * 1024) + + # write dummy data to be emulated to memory + mu.mem_write(SCRATCH_ADDRESS, b"\x01" * 64) + + # initialize machine registers + for i in range(UC_ARM_REG_R0, UC_ARM_REG_R12): + mu.reg_write(i, i - UC_ARM_REG_R0) + + mu.reg_write(UC_ARM_REG_R1, SCRATCH_ADDRESS) + mu.reg_write(UC_ARM_REG_R0, SCRATCH_ADDRESS + 0x100) + + mu.reg_write(UC_ARM_REG_SP, 0x1234) + mu.reg_write(UC_ARM_REG_D6, UC_ARM_REG_D6) + mu.reg_write(UC_ARM_REG_D7, UC_ARM_REG_D7) + + regress.logger.debug(">>> Before emulation") + regress.logger.debug("\tD6 = %#x", mu.reg_read(UC_ARM_REG_D6)) + regress.logger.debug("\tD7 = %#x", mu.reg_read(UC_ARM_REG_D7)) + + for i in range(UC_ARM_REG_R0, UC_ARM_REG_R12): + regress.logger.debug("\tR%d = %#x", (i - UC_ARM_REG_R0), mu.reg_read(i)) + + addr = SCRATCH_ADDRESS + data = mu.mem_read(addr, 100) + regress.logger.debug("Memory at addr %#x: %s", addr, binascii.hexlify(data)) + + addr = SCRATCH_ADDRESS + 0x100 + data = mu.mem_read(addr, 100) + regress.logger.debug("Memory at addr %#x: %s", addr, binascii.hexlify(data)) + + self.assertEqual(UC_ARM_REG_D6, mu.reg_read(UC_ARM_REG_D6)) + self.assertEqual(UC_ARM_REG_D7, mu.reg_read(UC_ARM_REG_D7)) + + # emulate machine code in infinite time + mu.emu_start(ADDRESS | 0b1, ADDRESS + len(code)) + + # now print out some registers + regress.logger.debug(">>> After emulation") + regress.logger.debug(">>> SP = %#x", mu.reg_read(UC_ARM_REG_SP)) + regress.logger.debug(">>> PC = %#x", mu.reg_read(UC_ARM_REG_PC)) + + for i in range(UC_ARM_REG_R0, UC_ARM_REG_R12): + regress.logger.debug("\tR%d = %#x", (i-UC_ARM_REG_R0), mu.reg_read(i)) + + regress.logger.debug("\tD6 = %#x", mu.reg_read(UC_ARM_REG_D6)) + regress.logger.debug("\tD7 = %#x", mu.reg_read(UC_ARM_REG_D7)) + + addr = SCRATCH_ADDRESS + data = mu.mem_read(addr, 100) + regress.logger.debug("Memory at addr %#x: %s", addr, binascii.hexlify(data)) + + addr = SCRATCH_ADDRESS + 0x100 + data = mu.mem_read(addr, 100) + regress.logger.debug("Memory at addr %#x: %s", addr, binascii.hexlify(data)) + + self.assertEqual(mu.reg_read(UC_ARM_REG_D6), 0x0101010101010101) + self.assertEqual(mu.reg_read(UC_ARM_REG_D7), 0x0101010101010101) + if __name__ == '__main__': regress.main() diff --git a/tests/regress/write_before_map.py b/tests/regress/write_before_map.py index 7bdb1e45ce..176dff4837 100755 --- a/tests/regress/write_before_map.py +++ b/tests/regress/write_before_map.py @@ -1,12 +1,12 @@ #!/usr/bin/env python -from __future__ import print_function +import regress + from unicorn import * from unicorn.x86_const import * -import regress -X86_CODE64 = "\x90" # NOP +X86_CODE64 = b"\x90" # NOP class WriteBeforeMap(regress.RegressTest): @@ -18,7 +18,10 @@ def runTest(self): ADDRESS = 0x1000000 # write machine code to be emulated to memory - mu.mem_write(ADDRESS, X86_CODE64) + with self.assertRaises(UcError) as raisedEx: + mu.mem_write(ADDRESS, X86_CODE64) + + self.assertEqual(UC_ERR_WRITE_UNMAPPED, raisedEx.exception.errno) if __name__ == '__main__': diff --git a/tests/regress/wrong_rip_arm.py b/tests/regress/wrong_rip_arm.py index de710d8533..4c2d9ff007 100755 --- a/tests/regress/wrong_rip_arm.py +++ b/tests/regress/wrong_rip_arm.py @@ -1,38 +1,45 @@ #!/usr/bin/python +import regress + from unicorn import * from unicorn.x86_const import * from unicorn.arm_const import * -import regress -# adds r1, #0x48 -# ldrsb r7, [r7, r7] -# ldrsh r7, [r2, r1] -# ldr r0, [pc, #0x168] -# cmp r7, #0xbf -# str r7, [r5, #0x20] -# ldr r1, [r5, #0x64] -# strb r7, [r5, #0xc] -# ldr r0, [pc, #0x1a0] -binary1 = b'\x48\x31\xff\x57\x57\x5e\x5a\x48\xbf\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xef\x08\x57\x54\x5f\x6a\x3b\x58\x0f\x05' -# binary1 = b'\x48\x31\xff\x57' -#adds r1, #0x48 -#ldrsb r7, [r7, r7] +CODE = ( + b'\x48\x31' # adds r1, #0x48 + b'\xff\x57' # ldrsb r7, [r7, r7] + b'\x57\x5e' # ldrsh r7, [r2, r1] + b'\x5a\x48' # ldr r0, [pc, #0x168] + b'\xbf\x2f' # cmp r7, #0xbf + b'\x2f\x62' # str r7, [r5, #0x20] + b'\x69\x6e' # ldr r1, [r5, #0x64] + b'\x2f\x73' # strb r7, [r5, #0xc] + b'\x68\x48' # ldr r0, [pc, #0x1a0] + + b'\xc1\xef\x08\x57\x54\x5f\x6a\x3b\x58\x0f\x05' # data? +) + +BASE = 0x00000000 + class WrongRIPArm(regress.RegressTest): def runTest(self): mu = Uc(UC_ARCH_ARM, UC_MODE_THUMB) - mu.mem_map(0, 2 * 1024 * 1024) + + mu.mem_map(BASE, 2 * 1024 * 1024) # write machine code to be emulated to memory - mu.mem_write(0, binary1) + mu.mem_write(BASE, CODE) mu.reg_write(UC_ARM_REG_R13, 1 * 1024 * 1024) + # emu for maximum 1 instruction. - mu.emu_start(0, len(binary1), 0, 1) + mu.emu_start(BASE | 0b1, BASE + len(CODE), count=1) + self.assertEqual(0x48, mu.reg_read(UC_ARM_REG_R1)) - pos = mu.reg_read(UC_ARM_REG_R15) - self.assertEqual(0x2, pos) + self.assertEqual(0x2, mu.reg_read(UC_ARM_REG_R15)) + if __name__ == '__main__': regress.main() diff --git a/tests/regress/x86_64_conditional_jump.py b/tests/regress/x86_64_conditional_jump.py index 7a4010520b..d02b8eb900 100755 --- a/tests/regress/x86_64_conditional_jump.py +++ b/tests/regress/x86_64_conditional_jump.py @@ -1,21 +1,26 @@ #!/usr/bin/python + import regress -import unicorn as U + +from unicorn import * +from unicorn.x86_const import * + class WrongConditionalPath(regress.RegressTest): def test_eflags(self): - # 0: 4d 31 f6 xor r14, r14 - # 3: 45 85 f6 test r14d, r14d - # 6: 75 fe jne 0x6 - # 8: f4 hlt - CODE = 'M1\xf6E\x85\xf6u\xfe\xf4' + code = ( + b'\x4d\x31\xf6' # xor r14, r14 + b'\x45\x85\xf6' # test r14d, r14d + b'\x75\xfe' # jne 0x6 + b'\xf4' # hlt + ) - uc = U.Uc(U.UC_ARCH_X86, U.UC_MODE_64) - uc.reg_write(U.x86_const.UC_X86_REG_RIP, 0x6000b0) - uc.reg_write(U.x86_const.UC_X86_REG_EFLAGS, 0x246) + uc = Uc(UC_ARCH_X86, UC_MODE_64) + uc.reg_write(UC_X86_REG_RIP, 0x6000b0) + uc.reg_write(UC_X86_REG_EFLAGS, 0x246) uc.mem_map(0x600000, 0x1000) - uc.mem_write(0x6000b0, CODE) + uc.mem_write(0x6000b0, code) uc.emu_start(0x6000b0 + 6, 0, count=1) @@ -33,7 +38,8 @@ def test_eflags(self): # RIP=00000000006000b6 RFL=00000246 [---Z-P-] CPL=3 II=0 A20=1 SMM=0 HLT=0 # 0x00000000006000b8: hlt # RIP=00000000006000b8 RFL=00000246 [---Z-P-] CPL=3 II=0 A20=1 SMM=0 HLT=0 - self.assertEqual(0x6000b0 + 8, uc.reg_read(U.x86_const.UC_X86_REG_RIP)) + self.assertEqual(0x6000b0 + 8, uc.reg_read(UC_X86_REG_RIP)) + if __name__ == '__main__': regress.main() diff --git a/tests/regress/x86_64_eflags.py b/tests/regress/x86_64_eflags.py index ca7c48d1c4..e5072c08b8 100755 --- a/tests/regress/x86_64_eflags.py +++ b/tests/regress/x86_64_eflags.py @@ -1,15 +1,19 @@ #!/usr/bin/python + import regress -import unicorn as U + +from unicorn import * +from unicorn.x86_const import * + class WrongEFLAGS(regress.RegressTest): def test_eflags(self): # xor r14,r14 - CODE = 'M1\xf6' + CODE = b'M1\xf6' - uc = U.Uc(U.UC_ARCH_X86, U.UC_MODE_64) - uc.reg_write(U.x86_const.UC_X86_REG_RIP, 0x6000b0) - uc.reg_write(U.x86_const.UC_X86_REG_EFLAGS, 0x200) + uc = Uc(UC_ARCH_X86, UC_MODE_64) + uc.reg_write(UC_X86_REG_RIP, 0x6000b0) + uc.reg_write(UC_X86_REG_EFLAGS, 0x200) uc.mem_map(0x600000, 0x1000) uc.mem_write(0x6000b0, CODE) @@ -31,8 +35,9 @@ def test_eflags(self): # (gdb) p $eflags # $4 = [ PF ZF IF ] - self.assertEqual(0x6000b3, uc.reg_read(U.x86_const.UC_X86_REG_RIP)) - self.assertEqual(0x246, uc.reg_read(U.x86_const.UC_X86_REG_EFLAGS)) + self.assertEqual(0x6000b3, uc.reg_read(UC_X86_REG_RIP)) + self.assertEqual(0x246, uc.reg_read(UC_X86_REG_EFLAGS)) + if __name__ == '__main__': regress.main() diff --git a/tests/regress/x86_64_msr.py b/tests/regress/x86_64_msr.py index 37853c534b..dbf82a3b30 100755 --- a/tests/regress/x86_64_msr.py +++ b/tests/regress/x86_64_msr.py @@ -1,10 +1,10 @@ #!/usr/bin/env python -from unicorn import * -from unicorn.x86_const import * -from struct import pack import regress +from unicorn import * +from unicorn.x86_const import * + CODE_ADDR = 0x40000 CODE_SIZE = 0x1000 @@ -32,7 +32,7 @@ def set_msr(uc, msr, value, scratch=SCRATCH_ADDR): orip = uc.reg_read(UC_X86_REG_RIP) # x86: wrmsr - buf = '\x0f\x30' + buf = b'\x0f\x30' uc.mem_write(scratch, buf) uc.reg_write(UC_X86_REG_RAX, value & 0xFFFFFFFF) uc.reg_write(UC_X86_REG_RDX, (value >> 32) & 0xFFFFFFFF) @@ -58,7 +58,7 @@ def get_msr(uc, msr, scratch=SCRATCH_ADDR): orip = uc.reg_read(UC_X86_REG_RIP) # x86: rdmsr - buf = '\x0f\x32' + buf = b'\x0f\x32' uc.mem_write(scratch, buf) uc.reg_write(UC_X86_REG_RCX, msr & 0xFFFFFFFF) uc.emu_start(scratch, scratch+len(buf), count=1) @@ -122,9 +122,9 @@ def test_gs(self): uc.mem_map(CODE_ADDR, CODE_SIZE) uc.mem_map(SCRATCH_ADDR, SCRATCH_SIZE) - code = '6548330C2518000000'.decode('hex') # x86-64: xor rcx, qword ptr gs:[0x18] + code = b'\x65\x48\x33\x0C\x25\x18\x00\x00\x00' # xor rcx, qword ptr gs:[0x18] uc.mem_write(CODE_ADDR, code) - uc.mem_write(SEGMENT_ADDR+0x18, 'AAAAAAAA') + uc.mem_write(SEGMENT_ADDR+0x18, b'AAAAAAAA') set_gs(uc, SEGMENT_ADDR) self.assertEqual(SEGMENT_ADDR, get_gs(uc)) @@ -140,9 +140,9 @@ def test_fs(self): uc.mem_map(CODE_ADDR, CODE_SIZE) uc.mem_map(SCRATCH_ADDR, SCRATCH_SIZE) - code = '6448330C2518000000'.decode('hex') # x86-64: xor rcx, qword ptr fs:[0x18] + code = b'\x64\x48\x33\x0C\x25\x18\x00\x00\x00' # xor rcx, qword ptr fs:[0x18] uc.mem_write(CODE_ADDR, code) - uc.mem_write(SEGMENT_ADDR+0x18, 'AAAAAAAA') + uc.mem_write(SEGMENT_ADDR+0x18, b'AAAAAAAA') set_fs(uc, SEGMENT_ADDR) self.assertEqual(SEGMENT_ADDR, get_fs(uc)) @@ -151,5 +151,6 @@ def test_fs(self): self.assertEqual(uc.reg_read(UC_X86_REG_RCX), 0x4141414141414141) + if __name__ == '__main__': regress.main() diff --git a/tests/regress/x86_eflags.py b/tests/regress/x86_eflags.py index 486f6d03c7..cf9f99f815 100755 --- a/tests/regress/x86_eflags.py +++ b/tests/regress/x86_eflags.py @@ -1,16 +1,21 @@ #!/usr/bin/python + import regress -import unicorn as U + +from unicorn import * +from unicorn.x86_const import * + class WrongEFLAGS2(regress.RegressTest): def test_eflags(self): # imul eax, ebx - CODE = '\x0f\xaf\xc3' + CODE = b'\x0f\xaf\xc3' + + uc = Uc(UC_ARCH_X86, UC_MODE_32) - uc = U.Uc(U.UC_ARCH_X86, U.UC_MODE_32) - uc.reg_write(U.x86_const.UC_X86_REG_EAX, 16) - uc.reg_write(U.x86_const.UC_X86_REG_EBX, 1) - uc.reg_write(U.x86_const.UC_X86_REG_EFLAGS, 0x292) + uc.reg_write(UC_X86_REG_EAX, 16) + uc.reg_write(UC_X86_REG_EBX, 1) + uc.reg_write(UC_X86_REG_EFLAGS, 0x292) uc.mem_map(0x600000, 0x1000) uc.mem_write(0x6000b0, CODE) @@ -32,7 +37,7 @@ def test_eflags(self): # (gdb) p/x $eflags # $4 = 0x202 - self.assertEqual(0x202, uc.reg_read(U.x86_const.UC_X86_REG_EFLAGS)) + self.assertEqual(0x202, uc.reg_read(UC_X86_REG_EFLAGS)) if __name__ == '__main__': regress.main() diff --git a/tests/regress/x86_fldt_fsqrt.py b/tests/regress/x86_fldt_fsqrt.py index 036ba33234..c67deb1c4a 100755 --- a/tests/regress/x86_fldt_fsqrt.py +++ b/tests/regress/x86_fldt_fsqrt.py @@ -1,31 +1,34 @@ #!/usr/bin/env python -from unicorn import * -from unicorn.x86_const import * -from struct import pack import regress -CODE_ADDR = 0x1000000 +from unicorn import * +from unicorn.x86_const import * + CODE = ( - '\xb8\x00\x00\x00\x02' # mov eax, 0x2000000 - '\xdb\x28' # fldt [eax] - '\xd9\xfa' # fsqrt + b'\xb8\x00\x00\x00\x02' # mov eax, 0x2000000 + b'\xdb\x28' # fldt [eax] + b'\xd9\xfa' # fsqrt ) +BASE = 0x1000000 + DATA_ADDR = 0x2000000 -DATA = '\0\0\0\0\0\0\0\0\0\1' +DATA = b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01' + class FldtFsqrt(regress.RegressTest): def test_fldt_fsqrt(self): uc = Uc(UC_ARCH_X86, UC_MODE_32) - uc.mem_map(CODE_ADDR, 0x1000) - uc.mem_write(CODE_ADDR, CODE) + uc.mem_map(BASE, 0x1000) + uc.mem_write(BASE, CODE) uc.mem_map(DATA_ADDR, 0x1000) uc.mem_write(DATA_ADDR, DATA) - uc.emu_start(CODE_ADDR, CODE_ADDR + len(CODE), 10000, 10) + uc.emu_start(BASE, BASE + len(CODE), 10000, 10) + if __name__ == '__main__': regress.main() diff --git a/tests/regress/x86_gdt.py b/tests/regress/x86_gdt.py index 5f4234a46c..e85837d0fe 100755 --- a/tests/regress/x86_gdt.py +++ b/tests/regress/x86_gdt.py @@ -1,25 +1,27 @@ #!/usr/bin/env python + +import regress + from unicorn import * from unicorn.x86_const import * from struct import pack -import regress -F_GRANULARITY = 0x8 -F_PROT_32 = 0x4 -F_LONG = 0x2 -F_AVAILABLE = 0x1 +F_GRANULARITY = 0x8 +F_PROT_32 = 0x4 +F_LONG = 0x2 +F_AVAILABLE = 0x1 A_PRESENT = 0x80 A_PRIV_3 = 0x60 A_PRIV_2 = 0x40 A_PRIV_1 = 0x20 -A_PRIV_0 = 0x0 +A_PRIV_0 = 0x00 A_CODE = 0x10 A_DATA = 0x10 -A_TSS = 0x0 +A_TSS = 0x0 A_GATE = 0x0 A_DATA_WRITABLE = 0x2 @@ -34,27 +36,32 @@ S_PRIV_1 = 0x1 S_PRIV_0 = 0x0 -CODE = '65330d18000000'.decode('hex') # xor ecx, dword ptr gs:[0x18] +CODE = b'\x65\x33\x0d\x18\x00\x00\x00' # xor ecx, dword ptr gs:[0x18] def create_selector(idx, flags): to_ret = flags to_ret |= idx << 3 + return to_ret + def create_gdt_entry(base, limit, access, flags): + return pack('> 16) & 0xf) << 48 + | (flags & 0xff) << 52 + | ((base >> 24) & 0xff) << 56 + )) - to_ret = limit & 0xffff; - to_ret |= (base & 0xffffff) << 16; - to_ret |= (access & 0xff) << 40; - to_ret |= ((limit >> 16) & 0xf) << 48; - to_ret |= (flags & 0xff) << 52; - to_ret |= ((base >> 24) & 0xff) << 56; - return pack(' Date: Sun, 13 Oct 2024 13:39:21 +0800 Subject: [PATCH 19/72] Fix memory leak of g_array_free --- glib_compat/garray.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/glib_compat/garray.c b/glib_compat/garray.c index 2ffe559a00..6cfc0fa5b4 100644 --- a/glib_compat/garray.c +++ b/glib_compat/garray.c @@ -333,7 +333,7 @@ gchar *g_array_free (GArray *farray, /* if others are holding a reference, preserve the wrapper but do free/return the data */ //if (!g_atomic_ref_count_dec (&array->ref_count)) - flags |= PRESERVE_WRAPPER; + // flags |= PRESERVE_WRAPPER; return array_free (array, flags); } From fbf34af81ca959b16f4b744667742842e41be56a Mon Sep 17 00:00:00 2001 From: mio Date: Sun, 13 Oct 2024 15:19:05 +0800 Subject: [PATCH 20/72] Fix off-by-one bug and add a unit test --- tests/unit/test_ctl.c | 3 +++ uc.c | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/unit/test_ctl.c b/tests/unit/test_ctl.c index cdc57aa4bb..5949882360 100644 --- a/tests/unit/test_ctl.c +++ b/tests/unit/test_ctl.c @@ -181,11 +181,14 @@ static void test_uc_ctl_change_page_size(void) { uc_engine *uc; uc_engine *uc2; + uint32_t pg = 0; OK(uc_open(UC_ARCH_ARM, UC_MODE_ARM, &uc)); OK(uc_open(UC_ARCH_ARM, UC_MODE_ARM, &uc2)); OK(uc_ctl_set_page_size(uc, 4096)); + OK(uc_ctl_get_page_size(uc, &pg)); + TEST_CHECK(pg == 4096); OK(uc_mem_map(uc2, 1 << 10, 1 << 10, UC_PROT_ALL)); uc_assert_err(UC_ERR_ARG, uc_mem_map(uc, 1 << 10, 1 << 10, UC_PROT_ALL)); diff --git a/uc.c b/uc.c index bc8019ea4b..dfddbec7e8 100644 --- a/uc.c +++ b/uc.c @@ -2523,7 +2523,8 @@ uc_err uc_ctl(uc_engine *uc, uc_control_type control, ...) break; } - while (page_size) { + // Bits is used to calculate the mask + while (page_size > 1) { bits++; page_size >>= 1; } From 7e328647742f1a93c670e5b2ba6d8012dda2c1b7 Mon Sep 17 00:00:00 2001 From: mio Date: Sun, 13 Oct 2024 16:34:25 +0800 Subject: [PATCH 21/72] Also include cmake directory in sdist --- bindings/python/setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bindings/python/setup.py b/bindings/python/setup.py index f9a737e638..85f9036481 100755 --- a/bindings/python/setup.py +++ b/bindings/python/setup.py @@ -65,7 +65,8 @@ def copy_sources(): shutil.copytree(os.path.join(ROOT_DIR, '../../tests'), os.path.join(SRC_DIR, 'tests/')) shutil.copytree(os.path.join(ROOT_DIR, '../../samples'), os.path.join(SRC_DIR, 'samples/')) shutil.copytree(os.path.join(ROOT_DIR, '../../glib_compat'), os.path.join(SRC_DIR, 'glib_compat/')) - + shutil.copytree(os.path.join(ROOT_DIR, '../../cmake'), os.path.join(SRC_DIR, 'cmake/')) + try: # remove site-specific configuration file # might not exist @@ -75,7 +76,6 @@ def copy_sources(): src.extend(glob.glob(os.path.join(ROOT_DIR, "../../*.[ch]"))) src.extend(glob.glob(os.path.join(ROOT_DIR, "../../*.mk"))) - src.extend(glob.glob(os.path.join(ROOT_DIR, "../../cmake/*.cmake"))) src.extend(glob.glob(os.path.join(ROOT_DIR, "../../LICENSE*"))) src.extend(glob.glob(os.path.join(ROOT_DIR, "../../README.md"))) From fea3411803e21210b4d4f06e085512b7d8bf0734 Mon Sep 17 00:00:00 2001 From: Eli Date: Sun, 13 Oct 2024 11:35:42 +0300 Subject: [PATCH 22/72] Minor Python regress fixes (#2030) * Fix erronous method name * Uncomment known failures * Opportunistic improvements --- tests/regress/arm64_reg_rw_w0_w30.py | 35 ++++++++++++++-------- tests/regress/core_ctl.py | 16 +++++----- tests/regress/high_mem.py | 4 +-- tests/regress/hook_readonly_write_local.py | 1 - tests/regress/memmap_segfault.py | 2 -- tests/regress/sparc_reg.py | 2 +- 6 files changed, 33 insertions(+), 27 deletions(-) diff --git a/tests/regress/arm64_reg_rw_w0_w30.py b/tests/regress/arm64_reg_rw_w0_w30.py index fc34ea4cde..42b2f14961 100644 --- a/tests/regress/arm64_reg_rw_w0_w30.py +++ b/tests/regress/arm64_reg_rw_w0_w30.py @@ -1,10 +1,10 @@ #!/usr/bin/python +import regress + from unicorn import * from unicorn.arm64_const import * -from unicorn.x86_const import * -import regress class Arm64RegReadWriteW0ThroughW30(regress.RegressTest): """ @@ -14,17 +14,26 @@ class Arm64RegReadWriteW0ThroughW30(regress.RegressTest): def runTest(self): uc = Uc(UC_ARCH_ARM64, UC_MODE_ARM) - - uc.reg_write(UC_ARM64_REG_X0, 0x1234567890abcdef) - self.assertEquals(uc.reg_read(UC_ARM64_REG_X0), 0x1234567890abcdef) - self.assertEquals(uc.reg_read(UC_ARM64_REG_W0), 0x90abcdef) - - uc.reg_write(UC_ARM64_REG_X30, 0xa1b2c3d4e5f6a7b8) - self.assertEquals(uc.reg_read(UC_ARM64_REG_W30), 0xe5f6a7b8) - - uc.reg_write(UC_ARM64_REG_W30, 0xaabbccdd) - self.assertEquals(uc.reg_read(UC_ARM64_REG_X30), 0xa1b2c3d4aabbccdd) - self.assertEquals(uc.reg_read(UC_ARM64_REG_W30), 0xaabbccdd) + + expected = 0x1234567890abcdef + + uc.reg_write(UC_ARM64_REG_X0, expected) + self.assertEqual(uc.reg_read(UC_ARM64_REG_X0), expected) + self.assertEqual(uc.reg_read(UC_ARM64_REG_W0), expected & 0xffffffff) + + # ---------------------------------------------------------- + + expected = 0xa1b2c3d4e5f6a7b8 + + uc.reg_write(UC_ARM64_REG_X30, expected) + self.assertEqual(uc.reg_read(UC_ARM64_REG_W30), expected & 0xffffffff) + + expected_lo = 0xaabbccdd + + uc.reg_write(UC_ARM64_REG_W30, expected_lo) + self.assertEqual(uc.reg_read(UC_ARM64_REG_X30), (expected & ~0xffffffff) | expected_lo) + self.assertEqual(uc.reg_read(UC_ARM64_REG_W30), expected_lo) + if __name__ == '__main__': regress.main() diff --git a/tests/regress/core_ctl.py b/tests/regress/core_ctl.py index 9cf1e1c2c3..730c1e8a95 100644 --- a/tests/regress/core_ctl.py +++ b/tests/regress/core_ctl.py @@ -53,8 +53,8 @@ def test_page_size(self): # set page size to 2 MiB; this should work uc.ctl_set_page_size(SIZE_2MB) - # BUG! was it set properly? - # self.assertEqual(SIZE_2MB, uc.ctl_get_page_size()) + # was it set properly? + self.assertEqual(SIZE_2MB, uc.ctl_get_page_size()) # set a page size which is not a power of 2 with self.assertRaises(UcError) as ex: @@ -62,8 +62,8 @@ def test_page_size(self): self.assertEqual(UC_ERR_ARG, ex.exception.errno) - # BUG! are we still with the valid value? - # self.assertEqual(SIZE_2MB, uc.ctl_get_page_size()) + # are we still with the valid value? + self.assertEqual(SIZE_2MB, uc.ctl_get_page_size()) # force uc to complete its initialization by triggering a random api uc.ctl_flush_tb() @@ -74,8 +74,8 @@ def test_page_size(self): self.assertEqual(UC_ERR_ARG, ex.exception.errno) - # BUG! are we still with the valid value? - # self.assertEqual(SIZE_2MB, uc.ctl_get_page_size()) + # are we still with the valid value? + self.assertEqual(SIZE_2MB, uc.ctl_get_page_size()) def test_timeout(self): MILLIS_1S = 1000 @@ -156,7 +156,7 @@ def test_tlb_mode(self): uc.mem_write(MAPPING_HI, NOPSLED) # this should prevents us from mapping to high addresses - uc.ctl_tlb_mode(UC_TLB_CPU) + uc.ctl_set_tlb_mode(UC_TLB_CPU) # this should fail with self.assertRaises(UcError) as ex: @@ -167,7 +167,7 @@ def test_tlb_mode(self): # ------------------------------------------------------ # this should allow us mapping to high addresses - uc.ctl_tlb_mode(UC_TLB_VIRTUAL) + uc.ctl_set_tlb_mode(UC_TLB_VIRTUAL) # this should ok now uc.emu_start(MAPPING_HI, MAPPING_HI + len(NOPSLED)) diff --git a/tests/regress/high_mem.py b/tests/regress/high_mem.py index 7c46c71df1..07a94200de 100644 --- a/tests/regress/high_mem.py +++ b/tests/regress/high_mem.py @@ -30,7 +30,7 @@ def test_virt_high_mapping(self): base = 0x0010000000000000 - self.uc.ctl_tlb_mode(UC_TLB_VIRTUAL) + self.uc.ctl_set_tlb_mode(UC_TLB_VIRTUAL) for i in range(12): code = base << i @@ -49,7 +49,7 @@ def test_cpu_high_mapping(self): base = 0x0010000000000000 - self.uc.ctl_tlb_mode(UC_TLB_CPU) + self.uc.ctl_set_tlb_mode(UC_TLB_CPU) for i in range(12): code = base << i diff --git a/tests/regress/hook_readonly_write_local.py b/tests/regress/hook_readonly_write_local.py index 60e6de456b..af1ed90d85 100755 --- a/tests/regress/hook_readonly_write_local.py +++ b/tests/regress/hook_readonly_write_local.py @@ -23,7 +23,6 @@ def hook_mem_read(uc, access, address, size, value, data): class REP(regress.RegressTest): - @regress.unittest.skip('writing to a UC_PROT_READ area will segfault Unicorn') def runTest(self): mu = Uc(UC_ARCH_X86, UC_MODE_32) diff --git a/tests/regress/memmap_segfault.py b/tests/regress/memmap_segfault.py index 5d217e62fe..c2e84933ce 100755 --- a/tests/regress/memmap_segfault.py +++ b/tests/regress/memmap_segfault.py @@ -31,8 +31,6 @@ def runTest(self): uc.mem_map(0x2000, 0x4000) uc.mem_write(0x1000, b' ' * 0x1004) - self.assertTrue(True, 'If not reached, then we have BUG (crash on x86_64 Linux).') - if __name__ == '__main__': regress.main() diff --git a/tests/regress/sparc_reg.py b/tests/regress/sparc_reg.py index abf4313351..12a28e0901 100755 --- a/tests/regress/sparc_reg.py +++ b/tests/regress/sparc_reg.py @@ -100,7 +100,7 @@ def runTest(self): self.assertEqual(1, uc.reg_read(UC_SPARC_REG_I7)) # BUG: PC seems to get reset to 4 when done executing - # self.assertEqual(4 * 32, uc.reg_read(UC_SPARC_REG_PC)) # make sure we executed all instructions + self.assertEqual(4 * 32, uc.reg_read(UC_SPARC_REG_PC)) # make sure we executed all instructions self.assertEqual(101, uc.reg_read(UC_SPARC_REG_SP)) self.assertEqual(201, uc.reg_read(UC_SPARC_REG_FP)) From e8ca3cbea54b215a5ff0e71442d1bcdb0d4a6e87 Mon Sep 17 00:00:00 2001 From: PhilippTakacs <76390863+PhilippTakacs@users.noreply.github.com> Date: Wed, 16 Oct 2024 15:51:13 +0200 Subject: [PATCH 23/72] Optimize memory handling (#1963) * optimize ram block handling Save the last element of the ram_list. This allows to faster find where to add new elements when they are not bigger then page size. * save ram_list freed this keeps the optimization for find_ram_offset() intact after snapshot restore. * cow only clear the tlb of affected pages * update flatview when possible Building each flatview new when the memory has changed is quite expensive when many MemoryRegions are used. This is an issue when using snapshots. * update benchmark for new api * save flatview in context this avoids rebuilding the flatview when restore a context. * init context flatview with zero * address_space_dispatch_clear remove subpage with higher priority * docutemnt the options for UC_CTL_CONTEXT_MODE Specialy stress that with UC_CTL_CONTEXT_MEMORY it is not possible to use the context with a different unicorn object. --- include/qemu.h | 1 + include/uc_priv.h | 6 ++ include/unicorn/unicorn.h | 10 +++ qemu/aarch64.h | 2 + qemu/arm.h | 2 + qemu/exec.c | 40 +++++++-- qemu/include/exec/memory-internal.h | 1 + qemu/include/exec/memory.h | 1 + qemu/m68k.h | 2 + qemu/mips.h | 2 + qemu/mips64.h | 2 + qemu/mips64el.h | 2 + qemu/mipsel.h | 2 + qemu/ppc.h | 2 + qemu/ppc64.h | 2 + qemu/riscv32.h | 2 + qemu/riscv64.h | 2 + qemu/s390x.h | 2 + qemu/softmmu/memory.c | 123 ++++++++++++++++++++++++---- qemu/sparc.h | 2 + qemu/sparc64.h | 2 + qemu/tricore.h | 2 + qemu/unicorn_common.h | 1 + qemu/x86_64.h | 2 + symbols.sh | 2 + tests/benchmarks/cow/benchmark.c | 41 +++++----- uc.c | 23 ++++++ 27 files changed, 236 insertions(+), 45 deletions(-) diff --git a/include/qemu.h b/include/qemu.h index 8705a1a6db..b6fedfacfb 100644 --- a/include/qemu.h +++ b/include/qemu.h @@ -43,6 +43,7 @@ typedef struct { typedef struct RAMList { bool freed; RAMBlock *mru_block; + RAMBlock *last_block; QLIST_HEAD(, RAMBlock) blocks; } RAMList; diff --git a/include/uc_priv.h b/include/uc_priv.h index fed75600e0..f9195b4d63 100644 --- a/include/uc_priv.h +++ b/include/uc_priv.h @@ -108,6 +108,8 @@ typedef MemoryRegion *(*uc_memory_mapping_t)(struct uc_struct *, hwaddr addr); typedef void (*uc_memory_filter_t)(MemoryRegion *, int32_t); +typedef bool (*uc_flatview_copy_t)(struct uc_struct *, FlatView *, FlatView *, bool); + typedef void (*uc_readonly_mem_t)(MemoryRegion *mr, bool readonly); typedef int (*uc_cpus_init)(struct uc_struct *, const char *); @@ -288,6 +290,7 @@ struct uc_struct { uc_args_uc_ram_size_ptr_t memory_map_ptr; uc_memory_mapping_t memory_mapping; uc_memory_filter_t memory_filter_subregions; + uc_flatview_copy_t flatview_copy; uc_mem_unmap_t memory_unmap; uc_mem_unmap_t memory_moveout; uc_mem_unmap_t memory_movein; @@ -427,6 +430,9 @@ struct uc_context { uc_mode mode; // the mode of this context uc_arch arch; // the arch of this context int snapshot_level; // the memory snapshot level to restore + bool ramblock_freed; // wheter there was a some ramblock freed + RAMBlock *last_block;// The last element of the ramblock list + FlatView *fv; // The current flatview of the memory char data[0]; // context }; diff --git a/include/unicorn/unicorn.h b/include/unicorn/unicorn.h index 791cfa6df9..c057dcc964 100644 --- a/include/unicorn/unicorn.h +++ b/include/unicorn/unicorn.h @@ -1018,6 +1018,16 @@ struct uc_tlb_entry { uc_prot perms; }; +/* + Variables to control which state should be stored in the context. + Defaults to UC_CTL_CONTEXT_CPU. The options are used in a bitfield + so to enable more then one content the binary or of the required + contents can be use. + The UC_CTL_CONTEXT_MEMORY stores some pointers to internal allocated + memory. Therefor it's not possible to use this context with another + unicorn object. +*/ + typedef enum uc_context_content { UC_CTL_CONTEXT_CPU = 1, UC_CTL_CONTEXT_MEMORY = 2, diff --git a/qemu/aarch64.h b/qemu/aarch64.h index fd70593c56..642bf3f7d5 100644 --- a/qemu/aarch64.h +++ b/qemu/aarch64.h @@ -54,6 +54,7 @@ #define vm_start vm_start_aarch64 #define address_space_dispatch_compact address_space_dispatch_compact_aarch64 #define flatview_translate flatview_translate_aarch64 +#define flatview_copy flatview_copy_aarch64 #define address_space_translate_for_iotlb address_space_translate_for_iotlb_aarch64 #define qemu_get_cpu qemu_get_cpu_aarch64 #define cpu_address_space_init cpu_address_space_init_aarch64 @@ -90,6 +91,7 @@ #define iotlb_to_section iotlb_to_section_aarch64 #define address_space_dispatch_new address_space_dispatch_new_aarch64 #define address_space_dispatch_free address_space_dispatch_free_aarch64 +#define address_space_dispatch_clear address_space_dispatch_clear_aarch64 #define flatview_read_continue flatview_read_continue_aarch64 #define address_space_read_full address_space_read_full_aarch64 #define address_space_write address_space_write_aarch64 diff --git a/qemu/arm.h b/qemu/arm.h index 894eba5a64..1199603c83 100644 --- a/qemu/arm.h +++ b/qemu/arm.h @@ -54,6 +54,7 @@ #define vm_start vm_start_arm #define address_space_dispatch_compact address_space_dispatch_compact_arm #define flatview_translate flatview_translate_arm +#define flatview_copy flatview_copy_arm #define address_space_translate_for_iotlb address_space_translate_for_iotlb_arm #define qemu_get_cpu qemu_get_cpu_arm #define cpu_address_space_init cpu_address_space_init_arm @@ -90,6 +91,7 @@ #define iotlb_to_section iotlb_to_section_arm #define address_space_dispatch_new address_space_dispatch_new_arm #define address_space_dispatch_free address_space_dispatch_free_arm +#define address_space_dispatch_clear address_space_dispatch_clear_arm #define flatview_read_continue flatview_read_continue_arm #define address_space_read_full address_space_read_full_arm #define address_space_write address_space_write_arm diff --git a/qemu/exec.c b/qemu/exec.c index af77e5de49..9786b19557 100644 --- a/qemu/exec.c +++ b/qemu/exec.c @@ -957,12 +957,10 @@ void flatview_add_to_dispatch(struct uc_struct *uc, FlatView *fv, MemoryRegionSe static ram_addr_t find_ram_offset_last(struct uc_struct *uc, ram_addr_t size) { - RAMBlock *block; ram_addr_t result = 0; + RAMBlock *block = uc->ram_list.last_block; - RAMBLOCK_FOREACH(block) { - result = MAX(block->offset + block->max_length, result); - } + result = block->offset + block->max_length; if (result + size > RAM_ADDR_MAX) { abort(); @@ -1076,18 +1074,26 @@ static void ram_block_add(struct uc_struct *uc, RAMBlock *new_block) * QLIST (which has an RCU-friendly variant) does not have insertion at * tail, so save the last element in last_block. */ - RAMBLOCK_FOREACH(block) { - last_block = block; - if (block->max_length < new_block->max_length) { - break; + if (uc->ram_list.freed || new_block->max_length > uc->target_page_size) { + RAMBLOCK_FOREACH(block) { + last_block = block; + if (block->max_length < new_block->max_length) { + break; + } } + } else { + last_block = uc->ram_list.last_block; + block = NULL; } + if (block) { QLIST_INSERT_BEFORE_RCU(block, new_block, next); } else if (last_block) { QLIST_INSERT_AFTER_RCU(last_block, new_block, next); + uc->ram_list.last_block = new_block; } else { /* list is empty */ QLIST_INSERT_HEAD_RCU(&uc->ram_list.blocks, new_block, next); + uc->ram_list.last_block = new_block; } uc->ram_list.mru_block = NULL; @@ -1165,6 +1171,7 @@ void qemu_ram_free(struct uc_struct *uc, RAMBlock *block) QLIST_REMOVE_RCU(block, next); uc->ram_list.mru_block = NULL; uc->ram_list.freed = true; + uc->ram_list.last_block = NULL; /* Write list before version */ //smp_wmb(); // call_rcu(block, reclaim_ramblock, rcu); @@ -1388,6 +1395,7 @@ static subpage_t *subpage_init(struct uc_struct *uc, FlatView *fv, hwaddr base) memory_region_init_io(fv->root->uc, &mmio->iomem, &subpage_ops, mmio, TARGET_PAGE_SIZE); mmio->iomem.subpage = true; + mmio->iomem.priority = uc->snapshot_level; #if defined(DEBUG_SUBPAGE) printf("%s: %p base " TARGET_FMT_plx " len %08x\n", __func__, mmio, base, TARGET_PAGE_SIZE); @@ -1448,6 +1456,22 @@ AddressSpaceDispatch *address_space_dispatch_new(struct uc_struct *uc, FlatView return d; } +void address_space_dispatch_clear(AddressSpaceDispatch *d) +{ + MemoryRegionSection *section; + struct uc_struct *uc = d->uc; + while (d->map.sections_nb > 0) { + d->map.sections_nb--; + section = &d->map.sections[d->map.sections_nb]; + if (section->mr->priority > uc->snapshot_level) { + phys_section_destroy(section->mr); + } + } + g_free(d->map.sections); + g_free(d->map.nodes); + g_free(d); +} + void address_space_dispatch_free(AddressSpaceDispatch *d) { phys_sections_free(&d->map); diff --git a/qemu/include/exec/memory-internal.h b/qemu/include/exec/memory-internal.h index d8f05c1bd7..3f4d666db4 100644 --- a/qemu/include/exec/memory-internal.h +++ b/qemu/include/exec/memory-internal.h @@ -45,6 +45,7 @@ void flatview_add_to_dispatch(struct uc_struct *uc, FlatView *fv, MemoryRegionSe AddressSpaceDispatch *address_space_dispatch_new(struct uc_struct *uc, FlatView *fv); void address_space_dispatch_compact(AddressSpaceDispatch *d); void address_space_dispatch_free(AddressSpaceDispatch *d); +void address_space_dispatch_clear(AddressSpaceDispatch *d); void mtree_print_dispatch(struct AddressSpaceDispatch *d, MemoryRegion *root); diff --git a/qemu/include/exec/memory.h b/qemu/include/exec/memory.h index 8a8d13c38c..eafc1a5de1 100644 --- a/qemu/include/exec/memory.h +++ b/qemu/include/exec/memory.h @@ -1220,5 +1220,6 @@ void memory_unmap(struct uc_struct *uc, MemoryRegion *mr); void memory_moveout(struct uc_struct *uc, MemoryRegion *mr); void memory_movein(struct uc_struct *uc, MemoryRegion *mr); int memory_free(struct uc_struct *uc); +bool flatview_copy(struct uc_struct *uc, FlatView *dst, FlatView *src, bool update_dispatcher); #endif diff --git a/qemu/m68k.h b/qemu/m68k.h index 9cdc982a59..2a1ffd67e4 100644 --- a/qemu/m68k.h +++ b/qemu/m68k.h @@ -54,6 +54,7 @@ #define vm_start vm_start_m68k #define address_space_dispatch_compact address_space_dispatch_compact_m68k #define flatview_translate flatview_translate_m68k +#define flatview_copy flatview_copy_m68k #define address_space_translate_for_iotlb address_space_translate_for_iotlb_m68k #define qemu_get_cpu qemu_get_cpu_m68k #define cpu_address_space_init cpu_address_space_init_m68k @@ -90,6 +91,7 @@ #define iotlb_to_section iotlb_to_section_m68k #define address_space_dispatch_new address_space_dispatch_new_m68k #define address_space_dispatch_free address_space_dispatch_free_m68k +#define address_space_dispatch_clear address_space_dispatch_clear_m68k #define flatview_read_continue flatview_read_continue_m68k #define address_space_read_full address_space_read_full_m68k #define address_space_write address_space_write_m68k diff --git a/qemu/mips.h b/qemu/mips.h index 1ef392f332..30cd7ef54b 100644 --- a/qemu/mips.h +++ b/qemu/mips.h @@ -54,6 +54,7 @@ #define vm_start vm_start_mips #define address_space_dispatch_compact address_space_dispatch_compact_mips #define flatview_translate flatview_translate_mips +#define flatview_copy flatview_copy_mips #define address_space_translate_for_iotlb address_space_translate_for_iotlb_mips #define qemu_get_cpu qemu_get_cpu_mips #define cpu_address_space_init cpu_address_space_init_mips @@ -90,6 +91,7 @@ #define iotlb_to_section iotlb_to_section_mips #define address_space_dispatch_new address_space_dispatch_new_mips #define address_space_dispatch_free address_space_dispatch_free_mips +#define address_space_dispatch_clear address_space_dispatch_clear_mips #define flatview_read_continue flatview_read_continue_mips #define address_space_read_full address_space_read_full_mips #define address_space_write address_space_write_mips diff --git a/qemu/mips64.h b/qemu/mips64.h index 6b6c6c3feb..ad9033e8d0 100644 --- a/qemu/mips64.h +++ b/qemu/mips64.h @@ -54,6 +54,7 @@ #define vm_start vm_start_mips64 #define address_space_dispatch_compact address_space_dispatch_compact_mips64 #define flatview_translate flatview_translate_mips64 +#define flatview_copy flatview_copy_mips64 #define address_space_translate_for_iotlb address_space_translate_for_iotlb_mips64 #define qemu_get_cpu qemu_get_cpu_mips64 #define cpu_address_space_init cpu_address_space_init_mips64 @@ -90,6 +91,7 @@ #define iotlb_to_section iotlb_to_section_mips64 #define address_space_dispatch_new address_space_dispatch_new_mips64 #define address_space_dispatch_free address_space_dispatch_free_mips64 +#define address_space_dispatch_clear address_space_dispatch_clear_mips64 #define flatview_read_continue flatview_read_continue_mips64 #define address_space_read_full address_space_read_full_mips64 #define address_space_write address_space_write_mips64 diff --git a/qemu/mips64el.h b/qemu/mips64el.h index 583d2e55fe..ad65fc39ad 100644 --- a/qemu/mips64el.h +++ b/qemu/mips64el.h @@ -54,6 +54,7 @@ #define vm_start vm_start_mips64el #define address_space_dispatch_compact address_space_dispatch_compact_mips64el #define flatview_translate flatview_translate_mips64el +#define flatview_copy flatview_copy_mips64el #define address_space_translate_for_iotlb address_space_translate_for_iotlb_mips64el #define qemu_get_cpu qemu_get_cpu_mips64el #define cpu_address_space_init cpu_address_space_init_mips64el @@ -90,6 +91,7 @@ #define iotlb_to_section iotlb_to_section_mips64el #define address_space_dispatch_new address_space_dispatch_new_mips64el #define address_space_dispatch_free address_space_dispatch_free_mips64el +#define address_space_dispatch_clear address_space_dispatch_clear_mips64el #define flatview_read_continue flatview_read_continue_mips64el #define address_space_read_full address_space_read_full_mips64el #define address_space_write address_space_write_mips64el diff --git a/qemu/mipsel.h b/qemu/mipsel.h index 388b46ce21..0ca05035d7 100644 --- a/qemu/mipsel.h +++ b/qemu/mipsel.h @@ -54,6 +54,7 @@ #define vm_start vm_start_mipsel #define address_space_dispatch_compact address_space_dispatch_compact_mipsel #define flatview_translate flatview_translate_mipsel +#define flatview_copy flatview_copy_mipsel #define address_space_translate_for_iotlb address_space_translate_for_iotlb_mipsel #define qemu_get_cpu qemu_get_cpu_mipsel #define cpu_address_space_init cpu_address_space_init_mipsel @@ -90,6 +91,7 @@ #define iotlb_to_section iotlb_to_section_mipsel #define address_space_dispatch_new address_space_dispatch_new_mipsel #define address_space_dispatch_free address_space_dispatch_free_mipsel +#define address_space_dispatch_clear address_space_dispatch_clear_mipsel #define flatview_read_continue flatview_read_continue_mipsel #define address_space_read_full address_space_read_full_mipsel #define address_space_write address_space_write_mipsel diff --git a/qemu/ppc.h b/qemu/ppc.h index 8979bc95e3..b1ac72fd10 100644 --- a/qemu/ppc.h +++ b/qemu/ppc.h @@ -54,6 +54,7 @@ #define vm_start vm_start_ppc #define address_space_dispatch_compact address_space_dispatch_compact_ppc #define flatview_translate flatview_translate_ppc +#define flatview_copy flatview_copy_ppc #define address_space_translate_for_iotlb address_space_translate_for_iotlb_ppc #define qemu_get_cpu qemu_get_cpu_ppc #define cpu_address_space_init cpu_address_space_init_ppc @@ -90,6 +91,7 @@ #define iotlb_to_section iotlb_to_section_ppc #define address_space_dispatch_new address_space_dispatch_new_ppc #define address_space_dispatch_free address_space_dispatch_free_ppc +#define address_space_dispatch_clear address_space_dispatch_clear_ppc #define flatview_read_continue flatview_read_continue_ppc #define address_space_read_full address_space_read_full_ppc #define address_space_write address_space_write_ppc diff --git a/qemu/ppc64.h b/qemu/ppc64.h index 0780f09c97..43c1570d41 100644 --- a/qemu/ppc64.h +++ b/qemu/ppc64.h @@ -54,6 +54,7 @@ #define vm_start vm_start_ppc64 #define address_space_dispatch_compact address_space_dispatch_compact_ppc64 #define flatview_translate flatview_translate_ppc64 +#define flatview_copy flatview_copy_ppc64 #define address_space_translate_for_iotlb address_space_translate_for_iotlb_ppc64 #define qemu_get_cpu qemu_get_cpu_ppc64 #define cpu_address_space_init cpu_address_space_init_ppc64 @@ -90,6 +91,7 @@ #define iotlb_to_section iotlb_to_section_ppc64 #define address_space_dispatch_new address_space_dispatch_new_ppc64 #define address_space_dispatch_free address_space_dispatch_free_ppc64 +#define address_space_dispatch_clear address_space_dispatch_clear_ppc64 #define flatview_read_continue flatview_read_continue_ppc64 #define address_space_read_full address_space_read_full_ppc64 #define address_space_write address_space_write_ppc64 diff --git a/qemu/riscv32.h b/qemu/riscv32.h index ab5886bce8..dde114c536 100644 --- a/qemu/riscv32.h +++ b/qemu/riscv32.h @@ -54,6 +54,7 @@ #define vm_start vm_start_riscv32 #define address_space_dispatch_compact address_space_dispatch_compact_riscv32 #define flatview_translate flatview_translate_riscv32 +#define flatview_copy flatview_copy_riscv32 #define address_space_translate_for_iotlb address_space_translate_for_iotlb_riscv32 #define qemu_get_cpu qemu_get_cpu_riscv32 #define cpu_address_space_init cpu_address_space_init_riscv32 @@ -90,6 +91,7 @@ #define iotlb_to_section iotlb_to_section_riscv32 #define address_space_dispatch_new address_space_dispatch_new_riscv32 #define address_space_dispatch_free address_space_dispatch_free_riscv32 +#define address_space_dispatch_clear address_space_dispatch_clear_riscv32 #define flatview_read_continue flatview_read_continue_riscv32 #define address_space_read_full address_space_read_full_riscv32 #define address_space_write address_space_write_riscv32 diff --git a/qemu/riscv64.h b/qemu/riscv64.h index 59bc60dbc8..32e0894da4 100644 --- a/qemu/riscv64.h +++ b/qemu/riscv64.h @@ -54,6 +54,7 @@ #define vm_start vm_start_riscv64 #define address_space_dispatch_compact address_space_dispatch_compact_riscv64 #define flatview_translate flatview_translate_riscv64 +#define flatview_copy flatview_copy_riscv64 #define address_space_translate_for_iotlb address_space_translate_for_iotlb_riscv64 #define qemu_get_cpu qemu_get_cpu_riscv64 #define cpu_address_space_init cpu_address_space_init_riscv64 @@ -90,6 +91,7 @@ #define iotlb_to_section iotlb_to_section_riscv64 #define address_space_dispatch_new address_space_dispatch_new_riscv64 #define address_space_dispatch_free address_space_dispatch_free_riscv64 +#define address_space_dispatch_clear address_space_dispatch_clear_riscv64 #define flatview_read_continue flatview_read_continue_riscv64 #define address_space_read_full address_space_read_full_riscv64 #define address_space_write address_space_write_riscv64 diff --git a/qemu/s390x.h b/qemu/s390x.h index e8fefc1078..53823df69a 100644 --- a/qemu/s390x.h +++ b/qemu/s390x.h @@ -54,6 +54,7 @@ #define vm_start vm_start_s390x #define address_space_dispatch_compact address_space_dispatch_compact_s390x #define flatview_translate flatview_translate_s390x +#define flatview_copy flatview_copy_s390x #define address_space_translate_for_iotlb address_space_translate_for_iotlb_s390x #define qemu_get_cpu qemu_get_cpu_s390x #define cpu_address_space_init cpu_address_space_init_s390x @@ -90,6 +91,7 @@ #define iotlb_to_section iotlb_to_section_s390x #define address_space_dispatch_new address_space_dispatch_new_s390x #define address_space_dispatch_free address_space_dispatch_free_s390x +#define address_space_dispatch_clear address_space_dispatch_clear_s390x #define flatview_read_continue flatview_read_continue_s390x #define address_space_read_full address_space_read_full_s390x #define address_space_write address_space_write_s390x diff --git a/qemu/softmmu/memory.c b/qemu/softmmu/memory.c index ee72511ed8..2bf6d0d39d 100644 --- a/qemu/softmmu/memory.c +++ b/qemu/softmmu/memory.c @@ -27,7 +27,7 @@ //#define DEBUG_UNASSIGNED void memory_region_transaction_begin(void); -void memory_region_transaction_commit(MemoryRegion *mr); +static void memory_region_transaction_commit(MemoryRegion *mr); typedef struct AddrRange AddrRange; @@ -94,6 +94,7 @@ static void make_contained(struct uc_struct *uc, MemoryRegion *current) MemoryRegion *memory_cow(struct uc_struct *uc, MemoryRegion *current, hwaddr begin, size_t size) { + hwaddr addr; hwaddr offset; hwaddr current_offset; MemoryRegion *ram = g_new(MemoryRegion, 1); @@ -112,18 +113,16 @@ MemoryRegion *memory_cow(struct uc_struct *uc, MemoryRegion *current, hwaddr beg g_free(ram); return NULL; } - memory_region_transaction_begin(); memcpy(ramblock_ptr(ram->ram_block, 0), ramblock_ptr(current->ram_block, current_offset), size); memory_region_add_subregion_overlap(current->container, offset, ram, uc->snapshot_level); if (uc->cpu) { - tlb_flush(uc->cpu); + for (addr = ram->addr; (int64_t)(ram->end - addr) > 0; addr += uc->target_page_size) { + tlb_flush_page(uc->cpu, addr); + } } - uc->memory_region_update_pending = true; - memory_region_transaction_commit(ram); - return ram; } @@ -196,19 +195,33 @@ MemoryRegion *memory_map_io(struct uc_struct *uc, ram_addr_t begin, size_t size, return mmio; } +static void memory_region_remove_subregion(MemoryRegion *mr, + MemoryRegion *subregion) +{ + assert(subregion->container == mr); + subregion->container = NULL; + QTAILQ_REMOVE(&mr->subregions, subregion, subregions_link); +} + void memory_region_filter_subregions(MemoryRegion *mr, int32_t level) { MemoryRegion *subregion, *subregion_next; - memory_region_transaction_begin(); + /* + * memory transaction/commit are only to rebuild the flatview. At + * this point there is need to rebuild the flatview, because this + * function is either called as part of a destructor or as part of + * a context restore. In the destructor case the caller remove the + * complete memory region and should do a transaction/commit. In + * the context restore case the flatview is taken from the context so + * no need to rebuild it. + */ QTAILQ_FOREACH_SAFE(subregion, &mr->subregions, subregions_link, subregion_next) { if (subregion->priority >= level) { - memory_region_del_subregion(mr, subregion); + memory_region_remove_subregion(mr, subregion); subregion->destructor(subregion); g_free(subregion); - mr->uc->memory_region_update_pending = true; } } - memory_region_transaction_commit(mr); } static void memory_region_remove_mapped_block(struct uc_struct *uc, MemoryRegion *mr, bool free) @@ -909,6 +922,77 @@ static void flatviews_init(struct uc_struct *uc) } } +bool flatview_copy(struct uc_struct *uc, FlatView *dst, FlatView *src, bool update_dispatcher) +{ + if (!dst->ranges || !dst->nr_allocated || dst->nr_allocated < src->nr) { + if (dst->ranges && dst->nr_allocated) { + free(dst->ranges); + } + dst->ranges = calloc(src->nr_allocated, sizeof(*dst->ranges)); + if (!dst->ranges) { + return false; + } + dst->nr_allocated = src->nr_allocated; + } + memcpy(dst->ranges, src->ranges, src->nr*sizeof(*dst->ranges)); + dst->nr = src->nr; + if (!update_dispatcher) { + return true; + } + MEMORY_LISTENER_CALL_GLOBAL(uc, begin, Forward); + if (dst->dispatch) { + address_space_dispatch_clear(dst->dispatch); + } + dst->dispatch = address_space_dispatch_new(uc, dst); + for (size_t j = 0; j < dst->nr; j++) { + MemoryRegionSection mrs = + section_from_flat_range(&dst->ranges[j], dst); + mrs.mr->subpage = false; + flatview_add_to_dispatch(uc, dst, &mrs); + } + address_space_dispatch_compact(dst->dispatch); + MEMORY_LISTENER_CALL_GLOBAL(uc, commit, Forward); + return true; +} + +static bool flatview_update(FlatView *fv, MemoryRegion *mr) +{ + struct uc_struct *uc = mr->uc; + MemoryRegion *c = mr; + AddrRange r; + hwaddr addr = 0; + r.size = mr->size; + do { + addr += c->addr; + } while ((c = c->container)); + r.start = int128_make64(addr); + + if (!mr->container || !QTAILQ_EMPTY(&mr->subregions)) + return false; + + for (size_t i = 0; i < fv->nr; i++) { + if (!addrrange_intersects(fv->ranges[i].addr, r)) { + continue; + } + if (!addrrange_equal(fv->ranges[i].addr, r)) { + break; + } + fv->ranges[i].mr = mr; + fv->ranges[i].offset_in_region = 0; + fv->ranges[i].readonly = mr->readonly; + address_space_dispatch_clear(fv->dispatch); + fv->dispatch = address_space_dispatch_new(uc, fv); + for (size_t j = 0; j < fv->nr; j++) { + MemoryRegionSection mrs = + section_from_flat_range(&fv->ranges[j], fv); + flatview_add_to_dispatch(uc, fv, &mrs); + } + address_space_dispatch_compact(fv->dispatch); + return true; + } + return false; +} + static void flatviews_reset(struct uc_struct *uc) { AddressSpace *as; @@ -975,18 +1059,23 @@ void memory_region_transaction_begin(void) { } -void memory_region_transaction_commit(MemoryRegion *mr) +static void memory_region_transaction_commit(MemoryRegion *mr) { - AddressSpace *as; + AddressSpace *as = memory_region_to_address_space(mr); + FlatView *fv = NULL; + if (as) + fv = address_space_to_flatview(as); if (mr->uc->memory_region_update_pending) { - flatviews_reset(mr->uc); - MEMORY_LISTENER_CALL_GLOBAL(mr->uc, begin, Forward); - QTAILQ_FOREACH(as, &mr->uc->address_spaces, address_spaces_link) { - address_space_set_flatview(as); + if (!fv || !flatview_update(fv, mr)) { + flatviews_reset(mr->uc); + QTAILQ_FOREACH(as, &mr->uc->address_spaces, address_spaces_link) { + address_space_set_flatview(as); + } } + mr->uc->memory_region_update_pending = false; MEMORY_LISTENER_CALL_GLOBAL(mr->uc, commit, Forward); } @@ -1238,7 +1327,7 @@ static void memory_region_update_container_subregions(MemoryRegion *subregion) done: mr->uc->memory_region_update_pending = true; - memory_region_transaction_commit(mr); + memory_region_transaction_commit(subregion); } static void memory_region_add_subregion_common(MemoryRegion *mr, diff --git a/qemu/sparc.h b/qemu/sparc.h index 6e726c9968..9941616733 100644 --- a/qemu/sparc.h +++ b/qemu/sparc.h @@ -54,6 +54,7 @@ #define vm_start vm_start_sparc #define address_space_dispatch_compact address_space_dispatch_compact_sparc #define flatview_translate flatview_translate_sparc +#define flatview_copy flatview_copy_sparc #define address_space_translate_for_iotlb address_space_translate_for_iotlb_sparc #define qemu_get_cpu qemu_get_cpu_sparc #define cpu_address_space_init cpu_address_space_init_sparc @@ -90,6 +91,7 @@ #define iotlb_to_section iotlb_to_section_sparc #define address_space_dispatch_new address_space_dispatch_new_sparc #define address_space_dispatch_free address_space_dispatch_free_sparc +#define address_space_dispatch_clear address_space_dispatch_clear_sparc #define flatview_read_continue flatview_read_continue_sparc #define address_space_read_full address_space_read_full_sparc #define address_space_write address_space_write_sparc diff --git a/qemu/sparc64.h b/qemu/sparc64.h index 747cfe3245..17fdd75965 100644 --- a/qemu/sparc64.h +++ b/qemu/sparc64.h @@ -54,6 +54,7 @@ #define vm_start vm_start_sparc64 #define address_space_dispatch_compact address_space_dispatch_compact_sparc64 #define flatview_translate flatview_translate_sparc64 +#define flatview_copy flatview_copy_sparc64 #define address_space_translate_for_iotlb address_space_translate_for_iotlb_sparc64 #define qemu_get_cpu qemu_get_cpu_sparc64 #define cpu_address_space_init cpu_address_space_init_sparc64 @@ -90,6 +91,7 @@ #define iotlb_to_section iotlb_to_section_sparc64 #define address_space_dispatch_new address_space_dispatch_new_sparc64 #define address_space_dispatch_free address_space_dispatch_free_sparc64 +#define address_space_dispatch_clear address_space_dispatch_clear_sparc64 #define flatview_read_continue flatview_read_continue_sparc64 #define address_space_read_full address_space_read_full_sparc64 #define address_space_write address_space_write_sparc64 diff --git a/qemu/tricore.h b/qemu/tricore.h index c4967b5997..d074ac28d2 100644 --- a/qemu/tricore.h +++ b/qemu/tricore.h @@ -54,6 +54,7 @@ #define vm_start vm_start_tricore #define address_space_dispatch_compact address_space_dispatch_compact_tricore #define flatview_translate flatview_translate_tricore +#define flatview_copy flatview_copy_tricore #define address_space_translate_for_iotlb address_space_translate_for_iotlb_tricore #define qemu_get_cpu qemu_get_cpu_tricore #define cpu_address_space_init cpu_address_space_init_tricore @@ -90,6 +91,7 @@ #define iotlb_to_section iotlb_to_section_tricore #define address_space_dispatch_new address_space_dispatch_new_tricore #define address_space_dispatch_free address_space_dispatch_free_tricore +#define address_space_dispatch_clear address_space_dispatch_clear_tricore #define flatview_read_continue flatview_read_continue_tricore #define address_space_read_full address_space_read_full_tricore #define address_space_write address_space_write_tricore diff --git a/qemu/unicorn_common.h b/qemu/unicorn_common.h index 9bf24c7a6b..b557c49058 100644 --- a/qemu/unicorn_common.h +++ b/qemu/unicorn_common.h @@ -140,6 +140,7 @@ static inline void uc_common_init(struct uc_struct* uc) uc->set_tlb = uc_set_tlb; uc->memory_mapping = find_memory_mapping; uc->memory_filter_subregions = memory_region_filter_subregions; + uc->flatview_copy = flatview_copy; uc->memory_cow = memory_cow; if (!uc->release) diff --git a/qemu/x86_64.h b/qemu/x86_64.h index 1a941508d5..77749382a4 100644 --- a/qemu/x86_64.h +++ b/qemu/x86_64.h @@ -54,6 +54,7 @@ #define vm_start vm_start_x86_64 #define address_space_dispatch_compact address_space_dispatch_compact_x86_64 #define flatview_translate flatview_translate_x86_64 +#define flatview_copy flatview_copy_x86_64 #define address_space_translate_for_iotlb address_space_translate_for_iotlb_x86_64 #define qemu_get_cpu qemu_get_cpu_x86_64 #define cpu_address_space_init cpu_address_space_init_x86_64 @@ -90,6 +91,7 @@ #define iotlb_to_section iotlb_to_section_x86_64 #define address_space_dispatch_new address_space_dispatch_new_x86_64 #define address_space_dispatch_free address_space_dispatch_free_x86_64 +#define address_space_dispatch_clear address_space_dispatch_clear_x86_64 #define flatview_read_continue flatview_read_continue_x86_64 #define address_space_read_full address_space_read_full_x86_64 #define address_space_write address_space_write_x86_64 diff --git a/symbols.sh b/symbols.sh index a33065b1ef..29600c7e2d 100755 --- a/symbols.sh +++ b/symbols.sh @@ -54,6 +54,7 @@ resume_all_vcpus \ vm_start \ address_space_dispatch_compact \ flatview_translate \ +flatview_copy \ address_space_translate_for_iotlb \ qemu_get_cpu \ cpu_address_space_init \ @@ -90,6 +91,7 @@ cpu_check_watchpoint \ iotlb_to_section \ address_space_dispatch_new \ address_space_dispatch_free \ +address_space_dispatch_clear \ flatview_read_continue \ address_space_read_full \ address_space_write \ diff --git a/tests/benchmarks/cow/benchmark.c b/tests/benchmarks/cow/benchmark.c index 327886ce3c..265959acf2 100644 --- a/tests/benchmarks/cow/benchmark.c +++ b/tests/benchmarks/cow/benchmark.c @@ -9,6 +9,8 @@ struct data { gsl_rstat_workspace *rstat_p; struct timespec start; + size_t nc; + uc_context **c; }; @@ -22,11 +24,8 @@ void update_stats(gsl_rstat_workspace *rstat_p, struct timespec *start, struct t static uint64_t CODEADDR = 0x1000; static uint64_t DATABASE = 0x40000000; static uint64_t BLOCKSIZE = 0x10000; +static size_t NRUNS = 200; -/*static void callback_mem(uc_engine *uc, uc_mem_type type, uint64_t addr, uint32_t size, uint64_t value, void *data) -{ - printf("callback mem valid: 0x%lX, value: 0x%lX\n", addr, value); -}*/ static int callback_mem_prot(uc_engine *uc, uc_mem_type type, uint64_t addr, uint32_t size, int64_t value, void *data) { printf("callback mem prot: 0x%lX, type: %X\n", addr, type); @@ -50,27 +49,21 @@ static void callback_block(uc_engine *uc, uint64_t addr, uint32_t size, void *da d->rstat_p = gsl_rstat_alloc(); } run = gsl_rstat_n(d->rstat_p); - if ((run >> 4) >= 20) { + if (run && !(run % 128)) { uc_emu_stop(uc); return; - } else if (run > 0 && run % 16 == 0) { - uc_snapshot(uc); } -/* if (run > 0 && run % 16 == 0) { - uc_emu_stop(uc); - return; - }*/ rsi = random(); memblock = random() & 15; offset = random() & (BLOCKSIZE - 1) & (~0xf); -// memblock = 0; -// offset = 0; if (memblock == 15 && (offset + 0x1000) > BLOCKSIZE) { offset -= 0x1000; } rbx += (memblock * BLOCKSIZE) + offset; +#ifndef NDEBUG printf("write at 0x%lX\n", rbx); printf("[%li] callback block: 0x%lX\n", run, addr); +#endif uc_reg_write(uc, UC_X86_REG_RBX, &rbx); uc_reg_write(uc, UC_X86_REG_RAX, &rax); uc_reg_write(uc, UC_X86_REG_RSI, &rsi); @@ -80,7 +73,9 @@ static void callback_block(uc_engine *uc, uint64_t addr, uint32_t size, void *da static void prepare_mapping(uc_engine *uc) { for (size_t i = 0; i < 16; i++) { +#ifndef NDEBUG printf("mem map: 0x%lX\n", DATABASE+i*BLOCKSIZE); +#endif uc_mem_map(uc, DATABASE+i*BLOCKSIZE, BLOCKSIZE, UC_PROT_READ|UC_PROT_WRITE); } } @@ -145,6 +140,7 @@ int main(int argc, char *argv[]) uc_err err; uc_hook hook_block; uc_hook hook_mem; + uc_context **con = calloc(NRUNS, sizeof(*con)); struct data d; uint64_t rax = 5; uint64_t rbx = DATABASE; @@ -156,9 +152,13 @@ int main(int argc, char *argv[]) } d.rstat_p = NULL; + d.c = con; + d.nc = 0; srandom(time(NULL)); uc_open(UC_ARCH_X86, UC_MODE_64, &uc); + uc_ctl_context_mode(uc, UC_CTL_CONTEXT_MEMORY); + uc_ctl_tlb_mode(uc, UC_TLB_VIRTUAL); prepare_code(uc, argv[1], &bin_mmap); prepare_mapping(uc); err = uc_hook_add(uc, &hook_block, UC_HOOK_BLOCK, &callback_block, &d, CODEADDR, 0x1000); @@ -168,18 +168,19 @@ int main(int argc, char *argv[]) uc_hook_add(uc, &hook_mem, UC_HOOK_MEM_INVALID, &callback_mem_prot, NULL, CODEADDR, 0x1000); uc_reg_write(uc, UC_X86_REG_RBX, &rbx); uc_reg_write(uc, UC_X86_REG_RAX, &rax); -/* err = uc_hook_add(uc, &hook_mem, UC_HOOK_MEM_VALID, &callback_mem, NULL, DATABASE, 16*BLOCKSIZE); - if (err) { - printf("err: %s\n", uc_strerror(err)); - return 1; - }*/ - for (int i = 0; i < 1; i++) { + + for (int i = 0; i < NRUNS; i++) { +#ifndef NDEBUG + printf("============ run: %i\n", i); +#endif err = uc_emu_start(uc, CODEADDR, -1, 0, 0); if (err) { printf("err: %s\n", uc_strerror(err)); return 1; } - uc_snapshot(uc); + uc_context_alloc(uc, &d.c[d.nc]); + uc_context_save(uc, d.c[d.nc]); + d.nc++; } print_stats(d.rstat_p); return 0; diff --git a/uc.c b/uc.c index dfddbec7e8..4ba27d8ad6 100644 --- a/uc.c +++ b/uc.c @@ -2106,6 +2106,7 @@ uc_err uc_context_alloc(uc_engine *uc, uc_context **context) (*_context)->context_size = size - sizeof(uc_context); (*_context)->arch = uc->arch; (*_context)->mode = uc->mode; + (*_context)->fv = NULL; restore_jit_state(uc); return UC_ERR_OK; } else { @@ -2142,11 +2143,23 @@ uc_err uc_context_save(uc_engine *uc, uc_context *context) uc_err ret = UC_ERR_OK; if (uc->context_content & UC_CTL_CONTEXT_MEMORY) { + if (!context->fv) { + context->fv = g_malloc0(sizeof(*context->fv)); + } + if (!context->fv) { + return UC_ERR_NOMEM; + } + if (!uc->flatview_copy(uc, context->fv, uc->address_space_memory.current_map, false)) { + restore_jit_state(uc); + return UC_ERR_NOMEM; + } ret = uc_snapshot(uc); if (ret != UC_ERR_OK) { restore_jit_state(uc); return ret; } + context->ramblock_freed = uc->ram_list.freed; + context->last_block = uc->ram_list.last_block; } context->snapshot_level = uc->snapshot_level; @@ -2418,6 +2431,11 @@ uc_err uc_context_restore(uc_engine *uc, uc_context *context) return ret; } uc_snapshot(uc); + uc->ram_list.freed = context->ramblock_freed; + uc->ram_list.last_block = context->last_block; + if (!uc->flatview_copy(uc, uc->address_space_memory.current_map, context->fv, true)) { + return UC_ERR_NOMEM; + } } if (uc->context_content & UC_CTL_CONTEXT_CPU) { @@ -2434,6 +2452,10 @@ uc_err uc_context_restore(uc_engine *uc, uc_context *context) UNICORN_EXPORT uc_err uc_context_free(uc_context *context) { + if (context->fv) { + free(context->fv->ranges); + g_free(context->fv); + } return uc_free(context); } @@ -2870,6 +2892,7 @@ static uc_err uc_restore_latest_snapshot(struct uc_struct *uc) g_array_remove_range(uc->unmapped_regions, i, 1); } uc->snapshot_level--; + return UC_ERR_OK; } From ffeddd75798586bbebd20c8756d6ea28dc1874d6 Mon Sep 17 00:00:00 2001 From: mio Date: Thu, 17 Oct 2024 13:34:31 +0800 Subject: [PATCH 24/72] use qemu_memalign for all cpu structs Some structs, specically CPUARMState is 16-bytes aligned. This causes segment fault because gcc tends to vectorize the assignment of the struct with infamous movaps tricks. Without this patch, we fail on manylinux with 2.17 glibc in release mode in i686. qemu_memalign will ensure the alignment across platforms. --- qemu/target/arm/cpu.c | 4 +++- qemu/target/arm/cpu64.c | 4 +++- qemu/target/i386/cpu.c | 3 ++- qemu/target/m68k/cpu.c | 3 ++- qemu/target/mips/cpu.c | 3 ++- qemu/target/ppc/translate_init.inc.c | 2 +- qemu/target/riscv/cpu.c | 3 ++- qemu/target/s390x/cpu.c | 3 ++- qemu/target/sparc/cpu.c | 4 ++-- qemu/target/tricore/cpu.c | 3 ++- uc.c | 2 +- 11 files changed, 22 insertions(+), 12 deletions(-) diff --git a/qemu/target/arm/cpu.c b/qemu/target/arm/cpu.c index bbcbe83fbb..7613381980 100644 --- a/qemu/target/arm/cpu.c +++ b/qemu/target/arm/cpu.c @@ -2102,10 +2102,12 @@ ARMCPU *cpu_arm_init(struct uc_struct *uc) CPUClass *cc; CPUARMState *env; - cpu = calloc(1, sizeof(*cpu)); + // cpu->env is 16 bytes aligned + cpu = qemu_memalign(16, sizeof(*cpu)); if (cpu == NULL) { return NULL; } + memset((void*)cpu, 0, sizeof(*cpu)); #if !defined(TARGET_AARCH64) if (uc->mode & UC_MODE_MCLASS) { diff --git a/qemu/target/arm/cpu64.c b/qemu/target/arm/cpu64.c index e6f01bb120..3c57a52aee 100644 --- a/qemu/target/arm/cpu64.c +++ b/qemu/target/arm/cpu64.c @@ -325,10 +325,12 @@ ARMCPU *cpu_aarch64_init(struct uc_struct *uc) CPUClass *cc; CPUARMState *env; - cpu = calloc(1, sizeof(*cpu)); + // cpu->env is 16 bytes alignment + cpu = qemu_memalign(16, sizeof(*cpu)); if (cpu == NULL) { return NULL; } + memset((void*)cpu, 0, sizeof(*cpu)); if (uc->cpu_model == INT_MAX) { uc->cpu_model = UC_CPU_ARM64_A72; diff --git a/qemu/target/i386/cpu.c b/qemu/target/i386/cpu.c index 826a89f8a0..854aaa7422 100644 --- a/qemu/target/i386/cpu.c +++ b/qemu/target/i386/cpu.c @@ -5076,10 +5076,11 @@ X86CPU *cpu_x86_init(struct uc_struct *uc) CPUClass *cc; X86CPUClass *xcc; - cpu = calloc(1, sizeof(*cpu)); + cpu = qemu_memalign(8, sizeof(*cpu)); if (cpu == NULL) { return NULL; } + memset((void*)cpu, 0, sizeof(*cpu)); if (uc->cpu_model == INT_MAX) { #ifdef TARGET_X86_64 diff --git a/qemu/target/m68k/cpu.c b/qemu/target/m68k/cpu.c index e3dc948496..6b636b80eb 100644 --- a/qemu/target/m68k/cpu.c +++ b/qemu/target/m68k/cpu.c @@ -265,10 +265,11 @@ M68kCPU *cpu_m68k_init(struct uc_struct *uc) CPUState *cs; CPUClass *cc; - cpu = calloc(1, sizeof(*cpu)); + cpu = qemu_memalign(8, sizeof(*cpu)); if (cpu == NULL) { return NULL; } + memset((void*)cpu, 0, sizeof(*cpu)); if (uc->cpu_model == INT_MAX) { uc->cpu_model = UC_CPU_M68K_CFV4E; // cfv4e diff --git a/qemu/target/mips/cpu.c b/qemu/target/mips/cpu.c index 27ad119976..640c8c9bb8 100644 --- a/qemu/target/mips/cpu.c +++ b/qemu/target/mips/cpu.c @@ -157,10 +157,11 @@ MIPSCPU *cpu_mips_init(struct uc_struct *uc) CPUClass *cc; CPUMIPSState *env; - cpu = calloc(1, sizeof(*cpu)); + cpu = qemu_memalign(8, sizeof(*cpu)); if (cpu == NULL) { return NULL; } + memset((void*)cpu, 0, sizeof(*cpu)); #ifdef TARGET_MIPS64 if (uc->cpu_model == INT_MAX) { diff --git a/qemu/target/ppc/translate_init.inc.c b/qemu/target/ppc/translate_init.inc.c index 782effaa68..1f6d98b7e0 100644 --- a/qemu/target/ppc/translate_init.inc.c +++ b/qemu/target/ppc/translate_init.inc.c @@ -11016,7 +11016,7 @@ PowerPCCPU *cpu_ppc_init(struct uc_struct *uc) CPUClass *cc; PowerPCCPUClass *pcc; - cpu = malloc(sizeof(*cpu)); + cpu = qemu_memalign(8, sizeof(*cpu)); if (cpu == NULL) { return NULL; } diff --git a/qemu/target/riscv/cpu.c b/qemu/target/riscv/cpu.c index 6405abd4c2..2313cfc6cc 100644 --- a/qemu/target/riscv/cpu.c +++ b/qemu/target/riscv/cpu.c @@ -335,10 +335,11 @@ RISCVCPU *cpu_riscv_init(struct uc_struct *uc) CPUState *cs; CPUClass *cc; - cpu = calloc(1, sizeof(*cpu)); + cpu = qemu_memalign(8, sizeof(*cpu)); if (cpu == NULL) { return NULL; } + memset((void*)cpu, 0, sizeof(*cpu)); #ifdef TARGET_RISCV32 if (uc->cpu_model == INT_MAX) { diff --git a/qemu/target/s390x/cpu.c b/qemu/target/s390x/cpu.c index a90687152a..036077a685 100644 --- a/qemu/target/s390x/cpu.c +++ b/qemu/target/s390x/cpu.c @@ -245,10 +245,11 @@ S390CPU *cpu_s390_init(struct uc_struct *uc, const char *cpu_model) CPUClass *cc; // int i; - cpu = calloc(1, sizeof(*cpu)); + cpu = qemu_memalign(8, sizeof(*cpu)); if (cpu == NULL) { return NULL; } + memset((void*)cpu, 0, sizeof(*cpu)); if (uc->cpu_model == INT_MAX) { uc->cpu_model = UC_CPU_S390X_QEMU; // qemu-s390x-cpu diff --git a/qemu/target/sparc/cpu.c b/qemu/target/sparc/cpu.c index e1386a154d..e0e1962d40 100644 --- a/qemu/target/sparc/cpu.c +++ b/qemu/target/sparc/cpu.c @@ -517,11 +517,11 @@ SPARCCPU *cpu_sparc_init(struct uc_struct *uc) CPUClass *cc; SPARCCPUClass *scc; - cpu = malloc(sizeof(*cpu)); + cpu = qemu_memalign(8, sizeof(*cpu)); if (cpu == NULL) { return NULL; } - memset(cpu, 0, sizeof(*cpu)); + memset((void*)cpu, 0, sizeof(*cpu)); if (uc->cpu_model == INT_MAX) { #ifdef TARGET_SPARC64 diff --git a/qemu/target/tricore/cpu.c b/qemu/target/tricore/cpu.c index 8a871f4527..c980b505f6 100644 --- a/qemu/target/tricore/cpu.c +++ b/qemu/target/tricore/cpu.c @@ -165,10 +165,11 @@ TriCoreCPU *cpu_tricore_init(struct uc_struct *uc) CPUState *cs; CPUClass *cc; - cpu = calloc(1, sizeof(*cpu)); + cpu = qemu_memalign(8, sizeof(*cpu)); if (cpu == NULL) { return NULL; } + memset((void*)cpu, 0, sizeof(*cpu)); if (uc->cpu_model == INT_MAX) { uc->cpu_model = 2; // tc27x diff --git a/uc.c b/uc.c index 4ba27d8ad6..0ff4196f1f 100644 --- a/uc.c +++ b/uc.c @@ -515,7 +515,7 @@ uc_err uc_close(uc_engine *uc) g_free(uc->cpu->thread); /* cpu */ - free(uc->cpu); + qemu_vfree(uc->cpu); /* flatviews */ g_hash_table_destroy(uc->flat_views); From c42cc0fe8629f8d40a18c52995fd1673c3a6330c Mon Sep 17 00:00:00 2001 From: mio Date: Thu, 17 Oct 2024 16:33:59 +0800 Subject: [PATCH 25/72] More 16-bits aligned cpu state targets --- qemu/target/i386/cpu.c | 3 ++- qemu/target/ppc/translate_init.inc.c | 3 ++- qemu/target/s390x/cpu.c | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/qemu/target/i386/cpu.c b/qemu/target/i386/cpu.c index 854aaa7422..5b735d7c47 100644 --- a/qemu/target/i386/cpu.c +++ b/qemu/target/i386/cpu.c @@ -5076,7 +5076,8 @@ X86CPU *cpu_x86_init(struct uc_struct *uc) CPUClass *cc; X86CPUClass *xcc; - cpu = qemu_memalign(8, sizeof(*cpu)); + // env->fpregs + cpu = qemu_memalign(16, sizeof(*cpu)); if (cpu == NULL) { return NULL; } diff --git a/qemu/target/ppc/translate_init.inc.c b/qemu/target/ppc/translate_init.inc.c index 1f6d98b7e0..d2fb1974ad 100644 --- a/qemu/target/ppc/translate_init.inc.c +++ b/qemu/target/ppc/translate_init.inc.c @@ -11016,7 +11016,8 @@ PowerPCCPU *cpu_ppc_init(struct uc_struct *uc) CPUClass *cc; PowerPCCPUClass *pcc; - cpu = qemu_memalign(8, sizeof(*cpu)); + // vsr + cpu = qemu_memalign(16, sizeof(*cpu)); if (cpu == NULL) { return NULL; } diff --git a/qemu/target/s390x/cpu.c b/qemu/target/s390x/cpu.c index 036077a685..53b9ccc07e 100644 --- a/qemu/target/s390x/cpu.c +++ b/qemu/target/s390x/cpu.c @@ -245,7 +245,8 @@ S390CPU *cpu_s390_init(struct uc_struct *uc, const char *cpu_model) CPUClass *cc; // int i; - cpu = qemu_memalign(8, sizeof(*cpu)); + // vregs + cpu = qemu_memalign(16, sizeof(*cpu)); if (cpu == NULL) { return NULL; } From 6fbbf3089af67643c2e8d6496ba84976b35461a1 Mon Sep 17 00:00:00 2001 From: "@Antelox" Date: Thu, 17 Oct 2024 13:35:42 +0200 Subject: [PATCH 26/72] Python binding setup refactoring + cibuildwheel workflow (#2026) * Python bindings: Make the test scripts handy for pytest * Python bindings: Update MANIFEST.in with new paths * Update .gitignore to exclude PyCharm-related files/folders * Python bindings: Update CMakeLists.txt in order to set CMAKE_OSX_ARCHITECTURES var * Python bindings: - Moved project package settings to the new TOML format - Refactored setup.py to cleanup/improve the code and make it ready for cibuildwheel - Updated README.md with the package long description part - Removed setup.cfg since universal wheel building will be deprecated soon * Python bindings: - Replaced old PyPI-publishing.yml workflow with brand-new one based on cibuildwheel - Removed old building scripts * Replaced macos-12 runner with macos-13 since it will be removed soon * Python bindings: Specify SYSTEM_VERSION_COMPAT=0 env var for macos-13 x86_64 runner as per cibuildwheel warning message * Python bindings: Enable i686 for debugging * Python bindings: Enable DEBUG flag according to the presence of tag release * Python bindings: Added matrix to cover i686 manylinux/musllinux builds * Python bindings: - Replaced macos-14 runner with macos-latest - Bumped cibuildwheel GitHub action to 2.21.3 version * Python bindings: - Adapt test_uc_ctl_tb_cache test to the recent changes - Fixed typos - PEP8 fixes * GitHub Action Workflow: Introduce BUILD_TYPE env var to select build type according to the presence of tag release --------- Co-authored-by: mio --- .github/workflows/PyPI-publishing.yml | 167 --- .github/workflows/build-uc2.yml | 998 +++++++++--------- .github/workflows/build-wheels-publish.yml | 360 +++++++ .gitignore | 8 +- CMakeLists.txt | 18 + bindings/python/MANIFEST.in | 9 +- bindings/python/README.md | 18 + bindings/python/build_wheel.sh | 15 - bindings/python/musl_wheel.sh | 9 - bindings/python/pyproject.toml | 40 + bindings/python/sample_all.sh | 31 - bindings/python/setup.cfg | 2 - bindings/python/setup.py | 180 +--- .../{sample_arm.py => tests/test_arm.py} | 23 +- .../{sample_arm64.py => tests/test_arm64.py} | 15 +- .../test_arm64eb.py} | 11 +- .../{sample_armeb.py => tests/test_armeb.py} | 21 +- .../{sample_ctl.py => tests/test_ctl.py} | 25 +- .../{sample_m68k.py => tests/test_m68k.py} | 9 +- .../{sample_mips.py => tests/test_mips.py} | 15 +- .../test_network_auditing.py} | 6 +- .../{sample_ppc.py => tests/test_ppc.py} | 11 +- .../{sample_riscv.py => tests/test_riscv.py} | 13 +- .../{sample_s390x.py => tests/test_s390x.py} | 7 +- .../{shellcode.py => tests/test_shellcode.py} | 51 +- .../{sample_sparc.py => tests/test_sparc.py} | 13 +- .../test_tricore.py} | 18 +- .../{sample_x86.py => tests/test_x86.py} | 152 +-- 28 files changed, 1193 insertions(+), 1052 deletions(-) delete mode 100644 .github/workflows/PyPI-publishing.yml create mode 100644 .github/workflows/build-wheels-publish.yml delete mode 100755 bindings/python/build_wheel.sh delete mode 100644 bindings/python/musl_wheel.sh create mode 100644 bindings/python/pyproject.toml delete mode 100755 bindings/python/sample_all.sh delete mode 100644 bindings/python/setup.cfg rename bindings/python/{sample_arm.py => tests/test_arm.py} (89%) rename bindings/python/{sample_arm64.py => tests/test_arm64.py} (92%) rename bindings/python/{sample_arm64eb.py => tests/test_arm64eb.py} (91%) rename bindings/python/{sample_armeb.py => tests/test_armeb.py} (88%) rename bindings/python/{sample_ctl.py => tests/test_ctl.py} (89%) rename bindings/python/{sample_m68k.py => tests/test_m68k.py} (96%) rename bindings/python/{sample_mips.py => tests/test_mips.py} (91%) rename bindings/python/{sample_network_auditing.py => tests/test_network_auditing.py} (99%) rename bindings/python/{sample_ppc.py => tests/test_ppc.py} (93%) rename bindings/python/{sample_riscv.py => tests/test_riscv.py} (92%) rename bindings/python/{sample_s390x.py => tests/test_s390x.py} (96%) rename bindings/python/{shellcode.py => tests/test_shellcode.py} (82%) rename bindings/python/{sample_sparc.py => tests/test_sparc.py} (88%) rename bindings/python/{sample_tricore.py => tests/test_tricore.py} (89%) rename bindings/python/{sample_x86.py => tests/test_x86.py} (85%) diff --git a/.github/workflows/PyPI-publishing.yml b/.github/workflows/PyPI-publishing.yml deleted file mode 100644 index d4ad442849..0000000000 --- a/.github/workflows/PyPI-publishing.yml +++ /dev/null @@ -1,167 +0,0 @@ -name: PyPI 📦 Distribution - -on: - push: - paths-ignore: - - ".gitignore" - - "docs/**" - - "README" - - "CREDITS.TXT" - - "COPYING_GLIB" - - "COPYING.LGPL2" - - "AUTHORS.TXT" - - "CHANGELOG" - - "COPYING" - pull_request: - -jobs: - build: - runs-on: ${{ matrix.config.os }} - name: ${{ matrix.config.name }} - strategy: - fail-fast: false - matrix: - config: - - { - os: windows-2019, - arch: x64, - python-ver: '3.8', - name: 'win_amd64' - } - - { - os: windows-2019, - arch: x32, - python-ver: '3.8', - name: 'win32' - } - - { - os: ubuntu-latest, - arch: x64, - python-ver: '3.8', - name: 'musllinux' - } - - { - os: ubuntu-latest, - arch: x64, - python-ver: '3.8', - name: 'manylinux2014_x86_64' - } - - { - os: ubuntu-latest, - arch: x32, - python-ver: '3.8', - name: 'manylinux2014_i686' - } - - { - os: ubuntu-latest, - arch: aarch64, - python-ver: '3.8', - name: 'manylinux2014_aarch64' - } - - { - os: ubuntu-latest, - arch: x64, - python-ver: '3.8', - name: 'sdist' - } - - { - os: macos-12, - arch: x86_64, - python-ver: '3.8', - name: 'macos_x86_64' - } - - { - os: macos-14, - arch: arm64, - python-ver: '3.10', - name: 'macos_arm64' - } - steps: - - uses: actions/checkout@v4 - - - name: '🛠️ Set up Python' - uses: actions/setup-python@v5 - with: - python-version: ${{ matrix.config.python-ver }} - - - name: '🛠️ Add msbuild to PATH' - if: contains(matrix.config.name, 'win') - uses: microsoft/setup-msbuild@v2 - with: - vs-version: '16.5' - - - name: '🛠️ Win MSVC 32 dev cmd setup' - if: contains(matrix.config.name, 'win32') - uses: ilammy/msvc-dev-cmd@v1 - with: - arch: x86 - - - name: '🛠️ Win MSVC 64 dev cmd setup' - if: contains(matrix.config.name, 'win_amd64') - uses: ilammy/msvc-dev-cmd@v1 - with: - arch: x64 - - - name: '🛠️ Win build dependencies' - if: contains(matrix.config.name, 'win') - shell: bash - run: | - choco install ninja cmake - - - name: '🛠️ macOS dependencies' - if: contains(matrix.config.name, 'macos') - run: | - brew install ninja - - - name: '🛠️ pip dependencies' - run: | - pip install --upgrade setuptools wheel - - - name: '🚧 Build distribution' - shell: bash - run: | - if [ ${{ matrix.config.name }} == 'win32' ]; then - cd bindings/python && python setup.py build -p win32 sdist bdist_wheel -p win32 - rm dist/*.tar.gz - elif [ ${{ matrix.config.name }} == 'manylinux2014_i686' ]; then - docker run --rm -v `pwd`/:/work dockcross/manylinux2014-x86 > ./dockcross - chmod +x ./dockcross - ./dockcross bindings/python/build_wheel.sh - elif [ ${{ matrix.config.name }} == 'manylinux2014_aarch64' ]; then - docker run --rm -v `pwd`/:/work dockcross/manylinux2014-aarch64 > ./dockcross - chmod +x ./dockcross - ./dockcross bindings/python/build_wheel.sh --plat-name manylinux2014_aarch64 - elif [ ${{ matrix.config.name }} == 'manylinux2014_x86_64' ]; then - docker run --rm -v `pwd`/:/work dockcross/manylinux2014-x64 > ./dockcross - chmod +x ./dockcross - ./dockcross bindings/python/build_wheel.sh - elif [ ${{ matrix.config.name }} == 'musllinux' ]; then - docker run --rm -v `pwd`:/work -w /work python:3.7-alpine sh /work/bindings/python/musl_wheel.sh - elif [ ${{ matrix.config.name }} == 'sdist' ]; then - cd bindings/python && python setup.py sdist - elif [ ${{ matrix.config.name }} == 'macos_arm64' ]; then - cd bindings/python && _PYTHON_HOST_PLATFORM="macosx-11.0-arm64" ARCHFLAGS="-arch arm64" python setup.py bdist_wheel - else - cd bindings/python && python setup.py bdist_wheel - fi - - name: '📤 Upload artifact' - uses: actions/upload-artifact@v4 - with: - name: ${{ matrix.config.name }} - path: ${{ github.workspace }}/bindings/python/dist/* - - publish: - needs: [build] - runs-on: ubuntu-latest - if: startsWith(github.ref, 'refs/tags') - steps: - - uses: actions/download-artifact@v4 - with: - merge-multiple: true - path: dist - - - name: '📦 Publish distribution to PyPI' - uses: pypa/gh-action-pypi-publish@release/v1 - with: - user: __token__ - password: ${{ secrets.pypi_pass }} diff --git a/.github/workflows/build-uc2.yml b/.github/workflows/build-uc2.yml index b5daa35143..554e3ec0e9 100644 --- a/.github/workflows/build-uc2.yml +++ b/.github/workflows/build-uc2.yml @@ -1,21 +1,22 @@ name: Build UC2 -on: +on: push: paths-ignore: - ".gitignore" - - "docs/**" - - "README" - - "CREDITS.TXT" - - "COPYING_GLIB" - - "COPYING.LGPL2" - "AUTHORS.TXT" - - "CHANGELOG" - "COPYING" + - "COPYING.LGPL2" + - "COPYING_GLIB" + - "CREDITS.TXT" + - "ChangeLog" + - "README.md" + - "docs/**" pull_request: env: - CI: true + # Build Debug mode if not tag release + BUILD_TYPE: ${{ startsWith(github.ref, 'refs/tags') && 'Release' || 'Debug' }} jobs: Windows: @@ -25,230 +26,222 @@ jobs: fail-fast: false matrix: config: - - { - os: windows-2019, - arch: x64, - python-arch: x64, - python-ver: '3.8', - name: 'windows-x64 MINGW64 shared', - shared: 'yes', - mingw: MINGW64, - mingw-arch: x86_64, - artifact: 'windows_mingw64-shared.7z', - build_type: 'Debug', - archiver: '7z a', - generators: 'Ninja' - } - - { - os: windows-2019, - arch: x64, - python-arch: x64, - python-ver: '3.8', - name: 'windows-x64 MINGW64 static', - shared: 'no', - mingw: MINGW64, - mingw-arch: x86_64, - artifact: 'windows_mingw64-static.7z', - build_type: 'Debug', - archiver: '7z a', - generators: 'Ninja' - } - # - { # This fails randomly which can't be reproduced. - # os: windows-2019, - # arch: x64, - # python-arch: x64, - # python-ver: '3.8', - # name: 'windows-x64 MINGW32 shared', - # shared: "yes", - # mingw: MINGW32, - # mingw-arch: i686, - # artifact: 'windows_mingw32.7z', - # build_type: 'Debug', - # archiver: '7z a', - # generators: 'Ninja' - # } - # - { # This fails randomly which can't be reproduced. - # os: windows-2019, - # arch: x64, - # python-arch: x64, - # python-ver: '3.8', - # name: 'windows-x64 MINGW32 static', - # shared: "no", - # mingw: MINGW32, - # mingw-arch: i686, - # artifact: 'windows_mingw32.7z', - # build_type: 'Debug', - # archiver: '7z a', - # generators: 'Ninja' - # } - - { - os: windows-2019, - arch: x64, - python-arch: x64, - python-ver: '3.8', - name: 'windows-x64 MSVC 64bit shared', - msvc-arch: x64, - artifact: 'windows_msvc64_shared.7z', - shared: 'yes', - build_type: 'Debug', - archiver: '7z a', - generators: 'Visual Studio 16 2019' - } - { - os: windows-2019, - arch: x86, - python-arch: x86, - python-ver: '3.8', - name: 'windows-x86 MSVC 32bit shared', - msvc-arch: x86, - artifact: 'windows_msvc32_shared.7z', - shared: 'yes', - build_type: 'Debug', - archiver: '7z a', - generators: 'Visual Studio 16 2019' - } - - { - os: windows-2019, - arch: x64, - python-arch: x64, - python-ver: '3.8', - name: 'windows-x64 MSVC 64bit static', - msvc-arch: x64, - artifact: 'windows_msvc64_static.7z', - shared: 'no', - build_type: 'Debug', - archiver: '7z a', - generators: 'Visual Studio 16 2019' - } - - { - os: windows-2019, - arch: x86, - python-arch: x86, - python-ver: '3.8', - name: 'windows-x86 MSVC 32bit static', - msvc-arch: x86, - artifact: 'windows_msvc32_static.7z', - shared: 'no', - build_type: 'Debug', - archiver: '7z a', - generators: 'Visual Studio 16 2019' - } + os: windows-2019, + arch: x64, + python-arch: x64, + python-ver: '3.8', + name: 'windows-x64 MINGW64 shared', + shared: 'yes', + mingw: MINGW64, + mingw-arch: x86_64, + artifact: 'windows_mingw64-shared.7z', + archiver: '7z a', + generators: 'Ninja' + } + - { + os: windows-2019, + arch: x64, + python-arch: x64, + python-ver: '3.8', + name: 'windows-x64 MINGW64 static', + shared: 'no', + mingw: MINGW64, + mingw-arch: x86_64, + artifact: 'windows_mingw64-static.7z', + archiver: '7z a', + generators: 'Ninja' + } +# - { # This fails randomly which can't be reproduced. +# os: windows-2019, +# arch: x64, +# python-arch: x64, +# python-ver: '3.8', +# name: 'windows-x64 MINGW32 shared', +# shared: "yes", +# mingw: MINGW32, +# mingw-arch: i686, +# artifact: 'windows_mingw32.7z', +# archiver: '7z a', +# generators: 'Ninja' +# } +# - { # This fails randomly which can't be reproduced. +# os: windows-2019, +# arch: x64, +# python-arch: x64, +# python-ver: '3.8', +# name: 'windows-x64 MINGW32 static', +# shared: "no", +# mingw: MINGW32, +# mingw-arch: i686, +# artifact: 'windows_mingw32.7z', +# archiver: '7z a', +# generators: 'Ninja' +# } + - { + os: windows-2019, + arch: x64, + python-arch: x64, + python-ver: '3.8', + name: 'windows-x64 MSVC 64bit shared', + msvc-arch: x64, + artifact: 'windows_msvc64_shared.7z', + shared: 'yes', + archiver: '7z a', + generators: 'Visual Studio 16 2019' + } + - { + os: windows-2019, + arch: x86, + python-arch: x86, + python-ver: '3.8', + name: 'windows-x86 MSVC 32bit shared', + msvc-arch: x86, + artifact: 'windows_msvc32_shared.7z', + shared: 'yes', + archiver: '7z a', + generators: 'Visual Studio 16 2019' + } + - { + os: windows-2019, + arch: x64, + python-arch: x64, + python-ver: '3.8', + name: 'windows-x64 MSVC 64bit static', + msvc-arch: x64, + artifact: 'windows_msvc64_static.7z', + shared: 'no', + archiver: '7z a', + generators: 'Visual Studio 16 2019' + } + - { + os: windows-2019, + arch: x86, + python-arch: x86, + python-ver: '3.8', + name: 'windows-x86 MSVC 32bit static', + msvc-arch: x86, + artifact: 'windows_msvc32_static.7z', + shared: 'no', + archiver: '7z a', + generators: 'Visual Studio 16 2019' + } compiler: [ gcc ] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v4 - - name: '🛠️ Win MINGW setup' - if: contains(matrix.config.mingw, 'MINGW') - uses: msys2/setup-msys2@v2 - with: - msystem: ${{ matrix.config.mingw }} - install: >- - git - mingw-w64-${{ matrix.config.mingw-arch }}-cmake - mingw-w64-${{ matrix.config.mingw-arch }}-ninja - mingw-w64-${{ matrix.config.mingw-arch }}-cmocka - mingw-w64-${{ matrix.config.mingw-arch }}-${{ matrix.compiler }} - mingw-w64-${{ matrix.config.mingw-arch }}-toolchain + - name: '🛠️ Win MINGW setup' + if: contains(matrix.config.mingw, 'MINGW') + uses: msys2/setup-msys2@v2 + with: + msystem: ${{ matrix.config.mingw }} + install: >- + git + mingw-w64-${{ matrix.config.mingw-arch }}-cmake + mingw-w64-${{ matrix.config.mingw-arch }}-ninja + mingw-w64-${{ matrix.config.mingw-arch }}-cmocka + mingw-w64-${{ matrix.config.mingw-arch }}-${{ matrix.compiler }} + mingw-w64-${{ matrix.config.mingw-arch }}-toolchain - - name: '🛠️ Win MSVC 64 setup' - if: contains(matrix.config.name, 'MSVC 64') - uses: microsoft/setup-msbuild@v2 - - - name: '🛠️ Win MSVC 64 dev cmd setup' - if: contains(matrix.config.name, 'MSVC 64') - uses: ilammy/msvc-dev-cmd@v1 - with: - arch: x64 + - name: '🛠️ Win MSVC 64 setup' + if: contains(matrix.config.name, 'MSVC 64') + uses: microsoft/setup-msbuild@v2 - - name: '🚧 Win MSVC 64 build' - if: contains(matrix.config.name, 'MSVC 64') - shell: bash - run: | - choco install ninja cmake - ninja --version - cmake --version - mkdir build - cmake \ - -S . \ - -B . \ - -DCMAKE_BUILD_TYPE=${{ matrix.config.build_type }} \ - -G "${{ matrix.config.generators }}" \ - -DCMAKE_INSTALL_PREFIX:PATH=instdir \ - -DBUILD_SHARED_LIBS=${{ matrix.config.shared }} - cmake --build . --config ${{ matrix.config.build_type }} - cmake --install . --strip --config ${{ matrix.config.build_type }} - ctest -VV -C ${{ matrix.config.build_type }} - mv Debug instdir + - name: '🛠️ Win MSVC 64 dev cmd setup' + if: contains(matrix.config.name, 'MSVC 64') + uses: ilammy/msvc-dev-cmd@v1 + with: + arch: x64 - - name: '🛠️ Win MSVC 32 setup' - if: contains(matrix.config.name, 'MSVC 32') - uses: ilammy/msvc-dev-cmd@v1 - with: - arch: x86 + - name: '🚧 Win MSVC 64 build' + if: contains(matrix.config.name, 'MSVC 64') + shell: bash + run: | + choco install ninja cmake + ninja --version + cmake --version + mkdir build + cmake \ + -S . \ + -B . \ + -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} \ + -G "${{ matrix.config.generators }}" \ + -DCMAKE_INSTALL_PREFIX:PATH=instdir \ + -DBUILD_SHARED_LIBS=${{ matrix.config.shared }} + cmake --build . --config ${{ env.BUILD_TYPE }} + cmake --install . --strip --config ${{ env.BUILD_TYPE }} + ctest -VV -C ${{ env.BUILD_TYPE }} + mv Debug instdir - - name: '🚧 Win MSVC 32 build' - if: contains(matrix.config.name, 'MSVC 32') - shell: bash - run: | - choco install ninja cmake - ninja --version - cmake --version - mkdir build - cmake \ - -S . \ - -B . \ - -A "win32" \ - -DCMAKE_BUILD_TYPE=${{ matrix.config.build_type }} \ - -G "${{ matrix.config.generators }}" \ - -DCMAKE_INSTALL_PREFIX:PATH=instdir \ - -DBUILD_SHARED_LIBS=${{ matrix.config.shared }} - cmake --build . --config ${{ matrix.config.build_type }} - cmake --install . --strip --config ${{ matrix.config.build_type }} - ctest -VV -C ${{ matrix.config.build_type }} - mv Debug instdir + - name: '🛠️ Win MSVC 32 setup' + if: contains(matrix.config.name, 'MSVC 32') + uses: ilammy/msvc-dev-cmd@v1 + with: + arch: x86 - - name: '🚧 Win MINGW build' - if: contains(matrix.config.mingw, 'MINGW') - shell: msys2 {0} - run: | - if [ ${{ matrix.config.mingw }} == 'MINGW32' ]; then - export CPPFLAGS=-D__USE_MINGW_ANSI_STDIO=1 - #export CC=i686-w64-mingw32-gcc - export AR=gcc-ar - export RANLIB=gcc-ranlib - export CFLAGS="-m32 -static" - export LDFLAGS="-m32" - export LDFLAGS_STATIC="-m32" - export UNICORN_QEMU_FLAGS="--cpu=i386" - fi - mkdir build - mkdir instdir - cmake \ - -S . \ - -B . \ - -DCMAKE_BUILD_TYPE=${{ matrix.config.build_type }} \ - -G "${{ matrix.config.generators }}" \ - -DCMAKE_INSTALL_PREFIX:PATH=instdir \ - -DCMAKE_C_FLAGS:STRING="-static" \ - -DBUILD_SHARED_LIBS=${{ matrix.config.shared }} - cmake --build . --config ${{ matrix.config.build_type }} - cmake --install . --strip - ctest -VV -C ${{ matrix.config.build_type }} + - name: '🚧 Win MSVC 32 build' + if: contains(matrix.config.name, 'MSVC 32') + shell: bash + run: | + choco install ninja cmake + ninja --version + cmake --version + mkdir build + cmake \ + -S . \ + -B . \ + -A "win32" \ + -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} \ + -G "${{ matrix.config.generators }}" \ + -DCMAKE_INSTALL_PREFIX:PATH=instdir \ + -DBUILD_SHARED_LIBS=${{ matrix.config.shared }} + cmake --build . --config ${{ env.BUILD_TYPE }} + cmake --install . --strip --config ${{ env.BUILD_TYPE }} + ctest -VV -C ${{ env.BUILD_TYPE }} + mv Debug instdir + + - name: '🚧 Win MINGW build' + if: contains(matrix.config.mingw, 'MINGW') + shell: msys2 {0} + run: | + if [ ${{ matrix.config.mingw }} == 'MINGW32' ]; then + export CPPFLAGS=-D__USE_MINGW_ANSI_STDIO=1 + #export CC=i686-w64-mingw32-gcc + export AR=gcc-ar + export RANLIB=gcc-ranlib + export CFLAGS="-m32 -static" + export LDFLAGS="-m32" + export LDFLAGS_STATIC="-m32" + export UNICORN_QEMU_FLAGS="--cpu=i386" + fi + mkdir build + mkdir instdir + cmake \ + -S . \ + -B . \ + -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} \ + -G "${{ matrix.config.generators }}" \ + -DCMAKE_INSTALL_PREFIX:PATH=instdir \ + -DCMAKE_C_FLAGS:STRING="-static" \ + -DBUILD_SHARED_LIBS=${{ matrix.config.shared }} + cmake --build . --config ${{ env.BUILD_TYPE }} + cmake --install . --strip + ctest -VV -C ${{ env.BUILD_TYPE }} - - name: '📦 Pack artifact' - if: always() - shell: bash - working-directory: instdir - run: | - ls -laR - ${{ matrix.config.archiver }} ../${{ matrix.config.artifact }} . ../test* + - name: '📦 Pack artifact' + if: always() + shell: bash + working-directory: instdir + run: | + ls -laR + ${{ matrix.config.archiver }} ../${{ matrix.config.artifact }} . ../test* - - name: '📤 Upload artifact' - if: always() - uses: actions/upload-artifact@v4 - with: - path: ./${{ matrix.config.artifact }} - name: ${{ matrix.config.artifact }} + - name: '📤 Upload artifact' + if: always() + uses: actions/upload-artifact@v4 + with: + path: ./${{ matrix.config.artifact }} + name: ${{ matrix.config.artifact }} Macos: runs-on: ${{ matrix.config.os }} @@ -257,169 +250,164 @@ jobs: fail-fast: false matrix: config: - - { - os: macos-12, # x64 - arch: x64, - python-arch: x64, - python-ver: '3.8', - name: 'macos-x64 cmake shared', - shared: 'yes', - artifact: 'macos-x64-cmake-shared-x64.7z', - build_type: 'Debug', - archiver: '7za a', - generators: 'Ninja' - } - - { - os: macos-12, - arch: x64, - python-arch: x64, - python-ver: '3.8', - name: 'macos-x64 cmake static', - shared: 'no', - artifact: 'macos-x64-cmake-static-x64.7z', - build_type: 'Debug', - archiver: '7za a', - generators: 'Ninja' - } - - { - os: macos-14, # arm64 - arch: arm64, - python-arch: arm64, - python-ver: '3.8', - name: 'macos-arm64 cmake shared', - shared: 'yes', - artifact: 'macos-arm64-cmake-shared-x64.7z', - build_type: 'Debug', - archiver: '7za a', - generators: 'Ninja' - } - - { - os: macos-14, - arch: arm64, - python-arch: arm64, - python-ver: '3.8', - name: 'macos-arm64 cmake static', - shared: 'no', - artifact: 'macos-arm64-cmake-static-x64.7z', - build_type: 'Debug', - archiver: '7za a', - generators: 'Ninja' - } - - { - os: macos-12, - arch: x86_64, - python-arch: x86_64, - python-ver: '3.8', - name: 'android cmake', - artifact: 'Android-x86_64.7z', - build_type: 'Debug', - archiver: '7za a', - generators: 'Ninja' - } + - { + os: macos-13, # x64 + arch: x64, + python-arch: x64, + python-ver: '3.8', + name: 'macos-x64 cmake shared', + shared: 'yes', + artifact: 'macos-x64-cmake-shared-x64.7z', + archiver: '7za a', + generators: 'Ninja' + } + - { + os: macos-13, + arch: x64, + python-arch: x64, + python-ver: '3.8', + name: 'macos-x64 cmake static', + shared: 'no', + artifact: 'macos-x64-cmake-static-x64.7z', + archiver: '7za a', + generators: 'Ninja' + } + - { + os: macos-14, # arm64 + arch: arm64, + python-arch: arm64, + python-ver: '3.8', + name: 'macos-arm64 cmake shared', + shared: 'yes', + artifact: 'macos-arm64-cmake-shared-x64.7z', + archiver: '7za a', + generators: 'Ninja' + } + - { + os: macos-14, + arch: arm64, + python-arch: arm64, + python-ver: '3.8', + name: 'macos-arm64 cmake static', + shared: 'no', + artifact: 'macos-arm64-cmake-static-x64.7z', + archiver: '7za a', + generators: 'Ninja' + } + - { + os: macos-13, + arch: x86_64, + python-arch: x86_64, + python-ver: '3.8', + name: 'android cmake', + artifact: 'Android-x86_64.7z', + archiver: '7za a', + generators: 'Ninja' + } compiler: [ gcc ] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v4 - # - name: '🛠️ Python setup' - # uses: actions/setup-python@v5 - # with: - # python-version: ${{ matrix.config.python-ver }} +# - name: '🛠️ Python setup' +# uses: actions/setup-python@v5 +# with: +# python-version: ${{ matrix.config.python-ver }} - - name: '🚧 Mac build' - if: contains(matrix.config.name, 'macos') - shell: bash - run: | - brew install ninja - ninja --version - cmake --version - mkdir build - mkdir instdir - cmake \ - -S . \ - -B . \ - -DCMAKE_BUILD_TYPE=${{ matrix.config.build_type }} \ - -G "${{ matrix.config.generators }}" \ - -DCMAKE_INSTALL_PREFIX:PATH=instdir \ - -DBUILD_SHARED_LIBS=${{ matrix.config.shared }} - cmake --build . --config ${{ matrix.config.build_type }} - cmake --install . --strip - ctest -VV -C ${{ matrix.config.build_type }} - - # - name: Setup tmate session - # if: ${{ failure() }} - # uses: mxschmitt/action-tmate@v3 + - name: '🚧 Mac build' + if: contains(matrix.config.name, 'macos') + shell: bash + run: | + brew install ninja + ninja --version + cmake --version + mkdir build + mkdir instdir + cmake \ + -S . \ + -B . \ + -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} \ + -G "${{ matrix.config.generators }}" \ + -DCMAKE_INSTALL_PREFIX:PATH=instdir \ + -DBUILD_SHARED_LIBS=${{ matrix.config.shared }} + cmake --build . --config ${{ env.BUILD_TYPE }} + cmake --install . --strip + ctest -VV -C ${{ env.BUILD_TYPE }} + +# - name: Setup tmate session +# if: ${{ failure() }} +# uses: mxschmitt/action-tmate@v3 - - name: '🚧 Android x86_64 build' - if: contains(matrix.config.name, 'android') - shell: bash - run: | - brew install ninja - mkdir build - mkdir instdir - cmake . -DCMAKE_TOOLCHAIN_FILE="$ANDROID_NDK/build/cmake/android.toolchain.cmake" \ - -DANDROID_PLATFORM=android-28 \ - -DANDROID_NDK="$ANDROID_NDK" \ - -DANDROID_ABI=${{ matrix.config.arch }} \ - -DOLP_SDK_ENABLE_TESTING=NO \ - -DOLP_SDK_BUILD_EXAMPLES=ON \ - -S . \ - -B . \ - -DCMAKE_BUILD_TYPE=${{ matrix.config.build_type }} \ - -G "${{ matrix.config.generators }}" \ - -DCMAKE_INSTALL_PREFIX:PATH=instdir - cmake --build . --config ${{ matrix.config.build_type }} - cmake --install . --strip + - name: '🚧 Android x86_64 build' + if: contains(matrix.config.name, 'android') + shell: bash + run: | + brew install ninja + mkdir build + mkdir instdir + cmake . -DCMAKE_TOOLCHAIN_FILE="$ANDROID_NDK/build/cmake/android.toolchain.cmake" \ + -DANDROID_PLATFORM=android-28 \ + -DANDROID_NDK="$ANDROID_NDK" \ + -DANDROID_ABI=${{ matrix.config.arch }} \ + -DOLP_SDK_ENABLE_TESTING=NO \ + -DOLP_SDK_BUILD_EXAMPLES=ON \ + -S . \ + -B . \ + -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} \ + -G "${{ matrix.config.generators }}" \ + -DCMAKE_INSTALL_PREFIX:PATH=instdir + cmake --build . --config ${{ env.BUILD_TYPE }} + cmake --install . --strip + + - name: '🚧 AVD Cache' + if: contains(matrix.config.name, 'android') + uses: actions/cache@v4 + id: avd-cache + with: + path: | + ~/.android/avd/* + ~/.android/adb* + key: avd-28 - - name: '🚧 AVD Cache' - if: contains(matrix.config.name, 'android') - uses: actions/cache@v4 - id: avd-cache - with: - path: | - ~/.android/avd/* - ~/.android/adb* - key: avd-28 - - - name: '🚧 Create x86_64 tests environment' - if: contains(matrix.config.name, 'android') && steps.avd-cache.outputs.cache-hit != 'true' - uses: reactivecircus/android-emulator-runner@v2 - with: - api-level: 28 - arch: ${{ matrix.config.arch }} - force-avd-creation: false - disable-animations: false - target: default - profile: Nexus 6 - emulator-options: -no-window -gpu swiftshader_indirect -no-snapshot -noaudio -no-boot-anim -verbose -show-kernel - script: echo "Generated AVD snapshot for caching." + - name: '🚧 Create x86_64 tests environment' + if: contains(matrix.config.name, 'android') && steps.avd-cache.outputs.cache-hit != 'true' + uses: reactivecircus/android-emulator-runner@v2 + with: + api-level: 28 + arch: ${{ matrix.config.arch }} + force-avd-creation: false + disable-animations: false + target: default + profile: Nexus 6 + emulator-options: -no-window -gpu swiftshader_indirect -no-snapshot -noaudio -no-boot-anim -verbose -show-kernel + script: echo "Generated AVD snapshot for caching." - - name: '🚧 Android x86_64 tests' - if: contains(matrix.config.name, 'android') - uses: reactivecircus/android-emulator-runner@v2 - with: - api-level: 28 - force-avd-creation: false - disable-animations: true - arch: ${{ matrix.config.arch }} - target: default - profile: Nexus 6 - emulator-options: -no-window -gpu swiftshader_indirect -no-snapshot -noaudio -no-boot-anim -verbose -show-kernel - script: bash ./adb.sh + - name: '🚧 Android x86_64 tests' + if: contains(matrix.config.name, 'android') + uses: reactivecircus/android-emulator-runner@v2 + with: + api-level: 28 + force-avd-creation: false + disable-animations: true + arch: ${{ matrix.config.arch }} + target: default + profile: Nexus 6 + emulator-options: -no-window -gpu swiftshader_indirect -no-snapshot -noaudio -no-boot-anim -verbose -show-kernel + script: bash ./adb.sh - - name: '📦 Pack artifact' - if: always() - shell: bash - working-directory: instdir - run: | - ls -laR - ${{ matrix.config.archiver }} ../${{ matrix.config.artifact }} . ../test* + - name: '📦 Pack artifact' + if: always() + shell: bash + working-directory: instdir + run: | + ls -laR + ${{ matrix.config.archiver }} ../${{ matrix.config.artifact }} . ../test* - - name: '📤 Upload artifact' - if: always() - uses: actions/upload-artifact@v4 - with: - path: ./${{ matrix.config.artifact }} - name: ${{ matrix.config.artifact }} + - name: '📤 Upload artifact' + if: always() + uses: actions/upload-artifact@v4 + with: + path: ./${{ matrix.config.artifact }} + name: ${{ matrix.config.artifact }} Linux: runs-on: ${{ matrix.config.os }} @@ -428,149 +416,143 @@ jobs: fail-fast: false matrix: config: - - { - os: ubuntu-latest, - arch: x64, - python-arch: x64, - python-ver: '3.8', - name: 'ubuntu-x64 cmake shared', - shared: 'yes', - artifact: 'ubuntu-cmake-shared-x64.7z', - build_type: 'Debug', - archiver: '7z a', - generators: 'Ninja' - } - - { - os: ubuntu-latest, - arch: x86, - python-arch: x86, - python-ver: '3.8', - name: 'ubuntu-x86 cmake shared', - shared: 'yes', - artifact: 'ubuntu-cmake-shared-x86.7z', - build_type: 'Debug', - archiver: '7z a', - generators: 'Ninja' - } - - { - os: ubuntu-latest, - arch: x64, - python-arch: x64, - python-ver: '3.8', - name: 'ubuntu-x64 cmake static', - shared: 'no', - artifact: 'ubuntu-cmake-static-x64.7z', - build_type: 'Debug', - archiver: '7z a', - generators: 'Ninja' - } - - { - os: ubuntu-latest, - arch: x86, - python-arch: x86, - python-ver: '3.8', - name: 'ubuntu-x86 cmake static', - shared: 'no', - artifact: 'ubuntu-cmake-static-x86.7z', - build_type: 'Debug', - archiver: '7z a', - generators: 'Ninja' - } - - { - os: ubuntu-latest, - arch: aarch64, - python-arch: aarch64, - python-ver: '3.8', - name: 'ubuntu-aarch64 cmake', - artifact: 'ubuntu-cmake-aarch64.7z', - build_type: 'Debug', - archiver: '7z a', - generators: 'Ninja', - distro: ubuntu20.04 - } - - { - os: ubuntu-latest, - arch: ppc64le, - python-arch: ppc, - python-ver: '3.8', - name: 'ubuntu-ppc64le cmake', - artifact: 'ubuntu-cmake-ppc64le.7z', - build_type: 'Debug', - archiver: '7z a', - generators: 'Ninja', - distro: ubuntu20.04 - } + - { + os: ubuntu-latest, + arch: x64, + python-arch: x64, + python-ver: '3.8', + name: 'ubuntu-x64 cmake shared', + shared: 'yes', + artifact: 'ubuntu-cmake-shared-x64.7z', + archiver: '7z a', + generators: 'Ninja' + } + - { + os: ubuntu-latest, + arch: x86, + python-arch: x86, + python-ver: '3.8', + name: 'ubuntu-x86 cmake shared', + shared: 'yes', + artifact: 'ubuntu-cmake-shared-x86.7z', + archiver: '7z a', + generators: 'Ninja' + } + - { + os: ubuntu-latest, + arch: x64, + python-arch: x64, + python-ver: '3.8', + name: 'ubuntu-x64 cmake static', + shared: 'no', + artifact: 'ubuntu-cmake-static-x64.7z', + archiver: '7z a', + generators: 'Ninja' + } + - { + os: ubuntu-latest, + arch: x86, + python-arch: x86, + python-ver: '3.8', + name: 'ubuntu-x86 cmake static', + shared: 'no', + artifact: 'ubuntu-cmake-static-x86.7z', + archiver: '7z a', + generators: 'Ninja' + } + - { + os: ubuntu-latest, + arch: aarch64, + python-arch: aarch64, + python-ver: '3.8', + name: 'ubuntu-aarch64 cmake', + artifact: 'ubuntu-cmake-aarch64.7z', + archiver: '7z a', + generators: 'Ninja', + distro: ubuntu20.04 + } + - { + os: ubuntu-latest, + arch: ppc64le, + python-arch: ppc, + python-ver: '3.8', + name: 'ubuntu-ppc64le cmake', + artifact: 'ubuntu-cmake-ppc64le.7z', + archiver: '7z a', + generators: 'Ninja', + distro: ubuntu20.04 + } compiler: [ gcc ] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v4 - # - name: '🛠️ Python setup' - # uses: actions/setup-python@v5 - # with: - # python-version: ${{ matrix.config.python-ver }} +# - name: '🛠️ Python setup' +# uses: actions/setup-python@v5 +# with: +# python-version: ${{ matrix.config.python-ver }} - - name: '🚧 Linux x64/x86 build' - if: contains(matrix.config.arch, 'x64') || contains(matrix.config.arch, 'x86') - shell: 'script -q -e -c "bash {0}"' - run: | - if [ ${{ matrix.config.arch }} == 'x64' ]; then - sudo apt install -q -y libcmocka-dev ninja-build - else - export CFLAGS="-m32" LDFLAGS="-m32" LDFLAGS_STATIC="-m32" UNICORN_QEMU_FLAGS="--cpu=i386" - sudo dpkg --add-architecture i386 - sudo apt install -q -y lib32ncurses-dev lib32z1-dev lib32gcc-9-dev libc6-dev-i386 gcc-multilib \ - libcmocka-dev:i386 libcmocka0:i386 libc6:i386 libgcc-s1:i386 ninja-build - fi - mkdir build - mkdir instdir - cmake \ - -S . \ - -B . \ - -DCMAKE_BUILD_TYPE=${{ matrix.config.build_type }} \ - -G "${{ matrix.config.generators }}" \ - -DCMAKE_INSTALL_PREFIX:PATH=instdir \ - -DBUILD_SHARED_LIBS=${{ matrix.config.shared }} - cmake --build . --config ${{ matrix.config.build_type }} - cmake --install . --strip - ctest -VV -C ${{ matrix.config.build_type }} - - - name: '🚧 Linux ppc64le/aarch64 build' - if: contains(matrix.config.arch, 'ppc64le') || contains(matrix.config.arch, 'aarch64') - uses: uraimo/run-on-arch-action@v2 - with: - arch: ${{ matrix.config.arch }} - distro: ${{ matrix.config.distro }} - setup: | - mkdir -p "${PWD}/instdir" - dockerRunArgs: | - --volume "${PWD}/instdir:/instdir" - shell: /bin/sh - install: | - apt-get update -q -y - apt-get install -q -y git cmake build-essential automake libcmocka-dev pkg-config ${{ matrix.compiler }} ninja-build + - name: '🚧 Linux x64/x86 build' + if: contains(matrix.config.arch, 'x64') || contains(matrix.config.arch, 'x86') + shell: 'script -q -e -c "bash {0}"' run: | + if [ ${{ matrix.config.arch }} == 'x64' ]; then + sudo apt install -q -y libcmocka-dev ninja-build + else + export CFLAGS="-m32" LDFLAGS="-m32" LDFLAGS_STATIC="-m32" UNICORN_QEMU_FLAGS="--cpu=i386" + sudo dpkg --add-architecture i386 + sudo apt install -q -y lib32ncurses-dev lib32z1-dev lib32gcc-9-dev libc6-dev-i386 gcc-multilib \ + libcmocka-dev:i386 libcmocka0:i386 libc6:i386 libgcc-s1:i386 ninja-build + fi mkdir build + mkdir instdir cmake \ -S . \ -B . \ - -DCMAKE_BUILD_TYPE=${{ matrix.config.build_type }} \ + -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} \ -G "${{ matrix.config.generators }}" \ - -DCMAKE_INSTALL_PREFIX:PATH=/instdir - cmake --build . --config ${{ matrix.config.build_type }} + -DCMAKE_INSTALL_PREFIX:PATH=instdir \ + -DBUILD_SHARED_LIBS=${{ matrix.config.shared }} + cmake --build . --config ${{ env.BUILD_TYPE }} cmake --install . --strip - ctest -VV -C ${{ matrix.config.build_type }} + ctest -VV -C ${{ env.BUILD_TYPE }} + + - name: '🚧 Linux ppc64le/aarch64 build' + if: contains(matrix.config.arch, 'ppc64le') || contains(matrix.config.arch, 'aarch64') + uses: uraimo/run-on-arch-action@v2 + with: + arch: ${{ matrix.config.arch }} + distro: ${{ matrix.config.distro }} + setup: | + mkdir -p "${PWD}/instdir" + dockerRunArgs: | + --volume "${PWD}/instdir:/instdir" + shell: /bin/sh + install: | + apt-get update -q -y + apt-get install -q -y git cmake build-essential automake libcmocka-dev pkg-config ${{ matrix.compiler }} ninja-build + run: | + mkdir build + cmake \ + -S . \ + -B . \ + -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} \ + -G "${{ matrix.config.generators }}" \ + -DCMAKE_INSTALL_PREFIX:PATH=/instdir + cmake --build . --config ${{ env.BUILD_TYPE }} + cmake --install . --strip + ctest -VV -C ${{ env.BUILD_TYPE }} - - name: '📦 Pack artifact' - if: always() - shell: bash - working-directory: instdir - run: | - ls -laR - ${{ matrix.config.archiver }} ../${{ matrix.config.artifact }} . ../test* + - name: '📦 Pack artifact' + if: always() + shell: bash + working-directory: instdir + run: | + ls -laR + ${{ matrix.config.archiver }} ../${{ matrix.config.artifact }} . ../test* - - name: '📤 Upload artifact' - if: always() - uses: actions/upload-artifact@v4 - with: - path: ./${{ matrix.config.artifact }} - name: ${{ matrix.config.artifact }} \ No newline at end of file + - name: '📤 Upload artifact' + if: always() + uses: actions/upload-artifact@v4 + with: + path: ./${{ matrix.config.artifact }} + name: ${{ matrix.config.artifact }} diff --git a/.github/workflows/build-wheels-publish.yml b/.github/workflows/build-wheels-publish.yml new file mode 100644 index 0000000000..39d860226d --- /dev/null +++ b/.github/workflows/build-wheels-publish.yml @@ -0,0 +1,360 @@ +name: Build wheels with cibuildwheel + +on: + push: + paths-ignore: + - ".gitignore" + - "AUTHORS.TXT" + - "COPYING" + - "COPYING.LGPL2" + - "COPYING_GLIB" + - "CREDITS.TXT" + - "ChangeLog" + - "README.md" + - "docs/**" + pull_request: + +env: + # Enable DEBUG flag if not tag release + UNICORN_DEBUG: ${{ startsWith(github.ref, 'refs/tags') && '0' || '1' }} + +jobs: + # job to be executed for every push - testing purpose + build_wheels_python38_only: + name: Building on ${{ matrix.os }} - ${{ matrix.arch }} ${{ matrix.cibw_build }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + include: + # NOTE: aarch64 builds are super slow due to QEMU emulation. Making this to parallelize and speed up workflow + # i686 - manylinux + - { os: ubuntu-latest, arch: i686, cibw_build: 'cp38-manylinux' } + # i686 - musllinux + - { os: ubuntu-latest, arch: i686, cibw_build: 'cp38-musllinux' } + # x86_64 - manylinux + - { os: ubuntu-latest, arch: x86_64, cibw_build: 'cp38-manylinux' } + # x86_64 - musllinux + - { os: ubuntu-latest, arch: x86_64, cibw_build: 'cp38-musllinux' } + # aarch64 - manylinux + - { os: ubuntu-latest, arch: aarch64, cibw_build: 'cp38-manylinux' } + # aarch64 - musllinux + - { os: ubuntu-latest, arch: aarch64, cibw_build: 'cp38-musllinux' } + - { os: macos-13, arch: x86_64, cibw_build: '' } + - { os: macos-latest, arch: arm64, cibw_build: '' } + - { os: windows-2019, arch: AMD64, cibw_build: '' } + - { os: windows-2019, arch: x86, cibw_build: '' } + + steps: + - uses: actions/checkout@v4 + + - name: '🛠️ Add msbuild to PATH' + if: runner.os == 'Windows' + uses: microsoft/setup-msbuild@v2 + with: + vs-version: '16.5' + + - name: '🛠️ Win build dependencies' + if: runner.os == 'Windows' + shell: bash + run: | + choco install ninja cmake + + - name: '🛠️ macOS dependencies' + if: runner.os == 'macOS' + run: | + brew install ninja + + # https://cibuildwheel.pypa.io/en/stable/faq/#macos-building-cpython-38-wheels-on-arm64 + - uses: actions/setup-python@v5 + if: runner.os == 'macOS' && runner.arch == 'ARM64' + with: + python-version: 3.8 + + - name: '🛠️ Win MSVC 32 dev cmd setup' + if: runner.os == 'Windows' && matrix.arch == 'x86' + uses: ilammy/msvc-dev-cmd@v1 + with: + arch: x86 + + - name: '🛠️ Win MSVC 64 dev cmd setup' + if: runner.os == 'Windows' && matrix.arch == 'AMD64' + uses: ilammy/msvc-dev-cmd@v1 + with: + arch: x64 + + - name: '🛠️ Set up QEMU' + if: runner.os == 'Linux' + uses: docker/setup-qemu-action@v3 + + - name: '🚧 cibuildwheel run - Linux' + if: matrix.os == 'ubuntu-latest' + uses: pypa/cibuildwheel@v2.21.3 + env: + CIBW_BUILD_FRONTEND: build + CIBW_BUILD: ${{ matrix.cibw_build }}* + CIBW_ARCHS: ${{ matrix.arch }} + CIBW_ENVIRONMENT: DEBUG=${{ env.UNICORN_DEBUG }} + CIBW_ENVIRONMENT_PASS_LINUX: DEBUG + CIBW_TEST_REQUIRES: pytest + CIBW_TEST_COMMAND: pytest {package}/tests + with: + package-dir: bindings/python + output-dir: wheelhouse + + - name: '🚧 cibuildwheel run - Windows' + if: matrix.os == 'windows-2019' + uses: pypa/cibuildwheel@v2.21.3 + env: + CIBW_BUILD_FRONTEND: build + CIBW_BUILD: 'cp38*' + CIBW_ARCHS: ${{ matrix.arch }} + CIBW_ENVIRONMENT: DEBUG=${{ env.UNICORN_DEBUG }} + CIBW_TEST_REQUIRES: pytest + CIBW_TEST_COMMAND: pytest {package}/tests + with: + package-dir: bindings/python + output-dir: wheelhouse + + - name: '🚧 cibuildwheel run - MacOS x86_84' + if: matrix.os == 'macos-13' + uses: pypa/cibuildwheel@v2.21.3 + env: + CIBW_BUILD_FRONTEND: build + CIBW_BUILD: 'cp38*' + CIBW_ENVIRONMENT: SYSTEM_VERSION_COMPAT=0 DEBUG=${{ env.UNICORN_DEBUG }} + CIBW_TEST_REQUIRES: pytest + CIBW_TEST_COMMAND: pytest {package}/tests + with: + package-dir: bindings/python + output-dir: wheelhouse + + - name: '🚧 cibuildwheel run - MacOS arm64' + if: matrix.os == 'macos-latest' + uses: pypa/cibuildwheel@v2.21.3 + env: + CIBW_BUILD_FRONTEND: build + CIBW_BUILD: 'cp38*' + CIBW_ENVIRONMENT: DEBUG=${{ env.UNICORN_DEBUG }} + CIBW_TEST_REQUIRES: pytest + CIBW_TEST_COMMAND: pytest {package}/tests + # https://github.com/pypa/cibuildwheel/pull/1169 + CIBW_TEST_SKIP: "cp38-macosx_*:arm64" + with: + package-dir: bindings/python + output-dir: wheelhouse + + # we re-tag cp38 wheel (just an old one) with py2 tag. Hacky but it works... + - name: '🚧 Python 2.7 wheels re-tagging - Windows' + if: matrix.os == 'windows-2019' + run: | + python -m pip install -U pip wheel && Get-ChildItem -Path wheelhouse/ -Filter *cp38*.whl | Foreach-Object { + python -m wheel tags --python-tag='py2' --abi-tag=none $_.FullName + } + - name: '🚧 Python 2.7 wheels re-tagging - Non-Windows' + if: matrix.os != 'windows-2019' + env: + PIP_BREAK_SYSTEM_PACKAGES: 1 + run: | + python3 -m pip install -U pip wheel && python3 -m wheel tags --python-tag='py2' --abi-tag=none wheelhouse/*cp38*.whl + + - uses: actions/upload-artifact@v4 + with: + name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }} + path: ./wheelhouse/*.whl + + # Job to be executed to build all wheels for all platforms/architectures/python versions only for tag release + build_wheels_all_versions: + name: Building on ${{ matrix.os }} - ${{ matrix.arch }} ${{ matrix.cibw_build }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + include: + # NOTE: aarch64 builds are super slow due to QEMU emulation. Making this to parallelize and speed up workflow + # i686 - manylinux + - { os: ubuntu-latest, arch: i686, cibw_build: 'cp37-manylinux' } + - { os: ubuntu-latest, arch: i686, cibw_build: 'cp39-manylinux' } + - { os: ubuntu-latest, arch: i686, cibw_build: 'cp310-manylinux' } + - { os: ubuntu-latest, arch: i686, cibw_build: 'cp311-manylinux' } + - { os: ubuntu-latest, arch: i686, cibw_build: 'cp312-manylinux' } + - { os: ubuntu-latest, arch: i686, cibw_build: 'cp313-manylinux' } + # i686 - musllinux + - { os: ubuntu-latest, arch: i686, cibw_build: 'cp37-musllinux' } + - { os: ubuntu-latest, arch: i686, cibw_build: 'cp39-musllinux' } + - { os: ubuntu-latest, arch: i686, cibw_build: 'cp310-musllinux' } + - { os: ubuntu-latest, arch: i686, cibw_build: 'cp311-musllinux' } + - { os: ubuntu-latest, arch: i686, cibw_build: 'cp312-musllinux' } + - { os: ubuntu-latest, arch: i686, cibw_build: 'cp313-musllinux' } + # x86_64 - manylinux + - { os: ubuntu-latest, arch: x86_64, cibw_build: 'cp37-manylinux' } + - { os: ubuntu-latest, arch: x86_64, cibw_build: 'cp39-manylinux' } + - { os: ubuntu-latest, arch: x86_64, cibw_build: 'cp310-manylinux' } + - { os: ubuntu-latest, arch: x86_64, cibw_build: 'cp311-manylinux' } + - { os: ubuntu-latest, arch: x86_64, cibw_build: 'cp312-manylinux' } + - { os: ubuntu-latest, arch: x86_64, cibw_build: 'cp313-manylinux' } + # x86_64 - musllinux + - { os: ubuntu-latest, arch: x86_64, cibw_build: 'cp37-musllinux' } + - { os: ubuntu-latest, arch: x86_64, cibw_build: 'cp39-musllinux' } + - { os: ubuntu-latest, arch: x86_64, cibw_build: 'cp310-musllinux' } + - { os: ubuntu-latest, arch: x86_64, cibw_build: 'cp311-musllinux' } + - { os: ubuntu-latest, arch: x86_64, cibw_build: 'cp312-musllinux' } + - { os: ubuntu-latest, arch: x86_64, cibw_build: 'cp313-musllinux' } + # aarch64 - manylinux + - { os: ubuntu-latest, arch: aarch64, cibw_build: 'cp37-manylinux' } + - { os: ubuntu-latest, arch: aarch64, cibw_build: 'cp39-manylinux' } + - { os: ubuntu-latest, arch: aarch64, cibw_build: 'cp310-manylinux' } + - { os: ubuntu-latest, arch: aarch64, cibw_build: 'cp311-manylinux' } + - { os: ubuntu-latest, arch: aarch64, cibw_build: 'cp312-manylinux' } + - { os: ubuntu-latest, arch: aarch64, cibw_build: 'cp313-manylinux' } + # aarch64 - musllinux + - { os: ubuntu-latest, arch: aarch64, cibw_build: 'cp37-musllinux' } + - { os: ubuntu-latest, arch: aarch64, cibw_build: 'cp39-musllinux' } + - { os: ubuntu-latest, arch: aarch64, cibw_build: 'cp310-musllinux' } + - { os: ubuntu-latest, arch: aarch64, cibw_build: 'cp311-musllinux' } + - { os: ubuntu-latest, arch: aarch64, cibw_build: 'cp312-musllinux' } + - { os: ubuntu-latest, arch: aarch64, cibw_build: 'cp313-musllinux' } + - { os: macos-13, arch: x86_64, cibw_build: '' } + - { os: macos-latest, arch: arm64, cibw_build: '' } + - { os: windows-2019, arch: AMD64, cibw_build: '' } + - { os: windows-2019, arch: x86, cibw_build: '' } + if: startsWith(github.ref, 'refs/tags') + steps: + - uses: actions/checkout@v4 + + - name: '🛠️ Add msbuild to PATH' + if: runner.os == 'Windows' + uses: microsoft/setup-msbuild@v2 + with: + vs-version: '16.5' + + - name: '🛠️ Win build dependencies' + if: runner.os == 'Windows' + shell: bash + run: | + choco install ninja cmake + + - name: '🛠️ macOS dependencies' + if: runner.os == 'macOS' + run: | + brew install ninja + + - name: '🛠️ Win MSVC 32 dev cmd setup' + if: runner.os == 'Windows' && matrix.arch == 'x86' + uses: ilammy/msvc-dev-cmd@v1 + with: + arch: x86 + + - name: '🛠️ Win MSVC 64 dev cmd setup' + if: runner.os == 'Windows' && matrix.arch == 'AMD64' + uses: ilammy/msvc-dev-cmd@v1 + with: + arch: x64 + + - name: '🛠️ Set up QEMU' + if: runner.os == 'Linux' + uses: docker/setup-qemu-action@v3 + + - name: '🚧 cibuildwheel run - Linux' + if: matrix.os == 'ubuntu-latest' + uses: pypa/cibuildwheel@v2.21.3 + env: + CIBW_BUILD_FRONTEND: build + CIBW_BUILD: ${{ matrix.cibw_build }}* + CIBW_ARCHS: ${{ matrix.arch }} + CIBW_ENVIRONMENT: DEBUG=${{ env.UNICORN_DEBUG }} + CIBW_ENVIRONMENT_PASS_LINUX: DEBUG + CIBW_TEST_REQUIRES: pytest + CIBW_TEST_COMMAND: pytest {package}/tests + with: + package-dir: bindings/python + output-dir: wheelhouse + + - name: '🚧 cibuildwheel run - Windows' + if: matrix.os == 'windows-2019' + uses: pypa/cibuildwheel@v2.21.3 + env: + CIBW_BUILD_FRONTEND: build + CIBW_SKIP: '*36* *38*' + CIBW_BUILD: 'cp*' + CIBW_ENVIRONMENT: DEBUG=${{ env.UNICORN_DEBUG }} + CIBW_ARCHS: ${{ matrix.arch }} + CIBW_TEST_REQUIRES: pytest + CIBW_TEST_COMMAND: pytest {package}/tests + with: + package-dir: bindings/python + output-dir: wheelhouse + + - name: '🚧 cibuildwheel run - MacOS x86_84' + if: matrix.os == 'macos-13' + uses: pypa/cibuildwheel@v2.21.3 + env: + CIBW_BUILD_FRONTEND: build + CIBW_SKIP: '*36* *38*' + CIBW_BUILD: 'cp*' + CIBW_ENVIRONMENT: DEBUG=${{ env.UNICORN_DEBUG }} + CIBW_TEST_REQUIRES: pytest + CIBW_TEST_COMMAND: pytest {package}/tests + with: + package-dir: bindings/python + output-dir: wheelhouse + + - name: '🚧 cibuildwheel run - MacOS arm64' + if: matrix.os == 'macos-latest' + uses: pypa/cibuildwheel@v2.21.3 + env: + CIBW_BUILD_FRONTEND: build + CIBW_SKIP: '*36* *37* *38*' + CIBW_BUILD: 'cp*' + CIBW_ENVIRONMENT: DEBUG=${{ env.UNICORN_DEBUG }} + CIBW_TEST_REQUIRES: pytest + CIBW_TEST_COMMAND: pytest {package}/tests + with: + package-dir: bindings/python + output-dir: wheelhouse + + - uses: actions/upload-artifact@v4 + with: + name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }} + path: ./wheelhouse/*.whl + + make_sdist: + name: Make SDist + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 # Optional, use if you use setuptools_scm + submodules: true # Optional, use if you have submodules + + - name: Build SDist + run: | + cd bindings/python + python3 -m pip install -U pip build + python3 -m build --sdist + + - uses: actions/upload-artifact@v4 + with: + path: bindings/python/dist/*.tar.gz + + publish: + needs: [ build_wheels_python38_only, build_wheels_all_versions, make_sdist ] + environment: pypi + permissions: + id-token: write + runs-on: ubuntu-latest + if: startsWith(github.ref, 'refs/tags') + steps: + - uses: actions/download-artifact@v4 + with: + merge-multiple: true + path: dist + + - name: Show downloaded artifacts + run: ls -laR dist + + - name: '📦 Publish distribution to PyPI' + uses: pypa/gh-action-pypi-publish@release/v1 + with: + user: __token__ + password: ${{ secrets.pypi_pass }} diff --git a/.gitignore b/.gitignore index 98fa46f04e..d62eca0468 100644 --- a/.gitignore +++ b/.gitignore @@ -90,4 +90,10 @@ packages/ cmocka/ zig-cache/ zig-out/ -.cache \ No newline at end of file +.cache + +################## +## PyCharm Project +################## + +.idea/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 3fcde11770..2c389bc34f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,6 +30,24 @@ if(APPLE AND NOT CMAKE_C_COMPILER) set(CMAKE_C_COMPILER "/usr/bin/cc") endif() +# Source: https://github.com/capstone-engine/capstone/blob/next/CMakeLists.txt +# If building for OSX it's best to allow CMake to handle building both architectures +if(APPLE) + # The cibuildwheel on Github Actions sets this env variable + # with the architecture flags it wants to build for. + if(DEFINED ENV{ARCHFLAGS}) + if("$ENV{ARCHFLAGS}" STREQUAL "-arch arm64 -arch x86_64") + set(CMAKE_OSX_ARCHITECTURES "x86_64;arm64") + elseif("$ENV{ARCHFLAGS}" STREQUAL "-arch arm64") + set(CMAKE_OSX_ARCHITECTURES "arm64") + elseif("$ENV{ARCHFLAGS}" STREQUAL "-arch x86_64") + set(CMAKE_OSX_ARCHITECTURES "x86_64") + endif() + else() + set(CMAKE_OSX_ARCHITECTURES "x86_64;arm64") + endif() +endif() + # Detect if unicorn is compiled as the top-level project set(PROJECT_IS_TOP_LEVEL OFF) if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) diff --git a/bindings/python/MANIFEST.in b/bindings/python/MANIFEST.in index a98ea526a4..8d1b49e2b9 100644 --- a/bindings/python/MANIFEST.in +++ b/bindings/python/MANIFEST.in @@ -1,4 +1,9 @@ recursive-include src * recursive-include prebuilt * -include LICENSE.TXT -include README.TXT +graft unicorn/lib +graft unicorn/include +global-include *.a +global-include *.so.2 +global-include *.*lib +global-include *.dll +global-include *.h diff --git a/bindings/python/README.md b/bindings/python/README.md index d44f6dd066..21c84432d7 100644 --- a/bindings/python/README.md +++ b/bindings/python/README.md @@ -1,3 +1,21 @@ +# Unicorn + +Unicorn is a lightweight, multi-platform, multi-architecture CPU emulator framework +based on [QEMU](http://qemu.org). + +Unicorn offers some unparalleled features: + +- Multi-architecture: ARM, ARM64 (ARMv8), M68K, MIPS, PowerPC, RISCV, SPARC, S390X, TriCore and X86 (16, 32, 64-bit) +- Clean/simple/lightweight/intuitive architecture-neutral API +- Implemented in pure C language, with bindings for Crystal, Clojure, Visual Basic, Perl, Rust, Ruby, Python, Java, .NET, Go, Delphi/Free Pascal, Haskell, Pharo, and Lua. +- Native support for Windows & *nix (with Mac OSX, Linux, *BSD & Solaris confirmed) +- High performance via Just-In-Time compilation +- Support for fine-grained instrumentation at various levels +- Thread-safety by design +- Distributed under free software license GPLv2 + +Further information is available at http://www.unicorn-engine.org + # Python Bindings for Unicorn Originally written by Nguyen Anh Quynh, polished and redesigned by elicn, maintained by all community contributors. diff --git a/bindings/python/build_wheel.sh b/bindings/python/build_wheel.sh deleted file mode 100755 index 70ff32018d..0000000000 --- a/bindings/python/build_wheel.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash -set -e -x - -cd bindings/python - -# Compile wheels -python3.7 setup.py bdist_wheel $@ -cd dist - -# We can't repair an aarch64 wheel on x64 hosts -# https://github.com/pypa/auditwheel/issues/244 -if [[ ! "$*" =~ "aarch64" ]];then - auditwheel repair *.whl - mv -f wheelhouse/*.whl . -fi diff --git a/bindings/python/musl_wheel.sh b/bindings/python/musl_wheel.sh deleted file mode 100644 index 5bc5ff3a94..0000000000 --- a/bindings/python/musl_wheel.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/sh - -# TODO: use cibuildwheel -apk update -apk add gcc make cmake pkgconfig linux-headers git musl-dev patchelf - -python3 -m pip install -U pip setuptools auditwheel - -cd bindings/python && python3 setup.py bdist_wheel && auditwheel repair dist/*.whl && mv -f wheelhouse/*.whl ./dist/ \ No newline at end of file diff --git a/bindings/python/pyproject.toml b/bindings/python/pyproject.toml new file mode 100644 index 0000000000..6857ca1cbf --- /dev/null +++ b/bindings/python/pyproject.toml @@ -0,0 +1,40 @@ +[build-system] +requires = ["setuptools", "build", "wheel"] +build-backend = "setuptools.build_meta" + +[project] +name = "unicorn" +version = "2.1.1" +requires-python = ">= 2.7, != 3.0.*, != 3.1.*, != 3.2.*, != 3.3.*, != 3.4.*, != 3.5.*, != 3.6.*" +authors = [ + { name = "Nguyen Anh Quynh", email = "quynh@gmail.com" }, +] +description = "Unicorn CPU emulator engine" +readme = "README.md" +keywords = ["emulation", "qemu", "unicorn"] +classifiers = [ + 'License :: OSI Approved :: BSD License', + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3.10', + 'Programming Language :: Python :: 3.11', + 'Programming Language :: Python :: 3.12', + 'Programming Language :: Python :: 3.13', +] + +[project.urls] +Homepage = "http://www.unicorn-engine.org" +Repository = "https://github.com/unicorn-engine/unicorn" +"Bug Tracker" = "https://github.com/unicorn-engine/unicorn/issues" +Changelog = "https://github.com/unicorn-engine/unicorn/blob/master/ChangeLog" + +[project.optional-dependencies] +test = [ + "pytest", + "pytest-cov", +] + +[tool.setuptools.packages.find] +include = ["unicorn*"] diff --git a/bindings/python/sample_all.sh b/bindings/python/sample_all.sh deleted file mode 100755 index 378c139da4..0000000000 --- a/bindings/python/sample_all.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/sh - -python3 ./sample_arm.py -echo "==========================" -python3 ./sample_armeb.py -echo "==========================" -python3 ./sample_arm64.py -echo "==========================" -python3 ./sample_arm64eb.py -echo "==========================" -python3 ./sample_m68k.py -echo "==========================" -python3 ./sample_mips.py -echo "==========================" -python3 ./sample_ppc.py -echo "==========================" -python3 ./sample_riscv.py -echo "==========================" -python3 ./sample_s390x.py -echo "==========================" -python3 ./sample_sparc.py -echo "==========================" -python3 ./sample_tricore.py -echo "==========================" -python3 ./sample_x86.py -echo "==========================" -python3 ./shellcode.py -echo "==========================" -python3 ./sample_ctl.py -echo "==========================" -python3 ./sample_network_auditing.py diff --git a/bindings/python/setup.cfg b/bindings/python/setup.cfg deleted file mode 100644 index 3c6e79cf31..0000000000 --- a/bindings/python/setup.cfg +++ /dev/null @@ -1,2 +0,0 @@ -[bdist_wheel] -universal=1 diff --git a/bindings/python/setup.py b/bindings/python/setup.py index 85f9036481..270f215232 100755 --- a/bindings/python/setup.py +++ b/bindings/python/setup.py @@ -1,29 +1,19 @@ -#!/usr/bin/env python # Python binding for Unicorn engine. Nguyen Anh Quynh -from __future__ import print_function import glob import logging import os -import subprocess +import platform import shutil +import subprocess import sys -import platform -import setuptools - from setuptools import setup -from sysconfig import get_platform from setuptools.command.build import build from setuptools.command.sdist import sdist from setuptools.command.bdist_egg import bdist_egg log = logging.getLogger(__name__) -SYSTEM = sys.platform - -# sys.maxint is 2**31 - 1 on both 32 and 64 bit mingw -IS_64BITS = platform.architecture()[0] == '64bit' - # are we building from the repository or from a source distribution? ROOT_DIR = os.path.dirname(os.path.realpath(__file__)) LIBS_DIR = os.path.join(ROOT_DIR, 'unicorn', 'lib') @@ -32,29 +22,28 @@ UC_DIR = SRC_DIR if os.path.exists(SRC_DIR) else os.path.join(ROOT_DIR, '../..') BUILD_DIR = os.path.join(UC_DIR, 'build_python') -VERSION = "2.1.1" - -if SYSTEM == 'darwin': +if sys.platform == 'darwin': LIBRARY_FILE = "libunicorn.2.dylib" STATIC_LIBRARY_FILE = "libunicorn.a" -elif SYSTEM in ('win32', 'cygwin'): +elif sys.platform in ('win32', 'cygwin'): LIBRARY_FILE = "unicorn.dll" STATIC_LIBRARY_FILE = "unicorn.lib" else: LIBRARY_FILE = "libunicorn.so.2" STATIC_LIBRARY_FILE = "libunicorn.a" + def clean_bins(): shutil.rmtree(LIBS_DIR, ignore_errors=True) shutil.rmtree(HEADERS_DIR, ignore_errors=True) + def copy_sources(): - """Copy the C sources into the source directory. + """ + Copy the C sources into the source directory. This rearranges the source files under the python distribution directory. """ - src = [] - shutil.rmtree(SRC_DIR, ignore_errors=True) os.mkdir(SRC_DIR) @@ -66,17 +55,16 @@ def copy_sources(): shutil.copytree(os.path.join(ROOT_DIR, '../../samples'), os.path.join(SRC_DIR, 'samples/')) shutil.copytree(os.path.join(ROOT_DIR, '../../glib_compat'), os.path.join(SRC_DIR, 'glib_compat/')) shutil.copytree(os.path.join(ROOT_DIR, '../../cmake'), os.path.join(SRC_DIR, 'cmake/')) - + try: - # remove site-specific configuration file - # might not exist + # remove site-specific configuration file, might not exist os.remove(os.path.join(SRC_DIR, 'qemu/config-host.mak')) except OSError: pass + src = [] src.extend(glob.glob(os.path.join(ROOT_DIR, "../../*.[ch]"))) src.extend(glob.glob(os.path.join(ROOT_DIR, "../../*.mk"))) - src.extend(glob.glob(os.path.join(ROOT_DIR, "../../LICENSE*"))) src.extend(glob.glob(os.path.join(ROOT_DIR, "../../README.md"))) src.extend(glob.glob(os.path.join(ROOT_DIR, "../../*.TXT"))) @@ -87,6 +75,7 @@ def copy_sources(): log.info("%s -> %s" % (filename, outpath)) shutil.copy(filename, outpath) + def build_libraries(): """ Prepare the unicorn directory for a binary distribution or installation. @@ -94,7 +83,6 @@ def build_libraries(): Will use a src/ dir if one exists in the current directory, otherwise assumes it's in the repo """ - cwd = os.getcwd() clean_bins() os.mkdir(HEADERS_DIR) os.mkdir(LIBS_DIR) @@ -102,158 +90,84 @@ def build_libraries(): # copy public headers shutil.copytree(os.path.join(UC_DIR, 'include', 'unicorn'), os.path.join(HEADERS_DIR, 'unicorn')) - # check if a prebuilt library exists - # if so, use it instead of building + # check if a prebuilt library exists and if so, use it instead of building if os.path.exists(os.path.join(ROOT_DIR, 'prebuilt', LIBRARY_FILE)): shutil.copy(os.path.join(ROOT_DIR, 'prebuilt', LIBRARY_FILE), LIBS_DIR) if STATIC_LIBRARY_FILE is not None and os.path.exists(os.path.join(ROOT_DIR, 'prebuilt', STATIC_LIBRARY_FILE)): shutil.copy(os.path.join(ROOT_DIR, 'prebuilt', STATIC_LIBRARY_FILE), LIBS_DIR) return - # otherwise, build!! - os.chdir(UC_DIR) + # otherwise, build + if not os.path.exists(BUILD_DIR): + os.mkdir(BUILD_DIR) - try: - subprocess.check_call(['msbuild', '/help']) - except: - has_msbuild = False - else: - has_msbuild = True + has_msbuild = shutil.which('msbuild') is not None + conf = 'Debug' if int(os.getenv('DEBUG', 0)) else 'Release' - if has_msbuild and SYSTEM == 'win32': + if has_msbuild and sys.platform == 'win32': plat = 'Win32' if platform.architecture()[0] == '32bit' else 'x64' - conf = 'Debug' if os.getenv('DEBUG', '') else 'Release' - if not os.path.exists(BUILD_DIR): - os.mkdir(BUILD_DIR) - - subprocess.check_call(['cmake', '-B', BUILD_DIR, '-G', "Visual Studio 16 2019", "-A", plat, "-DCMAKE_BUILD_TYPE=" + conf]) - subprocess.check_call(['msbuild', 'unicorn.sln', '-m', '-p:Platform=' + plat, '-p:Configuration=' + conf], cwd=BUILD_DIR) + + subprocess.check_call(['cmake', '-B', BUILD_DIR, '-G', "Visual Studio 16 2019", "-A", plat, + "-DCMAKE_BUILD_TYPE=" + conf], cwd=UC_DIR) + subprocess.check_call(['msbuild', 'unicorn.sln', '-m', '-p:Platform=' + plat, '-p:Configuration=' + conf], + cwd=BUILD_DIR) obj_dir = os.path.join(BUILD_DIR, conf) shutil.copy(os.path.join(obj_dir, LIBRARY_FILE), LIBS_DIR) shutil.copy(os.path.join(BUILD_DIR, STATIC_LIBRARY_FILE), LIBS_DIR) else: - # platform description refs at https://docs.python.org/2/library/sys.html#sys.platform - if not os.path.exists(BUILD_DIR): - os.mkdir(BUILD_DIR) - conf = 'Debug' if os.getenv('DEBUG', '') else 'Release' - cmake_args = ["cmake", '-B', BUILD_DIR, '-S', UC_DIR, "-DCMAKE_BUILD_TYPE=" + conf] - if os.getenv("TRACE", ""): + if os.getenv("TRACE"): cmake_args += ["-DUNICORN_TRACER=on"] - subprocess.check_call(cmake_args) - os.chdir(BUILD_DIR) + subprocess.check_call(cmake_args, cwd=UC_DIR) threads = os.getenv("THREADS", "4") - subprocess.check_call(["cmake", "--build", ".", "-j" + threads]) - - shutil.copy(LIBRARY_FILE, LIBS_DIR) - shutil.copy(STATIC_LIBRARY_FILE, LIBS_DIR) + subprocess.check_call(["cmake", "--build", ".", "-j" + threads], cwd=BUILD_DIR) - os.chdir(cwd) + shutil.copy(os.path.join(BUILD_DIR, LIBRARY_FILE), LIBS_DIR) + shutil.copy(os.path.join(BUILD_DIR, STATIC_LIBRARY_FILE), LIBS_DIR) -class custom_sdist(sdist): +class CustomSDist(sdist): def run(self): clean_bins() copy_sources() - return sdist.run(self) + return super().run() + -class custom_build(build): +class CustomBuild(build): def run(self): if 'LIBUNICORN_PATH' in os.environ: log.info("Skipping building C extensions since LIBUNICORN_PATH is set") else: log.info("Building C extensions") build_libraries() - return build.run(self) + return super().run() -class custom_bdist_egg(bdist_egg): + +class CustomBDistEgg(bdist_egg): def run(self): self.run_command('build') - return bdist_egg.run(self) - -def dummy_src(): - return [] - -cmdclass = {} -cmdclass['build'] = custom_build -cmdclass['sdist'] = custom_sdist -cmdclass['bdist_egg'] = custom_bdist_egg - -if 'bdist_wheel' in sys.argv and '--plat-name' not in sys.argv: - idx = sys.argv.index('bdist_wheel') + 1 - sys.argv.insert(idx, '--plat-name') - name = get_platform() - if 'linux' in name: - # linux_* platform tags are disallowed because the python ecosystem is fubar - # linux builds should be built in the centos 5 vm for maximum compatibility - # see https://github.com/pypa/manylinux - # see also https://github.com/angr/angr-dev/blob/master/bdist.sh - sys.argv.insert(idx + 1, 'manylinux1_' + platform.machine()) - elif 'mingw' in name: - if IS_64BITS: - sys.argv.insert(idx + 1, 'win_amd64') - else: - sys.argv.insert(idx + 1, 'win32') - else: - # https://www.python.org/dev/peps/pep-0425/ - sys.argv.insert(idx + 1, name.replace('.', '_').replace('-', '_')) + return super().run() + + +cmdclass = {'build': CustomBuild, 'sdist': CustomSDist, 'bdist_egg': CustomBDistEgg} try: from setuptools.command.develop import develop - class custom_develop(develop): + + + class CustomDevelop(develop): def run(self): log.info("Building C extensions") build_libraries() - return develop.run(self) + return super().run() - cmdclass['develop'] = custom_develop + + cmdclass['develop'] = CustomDevelop except ImportError: print("Proper 'develop' support unavailable.") -def join_all(src, files): - return tuple(os.path.join(src, f) for f in files) - -long_desc = ''' -Unicorn is a lightweight, multi-platform, multi-architecture CPU emulator framework -based on [QEMU](http://qemu.org). - -Unicorn offers some unparalleled features: - -- Multi-architecture: ARM, ARM64 (ARMv8), M68K, MIPS, PowerPC, RISCV, SPARC, S390X, TriCore and X86 (16, 32, 64-bit) -- Clean/simple/lightweight/intuitive architecture-neutral API -- Implemented in pure C language, with bindings for Crystal, Clojure, Visual Basic, Perl, Rust, Ruby, Python, Java, .NET, Go, Delphi/Free Pascal, Haskell, Pharo, and Lua. -- Native support for Windows & *nix (with Mac OSX, Linux, *BSD & Solaris confirmed) -- High performance via Just-In-Time compilation -- Support for fine-grained instrumentation at various levels -- Thread-safety by design -- Distributed under free software license GPLv2 - -Further information is available at http://www.unicorn-engine.org -''' - setup( - provides=['unicorn'], - packages=setuptools.find_packages(include=["unicorn", "unicorn.*"]), - name='unicorn', - version=VERSION, - author='Nguyen Anh Quynh', - author_email='aquynh@gmail.com', - description='Unicorn CPU emulator engine', - long_description=long_desc, - long_description_content_type="text/markdown", - url='http://www.unicorn-engine.org', - classifiers=[ - 'License :: OSI Approved :: BSD License', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 3', - ], - requires=['ctypes'], cmdclass=cmdclass, - zip_safe=False, - include_package_data=True, - is_pure=False, - package_data={ - 'unicorn': ['unicorn/py.typed', 'lib/*', 'include/unicorn/*'] - } + has_ext_modules=lambda: True, # It's not a Pure Python wheel ) diff --git a/bindings/python/sample_arm.py b/bindings/python/tests/test_arm.py similarity index 89% rename from bindings/python/sample_arm.py rename to bindings/python/tests/test_arm.py index b906d53875..a0f7ff5824 100755 --- a/bindings/python/sample_arm.py +++ b/bindings/python/tests/test_arm.py @@ -6,22 +6,21 @@ from unicorn import * from unicorn.arm_const import * - # code to be emulated -ARM_CODE = b"\x37\x00\xa0\xe3\x03\x10\x42\xe0" # mov r0, #0x37; sub r1, r2, r3 -THUMB_CODE = b"\x83\xb0" # sub sp, #0xc +ARM_CODE = b"\x37\x00\xa0\xe3\x03\x10\x42\xe0" # mov r0, #0x37; sub r1, r2, r3 +THUMB_CODE = b"\x83\xb0" # sub sp, #0xc # memory address where emulation starts -ADDRESS = 0x10000 +ADDRESS = 0x10000 # callback for tracing basic blocks def hook_block(uc, address, size, user_data): - print(">>> Tracing basic block at 0x%x, block size = 0x%x" %(address, size)) + print(">>> Tracing basic block at 0x%x, block size = 0x%x" % (address, size)) # callback for tracing instructions def hook_code(uc, address, size, user_data): - print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" %(address, size)) + print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" % (address, size)) # Test ARM @@ -41,8 +40,8 @@ def test_arm(): mu.reg_write(UC_ARM_REG_R0, 0x1234) mu.reg_write(UC_ARM_REG_R2, 0x6789) mu.reg_write(UC_ARM_REG_R3, 0x3333) - mu.reg_write(UC_ARM_REG_APSR, 0xFFFFFFFF) #All application flags turned on - + mu.reg_write(UC_ARM_REG_APSR, 0xFFFFFFFF) # All application flags turned on + # tracing all basic blocks with customized callback mu.hook_add(UC_HOOK_BLOCK, hook_block) @@ -57,8 +56,8 @@ def test_arm(): r0 = mu.reg_read(UC_ARM_REG_R0) r1 = mu.reg_read(UC_ARM_REG_R1) - print(">>> R0 = 0x%x" %r0) - print(">>> R1 = 0x%x" %r1) + print(">>> R0 = 0x%x" % r0) + print(">>> R1 = 0x%x" % r1) except UcError as e: print("ERROR: %s" % e) @@ -93,11 +92,12 @@ def test_thumb(): print(">>> Emulation done. Below is the CPU context") sp = mu.reg_read(UC_ARM_REG_SP) - print(">>> SP = 0x%x" %sp) + print(">>> SP = 0x%x" % sp) except UcError as e: print("ERROR: %s" % e) + def test_read_sctlr(): print("Read SCTLR") try: @@ -118,6 +118,7 @@ def test_read_sctlr(): except UcError as e: print("ERROR: %s" % e) + if __name__ == '__main__': test_arm() print("=" * 26) diff --git a/bindings/python/sample_arm64.py b/bindings/python/tests/test_arm64.py similarity index 92% rename from bindings/python/sample_arm64.py rename to bindings/python/tests/test_arm64.py index 0ebddf9139..d615f894a9 100755 --- a/bindings/python/sample_arm64.py +++ b/bindings/python/tests/test_arm64.py @@ -6,25 +6,24 @@ from unicorn import * from unicorn.arm64_const import * - # code to be emulated -ARM64_CODE = b"\xab\x05\x00\xb8\xaf\x05\x40\x38" # str x11, [x13]; ldrb x15, [x13] +ARM64_CODE = b"\xab\x05\x00\xb8\xaf\x05\x40\x38" # str x11, [x13]; ldrb x15, [x13] # MSR code -ARM64_MRS_CODE = b"\x62\xd0\x3b\xd5" # mrs x2, tpidrro_el0 +ARM64_MRS_CODE = b"\x62\xd0\x3b\xd5" # mrs x2, tpidrro_el0 # memory address where emulation starts -ADDRESS = 0x10000 +ADDRESS = 0x10000 # callback for tracing basic blocks def hook_block(uc, address, size, user_data): - print(">>> Tracing basic block at 0x%x, block size = 0x%x" %(address, size)) + print(">>> Tracing basic block at 0x%x, block size = 0x%x" % (address, size)) # callback for tracing instructions def hook_code(uc, address, size, user_data): - print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" %(address, size)) + print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" % (address, size)) # Test ARM64 @@ -61,7 +60,7 @@ def test_arm64(): x11 = mu.reg_read(UC_ARM64_REG_X11) x13 = mu.reg_read(UC_ARM64_REG_X13) x15 = mu.reg_read(UC_ARM64_REG_X15) - print(">>> X15 = 0x%x" %x15) + print(">>> X15 = 0x%x" % x15) except UcError as e: print("ERROR: %s" % e) @@ -85,6 +84,7 @@ def test_arm64_read_sctlr(): except UcError as e: print("ERROR: %s" % e) + def test_arm64_hook_mrs(): def _hook_mrs(uc, reg, cp_reg, _): print(f">>> Hook MRS instruction: reg = 0x{reg:x}(UC_ARM64_REG_X2) cp_reg = {cp_reg}") @@ -116,6 +116,7 @@ def _hook_mrs(uc, reg, cp_reg, _): except UcError as e: print("ERROR: %s" % e) + if __name__ == '__main__': test_arm64() print("=" * 26) diff --git a/bindings/python/sample_arm64eb.py b/bindings/python/tests/test_arm64eb.py similarity index 91% rename from bindings/python/sample_arm64eb.py rename to bindings/python/tests/test_arm64eb.py index 4d96f46de7..2a8b87ae7c 100755 --- a/bindings/python/sample_arm64eb.py +++ b/bindings/python/tests/test_arm64eb.py @@ -7,22 +7,21 @@ from unicorn import * from unicorn.arm64_const import * - # code to be emulated -ARM64_CODE = b"\xab\x05\x00\xb8\xaf\x05\x40\x38" # str x11, [x13]; ldrb x15, [x13] +ARM64_CODE = b"\xab\x05\x00\xb8\xaf\x05\x40\x38" # str x11, [x13]; ldrb x15, [x13] # memory address where emulation starts -ADDRESS = 0x10000 +ADDRESS = 0x10000 # callback for tracing basic blocks def hook_block(uc, address, size, user_data): - print(">>> Tracing basic block at 0x%x, block size = 0x%x" %(address, size)) + print(">>> Tracing basic block at 0x%x, block size = 0x%x" % (address, size)) # callback for tracing instructions def hook_code(uc, address, size, user_data): - print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" %(address, size)) + print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" % (address, size)) # Test ARM64 @@ -59,7 +58,7 @@ def test_arm64(): x11 = mu.reg_read(UC_ARM64_REG_X11) x13 = mu.reg_read(UC_ARM64_REG_X13) x15 = mu.reg_read(UC_ARM64_REG_X15) - print(">>> X15 = 0x%x" %x15) + print(">>> X15 = 0x%x" % x15) except UcError as e: print("ERROR: %s" % e) diff --git a/bindings/python/sample_armeb.py b/bindings/python/tests/test_armeb.py similarity index 88% rename from bindings/python/sample_armeb.py rename to bindings/python/tests/test_armeb.py index 9a2158dba6..f818d2746d 100755 --- a/bindings/python/sample_armeb.py +++ b/bindings/python/tests/test_armeb.py @@ -5,22 +5,21 @@ from unicorn import * from unicorn.arm_const import * - # code to be emulated -ARM_CODE = b"\xe3\xa0\x00\x37\xe0\x42\x10\x03" # mov r0, #0x37; sub r1, r2, r3 -THUMB_CODE = b"\xb0\x83" # sub sp, #0xc +ARM_CODE = b"\xe3\xa0\x00\x37\xe0\x42\x10\x03" # mov r0, #0x37; sub r1, r2, r3 +THUMB_CODE = b"\xb0\x83" # sub sp, #0xc # memory address where emulation starts -ADDRESS = 0x10000 +ADDRESS = 0x10000 # callback for tracing basic blocks def hook_block(uc, address, size, user_data): - print(">>> Tracing basic block at 0x%x, block size = 0x%x" %(address, size)) + print(">>> Tracing basic block at 0x%x, block size = 0x%x" % (address, size)) # callback for tracing instructions def hook_code(uc, address, size, user_data): - print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" %(address, size)) + print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" % (address, size)) # Test ARM @@ -40,8 +39,8 @@ def test_arm(): mu.reg_write(UC_ARM_REG_R0, 0x1234) mu.reg_write(UC_ARM_REG_R2, 0x6789) mu.reg_write(UC_ARM_REG_R3, 0x3333) - mu.reg_write(UC_ARM_REG_APSR, 0xFFFFFFFF) #All application flags turned on - + mu.reg_write(UC_ARM_REG_APSR, 0xFFFFFFFF) # All application flags turned on + # tracing all basic blocks with customized callback mu.hook_add(UC_HOOK_BLOCK, hook_block) @@ -56,8 +55,8 @@ def test_arm(): r0 = mu.reg_read(UC_ARM_REG_R0) r1 = mu.reg_read(UC_ARM_REG_R1) - print(">>> R0 = 0x%x" %r0) - print(">>> R1 = 0x%x" %r1) + print(">>> R0 = 0x%x" % r0) + print(">>> R1 = 0x%x" % r1) except UcError as e: print("ERROR: %s" % e) @@ -92,7 +91,7 @@ def test_thumb(): print(">>> Emulation done. Below is the CPU context") sp = mu.reg_read(UC_ARM_REG_SP) - print(">>> SP = 0x%x" %sp) + print(">>> SP = 0x%x" % sp) except UcError as e: print("ERROR: %s" % e) diff --git a/bindings/python/sample_ctl.py b/bindings/python/tests/test_ctl.py similarity index 89% rename from bindings/python/sample_ctl.py rename to bindings/python/tests/test_ctl.py index c1cbc0703f..5a00340c1f 100755 --- a/bindings/python/sample_ctl.py +++ b/bindings/python/tests/test_ctl.py @@ -6,6 +6,7 @@ from unicorn.x86_const import * from datetime import datetime + def test_uc_ctl_read(): uc = Uc(UC_ARCH_X86, UC_MODE_32) @@ -21,6 +22,7 @@ def test_uc_ctl_read(): print(f">>> arch={arch} mode={mode} page size={page_size} timeout={timeout}") + def time_emulation(uc, start, end): n = datetime.now() @@ -28,6 +30,7 @@ def time_emulation(uc, start, end): return (datetime.now() - n).total_seconds() * 1e6 + def test_uc_ctl_tb_cache(): # Initialize emulator in X86-32bit mode uc = Uc(UC_ARCH_X86, UC_MODE_32) @@ -35,21 +38,21 @@ def test_uc_ctl_tb_cache(): # Fill the code buffer with NOP. code = b"\x90" * 8 * 512 - - print("Controling the TB cache in a finer granularity by uc_ctl.") + + print("Controlling the TB cache in a finer granularity by uc_ctl.") uc.mem_map(addr, 0x10000) # Write our code to the memory. uc.mem_write(addr, code) - + # Do emulation without any cache. standard = time_emulation(uc, addr, addr + len(code)) # Now we request cache for all TBs. for i in range(8): tb = uc.ctl_request_cache(addr + i * 512) - print(f">>> TB is cached at {hex(tb.pc)} which has {tb.icount} instructions with {tb.size} bytes") + print(f">>> TB is cached at {hex(tb[0])} which has {tb[1]} instructions with {tb[2]} bytes") # Do emulation with all TB cached. cached = time_emulation(uc, addr, addr + len(code)) @@ -62,12 +65,15 @@ def test_uc_ctl_tb_cache(): print(f">>> Run time: First time {standard}, Cached: {cached}, Cached evicted: {evicted}") + def trace_new_edge(uc, cur, prev, data): print(f">>> Getting a new edge from {hex(prev.pc + prev.size - 1)} to {hex(cur.pc)}") + def trace_tcg_sub(uc, address, arg1, arg2, size, data): print(f">>> Get a tcg sub opcode at {hex(address)} with args: {arg1} and {arg2}") + def test_uc_ctl_exits(): uc = Uc(UC_ARCH_X86, UC_MODE_32) addr = 0x1000 @@ -98,7 +104,7 @@ def test_uc_ctl_exits(): uc.ctl_set_exits(exits) - # This should stop at ADDRESS + 6 and increase eax, even thouhg we don't provide an exit. + # This should stop at ADDRESS + 6 and increase eax, even though we don't provide an exit. uc.emu_start(addr, 0) eax = uc.reg_read(UC_X86_REG_EAX) @@ -106,7 +112,7 @@ def test_uc_ctl_exits(): print(f">>> eax = {hex(eax)} and ebx = {hex(ebx)} after the first emulation") - # This should stop at ADDRESS + 8, even thouhg we don't provide an exit. + # This should stop at ADDRESS + 8, even though we don't provide an exit. uc.emu_start(addr, 0) eax = uc.reg_read(UC_X86_REG_EAX) @@ -114,9 +120,10 @@ def test_uc_ctl_exits(): print(f">>> eax = {hex(eax)} and ebx = {hex(ebx)} after the first emulation") + if __name__ == "__main__": test_uc_ctl_read() - print("="*32) + print("=" * 32) test_uc_ctl_tb_cache() - print("="*32) - test_uc_ctl_exits() \ No newline at end of file + print("=" * 32) + test_uc_ctl_exits() diff --git a/bindings/python/sample_m68k.py b/bindings/python/tests/test_m68k.py similarity index 96% rename from bindings/python/sample_m68k.py rename to bindings/python/tests/test_m68k.py index 7e35cf5974..57b05ea61a 100755 --- a/bindings/python/sample_m68k.py +++ b/bindings/python/tests/test_m68k.py @@ -6,21 +6,20 @@ from unicorn import * from unicorn.m68k_const import * - # code to be emulated -M68K_CODE = b"\x76\xed" # movq #-19, %d3 +M68K_CODE = b"\x76\xed" # movq #-19, %d3 # memory address where emulation starts -ADDRESS = 0x10000 +ADDRESS = 0x10000 # callback for tracing basic blocks def hook_block(uc, address, size, user_data): - print(">>> Tracing basic block at 0x%x, block size = 0x%x" %(address, size)) + print(">>> Tracing basic block at 0x%x, block size = 0x%x" % (address, size)) # callback for tracing instructions def hook_code(uc, address, size, user_data): - print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" %(address, size)) + print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" % (address, size)) # Test ARM diff --git a/bindings/python/sample_mips.py b/bindings/python/tests/test_mips.py similarity index 91% rename from bindings/python/sample_mips.py rename to bindings/python/tests/test_mips.py index 7199b63174..3ec5ec565b 100755 --- a/bindings/python/sample_mips.py +++ b/bindings/python/tests/test_mips.py @@ -6,23 +6,22 @@ from unicorn import * from unicorn.mips_const import * - # code to be emulated -MIPS_CODE_EB = b"\x34\x21\x34\x56" # ori $at, $at, 0x3456; -MIPS_CODE_EL = b"\x56\x34\x21\x34" # ori $at, $at, 0x3456; +MIPS_CODE_EB = b"\x34\x21\x34\x56" # ori $at, $at, 0x3456; +MIPS_CODE_EL = b"\x56\x34\x21\x34" # ori $at, $at, 0x3456; # memory address where emulation starts -ADDRESS = 0x10000 +ADDRESS = 0x10000 # callback for tracing basic blocks def hook_block(uc, address, size, user_data): - print(">>> Tracing basic block at 0x%x, block size = 0x%x" %(address, size)) + print(">>> Tracing basic block at 0x%x, block size = 0x%x" % (address, size)) # callback for tracing instructions def hook_code(uc, address, size, user_data): - print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" %(address, size)) + print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" % (address, size)) # Test MIPS EB @@ -54,7 +53,7 @@ def test_mips_eb(): print(">>> Emulation done. Below is the CPU context") r1 = mu.reg_read(UC_MIPS_REG_1) - print(">>> R1 = 0x%x" %r1) + print(">>> R1 = 0x%x" % r1) except UcError as e: print("ERROR: %s" % e) @@ -89,7 +88,7 @@ def test_mips_el(): print(">>> Emulation done. Below is the CPU context") r1 = mu.reg_read(UC_MIPS_REG_1) - print(">>> R1 = 0x%x" %r1) + print(">>> R1 = 0x%x" % r1) except UcError as e: print("ERROR: %s" % e) diff --git a/bindings/python/sample_network_auditing.py b/bindings/python/tests/test_network_auditing.py similarity index 99% rename from bindings/python/sample_network_auditing.py rename to bindings/python/tests/test_network_auditing.py index bfccf828ec..1d0c7a7875 100755 --- a/bindings/python/sample_network_auditing.py +++ b/bindings/python/tests/test_network_auditing.py @@ -3,10 +3,11 @@ # Nguyen Tan Cong from __future__ import print_function -from unicorn import * -from unicorn.x86_const import * +import pytest import struct import uuid +from unicorn import * +from unicorn.x86_const import * SIZE_REG = 4 SOCKETCALL_MAX_ARGS = 3 @@ -360,6 +361,7 @@ def hook_intr(uc, intno, user_data): print_sockcall(msg) +@pytest.mark.parametrize("code", [X86_SEND_ETCPASSWD, X86_BIND_TCP, X86_REVERSE_TCP, X86_REVERSE_TCP_2]) # Test X86 32 bit def test_i386(code): global fd_chains diff --git a/bindings/python/sample_ppc.py b/bindings/python/tests/test_ppc.py similarity index 93% rename from bindings/python/sample_ppc.py rename to bindings/python/tests/test_ppc.py index 1a0d879de0..e0cbbb0e1a 100755 --- a/bindings/python/sample_ppc.py +++ b/bindings/python/tests/test_ppc.py @@ -1,26 +1,24 @@ #!/usr/bin/env python # Sample code for PPC of Unicorn. Nguyen Anh Quynh -# from __future__ import print_function from unicorn import * from unicorn.ppc_const import * - # code to be emulated -PPC_CODE = b"\x7F\x46\x1A\x14" # add r26, r6, r3 +PPC_CODE = b"\x7F\x46\x1A\x14" # add r26, r6, r3 # memory address where emulation starts -ADDRESS = 0x10000 +ADDRESS = 0x10000 # callback for tracing basic blocks def hook_block(uc, address, size, user_data): - print(">>> Tracing basic block at 0x%x, block size = 0x%x" %(address, size)) + print(">>> Tracing basic block at 0x%x, block size = 0x%x" % (address, size)) # callback for tracing instructions def hook_code(uc, address, size, user_data): - print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" %(address, size)) + print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" % (address, size)) # Test PPC @@ -62,4 +60,3 @@ def test_ppc(): if __name__ == '__main__': test_ppc() - diff --git a/bindings/python/sample_riscv.py b/bindings/python/tests/test_riscv.py similarity index 92% rename from bindings/python/sample_riscv.py rename to bindings/python/tests/test_riscv.py index 3bd032bdd6..39cf83f530 100755 --- a/bindings/python/sample_riscv.py +++ b/bindings/python/tests/test_riscv.py @@ -1,12 +1,10 @@ #!/usr/bin/env python # Sample code for RISCV of Unicorn. Nguyen Anh Quynh -# from __future__ import print_function from unicorn import * from unicorn.riscv_const import * - ''' $ cstool riscv64 1305100093850502 0 13 05 10 00 addi a0, zero, 1 @@ -15,17 +13,17 @@ RISCV_CODE = b"\x13\x05\x10\x00\x93\x85\x05\x02" # memory address where emulation starts -ADDRESS = 0x10000 +ADDRESS = 0x10000 # callback for tracing basic blocks def hook_block(uc, address, size, user_data): - print(">>> Tracing basic block at 0x%x, block size = 0x%x" %(address, size)) + print(">>> Tracing basic block at 0x%x, block size = 0x%x" % (address, size)) # callback for tracing instructions def hook_code(uc, address, size, user_data): - print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" %(address, size)) + print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" % (address, size)) # Test RISCV @@ -59,8 +57,8 @@ def test_riscv(): a0 = mu.reg_read(UC_RISCV_REG_A0) a1 = mu.reg_read(UC_RISCV_REG_A1) - print(">>> A0 = 0x%x" %a0) - print(">>> A1 = 0x%x" %a1) + print(">>> A0 = 0x%x" % a0) + print(">>> A1 = 0x%x" % a1) except UcError as e: print("ERROR: %s" % e) @@ -68,4 +66,3 @@ def test_riscv(): if __name__ == '__main__': test_riscv() - diff --git a/bindings/python/sample_s390x.py b/bindings/python/tests/test_s390x.py similarity index 96% rename from bindings/python/sample_s390x.py rename to bindings/python/tests/test_s390x.py index 6b96d8270d..68798cd077 100644 --- a/bindings/python/sample_s390x.py +++ b/bindings/python/tests/test_s390x.py @@ -8,17 +8,17 @@ S390X_CODE = b"\x18\x23" # memory address where emulation starts -ADDRESS = 0x10000 +ADDRESS = 0x10000 # callback for tracing basic blocks def hook_block(uc, address, size, user_data): - print(">>> Tracing basic block at 0x%x, block size = 0x%x" %(address, size)) + print(">>> Tracing basic block at 0x%x, block size = 0x%x" % (address, size)) # callback for tracing instructions def hook_code(uc, address, size, user_data): - print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" %(address, size)) + print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" % (address, size)) # Test RISCV @@ -60,4 +60,3 @@ def test_s390x(): if __name__ == '__main__': test_s390x() - diff --git a/bindings/python/shellcode.py b/bindings/python/tests/test_shellcode.py similarity index 82% rename from bindings/python/shellcode.py rename to bindings/python/tests/test_shellcode.py index 5c393e1f8c..1313f883ad 100755 --- a/bindings/python/shellcode.py +++ b/bindings/python/tests/test_shellcode.py @@ -4,11 +4,12 @@ # KaiJern Lau from __future__ import print_function +import pytest from unicorn import * from unicorn.x86_const import * # Original shellcode from this example. -#X86_CODE32 = b"\xeb\x19\x31\xc0\x31\xdb\x31\xd2\x31\xc9\xb0\x04\xb3\x01\x59\xb2\x05\xcd\x80\x31\xc0\xb0\x01\x31\xdb\xcd\x80\xe8\xe2\xff\xff\xff\x68\x65\x6c\x6c\x6f" +# X86_CODE32 = b"\xeb\x19\x31\xc0\x31\xdb\x31\xd2\x31\xc9\xb0\x04\xb3\x01\x59\xb2\x05\xcd\x80\x31\xc0\xb0\x01\x31\xdb\xcd\x80\xe8\xe2\xff\xff\xff\x68\x65\x6c\x6c\x6f" # Linux/x86 execve /bin/sh shellcode 23 bytes, from http://shell-storm.org/shellcode/files/shellcode-827.php # 0: 31 c0 xor eax,eax @@ -44,20 +45,22 @@ # memory address where emulation starts ADDRESS = 0x1000000 + # callback for tracing instructions def hook_code(uc, address, size, user_data): - print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" %(address, size)) + print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" % (address, size)) # read this instruction code from memory tmp = uc.mem_read(address, size) - print("*** PC = %x *** :" %(address), end="") + print("*** PC = %x *** :" % (address), end="") for i in tmp: - print(" %02x" %i, end="") + print(" %02x" % i, end="") print("") # callback for tracing basic blocks def hook_block(uc, address, size, user_data): - print(">>> Tracing basic block at 0x%x, block size = 0x%x" %(address, size)) + print(">>> Tracing basic block at 0x%x, block size = 0x%x" % (address, size)) + def read_string(uc, address): ret = "" @@ -70,21 +73,22 @@ def read_string(uc, address): read_bytes += 1 return ret + # callback for tracing Linux interrupt def hook_intr(uc, intno, user_data): # only handle Linux syscall if intno != 0x80: - print("got interrupt %x ???" %intno) + print("got interrupt %x ???" % intno) uc.emu_stop() return eax = uc.reg_read(UC_X86_REG_EAX) eip = uc.reg_read(UC_X86_REG_EIP) - if eax == 1: # sys_exit - print(">>> 0x%x: interrupt 0x%x, EAX = 0x%x" %(eip, intno, eax)) + if eax == 1: # sys_exit + print(">>> 0x%x: interrupt 0x%x, EAX = 0x%x" % (eip, intno, eax)) uc.emu_stop() - elif eax == 4: # sys_write + elif eax == 4: # sys_write # ECX = buffer address ecx = uc.reg_read(UC_X86_REG_ECX) # EDX = buffer size @@ -92,49 +96,53 @@ def hook_intr(uc, intno, user_data): try: buf = uc.mem_read(ecx, edx) print(">>> 0x%x: interrupt 0x%x, SYS_WRITE. buffer = 0x%x, size = %u, content = " \ - %(eip, intno, ecx, edx), end="") + % (eip, intno, ecx, edx), end="") for i in buf: - print("%c" %i, end="") + print("%c" % i, end="") print("") except UcError as e: print(">>> 0x%x: interrupt 0x%x, SYS_WRITE. buffer = 0x%x, size = %u, content = \n" \ - %(eip, intno, ecx, edx)) - elif eax == 11: # sys_write + % (eip, intno, ecx, edx)) + elif eax == 11: # sys_write ebx = uc.reg_read(UC_X86_REG_EBX) filename = read_string(uc, ebx) print(">>> SYS_EXECV filename=%s" % filename) else: - print(">>> 0x%x: interrupt 0x%x, EAX = 0x%x" %(eip, intno, eax)) + print(">>> 0x%x: interrupt 0x%x, EAX = 0x%x" % (eip, intno, eax)) def hook_syscall32(mu, user_data): eax = mu.reg_read(UC_X86_REG_EAX) - print(">>> got SYSCALL with EAX = 0x%x" %(eax)) + print(">>> got SYSCALL with EAX = 0x%x" % (eax)) mu.emu_stop() + def hook_syscall64(mu, user_data): rax = mu.reg_read(UC_X86_REG_RAX) rdi = mu.reg_read(UC_X86_REG_RDI) - print(">>> got SYSCALL with RAX = %d" %(rax)) - - if rax == 59: #sys_execve + print(">>> got SYSCALL with RAX = %d" % (rax)) + + if rax == 59: # sys_execve filename = read_string(mu, rdi) print(">>> SYS_EXECV filename=%s" % filename) else: rip = mu.reg_read(UC_X86_REG_RIP) - print(">>> Syscall Found at 0x%x: , RAX = 0x%x" %(rip, rax)) + print(">>> Syscall Found at 0x%x: , RAX = 0x%x" % (rip, rax)) mu.emu_stop() + +@pytest.mark.parametrize("mode,code", + [(UC_MODE_32, X86_CODE32_SELF), (UC_MODE_32, X86_CODE32), (UC_MODE_64, X86_CODE64)]) # Test X86 32 bit def test_i386(mode, code): if mode == UC_MODE_32: print("Emulate x86_32 code") elif mode == UC_MODE_64: print("Emulate x86_64 code") - + try: # Initialize emulator mu = Uc(UC_ARCH_X86, mode) @@ -171,9 +179,10 @@ def test_i386(mode, code): except UcError as e: print("ERROR: %s" % e) + if __name__ == '__main__': test_i386(UC_MODE_32, X86_CODE32_SELF) print("=" * 20) test_i386(UC_MODE_32, X86_CODE32) print("=" * 20) - test_i386(UC_MODE_64, X86_CODE64) \ No newline at end of file + test_i386(UC_MODE_64, X86_CODE64) diff --git a/bindings/python/sample_sparc.py b/bindings/python/tests/test_sparc.py similarity index 88% rename from bindings/python/sample_sparc.py rename to bindings/python/tests/test_sparc.py index 5dbe746ec9..badb7b76ff 100755 --- a/bindings/python/sample_sparc.py +++ b/bindings/python/tests/test_sparc.py @@ -6,21 +6,20 @@ from unicorn import * from unicorn.sparc_const import * - # code to be emulated -SPARC_CODE = b"\x86\x00\x40\x02" # add %g1, %g2, %g3; +SPARC_CODE = b"\x86\x00\x40\x02" # add %g1, %g2, %g3; # memory address where emulation starts -ADDRESS = 0x10000 +ADDRESS = 0x10000 # callback for tracing basic blocks def hook_block(uc, address, size, user_data): - print(">>> Tracing basic block at 0x%x, block size = 0x%x" %(address, size)) + print(">>> Tracing basic block at 0x%x, block size = 0x%x" % (address, size)) # callback for tracing instructions def hook_code(uc, address, size, user_data): - print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" %(address, size)) + print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" % (address, size)) # Test SPARC @@ -28,7 +27,7 @@ def test_sparc(): print("Emulate SPARC code") try: # Initialize emulator in SPARC EB mode - mu = Uc(UC_ARCH_SPARC, UC_MODE_SPARC32|UC_MODE_BIG_ENDIAN) + mu = Uc(UC_ARCH_SPARC, UC_MODE_SPARC32 | UC_MODE_BIG_ENDIAN) # map 2MB memory for this emulation mu.mem_map(ADDRESS, 2 * 1024 * 1024) @@ -54,7 +53,7 @@ def test_sparc(): print(">>> Emulation done. Below is the CPU context") g3 = mu.reg_read(UC_SPARC_REG_G3) - print(">>> G3 = 0x%x" %g3) + print(">>> G3 = 0x%x" % g3) except UcError as e: print("ERROR: %s" % e) diff --git a/bindings/python/sample_tricore.py b/bindings/python/tests/test_tricore.py similarity index 89% rename from bindings/python/sample_tricore.py rename to bindings/python/tests/test_tricore.py index 2e7174e1f7..20bbc03a5b 100755 --- a/bindings/python/sample_tricore.py +++ b/bindings/python/tests/test_tricore.py @@ -1,26 +1,29 @@ #!/usr/bin/env python -''' +""" Created for Unicorn Engine by Eric Poole , 2022 Copyright 2022 Aptiv -''' +""" from __future__ import print_function from unicorn import * from unicorn.tricore_const import * # code to be emulated -TRICORE_CODE = b"\x82\x11\xbb\x00\x00\x08" # mov d0, #0x1; mov.u d0, #0x8000 +TRICORE_CODE = b"\x82\x11\xbb\x00\x00\x08" # mov d0, #0x1; mov.u d0, #0x8000 # memory address where emulation starts -ADDRESS = 0x10000 +ADDRESS = 0x10000 + # callback for tracing basic blocks def hook_block(uc, address, size, user_data): - print(">>> Tracing basic block at 0x%x, block size = 0x%x" %(address, size)) + print(">>> Tracing basic block at 0x%x, block size = 0x%x" % (address, size)) + # callback for tracing instructions def hook_code(uc, address, size, user_data): - print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" %(address, size)) + print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" % (address, size)) + # Test TriCore def test_tricore(): @@ -48,10 +51,11 @@ def test_tricore(): print(">>> Emulation done. Below is the CPU context") r0 = mu.reg_read(UC_TRICORE_REG_D0) - print(">>> D0 = 0x%x" %r0) + print(">>> D0 = 0x%x" % r0) except UcError as e: print("ERROR: %s" % e) + if __name__ == '__main__': test_tricore() diff --git a/bindings/python/sample_x86.py b/bindings/python/tests/test_x86.py similarity index 85% rename from bindings/python/sample_x86.py rename to bindings/python/tests/test_x86.py index 36dfe735fa..f8edce06b0 100755 --- a/bindings/python/sample_x86.py +++ b/bindings/python/tests/test_x86.py @@ -2,21 +2,21 @@ # Sample code for X86 of Unicorn. Nguyen Anh Quynh from __future__ import print_function +import pickle from unicorn import * from unicorn.x86_const import * -import pickle -X86_CODE32 = b"\x41\x4a\x66\x0f\xef\xc1" # INC ecx; DEC edx; PXOR xmm0, xmm1 -X86_CODE32_LOOP = b"\x41\x4a\xeb\xfe" # INC ecx; DEC edx; JMP self-loop -X86_CODE32_JUMP = b"\xeb\x02\x90\x90\x90\x90\x90\x90" # jmp 4; nop; nop; nop; nop; nop; nop -X86_CODE32_JMP_INVALID = b"\xe9\xe9\xee\xee\xee\x41\x4a" # JMP outside; INC ecx; DEC edx -X86_CODE32_MEM_READ = b"\x8B\x0D\xAA\xAA\xAA\xAA\x41\x4a" # mov ecx,[0xaaaaaaaa]; INC ecx; DEC edx -X86_CODE32_MEM_WRITE = b"\x89\x0D\xAA\xAA\xAA\xAA\x41\x4a" # mov [0xaaaaaaaa], ecx; INC ecx; DEC edx +X86_CODE32 = b"\x41\x4a\x66\x0f\xef\xc1" # INC ecx; DEC edx; PXOR xmm0, xmm1 +X86_CODE32_LOOP = b"\x41\x4a\xeb\xfe" # INC ecx; DEC edx; JMP self-loop +X86_CODE32_JUMP = b"\xeb\x02\x90\x90\x90\x90\x90\x90" # jmp 4; nop; nop; nop; nop; nop; nop +X86_CODE32_JMP_INVALID = b"\xe9\xe9\xee\xee\xee\x41\x4a" # JMP outside; INC ecx; DEC edx +X86_CODE32_MEM_READ = b"\x8B\x0D\xAA\xAA\xAA\xAA\x41\x4a" # mov ecx,[0xaaaaaaaa]; INC ecx; DEC edx +X86_CODE32_MEM_WRITE = b"\x89\x0D\xAA\xAA\xAA\xAA\x41\x4a" # mov [0xaaaaaaaa], ecx; INC ecx; DEC edx X86_CODE64 = b"\x41\xBC\x3B\xB0\x28\x2A\x49\x0F\xC9\x90\x4D\x0F\xAD\xCF\x49\x87\xFD\x90\x48\x81\xD2\x8A\xCE\x77\x35\x48\xF7\xD9\x4D\x29\xF4\x49\x81\xC9\xF6\x8A\xC6\x53\x4D\x87\xED\x48\x0F\xAD\xD2\x49\xF7\xD4\x48\xF7\xE1\x4D\x19\xC5\x4D\x89\xC5\x48\xF7\xD6\x41\xB8\x4F\x8D\x6B\x59\x4D\x87\xD0\x68\x6A\x1E\x09\x3C\x59" -X86_CODE32_INOUT = b"\x41\xE4\x3F\x4a\xE6\x46\x43" # INC ecx; IN AL, 0x3f; DEC edx; OUT 0x46, AL; INC ebx -X86_CODE64_SYSCALL = b'\x0f\x05' # SYSCALL -X86_CODE16 = b'\x00\x00' # add byte ptr [bx + si], al -X86_MMIO_CODE = b"\x89\x0d\x04\x00\x02\x00\x8b\x0d\x04\x00\x02\x00" # mov [0x20004], ecx; mov ecx, [0x20004] +X86_CODE32_INOUT = b"\x41\xE4\x3F\x4a\xE6\x46\x43" # INC ecx; IN AL, 0x3f; DEC edx; OUT 0x46, AL; INC ebx +X86_CODE64_SYSCALL = b'\x0f\x05' # SYSCALL +X86_CODE16 = b'\x00\x00' # add byte ptr [bx + si], al +X86_MMIO_CODE = b"\x89\x0d\x04\x00\x02\x00\x8b\x0d\x04\x00\x02\x00" # mov [0x20004], ecx; mov ecx, [0x20004] # memory address where emulation starts ADDRESS = 0x1000000 @@ -24,28 +24,29 @@ # callback for tracing basic blocks def hook_block(uc, address, size, user_data): - print(">>> Tracing basic block at 0x%x, block size = 0x%x" %(address, size)) + print(">>> Tracing basic block at 0x%x, block size = 0x%x" % (address, size)) # callback for tracing instructions def hook_code(uc, address, size, user_data): - print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" %(address, size)) + print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" % (address, size)) eflags = uc.reg_read(UC_X86_REG_EFLAGS) - print(">>> --- EFLAGS is 0x%x" %eflags) + print(">>> --- EFLAGS is 0x%x" % eflags) + def hook_code64(uc, address, size, user_data): - print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" %(address, size)) + print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" % (address, size)) rip = uc.reg_read(UC_X86_REG_RIP) - print(">>> RIP is 0x%x" %rip) + print(">>> RIP is 0x%x" % rip) # callback for tracing invalid memory access (READ or WRITE) def hook_mem_invalid(uc, access, address, size, value, user_data): if access == UC_MEM_WRITE_UNMAPPED: print(">>> Missing memory is being WRITE at 0x%x, data size = %u, data value = 0x%x" \ - %(address, size, value)) + % (address, size, value)) # map this memory in with 2MB in size - uc.mem_map(0xaaaa0000, 2 * 1024*1024) + uc.mem_map(0xaaaa0000, 2 * 1024 * 1024) # return True to indicate we want to continue emulation return True else: @@ -57,16 +58,16 @@ def hook_mem_invalid(uc, access, address, size, value, user_data): def hook_mem_access(uc, access, address, size, value, user_data): if access == UC_MEM_WRITE: print(">>> Memory is being WRITE at 0x%x, data size = %u, data value = 0x%x" \ - %(address, size, value)) - else: # READ + % (address, size, value)) + else: # READ print(">>> Memory is being READ at 0x%x, data size = %u" \ - %(address, size)) + % (address, size)) # callback for IN instruction def hook_in(uc, port, size, user_data): eip = uc.reg_read(UC_X86_REG_EIP) - print("--- reading from port 0x%x, size: %u, address: 0x%x" %(port, size, eip)) + print("--- reading from port 0x%x, size: %u, address: 0x%x" % (port, size, eip)) if size == 1: # read 1 byte to AL return 0xf1 @@ -83,7 +84,7 @@ def hook_in(uc, port, size, user_data): # callback for OUT instruction def hook_out(uc, port, size, value, user_data): eip = uc.reg_read(UC_X86_REG_EIP) - print("--- writing to port 0x%x, size: %u, value: 0x%x, address: 0x%x" %(port, size, value, eip)) + print("--- writing to port 0x%x, size: %u, value: 0x%x, address: 0x%x" % (port, size, value, eip)) # confirm that value is indeed the value of AL/AX/EAX v = 0 @@ -97,7 +98,7 @@ def hook_out(uc, port, size, value, user_data): # read 4 bytes in EAX v = uc.reg_read(UC_X86_REG_EAX) - print("--- register value = 0x%x" %v) + print("--- register value = 0x%x" % v) # Test X86 32 bit @@ -134,15 +135,15 @@ def test_i386(): r_ecx = mu.reg_read(UC_X86_REG_ECX) r_edx = mu.reg_read(UC_X86_REG_EDX) r_xmm0 = mu.reg_read(UC_X86_REG_XMM0) - print(">>> ECX = 0x%x" %r_ecx) - print(">>> EDX = 0x%x" %r_edx) - print(">>> XMM0 = 0x%.32x" %r_xmm0) + print(">>> ECX = 0x%x" % r_ecx) + print(">>> EDX = 0x%x" % r_edx) + print(">>> XMM0 = 0x%.32x" % r_xmm0) # read from memory tmp = mu.mem_read(ADDRESS, 4) - print(">>> Read 4 bytes from [0x%x] = 0x" %(ADDRESS), end="") + print(">>> Read 4 bytes from [0x%x] = 0x" % (ADDRESS), end="") for i in reversed(tmp): - print("%x" %(i), end="") + print("%x" % (i), end="") print("") except UcError as e: @@ -179,14 +180,14 @@ def test_i386_map_ptr(): r_ecx = mu.reg_read(UC_X86_REG_ECX) r_edx = mu.reg_read(UC_X86_REG_EDX) - print(">>> ECX = 0x%x" %r_ecx) - print(">>> EDX = 0x%x" %r_edx) + print(">>> ECX = 0x%x" % r_ecx) + print(">>> EDX = 0x%x" % r_edx) # read from memory tmp = mu.mem_read(ADDRESS, 4) - print(">>> Read 4 bytes from [0x%x] = 0x" %(ADDRESS), end="") + print(">>> Read 4 bytes from [0x%x] = 0x" % (ADDRESS), end="") for i in reversed(tmp): - print("%x" %(i), end="") + print("%x" % (i), end="") print("") except UcError as e: @@ -226,12 +227,13 @@ def test_i386_invalid_mem_read(): r_ecx = mu.reg_read(UC_X86_REG_ECX) r_edx = mu.reg_read(UC_X86_REG_EDX) - print(">>> ECX = 0x%x" %r_ecx) - print(">>> EDX = 0x%x" %r_edx) + print(">>> ECX = 0x%x" % r_ecx) + print(">>> EDX = 0x%x" % r_edx) except UcError as e: print("ERROR: %s" % e) + def test_i386_jump(): print("Emulate i386 code with jump") try: @@ -298,22 +300,22 @@ def test_i386_invalid_mem_write(): r_ecx = mu.reg_read(UC_X86_REG_ECX) r_edx = mu.reg_read(UC_X86_REG_EDX) - print(">>> ECX = 0x%x" %r_ecx) - print(">>> EDX = 0x%x" %r_edx) + print(">>> ECX = 0x%x" % r_ecx) + print(">>> EDX = 0x%x" % r_edx) # read from memory - print(">>> Read 4 bytes from [0x%x] = 0x" %(0xaaaaaaaa), end="") + print(">>> Read 4 bytes from [0x%x] = 0x" % (0xaaaaaaaa), end="") tmp = mu.mem_read(0xaaaaaaaa, 4) for i in reversed(tmp): if i != 0: - print("%x" %i, end="") + print("%x" % i, end="") print("") try: tmp = mu.mem_read(0xffffffaa, 4) - print(">>> Read 4 bytes from [0x%x] = 0x" %(0xffffffaa), end="") + print(">>> Read 4 bytes from [0x%x] = 0x" % (0xffffffaa), end="") for i in reversed(tmp): - print("%x" %i, end="") + print("%x" % i, end="") print("") except UcError as e: @@ -322,6 +324,7 @@ def test_i386_invalid_mem_write(): except UcError as e: print("ERROR: %s" % e) + def test_i386_jump_invalid(): print("Emulate i386 code that jumps to invalid memory") try: @@ -347,18 +350,19 @@ def test_i386_jump_invalid(): try: mu.emu_start(ADDRESS, ADDRESS + len(X86_CODE32_JMP_INVALID)) except UcError as e: - print("Failed on uc_emu_start() with error returned 8: %s" %e) + print("Failed on uc_emu_start() with error returned 8: %s" % e) print(">>> Emulation done. Below is the CPU context") r_ecx = mu.reg_read(UC_X86_REG_ECX) r_edx = mu.reg_read(UC_X86_REG_EDX) - print(">>> ECX = 0x%x" %r_ecx) - print(">>> EDX = 0x%x" %r_edx) + print(">>> ECX = 0x%x" % r_ecx) + print(">>> EDX = 0x%x" % r_edx) except UcError as e: print("ERROR %s" % e) + def test_i386_loop(): print("Emulate i386 code that loop forever") try: @@ -375,18 +379,19 @@ def test_i386_loop(): mu.reg_write(UC_X86_REG_ECX, 0x1234) mu.reg_write(UC_X86_REG_EDX, 0x7890) - mu.emu_start(ADDRESS, ADDRESS + len(X86_CODE32_LOOP), timeout=2*UC_SECOND_SCALE) + mu.emu_start(ADDRESS, ADDRESS + len(X86_CODE32_LOOP), timeout=2 * UC_SECOND_SCALE) print(">>> Emulation done. Below is the CPU context") r_ecx = mu.reg_read(UC_X86_REG_ECX) r_edx = mu.reg_read(UC_X86_REG_EDX) - print(">>> ECX = 0x%x" %r_ecx) - print(">>> EDX = 0x%x" %r_edx) + print(">>> ECX = 0x%x" % r_ecx) + print(">>> EDX = 0x%x" % r_edx) except UcError as e: print("ERROR: %s" % e) + # Test X86 32 bit with IN/OUT instruction def test_i386_inout(): print("Emulate i386 code with IN/OUT instructions") @@ -422,8 +427,8 @@ def test_i386_inout(): r_ecx = mu.reg_read(UC_X86_REG_ECX) r_eax = mu.reg_read(UC_X86_REG_EAX) - print(">>> EAX = 0x%x" %r_eax) - print(">>> ECX = 0x%x" %r_ecx) + print(">>> EAX = 0x%x" % r_eax) + print(">>> ECX = 0x%x" % r_ecx) except UcError as e: print("ERROR: %s" % e) @@ -446,10 +451,10 @@ def test_i386_context_save(): mu.reg_write(UC_X86_REG_EAX, 1) print(">>> Running emulation for the first time") - mu.emu_start(address, address+1) + mu.emu_start(address, address + 1) print(">>> Emulation done. Below is the CPU context") - print(">>> EAX = 0x%x" %(mu.reg_read(UC_X86_REG_EAX))) + print(">>> EAX = 0x%x" % (mu.reg_read(UC_X86_REG_EAX))) print(">>> Saving CPU context") saved_context = mu.context_save() @@ -457,9 +462,9 @@ def test_i386_context_save(): pickled_saved_context = pickle.dumps(saved_context) print(">>> Running emulation for the second time") - mu.emu_start(address, address+1) + mu.emu_start(address, address + 1) print(">>> Emulation done. Below is the CPU context") - print(">>> EAX = 0x%x" %(mu.reg_read(UC_X86_REG_EAX))) + print(">>> EAX = 0x%x" % (mu.reg_read(UC_X86_REG_EAX))) print(">>> Unpickling CPU context") saved_context = pickle.loads(pickled_saved_context) @@ -469,11 +474,12 @@ def test_i386_context_save(): print(">>> CPU context restored. Below is the CPU context") mu.context_restore(saved_context) - print(">>> EAX = 0x%x" %(mu.reg_read(UC_X86_REG_EAX))) + print(">>> EAX = 0x%x" % (mu.reg_read(UC_X86_REG_EAX))) except UcError as e: print("ERROR: %s" % e) + def test_x86_64(): print("Emulate x86_64 code") try: @@ -509,13 +515,13 @@ def test_x86_64(): mu.hook_add(UC_HOOK_BLOCK, hook_block) # tracing all instructions in range [ADDRESS, ADDRESS+20] - mu.hook_add(UC_HOOK_CODE, hook_code64, None, ADDRESS, ADDRESS+20) + mu.hook_add(UC_HOOK_CODE, hook_code64, None, ADDRESS, ADDRESS + 20) # tracing all memory READ & WRITE access mu.hook_add(UC_HOOK_MEM_WRITE, hook_mem_access) mu.hook_add(UC_HOOK_MEM_READ, hook_mem_access) # actually you can also use READ_WRITE to trace all memory access - #mu.hook_add(UC_HOOK_MEM_READ | UC_HOOK_MEM_WRITE, hook_mem_access) + # mu.hook_add(UC_HOOK_MEM_READ | UC_HOOK_MEM_WRITE, hook_mem_access) try: # emulate machine code in infinite time @@ -541,20 +547,20 @@ def test_x86_64(): r14 = mu.reg_read(UC_X86_REG_R14) r15 = mu.reg_read(UC_X86_REG_R15) - print(">>> RAX = 0x%x" %rax) - print(">>> RBX = 0x%x" %rbx) - print(">>> RCX = 0x%x" %rcx) - print(">>> RDX = 0x%x" %rdx) - print(">>> RSI = 0x%x" %rsi) - print(">>> RDI = 0x%x" %rdi) - print(">>> R8 = 0x%x" %r8) - print(">>> R9 = 0x%x" %r9) - print(">>> R10 = 0x%x" %r10) - print(">>> R11 = 0x%x" %r11) - print(">>> R12 = 0x%x" %r12) - print(">>> R13 = 0x%x" %r13) - print(">>> R14 = 0x%x" %r14) - print(">>> R15 = 0x%x" %r15) + print(">>> RAX = 0x%x" % rax) + print(">>> RBX = 0x%x" % rbx) + print(">>> RCX = 0x%x" % rcx) + print(">>> RDX = 0x%x" % rdx) + print(">>> RSI = 0x%x" % rsi) + print(">>> RDI = 0x%x" % rdi) + print(">>> R8 = 0x%x" % r8) + print(">>> R9 = 0x%x" % r9) + print(">>> R10 = 0x%x" % r10) + print(">>> R11 = 0x%x" % r11) + print(">>> R12 = 0x%x" % r12) + print(">>> R13 = 0x%x" % r13) + print(">>> R14 = 0x%x" % r14) + print(">>> R15 = 0x%x" % r15) except UcError as e: @@ -626,19 +632,22 @@ def test_x86_16(): print(">>> Emulation done. Below is the CPU context") tmp = mu.mem_read(11, 1) - print(">>> Read 1 bytes from [0x%x] = 0x%x" %(11, tmp[0])) + print(">>> Read 1 bytes from [0x%x] = 0x%x" % (11, tmp[0])) except UcError as e: print("ERROR: %s" % e) + def mmio_read_cb(uc, offset, size, data): print(f">>> Read IO memory at offset {hex(offset)} with {hex(size)} bytes and return 0x19260817") return 0x19260817 + def mmio_write_cb(uc, offset, size, value, data): print(f">>> Write value {hex(value)} to IO memory at offset {hex(offset)} with {hex(size)} bytes") + def test_i386_mmio(): print("Test i386 IO memory") try: @@ -664,6 +673,7 @@ def test_i386_mmio(): except UcError as e: print("ERROR: %s" % e) + if __name__ == '__main__': test_x86_16() test_i386() From 3691e33a0fe6f27125dcf4d659d9a8d00189fc92 Mon Sep 17 00:00:00 2001 From: "@Antelox" Date: Thu, 17 Oct 2024 15:15:56 +0200 Subject: [PATCH 27/72] Feat: Option to manually specify the build type in the workflow (#2034) * Python bindings: Fix upload-artifact actions * Github Action: Enable workflow_dispatch to manually trigger jobs to produce either Debug or Release builds --- .github/workflows/build-uc2.yml | 14 ++++++++++++-- .github/workflows/build-wheels-publish.yml | 19 +++++++++++++++---- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build-uc2.yml b/.github/workflows/build-uc2.yml index 554e3ec0e9..f48595afdd 100644 --- a/.github/workflows/build-uc2.yml +++ b/.github/workflows/build-uc2.yml @@ -1,6 +1,16 @@ name: Build UC2 on: + workflow_dispatch: + inputs: + buildType: + description: 'Build Type' + required: false + default: '' + type: choice + options: + - 'Debug' + - 'Release' push: paths-ignore: - ".gitignore" @@ -15,8 +25,8 @@ on: pull_request: env: - # Build Debug mode if not tag release - BUILD_TYPE: ${{ startsWith(github.ref, 'refs/tags') && 'Release' || 'Debug' }} + # Specify build type either according to the tag release or manual override + BUILD_TYPE: ${{ inputs.buildType != '' && inputs.buildType || startsWith(github.ref, 'refs/tags') && 'Release' || 'Debug' }} jobs: Windows: diff --git a/.github/workflows/build-wheels-publish.yml b/.github/workflows/build-wheels-publish.yml index 39d860226d..4327b86901 100644 --- a/.github/workflows/build-wheels-publish.yml +++ b/.github/workflows/build-wheels-publish.yml @@ -1,6 +1,16 @@ name: Build wheels with cibuildwheel on: + workflow_dispatch: + inputs: + debugMode: + description: 'Debug Mode' + required: false + default: '' + type: choice + options: + - '0' + - '1' push: paths-ignore: - ".gitignore" @@ -15,8 +25,8 @@ on: pull_request: env: - # Enable DEBUG flag if not tag release - UNICORN_DEBUG: ${{ startsWith(github.ref, 'refs/tags') && '0' || '1' }} + # Enable DEBUG flag either according to the tag release or manual override + UNICORN_DEBUG: ${{ inputs.debugMode != '' && inputs.debugMode || startsWith(github.ref, 'refs/tags') && '0' || '1' }} jobs: # job to be executed for every push - testing purpose @@ -160,7 +170,7 @@ jobs: - uses: actions/upload-artifact@v4 with: - name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }} + name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }}-py38 path: ./wheelhouse/*.whl # Job to be executed to build all wheels for all platforms/architectures/python versions only for tag release @@ -315,7 +325,7 @@ jobs: - uses: actions/upload-artifact@v4 with: - name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }} + name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }}-all path: ./wheelhouse/*.whl make_sdist: @@ -335,6 +345,7 @@ jobs: - uses: actions/upload-artifact@v4 with: + name: sdist-archive path: bindings/python/dist/*.tar.gz publish: From 28ff8d8627fb0dc13555fd1eb2368b081907d500 Mon Sep 17 00:00:00 2001 From: Eli Date: Fri, 18 Oct 2024 18:22:04 +0300 Subject: [PATCH 28/72] Hook tlb fill (#2037) * Support TLB fill hooks * Improve consistency among structure names --- .../python/unicorn/unicorn_py3/unicorn.py | 30 +++++++++++++++---- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/bindings/python/unicorn/unicorn_py3/unicorn.py b/bindings/python/unicorn/unicorn_py3/unicorn.py index 5e747005bf..c93566b461 100644 --- a/bindings/python/unicorn/unicorn_py3/unicorn.py +++ b/bindings/python/unicorn/unicorn_py3/unicorn.py @@ -16,6 +16,7 @@ MemRegionStruct = Tuple[int, int, int] TBStruct = Tuple[int, int, int] +TLBEntryStruct = Tuple[int, int] class UcTupledStruct(ctypes.Structure, Generic[VT]): @@ -32,11 +33,11 @@ def value(self) -> VT: return tuple(getattr(self, fname) for fname, *_ in self.__class__._fields_) # type: ignore -class _uc_mem_region(UcTupledStruct[MemRegionStruct]): +class uc_mem_region(UcTupledStruct[MemRegionStruct]): _fields_ = ( ('begin', ctypes.c_uint64), ('end', ctypes.c_uint64), - ('perms', ctypes.c_uint32), + ('perms', ctypes.c_uint32) ) @@ -51,6 +52,16 @@ class uc_tb(UcTupledStruct[TBStruct]): ) +class uc_tlb_entry(UcTupledStruct[TLBEntryStruct]): + """TLB entry. + """ + + _fields_ = ( + ('paddr', ctypes.c_uint64), + ('perms', ctypes.c_uint32) + ) + + def __load_uc_lib() -> ctypes.CDLL: from pathlib import Path, PurePath @@ -198,7 +209,7 @@ def __set_prototype(fname: str, restype: Type[ctypes._CData], *argtypes: Type[ct __set_prototype('uc_mem_map_ptr', uc_err, uc_engine, u64, size_t, u32, void_p) __set_prototype('uc_mem_protect', uc_err, uc_engine, u64, size_t, u32) __set_prototype('uc_mem_read', uc_err, uc_engine, u64, PTR(char), size_t) - __set_prototype('uc_mem_regions', uc_err, uc_engine, PTR(PTR(_uc_mem_region)), PTR(u32)) + __set_prototype('uc_mem_regions', uc_err, uc_engine, PTR(PTR(uc_mem_region)), PTR(u32)) __set_prototype('uc_mem_unmap', uc_err, uc_engine, u64, size_t) __set_prototype('uc_mem_write', uc_err, uc_engine, u64, PTR(char), size_t) __set_prototype('uc_mmio_map', uc_err, uc_engine, u64, size_t, void_p, void_p, void_p, void_p) @@ -234,6 +245,7 @@ def __set_prototype(fname: str, restype: Type[ctypes._CData], *argtypes: Type[ct HOOK_INSN_INVALID_CFUNC = ctypes.CFUNCTYPE(ctypes.c_bool, uc_engine, ctypes.c_void_p) HOOK_EDGE_GEN_CFUNC = ctypes.CFUNCTYPE(None, uc_engine, ctypes.POINTER(uc_tb), ctypes.POINTER(uc_tb), ctypes.c_void_p) HOOK_TCG_OPCODE_CFUNC = ctypes.CFUNCTYPE(None, uc_engine, ctypes.c_uint64, ctypes.c_uint64, ctypes.c_uint64, ctypes.c_uint32, ctypes.c_void_p) +HOOK_TLB_FILL_CFUNC = ctypes.CFUNCTYPE(ctypes.c_bool, uc_engine, ctypes.c_uint64, ctypes.c_int, ctypes.POINTER(uc_tlb_entry), ctypes.c_void_p) # mmio callback signatures MMIO_READ_CFUNC = ctypes.CFUNCTYPE(ctypes.c_uint64, uc_engine, ctypes.c_uint64, ctypes.c_uint, ctypes.c_void_p) @@ -937,7 +949,7 @@ def mem_regions(self) -> Iterator[Tuple[int, int, int]]: Raises: `UcError` in case an itnernal error has been encountered """ - regions = ctypes.POINTER(_uc_mem_region)() + regions = ctypes.POINTER(uc_mem_region)() count = ctypes.c_uint32() status = uclib.uc_mem_regions(self._uch, ctypes.byref(regions), ctypes.byref(count)) @@ -1092,6 +1104,13 @@ def __hook_tcg_op_cb(uc: Uc, address: int, arg1: int, arg2: int, size: int, key: return (__hook_tcg_op_cb, opcode, flags) + def __hok_tlb_fill(): + @uccallback(self, HOOK_TLB_FILL_CFUNC) + def __hook_tlb_fill_cb(uc: Uc, vaddr: int, access: int, entry: ctypes._Pointer[uc_tlb_entry], key: int): + callback(uc, vaddr, access, entry.contents, user_data) + + return (__hook_tlb_fill_cb,) + handlers: Dict[int, Callable[[], Tuple]] = { uc.UC_HOOK_INTR : __hook_intr, uc.UC_HOOK_INSN : __hook_insn, @@ -1109,7 +1128,8 @@ def __hook_tcg_op_cb(uc: Uc, address: int, arg1: int, arg2: int, size: int, key: # uc.UC_HOOK_MEM_READ_AFTER uc.UC_HOOK_INSN_INVALID : __hook_invalid_insn, uc.UC_HOOK_EDGE_GENERATED : __hook_edge_gen, - uc.UC_HOOK_TCG_OPCODE : __hook_tcg_opcode + uc.UC_HOOK_TCG_OPCODE : __hook_tcg_opcode, + uc.UC_HOOK_TLB_FILL : __hok_tlb_fill } # the same callback may be registered for multiple hook types if they From 16916b2f1ee294967a13e552f248c7ae72bc09ed Mon Sep 17 00:00:00 2001 From: lazymio Date: Sat, 19 Oct 2024 16:32:43 +0800 Subject: [PATCH 29/72] Fix small typo --- bindings/python/unicorn/unicorn_py3/unicorn.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bindings/python/unicorn/unicorn_py3/unicorn.py b/bindings/python/unicorn/unicorn_py3/unicorn.py index c93566b461..9b5f8e0cb5 100644 --- a/bindings/python/unicorn/unicorn_py3/unicorn.py +++ b/bindings/python/unicorn/unicorn_py3/unicorn.py @@ -1104,7 +1104,7 @@ def __hook_tcg_op_cb(uc: Uc, address: int, arg1: int, arg2: int, size: int, key: return (__hook_tcg_op_cb, opcode, flags) - def __hok_tlb_fill(): + def __hook_tlb_fill(): @uccallback(self, HOOK_TLB_FILL_CFUNC) def __hook_tlb_fill_cb(uc: Uc, vaddr: int, access: int, entry: ctypes._Pointer[uc_tlb_entry], key: int): callback(uc, vaddr, access, entry.contents, user_data) @@ -1129,7 +1129,7 @@ def __hook_tlb_fill_cb(uc: Uc, vaddr: int, access: int, entry: ctypes._Pointer[u uc.UC_HOOK_INSN_INVALID : __hook_invalid_insn, uc.UC_HOOK_EDGE_GENERATED : __hook_edge_gen, uc.UC_HOOK_TCG_OPCODE : __hook_tcg_opcode, - uc.UC_HOOK_TLB_FILL : __hok_tlb_fill + uc.UC_HOOK_TLB_FILL : __hook_tlb_fill } # the same callback may be registered for multiple hook types if they From 957df0ec3a9ef85b15f9a54d4a13a17095884b55 Mon Sep 17 00:00:00 2001 From: Eli Date: Fri, 25 Oct 2024 15:22:53 +0300 Subject: [PATCH 30/72] Fix TLB fill hook (#2042) * Fix the TLB fill hook * Add missing annotations --- bindings/python/unicorn/unicorn_py3/unicorn.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/bindings/python/unicorn/unicorn_py3/unicorn.py b/bindings/python/unicorn/unicorn_py3/unicorn.py index 9b5f8e0cb5..e2d844cda4 100644 --- a/bindings/python/unicorn/unicorn_py3/unicorn.py +++ b/bindings/python/unicorn/unicorn_py3/unicorn.py @@ -1048,7 +1048,7 @@ def hook_add(self, htype: int, callback: Callable, user_data: Any = None, begin: def __hook_intr(): @uccallback(self, HOOK_INTR_CFUNC) - def __hook_intr_cb(uc: Uc, intno: int, key: int): + def __hook_intr_cb(uc: Uc, intno: int, key: int) -> None: callback(uc, intno, user_data) return (__hook_intr_cb,) @@ -1061,7 +1061,7 @@ def __hook_insn(): def __hook_code(): @uccallback(self, HOOK_CODE_CFUNC) - def __hook_code_cb(uc: Uc, address: int, size: int, key: int): + def __hook_code_cb(uc: Uc, address: int, size: int, key: int) -> None: callback(uc, address, size, user_data) return (__hook_code_cb,) @@ -1089,14 +1089,14 @@ def __hook_insn_invalid_cb(uc: Uc, key: int) -> bool: def __hook_edge_gen(): @uccallback(self, HOOK_EDGE_GEN_CFUNC) - def __hook_edge_gen_cb(uc: Uc, cur: ctypes._Pointer[uc_tb], prev: ctypes._Pointer[uc_tb], key: int): + def __hook_edge_gen_cb(uc: Uc, cur: ctypes._Pointer[uc_tb], prev: ctypes._Pointer[uc_tb], key: int) -> None: callback(uc, cur.contents, prev.contents, user_data) return (__hook_edge_gen_cb,) def __hook_tcg_opcode(): @uccallback(self, HOOK_TCG_OPCODE_CFUNC) - def __hook_tcg_op_cb(uc: Uc, address: int, arg1: int, arg2: int, size: int, key: int): + def __hook_tcg_op_cb(uc: Uc, address: int, arg1: int, arg2: int, size: int, key: int) -> None: callback(uc, address, arg1, arg2, size, user_data) opcode = ctypes.c_uint64(aux1) @@ -1106,8 +1106,8 @@ def __hook_tcg_op_cb(uc: Uc, address: int, arg1: int, arg2: int, size: int, key: def __hook_tlb_fill(): @uccallback(self, HOOK_TLB_FILL_CFUNC) - def __hook_tlb_fill_cb(uc: Uc, vaddr: int, access: int, entry: ctypes._Pointer[uc_tlb_entry], key: int): - callback(uc, vaddr, access, entry.contents, user_data) + def __hook_tlb_fill_cb(uc: Uc, vaddr: int, access: int, entry: ctypes._Pointer[uc_tlb_entry], key: int) -> bool: + return callback(uc, vaddr, access, entry.contents, user_data) return (__hook_tlb_fill_cb,) From ab23d4ceb0417343949363b813823f64408c66a5 Mon Sep 17 00:00:00 2001 From: PhilippTakacs <76390863+PhilippTakacs@users.noreply.github.com> Date: Thu, 31 Oct 2024 17:02:11 +0100 Subject: [PATCH 31/72] Optimize Notdirty write (#2031) * enable notdirty_write for snapshots when possible Snapshots only happens when the priority of the memory region is smaller then the snapshot_level. After a snapshot notdirty can be set. * disable notdirty_write for self modifying code When SMC access the memory region more then once the tb must be rebuild multible times. fixes #2029 * notdirty_write better hook check Check all relevant memory hooks before enabling notdirty write. This also checks if the memory hook is registered for the affected region. So it is possible to use notdirty write and have some hooks on different addresses. * notdirty_write check for addr_write in snapshot case * self modifying code clear recursive mem access when self modifying code does unaligned memory accese sometimes uc->size_recur_mem is changed but for notdirty write not changed back. This causes mem_hooks to be missed. To fix this uc->size_recur_mem is set to 0 before each cpu_exec() call. --- qemu/accel/tcg/cputlb.c | 23 +++++++------ qemu/accel/tcg/translate-all.c | 2 +- qemu/include/exec/exec-all.h | 23 +++++++++++++ qemu/softmmu/cpus.c | 1 + tests/unit/test_x86.c | 63 ++++++++++++++++++++++++++++++++++ uc.c | 2 ++ 6 files changed, 102 insertions(+), 12 deletions(-) diff --git a/qemu/accel/tcg/cputlb.c b/qemu/accel/tcg/cputlb.c index f3f89aefc3..5206f13231 100644 --- a/qemu/accel/tcg/cputlb.c +++ b/qemu/accel/tcg/cputlb.c @@ -1188,15 +1188,15 @@ tb_page_addr_t get_page_addr_code(CPUArchState *env, target_ulong addr) static void notdirty_write(CPUState *cpu, vaddr mem_vaddr, unsigned size, CPUIOTLBEntry *iotlbentry, uintptr_t retaddr, - MemoryRegion *mr) + CPUTLBEntry *tlbe) { +#ifdef TARGET_ARM + struct uc_struct *uc = cpu->uc; +#endif ram_addr_t ram_addr = mem_vaddr + iotlbentry->addr; + MemoryRegion *mr = cpu->uc->memory_mapping(cpu->uc, tlbe->paddr | (mem_vaddr & ~TARGET_PAGE_MASK)); - if (mr == NULL) { - mr = cpu->uc->memory_mapping(cpu->uc, mem_vaddr); - } - - if ((mr->perms & UC_PROT_EXEC) != 0) { + if (mr && (mr->perms & UC_PROT_EXEC) != 0) { struct page_collection *pages = page_collection_lock(cpu->uc, ram_addr, ram_addr + size); tb_invalidate_phys_page_fast(cpu->uc, pages, ram_addr, size, retaddr); @@ -1208,8 +1208,9 @@ static void notdirty_write(CPUState *cpu, vaddr mem_vaddr, unsigned size, // - have memory hooks installed // - or doing snapshot // , then never clean the tlb - if (!(cpu->uc->snapshot_level > 0 || mr->priority > 0) && - !(HOOK_EXISTS(cpu->uc, UC_HOOK_MEM_READ) || HOOK_EXISTS(cpu->uc, UC_HOOK_MEM_WRITE))) { + if (!(!mr || (tlbe->addr_write != -1 && mr->priority < cpu->uc->snapshot_level)) && + !(tlbe->addr_code != -1) && + !uc_mem_hook_installed(cpu->uc, tlbe->paddr | (mem_vaddr & ~TARGET_PAGE_MASK))) { tlb_set_dirty(cpu, mem_vaddr); } } @@ -1288,7 +1289,7 @@ void *probe_access(CPUArchState *env, target_ulong addr, int size, /* Handle clean RAM pages. */ if (tlb_addr & TLB_NOTDIRTY) { - notdirty_write(env_cpu(env), addr, size, iotlbentry, retaddr, NULL); + notdirty_write(env_cpu(env), addr, size, iotlbentry, retaddr, entry); } } @@ -1414,7 +1415,7 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr, if (unlikely(tlb_addr & TLB_NOTDIRTY)) { notdirty_write(env_cpu(env), addr, 1 << s_bits, - &env_tlb(env)->d[mmu_idx].iotlb[index], retaddr, NULL); + &env_tlb(env)->d[mmu_idx].iotlb[index], retaddr, tlbe); } return hostaddr; @@ -2273,7 +2274,7 @@ store_helper(CPUArchState *env, target_ulong addr, uint64_t val, /* Handle clean RAM pages. */ if (tlb_addr & TLB_NOTDIRTY) { - notdirty_write(env_cpu(env), addr, size, iotlbentry, retaddr, mr); + notdirty_write(env_cpu(env), addr, size, iotlbentry, retaddr, entry); } haddr = (void *)((uintptr_t)addr + entry->addend); diff --git a/qemu/accel/tcg/translate-all.c b/qemu/accel/tcg/translate-all.c index 217522d085..366f3a4f5c 100644 --- a/qemu/accel/tcg/translate-all.c +++ b/qemu/accel/tcg/translate-all.c @@ -1839,7 +1839,7 @@ TranslationBlock *tb_gen_code(CPUState *cpu, } /* Undoes tlb_set_dirty in notdirty_write. */ - if (!(HOOK_EXISTS(cpu->uc, UC_HOOK_MEM_READ) || HOOK_EXISTS(cpu->uc, UC_HOOK_MEM_WRITE))) { + if (!uc_mem_hook_installed(cpu->uc, tb->pc)) { tlb_reset_dirty_by_vaddr(cpu, pc & TARGET_PAGE_MASK, (pc & ~TARGET_PAGE_MASK) + tb->size); } diff --git a/qemu/include/exec/exec-all.h b/qemu/include/exec/exec-all.h index b999c71678..3b23c30430 100644 --- a/qemu/include/exec/exec-all.h +++ b/qemu/include/exec/exec-all.h @@ -477,4 +477,27 @@ address_space_translate_for_iotlb(CPUState *cpu, int asidx, hwaddr addr, hwaddr memory_region_section_get_iotlb(CPUState *cpu, MemoryRegionSection *section); +static inline bool uc_mem_hook_installed(struct uc_struct *uc, target_ulong paddr) +{ + if (HOOK_EXISTS_BOUNDED(uc, UC_HOOK_MEM_FETCH_UNMAPPED, paddr)) + return true; + if (HOOK_EXISTS_BOUNDED(uc, UC_HOOK_MEM_READ_UNMAPPED, paddr)) + return true; + if (HOOK_EXISTS_BOUNDED(uc, UC_HOOK_MEM_READ, paddr)) + return true; + if (HOOK_EXISTS_BOUNDED(uc, UC_HOOK_MEM_READ_PROT, paddr)) + return true; + if (HOOK_EXISTS_BOUNDED(uc, UC_HOOK_MEM_FETCH_PROT, paddr)) + return true; + if (HOOK_EXISTS_BOUNDED(uc, UC_HOOK_MEM_READ_AFTER, paddr)) + return true; + if (HOOK_EXISTS_BOUNDED(uc, UC_HOOK_MEM_WRITE, paddr)) + return true; + if (HOOK_EXISTS_BOUNDED(uc, UC_HOOK_MEM_WRITE_UNMAPPED, paddr)) + return true; + if (HOOK_EXISTS_BOUNDED(uc, UC_HOOK_MEM_WRITE_PROT, paddr)) + return true; + return false; +} + #endif diff --git a/qemu/softmmu/cpus.c b/qemu/softmmu/cpus.c index 22511ac73c..afb8bbedff 100644 --- a/qemu/softmmu/cpus.c +++ b/qemu/softmmu/cpus.c @@ -93,6 +93,7 @@ static int tcg_cpu_exec(struct uc_struct *uc) // (cpu->singlestep_enabled & SSTEP_NOTIMER) == 0); if (cpu_can_run(cpu)) { uc->quit_request = false; + uc->size_recur_mem = 0; r = cpu_exec(uc, cpu); // quit current TB but continue emulating? diff --git a/tests/unit/test_x86.c b/tests/unit/test_x86.c index c3d0b15776..3eee30e8a3 100644 --- a/tests/unit/test_x86.c +++ b/tests/unit/test_x86.c @@ -624,6 +624,67 @@ static void test_x86_smc_xor(void) OK(uc_close(uc)); } +static void test_x86_smc_add(void) +{ + uc_engine *uc; + uint64_t stack_base = 0x20000; + int r_rsp; + /* + * mov qword ptr [rip+0x10], rax + * mov word ptr [rip], 0x0548 + * [orig] mov eax, dword ptr [rax + 0x12345678]; [after SMC] 480578563412 add rax, 0x12345678 + * hlt + */ + char code[] = "\x48\x89\x05\x10\x00\x00\x00\x66\xc7\x05\x00\x00\x00\x00\x48\x05\x8b\x80\x78\x56\x34\x12\xf4"; + uc_common_setup(&uc, UC_ARCH_X86, UC_MODE_64, code, sizeof(code) - 1); + + OK(uc_mem_map(uc, stack_base, 0x2000, UC_PROT_ALL)); + r_rsp = stack_base + 0x1800; + OK(uc_reg_write(uc, UC_X86_REG_RSP, &r_rsp)); + OK(uc_emu_start(uc, code_start, -1, 0, 0)); +} + +static void test_x86_smc_mem_hook_callback(uc_engine *uc, uc_mem_type t, + uint64_t addr, int size, + uint64_t value, void *user_data) +{ + uint64_t write_addresses[] = { 0x1030, 0x1010, 0x1010, 0x1018, 0x1018, 0x1029, 0x1029 }; + unsigned int *i = user_data; + + TEST_CHECK(*i < (sizeof(write_addresses)/sizeof(write_addresses[0]))); + TEST_CHECK(write_addresses[*i] == addr); + (*i)++; +} + +static void test_x86_smc_mem_hook(void) +{ + uc_engine *uc; + uc_hook hook; + uint64_t stack_base = 0x20000; + int r_rsp; + unsigned int i = 0; + /* + * mov qword ptr [rip+0x29], rax + * mov word ptr [rip], 0x0548 + * [orig] mov eax, dword ptr [rax + 0x12345678]; [after SMC] 480578563412 add rax, 0x12345678 + * nop + * nop + * nop + * mov qword ptr [rip-0x08], rax + * mov word ptr [rip], 0x0548 + * [orig] mov eax, dword ptr [rax + 0x12345678]; [after SMC] 480578563412 add rax, 0x12345678 + * hlt + */ + char code[] = "\x48\x89\x05\x29\x00\x00\x00\x66\xC7\x05\x00\x00\x00\x00\x48\x05\x8B\x80\x78\x56\x34\x12\x90\x90\x90\x48\x89\x05\xF8\xFF\xFF\xFF\x66\xC7\x05\x00\x00\x00\x00\x48\x05\x8B\x80\x78\x56\x34\x12\xF4"; + uc_common_setup(&uc, UC_ARCH_X86, UC_MODE_64, code, sizeof(code) - 1); + + OK(uc_hook_add(uc, &hook, UC_HOOK_MEM_WRITE, test_x86_smc_mem_hook_callback, &i, 1, 0)); + OK(uc_mem_map(uc, stack_base, 0x2000, UC_PROT_ALL)); + r_rsp = stack_base + 0x1800; + OK(uc_reg_write(uc, UC_X86_REG_RSP, &r_rsp)); + OK(uc_emu_start(uc, code_start, -1, 0, 0)); +} + static uint64_t test_x86_mmio_uc_mem_rw_read_callback(uc_engine *uc, uint64_t offset, unsigned size, @@ -1849,6 +1910,8 @@ TEST_LIST = { {"test_x86_mmio", test_x86_mmio}, {"test_x86_missing_code", test_x86_missing_code}, {"test_x86_smc_xor", test_x86_smc_xor}, + {"test_x86_smc_add", test_x86_smc_add}, + {"test_x86_smc_mem_hook", test_x86_smc_mem_hook}, {"test_x86_mmio_uc_mem_rw", test_x86_mmio_uc_mem_rw}, {"test_x86_sysenter", test_x86_sysenter}, {"test_x86_hook_cpuid", test_x86_hook_cpuid}, diff --git a/uc.c b/uc.c index 0ff4196f1f..11b6a3f838 100644 --- a/uc.c +++ b/uc.c @@ -2160,6 +2160,7 @@ uc_err uc_context_save(uc_engine *uc, uc_context *context) } context->ramblock_freed = uc->ram_list.freed; context->last_block = uc->ram_list.last_block; + uc->tcg_flush_tlb(uc); } context->snapshot_level = uc->snapshot_level; @@ -2436,6 +2437,7 @@ uc_err uc_context_restore(uc_engine *uc, uc_context *context) if (!uc->flatview_copy(uc, uc->address_space_memory.current_map, context->fv, true)) { return UC_ERR_NOMEM; } + uc->tcg_flush_tlb(uc); } if (uc->context_content & UC_CTL_CONTEXT_CPU) { From f71bc1a115d4c96cd4c7d9e6a2b2b955b59c91dd Mon Sep 17 00:00:00 2001 From: tbodt Date: Sun, 3 Nov 2024 20:53:26 -0800 Subject: [PATCH 32/72] Several bugfixes (#2049) * Remove global variable from aarch64 tcg target This obviously breaks trying to run two unicorn instances at once on aarch64. It appears a similar variable had already been moved to the state struct for i386 tcg target. * Reenable writing to jit region while calling tb_add_jump On arm macs, every place that writes to jit code needs to have tb_exec_unlock called first. This is already in most necessary places, but not this one. * Don't forget to call restore_jit_state in uc_context_restore Every time UC_INIT is used, restore_jit_state must be used on the return path, or occasional assertion failures will pop up on arm macs. * Restore pc before calling into tlb fill hook In my application it is important to have correct pc values available from this hook. --- qemu/accel/tcg/cpu-exec.c | 2 ++ qemu/include/tcg/tcg.h | 1 + qemu/softmmu/unicorn_vtlb.c | 2 ++ qemu/tcg/aarch64/tcg-target.inc.c | 6 ++---- uc.c | 6 +++++- 5 files changed, 12 insertions(+), 5 deletions(-) diff --git a/qemu/accel/tcg/cpu-exec.c b/qemu/accel/tcg/cpu-exec.c index f938cfaa70..9c94efd111 100644 --- a/qemu/accel/tcg/cpu-exec.c +++ b/qemu/accel/tcg/cpu-exec.c @@ -288,7 +288,9 @@ static inline TranslationBlock *tb_find(CPUState *cpu, } /* See if we can patch the calling TB. */ if (last_tb) { + tb_exec_unlock(cpu->uc); tb_add_jump(last_tb, tb_exit, tb); + tb_exec_lock(cpu->uc); } return tb; diff --git a/qemu/include/tcg/tcg.h b/qemu/include/tcg/tcg.h index 9908acaa57..fef0cc455a 100644 --- a/qemu/include/tcg/tcg.h +++ b/qemu/include/tcg/tcg.h @@ -709,6 +709,7 @@ struct TCGContext { TCGv_i64 cpu_bndu[4]; /* qemu/tcg/i386/tcg-target.inc.c */ + /* qemu/tcg/aarch64/tcg-target.inc.c */ void *tb_ret_addr; /* target/riscv/translate.c */ diff --git a/qemu/softmmu/unicorn_vtlb.c b/qemu/softmmu/unicorn_vtlb.c index 8b4e9e0b29..25c684064f 100644 --- a/qemu/softmmu/unicorn_vtlb.c +++ b/qemu/softmmu/unicorn_vtlb.c @@ -55,6 +55,8 @@ bool unicorn_fill_tlb(CPUState *cs, vaddr address, int size, struct hook *hook; HOOK_FOREACH_VAR_DECLARE; + cpu_restore_state(cs, retaddr, false); + HOOK_FOREACH(uc, hook, UC_HOOK_TLB_FILL) { if (hook->to_delete) { continue; diff --git a/qemu/tcg/aarch64/tcg-target.inc.c b/qemu/tcg/aarch64/tcg-target.inc.c index c1f6e108b3..23349c7661 100644 --- a/qemu/tcg/aarch64/tcg-target.inc.c +++ b/qemu/tcg/aarch64/tcg-target.inc.c @@ -1858,8 +1858,6 @@ static void tcg_out_qemu_st(TCGContext *s, TCGReg data_reg, TCGReg addr_reg, #endif /* CONFIG_SOFTMMU */ } -static tcg_insn_unit *tb_ret_addr; - static void tcg_out_op(TCGContext *s, TCGOpcode opc, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) @@ -1885,7 +1883,7 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, tcg_out_goto_long(s, s->code_gen_epilogue); } else { tcg_out_movi(s, TCG_TYPE_I64, TCG_REG_X0, a0); - tcg_out_goto_long(s, tb_ret_addr); + tcg_out_goto_long(s, s->tb_ret_addr); } break; @@ -2859,7 +2857,7 @@ static void tcg_target_qemu_prologue(TCGContext *s) tcg_out_movi(s, TCG_TYPE_REG, TCG_REG_X0, 0); /* TB epilogue */ - tb_ret_addr = s->code_ptr; + s->tb_ret_addr = s->code_ptr; /* Remove TCG locals stack space. */ tcg_out_insn(s, 3401, ADDI, TCG_TYPE_I64, TCG_REG_SP, TCG_REG_SP, diff --git a/uc.c b/uc.c index 11b6a3f838..5f6a35f982 100644 --- a/uc.c +++ b/uc.c @@ -2429,6 +2429,7 @@ uc_err uc_context_restore(uc_engine *uc, uc_context *context) uc->snapshot_level = context->snapshot_level; ret = uc_restore_latest_snapshot(uc); if (ret != UC_ERR_OK) { + restore_jit_state(uc); return ret; } uc_snapshot(uc); @@ -2443,9 +2444,12 @@ uc_err uc_context_restore(uc_engine *uc, uc_context *context) if (uc->context_content & UC_CTL_CONTEXT_CPU) { if (!uc->context_restore) { memcpy(uc->cpu->env_ptr, context->data, context->context_size); + restore_jit_state(uc); return UC_ERR_OK; } else { - return uc->context_restore(uc, context); + ret = uc->context_restore(uc, context); + restore_jit_state(uc); + return ret; } } return UC_ERR_OK; From 7d8fe2ab1164e51448557d1a8b7cf1f6ae632450 Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Mon, 11 Nov 2024 05:09:45 -0800 Subject: [PATCH 33/72] riscv: Expose privilege level as pseudo-register PRIV (#1989) Unlike some other architectures, RISC-V does not expose the current privilege mode in any architecturally-defined register. That is intentional to make it easier to implement virtualization in software, but a Unicorn caller operates outside of the emulated hart and so it can and should be able to observe and change the current privilege mode in order to properly emulate certain behaviors of a real CPU. The current privilege level is therefore now exposed as a new pseudo-register using the name "priv", which matches the name of the virtual register used by RISC-V's debug extension to allow the debugger to read and change the privilege mode while the hart is halted. Unicorn's use of it is conceptually similar to a debugger. The bit encoding of this register is the same as specified in RISC-V Debug Specification v1.0-rc3 Section 4.10.1. It's defined as a "virtual" register exposing a subset of fields from the dcsr register, although here it's implemented directly inside the Unicorn code because QEMU doesn't currently have explicit support for the CSRs from the debug specification. If it supports "dcsr" in a future release then this implementation could change to wrap reading and writing that CSR and then projecting the "prv" and "v" bitfields into the correct locations for the virtual register. --- bindings/dotnet/UnicornEngine/Const/Riscv.fs | 3 +- bindings/go/unicorn/riscv_const.go | 3 +- .../src/main/java/unicorn/RiscvConst.java | 3 +- bindings/pascal/unicorn/RiscvConst.pas | 3 +- bindings/python/unicorn/riscv_const.py | 3 +- .../lib/unicorn_engine/riscv_const.rb | 3 +- bindings/rust/src/riscv.rs | 3 +- bindings/zig/unicorn/riscv_const.zig | 3 +- include/unicorn/riscv.h | 2 + qemu/target/riscv/unicorn.c | 82 +++++++++++++++++++ tests/unit/test_riscv.c | 71 ++++++++++++++++ 11 files changed, 171 insertions(+), 8 deletions(-) diff --git a/bindings/dotnet/UnicornEngine/Const/Riscv.fs b/bindings/dotnet/UnicornEngine/Const/Riscv.fs index fe61ec2c1c..244e5fec45 100644 --- a/bindings/dotnet/UnicornEngine/Const/Riscv.fs +++ b/bindings/dotnet/UnicornEngine/Const/Riscv.fs @@ -222,7 +222,8 @@ module Riscv = let UC_RISCV_REG_F30 = 188 let UC_RISCV_REG_F31 = 189 let UC_RISCV_REG_PC = 190 - let UC_RISCV_REG_ENDING = 191 + let UC_RISCV_REG_PRIV = 191 + let UC_RISCV_REG_ENDING = 192 // Alias registers let UC_RISCV_REG_ZERO = 1 diff --git a/bindings/go/unicorn/riscv_const.go b/bindings/go/unicorn/riscv_const.go index b41ec2322a..08458f77a6 100644 --- a/bindings/go/unicorn/riscv_const.go +++ b/bindings/go/unicorn/riscv_const.go @@ -217,7 +217,8 @@ const ( RISCV_REG_F30 = 188 RISCV_REG_F31 = 189 RISCV_REG_PC = 190 - RISCV_REG_ENDING = 191 + RISCV_REG_PRIV = 191 + RISCV_REG_ENDING = 192 // Alias registers RISCV_REG_ZERO = 1 diff --git a/bindings/java/src/main/java/unicorn/RiscvConst.java b/bindings/java/src/main/java/unicorn/RiscvConst.java index 27b65bd472..5814180974 100644 --- a/bindings/java/src/main/java/unicorn/RiscvConst.java +++ b/bindings/java/src/main/java/unicorn/RiscvConst.java @@ -219,7 +219,8 @@ public interface RiscvConst { public static final int UC_RISCV_REG_F30 = 188; public static final int UC_RISCV_REG_F31 = 189; public static final int UC_RISCV_REG_PC = 190; - public static final int UC_RISCV_REG_ENDING = 191; + public static final int UC_RISCV_REG_PRIV = 191; + public static final int UC_RISCV_REG_ENDING = 192; // Alias registers public static final int UC_RISCV_REG_ZERO = 1; diff --git a/bindings/pascal/unicorn/RiscvConst.pas b/bindings/pascal/unicorn/RiscvConst.pas index 0eb6e7a2b0..075e271c65 100644 --- a/bindings/pascal/unicorn/RiscvConst.pas +++ b/bindings/pascal/unicorn/RiscvConst.pas @@ -220,7 +220,8 @@ interface UC_RISCV_REG_F30 = 188; UC_RISCV_REG_F31 = 189; UC_RISCV_REG_PC = 190; - UC_RISCV_REG_ENDING = 191; + UC_RISCV_REG_PRIV = 191; + UC_RISCV_REG_ENDING = 192; // Alias registers UC_RISCV_REG_ZERO = 1; diff --git a/bindings/python/unicorn/riscv_const.py b/bindings/python/unicorn/riscv_const.py index 1765bfdb73..3e63376fd5 100644 --- a/bindings/python/unicorn/riscv_const.py +++ b/bindings/python/unicorn/riscv_const.py @@ -215,7 +215,8 @@ UC_RISCV_REG_F30 = 188 UC_RISCV_REG_F31 = 189 UC_RISCV_REG_PC = 190 -UC_RISCV_REG_ENDING = 191 +UC_RISCV_REG_PRIV = 191 +UC_RISCV_REG_ENDING = 192 # Alias registers UC_RISCV_REG_ZERO = 1 diff --git a/bindings/ruby/unicorn_gem/lib/unicorn_engine/riscv_const.rb b/bindings/ruby/unicorn_gem/lib/unicorn_engine/riscv_const.rb index 741cfebb1d..33203d0a4d 100644 --- a/bindings/ruby/unicorn_gem/lib/unicorn_engine/riscv_const.rb +++ b/bindings/ruby/unicorn_gem/lib/unicorn_engine/riscv_const.rb @@ -217,7 +217,8 @@ module UnicornEngine UC_RISCV_REG_F30 = 188 UC_RISCV_REG_F31 = 189 UC_RISCV_REG_PC = 190 - UC_RISCV_REG_ENDING = 191 + UC_RISCV_REG_PRIV = 191 + UC_RISCV_REG_ENDING = 192 # Alias registers UC_RISCV_REG_ZERO = 1 diff --git a/bindings/rust/src/riscv.rs b/bindings/rust/src/riscv.rs index 073a4c30d0..53c5990bc3 100644 --- a/bindings/rust/src/riscv.rs +++ b/bindings/rust/src/riscv.rs @@ -201,7 +201,8 @@ pub enum RegisterRISCV { F30 = 188, F31 = 189, PC = 190, - ENDING = 191, + PRIV = 191, + ENDING = 192, } impl RegisterRISCV { diff --git a/bindings/zig/unicorn/riscv_const.zig b/bindings/zig/unicorn/riscv_const.zig index 3e713449c2..00a34001f7 100644 --- a/bindings/zig/unicorn/riscv_const.zig +++ b/bindings/zig/unicorn/riscv_const.zig @@ -217,7 +217,8 @@ pub const riscvConst = enum(c_int) { RISCV_REG_F30 = 188, RISCV_REG_F31 = 189, RISCV_REG_PC = 190, - RISCV_REG_ENDING = 191, + RISCV_REG_PRIV = 191, + RISCV_REG_ENDING = 192, // Alias registers RISCV_REG_ZERO = 1, diff --git a/include/unicorn/riscv.h b/include/unicorn/riscv.h index c4527a4b75..cf1595ae4f 100644 --- a/include/unicorn/riscv.h +++ b/include/unicorn/riscv.h @@ -235,6 +235,8 @@ typedef enum uc_riscv_reg { UC_RISCV_REG_PC, // PC register + UC_RISCV_REG_PRIV, // Virtual register for the current privilege level + UC_RISCV_REG_ENDING, // <-- mark the end of the list or registers //> Alias registers diff --git a/qemu/target/riscv/unicorn.c b/qemu/target/riscv/unicorn.c index 69e782d4f0..80c282580c 100644 --- a/qemu/target/riscv/unicorn.c +++ b/qemu/target/riscv/unicorn.c @@ -79,6 +79,63 @@ static void riscv_release(void *ctx) static void reg_reset(struct uc_struct *uc) {} +static uc_err reg_read_priv(CPURISCVState *env, target_ulong *value) +{ + // This structure is based on RISC-V Debug Specification 1.0.0-rc3, + // Section 4.10.1, Virtual Debug Registers: Privilege Mode. + // This encoding should match the decoding in reg_write_priv. + target_ulong priv_value = 0; + switch (env->priv) { + default: + // No other value should be possible, but we'll report + // 0 (U-Mode) in this case since that's most conservative. + break; + case PRV_U: + priv_value = 0; + break; + case PRV_S: + priv_value = 1; + break; + case PRV_M: + priv_value = 3; + break; + } + if (riscv_cpu_virt_enabled(env)) { + // The "v" bit is set to indicate either VS or VU mode. + priv_value |= 0b100; + } + *value = priv_value; + return UC_ERR_OK; +} + +static uc_err reg_write_priv(CPURISCVState *env, target_ulong value) +{ + // This structure is based on RISC-V Debug Specification 1.0.0-rc3, + // Section 4.10.1, Virtual Debug Registers: Privilege Mode. + // This decoding should match the encoding in reg_read_priv. + if ((value & ~0b111) != 0) { + // Only the low three bits are settable. + return UC_ERR_ARG; + } + target_ulong prv = value & 0b11; + bool v = (value & 0b100) != 0; + switch (prv) { + default: + return UC_ERR_ARG; + case 0: + riscv_cpu_set_mode(env, PRV_U); + break; + case 1: + riscv_cpu_set_mode(env, PRV_S); + break; + case 3: + riscv_cpu_set_mode(env, PRV_M); + break; + } + riscv_cpu_set_virt_enabled(env, v); + return UC_ERR_OK; +} + DEFAULT_VISIBILITY uc_err reg_read(void *_env, int mode, unsigned int regid, void *value, size_t *size) @@ -121,6 +178,20 @@ uc_err reg_read(void *_env, int mode, unsigned int regid, void *value, #else CHECK_REG_TYPE(uint32_t); *(uint32_t *)value = env->pc; +#endif + break; + case UC_RISCV_REG_PRIV:; + target_ulong priv_value; + ret = reg_read_priv(env, &priv_value); + if (ret != UC_ERR_OK) { + return ret; + } +#ifdef TARGET_RISCV64 + CHECK_REG_TYPE(uint64_t); + *(uint64_t *)value = priv_value; +#else + CHECK_REG_TYPE(uint32_t); + *(uint32_t *)value = priv_value; #endif break; } @@ -174,6 +245,17 @@ uc_err reg_write(void *_env, int mode, unsigned int regid, const void *value, #endif *setpc = 1; break; + case UC_RISCV_REG_PRIV: +#ifdef TARGET_RISCV64 + CHECK_REG_TYPE(uint64_t); + uint64_t val; + val = *(uint64_t *)value; +#else + CHECK_REG_TYPE(uint32_t); + uint32_t val; + val = *(uint32_t *)value; +#endif + ret = reg_write_priv(env, (target_ulong)val); } } diff --git a/tests/unit/test_riscv.c b/tests/unit/test_riscv.c index 880f57c927..24b9f35e3a 100644 --- a/tests/unit/test_riscv.c +++ b/tests/unit/test_riscv.c @@ -720,6 +720,76 @@ static void test_riscv_mmu(void) TEST_CHECK(data_value == data_result); } +static void test_riscv_priv(void) +{ + uc_engine *uc; + uc_err err; + uint32_t m_entry_address = 0x1000; + uint32_t main_address = 0x3000; + uint64_t priv_value = ~0; + uint64_t pc = ~0; + uint64_t reg_value; + + /* + li t0, 0 + csrw mstatus, t0 + li t1, 0x3000 + csrw mepc, t1 + mret + */ + char code_m_entry[] = "\x93\x02\x00\x00" + "\x73\x90\x02\x30" + "\x37\x33\x00\x00" + "\x73\x10\x13\x34" + "\x73\x00\x20\x30"; + + /* + csrw sscratch, t0 + nop + */ + char code_main[] = "\x73\x90\x02\x14" + "\x13\x00\x00\x00"; + int main_end_address = main_address + sizeof(code_main) - 1; + + OK(uc_open(UC_ARCH_RISCV, UC_MODE_RISCV64, &uc)); + OK(uc_ctl_tlb_mode(uc, UC_TLB_CPU)); + OK(uc_mem_map(uc, m_entry_address, 0x1000, UC_PROT_ALL)); + OK(uc_mem_map(uc, main_address, 0x1000, UC_PROT_ALL)); + OK(uc_mem_write(uc, m_entry_address, &code_m_entry, sizeof(code_m_entry))); + OK(uc_mem_write(uc, main_address, &code_main, sizeof(code_main))); + + // Before anything executes we should be in M-Mode + OK(uc_reg_read(uc, UC_RISCV_REG_PRIV, &priv_value)); + TEST_ASSERT(priv_value == 3); + + // We'll put a sentinel value in sscratch so we can determine whether we've + // successfully written to it below. + reg_value = 0xffff; + OK(uc_reg_write(uc, UC_RISCV_REG_SSCRATCH, ®_value)); + + // Run until we reach the "csrw" at the start of code_main, at which + // point we should be in U-Mode due to the mret instruction. + OK(uc_emu_start(uc, m_entry_address, main_address, 0, 10)); + + OK(uc_reg_read(uc, UC_RISCV_REG_PC, &pc)); + TEST_ASSERT(pc == main_address); + OK(uc_reg_read(uc, UC_RISCV_REG_PRIV, &priv_value)); + TEST_ASSERT(priv_value == 0); // Now in U-Mode + + // U-Mode can't write to sscratch, so execution at this point should + // cause an invalid instruction exception. + err = uc_emu_start(uc, main_address, main_end_address, 0, 0); + OK(uc_reg_read(uc, UC_RISCV_REG_PC, &pc)); + TEST_ASSERT(err == UC_ERR_EXCEPTION); + + // ...but if we force S-Mode then we should be able to set it successfully. + priv_value = 1; + OK(uc_reg_write(uc, UC_RISCV_REG_PRIV, &priv_value)); + OK(uc_emu_start(uc, main_address, main_end_address, 0, 0)); + OK(uc_reg_read(uc, UC_RISCV_REG_SSCRATCH, ®_value)); + TEST_ASSERT(reg_value == 0); +} + TEST_LIST = { {"test_riscv32_nop", test_riscv32_nop}, {"test_riscv64_nop", test_riscv64_nop}, @@ -744,4 +814,5 @@ TEST_LIST = { {"test_riscv_correct_address_in_long_jump_hook", test_riscv_correct_address_in_long_jump_hook}, {"test_riscv_mmu", test_riscv_mmu}, + {"test_riscv_priv", test_riscv_priv}, {NULL, NULL}}; From 3a015153672ae16d46b15041e3c4b8e3faae4915 Mon Sep 17 00:00:00 2001 From: Sai Ashwin Date: Sat, 7 Dec 2024 12:19:23 +0530 Subject: [PATCH 34/72] Rust Bindings: reg_read_batch and reg_write_batch (#2060) * Added binding for rust reg_{read,write}_batch * Fix reg_write_batch values pointer --- bindings/rust/src/ffi.rs | 12 ++++++++++ bindings/rust/src/lib.rs | 49 ++++++++++++++++++++++++++++++++++++++++ tests/rust-tests/main.rs | 32 ++++++++++++++++++++++++++ 3 files changed, 93 insertions(+) diff --git a/bindings/rust/src/ffi.rs b/bindings/rust/src/ffi.rs index 742e22412c..7f7a205bf9 100644 --- a/bindings/rust/src/ffi.rs +++ b/bindings/rust/src/ffi.rs @@ -23,7 +23,19 @@ extern "C" { pub fn uc_errno(engine: uc_handle) -> uc_error; pub fn uc_strerror(error_code: uc_error) -> *const c_char; pub fn uc_reg_write(engine: uc_handle, regid: c_int, value: *const c_void) -> uc_error; + pub fn uc_reg_write_batch( + engine: uc_handle, + regids: *const c_int, + values: *const *const c_void, + count: c_int, + ) -> uc_error; pub fn uc_reg_read(engine: uc_handle, regid: c_int, value: *mut c_void) -> uc_error; + pub fn uc_reg_read_batch( + engine: uc_handle, + regids: *const c_int, + values: *const *mut c_void, + count: c_int, + ) -> uc_error; pub fn uc_mem_write( engine: uc_handle, address: u64, diff --git a/bindings/rust/src/lib.rs b/bindings/rust/src/lib.rs index a1e7b16925..cd84b4eaa1 100644 --- a/bindings/rust/src/lib.rs +++ b/bindings/rust/src/lib.rs @@ -523,6 +523,28 @@ impl<'a, D> Unicorn<'a, D> { .into() } + /// Write values into batch of registers + pub fn reg_write_batch>( + &self, + regids: &[T], + values: &[u64], + count: i32, + ) -> Result<(), uc_error> { + let mut values_ptrs: Vec<*const u64> = vec![0 as *const u64; count as usize]; + for i in 0..values.len() { + values_ptrs[i as usize] = &values[i] as *const u64; + } + unsafe { + ffi::uc_reg_write_batch( + self.get_handle(), + regids.as_ptr() as *const i32, + values_ptrs.as_ptr() as *const *const c_void, + count, + ) + } + .into() + } + /// Write variable sized values into registers. /// /// The user has to make sure that the buffer length matches the register size. @@ -540,6 +562,33 @@ impl<'a, D> Unicorn<'a, D> { .and(Ok(value)) } + /// Read batch of registers + /// + /// Not to be used with registers larger than 64 bit + pub fn reg_read_batch>( + &self, + regids: &[T], + count: i32, + ) -> Result, uc_error> { + unsafe { + let mut addrs_vec = vec![0u64; count as usize]; + let addrs = addrs_vec.as_mut_slice(); + for i in 0..count { + addrs[i as usize] = &mut addrs[i as usize] as *mut u64 as u64; + } + let res = ffi::uc_reg_read_batch( + self.get_handle(), + regids.as_ptr() as *const i32, + addrs.as_ptr() as *const *mut c_void, + count, + ); + match res { + uc_error::OK => Ok(addrs_vec), + _ => Err(res), + } + } + } + /// Read 128, 256 or 512 bit register value into heap allocated byte array. /// /// This adds safe support for registers >64 bit (GDTR/IDTR, XMM, YMM, ZMM, ST (x86); Q, V (arm64)). diff --git a/tests/rust-tests/main.rs b/tests/rust-tests/main.rs index b35e395f34..1661890e8d 100644 --- a/tests/rust-tests/main.rs +++ b/tests/rust-tests/main.rs @@ -815,3 +815,35 @@ fn x86_tlb_callback() { assert_eq!(emu.remove_hook(tlb_hook), Ok(())); assert_eq!(emu.remove_hook(syscall_hook), Ok(())); } + +#[test] +fn x86_reg_rw_batch() { + // mov rax, 0x10, mov rbx, 0x20, mov rcx, 0x30, mov rdx, 0x40 + let code: Vec = vec![0x48, 0xC7, 0xC0, 0x10, 0x00, 0x00, 0x00, 0x48, 0xC7, 0xC3, 0x20, 0x00, 0x00, 0x00, 0x48, 0xC7, 0xC1, 0x30, 0x00, 0x00, 0x00, 0x48, 0xC7, 0xC2, 0x40, 0x00, 0x00, 0x00]; + let expect: Vec = vec![0x10, 0x20, 0x30, 0x40]; + let mut emu = unicorn_engine::Unicorn::new(Arch::X86, Mode::MODE_64) + .expect("failed to initialize unicorn instance"); + assert_eq!(emu.mem_map(0x1000, 0x1000, Permission::ALL), Ok(())); + assert_eq!(emu.mem_write(0x1000, &code), Ok(())); + + assert_eq!( + emu.emu_start(0x1000, (0x1000 + code.len()) as u64, 0, 0), + Ok(()) + ); + + let regids = vec![RegisterX86::RAX, RegisterX86::RBX, RegisterX86::RCX, RegisterX86::RDX]; + assert_eq!( + emu.reg_read_batch(®ids, 4), + Ok(expect) + ); + let regvals = vec![0x50, 0x60, 0x70, 0x80]; + assert_eq!( + emu.reg_write_batch(®ids, ®vals, 4), + Ok(()) + ); + assert_eq!( + emu.reg_read_batch(®ids, 4), + Ok(vec![0x50, 0x60, 0x70, 0x80]) + ); + +} From f78a3f2f5951ab2869c9a3624a5da0f1356366bf Mon Sep 17 00:00:00 2001 From: "@Antelox" Date: Sat, 7 Dec 2024 07:52:21 +0100 Subject: [PATCH 35/72] Python bindings: Fix editable install + Execute Python2.7 workflow tests (#2044) * Python binding: - Added missing `license` field in pyproject.toml file - Fixed editable mode install and some more code cleanup in setup.py - Refreshed README.md - Replaced f-string formatter in tests with `format` method in order to be py2-compatible - Fixed typos - PEP8 fixes * GitHub Action: Install Python2.7 and run tests for re-tagged wheels on native arch runners only * Python bindings: - Use #x formatter to format hex values --- .github/workflows/build-wheels-publish.yml | 22 +++ bindings/python/README.md | 17 +-- bindings/python/pyproject.toml | 1 + bindings/python/setup.py | 30 +--- bindings/python/tests/test_arm64.py | 5 +- bindings/python/tests/test_ctl.py | 25 +++- .../python/tests/test_network_auditing.py | 2 +- bindings/python/tests/test_x86.py | 9 +- bindings/python/unicorn/unicorn_py2.py | 129 +++++++++++------- .../python/unicorn/unicorn_py3/unicorn.py | 26 ++-- 10 files changed, 152 insertions(+), 114 deletions(-) diff --git a/.github/workflows/build-wheels-publish.yml b/.github/workflows/build-wheels-publish.yml index 4327b86901..651778e31c 100644 --- a/.github/workflows/build-wheels-publish.yml +++ b/.github/workflows/build-wheels-publish.yml @@ -160,7 +160,9 @@ jobs: run: | python -m pip install -U pip wheel && Get-ChildItem -Path wheelhouse/ -Filter *cp38*.whl | Foreach-Object { python -m wheel tags --python-tag='py2' --abi-tag=none $_.FullName + break } + - name: '🚧 Python 2.7 wheels re-tagging - Non-Windows' if: matrix.os != 'windows-2019' env: @@ -168,6 +170,26 @@ jobs: run: | python3 -m pip install -U pip wheel && python3 -m wheel tags --python-tag='py2' --abi-tag=none wheelhouse/*cp38*.whl + - uses: LizardByte/setup-python-action@master + if: (matrix.os == 'ubuntu-latest' && matrix.arch == 'x86_64' && matrix.cibw_build == 'cp38-manylinux') || matrix.os == 'macos-latest' || (matrix.os == 'windows-2019' && matrix.arch == 'AMD64') + with: + python-version: 2.7 + + - name: 'Python 2.7 tests - Windows' + if: matrix.os == 'windows-2019' && matrix.arch == 'AMD64' + run: | + C:\Python27\python.exe -m pip install -U pip pytest && Get-ChildItem -Path wheelhouse/ -Filter *py2*.whl | Foreach-Object { + C:\Python27\python.exe -m pip install $_.FullName + C:\Python27\python.exe -m pytest bindings/python/tests + break + } + + # we install and test python2.7 wheels only on native arch + # NOTE: no python2.7 support for macos-13: https://github.com/LizardByte/setup-python-action/issues/2 + - name: 'Python 2.7 tests - Non-Windows' + if: (matrix.os == 'ubuntu-latest' && matrix.arch == 'x86_64' && matrix.cibw_build == 'cp38-manylinux') || matrix.os == 'macos-latest' + run: python2 -m pip install wheelhouse/*py2*.whl && python2 -m pip install -U pip pytest && python2 -m pytest bindings/python/tests + - uses: actions/upload-artifact@v4 with: name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }}-py38 diff --git a/bindings/python/README.md b/bindings/python/README.md index 21c84432d7..11e6e5b6a8 100644 --- a/bindings/python/README.md +++ b/bindings/python/README.md @@ -25,27 +25,21 @@ Originally written by Nguyen Anh Quynh, polished and redesigned by elicn, mainta Install a prebuilt wheel from PyPI: ```bash -pip3 install unicorn +python3 -m pip install unicorn ``` In case you would like to develop the bindings: ```bash -# Python3 -DEBUG=1 THREADS=4 pip3 install --user -e . +DEBUG=1 THREADS=4 python3 -m pip install --user -e . # Workaround for Pylance -DEBUG=1 THREADS=4 pip3 install --user -e . --config-settings editable_mode=strict -# Python2 -DEBUG=1 THREADS=4 pip install -e . +DEBUG=1 THREADS=4 python3 -m pip install --user -e . --config-settings editable_mode=strict ``` or install it by building it by yourself: ```bash -# Python3 -THREADS=4 pip3 install --user . -# Python2, unfortunately `pip2` doesn't support in-tree build -THREADS=4 python3 setup.py install +THREADS=4 python3 -m pip install --user . ``` Explanations for arguments: @@ -59,4 +53,5 @@ Note that you should setup a valid building environment according to docs/COMPIL ## Python2 compatibility -By default, Unicorn python bindings will be maintained against Python3 as it offers more powerful features which improves developing efficiency. Meanwhile, Unicorn will only keep compatible with all features Unicorn1 offers regarding Python2 because Python2 has reached end-of-life for more than 3 years as the time of writing this README. While offering all features for both Python2 & Python3 is desirable and doable, it inevitably costs too much efforts to maintain and few users really rely on this. Therefore, we assume that if users still stick to Python2, previous Unicorn1 features we offer should be enough. If you really want some new features Unicorn2 offers, please check and pull request to `unicorn/unicorn_py2``. We are happy to review and accept! \ No newline at end of file +By default, Unicorn python bindings works with Python3.7 and above, as it offers more powerful features which improves developing efficiency compared to Python2. However, Unicorn will only keep compatible with all features Unicorn1 offers regarding Python2 because it has reached end-of-life for more than 3 years at the time of writing this README. While offering all features for both Python2 & Python3 is desirable and doable, it inevitably costs too much efforts to maintain and few users really rely on this. Therefore, we assume that if users still stick to Python2, previous Unicorn1 features should be enough. If you really want some new features Unicorn2 offers, please check and pull request to `unicorn/unicorn_py2`. We are happy to review and accept! +Even though the build of wheel packages requires Python3, it's still possible to re-tag the wheel produced from Python3 with `py2` tag and then run `python2 -m pip install `. For detailed commands please refer to our workflow files. diff --git a/bindings/python/pyproject.toml b/bindings/python/pyproject.toml index 6857ca1cbf..8da778ab10 100644 --- a/bindings/python/pyproject.toml +++ b/bindings/python/pyproject.toml @@ -12,6 +12,7 @@ authors = [ description = "Unicorn CPU emulator engine" readme = "README.md" keywords = ["emulation", "qemu", "unicorn"] +license = { text = "BSD License" } classifiers = [ 'License :: OSI Approved :: BSD License', 'Programming Language :: Python :: 2.7', diff --git a/bindings/python/setup.py b/bindings/python/setup.py index 270f215232..b031f69e3f 100755 --- a/bindings/python/setup.py +++ b/bindings/python/setup.py @@ -8,9 +8,8 @@ import subprocess import sys from setuptools import setup -from setuptools.command.build import build +from setuptools.command.build_py import build_py from setuptools.command.sdist import sdist -from setuptools.command.bdist_egg import bdist_egg log = logging.getLogger(__name__) @@ -134,7 +133,7 @@ def run(self): return super().run() -class CustomBuild(build): +class CustomBuild(build_py): def run(self): if 'LIBUNICORN_PATH' in os.environ: log.info("Skipping building C extensions since LIBUNICORN_PATH is set") @@ -144,30 +143,7 @@ def run(self): return super().run() -class CustomBDistEgg(bdist_egg): - def run(self): - self.run_command('build') - return super().run() - - -cmdclass = {'build': CustomBuild, 'sdist': CustomSDist, 'bdist_egg': CustomBDistEgg} - -try: - from setuptools.command.develop import develop - - - class CustomDevelop(develop): - def run(self): - log.info("Building C extensions") - build_libraries() - return super().run() - - - cmdclass['develop'] = CustomDevelop -except ImportError: - print("Proper 'develop' support unavailable.") - setup( - cmdclass=cmdclass, + cmdclass={'build_py': CustomBuild, 'sdist': CustomSDist}, has_ext_modules=lambda: True, # It's not a Pure Python wheel ) diff --git a/bindings/python/tests/test_arm64.py b/bindings/python/tests/test_arm64.py index d615f894a9..e6507b85ae 100755 --- a/bindings/python/tests/test_arm64.py +++ b/bindings/python/tests/test_arm64.py @@ -87,7 +87,8 @@ def test_arm64_read_sctlr(): def test_arm64_hook_mrs(): def _hook_mrs(uc, reg, cp_reg, _): - print(f">>> Hook MRS instruction: reg = 0x{reg:x}(UC_ARM64_REG_X2) cp_reg = {cp_reg}") + print(">>> Hook MRS instruction: reg = {reg:#x}(UC_ARM64_REG_X2) cp_reg = {cp_reg}".format(reg=reg, + cp_reg=cp_reg)) uc.reg_write(reg, 0x114514) print(">>> Write 0x114514 to X") @@ -111,7 +112,7 @@ def _hook_mrs(uc, reg, cp_reg, _): # Start emulation mu.emu_start(0x1000, 0x1000 + len(ARM64_MRS_CODE)) - print(f">>> X2 = {mu.reg_read(UC_ARM64_REG_X2):x}") + print(">>> X2 = {reg:#x}".format(reg=mu.reg_read(UC_ARM64_REG_X2))) except UcError as e: print("ERROR: %s" % e) diff --git a/bindings/python/tests/test_ctl.py b/bindings/python/tests/test_ctl.py index 5a00340c1f..b69df22652 100755 --- a/bindings/python/tests/test_ctl.py +++ b/bindings/python/tests/test_ctl.py @@ -2,6 +2,8 @@ # Sample code for Unicorn. # By Lazymio(@wtdcode), 2021 +import pytest +import sys from unicorn import * from unicorn.x86_const import * from datetime import datetime @@ -20,7 +22,9 @@ def test_uc_ctl_read(): timeout = uc.ctl_get_timeout() - print(f">>> arch={arch} mode={mode} page size={page_size} timeout={timeout}") + print(">>> arch={arch} mode={mode} page size={page_size} timeout={timeout}".format(arch=arch, mode=mode, + page_size=page_size, + timeout=timeout)) def time_emulation(uc, start, end): @@ -31,6 +35,8 @@ def time_emulation(uc, start, end): return (datetime.now() - n).total_seconds() * 1e6 +# TODO: Check if worth adapting the ctl_request_cache method for py2 bindings +@pytest.mark.skipif(sys.version_info < (3, 7), reason="requires python3.7 or higher") def test_uc_ctl_tb_cache(): # Initialize emulator in X86-32bit mode uc = Uc(UC_ARCH_X86, UC_MODE_32) @@ -52,7 +58,7 @@ def test_uc_ctl_tb_cache(): # Now we request cache for all TBs. for i in range(8): tb = uc.ctl_request_cache(addr + i * 512) - print(f">>> TB is cached at {hex(tb[0])} which has {tb[1]} instructions with {tb[2]} bytes") + print(">>> TB is cached at {:#x} which has {} instructions with {} bytes".format(tb[0], tb[1], tb[2])) # Do emulation with all TB cached. cached = time_emulation(uc, addr, addr + len(code)) @@ -63,17 +69,22 @@ def test_uc_ctl_tb_cache(): evicted = time_emulation(uc, addr, addr + len(code)) - print(f">>> Run time: First time {standard}, Cached: {cached}, Cached evicted: {evicted}") + print(">>> Run time: First time {standard}, Cached: {cached}, Cached evicted: {evicted}".format(standard=standard, + cached=cached, + evicted=evicted)) def trace_new_edge(uc, cur, prev, data): - print(f">>> Getting a new edge from {hex(prev.pc + prev.size - 1)} to {hex(cur.pc)}") + print(">>> Getting a new edge from {:#x} to {:#x}".format(prev.pc + prev.size - 1, cur.pc)) def trace_tcg_sub(uc, address, arg1, arg2, size, data): - print(f">>> Get a tcg sub opcode at {hex(address)} with args: {arg1} and {arg2}") + print(">>> Get a tcg sub opcode at {address:#x} with args: {arg1} and {arg2}".format(address=address, arg1=arg1, + arg2=arg2)) +# TODO: Check if worth adapting the hook_add method for py2 bindings +@pytest.mark.skipif(sys.version_info < (3, 7), reason="requires python3.7 or higher") def test_uc_ctl_exits(): uc = Uc(UC_ARCH_X86, UC_MODE_32) addr = 0x1000 @@ -110,7 +121,7 @@ def test_uc_ctl_exits(): eax = uc.reg_read(UC_X86_REG_EAX) ebx = uc.reg_read(UC_X86_REG_EBX) - print(f">>> eax = {hex(eax)} and ebx = {hex(ebx)} after the first emulation") + print(">>> eax = {eax:#x} and ebx = {ebx:#x} after the first emulation".format(eax=eax, ebx=ebx)) # This should stop at ADDRESS + 8, even though we don't provide an exit. uc.emu_start(addr, 0) @@ -118,7 +129,7 @@ def test_uc_ctl_exits(): eax = uc.reg_read(UC_X86_REG_EAX) ebx = uc.reg_read(UC_X86_REG_EBX) - print(f">>> eax = {hex(eax)} and ebx = {hex(ebx)} after the first emulation") + print(">>> eax = {eax:#x} and ebx = {ebx:#x} after the first emulation".format(eax=eax, ebx=ebx)) if __name__ == "__main__": diff --git a/bindings/python/tests/test_network_auditing.py b/bindings/python/tests/test_network_auditing.py index 1d0c7a7875..145e4f6ed4 100755 --- a/bindings/python/tests/test_network_auditing.py +++ b/bindings/python/tests/test_network_auditing.py @@ -356,7 +356,7 @@ def hook_intr(uc, intno, user_data): fd = args[0] how = args[1] - msg = "fd(%d) is shutted down because of %d" % (fd, how) + msg = "fd(%d) is shut down because of %d" % (fd, how) fd_chains.add_log(fd, msg) print_sockcall(msg) diff --git a/bindings/python/tests/test_x86.py b/bindings/python/tests/test_x86.py index f8edce06b0..94c071d38f 100755 --- a/bindings/python/tests/test_x86.py +++ b/bindings/python/tests/test_x86.py @@ -639,13 +639,16 @@ def test_x86_16(): def mmio_read_cb(uc, offset, size, data): - print(f">>> Read IO memory at offset {hex(offset)} with {hex(size)} bytes and return 0x19260817") + print(">>> Read IO memory at offset {offset:#x} with {size:#x} bytes and return 0x19260817".format(offset=offset, + size=size)) return 0x19260817 def mmio_write_cb(uc, offset, size, value, data): - print(f">>> Write value {hex(value)} to IO memory at offset {hex(offset)} with {hex(size)} bytes") + print(">>> Write value {value:#x} to IO memory at offset {offset:#x} with {size:#x} bytes".format(value=value, + offset=offset, + size=size)) def test_i386_mmio(): @@ -668,7 +671,7 @@ def test_i386_mmio(): mu.emu_start(0x10000, 0x10000 + len(X86_MMIO_CODE)) # now print out some registers - print(f">>> Emulation done. ECX={hex(mu.reg_read(UC_X86_REG_ECX))}") + print(">>> Emulation done. ECX={reg:#x}".format(reg=mu.reg_read(UC_X86_REG_ECX))) except UcError as e: print("ERROR: %s" % e) diff --git a/bindings/python/unicorn/unicorn_py2.py b/bindings/python/unicorn/unicorn_py2.py index cc5450f0f2..e93ca96265 100644 --- a/bindings/python/unicorn/unicorn_py2.py +++ b/bindings/python/unicorn/unicorn_py2.py @@ -3,17 +3,16 @@ import ctypes import ctypes.util import distutils.sysconfig -from functools import wraps -import pkg_resources import inspect import os.path +import pkg_resources import sys import weakref -import functools +from functools import wraps, partial from collections import namedtuple -# We can't place this file in a separate folder due to Python2 limitations but -# anyway we just maintain it with minimum efforts and it has been more than 3 +# We can't place this file in a separate folder due to Python2 limitations, but +# anyway we just maintain it with minimum efforts, and it has been more than 3 # years since EOL of Python2 so it should be fine. from . import x86_const, arm_const, arm64_const, unicorn_const as uc @@ -23,16 +22,11 @@ if not hasattr(sys.modules[__name__], "__file__"): __file__ = inspect.getfile(inspect.currentframe()) -_python2 = sys.version_info[0] < 3 -if _python2: - range = xrange - -_lib = { 'darwin': 'libunicorn.2.dylib', - 'win32': 'unicorn.dll', - 'cygwin': 'cygunicorn.dll', - 'linux': 'libunicorn.so.2', - 'linux2': 'libunicorn.so.2' } - +_lib = {'darwin': 'libunicorn.2.dylib', + 'win32': 'unicorn.dll', + 'cygwin': 'cygunicorn.dll', + 'linux': 'libunicorn.so.2', + 'linux2': 'libunicorn.so.2'} # Windows DLL in dependency order _all_windows_dlls = ( @@ -43,6 +37,7 @@ _loaded_windows_dlls = set() + def _load_win_support(path): for dll in _all_windows_dlls: if dll in _loaded_windows_dlls: @@ -51,18 +46,20 @@ def _load_win_support(path): lib_file = os.path.join(path, dll) if ('/' not in path and '\\' not in path) or os.path.exists(lib_file): try: - #print('Trying to load Windows library', lib_file) + # print('Trying to load Windows library', lib_file) ctypes.cdll.LoadLibrary(lib_file) - #print('SUCCESS') + # print('SUCCESS') _loaded_windows_dlls.add(dll) except OSError as e: - #print('FAIL to load %s' %lib_file, e) + # print('FAIL to load %s' %lib_file, e) continue + # Initial attempt: load all dlls globally if sys.platform in ('win32', 'cygwin'): _load_win_support('') + def _load_lib(path): try: if sys.platform in ('win32', 'cygwin'): @@ -70,12 +67,13 @@ def _load_lib(path): lib_file = os.path.join(path, _lib.get(sys.platform, 'libunicorn.so.2')) dll = ctypes.cdll.LoadLibrary(lib_file) - #print('SUCCESS') + # print('SUCCESS') return dll except OSError as e: - #print('FAIL to load %s' %lib_file, e) + # print('FAIL to load %s' %lib_file, e) return None + _uc = None # Loading attempts, in order @@ -95,7 +93,7 @@ def _load_lib(path): os.getenv('PATH', '')] # print(_path_list) -#print("-" * 80) +# print("-" * 80) for _path in _path_list: if _path is None: continue @@ -104,6 +102,7 @@ def _load_lib(path): else: raise ImportError("ERROR: fail to load the dynamic library.") + # __version__ = "%u.%u.%u" % (uc.UC_VERSION_MAJOR, uc.UC_VERSION_MINOR, uc.UC_VERSION_EXTRA) # setup all the function prototype @@ -112,7 +111,9 @@ def _setup_prototype(lib, fname, restype, *argtypes): getattr(lib, fname).restype = restype getattr(lib, fname).argtypes = argtypes except AttributeError: - raise ImportError("ERROR: Fail to setup some function prototypes. Make sure you have cleaned your unicorn1 installation.") + raise ImportError( + "ERROR: Fail to setup some function prototypes. Make sure you have cleaned your unicorn1 installation.") + ucerr = ctypes.c_int uc_mode = ctypes.c_int @@ -121,13 +122,15 @@ def _setup_prototype(lib, fname, restype, *argtypes): uc_context = ctypes.c_void_p uc_hook_h = ctypes.c_size_t + class _uc_mem_region(ctypes.Structure): _fields_ = [ ("begin", ctypes.c_uint64), - ("end", ctypes.c_uint64), + ("end", ctypes.c_uint64), ("perms", ctypes.c_uint32), ] + class uc_tb(ctypes.Structure): """"TranslationBlock""" _fields_ = [ @@ -136,6 +139,7 @@ class uc_tb(ctypes.Structure): ("size", ctypes.c_uint16) ] + _setup_prototype(_uc, "uc_version", ctypes.c_uint, ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_int)) _setup_prototype(_uc, "uc_arch_supported", ctypes.c_bool, ctypes.c_int) _setup_prototype(_uc, "uc_open", ucerr, ctypes.c_uint, ctypes.c_uint, ctypes.POINTER(uc_engine)) @@ -146,12 +150,15 @@ class uc_tb(ctypes.Structure): _setup_prototype(_uc, "uc_reg_write", ucerr, uc_engine, ctypes.c_int, ctypes.c_void_p) _setup_prototype(_uc, "uc_mem_read", ucerr, uc_engine, ctypes.c_uint64, ctypes.POINTER(ctypes.c_char), ctypes.c_size_t) _setup_prototype(_uc, "uc_mem_write", ucerr, uc_engine, ctypes.c_uint64, ctypes.POINTER(ctypes.c_char), ctypes.c_size_t) -_setup_prototype(_uc, "uc_emu_start", ucerr, uc_engine, ctypes.c_uint64, ctypes.c_uint64, ctypes.c_uint64, ctypes.c_size_t) +_setup_prototype(_uc, "uc_emu_start", ucerr, uc_engine, ctypes.c_uint64, ctypes.c_uint64, ctypes.c_uint64, + ctypes.c_size_t) _setup_prototype(_uc, "uc_emu_stop", ucerr, uc_engine) _setup_prototype(_uc, "uc_hook_del", ucerr, uc_engine, uc_hook_h) -_setup_prototype(_uc, "uc_mmio_map", ucerr, uc_engine, ctypes.c_uint64, ctypes.c_size_t, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p) +_setup_prototype(_uc, "uc_mmio_map", ucerr, uc_engine, ctypes.c_uint64, ctypes.c_size_t, ctypes.c_void_p, + ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p) _setup_prototype(_uc, "uc_mem_map", ucerr, uc_engine, ctypes.c_uint64, ctypes.c_size_t, ctypes.c_uint32) -_setup_prototype(_uc, "uc_mem_map_ptr", ucerr, uc_engine, ctypes.c_uint64, ctypes.c_size_t, ctypes.c_uint32, ctypes.c_void_p) +_setup_prototype(_uc, "uc_mem_map_ptr", ucerr, uc_engine, ctypes.c_uint64, ctypes.c_size_t, ctypes.c_uint32, + ctypes.c_void_p) _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_query", ucerr, uc_engine, ctypes.c_uint32, ctypes.POINTER(ctypes.c_size_t)) @@ -163,9 +170,11 @@ class uc_tb(ctypes.Structure): _setup_prototype(_uc, "uc_context_reg_read", ucerr, uc_context, ctypes.c_int, ctypes.c_void_p) _setup_prototype(_uc, "uc_context_reg_write", ucerr, uc_context, ctypes.c_int, ctypes.c_void_p) _setup_prototype(_uc, "uc_context_free", ucerr, uc_context) -_setup_prototype(_uc, "uc_mem_regions", ucerr, uc_engine, ctypes.POINTER(ctypes.POINTER(_uc_mem_region)), ctypes.POINTER(ctypes.c_uint32)) +_setup_prototype(_uc, "uc_mem_regions", ucerr, uc_engine, ctypes.POINTER(ctypes.POINTER(_uc_mem_region)), + ctypes.POINTER(ctypes.c_uint32)) # https://bugs.python.org/issue42880 -_setup_prototype(_uc, "uc_hook_add", ucerr, uc_engine, ctypes.POINTER(uc_hook_h), ctypes.c_int, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_uint64, ctypes.c_uint64) +_setup_prototype(_uc, "uc_hook_add", ucerr, uc_engine, ctypes.POINTER(uc_hook_h), ctypes.c_int, ctypes.c_void_p, + ctypes.c_void_p, ctypes.c_uint64, ctypes.c_uint64) _setup_prototype(_uc, "uc_ctl", ucerr, uc_engine, ctypes.c_int) UC_HOOK_CODE_CB = ctypes.CFUNCTYPE(None, uc_engine, ctypes.c_uint64, ctypes.c_size_t, ctypes.c_void_p) @@ -203,6 +212,7 @@ class uc_tb(ctypes.Structure): None, uc_engine, ctypes.c_uint64, ctypes.c_uint64, ctypes.c_uint64, ctypes.c_void_p ) + # access to error code via @errno of UcError class UcError(Exception): def __init__(self, errno): @@ -232,28 +242,30 @@ def version_bind(): def uc_arch_supported(query): return _uc.uc_arch_supported(query) + # uc_reg_read/write and uc_context_reg_read/write. def reg_read(reg_read_func, arch, reg_id, opt=None): if arch == uc.UC_ARCH_X86: - if reg_id in [x86_const.UC_X86_REG_IDTR, x86_const.UC_X86_REG_GDTR, x86_const.UC_X86_REG_LDTR, x86_const.UC_X86_REG_TR]: + if reg_id in [x86_const.UC_X86_REG_IDTR, x86_const.UC_X86_REG_GDTR, x86_const.UC_X86_REG_LDTR, + x86_const.UC_X86_REG_TR]: reg = uc_x86_mmr() status = reg_read_func(reg_id, ctypes.byref(reg)) if status != uc.UC_ERR_OK: raise UcError(status) return reg.selector, reg.base, reg.limit, reg.flags - if reg_id in range(x86_const.UC_X86_REG_FP0, x86_const.UC_X86_REG_FP0+8): + if reg_id in xrange(x86_const.UC_X86_REG_FP0, x86_const.UC_X86_REG_FP0 + 8): reg = uc_x86_float80() status = reg_read_func(reg_id, ctypes.byref(reg)) if status != uc.UC_ERR_OK: raise UcError(status) return reg.mantissa, reg.exponent - if reg_id in range(x86_const.UC_X86_REG_XMM0, x86_const.UC_X86_REG_XMM0+8): + if reg_id in xrange(x86_const.UC_X86_REG_XMM0, x86_const.UC_X86_REG_XMM0 + 8): reg = uc_x86_xmm() status = reg_read_func(reg_id, ctypes.byref(reg)) if status != uc.UC_ERR_OK: raise UcError(status) return reg.low_qword | (reg.high_qword << 64) - if reg_id in range(x86_const.UC_X86_REG_YMM0, x86_const.UC_X86_REG_YMM0+16): + if reg_id in xrange(x86_const.UC_X86_REG_YMM0, x86_const.UC_X86_REG_YMM0 + 16): reg = uc_x86_ymm() status = reg_read_func(reg_id, ctypes.byref(reg)) if status != uc.UC_ERR_OK: @@ -291,7 +303,8 @@ def reg_read(reg_read_func, arch, reg_id, opt=None): raise UcError(status) return reg.val - elif reg_id in range(arm64_const.UC_ARM64_REG_Q0, arm64_const.UC_ARM64_REG_Q31+1) or range(arm64_const.UC_ARM64_REG_V0, arm64_const.UC_ARM64_REG_V31+1): + elif reg_id in xrange(arm64_const.UC_ARM64_REG_Q0, arm64_const.UC_ARM64_REG_Q31 + 1) or xrange( + arm64_const.UC_ARM64_REG_V0, arm64_const.UC_ARM64_REG_V31 + 1): reg = uc_arm64_neon128() status = reg_read_func(reg_id, ctypes.byref(reg)) if status != uc.UC_ERR_OK: @@ -305,26 +318,28 @@ def reg_read(reg_read_func, arch, reg_id, opt=None): raise UcError(status) return reg.value + def reg_write(reg_write_func, arch, reg_id, value): reg = None if arch == uc.UC_ARCH_X86: - if reg_id in [x86_const.UC_X86_REG_IDTR, x86_const.UC_X86_REG_GDTR, x86_const.UC_X86_REG_LDTR, x86_const.UC_X86_REG_TR]: + if reg_id in [x86_const.UC_X86_REG_IDTR, x86_const.UC_X86_REG_GDTR, x86_const.UC_X86_REG_LDTR, + x86_const.UC_X86_REG_TR]: assert isinstance(value, tuple) and len(value) == 4 reg = uc_x86_mmr() reg.selector = value[0] reg.base = value[1] reg.limit = value[2] reg.flags = value[3] - if reg_id in range(x86_const.UC_X86_REG_FP0, x86_const.UC_X86_REG_FP0+8): + if reg_id in xrange(x86_const.UC_X86_REG_FP0, x86_const.UC_X86_REG_FP0 + 8): reg = uc_x86_float80() reg.mantissa = value[0] reg.exponent = value[1] - if reg_id in range(x86_const.UC_X86_REG_XMM0, x86_const.UC_X86_REG_XMM0+8): + if reg_id in xrange(x86_const.UC_X86_REG_XMM0, x86_const.UC_X86_REG_XMM0 + 8): reg = uc_x86_xmm() reg.low_qword = value & 0xffffffffffffffff reg.high_qword = value >> 64 - if reg_id in range(x86_const.UC_X86_REG_YMM0, x86_const.UC_X86_REG_YMM0+16): + if reg_id in xrange(x86_const.UC_X86_REG_YMM0, x86_const.UC_X86_REG_YMM0 + 16): reg = uc_x86_ymm() reg.first_qword = value & 0xffffffffffffffff reg.second_qword = (value >> 64) & 0xffffffffffffffff @@ -336,7 +351,8 @@ def reg_write(reg_write_func, arch, reg_id, value): reg.value = value[1] if arch == uc.UC_ARCH_ARM64: - if reg_id in range(arm64_const.UC_ARM64_REG_Q0, arm64_const.UC_ARM64_REG_Q31+1) or range(arm64_const.UC_ARM64_REG_V0, arm64_const.UC_ARM64_REG_V31+1): + if reg_id in xrange(arm64_const.UC_ARM64_REG_Q0, arm64_const.UC_ARM64_REG_Q31 + 1) or xrange( + arm64_const.UC_ARM64_REG_V0, arm64_const.UC_ARM64_REG_V31 + 1): reg = uc_arm64_neon128() reg.low_qword = value & 0xffffffffffffffff reg.high_qword = value >> 64 @@ -364,6 +380,7 @@ def reg_write(reg_write_func, arch, reg_id, value): return + def _catch_hook_exception(func): @wraps(func) def wrapper(self, *args, **kwargs): @@ -396,6 +413,7 @@ class uc_arm_cp_reg(ctypes.Structure): ("val", ctypes.c_uint64) ] + class uc_arm64_cp_reg(ctypes.Structure): """ARM64 coprocessors registers for instructions MRS, MSR""" _fields_ = [ @@ -407,21 +425,24 @@ class uc_arm64_cp_reg(ctypes.Structure): ("val", ctypes.c_uint64) ] + class uc_x86_mmr(ctypes.Structure): """Memory-Management Register for instructions IDTR, GDTR, LDTR, TR.""" _fields_ = [ ("selector", ctypes.c_uint16), # not used by GDTR and IDTR - ("base", ctypes.c_uint64), # handle 32 or 64 bit CPUs + ("base", ctypes.c_uint64), # handle 32 or 64 bit CPUs ("limit", ctypes.c_uint32), - ("flags", ctypes.c_uint32), # not used by GDTR and IDTR + ("flags", ctypes.c_uint32), # not used by GDTR and IDTR ] + class uc_x86_msr(ctypes.Structure): _fields_ = [ ("rid", ctypes.c_uint32), ("value", ctypes.c_uint64), ] + class uc_x86_float80(ctypes.Structure): """Float80""" _fields_ = [ @@ -437,6 +458,7 @@ class uc_x86_xmm(ctypes.Structure): ("high_qword", ctypes.c_uint64), ] + class uc_x86_ymm(ctypes.Structure): """256-bit ymm register""" _fields_ = [ @@ -446,6 +468,7 @@ class uc_x86_ymm(ctypes.Structure): ("fourth_qword", ctypes.c_uint64), ] + class uc_arm64_neon128(ctypes.Structure): """128-bit neon register""" _fields_ = [ @@ -453,10 +476,12 @@ class uc_arm64_neon128(ctypes.Structure): ("high_qword", ctypes.c_uint64), ] + # Subclassing ref to allow property assignment. class UcRef(weakref.ref): pass + # This class tracks Uc instance destruction and releases handles. class UcCleanupManager(object): def __init__(self): @@ -486,6 +511,7 @@ def _finalizer(self, ref): del self._refs[id(ref)] ref._class.release_handle(ref._uch) + class Uc(object): _cleanup = UcCleanupManager() @@ -540,11 +566,11 @@ def emu_stop(self): # return the value of a register def reg_read(self, reg_id, opt=None): - return reg_read(functools.partial(_uc.uc_reg_read, self._uch), self._arch, reg_id, opt) + return reg_read(partial(_uc.uc_reg_read, self._uch), self._arch, reg_id, opt) # write to a register def reg_write(self, reg_id, value): - return reg_write(functools.partial(_uc.uc_reg_write, self._uch), self._arch, reg_id, value) + return reg_write(partial(_uc.uc_reg_write, self._uch), self._arch, reg_id, value) # read from MSR - X86 only def msr_read(self, msr_id): @@ -681,7 +707,8 @@ def _hook_insn_sys_cb(self, handle, reg, pcp_reg, user_data): (cb, data) = self._callbacks[user_data] - return cb(self, reg, uc_arm64_cp_reg_tuple(cp_reg.crn, cp_reg.crm, cp_reg.op0, cp_reg.op1, cp_reg.op2, cp_reg.val), data) + return cb(self, reg, + uc_arm64_cp_reg_tuple(cp_reg.crn, cp_reg.crm, cp_reg.op0, cp_reg.op1, cp_reg.op2, cp_reg.val), data) @_catch_hook_exception def _hook_insn_out_cb(self, handle, port, size, value, user_data): @@ -711,7 +738,7 @@ def __ctl_w(self, ctl, nr): return self.__ctl(ctl, nr, uc.UC_CTL_IO_WRITE) def __ctl_rw(self, ctl, nr): - return self.__ctl(ctl, nr, uc.UC_CTL_IO_READ_WRITE) + return self.__ctl(ctl, nr, uc.UC_CTL_IO_READ_WRITE) def __ctl_r_1_arg(self, ctl, ctp): arg = ctp() @@ -795,7 +822,8 @@ def hook_add(self, htype, callback, user_data=None, begin=1, end=0, arg1=0, arg2 cb = ctypes.cast(UC_HOOK_INSN_OUT_CB(self._hook_insn_out_cb), UC_HOOK_INSN_OUT_CB) if arg1 in (x86_const.UC_X86_INS_SYSCALL, x86_const.UC_X86_INS_SYSENTER): # SYSCALL/SYSENTER instruction cb = ctypes.cast(UC_HOOK_INSN_SYSCALL_CB(self._hook_insn_syscall_cb), UC_HOOK_INSN_SYSCALL_CB) - if arg1 in (arm64_const.UC_ARM64_INS_MRS, arm64_const.UC_ARM64_INS_MSR, arm64_const.UC_ARM64_INS_SYS, arm64_const.UC_ARM64_INS_SYSL): + if arg1 in (arm64_const.UC_ARM64_INS_MRS, arm64_const.UC_ARM64_INS_MSR, arm64_const.UC_ARM64_INS_SYS, + arm64_const.UC_ARM64_INS_SYSL): cb = ctypes.cast(UC_HOOK_INSN_SYS_CB(self._hook_insn_sys_cb), UC_HOOK_INSN_SYS_CB) status = _uc.uc_hook_add( self._uch, ctypes.byref(_h2), htype, cb, @@ -807,7 +835,8 @@ def hook_add(self, htype, callback, user_data=None, begin=1, end=0, arg1=0, arg2 flags = ctypes.c_int(arg2) status = _uc.uc_hook_add( - self._uch, ctypes.byref(_h2), htype, ctypes.cast(UC_HOOK_TCG_OPCODE_CB(self._hook_tcg_op_cb), UC_HOOK_TCG_OPCODE_CB), + self._uch, ctypes.byref(_h2), htype, + ctypes.cast(UC_HOOK_TCG_OPCODE_CB(self._hook_tcg_op_cb), UC_HOOK_TCG_OPCODE_CB), ctypes.cast(self._callback_count, ctypes.c_void_p), ctypes.c_uint64(begin), ctypes.c_uint64(end), opcode, flags ) @@ -905,7 +934,7 @@ def mem_regions(self): raise UcError(status) try: - for i in range(count.value): + for i in xrange(count.value): yield (regions[i].begin, regions[i].end, regions[i].perms) finally: _uc.uc_free(regions) @@ -940,11 +969,11 @@ def mode(self): # return the value of a register def reg_read(self, reg_id, opt=None): - return reg_read(functools.partial(_uc.uc_context_reg_read, self._context), self.arch, reg_id, opt) + return reg_read(partial(_uc.uc_context_reg_read, self._context), self.arch, reg_id, opt) # write to a register def reg_write(self, reg_id, value): - return reg_write(functools.partial(_uc.uc_context_reg_write, self._context), self.arch, reg_id, value) + return reg_write(partial(_uc.uc_context_reg_write, self._context), self.arch, reg_id, value) # Make UcContext picklable def __getstate__(self): @@ -990,4 +1019,4 @@ def debug(): return "python-%s-c%u.%u-b%u.%u" % ( all_archs, major, minor, uc.UC_API_MAJOR, uc.UC_API_MINOR - ) \ No newline at end of file + ) diff --git a/bindings/python/unicorn/unicorn_py3/unicorn.py b/bindings/python/unicorn/unicorn_py3/unicorn.py index e2d844cda4..d4a7288b5d 100644 --- a/bindings/python/unicorn/unicorn_py3/unicorn.py +++ b/bindings/python/unicorn/unicorn_py3/unicorn.py @@ -283,7 +283,7 @@ def __str__(self) -> str: def uc_version() -> Tuple[int, int, int]: """Retrieve Unicorn library version. - Returns: a tuple containing major, minor and a combined verion number + Returns: a tuple containing major, minor and a combined version number """ major = ctypes.c_int() @@ -300,7 +300,7 @@ def uc_version() -> Tuple[int, int, int]: def version_bind() -> Tuple[int, int, int]: """Retrieve Unicorn bindings version. - Returns: a tuple containing major, minor and a combined verion number + Returns: a tuple containing major, minor and a combined version number """ major = uc.UC_API_MAJOR @@ -319,7 +319,7 @@ def uc_arch_supported(atype: int) -> bool: def debug() -> str: - """Get verbose verion string. + """Get verbose version string. """ archs = ( @@ -559,7 +559,7 @@ def __seq_tuple(elem: Union[int, Tuple[int, Any]]) -> Tuple[int, Type, Any]: return self._reg_read_batch([__seq_tuple(elem) for elem in reg_data]) def reg_write_batch(self, reg_data: Sequence[Tuple[int, Any]]) -> None: - """Write a sequece of architectural registers. This provides with faster means to + """Write a sequence of architectural registers. This provides with faster means to write multiple registers. Args: @@ -599,7 +599,7 @@ def ucsubclass(cls): # inherit from UcIntel and only then Uc, instead of Uc directly. that is: # Pegasus -> UcIntel -> Uc -> RegStateManager -> object # - # note that all Pegasus subclasses will have the same inheritence chain, + # note that all Pegasus subclasses will have the same inheritance chain, # regardless of the arch and mode the might use to initialize. def __replace(seq: Tuple, item, repl) -> Tuple: @@ -713,7 +713,7 @@ def __init__(self, arch: int, mode: int, cpu: Optional[int] = None) -> None: self._hook_exception: Optional[Exception] = None - # create a finalizer object that will apropriately free up resources when + # create a finalizer object that will appropriately free up resources when # this instance undergoes garbage collection. self.__finalizer = weakref.finalize(self, Uc.release_handle, self._uch) @@ -871,7 +871,7 @@ def mem_unmap(self, address: int, size: int) -> None: # TODO: this is where mmio callbacks need to be released from cache, # but we cannot tell whether this is an mmio range. also, memory ranges - # might be splitted by 'map_protect' after they were mapped, so the + # might be split by 'map_protect' after they were mapped, so the # (start, end) tuple may not be suitable for retrieving the callbacks. # # here we try to do that on a best-effort basis: @@ -910,10 +910,10 @@ def mmio_map(self, address: int, size: int, size : range size (in bytes) read_cb : read callback to invoke upon read access. if not specified, reads \ from the mmio range will be silently dropped - read_ud : optinal context object to pass on to the read callback - write_cb : write callback to invoke unpon a write access. if not specified, writes \ + read_ud : optional context object to pass on to the read callback + write_cb : write callback to invoke upon a write access. if not specified, writes \ to the mmio range will be silently dropped - write_ud : optinal context object to pass on to the write callback + write_ud : optional context object to pass on to the write callback """ @uccallback(self, MMIO_READ_CFUNC) @@ -946,7 +946,7 @@ def mem_regions(self) -> Iterator[Tuple[int, int, int]]: Returns: an iterator whose elements contain begin, end and perms properties of each range - Raises: `UcError` in case an itnernal error has been encountered + Raises: `UcError` in case an internal error has been encountered """ regions = ctypes.POINTER(uc_mem_region)() @@ -1024,7 +1024,7 @@ def __do_hook_add(self, htype: int, fptr: ctypes._FuncPointer, begin: int, end: if status != uc.UC_ERR_OK: raise UcError(status) - # hold a reference to the funcion pointer to prevent it from being gc-ed + # hold a reference to the function pointer to prevent it from being gc-ed self._callbacks[handle.value] = fptr return handle.value @@ -1056,7 +1056,7 @@ def __hook_intr_cb(uc: Uc, intno: int, key: int) -> None: def __hook_insn(): # each arch is expected to overload hook_add and implement this handler on their own. # if we got here, it means this particular architecture does not support hooking any - # instruction and so we fail + # instruction, and so we fail raise UcError(uc.UC_ERR_ARG) def __hook_code(): From 9ec1f604dc1d0e9b393d15e86dd2aa03abbc6fc9 Mon Sep 17 00:00:00 2001 From: lazymio Date: Sat, 7 Dec 2024 15:56:56 +0800 Subject: [PATCH 36/72] Fix regression: Setting eflags within the hook should take effect This add an extra compute_eflags after the hooks --- qemu/target/i386/translate.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/qemu/target/i386/translate.c b/qemu/target/i386/translate.c index 22f0bf4c8e..8ba7909930 100644 --- a/qemu/target/i386/translate.c +++ b/qemu/target/i386/translate.c @@ -4834,6 +4834,9 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) gen_uc_tracecode(tcg_ctx, 0xf1f1f1f1, UC_HOOK_CODE_IDX, env->uc, pc_start); check_exit_request(tcg_ctx); + + // Unicorn: Previous hook might change eflags to any state, let's sync it + gen_compute_eflags(s); } s->override = -1; From 9750d6e2fc85fe867aeb3dc5aff07c95d912a21a Mon Sep 17 00:00:00 2001 From: lazymio Date: Sat, 7 Dec 2024 15:57:51 +0800 Subject: [PATCH 37/72] QoL changes --- bindings/python/setup.py | 2 ++ tests/regress/jumping.py | 6 +++--- tests/regress/regress.py | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/bindings/python/setup.py b/bindings/python/setup.py index b031f69e3f..2b3797a8a3 100755 --- a/bindings/python/setup.py +++ b/bindings/python/setup.py @@ -118,6 +118,8 @@ def build_libraries(): cmake_args = ["cmake", '-B', BUILD_DIR, '-S', UC_DIR, "-DCMAKE_BUILD_TYPE=" + conf] if os.getenv("TRACE"): cmake_args += ["-DUNICORN_TRACER=on"] + if conf == "Debug": + cmake_args += ["-DUNICORN_LOGGING=on"] subprocess.check_call(cmake_args, cwd=UC_DIR) threads = os.getenv("THREADS", "4") subprocess.check_call(["cmake", "--build", ".", "-j" + threads], cwd=BUILD_DIR) diff --git a/tests/regress/jumping.py b/tests/regress/jumping.py index b7bb109058..8df38c7fbd 100755 --- a/tests/regress/jumping.py +++ b/tests/regress/jumping.py @@ -12,9 +12,9 @@ # rdx would never be set to 0xbabe unless we set zf to 1 CODE = ( b"\x48\x31\xc0" # xor rax, rax - b"\x48\xb8\x04\x00\x00\x00\x00\x00\x00\x00" # movabs rax, 0x4 - b"\x48\x3d\x05\x00\x00\x00" # cmp rax, 0x5 <-- never true, zf is cleared - b"\x74\x05" # je 0x1a + b"\x48\xb8\x04\x00\x00\x00\x00\x00\x00\x00" # 03: movabs rax, 0x4 + b"\x48\x3d\x05\x00\x00\x00" # 0d: cmp rax, 0x5 <-- never true, zf is cleared + b"\x74\x05" # 13: je 0x1a b"\xe9\x0f\x00\x00\x00" # jmp 0x29 b"\x48\xba\xbe\xba\x00\x00\x00\x00\x00\x00" # 1a: movabs rdx, 0xbabe <-- never reached unless we set zf b"\xe9\x0f\x00\x00\x00" # jmp 0x38 diff --git a/tests/regress/regress.py b/tests/regress/regress.py index e0f873eabd..00d5bd4b9b 100755 --- a/tests/regress/regress.py +++ b/tests/regress/regress.py @@ -29,7 +29,7 @@ def __setup_logger(name): logger = __setup_logger('UnicornRegress') -logger.setLevel(logging.INFO) +logger.setLevel(os.environ.get("UNICORN_DEBUG", "INFO").upper()) def main(): From c22651c9fe883a8d37009ddcbb22a926f79b73a9 Mon Sep 17 00:00:00 2001 From: lazymio Date: Sat, 7 Dec 2024 16:31:20 +0800 Subject: [PATCH 38/72] Fix regression: We should also sync npc for SPARC --- qemu/target/sparc/translate.c | 1 + 1 file changed, 1 insertion(+) diff --git a/qemu/target/sparc/translate.c b/qemu/target/sparc/translate.c index 63182abb54..2891e7fc2d 100644 --- a/qemu/target/sparc/translate.c +++ b/qemu/target/sparc/translate.c @@ -5964,6 +5964,7 @@ static void sparc_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) // Sync PC in advance tcg_gen_movi_tl(tcg_ctx, tcg_ctx->cpu_pc, dc->pc); + tcg_gen_movi_tl(tcg_ctx, tcg_ctx->cpu_npc, dc->npc); gen_uc_tracecode(tcg_ctx, 4, UC_HOOK_CODE_IDX, uc, dc->pc); // the callback might want to stop emulation immediately From 3b2f54fc610664760b7198ef8ad7dda5bacb8a37 Mon Sep 17 00:00:00 2001 From: lazymio Date: Sat, 7 Dec 2024 17:09:59 +0800 Subject: [PATCH 39/72] Fix regression: We should triage MIPS internal exceptions to Unicorn exceptions --- qemu/accel/tcg/cpu-exec.c | 4 +++- qemu/target/mips/helper.c | 12 ++++++++++++ qemu/target/mips/op_helper.c | 9 +++++++++ 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/qemu/accel/tcg/cpu-exec.c b/qemu/accel/tcg/cpu-exec.c index 9c94efd111..e8d493edd6 100644 --- a/qemu/accel/tcg/cpu-exec.c +++ b/qemu/accel/tcg/cpu-exec.c @@ -408,7 +408,9 @@ static inline bool cpu_handle_exception(CPUState *cpu, int *ret) // Unicorn: If un-catched interrupt, stop executions. if (!catched) { // printf("AAAAAAAAAAAA\n"); qq - uc->invalid_error = UC_ERR_EXCEPTION; + if (uc->invalid_error == UC_ERR_OK) { + uc->invalid_error = UC_ERR_EXCEPTION; + } cpu->halted = 1; *ret = EXCP_HLT; return true; diff --git a/qemu/target/mips/helper.c b/qemu/target/mips/helper.c index 35a512a88a..3c2ba8cec0 100644 --- a/qemu/target/mips/helper.c +++ b/qemu/target/mips/helper.c @@ -535,6 +535,18 @@ static void raise_mmu_exception(CPUMIPSState *env, target_ulong address, #endif cs->exception_index = exception; env->error_code = error_code; + + // Dispatch internal exceptions to Unicorn Exceptions + switch (exception) { + case EXCP_TLBL: + env->uc->invalid_error = UC_ERR_READ_UNMAPPED; + env->uc->invalid_addr = address; + break; + case EXCP_TLBS: + env->uc->invalid_error = UC_ERR_WRITE_UNMAPPED; + env->uc->invalid_addr = address; + break; + } } hwaddr mips_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) diff --git a/qemu/target/mips/op_helper.c b/qemu/target/mips/op_helper.c index fea51a6946..9802b9cebd 100644 --- a/qemu/target/mips/op_helper.c +++ b/qemu/target/mips/op_helper.c @@ -1098,6 +1098,15 @@ void mips_cpu_do_unaligned_access(CPUState *cs, vaddr addr, } } + switch (excp) { + case EXCP_AdEL: + env->uc->invalid_error = UC_ERR_READ_UNALIGNED; + break; + case EXCP_AdES: + env->uc->invalid_error = UC_ERR_WRITE_UNALIGNED; + break; + } + do_raise_exception_err(env, excp, error_code, retaddr); } From 69200d4f00a192f222869403be717a83612223f1 Mon Sep 17 00:00:00 2001 From: lazymio Date: Sat, 7 Dec 2024 17:30:45 +0800 Subject: [PATCH 40/72] Fix regression: If invalid instruction is handled, allow emulation to continue --- qemu/accel/tcg/cpu-exec.c | 3 ++ tests/regress/invalid_insn.py | 71 +++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+) create mode 100644 tests/regress/invalid_insn.py diff --git a/qemu/accel/tcg/cpu-exec.c b/qemu/accel/tcg/cpu-exec.c index e8d493edd6..09148f1dbc 100644 --- a/qemu/accel/tcg/cpu-exec.c +++ b/qemu/accel/tcg/cpu-exec.c @@ -359,6 +359,9 @@ static inline bool cpu_handle_exception(CPUState *cpu, int *ret) // we want to stop emulation *ret = EXCP_HLT; return true; + } else { + // Continue execution because user hints us it has been handled + cpu->exception_index = -1; } } diff --git a/tests/regress/invalid_insn.py b/tests/regress/invalid_insn.py new file mode 100644 index 0000000000..42d74360f8 --- /dev/null +++ b/tests/regress/invalid_insn.py @@ -0,0 +1,71 @@ + +import regress + +from unicorn import * +from unicorn.x86_const import * + +from capstone import * + + +CODE = bytes.fromhex( + '48 31 c0' # xor rax,rax + '48 0f c7 f0' # rdrand rax + 'f4' # hlt +) + +BASE = 0x100000 +PAGE_SIZE = 0x1000 + +# max possible length of a x86 instruction +MAX_INSN_LEN = 15 + + +def hook_invalid_insn(uc, ud): + regress.logger.debug('entered invalid instruction handler') + + pc = uc.reg_read(UC_X86_REG_RIP) + data = uc.mem_read(pc, MAX_INSN_LEN) + + md = Cs(CS_ARCH_X86, CS_MODE_64) + insn = next(md.disasm(data, pc, 1)) + + if insn.mnemonic == 'rdrand': + # chosen by fair dice roll, guaranteed to be random + rax = 4 + + # set result to rax + uc.reg_write(UC_X86_REG_RAX, rax) + + # resume emulation from next instruction + uc.reg_write(UC_X86_REG_RIP, pc + insn.size) + + # signal uc we are ok + return True + + # not handled, uc will crash + return False + + +class TestHooks(regress.RegressTest): + def test_invalid_insn_recover(self): + mu = Uc(UC_ARCH_X86, UC_MODE_64) + + mu.mem_map(BASE, PAGE_SIZE) + mu.mem_write(BASE, CODE) + + mu.hook_add(UC_HOOK_INSN_INVALID, hook_invalid_insn) + + try: + mu.emu_start(BASE, BASE + len(CODE)) + except UcError as ex: + if ex.errno == UC_ERR_INSN_INVALID: + self.fail('invalid instruction did not recover properly') + + # unexpected exception, re-raise + raise + + self.assertNotEqual(0, mu.reg_read(UC_X86_REG_RAX)) + + +if __name__ == '__main__': + regress.main() From b0b412bc6cc815784853ccb03657ae24a3d7b90a Mon Sep 17 00:00:00 2001 From: lazymio Date: Sat, 7 Dec 2024 22:35:28 +0800 Subject: [PATCH 41/72] Only enable JIT protect support with SPRR --- qemu/configure | 1 + 1 file changed, 1 insertion(+) diff --git a/qemu/configure b/qemu/configure index 11f8b0f5ae..bd97f5c8c1 100755 --- a/qemu/configure +++ b/qemu/configure @@ -2171,6 +2171,7 @@ EOF have_sprr='yes' else have_sprr='no' + have_pthread_jit_protect='no' fi fi fi From b7199261b5bd7c937aed6381460b832b974f254d Mon Sep 17 00:00:00 2001 From: lazymio Date: Sat, 7 Dec 2024 22:37:32 +0800 Subject: [PATCH 42/72] No longer install cmake from choco due to Github Action errors --- .github/workflows/Crate-publishing.yml | 2 +- .github/workflows/Nuget-publishing.yml | 2 +- .github/workflows/build-uc2.yml | 4 ++-- .github/workflows/build-wheels-publish.yml | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/Crate-publishing.yml b/.github/workflows/Crate-publishing.yml index 9a7340da8c..19a8029ba8 100644 --- a/.github/workflows/Crate-publishing.yml +++ b/.github/workflows/Crate-publishing.yml @@ -61,7 +61,7 @@ jobs: if: contains(matrix.config.os, 'win') shell: bash run: | - choco install ninja cmake + choco install ninja - name: '🛠️ macOS build dependencies' if: contains(matrix.config.os, 'macOS') diff --git a/.github/workflows/Nuget-publishing.yml b/.github/workflows/Nuget-publishing.yml index 521f07f5c6..79d39c5c9c 100644 --- a/.github/workflows/Nuget-publishing.yml +++ b/.github/workflows/Nuget-publishing.yml @@ -71,7 +71,7 @@ jobs: if: contains(matrix.config.name, 'MSVC 64') shell: bash run: | - choco install ninja cmake + choco install ninja ninja --version cmake --version mkdir build diff --git a/.github/workflows/build-uc2.yml b/.github/workflows/build-uc2.yml index f48595afdd..706278d888 100644 --- a/.github/workflows/build-uc2.yml +++ b/.github/workflows/build-uc2.yml @@ -167,7 +167,7 @@ jobs: if: contains(matrix.config.name, 'MSVC 64') shell: bash run: | - choco install ninja cmake + choco install ninja ninja --version cmake --version mkdir build @@ -193,7 +193,7 @@ jobs: if: contains(matrix.config.name, 'MSVC 32') shell: bash run: | - choco install ninja cmake + choco install ninja ninja --version cmake --version mkdir build diff --git a/.github/workflows/build-wheels-publish.yml b/.github/workflows/build-wheels-publish.yml index 651778e31c..0d44c42d9a 100644 --- a/.github/workflows/build-wheels-publish.yml +++ b/.github/workflows/build-wheels-publish.yml @@ -68,7 +68,7 @@ jobs: if: runner.os == 'Windows' shell: bash run: | - choco install ninja cmake + choco install ninja - name: '🛠️ macOS dependencies' if: runner.os == 'macOS' @@ -264,7 +264,7 @@ jobs: if: runner.os == 'Windows' shell: bash run: | - choco install ninja cmake + choco install ninja - name: '🛠️ macOS dependencies' if: runner.os == 'macOS' From 958ed091536ad2deb5a792f744af24be5be54b6b Mon Sep 17 00:00:00 2001 From: lazymio Date: Sat, 7 Dec 2024 23:33:34 +0800 Subject: [PATCH 43/72] No longer need SPRR and probe it runtime --- qemu/accel/tcg/translate-all.c | 8 ++-- qemu/configure | 7 ++- qemu/include/tcg/tcg-apple-jit.h | 78 ++++++++++++++++++++++++++------ uc.c | 5 +- 4 files changed, 74 insertions(+), 24 deletions(-) diff --git a/qemu/accel/tcg/translate-all.c b/qemu/accel/tcg/translate-all.c index 366f3a4f5c..3f6d2630f7 100644 --- a/qemu/accel/tcg/translate-all.c +++ b/qemu/accel/tcg/translate-all.c @@ -2172,7 +2172,7 @@ void tcg_flush_softmmu_tlb(struct uc_struct *uc) } -#if defined(__APPLE__) && defined(HAVE_PTHREAD_JIT_PROTECT) && defined(HAVE_SPRR) && (defined(__arm__) || defined(__aarch64__)) +#if defined(__APPLE__) && defined(HAVE_PTHREAD_JIT_PROTECT) && (defined(__arm__) || defined(__aarch64__)) static bool tb_exec_is_locked(struct uc_struct *uc) { return uc->current_executable; @@ -2180,13 +2180,11 @@ static bool tb_exec_is_locked(struct uc_struct *uc) static void tb_exec_change(struct uc_struct *uc, bool executable) { - assert(uc->current_executable == thread_executable()); + assert_executable(uc->current_executable); if (uc->current_executable != executable) { jit_write_protect(executable); uc->current_executable = executable; - assert( - executable == thread_executable() - ); + assert_executable(executable); } } #else /* not needed on non-Darwin platforms */ diff --git a/qemu/configure b/qemu/configure index bd97f5c8c1..9d17e009cb 100755 --- a/qemu/configure +++ b/qemu/configure @@ -2155,11 +2155,17 @@ int main() { // In Apple Hypervisor, this value is not accessbile and // pthread_jit_write_protect_np essentially is a no-op + + /* if (!commpage_sprr) { return 1; } else { return 0; } + */ + + // Now it is accessible but always zero, let's probe it runtime. + return 0; } EOF if ! compile_prog ""; then @@ -2171,7 +2177,6 @@ EOF have_sprr='yes' else have_sprr='no' - have_pthread_jit_protect='no' fi fi fi diff --git a/qemu/include/tcg/tcg-apple-jit.h b/qemu/include/tcg/tcg-apple-jit.h index 9c86b3a2df..0cf9146af1 100644 --- a/qemu/include/tcg/tcg-apple-jit.h +++ b/qemu/include/tcg/tcg-apple-jit.h @@ -25,18 +25,18 @@ #ifndef TCG_APPLE_JIT_H #define TCG_APPLE_JIT_H -#if defined(__APPLE__) && defined(HAVE_PTHREAD_JIT_PROTECT) && defined(HAVE_SPRR) && (defined(__arm__) || defined(__aarch64__)) +#include "assert.h" +#include "stdint.h" +#include "stdlib.h" +#include "stdbool.h" -/* write protect enable = write disable */ -static inline void jit_write_protect(int enabled) -{ - return pthread_jit_write_protect_np(enabled); -} +#if defined(__APPLE__) && defined(HAVE_SPRR) && (defined(__arm__) || defined(__aarch64__)) // 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/ +// On Github Action (Virtualized environment), this shall always returns 0 static inline uint64_t read_sprr_perm(void) { uint64_t v; @@ -50,7 +50,11 @@ __attribute__((unused)) static inline uint8_t thread_mask() { uint64_t v = read_sprr_perm(); - return (v >> 20) & 3; + if (v == 0) { + return 0; + } else { + return (v >> 20) & 3; + } } __attribute__((unused)) static inline bool thread_writeable() @@ -63,25 +67,69 @@ __attribute__((unused)) static inline bool thread_executable() return thread_mask() == 1; } +static inline void assert_executable(bool executable) { + uint64_t v = read_sprr_perm(); + + if (!v) { + assert(executable == thread_executable()); + } +} + +#else + +// 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 inline uint64_t read_sprr_perm(void) +{ + return 0; +} + +__attribute__((unused)) static inline uint8_t thread_mask() +{ + return 0; +} + +__attribute__((unused)) static inline bool thread_writeable() +{ + return false; +} + +__attribute__((unused)) static inline bool thread_executable() +{ + return false; +} + +static inline void assert_executable(bool executable) { +} + +#endif + + +#if defined(__APPLE__) && defined(HAVE_PTHREAD_JIT_PROTECT) && defined(HAVE_SPRR) && (defined(__arm__) || defined(__aarch64__)) + +/* write protect enable = write disable */ +static inline void jit_write_protect(int enabled) +{ + return pthread_jit_write_protect_np(enabled); +} + #define JIT_CALLBACK_GUARD(x) \ { \ bool executable = uc->current_executable; \ - assert (executable == thread_executable()); \ + assert_executable(executable); \ x; \ - if (executable != thread_executable()) { \ - jit_write_protect(executable); \ - } \ + jit_write_protect(executable); \ } \ #define JIT_CALLBACK_GUARD_VAR(var, x) \ { \ bool executable = uc->current_executable; \ - assert (executable == thread_executable()); \ + assert_executable(executable); \ var = x; \ - if (executable != thread_executable()) { \ - jit_write_protect(executable); \ - } \ + jit_write_protect(executable); \ } \ diff --git a/uc.c b/uc.c index 5f6a35f982..895bd4fa4a 100644 --- a/uc.c +++ b/uc.c @@ -35,8 +35,7 @@ static void clear_deleted_hooks(uc_engine *uc); static uc_err uc_snapshot(uc_engine *uc); static uc_err uc_restore_latest_snapshot(uc_engine *uc); -#if defined(__APPLE__) && defined(HAVE_PTHREAD_JIT_PROTECT) && \ - defined(HAVE_SPRR) && (defined(__arm__) || defined(__aarch64__)) +#if defined(__APPLE__) && defined(HAVE_PTHREAD_JIT_PROTECT) && (defined(__arm__) || defined(__aarch64__)) static void save_jit_state(uc_engine *uc) { if (!uc->nested) { @@ -51,7 +50,7 @@ static void restore_jit_state(uc_engine *uc) { assert(uc->nested > 0); if (uc->nested == 1) { - assert(uc->current_executable == thread_executable()); + assert_executable(uc->current_executable); if (uc->current_executable != uc->thread_executable_entry) { if (uc->thread_executable_entry) { jit_write_protect(true); From 13a8da8538bf86612d6c0e53632721acb6278cfc Mon Sep 17 00:00:00 2001 From: lazymio Date: Sun, 8 Dec 2024 14:46:28 +0800 Subject: [PATCH 44/72] Check SPRR by issuing MRS --- qemu/configure | 30 ++++++++++++------------------ qemu/include/tcg/tcg-apple-jit.h | 22 ++++++++++------------ 2 files changed, 22 insertions(+), 30 deletions(-) diff --git a/qemu/configure b/qemu/configure index 9d17e009cb..9a480fc78d 100755 --- a/qemu/configure +++ b/qemu/configure @@ -2151,32 +2151,26 @@ EOF cat > $TMPC << EOF #include "stdint.h" int main() { - uint64_t commpage_sprr = (*(uint64_t*)0xFFFFFC10C); - - // In Apple Hypervisor, this value is not accessbile and - // pthread_jit_write_protect_np essentially is a no-op - - /* - if (!commpage_sprr) { - return 1; - } else { - return 0; - } - */ + uint64_t v; - // Now it is accessible but always zero, let's probe it runtime. + __asm__ __volatile__("isb sy\n" + "mrs %0, S3_6_c15_c1_5\n" + : "=r"(v)::"memory"); + // In Apple Hypervisor virtualized environment (EL1), this value is not accessbile + // but pthread_jit_write_protect_np essentially is a no-op. return 0; } EOF if ! compile_prog ""; then - have_sprr='no' + have_sprr_mrs='no' have_pthread_jit_protect='no' else $TMPE if [ $? -eq 0 ]; then - have_sprr='yes' + have_sprr_mrs='yes' else - have_sprr='no' + have_sprr_mrs='no' + have_pthread_jit_protect='no' fi fi fi @@ -2560,8 +2554,8 @@ if test "$have_pthread_jit_protect" = "yes" ; then echo "HAVE_PTHREAD_JIT_PROTECT=y" >> $config_host_mak fi -if test "$have_sprr" = "yes" ; then - echo "HAVE_SPRR=y" >> $config_host_mak +if test "$have_sprr_mrs" = "yes" ; then + echo "HAVE_SPRR_MRS=y" >> $config_host_mak fi # Hold two types of flag: diff --git a/qemu/include/tcg/tcg-apple-jit.h b/qemu/include/tcg/tcg-apple-jit.h index 0cf9146af1..358dd21da1 100644 --- a/qemu/include/tcg/tcg-apple-jit.h +++ b/qemu/include/tcg/tcg-apple-jit.h @@ -30,13 +30,12 @@ #include "stdlib.h" #include "stdbool.h" -#if defined(__APPLE__) && defined(HAVE_SPRR) && (defined(__arm__) || defined(__aarch64__)) - // 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/ // On Github Action (Virtualized environment), this shall always returns 0 +#if defined(HAVE_SPRR_MRS) static inline uint64_t read_sprr_perm(void) { uint64_t v; @@ -45,6 +44,14 @@ static inline uint64_t read_sprr_perm(void) : "=r"(v)::"memory"); return v; } +#else +static inline uint64_t read_sprr_perm(void) +{ + return 0; +} +#endif + +#if defined(__APPLE__) && defined(HAVE_PTHREAD_JIT_PROTECT) && (defined(__arm__) || defined(__aarch64__)) __attribute__((unused)) static inline uint8_t thread_mask() { @@ -77,15 +84,6 @@ static inline void assert_executable(bool executable) { #else -// 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 inline uint64_t read_sprr_perm(void) -{ - return 0; -} - __attribute__((unused)) static inline uint8_t thread_mask() { return 0; @@ -107,7 +105,7 @@ static inline void assert_executable(bool executable) { #endif -#if defined(__APPLE__) && defined(HAVE_PTHREAD_JIT_PROTECT) && defined(HAVE_SPRR) && (defined(__arm__) || defined(__aarch64__)) +#if defined(__APPLE__) && defined(HAVE_PTHREAD_JIT_PROTECT) && (defined(__arm__) || defined(__aarch64__)) /* write protect enable = write disable */ static inline void jit_write_protect(int enabled) From 1ad2da35e63cfa04c6aa0cff69414c0a9e6374de Mon Sep 17 00:00:00 2001 From: mio Date: Fri, 20 Dec 2024 00:50:11 +0800 Subject: [PATCH 45/72] Fix QEMU_UNUSED_FUNC --- qemu/include/tcg/tcg-apple-jit.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/qemu/include/tcg/tcg-apple-jit.h b/qemu/include/tcg/tcg-apple-jit.h index 358dd21da1..ed996de5c8 100644 --- a/qemu/include/tcg/tcg-apple-jit.h +++ b/qemu/include/tcg/tcg-apple-jit.h @@ -27,8 +27,8 @@ #include "assert.h" #include "stdint.h" -#include "stdlib.h" #include "stdbool.h" +#include "qemu/compiler.h" // Returns the S3_6_c15_c1_5 register's value // Taken from @@ -53,7 +53,7 @@ static inline uint64_t read_sprr_perm(void) #if defined(__APPLE__) && defined(HAVE_PTHREAD_JIT_PROTECT) && (defined(__arm__) || defined(__aarch64__)) -__attribute__((unused)) static inline uint8_t thread_mask() +QEMU_UNUSED_FUNC static inline uint8_t thread_mask() { uint64_t v = read_sprr_perm(); @@ -64,12 +64,12 @@ __attribute__((unused)) static inline uint8_t thread_mask() } } -__attribute__((unused)) static inline bool thread_writeable() +QEMU_UNUSED_FUNC static inline bool thread_writeable() { return thread_mask() == 3; } -__attribute__((unused)) static inline bool thread_executable() +QEMU_UNUSED_FUNC static inline bool thread_executable() { return thread_mask() == 1; } @@ -84,17 +84,17 @@ static inline void assert_executable(bool executable) { #else -__attribute__((unused)) static inline uint8_t thread_mask() +QEMU_UNUSED_FUNC static inline uint8_t thread_mask() { return 0; } -__attribute__((unused)) static inline bool thread_writeable() +QEMU_UNUSED_FUNC static inline bool thread_writeable() { return false; } -__attribute__((unused)) static inline bool thread_executable() +QEMU_UNUSED_FUNC static inline bool thread_executable() { return false; } From a540df45a6fcca6b46c24dc7e7a810ea379328d5 Mon Sep 17 00:00:00 2001 From: mio Date: Fri, 20 Dec 2024 00:57:19 +0800 Subject: [PATCH 46/72] Only enable SPRR on physical machines --- qemu/include/tcg/tcg-apple-jit.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qemu/include/tcg/tcg-apple-jit.h b/qemu/include/tcg/tcg-apple-jit.h index ed996de5c8..f471403a9e 100644 --- a/qemu/include/tcg/tcg-apple-jit.h +++ b/qemu/include/tcg/tcg-apple-jit.h @@ -51,7 +51,7 @@ static inline uint64_t read_sprr_perm(void) } #endif -#if defined(__APPLE__) && defined(HAVE_PTHREAD_JIT_PROTECT) && (defined(__arm__) || defined(__aarch64__)) +#if defined(__APPLE__) && defined(HAVE_SPRR_MRS) && defined(HAVE_PTHREAD_JIT_PROTECT) && (defined(__arm__) || defined(__aarch64__)) QEMU_UNUSED_FUNC static inline uint8_t thread_mask() { From 1cbb7b40b23b3e0c26e03c98da10d47622a41e0b Mon Sep 17 00:00:00 2001 From: mio Date: Fri, 20 Dec 2024 01:04:50 +0800 Subject: [PATCH 47/72] Allow have_pthread_jit_protect but not have_sprr_mrs --- qemu/configure | 2 -- 1 file changed, 2 deletions(-) diff --git a/qemu/configure b/qemu/configure index 9a480fc78d..db820ca13b 100755 --- a/qemu/configure +++ b/qemu/configure @@ -2163,14 +2163,12 @@ int main() { EOF if ! compile_prog ""; then have_sprr_mrs='no' - have_pthread_jit_protect='no' else $TMPE if [ $? -eq 0 ]; then have_sprr_mrs='yes' else have_sprr_mrs='no' - have_pthread_jit_protect='no' fi fi fi From 7737e7b4361cd72f7c8aceea37791818fd479e2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20T=C3=B4rres?= Date: Fri, 20 Dec 2024 01:17:23 -0800 Subject: [PATCH 48/72] make i386 instructions RDTSC and RDTSCP hookable (#2066) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * instruction hooks for RDTSC and RDTSCP Signed-off-by: Pedro Tôrres * update hookable instruction list Signed-off-by: Pedro Tôrres * test RDTSC and RDTSCP instruction hooks Signed-off-by: Pedro Tôrres --------- Signed-off-by: Pedro Tôrres Co-authored-by: mio --- bindings/go/unicorn/hook.c | 4 ++ bindings/go/unicorn/hook.go | 8 ++++ bindings/go/unicorn/hook.h | 1 + qemu/target/i386/misc_helper.c | 68 ++++++++++++++++++++++++-- qemu/target/i386/unicorn.c | 5 +- tests/unit/test_x86.c | 88 ++++++++++++++++++++++++++++++++++ 6 files changed, 167 insertions(+), 7 deletions(-) diff --git a/bindings/go/unicorn/hook.c b/bindings/go/unicorn/hook.c index a2b7dc9378..9018425ac3 100644 --- a/bindings/go/unicorn/hook.c +++ b/bindings/go/unicorn/hook.c @@ -40,3 +40,7 @@ void hookX86Out_cgo(uc_engine *handle, uint32_t port, uint32_t size, uint32_t va void hookX86Syscall_cgo(uc_engine *handle, uintptr_t user) { hookX86Syscall(handle, (void *)user); } + +int hookX86Cpuid_cgo(uc_engine *handle, uintptr_t user) { + return hookX86Cpuid(handle, (void *)user); +} diff --git a/bindings/go/unicorn/hook.go b/bindings/go/unicorn/hook.go index 0d7fd6816b..ecd958f9a9 100644 --- a/bindings/go/unicorn/hook.go +++ b/bindings/go/unicorn/hook.go @@ -98,6 +98,12 @@ func hookX86Syscall(handle unsafe.Pointer, user unsafe.Pointer) { hook.Callback.(func(Unicorn))(hook.Uc) } +//export hookX86Cpuid +func hookX86Cpuid(handle unsafe.Pointer, user unsafe.Pointer) bool { + hook := hookMap.get(user) + return hook.Callback.(func(Unicorn) bool)(hook.Uc) +} + func (u *uc) HookAdd(htype int, cb interface{}, begin, end uint64, extra ...int) (Hook, error) { var callback unsafe.Pointer var insn C.int @@ -119,6 +125,8 @@ func (u *uc) HookAdd(htype int, cb interface{}, begin, end uint64, extra ...int) callback = C.hookX86Out_cgo case X86_INS_SYSCALL, X86_INS_SYSENTER: callback = C.hookX86Syscall_cgo + case X86_INS_CPUID, X86_INS_RDTSC, X86_INS_RDTSCP: + callback = C.hookX86Cpuid_cgo default: return 0, errors.New("Unknown instruction type.") } diff --git a/bindings/go/unicorn/hook.h b/bindings/go/unicorn/hook.h index 35813a0ad3..952a1d9e80 100644 --- a/bindings/go/unicorn/hook.h +++ b/bindings/go/unicorn/hook.h @@ -7,3 +7,4 @@ void hookInterrupt_cgo(uc_engine *handle, uint32_t intno, uintptr_t user); uint32_t hookX86In_cgo(uc_engine *handle, uint32_t port, uint32_t size, uintptr_t user); void hookX86Out_cgo(uc_engine *handle, uint32_t port, uint32_t size, uint32_t value, uintptr_t user); void hookX86Syscall_cgo(uc_engine *handle, uintptr_t user); +int hookX86Cpuid_cgo(uc_engine *handle, uintptr_t user); diff --git a/qemu/target/i386/misc_helper.c b/qemu/target/i386/misc_helper.c index 2593321244..a8aec2bfff 100644 --- a/qemu/target/i386/misc_helper.c +++ b/qemu/target/i386/misc_helper.c @@ -209,21 +209,79 @@ void helper_invlpg(CPUX86State *env, target_ulong addr) void helper_rdtsc(CPUX86State *env) { uint64_t val; + uc_engine *uc = env->uc; + struct hook *hook; + int skip_rdtsc = 0; if ((env->cr[4] & CR4_TSD_MASK) && ((env->hflags & HF_CPL_MASK) != 0)) { raise_exception_ra(env, EXCP0D_GPF, GETPC()); } cpu_svm_check_intercept_param(env, SVM_EXIT_RDTSC, 0, GETPC()); - val = cpu_get_tsc(env) + env->tsc_offset; - env->regs[R_EAX] = (uint32_t)(val); - env->regs[R_EDX] = (uint32_t)(val >> 32); + // Unicorn: call registered RDTSC hooks + HOOK_FOREACH_VAR_DECLARE; + HOOK_FOREACH(env->uc, hook, UC_HOOK_INSN) { + if (hook->to_delete) + continue; + if (!HOOK_BOUND_CHECK(hook, env->eip)) + continue; + + // Multiple rdtsc callbacks returning different values is undefined. + // true -> skip the rdtsc instruction + if (hook->insn == UC_X86_INS_RDTSC) { + JIT_CALLBACK_GUARD_VAR(skip_rdtsc, ((uc_cb_insn_cpuid_t)hook->callback)(env->uc, hook->user_data)); + } + + // the last callback may already asked to stop emulation + if (env->uc->stop_request) + break; + } + + if (!skip_rdtsc) { + val = cpu_get_tsc(env) + env->tsc_offset; + env->regs[R_EAX] = (uint32_t)(val); + env->regs[R_EDX] = (uint32_t)(val >> 32); + } } void helper_rdtscp(CPUX86State *env) { - helper_rdtsc(env); - env->regs[R_ECX] = (uint32_t)(env->tsc_aux); + uint64_t val; + uc_engine *uc = env->uc; + struct hook *hook; + int skip_rdtscp = 0; + + if ((env->cr[4] & CR4_TSD_MASK) && ((env->hflags & HF_CPL_MASK) != 0)) { + raise_exception_ra(env, EXCP0D_GPF, GETPC()); + } + cpu_svm_check_intercept_param(env, SVM_EXIT_RDTSC, 0, GETPC()); + + // Unicorn: call registered RDTSCP hooks + HOOK_FOREACH_VAR_DECLARE; + HOOK_FOREACH(env->uc, hook, UC_HOOK_INSN) { + if (hook->to_delete) + continue; + if (!HOOK_BOUND_CHECK(hook, env->eip)) + continue; + + // Multiple rdtscp callbacks returning different values is undefined. + // true -> skip the rdtscp instruction + if (hook->insn == UC_X86_INS_RDTSCP) { + JIT_CALLBACK_GUARD_VAR(skip_rdtscp, ((uc_cb_insn_cpuid_t)hook->callback)(env->uc, hook->user_data)); + } + + // the last callback may already asked to stop emulation + if (env->uc->stop_request) + break; + } + + if (!skip_rdtscp) { + val = cpu_get_tsc(env) + env->tsc_offset; + env->regs[R_EAX] = (uint32_t)(val); + env->regs[R_EDX] = (uint32_t)(val >> 32); + + env->regs[R_ECX] = (uint32_t)(env->tsc_aux); + } } void helper_rdpmc(CPUX86State *env) diff --git a/qemu/target/i386/unicorn.c b/qemu/target/i386/unicorn.c index 3d6968b9b4..a694af467c 100644 --- a/qemu/target/i386/unicorn.c +++ b/qemu/target/i386/unicorn.c @@ -2000,10 +2000,11 @@ static bool x86_stop_interrupt(struct uc_struct *uc, int intno) static bool x86_insn_hook_validate(uint32_t insn_enum) { - // for x86 we can only hook IN, OUT, and SYSCALL + // for x86 we can only hook IN, OUT, SYSCALL, SYSENTER, CPUID, RDTSC, and RDTSCP if (insn_enum != UC_X86_INS_IN && insn_enum != UC_X86_INS_OUT && insn_enum != UC_X86_INS_SYSCALL && insn_enum != UC_X86_INS_SYSENTER && - insn_enum != UC_X86_INS_CPUID) { + insn_enum != UC_X86_INS_CPUID && insn_enum != UC_X86_INS_RDTSC && + insn_enum != UC_X86_INS_RDTSCP) { return false; } return true; diff --git a/tests/unit/test_x86.c b/tests/unit/test_x86.c index 3eee30e8a3..079e986321 100644 --- a/tests/unit/test_x86.c +++ b/tests/unit/test_x86.c @@ -1891,6 +1891,92 @@ static void test_x86_ro_segfault(void) OK(uc_close(uc)); } +static bool test_x86_hook_insn_rdtsc_cb(uc_engine *uc, void *user_data) +{ + uint64_t h = 0x00000000FEDCBA98; + OK(uc_reg_write(uc, UC_X86_REG_RDX, &h)); + + uint64_t l = 0x0000000076543210; + OK(uc_reg_write(uc, UC_X86_REG_RAX, &l)); + + return true; +} + +static void test_x86_hook_insn_rdtsc(void) +{ + char code[] = "\x0F\x31"; // RDTSC + + uc_engine *uc; + uc_common_setup(&uc, UC_ARCH_X86, UC_MODE_64, code, sizeof code - 1); + + uc_hook hook; + OK(uc_hook_add(uc, &hook, UC_HOOK_INSN, test_x86_hook_insn_rdtsc_cb, NULL, + 1, 0, UC_X86_INS_RDTSC)); + + OK(uc_emu_start(uc, code_start, code_start + sizeof code - 1, 0, 0)); + + OK(uc_hook_del(uc, hook)); + + uint64_t h = 0; + OK(uc_reg_read(uc, UC_X86_REG_RDX, &h)); + TEST_CHECK(h == 0x00000000FEDCBA98); + + uint64_t l = 0; + OK(uc_reg_read(uc, UC_X86_REG_RAX, &l)); + TEST_CHECK(l == 0x0000000076543210); + + OK(uc_close(uc)); +} + +static bool test_x86_hook_insn_rdtscp_cb(uc_engine *uc, void *user_data) +{ + uint64_t h = 0x0000000001234567; + OK(uc_reg_write(uc, UC_X86_REG_RDX, &h)); + + uint64_t l = 0x0000000089ABCDEF; + OK(uc_reg_write(uc, UC_X86_REG_RAX, &l)); + + uint64_t i = 0x00000000DEADBEEF; + OK(uc_reg_write(uc, UC_X86_REG_RCX, &i)); + + return true; +} + +static void test_x86_hook_insn_rdtscp(void) +{ + uc_engine *uc; + OK(uc_open(UC_ARCH_X86, UC_MODE_64, &uc)); + + OK(uc_ctl_set_cpu_model(uc, UC_CPU_X86_HASWELL)); + + OK(uc_mem_map(uc, code_start, code_len, UC_PROT_ALL)); + + char code[] = "\x0F\x01\xF9"; // RDTSCP + OK(uc_mem_write(uc, code_start, code, sizeof code - 1)); + + uc_hook hook; + OK(uc_hook_add(uc, &hook, UC_HOOK_INSN, test_x86_hook_insn_rdtscp_cb, NULL, + 1, 0, UC_X86_INS_RDTSCP)); + + OK(uc_emu_start(uc, code_start, code_start + sizeof code - 1, 0, 0)); + + OK(uc_hook_del(uc, hook)); + + uint64_t h = 0; + OK(uc_reg_read(uc, UC_X86_REG_RDX, &h)); + TEST_CHECK(h == 0x0000000001234567); + + uint64_t l = 0; + OK(uc_reg_read(uc, UC_X86_REG_RAX, &l)); + TEST_CHECK(l == 0x0000000089ABCDEF); + + uint64_t i = 0; + OK(uc_reg_read(uc, UC_X86_REG_RCX, &i)); + TEST_CHECK(i == 0x00000000DEADBEEF); + + OK(uc_close(uc)); +} + TEST_LIST = { {"test_x86_in", test_x86_in}, {"test_x86_out", test_x86_out}, @@ -1947,4 +2033,6 @@ TEST_LIST = { {"test_bswap_x64", test_bswap_ax}, {"test_rex_x64", test_rex_x64}, {"test_x86_ro_segfault", test_x86_ro_segfault}, + {"test_x86_hook_insn_rdtsc", test_x86_hook_insn_rdtsc}, + {"test_x86_hook_insn_rdtscp", test_x86_hook_insn_rdtscp}, {NULL, NULL}}; From 28990888443ec2b5538c8d935eb41e139d42583e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20T=C3=B4rres?= Date: Fri, 20 Dec 2024 19:02:28 -0800 Subject: [PATCH 49/72] Allow Statically Linking in Go (#2067) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * unset -L and -rpath from CGO_LDFLAGS Signed-off-by: Pedro Tôrres * allow go statically linking Signed-off-by: Pedro Tôrres * fix setup.py --------- Signed-off-by: Pedro Tôrres Co-authored-by: mio --- CMakeLists.txt | 1 + bindings/go/unicorn/cgo.go | 4 ++ bindings/go/unicorn/cgo_dynamic.go | 6 ++ bindings/go/unicorn/cgo_static.go | 7 +++ bindings/go/unicorn/unicorn.go | 3 - bindings/python/setup.py | 2 +- cmake/bundle_static.cmake | 88 +++++------------------------- 7 files changed, 34 insertions(+), 77 deletions(-) create mode 100644 bindings/go/unicorn/cgo.go create mode 100644 bindings/go/unicorn/cgo_dynamic.go create mode 100644 bindings/go/unicorn/cgo_static.go diff --git a/CMakeLists.txt b/CMakeLists.txt index 2c389bc34f..dd3e347689 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1515,6 +1515,7 @@ if(UNICORN_INSTALL AND NOT MSVC) ) endif() install(FILES $ DESTINATION ${CMAKE_INSTALL_LIBDIR}) + install(FILES $/$ DESTINATION ${CMAKE_INSTALL_LIBDIR}) install(FILES ${UNICORN_HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/unicorn) if (ATOMIC_LINKAGE_FIX) set(ATOMIC_LINK_PKG_CONFIG " -latomic") diff --git a/bindings/go/unicorn/cgo.go b/bindings/go/unicorn/cgo.go new file mode 100644 index 0000000000..88ea004fb6 --- /dev/null +++ b/bindings/go/unicorn/cgo.go @@ -0,0 +1,4 @@ +package unicorn + +// #cgo CFLAGS: -I../../../include -O3 -Wall -Werror +import "C" diff --git a/bindings/go/unicorn/cgo_dynamic.go b/bindings/go/unicorn/cgo_dynamic.go new file mode 100644 index 0000000000..49c3b8cf07 --- /dev/null +++ b/bindings/go/unicorn/cgo_dynamic.go @@ -0,0 +1,6 @@ +//go:build !static + +package unicorn + +// #cgo LDFLAGS: -lunicorn +import "C" diff --git a/bindings/go/unicorn/cgo_static.go b/bindings/go/unicorn/cgo_static.go new file mode 100644 index 0000000000..72cf081dac --- /dev/null +++ b/bindings/go/unicorn/cgo_static.go @@ -0,0 +1,7 @@ +//go:build static + +package unicorn + +// #cgo !darwin LDFLAGS: -lunicorn -lpthread -lm -latomic +// #cgo darwin LDFLAGS: -lunicorn.o +import "C" diff --git a/bindings/go/unicorn/unicorn.go b/bindings/go/unicorn/unicorn.go index 50b221adaa..802c816367 100644 --- a/bindings/go/unicorn/unicorn.go +++ b/bindings/go/unicorn/unicorn.go @@ -7,9 +7,6 @@ import ( ) /* -#cgo CFLAGS: -O3 -Wall -Werror -I../../../include -#cgo LDFLAGS: -L../../../ -lunicorn -Wl,-rpath,${SRCDIR}/../../../ -#cgo linux LDFLAGS: -L../../../ -lunicorn -lrt -Wl,-rpath,${SRCDIR}/../../../ #include #include "uc.h" */ diff --git a/bindings/python/setup.py b/bindings/python/setup.py index 2b3797a8a3..534be5a482 100755 --- a/bindings/python/setup.py +++ b/bindings/python/setup.py @@ -113,7 +113,7 @@ def build_libraries(): obj_dir = os.path.join(BUILD_DIR, conf) shutil.copy(os.path.join(obj_dir, LIBRARY_FILE), LIBS_DIR) - shutil.copy(os.path.join(BUILD_DIR, STATIC_LIBRARY_FILE), LIBS_DIR) + shutil.copy(os.path.join(obj_dir, STATIC_LIBRARY_FILE), LIBS_DIR) else: cmake_args = ["cmake", '-B', BUILD_DIR, '-S', UC_DIR, "-DCMAKE_BUILD_TYPE=" + conf] if os.getenv("TRACE"): diff --git a/cmake/bundle_static.cmake b/cmake/bundle_static.cmake index 613f832c67..0123fe65b0 100644 --- a/cmake/bundle_static.cmake +++ b/cmake/bundle_static.cmake @@ -40,76 +40,18 @@ function(bundle_static_library tgt_name bundled_tgt_name library_name) list(REMOVE_DUPLICATES static_libs) list(REMOVE_DUPLICATES dep_libs) - set(bundled_tgt_full_name - ${CMAKE_BINARY_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}${library_name}${CMAKE_STATIC_LIBRARY_SUFFIX}) - - if (APPLE) - find_program(lib_tool libtool REQUIRED) - - foreach(tgt IN LISTS static_libs) - list(APPEND static_libs_full_names $) - endforeach() - - add_custom_command( - COMMAND ${lib_tool} -static -o ${bundled_tgt_full_name} ${static_libs_full_names} - OUTPUT ${bundled_tgt_full_name} - COMMENT "Bundling ${bundled_tgt_name}" - VERBATIM) - elseif(UNIX OR MINGW) - file(WRITE ${CMAKE_BINARY_DIR}/${bundled_tgt_name}.ar.in - "CREATE ${bundled_tgt_full_name}\n" ) - - foreach(tgt IN LISTS static_libs) - file(APPEND ${CMAKE_BINARY_DIR}/${bundled_tgt_name}.ar.in - "ADDLIB $\n") - endforeach() - - file(APPEND ${CMAKE_BINARY_DIR}/${bundled_tgt_name}.ar.in "SAVE\n") - file(APPEND ${CMAKE_BINARY_DIR}/${bundled_tgt_name}.ar.in "END\n") - - file(GENERATE - OUTPUT ${CMAKE_BINARY_DIR}/${bundled_tgt_name}.ar - INPUT ${CMAKE_BINARY_DIR}/${bundled_tgt_name}.ar.in) - - set(ar_tool ${CMAKE_AR}) - if (CMAKE_INTERPROCEDURAL_OPTIMIZATION) - set(ar_tool ${CMAKE_CXX_COMPILER_AR}) - endif() - - add_custom_command( - COMMAND ${ar_tool} -M < ${CMAKE_BINARY_DIR}/${bundled_tgt_name}.ar - OUTPUT ${bundled_tgt_full_name} - COMMENT "Bundling ${bundled_tgt_name}" - VERBATIM) - elseif(WIN32) - # https://stackoverflow.com/a/38096930/1806760 - get_filename_component(vs_bin_path "${CMAKE_LINKER}" DIRECTORY) - - find_program(lib_tool lib HINTS "${vs_bin_path}" REQUIRED) - - foreach(tgt IN LISTS static_libs) - list(APPEND static_libs_full_names $) - endforeach() - - add_custom_command( - COMMAND ${lib_tool} /NOLOGO /OUT:${bundled_tgt_full_name} ${static_libs_full_names} - OUTPUT ${bundled_tgt_full_name} - COMMENT "Bundling ${bundled_tgt_name}" - VERBATIM) - else() - message(FATAL_ERROR "Unknown bundle scenario!") - endif() - - add_custom_target(bundling_target ALL DEPENDS ${bundled_tgt_full_name}) - add_dependencies(bundling_target ${tgt_name}) - - add_library(${bundled_tgt_name} STATIC IMPORTED) - set_target_properties(${bundled_tgt_name} - PROPERTIES - IMPORTED_LOCATION ${bundled_tgt_full_name} - INTERFACE_INCLUDE_DIRECTORIES $ - INTERFACE_LINK_LIBRARIES "${dep_libs}") - #IMPORTED_LINK_INTERFACE_LIBRARIES "${dep_libs}") # Deprecated - add_dependencies(${bundled_tgt_name} bundling_target) - -endfunction() \ No newline at end of file + foreach(tgt IN LISTS static_libs) + list(APPEND static_libs_objects $) + endforeach() + + add_library(${bundled_tgt_name} STATIC ${static_libs_objects}) + set_target_properties(${bundled_tgt_name} PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES $ + INTERFACE_LINK_LIBRARIES "${dep_libs}" + OUTPUT_NAME "${library_name}" + SYMLINK_NAME "${library_name}.o" + ) + add_custom_command(TARGET ${bundled_tgt_name} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E create_symlink "$" "$/$" + ) +endfunction() From 07e8162cca6b027064c5beefcfca3ba210a72174 Mon Sep 17 00:00:00 2001 From: Aubrey <32604996+Sanae6@users.noreply.github.com> Date: Sun, 29 Dec 2024 08:21:43 -0600 Subject: [PATCH 50/72] improve aarch64 feature gate in rust bindings (#2069) --- Cargo.toml | 4 +++- bindings/rust/src/lib.rs | 6 ++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 862b11dbe7..76db12c3f0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,7 +45,9 @@ dynamic_linkage = [] arch_all = ["arch_x86", "arch_arm", "arch_aarch64", "arch_riscv", "arch_mips", "arch_sparc", "arch_m68k", "arch_ppc", "arch_s390x", "arch_tricore"] arch_x86 = [] arch_arm = [] -arch_aarch64 = [] +# NOTE: unicorn-c only separates on top-level arch name, +# not on the bit-length, so we include both arm and aarch64 +arch_aarch64 = ["arch_arm"] arch_riscv = [] arch_mips = [] arch_sparc = [] diff --git a/bindings/rust/src/lib.rs b/bindings/rust/src/lib.rs index cd84b4eaa1..7cc95a3946 100644 --- a/bindings/rust/src/lib.rs +++ b/bindings/rust/src/lib.rs @@ -54,11 +54,9 @@ mod arm; pub use crate::arm::*; // include arm64 support if conditionally compiled in -// NOTE: unicorn-c only separates on top-level arch name, -// not on the bit-length, so we include both -#[cfg(feature = "arch_arm")] +#[cfg(feature = "arch_aarch64")] mod arm64; -#[cfg(feature = "arch_arm")] +#[cfg(feature = "arch_aarch64")] pub use crate::arm64::*; // include m68k support if conditionally compiled in From 9cfd5cfac34f4912b7115f3e025ce41bfc252592 Mon Sep 17 00:00:00 2001 From: "@Antelox" Date: Sun, 29 Dec 2024 15:24:48 +0100 Subject: [PATCH 51/72] - Improved the GitHub python binding workflow: (#2072) - Added fullMode input in workflow_dispatch - Take decision whether to build either in debug or release mode and if to build for all python versions according to the commit message patterns - Set proper artifact names - Removed not needed steps - Compacted some steps in order to leverage more the matrix feature - Bumped cibuildwheel action to 2.22.0 - Run actual regress tests in place of sample scripts - Specify optional test install in pyproject.toml with proper requirements - Derive package version from git tags - Add GENERATORS env var support in setup.py to specify cmake generator and minor refactoring - Minor cleanup/refactoring for the regress test suite - Marked some regress tests with skipIf to skip them in case of old python versions - Marked some failing regress tests to be checked with skipIf --- .github/workflows/build-wheels-publish.yml | 315 +++++++----------- bindings/python/pyproject.toml | 10 +- bindings/python/setup.py | 17 +- bindings/python/tests/test_arm.py | 1 - bindings/python/tests/test_arm64.py | 1 - bindings/python/tests/test_arm64eb.py | 1 - bindings/python/tests/test_armeb.py | 1 - bindings/python/tests/test_ctl.py | 4 - bindings/python/tests/test_m68k.py | 1 - bindings/python/tests/test_mips.py | 1 - .../python/tests/test_network_auditing.py | 3 - bindings/python/tests/test_ppc.py | 1 - bindings/python/tests/test_riscv.py | 1 - bindings/python/tests/test_shellcode.py | 4 - bindings/python/tests/test_sparc.py | 1 - bindings/python/tests/test_tricore.py | 1 - bindings/python/tests/test_x86.py | 1 - tests/regress/arm64_reg_rw_w0_w30.py | 3 - tests/regress/arm_bx_unmapped.py | 83 +++-- tests/regress/arm_bxeq_hang.py | 3 - tests/regress/arm_fp_vfp_disabled.py | 25 +- tests/regress/arm_init_input_crash.py | 17 +- tests/regress/arm_memcpy_neon.py | 58 ++-- tests/regress/arm_movr12_hang.py | 8 +- tests/regress/arm_vldr_invalid.py | 6 +- tests/regress/arm_wfi_first_insn_of_tb.py | 4 - tests/regress/bad_ram.py | 9 +- tests/regress/callback-pc.py | 6 +- tests/regress/core_ctl.py | 15 +- tests/regress/crash_tb.py | 8 +- tests/regress/deadlock_1.py | 5 +- tests/regress/emu_clear_errors.py | 27 +- tests/regress/emu_stop_segfault.py | 8 +- .../ensure_typedef_consts_generated.py | 5 +- tests/regress/fpu_ip.py | 16 +- tests/regress/fpu_mem_write.py | 7 +- tests/regress/hang.py | 6 +- tests/regress/high_mem.py | 14 +- tests/regress/hook_add_crash.py | 10 +- tests/regress/hook_code_add_del.py | 9 +- tests/regress/hook_code_stop_emu.py | 4 - tests/regress/hook_raises_exception.py | 2 +- tests/regress/hook_readonly_write_local.py | 11 +- tests/regress/init.py | 54 ++- tests/regress/invalid_insn.py | 14 +- tests/regress/invalid_write.py | 7 +- tests/regress/jmp_ebx_hang.py | 7 +- tests/regress/jumping.py | 19 +- tests/regress/leaked_refs.py | 18 +- tests/regress/memmap.py | 5 +- tests/regress/memmap_segfault.py | 4 - tests/regress/mips_branch_delay.py | 13 +- tests/regress/mips_cp1.py | 5 +- tests/regress/mips_except.py | 11 +- tests/regress/mips_kernel_mmu.py | 8 +- tests/regress/mips_single_step_sp.py | 23 +- tests/regress/mips_syscall_pc.py | 10 +- tests/regress/mov_gs_eax.py | 6 +- tests/regress/movsd.py | 8 +- tests/regress/osx_qemu_thread_create_crash.py | 4 +- tests/regress/potential_memory_leak.py | 15 +- tests/regress/pshufb.py | 4 +- tests/regress/reg_write_sign_extension.py | 22 +- tests/regress/regress.py | 54 +-- tests/regress/rep_hook.py | 4 - tests/regress/run_across_bb.py | 18 +- tests/regress/segfault_on_stop.py | 3 +- tests/regress/sparc64.py | 13 +- tests/regress/sparc_reg.py | 78 ++--- .../tcg_liveness_analysis_bug_issue-287.py | 20 +- tests/regress/translator_buffer.py | 20 +- tests/regress/vld.py | 25 +- tests/regress/write_before_map.py | 6 +- tests/regress/wrong_rip.py | 14 +- tests/regress/wrong_rip_arm.py | 23 +- tests/regress/wrong_sp_arm.py | 4 +- tests/regress/x86_64_conditional_jump.py | 3 - tests/regress/x86_64_eflags.py | 4 - tests/regress/x86_64_msr.py | 41 +-- tests/regress/x86_eflags.py | 5 +- tests/regress/x86_fldt_fsqrt.py | 4 - tests/regress/x86_gdt.py | 27 +- tests/regress/x86_ld_crash.py | 7 +- tests/regress/x86_self_modifying.py | 7 +- tests/regress/x86_set_ip.py | 3 - 85 files changed, 539 insertions(+), 834 deletions(-) diff --git a/.github/workflows/build-wheels-publish.yml b/.github/workflows/build-wheels-publish.yml index 0d44c42d9a..1aa61c66db 100644 --- a/.github/workflows/build-wheels-publish.yml +++ b/.github/workflows/build-wheels-publish.yml @@ -1,4 +1,4 @@ -name: Build wheels with cibuildwheel +name: Build and publish wheels with cibuildwheel on: workflow_dispatch: @@ -11,6 +11,14 @@ on: options: - '0' - '1' + fullMode: + description: 'Full Mode' + required: false + default: '' + type: choice + options: + - '0' + - '1' push: paths-ignore: - ".gitignore" @@ -26,12 +34,12 @@ on: env: # Enable DEBUG flag either according to the tag release or manual override - UNICORN_DEBUG: ${{ inputs.debugMode != '' && inputs.debugMode || startsWith(github.ref, 'refs/tags') && '0' || '1' }} + UNICORN_DEBUG: ${{ inputs.debugMode != '' && inputs.debugMode || startsWith(github.ref, 'refs/tags') && '0' || contains(github.event.head_commit.message, 'CI(release)') && '0' || '1' }} jobs: # job to be executed for every push - testing purpose build_wheels_python38_only: - name: Building on ${{ matrix.os }} - ${{ matrix.arch }} ${{ matrix.cibw_build }} + name: Building on ${{ matrix.os }} - ${{ matrix.arch }} - ${{ matrix.cibw_build }} runs-on: ${{ matrix.os }} strategy: fail-fast: false @@ -39,24 +47,35 @@ jobs: include: # NOTE: aarch64 builds are super slow due to QEMU emulation. Making this to parallelize and speed up workflow # i686 - manylinux - - { os: ubuntu-latest, arch: i686, cibw_build: 'cp38-manylinux' } + - { os: ubuntu-latest, arch: i686, cibw_build: 'cp38-manylinux*', cibw_skip: '' } # i686 - musllinux - - { os: ubuntu-latest, arch: i686, cibw_build: 'cp38-musllinux' } + - { os: ubuntu-latest, arch: i686, cibw_build: 'cp38-musllinux*', cibw_skip: '' } # x86_64 - manylinux - - { os: ubuntu-latest, arch: x86_64, cibw_build: 'cp38-manylinux' } + - { os: ubuntu-latest, arch: x86_64, cibw_build: 'cp38-manylinux*', cibw_skip: '' } # x86_64 - musllinux - - { os: ubuntu-latest, arch: x86_64, cibw_build: 'cp38-musllinux' } + - { os: ubuntu-latest, arch: x86_64, cibw_build: 'cp38-musllinux*', cibw_skip: '' } # aarch64 - manylinux - - { os: ubuntu-latest, arch: aarch64, cibw_build: 'cp38-manylinux' } + - { os: ubuntu-latest, arch: aarch64, cibw_build: 'cp38-manylinux*', cibw_skip: '' } # aarch64 - musllinux - - { os: ubuntu-latest, arch: aarch64, cibw_build: 'cp38-musllinux' } - - { os: macos-13, arch: x86_64, cibw_build: '' } - - { os: macos-latest, arch: arm64, cibw_build: '' } - - { os: windows-2019, arch: AMD64, cibw_build: '' } - - { os: windows-2019, arch: x86, cibw_build: '' } + - { os: ubuntu-latest, arch: aarch64, cibw_build: 'cp38-musllinux*', cibw_skip: '' } + - { os: macos-13, arch: x86_64, cibw_build: 'cp38*', cibw_skip: '' } + - { os: macos-latest, arch: arm64, cibw_build: 'cp38*', cibw_skip: '' } + - { os: windows-2019, arch: AMD64, cibw_build: 'cp38*', cibw_skip: '' } + - { os: windows-2019, arch: x86, cibw_build: 'cp38*', cibw_skip: '' } steps: - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + # https://github.com/actions/upload-artifact/issues/22 + - name: Prepare a unique name for Artifacts + shell: bash + run: | + # replace not-allowed chars with dash + name="cibw-wheels-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.cibw_build }}" + name=$(echo -n "$name" | sed -e 's/[ \t:\/\\"<>|*?]/-/g' -e 's/--*/-/g' | sed -e 's/\-$//') + echo "ARTIFACT_NAME=$name" >> $GITHUB_ENV - name: '🛠️ Add msbuild to PATH' if: runner.os == 'Windows' @@ -64,17 +83,6 @@ jobs: with: vs-version: '16.5' - - name: '🛠️ Win build dependencies' - if: runner.os == 'Windows' - shell: bash - run: | - choco install ninja - - - name: '🛠️ macOS dependencies' - if: runner.os == 'macOS' - run: | - brew install ninja - # https://cibuildwheel.pypa.io/en/stable/faq/#macos-building-cpython-38-wheels-on-arm64 - uses: actions/setup-python@v5 if: runner.os == 'macOS' && runner.arch == 'ARM64' @@ -94,60 +102,20 @@ jobs: arch: x64 - name: '🛠️ Set up QEMU' - if: runner.os == 'Linux' + if: runner.os == 'Linux' && matrix.arch != 'x86_64' uses: docker/setup-qemu-action@v3 - - name: '🚧 cibuildwheel run - Linux' - if: matrix.os == 'ubuntu-latest' - uses: pypa/cibuildwheel@v2.21.3 + - name: '🚧 cibuildwheel run' + uses: pypa/cibuildwheel@v2.22.0 env: CIBW_BUILD_FRONTEND: build - CIBW_BUILD: ${{ matrix.cibw_build }}* + CIBW_BUILD: ${{ matrix.cibw_build }} + CIBW_SKIP: ${{ matrix.cibw_skip }} CIBW_ARCHS: ${{ matrix.arch }} CIBW_ENVIRONMENT: DEBUG=${{ env.UNICORN_DEBUG }} CIBW_ENVIRONMENT_PASS_LINUX: DEBUG - CIBW_TEST_REQUIRES: pytest - CIBW_TEST_COMMAND: pytest {package}/tests - with: - package-dir: bindings/python - output-dir: wheelhouse - - - name: '🚧 cibuildwheel run - Windows' - if: matrix.os == 'windows-2019' - uses: pypa/cibuildwheel@v2.21.3 - env: - CIBW_BUILD_FRONTEND: build - CIBW_BUILD: 'cp38*' - CIBW_ARCHS: ${{ matrix.arch }} - CIBW_ENVIRONMENT: DEBUG=${{ env.UNICORN_DEBUG }} - CIBW_TEST_REQUIRES: pytest - CIBW_TEST_COMMAND: pytest {package}/tests - with: - package-dir: bindings/python - output-dir: wheelhouse - - - name: '🚧 cibuildwheel run - MacOS x86_84' - if: matrix.os == 'macos-13' - uses: pypa/cibuildwheel@v2.21.3 - env: - CIBW_BUILD_FRONTEND: build - CIBW_BUILD: 'cp38*' - CIBW_ENVIRONMENT: SYSTEM_VERSION_COMPAT=0 DEBUG=${{ env.UNICORN_DEBUG }} - CIBW_TEST_REQUIRES: pytest - CIBW_TEST_COMMAND: pytest {package}/tests - with: - package-dir: bindings/python - output-dir: wheelhouse - - - name: '🚧 cibuildwheel run - MacOS arm64' - if: matrix.os == 'macos-latest' - uses: pypa/cibuildwheel@v2.21.3 - env: - CIBW_BUILD_FRONTEND: build - CIBW_BUILD: 'cp38*' - CIBW_ENVIRONMENT: DEBUG=${{ env.UNICORN_DEBUG }} - CIBW_TEST_REQUIRES: pytest - CIBW_TEST_COMMAND: pytest {package}/tests + CIBW_TEST_EXTRAS: test + CIBW_TEST_COMMAND: 'python -m unittest discover -v {project}/tests/regress "*.py"' # https://github.com/pypa/cibuildwheel/pull/1169 CIBW_TEST_SKIP: "cp38-macosx_*:arm64" with: @@ -155,49 +123,42 @@ jobs: output-dir: wheelhouse # we re-tag cp38 wheel (just an old one) with py2 tag. Hacky but it works... - - name: '🚧 Python 2.7 wheels re-tagging - Windows' - if: matrix.os == 'windows-2019' - run: | - python -m pip install -U pip wheel && Get-ChildItem -Path wheelhouse/ -Filter *cp38*.whl | Foreach-Object { - python -m wheel tags --python-tag='py2' --abi-tag=none $_.FullName - break - } - - - name: '🚧 Python 2.7 wheels re-tagging - Non-Windows' - if: matrix.os != 'windows-2019' + - name: '🚧 Python 2.7 wheels re-tagging' env: PIP_BREAK_SYSTEM_PACKAGES: 1 + shell: bash run: | - python3 -m pip install -U pip wheel && python3 -m wheel tags --python-tag='py2' --abi-tag=none wheelhouse/*cp38*.whl + python3 -m pip install -U pip wheel + python3 -m wheel tags --python-tag='py2' --abi-tag=none wheelhouse/*cp38*.whl - - uses: LizardByte/setup-python-action@master - if: (matrix.os == 'ubuntu-latest' && matrix.arch == 'x86_64' && matrix.cibw_build == 'cp38-manylinux') || matrix.os == 'macos-latest' || (matrix.os == 'windows-2019' && matrix.arch == 'AMD64') + - uses: LizardByte/setup-python-action@v2024.919.163656 + if: (matrix.os == 'ubuntu-latest' && matrix.arch == 'x86_64' && matrix.cibw_build == 'cp38-manylinux*') || matrix.os == 'macos-latest' || (matrix.os == 'windows-2019' && matrix.arch == 'AMD64') with: python-version: 2.7 + # we install and test python2.7 wheels only on native arch - name: 'Python 2.7 tests - Windows' if: matrix.os == 'windows-2019' && matrix.arch == 'AMD64' + shell: bash run: | - C:\Python27\python.exe -m pip install -U pip pytest && Get-ChildItem -Path wheelhouse/ -Filter *py2*.whl | Foreach-Object { - C:\Python27\python.exe -m pip install $_.FullName - C:\Python27\python.exe -m pytest bindings/python/tests - break - } + C:/Python27/python.exe -m pip install capstone==4.0.2 wheelhouse/*py2*.whl + C:/Python27/python.exe -m unittest discover tests/regress "*.py" - # we install and test python2.7 wheels only on native arch # NOTE: no python2.7 support for macos-13: https://github.com/LizardByte/setup-python-action/issues/2 - name: 'Python 2.7 tests - Non-Windows' - if: (matrix.os == 'ubuntu-latest' && matrix.arch == 'x86_64' && matrix.cibw_build == 'cp38-manylinux') || matrix.os == 'macos-latest' - run: python2 -m pip install wheelhouse/*py2*.whl && python2 -m pip install -U pip pytest && python2 -m pytest bindings/python/tests + if: (matrix.os == 'ubuntu-latest' && matrix.arch == 'x86_64' && matrix.cibw_build == 'cp38-manylinux*') || matrix.os == 'macos-latest' + run: | + python2 -m pip install capstone==4.0.2 wheelhouse/*py2*.whl + python2 -m unittest discover tests/regress "*.py" - uses: actions/upload-artifact@v4 with: - name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }}-py38 + name: ${{ env.ARTIFACT_NAME }} path: ./wheelhouse/*.whl # Job to be executed to build all wheels for all platforms/architectures/python versions only for tag release build_wheels_all_versions: - name: Building on ${{ matrix.os }} - ${{ matrix.arch }} ${{ matrix.cibw_build }} + name: Building on ${{ matrix.os }} - ${{ matrix.arch }} - ${{ matrix.cibw_build }} runs-on: ${{ matrix.os }} strategy: fail-fast: false @@ -205,54 +166,65 @@ jobs: include: # NOTE: aarch64 builds are super slow due to QEMU emulation. Making this to parallelize and speed up workflow # i686 - manylinux - - { os: ubuntu-latest, arch: i686, cibw_build: 'cp37-manylinux' } - - { os: ubuntu-latest, arch: i686, cibw_build: 'cp39-manylinux' } - - { os: ubuntu-latest, arch: i686, cibw_build: 'cp310-manylinux' } - - { os: ubuntu-latest, arch: i686, cibw_build: 'cp311-manylinux' } - - { os: ubuntu-latest, arch: i686, cibw_build: 'cp312-manylinux' } - - { os: ubuntu-latest, arch: i686, cibw_build: 'cp313-manylinux' } + - { os: ubuntu-latest, arch: i686, cibw_build: 'cp37-manylinux*', cibw_skip: '' } + - { os: ubuntu-latest, arch: i686, cibw_build: 'cp39-manylinux*', cibw_skip: '' } + - { os: ubuntu-latest, arch: i686, cibw_build: 'cp310-manylinux*', cibw_skip: '' } + - { os: ubuntu-latest, arch: i686, cibw_build: 'cp311-manylinux*', cibw_skip: '' } + - { os: ubuntu-latest, arch: i686, cibw_build: 'cp312-manylinux*', cibw_skip: '' } + - { os: ubuntu-latest, arch: i686, cibw_build: 'cp313-manylinux*', cibw_skip: '' } # i686 - musllinux - - { os: ubuntu-latest, arch: i686, cibw_build: 'cp37-musllinux' } - - { os: ubuntu-latest, arch: i686, cibw_build: 'cp39-musllinux' } - - { os: ubuntu-latest, arch: i686, cibw_build: 'cp310-musllinux' } - - { os: ubuntu-latest, arch: i686, cibw_build: 'cp311-musllinux' } - - { os: ubuntu-latest, arch: i686, cibw_build: 'cp312-musllinux' } - - { os: ubuntu-latest, arch: i686, cibw_build: 'cp313-musllinux' } + - { os: ubuntu-latest, arch: i686, cibw_build: 'cp37-musllinux*', cibw_skip: '' } + - { os: ubuntu-latest, arch: i686, cibw_build: 'cp39-musllinux*', cibw_skip: '' } + - { os: ubuntu-latest, arch: i686, cibw_build: 'cp310-musllinux*', cibw_skip: '' } + - { os: ubuntu-latest, arch: i686, cibw_build: 'cp311-musllinux*', cibw_skip: '' } + - { os: ubuntu-latest, arch: i686, cibw_build: 'cp312-musllinux*', cibw_skip: '' } + - { os: ubuntu-latest, arch: i686, cibw_build: 'cp313-musllinux*', cibw_skip: '' } # x86_64 - manylinux - - { os: ubuntu-latest, arch: x86_64, cibw_build: 'cp37-manylinux' } - - { os: ubuntu-latest, arch: x86_64, cibw_build: 'cp39-manylinux' } - - { os: ubuntu-latest, arch: x86_64, cibw_build: 'cp310-manylinux' } - - { os: ubuntu-latest, arch: x86_64, cibw_build: 'cp311-manylinux' } - - { os: ubuntu-latest, arch: x86_64, cibw_build: 'cp312-manylinux' } - - { os: ubuntu-latest, arch: x86_64, cibw_build: 'cp313-manylinux' } + - { os: ubuntu-latest, arch: x86_64, cibw_build: 'cp37-manylinux*', cibw_skip: '' } + - { os: ubuntu-latest, arch: x86_64, cibw_build: 'cp39-manylinux*', cibw_skip: '' } + - { os: ubuntu-latest, arch: x86_64, cibw_build: 'cp310-manylinux*', cibw_skip: '' } + - { os: ubuntu-latest, arch: x86_64, cibw_build: 'cp311-manylinux*', cibw_skip: '' } + - { os: ubuntu-latest, arch: x86_64, cibw_build: 'cp312-manylinux*', cibw_skip: '' } + - { os: ubuntu-latest, arch: x86_64, cibw_build: 'cp313-manylinux*', cibw_skip: '' } # x86_64 - musllinux - - { os: ubuntu-latest, arch: x86_64, cibw_build: 'cp37-musllinux' } - - { os: ubuntu-latest, arch: x86_64, cibw_build: 'cp39-musllinux' } - - { os: ubuntu-latest, arch: x86_64, cibw_build: 'cp310-musllinux' } - - { os: ubuntu-latest, arch: x86_64, cibw_build: 'cp311-musllinux' } - - { os: ubuntu-latest, arch: x86_64, cibw_build: 'cp312-musllinux' } - - { os: ubuntu-latest, arch: x86_64, cibw_build: 'cp313-musllinux' } + - { os: ubuntu-latest, arch: x86_64, cibw_build: 'cp37-musllinux*', cibw_skip: '' } + - { os: ubuntu-latest, arch: x86_64, cibw_build: 'cp39-musllinux*', cibw_skip: '' } + - { os: ubuntu-latest, arch: x86_64, cibw_build: 'cp310-musllinux*', cibw_skip: '' } + - { os: ubuntu-latest, arch: x86_64, cibw_build: 'cp311-musllinux*', cibw_skip: '' } + - { os: ubuntu-latest, arch: x86_64, cibw_build: 'cp312-musllinux*', cibw_skip: '' } + - { os: ubuntu-latest, arch: x86_64, cibw_build: 'cp313-musllinux*', cibw_skip: '' } # aarch64 - manylinux - - { os: ubuntu-latest, arch: aarch64, cibw_build: 'cp37-manylinux' } - - { os: ubuntu-latest, arch: aarch64, cibw_build: 'cp39-manylinux' } - - { os: ubuntu-latest, arch: aarch64, cibw_build: 'cp310-manylinux' } - - { os: ubuntu-latest, arch: aarch64, cibw_build: 'cp311-manylinux' } - - { os: ubuntu-latest, arch: aarch64, cibw_build: 'cp312-manylinux' } - - { os: ubuntu-latest, arch: aarch64, cibw_build: 'cp313-manylinux' } + - { os: ubuntu-latest, arch: aarch64, cibw_build: 'cp37-manylinux*', cibw_skip: '' } + - { os: ubuntu-latest, arch: aarch64, cibw_build: 'cp39-manylinux*', cibw_skip: '' } + - { os: ubuntu-latest, arch: aarch64, cibw_build: 'cp310-manylinux*', cibw_skip: '' } + - { os: ubuntu-latest, arch: aarch64, cibw_build: 'cp311-manylinux*', cibw_skip: '' } + - { os: ubuntu-latest, arch: aarch64, cibw_build: 'cp312-manylinux*', cibw_skip: '' } + - { os: ubuntu-latest, arch: aarch64, cibw_build: 'cp313-manylinux*', cibw_skip: '' } # aarch64 - musllinux - - { os: ubuntu-latest, arch: aarch64, cibw_build: 'cp37-musllinux' } - - { os: ubuntu-latest, arch: aarch64, cibw_build: 'cp39-musllinux' } - - { os: ubuntu-latest, arch: aarch64, cibw_build: 'cp310-musllinux' } - - { os: ubuntu-latest, arch: aarch64, cibw_build: 'cp311-musllinux' } - - { os: ubuntu-latest, arch: aarch64, cibw_build: 'cp312-musllinux' } - - { os: ubuntu-latest, arch: aarch64, cibw_build: 'cp313-musllinux' } - - { os: macos-13, arch: x86_64, cibw_build: '' } - - { os: macos-latest, arch: arm64, cibw_build: '' } - - { os: windows-2019, arch: AMD64, cibw_build: '' } - - { os: windows-2019, arch: x86, cibw_build: '' } - if: startsWith(github.ref, 'refs/tags') + - { os: ubuntu-latest, arch: aarch64, cibw_build: 'cp37-musllinux*', cibw_skip: '' } + - { os: ubuntu-latest, arch: aarch64, cibw_build: 'cp39-musllinux*', cibw_skip: '' } + - { os: ubuntu-latest, arch: aarch64, cibw_build: 'cp310-musllinux*', cibw_skip: '' } + - { os: ubuntu-latest, arch: aarch64, cibw_build: 'cp311-musllinux*', cibw_skip: '' } + - { os: ubuntu-latest, arch: aarch64, cibw_build: 'cp312-musllinux*', cibw_skip: '' } + - { os: ubuntu-latest, arch: aarch64, cibw_build: 'cp313-musllinux*', cibw_skip: '' } + - { os: macos-13, arch: x86_64, cibw_build: 'cp*', cibw_skip: '*36* *38*' } + - { os: macos-latest, arch: arm64, cibw_build: 'cp*', cibw_skip: '*36* *37* *38*' } + - { os: windows-2019, arch: AMD64, cibw_build: 'cp*', cibw_skip: '*36* *38*' } + - { os: windows-2019, arch: x86, cibw_build: 'cp*', cibw_skip: '*36* *38*' } + if: ${{ inputs.fullMode == 1 || startsWith(github.ref, 'refs/tags') || contains(github.event.head_commit.message, 'CI(full)') }} steps: - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + # https://github.com/actions/upload-artifact/issues/22 + - name: Prepare a unique name for Artifacts + shell: bash + run: | + # replace not-allowed chars with dash + name="cibw-wheels-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.cibw_build }}" + name=$(echo -n "$name" | sed -e 's/[ \t:\/\\"<>|*?]/-/g' -e 's/--*/-/g' | sed -e 's/\-$//') + echo "ARTIFACT_NAME=$name" >> $GITHUB_ENV - name: '🛠️ Add msbuild to PATH' if: runner.os == 'Windows' @@ -260,17 +232,6 @@ jobs: with: vs-version: '16.5' - - name: '🛠️ Win build dependencies' - if: runner.os == 'Windows' - shell: bash - run: | - choco install ninja - - - name: '🛠️ macOS dependencies' - if: runner.os == 'macOS' - run: | - brew install ninja - - name: '🛠️ Win MSVC 32 dev cmd setup' if: runner.os == 'Windows' && matrix.arch == 'x86' uses: ilammy/msvc-dev-cmd@v1 @@ -284,70 +245,27 @@ jobs: arch: x64 - name: '🛠️ Set up QEMU' - if: runner.os == 'Linux' + if: runner.os == 'Linux' && matrix.arch != 'x86_64' uses: docker/setup-qemu-action@v3 - - name: '🚧 cibuildwheel run - Linux' - if: matrix.os == 'ubuntu-latest' - uses: pypa/cibuildwheel@v2.21.3 + - name: '🚧 cibuildwheel run' + uses: pypa/cibuildwheel@v2.22.0 env: CIBW_BUILD_FRONTEND: build - CIBW_BUILD: ${{ matrix.cibw_build }}* + CIBW_BUILD: ${{ matrix.cibw_build }} + CIBW_SKIP: ${{ matrix.cibw_skip }} CIBW_ARCHS: ${{ matrix.arch }} CIBW_ENVIRONMENT: DEBUG=${{ env.UNICORN_DEBUG }} CIBW_ENVIRONMENT_PASS_LINUX: DEBUG - CIBW_TEST_REQUIRES: pytest - CIBW_TEST_COMMAND: pytest {package}/tests - with: - package-dir: bindings/python - output-dir: wheelhouse - - - name: '🚧 cibuildwheel run - Windows' - if: matrix.os == 'windows-2019' - uses: pypa/cibuildwheel@v2.21.3 - env: - CIBW_BUILD_FRONTEND: build - CIBW_SKIP: '*36* *38*' - CIBW_BUILD: 'cp*' - CIBW_ENVIRONMENT: DEBUG=${{ env.UNICORN_DEBUG }} - CIBW_ARCHS: ${{ matrix.arch }} - CIBW_TEST_REQUIRES: pytest - CIBW_TEST_COMMAND: pytest {package}/tests - with: - package-dir: bindings/python - output-dir: wheelhouse - - - name: '🚧 cibuildwheel run - MacOS x86_84' - if: matrix.os == 'macos-13' - uses: pypa/cibuildwheel@v2.21.3 - env: - CIBW_BUILD_FRONTEND: build - CIBW_SKIP: '*36* *38*' - CIBW_BUILD: 'cp*' - CIBW_ENVIRONMENT: DEBUG=${{ env.UNICORN_DEBUG }} - CIBW_TEST_REQUIRES: pytest - CIBW_TEST_COMMAND: pytest {package}/tests - with: - package-dir: bindings/python - output-dir: wheelhouse - - - name: '🚧 cibuildwheel run - MacOS arm64' - if: matrix.os == 'macos-latest' - uses: pypa/cibuildwheel@v2.21.3 - env: - CIBW_BUILD_FRONTEND: build - CIBW_SKIP: '*36* *37* *38*' - CIBW_BUILD: 'cp*' - CIBW_ENVIRONMENT: DEBUG=${{ env.UNICORN_DEBUG }} - CIBW_TEST_REQUIRES: pytest - CIBW_TEST_COMMAND: pytest {package}/tests + CIBW_TEST_EXTRAS: test + CIBW_TEST_COMMAND: 'python -m unittest discover -v {project}/tests/regress "*.py"' with: package-dir: bindings/python output-dir: wheelhouse - uses: actions/upload-artifact@v4 with: - name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }}-all + name: ${{ env.ARTIFACT_NAME }} path: ./wheelhouse/*.whl make_sdist: @@ -356,8 +274,7 @@ jobs: steps: - uses: actions/checkout@v4 with: - fetch-depth: 0 # Optional, use if you use setuptools_scm - submodules: true # Optional, use if you have submodules + fetch-depth: 0 - name: Build SDist run: | diff --git a/bindings/python/pyproject.toml b/bindings/python/pyproject.toml index 8da778ab10..0d666870ff 100644 --- a/bindings/python/pyproject.toml +++ b/bindings/python/pyproject.toml @@ -1,10 +1,10 @@ [build-system] -requires = ["setuptools", "build", "wheel"] +requires = ["setuptools", "build", "wheel", "versioningit"] build-backend = "setuptools.build_meta" [project] name = "unicorn" -version = "2.1.1" +dynamic = ["version"] requires-python = ">= 2.7, != 3.0.*, != 3.1.*, != 3.2.*, != 3.3.*, != 3.4.*, != 3.5.*, != 3.6.*" authors = [ { name = "Nguyen Anh Quynh", email = "quynh@gmail.com" }, @@ -33,9 +33,11 @@ Changelog = "https://github.com/unicorn-engine/unicorn/blob/master/ChangeLog" [project.optional-dependencies] test = [ - "pytest", - "pytest-cov", + "capstone==6.0.0a2;python_version>'3.7'", + "capstone==5.0.1;python_version<='3.7'" ] [tool.setuptools.packages.find] include = ["unicorn*"] + +[tool.versioningit] diff --git a/bindings/python/setup.py b/bindings/python/setup.py index 534be5a482..6961f9b503 100755 --- a/bindings/python/setup.py +++ b/bindings/python/setup.py @@ -102,12 +102,17 @@ def build_libraries(): has_msbuild = shutil.which('msbuild') is not None conf = 'Debug' if int(os.getenv('DEBUG', 0)) else 'Release' + cmake_args = ['cmake', '-B', BUILD_DIR, "-DCMAKE_BUILD_TYPE=" + conf] + if os.getenv("UNICORN_TRACER"): + cmake_args += ["-DUNICORN_TRACER=on"] + if conf == 'Debug': + cmake_args += ["-DUNICORN_LOGGING=on"] if has_msbuild and sys.platform == 'win32': + generators = os.getenv('GENERATORS') or 'Visual Studio 16 2019' plat = 'Win32' if platform.architecture()[0] == '32bit' else 'x64' - - subprocess.check_call(['cmake', '-B', BUILD_DIR, '-G', "Visual Studio 16 2019", "-A", plat, - "-DCMAKE_BUILD_TYPE=" + conf], cwd=UC_DIR) + cmake_args += ['-G', generators, "-A", plat] + subprocess.check_call(cmake_args, cwd=UC_DIR) subprocess.check_call(['msbuild', 'unicorn.sln', '-m', '-p:Platform=' + plat, '-p:Configuration=' + conf], cwd=BUILD_DIR) @@ -115,11 +120,7 @@ def build_libraries(): shutil.copy(os.path.join(obj_dir, LIBRARY_FILE), LIBS_DIR) shutil.copy(os.path.join(obj_dir, STATIC_LIBRARY_FILE), LIBS_DIR) else: - cmake_args = ["cmake", '-B', BUILD_DIR, '-S', UC_DIR, "-DCMAKE_BUILD_TYPE=" + conf] - if os.getenv("TRACE"): - cmake_args += ["-DUNICORN_TRACER=on"] - if conf == "Debug": - cmake_args += ["-DUNICORN_LOGGING=on"] + cmake_args += ['-S', UC_DIR] subprocess.check_call(cmake_args, cwd=UC_DIR) threads = os.getenv("THREADS", "4") subprocess.check_call(["cmake", "--build", ".", "-j" + threads], cwd=BUILD_DIR) diff --git a/bindings/python/tests/test_arm.py b/bindings/python/tests/test_arm.py index a0f7ff5824..b2a1fe85c7 100755 --- a/bindings/python/tests/test_arm.py +++ b/bindings/python/tests/test_arm.py @@ -2,7 +2,6 @@ # Sample code for ARM of Unicorn. Nguyen Anh Quynh # Python sample ported by Loi Anh Tuan -from __future__ import print_function from unicorn import * from unicorn.arm_const import * diff --git a/bindings/python/tests/test_arm64.py b/bindings/python/tests/test_arm64.py index e6507b85ae..6969ff7747 100755 --- a/bindings/python/tests/test_arm64.py +++ b/bindings/python/tests/test_arm64.py @@ -2,7 +2,6 @@ # Sample code for ARM64 of Unicorn. Nguyen Anh Quynh # Python sample ported by Loi Anh Tuan -from __future__ import print_function from unicorn import * from unicorn.arm64_const import * diff --git a/bindings/python/tests/test_arm64eb.py b/bindings/python/tests/test_arm64eb.py index 2a8b87ae7c..d1dd138ae7 100755 --- a/bindings/python/tests/test_arm64eb.py +++ b/bindings/python/tests/test_arm64eb.py @@ -3,7 +3,6 @@ # Python sample ported by Loi Anh Tuan # AARCH64 Python sample ported by zhangwm -from __future__ import print_function from unicorn import * from unicorn.arm64_const import * diff --git a/bindings/python/tests/test_armeb.py b/bindings/python/tests/test_armeb.py index f818d2746d..74239e9598 100755 --- a/bindings/python/tests/test_armeb.py +++ b/bindings/python/tests/test_armeb.py @@ -1,7 +1,6 @@ #!/usr/bin/env python # Sample code for ARM big endian of Unicorn. zhangwm -from __future__ import print_function from unicorn import * from unicorn.arm_const import * diff --git a/bindings/python/tests/test_ctl.py b/bindings/python/tests/test_ctl.py index b69df22652..3833e88604 100755 --- a/bindings/python/tests/test_ctl.py +++ b/bindings/python/tests/test_ctl.py @@ -2,8 +2,6 @@ # Sample code for Unicorn. # By Lazymio(@wtdcode), 2021 -import pytest -import sys from unicorn import * from unicorn.x86_const import * from datetime import datetime @@ -36,7 +34,6 @@ def time_emulation(uc, start, end): # TODO: Check if worth adapting the ctl_request_cache method for py2 bindings -@pytest.mark.skipif(sys.version_info < (3, 7), reason="requires python3.7 or higher") def test_uc_ctl_tb_cache(): # Initialize emulator in X86-32bit mode uc = Uc(UC_ARCH_X86, UC_MODE_32) @@ -84,7 +81,6 @@ def trace_tcg_sub(uc, address, arg1, arg2, size, data): # TODO: Check if worth adapting the hook_add method for py2 bindings -@pytest.mark.skipif(sys.version_info < (3, 7), reason="requires python3.7 or higher") def test_uc_ctl_exits(): uc = Uc(UC_ARCH_X86, UC_MODE_32) addr = 0x1000 diff --git a/bindings/python/tests/test_m68k.py b/bindings/python/tests/test_m68k.py index 57b05ea61a..43a7d166eb 100755 --- a/bindings/python/tests/test_m68k.py +++ b/bindings/python/tests/test_m68k.py @@ -2,7 +2,6 @@ # Sample code for ARM of Unicorn. Nguyen Anh Quynh # Python sample ported by Loi Anh Tuan -from __future__ import print_function from unicorn import * from unicorn.m68k_const import * diff --git a/bindings/python/tests/test_mips.py b/bindings/python/tests/test_mips.py index 3ec5ec565b..93ada8492a 100755 --- a/bindings/python/tests/test_mips.py +++ b/bindings/python/tests/test_mips.py @@ -2,7 +2,6 @@ # Sample code for MIPS of Unicorn. Nguyen Anh Quynh # Python sample ported by Loi Anh Tuan -from __future__ import print_function from unicorn import * from unicorn.mips_const import * diff --git a/bindings/python/tests/test_network_auditing.py b/bindings/python/tests/test_network_auditing.py index 145e4f6ed4..b137cf6460 100755 --- a/bindings/python/tests/test_network_auditing.py +++ b/bindings/python/tests/test_network_auditing.py @@ -2,8 +2,6 @@ # Unicorn sample for auditing network connection and file handling in shellcode. # Nguyen Tan Cong -from __future__ import print_function -import pytest import struct import uuid from unicorn import * @@ -361,7 +359,6 @@ def hook_intr(uc, intno, user_data): print_sockcall(msg) -@pytest.mark.parametrize("code", [X86_SEND_ETCPASSWD, X86_BIND_TCP, X86_REVERSE_TCP, X86_REVERSE_TCP_2]) # Test X86 32 bit def test_i386(code): global fd_chains diff --git a/bindings/python/tests/test_ppc.py b/bindings/python/tests/test_ppc.py index e0cbbb0e1a..2bb4fe3aaa 100755 --- a/bindings/python/tests/test_ppc.py +++ b/bindings/python/tests/test_ppc.py @@ -1,7 +1,6 @@ #!/usr/bin/env python # Sample code for PPC of Unicorn. Nguyen Anh Quynh -from __future__ import print_function from unicorn import * from unicorn.ppc_const import * diff --git a/bindings/python/tests/test_riscv.py b/bindings/python/tests/test_riscv.py index 39cf83f530..a5190d67a3 100755 --- a/bindings/python/tests/test_riscv.py +++ b/bindings/python/tests/test_riscv.py @@ -1,7 +1,6 @@ #!/usr/bin/env python # Sample code for RISCV of Unicorn. Nguyen Anh Quynh -from __future__ import print_function from unicorn import * from unicorn.riscv_const import * diff --git a/bindings/python/tests/test_shellcode.py b/bindings/python/tests/test_shellcode.py index 1313f883ad..ca16c184af 100755 --- a/bindings/python/tests/test_shellcode.py +++ b/bindings/python/tests/test_shellcode.py @@ -3,8 +3,6 @@ # Nguyen Anh Quynh # KaiJern Lau -from __future__ import print_function -import pytest from unicorn import * from unicorn.x86_const import * @@ -134,8 +132,6 @@ def hook_syscall64(mu, user_data): mu.emu_stop() -@pytest.mark.parametrize("mode,code", - [(UC_MODE_32, X86_CODE32_SELF), (UC_MODE_32, X86_CODE32), (UC_MODE_64, X86_CODE64)]) # Test X86 32 bit def test_i386(mode, code): if mode == UC_MODE_32: diff --git a/bindings/python/tests/test_sparc.py b/bindings/python/tests/test_sparc.py index badb7b76ff..058e36f5a6 100755 --- a/bindings/python/tests/test_sparc.py +++ b/bindings/python/tests/test_sparc.py @@ -2,7 +2,6 @@ # Sample code for SPARC of Unicorn. Nguyen Anh Quynh # Python sample ported by Loi Anh Tuan -from __future__ import print_function from unicorn import * from unicorn.sparc_const import * diff --git a/bindings/python/tests/test_tricore.py b/bindings/python/tests/test_tricore.py index 20bbc03a5b..ec8a33567d 100755 --- a/bindings/python/tests/test_tricore.py +++ b/bindings/python/tests/test_tricore.py @@ -5,7 +5,6 @@ Copyright 2022 Aptiv """ -from __future__ import print_function from unicorn import * from unicorn.tricore_const import * diff --git a/bindings/python/tests/test_x86.py b/bindings/python/tests/test_x86.py index 94c071d38f..3f852b8f77 100755 --- a/bindings/python/tests/test_x86.py +++ b/bindings/python/tests/test_x86.py @@ -1,7 +1,6 @@ #!/usr/bin/env python # Sample code for X86 of Unicorn. Nguyen Anh Quynh -from __future__ import print_function import pickle from unicorn import * from unicorn.x86_const import * diff --git a/tests/regress/arm64_reg_rw_w0_w30.py b/tests/regress/arm64_reg_rw_w0_w30.py index 42b2f14961..3f50c0b42a 100644 --- a/tests/regress/arm64_reg_rw_w0_w30.py +++ b/tests/regress/arm64_reg_rw_w0_w30.py @@ -1,7 +1,4 @@ -#!/usr/bin/python - import regress - from unicorn import * from unicorn.arm64_const import * diff --git a/tests/regress/arm_bx_unmapped.py b/tests/regress/arm_bx_unmapped.py index 26dab3693f..08ba75daf0 100644 --- a/tests/regress/arm_bx_unmapped.py +++ b/tests/regress/arm_bx_unmapped.py @@ -1,10 +1,7 @@ - import regress - from unicorn import * from unicorn.arm_const import * - MAIN_ADDRESS = 0x8d68 ADDRESS = MAIN_ADDRESS & ~(0x1000 - 1) STACK_ADDR = ADDRESS + 0x1000 @@ -15,51 +12,51 @@ def runTest(self): # code to be emulated code = { 0x8cd4: ( - b'\x04\xb0\x2d\xe5' # 8cd4 push {r11} - b'\x00\xb0\x8d\xe2' # 8cd8 add r11, sp, #0 - b'\x0f\x30\xa0\xe1' # 8cdc mov r3, pc - b'\x03\x00\xa0\xe1' # 8ce0 mov r0, r3 - b'\x00\xd0\x4b\xe2' # 8ce4 sub sp, r11, #0 - b'\x04\xb0\x9d\xe4' # 8ce8 pop {r11} - b'\x1e\xff\x2f\xe1' # 8cec bx lr + b'\x04\xb0\x2d\xe5' # 8cd4 push {r11} + b'\x00\xb0\x8d\xe2' # 8cd8 add r11, sp, #0 + b'\x0f\x30\xa0\xe1' # 8cdc mov r3, pc + b'\x03\x00\xa0\xe1' # 8ce0 mov r0, r3 + b'\x00\xd0\x4b\xe2' # 8ce4 sub sp, r11, #0 + b'\x04\xb0\x9d\xe4' # 8ce8 pop {r11} + b'\x1e\xff\x2f\xe1' # 8cec bx lr ), 0x8cf0: ( - b'\x04\xb0\x2d\xe5' # 8cf0 push {r11} - b'\x00\xb0\x8d\xe2' # 8cf4 add r11, sp, #0 - b'\x04\x60\x2d\xe5' # 8cf8 push {r6} - b'\x01\x60\x8f\xe2' # 8cfc add r6, pc, $1 - b'\x16\xff\x2f\xe1' # 8d00 bx r6 - # .thumb - b'\x7b\x46' # 8d04 mov r3, pc - b'\x03\xf1\x08\x03' # 8d06 add r3, $0x8 # elicn: used to be $0x4 but it kept failing - b'\x08\xb4' # 8d0a push {r3} - b'\x00\xbd' # 8d0c pop {pc} - b'\x00\x00' # 8d0e (alignment) - # .arm - b'\x04\x60\x9d\xe4' # 8d10 pop {r6} - b'\x03\x00\xa0\xe1' # 8d14 mov r0, r3 - b'\x00\xd0\x4b\xe2' # 8d18 sub sp, r11, #0 - b'\x04\xb0\x9d\xe4' # 8d1c pop {r11} - b'\x1e\xff\x2f\xe1' # 8d20 bx lr + b'\x04\xb0\x2d\xe5' # 8cf0 push {r11} + b'\x00\xb0\x8d\xe2' # 8cf4 add r11, sp, #0 + b'\x04\x60\x2d\xe5' # 8cf8 push {r6} + b'\x01\x60\x8f\xe2' # 8cfc add r6, pc, $1 + b'\x16\xff\x2f\xe1' # 8d00 bx r6 + # .thumb + b'\x7b\x46' # 8d04 mov r3, pc + b'\x03\xf1\x08\x03' # 8d06 add r3, $0x8 # elicn: used to be $0x4 but it kept failing + b'\x08\xb4' # 8d0a push {r3} + b'\x00\xbd' # 8d0c pop {pc} + b'\x00\x00' # 8d0e (alignment) + # .arm + b'\x04\x60\x9d\xe4' # 8d10 pop {r6} + b'\x03\x00\xa0\xe1' # 8d14 mov r0, r3 + b'\x00\xd0\x4b\xe2' # 8d18 sub sp, r11, #0 + b'\x04\xb0\x9d\xe4' # 8d1c pop {r11} + b'\x1e\xff\x2f\xe1' # 8d20 bx lr ), - 0x8d24: ( # elicn: used to be 0x8d20 but it caused this block to overlap with the previous one - b'\x04\xb0\x2d\xe5' # 8d24 push {r11} - b'\x00\xb0\x8d\xe2' # 8d28 add r11, sp, #0 - b'\x0e\x30\xa0\xe1' # 8d2c mov r3, lr - b'\x03\x00\xa0\xe1' # 8d20 mov r0, r3 - b'\x00\xd0\x4b\xe2' # 8d34 sub sp, r11, #0 - b'\x04\xb0\x9d\xe4' # 8d38 pop {r11} - b'\x1e\xff\x2f\xe1' # 8d3c bx lr + 0x8d24: ( # elicn: used to be 0x8d20 but it caused this block to overlap with the previous one + b'\x04\xb0\x2d\xe5' # 8d24 push {r11} + b'\x00\xb0\x8d\xe2' # 8d28 add r11, sp, #0 + b'\x0e\x30\xa0\xe1' # 8d2c mov r3, lr + b'\x03\x00\xa0\xe1' # 8d20 mov r0, r3 + b'\x00\xd0\x4b\xe2' # 8d34 sub sp, r11, #0 + b'\x04\xb0\x9d\xe4' # 8d38 pop {r11} + b'\x1e\xff\x2f\xe1' # 8d3c bx lr ), 0x8d68: ( - b'\xd9\xff\xff\xeb' # 8d68 bl 0x8cd4 <-- MAIN_ADDRESS - b'\x00\x40\xa0\xe1' # 8d6c mov r4, r0 - b'\xde\xff\xff\xeb' # 8d70 bl 0x8cf0 - b'\x00\x30\xa0\xe1' # 8d74 mov r3, r0 - b'\x03\x40\x84\xe0' # 8d78 add r4, r4, r3 - b'\xe8\xff\xff\xeb' # 8d7c bl 0x8d24 - b'\x00\x30\xa0\xe1' # 8d80 mov r3, r0 - b'\x03\x20\x84\xe0' # 8d84 add r2, r4, r3 + b'\xd9\xff\xff\xeb' # 8d68 bl 0x8cd4 <-- MAIN_ADDRESS + b'\x00\x40\xa0\xe1' # 8d6c mov r4, r0 + b'\xde\xff\xff\xeb' # 8d70 bl 0x8cf0 + b'\x00\x30\xa0\xe1' # 8d74 mov r3, r0 + b'\x03\x40\x84\xe0' # 8d78 add r4, r4, r3 + b'\xe8\xff\xff\xeb' # 8d7c bl 0x8d24 + b'\x00\x30\xa0\xe1' # 8d80 mov r3, r0 + b'\x03\x20\x84\xe0' # 8d84 add r2, r4, r3 ) } diff --git a/tests/regress/arm_bxeq_hang.py b/tests/regress/arm_bxeq_hang.py index 1eedf4b6eb..d85a16544e 100755 --- a/tests/regress/arm_bxeq_hang.py +++ b/tests/regress/arm_bxeq_hang.py @@ -1,7 +1,4 @@ -#!/usr/bin/python - import regress - from unicorn import * from unicorn.arm_const import * diff --git a/tests/regress/arm_fp_vfp_disabled.py b/tests/regress/arm_fp_vfp_disabled.py index 9bd2245c2d..6a636a55b9 100755 --- a/tests/regress/arm_fp_vfp_disabled.py +++ b/tests/regress/arm_fp_vfp_disabled.py @@ -1,28 +1,25 @@ -#!/usr/bin/python - -# Added by Peter Mackay, relating to issue 571 -# "ARM NEON/VFP support seems to exist but is disabled by default" +# Added by Peter Mackay, relating to issue 571 +# ARM NEON/VFP support seems to exist but is disabled by default # https://github.com/unicorn-engine/unicorn/issues/571 import regress - from unicorn import * from unicorn.arm_const import * - CODE = ( - b'\x11\xEE\x50\x1F' # MRC p15, #0, r1, c1, c0, #2 - b'\x41\xF4\x70\x01' # ORR r1, r1, #(0xf << 20) - b'\x01\xEE\x50\x1F' # MCR p15, #0, r1, c1, c0, #2 - b'\x4F\xF0\x00\x01' # MOV r1, #0 - b'\x07\xEE\x95\x1F' # MCR p15, #0, r1, c7, c5, #4 - b'\x4F\xF0\x80\x40' # MOV r0,#0x40000000 - b'\xE8\xEE\x10\x0A' # FMXR FPEXC, r0 - b'\x2d\xed\x02\x8b' # vpush {d8} + b'\x11\xEE\x50\x1F' # MRC p15, #0, r1, c1, c0, #2 + b'\x41\xF4\x70\x01' # ORR r1, r1, #(0xf << 20) + b'\x01\xEE\x50\x1F' # MCR p15, #0, r1, c1, c0, #2 + b'\x4F\xF0\x00\x01' # MOV r1, #0 + b'\x07\xEE\x95\x1F' # MCR p15, #0, r1, c7, c5, #4 + b'\x4F\xF0\x80\x40' # MOV r0,#0x40000000 + b'\xE8\xEE\x10\x0A' # FMXR FPEXC, r0 + b'\x2d\xed\x02\x8b' # vpush {d8} ) BASE = 0x1000 + class FpVfpDisabled(regress.RegressTest): def runTest(self): diff --git a/tests/regress/arm_init_input_crash.py b/tests/regress/arm_init_input_crash.py index aa79a19cf4..3a89491a55 100755 --- a/tests/regress/arm_init_input_crash.py +++ b/tests/regress/arm_init_input_crash.py @@ -1,21 +1,21 @@ -#!/usr/bin/env python # Sample code for ARM of Unicorn. Nguyen Anh Quynh # Python sample ported by Loi Anh Tuan -# -import regress +import platform +import regress +import sys +import unittest from unicorn import * from unicorn.arm_const import * - # code to be emulated ARM_CODE = ( - b"\x37\x00\xa0\xe3" # mov r0, #0x37 - b"\x03\x10\x42\xe0" # sub r1, r2, r3 + b"\x37\x00\xa0\xe3" # mov r0, #0x37 + b"\x03\x10\x42\xe0" # sub r1, r2, r3 ) -THUMB_CODE = b"\x83\xb0" # sub sp, #0xc +THUMB_CODE = b"\x83\xb0" # sub sp, #0xc # memory address where emulation starts ADDRESS = 0xF0000000 @@ -32,6 +32,7 @@ def hook_code(uc, address, size, user_data): class TestInitInputCrash(regress.RegressTest): + @unittest.skipIf(sys.platform == 'win32' or platform.machine().lower() not in ('x86_64', 'arm64'), 'TO BE CHECKED!') def test_arm(self): regress.logger.debug("Emulate ARM code") @@ -42,7 +43,7 @@ def test_arm(self): mu.mem_map(ADDRESS, mem_size) stack_address = ADDRESS + mem_size - stack_size = stack_address # >>> here huge memory size + stack_size = stack_address # >>> here huge memory size mu.mem_map(stack_address, stack_size) # write machine code to be emulated to memory diff --git a/tests/regress/arm_memcpy_neon.py b/tests/regress/arm_memcpy_neon.py index c8fc8a6029..882dd53453 100644 --- a/tests/regress/arm_memcpy_neon.py +++ b/tests/regress/arm_memcpy_neon.py @@ -1,44 +1,44 @@ - import regress - +import sys +import unittest from unicorn import * from unicorn.arm_const import * - -SHELLCODE = bytes.fromhex( - '03 f0 8f e0' # 0001F894 ADD PC, PC, R3 - '0d 07 21 f4' # 0001F898 VLD1.8 {D0}, [R1]! - '0d 07 0c f4' # 0001F89C VST1.8 {D0}, [R12]! - '0d 07 21 f4' # 0001F8A0 VLD1.8 {D0}, [R1]! - '0d 07 0c f4' # 0001F8A4 VST1.8 {D0}, [R12]! - '0d 07 21 f4' # 0001F8A8 VLD1.8 {D0}, [R1]! - '0d 07 0c f4' # 0001F8AC VST1.8 {D0}, [R12]! - '0d 07 21 f4' # 0001F8B0 VLD1.8 {D0}, [R1]! - '0d 07 0c f4' # 0001F8B4 VST1.8 {D0}, [R12]! - '0d 07 21 f4' # 0001F8B8 VLD1.8 {D0}, [R1]! - '0d 07 0c f4' # 0001F8BC VST1.8 {D0}, [R12]! - '0d 07 21 f4' # 0001F8C0 VLD1.8 {D0}, [R1]! - '0d 07 0c f4' # 0001F8C4 VST1.8 {D0}, [R12]! - '0d 07 21 f4' # 0001F8C8 VLD1.8 {D0}, [R1]! - '0d 07 0c f4' # 0001F8CC VST1.8 {D0}, [R12]! - '04 00 12 e3' # 0001F8D0 TST R2, #4 - '04 30 91 14' # 0001F8D4 LDRNE R3, [R1],#4 - '04 30 8c 14' # 0001F8D8 STRNE R3, [R12],#4 - '82 2f b0 e1' # 0001F8DC MOVS R2, R2,LSL#31 - 'b2 30 d1 20' # 0001F8E0 LDRHCS R3, [R1],#2 - '00 10 d1 15' # 0001F8E4 LDRBNE R1, [R1] - 'b2 30 cc 20' # 0001F8E8 STRHCS R3, [R12],#2 - '00 10 cc 15' # 0001F8EC STRBNE R1, [R12] +SHELLCODE = ( + b'\x03\xF0\x8F\xE0' # 0001F894 ADD PC, PC, R3 + b'\x0D\x07\x21\xF4' # 0001F898 VLD1.8 {D0}, [R1]! + b'\x0D\x07\x0C\xF4' # 0001F89C VST1.8 {D0}, [R12]! + b'\x0D\x07\x21\xF4' # 0001F8A0 VLD1.8 {D0}, [R1]! + b'\x0D\x07\x0C\xF4' # 0001F8A4 VST1.8 {D0}, [R12]! + b'\x0D\x07\x21\xF4' # 0001F8A8 VLD1.8 {D0}, [R1]! + b'\x0D\x07\x0C\xF4' # 0001F8AC VST1.8 {D0}, [R12]! + b'\x0D\x07\x21\xF4' # 0001F8B0 VLD1.8 {D0}, [R1]! + b'\x0D\x07\x0C\xF4' # 0001F8B4 VST1.8 {D0}, [R12]! + b'\x0D\x07\x21\xF4' # 0001F8B8 VLD1.8 {D0}, [R1]! + b'\x0D\x07\x0C\xF4' # 0001F8BC VST1.8 {D0}, [R12]! + b'\x0D\x07\x21\xF4' # 0001F8C0 VLD1.8 {D0}, [R1]! + b'\x0D\x07\x0C\xF4' # 0001F8C4 VST1.8 {D0}, [R12]! + b'\x0D\x07\x21\xF4' # 0001F8C8 VLD1.8 {D0}, [R1]! + b'\x0D\x07\x0C\xF4' # 0001F8CC VST1.8 {D0}, [R12]! + b'\x04\x00\x12\xE3' # 0001F8D0 TST R2, #4 + b'\x04\x30\x91\x14' # 0001F8D4 LDRNE R3, [R1],#4 + b'\x04\x30\x8C\x14' # 0001F8D8 STRNE R3, [R12],#4 + b'\x82\x2F\xB0\xE1' # 0001F8DC MOVS R2, R2,LSL#31 + b'\xB2\x30\xD1\x20' # 0001F8E0 LDRHCS R3, [R1],#2 + b'\x00\x10\xD1\x15' # 0001F8E4 LDRBNE R1, [R1] + b'\xB2\x30\xCC\x20' # 0001F8E8 STRHCS R3, [R12],#2 + b'\x00\x10\xCC\x15' # 0001F8EC STRBNE R1, [R12] ) - BASE = 0x1F894 COPY_SRC = 0x1000 COPY_DST = 0x2000 COPY_LEN = 8 DATA = b'c8' * COPY_LEN + class ArmMemcpy(regress.RegressTest): + @unittest.skipIf(sys.version_info < (3, 7), reason="requires python3.7 or higher") def test_arm_memcpy(self): uc = Uc(UC_ARCH_ARM, UC_MODE_ARM) @@ -46,7 +46,7 @@ def test_arm_memcpy(self): uc.mem_map(COPY_DST, 0x1000) uc.mem_map(BASE & ~(0x1000 - 1), 0x1000) uc.mem_write(COPY_SRC, DATA) - uc.mem_write(BASE, bytes(SHELLCODE)) + uc.mem_write(BASE, SHELLCODE) uc.reg_write_batch(( (UC_ARM_REG_R12, COPY_DST), diff --git a/tests/regress/arm_movr12_hang.py b/tests/regress/arm_movr12_hang.py index ac779ce48a..9e4eeba033 100755 --- a/tests/regress/arm_movr12_hang.py +++ b/tests/regress/arm_movr12_hang.py @@ -1,13 +1,13 @@ -#!/usr/bin/python - +import platform import regress - +import unittest from unicorn import * from unicorn.arm_const import * class MovHang(regress.RegressTest): + @unittest.skipIf(platform.machine().lower() == 'aarch64', reason='TO BE CHECKED!') def runTest(self): uc = Uc(UC_ARCH_ARM, UC_MODE_ARM) uc.mem_map(0x1000, 0x1000) @@ -23,7 +23,7 @@ def hook_block(uc, addr, *args): uc.hook_add(UC_HOOK_BLOCK, hook_block) uc.count = 0 - #print 'block should only run once' + # print 'block should only run once' uc.emu_start(0x1000, 0x1004, timeout=500) self.assertEqual(0x0, uc.reg_read(UC_ARM_REG_R12)) diff --git a/tests/regress/arm_vldr_invalid.py b/tests/regress/arm_vldr_invalid.py index eeaaac0d93..955bf02f30 100755 --- a/tests/regress/arm_vldr_invalid.py +++ b/tests/regress/arm_vldr_invalid.py @@ -1,9 +1,5 @@ -#!/usr/bin/python - import regress - from unicorn import * -from unicorn.arm_const import * class VldrPcInsn(regress.RegressTest): @@ -12,7 +8,7 @@ def runTest(self): uc = Uc(UC_ARCH_ARM, UC_MODE_ARM) uc.mem_map(0x1000, 0x1000) - uc.mem_write(0x1000, b'\xed\x9f\x8a\x3d') # vldr s16, [pc, #244] + uc.mem_write(0x1000, b'\xed\x9f\x8a\x3d') # vldr s16, [pc, #244] with self.assertRaises(UcError) as ex: uc.emu_start(0x1000, 0x1004) diff --git a/tests/regress/arm_wfi_first_insn_of_tb.py b/tests/regress/arm_wfi_first_insn_of_tb.py index 7db9ed7139..e2f7458ba2 100644 --- a/tests/regress/arm_wfi_first_insn_of_tb.py +++ b/tests/regress/arm_wfi_first_insn_of_tb.py @@ -1,9 +1,5 @@ - import regress - from unicorn import * -from unicorn.arm_const import * - CODE = ( b'\x00\x00\x8a\xe0' # ADD R0, R10, R0 diff --git a/tests/regress/bad_ram.py b/tests/regress/bad_ram.py index 041da81d1f..e35eb26bb3 100755 --- a/tests/regress/bad_ram.py +++ b/tests/regress/bad_ram.py @@ -1,18 +1,15 @@ -#!/usr/bin/python - import regress - from unicorn import * from unicorn.x86_const import * -class Hang(regress.RegressTest): +class BadRam(regress.RegressTest): def runTest(self): PAGE_SIZE = 0x5000 CODE_ADDR = 0x400000 - RSP_ADDR = 0x200000 + RSP_ADDR = 0x200000 - CODE = b"\xCA\x24\x5D" # retf 0x5d24 + CODE = b"\xCA\x24\x5D" # retf 0x5d24 mu = Uc(UC_ARCH_X86, UC_MODE_64) diff --git a/tests/regress/callback-pc.py b/tests/regress/callback-pc.py index 640d694218..810e004b6b 100755 --- a/tests/regress/callback-pc.py +++ b/tests/regress/callback-pc.py @@ -1,16 +1,12 @@ -#!/usr/bin/env python - # reg_write() can't modify PC from within trace callbacks # issue #210 import regress - from unicorn import * from unicorn.arm_const import * - BASE_ADDRESS = 0x10000000 -THUMB_CODE = b"\x83\xb0" * 5 # sub sp, #0xc +THUMB_CODE = b"\x83\xb0" * 5 # sub sp, #0xc TARGET_PC = 0xffffffff diff --git a/tests/regress/core_ctl.py b/tests/regress/core_ctl.py index 730c1e8a95..3a2ffc5c3f 100644 --- a/tests/regress/core_ctl.py +++ b/tests/regress/core_ctl.py @@ -1,12 +1,11 @@ -#!/usr/bin/env python - +import platform import regress - +import sys +import unittest from unicorn import * from unicorn.arm_const import * from unicorn.x86_const import * - # count down from maxint to zero _VALID_CODE = ( b'\x31\xc9' # xor ecx, ecx @@ -17,7 +16,7 @@ ) _INVALID_CODE = ( - b'\xff\xff' # (invalid) + b'\xff\xff' # (invalid) ) CODE = _VALID_CODE + _INVALID_CODE @@ -44,6 +43,8 @@ def test_mode(self): self.assertEqual(UC_MODE_BIG_ENDIAN, uc.ctl_get_mode()) self.assertEqual(UC_CPU_ARM_CORTEX_M0, uc.ctl_get_cpu_model()) + @unittest.skipIf(sys.version_info < (3, 7), reason="requires python3.7 or higher") + @unittest.skipIf(sys.platform == 'win32' or platform.machine().lower() not in ('x86_64', 'arm64'), 'TO BE CHECKED!') def test_page_size(self): SIZE_4KB = 4 * 1024 ** 1 SIZE_2MB = 2 * 1024 ** 2 @@ -77,6 +78,7 @@ def test_page_size(self): # are we still with the valid value? self.assertEqual(SIZE_2MB, uc.ctl_get_page_size()) + @unittest.skipIf(platform.machine().lower() == 'aarch64', reason='TO BE CHECKED!') def test_timeout(self): MILLIS_1S = 1000 @@ -137,6 +139,7 @@ def __hook_code(uc, *args): # not failing on an invalid instruction is another good indication for that self.assertEqual(GOOD_EXIT, uc.reg_read(UC_X86_REG_EIP)) + @unittest.skipIf(sys.version_info < (3, 7), reason="requires python3.7 or higher") def test_tlb_mode(self): MAPPING_LO = 0x0000000001000000 MAPPING_HI = 0x0010000000000000 @@ -155,7 +158,7 @@ def test_tlb_mode(self): uc.mem_map(MAPPING_HI, 0x1000) uc.mem_write(MAPPING_HI, NOPSLED) - # this should prevents us from mapping to high addresses + # this should prevent us from mapping to high addresses uc.ctl_set_tlb_mode(UC_TLB_CPU) # this should fail diff --git a/tests/regress/crash_tb.py b/tests/regress/crash_tb.py index 9ecf61cd55..52315ddc3c 100755 --- a/tests/regress/crash_tb.py +++ b/tests/regress/crash_tb.py @@ -1,14 +1,12 @@ -#!/usr/bin/python - +import regress from unicorn import * from unicorn.x86_const import * -import regress - CODE_ADDR = 0x0 binary1 = b'\xb8\x02\x00\x00\x00' binary2 = b'\xb8\x01\x00\x00\x00' + class CrashTB(regress.RegressTest): def runTest(self): @@ -32,6 +30,6 @@ def runTest(self): self.assertEqual(0x1, mu.reg_read(UC_X86_REG_RAX)) + if __name__ == '__main__': regress.main() - diff --git a/tests/regress/deadlock_1.py b/tests/regress/deadlock_1.py index 269a573fa2..4229bbd667 100755 --- a/tests/regress/deadlock_1.py +++ b/tests/regress/deadlock_1.py @@ -1,11 +1,11 @@ -#!/usr/bin/python # From issue #1 of Ryan Hileman -from unicorn import * import regress +from unicorn import * CODE = b"\x90\x91\x92" + class DeadLock(regress.RegressTest): def runTest(self): @@ -16,5 +16,6 @@ def runTest(self): with self.assertRaises(UcError): mu.emu_start(0x100000, 0x1000 + len(CODE)) + if __name__ == '__main__': regress.main() diff --git a/tests/regress/emu_clear_errors.py b/tests/regress/emu_clear_errors.py index e6c094dd34..cf61b324df 100755 --- a/tests/regress/emu_clear_errors.py +++ b/tests/regress/emu_clear_errors.py @@ -1,27 +1,22 @@ -#!/usr/bin/python - -import binascii import regress - from unicorn import * from unicorn.x86_const import * - -CODE = binascii.unhexlify(( - "8B 74 01 28" # mov esi, dword ptr [ecx + eax + 0x28] mapped: 0x1000 - "03 F0" # add esi, eax 0x1004 - "8D 45 FC" # lea eax, dword ptr [ebp - 4] 0x1006 - "50" # push eax 0x1009 - "6A 40" # push 0x40 0x100A - "6A 10" # push 0x10 0x100C - "56" # push esi 0x100E -).replace(' ', '')) - +CODE = ( + b'\x8B\x74\x01\x28' # mov esi, dword ptr [ecx + eax + 0x28] mapped: 0x1000 + b'\x03\xF0' # add esi, eax 0x1004 + b'\x8D\x45\xFC' # lea eax, dword ptr [ebp - 4] 0x1006 + b'\x50' # push eax 0x1009 + b'\x6A\x40' # push 0x40 0x100A + b'\x6A\x10' # push 0x10 0x100C + b'\x56' # push esi 0x100E +) BASE = 0x1000 STACK = 0x4000 -class HookCodeStopEmuTest(regress.RegressTest): +class EmuClearErrorsTest(regress.RegressTest): + def test_hook_code_stop_emu(self): mu = Uc(UC_ARCH_X86, UC_MODE_32) diff --git a/tests/regress/emu_stop_segfault.py b/tests/regress/emu_stop_segfault.py index 8c22e9dede..ba6c2280a8 100755 --- a/tests/regress/emu_stop_segfault.py +++ b/tests/regress/emu_stop_segfault.py @@ -1,9 +1,8 @@ -#!/usr/bin/python +""" See https://github.com/unicorn-engine/unicorn/issues/65 """ -"""See https://github.com/unicorn-engine/unicorn/issues/65""" - -import unicorn import regress +import unicorn + class EmuStopSegFault(regress.RegressTest): @@ -16,5 +15,6 @@ def runTest(self): # The following should not trigger a null pointer dereference self.assertEqual(None, mu.emu_stop()) + if __name__ == '__main__': regress.main() diff --git a/tests/regress/ensure_typedef_consts_generated.py b/tests/regress/ensure_typedef_consts_generated.py index ebe7939731..8c0b88d3de 100755 --- a/tests/regress/ensure_typedef_consts_generated.py +++ b/tests/regress/ensure_typedef_consts_generated.py @@ -1,6 +1,5 @@ -#!/usr/bin/python - -"""See https://github.com/unicorn-engine/unicorn/issues/161 +""" +See https://github.com/unicorn-engine/unicorn/issues/161 Ensure that constants which are specified via a typedef, rather than an enum, are included in the bindings by the script for autogenerating mappings for diff --git a/tests/regress/fpu_ip.py b/tests/regress/fpu_ip.py index 3c7a139559..f6efc7d641 100755 --- a/tests/regress/fpu_ip.py +++ b/tests/regress/fpu_ip.py @@ -1,18 +1,16 @@ -#!/usr/bin/python - import regress - +import sys +import unittest from unicorn import * from unicorn.x86_const import * -from capstone import Cs, CS_ARCH_X86, CS_ARCH_X86, CS_MODE_64, CS_MODE_32 - +from capstone import Cs, CS_ARCH_X86, CS_MODE_64, CS_MODE_32 CODE = ( b'\xc7\x04\x24\x7f\x03\x00\x00' # mov DWORD PTR [rsp],0x37f b'\xd9\x2c\x24' # fldcw WORD PTR [rsp] b'\xd9\xd0' # fnop b'\xd9\x74\x24\x08' # fnstenv [rsp+0x8] - b'\x59' # pop rcx + b'\x59' # pop rcx ) BASE = 0x00000000 @@ -29,6 +27,7 @@ def hook_code(uc, addr, size, user_data): class FpuIP(regress.RegressTest): + @unittest.skipIf(sys.version_info < (3, 7), reason="requires python3.7 or higher") def test_32(self): mu = Uc(UC_ARCH_X86, UC_MODE_32) cs = Cs(CS_ARCH_X86, CS_MODE_32) @@ -40,9 +39,10 @@ def test_32(self): mu.emu_start(BASE, BASE + len(CODE), count=5) - self.assertSequenceEqual(b'\x7f\x03\x00\x00\x00\x00\x00\x00', mu.mem_read(STACK + 8, 8)) + self.assertSequenceEqual(b'\x7f\x03\x00\x00\x00\x00\x00\x00', mu.mem_read(STACK + 8, 8)) self.assertSequenceEqual(b'\x55\x55\x00\x00\x00\x00\x00\x00', mu.mem_read(STACK + 16, 8)) + @unittest.skipIf(sys.version_info < (3, 7), reason="requires python3.7 or higher") def test_64(self): mu = Uc(UC_ARCH_X86, UC_MODE_64) cs = Cs(CS_ARCH_X86, CS_MODE_64) @@ -54,7 +54,7 @@ def test_64(self): mu.emu_start(BASE, BASE + len(CODE), count=5) - self.assertSequenceEqual(b'\x7f\x03\x00\x00\x00\x00\x00\x00', mu.mem_read(STACK + 8, 8)) + self.assertSequenceEqual(b'\x7f\x03\x00\x00\x00\x00\x00\x00', mu.mem_read(STACK + 8, 8)) self.assertSequenceEqual(b'\x55\x55\x00\x00\x00\x00\x00\x00', mu.mem_read(STACK + 16, 8)) diff --git a/tests/regress/fpu_mem_write.py b/tests/regress/fpu_mem_write.py index 19dd89173e..637d00ead2 100755 --- a/tests/regress/fpu_mem_write.py +++ b/tests/regress/fpu_mem_write.py @@ -1,14 +1,10 @@ -#!/usr/bin/python - import regress - from unicorn import * from unicorn.x86_const import * - CODE = ( b'\x9b\xd9\x3c\x24' # fstcw WORD PTR [esp] - b'\x59' # pop ecx + b'\x59' # pop ecx ) BASE = 0x00000000 @@ -17,7 +13,6 @@ def hook_mem_write(uc, access, address, size, value, user_data): regress.logger.debug("mem WRITE to: %#x, size = %u, value = %#x", address, size, value) - return True diff --git a/tests/regress/hang.py b/tests/regress/hang.py index 6cfb25403d..d317c5b0d4 100755 --- a/tests/regress/hang.py +++ b/tests/regress/hang.py @@ -1,8 +1,5 @@ -#!/usr/bin/python - import binascii import regress - from unicorn import * from unicorn.x86_const import * @@ -13,7 +10,7 @@ def hook_code(uc, address, size, user_data): if size == 0xf1f1f1f1: return - regress.logger.debug("[%#x] = %s" , address, binascii.hexlify(uc.mem_read(address, size))) + regress.logger.debug("[%#x] = %s", address, binascii.hexlify(uc.mem_read(address, size))) # callback for tracing Linux interrupt @@ -31,6 +28,7 @@ def hook_intr(uc, intno, user_data): class Hang(regress.RegressTest): + def runTest(self): # self modifying shellcode execve('/bin/sh') shellcode = ( diff --git a/tests/regress/high_mem.py b/tests/regress/high_mem.py index 07a94200de..5547bcd282 100644 --- a/tests/regress/high_mem.py +++ b/tests/regress/high_mem.py @@ -1,11 +1,9 @@ -#!/usr/bin/env python3 - import regress - +import sys +import unittest from unicorn import Uc, UcError, UC_ARCH_X86, UC_MODE_64 from unicorn.unicorn_const import UC_TLB_VIRTUAL, UC_TLB_CPU, UC_ERR_FETCH_UNMAPPED - MAX_INTEL_INSN_SIZE = 15 @@ -23,8 +21,10 @@ def map_code_page(self, address, payload): self.uc.mem_map(address, 0x1000) self.uc.mem_write(address, payload) + @unittest.skipIf(sys.version_info < (3, 7), reason="requires python3.7 or higher") def test_virt_high_mapping(self): - """Mapping memory at high addresses should work when TLB mode + """ + Mapping memory at high addresses should work when TLB mode is set to VIRTUAL. """ @@ -42,8 +42,10 @@ def test_virt_high_mapping(self): except UcError: self.fail('high mapping failed at %#018x' % code) + @unittest.skipIf(sys.version_info < (3, 7), reason="requires python3.7 or higher") def test_cpu_high_mapping(self): - """Mapping memory at high addresses should work fail TLB mode + """ + Mapping memory at high addresses should work fail TLB mode is set to CPU (default). """ diff --git a/tests/regress/hook_add_crash.py b/tests/regress/hook_add_crash.py index f24933d500..812fa23996 100755 --- a/tests/regress/hook_add_crash.py +++ b/tests/regress/hook_add_crash.py @@ -1,21 +1,15 @@ -#!/usr/bin/env python - -"""https://github.com/unicorn-engine/unicorn/issues/165""" +""" https://github.com/unicorn-engine/unicorn/issues/165 """ import regress - from unicorn import * -def hook_mem_read_unmapped(mu, access, address, size, value, user_data): - pass - class TestHook(regress.RegressTest): def test_excessive_hooks(self): mu = Uc(UC_ARCH_X86, UC_MODE_32) for _ in range(1337): - mu.hook_add(UC_HOOK_MEM_READ_UNMAPPED, hook_mem_read_unmapped) + mu.hook_add(UC_HOOK_MEM_READ_UNMAPPED, lambda *args, **kwargs: None) if __name__ == '__main__': diff --git a/tests/regress/hook_code_add_del.py b/tests/regress/hook_code_add_del.py index 67cdf66fd3..e3ce082245 100755 --- a/tests/regress/hook_code_add_del.py +++ b/tests/regress/hook_code_add_del.py @@ -1,10 +1,6 @@ -#!/usr/bin/python +""" https://github.com/unicorn-engine/unicorn/issues/334 """ -'''https://github.com/unicorn-engine/unicorn/issues/334''' - -from __future__ import print_function import regress - from unicorn import * from unicorn.x86_const import * @@ -26,6 +22,7 @@ EP = ADDRESS + 0x54 + def hook_code(mu, address, size, user_data): regress.logger.debug(">>> Tracing instruction at %#x, instruction size = %u", address, size) @@ -43,7 +40,7 @@ def runTest(self): i = emu.hook_add(UC_HOOK_CODE, hook_code, None) emu.hook_del(i) - emu.emu_start(EP, EP + len(CODE), count = 3) + emu.emu_start(EP, EP + len(CODE), count=3) regress.logger.debug("EIP: %#x", emu.reg_read(UC_X86_REG_EIP)) diff --git a/tests/regress/hook_code_stop_emu.py b/tests/regress/hook_code_stop_emu.py index bfcaa0d8a1..be28386ca0 100755 --- a/tests/regress/hook_code_stop_emu.py +++ b/tests/regress/hook_code_stop_emu.py @@ -1,11 +1,7 @@ -#!/usr/bin/python - import regress - from unicorn import * from unicorn.x86_const import * - CODE = ( b'\x48\xc7\xc0\x03\x00\x00\x00' # 0x1000: mov rax, 3 b'\x0f\x05' # 0x1007: syscall diff --git a/tests/regress/hook_raises_exception.py b/tests/regress/hook_raises_exception.py index 0c7b5bc1e2..b822cf87e3 100644 --- a/tests/regress/hook_raises_exception.py +++ b/tests/regress/hook_raises_exception.py @@ -5,7 +5,7 @@ CODE_ADDR = 0x1000 -class HookCounter(object): +class HookCounter: """Counts number of hook calls.""" def __init__(self): diff --git a/tests/regress/hook_readonly_write_local.py b/tests/regress/hook_readonly_write_local.py index af1ed90d85..7555e1f727 100755 --- a/tests/regress/hook_readonly_write_local.py +++ b/tests/regress/hook_readonly_write_local.py @@ -1,21 +1,18 @@ -#!/usr/bin/python - import regress - from unicorn import * from unicorn.x86_const import * - PAGE_SIZE = 0x1000 ACCESS_ADDR = 0x1000 CODE = ( - b'\xA1\x00\x10\x00\x00' # mov eax, [0x1000] - b'\xA1\x00\x10\x00\x00' # mov eax, [0x1000] + b'\xA1\x00\x10\x00\x00' # mov eax, [0x1000] + b'\xA1\x00\x10\x00\x00' # mov eax, [0x1000] ) BASE = 0x00000000 + def hook_mem_read(uc, access, address, size, value, data): regress.logger.debug("Reading at %#x", address) # BUG: unicorn will segfault when calling "uc.mem_write" to write to a location that was mapped only as UC_PROT_READ @@ -29,7 +26,7 @@ def runTest(self): mu.mem_map(BASE, PAGE_SIZE) mu.mem_write(BASE, CODE) mu.mem_map(ACCESS_ADDR, PAGE_SIZE, UC_PROT_READ) - mu.hook_add(UC_HOOK_MEM_READ, hook_mem_read, begin = ACCESS_ADDR, end = ACCESS_ADDR + PAGE_SIZE) + mu.hook_add(UC_HOOK_MEM_READ, hook_mem_read, begin=ACCESS_ADDR, end=ACCESS_ADDR + PAGE_SIZE) mu.emu_start(BASE, BASE + len(CODE)) diff --git a/tests/regress/init.py b/tests/regress/init.py index a7391ccb69..fa89fb54c4 100755 --- a/tests/regress/init.py +++ b/tests/regress/init.py @@ -1,32 +1,25 @@ -#!/usr/bin/python # By Mariano Graziano +import platform import regress import struct import sys - +import unittest from unicorn import * from unicorn.x86_const import * -if sys.version_info.major == 2: - range = xrange - - -mu = 0 - class Init(regress.RegressTest): def init_unicorn(self, ip, sp, counter): - global mu - #print "[+] Emulating IP: %x SP: %x - Counter: %x" % (ip, sp, counter) - mu = Uc(UC_ARCH_X86, UC_MODE_64) - mu.mem_map(0x1000000, 2 * 1024 * 1024) - mu.mem_write(0x1000000, b"\x90") - mu.mem_map(0x8000000, 8 * 1024 * 1024) - mu.reg_write(UC_X86_REG_RSP, sp) + regress.logger.debug("[+] Emulating IP: %x SP: %x - Counter: %x" % (ip, sp, counter)) + self.emulator = Uc(UC_ARCH_X86, UC_MODE_64) + self.emulator.mem_map(0x1000000, 2 * 1024 * 1024) + self.emulator.mem_write(0x1000000, b"\x90") + self.emulator.mem_map(0x8000000, 8 * 1024 * 1024) + self.emulator.reg_write(UC_X86_REG_RSP, sp) content = self.generate_value(counter) - mu.mem_write(sp, content) + self.emulator.mem_write(sp, content) self.set_hooks() def generate_value(self, counter): @@ -36,46 +29,41 @@ def generate_value(self, counter): return struct.pack(">> Missing memory is being WRITE at 0x%x, data size = %u, data value = 0x%x", address, size, value) + regress.logger.debug(">>> Missing memory is being WRITE at 0x%x, data size = %u, data value = 0x%x", + address, size, value) address_page = address & 0xFFFFFFFFFFFFF000 - mu.mem_map(address_page, 2 * 1024 * 1024) - mu.mem_write(address, str(value)) + uc.mem_map(address_page, 2 * 1024 * 1024) + uc.mem_write(address, str(value)) return True - else: - return False + return False def hook_mem_fetch_unmapped(self, uc, access, address, size, value, user_data): - global mu - regress.logger.debug("[ HOOK_MEM_FETCH - Address: 0x%x ]", address) regress.logger.debug("[ mem_fetch_unmapped: faulting address at 0x%x ]", address) - mu.mem_write(0x1000003, b"\x90") - mu.reg_write(UC_X86_REG_RIP, 0x1000001) + uc.mem_write(0x1000003, b"\x90") + uc.reg_write(UC_X86_REG_RIP, 0x1000001) return True + @unittest.skipIf(sys.version_info < (3, 7), reason="requires python3.7 or higher") + @unittest.skipIf(sys.platform == 'win32' or platform.machine().lower() not in ('x86_64', 'arm64'), 'TO BE CHECKED!') def runTest(self): - global mu - ips = range(0x1000000, 0x1001000) sps = range(0x8000000, 0x8001000) for i, (ip, sp) in enumerate(zip(ips, sps)): self.init_unicorn(ip, sp, i) - mu.emu_start(0x1000000, 0x1000000 + 0x1) + self.emulator.emu_start(0x1000000, 0x1000000 + 0x1) if __name__ == '__main__': diff --git a/tests/regress/invalid_insn.py b/tests/regress/invalid_insn.py index 42d74360f8..b3c7dd95eb 100644 --- a/tests/regress/invalid_insn.py +++ b/tests/regress/invalid_insn.py @@ -1,16 +1,12 @@ - import regress - from unicorn import * from unicorn.x86_const import * - from capstone import * - -CODE = bytes.fromhex( - '48 31 c0' # xor rax,rax - '48 0f c7 f0' # rdrand rax - 'f4' # hlt +CODE = ( + b'\x48\x31\xc0' # xor rax,rax + b'\x48\x0f\xc7\xf0' # rdrand rax + b'\xf4' # hlt ) BASE = 0x100000 @@ -41,7 +37,7 @@ def hook_invalid_insn(uc, ud): # signal uc we are ok return True - + # not handled, uc will crash return False diff --git a/tests/regress/invalid_write.py b/tests/regress/invalid_write.py index b37305fc3c..92aa211a05 100755 --- a/tests/regress/invalid_write.py +++ b/tests/regress/invalid_write.py @@ -1,13 +1,10 @@ -#!/usr/bin/env python # Test callback that returns False to cancel emulation -from __future__ import print_function +import regress from unicorn import * from unicorn.x86_const import * -import regress - -X86_CODE32_MEM_WRITE = b"\x89\x0D\xAA\xAA\xAA\xAA\x41\x4a" # mov [0xaaaaaaaa], ecx; INC ecx; DEC edx +X86_CODE32_MEM_WRITE = b"\x89\x0D\xAA\xAA\xAA\xAA\x41\x4a" # mov [0xaaaaaaaa], ecx; INC ecx; DEC edx # callback for tracing invalid memory access (READ or WRITE) diff --git a/tests/regress/jmp_ebx_hang.py b/tests/regress/jmp_ebx_hang.py index a4c5346e05..24ad066206 100755 --- a/tests/regress/jmp_ebx_hang.py +++ b/tests/regress/jmp_ebx_hang.py @@ -1,16 +1,13 @@ -#!/usr/bin/env python - -"""See https://github.com/unicorn-engine/unicorn/issues/82""" +""" See https://github.com/unicorn-engine/unicorn/issues/82 """ import regress - from unicorn import * from unicorn.x86_const import * - CODE_ADDR = 0x10101000 CODE = b'\xff\xe3' # jmp ebx + class JumEbxHang(regress.RegressTest): def runTest(self): mu = Uc(UC_ARCH_X86, UC_MODE_32) diff --git a/tests/regress/jumping.py b/tests/regress/jumping.py index 8df38c7fbd..04c6efc630 100755 --- a/tests/regress/jumping.py +++ b/tests/regress/jumping.py @@ -1,13 +1,11 @@ -#!/usr/bin/env python # Mariano Graziano -import binascii import regress - +import sys +import unittest from unicorn import * from unicorn.x86_const import * - # set rdx to either 0xbabe or 0xc0ca, based on a comparison. # rdx would never be set to 0xbabe unless we set zf to 1 CODE = ( @@ -68,20 +66,20 @@ def hook_block(self, uc, address, size, _): # callback for tracing instructions def hook_code(self, uc, address, size, _): insn = uc.mem_read(address, size) - regress.logger.debug(">>> Tracing instruction at %#x : %s", address, binascii.hexlify(insn)) + regress.logger.debug(">>> Tracing instruction at %#x : %s", address, insn.hex()) regs = uc.reg_read_batch(( UC_X86_REG_RAX, UC_X86_REG_RBX, UC_X86_REG_RCX, UC_X86_REG_RDX, UC_X86_REG_RSI, UC_X86_REG_RDI, UC_X86_REG_RBP, UC_X86_REG_RSP, - UC_X86_REG_R8, UC_X86_REG_R9, UC_X86_REG_R10, UC_X86_REG_R11, + UC_X86_REG_R8, UC_X86_REG_R9, UC_X86_REG_R10, UC_X86_REG_R11, UC_X86_REG_R12, UC_X86_REG_R13, UC_X86_REG_R14, UC_X86_REG_R15, UC_X86_REG_EFLAGS )) zf = (regs[16] >> 6) & 0b1 - regress.logger.debug(" RAX = %08x, R8 = %08x", regs[0], regs[ 8]) - regress.logger.debug(" RBX = %08x, R9 = %08x", regs[1], regs[ 9]) + regress.logger.debug(" RAX = %08x, R8 = %08x", regs[0], regs[8]) + regress.logger.debug(" RBX = %08x, R9 = %08x", regs[1], regs[9]) regress.logger.debug(" RCX = %08x, R10 = %08x", regs[2], regs[10]) regress.logger.debug(" RDX = %08x, R11 = %08x", regs[3], regs[11]) regress.logger.debug(" RSI = %08x, R12 = %08x", regs[4], regs[12]) @@ -94,7 +92,6 @@ def hook_code(self, uc, address, size, _): self.multipath() regress.logger.debug("-" * 32) - def setUp(self): # decide how to fixate zf value: 0 to clear, 1 to set self.fixed_zf = 1 @@ -110,6 +107,7 @@ def setUp(self): self.uc = uc + @unittest.skipIf(sys.version_info < (3, 7), reason="requires python3.7 or higher") def runTest(self): # tracing all basic blocks with customized callback self.uc.hook_add(UC_HOOK_BLOCK, self.hook_block) @@ -120,7 +118,8 @@ def runTest(self): # emulate machine code in infinite time self.uc.emu_start(BASE, BASE + len(CODE)) - self.assertEqual(self.uc.reg_read(UC_X86_REG_RDX), 0xbabe, "rdx contains the wrong value. eflags modification failed") + self.assertEqual(self.uc.reg_read(UC_X86_REG_RDX), 0xbabe, + "rdx contains the wrong value. eflags modification failed") if __name__ == '__main__': diff --git a/tests/regress/leaked_refs.py b/tests/regress/leaked_refs.py index c8091cf4a3..9d01734b0d 100644 --- a/tests/regress/leaked_refs.py +++ b/tests/regress/leaked_refs.py @@ -1,13 +1,9 @@ -#!/usr/bin/python - import gc import regress import weakref - from unicorn import * from unicorn.x86_const import * - ADDRESS = 0x8048000 STACK_ADDRESS = 0xffff000 STACK_SIZE = 0x1000 @@ -26,10 +22,10 @@ EP = ADDRESS + 0x54 - # Dictionary to keep weak references to instances instances = weakref.WeakValueDictionary() + def create_instance(key, *args, **kwargs): obj = Uc(*args, **kwargs) instances[key] = obj @@ -52,22 +48,22 @@ def emu_loop(key): i = emu.hook_add(UC_HOOK_CODE, hook_code, None) emu.hook_del(i) - emu.emu_start(EP, EP + len(CODE), count = 3) + emu.emu_start(EP, EP + len(CODE), count=3) regress.logger.debug("EIP: %#x", emu.reg_read(UC_X86_REG_EIP)) -def debugMem(): - gc.collect() # don't care about stuff that would be garbage collected properly - assert(len(instances) == 0) +class EmuLoopReferenceTest(regress.RegressTest): + def debug_mem(self): + gc.collect() # don't care about stuff that would be garbage collected properly + self.assertEqual(len(instances), 0) -class EmuLoopReferenceTest(regress.RegressTest): def runTest(self): for i in range(5): emu_loop('obj%d' % i) - debugMem() + self.debug_mem() if __name__ == '__main__': diff --git a/tests/regress/memmap.py b/tests/regress/memmap.py index 305c99eba6..408a764666 100755 --- a/tests/regress/memmap.py +++ b/tests/regress/memmap.py @@ -1,12 +1,9 @@ -#!/usr/bin/python # By Ryan Hileman, issue #9 # this prints out 2 lines and the contents must be the same import regress - from unicorn import * -from unicorn.x86_const import * class MemMap(regress.RegressTest): @@ -40,7 +37,7 @@ def test_mmap_weird(self): for i in range(20): with self.assertRaises(UcError): u.mem_map(i * 0x1000, 5) - u.mem_read(i * 0x1000+6, 1) + u.mem_read(i * 0x1000 + 6, 1) if __name__ == '__main__': diff --git a/tests/regress/memmap_segfault.py b/tests/regress/memmap_segfault.py index c2e84933ce..4deeb62afd 100755 --- a/tests/regress/memmap_segfault.py +++ b/tests/regress/memmap_segfault.py @@ -1,9 +1,5 @@ -#!/usr/bin/env python - import regress - from unicorn import * -from unicorn.x86_const import * class MmapSeg1(regress.RegressTest): diff --git a/tests/regress/mips_branch_delay.py b/tests/regress/mips_branch_delay.py index 959a7f58d9..d9a5d0b91c 100755 --- a/tests/regress/mips_branch_delay.py +++ b/tests/regress/mips_branch_delay.py @@ -1,15 +1,13 @@ -#!/usr/bin/python - import regress - +import sys +import unittest from capstone import * from unicorn import * - CODE = ( - b'\x00\x00\xa4\x12' # beq $a0, $s5, 0x4008a0 - b'\x6a\x00\x82\x28' # slti $v0, $a0, 0x6a - b'\x00\x00\x00\x00' # nop + b'\x00\x00\xa4\x12' # beq $a0, $s5, 0x4008a0 + b'\x6a\x00\x82\x28' # slti $v0, $a0, 0x6a + b'\x00\x00\x00\x00' # nop ) BASE = 0x400000 @@ -17,6 +15,7 @@ class MipsBranchDelay(regress.RegressTest): + @unittest.skipIf(sys.version_info < (3, 7), reason="requires python3.7 or higher") def runTest(self): md = Cs(CS_ARCH_MIPS, CS_MODE_MIPS32 + CS_MODE_LITTLE_ENDIAN) diff --git a/tests/regress/mips_cp1.py b/tests/regress/mips_cp1.py index 9f8956f2c9..d7c674f29c 100644 --- a/tests/regress/mips_cp1.py +++ b/tests/regress/mips_cp1.py @@ -1,11 +1,8 @@ - import regress - from unicorn import * from unicorn.mips_const import * - -CODE = b'\x44\x43\xF8\x00' # cfc1 $v1, FCSR +CODE = b'\x44\x43\xF8\x00' # cfc1 $v1, FCSR BASE = 0x416CB0 diff --git a/tests/regress/mips_except.py b/tests/regress/mips_except.py index 48f01d66f9..d05f44e340 100755 --- a/tests/regress/mips_except.py +++ b/tests/regress/mips_except.py @@ -1,14 +1,12 @@ -#!/usr/bin/python - import regress - +import sys +import unittest from unicorn import * from unicorn.mips_const import * - CODE = ( - b'\x00\x00\x00\x00' # nop - b'\x00\x00\xa4\x8f' # lw $a0, 0($sp) + b'\x00\x00\x00\x00' # nop + b'\x00\x00\xa4\x8f' # lw $a0, 0($sp) ) BASE = 0x20000000 @@ -16,6 +14,7 @@ class MipsExcept(regress.RegressTest): + @unittest.skipIf(sys.version_info < (3, 7), reason="requires python3.7 or higher") def runTest(self): uc = Uc(UC_ARCH_MIPS, UC_MODE_MIPS32 + UC_MODE_LITTLE_ENDIAN) diff --git a/tests/regress/mips_kernel_mmu.py b/tests/regress/mips_kernel_mmu.py index 584af41d61..79c8f54b2b 100755 --- a/tests/regress/mips_kernel_mmu.py +++ b/tests/regress/mips_kernel_mmu.py @@ -1,16 +1,12 @@ -#!/usr/bin/python - import regress - from unicorn import * from unicorn.mips_const import * - -CODE = b'\x34\x21\x34\x56' # ori $at, $at, 0x3456 +CODE = b'\x34\x21\x34\x56' # ori $at, $at, 0x3456 BASE = 0x10000000 -class MipsSyscall(regress.RegressTest): +class MipsKernelMMU(regress.RegressTest): def test_syscall(self): uc = Uc(UC_ARCH_MIPS, UC_MODE_MIPS32 + UC_MODE_BIG_ENDIAN) diff --git a/tests/regress/mips_single_step_sp.py b/tests/regress/mips_single_step_sp.py index d4ceea9f35..6b28b9eca8 100755 --- a/tests/regress/mips_single_step_sp.py +++ b/tests/regress/mips_single_step_sp.py @@ -1,27 +1,26 @@ -#!/usr/bin/python - import regress - +import sys +import unittest from unicorn import * from unicorn.mips_const import * - CODE = ( - b'\xf8\xff\x01\x24' # addiu $at, $zero, -8 - b'\x24\xe8\xa1\x03' # and $sp, $sp, $at - b'\x09\xf8\x20\x03' # jalr $t9 - b'\xe8\xff\xbd\x23' # addi $sp, $sp, -0x18 - b'\xb8\xff\xbd\x27' # addiu $sp, $sp, -0x48 - b'\x00\x00\x00\x00' # nop + b'\xf8\xff\x01\x24' # addiu $at, $zero, -8 + b'\x24\xe8\xa1\x03' # and $sp, $sp, $at + b'\x09\xf8\x20\x03' # jalr $t9 + b'\xe8\xff\xbd\x23' # addi $sp, $sp, -0x18 + b'\xb8\xff\xbd\x27' # addiu $sp, $sp, -0x48 + b'\x00\x00\x00\x00' # nop ) BASE = 0x4010dc + def code_hook(uc, addr, size, user_data): regress.logger.debug('code hook: pc=%08x sp=%08x', addr, uc.reg_read(UC_MIPS_REG_SP)) -def run(step) -> int: +def run(step): uc = Uc(UC_ARCH_MIPS, UC_MODE_MIPS32 + UC_MODE_LITTLE_ENDIAN) if step: @@ -45,6 +44,8 @@ def run(step) -> int: class MipsSingleStep(regress.RegressTest): + + @unittest.skipIf(sys.version_info < (3, 7), reason="requires python3.7 or higher") def runTest(self): sp1 = run(step=False) sp2 = run(step=True) diff --git a/tests/regress/mips_syscall_pc.py b/tests/regress/mips_syscall_pc.py index 09586bb44c..693674b525 100755 --- a/tests/regress/mips_syscall_pc.py +++ b/tests/regress/mips_syscall_pc.py @@ -1,16 +1,14 @@ -#!/usr/bin/python - import regress - from unicorn import * from unicorn.mips_const import * - CODE = b'\x0c\x00\x00\x00' # syscall BASE = 0x40000 + def intr_hook(uc, intno, data): - regress.logger.debug('interrupt=%d, v0=%d, pc=%#010x', intno, uc.reg_read(UC_MIPS_REG_V0), uc.reg_read(UC_MIPS_REG_PC)) + regress.logger.debug('interrupt=%d, v0=%d, pc=%#010x', intno, uc.reg_read(UC_MIPS_REG_V0), + uc.reg_read(UC_MIPS_REG_PC)) class MipsSyscall(regress.RegressTest): @@ -22,7 +20,7 @@ def test(self): uc.reg_write(UC_MIPS_REG_V0, 100) uc.hook_add(UC_HOOK_INTR, intr_hook) - uc.emu_start(BASE, BASE+len(CODE)) + uc.emu_start(BASE, BASE + len(CODE)) self.assertEqual(0x40004, uc.reg_read(UC_MIPS_REG_PC)) diff --git a/tests/regress/mov_gs_eax.py b/tests/regress/mov_gs_eax.py index 9237f5786c..5178c03222 100755 --- a/tests/regress/mov_gs_eax.py +++ b/tests/regress/mov_gs_eax.py @@ -1,11 +1,7 @@ -#!/usr/bin/python - import regress - from unicorn import * from unicorn.x86_const import * - CODE = ( b'\x8e\xe8' # mov gs, eax b'\xb8\x01\x00\x00\x00' # mov eax, 1 @@ -14,7 +10,7 @@ BASE = 0x1000 -class VldrPcInsn(regress.RegressTest): +class MovGsEax(regress.RegressTest): def runTest(self): uc = Uc(UC_ARCH_X86, UC_MODE_32) diff --git a/tests/regress/movsd.py b/tests/regress/movsd.py index 34ea93cf64..898fb8a031 100755 --- a/tests/regress/movsd.py +++ b/tests/regress/movsd.py @@ -1,12 +1,10 @@ -#!/usr/bin/python # By Ryan Hileman, issue #3 import regress - +import sys +import unittest from capstone import Cs, CS_ARCH_X86, CS_MODE_64 from unicorn import * -from unicorn.x86_const import * - CODE = b'\xf2\x0f\x10\x05\xaa\x12\x00\x00' @@ -24,6 +22,8 @@ def hook_code(uc, addr, size, md): class Movsd(regress.RegressTest): + + @unittest.skipIf(sys.version_info < (3, 7), reason="requires python3.7 or higher") def runTest(self): addr = 0x400000 mu = Uc(UC_ARCH_X86, UC_MODE_64) diff --git a/tests/regress/osx_qemu_thread_create_crash.py b/tests/regress/osx_qemu_thread_create_crash.py index b2abf097e7..41cda64aa0 100755 --- a/tests/regress/osx_qemu_thread_create_crash.py +++ b/tests/regress/osx_qemu_thread_create_crash.py @@ -1,9 +1,7 @@ -#!/usr/bin/env python - import regress - from unicorn import * + # OS X: OK with 2047 iterations. # OS X: Crashes at 2048:th iteration ("qemu: qemu_thread_create: Resource temporarily unavailable"). # Linux: No crashes observed. diff --git a/tests/regress/potential_memory_leak.py b/tests/regress/potential_memory_leak.py index a37d0bdc8e..a8705eae72 100755 --- a/tests/regress/potential_memory_leak.py +++ b/tests/regress/potential_memory_leak.py @@ -1,15 +1,22 @@ -#!/usr/bin/env python - import platform -import resource import regress - +import sys +import unittest from unicorn import * +try: + # Only available on Unix: https://docs.python.org/3/library/resource.html + import resource +except: + pass ITERATIONS = 10000 + class MemoryLeak(regress.RegressTest): + + @unittest.skipIf(sys.platform == 'win32', reason='Test for Unix only') + @unittest.skipIf(platform.machine().lower() == 'aarch64', reason='TO BE CHECKED!') def test(self): if platform.system() == "Darwin": rusage_multiplier = 1 diff --git a/tests/regress/pshufb.py b/tests/regress/pshufb.py index 24eba5e5f9..3ee7ccc692 100755 --- a/tests/regress/pshufb.py +++ b/tests/regress/pshufb.py @@ -1,8 +1,6 @@ -#!/usr/bin/python # By Ryan Hileman, issue #91 import regress - from unicorn import * from unicorn.x86_const import * @@ -15,7 +13,7 @@ def runTest(self): uc.mem_map(0x2000, 0x1000) - uc.mem_write(0x2000, b'\x66\x0f\x38\x00\xc1') # pshufb xmm0, xmm1 + uc.mem_write(0x2000, b'\x66\x0f\x38\x00\xc1') # pshufb xmm0, xmm1 # Invalid instruction -> test failed uc.emu_start(0x2000, 0x2005) diff --git a/tests/regress/reg_write_sign_extension.py b/tests/regress/reg_write_sign_extension.py index 813c5a00cc..61e4ab261e 100755 --- a/tests/regress/reg_write_sign_extension.py +++ b/tests/regress/reg_write_sign_extension.py @@ -1,26 +1,22 @@ -#!/usr/bin/env python - -"""See https://github.com/unicorn-engine/unicorn/issues/98""" +""" See https://github.com/unicorn-engine/unicorn/issues/98 """ import regress - from unicorn import * ADDR = 0xffaabbcc -def hook_mem_invalid(mu, access, address, size, value, user_data): - regress.logger.debug(">>> Access type: %u, expected value: 0x%x, actual value: 0x%x", access, ADDR, address) - - assert(address == ADDR) +class RegWriteSignExt(regress.RegressTest): - mu.mem_map(address & 0xfffff000, 4 * 1024) - mu.mem_write(address, b'\xcc') + def hook_mem_invalid(self, mu, access, address, size, value, user_data): + regress.logger.debug(">>> Access type: %u, expected value: 0x%x, actual value: 0x%x", access, ADDR, address) - return True + self.assertEqual(address, ADDR) + mu.mem_map(address & 0xfffff000, 4 * 1024) + mu.mem_write(address, b'\xcc') -class RegWriteSignExt(regress.RegressTest): + return True def runTest(self): mu = Uc(UC_ARCH_X86, UC_MODE_32) @@ -30,7 +26,7 @@ def runTest(self): # jmp ebx mu.mem_write(0x10000000, b'\xff\xe3') - mu.hook_add(UC_HOOK_MEM_FETCH_UNMAPPED | UC_HOOK_MEM_FETCH_PROT, hook_mem_invalid) + mu.hook_add(UC_HOOK_MEM_FETCH_UNMAPPED | UC_HOOK_MEM_FETCH_PROT, self.hook_mem_invalid) mu.emu_start(0x10000000, 0x10000000 + 2, count=1) diff --git a/tests/regress/regress.py b/tests/regress/regress.py index 00d5bd4b9b..21481f9dee 100755 --- a/tests/regress/regress.py +++ b/tests/regress/regress.py @@ -1,27 +1,25 @@ -#!/usr/bin/env python - -import glob import logging import os import unittest class RegressTest(unittest.TestCase): - """Regress test case dummy class. - """ + """ Regress test case dummy class. """ + + +def main(): + unittest.main() def __setup_logger(name): - """Set up a unified logger for all tests. - """ + """ Set up a unified logger for all tests. """ instance = logging.getLogger(name) instance.propagate = False - handler = logging.StreamHandler() - formatter = logging.Formatter('[%(levelname)s] %(message)s') - - if not instance.hasHandlers(): + if not instance.handlers: + handler = logging.StreamHandler() + formatter = logging.Formatter('[%(levelname)s] %(message)s') handler.setFormatter(formatter) instance.addHandler(handler) @@ -29,36 +27,4 @@ def __setup_logger(name): logger = __setup_logger('UnicornRegress') -logger.setLevel(os.environ.get("UNICORN_DEBUG", "INFO").upper()) - - -def main(): - unittest.main() - - -if __name__ == '__main__': - suite = unittest.TestSuite() - - logger.info('starting discovery') - - # Find all unittest type in this directory and run it. - directory = os.path.dirname(__file__) or '.' - pyfiles = glob.glob(directory + '/*.py') - modules = [os.path.splitext(os.path.basename(f))[0] for f in pyfiles if os.path.isfile(f) and f != __file__] - - logger.info('%d test modules found', len(modules)) - - for mname in modules: - try: - module = __import__(mname) - except ImportError as ex: - logger.error('could not load %s: %s is missing', mname, ex.name) - else: - tests = unittest.defaultTestLoader.loadTestsFromModule(module) - suite.addTests(tests) - - logger.debug('found %d test cases in %s', tests.countTestCases(), mname) - - logger.info('%d test cases were added', suite.countTestCases()) - - unittest.TextTestRunner().run(suite) +logger.setLevel((os.getenv('REGRESS_LOG_LEVEL') or 'INFO').upper()) diff --git a/tests/regress/rep_hook.py b/tests/regress/rep_hook.py index cbcb9c1456..8d2a91583a 100755 --- a/tests/regress/rep_hook.py +++ b/tests/regress/rep_hook.py @@ -1,11 +1,7 @@ -#!/usr/bin/python - import regress - from unicorn import * from unicorn.x86_const import * - PAGE_SIZE = 0x1000 CODE = b'\xf3\xaa' # rep stosb diff --git a/tests/regress/run_across_bb.py b/tests/regress/run_across_bb.py index 62326751c1..14f8819b85 100755 --- a/tests/regress/run_across_bb.py +++ b/tests/regress/run_across_bb.py @@ -1,4 +1,3 @@ -#!/usr/bin/python # # This test demonstrates emulation behavior within and across # basic blocks. @@ -6,11 +5,9 @@ import binascii import struct import regress - from unicorn import * from unicorn.x86_const import * - CODE = ( b"\xb8\x00\x00\x00\x00" # 1000: mov eax,0x0 b"\x40" # 1005: inc eax @@ -22,7 +19,7 @@ b"\xcc" # 100f: int3 b"\xb8\x00\x00\x00\x00" # 1010: mov eax,0x0 b"\x40" # 1015: inc eax - b"\x40" # 1016: inc eax + b"\x40" # 1016: inc eax ) @@ -116,13 +113,11 @@ def hook_code(uc, address, size, user_data): showpc(mu) - ####################################################################### # emu_run ONE: # exectue four instructions, until the last instruction in a BB ####################################################################### - mu.emu_start(0x1000, 0x100c) # should exec the following four instructions: # 1000: b8 00 00 00 00 mov eax,0x0 < @@ -134,22 +129,20 @@ def hook_code(uc, address, size, user_data): self.assertEqual(0x100c, mu.reg_read(UC_X86_REG_EIP), "unexpected PC (2)") # single push, so stack diff is 0x4 - TOP_OF_STACK = 0x2800-0x4 + TOP_OF_STACK = 0x2800 - 0x4 self.assertEqual(TOP_OF_STACK, mu.reg_read(UC_X86_REG_ESP), "unexpected SP (2)") # top of stack should be 0x1010 - self.assertEqual(0x1010, + self.assertEqual(0x1010, struct.unpack("> 30) & 0b1) -class TestReadMem(regress.RegressTest): +class TestIssue287(regress.RegressTest): def runTest(self): uc = Uc(UC_ARCH_ARM, UC_MODE_ARM) diff --git a/tests/regress/translator_buffer.py b/tests/regress/translator_buffer.py index 74dbfafb6e..a3e440a440 100755 --- a/tests/regress/translator_buffer.py +++ b/tests/regress/translator_buffer.py @@ -1,9 +1,9 @@ -#!/usr/bin/python # By Mariano Graziano -import struct +import platform import regress - +import struct +import unittest from unicorn import * from unicorn.x86_const import * @@ -21,7 +21,7 @@ def __page_aligned(address): regress.logger.debug("mapping code : %#x", __page_aligned(code)) regress.logger.debug("mapping stack : %#x", __page_aligned(stack)) - self.mu.mem_map(__page_aligned(code), 0x1000) + self.mu.mem_map(__page_aligned(code), 0x1000) self.mu.mem_map(__page_aligned(stack), 0x1000) self.mu.reg_write(UC_X86_REG_RSP, stack) @@ -44,7 +44,8 @@ def hook_mem_fetch_unmapped(self, uc, access, address, size, value, user_data): return True def hook_mem_invalid(self, uc, access, address, size, value, user_data): - regress.logger.debug("invalid mem access: access type = %d, to = %#x, size = %u, value = %#x", access, address, size, value) + regress.logger.debug("invalid mem access: access type = %d, to = %#x, size = %u, value = %#x", access, address, + size, value) return True @@ -65,7 +66,7 @@ def write_reg(self, reg, value): self.mu.reg_write(reg, value) -class Init(regress.RegressTest): +class TranslatorBuffer(regress.RegressTest): def init_unicorn(self, ip, sp, magic): emu = Emulator(ip, sp) @@ -74,10 +75,11 @@ def init_unicorn(self, ip, sp, magic): emu.emu(1) + @unittest.skipIf(platform.machine().lower() == 'aarch64', reason='TO BE CHECKED!') def runTest(self): - ip_base = 0x000fffff816a0000 # was: 0xffffffff816a0000 - sp_base = 0x000f88001b800000 # was: 0xffff88001b800000 - mg_base = 0x000f880026f02000 # was: 0xffff880026f02000 + ip_base = 0x000fffff816a0000 # was: 0xffffffff816a0000 + sp_base = 0x000f88001b800000 # was: 0xffff88001b800000 + mg_base = 0x000f880026f02000 # was: 0xffff880026f02000 ips = range(0x9000, 0xf000, 8) sps = range(0x0000, 0x6000, 8) diff --git a/tests/regress/vld.py b/tests/regress/vld.py index 7b3ca907d4..c811424a18 100755 --- a/tests/regress/vld.py +++ b/tests/regress/vld.py @@ -1,25 +1,21 @@ -#!/usr/bin/env python # Moshe Kravchik import binascii import regress - from unicorn import * from unicorn.arm_const import * - -#enable VFP +# enable VFP ENABLE_VFP_CODE = ( - b"\x4f\xf4\x70\x03" # 00000016 mov.w r3, #0xf00000 - b"\x01\xee\x50\x3f" # 0000001a mcr p15, #0x0, r3, c1, c0, #0x2 - b"\xbf\xf3\x6f\x8f" # 0000bfb6 isb sy - b"\x4f\xf0\x80\x43" # 0000bfba mov.w r3, #0x40000000 - b"\xe8\xee\x10\x3a" # 0000bfbe vmsr fpexc, r3 + b"\x4f\xf4\x70\x03" # 00000016 mov.w r3, #0xf00000 + b"\x01\xee\x50\x3f" # 0000001a mcr p15, #0x0, r3, c1, c0, #0x2 + b"\xbf\xf3\x6f\x8f" # 0000bfb6 isb sy + b"\x4f\xf0\x80\x43" # 0000bfba mov.w r3, #0x40000000 + b"\xe8\xee\x10\x3a" # 0000bfbe vmsr fpexc, r3 ) - -VLD_CODE = b"\x21\xf9\x0f\x6a" # 0000002a vld1.8 {d6, d7}, [r1] -VST_CODE = b"\x00\xf9\x0f\x6a" # 0000002e vst1.8 {d6, d7}, [r0] +VLD_CODE = b"\x21\xf9\x0f\x6a" # 0000002a vld1.8 {d6, d7}, [r1] +VST_CODE = b"\x00\xf9\x0f\x6a" # 0000002e vst1.8 {d6, d7}, [r0] # memory address where emulation starts ADDRESS = 0x10000 @@ -27,8 +23,9 @@ class SIMDNotReadArm(regress.RegressTest): + def runTest(self): - code = ENABLE_VFP_CODE+VLD_CODE+VST_CODE + code = ENABLE_VFP_CODE + VLD_CODE + VST_CODE regress.logger.debug("Emulate THUMB code") # Initialize emulator in thumb mode @@ -84,7 +81,7 @@ def runTest(self): regress.logger.debug(">>> PC = %#x", mu.reg_read(UC_ARM_REG_PC)) for i in range(UC_ARM_REG_R0, UC_ARM_REG_R12): - regress.logger.debug("\tR%d = %#x", (i-UC_ARM_REG_R0), mu.reg_read(i)) + regress.logger.debug("\tR%d = %#x", (i - UC_ARM_REG_R0), mu.reg_read(i)) regress.logger.debug("\tD6 = %#x", mu.reg_read(UC_ARM_REG_D6)) regress.logger.debug("\tD7 = %#x", mu.reg_read(UC_ARM_REG_D7)) diff --git a/tests/regress/write_before_map.py b/tests/regress/write_before_map.py index 176dff4837..6c68f5fd6f 100755 --- a/tests/regress/write_before_map.py +++ b/tests/regress/write_before_map.py @@ -1,12 +1,8 @@ -#!/usr/bin/env python - import regress - from unicorn import * from unicorn.x86_const import * - -X86_CODE64 = b"\x90" # NOP +X86_CODE64 = b"\x90" # NOP class WriteBeforeMap(regress.RegressTest): diff --git a/tests/regress/wrong_rip.py b/tests/regress/wrong_rip.py index 87549806c2..35ead87122 100755 --- a/tests/regress/wrong_rip.py +++ b/tests/regress/wrong_rip.py @@ -1,12 +1,10 @@ -#!/usr/bin/python - +import regress from unicorn import * from unicorn.x86_const import * -import regress +binary1 = b'\xb8\x02\x00\x00\x00' # mov eax, 2 +binary2 = b'\xb8\x01\x00\x00\x00' # mov eax, 1 -binary1 = b'\xb8\x02\x00\x00\x00' # mov eax, 2 -binary2 = b'\xb8\x01\x00\x00\x00' # mov eax, 1 class WrongRIP(regress.RegressTest): @@ -40,7 +38,7 @@ def test_step2(self): self.assertEqual(0xa, mu.reg_read(UC_X86_REG_RIP)) def test_step3(self): - bin3 = b'\x40\x01\xc1\x31\xf6' # inc eax; add ecx, eax; xor esi, esi + bin3 = b'\x40\x01\xc1\x31\xf6' # inc eax; add ecx, eax; xor esi, esi mu = Uc(UC_ARCH_X86, UC_MODE_32) mu.mem_map(0, 2 * 1024 * 1024) # write machine code to be emulated to memory @@ -51,7 +49,7 @@ def test_step3(self): self.assertEqual(0x1, mu.reg_read(UC_X86_REG_EIP)) def test_step_then_fin(self): - bin4 = b'\x40\x01\xc1\x31\xf6\x90\x90\x90' # inc eax; add ecx, eax; xor esi, esi + bin4 = b'\x40\x01\xc1\x31\xf6\x90\x90\x90' # inc eax; add ecx, eax; xor esi, esi mu = Uc(UC_ARCH_X86, UC_MODE_32) mu.mem_map(0, 2 * 1024 * 1024) # write machine code to be emulated to memory @@ -66,6 +64,6 @@ def test_step_then_fin(self): self.assertEqual(0x1, mu.reg_read(UC_X86_REG_EAX)) self.assertEqual(len(bin4), mu.reg_read(UC_X86_REG_EIP)) + if __name__ == '__main__': regress.main() - diff --git a/tests/regress/wrong_rip_arm.py b/tests/regress/wrong_rip_arm.py index 4c2d9ff007..85293de30a 100755 --- a/tests/regress/wrong_rip_arm.py +++ b/tests/regress/wrong_rip_arm.py @@ -1,22 +1,17 @@ -#!/usr/bin/python - import regress - from unicorn import * -from unicorn.x86_const import * from unicorn.arm_const import * - CODE = ( - b'\x48\x31' # adds r1, #0x48 - b'\xff\x57' # ldrsb r7, [r7, r7] - b'\x57\x5e' # ldrsh r7, [r2, r1] - b'\x5a\x48' # ldr r0, [pc, #0x168] - b'\xbf\x2f' # cmp r7, #0xbf - b'\x2f\x62' # str r7, [r5, #0x20] - b'\x69\x6e' # ldr r1, [r5, #0x64] - b'\x2f\x73' # strb r7, [r5, #0xc] - b'\x68\x48' # ldr r0, [pc, #0x1a0] + b'\x48\x31' # adds r1, #0x48 + b'\xff\x57' # ldrsb r7, [r7, r7] + b'\x57\x5e' # ldrsh r7, [r2, r1] + b'\x5a\x48' # ldr r0, [pc, #0x168] + b'\xbf\x2f' # cmp r7, #0xbf + b'\x2f\x62' # str r7, [r5, #0x20] + b'\x69\x6e' # ldr r1, [r5, #0x64] + b'\x2f\x73' # strb r7, [r5, #0xc] + b'\x68\x48' # ldr r0, [pc, #0x1a0] b'\xc1\xef\x08\x57\x54\x5f\x6a\x3b\x58\x0f\x05' # data? ) diff --git a/tests/regress/wrong_sp_arm.py b/tests/regress/wrong_sp_arm.py index 13dbd36c2e..01aa773eaa 100755 --- a/tests/regress/wrong_sp_arm.py +++ b/tests/regress/wrong_sp_arm.py @@ -1,11 +1,10 @@ -#!/usr/bin/python # By Ryan Hileman, issue #16 +import regress from unicorn import * from unicorn.arm_const import * from unicorn.arm64_const import * -import regress class WrongSPArm(regress.RegressTest): @@ -24,5 +23,6 @@ def test_arm(self): uc.reg_write(UC_ARM_REG_SP, 4) self.assertEqual(0x4, uc.reg_read(UC_ARM_REG_SP)) + if __name__ == '__main__': regress.main() diff --git a/tests/regress/x86_64_conditional_jump.py b/tests/regress/x86_64_conditional_jump.py index d02b8eb900..5b35eb2456 100755 --- a/tests/regress/x86_64_conditional_jump.py +++ b/tests/regress/x86_64_conditional_jump.py @@ -1,7 +1,4 @@ -#!/usr/bin/python - import regress - from unicorn import * from unicorn.x86_const import * diff --git a/tests/regress/x86_64_eflags.py b/tests/regress/x86_64_eflags.py index e5072c08b8..55e62aa988 100755 --- a/tests/regress/x86_64_eflags.py +++ b/tests/regress/x86_64_eflags.py @@ -1,7 +1,4 @@ -#!/usr/bin/python - import regress - from unicorn import * from unicorn.x86_const import * @@ -19,7 +16,6 @@ def test_eflags(self): uc.mem_write(0x6000b0, CODE) uc.emu_start(0x6000b0, 0, count=1) - # Here's the original execution trace for this on actual hardware. # # (gdb) x/i $pc diff --git a/tests/regress/x86_64_msr.py b/tests/regress/x86_64_msr.py index dbf82a3b30..673c6340fa 100755 --- a/tests/regress/x86_64_msr.py +++ b/tests/regress/x86_64_msr.py @@ -1,11 +1,7 @@ -#!/usr/bin/env python - import regress - from unicorn import * from unicorn.x86_const import * - CODE_ADDR = 0x40000 CODE_SIZE = 0x1000 @@ -15,16 +11,15 @@ SEGMENT_ADDR = 0x5000 SEGMENT_SIZE = 0x1000 - FSMSR = 0xC0000100 GSMSR = 0xC0000101 def set_msr(uc, msr, value, scratch=SCRATCH_ADDR): - ''' + """ set the given model-specific register (MSR) to the given value. this will clobber some memory at the given scratch address, as it emits some code. - ''' + """ # save clobbered registers orax = uc.reg_read(UC_X86_REG_RAX) ordx = uc.reg_read(UC_X86_REG_RDX) @@ -37,7 +32,7 @@ def set_msr(uc, msr, value, scratch=SCRATCH_ADDR): uc.reg_write(UC_X86_REG_RAX, value & 0xFFFFFFFF) uc.reg_write(UC_X86_REG_RDX, (value >> 32) & 0xFFFFFFFF) uc.reg_write(UC_X86_REG_RCX, msr & 0xFFFFFFFF) - uc.emu_start(scratch, scratch+len(buf), count=1) + uc.emu_start(scratch, scratch + len(buf), count=1) # restore clobbered registers uc.reg_write(UC_X86_REG_RAX, orax) @@ -47,10 +42,10 @@ def set_msr(uc, msr, value, scratch=SCRATCH_ADDR): def get_msr(uc, msr, scratch=SCRATCH_ADDR): - ''' + """ fetch the contents of the given model-specific register (MSR). this will clobber some memory at the given scratch address, as it emits some code. - ''' + """ # save clobbered registers orax = uc.reg_read(UC_X86_REG_RAX) ordx = uc.reg_read(UC_X86_REG_RDX) @@ -61,7 +56,7 @@ def get_msr(uc, msr, scratch=SCRATCH_ADDR): buf = b'\x0f\x32' uc.mem_write(scratch, buf) uc.reg_write(UC_X86_REG_RCX, msr & 0xFFFFFFFF) - uc.emu_start(scratch, scratch+len(buf), count=1) + uc.emu_start(scratch, scratch + len(buf), count=1) eax = uc.reg_read(UC_X86_REG_EAX) edx = uc.reg_read(UC_X86_REG_EDX) @@ -75,32 +70,32 @@ def get_msr(uc, msr, scratch=SCRATCH_ADDR): def set_gs(uc, addr): - ''' + """ set the GS.base hidden descriptor-register field to the given address. this enables referencing the gs segment on x86-64. - ''' + """ return set_msr(uc, GSMSR, addr) def get_gs(uc): - ''' + """ fetch the GS.base hidden descriptor-register field. - ''' + """ return get_msr(uc, GSMSR) def set_fs(uc, addr): - ''' + """ set the FS.base hidden descriptor-register field to the given address. this enables referencing the fs segment on x86-64. - ''' + """ return set_msr(uc, FSMSR, addr) def get_fs(uc): - ''' + """ fetch the FS.base hidden descriptor-register field. - ''' + """ return get_msr(uc, FSMSR) @@ -124,12 +119,12 @@ def test_gs(self): code = b'\x65\x48\x33\x0C\x25\x18\x00\x00\x00' # xor rcx, qword ptr gs:[0x18] uc.mem_write(CODE_ADDR, code) - uc.mem_write(SEGMENT_ADDR+0x18, b'AAAAAAAA') + uc.mem_write(SEGMENT_ADDR + 0x18, b'AAAAAAAA') set_gs(uc, SEGMENT_ADDR) self.assertEqual(SEGMENT_ADDR, get_gs(uc)) - uc.emu_start(CODE_ADDR, CODE_ADDR+len(code)) + uc.emu_start(CODE_ADDR, CODE_ADDR + len(code)) self.assertEqual(uc.reg_read(UC_X86_REG_RCX), 0x4141414141414141) @@ -142,12 +137,12 @@ def test_fs(self): code = b'\x64\x48\x33\x0C\x25\x18\x00\x00\x00' # xor rcx, qword ptr fs:[0x18] uc.mem_write(CODE_ADDR, code) - uc.mem_write(SEGMENT_ADDR+0x18, b'AAAAAAAA') + uc.mem_write(SEGMENT_ADDR + 0x18, b'AAAAAAAA') set_fs(uc, SEGMENT_ADDR) self.assertEqual(SEGMENT_ADDR, get_fs(uc)) - uc.emu_start(CODE_ADDR, CODE_ADDR+len(code)) + uc.emu_start(CODE_ADDR, CODE_ADDR + len(code)) self.assertEqual(uc.reg_read(UC_X86_REG_RCX), 0x4141414141414141) diff --git a/tests/regress/x86_eflags.py b/tests/regress/x86_eflags.py index cf9f99f815..c52d63fd5a 100755 --- a/tests/regress/x86_eflags.py +++ b/tests/regress/x86_eflags.py @@ -1,7 +1,4 @@ -#!/usr/bin/python - import regress - from unicorn import * from unicorn.x86_const import * @@ -21,7 +18,6 @@ def test_eflags(self): uc.mem_write(0x6000b0, CODE) uc.emu_start(0x6000b0, 0, count=1) - # Here's the original execution trace for this on actual hardware. # # (gdb) x/i $eip @@ -39,5 +35,6 @@ def test_eflags(self): self.assertEqual(0x202, uc.reg_read(UC_X86_REG_EFLAGS)) + if __name__ == '__main__': regress.main() diff --git a/tests/regress/x86_fldt_fsqrt.py b/tests/regress/x86_fldt_fsqrt.py index c67deb1c4a..98041129fa 100755 --- a/tests/regress/x86_fldt_fsqrt.py +++ b/tests/regress/x86_fldt_fsqrt.py @@ -1,9 +1,5 @@ -#!/usr/bin/env python - import regress - from unicorn import * -from unicorn.x86_const import * CODE = ( b'\xb8\x00\x00\x00\x02' # mov eax, 0x2000000 diff --git a/tests/regress/x86_gdt.py b/tests/regress/x86_gdt.py index e85837d0fe..ad23f69848 100755 --- a/tests/regress/x86_gdt.py +++ b/tests/regress/x86_gdt.py @@ -1,12 +1,8 @@ -#!/usr/bin/env python - import regress - from unicorn import * from unicorn.x86_const import * from struct import pack - F_GRANULARITY = 0x8 F_PROT_32 = 0x4 F_LONG = 0x2 @@ -36,7 +32,8 @@ S_PRIV_1 = 0x1 S_PRIV_0 = 0x0 -CODE = b'\x65\x33\x0d\x18\x00\x00\x00' # xor ecx, dword ptr gs:[0x18] +CODE = b'\x65\x33\x0d\x18\x00\x00\x00' # xor ecx, dword ptr gs:[0x18] + def create_selector(idx, flags): to_ret = flags @@ -47,16 +44,16 @@ def create_selector(idx, flags): def create_gdt_entry(base, limit, access, flags): return pack('> 16) & 0xf) << 48 - | (flags & 0xff) << 52 - | ((base >> 24) & 0xff) << 56 + limit & 0xffff + | (base & 0xffffff) << 16 + | (access & 0xff) << 40 + | ((limit >> 16) & 0xf) << 48 + | (flags & 0xff) << 52 + | ((base >> 24) & 0xff) << 56 )) -def hook_mem_read(uc, type, addr,*args): +def hook_mem_read(uc, type, addr, *args): regress.logger.debug("%#x", addr) return False @@ -72,6 +69,7 @@ def hook_mem_read(uc, type, addr,*args): SEGMENT_ADDR = 0x5000 SEGMENT_SIZE = 0x1000 + class GdtRead(regress.RegressTest): def test_gdt(self): @@ -85,7 +83,8 @@ def test_gdt(self): uc.mem_write(CODE_ADDR, CODE) uc.mem_write(SEGMENT_ADDR + 0x18, b'AAAA') - gdt_entry = create_gdt_entry(SEGMENT_ADDR, SEGMENT_SIZE, A_PRESENT | A_DATA | A_DATA_WRITABLE | A_PRIV_3 | A_DIR_CON_BIT, F_PROT_32) + gdt_entry = create_gdt_entry(SEGMENT_ADDR, SEGMENT_SIZE, + A_PRESENT | A_DATA | A_DATA_WRITABLE | A_PRIV_3 | A_DIR_CON_BIT, F_PROT_32) uc.mem_write(GDT_ADDR + 8, gdt_entry) uc.reg_write(UC_X86_REG_GDTR, (0, GDT_ADDR, GDT_LIMIT, 0x0)) @@ -93,7 +92,7 @@ def test_gdt(self): selector = create_selector(1, S_GDT | S_PRIV_3) uc.reg_write(UC_X86_REG_GS, selector) - uc.emu_start(CODE_ADDR, CODE_ADDR+len(CODE)) + uc.emu_start(CODE_ADDR, CODE_ADDR + len(CODE)) self.assertEqual(uc.reg_read(UC_X86_REG_ECX), 0x41414141) diff --git a/tests/regress/x86_ld_crash.py b/tests/regress/x86_ld_crash.py index fb68843bc1..752c7f0c7b 100755 --- a/tests/regress/x86_ld_crash.py +++ b/tests/regress/x86_ld_crash.py @@ -1,13 +1,10 @@ - import regress - from unicorn import * from unicorn.x86_const import * - CODE = ( - b'\x8b\x83\xd4\x05\x00\x00' # mov eax, DWORD PTR [ebx+0x5d4] - b'\x8b\x93\x80\x05\x00\x00' # mov edx, DWORD PTR [ebx+0x580] + b'\x8b\x83\xd4\x05\x00\x00' # mov eax, DWORD PTR [ebx+0x5d4] + b'\x8b\x93\x80\x05\x00\x00' # mov edx, DWORD PTR [ebx+0x580] ) BASE = 0x47bb000 diff --git a/tests/regress/x86_self_modifying.py b/tests/regress/x86_self_modifying.py index b8930db7e7..7275b30e58 100755 --- a/tests/regress/x86_self_modifying.py +++ b/tests/regress/x86_self_modifying.py @@ -1,8 +1,5 @@ -#!/usr/bin/env python - import os import regress - from unicorn import * from unicorn.x86_const import * @@ -12,12 +9,14 @@ CODE_ADDR = 0x08048000 STACK_ADDR = 0x2000000 -CODE = open(filename, 'rb').read() +with open(filename, 'rb') as f: + CODE = f.read() CODE_SIZE = len(CODE) + (0x1000 - len(CODE) % 0x1000) STACK_SIZE = 0x8000 ENTRY_POINT = 0x8048074 + def hook_intr(uc, intno, data): uc.emu_stop() diff --git a/tests/regress/x86_set_ip.py b/tests/regress/x86_set_ip.py index a23da516dc..803bcb36f6 100644 --- a/tests/regress/x86_set_ip.py +++ b/tests/regress/x86_set_ip.py @@ -1,10 +1,7 @@ - import regress - from unicorn import * from unicorn.x86_const import * - NOPSLED = b"\x90" * 5 From 4f417c3f11fb8d6e604f63a58f5a060b5e78405a Mon Sep 17 00:00:00 2001 From: Michael-c0de <82742216+Michael-c0de@users.noreply.github.com> Date: Sun, 29 Dec 2024 23:24:32 +0800 Subject: [PATCH 52/72] patch multiple UC_HOOK_MEM callbacks for unaligned access (#2063) * patch multiple UC_HOOK_MEM callbacks for unaligned access * update test_x86.c for #2063 * update test_x86.c for build on win --------- Co-authored-by: yaojiale2024@iscas.ac.cn Co-authored-by: lazymio --- qemu/accel/tcg/cputlb.c | 3 ++- tests/unit/test_x86.c | 44 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/qemu/accel/tcg/cputlb.c b/qemu/accel/tcg/cputlb.c index 5206f13231..1d9e05aebf 100644 --- a/qemu/accel/tcg/cputlb.c +++ b/qemu/accel/tcg/cputlb.c @@ -1593,7 +1593,8 @@ load_helper(CPUArchState *env, target_ulong addr, TCGMemOpIdx oi, } // now it is read on mapped memory - if (!code_read) { + // patch issue #1041 multiple UC_HOOK_MEM callbacks for unaligned access + if (!code_read && !uc->size_recur_mem) { // this is date reading HOOK_FOREACH(uc, hook, UC_HOOK_MEM_READ) { if (hook->to_delete) diff --git a/tests/unit/test_x86.c b/tests/unit/test_x86.c index 079e986321..92b08f7dfe 100644 --- a/tests/unit/test_x86.c +++ b/tests/unit/test_x86.c @@ -1342,6 +1342,48 @@ static void test_x86_unaligned_access(void) OK(uc_close(uc)); } + +static void test_x86_64_unaligned_access(void){ + uc_engine *uc; + uc_hook hook; + char code[] = { + "\x48\x89\x01" // mov qword ptr [rcx],rax + "\x48\x8b\x00" // mov rax,qword ptr [rax] + "\xcc" + }; + uint64_t r_rax = LEINT64(0x2fffff); + uint64_t r_rcx = LEINT64(0x2fffff); + struct writelog_t write_log[10]; + struct writelog_t read_log[10]; + memset(write_log, 0, sizeof(write_log)); + memset(read_log, 0, sizeof(read_log)); + uc_common_setup(&uc, UC_ARCH_X86, UC_MODE_64, code, sizeof(code) - 1); + OK(uc_mem_map(uc, 0x200000, 0x200000, UC_PROT_ALL)); + OK(uc_hook_add(uc, &hook, UC_HOOK_MEM_WRITE, + test_x86_unaligned_access_callback, write_log, 1, 0)); + OK(uc_hook_add(uc, &hook, UC_HOOK_MEM_READ, + test_x86_unaligned_access_callback, read_log, 1, 0)); + + OK(uc_reg_write(uc, UC_X86_REG_RAX, &r_rax)); + OK(uc_reg_write(uc, UC_X86_REG_RCX, &r_rcx)); + + OK(uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 2)); + + TEST_CHECK(write_log[0].addr == 0x2fffff); + TEST_CHECK(write_log[0].size == 8); + TEST_CHECK(write_log[1].size == 0); + + TEST_CHECK(read_log[0].addr == 0x2fffff); + TEST_CHECK(read_log[0].size == 8); + TEST_CHECK(read_log[1].size == 0); + + uint64_t b; + OK(uc_mem_read(uc, 0x2fffff, &b, 8)); + TEST_CHECK(b == 0x2fffff); + + OK(uc_close(uc)); + +} #endif static bool test_x86_lazy_mapping_mem_callback(uc_engine *uc, uc_mem_type type, @@ -2019,6 +2061,8 @@ TEST_LIST = { {"test_x86_invalid_vex_l", test_x86_invalid_vex_l}, #if !defined(TARGET_READ_INLINED) && defined(BOOST_LITTLE_ENDIAN) {"test_x86_unaligned_access", test_x86_unaligned_access}, + {"test_x86_64_unaligned_access", test_x86_64_unaligned_access}, + #endif {"test_x86_lazy_mapping", test_x86_lazy_mapping}, {"test_x86_16_incorrect_ip", test_x86_16_incorrect_ip}, From 77d64c05c2f84b75ae9c6186b444214d43c5e618 Mon Sep 17 00:00:00 2001 From: lazymio Date: Mon, 30 Dec 2024 21:31:05 +0800 Subject: [PATCH 53/72] Add a link to FAQ --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 9dabbffc52..293d05db01 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,7 @@ See [docs/COMPILE.md](docs/COMPILE.md) file for how to compile and install Unico More documentation is available in [docs/README.md](docs/README.md). +For common questions, read [docs/FAQ.md](docs/FAQ.md) before raising an issue. Contact ------- From 9e35265a97e9fabe3aa1e186176cf89397b73bbf Mon Sep 17 00:00:00 2001 From: mio Date: Sat, 4 Jan 2025 15:41:05 +0800 Subject: [PATCH 54/72] Update docs for testing --- docs/Testing.md | 13 +++++++++++++ tests/README.md | 3 +++ 2 files changed, 16 insertions(+) create mode 100644 docs/Testing.md create mode 100644 tests/README.md diff --git a/docs/Testing.md b/docs/Testing.md new file mode 100644 index 0000000000..c6eb3d42b7 --- /dev/null +++ b/docs/Testing.md @@ -0,0 +1,13 @@ +# Testing in Unicorn + +Unicorn focus on the testing to reduce bugs and ensure the expected behaviors. The `tests/` directory contains a few subdirectories for testing purposes. + +- `unit/`: C unit tests since Unicorn 2. +- `regress/`: The regression tests written in Python and C, imported from Unicorn 1. +- `rust-tests/`: The tests written in rust. +- `fuzz/`: The fuzz drivers for OSS-Fuzz. +- `benchmarks/`: The benchmark suite imported from Unicorn 1. + +## Contribution Guide + +Generally, it is ideal to add new tests whenever a PR is made. `unit/` should be the first place for the new tests to go. \ No newline at end of file diff --git a/tests/README.md b/tests/README.md new file mode 100644 index 0000000000..8956a4f6f6 --- /dev/null +++ b/tests/README.md @@ -0,0 +1,3 @@ +# Testing + +See [this](../docs/Testing.md) \ No newline at end of file From d50bbb5d5bb92fae8000a380ed7e11aabb9b6965 Mon Sep 17 00:00:00 2001 From: mio Date: Sat, 4 Jan 2025 15:48:32 +0800 Subject: [PATCH 55/72] Add unit test from #2078 --- tests/unit/test_arm64.c | 54 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/tests/unit/test_arm64.c b/tests/unit/test_arm64.c index e016dc8283..e48bbda9c0 100644 --- a/tests/unit/test_arm64.c +++ b/tests/unit/test_arm64.c @@ -529,6 +529,59 @@ static void test_arm64_pc_wrap(void) OK(uc_close(uc)); } +static void +test_arm64_mem_prot_regress_hook_mem(uc_engine *uc, uc_mem_type type, + uint64_t address, int size, int64_t value, void *user_data) +{ + // fprintf(stderr, "%s %p %d\n", (type == UC_MEM_WRITE) ? "UC_MEM_WRITE" : "UC_MEM_READ", (void *)address, size); +} + +static bool +test_arm64_mem_prot_regress_hook_prot(uc_engine *uc, uc_mem_type type, + uint64_t address, int size, int64_t value, void *user_data) +{ + // fprintf(stderr, "%s %p %d\n", (type == UC_MEM_WRITE_PROT) ? "UC_MEM_WRITE_PROT" : ((type == UC_MEM_FETCH_PROT) ? "UC_MEM_FETCH_PROT" : "UC_MEM_READ_PROT"), (void *)address, size); + return false; +} + +static bool +test_arm64_mem_prot_regress_hook_unm(uc_engine *uc, uc_mem_type type, + uint64_t address, int size, int64_t value, void *user_data) +{ + // fprintf(stderr, "%s %p %d\n", (type == UC_MEM_WRITE_UNMAPPED) ? "UC_MEM_WRITE_UNMAPPED" : ((type == UC_MEM_FETCH_UNMAPPED) ? "UC_MEM_FETCH_UNMAPPED" : "UC_MEM_READ_UNMAPPED"), (void *)address, size); + return false; +} + +// https://github.com/unicorn-engine/unicorn/issues/2078 +static void test_arm64_mem_prot_regress(void) +{ + const uint8_t code[] = { + 0x08, 0x40, 0x5e, 0x78, // ldurh w8, [x0, #-0x1c] + }; + + uc_engine *uc; + OK(uc_open(UC_ARCH_ARM64, UC_MODE_ARM, &uc)); + + OK(uc_mem_map(uc, 0, 0x4000, UC_PROT_READ|UC_PROT_EXEC)); + OK(uc_mem_map(uc, 0x4000, 0xC000, UC_PROT_READ|UC_PROT_WRITE)); + OK(uc_mem_write(uc, 0, code, sizeof(code))); + uc_hook hh_mem; + OK(uc_hook_add(uc, &hh_mem, UC_HOOK_MEM_READ | UC_HOOK_MEM_WRITE, test_arm64_mem_prot_regress_hook_mem, NULL, 1, 0)); + + uc_hook hh_prot; + OK(uc_hook_add(uc, &hh_prot, UC_HOOK_MEM_READ_PROT | UC_HOOK_MEM_WRITE_PROT | UC_HOOK_MEM_FETCH_PROT, test_arm64_mem_prot_regress_hook_prot, NULL, 1, 0)); + + uc_hook hh_unm; + OK(uc_hook_add(uc, &hh_unm, UC_HOOK_MEM_READ_UNMAPPED | UC_HOOK_MEM_WRITE_UNMAPPED | UC_HOOK_MEM_FETCH_UNMAPPED, test_arm64_mem_prot_regress_hook_unm, NULL, 1, 0)); + + const uint64_t value = 0x801b; + OK(uc_reg_write(uc, UC_ARM64_REG_X0, &value)); + + OK(uc_emu_start(uc, 0, sizeof(code), 0, 0)); + + OK(uc_close(uc)); +} + TEST_LIST = {{"test_arm64_until", test_arm64_until}, {"test_arm64_code_patching", test_arm64_code_patching}, {"test_arm64_code_patching_count", test_arm64_code_patching_count}, @@ -544,4 +597,5 @@ TEST_LIST = {{"test_arm64_until", test_arm64_until}, test_arm64_block_invalid_mem_read_write_sync}, {"test_arm64_mmu", test_arm64_mmu}, {"test_arm64_pc_wrap", test_arm64_pc_wrap}, + {"test_arm64_mem_prot_regress", test_arm64_mem_prot_regress}, {NULL, NULL}}; From fcca82bb287025b89e31e1f15e817959f9e76559 Mon Sep 17 00:00:00 2001 From: mio Date: Sat, 4 Jan 2025 15:48:54 +0800 Subject: [PATCH 56/72] Format code --- include/uc_priv.h | 19 +++++++------- include/unicorn/unicorn.h | 2 +- qemu/target/i386/unicorn.c | 3 ++- tests/unit/test_arm64.c | 51 +++++++++++++++++++++++++------------- tests/unit/test_x86.c | 44 ++++++++++++++++---------------- uc.c | 9 ++++--- 6 files changed, 75 insertions(+), 53 deletions(-) diff --git a/include/uc_priv.h b/include/uc_priv.h index f9195b4d63..07b5c244cf 100644 --- a/include/uc_priv.h +++ b/include/uc_priv.h @@ -108,7 +108,8 @@ typedef MemoryRegion *(*uc_memory_mapping_t)(struct uc_struct *, hwaddr addr); typedef void (*uc_memory_filter_t)(MemoryRegion *, int32_t); -typedef bool (*uc_flatview_copy_t)(struct uc_struct *, FlatView *, FlatView *, bool); +typedef bool (*uc_flatview_copy_t)(struct uc_struct *, FlatView *, FlatView *, + bool); typedef void (*uc_readonly_mem_t)(MemoryRegion *mr, bool readonly); @@ -426,14 +427,14 @@ struct uc_struct { // Metadata stub for the variable-size cpu context used with uc_context_*() struct uc_context { - size_t context_size; // size of the real internal context structure - uc_mode mode; // the mode of this context - uc_arch arch; // the arch of this context - int snapshot_level; // the memory snapshot level to restore - bool ramblock_freed; // wheter there was a some ramblock freed - RAMBlock *last_block;// The last element of the ramblock list - FlatView *fv; // The current flatview of the memory - char data[0]; // context + size_t context_size; // size of the real internal context structure + uc_mode mode; // the mode of this context + uc_arch arch; // the arch of this context + int snapshot_level; // the memory snapshot level to restore + bool ramblock_freed; // wheter there was a some ramblock freed + RAMBlock *last_block; // The last element of the ramblock list + FlatView *fv; // The current flatview of the memory + char data[0]; // context }; // We have to support 32bit system so we can't hold uint64_t on void* diff --git a/include/unicorn/unicorn.h b/include/unicorn/unicorn.h index c057dcc964..1ee78f3aa1 100644 --- a/include/unicorn/unicorn.h +++ b/include/unicorn/unicorn.h @@ -65,7 +65,7 @@ typedef size_t uc_hook; #define UNICORN_DEPRECATED __declspec(deprecated) #else #pragma message( \ - "WARNING: You need to implement UNICORN_DEPRECATED for this compiler") + "WARNING: You need to implement UNICORN_DEPRECATED for this compiler") #define UNICORN_DEPRECATED #endif diff --git a/qemu/target/i386/unicorn.c b/qemu/target/i386/unicorn.c index a694af467c..b36eb8fd0c 100644 --- a/qemu/target/i386/unicorn.c +++ b/qemu/target/i386/unicorn.c @@ -2000,7 +2000,8 @@ static bool x86_stop_interrupt(struct uc_struct *uc, int intno) static bool x86_insn_hook_validate(uint32_t insn_enum) { - // for x86 we can only hook IN, OUT, SYSCALL, SYSENTER, CPUID, RDTSC, and RDTSCP + // for x86 we can only hook IN, OUT, SYSCALL, SYSENTER, CPUID, RDTSC, and + // RDTSCP if (insn_enum != UC_X86_INS_IN && insn_enum != UC_X86_INS_OUT && insn_enum != UC_X86_INS_SYSCALL && insn_enum != UC_X86_INS_SYSENTER && insn_enum != UC_X86_INS_CPUID && insn_enum != UC_X86_INS_RDTSC && diff --git a/tests/unit/test_arm64.c b/tests/unit/test_arm64.c index e48bbda9c0..2de61d99eb 100644 --- a/tests/unit/test_arm64.c +++ b/tests/unit/test_arm64.c @@ -529,26 +529,36 @@ static void test_arm64_pc_wrap(void) OK(uc_close(uc)); } -static void -test_arm64_mem_prot_regress_hook_mem(uc_engine *uc, uc_mem_type type, - uint64_t address, int size, int64_t value, void *user_data) +static void test_arm64_mem_prot_regress_hook_mem(uc_engine *uc, + uc_mem_type type, + uint64_t address, int size, + int64_t value, void *user_data) { - // fprintf(stderr, "%s %p %d\n", (type == UC_MEM_WRITE) ? "UC_MEM_WRITE" : "UC_MEM_READ", (void *)address, size); + // fprintf(stderr, "%s %p %d\n", (type == UC_MEM_WRITE) ? "UC_MEM_WRITE" : + // "UC_MEM_READ", (void *)address, size); } -static bool -test_arm64_mem_prot_regress_hook_prot(uc_engine *uc, uc_mem_type type, - uint64_t address, int size, int64_t value, void *user_data) +static bool test_arm64_mem_prot_regress_hook_prot(uc_engine *uc, + uc_mem_type type, + uint64_t address, int size, + int64_t value, + void *user_data) { - // fprintf(stderr, "%s %p %d\n", (type == UC_MEM_WRITE_PROT) ? "UC_MEM_WRITE_PROT" : ((type == UC_MEM_FETCH_PROT) ? "UC_MEM_FETCH_PROT" : "UC_MEM_READ_PROT"), (void *)address, size); + // fprintf(stderr, "%s %p %d\n", (type == UC_MEM_WRITE_PROT) ? + // "UC_MEM_WRITE_PROT" : ((type == UC_MEM_FETCH_PROT) ? "UC_MEM_FETCH_PROT" + // : "UC_MEM_READ_PROT"), (void *)address, size); return false; } -static bool -test_arm64_mem_prot_regress_hook_unm(uc_engine *uc, uc_mem_type type, - uint64_t address, int size, int64_t value, void *user_data) +static bool test_arm64_mem_prot_regress_hook_unm(uc_engine *uc, + uc_mem_type type, + uint64_t address, int size, + int64_t value, void *user_data) { - // fprintf(stderr, "%s %p %d\n", (type == UC_MEM_WRITE_UNMAPPED) ? "UC_MEM_WRITE_UNMAPPED" : ((type == UC_MEM_FETCH_UNMAPPED) ? "UC_MEM_FETCH_UNMAPPED" : "UC_MEM_READ_UNMAPPED"), (void *)address, size); + // fprintf(stderr, "%s %p %d\n", (type == UC_MEM_WRITE_UNMAPPED) ? + // "UC_MEM_WRITE_UNMAPPED" : ((type == UC_MEM_FETCH_UNMAPPED) ? + // "UC_MEM_FETCH_UNMAPPED" : "UC_MEM_READ_UNMAPPED"), (void *)address, + // size); return false; } @@ -562,17 +572,24 @@ static void test_arm64_mem_prot_regress(void) uc_engine *uc; OK(uc_open(UC_ARCH_ARM64, UC_MODE_ARM, &uc)); - OK(uc_mem_map(uc, 0, 0x4000, UC_PROT_READ|UC_PROT_EXEC)); - OK(uc_mem_map(uc, 0x4000, 0xC000, UC_PROT_READ|UC_PROT_WRITE)); + OK(uc_mem_map(uc, 0, 0x4000, UC_PROT_READ | UC_PROT_EXEC)); + OK(uc_mem_map(uc, 0x4000, 0xC000, UC_PROT_READ | UC_PROT_WRITE)); OK(uc_mem_write(uc, 0, code, sizeof(code))); uc_hook hh_mem; - OK(uc_hook_add(uc, &hh_mem, UC_HOOK_MEM_READ | UC_HOOK_MEM_WRITE, test_arm64_mem_prot_regress_hook_mem, NULL, 1, 0)); + OK(uc_hook_add(uc, &hh_mem, UC_HOOK_MEM_READ | UC_HOOK_MEM_WRITE, + test_arm64_mem_prot_regress_hook_mem, NULL, 1, 0)); uc_hook hh_prot; - OK(uc_hook_add(uc, &hh_prot, UC_HOOK_MEM_READ_PROT | UC_HOOK_MEM_WRITE_PROT | UC_HOOK_MEM_FETCH_PROT, test_arm64_mem_prot_regress_hook_prot, NULL, 1, 0)); + OK(uc_hook_add(uc, &hh_prot, + UC_HOOK_MEM_READ_PROT | UC_HOOK_MEM_WRITE_PROT | + UC_HOOK_MEM_FETCH_PROT, + test_arm64_mem_prot_regress_hook_prot, NULL, 1, 0)); uc_hook hh_unm; - OK(uc_hook_add(uc, &hh_unm, UC_HOOK_MEM_READ_UNMAPPED | UC_HOOK_MEM_WRITE_UNMAPPED | UC_HOOK_MEM_FETCH_UNMAPPED, test_arm64_mem_prot_regress_hook_unm, NULL, 1, 0)); + OK(uc_hook_add(uc, &hh_unm, + UC_HOOK_MEM_READ_UNMAPPED | UC_HOOK_MEM_WRITE_UNMAPPED | + UC_HOOK_MEM_FETCH_UNMAPPED, + test_arm64_mem_prot_regress_hook_unm, NULL, 1, 0)); const uint64_t value = 0x801b; OK(uc_reg_write(uc, UC_ARM64_REG_X0, &value)); diff --git a/tests/unit/test_x86.c b/tests/unit/test_x86.c index 92b08f7dfe..df36332d0d 100644 --- a/tests/unit/test_x86.c +++ b/tests/unit/test_x86.c @@ -632,10 +632,11 @@ static void test_x86_smc_add(void) /* * mov qword ptr [rip+0x10], rax * mov word ptr [rip], 0x0548 - * [orig] mov eax, dword ptr [rax + 0x12345678]; [after SMC] 480578563412 add rax, 0x12345678 - * hlt + * [orig] mov eax, dword ptr [rax + 0x12345678]; [after SMC] 480578563412 + * add rax, 0x12345678 hlt */ - char code[] = "\x48\x89\x05\x10\x00\x00\x00\x66\xc7\x05\x00\x00\x00\x00\x48\x05\x8b\x80\x78\x56\x34\x12\xf4"; + char code[] = "\x48\x89\x05\x10\x00\x00\x00\x66\xc7\x05\x00\x00\x00\x00\x48" + "\x05\x8b\x80\x78\x56\x34\x12\xf4"; uc_common_setup(&uc, UC_ARCH_X86, UC_MODE_64, code, sizeof(code) - 1); OK(uc_mem_map(uc, stack_base, 0x2000, UC_PROT_ALL)); @@ -648,10 +649,11 @@ static void test_x86_smc_mem_hook_callback(uc_engine *uc, uc_mem_type t, uint64_t addr, int size, uint64_t value, void *user_data) { - uint64_t write_addresses[] = { 0x1030, 0x1010, 0x1010, 0x1018, 0x1018, 0x1029, 0x1029 }; + uint64_t write_addresses[] = {0x1030, 0x1010, 0x1010, 0x1018, + 0x1018, 0x1029, 0x1029}; unsigned int *i = user_data; - TEST_CHECK(*i < (sizeof(write_addresses)/sizeof(write_addresses[0]))); + TEST_CHECK(*i < (sizeof(write_addresses) / sizeof(write_addresses[0]))); TEST_CHECK(write_addresses[*i] == addr); (*i)++; } @@ -666,19 +668,19 @@ static void test_x86_smc_mem_hook(void) /* * mov qword ptr [rip+0x29], rax * mov word ptr [rip], 0x0548 - * [orig] mov eax, dword ptr [rax + 0x12345678]; [after SMC] 480578563412 add rax, 0x12345678 - * nop - * nop - * nop - * mov qword ptr [rip-0x08], rax - * mov word ptr [rip], 0x0548 - * [orig] mov eax, dword ptr [rax + 0x12345678]; [after SMC] 480578563412 add rax, 0x12345678 - * hlt + * [orig] mov eax, dword ptr [rax + 0x12345678]; [after SMC] 480578563412 + * add rax, 0x12345678 nop nop nop mov qword ptr [rip-0x08], rax mov word + * ptr [rip], 0x0548 [orig] mov eax, dword ptr [rax + 0x12345678]; [after + * SMC] 480578563412 add rax, 0x12345678 hlt */ - char code[] = "\x48\x89\x05\x29\x00\x00\x00\x66\xC7\x05\x00\x00\x00\x00\x48\x05\x8B\x80\x78\x56\x34\x12\x90\x90\x90\x48\x89\x05\xF8\xFF\xFF\xFF\x66\xC7\x05\x00\x00\x00\x00\x48\x05\x8B\x80\x78\x56\x34\x12\xF4"; + char code[] = + "\x48\x89\x05\x29\x00\x00\x00\x66\xC7\x05\x00\x00\x00\x00\x48\x05\x8B" + "\x80\x78\x56\x34\x12\x90\x90\x90\x48\x89\x05\xF8\xFF\xFF\xFF\x66\xC7" + "\x05\x00\x00\x00\x00\x48\x05\x8B\x80\x78\x56\x34\x12\xF4"; uc_common_setup(&uc, UC_ARCH_X86, UC_MODE_64, code, sizeof(code) - 1); - OK(uc_hook_add(uc, &hook, UC_HOOK_MEM_WRITE, test_x86_smc_mem_hook_callback, &i, 1, 0)); + OK(uc_hook_add(uc, &hook, UC_HOOK_MEM_WRITE, test_x86_smc_mem_hook_callback, + &i, 1, 0)); OK(uc_mem_map(uc, stack_base, 0x2000, UC_PROT_ALL)); r_rsp = stack_base + 0x1800; OK(uc_reg_write(uc, UC_X86_REG_RSP, &r_rsp)); @@ -1343,14 +1345,13 @@ static void test_x86_unaligned_access(void) OK(uc_close(uc)); } -static void test_x86_64_unaligned_access(void){ +static void test_x86_64_unaligned_access(void) +{ uc_engine *uc; uc_hook hook; - char code[] = { - "\x48\x89\x01" // mov qword ptr [rcx],rax - "\x48\x8b\x00" // mov rax,qword ptr [rax] - "\xcc" - }; + char code[] = {"\x48\x89\x01" // mov qword ptr [rcx],rax + "\x48\x8b\x00" // mov rax,qword ptr [rax] + "\xcc"}; uint64_t r_rax = LEINT64(0x2fffff); uint64_t r_rcx = LEINT64(0x2fffff); struct writelog_t write_log[10]; @@ -1382,7 +1383,6 @@ static void test_x86_64_unaligned_access(void){ TEST_CHECK(b == 0x2fffff); OK(uc_close(uc)); - } #endif diff --git a/uc.c b/uc.c index 895bd4fa4a..623e898e76 100644 --- a/uc.c +++ b/uc.c @@ -35,7 +35,8 @@ static void clear_deleted_hooks(uc_engine *uc); static uc_err uc_snapshot(uc_engine *uc); static uc_err uc_restore_latest_snapshot(uc_engine *uc); -#if defined(__APPLE__) && defined(HAVE_PTHREAD_JIT_PROTECT) && (defined(__arm__) || defined(__aarch64__)) +#if defined(__APPLE__) && defined(HAVE_PTHREAD_JIT_PROTECT) && \ + (defined(__arm__) || defined(__aarch64__)) static void save_jit_state(uc_engine *uc) { if (!uc->nested) { @@ -2148,7 +2149,8 @@ uc_err uc_context_save(uc_engine *uc, uc_context *context) if (!context->fv) { return UC_ERR_NOMEM; } - if (!uc->flatview_copy(uc, context->fv, uc->address_space_memory.current_map, false)) { + if (!uc->flatview_copy(uc, context->fv, + uc->address_space_memory.current_map, false)) { restore_jit_state(uc); return UC_ERR_NOMEM; } @@ -2434,7 +2436,8 @@ uc_err uc_context_restore(uc_engine *uc, uc_context *context) uc_snapshot(uc); uc->ram_list.freed = context->ramblock_freed; uc->ram_list.last_block = context->last_block; - if (!uc->flatview_copy(uc, uc->address_space_memory.current_map, context->fv, true)) { + if (!uc->flatview_copy(uc, uc->address_space_memory.current_map, + context->fv, true)) { return UC_ERR_NOMEM; } uc->tcg_flush_tlb(uc); From 6974b535884ab09369101dd451316479679f6012 Mon Sep 17 00:00:00 2001 From: mio Date: Sat, 4 Jan 2025 15:57:02 +0800 Subject: [PATCH 57/72] Fix #2078 We shall only go through the else branch for code_read --- qemu/accel/tcg/cputlb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qemu/accel/tcg/cputlb.c b/qemu/accel/tcg/cputlb.c index 1d9e05aebf..30e47e2447 100644 --- a/qemu/accel/tcg/cputlb.c +++ b/qemu/accel/tcg/cputlb.c @@ -1665,7 +1665,7 @@ load_helper(CPUArchState *env, target_ulong addr, TCGMemOpIdx oi, return 0; } } - } else { + } else if (code_read) { // code fetching // Unicorn: callback on fetch from NX if (mr != NULL && !(mr->perms & UC_PROT_EXEC)) { // non-executable From e97d084aece09e0da3e1061f7301c1ba940caf21 Mon Sep 17 00:00:00 2001 From: EvianZhang Date: Sat, 4 Jan 2025 15:59:20 +0800 Subject: [PATCH 58/72] Add more EIP syncs for x86 target memory hooks (#2064) Co-authored-by: lazymio --- qemu/target/i386/translate.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/qemu/target/i386/translate.c b/qemu/target/i386/translate.c index 8ba7909930..be63255591 100644 --- a/qemu/target/i386/translate.c +++ b/qemu/target/i386/translate.c @@ -2914,6 +2914,9 @@ static inline void gen_ldq_env_A0(DisasContext *s, int offset) { TCGContext *tcg_ctx = s->uc->tcg_ctx; + if (HOOK_EXISTS(s->uc, UC_HOOK_MEM_READ)) + gen_sync_pc(tcg_ctx, s->prev_pc); // Unicorn: sync EIP + tcg_gen_qemu_ld_i64(tcg_ctx, s->tmp1_i64, s->A0, s->mem_index, MO_LEQ); tcg_gen_st_i64(tcg_ctx, s->tmp1_i64, tcg_ctx->cpu_env, offset); } @@ -2922,6 +2925,9 @@ static inline void gen_stq_env_A0(DisasContext *s, int offset) { TCGContext *tcg_ctx = s->uc->tcg_ctx; + if (HOOK_EXISTS(s->uc, UC_HOOK_MEM_WRITE)) + gen_sync_pc(tcg_ctx, s->prev_pc); // Unicorn: sync EIP + tcg_gen_ld_i64(tcg_ctx, s->tmp1_i64, tcg_ctx->cpu_env, offset); tcg_gen_qemu_st_i64(tcg_ctx, s->tmp1_i64, s->A0, s->mem_index, MO_LEQ); } @@ -2930,6 +2936,10 @@ static inline void gen_ldo_env_A0(DisasContext *s, int offset) { TCGContext *tcg_ctx = s->uc->tcg_ctx; int mem_index = s->mem_index; + + if (HOOK_EXISTS(s->uc, UC_HOOK_MEM_READ)) + gen_sync_pc(tcg_ctx, s->prev_pc); // Unicorn: sync EIP + tcg_gen_qemu_ld_i64(tcg_ctx, s->tmp1_i64, s->A0, mem_index, MO_LEQ); tcg_gen_st_i64(tcg_ctx, s->tmp1_i64, tcg_ctx->cpu_env, offset + offsetof(ZMMReg, ZMM_Q(0))); tcg_gen_addi_tl(tcg_ctx, s->tmp0, s->A0, 8); @@ -2941,6 +2951,10 @@ static inline void gen_sto_env_A0(DisasContext *s, int offset) { TCGContext *tcg_ctx = s->uc->tcg_ctx; int mem_index = s->mem_index; + + if (HOOK_EXISTS(s->uc, UC_HOOK_MEM_WRITE)) + gen_sync_pc(tcg_ctx, s->prev_pc); // Unicorn: sync EIP + tcg_gen_ld_i64(tcg_ctx, s->tmp1_i64, tcg_ctx->cpu_env, offset + offsetof(ZMMReg, ZMM_Q(0))); tcg_gen_qemu_st_i64(tcg_ctx, s->tmp1_i64, s->A0, mem_index, MO_LEQ); tcg_gen_addi_tl(tcg_ctx, s->tmp0, s->A0, 8); From c4567ff69ec5ee0511866e043ce0d3be7e63f581 Mon Sep 17 00:00:00 2001 From: mio Date: Sat, 4 Jan 2025 16:12:50 +0800 Subject: [PATCH 59/72] Update docs about macOS compiling --- docs/COMPILE.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/COMPILE.md b/docs/COMPILE.md index ad16bcdf62..e55a7f829a 100644 --- a/docs/COMPILE.md +++ b/docs/COMPILE.md @@ -20,6 +20,8 @@ macOS: brew install cmake pkg-config ``` +Note you need to install these build dependencies for python bindings on macOS too. + - Build with the following commands. ```bash From 2c688bae734b1e63b2e2fd5d715c1070af722651 Mon Sep 17 00:00:00 2001 From: mio Date: Sat, 4 Jan 2025 17:05:34 +0800 Subject: [PATCH 60/72] Remove the outdated hack to zero all code gen buffer This shall also improve performance Fix #2001 --- qemu/tcg/tcg.c | 3 ++- qemu/unicorn_common.h | 6 ++++-- tests/unit/test_x86.c | 12 ++++++++++++ 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/qemu/tcg/tcg.c b/qemu/tcg/tcg.c index a91f2de6f1..65703a3a77 100644 --- a/qemu/tcg/tcg.c +++ b/qemu/tcg/tcg.c @@ -407,7 +407,7 @@ static void tcg_region_assign(TCGContext *s, size_t curr_region) s->code_gen_ptr = start; s->code_gen_buffer_size = (char *)end - (char *)start; - memset(s->code_gen_buffer, 0x00, s->code_gen_buffer_size); + // memset(s->code_gen_buffer, 0x00, s->code_gen_buffer_size); // Outdated Unicorn hacks s->code_gen_highwater = (char *)end - TCG_HIGHWATER; } @@ -812,6 +812,7 @@ TranslationBlock *tcg_tb_alloc(TCGContext *s) } s->code_gen_ptr = next; s->data_gen_ptr = NULL; + // memset((void*)tb, 0x00, sizeof(TranslationBlock)); // not necessary as both tb and tb->tc.ptr is reused here return tb; } diff --git a/qemu/unicorn_common.h b/qemu/unicorn_common.h index b557c49058..8ce54aaa9e 100644 --- a/qemu/unicorn_common.h +++ b/qemu/unicorn_common.h @@ -40,6 +40,10 @@ static void release_common(void *t) int i; #endif + // Clear bps + cpu_watchpoint_remove_all(CPU(s->uc->cpu), BP_CPU); + cpu_breakpoint_remove_all(CPU(s->uc->cpu), BP_CPU); + // Clean TCG. TCGOpDef* def = s->tcg_op_defs; g_free(def->args_ct); @@ -72,8 +76,6 @@ static void release_common(void *t) /* qemu/util/qht.c:264: map = qht_map_create(n_buckets); */ qht_destroy(&s->tb_ctx.htable); - cpu_watchpoint_remove_all(CPU(s->uc->cpu), BP_CPU); - cpu_breakpoint_remove_all(CPU(s->uc->cpu), BP_CPU); #if TCG_TARGET_REG_BITS == 32 for(i = 0; i < s->nb_globals; i++) { diff --git a/tests/unit/test_x86.c b/tests/unit/test_x86.c index df36332d0d..51f655787b 100644 --- a/tests/unit/test_x86.c +++ b/tests/unit/test_x86.c @@ -2019,6 +2019,17 @@ static void test_x86_hook_insn_rdtscp(void) OK(uc_close(uc)); } +static void test_x86_dr7() { + uc_engine *uc; + char code[] = + "\x48\xC7\xC0\x05\x00\x01\x00\x0F\x23\xF8"; // mov rax, 0x10005 + // mov dr7, rax + uc_common_setup(&uc, UC_ARCH_X86, UC_MODE_64, code, sizeof(code) - 1); + OK(uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 0)); + + OK(uc_close(uc)); +} + TEST_LIST = { {"test_x86_in", test_x86_in}, {"test_x86_out", test_x86_out}, @@ -2079,4 +2090,5 @@ TEST_LIST = { {"test_x86_ro_segfault", test_x86_ro_segfault}, {"test_x86_hook_insn_rdtsc", test_x86_hook_insn_rdtsc}, {"test_x86_hook_insn_rdtscp", test_x86_hook_insn_rdtscp}, + {"test_x86_dr7", test_x86_dr7}, {NULL, NULL}}; From 80f0dac6f0afeef0ee44c76222d5e1926ca412c8 Mon Sep 17 00:00:00 2001 From: mio Date: Sat, 4 Jan 2025 17:21:02 +0800 Subject: [PATCH 61/72] Fix warning --- tests/unit/test_x86.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/test_x86.c b/tests/unit/test_x86.c index 51f655787b..218d7ff0f2 100644 --- a/tests/unit/test_x86.c +++ b/tests/unit/test_x86.c @@ -61,7 +61,7 @@ static void QuickTest_run(QuickTest *test) uint64_t value = 0; OK(uc_reg_read(uc, out->reg, &value)); acutest_check_(value == out->value, out->file, out->line, - "OUT_REG(%s, 0x%llX) = 0x%llX", out->name, + "OUT_REG(%s, 0x%lX) = 0x%lX", out->name, out->value, value); } else { uint32_t value = 0; From 996ad57e348426a9891fa86da4131905da957fd2 Mon Sep 17 00:00:00 2001 From: mio Date: Sat, 4 Jan 2025 17:35:09 +0800 Subject: [PATCH 62/72] Add an option to determine if building all-in-one objects archive --- CMakeLists.txt | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index dd3e347689..b1f389bf49 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -81,6 +81,7 @@ include(cmake/bundle_static.cmake) # Also we would like users to link a native cmake target, instead of a custom target for better # compatability. option(BUILD_SHARED_LIBS "Build shared instead of static library" ${PROJECT_IS_TOP_LEVEL}) +option(UNICORN_LEGACY_STATIC_ARCHIVE "Enable Unicorn v1 style all-in-one objects archive for libunicorn.a" ${PROJECT_IS_TOP_LEVEL}) option(UNICORN_FUZZ "Enable fuzzing" OFF) option(UNICORN_LOGGING "Enable logging" OFF) option(UNICORN_BUILD_TESTS "Build unicorn tests" ${PROJECT_IS_TOP_LEVEL}) @@ -1442,16 +1443,18 @@ if (BUILD_SHARED_LIBS) endif() # Black magic for generating static archives... -if (BUILD_SHARED_LIBS) - if (MSVC) - # Avoid the import lib built by MVSC clash with our archive. - set_target_properties(unicorn PROPERTIES ARCHIVE_OUTPUT_NAME "unicorn-import") +if (UNICORN_LEGACY_STATIC_ARCHIVE) + if (BUILD_SHARED_LIBS) + if (MSVC) + # Avoid the import lib built by MVSC clash with our archive. + set_target_properties(unicorn PROPERTIES ARCHIVE_OUTPUT_NAME "unicorn-import") + endif() + bundle_static_library(unicorn_static unicorn_archive unicorn) + else() + # Rename the "static" lib to avoid filename clash. + set_target_properties(unicorn PROPERTIES OUTPUT_NAME "unicorn-static") + bundle_static_library(unicorn unicorn_archive unicorn) endif() - bundle_static_library(unicorn_static unicorn_archive unicorn) -else() - # Rename the "static" lib to avoid filename clash. - set_target_properties(unicorn PROPERTIES OUTPUT_NAME "unicorn-static") - bundle_static_library(unicorn unicorn_archive unicorn) endif() if(UNICORN_FUZZ) @@ -1514,8 +1517,10 @@ if(UNICORN_INSTALL AND NOT MSVC) LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ) endif() - install(FILES $ DESTINATION ${CMAKE_INSTALL_LIBDIR}) - install(FILES $/$ DESTINATION ${CMAKE_INSTALL_LIBDIR}) + if (UNICORN_LEGACY_STATIC_ARCHIVE) + install(FILES $ DESTINATION ${CMAKE_INSTALL_LIBDIR}) + install(FILES $/$ DESTINATION ${CMAKE_INSTALL_LIBDIR}) + endif() install(FILES ${UNICORN_HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/unicorn) if (ATOMIC_LINKAGE_FIX) set(ATOMIC_LINK_PKG_CONFIG " -latomic") From 8442eb6feb4d902796406e48307589ce5909749d Mon Sep 17 00:00:00 2001 From: Glenn Baker <153300837+glennsec@users.noreply.github.com> Date: Sat, 4 Jan 2025 11:48:53 +0100 Subject: [PATCH 63/72] qemu/tcg: fix UC_HOOK_MEM_READ on aarch64. (#2028) * qemu/tcg: fix UC_HOOK_MEM_READ on aarch64. Directly jump into the slow path when there is any hookmem enabled. This fixes #1908. Signed-off-by: Glenn Baker * qemu/tcg: fix UC_HOOK_MEM_READ on ppc64. Directly jump into the slow path when there is any hookmem enabled. Signed-off-by: Glenn Baker * qemu/tcg: check for UC_HOOK_MEM_READ_AFTER. Use has_hookmem() helper to determine wether "slow-path" TLB read is needed. Add this helper to x86 architecture as well so that to check for all hookmem. Signed-off-by: Glenn Baker * qemu/tcg: factor out has_hookmem(). It's the same implementation for all architectures, so factor out has_hookmem() into tcg_uc_has_hookmem(). Signed-off-by: Glenn Baker --------- Signed-off-by: Glenn Baker --- qemu/include/tcg/tcg.h | 7 +++++++ qemu/tcg/aarch64/tcg-target.inc.c | 12 +++++++++--- qemu/tcg/i386/tcg-target.inc.c | 2 +- qemu/tcg/ppc/tcg-target.inc.c | 18 ++++++++++++++---- 4 files changed, 31 insertions(+), 8 deletions(-) diff --git a/qemu/include/tcg/tcg.h b/qemu/include/tcg/tcg.h index fef0cc455a..c1d16b0b42 100644 --- a/qemu/include/tcg/tcg.h +++ b/qemu/include/tcg/tcg.h @@ -1578,4 +1578,11 @@ struct jit_code_entry { void uc_del_inline_hook(uc_engine *uc, struct hook *hk); void uc_add_inline_hook(uc_engine *uc, struct hook *hk, void** args, int args_len); +static inline bool tcg_uc_has_hookmem(TCGContext *s) +{ + return HOOK_EXISTS(s->uc, UC_HOOK_MEM_READ) || + HOOK_EXISTS(s->uc, UC_HOOK_MEM_READ_AFTER) || + HOOK_EXISTS(s->uc, UC_HOOK_MEM_WRITE); +} + #endif /* TCG_H */ diff --git a/qemu/tcg/aarch64/tcg-target.inc.c b/qemu/tcg/aarch64/tcg-target.inc.c index 23349c7661..50c9e595bb 100644 --- a/qemu/tcg/aarch64/tcg-target.inc.c +++ b/qemu/tcg/aarch64/tcg-target.inc.c @@ -1587,7 +1587,8 @@ static bool tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *lb) MemOp opc = get_memop(oi); MemOp size = opc & MO_SIZE; - if (!reloc_pc19(lb->label_ptr[0], s->code_ptr)) { + const int type = tcg_uc_has_hookmem(s) ? R_AARCH64_JUMP26 : R_AARCH64_CONDBR19; + if (!patch_reloc(lb->label_ptr[0], type, (intptr_t)s->code_ptr, 0)) { return false; } @@ -1612,7 +1613,8 @@ static bool tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *lb) MemOp opc = get_memop(oi); MemOp size = opc & MO_SIZE; - if (!reloc_pc19(lb->label_ptr[0], s->code_ptr)) { + const int type = tcg_uc_has_hookmem(s) ? R_AARCH64_JUMP26 : R_AARCH64_CONDBR19; + if (!patch_reloc(lb->label_ptr[0], type, (intptr_t)s->code_ptr, 0)) { return false; } @@ -1711,7 +1713,11 @@ static void tcg_out_tlb_read(TCGContext *s, TCGReg addr_reg, MemOp opc, /* If not equal, we jump to the slow path. */ *label_ptr = s->code_ptr; - tcg_out_insn(s, 3202, B_C, TCG_COND_NE, 0); + // Unicorn: fast path if hookmem is not enabled + if (!tcg_uc_has_hookmem(s)) + tcg_out_insn(s, 3202, B_C, TCG_COND_NE, 0); + else + tcg_out_insn(s, 3206, B, 0); } #endif /* CONFIG_SOFTMMU */ diff --git a/qemu/tcg/i386/tcg-target.inc.c b/qemu/tcg/i386/tcg-target.inc.c index fc5cdbba44..15cc1c05d9 100644 --- a/qemu/tcg/i386/tcg-target.inc.c +++ b/qemu/tcg/i386/tcg-target.inc.c @@ -1763,7 +1763,7 @@ static inline void tcg_out_tlb_load(TCGContext *s, TCGReg addrlo, TCGReg addrhi, tcg_out_mov(s, ttype, r1, addrlo); // Unicorn: fast path if hookmem is not enable - if (!HOOK_EXISTS(s->uc, UC_HOOK_MEM_READ) && !HOOK_EXISTS(s->uc, UC_HOOK_MEM_WRITE)) + if (!tcg_uc_has_hookmem(s)) tcg_out_opc(s, OPC_JCC_long + JCC_JNE, 0, 0, 0); else /* slow_path, so data access will go via load_helper() */ diff --git a/qemu/tcg/ppc/tcg-target.inc.c b/qemu/tcg/ppc/tcg-target.inc.c index 41a1c511c3..00b7942901 100644 --- a/qemu/tcg/ppc/tcg-target.inc.c +++ b/qemu/tcg/ppc/tcg-target.inc.c @@ -2014,7 +2014,8 @@ static bool tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *lb) MemOp opc = get_memop(oi); TCGReg hi, lo, arg = TCG_REG_R3; - if (!reloc_pc14(lb->label_ptr[0], s->code_ptr)) { + const int type = tcg_uc_has_hookmem(s) ? R_PPC_REL24 : R_PPC_REL14; + if (!patch_reloc(lb->label_ptr[0], type, (intptr_t)s->code_ptr, 0)) { return false; } @@ -2062,7 +2063,8 @@ static bool tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *lb) MemOp s_bits = opc & MO_SIZE; TCGReg hi, lo, arg = TCG_REG_R3; - if (!reloc_pc14(lb->label_ptr[0], s->code_ptr)) { + const int type = tcg_uc_has_hookmem(s) ? R_PPC_REL24 : R_PPC_REL14; + if (!patch_reloc(lb->label_ptr[0], type, (intptr_t)s->code_ptr, 0)) { return false; } @@ -2142,7 +2144,11 @@ static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, bool is_64) /* Load a pointer into the current opcode w/conditional branch-link. */ label_ptr = s->code_ptr; - tcg_out32(s, BC | BI(7, CR_EQ) | BO_COND_FALSE | LK); + // Unicorn: fast path if hookmem is not enabled + if (!tcg_uc_has_hookmem(s)) + tcg_out32(s, BC | BI(7, CR_EQ) | BO_COND_FALSE | LK); + else + tcg_out32(s, B | LK); rbase = TCG_REG_R3; #else /* !CONFIG_SOFTMMU */ @@ -2217,7 +2223,11 @@ static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, bool is_64) /* Load a pointer into the current opcode w/conditional branch-link. */ label_ptr = s->code_ptr; - tcg_out32(s, BC | BI(7, CR_EQ) | BO_COND_FALSE | LK); + // Unicorn: fast path if hookmem is not enabled + if (!tcg_uc_has_hookmem(s)) + tcg_out32(s, BC | BI(7, CR_EQ) | BO_COND_FALSE | LK); + else + tcg_out32(s, B | LK); rbase = TCG_REG_R3; #else /* !CONFIG_SOFTMMU */ From 87206327647c984bfa6628b71115687c55cc9cb3 Mon Sep 17 00:00:00 2001 From: lazymio Date: Sat, 4 Jan 2025 19:00:22 +0800 Subject: [PATCH 64/72] Add the unit test from saicao Co-authored-by: Sai Cao <1665673333@qq.com> --- tests/unit/test_arm64.c | 49 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/tests/unit/test_arm64.c b/tests/unit/test_arm64.c index 2de61d99eb..69f309929f 100644 --- a/tests/unit/test_arm64.c +++ b/tests/unit/test_arm64.c @@ -599,6 +599,54 @@ static void test_arm64_mem_prot_regress(void) OK(uc_close(uc)); } +static bool test_arm64_mem_read_write_cb(uc_engine *uc, int type, + uint64_t address, int size, + int64_t value, void *user_data) +{ + uint64_t *count = (uint64_t *)user_data; + switch (type) { + case UC_MEM_READ: + count[0]++; + break; + case UC_MEM_WRITE: + count[1]++; + break; + } + + return 0; +} +static void test_arm64_mem_hook_read_write(void) +{ + uc_engine *uc; + // ldp x1, x2, [sp] + // stp x1, x2,[sp] + // ldp x1, x2, [sp] + // stp x1, x2,[sp] + const char code[] = {0xe1, 0x0b, 0x40, 0xa9, 0xe1, 0x0b, 0x00, 0xa9, + 0xe1, 0x0b, 0x40, 0xa9, 0xe1, 0x0b, 0x00, 0xa9}; + uint64_t r_sp; + r_sp = 0x16db6a040; + uc_hook hk; + uint64_t counter[2] = {0, 0}; + + uc_common_setup(&uc, UC_ARCH_ARM64, UC_MODE_ARM, code, sizeof(code), + UC_CPU_ARM64_A72); + + uc_reg_write(uc, UC_ARM64_REG_SP, &r_sp); + uc_mem_map(uc, 0x16db68000, 1024 * 16, UC_PROT_ALL); + + OK(uc_hook_add(uc, &hk, UC_HOOK_MEM_READ, test_arm64_mem_read_write_cb, + counter, 1, 0)); + OK(uc_hook_add(uc, &hk, UC_HOOK_MEM_WRITE, test_arm64_mem_read_write_cb, + counter, 1, 0)); + + uc_assert_err(UC_ERR_OK, uc_emu_start(uc, code_start, + code_start + sizeof(code), 0, 0)); + + TEST_CHECK(counter[0] == 4 && counter[1] == 4); + OK(uc_close(uc)); +} + TEST_LIST = {{"test_arm64_until", test_arm64_until}, {"test_arm64_code_patching", test_arm64_code_patching}, {"test_arm64_code_patching_count", test_arm64_code_patching_count}, @@ -615,4 +663,5 @@ TEST_LIST = {{"test_arm64_until", test_arm64_until}, {"test_arm64_mmu", test_arm64_mmu}, {"test_arm64_pc_wrap", test_arm64_pc_wrap}, {"test_arm64_mem_prot_regress", test_arm64_mem_prot_regress}, + {"test_arm64_mem_hook_read_write", test_arm64_mem_hook_read_write}, {NULL, NULL}}; From 9682fc42f729d3895cc1b3f18ceb8d0774830733 Mon Sep 17 00:00:00 2001 From: Daniel Roethlisberger Date: Tue, 7 Jan 2025 04:03:34 +0100 Subject: [PATCH 65/72] Allow uc_ctl_set_page_size() for arm64 (#2084) --- tests/unit/test_ctl.c | 26 ++++++++++++++++++++++++++ uc.c | 2 +- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/tests/unit/test_ctl.c b/tests/unit/test_ctl.c index 5949882360..9e2b4a4dee 100644 --- a/tests/unit/test_ctl.c +++ b/tests/unit/test_ctl.c @@ -198,6 +198,29 @@ static void test_uc_ctl_change_page_size(void) } #endif +// Test requires UC_ARCH_ARM64. +#ifdef UNICORN_HAS_ARM64 +static void test_uc_ctl_change_page_size_arm64(void) +{ + uc_engine *uc; + uc_engine *uc2; + uint32_t pg = 0; + + OK(uc_open(UC_ARCH_ARM64, UC_MODE_ARM, &uc)); + OK(uc_open(UC_ARCH_ARM64, UC_MODE_ARM, &uc2)); + + OK(uc_ctl_set_page_size(uc, 16384)); + OK(uc_ctl_get_page_size(uc, &pg)); + TEST_CHECK(pg == 16384); + + OK(uc_mem_map(uc2, 1 << 10, 1 << 10, UC_PROT_ALL)); + uc_assert_err(UC_ERR_ARG, uc_mem_map(uc, 1 << 10, 1 << 10, UC_PROT_ALL)); + + OK(uc_close(uc)); + OK(uc_close(uc2)); +} +#endif + // Test requires UC_ARCH_ARM. #ifdef UNICORN_HAS_ARM // Copy from test_arm.c but with new API. @@ -414,6 +437,9 @@ TEST_LIST = {{"test_uc_ctl_mode", test_uc_ctl_mode}, #ifdef UNICORN_HAS_ARM {"test_uc_ctl_change_page_size", test_uc_ctl_change_page_size}, {"test_uc_ctl_arm_cpu", test_uc_ctl_arm_cpu}, +#endif +#ifdef UNICORN_HAS_ARM64 + {"test_uc_ctl_change_page_size_arm64", test_uc_ctl_change_page_size_arm64}, #endif {"test_uc_hook_cached_uaf", test_uc_hook_cached_uaf}, {"test_uc_emu_stop_set_ip", test_uc_emu_stop_set_ip}, diff --git a/uc.c b/uc.c index 623e898e76..e1dcd4d549 100644 --- a/uc.c +++ b/uc.c @@ -2543,7 +2543,7 @@ uc_err uc_ctl(uc_engine *uc, uc_control_type control, ...) break; } - if (uc->arch != UC_ARCH_ARM) { + if (uc->arch != UC_ARCH_ARM && uc->arch != UC_ARCH_ARM64) { err = UC_ERR_ARG; break; } From a78d690da54f3afbd3213502c997ba68b29aa404 Mon Sep 17 00:00:00 2001 From: "Z. Liu" Date: Tue, 14 Jan 2025 08:47:54 +0800 Subject: [PATCH 66/72] configure: add for clock_adjtime on musl (#2085) for glibc, if _GNU_SOURCE is defined, will include but not for musl, so add "#include " although `man clock_adjtime` said "#include ", but it won't work for glibc w/o "#include ", I don't known why yet. PS it seems clock_adjtime is used nowhere? Signed-off-by: Z. Liu --- qemu/configure | 1 + 1 file changed, 1 insertion(+) diff --git a/qemu/configure b/qemu/configure index db820ca13b..47d4a4c6ef 100755 --- a/qemu/configure +++ b/qemu/configure @@ -1455,6 +1455,7 @@ fi clock_adjtime=no cat > $TMPC < +#include int main(void) { From 0c512f91a176125426cb0441fc6ea316ea50dfa2 Mon Sep 17 00:00:00 2001 From: lazymio Date: Sat, 18 Jan 2025 15:07:22 +0800 Subject: [PATCH 67/72] Fix #1643 This adds an extra op to translator to allow the block hook sync pc in the very begining --- qemu/accel/tcg/translator.c | 1 + qemu/include/exec/translator.h | 3 +++ qemu/target/arm/translate-a64.c | 7 +++++++ qemu/target/arm/translate.c | 8 ++++++++ qemu/target/i386/translate.c | 8 ++++++++ qemu/target/m68k/translate.c | 8 ++++++++ qemu/target/mips/translate.c | 8 ++++++++ qemu/target/ppc/translate.c | 7 +++++++ qemu/target/riscv/translate.c | 8 ++++++++ qemu/target/s390x/translate.c | 9 +++++++++ qemu/target/sparc/translate.c | 10 ++++++++++ qemu/target/tricore/translate.c | 8 ++++++++ 12 files changed, 85 insertions(+) diff --git a/qemu/accel/tcg/translator.c b/qemu/accel/tcg/translator.c index eccf06a703..72f21c410c 100644 --- a/qemu/accel/tcg/translator.c +++ b/qemu/accel/tcg/translator.c @@ -74,6 +74,7 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db, * full translation cache */ if (HOOK_EXISTS_BOUNDED(uc, UC_HOOK_BLOCK, tb->pc)) { + ops->pc_sync(db, cpu); prev_op = tcg_last_op(tcg_ctx); block_hook = true; gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, UC_HOOK_BLOCK_IDX, uc, db->pc_first); diff --git a/qemu/include/exec/translator.h b/qemu/include/exec/translator.h index 1b0be0803b..a86cd75977 100644 --- a/qemu/include/exec/translator.h +++ b/qemu/include/exec/translator.h @@ -104,6 +104,8 @@ typedef struct DisasContextBase { * * @tb_stop: * Emit any opcodes required to exit the TB, based on db->is_jmp. + * @pc_sync: + * Sync pc at this point */ typedef struct TranslatorOps { void (*init_disas_context)(DisasContextBase *db, CPUState *cpu); @@ -113,6 +115,7 @@ typedef struct TranslatorOps { const CPUBreakpoint *bp); void (*translate_insn)(DisasContextBase *db, CPUState *cpu); void (*tb_stop)(DisasContextBase *db, CPUState *cpu); + void (*pc_sync)(DisasContextBase *db, CPUState *cpu); } TranslatorOps; /** diff --git a/qemu/target/arm/translate-a64.c b/qemu/target/arm/translate-a64.c index 28abf77489..922976536e 100644 --- a/qemu/target/arm/translate-a64.c +++ b/qemu/target/arm/translate-a64.c @@ -14834,6 +14834,12 @@ static void aarch64_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) } } +static void aarch64_sync_pc(DisasContextBase *db, CPUState *cpu) +{ + DisasContext *dc = container_of(db, DisasContext, base); + gen_a64_set_pc_im(dc->uc->tcg_ctx, dc->base.pc_next); +} + const TranslatorOps aarch64_translator_ops = { .init_disas_context = aarch64_tr_init_disas_context, .tb_start = aarch64_tr_tb_start, @@ -14841,4 +14847,5 @@ const TranslatorOps aarch64_translator_ops = { .breakpoint_check = aarch64_tr_breakpoint_check, .translate_insn = aarch64_tr_translate_insn, .tb_stop = aarch64_tr_tb_stop, + .pc_sync = aarch64_sync_pc }; diff --git a/qemu/target/arm/translate.c b/qemu/target/arm/translate.c index f8fe7ea77c..079b727a5e 100644 --- a/qemu/target/arm/translate.c +++ b/qemu/target/arm/translate.c @@ -11739,6 +11739,12 @@ static void arm_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) } } +static void arm_pc_sync(DisasContextBase *db, CPUState *state) +{ + DisasContext *dc = container_of(db, DisasContext, base); + gen_set_pc_im(dc, dc->base.pc_next); +} + static const TranslatorOps arm_translator_ops = { .init_disas_context = arm_tr_init_disas_context, .tb_start = arm_tr_tb_start, @@ -11746,6 +11752,7 @@ static const TranslatorOps arm_translator_ops = { .breakpoint_check = arm_tr_breakpoint_check, .translate_insn = arm_tr_translate_insn, .tb_stop = arm_tr_tb_stop, + .pc_sync = arm_pc_sync }; static const TranslatorOps thumb_translator_ops = { @@ -11755,6 +11762,7 @@ static const TranslatorOps thumb_translator_ops = { .breakpoint_check = arm_tr_breakpoint_check, .translate_insn = thumb_tr_translate_insn, .tb_stop = arm_tr_tb_stop, + .pc_sync = arm_pc_sync }; /* generate intermediate code for basic block 'tb'. */ diff --git a/qemu/target/i386/translate.c b/qemu/target/i386/translate.c index be63255591..741102bed4 100644 --- a/qemu/target/i386/translate.c +++ b/qemu/target/i386/translate.c @@ -9441,6 +9441,13 @@ static void i386_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) } } +static void i386_sync_pc(DisasContextBase *db, CPUState *cpu) +{ + DisasContext *dc = container_of(db, DisasContext, base); + + gen_jmp_im(dc, dc->base.pc_next - dc->cs_base); +} + static const TranslatorOps i386_tr_ops = { .init_disas_context = i386_tr_init_disas_context, .tb_start = i386_tr_tb_start, @@ -9448,6 +9455,7 @@ static const TranslatorOps i386_tr_ops = { .breakpoint_check = i386_tr_breakpoint_check, .translate_insn = i386_tr_translate_insn, .tb_stop = i386_tr_tb_stop, + .pc_sync = i386_sync_pc, }; /* generate intermediate code for basic block 'tb'. */ diff --git a/qemu/target/m68k/translate.c b/qemu/target/m68k/translate.c index 91dc6aad20..5d0fa7497a 100644 --- a/qemu/target/m68k/translate.c +++ b/qemu/target/m68k/translate.c @@ -6414,6 +6414,13 @@ static void m68k_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) } } +void m68k_sync_pc(DisasContextBase *db, CPUState *cpu) +{ + DisasContext *dc = container_of(db, DisasContext, base); + + tcg_gen_movi_i32(dc->uc->tcg_ctx, QREG_PC, dc->base.pc_next); +} + static const TranslatorOps m68k_tr_ops = { .init_disas_context = m68k_tr_init_disas_context, .tb_start = m68k_tr_tb_start, @@ -6421,6 +6428,7 @@ static const TranslatorOps m68k_tr_ops = { .breakpoint_check = m68k_tr_breakpoint_check, .translate_insn = m68k_tr_translate_insn, .tb_stop = m68k_tr_tb_stop, + .pc_sync = m68k_sync_pc }; void gen_intermediate_code(CPUState *cpu, TranslationBlock *tb, int max_insns) diff --git a/qemu/target/mips/translate.c b/qemu/target/mips/translate.c index f828dbcc1c..383fcb1b51 100644 --- a/qemu/target/mips/translate.c +++ b/qemu/target/mips/translate.c @@ -31063,6 +31063,13 @@ static void mips_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) } } +static void mips_sync_pc(DisasContextBase *db, CPUState *cpu) +{ + DisasContext *s = container_of(db, DisasContext, base); + + gen_save_pc(s->uc->tcg_ctx, s->base.pc_next); +} + static const TranslatorOps mips_tr_ops = { .init_disas_context = mips_tr_init_disas_context, .tb_start = mips_tr_tb_start, @@ -31070,6 +31077,7 @@ static const TranslatorOps mips_tr_ops = { .breakpoint_check = mips_tr_breakpoint_check, .translate_insn = mips_tr_translate_insn, .tb_stop = mips_tr_tb_stop, + .pc_sync = mips_sync_pc }; void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int max_insns) diff --git a/qemu/target/ppc/translate.c b/qemu/target/ppc/translate.c index 1a23691352..9822aa328f 100644 --- a/qemu/target/ppc/translate.c +++ b/qemu/target/ppc/translate.c @@ -7731,6 +7731,12 @@ static void ppc_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) } } +static void ppc_sync_pc(DisasContextBase *db, CPUState *cpu) +{ + DisasContext *ctx = container_of(db, DisasContext, base); + gen_update_nip(ctx, ctx->base.pc_next); +} + static const TranslatorOps ppc_tr_ops = { .init_disas_context = ppc_tr_init_disas_context, .tb_start = ppc_tr_tb_start, @@ -7738,6 +7744,7 @@ static const TranslatorOps ppc_tr_ops = { .breakpoint_check = ppc_tr_breakpoint_check, .translate_insn = ppc_tr_translate_insn, .tb_stop = ppc_tr_tb_stop, + .pc_sync = ppc_sync_pc }; void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int max_insns) diff --git a/qemu/target/riscv/translate.c b/qemu/target/riscv/translate.c index 2ed311782d..792bc12fd0 100644 --- a/qemu/target/riscv/translate.c +++ b/qemu/target/riscv/translate.c @@ -919,6 +919,13 @@ static void riscv_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) } } +static void riscv_sync_pc(DisasContextBase *db, CPUState *cpu) +{ + DisasContext *dc = container_of(db, DisasContext, base); + TCGContext *tcg_ctx = dc->uc->tcg_ctx; + tcg_gen_movi_tl(tcg_ctx, tcg_ctx->cpu_pc, dc->base.pc_next); +} + static const TranslatorOps riscv_tr_ops = { .init_disas_context = riscv_tr_init_disas_context, .tb_start = riscv_tr_tb_start, @@ -926,6 +933,7 @@ static const TranslatorOps riscv_tr_ops = { .breakpoint_check = riscv_tr_breakpoint_check, .translate_insn = riscv_tr_translate_insn, .tb_stop = riscv_tr_tb_stop, + .pc_sync = riscv_sync_pc }; void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int max_insns) diff --git a/qemu/target/s390x/translate.c b/qemu/target/s390x/translate.c index b9ef06c6cf..09eea03e60 100644 --- a/qemu/target/s390x/translate.c +++ b/qemu/target/s390x/translate.c @@ -6683,6 +6683,8 @@ static DisasJumpType translate_one(CPUS390XState *env, DisasContext *s) // Unicorn: trace this instruction on request if (HOOK_EXISTS_BOUNDED(s->uc, UC_HOOK_CODE, s->base.pc_next)) { + update_psw_addr(s); + update_cc_op(s); gen_uc_tracecode(tcg_ctx, s->ilen, UC_HOOK_CODE_IDX, s->uc, s->base.pc_next); // the callback might want to stop emulation immediately check_exit_request(tcg_ctx); @@ -6928,6 +6930,12 @@ static void s390x_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) } } +static void s390x_sync_pc(DisasContextBase *db, CPUState *cpu) +{ + DisasContext *s = container_of(db, DisasContext, base); + update_psw_addr(s); +} + static const TranslatorOps s390x_tr_ops = { .init_disas_context = s390x_tr_init_disas_context, .tb_start = s390x_tr_tb_start, @@ -6935,6 +6943,7 @@ static const TranslatorOps s390x_tr_ops = { .breakpoint_check = s390x_tr_breakpoint_check, .translate_insn = s390x_tr_translate_insn, .tb_stop = s390x_tr_tb_stop, + .pc_sync = s390x_sync_pc }; void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int max_insns) diff --git a/qemu/target/sparc/translate.c b/qemu/target/sparc/translate.c index 2891e7fc2d..95b24ab649 100644 --- a/qemu/target/sparc/translate.c +++ b/qemu/target/sparc/translate.c @@ -6018,6 +6018,15 @@ static void sparc_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) } } +static void sparc_sync_pc(DisasContextBase *db, CPUState *cpu) +{ + DisasContext *dc = container_of(db, DisasContext, base); + TCGContext *tcg_ctx = dc->uc->tcg_ctx; + + tcg_gen_movi_tl(tcg_ctx, tcg_ctx->cpu_pc, dc->pc); + tcg_gen_movi_tl(tcg_ctx, tcg_ctx->cpu_npc, dc->npc); +} + static const TranslatorOps sparc_tr_ops = { .init_disas_context = sparc_tr_init_disas_context, .tb_start = sparc_tr_tb_start, @@ -6025,6 +6034,7 @@ static const TranslatorOps sparc_tr_ops = { .breakpoint_check = sparc_tr_breakpoint_check, .translate_insn = sparc_tr_translate_insn, .tb_stop = sparc_tr_tb_stop, + .pc_sync = sparc_sync_pc }; void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int max_insns) diff --git a/qemu/target/tricore/translate.c b/qemu/target/tricore/translate.c index 016306fbf5..75188b8be6 100644 --- a/qemu/target/tricore/translate.c +++ b/qemu/target/tricore/translate.c @@ -9297,12 +9297,20 @@ static void tricore_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) } } +static void tricore_pc_sync(DisasContextBase *db, CPUState *cpu) +{ + DisasContext *ctx = container_of(db, DisasContext, base); + + gen_save_pc(ctx, ctx->base.pc_next); +} + static const TranslatorOps tricore_tr_ops = { .init_disas_context = tricore_tr_init_disas_context, .tb_start = tricore_tr_tb_start, .insn_start = tricore_tr_insn_start, .translate_insn = tricore_tr_translate_insn, .tb_stop = tricore_tr_tb_stop, + .pc_sync = tricore_pc_sync }; From 99c57a8ecfece7b2adb2bdcb4591d48d1563cf73 Mon Sep 17 00:00:00 2001 From: lazymio Date: Sat, 18 Jan 2025 15:17:47 +0800 Subject: [PATCH 68/72] Add a test for #1643 --- tests/unit/test_x86.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/tests/unit/test_x86.c b/tests/unit/test_x86.c index 218d7ff0f2..036ef2e1df 100644 --- a/tests/unit/test_x86.c +++ b/tests/unit/test_x86.c @@ -2030,6 +2030,34 @@ static void test_x86_dr7() { OK(uc_close(uc)); } +static void test_x86_hook_block_cb(uc_engine *uc, uint64_t address, + uint32_t size, void *user_data) +{ + uint32_t pc; + + OK(uc_reg_read(uc, UC_X86_REG_EIP, (void*)&pc)); + + TEST_CHECK(pc == address); + *((uint64_t*)user_data) += 1; +} + +static void test_x86_hook_block() +{ + uc_engine *uc; + char code[] = "\xeb\x02\x90\x90\x90\x90\x90\x90"; // jmp 4; nop; nop; nop; + // nop; nop; nop + uint64_t cnt = 0; + uc_hook hk; + + uc_common_setup(&uc, UC_ARCH_X86, UC_MODE_32, code, sizeof(code) - 1); + + OK(uc_hook_add(uc, &hk, UC_HOOK_BLOCK, test_x86_hook_block_cb, (void*)&cnt, 1, 0)); + OK(uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 0)); + + TEST_CHECK(cnt == 2); + OK(uc_close(uc)); +} + TEST_LIST = { {"test_x86_in", test_x86_in}, {"test_x86_out", test_x86_out}, @@ -2091,4 +2119,5 @@ TEST_LIST = { {"test_x86_hook_insn_rdtsc", test_x86_hook_insn_rdtsc}, {"test_x86_hook_insn_rdtscp", test_x86_hook_insn_rdtscp}, {"test_x86_dr7", test_x86_dr7}, + {"test_x86_hook_block", test_x86_hook_block}, {NULL, NULL}}; From a0fa2c12499943448d1f83c9e0150d6ca7c9c092 Mon Sep 17 00:00:00 2001 From: lazymio Date: Sat, 18 Jan 2025 15:18:37 +0800 Subject: [PATCH 69/72] Fix a warning --- tests/unit/test_x86.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/test_x86.c b/tests/unit/test_x86.c index 036ef2e1df..13f03e7c04 100644 --- a/tests/unit/test_x86.c +++ b/tests/unit/test_x86.c @@ -61,7 +61,7 @@ static void QuickTest_run(QuickTest *test) uint64_t value = 0; OK(uc_reg_read(uc, out->reg, &value)); acutest_check_(value == out->value, out->file, out->line, - "OUT_REG(%s, 0x%lX) = 0x%lX", out->name, + "OUT_REG(%s, 0x%" PRIx64 ") = 0x%"PRIx64"", out->name, out->value, value); } else { uint32_t value = 0; From ab565729e7cab6a4b1b09b966ccaf2f2fa306f40 Mon Sep 17 00:00:00 2001 From: lazymio Date: Sat, 18 Jan 2025 15:18:49 +0800 Subject: [PATCH 70/72] Format code --- include/unicorn/unicorn.h | 2 +- tests/regress/x86_vex | Bin 0 -> 34136 bytes tests/unit/test_ctl.c | 29 +++++++++++++++-------------- tests/unit/test_x86.c | 16 +++++++++------- 4 files changed, 25 insertions(+), 22 deletions(-) create mode 100755 tests/regress/x86_vex diff --git a/include/unicorn/unicorn.h b/include/unicorn/unicorn.h index 1ee78f3aa1..c057dcc964 100644 --- a/include/unicorn/unicorn.h +++ b/include/unicorn/unicorn.h @@ -65,7 +65,7 @@ typedef size_t uc_hook; #define UNICORN_DEPRECATED __declspec(deprecated) #else #pragma message( \ - "WARNING: You need to implement UNICORN_DEPRECATED for this compiler") + "WARNING: You need to implement UNICORN_DEPRECATED for this compiler") #define UNICORN_DEPRECATED #endif diff --git a/tests/regress/x86_vex b/tests/regress/x86_vex new file mode 100755 index 0000000000000000000000000000000000000000..62d95ab8763db64023b0a7f322a1e8e3db15f0bd GIT binary patch literal 34136 zcmeI5du&tJ9mmhLP2xak0<8*!LEN(OEFtAlf*OhwlMXi8(2#C{Hn{}HAudS_wnIWw z6%!XVP_;`0XsDE7qG^qxtY|~4J&7$nTBoIunnXq1q5Ql{gF(Kx_Ho;_hYsoju zc9d0=Q|j2DR34j@=21Hzp`a{RmQ_`b%~tB$$EFF*pMqrFsDv#0U2T4fsUfw!oom#= zWM`@Q4kH)Lv@14+%CHt+N#^ zKJSV~_pTLn2O8_(QfU;<2l2`~XB zzyz286JP>NfC(@GCcp%k025#WOn?b60Vco%m;e)C0!)AjFaajO1egF5U;<2l2`~XB zzyz286JP>NfC(@GCcp&#djj|RMQpg;6dn$WZmS4XBr-+xm;L#XP%uAobDBsFW{M=v zt{lf1hU4)}dvv(HG>kgkeP;Q_J%=TB*OL<-ZqE%5n=Pm>E?TGClZmw=sl>DFNvmW} z_6FzQ?tHSNqSuiZvCjNnMXwo8A-b&ZKi(~*zV#vV>0-r~iZ%Z&)i2TgQ>uTcxEt+7 zd;8}^4xz7cvvjVrW(%|+>&KORTZWEo`~(lpPa zA3Z)P_MYNTqPc+PwJLH^*IbaUiOEdkTWZh@YMM7q&bJ}&rk6zB<=P|wC{&@ zyFE(nE9?0FbnQ9gXb&2+r)k>yI?gA})=4yTpxJ7V&eb(@(lzg5KTLgROQ(#kOG(xdmqw!Zrs`a<<-?}{NF z_f0Z(?DcVqL%DBU+=bbr%JY_tOJTbEGcVv#vWjkpEtlzsmNN^?x2i}xkusEIV{y5qbrPmQBHXwb!RT4lVc?`oN% zoge9S$Pw$3vEQ>Jyz%aYkiSLOALrd!K8}8eb(D9$rjPTL_>P-otoUsbGS@^N)wQOj zuT`8CPn+98gBH%ZVjVt--!qc3260~VVy?-a6sG(eN<705pJ|9cXo%+-;`0shhYj(C zhWJuLyuc7&sm62eo1Q&wYL+!KV~WLWlEi28-cEG><+#1l|MloD{br7+q`mHQ&Zt{h1Ir&3vEh~RjX@PuX(I)jkCVCw$4!G(Q2)I zSDTFWrJT|In)bBeNx=p)Q3R)W>3r@+M9An&It7Xw1D)~B#!27VN^3$CquEaU*aUk_ zjWOf~u!kY!2GsU*1Iqbe_-eu2fONfC(@GCcp%k025#WOn?b60Vco%m;e)C0!)AjFaajO1egF5U;<2l2`~XBzyz4U zgamBJ*$V4~(SMVxcL5-RV7w@R{`*@GO5Bdy0PK2-T2I0S0@8d~DXanZGVUzsg8dNI z2m1*24_JmIOg31trY6gBta`~0=eJ+iZ5k6d5tZXBDkyQS7!rzX|8 zyxzS+uHWl*H~Z^_+~Sm-jUL>{KrvTS3$^3*D_)bUNp7k|VdPM})$8`F`&_uG= zwHsE8wl!z8jL+|dl*g-$SCR$pzra9XW)a8TAtt%0L3XvdoGpHr+<-=0UXj&Q>ux5f z@fV7JMRy@wf!&88zlHl7Xyn&5Kk>NgQ*${IZfHQD{0Ct!bO#`v$M_$N~QAEfx_Q~YR(PwR{BmY{ni=ne_G zKZ5R#AXc;%>3{EYU_?YOOLtJv{S*I59UE3j`m|sfU|RlvmYh81?h(yXZVI{cNp>G* zi0)z0*T4fv!~97BVFEFeGM77%Uj?H8%)vgJNkOcpO!}FnUEv`l?amW%_Vkt+o}Zss z)<2wkXx&e*Y|FlO^py*3Vrp07siHIPCC4rteCH=so-4QN`z*hU#rBxWtESw0>C#It z9{&C1*ESt_ Y(~+)Q8R1fC>49@s->a;NT_T+S1{>c(761SM literal 0 HcmV?d00001 diff --git a/tests/unit/test_ctl.c b/tests/unit/test_ctl.c index 9e2b4a4dee..3da761b5ae 100644 --- a/tests/unit/test_ctl.c +++ b/tests/unit/test_ctl.c @@ -428,21 +428,22 @@ static void test_noexec(void) OK(uc_close(uc)); } -TEST_LIST = {{"test_uc_ctl_mode", test_uc_ctl_mode}, - {"test_uc_ctl_page_size", test_uc_ctl_page_size}, - {"test_uc_ctl_arch", test_uc_ctl_arch}, - {"test_uc_ctl_time_out", test_uc_ctl_time_out}, - {"test_uc_ctl_exits", test_uc_ctl_exits}, - {"test_uc_ctl_tb_cache", test_uc_ctl_tb_cache}, +TEST_LIST = { + {"test_uc_ctl_mode", test_uc_ctl_mode}, + {"test_uc_ctl_page_size", test_uc_ctl_page_size}, + {"test_uc_ctl_arch", test_uc_ctl_arch}, + {"test_uc_ctl_time_out", test_uc_ctl_time_out}, + {"test_uc_ctl_exits", test_uc_ctl_exits}, + {"test_uc_ctl_tb_cache", test_uc_ctl_tb_cache}, #ifdef UNICORN_HAS_ARM - {"test_uc_ctl_change_page_size", test_uc_ctl_change_page_size}, - {"test_uc_ctl_arm_cpu", test_uc_ctl_arm_cpu}, + {"test_uc_ctl_change_page_size", test_uc_ctl_change_page_size}, + {"test_uc_ctl_arm_cpu", test_uc_ctl_arm_cpu}, #endif #ifdef UNICORN_HAS_ARM64 - {"test_uc_ctl_change_page_size_arm64", test_uc_ctl_change_page_size_arm64}, + {"test_uc_ctl_change_page_size_arm64", test_uc_ctl_change_page_size_arm64}, #endif - {"test_uc_hook_cached_uaf", test_uc_hook_cached_uaf}, - {"test_uc_emu_stop_set_ip", test_uc_emu_stop_set_ip}, - {"test_tlb_clear", test_tlb_clear}, - {"test_noexec", test_noexec}, - {NULL, NULL}}; + {"test_uc_hook_cached_uaf", test_uc_hook_cached_uaf}, + {"test_uc_emu_stop_set_ip", test_uc_emu_stop_set_ip}, + {"test_tlb_clear", test_tlb_clear}, + {"test_noexec", test_noexec}, + {NULL, NULL}}; diff --git a/tests/unit/test_x86.c b/tests/unit/test_x86.c index 13f03e7c04..59d0408aab 100644 --- a/tests/unit/test_x86.c +++ b/tests/unit/test_x86.c @@ -61,8 +61,8 @@ static void QuickTest_run(QuickTest *test) uint64_t value = 0; OK(uc_reg_read(uc, out->reg, &value)); acutest_check_(value == out->value, out->file, out->line, - "OUT_REG(%s, 0x%" PRIx64 ") = 0x%"PRIx64"", out->name, - out->value, value); + "OUT_REG(%s, 0x%" PRIx64 ") = 0x%" PRIx64 "", + out->name, out->value, value); } else { uint32_t value = 0; OK(uc_reg_read(uc, out->reg, &value)); @@ -2019,7 +2019,8 @@ static void test_x86_hook_insn_rdtscp(void) OK(uc_close(uc)); } -static void test_x86_dr7() { +static void test_x86_dr7() +{ uc_engine *uc; char code[] = "\x48\xC7\xC0\x05\x00\x01\x00\x0F\x23\xF8"; // mov rax, 0x10005 @@ -2031,14 +2032,14 @@ static void test_x86_dr7() { } static void test_x86_hook_block_cb(uc_engine *uc, uint64_t address, - uint32_t size, void *user_data) + uint32_t size, void *user_data) { uint32_t pc; - OK(uc_reg_read(uc, UC_X86_REG_EIP, (void*)&pc)); + OK(uc_reg_read(uc, UC_X86_REG_EIP, (void *)&pc)); TEST_CHECK(pc == address); - *((uint64_t*)user_data) += 1; + *((uint64_t *)user_data) += 1; } static void test_x86_hook_block() @@ -2051,7 +2052,8 @@ static void test_x86_hook_block() uc_common_setup(&uc, UC_ARCH_X86, UC_MODE_32, code, sizeof(code) - 1); - OK(uc_hook_add(uc, &hk, UC_HOOK_BLOCK, test_x86_hook_block_cb, (void*)&cnt, 1, 0)); + OK(uc_hook_add(uc, &hk, UC_HOOK_BLOCK, test_x86_hook_block_cb, (void *)&cnt, + 1, 0)); OK(uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 0)); TEST_CHECK(cnt == 2); From 7f6dcc753950c31e4fa09839a481d107d9184458 Mon Sep 17 00:00:00 2001 From: lazymio Date: Sat, 18 Jan 2025 15:38:47 +0800 Subject: [PATCH 71/72] Test the pc is correctly sync-ed for arm64 --- tests/unit/test_arm64.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/unit/test_arm64.c b/tests/unit/test_arm64.c index 69f309929f..cd6ddda583 100644 --- a/tests/unit/test_arm64.c +++ b/tests/unit/test_arm64.c @@ -299,6 +299,9 @@ static void test_arm64_correct_address_in_long_jump_hook(void) static void test_arm64_block_sync_pc_cb(uc_engine *uc, uint64_t addr, uint32_t size, void *data) { + uint64_t pc; + OK(uc_reg_read(uc, UC_ARM64_REG_PC, (void*)&pc)); + TEST_CHECK(pc == addr); uint64_t val = code_start; bool first = *(bool *)data; if (first) { From 48147c15e8383ebce451459ff54f73131cce202f Mon Sep 17 00:00:00 2001 From: ZakDanger Date: Sun, 19 Jan 2025 01:00:16 +1100 Subject: [PATCH 72/72] Added start in mips16 mode support, and unit test for it - on dev branch --- qemu/target/mips/unicorn.c | 17 ++++++++++++++--- tests/unit/test_mips.c | 31 ++++++++++++++++++++++++++----- 2 files changed, 40 insertions(+), 8 deletions(-) diff --git a/qemu/target/mips/unicorn.c b/qemu/target/mips/unicorn.c index ab922e6f4c..58348511b3 100644 --- a/qemu/target/mips/unicorn.c +++ b/qemu/target/mips/unicorn.c @@ -19,12 +19,18 @@ MIPSCPU *cpu_mips_init(struct uc_struct *uc); static void mips_set_pc(struct uc_struct *uc, uint64_t address) { - ((CPUMIPSState *)uc->cpu->env_ptr)->active_tc.PC = address; + ((CPUMIPSState *)uc->cpu->env_ptr)->active_tc.PC = address & ~(uint64_t )1ULL; + if (address & 1) { + ((CPUMIPSState *)uc->cpu->env_ptr)->hflags |= MIPS_HFLAG_M16; + } else { + ((CPUMIPSState *)uc->cpu->env_ptr)->hflags &= ~(MIPS_HFLAG_M16); + } } static uint64_t mips_get_pc(struct uc_struct *uc) { - return ((CPUMIPSState *)uc->cpu->env_ptr)->active_tc.PC; + return ((CPUMIPSState *)uc->cpu->env_ptr)->active_tc.PC | + !!(((CPUMIPSState *)uc->cpu->env_ptr)->hflags & (MIPS_HFLAG_M16)); } static void mips_release(void *ctx) @@ -128,7 +134,12 @@ uc_err reg_write(void *_env, int mode, unsigned int regid, const void *value, break; case UC_MIPS_REG_PC: CHECK_REG_TYPE(mipsreg_t); - env->active_tc.PC = *(mipsreg_t *)value; + env->active_tc.PC = *(mipsreg_t *)value & ~1ULL; + if ((*(uint32_t *)value & 1)) { + env->hflags |= MIPS_HFLAG_M16; + } else { + env->hflags &= ~(MIPS_HFLAG_M16); + } *setpc = 1; break; case UC_MIPS_REG_CP0_CONFIG3: diff --git a/tests/unit/test_mips.c b/tests/unit/test_mips.c index 8a00b8568c..6e9d4dd1f4 100644 --- a/tests/unit/test_mips.c +++ b/tests/unit/test_mips.c @@ -17,7 +17,7 @@ static void test_mips_el_ori(void) char code[] = "\x56\x34\x21\x34"; // ori $at, $at, 0x3456; int r_r1 = 0x6789; - uc_common_setup(&uc, UC_ARCH_MIPS, UC_MODE_32 | UC_MODE_LITTLE_ENDIAN, code, + uc_common_setup(&uc, UC_ARCH_MIPS, UC_MODE_MIPS32 | UC_MODE_LITTLE_ENDIAN, code, sizeof(code) - 1); OK(uc_reg_write(uc, UC_MIPS_REG_1, &r_r1)); @@ -36,7 +36,7 @@ static void test_mips_eb_ori(void) char code[] = "\x34\x21\x34\x56"; // ori $at, $at, 0x3456; int r_r1 = 0x6789; - uc_common_setup(&uc, UC_ARCH_MIPS, UC_MODE_32 | UC_MODE_BIG_ENDIAN, code, + uc_common_setup(&uc, UC_ARCH_MIPS, UC_MODE_MIPS32 | UC_MODE_BIG_ENDIAN, code, sizeof(code) - 1); OK(uc_reg_write(uc, UC_MIPS_REG_1, &r_r1)); @@ -56,7 +56,7 @@ static void test_mips_stop_at_branch(void) "\x02\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x00"; // j 0x8; nop; int r_pc = 0x0; - uc_common_setup(&uc, UC_ARCH_MIPS, UC_MODE_32 | UC_MODE_LITTLE_ENDIAN, code, + uc_common_setup(&uc, UC_ARCH_MIPS, UC_MODE_MIPS32 | UC_MODE_LITTLE_ENDIAN, code, sizeof(code) - 1); // Execute one instruction with branch delay slot. @@ -78,7 +78,7 @@ static void test_mips_stop_at_delay_slot(void) "\x02\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x00"; // j 0x8; nop; int r_pc = 0x0; - uc_common_setup(&uc, UC_ARCH_MIPS, UC_MODE_32 | UC_MODE_LITTLE_ENDIAN, code, + uc_common_setup(&uc, UC_ARCH_MIPS, UC_MODE_MIPS32 | UC_MODE_LITTLE_ENDIAN, code, sizeof(code) - 1); // Stop at the delay slot by design. @@ -99,7 +99,7 @@ static void test_mips_lwx_exception_issue_1314(void) char code[] = "\x0a\xc8\x79\x7e"; // lwx $t9, $t9($s3) int reg; - uc_common_setup(&uc, UC_ARCH_MIPS, UC_MODE_32 | UC_MODE_LITTLE_ENDIAN, code, + uc_common_setup(&uc, UC_ARCH_MIPS, UC_MODE_MIPS32 | UC_MODE_LITTLE_ENDIAN, code, sizeof(code) - 1); OK(uc_mem_map(uc, 0x10000, 0x4000, UC_PROT_ALL)); @@ -126,10 +126,31 @@ static void test_mips_lwx_exception_issue_1314(void) OK(uc_close(uc)); } +static void test_mips_mips16(void) +{ + uc_engine *uc; + char code[] = "\xC4\x6B\x49\xE3"; // li $v1, 0xC4; addu $v0, $v1, $v0 + int r_v0 = 0x6789; + int mips16_lowbit = 1; + + uc_common_setup(&uc, UC_ARCH_MIPS, UC_MODE_MIPS32 | UC_MODE_LITTLE_ENDIAN, code, + sizeof(code) - 1); + OK(uc_reg_write(uc, UC_MIPS_REG_V0, &r_v0)); + + OK(uc_emu_start(uc, code_start | mips16_lowbit, code_start + sizeof(code) - 1, 0, 0)); + + OK(uc_reg_read(uc, UC_MIPS_REG_V0, &r_v0)); + + TEST_CHECK(r_v0 == 0x684D); + + OK(uc_close(uc)); +} + TEST_LIST = { {"test_mips_stop_at_branch", test_mips_stop_at_branch}, {"test_mips_stop_at_delay_slot", test_mips_stop_at_delay_slot}, {"test_mips_el_ori", test_mips_el_ori}, {"test_mips_eb_ori", test_mips_eb_ori}, {"test_mips_lwx_exception_issue_1314", test_mips_lwx_exception_issue_1314}, + {"test_mips_mips16", test_mips_mips16}, {NULL, NULL}}; \ No newline at end of file