GithubHelp home page GithubHelp logo

unicorn-engine / unicorn Goto Github PK

View Code? Open in Web Editor NEW
7.2K 7.2K 1.3K 15.49 MB

Unicorn CPU emulator framework (ARM, AArch64, M68K, Mips, Sparc, PowerPC, RiscV, S390x, TriCore, X86)

Home Page: http://www.unicorn-engine.org

License: GNU General Public License v2.0

Makefile 0.12% Python 1.79% C 90.02% C++ 0.43% Shell 0.76% Objective-C 0.22% Java 1.62% Go 0.55% F# 0.77% C# 0.10% Ruby 0.67% Haskell 0.56% Assembly 0.01% Pascal 0.82% CMake 0.28% VBA 0.50% Rust 0.58% Visual Basic 6.0 0.18%
arm arm64 cpu cpu-emulator emulator framework m68k mips powerpc reverse-engineering riscv s390x security sparc systemz tricore x86 x86-64

unicorn's People

Contributors

adrianherrera avatar aquynh avatar bet4it avatar catenacyber avatar chenhuitao avatar chfl4gs avatar cseagle avatar danghvu avatar domenukk avatar enkomio avatar eqv avatar farmdve avatar feliam avatar jonathonreinhart avatar lunixbochs avatar mothran avatar mrexodia avatar practicalswift avatar qducasse avatar radare avatar relapids avatar rhelmot avatar samothtronicien avatar sashs avatar seanheelan avatar stephengroat avatar tsrberry avatar williballenthin avatar wtdcode avatar xizhizhang avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

unicorn's Issues

Segfault in uc_mem_write

Test case:

#include <unicorn.h>

#define UC_BUG_WRITE_SIZE 13000

int main() {
        int size;
        char *buf;
        uch uh;
        uc_err err = uc_open (UC_ARCH_X86, UC_MODE_64, &uh);
        if (err) {
                fprintf (stderr, "Cannot initialize unicorn\n");
                return 1;
        }
        size = UC_BUG_WRITE_SIZE;
        buf = malloc (size);
        if (!buf) {
                fprintf (stderr, "Cannot allocate\n");
                return 1;
        }
        memset (buf, 0, size);
        uc_mem_map (uh, 0x1000, size);
        uc_mem_write (uh, 0x1000, buf, size);
        uc_close (&uh);
        return 0;
}

movsd instruction fails to decode

This is a minimized test case from the middle of an otherwise working program. The instruction in question (movsd xmm0, qword ptr [rip + 0x12aa]) works in qemu-user 2.0.0, but fails to decode in Unicorn.

from capstone import *
from unicorn import *
from unicorn.x86_const import *

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)
    ])

def hook_code(uc, addr, size, user_data):
    mem = uc.mem_read(addr, size)
    print 'instruction:', str(mem).encode('hex'), dis(mem, addr)
    print 'reference:  ', code.encode('hex'), dis(code, addr)

addr = 0x400000
mu = Uc(UC_ARCH_X86, UC_MODE_64)
mu.hook_add(UC_HOOK_CODE, hook_code)
mu.mem_map(addr, 8 * 1024 * 1024)
mu.mem_write(addr, code)
mu.emu_start(addr, addr + len(code))

Output:

instruction: f20f10 
reference:   f20f1005aa120000 movsd xmm0, qword ptr [rip + 0x12aa]

instruction was passed into the code callback from Unicorn and appears to be truncated.
reference is decoded from the original code object directly using Capstone.

The instruction and reference lines would be identical if everything worked.

When a Unicorn instance encounters this instruction, the PC fails to advance, causing an infinite loop.

assertion error (followed by exit()) in mem_map when size is 1

While playing around with the script from issue #12, I hit an assertion error that terminated my python interpreter. It's quite easy to trigger:

import unicorn

u = unicorn.Uc(unicorn.UC_ARCH_X86, unicorn.UC_MODE_32)
u.mem_map(0x2000, 1)
print "I am never reached"

Running that results in:

