From 1ba59ed70ae60c2b35b05a7416312af418c84fae Mon Sep 17 00:00:00 2001 From: Charles Ferguson Date: Sat, 15 Jan 2022 17:45:45 +0000 Subject: [PATCH 1/9] Clear Python pending hook exception before we enter the emulation. The pending exception hook is set when the hook raised an exception and wants to report it outside the emulation loop. However, it is never cleared back to None. This means that after an exception is raised in a hook, all subsequent execution (even if successful) will raise the exception. This change clears the exception before we start another emulation, which should ensure that if we have _hook_exception set, it really is from hooks in this emulation run. --- bindings/python/unicorn/unicorn.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bindings/python/unicorn/unicorn.py b/bindings/python/unicorn/unicorn.py index c929c23c..dcbd2cf3 100644 --- a/bindings/python/unicorn/unicorn.py +++ b/bindings/python/unicorn/unicorn.py @@ -460,6 +460,7 @@ class Uc(object): # emulate from @begin, and stop when reaching address @until def emu_start(self, begin, until, timeout=0, count=0): + self._hook_exception = None status = _uc.uc_emu_start(self._uch, begin, until, timeout, count) if status != uc.UC_ERR_OK: raise UcError(status) From a5ceca6d515564c5b5e3ba5f7e395b8d293a8b25 Mon Sep 17 00:00:00 2001 From: lazymio Date: Sat, 15 Jan 2022 22:11:14 +0100 Subject: [PATCH 2/9] Remove the static variable in flatviews_init Or we may get an invalid old (and free-ed) uc instance reference --- include/uc_priv.h | 2 ++ qemu/softmmu/memory.c | 12 +++++------- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/include/uc_priv.h b/include/uc_priv.h index 5c1e181d..1262c6de 100644 --- a/include/uc_priv.h +++ b/include/uc_priv.h @@ -368,6 +368,8 @@ struct uc_struct { int nested_level; // Current nested_level struct TranslationBlock *last_tb; // The real last tb we executed. + + FlatView *empty_view; // Static function variable moved from flatviews_init }; // Metadata stub for the variable-size cpu context used with uc_context_*() diff --git a/qemu/softmmu/memory.c b/qemu/softmmu/memory.c index c62e272f..3fe3467b 100644 --- a/qemu/softmmu/memory.c +++ b/qemu/softmmu/memory.c @@ -783,8 +783,6 @@ static void address_space_update_topology_pass(AddressSpace *as, static void flatviews_init(struct uc_struct *uc) { - static FlatView *empty_view; - if (uc->flat_views) { return; } @@ -792,13 +790,13 @@ static void flatviews_init(struct uc_struct *uc) uc->flat_views = g_hash_table_new_full(NULL, NULL, NULL, (GDestroyNotify) flatview_unref); - if (!empty_view) { - empty_view = generate_memory_topology(uc, NULL); + if (!uc->empty_view) { + uc->empty_view = generate_memory_topology(uc, NULL); /* We keep it alive forever in the global variable. */ - flatview_ref(empty_view); + flatview_ref(uc->empty_view); } else { - g_hash_table_replace(uc->flat_views, NULL, empty_view); - flatview_ref(empty_view); + g_hash_table_replace(uc->flat_views, NULL, uc->empty_view); + flatview_ref(uc->empty_view); } } From ea9c7425b028f5f54c33e23044c14c31a8a95813 Mon Sep 17 00:00:00 2001 From: lazymio Date: Sun, 16 Jan 2022 16:42:26 +0100 Subject: [PATCH 3/9] Fix the wrong PC when arm translation fectches unmapped memory This behavior keeps the same with Unicorn1, though, different from arm doc --- qemu/target/arm/translate.c | 15 +++++++++ tests/unit/test_arm.c | 64 +++++++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+) diff --git a/qemu/target/arm/translate.c b/qemu/target/arm/translate.c index 9de036cd..426995e4 100644 --- a/qemu/target/arm/translate.c +++ b/qemu/target/arm/translate.c @@ -11432,6 +11432,21 @@ static void arm_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) dc->pc_curr = dc->base.pc_next; insn = arm_ldl_code(env, dc->base.pc_next, dc->sctlr_b); dc->insn = insn; + + // Unicorn: + // + // If we get an error during fetching code, we have to skip the instruction decoding + // to ensure the PC remains unchanged. + // + // This is to keep the same behavior with Unicorn1, though, it's inconsistent with + // official arm documents. + // + // See discussion here: https://github.com/unicorn-engine/unicorn/issues/1536 + if (dc->uc->invalid_error) { + dcbase->is_jmp = DISAS_WFI; + return; + } + dc->base.pc_next += 4; disas_arm_insn(dc, insn); diff --git a/tests/unit/test_arm.c b/tests/unit/test_arm.c index 072d130f..b9dbfc52 100644 --- a/tests/unit/test_arm.c +++ b/tests/unit/test_arm.c @@ -529,6 +529,69 @@ static void test_arm_hflags_rebuilt() OK(uc_close(uc)); } +static bool test_arm_mem_access_abort_hook_mem(uc_engine *uc, uc_mem_type type, + uint64_t addr, int size, + int64_t val, void *data) +{ + OK(uc_reg_read(uc, UC_ARM_REG_PC, data)); + return false; +} + +static bool test_arm_mem_access_abort_hook_insn_invalid(uc_engine *uc, + void *data) +{ + OK(uc_reg_read(uc, UC_ARM_REG_PC, data)); + return false; +} + +static void test_arm_mem_access_abort() +{ + // LDR r0, [r0] + // Undefined instruction + char code[] = "\x00\x00\x90\xe5\x00\xa0\xf0\xf7"; + uc_engine *uc; + uint32_t r_pc, r_r0, r_pc_in_hook; + uc_hook hk, hkk; + + uc_common_setup(&uc, UC_ARCH_ARM, UC_MODE_ARM, code, sizeof(code) - 1, + UC_CPU_ARM_CORTEX_A9); + + r_r0 = 0x990000; + OK(uc_reg_write(uc, UC_ARM_REG_R0, &r_r0)); + + OK(uc_hook_add(uc, &hk, + UC_HOOK_MEM_READ_UNMAPPED | UC_HOOK_MEM_WRITE_UNMAPPED | + UC_HOOK_MEM_FETCH_UNMAPPED, + test_arm_mem_access_abort_hook_mem, (void *)&r_pc_in_hook, 1, + 0)); + OK(uc_hook_add(uc, &hkk, UC_HOOK_INSN_INVALID, + test_arm_mem_access_abort_hook_insn_invalid, + (void *)&r_pc_in_hook, 1, 0)); + + uc_assert_err(UC_ERR_READ_UNMAPPED, + uc_emu_start(uc, code_start, code_start + 4, 0, 0)); + + OK(uc_reg_read(uc, UC_ARM_REG_PC, &r_pc)); + + TEST_CHECK(r_pc == r_pc_in_hook); + + uc_assert_err(UC_ERR_INSN_INVALID, + uc_emu_start(uc, code_start + 4, code_start + 8, 0, 0)); + + OK(uc_reg_read(uc, UC_ARM_REG_PC, &r_pc)); + + TEST_CHECK(r_pc == r_pc_in_hook); + + uc_assert_err(UC_ERR_FETCH_UNMAPPED, + uc_emu_start(uc, 0x900000, 0x900000 + 8, 0, 0)); + + OK(uc_reg_read(uc, UC_ARM_REG_PC, &r_pc)); + + TEST_CHECK(r_pc == r_pc_in_hook); + + OK(uc_close(uc)); +} + TEST_LIST = {{"test_arm_nop", test_arm_nop}, {"test_arm_thumb_sub", test_arm_thumb_sub}, {"test_armeb_sub", test_armeb_sub}, @@ -545,4 +608,5 @@ TEST_LIST = {{"test_arm_nop", test_arm_nop}, test_arm_not_allow_privilege_escalation}, {"test_arm_mrc", test_arm_mrc}, {"test_arm_hflags_rebuilt", test_arm_hflags_rebuilt}, + {"test_arm_mem_access_abort", test_arm_mem_access_abort}, {NULL, NULL}}; \ No newline at end of file From 5559c097d58dff824f221e8063c51dd21bb7c391 Mon Sep 17 00:00:00 2001 From: Bet4 <0xbet4@gmail.com> Date: Mon, 17 Jan 2022 21:36:45 +0800 Subject: [PATCH 4/9] rust: Allow to remove self inside a hook --- bindings/rust/src/ffi.rs | 7 ++-- bindings/rust/src/lib.rs | 68 +++++++++++++++++++++------------- bindings/rust/tests/unicorn.rs | 18 ++++++--- 3 files changed, 59 insertions(+), 34 deletions(-) diff --git a/bindings/rust/src/ffi.rs b/bindings/rust/src/ffi.rs index e2b7c078..47240b43 100644 --- a/bindings/rust/src/ffi.rs +++ b/bindings/rust/src/ffi.rs @@ -96,12 +96,13 @@ pub trait IsUcHook<'a> {} impl<'a, D, F> IsUcHook<'a> for UcHook<'a, D, F> {} -pub extern "C" fn mmio_read_callback_proxy ( +pub extern "C" fn mmio_read_callback_proxy( uc: uc_handle, offset: u64, size: usize, user_data: *mut UcHook, -) -> u64 where +) -> u64 +where F: FnMut(&mut crate::Unicorn, u64, usize) -> u64, { let user_data = unsafe { &mut *user_data }; @@ -109,7 +110,7 @@ pub extern "C" fn mmio_read_callback_proxy ( (user_data.callback)(&mut user_data.uc, offset, size) } -pub extern "C" fn mmio_write_callback_proxy ( +pub extern "C" fn mmio_write_callback_proxy( uc: uc_handle, offset: u64, size: usize, diff --git a/bindings/rust/src/lib.rs b/bindings/rust/src/lib.rs index 9ec402e0..203b6e0d 100644 --- a/bindings/rust/src/lib.rs +++ b/bindings/rust/src/lib.rs @@ -84,29 +84,32 @@ pub struct MmioCallbackScope<'a> { impl<'a> MmioCallbackScope<'a> { fn has_regions(&self) -> bool { - self.regions.len() > 0 + !self.regions.is_empty() } fn unmap(&mut self, begin: u64, size: usize) { let end: u64 = begin + size as u64; - self.regions = self.regions.iter().flat_map( |(b, s)| { - let e: u64 = b + *s as u64; - if begin > *b { - if begin >= e { - // The unmapped region is completely after this region - vec![(*b, *s)] - } else { - if end >= e { + self.regions = self + .regions + .iter() + .flat_map(|(b, s)| { + let e: u64 = b + *s as u64; + if begin > *b { + if begin >= e { + // The unmapped region is completely after this region + vec![(*b, *s)] + } else if end >= e { // The unmapped region overlaps with the end of this region vec![(*b, (begin - *b) as usize)] } else { // The unmapped region is in the middle of this region let second_b = end + 1; - vec![(*b, (begin - *b) as usize), (second_b, (e - second_b) as usize)] + vec![ + (*b, (begin - *b) as usize), + (second_b, (e - second_b) as usize), + ] } - } - } else { - if end > *b { + } else if end > *b { if end >= e { // The unmapped region completely contains this region vec![] @@ -118,8 +121,8 @@ impl<'a> MmioCallbackScope<'a> { // The unmapped region is completely before this region vec![(*b, *s)] } - } - }).collect(); + }) + .collect(); } } @@ -318,7 +321,7 @@ impl<'a, D> Unicorn<'a, D> { /// `size` must be a multiple of 4kb or this will return `Error::ARG`. pub fn mmio_map( &mut self, - address: u64, + address: u64, size: libc::size_t, read_callback: Option, write_callback: Option, @@ -327,7 +330,7 @@ impl<'a, D> Unicorn<'a, D> { R: FnMut(&mut Unicorn, u64, usize) -> u64, W: FnMut(&mut Unicorn, u64, usize, u64), { - let mut read_data = read_callback.map( |c| { + let mut read_data = read_callback.map(|c| { Box::new(ffi::UcHook { callback: c, uc: Unicorn { @@ -335,7 +338,7 @@ impl<'a, D> Unicorn<'a, D> { }, }) }); - let mut write_data = write_callback.map( |c| { + let mut write_data = write_callback.map(|c| { Box::new(ffi::UcHook { callback: c, uc: Unicorn { @@ -363,9 +366,9 @@ impl<'a, D> Unicorn<'a, D> { }; if err == uc_error::OK { - let rd = read_data.map( |c| c as Box ); - let wd = write_data.map( |c| c as Box ); - self.inner_mut().mmio_callbacks.push(MmioCallbackScope{ + let rd = read_data.map(|c| c as Box); + let wd = write_data.map(|c| c as Box); + self.inner_mut().mmio_callbacks.push(MmioCallbackScope { regions: vec![(address, size)], read_callback: rd, write_callback: wd, @@ -390,7 +393,12 @@ impl<'a, D> Unicorn<'a, D> { where F: FnMut(&mut Unicorn, u64, usize) -> u64, { - self.mmio_map(address, size, Some(callback), None::, u64, usize, u64)>) + self.mmio_map( + address, + size, + Some(callback), + None::, u64, usize, u64)>, + ) } /// Map in a write-only MMIO region backed by a callback. @@ -406,7 +414,12 @@ impl<'a, D> Unicorn<'a, D> { where F: FnMut(&mut Unicorn, u64, usize, u64), { - self.mmio_map(address, size, None::, u64, usize) -> u64>, Some(callback)) + self.mmio_map( + address, + size, + None::, u64, usize) -> u64>, + Some(callback), + ) } /// Unmap a memory region. @@ -429,7 +442,9 @@ impl<'a, D> Unicorn<'a, D> { for scope in self.inner_mut().mmio_callbacks.iter_mut() { scope.unmap(address, size); } - self.inner_mut().mmio_callbacks.retain( |scope| scope.has_regions() ); + self.inner_mut() + .mmio_callbacks + .retain(|scope| scope.has_regions()); } /// Set the memory permissions for an existing memory region. @@ -817,11 +832,12 @@ impl<'a, D> Unicorn<'a, D> { let err: uc_error; // drop the hook - self.inner_mut() + let inner = self.inner_mut(); + inner .hooks .retain(|(hook_ptr, _hook_impl)| hook_ptr != &hook); - err = unsafe { ffi::uc_hook_del(self.inner().uc, hook) }; + err = unsafe { ffi::uc_hook_del(inner.uc, hook) }; if err == uc_error::OK { Ok(()) diff --git a/bindings/rust/tests/unicorn.rs b/bindings/rust/tests/unicorn.rs index 79f659aa..dba25c49 100644 --- a/bindings/rust/tests/unicorn.rs +++ b/bindings/rust/tests/unicorn.rs @@ -427,7 +427,10 @@ fn x86_mmio() { { // MOV eax, [0x2004]; MOV [0x2008], ax; - let x86_code: Vec = vec![0x8B, 0x04, 0x25, 0x04, 0x20, 0x00, 0x00, 0x66, 0x89, 0x04, 0x25, 0x08, 0x20, 0x00, 0x00]; + let x86_code: Vec = vec![ + 0x8B, 0x04, 0x25, 0x04, 0x20, 0x00, 0x00, 0x66, 0x89, 0x04, 0x25, 0x08, 0x20, 0x00, + 0x00, + ]; let read_cell = Rc::new(RefCell::new(MmioReadExpectation(0, 0))); let cb_read_cell = read_cell.clone(); @@ -436,7 +439,7 @@ fn x86_mmio() { 42 }; - let write_cell = Rc::new(RefCell::new(MmioWriteExpectation(0,0,0))); + let write_cell = Rc::new(RefCell::new(MmioWriteExpectation(0, 0, 0))); let cb_write_cell = write_cell.clone(); let write_callback = move |_: &mut Unicorn<'_, ()>, offset, size, value| { *cb_write_cell.borrow_mut() = MmioWriteExpectation(offset, size, value); @@ -444,7 +447,10 @@ fn x86_mmio() { assert_eq!(emu.mem_write(0x1000, &x86_code), Ok(())); - assert_eq!(emu.mmio_map(0x2000, 0x1000, Some(read_callback), Some(write_callback)), Ok(())); + assert_eq!( + emu.mmio_map(0x2000, 0x1000, Some(read_callback), Some(write_callback)), + Ok(()) + ); assert_eq!( emu.emu_start( @@ -494,9 +500,11 @@ fn x86_mmio() { { // MOV ax, 42; MOV [0x2008], ax; - let x86_code: Vec = vec![0x66, 0xB8, 0x2A, 0x00, 0x66, 0x89, 0x04, 0x25, 0x08, 0x20, 0x00, 0x00]; + let x86_code: Vec = vec![ + 0x66, 0xB8, 0x2A, 0x00, 0x66, 0x89, 0x04, 0x25, 0x08, 0x20, 0x00, 0x00, + ]; - let write_cell = Rc::new(RefCell::new(MmioWriteExpectation(0,0,0))); + let write_cell = Rc::new(RefCell::new(MmioWriteExpectation(0, 0, 0))); let cb_write_cell = write_cell.clone(); let write_callback = move |_: &mut Unicorn<'_, ()>, offset, size, value| { *cb_write_cell.borrow_mut() = MmioWriteExpectation(offset, size, value); From 28e791a37fff67ffa7551ef5339b58737f1b15b4 Mon Sep 17 00:00:00 2001 From: mio Date: Tue, 18 Jan 2022 19:35:43 +0100 Subject: [PATCH 5/9] Add debug tracing feature It's disabled by default, use -DUNICORN_TRACER=on to enable it --- CMakeLists.txt | 86 ++++++++++++++++++++++++++++++++++ bindings/python/setup.py | 5 +- include/uc_priv.h | 26 ++++++++++ qemu/accel/tcg/cpu-exec.c | 3 ++ qemu/accel/tcg/translate-all.c | 2 + uc.c | 28 +++++++++++ 6 files changed, 149 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b94ec1fe..e6d76cf2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,6 +20,8 @@ if (NOT UNICORN_ARCH) set(UNICORN_ARCH "x86 arm aarch64 riscv mips sparc m68k ppc") endif() +option(UNICORN_TRACER "Trace unicorn execution" OFF) + string(TOUPPER ${UNICORN_ARCH} UNICORN_ARCH) string(REPLACE " " ";" UNICORN_ARCH_LIST ${UNICORN_ARCH}) @@ -207,6 +209,9 @@ else() if (UNICORN_FUZZ) set (EXTRA_CFLAGS "${EXTRA_CFLAGS} ${CMAKE_C_FLAGS}") endif() + if(UNICORN_TRACER) + set (EXTRA_CFLAGS "${EXTRA_CFLAGS} -DUNICORN_TRACER") + endif() set(TARGET_LIST "--target-list=") if (UNICORN_HAS_X86) @@ -414,6 +419,11 @@ else() # Log and pow target_link_libraries(x86_64-softmmu m) endif() + +if(UNICORN_TRACER) + target_compile_options(x86_64-softmmu PRIVATE -DUNICORN_TRACER) +endif() + endif() if (UNICORN_HAS_ARM) @@ -452,6 +462,10 @@ else() ) endif() +if(UNICORN_TRACER) + target_compile_options(arm-softmmu PRIVATE -DUNICORN_TRACER) +endif() + add_library(armeb-softmmu ${UNICORN_ARCH_COMMON} @@ -486,6 +500,11 @@ else() -I${CMAKE_CURRENT_SOURCE_DIR}/qemu/target/arm ) endif() + +if(UNICORN_TRACER) + target_compile_options(armeb-softmmu PRIVATE -DUNICORN_TRACER) +endif() + endif() if (UNICORN_HAS_AARCH64) @@ -530,6 +549,10 @@ else() ) endif() +if(UNICORN_TRACER) + target_compile_options(aarch64-softmmu PRIVATE -DUNICORN_TRACER) +endif() + add_library(aarch64eb-softmmu ${UNICORN_ARCH_COMMON} @@ -570,6 +593,11 @@ else() -I${CMAKE_CURRENT_SOURCE_DIR}/qemu/target/arm ) endif() + +if(UNICORN_TRACER) + target_compile_options(aarch64eb-softmmu PRIVATE -DUNICORN_TRACER) +endif() + endif() if (UNICORN_HAS_M68K) @@ -600,6 +628,11 @@ else() -I${CMAKE_CURRENT_SOURCE_DIR}/qemu/target/m68k ) endif() + +if(UNICORN_TRACER) + target_compile_options(m68k-softmmu PRIVATE -DUNICORN_TRACER) +endif() + endif() if (UNICORN_HAS_MIPS) @@ -635,6 +668,10 @@ else() ) endif() +if(UNICORN_TRACER) + target_compile_options(mips-softmmu PRIVATE -DUNICORN_TRACER) +endif() + add_library(mipsel-softmmu ${UNICORN_ARCH_COMMON} @@ -667,6 +704,10 @@ else() ) endif() +if(UNICORN_TRACER) + target_compile_options(mipsel-softmmu PRIVATE -DUNICORN_TRACER) +endif() + add_library(mips64-softmmu ${UNICORN_ARCH_COMMON} @@ -699,6 +740,10 @@ else() ) endif() +if(UNICORN_TRACER) + target_compile_options(mips64-softmmu PRIVATE -DUNICORN_TRACER) +endif() + add_library(mips64el-softmmu ${UNICORN_ARCH_COMMON} @@ -730,6 +775,11 @@ else() -I${CMAKE_CURRENT_SOURCE_DIR}/qemu/target/mips ) endif() + +if(UNICORN_TRACER) + target_compile_options(mips64el-softmmu PRIVATE -DUNICORN_TRACER) +endif() + endif() if (UNICORN_HAS_SPARC) @@ -764,6 +814,10 @@ else() ) endif() +if(UNICORN_TRACER) + target_compile_options(sparc-softmmu PRIVATE -DUNICORN_TRACER) +endif() + add_library(sparc64-softmmu ${UNICORN_ARCH_COMMON} @@ -795,6 +849,11 @@ else() -I${CMAKE_CURRENT_SOURCE_DIR}/qemu/target/sparc ) endif() + +if(UNICORN_TRACER) + target_compile_options(sparc64-softmmu PRIVATE -DUNICORN_TRACER) +endif() + endif() if (UNICORN_HAS_PPC) @@ -842,6 +901,10 @@ else() ) endif() +if(UNICORN_TRACER) + target_compile_options(ppc-softmmu PRIVATE -DUNICORN_TRACER) +endif() + add_library(ppc64-softmmu ${UNICORN_ARCH_COMMON} @@ -889,6 +952,11 @@ else() -I${CMAKE_CURRENT_SOURCE_DIR}/qemu/target/ppc ) endif() + +if(UNICORN_TRACER) + target_compile_options(ppc64-softmmu PRIVATE -DUNICORN_TRACER) +endif() + endif() if (UNICORN_HAS_RISCV) @@ -921,6 +989,10 @@ else() ) endif() +if(UNICORN_TRACER) + target_compile_options(riscv32-softmmu PRIVATE -DUNICORN_TRACER) +endif() + add_library(riscv64-softmmu ${UNICORN_ARCH_COMMON} @@ -949,6 +1021,11 @@ else() -I${CMAKE_CURRENT_SOURCE_DIR}/qemu/target/riscv ) endif() + +if(UNICORN_TRACER) + target_compile_options(riscv64-softmmu PRIVATE -DUNICORN_TRACER) +endif() + endif() @@ -1113,6 +1190,15 @@ set(UNICORN_TEST_FILE ${UNICORN_TEST_FILE} test_mem) set(UNICORN_TEST_FILE ${UNICORN_TEST_FILE} test_ctl) set(UNICORN_SAMPLE_FILE ${UNICORN_SAMPLE_FILE} sample_ctl) +if(UNICORN_TRACER) + target_compile_options(unicorn-common PRIVATE -DUNICORN_TRACER) + target_compile_options(unicorn PRIVATE -DUNICORN_TRACER) +endif() + +target_compile_options(unicorn-common PRIVATE + ${UNICORN_COMPILE_OPTIONS} +) + target_compile_options(unicorn PRIVATE ${UNICORN_COMPILE_OPTIONS} ) diff --git a/bindings/python/setup.py b/bindings/python/setup.py index 9a855a27..7c3cb4ae 100755 --- a/bindings/python/setup.py +++ b/bindings/python/setup.py @@ -131,7 +131,10 @@ def build_libraries(): os.mkdir(BUILD_DIR) conf = 'Debug' if os.getenv('DEBUG', '') else 'Release' - subprocess.check_call(["cmake", '-B', BUILD_DIR, "-DCMAKE_BUILD_TYPE=" + conf]) + cmake_args = ["cmake", '-B', BUILD_DIR, "-DCMAKE_BUILD_TYPE=" + conf] + if os.getenv("TRACE", ""): + cmake_args += ["-DUNICORN_TRACER=on"] + subprocess.check_call(cmake_args) os.chdir(BUILD_DIR) threads = os.getenv("THREADS", "4") subprocess.check_call(["cmake", "--build", ".", "-j" + threads]) diff --git a/include/uc_priv.h b/include/uc_priv.h index 1262c6de..f4431a77 100644 --- a/include/uc_priv.h +++ b/include/uc_priv.h @@ -398,5 +398,31 @@ static inline int uc_addr_is_exit(uc_engine *uc, uint64_t addr) return g_tree_lookup(uc->exits, (gpointer)(&addr)) == (gpointer)1; } +#ifdef UNICORN_TRACER +#define UC_TRACE_START(loc) trace_start(get_tracer(), loc) +#define UC_TRACE_END(loc, fmt, ...) \ + trace_end(get_tracer(), loc, fmt, __VA_ARGS__) + +typedef enum trace_loc { + UC_TRACE_TB_EXEC = 0, + UC_TRACE_TB_TRANS, + UC_TRACER_MAX +} trace_loc; + +typedef struct uc_tracer { + int64_t starts[UC_TRACER_MAX]; +} uc_tracer; + +uc_tracer *get_tracer(); + +void trace_start(uc_tracer *tracer, trace_loc loc); + +void trace_end(uc_tracer *tracer, trace_loc loc, const char *fmt, ...); + +#else +#define UC_TRACE_START(loc) +#define UC_TRACE_END(loc, fmt, ...) +#endif + #endif /* vim: set ts=4 noet: */ diff --git a/qemu/accel/tcg/cpu-exec.c b/qemu/accel/tcg/cpu-exec.c index cb70582c..6c228f94 100644 --- a/qemu/accel/tcg/cpu-exec.c +++ b/qemu/accel/tcg/cpu-exec.c @@ -55,9 +55,12 @@ static inline tcg_target_ulong cpu_tb_exec(CPUState *cpu, TranslationBlock *itb) int tb_exit; uint8_t *tb_ptr = itb->tc.ptr; + UC_TRACE_START(UC_TRACE_TB_EXEC); tb_exec_lock(cpu->uc->tcg_ctx); ret = tcg_qemu_tb_exec(env, tb_ptr); tb_exec_unlock(cpu->uc->tcg_ctx); + UC_TRACE_END(UC_TRACE_TB_EXEC, "[uc] exec tb 0x%" PRIx64 ": ", itb->pc); + cpu->can_do_io = 1; last_tb = (TranslationBlock *)(ret & ~TB_EXIT_MASK); tb_exit = ret & TB_EXIT_MASK; diff --git a/qemu/accel/tcg/translate-all.c b/qemu/accel/tcg/translate-all.c index e48670eb..918a7979 100644 --- a/qemu/accel/tcg/translate-all.c +++ b/qemu/accel/tcg/translate-all.c @@ -1619,7 +1619,9 @@ TranslationBlock *tb_gen_code(CPUState *cpu, tcg_func_start(tcg_ctx); tcg_ctx->cpu = env_cpu(env); + UC_TRACE_START(UC_TRACE_TB_TRANS); gen_intermediate_code(cpu, tb, max_insns); + UC_TRACE_END(UC_TRACE_TB_TRANS, "[uc] translate tb 0x%" PRIx64 ": ", tb->pc); tcg_ctx->cpu = NULL; /* generate machine code */ diff --git a/uc.c b/uc.c index d6c3447e..0e870858 100644 --- a/uc.c +++ b/uc.c @@ -2196,3 +2196,31 @@ uc_err uc_ctl(uc_engine *uc, uc_control_type control, ...) return err; } + +#ifdef UNICORN_TRACER +uc_tracer *get_tracer() +{ + static uc_tracer tracer; + return &tracer; +} + +void trace_start(uc_tracer *tracer, trace_loc loc) +{ + tracer->starts[loc] = get_clock(); +} + +void trace_end(uc_tracer *tracer, trace_loc loc, const char *fmt, ...) +{ + va_list args; + int64_t end = get_clock(); + + va_start(args, fmt); + + vfprintf(stderr, fmt, args); + + va_end(args); + + fprintf(stderr, "%.6fms\n", + (double)(end - tracer->starts[loc]) / (double)(1000000)); +} +#endif \ No newline at end of file From 0da1f02fde5b109457707ae6be30196bba270b16 Mon Sep 17 00:00:00 2001 From: mio Date: Tue, 18 Jan 2022 19:48:40 +0100 Subject: [PATCH 6/9] Fix scale in tracing --- uc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/uc.c b/uc.c index 0e870858..b1ed43a9 100644 --- a/uc.c +++ b/uc.c @@ -2220,7 +2220,7 @@ void trace_end(uc_tracer *tracer, trace_loc loc, const char *fmt, ...) va_end(args); - fprintf(stderr, "%.6fms\n", - (double)(end - tracer->starts[loc]) / (double)(1000000)); + fprintf(stderr, "%.6fus\n", + (double)(end - tracer->starts[loc]) / (double)(1000)); } #endif \ No newline at end of file From c84dbac9a8bf954e5b14d8f8b597c1565eb9fe5d Mon Sep 17 00:00:00 2001 From: mio Date: Tue, 18 Jan 2022 20:15:28 +0100 Subject: [PATCH 7/9] Rename build dir for python bindings --- bindings/python/setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindings/python/setup.py b/bindings/python/setup.py index 7c3cb4ae..fd2604e8 100755 --- a/bindings/python/setup.py +++ b/bindings/python/setup.py @@ -27,7 +27,7 @@ LIBS_DIR = os.path.join(ROOT_DIR, 'unicorn', 'lib') HEADERS_DIR = os.path.join(ROOT_DIR, 'unicorn', 'include') SRC_DIR = os.path.join(ROOT_DIR, 'src') UC_DIR = os.path.join(ROOT_DIR, '../..') -BUILD_DIR = os.path.join(UC_DIR, 'build') +BUILD_DIR = os.path.join(UC_DIR, 'build_python') VERSION = "2.0.0rc5.post1" From 218bddc0e0bc4f0e0224602276f0ea35ee194e4d Mon Sep 17 00:00:00 2001 From: mio Date: Tue, 18 Jan 2022 21:01:49 +0100 Subject: [PATCH 8/9] Only use MAP_JIT on Apple Silicon MAP_JIT causes performance regression for fork() See https://github.com/desktop/desktop/issues/12978 --- qemu/include/qemu/osdep.h | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/qemu/include/qemu/osdep.h b/qemu/include/qemu/osdep.h index c0ff0aa5..cf0c94bb 100644 --- a/qemu/include/qemu/osdep.h +++ b/qemu/include/qemu/osdep.h @@ -119,18 +119,13 @@ struct uc_struct; * Only allow MAP_JIT for Mojave or later. * * Source: https://github.com/moby/hyperkit/pull/259/files#diff-e6b5417230ff2daff9155d9b15aefae12e89410ec2dca1f59d04be511f6737fcR41 + * + * But using MAP_JIT causes performance regression for fork() so we only use MAP_JIT on Apple M1. + * + * Issue: https://github.com/desktop/desktop/issues/12978 */ -#if defined(__APPLE__) - #if defined(HAVE_PTHREAD_JIT_PROTECT) - #define USE_MAP_JIT - #else - #include - #ifdef __MAC_OS_X_VERSION_MIN_REQUIRED - #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101400 && defined(MAP_JIT) - #define USE_MAP_JIT - #endif - #endif - #endif +#if defined(__APPLE__) && defined(HAVE_PTHREAD_JIT_PROTECT) && defined(__arm__) +#define USE_MAP_JIT #endif #include From 3cbe32053b07a8c66e0f8edea5042c4d67cce60c Mon Sep 17 00:00:00 2001 From: mio Date: Tue, 18 Jan 2022 21:09:01 +0100 Subject: [PATCH 9/9] Change git url to https url to avoid git submodule clone error --- .gitmodules | 2 +- docs/Unicorn_Engine_Documentation | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index 5284170e..4e459e71 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "docs/Unicorn_Engine_Documentation"] path = docs/Unicorn_Engine_Documentation - url = git@github.com:kabeor/Unicorn-Engine-Documentation + url = https://github.com/kabeor/Unicorn-Engine-Documentation diff --git a/docs/Unicorn_Engine_Documentation b/docs/Unicorn_Engine_Documentation index e1111e1b..db5434ad 160000 --- a/docs/Unicorn_Engine_Documentation +++ b/docs/Unicorn_Engine_Documentation @@ -1 +1 @@ -Subproject commit e1111e1b8b253bf7292ad911f43c125808a95f1e +Subproject commit db5434ad4e40c6657766aa6a54eafd92af38192e