$ python assert.py 
python: /home/yans/code/angr/unicorn/qemu/exec.c:955: find_ram_offset_x86_64: Assertion `size != 0' failed.
Aborted

UC_MEM_READ vs UC_PROT_READ

confusing enums, for all RWX combinations, all of them should have the same values or just reuse them to avoid human errors when using them.

cffi Python bindings

ctypes is pretty slow, and especially bad with pypy. What are your thoughts on using cffi instead? I know it adds an extra dependency, so maybe cffi could be used if available with a ctypes fallback?

Can't set ARM stack pointer

from unicorn import *
from unicorn.arm_const import *
uc = Uc(UC_ARCH_ARM, UC_MODE_32)
uc.reg_write(ARM_REG_SP, 4)
print 'Writing 4 to SP'
print 'SP =', uc.reg_read(ARM_REG_SP)

Output:

Writing 4 to SP
SP = 0

Expected:

Writing 4 to SP
SP = 4

instruction hooking is very architecture and instruction specific

Right now, three x86 instruction types are hookable (IN, OUT, SYSCALL). Each has their own callback type. This could make the API both huge and very architecture-specific if we added support for hooking very many more instructions.

I think the interface could be nicer if callback types simply had some number of (u?)int64 arguments and there was an easy way for bindings to figure out which callback type to use.

Documentate map restrictions

It's really painful to try to skip the bugs in the uc_mem api, and having so much restrictions it gets really anoying to use. mainly because during those last 3 days i had to change the code of the r2 plugin many times because the way memory is read or written.

Looks like read/write ops should be done on aligned address (aligned to wat)
Same for sizes

capstone + unicorn compilation issues

When compiling a C based tool with unicorn I found a number of variable collisions were being reported.

#include <inttypes.h>
#include <string.h>
#include <unistd.h>

#include <unicorn/unicorn.h>
#include <capstone/capstone.h>

int main(int argc, char **argv, char **envp)
{
    return 0;
}

Then compiling with:
clang ./unicorn_capstone.c -lcapstone -o test

Errors from clang:

/usr/include/capstone/arm.h:277:2: error: redefinition of enumerator 'ARM_REG_D0'
        ARM_REG_D0,
        ^
/usr/include/unicorn/arm.h:31:2: note: previous definition is here
        ARM_REG_D0,
        ^
In file included from ./unicorn_capstone.c:6:
In file included from /usr/include/capstone/capstone.h:203:
/usr/include/capstone/arm.h:278:2: error: redefinition of enumerator 'ARM_REG_D1'
        ARM_REG_D1,
        ^
/usr/include/unicorn/arm.h:32:2: note: previous definition is here
        ARM_REG_D1,
        ^
In file included from ./unicorn_capstone.c:6:
In file included from /usr/include/capstone/capstone.h:203:

... repeated

My version of unicorn: 3e5ebc5
Capstone: 3.0.4-1
Linux arch, clang 3.6.2-2

I assume I am just missing something obvious in my compilation steps.

Thanks,

Sign extension issue on uc_reg_write leads to incorrect behaviour

I'm running Unicorn on a 64-bit host and emulating x86 32-bit code. When a register is written the value to be written is cast using int32_t, and so sign extension occurs, despite the fact that the source variable is unsigned and so is the destination. e.g see line 605 of qemu/target-i386/unicorn.c

  604                 case UC_X86_REG_EBX:
  605                     X86_CPU(uc, mycpu)->env.regs[R_EBX] = *(int32_t *)value;
  606                     break;

This compiles to the following:

(lldb) x/10i `$pc`
->  0x1030a755d: 48 8b 45 e0           movq   -0x20(%rbp), %rax
    0x1030a7561: 48 63 00              movslq (%rax), %rax
    0x1030a7564: 48 8b 7d d0           movq   -0x30(%rbp), %rdi
    0x1030a7568: 48 8b 75 d8           movq   -0x28(%rbp), %rsi
    0x1030a756c: 48 89 85 28 ff ff ff  movq   %rax, -0xd8(%rbp)
    0x1030a7573: e8 68 37 02 00        callq  0x1030cace0               ; object_dynamic_cast_assert at object.c:450
    0x1030a7578: 48 8b 8d 28 ff ff ff  movq   -0xd8(%rbp), %rcx
    0x1030a757f: 48 89 88 18 82 00 00  movq   %rcx, 0x8218(%rax)
    0x1030a7586: e9 88 0b 00 00        jmp    0x1030a8113               ; <+4515> at unicorn.c:689

As you can see, movslq is used, so if we print the value at the start and the end of the sequence we get the following:

(lldb) p/x *(int32_t *) value
(int32_t) $0 = 0x99d20000
(lldb) register read rcx
     rcx = 0xffffffff99d20000

This doesn't seem to be a problem if reg_read is used to retrieve the value but if the value is used as an address to an instruction which triggers an invalid memory access then the UC_HOOK_MEM_INVALID hook will be passed the sign extended 64-bit value rather than the 32-bit one. The following script demonstrates the issue:

#!/usr/bin/env python

import unicorn
ADDR = 0xffaabbcc

def hook_mem_invalid(mu, access, address, size, value, user_data):
    assert(address == ADDR)
    mu.mem_map(address & 0xfffff000, 4 * 1024)
    mu.mem_write(address, b'\xcc')
    return True

mu = unicorn.Uc(unicorn.UC_ARCH_X86, unicorn.UC_MODE_32)
mu.reg_write(unicorn.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_INVALID, hook_mem_invalid)
mu.emu_start(0x10000000, 0x10000000 + 2, count=1)

If the bug did not exist then I would expect the assert to pass and the mem_map/mem_write to succeed. Instead the mem_map maps a different address to the mem_write as it explicitly masks off the lower 32-bits.

No mem_unmap() API

Can't reliably emulate code's memory faults unless we can unmap memory too.

I'd be satisfied if mem_map() returned a handle to the MemoryRegion that I had to pass to unmap. If I want page-sized unmaps I can handle that in my own code.

Specifying flags for qemu's configure script

It would be nice to have a way to pass additional flags to the qemu configure script (invoked on the Makefile at line 207 and 211) without editing the Makefile by hand. In particular, on systems where both python3 and python2 are installed, it becomes necessary to pass the '--python=$path_to_python2' to the configure script to ensure that the build process can be completed.

Allow mapped memory blocks to be marked NX

At this point I can instrument and segregate instruction fetches from data reads. How would people like to receive this notification? New callback type? Overload the UC_HOOK_MEM_INVALID type to and UC_MEM_NX type code?

Can't mem_map() with protection mask

Simulation of environments with (W ^ X) protections will be more difficult if pages can't be protected.

I could also see a use for an mprotect()-like API that can change protections on existing pages.

Similar to #10, I think mem_map() should return a handle to the MemoryRegion that you pass to mem_protect() to change region protections.

adjacent non-overlapping mem_map() zeroes previously mapped memory

from unicorn import *
uc = Uc(UC_ARCH_X86, UC_MODE_64)
uc.mem_map(0x8048000, 0x2000)
uc.mem_write(0x8048000, 'test')
print 1, str(uc.mem_read(0x8048000, 4)).encode('hex')
uc.mem_map(0x804a000, 0x8000)
print 2, str(uc.mem_read(0x8048000, 4)).encode('hex')

Output:

1 74657374
2 00000000

Expected:

1 74657374
2 74657374

segfault with mem_map

While playing around with the script from issue #12, I hit a segfault in Unicorn. The script to trigger it:

import unicorn

u = unicorn.Uc(unicorn.UC_ARCH_X86, unicorn.UC_MODE_32)
u.mem_map(0x2000, 0x1000)
u.mem_read(0x2000, 1)

for i in range(20):
    try:
        u = unicorn.Uc(unicorn.UC_ARCH_X86, unicorn.UC_MODE_32)
        u.mem_map(i*0x1000, 5)
        u.mem_read(i*0x1000, 1)
        print hex(i*0x1000) + " succeeeded"
    except unicorn.UcError:
        print hex(i*0x1000) + " failed"

for i in range(20):
    try:
        u = unicorn.Uc(unicorn.UC_ARCH_X86, unicorn.UC_MODE_32)
        u.mem_map(i*0x1000, 5)
        u.mem_read(i*0x1000, 1)
        print hex(i*0x1000) + " succeeeded"
    except unicorn.UcError:
        print hex(i*0x1000) + " failed"

The segfault is in Python itself, so I'm guessing the bindings do something very bad:

[34174.852695] python[951]: segfault at c020 ip 0000000000516b28 sp 00007ffe237572c0 error 4 in python[400000+2bd000]

uc_mem_write overflows like there's no tomorrow

just pass a big size in the length parameter

==1915== Invalid write of size 8
==1915==    at 0x4C2E943: memcpy@@GLIBC_2.14 (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==1915==    by 0xA5AD137: address_space_rw_x86_64 (exec.c:1726)
==1915==    by 0xA5AD401: cpu_physical_memory_rw_x86_64 (exec.c:1787)
==1915==    by 0xA574334: cpu_physical_mem_write (unicorn_common.h:19)
==1915==    by 0xABB2295: uc_mem_write (uc.c:364)
==1915==    by 0xA2D415A: r_debug_unicorn_init (debug_unicorn.c:493)
==1915==    by 0xA2D3E3D: r_debug_unicorn_attach (debug_unicorn.c:423)
==1915==    by 0x5C8E709: r_debug_attach (debug.c:126)
==1915==    by 0x4E725E8: cmd_debug_pid (cmd_debug.c:402)
==1915==    by 0x4E7A821: cmd_debug (cmd_debug.c:2640)
==1915==    by 0x4EDEA82: r_cmd_call (cmd_api.c:209)
==1915==    by 0x4EB3FF3: r_core_cmd_subst_i (cmd.c:1685)
==1915==  Address 0x25c01000 is not stack'd, malloc'd or (recently) free'd

==1915== Process terminating with default action of signal 11 (SIGSEGV)
==1915==  Access not within mapped region at address 0x25C01000
==1915==    at 0x4C2E943: memcpy@@GLIBC_2.14 (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==1915==    by 0xA5AD137: address_space_rw_x86_64 (exec.c:1726)
==1915==    by 0xA5AD401: cpu_physical_memory_rw_x86_64 (exec.c:1787)
==1915==    by 0xA574334: cpu_physical_mem_write (unicorn_common.h:19)
==1915==    by 0xABB2295: uc_mem_write (uc.c:364)
==1915==    by 0xA2D415A: r_debug_unicorn_init (debug_unicorn.c:493)
==1915==    by 0xA2D3E3D: r_debug_unicorn_attach (debug_unicorn.c:423)
==1915==    by 0x5C8E709: r_debug_attach (debug.c:126)
==1915==    by 0x4E725E8: cmd_debug_pid (cmd_debug.c:402)
==1915==    by 0x4E7A821: cmd_debug (cmd_debug.c:2640)
==1915==    by 0x4EDEA82: r_cmd_call (cmd_api.c:209)
==1915==    by 0x4EB3FF3: r_core_cmd_subst_i (cmd.c:1685)

emu_stop segfault

I'm not sure if this is due to a misunderstanding on my behalf of how this API is supposed to be used, but earlier today when playing around with something I found the following causes a NULL pointer dereference.

import unicorn
ADDR = 0x10101000
mu = unicorn.Uc(unicorn.UC_ARCH_X86, unicorn.UC_MODE_32)
mu.mem_map(ADDR, 1024 * 4)
mu.mem_write(ADDR, b'\x41')
mu.emu_start(ADDR, ADDR + 1, count=1)
mu.emu_stop()

The crash details are as follows

* thread #1: tid = 0xd25de, 0x000000010295564c libunicorn.dylib`cpu_exit(cpu=0x0000000000000000) + 12 at cpu.c:112, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0xac)
    frame #0: 0x000000010295564c libunicorn.dylib`cpu_exit(cpu=0x0000000000000000) + 12 at cpu.c:112
   109
   110  void cpu_exit(CPUState *cpu)
   111  {
-> 112      cpu->exit_request = 1;
   113      cpu->tcg_exit_req = 1;
   114  }
   115
(lldb) bt
* thread #1: tid = 0xd25de, 0x000000010295564c libunicorn.dylib`cpu_exit(cpu=0x0000000000000000) + 12 at cpu.c:112, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0xac)
  * frame #0: 0x000000010295564c libunicorn.dylib`cpu_exit(cpu=0x0000000000000000) + 12 at cpu.c:112
    frame #1: 0x0000000102ff7d99 libunicorn.dylib`uc_emu_stop(handle=4311769600) + 73 at uc.c:495
    frame #2: 0x00007fff8c9d3f44 libffi.dylib`ffi_call_unix64 + 76
    frame #3: 0x00007fff8c9d4781 libffi.dylib`ffi_call + 853
    frame #4: 0x00000001007c8762 _ctypes.so`_ctypes_callproc + 874
    frame #5: 0x00000001007c2bad _ctypes.so`___lldb_unnamed_function18$$_ctypes.so + 1092
    frame #6: 0x000000010000e2ac Python`PyObject_Call + 99
    frame #7: 0x000000010008ac00 Python`PyEval_EvalFrameEx + 11370
    frame #8: 0x000000010008e60e Python`___lldb_unnamed_function1477$$Python + 262
    frame #9: 0x000000010008b3e3 Python`PyEval_EvalFrameEx + 13389
    frame #10: 0x0000000100087d62 Python`PyEval_EvalCodeEx + 1413
    frame #11: 0x00000001000877d7 Python`PyEval_EvalCode + 54
    frame #12: 0x00000001000a77bd Python`___lldb_unnamed_function1599$$Python + 53
    frame #13: 0x00000001000a7860 Python`PyRun_FileExFlags + 133
    frame #14: 0x00000001000a73fd Python`PyRun_SimpleFileExFlags + 769
    frame #15: 0x00000001000b8b23 Python`Py_Main + 3051
    frame #16: 0x00007fff8ed0f5c9 libdyld.dylib`start + 1
(lldb) f 1
frame #1: 0x0000000102ff7d99 libunicorn.dylib`uc_emu_stop(handle=4311769600) + 73 at uc.c:495
   492
   493      uc->stop_request = true;
   494      // exit the current TB
-> 495      cpu_exit(uc->current_cpu);
   496
   497      return UC_ERR_OK;
   498  }
(lldb) p uc->current_cpu
(CPUState *) $0 = 0x0000000000000000

I'm not familiar enough with Unicorn yet to guess at why current_cpu would be 0x0.

uc_mem_map seems to be a misnomer

I'm not sure why uc_mem_map is named as such. It doesn't "map" anything, it simply adds memory (RAM) at the given address. "mapping" implies making something visible in memory at the address, like mmap(2) does:

mmap, munmap - map or unmap files or devices into memory

In Linux KVM, they simply refer to this as adding a "memory region".

If, on the other hand, we were able to open a file, and make its contents visible in the memory space of the emulated machine at a particular address (a la mmap(2)), that I would consider to be "mapping".

mem_map's round-to-8k behavior seems too be a) too implicit and b) broken

Uc.mem_map rounds the address down to the nearest 8k boundary and the size up to the nearest 8k boundary. I think it'd be better to raise an an exception on "misalignment" rather than mysteriously do the rounding, especially as 8k is different from the standard 4k pagesize.

Aside from that, it doesn't seem to properly work. Consider the following script:

import unicorn

for i in range(20):
    try:
        u = unicorn.Uc(unicorn.UC_ARCH_X86, unicorn.UC_MODE_32)
        u.mem_map(i*0x1000, 5)
        u.mem_read(i*0x1000+6, 1)
        print hex(i*0x1000) + " succeeeded"
    except unicorn.UcError as e:
        print hex(i*0x1000) + " failed:",e

My understanding is that all of the reads should succeed, since the address should be rounded down, the size should be rounded up, and the memory access should be valid. However, they all fail:

0x0 failed: Invalid memory read (UC_ERR_MEM_READ)
0x1000 failed: Invalid memory read (UC_ERR_MEM_READ)
0x2000 failed: Invalid memory read (UC_ERR_MEM_READ)
0x3000 failed: Invalid memory read (UC_ERR_MEM_READ)
0x4000 failed: Invalid memory read (UC_ERR_MEM_READ)
0x5000 failed: Invalid memory read (UC_ERR_MEM_READ)
0x6000 failed: Invalid memory read (UC_ERR_MEM_READ)
0x7000 failed: Invalid memory read (UC_ERR_MEM_READ)
0x8000 failed: Invalid memory read (UC_ERR_MEM_READ)
0x9000 failed: Invalid memory read (UC_ERR_MEM_READ)
0xa000 failed: Invalid memory read (UC_ERR_MEM_READ)
0xb000 failed: Invalid memory read (UC_ERR_MEM_READ)
0xc000 failed: Invalid memory read (UC_ERR_MEM_READ)
0xd000 failed: Invalid memory read (UC_ERR_MEM_READ)
0xe000 failed: Invalid memory read (UC_ERR_MEM_READ)
0xf000 failed: Invalid memory read (UC_ERR_MEM_READ)
0x10000 failed: Invalid memory read (UC_ERR_MEM_READ)
0x11000 failed: Invalid memory read (UC_ERR_MEM_READ)
0x12000 failed: Invalid memory read (UC_ERR_MEM_READ)
0x13000 failed: Invalid memory read (UC_ERR_MEM_READ)

On top of that, it looks like it might actually map 5 bytes, somehow (although only for every other page!). For example, if we read from the beginning of the page, instead of six bytes in, with the following script:

import unicorn

for i in range(20):
    try:
        u = unicorn.Uc(unicorn.UC_ARCH_X86, unicorn.UC_MODE_32)
        u.mem_map(i*0x1000, 5)
        u.mem_read(i*0x1000, 1)
        print hex(i*0x1000) + " succeeeded"
    except unicorn.UcError as e:
        print hex(i*0x1000) + " failed:",e

This happens:

0x0 succeeeded
0x1000 failed: Invalid memory read (UC_ERR_MEM_READ)
0x2000 succeeeded
0x3000 failed: Invalid memory read (UC_ERR_MEM_READ)
0x4000 succeeeded
0x5000 failed: Invalid memory read (UC_ERR_MEM_READ)
0x6000 succeeeded
0x7000 failed: Invalid memory read (UC_ERR_MEM_READ)
0x8000 succeeeded
0x9000 failed: Invalid memory read (UC_ERR_MEM_READ)
0xa000 succeeeded
0xb000 failed: Invalid memory read (UC_ERR_MEM_READ)
0xc000 succeeeded
0xd000 failed: Invalid memory read (UC_ERR_MEM_READ)
0xe000 succeeeded
0xf000 failed: Invalid memory read (UC_ERR_MEM_READ)
0x10000 succeeeded
0x11000 failed: Invalid memory read (UC_ERR_MEM_READ)
0x12000 succeeeded
0x13000 failed: Invalid memory read (UC_ERR_MEM_READ)

The addresses that succeed are 8k-aligned (but the mapping seems to be only 5 bytes, as the first script fails!).

CPU state API

It would reduce API overhead if a pointer to an arch-specific CPU state could be retrieved via an API call as well as passed into callbacks.

If I add a basic block hook that prints changed registers on block entry (which can be found in usercorn), it generates 15+ extra read_reg() API calls per invocation.

You could pass callbacks a zero-copy pointer to a CPU state which could be accessed for current registers. This can be useful on almost all of the callback hook types. For example, on syscall I need to access rax unconditionally, as well as a variable number of registers.

This could also make generic instruction hooking nicer, as you only need to pass the CPU state and immediate values.

Example state struct:

struct uc_x86_state {
    uint64_t *rip;
    uint64_t *rax, *rbx, *rcx, *rdx, *rsi, *rdi,
             *r8, *r9, *r10, *r11, *r12, *r13, *r14, *r15;
};

I don't think there's a nice way to both make this zero-cost and keep the Unicorn register consts, due to overlapping registers like X86_REG_AX, X86_REG_EAX, X86_REG_RAX

Document the optional arguments to uc_hook_add

These are documented nowhere. Will someone that know what they are supposed mean and which HOOK types require them please document them somewhere that makes sense, like unicorn.h? Magic combinations like 1, 0 (the special case where arg1>arg2) make no sense. If you want to signal a special case like "call for all XXX" why not use the specific pair 0, 0?

UC_ERR_MEM_READ in syscall test

From sample_x86.py:

Emulate x86_64 code with 'syscall' instruction
ERROR: Invalid memory read (UC_ERR_MEM_READ)
>>> Emulation done. Below is the CPU context
>>> RAX = 0x200

In a local test, it looks like the next "instruction" after the syscall is executed, even though the emulator was marked to exit there.

Incorrect instruction sizes reported for sample_x86 under mingw32

The output I see when running under Mingw32 is (note some lengths reported as 0xf1f1f1f1). Is anyone else seeing this?:

$ ./sample_x86.exe -32
Emulate i386 code
>>> Tracing basic block at 0x1000000, block size = 0x2
>>> Tracing instruction at 0x1000000, instruction size = 0x1
>>> --- EFLAGS is 0x0
>>> Tracing instruction at 0x1000001, instruction size = 0xf1f1f1f1
>>> --- EFLAGS is 0x4
>>> Emulation done. Below is the CPU context
>>> ECX = 0x1235
>>> EDX = 0x788f
>>> Read 4 bytes from [0x1000000] = 0x4a41
===================================
Emulate i386 code with IN/OUT instructions
>>> Tracing basic block at 0x1000000, block size = 0x7
>>> Tracing instruction at 0x1000000, instruction size = 0x1
>>> --- EFLAGS is 0x0
>>> Tracing instruction at 0x1000001, instruction size = 0xf1f1f1f1
>>> --- EFLAGS is 0x0
--- reading from port 0x3f, size: 1, address: 0x1000001
>>> Tracing instruction at 0x1000003, instruction size = 0x1
>>> --- EFLAGS is 0x0
>>> Tracing instruction at 0x1000004, instruction size = 0xf1f1f1f1
>>> --- EFLAGS is 0x94
--- writing to port 0x46, size: 1, value: 0xf1, address: 0x1000004
--- register value = 0xf1
>>> Tracing instruction at 0x1000006, instruction size = 0x1
>>> --- EFLAGS is 0x94
>>> Emulation done. Below is the CPU context
>>> EAX = 0x12f1
>>> ECX = 0x678a
===================================
Emulate i386 code with jump
>>> Tracing basic block at 0x1000000, block size = 0x2
>>> Tracing instruction at 0x1000000, instruction size = 0x2
>>> --- EFLAGS is 0x0
>>> Emulation done. Below is the CPU context
===================================
Emulate i386 code that loop forever
>>> Emulation done. Below is the CPU context
>>> ECX = 0x1235
>>> EDX = 0x788f
===================================
Emulate i386 code that read from invalid memory
>>> Tracing basic block at 0x1000000, block size = 0x8
>>> Tracing instruction at 0x1000000, instruction size = 0x6
>>> --- EFLAGS is 0x0
Failed on uc_emu_start() with error returned 7: Invalid memory read (UC_ERR_MEM_READ)
>>> Emulation done. Below is the CPU context
>>> ECX = 0x1234
>>> EDX = 0x7890
===================================
Emulate i386 code that write to invalid memory
>>> Tracing basic block at 0x1000000, block size = 0x8
>>> Tracing instruction at 0x1000000, instruction size = 0x6
>>> --- EFLAGS is 0x0
>>> Missing memory is being WRITE at 0xaaaaaaaa, data size = 4, data value = 0x1234
>>> Tracing instruction at 0x1000006, instruction size = 0x1
>>> --- EFLAGS is 0x0
>>> Tracing instruction at 0x1000007, instruction size = 0xf1f1f1f1
>>> --- EFLAGS is 0x4
>>> Emulation done. Below is the CPU context
>>> ECX = 0x1235
>>> EDX = 0x788f
>>> Read 4 bytes from [0xaaaaaaaa] = 0x1234
>>> Failed to read 4 bytes from [0xffffffaa]
===================================
Emulate i386 code that jumps to invalid memory
>>> Tracing basic block at 0x1000000, block size = 0x5
>>> Tracing instruction at 0x1000000, instruction size = 0x5
>>> --- EFLAGS is 0x0
Failed on uc_emu_start() with error returned 9: Invalid code address (UC_ERR_CODE_INVALID)
>>> Emulation done. Below is the CPU context
>>> ECX = 0x1234
>>> EDX = 0x7890

FPU save env instructions not correctly writing to memory

Regression at #43

I found that the fstcw and fnstenv failed to write to memory. There memory write hook fires but a value of zero is written, when control word should be set to its default value as per:

unicorn/qemu/target-i386/cpu.c x86_cpu_reset()

    cpu_set_fpuc(env, 0x37f);

With fstenv:

     fnop
     fstenv [esp]

Output:

mem WRITE: 0x2000, data size = 4, data value = 0x0
value at ESP [0x2000]: 
0x0  0x0  0x0  0x0  0x0  0x0  0x0  0x0  0x55  0x55 

The 0x5555 appears to be the fptag in helper_fstenv()

Extend uc_open() to specify CPU model

we need to let users to specify CPU model at the time the engine is initialized. this is highlighted with a recent issue #114 (comment)

one way to do this is to extend uc_open() to contain one more param for CPU model. something like:

uc_err uc_open(uc_arch arch, uc_mode mode, uc_cpu model, uc_engine **uc);

any comments?

Assuming 4KB page size is wrong

For example, on intel it is possible to configure the MMU to have from 1KB to 64KB of page size. Imho this restriction should be configurable. Also, other archs uses != 4KB as default page size (for example aarch64 uses 64KB in kernel land)

Go binding UC_* const prefixes are unnecessary

Go already namespaces the Unicorn import, so uc.UC_MODE_ARM is a bit verbose, especially when talking about register names:

var LinuxRegs = []int{uc.UC_MIPS_REG_A0, uc.UC_MIPS_REG_A1, uc.UC_MIPS_REG_A2, uc.UC_MIPS_REG_A3}

Use of global variables

The Unicorn API gives the impression that one can create multiple instances of the Unicorn engine, and simultaneously emulate multiple machines. However, the use of (static) global variables precludes this possibility. These need to be moved into the data of the unicorn instance itself, or removed.

  • uc.c
    • map_begin
    • map_end
    • map_count
    • all_arch
    • archs_enable().initialized (what's the point in this function, anyway?)

Crash on Windows 64bit with Msys2

I compiled latest code on Windows 64bit using Msys2 (as described in https://github.com/unicorn-engine/unicorn/blob/master/COMPILE.TXT, section 7)

After that, I run samples/sample_x83.exe -32, and this crashes immediately.

With some investigation, I found that the crash happens inside uc_emu_start(), when executing qemu_thread_join(), and the exact place is at https://github.com/unicorn-engine/unicorn/blob/master/qemu/util/qemu-thread-win32.c#L319

I searched around a bit, and think perhaps this is due to some bugs in WaitForSingleObject() of Msys2, but not entirely sure, or how to fix this.

Note that this does not happen with Windows 32bit + Msys2. Only Windows 64bit + Msys2 has this issue.

Any Windows experts can help? Thanks.

Usercorn is broken by new memory APIs

Usercorn was broken entirely by one of the memory API changes.

I'm mapping memory with UC_PROT_ALL yet I get UC_ERR_CODE_INVALID when launching any binary with Usercorn under either the Go or Python bindings.

Current data types are messy

currently we have the following data types in unicorn.h.

ucengine
uchook
uc_arch
uc_mode
uc_err
uc_mem_type
uc_hook_t

the data types are not consistently named, as some are with _ and _t, some are not. also uchook & uc_hook_t look similar, and can be mistaken with each other.

any idea on how to renaming these? here is my proposal:

ucengine  --> uc_engine
uchook     --> uc_hook
uc_arch    --> <no change>
uc_mode  --> <no change>
uc_err       --> <no change>
uc_mem_type  --> <no change>
uc_hook_t        --> uc_hook_type

but still, uc_mem_type & uc_hook_type can have better names?

Change memory protection API

I think that the memory mapping API needs support for listing them and being able to change the permissions of a specific range inside a map. This is useful for MMU breakpoints

Valgrind cries at uc_open(UC_ARCH_X86, UC_MODE_64, &uh);

$ cat a.c
#include <unicorn/unicorn.h>
main() {
        uch uh;
        uc_err err;
        err = uc_open (UC_ARCH_X86, UC_MODE_64, &uh);
}

$ gcc a.c -lunicorn
$ valgrind ./a.out
==15571== Conditional jump or move depends on uninitialised value(s)
==15571==    at 0x4F1AAAB: tcg_target_init_x86_64 (tcg-target.c:2280)
==15571==    by 0x4F1B076: tcg_context_init_x86_64 (tcg.c:372)
==15571==    by 0x4F0EAB6: cpu_gen_init_x86_64 (translate-all.c:134)
==15571==    by 0x4F0F29D: tcg_exec_init_x86_64 (translate-all.c:715)
==15571==    by 0x4F4EB95: tcg_init (accel.c:46)
==15571==    by 0x4F4EDE8: accel_init_machine (accel.c:116)
==15571==    by 0x4F4EBDA: configure_accelerator (accel.c:73)
==15571==    by 0x4F50327: machine_initialize (vl.c:128)
==15571==    by 0x5511F12: uc_open (uc.c:232)
==15571==    by 0x400683: main (in /home/pancake/prg/unicorn/a.out)
==15571== 
==15571== Conditional jump or move depends on uninitialised value(s)
==15571==    at 0x4F1AADD: tcg_target_init_x86_64 (tcg-target.c:2295)
==15571==    by 0x4F1B076: tcg_context_init_x86_64 (tcg.c:372)
==15571==    by 0x4F0EAB6: cpu_gen_init_x86_64 (translate-all.c:134)
==15571==    by 0x4F0F29D: tcg_exec_init_x86_64 (translate-all.c:715)
==15571==    by 0x4F4EB95: tcg_init (accel.c:46)
==15571==    by 0x4F4EDE8: accel_init_machine (accel.c:116)
==15571==    by 0x4F4EBDA: configure_accelerator (accel.c:73)
==15571==    by 0x4F50327: machine_initialize (vl.c:128)
==15571==    by 0x5511F12: uc_open (uc.c:232)
==15571==    by 0x400683: main (in /home/pancake/prg/unicorn/a.out)
==15571== 

Value of UC_MODE_BIG_ENDIAN

Is there a compelling reason for this to be 1<<31 (ie 2147483648)? This is larger than INT_MAX and cause javac to complain.

Variables used uninitialized

target-mips/translate.c: In function 'gen_intermediate_code_mips':
target-mips/translate.c:19279:41: warning: 'save_opparam_ptr' may be used uninitialized in this function [-Wmaybe-uninitialized]
                 *(save_opparam_ptr + 1) = insn_bytes;
                                         ^
target-mips/translate.c:19174:13: note: 'save_opparam_ptr' was declared here
     TCGArg *save_opparam_ptr;
             ^
target-mips/translate.c:19290:12: warning: 'is_slot' may be used uninitialized in this function [-Wmaybe-uninitialized]
         if (is_slot) {
            ^
target-mips/translate.c:19172:9: note: 'is_slot' was declared here
     int is_slot;
         ^
  CC    sparc64-softmmu/hw/sparc64/sun4u.o
target-mips/translate.c: In function 'gen_intermediate_code_pc_mips':
target-mips/translate.c:19279:41: warning: 'save_opparam_ptr' may be used uninitialized in this function [-Wmaybe-uninitialized]
                 *(save_opparam_ptr + 1) = insn_bytes;
                                         ^
target-mips/translate.c:19174:13: note: 'save_opparam_ptr' was declared here
     TCGArg *save_opparam_ptr;
             ^
target-mips/translate.c:19290:12: warning: 'is_slot' may be used uninitialized in this function [-Wmaybe-uninitialized]
         if (is_slot) {
            ^
target-mips/translate.c:19172:9: note: 'is_slot' was declared here
     int is_slot;
         ^
  CC    sparc64-softmmu/target-sparc/translate.o
target-mips/translate.c: In function 'gen_intermediate_code_mipsel':
target-mips/translate.c:19279:41: warning: 'save_opparam_ptr' may be used uninitialized in this function [-Wmaybe-uninitialized]
                 *(save_opparam_ptr + 1) = insn_bytes;
                                         ^
target-mips/translate.c:19174:13: note: 'save_opparam_ptr' was declared here
     TCGArg *save_opparam_ptr;
             ^
target-mips/translate.c:19290:12: warning: 'is_slot' may be used uninitialized in this function [-Wmaybe-uninitialized]
         if (is_slot) {
            ^
target-mips/translate.c:19172:9: note: 'is_slot' was declared here
     int is_slot;
         ^
  CC    aarch64-softmmu/target-arm/helper-a64.o
target-mips/translate.c: In function 'gen_intermediate_code_pc_mipsel':
target-mips/translate.c:19279:41: warning: 'save_opparam_ptr' may be used uninitialized in this function [-Wmaybe-uninitialized]
                 *(save_opparam_ptr + 1) = insn_bytes;
                                         ^
target-mips/translate.c:19174:13: note: 'save_opparam_ptr' was declared here
     TCGArg *save_opparam_ptr;
             ^
target-mips/translate.c:19290:12: warning: 'is_slot' may be used uninitialized in this function [-Wmaybe-uninitialized]
         if (is_slot) {
            ^
target-mips/translate.c:19172:9: note: 'is_slot' was declared here
     int is_slot;
         ^
CFLAGS=-fsanitize=address make
  CC    mips64el-softmmu/target-mips/unicorn.o
target-mips/translate.c: In function 'gen_intermediate_code_mips64el':
target-mips/translate.c:19279:41: warning: 'save_opparam_ptr' may be used uninitialized in this function [-Wmaybe-uninitialized]
                 *(save_opparam_ptr + 1) = insn_bytes;
                                         ^ 
target-mips/translate.c:19174:13: note: 'save_opparam_ptr' was declared here
     TCGArg *save_opparam_ptr;
             ^
target-mips/translate.c:19290:12: warning: 'is_slot' may be used uninitialized in this function [-Wmaybe-uninitialized]
         if (is_slot) {
            ^
target-mips/translate.c:19172:9: note: 'is_slot' was declared here
     int is_slot;
         ^
target-mips/translate.c: In function 'gen_intermediate_code_mips64':
target-mips/translate.c:19279:41: warning: 'save_opparam_ptr' may be used uninitialized in this function [-Wmaybe-uninitialized]
                 *(save_opparam_ptr + 1) = insn_bytes;
                                         ^ 
target-mips/translate.c:19174:13: note: 'save_opparam_ptr' was declared here
     TCGArg *save_opparam_ptr;
             ^
target-mips/translate.c:19290:12: warning: 'is_slot' may be used uninitialized in this function [-Wmaybe-uninitialized]
         if (is_slot) {
            ^
target-mips/translate.c:19172:9: note: 'is_slot' was declared here
     int is_slot;
         ^
target-mips/translate.c: In function 'gen_intermediate_code_pc_mips64el':
target-mips/translate.c:19279:41: warning: 'save_opparam_ptr' may be used uninitialized in this function [-Wmaybe-uninitialized]
                 *(save_opparam_ptr + 1) = insn_bytes;
                                         ^ 
target-mips/translate.c:19174:13: note: 'save_opparam_ptr' was declared here
     TCGArg *save_opparam_ptr;
             ^
target-mips/translate.c:19290:12: warning: 'is_slot' may be used uninitialized in this function [-Wmaybe-uninitialized]
         if (is_slot) {
            ^
target-mips/translate.c:19172:9: note: 'is_slot' was declared here
     int is_slot;
         ^
target-mips/translate.c: In function 'gen_intermediate_code_pc_mips64':
target-mips/translate.c:19279:41: warning: 'save_opparam_ptr' may be used uninitialized in this function [-Wmaybe-uninitialized]
                 *(save_opparam_ptr + 1) = insn_bytes;
                                         ^ 
target-mips/translate.c:19174:13: note: 'save_opparam_ptr' was declared here
     TCGArg *save_opparam_ptr;
             ^
target-mips/translate.c:19290:12: warning: 'is_slot' may be used uninitialized in this function [-Wmaybe-uninitialized]
         if (is_slot) {
            ^
target-mips/translate.c:19172:9: note: 'is_slot' was declared here
     int is_slot;
         ^
make[1]: Leaving directory '/home/pancake/prg/unicorn/qemu'

Inconsistency with the uc_mem_type enum?

Currently the enum looks as follows:

152 typedef enum uc_mem_type {
153     UC_MEM_READ = 16,   // Unmapped memory is read from
154     UC_MEM_WRITE,       // Unmapped memory is written to
155     UC_MEM_READ_WRITE,  // Unmapped memory is accessed (either READ or WRITE)
156     UC_MEM_EXEC,        // Unmapped memory is fetched
157     UC_MEM_WRITE_PROT,  // Write to write protected, but mapped, memory
158     UC_MEM_READ_PROT,   // Read from read protected, but mapped, memory
159     UC_MEM_EXEC_PROT,   // Fetch from non-executable, but mapped, memory
160 } uc_mem_type;

Two questions:

  1. Why is there a UC_MEM_READ_WRITE? There's no UC_MEM_READ_WRITE_EXEC, so is there an argument for the former but not the latter?

  2. Why is there no UC_MEM_READ_WRITE_PROT? I wouldn't expect there to be, but since we have UC_MEM_READ_WRITE I was wondering if there was a reason for that but not the _PROT variant.

In short, I'm really asking "Why not remove UC_MEM_READ_WRITE" to be left with the following:

152 typedef enum uc_mem_type {
153     UC_MEM_READ = 16,   // Unmapped memory is read from
154     UC_MEM_WRITE,       // Unmapped memory is written to
156     UC_MEM_EXEC,        // Unmapped memory is fetched
157     UC_MEM_WRITE_PROT,  // Write to write protected, but mapped, memory
158     UC_MEM_READ_PROT,   // Read from read protected, but mapped, memory
159     UC_MEM_EXEC_PROT,   // Fetch from non-executable, but mapped, memory
160 } uc_mem_type;

Emulator hang on jmp REG, where REG contains a non-zero address of an unmapped page

If I emulate a jmp ebx instruction with EBX pointing to an unmapped page the behaviour seems to differ in the case of EBX being zero vs non-zero. In the zero case I get a UC_ERR_INVALID_ADDR error, as one might expect. In the non-zero case the emulator hangs. Is this a bug or am I doing something silly? If it's the former case I'll send a PR to regress/ with the code below.

#!/usr/bin/env python

import unicorn
CODE_ADDR = 0x10101000
CODE = b'\xff\xe3'
mu = unicorn.Uc(unicorn.UC_ARCH_X86, unicorn.UC_MODE_32)
mu.mem_map(CODE_ADDR, 1024 * 4)
mu.mem_write(CODE_ADDR, CODE)
mu.reg_write(unicorn.x86_const.UC_X86_REG_EBX, 0x0)

print "jmp ebx, with ebx == 0"
try:
    mu.emu_start(CODE_ADDR, CODE_ADDR + 2, count=1)
except unicorn.UcError as e:
    print "Error: %s" % e

mu = unicorn.Uc(unicorn.UC_ARCH_X86, unicorn.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.mem_write(CODE_ADDR, CODE)
print "jmp ebx, with ebx == 0xaa96a47f"
try:
    mu.emu_start(CODE_ADDR, CODE_ADDR + 2, count=1)
except unicorn.UcError as e:
    print "Error: %s" % e

_timeout_fn() still being called after emulation finishes

I modified samples/sample_arm.c to use a 5-second timeout when calling uc_emu_start(). This caused it to segfault intermittently while calling uc_emu_start() for the second emulation session (the one in test_thumb()).

It looks like this happened because the emu timer tried to call uc_emu_stop() on the previous emulation session (the one in test_arm()), which had already been cleaned up by that point. This led to a null dereference in cpu_exit() looking pretty similar to #65, but with a different cause:

Thread 3 (Thread 0x7fffdfbff700 (LWP 15210)):
#0  0x00007fffe338e391 in cpu_exit (cpu=0x0) at qom/cpu.c:112
#1  0x00007fffe3a04389 in uc_emu_stop (handle=0x603010) at ../uc.c:570
#2  0x00007fffe3a0404c in _timeout_fn (arg=0x603010) at ../uc.c:457
#3  0x00007fffe2435182 in start_thread (arg=0x7fffdfbff700) at pthread_create.c:312
#4  0x00007fffe2f9147d in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:111

Thread 1 (Thread 0x7ffff7fde740 (LWP 15208)):
#0  0x00007fffe33d6abf in arm_feature_arm (env=0x7a1840, feature=0xe) at /home/gaffe/repos/unicorn/qemu/target-arm/cpu.h:755
#1  0x00007fffe33dc9ab in register_cp_regs_for_features_arm (cpu=0x799610) at /home/gaffe/repos/unicorn/qemu/target-arm/helper.c:2992
#2  0x00007fffe342ad4d in arm_cpu_realizefn_arm (uc=0x603010, dev=0x799610, errp=0x7fffffffe060) at /home/gaffe/repos/unicorn/qemu/target-arm/cpu.c:393
#3  0x00007fffe3393d18 in device_set_realized (uc=0x603010, obj=0x799610, value=0x1, errp=0x7fffffffe210) at hw/core/qdev.c:184
#4  0x00007fffe3392b0f in property_set_bool (uc=0x603010, obj=0x799610, v=0x7ac9d0, opaque=0x62c800, name=0x7fffe3a12380 "realized", errp=0x7fffffffe210) at qom/object.c:1504
#5  0x00007fffe3390ed4 in object_property_set (uc=0x603010, obj=0x799610, v=0x7ac9d0, name=0x7fffe3a12380 "realized", errp=0x7fffffffe210) at qom/object.c:829
#6  0x00007fffe3393503 in object_property_set_qobject (uc=0x603010, obj=0x799610, value=0x631bc0, name=0x7fffe3a12380 "realized", errp=0x7fffffffe210) at qom/qom-qobject.c:24
#7  0x00007fffe339123a in object_property_set_bool (uc=0x603010, obj=0x799610, value=0x1, name=0x7fffe3a12380 "realized", errp=0x7fffffffe210) at qom/object.c:897
#8  0x00007fffe338e126 in cpu_generic_init (uc=0x603010, typename=0x7fffe3a183d0 "arm-cpu", cpu_model=0x7fffe3a1aff2 "pxa255") at qom/cpu.c:66
#9  0x00007fffe33dcee5 in cpu_arm_init_arm (uc=0x603010, cpu_model=0x7fffe3a1aff2 "pxa255") at /home/gaffe/repos/unicorn/qemu/target-arm/helper.c:3088
#10 0x00007fffe3447ab3 in tosa_init_arm (uc=0x603010, machine=0x617fa0) at /home/gaffe/repos/unicorn/qemu/hw/arm/tosa.c:22
#11 0x00007fffe338da2b in machine_initialize (uc=0x603010) at vl.c:136
#12 0x00007fffe3a038e1 in uc_open (arch=UC_ARCH_ARM, mode=UC_MODE_THUMB, handle=0x7fffffffe378) at ../uc.c:236
#13 0x0000000000400c1f in test_thumb ()
#14 0x0000000000400da5 in main ()
#15 0x00007fffe2eb8ec5 in __libc_start_main (main=0x400d7e <main>, argc=0x1, argv=0x7fffffffe4a8, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>,
    stack_end=0x7fffffffe498) at libc-start.c:287
#16 0x00000000004008d9 in _start ()

It sounds like there should be a check in uc_emu_start() to see if emulation ends before the emu timer fires. If that does happen, uc_emu_start() should disable the timer so that it won't call _timeout_fn() after emulation is already over.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.