diff --git a/.gitignore b/.gitignore index b184113c..b2f4e815 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,8 @@ *.so.* *.exe *.dll +*.class +*.jar qemu/config-all-devices.mak @@ -125,12 +127,16 @@ mips_delay_slot_code_hook threaded_emu_start emu_stop_in_hook_overrun mips_branch_likely_issue +emu_clear_errors test_mem_map_ptr test_mem_high rw_hookstack hook_extrainvoke sysenter_hook_x86 +test_tb_x86 +test_multihook +test_pc_change memleak_x86 memleak_arm diff --git a/Makefile b/Makefile index 4d7e4e73..5bd94a93 100644 --- a/Makefile +++ b/Makefile @@ -222,13 +222,13 @@ else endif compile_lib: config qemu/config-host.h-timestamp - rm -rf lib$(LIBNAME)* $(LIBNAME)*.lib $(LIBNAME)*.dll cyg$(LIBNAME)*.dll && cd qemu && $(MAKE) -j 8 + rm -rf lib$(LIBNAME)* $(LIBNAME)*.lib $(LIBNAME)*.dll cyg$(LIBNAME)*.dll && cd qemu && $(MAKE) -j 4 $(MAKE) unicorn cd samples && $(MAKE) clean unicorn: $(LIBRARY) $(ARCHIVE) -$(LIBRARY): $(UC_TARGET_OBJ) uc.o hook.o +$(LIBRARY): $(UC_TARGET_OBJ) uc.o list.o ifeq ($(UNICORN_SHARED),yes) ifeq ($(V),0) $(call log,GEN,$(LIBRARY)) @@ -241,7 +241,7 @@ ifneq (,$(LIBRARY_SYMLINK)) endif endif -$(ARCHIVE): $(UC_TARGET_OBJ) uc.o hook.o +$(ARCHIVE): $(UC_TARGET_OBJ) uc.o list.o ifeq ($(UNICORN_STATIC),yes) ifeq ($(V),0) $(call log,GEN,$(ARCHIVE)) diff --git a/README.md b/README.md index 52040da6..942e1b8e 100644 --- a/README.md +++ b/README.md @@ -18,13 +18,32 @@ Unicorn offers some unparalleled features: Further information is available at http://www.unicorn-engine.org -Compilation ------------ - -See [COMPILE.TXT](COMPILE.TXT) file for how to compile and install Unicorn. - - License ------- This project is released under the [GPL license](COPYING). + + +Compilation & Docs +------------------ + +See [COMPILE.TXT](COMPILE.TXT) file for how to compile and install Unicorn. + +More documentation is available in [docs/README.md](docs/README.md). + + +Contact +------- + +[Contact us](http://www.unicorn-engine.org/contact/) via mailing list, email or twitter for any questions. + + +Contribute +---------- + +If you want to contribute, please pick up something from our [Github issues](https://github.com/unicorn-engine/unicorn/issues). + +We also maintain a list of more challenged problems in a [TODO list](https://github.com/unicorn-engine/unicorn/wiki/TODO). + +[CREDITS.TXT](CREDITS.TXT) records important contributors of our project. + diff --git a/bindings/const_generator.py b/bindings/const_generator.py index 7adafe33..826340b2 100644 --- a/bindings/const_generator.py +++ b/bindings/const_generator.py @@ -116,7 +116,8 @@ def gen(lang): if f[0].startswith("UC_" + prefix.upper()): if len(f) > 1 and f[1] not in ('//', '='): - print("Error: Unable to convert %s" % f) + print("WARNING: Unable to convert %s" % f) + print(" Line =", line) continue elif len(f) > 1 and f[1] == '=': rhs = ''.join(f[2:]) diff --git a/bindings/dotnet/UnicornManaged/Const/Common.fs b/bindings/dotnet/UnicornManaged/Const/Common.fs index 936b6d90..6f106a3e 100644 --- a/bindings/dotnet/UnicornManaged/Const/Common.fs +++ b/bindings/dotnet/UnicornManaged/Const/Common.fs @@ -21,22 +21,26 @@ module Common = let UC_ARCH_MAX = 8 let UC_MODE_LITTLE_ENDIAN = 0 + let UC_MODE_BIG_ENDIAN = 1073741824 let UC_MODE_ARM = 0 - let UC_MODE_16 = 2 - let UC_MODE_32 = 4 - let UC_MODE_64 = 8 let UC_MODE_THUMB = 16 let UC_MODE_MCLASS = 32 let UC_MODE_V8 = 64 let UC_MODE_MICRO = 16 let UC_MODE_MIPS3 = 32 let UC_MODE_MIPS32R6 = 64 - let UC_MODE_V9 = 16 - let UC_MODE_QPX = 16 - let UC_MODE_BIG_ENDIAN = 1073741824 let UC_MODE_MIPS32 = 4 let UC_MODE_MIPS64 = 8 + let UC_MODE_16 = 2 + let UC_MODE_32 = 4 + let UC_MODE_64 = 8 + let UC_MODE_PPC32 = 4 + let UC_MODE_PPC64 = 8 + let UC_MODE_QPX = 16 + let UC_MODE_SPARC32 = 4 + let UC_MODE_SPARC64 = 8 + let UC_MODE_V9 = 16 let UC_ERR_OK = 0 let UC_ERR_NOMEM = 1 @@ -87,6 +91,8 @@ module Common = let UC_HOOK_MEM_WRITE_INVALID = 288 let UC_HOOK_MEM_FETCH_INVALID = 576 let UC_HOOK_MEM_INVALID = 1008 + let UC_HOOK_MEM_VALID = 7168 + let UC_QUERY_MODE = 1 let UC_PROT_NONE = 0 let UC_PROT_READ = 1 diff --git a/bindings/go/unicorn/unicorn_const.go b/bindings/go/unicorn/unicorn_const.go index 38ba0c8b..8d634be9 100644 --- a/bindings/go/unicorn/unicorn_const.go +++ b/bindings/go/unicorn/unicorn_const.go @@ -16,22 +16,26 @@ const ( ARCH_MAX = 8 MODE_LITTLE_ENDIAN = 0 + MODE_BIG_ENDIAN = 1073741824 MODE_ARM = 0 - MODE_16 = 2 - MODE_32 = 4 - MODE_64 = 8 MODE_THUMB = 16 MODE_MCLASS = 32 MODE_V8 = 64 MODE_MICRO = 16 MODE_MIPS3 = 32 MODE_MIPS32R6 = 64 - MODE_V9 = 16 - MODE_QPX = 16 - MODE_BIG_ENDIAN = 1073741824 MODE_MIPS32 = 4 MODE_MIPS64 = 8 + MODE_16 = 2 + MODE_32 = 4 + MODE_64 = 8 + MODE_PPC32 = 4 + MODE_PPC64 = 8 + MODE_QPX = 16 + MODE_SPARC32 = 4 + MODE_SPARC64 = 8 + MODE_V9 = 16 ERR_OK = 0 ERR_NOMEM = 1 @@ -82,6 +86,8 @@ const ( HOOK_MEM_WRITE_INVALID = 288 HOOK_MEM_FETCH_INVALID = 576 HOOK_MEM_INVALID = 1008 + HOOK_MEM_VALID = 7168 + QUERY_MODE = 1 PROT_NONE = 0 PROT_READ = 1 diff --git a/bindings/java/samples/Sample_mips.java b/bindings/java/samples/Sample_mips.java index 58191acd..8220c42c 100644 --- a/bindings/java/samples/Sample_mips.java +++ b/bindings/java/samples/Sample_mips.java @@ -113,7 +113,7 @@ public class Sample_mips { System.out.print("Emulate MIPS code (little-endian)\n"); // Initialize emulator in MIPS mode - Unicorn u = new Unicorn(Unicorn.UC_ARCH_MIPS, Unicorn.UC_MODE_MIPS32); + Unicorn u = new Unicorn(Unicorn.UC_ARCH_MIPS, Unicorn.UC_MODE_MIPS32 + Unicorn.UC_MODE_LITTLE_ENDIAN); // map 2MB memory for this emulation u.mem_map(ADDRESS, 2 * 1024 * 1024, Unicorn.UC_PROT_ALL); diff --git a/bindings/java/samples/Sample_sparc.java b/bindings/java/samples/Sample_sparc.java index 60298979..71ebdb3f 100644 --- a/bindings/java/samples/Sample_sparc.java +++ b/bindings/java/samples/Sample_sparc.java @@ -76,7 +76,7 @@ public class Sample_sparc { System.out.print("Emulate SPARC code\n"); // Initialize emulator in Sparc mode - Unicorn u = new Unicorn(Unicorn.UC_ARCH_SPARC, Unicorn.UC_MODE_32); + Unicorn u = new Unicorn(Unicorn.UC_ARCH_SPARC, Unicorn.UC_MODE_32 + Unicorn.UC_MODE_BIG_ENDIAN); // map 2MB memory for this emulation u.mem_map(ADDRESS, 2 * 1024 * 1024, Unicorn.UC_PROT_ALL); diff --git a/bindings/java/unicorn/UnicornConst.java b/bindings/java/unicorn/UnicornConst.java index 2bd8eabe..0fe4f193 100644 --- a/bindings/java/unicorn/UnicornConst.java +++ b/bindings/java/unicorn/UnicornConst.java @@ -18,22 +18,26 @@ public interface UnicornConst { public static final int UC_ARCH_MAX = 8; public static final int UC_MODE_LITTLE_ENDIAN = 0; + public static final int UC_MODE_BIG_ENDIAN = 1073741824; public static final int UC_MODE_ARM = 0; - public static final int UC_MODE_16 = 2; - public static final int UC_MODE_32 = 4; - public static final int UC_MODE_64 = 8; public static final int UC_MODE_THUMB = 16; public static final int UC_MODE_MCLASS = 32; public static final int UC_MODE_V8 = 64; public static final int UC_MODE_MICRO = 16; public static final int UC_MODE_MIPS3 = 32; public static final int UC_MODE_MIPS32R6 = 64; - public static final int UC_MODE_V9 = 16; - public static final int UC_MODE_QPX = 16; - public static final int UC_MODE_BIG_ENDIAN = 1073741824; public static final int UC_MODE_MIPS32 = 4; public static final int UC_MODE_MIPS64 = 8; + public static final int UC_MODE_16 = 2; + public static final int UC_MODE_32 = 4; + public static final int UC_MODE_64 = 8; + public static final int UC_MODE_PPC32 = 4; + public static final int UC_MODE_PPC64 = 8; + public static final int UC_MODE_QPX = 16; + public static final int UC_MODE_SPARC32 = 4; + public static final int UC_MODE_SPARC64 = 8; + public static final int UC_MODE_V9 = 16; public static final int UC_ERR_OK = 0; public static final int UC_ERR_NOMEM = 1; @@ -84,6 +88,8 @@ public interface UnicornConst { public static final int UC_HOOK_MEM_WRITE_INVALID = 288; public static final int UC_HOOK_MEM_FETCH_INVALID = 576; public static final int UC_HOOK_MEM_INVALID = 1008; + public static final int UC_HOOK_MEM_VALID = 7168; + public static final int UC_QUERY_MODE = 1; public static final int UC_PROT_NONE = 0; public static final int UC_PROT_READ = 1; diff --git a/bindings/msvc/samples/main.c b/bindings/msvc/samples/main.c index c9a24001..1539d68c 100644 --- a/bindings/msvc/samples/main.c +++ b/bindings/msvc/samples/main.c @@ -75,7 +75,7 @@ int main(int argc, char **argv, char **envp) #endif // Initialize emulator in MIPS 32bit little endian mode - err = uc_open(UC_ARCH_MIPS, UC_MODE_MIPS32, &uc); + err = uc_open(UC_ARCH_MIPS, UC_MODE_MIPS32 | UC_MODE_LITTLE_ENDIAN, &uc); if (err) { printf("Failed on uc_open() with error returned: %u\n", err); diff --git a/bindings/python/sample_network_auditing.py b/bindings/python/sample_network_auditing.py index 66e575ce..69a2d2d7 100755 --- a/bindings/python/sample_network_auditing.py +++ b/bindings/python/sample_network_auditing.py @@ -7,7 +7,6 @@ from unicorn import * from unicorn.x86_const import * import struct import uuid -import random SIZE_REG = 4 SOCKETCALL_MAX_ARGS = 3 @@ -51,10 +50,11 @@ X86_REVERSE_TCP_2 = b"\x31\xc0\x31\xdb\x31\xc9\x31\xd2\xb0\x66\xb3\x01\x51\x6a\x # memory address where emulation starts ADDRESS = 0x1000000 + # supported classes class IdGenerator: def __init__(self): - self.__next_id = 3 # exclude sdtin, stdout, stderr + self.__next_id = 3 # exclude sdtin, stdout, stderr def next(self): next_id = self.__next_id @@ -63,6 +63,7 @@ class IdGenerator: return next_id + class LogChain: def __init__(self): self.__chains = {} @@ -72,11 +73,11 @@ class LogChain: self.__chains = {} self.__linking_fds = {} - def create_chain(self, id): - if not self.__chains.has_key(id): - self.__chains[id] = [] + def create_chain(self, my_id): + if not my_id in self.__chains: + self.__chains[my_id] = [] else: - print("LogChain: id %d existed" % id) + print("LogChain: id %d existed" % my_id) def add_log(self, id, msg): fd = self.get_original_fd(id) @@ -87,20 +88,20 @@ class LogChain: print("LogChain: id %d doesn't exist" % id) def link_fd(self, from_fd, to_fd): - if not self.__linking_fds.has_key(to_fd): + if not to_fd in self.__linking_fds: self.__linking_fds[to_fd] = [] self.__linking_fds[to_fd].append(from_fd) def get_original_fd(self, fd): - if self.__chains.has_key(fd): + if fd in self.__chains: return fd - for orig_fd, links in self.__linking_fds.iteritems(): + for orig_fd, links in self.__linking_fds.items(): if fd in links: return orig_fd - return None + return None def print_report(self): print(""" @@ -108,10 +109,11 @@ class LogChain: | START REPORT | ---------------- """) - for id, logs in self.__chains.iteritems(): - print("---- START FD(%d) ----" % id) + + for my_id, logs in self.__chains.items(): + print("---- START FD(%d) ----" % my_id) print("\n".join(logs)) - print("---- END FD(%d) ----" % id) + print("---- END FD(%d) ----" % my_id) print(""" -------------- @@ -119,10 +121,9 @@ class LogChain: -------------- """) + # end supported classes -id_gen = IdGenerator() -fd_chains = LogChain() # utilities def bin_to_ipv4(ip): @@ -132,6 +133,7 @@ def bin_to_ipv4(ip): (ip & 0xff00) >> 8, (ip & 0xff)) + def read_string(uc, addr): ret = "" @@ -140,36 +142,43 @@ def read_string(uc, addr): while c != 0x0: ret += chr(c) - c = uc.mem_read(addr+read_bytes, 1)[0] + c = uc.mem_read(addr + read_bytes, 1)[0] read_bytes += 1 return ret + def parse_sock_address(sock_addr): sin_family, = struct.unpack("HI", sock_addr[2:8]) + + if sin_family == 2: # AF_INET + port, host = struct.unpack(">HI", sock_addr[2:8]) return "%s:%d" % (bin_to_ipv4(host), port) - elif sin_family == 6: # AF_INET6 + elif sin_family == 6: # AF_INET6 return "" + def print_sockcall(msg): print(">>> SOCKCALL %s" % msg) + + # end utilities # 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(">>> Instruction code at [0x%x] =" %(address), end="") + print(">>> Instruction code at [0x%x] =" % (address), end="") for i in tmp: - print(" %x" %i, end="") + print(" %x" % i, end="") print("") + # callback for tracing Linux interrupt def hook_intr(uc, intno, user_data): + global id_gen + # only handle Linux syscall if intno != 0x80: return @@ -182,17 +191,17 @@ def hook_intr(uc, intno, user_data): # print(">>> INTERRUPT %d" % eax) - if eax == 1: # sys_exit + if eax == 1: # sys_exit print(">>> SYS_EXIT") uc.emu_stop() - elif eax == 3: # sys_read + elif eax == 3: # sys_read fd = ebx buf = ecx count = edx dummy_content = str(uuid.uuid1())[:32] if len(dummy_content) > count: - dummy_content = dummy_content[:count] + dummy_content = dummy_content[:count] uc.mem_write(buf, dummy_content) @@ -200,7 +209,7 @@ def hook_intr(uc, intno, user_data): fd_chains.add_log(fd, msg) print(">>> %s" % msg) - elif eax == 4: # sys_write + elif eax == 4: # sys_write fd = ebx buf = ecx count = edx @@ -211,13 +220,13 @@ def hook_intr(uc, intno, user_data): print(">>> %s" % msg) fd_chains.add_log(fd, msg) - elif eax == 5: # sys_open + elif eax == 5: # sys_open filename_addr = ebx flags = ecx mode = edx filename = read_string(uc, filename_addr) - dummy_fd = id_gen.next() + dummy_fd = id_gen.next() uc.reg_write(UC_X86_REG_EAX, dummy_fd) msg = "open file (filename=%s flags=%d mode=%d) with fd(%d)" % (filename, flags, mode, dummy_fd) @@ -225,42 +234,42 @@ def hook_intr(uc, intno, user_data): fd_chains.create_chain(dummy_fd) fd_chains.add_log(dummy_fd, msg) print(">>> %s" % msg) - elif eax == 11: # sys_execv + elif eax == 11: # sys_execv # print(">>> ebx=0x%x, ecx=0x%x, edx=0x%x" % (ebx, ecx, edx)) filename = read_string(uc, ebx) print(">>> SYS_EXECV filename=%s" % filename) - elif eax == 63: # sys_dup2 + elif eax == 63: # sys_dup2 fd_chains.link_fd(ecx, ebx) print(">>> SYS_DUP2 oldfd=%d newfd=%d" % (ebx, ecx)) - elif eax == 102: # sys_socketcall + elif eax == 102: # sys_socketcall # ref: http://www.skyfree.org/linux/kernel_network/socket.html call = uc.reg_read(UC_X86_REG_EBX) args = uc.reg_read(UC_X86_REG_ECX) SOCKETCALL_NUM_ARGS = { - 1: 3, # sys_socket - 2: 3, # sys_bind - 3: 3, # sys_connect - 4: 2, # sys_listen - 5: 3, # sys_accept - 9: 4, # sys_send + 1: 3, # sys_socket + 2: 3, # sys_bind + 3: 3, # sys_connect + 4: 2, # sys_listen + 5: 3, # sys_accept + 9: 4, # sys_send 11: 4, # sys_receive - 13: 2 # sys_shutdown + 13: 2 # sys_shutdown } - buf = uc.mem_read(args, SOCKETCALL_NUM_ARGS[call]*SIZE_REG) - args = struct.unpack("<" + "I"*SOCKETCALL_NUM_ARGS[call], buf) + buf = uc.mem_read(args, SOCKETCALL_NUM_ARGS[call] * SIZE_REG) + args = struct.unpack("<" + "I" * SOCKETCALL_NUM_ARGS[call], buf) # int sys_socketcall(int call, unsigned long *args) - if call == 1: # sys_socket + if call == 1: # sys_socket # err = sys_socket(a0,a1,a[2]) # int sys_socket(int family, int type, int protocol) family = args[0] sock_type = args[1] protocol = args[2] - dummy_fd = id_gen.next() + dummy_fd = id_gen.next() uc.reg_write(UC_X86_REG_EAX, dummy_fd) if family == 2: # AF_INET @@ -269,10 +278,10 @@ def hook_intr(uc, intno, user_data): fd_chains.create_chain(dummy_fd) fd_chains.add_log(dummy_fd, msg) print_sockcall(msg) - elif family == 3: # AF_INET6 + elif family == 3: # AF_INET6 pass - elif call == 2: # sys_bind + elif call == 2: # sys_bind fd = args[0] umyaddr = args[1] addrlen = args[2] @@ -283,19 +292,19 @@ def hook_intr(uc, intno, user_data): fd_chains.add_log(fd, msg) print_sockcall(msg) - elif call == 3: # sys_connect + elif call == 3: # sys_connect # err = sys_connect(a0, (struct sockaddr *)a1, a[2]) # int sys_connect(int fd, struct sockaddr *uservaddr, int addrlen) fd = args[0] uservaddr = args[1] addrlen = args[2] - sock_addr = uc.mem_read(uservaddr, addrlen) + sock_addr = uc.mem_read(uservaddr, addrlen) msg = "fd(%d) connect to %s" % (fd, parse_sock_address(sock_addr)) fd_chains.add_log(fd, msg) print_sockcall(msg) - elif call == 4: # sys_listen + elif call == 4: # sys_listen fd = args[0] backlog = args[1] @@ -303,7 +312,7 @@ def hook_intr(uc, intno, user_data): fd_chains.add_log(fd, msg) print_sockcall(msg) - elif call == 5: # sys_accept + elif call == 5: # sys_accept fd = args[0] upeer_sockaddr = args[1] upeer_addrlen = args[2] @@ -321,7 +330,7 @@ def hook_intr(uc, intno, user_data): fd_chains.add_log(fd, msg) print_sockcall(msg) - elif call == 9: # sys_send + elif call == 9: # sys_send fd = args[0] buff = args[1] length = args[2] @@ -332,7 +341,7 @@ def hook_intr(uc, intno, user_data): fd_chains.add_log(fd, msg) print_sockcall(msg) - elif call == 11: # sys_receive + elif call == 11: # sys_receive fd = args[0] ubuf = args[1] size = args[2] @@ -342,7 +351,7 @@ def hook_intr(uc, intno, user_data): fd_chains.add_log(fd, msg) print_sockcall(msg) - elif call == 13: # sys_shutdown + elif call == 13: # sys_shutdown fd = args[0] how = args[1] @@ -350,8 +359,11 @@ def hook_intr(uc, intno, user_data): fd_chains.add_log(fd, msg) print_sockcall(msg) + # Test X86 32 bit def test_i386(code): + global fd_chains + fd_chains.clean() print("Emulate i386 code") try: @@ -366,7 +378,7 @@ def test_i386(code): # initialize stack mu.reg_write(UC_X86_REG_ESP, ADDRESS + 0x200000) - + # tracing all instructions with customized callback # mu.hook_add(UC_HOOK_CODE, hook_code) @@ -384,9 +396,13 @@ def test_i386(code): fd_chains.print_report() + +# Globals +fd_chains = LogChain() +id_gen = IdGenerator() + if __name__ == '__main__': - test_i386(X86_SEND_ETCPASSWD) + test_i386(X86_SEND_ETCPASSWD) test_i386(X86_BIND_TCP) test_i386(X86_REVERSE_TCP) test_i386(X86_REVERSE_TCP_2) - diff --git a/bindings/python/sample_sparc.py b/bindings/python/sample_sparc.py index 307ba936..ad47d387 100755 --- a/bindings/python/sample_sparc.py +++ b/bindings/python/sample_sparc.py @@ -28,7 +28,7 @@ def test_sparc(): print("Emulate SPARC code") try: # Initialize emulator in SPARC EB mode - mu = Uc(UC_ARCH_SPARC, UC_MODE_32) + 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) diff --git a/bindings/python/unicorn/unicorn.py b/bindings/python/unicorn/unicorn.py index 08be9632..9fd9a896 100644 --- a/bindings/python/unicorn/unicorn.py +++ b/bindings/python/unicorn/unicorn.py @@ -314,25 +314,7 @@ class Uc(object): self._callbacks[self._callback_count] = (callback, user_data) cb = None - if htype in (UC_HOOK_BLOCK, UC_HOOK_CODE): - begin = ctypes.c_uint64(arg1) - end = ctypes.c_uint64(arg2) - # set callback with wrapper, so it can be called - # with this object as param - cb = ctypes.cast(UC_HOOK_CODE_CB(self._hookcode_cb), UC_HOOK_CODE_CB) - status = _uc.uc_hook_add(self._uch, ctypes.byref(_h2), htype, cb, \ - ctypes.cast(self._callback_count, ctypes.c_void_p), begin, end) - elif htype & UC_HOOK_MEM_READ_UNMAPPED or htype & UC_HOOK_MEM_WRITE_UNMAPPED or \ - htype & UC_HOOK_MEM_FETCH_UNMAPPED or htype & UC_HOOK_MEM_READ_PROT or \ - htype & UC_HOOK_MEM_WRITE_PROT or htype & UC_HOOK_MEM_FETCH_PROT: - cb = ctypes.cast(UC_HOOK_MEM_INVALID_CB(self._hook_mem_invalid_cb), UC_HOOK_MEM_INVALID_CB) - status = _uc.uc_hook_add(self._uch, ctypes.byref(_h2), htype, \ - cb, ctypes.cast(self._callback_count, ctypes.c_void_p)) - elif htype in (UC_HOOK_MEM_READ, UC_HOOK_MEM_WRITE, UC_HOOK_MEM_READ | UC_HOOK_MEM_WRITE): - cb = ctypes.cast(UC_HOOK_MEM_ACCESS_CB(self._hook_mem_access_cb), UC_HOOK_MEM_ACCESS_CB) - status = _uc.uc_hook_add(self._uch, ctypes.byref(_h2), htype, \ - cb, ctypes.cast(self._callback_count, ctypes.c_void_p)) - elif htype == UC_HOOK_INSN: + if htype == UC_HOOK_INSN: insn = ctypes.c_int(arg1) if arg1 == x86_const.UC_X86_INS_IN: # IN instruction cb = ctypes.cast(UC_HOOK_INSN_IN_CB(self._hook_insn_in_cb), UC_HOOK_INSN_IN_CB) @@ -346,6 +328,25 @@ class Uc(object): cb = ctypes.cast(UC_HOOK_INTR_CB(self._hook_intr_cb), UC_HOOK_INTR_CB) status = _uc.uc_hook_add(self._uch, ctypes.byref(_h2), htype, \ cb, ctypes.cast(self._callback_count, ctypes.c_void_p)) + else: + begin = ctypes.c_uint64(arg1) + end = ctypes.c_uint64(arg2) + if htype in (UC_HOOK_BLOCK, UC_HOOK_CODE): + # set callback with wrapper, so it can be called + # with this object as param + cb = ctypes.cast(UC_HOOK_CODE_CB(self._hookcode_cb), UC_HOOK_CODE_CB) + status = _uc.uc_hook_add(self._uch, ctypes.byref(_h2), htype, cb, \ + ctypes.cast(self._callback_count, ctypes.c_void_p), begin, end) + elif htype & UC_HOOK_MEM_READ_UNMAPPED or htype & UC_HOOK_MEM_WRITE_UNMAPPED or \ + htype & UC_HOOK_MEM_FETCH_UNMAPPED or htype & UC_HOOK_MEM_READ_PROT or \ + htype & UC_HOOK_MEM_WRITE_PROT or htype & UC_HOOK_MEM_FETCH_PROT: + cb = ctypes.cast(UC_HOOK_MEM_INVALID_CB(self._hook_mem_invalid_cb), UC_HOOK_MEM_INVALID_CB) + status = _uc.uc_hook_add(self._uch, ctypes.byref(_h2), htype, \ + cb, ctypes.cast(self._callback_count, ctypes.c_void_p)) + else: + cb = ctypes.cast(UC_HOOK_MEM_ACCESS_CB(self._hook_mem_access_cb), UC_HOOK_MEM_ACCESS_CB) + status = _uc.uc_hook_add(self._uch, ctypes.byref(_h2), htype, \ + cb, ctypes.cast(self._callback_count, ctypes.c_void_p)) # save the ctype function so gc will leave it alone. self._ctype_cbs[self._callback_count] = cb diff --git a/bindings/python/unicorn/unicorn_const.py b/bindings/python/unicorn/unicorn_const.py index 01cde40c..4e942ef5 100644 --- a/bindings/python/unicorn/unicorn_const.py +++ b/bindings/python/unicorn/unicorn_const.py @@ -14,22 +14,26 @@ UC_ARCH_M68K = 7 UC_ARCH_MAX = 8 UC_MODE_LITTLE_ENDIAN = 0 +UC_MODE_BIG_ENDIAN = 1073741824 UC_MODE_ARM = 0 -UC_MODE_16 = 2 -UC_MODE_32 = 4 -UC_MODE_64 = 8 UC_MODE_THUMB = 16 UC_MODE_MCLASS = 32 UC_MODE_V8 = 64 UC_MODE_MICRO = 16 UC_MODE_MIPS3 = 32 UC_MODE_MIPS32R6 = 64 -UC_MODE_V9 = 16 -UC_MODE_QPX = 16 -UC_MODE_BIG_ENDIAN = 1073741824 UC_MODE_MIPS32 = 4 UC_MODE_MIPS64 = 8 +UC_MODE_16 = 2 +UC_MODE_32 = 4 +UC_MODE_64 = 8 +UC_MODE_PPC32 = 4 +UC_MODE_PPC64 = 8 +UC_MODE_QPX = 16 +UC_MODE_SPARC32 = 4 +UC_MODE_SPARC64 = 8 +UC_MODE_V9 = 16 UC_ERR_OK = 0 UC_ERR_NOMEM = 1 @@ -80,6 +84,8 @@ UC_HOOK_MEM_READ_INVALID = 144 UC_HOOK_MEM_WRITE_INVALID = 288 UC_HOOK_MEM_FETCH_INVALID = 576 UC_HOOK_MEM_INVALID = 1008 +UC_HOOK_MEM_VALID = 7168 +UC_QUERY_MODE = 1 UC_PROT_NONE = 0 UC_PROT_READ = 1 diff --git a/docs/README b/docs/README.md similarity index 100% rename from docs/README rename to docs/README.md diff --git a/hook.c b/hook.c deleted file mode 100644 index 6e575d83..00000000 --- a/hook.c +++ /dev/null @@ -1,279 +0,0 @@ -/* Unicorn Emulator Engine */ -/* By Nguyen Anh Quynh , 2015 */ - -#include "uc_priv.h" -#include "hook.h" - - -// return index for a new hook entry in hook_callbacks[] array. -// this realloc memory if needed. -size_t hook_find_new(struct uc_struct *uc) -{ - size_t i; - struct hook_struct *new; - - // find the first free slot. skip slot 0, so index > 0 - for(i = 1; i < uc->hook_size; i++) { - if (uc->hook_callbacks[i].callback == NULL) { - return i; - } - } - - // not found, so the array is full. - // we have to realloc hook_callbacks[] to contain new hooks - new = realloc(uc->hook_callbacks, - (uc->hook_size + HOOK_SIZE) * sizeof(uc->hook_callbacks[0])); - if (!new) // OOM ? - return 0; - - // reset the newly added slots - memset(new + uc->hook_size, 0, HOOK_SIZE * sizeof(uc->hook_callbacks[0])); - - uc->hook_callbacks = new; - uc->hook_size += HOOK_SIZE; - - // return the first newly allocated slot - return uc->hook_size - HOOK_SIZE; -} - -// return -1 on failure, index to hook_callbacks[] on success. -size_t hook_add(struct uc_struct *uc, int type, uint64_t begin, uint64_t end, void *callback, void *user_data) -{ - int i; - - // find the first free slot. skip slot 0, so index > 0 - i = hook_find_new(uc); - if (i) { - uc->hook_callbacks[i].hook_type = type; - uc->hook_callbacks[i].begin = begin; - uc->hook_callbacks[i].end = end; - uc->hook_callbacks[i].callback = callback; - uc->hook_callbacks[i].user_data = user_data; - - switch(type) { - default: break; - case UC_HOOK_BLOCK: - uc->hook_block = true; - if (begin > end) - uc->hook_block_idx = i; - break; - case UC_HOOK_CODE: - uc->hook_insn = true; - if (begin > end) - uc->hook_insn_idx = i; - break; - case UC_HOOK_MEM_READ: - uc->hook_mem_read = true; - if (begin > end) - uc->hook_read_idx = i; - break; - case UC_HOOK_MEM_WRITE: - uc->hook_mem_write = true; - if (begin > end) - uc->hook_write_idx = i; - break; - case UC_HOOK_MEM_READ | UC_HOOK_MEM_WRITE: - uc->hook_mem_read = true; - uc->hook_mem_write = true; - if (begin > end) { - uc->hook_read_idx = i; - uc->hook_write_idx = i; - } - break; - } - - return i; - } - - // not found - return 0; -} - -// return 0 on success, -1 on failure -uc_err hook_del(struct uc_struct *uc, uc_hook hh) -{ - if (hh == uc->hook_block_idx) { - uc->hook_block_idx = 0; - } - - if (hh == uc->hook_insn_idx) { - uc->hook_insn_idx = 0; - } - - if (hh == uc->hook_read_idx) { - uc->hook_read_idx = 0; - } - - if (hh == uc->hook_write_idx) { - uc->hook_write_idx = 0; - } - - if (hh == uc->hook_mem_read_idx) { - uc->hook_mem_read_idx = 0; - } - - if (hh == uc->hook_mem_write_idx) { - uc->hook_mem_write_idx = 0; - } - - if (hh == uc->hook_mem_fetch_idx) { - uc->hook_mem_fetch_idx = 0; - } - - if (hh == uc->hook_mem_read_prot_idx) { - uc->hook_mem_read_prot_idx = 0; - } - - if (hh == uc->hook_mem_write_prot_idx) { - uc->hook_mem_write_prot_idx = 0; - } - - if (hh == uc->hook_mem_fetch_prot_idx) { - uc->hook_mem_fetch_prot_idx = 0; - } - - if (hh == uc->hook_intr_idx) { - uc->hook_intr_idx = 0; - } - - if (hh == uc->hook_out_idx) { - uc->hook_out_idx = 0; - } - - if (hh == uc->hook_in_idx) { - uc->hook_in_idx = 0; - } - - if(hh == uc->hook_syscall_idx) { - uc->hook_syscall_idx = 0; - } - - uc->hook_callbacks[hh].callback = NULL; - uc->hook_callbacks[hh].user_data = NULL; - uc->hook_callbacks[hh].hook_type = 0; - uc->hook_callbacks[hh].begin = 0; - uc->hook_callbacks[hh].end = 0; - - return UC_ERR_OK; -} - -// return NULL on failure -static struct hook_struct *_hook_find(struct uc_struct *uc, int type, uint64_t address) -{ - int i; - - switch(type) { - default: break; - case UC_HOOK_BLOCK: - // already hooked all blocks? - if (uc->hook_block_idx) - return &uc->hook_callbacks[uc->hook_block_idx]; - break; - case UC_HOOK_CODE: - // already hooked all the code? - if (uc->hook_insn_idx) - return &uc->hook_callbacks[uc->hook_insn_idx]; - break; - case UC_HOOK_MEM_READ: - // already hooked all memory read? - if (uc->hook_read_idx) { - return &uc->hook_callbacks[uc->hook_read_idx]; - } - break; - case UC_HOOK_MEM_WRITE: - // already hooked all memory write? - if (uc->hook_write_idx) - return &uc->hook_callbacks[uc->hook_write_idx]; - break; - } - - // no trace-all callback - for(i = 1; i < uc->hook_size; i++) { - switch(type) { - default: break; - case UC_HOOK_BLOCK: - case UC_HOOK_CODE: - if (uc->hook_callbacks[i].hook_type == type) { - if (uc->hook_callbacks[i].begin <= address && address <= uc->hook_callbacks[i].end) - return &uc->hook_callbacks[i]; - } - break; - case UC_HOOK_MEM_READ: - if (uc->hook_callbacks[i].hook_type & UC_HOOK_MEM_READ) { - if (uc->hook_callbacks[i].begin <= address && address <= uc->hook_callbacks[i].end) - return &uc->hook_callbacks[i]; - } - break; - case UC_HOOK_MEM_WRITE: - if (uc->hook_callbacks[i].hook_type & UC_HOOK_MEM_WRITE) { - if (uc->hook_callbacks[i].begin <= address && address <= uc->hook_callbacks[i].end) - return &uc->hook_callbacks[i]; - } - break; - } - } - - // not found - return NULL; -} - - -static void hook_count_cb(struct uc_struct *uc, uint64_t address, uint32_t size, void *user_data) -{ - // count this instruction - uc->emu_counter++; - - if (uc->emu_counter > uc->emu_count) - uc_emu_stop(uc); - else if (uc->hook_count_callback) - uc->hook_count_callback(uc, address, size, user_data); -} - -struct hook_struct *hook_find(struct uc_struct *uc, int type, uint64_t address) -{ - // stop executing callbacks if we already got stop request - if (uc->stop_request) - return NULL; - - // UC_HOOK_CODE is special because we may need to count instructions - if (type == UC_HOOK_CODE && uc->emu_count > 0) { - struct hook_struct *st = _hook_find(uc, type, address); - if (st) { - // prepare this struct to pass back to caller - uc->hook_count.hook_type = UC_HOOK_CODE; - uc->hook_count.begin = st->begin; - uc->hook_count.end = st->end; - uc->hook_count.callback = hook_count_cb; - uc->hook_count.user_data = st->user_data; - // save this hook callback so we can call it later - uc->hook_count_callback = st->callback; - } else { - // there is no callback, but we still need to - // handle instruction count - uc->hook_count.hook_type = UC_HOOK_CODE; - uc->hook_count.begin = 1; - uc->hook_count.end = 0; - uc->hook_count.callback = hook_count_cb; - uc->hook_count.user_data = NULL; - uc->hook_count_callback = NULL; // no callback - } - - return &(uc->hook_count); - } else - return _hook_find(uc, type, address); -} - - -// TCG helper -void helper_uc_tracecode(int32_t size, void *callback, void *handle, int64_t address, void *user_data); -void helper_uc_tracecode(int32_t size, void *callback, void *handle, int64_t address, void *user_data) -{ - struct uc_struct *uc = handle; - - // sync PC in CPUArchState with address - if (uc->set_pc) { - uc->set_pc(uc, address); - } - - ((uc_cb_hookcode_t)callback)(uc, address, size, user_data); -} diff --git a/include/hook.h b/include/hook.h deleted file mode 100644 index 6fbdf509..00000000 --- a/include/hook.h +++ /dev/null @@ -1,20 +0,0 @@ -/* Unicorn Emulator Engine */ -/* By Nguyen Anh Quynh , 2015 */ - -#ifndef UC_HOOK_H -#define UC_HOOK_H - -// return -1 on failure, index to traces[] on success. -size_t hook_add(struct uc_struct *uc, int type, uint64_t begin, uint64_t end, void *callback, void *user_data); - -// return 0 on success, -1 on failure -uc_err hook_del(struct uc_struct *uc, uc_hook hh); - -// return NULL on failure -struct hook_struct *hook_find(struct uc_struct *uc, int type, uint64_t address); - -// return index of an free hook entry in hook_callbacks[] array. -// this realloc memory if needed. -size_t hook_find_new(struct uc_struct *uc); - -#endif diff --git a/include/list.h b/include/list.h new file mode 100644 index 00000000..75ec13e2 --- /dev/null +++ b/include/list.h @@ -0,0 +1,20 @@ +#ifndef UC_LLIST_H +#define UC_LLIST_H + +#include + +struct list_item { + struct list_item *next; + void *data; +}; + +struct list { + struct list_item *head, *tail; +}; + +struct list *list_new(void); +void list_clear(struct list *list); +void *list_append(struct list *list, void *data); +bool list_remove(struct list *list, void *data); + +#endif diff --git a/include/uc_priv.h b/include/uc_priv.h index 32616ae0..d9fe1a2d 100644 --- a/include/uc_priv.h +++ b/include/uc_priv.h @@ -9,7 +9,16 @@ #include "qemu.h" #include "unicorn/unicorn.h" -#include "hook.h" +#include "list.h" + +// These are masks of supported modes for each cpu/arch. +// They should be updated when changes are made to the uc_mode enum typedef. +#define UC_MODE_ARM_MASK (UC_MODE_ARM|UC_MODE_THUMB|UC_MODE_LITTLE_ENDIAN) +#define UC_MODE_MIPS_MASK (UC_MODE_MIPS32|UC_MODE_MIPS64|UC_MODE_LITTLE_ENDIAN|UC_MODE_BIG_ENDIAN) +#define UC_MODE_X86_MASK (UC_MODE_16|UC_MODE_32|UC_MODE_64|UC_MODE_LITTLE_ENDIAN) +#define UC_MODE_PPC_MASK (UC_MODE_PPC64|UC_MODE_BIG_ENDIAN) +#define UC_MODE_SPARC_MASK (UC_MODE_SPARC32|UC_MODE_SPARC64|UC_MODE_BIG_ENDIAN) +#define UC_MODE_M68K_MASK (UC_MODE_BIG_ENDIAN) #define ARR_SIZE(a) (sizeof(a)/sizeof(a[0])) @@ -23,6 +32,8 @@ typedef struct ModuleEntry { typedef QTAILQ_HEAD(, ModuleEntry) ModuleTypeList; +typedef uc_err (*query_t)(struct uc_struct *uc, uc_query_type type, size_t *result); + // return 0 on success, -1 on failure typedef int (*reg_read_t)(struct uc_struct *uc, unsigned int regid, void *value); typedef int (*reg_write_t)(struct uc_struct *uc, unsigned int regid, const void *value); @@ -60,16 +71,62 @@ typedef bool (*uc_args_int_t)(int intno); // some architecture redirect virtual memory to physical memory like Mips typedef uint64_t (*uc_mem_redirect_t)(uint64_t address); - -struct hook_struct { - int hook_type; // uc_tracecode_type & uc_tracemem_type - uint64_t begin, end; // range of address to be monitored - void *callback; // either uc_cb_tracecode_t or uc_cb_tracemem_t +struct hook { + int type; // UC_HOOK_* + int insn; // instruction for HOOK_INSN + int refs; // reference count to free hook stored in multiple lists + uint64_t begin, end; // only trigger if PC or memory access is in this address (depends on hook type) + void *callback; // a uc_cb_* type void *user_data; }; -// extend memory to keep 32 more hooks each time -#define HOOK_SIZE 32 +// hook list offsets +// mirrors the order of uc_hook_type from include/unicorn/unicorn.h +enum uc_hook_idx { + UC_HOOK_INTR_IDX, + UC_HOOK_INSN_IDX, + UC_HOOK_CODE_IDX, + UC_HOOK_BLOCK_IDX, + UC_HOOK_MEM_READ_UNMAPPED_IDX, + UC_HOOK_MEM_WRITE_UNMAPPED_IDX, + UC_HOOK_MEM_FETCH_UNMAPPED_IDX, + UC_HOOK_MEM_READ_PROT_IDX, + UC_HOOK_MEM_WRITE_PROT_IDX, + UC_HOOK_MEM_FETCH_PROT_IDX, + UC_HOOK_MEM_READ_IDX, + UC_HOOK_MEM_WRITE_IDX, + UC_HOOK_MEM_FETCH_IDX, + + UC_HOOK_MAX, +}; + +// for loop macro to loop over hook lists +#define HOOK_FOREACH(uc, hh, idx) \ + struct list_item *cur; \ + for ( \ + cur = (uc)->hook[idx##_IDX].head; \ + cur != NULL && ((hh) = (struct hook *)cur->data) \ + /* stop excuting callbacks on stop request */ \ + && !uc->stop_request; \ + cur = cur->next) + +// if statement to check hook bounds +#define HOOK_BOUND_CHECK(hh, addr) \ + ((((addr) >= (hh)->begin && (addr) <= (hh)->end) \ + || (hh)->begin > (hh)->end)) + +#define HOOK_EXISTS(uc, idx) ((uc)->hook[idx##_IDX].head != NULL) +#define HOOK_EXISTS_BOUNDED(uc, idx, addr) _hook_exists_bounded((uc)->hook[idx##_IDX].head, addr) + +static inline bool _hook_exists_bounded(struct list_item *cur, uint64_t addr) +{ + while (cur != NULL) { + if (HOOK_BOUND_CHECK((struct hook *)cur->data, addr)) + return true; + cur = cur->next; + } + return false; +} //relloc increment, KEEP THIS A POWER OF 2! #define MEM_BLOCK_INCR 32 @@ -84,6 +141,7 @@ struct uc_struct { struct CPUTailQ cpus; // qemu/cpu-exec.c uc_err errnum; // qemu/cpu-exec.c AddressSpace as; + query_t query; reg_read_t reg_read; reg_write_t reg_write; reg_reset_t reg_reset; @@ -146,38 +204,20 @@ struct uc_struct { bool apic_report_tpr_access; CPUState *current_cpu; - // all the hook callbacks - size_t hook_size; - struct hook_struct *hook_callbacks; + // linked lists containing hooks per type + struct list hook[UC_HOOK_MAX]; // hook to count number of instructions for uc_emu_start() - struct hook_struct hook_count; - uc_cb_hookcode_t hook_count_callback; + uc_hook count_hook; size_t emu_counter; // current counter of uc_emu_start() size_t emu_count; // save counter of uc_emu_start() - // indexes if hooking ALL block/code/read/write events - unsigned int hook_block_idx, hook_insn_idx, hook_read_idx, hook_write_idx; - // boolean variables for quick check on hooking block, code, memory accesses - bool hook_block, hook_insn, hook_mem_read, hook_mem_write; uint64_t block_addr; // save the last block address we hooked - // indexes to event callbacks - int hook_mem_read_idx; // for handling invalid memory read access on unmapped memory - int hook_mem_write_idx; // for handling invalid memory write access on unmapped memory - int hook_mem_fetch_idx; // for handling invalid memory fetch access on unmapped memory - int hook_mem_read_prot_idx; // for handling invalid memory read access on read-protected memory - int hook_mem_write_prot_idx; // for handling invalid memory write access on write-protected memory - int hook_mem_fetch_prot_idx; // for handling invalid memory fetch access on non-executable memory - - int hook_intr_idx; // for handling interrupt - int hook_out_idx; // for handling OUT instruction (X86) - int hook_in_idx; // for handling IN instruction (X86) - int hook_syscall_idx; // for handling SYSCALL/SYSENTER (X86) - bool init_tcg; // already initialized local TCGv variables? bool stop_request; // request to immediately stop emulation - for uc_emu_stop() + bool quit_request; // request to quit the current TB, but continue to emulate - for uc_mem_protect() bool emulation_done; // emulation is done by uc_emu_start() QemuThread timer; // timer for emulation timeout uint64_t timeout; // timeout for uc_emu_start() diff --git a/include/unicorn/unicorn.h b/include/unicorn/unicorn.h index 719cbcf6..2a556841 100644 --- a/include/unicorn/unicorn.h +++ b/include/unicorn/unicorn.h @@ -60,8 +60,10 @@ typedef size_t uc_hook; #define UC_API_MAJOR 0 #define UC_API_MINOR 9 -// Macro to create combined version which can be compared to -// result of uc_version() API. +/* + Macro to create combined version which can be compared to + result of uc_version() API. +*/ #define UC_MAKE_VERSION(major, minor) ((major << 8) + minor) // Scales to calculate timeout on microsecond unit @@ -76,7 +78,7 @@ typedef enum uc_arch { UC_ARCH_ARM64, // ARM-64, also called AArch64 UC_ARCH_MIPS, // Mips architecture UC_ARCH_X86, // X86 architecture (including x86 & x86-64) - UC_ARCH_PPC, // PowerPC architecture + UC_ARCH_PPC, // PowerPC architecture (currently unsupported) UC_ARCH_SPARC, // Sparc architecture UC_ARCH_M68K, // M68K architecture UC_ARCH_MAX, @@ -84,22 +86,32 @@ typedef enum uc_arch { // Mode type typedef enum uc_mode { - UC_MODE_LITTLE_ENDIAN = 0, // little-endian mode (default mode) - UC_MODE_ARM = 0, // 32-bit ARM - UC_MODE_16 = 1 << 1, // 16-bit mode (X86) - UC_MODE_32 = 1 << 2, // 32-bit mode (X86) - UC_MODE_64 = 1 << 3, // 64-bit mode (X86, PPC) - UC_MODE_THUMB = 1 << 4, // ARM's Thumb mode, including Thumb-2 - UC_MODE_MCLASS = 1 << 5, // ARM's Cortex-M series - UC_MODE_V8 = 1 << 6, // ARMv8 A32 encodings for ARM - UC_MODE_MICRO = 1 << 4, // MicroMips mode (MIPS) - UC_MODE_MIPS3 = 1 << 5, // Mips III ISA - UC_MODE_MIPS32R6 = 1 << 6, // Mips32r6 ISA - UC_MODE_V9 = 1 << 4, // SparcV9 mode (Sparc) - UC_MODE_QPX = 1 << 4, // Quad Processing eXtensions mode (PPC) - UC_MODE_BIG_ENDIAN = 1 << 30, // big-endian mode - UC_MODE_MIPS32 = UC_MODE_32, // Mips32 ISA (Mips) - UC_MODE_MIPS64 = UC_MODE_64, // Mips64 ISA (Mips) + UC_MODE_LITTLE_ENDIAN = 0, // little-endian mode (default mode) + UC_MODE_BIG_ENDIAN = 1 << 30, // big-endian mode + // arm / arm64 + UC_MODE_ARM = 0, // ARM mode + UC_MODE_THUMB = 1 << 4, // THUMB mode (including Thumb-2) + UC_MODE_MCLASS = 1 << 5, // ARM's Cortex-M series (currently unsupported) + UC_MODE_V8 = 1 << 6, // ARMv8 A32 encodings for ARM (currently unsupported) + // mips + UC_MODE_MICRO = 1 << 4, // MicroMips mode (currently unsupported) + UC_MODE_MIPS3 = 1 << 5, // Mips III ISA (currently unsupported) + UC_MODE_MIPS32R6 = 1 << 6, // Mips32r6 ISA (currently unsupported) + UC_MODE_MIPS32 = 1 << 2, // Mips32 ISA + UC_MODE_MIPS64 = 1 << 3, // Mips64 ISA + // x86 / x64 + UC_MODE_16 = 1 << 1, // 16-bit mode + UC_MODE_32 = 1 << 2, // 32-bit mode + UC_MODE_64 = 1 << 3, // 64-bit mode + // ppc + UC_MODE_PPC32 = 1 << 2, // 32-bit mode (currently unsupported) + UC_MODE_PPC64 = 1 << 3, // 64-bit mode (currently unsupported) + UC_MODE_QPX = 1 << 4, // Quad Processing eXtensions mode (currently unsupported) + // sparc + UC_MODE_SPARC32 = 1 << 2, // 32-bit mode + UC_MODE_SPARC64 = 1 << 3, // 64-bit mode + UC_MODE_V9 = 1 << 4, // SparcV9 mode (currently unsupported) + // m68k } uc_mode; // All type of errors encountered by Unicorn API. @@ -129,27 +141,39 @@ typedef enum uc_err { } uc_err; -// Callback function for tracing code (UC_HOOK_CODE & UC_HOOK_BLOCK) -// @address: address where the code is being executed -// @size: size of machine instruction(s) being executed, or 0 when size is unknown -// @user_data: user data passed to tracing APIs. +/* + Callback function for tracing code (UC_HOOK_CODE & UC_HOOK_BLOCK) + + @address: address where the code is being executed + @size: size of machine instruction(s) being executed, or 0 when size is unknown + @user_data: user data passed to tracing APIs. +*/ typedef void (*uc_cb_hookcode_t)(uc_engine *uc, uint64_t address, uint32_t size, void *user_data); -// Callback function for tracing interrupts (for uc_hook_intr()) -// @intno: interrupt number -// @user_data: user data passed to tracing APIs. +/* + Callback function for tracing interrupts (for uc_hook_intr()) + + @intno: interrupt number + @user_data: user data passed to tracing APIs. +*/ typedef void (*uc_cb_hookintr_t)(uc_engine *uc, uint32_t intno, void *user_data); -// Callback function for tracing IN instruction of X86 -// @port: port number -// @size: data size (1/2/4) to be read from this port -// @user_data: user data passed to tracing APIs. +/* + Callback function for tracing IN instruction of X86 + + @port: port number + @size: data size (1/2/4) to be read from this port + @user_data: user data passed to tracing APIs. +*/ typedef uint32_t (*uc_cb_insn_in_t)(uc_engine *uc, uint32_t port, int size, void *user_data); -// x86's handler for OUT -// @port: port number -// @size: data size (1/2/4) to be written to this port -// @value: data value to be written to this port +/* + Callback function for OUT instruction of X86 + + @port: port number + @size: data size (1/2/4) to be written to this port + @value: data value to be written to this port +*/ typedef void (*uc_cb_insn_out_t)(uc_engine *uc, uint32_t port, int size, uint32_t value, void *user_data); // All type of memory accesses for UC_HOOK_MEM_* @@ -194,27 +218,52 @@ typedef enum uc_hook_type { #define UC_HOOK_MEM_FETCH_INVALID (UC_HOOK_MEM_FETCH_PROT + UC_HOOK_MEM_FETCH_UNMAPPED) // hook type for all events of illegal memory access #define UC_HOOK_MEM_INVALID (UC_HOOK_MEM_UNMAPPED + UC_HOOK_MEM_PROT) +// hook type for all events of valid memory access +#define UC_HOOK_MEM_VALID (UC_HOOK_MEM_READ + UC_HOOK_MEM_WRITE + UC_HOOK_MEM_FETCH) -// Callback function for hooking memory (UC_MEM_READ, UC_MEM_WRITE & UC_MEM_FETCH) -// @type: this memory is being READ, or WRITE -// @address: address where the code is being executed -// @size: size of data being read or written -// @value: value of data being written to memory, or irrelevant if type = READ. -// @user_data: user data passed to tracing APIs +/* + Callback function for hooking memory (UC_MEM_READ, UC_MEM_WRITE & UC_MEM_FETCH) + + @type: this memory is being READ, or WRITE + @address: address where the code is being executed + @size: size of data being read or written + @value: value of data being written to memory, or irrelevant if type = READ. + @user_data: user data passed to tracing APIs +*/ typedef void (*uc_cb_hookmem_t)(uc_engine *uc, uc_mem_type type, uint64_t address, int size, int64_t value, void *user_data); -// Callback function for handling invalid memory access events (UC_MEM_*_UNMAPPED and -// UC_MEM_*PROT events) -// @type: this memory is being READ, or WRITE -// @address: address where the code is being executed -// @size: size of data being read or written -// @value: value of data being written to memory, or irrelevant if type = READ. -// @user_data: user data passed to tracing APIs -// @return: return true to continue, or false to stop program (due to invalid memory). +/* + Callback function for handling invalid memory access events (UC_MEM_*_UNMAPPED and + UC_MEM_*PROT events) + + @type: this memory is being READ, or WRITE + @address: address where the code is being executed + @size: size of data being read or written + @value: value of data being written to memory, or irrelevant if type = READ. + @user_data: user data passed to tracing APIs + + @return: return true to continue, or false to stop program (due to invalid memory). +*/ typedef bool (*uc_cb_eventmem_t)(uc_engine *uc, uc_mem_type type, uint64_t address, int size, int64_t value, void *user_data); +/* + Memory region mapped by uc_mem_map() and uc_mem_map_ptr() + Retrieve the list of memory regions with uc_mem_regions() +*/ +typedef struct uc_mem_region { + uint64_t begin; // begin address of the region (inclusive) + uint64_t end; // end address of the region (inclusive) + uint32_t perms; // memory permissions of the region +} uc_mem_region; + +// All type of queries for uc_query() API. +typedef enum uc_query_type { + // Dynamically query current hardware mode. + // For ARM, uc_query() return 1 for Thumb mode, and 0 for Arm mode + UC_QUERY_MODE = 1, +} uc_query_type; /* Return combined API version & major and minor version numbers. @@ -256,7 +305,7 @@ bool uc_arch_supported(uc_arch arch); @uc: pointer to uc_engine, which will be updated at return time @return UC_ERR_OK on success, or other value on failure (refer to uc_err enum - for detailed error). + for detailed error). */ UNICORN_EXPORT uc_err uc_open(uc_arch arch, uc_mode mode, uc_engine **uc); @@ -271,11 +320,24 @@ uc_err uc_open(uc_arch arch, uc_mode mode, uc_engine **uc); @uc: pointer to a handle returned by uc_open() @return UC_ERR_OK on success, or other value on failure (refer to uc_err enum - for detailed error). + for detailed error). */ UNICORN_EXPORT uc_err uc_close(uc_engine *uc); +/* + Query internal status of engine. + + @uc: handle returned by uc_open() + @type: query type. See uc_query_type + + @result: save the internal status queried + + @return: error code of uc_err enum type (UC_ERR_*, see above) +*/ +UNICORN_EXPORT +uc_err uc_query(uc_engine *uc, uc_query_type type, size_t *result); + /* Report the last error number when some API function fail. Like glibc's errno, uc_errno might not retain its old value once accessed. @@ -293,7 +355,7 @@ uc_err uc_errno(uc_engine *uc); @code: error code (see UC_ERR_* above) @return: returns a pointer to a string that describes the error code - passed in the argument @code + passed in the argument @code */ UNICORN_EXPORT const char *uc_strerror(uc_err code); @@ -306,7 +368,7 @@ const char *uc_strerror(uc_err code); @value: pointer to the value that will set to register @regid @return UC_ERR_OK on success, or other value on failure (refer to uc_err enum - for detailed error). + for detailed error). */ UNICORN_EXPORT uc_err uc_reg_write(uc_engine *uc, int regid, const void *value); @@ -319,7 +381,7 @@ uc_err uc_reg_write(uc_engine *uc, int regid, const void *value); @value: pointer to a variable storing the register value. @return UC_ERR_OK on success, or other value on failure (refer to uc_err enum - for detailed error). + for detailed error). */ UNICORN_EXPORT uc_err uc_reg_read(uc_engine *uc, int regid, void *value); @@ -335,7 +397,7 @@ uc_err uc_reg_read(uc_engine *uc, int regid, void *value); NOTE: @bytes must be big enough to contain @size bytes. @return UC_ERR_OK on success, or other value on failure (refer to uc_err enum - for detailed error). + for detailed error). */ UNICORN_EXPORT uc_err uc_mem_write(uc_engine *uc, uint64_t address, const void *bytes, size_t size); @@ -351,7 +413,7 @@ uc_err uc_mem_write(uc_engine *uc, uint64_t address, const void *bytes, size_t s NOTE: @bytes must be big enough to contain @size bytes. @return UC_ERR_OK on success, or other value on failure (refer to uc_err enum - for detailed error). + for detailed error). */ UNICORN_EXPORT uc_err uc_mem_read(uc_engine *uc, uint64_t address, void *bytes, size_t size); @@ -368,7 +430,7 @@ uc_err uc_mem_read(uc_engine *uc, uint64_t address, void *bytes, size_t size); we will emulate all the code available, until the code is finished. @return UC_ERR_OK on success, or other value on failure (refer to uc_err enum - for detailed error). + for detailed error). */ UNICORN_EXPORT uc_err uc_emu_start(uc_engine *uc, uint64_t begin, uint64_t until, uint64_t timeout, size_t count); @@ -381,7 +443,7 @@ uc_err uc_emu_start(uc_engine *uc, uint64_t begin, uint64_t until, uint64_t time @uc: handle returned by uc_open() @return UC_ERR_OK on success, or other value on failure (refer to uc_err enum - for detailed error). + for detailed error). */ UNICORN_EXPORT uc_err uc_emu_stop(uc_engine *uc); @@ -399,7 +461,7 @@ uc_err uc_emu_stop(uc_engine *uc); @...: variable arguments (depending on @type) @return UC_ERR_OK on success, or other value on failure (refer to uc_err enum - for detailed error). + for detailed error). */ UNICORN_EXPORT uc_err uc_hook_add(uc_engine *uc, uc_hook *hh, int type, void *callback, void *user_data, ...); @@ -414,7 +476,7 @@ uc_err uc_hook_add(uc_engine *uc, uc_hook *hh, int type, void *callback, void *u @hh: handle returned by uc_hook_add() @return UC_ERR_OK on success, or other value on failure (refer to uc_err enum - for detailed error). + for detailed error). */ UNICORN_EXPORT uc_err uc_hook_del(uc_engine *uc, uc_hook hh); @@ -441,7 +503,7 @@ typedef enum uc_prot { or this will return with UC_ERR_ARG error. @return UC_ERR_OK on success, or other value on failure (refer to uc_err enum - for detailed error). + for detailed error). */ UNICORN_EXPORT uc_err uc_mem_map(uc_engine *uc, uint64_t address, size_t size, uint32_t perms); @@ -463,7 +525,7 @@ uc_err uc_mem_map(uc_engine *uc, uint64_t address, size_t size, uint32_t perms); least PROT_READ | PROT_WRITE. If it is not, the resulting behavior is undefined. @return UC_ERR_OK on success, or other value on failure (refer to uc_err enum - for detailed error). + for detailed error). */ UNICORN_EXPORT uc_err uc_mem_map_ptr(uc_engine *uc, uint64_t address, size_t size, uint32_t perms, void *ptr); @@ -472,14 +534,14 @@ uc_err uc_mem_map_ptr(uc_engine *uc, uint64_t address, size_t size, uint32_t per Unmap a region of emulation memory. This API deletes a memory mapping from the emulation memory space. - @handle: handle returned by uc_open() + @uc: handle returned by uc_open() @address: starting address of the memory region to be unmapped. This address must be aligned to 4KB, or this will return with UC_ERR_ARG error. @size: size of the memory region to be modified. This size must be multiple of 4KB, or this will return with UC_ERR_ARG error. @return UC_ERR_OK on success, or other value on failure (refer to uc_err enum - for detailed error). + for detailed error). */ UNICORN_EXPORT uc_err uc_mem_unmap(uc_engine *uc, uint64_t address, size_t size); @@ -488,7 +550,7 @@ uc_err uc_mem_unmap(uc_engine *uc, uint64_t address, size_t size); Set memory permissions for emulation memory. This API changes permissions on an existing memory region. - @handle: handle returned by uc_open() + @uc: handle returned by uc_open() @address: starting address of the memory region to be modified. This address must be aligned to 4KB, or this will return with UC_ERR_ARG error. @size: size of the memory region to be modified. @@ -498,11 +560,28 @@ uc_err uc_mem_unmap(uc_engine *uc, uint64_t address, size_t size); or this will return with UC_ERR_ARG error. @return UC_ERR_OK on success, or other value on failure (refer to uc_err enum - for detailed error). + for detailed error). */ UNICORN_EXPORT uc_err uc_mem_protect(uc_engine *uc, uint64_t address, size_t size, uint32_t perms); +/* + Retrieve all memory regions mapped by uc_mem_map() and uc_mem_map_ptr() + This API allocates memory for @regions, and user must free this memory later + by free() to avoid leaking memory. + NOTE: memory regions may be splitted by uc_mem_unmap() + + @uc: handle returned by uc_open() + @regions: pointer to an array of uc_mem_region struct. This is allocated by + Unicorn, and must be freed by user later + @count: pointer to number of struct uc_mem_region contained in @regions + + @return UC_ERR_OK on success, or other value on failure (refer to uc_err enum + for detailed error). +*/ +UNICORN_EXPORT +uc_err uc_mem_regions(uc_engine *uc, uc_mem_region **regions, uint32_t *count); + #ifdef __cplusplus } #endif diff --git a/list.c b/list.c new file mode 100644 index 00000000..6dbe4782 --- /dev/null +++ b/list.c @@ -0,0 +1,68 @@ +#include +#include +#include "list.h" + +// simple linked list implementation + +struct list *list_new(void) +{ + return calloc(1, sizeof(struct list)); +} + +// removed linked list nodes but does not free their content +void list_clear(struct list *list) +{ + struct list_item *next, *cur = list->head; + while (cur != NULL) { + next = cur->next; + free(cur); + cur = next; + } + list->head = NULL; + list->tail = NULL; +} + +// returns generated linked list node, or NULL on failure +void *list_append(struct list *list, void *data) +{ + struct list_item *item = malloc(sizeof(struct list_item)); + if (item == NULL) { + return NULL; + } + item->next = NULL; + item->data = data; + if (list->head == NULL) { + list->head = item; + } else { + list->tail->next = item; + } + list->tail = item; + return item; +} + +// returns true if entry was removed, false otherwise +bool list_remove(struct list *list, void *data) +{ + struct list_item *next, *cur, *prev = NULL; + // is list empty? + if (list->head == NULL) { + return false; + } + cur = list->head; + while (cur != NULL) { + next = cur->next; + if (cur->data == data) { + if (cur == list->head) { + list->head = next; + } + if (cur == list->tail) { + list->tail = prev; + } + free(cur); + return true; + } + prev = cur; + cur = next; + } + return false; +} diff --git a/qemu/Makefile.objs b/qemu/Makefile.objs index 4e7cc899..d309d2a4 100644 --- a/qemu/Makefile.objs +++ b/qemu/Makefile.objs @@ -6,7 +6,7 @@ util-obj-y = util/ qobject/ qapi/ qapi-types.o qapi-visit.o # block-obj-y is code used by both qemu system emulation and qemu-img block-obj-y = -block-obj-y += ../uc.o ../hook.o +block-obj-y += ../uc.o ../list.o #block-obj-$(CONFIG_POSIX) += aio-posix.o #block-obj-$(CONFIG_WIN32) += aio-win32.o diff --git a/qemu/aarch64.h b/qemu/aarch64.h index e368bee4..ad923e4d 100644 --- a/qemu/aarch64.h +++ b/qemu/aarch64.h @@ -1,6 +1,9 @@ /* Autogen header for Unicorn Engine - DONOT MODIFY */ #ifndef UNICORN_AUTOGEN_AARCH64_H #define UNICORN_AUTOGEN_AARCH64_H +#define aarch64_tb_set_jmp_target aarch64_tb_set_jmp_target_aarch64 +#define use_idiv_instructions_rt use_idiv_instructions_rt_aarch64 +#define tcg_target_deposit_valid tcg_target_deposit_valid_aarch64 #define helper_power_down helper_power_down_aarch64 #define check_exit_request check_exit_request_aarch64 #define address_space_unregister address_space_unregister_aarch64 @@ -2806,7 +2809,6 @@ #define tb_phys_invalidate tb_phys_invalidate_aarch64 #define tb_reset_jump tb_reset_jump_aarch64 #define tb_set_jmp_target tb_set_jmp_target_aarch64 -#define tb_set_jmp_target1 tb_set_jmp_target1_aarch64 #define tcg_accel_class_init tcg_accel_class_init_aarch64 #define tcg_accel_type tcg_accel_type_aarch64 #define tcg_add_param_i32 tcg_add_param_i32_aarch64 diff --git a/qemu/arm.h b/qemu/arm.h index 09b8a147..009168bf 100644 --- a/qemu/arm.h +++ b/qemu/arm.h @@ -1,6 +1,9 @@ /* Autogen header for Unicorn Engine - DONOT MODIFY */ #ifndef UNICORN_AUTOGEN_ARM_H #define UNICORN_AUTOGEN_ARM_H +#define aarch64_tb_set_jmp_target aarch64_tb_set_jmp_target_arm +#define use_idiv_instructions_rt use_idiv_instructions_rt_arm +#define tcg_target_deposit_valid tcg_target_deposit_valid_arm #define helper_power_down helper_power_down_arm #define check_exit_request check_exit_request_arm #define address_space_unregister address_space_unregister_arm @@ -2806,7 +2809,6 @@ #define tb_phys_invalidate tb_phys_invalidate_arm #define tb_reset_jump tb_reset_jump_arm #define tb_set_jmp_target tb_set_jmp_target_arm -#define tb_set_jmp_target1 tb_set_jmp_target1_arm #define tcg_accel_class_init tcg_accel_class_init_arm #define tcg_accel_type tcg_accel_type_arm #define tcg_add_param_i32 tcg_add_param_i32_arm diff --git a/qemu/configure b/qemu/configure index 7f6d9164..65d09a3b 100755 --- a/qemu/configure +++ b/qemu/configure @@ -1520,8 +1520,6 @@ echo "PIE $pie" config_host_mak="config-host.mak" -echo "# Automatically generated by configure - do not modify" >config-all-disas.mak - echo "# Automatically generated by configure - do not modify" > $config_host_mak echo >> $config_host_mak @@ -1905,7 +1903,6 @@ ldflags="" if test "$tcg_interpreter" = "yes" ; then echo "CONFIG_TCI_DIS=y" >> $config_target_mak - echo "CONFIG_TCI_DIS=y" >> config-all-disas.mak fi case "$ARCH" in diff --git a/qemu/cpu-exec.c b/qemu/cpu-exec.c index ca3f69ea..b9eb9067 100644 --- a/qemu/cpu-exec.c +++ b/qemu/cpu-exec.c @@ -64,6 +64,8 @@ int cpu_exec(struct uc_struct *uc, CPUArchState *env) // qq TranslationBlock *tb; uint8_t *tc_ptr; uintptr_t next_tb; + struct hook *hook; + /* This must be volatile so it is not trashed by longjmp() */ volatile bool have_tb_lock = false; @@ -92,12 +94,14 @@ int cpu_exec(struct uc_struct *uc, CPUArchState *env) // qq cc->cpu_exec_enter(cpu); cpu->exception_index = -1; + env->invalid_error = UC_ERR_OK; /* prepare setjmp context for exception handling */ for(;;) { if (sigsetjmp(cpu->jmp_env, 0) == 0) { if (uc->stop_request || uc->invalid_error) break; + /* if an exception is pending, we execute it here */ if (cpu->exception_index >= 0) { //printf(">>> GOT INTERRUPT. exception idx = %x\n", cpu->exception_index); // qq @@ -126,11 +130,10 @@ int cpu_exec(struct uc_struct *uc, CPUArchState *env) // qq ret = cpu->exception_index; break; #else - // Unicorn: call interrupt callback if registered - if (uc->hook_intr_idx) - ((uc_cb_hookintr_t)uc->hook_callbacks[uc->hook_intr_idx].callback)( - uc, cpu->exception_index, - uc->hook_callbacks[uc->hook_intr_idx].user_data); + // Unicorn: call registered interrupt callbacks + HOOK_FOREACH(uc, hook, UC_HOOK_INTR) { + ((uc_cb_hookintr_t)hook->callback)(uc, cpu->exception_index, hook->user_data); + } cpu->exception_index = -1; #if defined(TARGET_X86_64) // point EIP to the next instruction after INT @@ -233,6 +236,7 @@ int cpu_exec(struct uc_struct *uc, CPUArchState *env) // qq tc_ptr = tb->tc_ptr; /* execute the generated code */ next_tb = cpu_tb_exec(cpu, tc_ptr); // qq + switch (next_tb & TB_EXIT_MASK) { case TB_EXIT_REQUESTED: /* Something asked us to stop executing @@ -299,12 +303,13 @@ static tcg_target_ulong cpu_tb_exec(CPUState *cpu, uint8_t *tb_ptr) TranslationBlock *tb = (TranslationBlock *)(next_tb & ~TB_EXIT_MASK); if (cc->synchronize_from_tb) { // avoid sync twice when helper_uc_tracecode() already did this. - if (env->uc->emu_counter <= env->uc->emu_count && !env->uc->stop_request) + if (env->uc->emu_counter <= env->uc->emu_count && + !env->uc->stop_request && !env->uc->quit_request) cc->synchronize_from_tb(cpu, tb); } else { assert(cc->set_pc); // avoid sync twice when helper_uc_tracecode() already did this. - if (env->uc->emu_counter <= env->uc->emu_count) + if (env->uc->emu_counter <= env->uc->emu_count && !env->uc->quit_request) cc->set_pc(cpu, tb->pc); } } diff --git a/qemu/cpus.c b/qemu/cpus.c index 68090a9b..e11d943b 100644 --- a/qemu/cpus.c +++ b/qemu/cpus.c @@ -230,8 +230,14 @@ static bool tcg_exec_all(struct uc_struct* uc) //qemu_clock_enable(QEMU_CLOCK_VIRTUAL, // (cpu->singlestep_enabled & SSTEP_NOTIMER) == 0); if (cpu_can_run(cpu)) { + uc->quit_request = false; r = tcg_cpu_exec(uc, env); - if (uc->stop_request) { + + // quit current TB but continue emulating? + if (uc->quit_request) { + // reset stop_request + uc->stop_request = false; + } else if (uc->stop_request) { //printf(">>> got STOP request!!!\n"); finish = true; break; diff --git a/qemu/header_gen.py b/qemu/header_gen.py index 0616d1de..14906321 100644 --- a/qemu/header_gen.py +++ b/qemu/header_gen.py @@ -7,6 +7,9 @@ import sys symbols = ( + 'aarch64_tb_set_jmp_target', + 'use_idiv_instructions_rt', + 'tcg_target_deposit_valid', 'helper_power_down', 'check_exit_request', 'address_space_unregister', @@ -2812,7 +2815,6 @@ symbols = ( 'tb_phys_invalidate', 'tb_reset_jump', 'tb_set_jmp_target', - 'tb_set_jmp_target1', 'tcg_accel_class_init', 'tcg_accel_type', 'tcg_add_param_i32', diff --git a/qemu/ioport.c b/qemu/ioport.c index 338a296a..c7ac6b85 100644 --- a/qemu/ioport.c +++ b/qemu/ioport.c @@ -66,39 +66,45 @@ const MemoryRegionOps unassigned_io_ops = { void cpu_outb(struct uc_struct *uc, pio_addr_t addr, uint8_t val) { //LOG_IOPORT("outb: %04"FMT_pioaddr" %02"PRIx8"\n", addr, val); - // Unicorn: call interrupt callback if registered - if (uc->hook_out_idx) - ((uc_cb_insn_out_t)uc->hook_callbacks[uc->hook_out_idx].callback)( - uc, addr, 1, val, - uc->hook_callbacks[uc->hook_out_idx].user_data); + // Unicorn: call registered OUT callbacks + struct hook *hook; + HOOK_FOREACH(uc, hook, UC_HOOK_INSN) { + if (hook->insn == UC_X86_INS_OUT) + ((uc_cb_insn_out_t)hook->callback)(uc, addr, 1, val, hook->user_data); + } } void cpu_outw(struct uc_struct *uc, pio_addr_t addr, uint16_t val) { //LOG_IOPORT("outw: %04"FMT_pioaddr" %04"PRIx16"\n", addr, val); - // Unicorn: call interrupt callback if registered - if (uc->hook_out_idx) - ((uc_cb_insn_out_t)uc->hook_callbacks[uc->hook_out_idx].callback)( - uc, addr, 2, val, - uc->hook_callbacks[uc->hook_out_idx].user_data); + // Unicorn: call registered OUT callbacks + struct hook *hook; + HOOK_FOREACH(uc, hook, UC_HOOK_INSN) { + if (hook->insn == UC_X86_INS_OUT) + ((uc_cb_insn_out_t)hook->callback)(uc, addr, 2, val, hook->user_data); + } } void cpu_outl(struct uc_struct *uc, pio_addr_t addr, uint32_t val) { //LOG_IOPORT("outl: %04"FMT_pioaddr" %08"PRIx32"\n", addr, val); - if (uc->hook_out_idx) - ((uc_cb_insn_out_t)uc->hook_callbacks[uc->hook_out_idx].callback)( - uc, addr, 4, val, - uc->hook_callbacks[uc->hook_out_idx].user_data); + // Unicorn: call registered OUT callbacks + struct hook *hook; + HOOK_FOREACH(uc, hook, UC_HOOK_INSN) { + if (hook->insn == UC_X86_INS_OUT) + ((uc_cb_insn_out_t)hook->callback)(uc, addr, 4, val, hook->user_data); + } } uint8_t cpu_inb(struct uc_struct *uc, pio_addr_t addr) { //LOG_IOPORT("inb : %04"FMT_pioaddr" %02"PRIx8"\n", addr, val); - if (uc->hook_in_idx) - return ((uc_cb_insn_in_t)uc->hook_callbacks[uc->hook_in_idx].callback)( - uc, addr, 1, - uc->hook_callbacks[uc->hook_in_idx].user_data); + // Unicorn: call registered IN callbacks + struct hook *hook; + HOOK_FOREACH(uc, hook, UC_HOOK_INSN) { + if (hook->insn == UC_X86_INS_IN) + return ((uc_cb_insn_in_t)hook->callback)(uc, addr, 1, hook->user_data); + } return 0; } @@ -106,10 +112,12 @@ uint8_t cpu_inb(struct uc_struct *uc, pio_addr_t addr) uint16_t cpu_inw(struct uc_struct *uc, pio_addr_t addr) { //LOG_IOPORT("inw : %04"FMT_pioaddr" %04"PRIx16"\n", addr, val); - if (uc->hook_in_idx) - return ((uc_cb_insn_in_t)uc->hook_callbacks[uc->hook_in_idx].callback)( - uc, addr, 2, - uc->hook_callbacks[uc->hook_in_idx].user_data); + // Unicorn: call registered IN callbacks + struct hook *hook; + HOOK_FOREACH(uc, hook, UC_HOOK_INSN) { + if (hook->insn == UC_X86_INS_IN) + return ((uc_cb_insn_in_t)hook->callback)(uc, addr, 2, hook->user_data); + } return 0; } @@ -117,10 +125,12 @@ uint16_t cpu_inw(struct uc_struct *uc, pio_addr_t addr) uint32_t cpu_inl(struct uc_struct *uc, pio_addr_t addr) { //LOG_IOPORT("inl : %04"FMT_pioaddr" %08"PRIx32"\n", addr, val); - if (uc->hook_in_idx) - return ((uc_cb_insn_in_t)uc->hook_callbacks[uc->hook_in_idx].callback)( - uc, addr, 4, - uc->hook_callbacks[uc->hook_in_idx].user_data); + // Unicorn: call registered IN callbacks + struct hook *hook; + HOOK_FOREACH(uc, hook, UC_HOOK_INSN) { + if (hook->insn == UC_X86_INS_IN) + return ((uc_cb_insn_in_t)hook->callback)(uc, addr, 4, hook->user_data); + } return 0; } diff --git a/qemu/m68k.h b/qemu/m68k.h index 7591ff9a..7c671e16 100644 --- a/qemu/m68k.h +++ b/qemu/m68k.h @@ -1,6 +1,9 @@ /* Autogen header for Unicorn Engine - DONOT MODIFY */ #ifndef UNICORN_AUTOGEN_M68K_H #define UNICORN_AUTOGEN_M68K_H +#define aarch64_tb_set_jmp_target aarch64_tb_set_jmp_target_m68k +#define use_idiv_instructions_rt use_idiv_instructions_rt_m68k +#define tcg_target_deposit_valid tcg_target_deposit_valid_m68k #define helper_power_down helper_power_down_m68k #define check_exit_request check_exit_request_m68k #define address_space_unregister address_space_unregister_m68k @@ -2806,7 +2809,6 @@ #define tb_phys_invalidate tb_phys_invalidate_m68k #define tb_reset_jump tb_reset_jump_m68k #define tb_set_jmp_target tb_set_jmp_target_m68k -#define tb_set_jmp_target1 tb_set_jmp_target1_m68k #define tcg_accel_class_init tcg_accel_class_init_m68k #define tcg_accel_type tcg_accel_type_m68k #define tcg_add_param_i32 tcg_add_param_i32_m68k diff --git a/qemu/mips.h b/qemu/mips.h index d50d7fe1..c596a0bb 100644 --- a/qemu/mips.h +++ b/qemu/mips.h @@ -1,6 +1,9 @@ /* Autogen header for Unicorn Engine - DONOT MODIFY */ #ifndef UNICORN_AUTOGEN_MIPS_H #define UNICORN_AUTOGEN_MIPS_H +#define aarch64_tb_set_jmp_target aarch64_tb_set_jmp_target_mips +#define use_idiv_instructions_rt use_idiv_instructions_rt_mips +#define tcg_target_deposit_valid tcg_target_deposit_valid_mips #define helper_power_down helper_power_down_mips #define check_exit_request check_exit_request_mips #define address_space_unregister address_space_unregister_mips @@ -2806,7 +2809,6 @@ #define tb_phys_invalidate tb_phys_invalidate_mips #define tb_reset_jump tb_reset_jump_mips #define tb_set_jmp_target tb_set_jmp_target_mips -#define tb_set_jmp_target1 tb_set_jmp_target1_mips #define tcg_accel_class_init tcg_accel_class_init_mips #define tcg_accel_type tcg_accel_type_mips #define tcg_add_param_i32 tcg_add_param_i32_mips diff --git a/qemu/mips64.h b/qemu/mips64.h index d62f4ee6..8180af93 100644 --- a/qemu/mips64.h +++ b/qemu/mips64.h @@ -1,6 +1,9 @@ /* Autogen header for Unicorn Engine - DONOT MODIFY */ #ifndef UNICORN_AUTOGEN_MIPS64_H #define UNICORN_AUTOGEN_MIPS64_H +#define aarch64_tb_set_jmp_target aarch64_tb_set_jmp_target_mips64 +#define use_idiv_instructions_rt use_idiv_instructions_rt_mips64 +#define tcg_target_deposit_valid tcg_target_deposit_valid_mips64 #define helper_power_down helper_power_down_mips64 #define check_exit_request check_exit_request_mips64 #define address_space_unregister address_space_unregister_mips64 @@ -2806,7 +2809,6 @@ #define tb_phys_invalidate tb_phys_invalidate_mips64 #define tb_reset_jump tb_reset_jump_mips64 #define tb_set_jmp_target tb_set_jmp_target_mips64 -#define tb_set_jmp_target1 tb_set_jmp_target1_mips64 #define tcg_accel_class_init tcg_accel_class_init_mips64 #define tcg_accel_type tcg_accel_type_mips64 #define tcg_add_param_i32 tcg_add_param_i32_mips64 diff --git a/qemu/mips64el.h b/qemu/mips64el.h index 8193f24b..e93b082b 100644 --- a/qemu/mips64el.h +++ b/qemu/mips64el.h @@ -1,6 +1,9 @@ /* Autogen header for Unicorn Engine - DONOT MODIFY */ #ifndef UNICORN_AUTOGEN_MIPS64EL_H #define UNICORN_AUTOGEN_MIPS64EL_H +#define aarch64_tb_set_jmp_target aarch64_tb_set_jmp_target_mips64el +#define use_idiv_instructions_rt use_idiv_instructions_rt_mips64el +#define tcg_target_deposit_valid tcg_target_deposit_valid_mips64el #define helper_power_down helper_power_down_mips64el #define check_exit_request check_exit_request_mips64el #define address_space_unregister address_space_unregister_mips64el @@ -2806,7 +2809,6 @@ #define tb_phys_invalidate tb_phys_invalidate_mips64el #define tb_reset_jump tb_reset_jump_mips64el #define tb_set_jmp_target tb_set_jmp_target_mips64el -#define tb_set_jmp_target1 tb_set_jmp_target1_mips64el #define tcg_accel_class_init tcg_accel_class_init_mips64el #define tcg_accel_type tcg_accel_type_mips64el #define tcg_add_param_i32 tcg_add_param_i32_mips64el diff --git a/qemu/mipsel.h b/qemu/mipsel.h index 02a39df5..66e0c9d9 100644 --- a/qemu/mipsel.h +++ b/qemu/mipsel.h @@ -1,6 +1,9 @@ /* Autogen header for Unicorn Engine - DONOT MODIFY */ #ifndef UNICORN_AUTOGEN_MIPSEL_H #define UNICORN_AUTOGEN_MIPSEL_H +#define aarch64_tb_set_jmp_target aarch64_tb_set_jmp_target_mipsel +#define use_idiv_instructions_rt use_idiv_instructions_rt_mipsel +#define tcg_target_deposit_valid tcg_target_deposit_valid_mipsel #define helper_power_down helper_power_down_mipsel #define check_exit_request check_exit_request_mipsel #define address_space_unregister address_space_unregister_mipsel @@ -2806,7 +2809,6 @@ #define tb_phys_invalidate tb_phys_invalidate_mipsel #define tb_reset_jump tb_reset_jump_mipsel #define tb_set_jmp_target tb_set_jmp_target_mipsel -#define tb_set_jmp_target1 tb_set_jmp_target1_mipsel #define tcg_accel_class_init tcg_accel_class_init_mipsel #define tcg_accel_type tcg_accel_type_mipsel #define tcg_add_param_i32 tcg_add_param_i32_mipsel diff --git a/qemu/powerpc.h b/qemu/powerpc.h index cb7a046c..60c518ac 100644 --- a/qemu/powerpc.h +++ b/qemu/powerpc.h @@ -1,6 +1,9 @@ /* Autogen header for Unicorn Engine - DONOT MODIFY */ #ifndef UNICORN_AUTOGEN_POWERPC_H #define UNICORN_AUTOGEN_POWERPC_H +#define aarch64_tb_set_jmp_target aarch64_tb_set_jmp_target_powerpc +#define use_idiv_instructions_rt use_idiv_instructions_rt_powerpc +#define tcg_target_deposit_valid tcg_target_deposit_valid_powerpc #define helper_power_down helper_power_down_powerpc #define check_exit_request check_exit_request_powerpc #define address_space_unregister address_space_unregister_powerpc @@ -2806,7 +2809,6 @@ #define tb_phys_invalidate tb_phys_invalidate_powerpc #define tb_reset_jump tb_reset_jump_powerpc #define tb_set_jmp_target tb_set_jmp_target_powerpc -#define tb_set_jmp_target1 tb_set_jmp_target1_powerpc #define tcg_accel_class_init tcg_accel_class_init_powerpc #define tcg_accel_type tcg_accel_type_powerpc #define tcg_add_param_i32 tcg_add_param_i32_powerpc diff --git a/qemu/softmmu_template.h b/qemu/softmmu_template.h index e3100fcd..f58065b9 100644 --- a/qemu/softmmu_template.h +++ b/qemu/softmmu_template.h @@ -178,23 +178,33 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx, uintptr_t haddr; DATA_TYPE res; int error_code; + struct hook *hook; + bool handled; struct uc_struct *uc = env->uc; MemoryRegion *mr = memory_mapping(uc, addr); // memory might be still unmapped while reading or fetching if (mr == NULL) { + handled = false; #if defined(SOFTMMU_CODE_ACCESS) error_code = UC_ERR_FETCH_UNMAPPED; - if (uc->hook_mem_fetch_idx != 0 && ((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_fetch_idx].callback)( - uc, UC_MEM_FETCH_UNMAPPED, addr, DATA_SIZE, 0, - uc->hook_callbacks[uc->hook_mem_fetch_idx].user_data)) { + HOOK_FOREACH(uc, hook, UC_HOOK_MEM_FETCH_UNMAPPED) { + if (!HOOK_BOUND_CHECK(hook, addr)) + continue; + if ((handled = ((uc_cb_eventmem_t)hook->callback)(uc, UC_MEM_FETCH_UNMAPPED, addr, DATA_SIZE, 0, hook->user_data))) + break; + } #else error_code = UC_ERR_READ_UNMAPPED; - if (uc->hook_mem_read_idx != 0 && ((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_read_idx].callback)( - uc, UC_MEM_READ_UNMAPPED, addr, DATA_SIZE, 0, - uc->hook_callbacks[uc->hook_mem_read_idx].user_data)) { + HOOK_FOREACH(uc, hook, UC_HOOK_MEM_READ_UNMAPPED) { + if (!HOOK_BOUND_CHECK(hook, addr)) + continue; + if ((handled = ((uc_cb_eventmem_t)hook->callback)(uc, UC_MEM_READ_UNMAPPED, addr, DATA_SIZE, 0, hook->user_data))) + break; + } #endif + if (handled) { env->invalid_error = UC_ERR_OK; mr = memory_mapping(uc, addr); // FIXME: what if mr is still NULL at this time? } else { @@ -209,9 +219,15 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx, #if defined(SOFTMMU_CODE_ACCESS) // Unicorn: callback on fetch from NX if (mr != NULL && !(mr->perms & UC_PROT_EXEC)) { // non-executable - if (uc->hook_mem_fetch_prot_idx != 0 && ((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_fetch_prot_idx].callback)( - uc, UC_MEM_FETCH_PROT, addr, DATA_SIZE, 0, - uc->hook_callbacks[uc->hook_mem_fetch_prot_idx].user_data)) { + handled = false; + HOOK_FOREACH(uc, hook, UC_HOOK_MEM_FETCH_PROT) { + if (!HOOK_BOUND_CHECK(hook, addr)) + continue; + if ((handled = ((uc_cb_eventmem_t)hook->callback)(uc, UC_MEM_FETCH_PROT, addr, DATA_SIZE, 0, hook->user_data))) + break; + } + + if (handled) { env->invalid_error = UC_ERR_OK; } else { env->invalid_addr = addr; @@ -224,19 +240,25 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx, #endif // Unicorn: callback on memory read - if (READ_ACCESS_TYPE == MMU_DATA_LOAD && env->uc->hook_mem_read) { - struct hook_struct *trace = hook_find(env->uc, UC_HOOK_MEM_READ, addr); - if (trace) { - ((uc_cb_hookmem_t)trace->callback)(env->uc, UC_MEM_READ, - (uint64_t)addr, (int)DATA_SIZE, (int64_t)0, trace->user_data); + if (READ_ACCESS_TYPE == MMU_DATA_LOAD) { + HOOK_FOREACH(uc, hook, UC_HOOK_MEM_READ) { + if (!HOOK_BOUND_CHECK(hook, addr)) + continue; + ((uc_cb_hookmem_t)hook->callback)(env->uc, UC_MEM_READ, addr, DATA_SIZE, 0, hook->user_data); } } // Unicorn: callback on non-readable memory if (READ_ACCESS_TYPE == MMU_DATA_LOAD && mr != NULL && !(mr->perms & UC_PROT_READ)) { //non-readable - if (uc->hook_mem_read_prot_idx != 0 && ((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_read_prot_idx].callback)( - uc, UC_MEM_READ_PROT, addr, DATA_SIZE, 0, - uc->hook_callbacks[uc->hook_mem_read_prot_idx].user_data)) { + handled = false; + HOOK_FOREACH(uc, hook, UC_HOOK_MEM_READ_PROT) { + if (!HOOK_BOUND_CHECK(hook, addr)) + continue; + if ((handled = ((uc_cb_eventmem_t)hook->callback)(uc, UC_MEM_READ_PROT, addr, DATA_SIZE, 0, hook->user_data))) + break; + } + + if (handled) { env->invalid_error = UC_ERR_OK; } else { env->invalid_addr = addr; @@ -368,23 +390,33 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx, uintptr_t haddr; DATA_TYPE res; int error_code; + struct hook *hook; + bool handled; struct uc_struct *uc = env->uc; MemoryRegion *mr = memory_mapping(uc, addr); // memory can be unmapped while reading or fetching if (mr == NULL) { + handled = false; #if defined(SOFTMMU_CODE_ACCESS) error_code = UC_ERR_FETCH_UNMAPPED; - if (uc->hook_mem_fetch_idx != 0 && ((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_fetch_idx].callback)( - uc, UC_MEM_FETCH_UNMAPPED, addr, DATA_SIZE, 0, - uc->hook_callbacks[uc->hook_mem_fetch_idx].user_data)) { + HOOK_FOREACH(uc, hook, UC_HOOK_MEM_FETCH_UNMAPPED) { + if (!HOOK_BOUND_CHECK(hook, addr)) + continue; + if ((handled = ((uc_cb_eventmem_t)hook->callback)(uc, UC_MEM_FETCH_UNMAPPED, addr, DATA_SIZE, 0, hook->user_data))) + break; + } #else error_code = UC_ERR_READ_UNMAPPED; - if (uc->hook_mem_read_idx != 0 && ((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_read_idx].callback)( - uc, UC_MEM_READ_UNMAPPED, addr, DATA_SIZE, 0, - uc->hook_callbacks[uc->hook_mem_read_idx].user_data)) { + HOOK_FOREACH(uc, hook, UC_HOOK_MEM_READ_UNMAPPED) { + if (!HOOK_BOUND_CHECK(hook, addr)) + continue; + if ((handled = ((uc_cb_eventmem_t)hook->callback)(uc, UC_MEM_READ_UNMAPPED, addr, DATA_SIZE, 0, hook->user_data))) + break; + } #endif + if (handled) { env->invalid_error = UC_ERR_OK; mr = memory_mapping(uc, addr); // FIXME: what if mr is still NULL at this time? } else { @@ -399,9 +431,15 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx, #if defined(SOFTMMU_CODE_ACCESS) // Unicorn: callback on fetch from NX if (mr != NULL && !(mr->perms & UC_PROT_EXEC)) { // non-executable - if (uc->hook_mem_fetch_prot_idx != 0 && ((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_fetch_prot_idx].callback)( - uc, UC_MEM_FETCH_PROT, addr, DATA_SIZE, 0, - uc->hook_callbacks[uc->hook_mem_fetch_prot_idx].user_data)) { + handled = false; + HOOK_FOREACH(uc, hook, UC_HOOK_MEM_FETCH_PROT) { + if (!HOOK_BOUND_CHECK(hook, addr)) + continue; + if ((handled = ((uc_cb_eventmem_t)hook->callback)(uc, UC_MEM_FETCH_PROT, addr, DATA_SIZE, 0, hook->user_data))) + break; + } + + if (handled) { env->invalid_error = UC_ERR_OK; } else { env->invalid_addr = addr; @@ -414,19 +452,25 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx, #endif // Unicorn: callback on memory read - if (READ_ACCESS_TYPE == MMU_DATA_LOAD && env->uc->hook_mem_read) { - struct hook_struct *trace = hook_find(env->uc, UC_HOOK_MEM_READ, addr); - if (trace) { - ((uc_cb_hookmem_t)trace->callback)(env->uc, UC_MEM_READ, - (uint64_t)addr, (int)DATA_SIZE, (int64_t)0, trace->user_data); + if (READ_ACCESS_TYPE == MMU_DATA_LOAD) { + HOOK_FOREACH(uc, hook, UC_HOOK_MEM_READ) { + if (!HOOK_BOUND_CHECK(hook, addr)) + continue; + ((uc_cb_hookmem_t)hook->callback)(env->uc, UC_MEM_READ, addr, DATA_SIZE, 0, hook->user_data); } } // Unicorn: callback on non-readable memory if (READ_ACCESS_TYPE == MMU_DATA_LOAD && mr != NULL && !(mr->perms & UC_PROT_READ)) { //non-readable - if (uc->hook_mem_read_prot_idx != 0 && ((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_read_prot_idx].callback)( - uc, UC_MEM_READ_PROT, addr, DATA_SIZE, 0, - uc->hook_callbacks[uc->hook_mem_read_prot_idx].user_data)) { + handled = false; + HOOK_FOREACH(uc, hook, UC_HOOK_MEM_READ_PROT) { + if (!HOOK_BOUND_CHECK(hook, addr)) + continue; + if ((handled = ((uc_cb_eventmem_t)hook->callback)(uc, UC_MEM_READ_PROT, addr, DATA_SIZE, 0, hook->user_data))) + break; + } + + if (handled) { env->invalid_error = UC_ERR_OK; } else { env->invalid_addr = addr; @@ -595,24 +639,30 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); target_ulong tlb_addr = env->tlb_table[mmu_idx][index].addr_write; uintptr_t haddr; + struct hook *hook; + bool handled; struct uc_struct *uc = env->uc; MemoryRegion *mr = memory_mapping(uc, addr); // Unicorn: callback on memory write - if (uc->hook_mem_write) { - struct hook_struct *trace = hook_find(uc, UC_HOOK_MEM_WRITE, addr); - if (trace) { - ((uc_cb_hookmem_t)trace->callback)(uc, UC_MEM_WRITE, - (uint64_t)addr, (int)DATA_SIZE, (int64_t)val, trace->user_data); - } + HOOK_FOREACH(uc, hook, UC_HOOK_MEM_WRITE) { + if (!HOOK_BOUND_CHECK(hook, addr)) + continue; + ((uc_cb_hookmem_t)hook->callback)(uc, UC_MEM_WRITE, addr, DATA_SIZE, val, hook->user_data); } // Unicorn: callback on invalid memory - if (uc->hook_mem_write_idx && mr == NULL) { - if (!((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_write_idx].callback)( - uc, UC_MEM_WRITE_UNMAPPED, addr, DATA_SIZE, (int64_t)val, - uc->hook_callbacks[uc->hook_mem_write_idx].user_data)) { + if (mr == NULL) { + handled = false; + HOOK_FOREACH(uc, hook, UC_HOOK_MEM_WRITE_UNMAPPED) { + if (!HOOK_BOUND_CHECK(hook, addr)) + continue; + if ((handled = ((uc_cb_eventmem_t)hook->callback)(uc, UC_MEM_WRITE_UNMAPPED, addr, DATA_SIZE, val, hook->user_data))) + break; + } + + if (!handled) { // save error & quit env->invalid_addr = addr; env->invalid_error = UC_ERR_WRITE_UNMAPPED; @@ -627,12 +677,17 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, // Unicorn: callback on non-writable memory if (mr != NULL && !(mr->perms & UC_PROT_WRITE)) { //non-writable - if (uc->hook_mem_write_prot_idx != 0 && ((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_write_prot_idx].callback)( - uc, UC_MEM_WRITE_PROT, addr, DATA_SIZE, (int64_t)val, - uc->hook_callbacks[uc->hook_mem_write_prot_idx].user_data)) { - env->invalid_error = UC_ERR_OK; + handled = false; + HOOK_FOREACH(uc, hook, UC_HOOK_MEM_WRITE_PROT) { + if (!HOOK_BOUND_CHECK(hook, addr)) + continue; + if ((handled = ((uc_cb_eventmem_t)hook->callback)(uc, UC_MEM_WRITE_PROT, addr, DATA_SIZE, val, hook->user_data))) + break; } - else { + + if (handled) { + env->invalid_error = UC_ERR_OK; + } else { env->invalid_addr = addr; env->invalid_error = UC_ERR_WRITE_PROT; // printf("***** Invalid memory write (ro) at " TARGET_FMT_lx "\n", addr); @@ -742,24 +797,30 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); target_ulong tlb_addr = env->tlb_table[mmu_idx][index].addr_write; uintptr_t haddr; + struct hook *hook; + bool handled; struct uc_struct *uc = env->uc; MemoryRegion *mr = memory_mapping(uc, addr); // Unicorn: callback on memory write - if (uc->hook_mem_write) { - struct hook_struct *trace = hook_find(uc, UC_HOOK_MEM_WRITE, addr); - if (trace) { - ((uc_cb_hookmem_t)trace->callback)(uc, UC_MEM_WRITE, - (uint64_t)addr, (int)DATA_SIZE, (int64_t)val, trace->user_data); - } + HOOK_FOREACH(uc, hook, UC_HOOK_MEM_WRITE) { + if (!HOOK_BOUND_CHECK(hook, addr)) + continue; + ((uc_cb_hookmem_t)hook->callback)(uc, UC_MEM_WRITE, addr, DATA_SIZE, val, hook->user_data); } // Unicorn: callback on invalid memory - if (uc->hook_mem_write_idx && mr == NULL) { - if (!((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_write_idx].callback)( - uc, UC_MEM_WRITE_UNMAPPED, addr, DATA_SIZE, (int64_t)val, - uc->hook_callbacks[uc->hook_mem_write_idx].user_data)) { + if (mr == NULL) { + handled = false; + HOOK_FOREACH(uc, hook, UC_HOOK_MEM_WRITE_UNMAPPED) { + if (!HOOK_BOUND_CHECK(hook, addr)) + continue; + if ((handled = ((uc_cb_eventmem_t)hook->callback)(uc, UC_MEM_WRITE_UNMAPPED, addr, DATA_SIZE, val, hook->user_data))) + break; + } + + if (!handled) { // save error & quit env->invalid_addr = addr; env->invalid_error = UC_ERR_WRITE_UNMAPPED; @@ -774,12 +835,17 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, // Unicorn: callback on non-writable memory if (mr != NULL && !(mr->perms & UC_PROT_WRITE)) { //non-writable - if (uc->hook_mem_write_prot_idx != 0 && ((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_write_prot_idx].callback)( - uc, UC_MEM_WRITE_PROT, addr, DATA_SIZE, (int64_t)val, - uc->hook_callbacks[uc->hook_mem_write_prot_idx].user_data)) { - env->invalid_error = UC_ERR_OK; + handled = false; + HOOK_FOREACH(uc, hook, UC_HOOK_MEM_WRITE_PROT) { + if (!HOOK_BOUND_CHECK(hook, addr)) + continue; + if ((handled = ((uc_cb_eventmem_t)hook->callback)(uc, UC_MEM_WRITE_PROT, addr, DATA_SIZE, val, hook->user_data))) + break; } - else { + + if (handled) { + env->invalid_error = UC_ERR_OK; + } else { env->invalid_addr = addr; env->invalid_error = UC_ERR_WRITE_PROT; // printf("***** Invalid memory write (ro) at " TARGET_FMT_lx "\n", addr); diff --git a/qemu/sparc.h b/qemu/sparc.h index 85e042e8..6c48ce53 100644 --- a/qemu/sparc.h +++ b/qemu/sparc.h @@ -1,6 +1,9 @@ /* Autogen header for Unicorn Engine - DONOT MODIFY */ #ifndef UNICORN_AUTOGEN_SPARC_H #define UNICORN_AUTOGEN_SPARC_H +#define aarch64_tb_set_jmp_target aarch64_tb_set_jmp_target_sparc +#define use_idiv_instructions_rt use_idiv_instructions_rt_sparc +#define tcg_target_deposit_valid tcg_target_deposit_valid_sparc #define helper_power_down helper_power_down_sparc #define check_exit_request check_exit_request_sparc #define address_space_unregister address_space_unregister_sparc @@ -2806,7 +2809,6 @@ #define tb_phys_invalidate tb_phys_invalidate_sparc #define tb_reset_jump tb_reset_jump_sparc #define tb_set_jmp_target tb_set_jmp_target_sparc -#define tb_set_jmp_target1 tb_set_jmp_target1_sparc #define tcg_accel_class_init tcg_accel_class_init_sparc #define tcg_accel_type tcg_accel_type_sparc #define tcg_add_param_i32 tcg_add_param_i32_sparc diff --git a/qemu/sparc64.h b/qemu/sparc64.h index a08c42e1..8c3d73f1 100644 --- a/qemu/sparc64.h +++ b/qemu/sparc64.h @@ -1,6 +1,9 @@ /* Autogen header for Unicorn Engine - DONOT MODIFY */ #ifndef UNICORN_AUTOGEN_SPARC64_H #define UNICORN_AUTOGEN_SPARC64_H +#define aarch64_tb_set_jmp_target aarch64_tb_set_jmp_target_sparc64 +#define use_idiv_instructions_rt use_idiv_instructions_rt_sparc64 +#define tcg_target_deposit_valid tcg_target_deposit_valid_sparc64 #define helper_power_down helper_power_down_sparc64 #define check_exit_request check_exit_request_sparc64 #define address_space_unregister address_space_unregister_sparc64 @@ -2806,7 +2809,6 @@ #define tb_phys_invalidate tb_phys_invalidate_sparc64 #define tb_reset_jump tb_reset_jump_sparc64 #define tb_set_jmp_target tb_set_jmp_target_sparc64 -#define tb_set_jmp_target1 tb_set_jmp_target1_sparc64 #define tcg_accel_class_init tcg_accel_class_init_sparc64 #define tcg_accel_type tcg_accel_type_sparc64 #define tcg_add_param_i32 tcg_add_param_i32_sparc64 diff --git a/qemu/target-arm/helper.h b/qemu/target-arm/helper.h index d2de58d6..6427c18c 100644 --- a/qemu/target-arm/helper.h +++ b/qemu/target-arm/helper.h @@ -1,4 +1,4 @@ -DEF_HELPER_5(uc_tracecode, void, i32, ptr, ptr, i64, ptr) +DEF_HELPER_4(uc_tracecode, void, i32, i32, ptr, i64) DEF_HELPER_FLAGS_1(clz_arm, TCG_CALL_NO_RWG_SE, i32, i32) diff --git a/qemu/target-arm/translate-a64.c b/qemu/target-arm/translate-a64.c index c76de8e0..e869da01 100644 --- a/qemu/target-arm/translate-a64.c +++ b/qemu/target-arm/translate-a64.c @@ -10984,10 +10984,8 @@ static void disas_a64_insn(CPUARMState *env, DisasContext *s) s->pc += 4; // Unicorn: trace this instruction on request - if (env->uc->hook_insn) { - struct hook_struct *trace = hook_find(s->uc, UC_HOOK_CODE, s->pc - 4); - if (trace) - gen_uc_tracecode(tcg_ctx, 4, trace->callback, env->uc, s->pc - 4, trace->user_data); + if (HOOK_EXISTS_BOUNDED(env->uc, UC_HOOK_CODE, s->pc - 4)) { + gen_uc_tracecode(tcg_ctx, 4, UC_HOOK_CODE_IDX, env->uc, s->pc - 4); // the callback might want to stop emulation immediately check_exit_request(tcg_ctx); } @@ -11114,13 +11112,10 @@ void gen_intermediate_code_internal_a64(ARMCPU *cpu, // Unicorn: trace this block on request // Only hook this block if it is not broken from previous translation due to // full translation cache - if (env->uc->hook_block && !env->uc->block_full) { - struct hook_struct *trace = hook_find(env->uc, UC_HOOK_BLOCK, pc_start); - if (trace) { - // save block address to see if we need to patch block size later - env->uc->block_addr = pc_start; - gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, trace->callback, env->uc, pc_start, trace->user_data); - } + if (!env->uc->block_full && HOOK_EXISTS_BOUNDED(env->uc, UC_HOOK_BLOCK, pc_start)) { + // save block address to see if we need to patch block size later + env->uc->block_addr = pc_start; + gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, UC_HOOK_BLOCK_IDX, env->uc, pc_start); } gen_tb_start(tcg_ctx); diff --git a/qemu/target-arm/translate.c b/qemu/target-arm/translate.c index 3560e6ed..d097c236 100644 --- a/qemu/target-arm/translate.c +++ b/qemu/target-arm/translate.c @@ -7687,10 +7687,8 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) // qq } // Unicorn: trace this instruction on request - if (s->uc->hook_insn) { - struct hook_struct *trace = hook_find(s->uc, UC_HOOK_CODE, s->pc - 4); - if (trace) - gen_uc_tracecode(tcg_ctx, 4, trace->callback, s->uc, s->pc - 4, trace->user_data); + if (HOOK_EXISTS_BOUNDED(s->uc, UC_HOOK_CODE, s->pc - 4)) { + gen_uc_tracecode(tcg_ctx, 4, UC_HOOK_CODE_IDX, s->uc, s->pc - 4); // the callback might want to stop emulation immediately check_exit_request(tcg_ctx); } @@ -10408,15 +10406,10 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s) // qq } // Unicorn: trace this instruction on request - if (env->uc->hook_insn) { - struct hook_struct *trace = hook_find(s->uc, UC_HOOK_CODE, s->pc); - if (trace) - gen_uc_tracecode(tcg_ctx, 2, trace->callback, env->uc, s->pc, trace->user_data); - // if requested to emulate only some instructions, check to see - // if we need to exit immediately - if (env->uc->emu_count > 0) { - check_exit_request(tcg_ctx); - } + if (HOOK_EXISTS_BOUNDED(s->uc, UC_HOOK_CODE, s->pc)) { + gen_uc_tracecode(tcg_ctx, 2, UC_HOOK_CODE_IDX, s->uc, s->pc); + // the callback might want to stop emulation immediately + check_exit_request(tcg_ctx); } insn = arm_lduw_code(env, s->pc, s->bswap_code); @@ -11237,13 +11230,10 @@ static inline void gen_intermediate_code_internal(ARMCPU *cpu, // Unicorn: trace this block on request // Only hook this block if it is not broken from previous translation due to // full translation cache - if (env->uc->hook_block && !env->uc->block_full) { - struct hook_struct *trace = hook_find(env->uc, UC_HOOK_BLOCK, pc_start); - if (trace) { - // save block address to see if we need to patch block size later - env->uc->block_addr = pc_start; - gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, trace->callback, env->uc, pc_start, trace->user_data); - } + if (!env->uc->block_full && HOOK_EXISTS_BOUNDED(env->uc, UC_HOOK_BLOCK, pc_start)) { + // save block address to see if we need to patch block size later + env->uc->block_addr = pc_start; + gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, UC_HOOK_BLOCK_IDX, env->uc, pc_start); } gen_tb_start(tcg_ctx); diff --git a/qemu/target-arm/unicorn_aarch64.c b/qemu/target-arm/unicorn_aarch64.c index 00f71c6a..2f43d5ea 100644 --- a/qemu/target-arm/unicorn_aarch64.c +++ b/qemu/target-arm/unicorn_aarch64.c @@ -99,6 +99,9 @@ int arm64_reg_write(struct uc_struct *uc, unsigned int regid, const void *value) break; case UC_ARM64_REG_PC: ARM_CPU(uc, mycpu)->env.pc = *(uint64_t *)value; + // force to quit execution and flush TB + uc->quit_request = true; + uc_emu_stop(uc); break; case UC_ARM64_REG_SP: ARM_CPU(uc, mycpu)->env.xregs[31] = *(uint64_t *)value; diff --git a/qemu/target-arm/unicorn_arm.c b/qemu/target-arm/unicorn_arm.c index 22c35cf9..98e1a0b0 100644 --- a/qemu/target-arm/unicorn_arm.c +++ b/qemu/target-arm/unicorn_arm.c @@ -59,36 +59,28 @@ int arm_reg_read(struct uc_struct *uc, unsigned int regid, void *value) mycpu = first_cpu; - switch(uc->mode) { - default: - break; - case UC_MODE_ARM: - case UC_MODE_THUMB: - if (regid >= UC_ARM_REG_R0 && regid <= UC_ARM_REG_R12) - *(int32_t *)value = ARM_CPU(uc, mycpu)->env.regs[regid - UC_ARM_REG_R0]; - else { - switch(regid) { - case UC_ARM_REG_CPSR: - *(int32_t *)value = cpsr_read(&ARM_CPU(uc, mycpu)->env); - break; - //case UC_ARM_REG_SP: - case UC_ARM_REG_R13: - *(int32_t *)value = ARM_CPU(uc, mycpu)->env.regs[13]; - break; - //case UC_ARM_REG_LR: - case UC_ARM_REG_R14: - *(int32_t *)value = ARM_CPU(uc, mycpu)->env.regs[14]; - break; - //case UC_ARM_REG_PC: - case UC_ARM_REG_R15: - *(int32_t *)value = ARM_CPU(uc, mycpu)->env.regs[15]; - break; - } - } - break; + if (regid >= UC_ARM_REG_R0 && regid <= UC_ARM_REG_R12) + *(int32_t *)value = ARM_CPU(uc, mycpu)->env.regs[regid - UC_ARM_REG_R0]; + else { + switch(regid) { + case UC_ARM_REG_CPSR: + *(int32_t *)value = cpsr_read(&ARM_CPU(uc, mycpu)->env); + break; + //case UC_ARM_REG_SP: + case UC_ARM_REG_R13: + *(int32_t *)value = ARM_CPU(uc, mycpu)->env.regs[13]; + break; + //case UC_ARM_REG_LR: + case UC_ARM_REG_R14: + *(int32_t *)value = ARM_CPU(uc, mycpu)->env.regs[14]; + break; + //case UC_ARM_REG_PC: + case UC_ARM_REG_R15: + *(int32_t *)value = ARM_CPU(uc, mycpu)->env.regs[15]; + break; + } } - return 0; } @@ -101,31 +93,28 @@ int arm_reg_write(struct uc_struct *uc, unsigned int regid, const void *value) { CPUState *mycpu = first_cpu; - switch(uc->mode) { - default: - break; + if (regid >= UC_ARM_REG_R0 && regid <= UC_ARM_REG_R12) + ARM_CPU(uc, mycpu)->env.regs[regid - UC_ARM_REG_R0] = *(uint32_t *)value; + else { + switch(regid) { + //case UC_ARM_REG_SP: + case UC_ARM_REG_R13: + ARM_CPU(uc, mycpu)->env.regs[13] = *(uint32_t *)value; + break; + //case UC_ARM_REG_LR: + case UC_ARM_REG_R14: + ARM_CPU(uc, mycpu)->env.regs[14] = *(uint32_t *)value; + break; + //case UC_ARM_REG_PC: + case UC_ARM_REG_R15: + ARM_CPU(uc, mycpu)->env.pc = *(uint32_t *)value; + ARM_CPU(uc, mycpu)->env.regs[15] = *(uint32_t *)value; + // force to quit execution and flush TB + uc->quit_request = true; + uc_emu_stop(uc); - case UC_MODE_ARM: - case UC_MODE_THUMB: - if (regid >= UC_ARM_REG_R0 && regid <= UC_ARM_REG_R12) - ARM_CPU(uc, mycpu)->env.regs[regid - UC_ARM_REG_R0] = *(uint32_t *)value; - else { - switch(regid) { - //case UC_ARM_REG_SP: - case UC_ARM_REG_R13: - ARM_CPU(uc, mycpu)->env.regs[13] = *(uint32_t *)value; - break; - //case UC_ARM_REG_LR: - case UC_ARM_REG_R14: - ARM_CPU(uc, mycpu)->env.regs[14] = *(uint32_t *)value; - break; - //case UC_ARM_REG_PC: - case UC_ARM_REG_R15: - ARM_CPU(uc, mycpu)->env.regs[15] = *(uint32_t *)value; - break; - } - } - break; + break; + } } return 0; @@ -141,6 +130,19 @@ static bool arm_stop_interrupt(int intno) } } +static uc_err arm_query(struct uc_struct *uc, uc_query_type type, size_t *result) +{ + CPUState *mycpu = first_cpu; + + switch(type) { + case UC_QUERY_MODE: + *result = (ARM_CPU(uc, mycpu)->env.thumb != 0); + return UC_ERR_OK; + default: + return UC_ERR_ARG; + } +} + void arm_uc_init(struct uc_struct* uc) { register_accel_types(uc); @@ -152,5 +154,6 @@ void arm_uc_init(struct uc_struct* uc) uc->set_pc = arm_set_pc; uc->stop_interrupt = arm_stop_interrupt; uc->release = arm_release; + uc->query = arm_query; uc_common_init(uc); } diff --git a/qemu/target-i386/helper.h b/qemu/target-i386/helper.h index 8ab196ac..d3b52d1f 100644 --- a/qemu/target-i386/helper.h +++ b/qemu/target-i386/helper.h @@ -1,4 +1,4 @@ -DEF_HELPER_5(uc_tracecode, void, i32, ptr, ptr, i64, ptr) +DEF_HELPER_4(uc_tracecode, void, i32, i32, ptr, i64) DEF_HELPER_FLAGS_4(cc_compute_all, TCG_CALL_NO_RWG_SE, tl, tl, tl, tl, int) DEF_HELPER_FLAGS_4(cc_compute_c, TCG_CALL_NO_RWG_SE, tl, tl, tl, tl, int) diff --git a/qemu/target-i386/seg_helper.c b/qemu/target-i386/seg_helper.c index 42b0f37c..4702dfcc 100644 --- a/qemu/target-i386/seg_helper.c +++ b/qemu/target-i386/seg_helper.c @@ -945,14 +945,16 @@ void helper_syscall(CPUX86State *env, int next_eip_addend) #else void helper_syscall(CPUX86State *env, int next_eip_addend) { - // Unicorn: call interrupt callback if registered - struct uc_struct *uc = env->uc; - if (uc->hook_syscall_idx) { - ((uc_cb_insn_syscall_t)uc->hook_callbacks[uc->hook_syscall_idx].callback)( - uc, uc->hook_callbacks[uc->hook_syscall_idx].user_data); + // Unicorn: call registered syscall hooks + struct hook *hook; + HOOK_FOREACH(env->uc, hook, UC_HOOK_INSN) { + if (!HOOK_BOUND_CHECK(hook, env->eip)) + continue; + if (hook->insn == UC_X86_INS_SYSCALL) + ((uc_cb_insn_syscall_t)hook->callback)(env->uc, hook->user_data); } - env->eip += next_eip_addend; + env->eip += next_eip_addend; return; int selector; @@ -2303,14 +2305,16 @@ void helper_lret_protected(CPUX86State *env, int shift, int addend) void helper_sysenter(CPUX86State *env, int next_eip_addend) { - // Unicorn: call interrupt callback if registered - struct uc_struct *uc = env->uc; - if (uc->hook_syscall_idx) { - ((uc_cb_insn_syscall_t)uc->hook_callbacks[uc->hook_syscall_idx].callback)( - uc, uc->hook_callbacks[uc->hook_syscall_idx].user_data); + // Unicorn: call registered SYSENTER hooks + struct hook *hook; + HOOK_FOREACH(env->uc, hook, UC_HOOK_INSN) { + if (!HOOK_BOUND_CHECK(hook, env->eip)) + continue; + if (hook->insn == UC_X86_INS_SYSENTER) + ((uc_cb_insn_syscall_t)hook->callback)(env->uc, hook->user_data); } - env->eip += next_eip_addend; + env->eip += next_eip_addend; return; if (env->sysenter_cs == 0) { diff --git a/qemu/target-i386/translate.c b/qemu/target-i386/translate.c index 285ba32c..d68cb516 100644 --- a/qemu/target-i386/translate.c +++ b/qemu/target-i386/translate.c @@ -516,14 +516,14 @@ static inline void gen_op_addq_A0_reg_sN(TCGContext *s, int shift, int reg) static inline void gen_op_ld_v(DisasContext *s, int idx, TCGv t0, TCGv a0) { - if (s->uc->hook_mem_read) + if (HOOK_EXISTS(s->uc, UC_HOOK_MEM_READ)) gen_jmp_im(s, s->prev_pc); // Unicorn: sync EIP tcg_gen_qemu_ld_tl(s->uc, t0, a0, s->mem_index, idx | MO_LE); } static inline void gen_op_st_v(DisasContext *s, int idx, TCGv t0, TCGv a0) { - if (s->uc->hook_mem_write) + if (HOOK_EXISTS(s->uc, UC_HOOK_MEM_WRITE)) gen_jmp_im(s, s->prev_pc); // Unicorn: sync EIP tcg_gen_qemu_st_tl(s->uc, t0, a0, s->mem_index, idx | MO_LE); } @@ -4745,12 +4745,10 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, TCGv cpu_tmp4 = *(TCGv *)tcg_ctx->cpu_tmp4; TCGv **cpu_T = (TCGv **)tcg_ctx->cpu_T; TCGv **cpu_regs = (TCGv **)tcg_ctx->cpu_regs; - struct hook_struct *trace = NULL; TCGArg *save_opparam_ptr = tcg_ctx->gen_opparam_ptr; bool cc_op_dirty = s->cc_op_dirty; bool changed_cc_op = false; - s->pc = pc_start; // end address tells us to stop emulation @@ -4768,19 +4766,15 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, } // Unicorn: trace this instruction on request - if (env->uc->hook_insn) { - trace = hook_find(env->uc, UC_HOOK_CODE, pc_start); - if (trace) { - if (s->last_cc_op != s->cc_op) { - sync_eflags(s, tcg_ctx); - s->last_cc_op = s->cc_op; - changed_cc_op = true; - } - // generate code to call callback - gen_uc_tracecode(tcg_ctx, 0xf1f1f1f1, trace->callback, env->uc, pc_start, trace->user_data); - // the callback might want to stop emulation immediately - check_exit_request(tcg_ctx); + if (HOOK_EXISTS_BOUNDED(env->uc, UC_HOOK_CODE, pc_start)) { + if (s->last_cc_op != s->cc_op) { + sync_eflags(s, tcg_ctx); + s->last_cc_op = s->cc_op; + changed_cc_op = true; } + gen_uc_tracecode(tcg_ctx, 0xf1f1f1f1, UC_HOOK_CODE_IDX, env->uc, pc_start); + // the callback might want to stop emulation immediately + check_exit_request(tcg_ctx); } prefixes = 0; @@ -8173,7 +8167,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, gen_helper_unlock(tcg_ctx, cpu_env); // Unicorn: patch the callback for the instruction size - if (trace) { + if (HOOK_EXISTS_BOUNDED(env->uc, UC_HOOK_CODE, pc_start)) { // int i; // for(i = 0; i < 20; i++) // printf("=== [%u] = %x\n", i, *(save_opparam_ptr + i)); @@ -8387,12 +8381,9 @@ static inline void gen_intermediate_code_internal(uint8_t *gen_opc_cc_op, // Unicorn: trace this block on request // Only hook this block if it is not broken from previous translation due to // full translation cache - if (env->uc->hook_block && !env->uc->block_full) { - struct hook_struct *trace = hook_find(env->uc, UC_HOOK_BLOCK, pc_start); - if (trace) { - env->uc->block_addr = pc_start; - gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, trace->callback, env->uc, pc_start, trace->user_data); - } + if (!env->uc->block_full && HOOK_EXISTS_BOUNDED(env->uc, UC_HOOK_BLOCK, pc_start)) { + env->uc->block_addr = pc_start; + gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, UC_HOOK_BLOCK_IDX, env->uc, pc_start); } gen_tb_start(tcg_ctx); diff --git a/qemu/target-i386/unicorn.c b/qemu/target-i386/unicorn.c index 3ac5d22f..c6a39be1 100644 --- a/qemu/target-i386/unicorn.c +++ b/qemu/target-i386/unicorn.c @@ -59,7 +59,6 @@ void x86_reg_reset(struct uc_struct *uc) env->features[FEAT_8000_0001_ECX] = CPUID_EXT3_LAHF_LM | CPUID_EXT3_ABM | CPUID_EXT3_SKINIT | CPUID_EXT3_CR8LEG; env->features[FEAT_7_0_EBX] = CPUID_7_0_EBX_BMI1 | CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_ADX | CPUID_7_0_EBX_SMAP; - env->invalid_error = UC_ERR_OK; // no error memset(env->regs, 0, sizeof(env->regs)); memset(env->segs, 0, sizeof(env->segs)); memset(env->cr, 0, sizeof(env->cr)); @@ -658,9 +657,15 @@ int x86_reg_write(struct uc_struct *uc, unsigned int regid, const void *value) break; case UC_X86_REG_EIP: X86_CPU(uc, mycpu)->env.eip = *(uint32_t *)value; + // force to quit execution and flush TB + uc->quit_request = true; + uc_emu_stop(uc); break; case UC_X86_REG_IP: WRITE_WORD(X86_CPU(uc, mycpu)->env.eip, *(uint16_t *)value); + // force to quit execution and flush TB + uc->quit_request = true; + uc_emu_stop(uc); break; case UC_X86_REG_CS: X86_CPU(uc, mycpu)->env.segs[R_CS].base = *(uint32_t *)value; @@ -808,12 +813,21 @@ int x86_reg_write(struct uc_struct *uc, unsigned int regid, const void *value) break; case UC_X86_REG_RIP: X86_CPU(uc, mycpu)->env.eip = *(uint64_t *)value; + // force to quit execution and flush TB + uc->quit_request = true; + uc_emu_stop(uc); break; case UC_X86_REG_EIP: WRITE_DWORD(X86_CPU(uc, mycpu)->env.eip, *(uint32_t *)value); + // force to quit execution and flush TB + uc->quit_request = true; + uc_emu_stop(uc); break; case UC_X86_REG_IP: WRITE_WORD(X86_CPU(uc, mycpu)->env.eip, *(uint16_t *)value); + // force to quit execution and flush TB + uc->quit_request = true; + uc_emu_stop(uc); break; case UC_X86_REG_CS: X86_CPU(uc, mycpu)->env.segs[R_CS].base = *(uint64_t *)value; diff --git a/qemu/target-m68k/helper.h b/qemu/target-m68k/helper.h index 865cf95d..caaadb3a 100644 --- a/qemu/target-m68k/helper.h +++ b/qemu/target-m68k/helper.h @@ -1,4 +1,4 @@ -DEF_HELPER_5(uc_tracecode, void, i32, ptr, ptr, i64, ptr) +DEF_HELPER_4(uc_tracecode, void, i32, i32, ptr, i64) DEF_HELPER_1(bitrev, i32, i32) DEF_HELPER_1(ff1, i32, i32) diff --git a/qemu/target-m68k/translate.c b/qemu/target-m68k/translate.c index 8c2951d9..bf45a66a 100644 --- a/qemu/target-m68k/translate.c +++ b/qemu/target-m68k/translate.c @@ -3043,11 +3043,8 @@ static void disas_m68k_insn(CPUM68KState * env, DisasContext *s) } // Unicorn: trace this instruction on request - if (env->uc->hook_insn) { - struct hook_struct *trace = hook_find(env->uc, UC_HOOK_CODE, s->pc); - if (trace) - gen_uc_tracecode(tcg_ctx, 2, trace->callback, env->uc, s->pc, trace->user_data); - + if (HOOK_EXISTS_BOUNDED(env->uc, UC_HOOK_CODE, s->pc)) { + gen_uc_tracecode(tcg_ctx, 2, UC_HOOK_CODE_IDX, env->uc, s->pc); // the callback might want to stop emulation immediately check_exit_request(tcg_ctx); } @@ -3109,13 +3106,10 @@ gen_intermediate_code_internal(M68kCPU *cpu, TranslationBlock *tb, // Unicorn: trace this block on request // Only hook this block if it is not broken from previous translation due to // full translation cache - if (env->uc->hook_block && !env->uc->block_full) { - struct hook_struct *trace = hook_find(env->uc, UC_HOOK_BLOCK, pc_start); - if (trace) { - // save block address to see if we need to patch block size later - env->uc->block_addr = pc_start; - gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, trace->callback, env->uc, pc_start, trace->user_data); - } + if (!env->uc->block_full && HOOK_EXISTS_BOUNDED(env->uc, UC_HOOK_BLOCK, pc_start)) { + // save block address to see if we need to patch block size later + env->uc->block_addr = pc_start; + gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, UC_HOOK_BLOCK_IDX, env->uc, pc_start); } gen_tb_start(tcg_ctx); diff --git a/qemu/target-m68k/unicorn.c b/qemu/target-m68k/unicorn.c index df732405..0055d4a1 100644 --- a/qemu/target-m68k/unicorn.c +++ b/qemu/target-m68k/unicorn.c @@ -70,6 +70,9 @@ int m68k_reg_write(struct uc_struct *uc, unsigned int regid, const void *value) default: break; case UC_M68K_REG_PC: M68K_CPU(uc, mycpu)->env.pc = *(uint32_t *)value; + // force to quit execution and flush TB + uc->quit_request = true; + uc_emu_stop(uc); break; } } diff --git a/qemu/target-mips/helper.h b/qemu/target-mips/helper.h index 81f733c2..1924bf6f 100644 --- a/qemu/target-mips/helper.h +++ b/qemu/target-mips/helper.h @@ -1,4 +1,4 @@ -DEF_HELPER_5(uc_tracecode, void, i32, ptr, ptr, i64, ptr) +DEF_HELPER_4(uc_tracecode, void, i32, i32, ptr, i64) DEF_HELPER_3(raise_exception_err, noreturn, env, i32, int) DEF_HELPER_2(raise_exception, noreturn, env, i32) diff --git a/qemu/target-mips/translate.c b/qemu/target-mips/translate.c index 75465915..ea9aa453 100644 --- a/qemu/target-mips/translate.c +++ b/qemu/target-mips/translate.c @@ -11343,12 +11343,9 @@ static int decode_mips16_opc (CPUMIPSState *env, DisasContext *ctx, bool *insn_n n_bytes = 2; // Unicorn: trace this instruction on request - if (env->uc->hook_insn) { - struct hook_struct *trace = hook_find(env->uc, UC_HOOK_CODE, ctx->pc); - if (trace) { - gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, trace->callback, env->uc, ctx->pc, trace->user_data); - *insn_need_patch = true; - } + if (HOOK_EXISTS_BOUNDED(env->uc, UC_HOOK_CODE, ctx->pc)) { + gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, UC_HOOK_CODE_IDX, env->uc, ctx->pc); + *insn_need_patch = true; // the callback might want to stop emulation immediately check_exit_request(tcg_ctx); } @@ -13942,12 +13939,9 @@ static int decode_micromips_opc (CPUMIPSState *env, DisasContext *ctx, bool *ins } // Unicorn: trace this instruction on request - if (env->uc->hook_insn) { - struct hook_struct *trace = hook_find(env->uc, UC_HOOK_CODE, ctx->pc); - if (trace) { - gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, trace->callback, env->uc, ctx->pc, trace->user_data); - *insn_need_patch = true; - } + if (HOOK_EXISTS_BOUNDED(env->uc, UC_HOOK_CODE, ctx->pc)) { + gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, UC_HOOK_CODE_IDX, env->uc, ctx->pc); + *insn_need_patch = true; // the callback might want to stop emulation immediately check_exit_request(tcg_ctx); } @@ -18504,13 +18498,10 @@ static void gen_msa(CPUMIPSState *env, DisasContext *ctx) // Unicorn: trace this instruction on request static void hook_insn(CPUMIPSState *env, DisasContext *ctx, bool *insn_need_patch, int *insn_patch_offset, int offset_value) { - if (env->uc->hook_insn) { - TCGContext *tcg_ctx = ctx->uc->tcg_ctx; - struct hook_struct *trace = hook_find(env->uc, UC_HOOK_CODE, ctx->pc); - if (trace) { - gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, trace->callback, env->uc, ctx->pc, trace->user_data); - *insn_need_patch = true; - } + TCGContext *tcg_ctx = ctx->uc->tcg_ctx; + if (HOOK_EXISTS_BOUNDED(env->uc, UC_HOOK_CODE, ctx->pc)) { + gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, UC_HOOK_CODE_IDX, env->uc, ctx->pc); + *insn_need_patch = true; // the callback might want to stop emulation immediately check_exit_request(tcg_ctx); *insn_patch_offset = offset_value; @@ -19223,13 +19214,10 @@ gen_intermediate_code_internal(MIPSCPU *cpu, TranslationBlock *tb, // Unicorn: trace this block on request // Only hook this block if it is not broken from previous translation due to // full translation cache - if (env->uc->hook_block && !env->uc->block_full) { - struct hook_struct *trace = hook_find(env->uc, UC_HOOK_BLOCK, pc_start); - if (trace) { - // save block address to see if we need to patch block size later - env->uc->block_addr = pc_start; - gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, trace->callback, env->uc, pc_start, trace->user_data); - } + if (!env->uc->block_full && HOOK_EXISTS_BOUNDED(env->uc, UC_HOOK_BLOCK, pc_start)) { + // save block address to see if we need to patch block size later + env->uc->block_addr = pc_start; + gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, UC_HOOK_BLOCK_IDX, env->uc, pc_start); } gen_tb_start(tcg_ctx); @@ -19275,7 +19263,7 @@ gen_intermediate_code_internal(MIPSCPU *cpu, TranslationBlock *tb, int insn_patch_offset = 1; // Unicorn: save param buffer - if (env->uc->hook_insn) + if (HOOK_EXISTS(env->uc, UC_HOOK_CODE)) save_opparam_ptr = tcg_ctx->gen_opparam_ptr; is_slot = ctx.hflags & MIPS_HFLAG_BMASK; diff --git a/qemu/target-mips/unicorn.c b/qemu/target-mips/unicorn.c index 3885d02d..6af98dce 100644 --- a/qemu/target-mips/unicorn.c +++ b/qemu/target-mips/unicorn.c @@ -81,6 +81,9 @@ int mips_reg_write(struct uc_struct *uc, unsigned int regid, const void *value) default: break; case UC_MIPS_REG_PC: MIPS_CPU(uc, mycpu)->env.active_tc.PC = *(uint32_t *)value; + // force to quit execution and flush TB + uc->quit_request = true; + uc_emu_stop(uc); break; } } diff --git a/qemu/target-sparc/helper.h b/qemu/target-sparc/helper.h index 2557dd08..503e1e5c 100644 --- a/qemu/target-sparc/helper.h +++ b/qemu/target-sparc/helper.h @@ -1,4 +1,4 @@ -DEF_HELPER_5(uc_tracecode, void, i32, ptr, ptr, i64, ptr) +DEF_HELPER_4(uc_tracecode, void, i32, i32, ptr, i64) DEF_HELPER_1(power_down, void, env) #ifndef TARGET_SPARC64 diff --git a/qemu/target-sparc/translate.c b/qemu/target-sparc/translate.c index fc71e88b..a7f067cf 100644 --- a/qemu/target-sparc/translate.c +++ b/qemu/target-sparc/translate.c @@ -2637,11 +2637,8 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn, bool hook_ins } // Unicorn: trace this instruction on request - if (hook_insn && dc->uc->hook_insn) { - struct hook_struct *trace = hook_find(dc->uc, UC_HOOK_CODE, dc->pc); - if (trace) - gen_uc_tracecode(tcg_ctx, 4, trace->callback, dc->uc, dc->pc, trace->user_data); - + if (hook_insn && HOOK_EXISTS_BOUNDED(dc->uc, UC_HOOK_CODE, dc->pc)) { + gen_uc_tracecode(tcg_ctx, 4, UC_HOOK_CODE_IDX, dc->uc, dc->pc); // the callback might want to stop emulation immediately check_exit_request(tcg_ctx); } @@ -5428,13 +5425,10 @@ static inline void gen_intermediate_code_internal(SPARCCPU *cpu, // Unicorn: trace this block on request // Only hook this block if it is not broken from previous translation due to // full translation cache - if (env->uc->hook_block && !env->uc->block_full) { - struct hook_struct *trace = hook_find(env->uc, UC_HOOK_BLOCK, pc_start); - if (trace) { - // save block address to see if we need to patch block size later - env->uc->block_addr = pc_start; - gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, trace->callback, env->uc, pc_start, trace->user_data); - } + if (!env->uc->block_full && HOOK_EXISTS_BOUNDED(env->uc, UC_HOOK_BLOCK, pc_start)) { + // save block address to see if we need to patch block size later + env->uc->block_addr = pc_start; + gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, UC_HOOK_BLOCK_IDX, env->uc, pc_start); } gen_tb_start(tcg_ctx); diff --git a/qemu/target-sparc/unicorn.c b/qemu/target-sparc/unicorn.c index d86497b5..e612457b 100644 --- a/qemu/target-sparc/unicorn.c +++ b/qemu/target-sparc/unicorn.c @@ -87,6 +87,9 @@ int sparc_reg_write(struct uc_struct *uc, unsigned int regid, const void *value) case UC_SPARC_REG_PC: SPARC_CPU(uc, mycpu)->env.pc = *(uint32_t *)value; SPARC_CPU(uc, mycpu)->env.npc = *(uint32_t *)value + 4; + // force to quit execution and flush TB + uc->quit_request = true; + uc_emu_stop(uc); break; } } diff --git a/qemu/tcg/aarch64/tcg-target.c b/qemu/tcg/aarch64/tcg-target.c index 6dff1756..ce8360f6 100644 --- a/qemu/tcg/aarch64/tcg-target.c +++ b/qemu/tcg/aarch64/tcg-target.c @@ -1241,7 +1241,7 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, { /* 99% of the time, we can signal the use of extension registers by looking to see if the opcode handles 64-bit data. */ - TCGType ext = (tcg_op_defs[opc].flags & TCG_OPF_64BIT) != 0; + TCGType ext = (s->tcg_op_defs[opc].flags & TCG_OPF_64BIT) != 0; /* Hoist the loads of the most common arguments. */ TCGArg a0 = args[0]; diff --git a/qemu/tcg/arm/tcg-target.c b/qemu/tcg/arm/tcg-target.c index cabbd1f1..17289d92 100644 --- a/qemu/tcg/arm/tcg-target.c +++ b/qemu/tcg/arm/tcg-target.c @@ -56,8 +56,8 @@ static int arm_arch = __ARM_ARCH; #define use_armv6_instructions (__ARM_ARCH >= 6 || arm_arch >= 6) #define use_armv7_instructions (__ARM_ARCH >= 7 || arm_arch >= 7) -#ifndef use_idiv_instructions -bool use_idiv_instructions; +#ifndef __ARM_ARCH_EXT_IDIV__ +bool use_idiv_instructions_rt; #endif /* ??? Ought to think about changing CONFIG_SOFTMMU to always defined. */ @@ -1981,10 +1981,10 @@ static void tcg_target_init(TCGContext *s) { /* Only probe for the platform and capabilities if we havn't already determined maximum values at compile time. */ -#ifndef use_idiv_instructions +#ifndef __ARM_ARCH_EXT_IDIV__ { unsigned long hwcap = qemu_getauxval(AT_HWCAP); - use_idiv_instructions = (hwcap & HWCAP_ARM_IDIVA) != 0; + use_idiv_instructions_rt = (hwcap & HWCAP_ARM_IDIVA) != 0; } #endif if (__ARM_ARCH < 7) { diff --git a/qemu/tcg/arm/tcg-target.h b/qemu/tcg/arm/tcg-target.h index 1c719e28..a6ea9763 100644 --- a/qemu/tcg/arm/tcg-target.h +++ b/qemu/tcg/arm/tcg-target.h @@ -52,7 +52,8 @@ typedef enum { #ifdef __ARM_ARCH_EXT_IDIV__ #define use_idiv_instructions 1 #else -extern bool use_idiv_instructions; +extern bool use_idiv_instructions_rt; +#define use_idiv_instructions use_idiv_instructions_rt #endif diff --git a/qemu/tcg/i386/tcg-target.c b/qemu/tcg/i386/tcg-target.c index aa06cc18..414c4a72 100644 --- a/qemu/tcg/i386/tcg-target.c +++ b/qemu/tcg/i386/tcg-target.c @@ -1209,7 +1209,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 (!s->uc->hook_mem_read && !s->uc->hook_mem_write) + if (!HOOK_EXISTS(s->uc, UC_HOOK_MEM_READ) && !HOOK_EXISTS(s->uc, UC_HOOK_MEM_WRITE)) tcg_out_opc(s, OPC_JCC_long + JCC_JNE, 0, 0, 0); else tcg_out_opc(s, OPC_JMP_long, 0, 0, 0); /* slow_path */ diff --git a/qemu/tcg/tcg-op.h b/qemu/tcg/tcg-op.h index 9f096a3d..87358240 100644 --- a/qemu/tcg/tcg-op.h +++ b/qemu/tcg/tcg-op.h @@ -27,14 +27,13 @@ int gen_new_label(TCGContext *); -static inline void gen_uc_tracecode(TCGContext *tcg_ctx, int32_t size, void *callback, void *uc, uint64_t pc, void *data) +static inline void gen_uc_tracecode(TCGContext *tcg_ctx, int32_t size, int32_t type, void *uc, uint64_t pc) { TCGv_i32 tsize = tcg_const_i32(tcg_ctx, size); - TCGv_ptr tcallback = tcg_const_ptr(tcg_ctx, callback); + TCGv_i32 ttype = tcg_const_i32(tcg_ctx, type); TCGv_ptr tuc = tcg_const_ptr(tcg_ctx, uc); TCGv_i64 tpc = tcg_const_i64(tcg_ctx, pc); - TCGv_ptr tdata = tcg_const_ptr(tcg_ctx, data); - gen_helper_uc_tracecode(tcg_ctx, tsize, tcallback, tuc, tpc, tdata); + gen_helper_uc_tracecode(tcg_ctx, tsize, ttype, tuc, tpc); } static inline void tcg_gen_op0(TCGContext *s, TCGOpcode opc) diff --git a/qemu/translate-all.c b/qemu/translate-all.c index 11bbb07b..aec38c7c 100644 --- a/qemu/translate-all.c +++ b/qemu/translate-all.c @@ -179,7 +179,7 @@ static int cpu_gen_code(CPUArchState *env, TranslationBlock *tb, int *gen_code_s gen_intermediate_code(env, tb); // Unicorn: when tracing block, patch 1st operand for block size - if (env->uc->hook_block && env->uc->block_addr == tb->pc) { + if (env->uc->block_addr == tb->pc && HOOK_EXISTS_BOUNDED(env->uc, UC_HOOK_BLOCK, tb->pc)) { if (env->uc->block_full) // block size is unknown *(s->gen_opparam_buf + 1) = 0; else diff --git a/qemu/util/Makefile.objs b/qemu/util/Makefile.objs index 25e6bf34..9f4021b9 100644 --- a/qemu/util/Makefile.objs +++ b/qemu/util/Makefile.objs @@ -8,3 +8,4 @@ util-obj-y += aes.o util-obj-y += qemu-option.o util-obj-y += crc32c.o util-obj-y += host-utils.o +util-obj-y += getauxval.o diff --git a/qemu/util/getauxval.c b/qemu/util/getauxval.c new file mode 100644 index 00000000..208bfa39 --- /dev/null +++ b/qemu/util/getauxval.c @@ -0,0 +1,109 @@ +/* + * QEMU access to the auxiliary vector + * + * Copyright (C) 2013 Red Hat, Inc + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu-common.h" +#include "qemu/osdep.h" + +#ifdef CONFIG_GETAUXVAL +/* Don't inline this in qemu/osdep.h, because pulling in for + the system declaration of getauxval pulls in the system , which + conflicts with qemu's version. */ + +#include + +unsigned long qemu_getauxval(unsigned long key) +{ + return getauxval(key); +} +#elif defined(__linux__) +#include "elf.h" + +/* Our elf.h doesn't contain Elf32_auxv_t and Elf64_auxv_t, which is ok because + that just makes it easier to define it properly for the host here. */ +typedef struct { + unsigned long a_type; + unsigned long a_val; +} ElfW_auxv_t; + +static const ElfW_auxv_t *auxv; + +static const ElfW_auxv_t *qemu_init_auxval(void) +{ + ElfW_auxv_t *a; + ssize_t size = 512, r, ofs; + int fd; + + /* Allocate some initial storage. Make sure the first entry is set + to end-of-list, so that we've got a valid list in case of error. */ + auxv = a = g_malloc(size); + a[0].a_type = 0; + a[0].a_val = 0; + + fd = open("/proc/self/auxv", O_RDONLY); + if (fd < 0) { + return a; + } + + /* Read the first SIZE bytes. Hopefully, this covers everything. */ + r = read(fd, a, size); + + if (r == size) { + /* Continue to expand until we do get a partial read. */ + do { + ofs = size; + size *= 2; + auxv = a = g_realloc(a, size); + r = read(fd, (char *)a + ofs, ofs); + } while (r == ofs); +} + + close(fd); + return a; +} + +unsigned long qemu_getauxval(unsigned long type) +{ + const ElfW_auxv_t *a = auxv; + + if (unlikely(a == NULL)) { + a = qemu_init_auxval(); + } + + for (; a->a_type != 0; a++) { + if (a->a_type == type) { + return a->a_val; + } + } + + return 0; +} + +#else + +unsigned long qemu_getauxval(unsigned long type) +{ + return 0; +} + +#endif diff --git a/qemu/util/qemu-thread-posix.c b/qemu/util/qemu-thread-posix.c index a250044f..2fbc0bd2 100644 --- a/qemu/util/qemu-thread-posix.c +++ b/qemu/util/qemu-thread-posix.c @@ -29,8 +29,8 @@ static void error_exit(int err, const char *msg) { - // fprintf(stderr, "qemu: %s: %s\n", msg, strerror(err)); - // abort(); + fprintf(stderr, "qemu: %s: %s\n", msg, strerror(err)); + abort(); } void qemu_mutex_init(QemuMutex *mutex) diff --git a/qemu/x86_64.h b/qemu/x86_64.h index 882a7b0d..ce8e0262 100644 --- a/qemu/x86_64.h +++ b/qemu/x86_64.h @@ -1,6 +1,9 @@ /* Autogen header for Unicorn Engine - DONOT MODIFY */ #ifndef UNICORN_AUTOGEN_X86_64_H #define UNICORN_AUTOGEN_X86_64_H +#define aarch64_tb_set_jmp_target aarch64_tb_set_jmp_target_x86_64 +#define use_idiv_instructions_rt use_idiv_instructions_rt_x86_64 +#define tcg_target_deposit_valid tcg_target_deposit_valid_x86_64 #define helper_power_down helper_power_down_x86_64 #define check_exit_request check_exit_request_x86_64 #define address_space_unregister address_space_unregister_x86_64 @@ -2806,7 +2809,6 @@ #define tb_phys_invalidate tb_phys_invalidate_x86_64 #define tb_reset_jump tb_reset_jump_x86_64 #define tb_set_jmp_target tb_set_jmp_target_x86_64 -#define tb_set_jmp_target1 tb_set_jmp_target1_x86_64 #define tcg_accel_class_init tcg_accel_class_init_x86_64 #define tcg_accel_type tcg_accel_type_x86_64 #define tcg_add_param_i32 tcg_add_param_i32_x86_64 diff --git a/samples/Makefile b/samples/Makefile index 0904512a..02a1f66d 100644 --- a/samples/Makefile +++ b/samples/Makefile @@ -27,7 +27,7 @@ LIBDIR = $(BUILDDIR) endif CFLAGS += -Wall -I$(INCDIR) -LDFLAGS += -L$(LIBDIR) -l$(LIBNAME) +LDFLAGS += -lpthread -L$(LIBDIR) -l$(LIBNAME) LDFLAGS_STATIC += $(UNICORN_DEP_LIBS_STATIC) ifeq ($(CROSS),) diff --git a/samples/mem_apis.c b/samples/mem_apis.c index 50554eeb..008f077d 100644 --- a/samples/mem_apis.c +++ b/samples/mem_apis.c @@ -142,13 +142,13 @@ static void do_nx_demo(bool cause_fault) /* bits 32 - page0: + page0: @0 times 4091 inc eax jmp page2 - page1: - times 4095 inc eax + page1: @1000 + times 4095 inc eax (or INC ECX) hlt - page2: + page2: @2000 jmp page1 */ memset(code_buf, 0x40, sizeof(code_buf)); // fill with inc eax @@ -170,7 +170,7 @@ static void do_nx_demo(bool cause_fault) // intercept code and invalid memory events if (uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)1, (uint64_t)0) != UC_ERR_OK || uc_hook_add(uc, &trace1, UC_HOOK_MEM_INVALID, - hook_mem_invalid, NULL) != UC_ERR_OK) { + hook_mem_invalid, NULL, (uint64_t)1, (uint64_t)0) != UC_ERR_OK) { printf("not ok - Failed to install hooks\n"); return; } @@ -251,7 +251,7 @@ static void do_perms_demo(bool change_perms) if (uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)1, (uint64_t)0) != UC_ERR_OK || uc_hook_add(uc, &trace1, UC_HOOK_MEM_INVALID, - hook_mem_invalid, NULL) != UC_ERR_OK) { + hook_mem_invalid, NULL, (uint64_t)1, (uint64_t)0) != UC_ERR_OK) { printf("not ok - Failed to install hooks\n"); return; } @@ -329,7 +329,7 @@ static void do_unmap_demo(bool do_unmap) if (uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)1, (uint64_t)0) != UC_ERR_OK || uc_hook_add(uc, &trace1, UC_HOOK_MEM_INVALID, - hook_mem_invalid, NULL) != UC_ERR_OK) { + hook_mem_invalid, NULL, (uint64_t)1, (uint64_t)0) != UC_ERR_OK) { printf("not ok - Failed to install hooks\n"); return; } diff --git a/samples/sample_mips.c b/samples/sample_mips.c index 3f37b189..b27a02b1 100644 --- a/samples/sample_mips.c +++ b/samples/sample_mips.c @@ -105,7 +105,7 @@ static void test_mips_el(void) printf("Emulate MIPS code (little-endian)\n"); // Initialize emulator in MIPS mode - err = uc_open(UC_ARCH_MIPS, UC_MODE_MIPS32, &uc); + err = uc_open(UC_ARCH_MIPS, UC_MODE_MIPS32 + UC_MODE_LITTLE_ENDIAN, &uc); if (err) { printf("Failed on uc_open() with error returned: %u (%s)\n", err, uc_strerror(err)); diff --git a/samples/sample_sparc.c b/samples/sample_sparc.c index e966f5af..e6276b14 100644 --- a/samples/sample_sparc.c +++ b/samples/sample_sparc.c @@ -57,7 +57,7 @@ static void test_sparc(void) printf("Emulate SPARC code\n"); // Initialize emulator in Sparc mode - err = uc_open(UC_ARCH_SPARC, UC_MODE_32, &uc); + err = uc_open(UC_ARCH_SPARC, UC_MODE_SPARC32|UC_MODE_BIG_ENDIAN, &uc); if (err) { printf("Failed on uc_open() with error returned: %u (%s)\n", err, uc_strerror(err)); diff --git a/tests/regress/Makefile b/tests/regress/Makefile index 388b0b12..f89f7c88 100644 --- a/tests/regress/Makefile +++ b/tests/regress/Makefile @@ -36,6 +36,7 @@ TESTS += emu_stop_in_hook_overrun TESTS += mips_branch_likely_issue TESTS += hook_extrainvoke TESTS += sysenter_hook_x86 +TESTS += emu_clear_errors TESTS += memleak_x86 TESTS += memleak_arm diff --git a/tests/regress/arm_vldr_invalid.py b/tests/regress/arm_vldr_invalid.py new file mode 100755 index 00000000..febf93e7 --- /dev/null +++ b/tests/regress/arm_vldr_invalid.py @@ -0,0 +1,18 @@ +#!/usr/bin/python + +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) + +if __name__ == '__main__': + regress.main() diff --git a/tests/regress/emu_clear_errors.c b/tests/regress/emu_clear_errors.c new file mode 100644 index 00000000..36c4e745 --- /dev/null +++ b/tests/regress/emu_clear_errors.c @@ -0,0 +1,154 @@ +#include +#include +#include +#include +#include +#include + +#include + +static int count = 1; + +bool cb_hookunmapped(uc_engine *uc, uc_mem_type type, uint64_t address, uint32_t size, int64_t value, void *user_data) { + uint32_t pc = 0; + uc_reg_read(uc, UC_X86_REG_EIP, &pc); + fprintf(stderr, "mem unmapped: 0x%x type: %x address: 0x%"PRIx64" length: %x value: 0x%"PRIx64"\n", + pc, type, address, size, value); + + uc_err err = UC_ERR_OK; + err = uc_emu_stop(uc); + if (err != UC_ERR_OK) { + fprintf(stderr, "stop not ok"); + exit(0); + } + return true; +} + +// move esi, dword ptr [ecx + eax + 0x28] +// add esi, eax +// lea eax, dword ptr [ebp - 4] +// push eax +// push 0x40 +// push 0x10 +// push esi +// call some address +#define CODE "\x8B\x74\x01\x28" \ + "\x0C\xF0" \ + "\x8D\x45\xFC" \ + "\x50" \ + "\x6A\x40" \ + "\x6A\x10" \ + "\x56" \ + "\xFF\x15\x20\x20\x00\x10" + +int main() { + uc_engine *uc; + + uc_err err = uc_open(UC_ARCH_X86, UC_MODE_32, &uc); + if (err != UC_ERR_OK) { + fprintf(stderr, "not ok %d - %s\n", count++, uc_strerror(err)); + exit(0); + } + fprintf(stderr, "ok %d - uc_open\n", count++); + + err = uc_mem_map(uc, 0x1000, 0x1000, UC_PROT_ALL); + if (err != UC_ERR_OK) { + fprintf(stderr, "not ok %d - %s\n", count++, uc_strerror(err)); + exit(0); + } + fprintf(stderr, "ok %d - uc_mem_map: code\n", count++); + + uint8_t code[0x1000]; + memset(code, 0x0, sizeof(code)); + memcpy(code, CODE, sizeof(CODE)); + + err = uc_mem_write(uc, 0x1000, code, sizeof(code)); + if (err != UC_ERR_OK) { + fprintf(stderr, "not ok %d - %s\n", count++, uc_strerror(err)); + exit(0); + } + fprintf(stderr, "ok %d - uc_mem_write: code\n", count++); + + uint32_t eip = 0x1000; + err = uc_reg_write(uc, UC_X86_REG_EIP, &eip); + if (err != UC_ERR_OK) { + fprintf(stderr, "not ok %d - %s\n", count++, uc_strerror(err)); + exit(0); + } + fprintf(stderr, "ok %d - uc_reg_write: eip\n", count++); + + err = uc_mem_map(uc, 0x4000, 0x4000, UC_PROT_ALL); + if (err != UC_ERR_OK) { + fprintf(stderr, "not ok %d - %s\n", count++, uc_strerror(err)); + exit(0); + } + fprintf(stderr, "ok %d - uc_mem_map: stack\n", count++); + + uint8_t stack[0x4000]; + memset(stack, 0x0, sizeof(stack)); + + err = uc_mem_write(uc, 0x4000, code, sizeof(code)); + if (err != UC_ERR_OK) { + fprintf(stderr, "not ok %d - %s\n", count++, uc_strerror(err)); + exit(0); + } + fprintf(stderr, "ok %d - uc_mem_write: stack\n", count++); + + uint32_t esp = 0x6000; + err = uc_reg_write(uc, UC_X86_REG_ESP, &esp); + if (err != UC_ERR_OK) { + fprintf(stderr, "not ok %d - %s\n", count++, uc_strerror(err)); + exit(0); + } + fprintf(stderr, "ok %d - uc_reg_write: esp\n", count++); + + uint32_t ebp = 0x6000; + err = uc_reg_write(uc, UC_X86_REG_EBP, &ebp); + if (err != UC_ERR_OK) { + fprintf(stderr, "not ok %d - %s\n", count++, uc_strerror(err)); + exit(0); + } + fprintf(stderr, "ok %d - uc_reg_write: ebp\n", count++); + + uc_hook h1; + + err = uc_hook_add(uc, &h1, UC_HOOK_MEM_UNMAPPED, cb_hookunmapped, NULL); + if (err != UC_ERR_OK) { + fprintf(stderr, "not ok %d - %s\n", count++, uc_strerror(err)); + exit(0); + } + fprintf(stderr, "ok %d - uc_hook_add\n", count++); + + // this should execute only a single instruction at 0x1000, because + // that instruction accesses invalid memory. + err = uc_emu_start(uc, 0x1000, 0x100F, 0, 0); + if (err != UC_ERR_OK) { + fprintf(stderr, "not ok %d - %s\n", count++, uc_strerror(err)); + exit(0); + } + fprintf(stderr, "ok %d - uc_emu_start\n", count++); + + // yes, not necessary, but to demonstrate the UC API is working as expected + eip = 0x1004; + err = uc_reg_write(uc, UC_X86_REG_EIP, &eip); + if (err != UC_ERR_OK) { + fprintf(stderr, "not ok %d - %s\n", count++, uc_strerror(err)); + exit(0); + } + fprintf(stderr, "ok %d - uc_reg_write: eip\n", count++); + + // this should execute the remaining instructions up to (but not includign) 0x100F. + // currently, it returns an error about an unmapped read. + // seems that this error should have been returned in the previous call + // to emu_start. + err = uc_emu_start(uc, 0x1004, 0x100F, 0, 0); + if (err != UC_ERR_OK) { + fprintf(stderr, "not ok %d - %s\n", count++, uc_strerror(err)); + exit(0); + } + fprintf(stderr, "ok %d - uc_emu_start\n", count++); + + fprintf(stderr, "ok %d - Done", count++); + + return 0; +} diff --git a/tests/regress/emu_clear_errors.py b/tests/regress/emu_clear_errors.py new file mode 100755 index 00000000..4fb6b1df --- /dev/null +++ b/tests/regress/emu_clear_errors.py @@ -0,0 +1,80 @@ +#!/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"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(" ", "")) + + +def showpc(mu): + pc = mu.reg_read(UC_X86_REG_EIP) + print("pc: 0x%x" % (pc)) + + +class HookCodeStopEmuTest(regress.RegressTest): + 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) + + # 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.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) + + # 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 + # lea eax, dword ptr [ebp - 4] 0x1006 + # push eax 0x1009 + # 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) + + +if __name__ == '__main__': + regress.main() diff --git a/tests/regress/invalid_write.py b/tests/regress/invalid_write.py new file mode 100755 index 00000000..b37305fc --- /dev/null +++ b/tests/regress/invalid_write.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python +# Test callback that returns False to cancel emulation + +from __future__ import print_function +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 + + +# callback for tracing invalid memory access (READ or WRITE) +def hook_mem_invalid(uc, access, address, size, value, user_data): + return False + + +class InvalidWrite(regress.RegressTest): + def test(self): + # Initialize emulator in X86-32bit mode + mu = Uc(UC_ARCH_X86, UC_MODE_32) + + # memory address where emulation starts + ADDRESS = 0x1000000 + + # 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, X86_CODE32_MEM_WRITE) + + # initialize machine registers + mu.reg_write(UC_X86_REG_ECX, 0x1234) + mu.reg_write(UC_X86_REG_EDX, 0x7890) + + # intercept invalid memory events + mu.hook_add(UC_HOOK_MEM_READ_UNMAPPED | UC_HOOK_MEM_WRITE_UNMAPPED, hook_mem_invalid) + + try: + # emulation should return with error UC_ERR_WRITE_UNMAPPED + mu.emu_start(ADDRESS, ADDRESS + len(X86_CODE32_MEM_WRITE)) + except UcError as e: + self.assertEqual(e.errno, UC_ERR_WRITE_UNMAPPED) + + +if __name__ == '__main__': + regress.main() diff --git a/tests/regress/sparc64.py b/tests/regress/sparc64.py index 27210bd1..34307639 100755 --- a/tests/regress/sparc64.py +++ b/tests/regress/sparc64.py @@ -5,7 +5,7 @@ from unicorn.sparc_const import * PAGE_SIZE = 1 * 1024 * 1024 -uc = Uc(UC_ARCH_SPARC, UC_MODE_64) +uc = Uc(UC_ARCH_SPARC, UC_MODE_SPARC64|UC_MODE_BIG_ENDIAN) uc.reg_write(UC_SPARC_REG_SP, 100) print 'writing sp = 100' diff --git a/tests/regress/sparc_jump_to_zero.c b/tests/regress/sparc_jump_to_zero.c index 99148f2f..538405f3 100644 --- a/tests/regress/sparc_jump_to_zero.c +++ b/tests/regress/sparc_jump_to_zero.c @@ -1,7 +1,7 @@ #include #define HARDWARE_ARCHITECTURE UC_ARCH_SPARC -#define HARDWARE_MODE UC_MODE_32 +#define HARDWARE_MODE UC_MODE_SPARC32|UC_MODE_BIG_ENDIAN #define MEMORY_STARTING_ADDRESS 0x1000000 #define MEMORY_SIZE 2 * 1024 * 1024 diff --git a/tests/regress/sparc_reg.py b/tests/regress/sparc_reg.py index 03832de9..3d55065f 100755 --- a/tests/regress/sparc_reg.py +++ b/tests/regress/sparc_reg.py @@ -5,7 +5,7 @@ from unicorn.sparc_const import * PAGE_SIZE = 1 * 1024 * 1024 -uc = Uc(UC_ARCH_SPARC, UC_MODE_32) +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) diff --git a/tests/unit/Makefile b/tests/unit/Makefile index e70c8071..67376fd2 100644 --- a/tests/unit/Makefile +++ b/tests/unit/Makefile @@ -4,7 +4,8 @@ CFLAGS += -L ../../ CFLAGS += -lcmocka -lunicorn CFLAGS += -I ../../include -ALL_TESTS = test_sanity test_x86 test_mem_map test_mem_high test_mem_map_ptr +ALL_TESTS = test_sanity test_x86 test_mem_map test_mem_high test_mem_map_ptr \ + test_tb_x86 test_multihook test_pc_change .PHONY: all all: ${ALL_TESTS} @@ -21,12 +22,18 @@ test: ${ALL_TESTS} ./test_mem_map ./test_mem_map_ptr ./test_mem_high + ./test_tb_x86 + ./test_multihook + ./test_pc_change test_sanity: test_sanity.c test_x86: test_x86.c test_mem_map: test_mem_map.c test_mem_map_ptr: test_mem_map_ptr.c test_mem_high: test_mem_high.c +test_tb_x86: test_tb_x86.c +test_multihook: test_multihook.c +test_pc_change: test_pc_change.c ${ALL_TESTS}: ${CC} ${CFLAGS} -o $@ $^ diff --git a/tests/unit/test_multihook.c b/tests/unit/test_multihook.c new file mode 100644 index 00000000..eb831865 --- /dev/null +++ b/tests/unit/test_multihook.c @@ -0,0 +1,111 @@ +#include "unicorn_test.h" +#include + +#define OK(x) uc_assert_success(x) + +/* Called before every test to set up a new instance */ +static int setup32(void **state) +{ + uc_engine *uc; + + OK(uc_open(UC_ARCH_X86, UC_MODE_32, &uc)); + + *state = uc; + return 0; +} + +/* Called after every test to clean up */ +static int teardown(void **state) +{ + uc_engine *uc = *state; + + OK(uc_close(uc)); + + *state = NULL; + return 0; +} + +/******************************************************************************/ + +struct bb { + uint64_t addr; + size_t size; +}; + +struct bbtest { + const struct bb *blocks; + unsigned int blocknum; +}; + + +static void test_basic_blocks_hook(uc_engine *uc, uint64_t address, uint32_t size, void *user_data) +{ + struct bbtest *bbtest = user_data; + const struct bb *bb = &bbtest->blocks[bbtest->blocknum]; + + printf("block hook 1: %d == %zu\n", size, bb->size); + assert_int_equal(address, bb->addr); + assert_int_equal((size_t)size, bb->size); +} + +static void test_basic_blocks_hook2(uc_engine *uc, uint64_t address, uint32_t size, void *user_data) +{ + struct bbtest *bbtest = user_data; + const struct bb *bb = &bbtest->blocks[bbtest->blocknum++]; + + printf("block hook 2: %d == %zu\n", size, bb->size); + assert_int_equal(address, bb->addr); + assert_int_equal((size_t)size, bb->size); +} + +static void test_basic_blocks(void **state) +{ + uc_engine *uc = *state; + uc_hook trace1, trace2; + +#define BASEADDR 0x1000000 + + uint64_t address = BASEADDR; + const uint8_t code[] = { + 0x33, 0xC0, // xor eax, eax + 0x90, // nop + 0x90, // nop + 0xEB, 0x00, // jmp $+2 + 0x90, // nop + 0x90, // nop + 0x90, // nop + }; + + static const struct bb blocks[] = { + {BASEADDR, 6}, + {BASEADDR+ 6, 3}, + }; + + struct bbtest bbtest = { + .blocks = blocks, + .blocknum = 0, + }; + + +#undef BASEADDR + + // map 2MB memory for this emulation + OK(uc_mem_map(uc, address, 2 * 1024 * 1024, UC_PROT_ALL)); + + // write machine code to be emulated to memory + OK(uc_mem_write(uc, address, code, sizeof(code))); + + // trace all basic blocks + OK(uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, test_basic_blocks_hook, &bbtest, (uint64_t)1, (uint64_t)0)); + OK(uc_hook_add(uc, &trace2, UC_HOOK_BLOCK, test_basic_blocks_hook2, &bbtest, (uint64_t)1, (uint64_t)0)); + + OK(uc_emu_start(uc, address, address+sizeof(code), 0, 0)); +} + +int main(void) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(test_basic_blocks, setup32, teardown), + }; + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/unit/test_pc_change.c b/tests/unit/test_pc_change.c new file mode 100644 index 00000000..7994e772 --- /dev/null +++ b/tests/unit/test_pc_change.c @@ -0,0 +1,104 @@ +// Test PC change during the callback. by Nguyen Anh Quynh, 2016 +#include "unicorn_test.h" +#include + +#define OK(x) uc_assert_success(x) + +/* Called before every test to set up a new instance */ +static int setup32(void **state) +{ + uc_engine *uc; + + OK(uc_open(UC_ARCH_X86, UC_MODE_32, &uc)); + + *state = uc; + return 0; +} + +/* Called after every test to clean up */ +static int teardown(void **state) +{ + uc_engine *uc = *state; + + OK(uc_close(uc)); + + *state = NULL; + return 0; +} + +/******************************************************************************/ + +static void test_code_hook(uc_engine *uc, uint64_t address, uint32_t size, void *user_data) +{ + uint8_t tmp[256]; + int32_t r_eip = 0x1000006; + printf("instruction at 0x%"PRIx64": ", address); + + if (!uc_mem_read(uc, address, tmp, size)) { + uint32_t i; + + for (i = 0; i < size; i++) { + printf("0x%x ", tmp[i]); + } + printf("\n"); + } + + if (address == 0x1000003) { + // change the PC to "inc EDX" + uc_reg_write(uc, UC_X86_REG_EIP, &r_eip); + } +} + +static void test_pc_change(void **state) +{ + uc_engine *uc = *state; + uc_hook trace1; + int32_t r_ecx = 3, r_edx = 15; + +#define BASEADDR 0x1000000 + + uint64_t address = BASEADDR; + const uint8_t code[] = { + 0x41, // inc ECX @0x1000000 + 0x41, // inc ECX + 0x41, // inc ECX + 0x41, // inc ECX @0x1000003 + 0x41, // inc ECX + 0x41, // inc ECX + + 0x42, // inc EDX @0x1000006 + 0x42, // inc EDX + }; + +#undef BASEADDR + + // map 2MB memory for this emulation + OK(uc_mem_map(uc, address, 2 * 1024 * 1024, UC_PROT_ALL)); + + // write machine code to be emulated to memory + OK(uc_mem_write(uc, address, code, sizeof(code))); + + uc_reg_write(uc, UC_X86_REG_ECX, &r_ecx); + uc_reg_write(uc, UC_X86_REG_EDX, &r_edx); + printf("ECX = %u, EDX = %u\n", r_ecx, r_edx); + + // trace all instructions + OK(uc_hook_add(uc, &trace1, UC_HOOK_CODE, test_code_hook, NULL, (uint64_t)1, (uint64_t)0)); + + OK(uc_emu_start(uc, address, address+sizeof(code), 0, 0)); + + uc_reg_read(uc, UC_X86_REG_ECX, &r_ecx); + uc_reg_read(uc, UC_X86_REG_EDX, &r_edx); + + printf("ECX = %u, EDX = %u\n", r_ecx, r_edx); + assert_int_equal(r_ecx, 6); + assert_int_equal(r_edx, 17); +} + +int main(void) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(test_pc_change, setup32, teardown), + }; + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/unit/test_tb_x86.c b/tests/unit/test_tb_x86.c new file mode 100644 index 00000000..bf691e8b --- /dev/null +++ b/tests/unit/test_tb_x86.c @@ -0,0 +1,311 @@ +/** + * Unicorn x86_32 self-modifying unit test + * + * This test demonstrates the flushing of instruction translation cache + * after a self-modification of Intel's x8's "IMUL Gv,Ev,Ib" instruction. + */ +#include "unicorn_test.h" +#include +#include +#include +#include + +#define RIP_NEXT_TO_THE_SELFMODIFY_OPCODE (1) + + +// Demostration of a self-modifying "IMUL eax,mem,Ib" opcode +// And the QEMU's ability to flush the translation buffer properly + +#define MIN(a, b) (a < b? a: b) + +#define CODE_SPACE (2 * 1024 * 1024) +#define PHY_STACK_REGION (0x60000000) + +#define X86_CODE32_ALPHA_MIXED \ + "\x89\xe1\xd9\xcd\xd9\x71\xf4\x5d\x55\x59\x49\x49\x49\x49\x49\x49" \ + "\x49\x49\x49\x49\x43\x43\x43\x43\x43\x43\x37\x51\x5a\x6a\x41\x58" \ + "\x50\x30\x41\x30\x41\x6b\x41\x41\x51\x32\x41\x42\x32\x42\x42\x30" \ + "\x42\x42\x41\x42\x58\x50\x38\x41\x42\x75\x4a\x49\x51\x51\x51\x52" \ + "\x47\x33\x47\x34\x51\x55\x51\x56\x50\x47\x47\x38\x47\x39\x50\x4a" \ + "\x50\x4b\x50\x4c\x50\x4d\x50\x4e\x50\x4f\x50\x50\x50\x31\x47\x42" \ + "\x47\x42\x50\x34\x50\x5a\x50\x45\x51\x52\x46\x32\x47\x31\x50\x4d" \ + "\x51\x51\x50\x4e\x41\x41" + + +/* Called before every test to set up a new instance */ +static int setup(void **state) +{ + uc_engine *uc; + + uc_assert_success(uc_open(UC_ARCH_X86, UC_MODE_64, &uc)); + + *state = uc; + return 0; +} + + +/* Called after every test to clean up */ +static int teardown(void **state) +{ + uc_engine *uc = *state; + + uc_assert_success(uc_close(uc)); + + *state = NULL; + return 0; +} + + + +static void dump_stack_mem(uc_engine *uc) +{ + uint8_t tmp[256]; + uint32_t size; + + size = sizeof(X86_CODE32_ALPHA_MIXED); + if (size > 255) size = 255; + if (!uc_mem_read(uc, PHY_STACK_REGION, tmp, size)) + { + uint32_t i; + + printf("Stack region dump"); + for (i=0; iinit_arch = m68k_uc_init; break; #endif #ifdef UNICORN_HAS_X86 case UC_ARCH_X86: + if ((mode & ~UC_MODE_X86_MASK) || + (mode & UC_MODE_BIG_ENDIAN) || + !(mode & (UC_MODE_16|UC_MODE_32|UC_MODE_64))) { + free(uc); + return UC_ERR_MODE; + } uc->init_arch = x86_uc_init; break; #endif #ifdef UNICORN_HAS_ARM case UC_ARCH_ARM: - uc->init_arch = arm_uc_init; - - // verify mode - if (mode != UC_MODE_ARM && mode != UC_MODE_THUMB) { + if ((mode & ~UC_MODE_ARM_MASK) || + (mode & UC_MODE_BIG_ENDIAN)) { free(uc); return UC_ERR_MODE; } + uc->init_arch = arm_uc_init; - if (mode == UC_MODE_THUMB) + if (mode & UC_MODE_THUMB) uc->thumb = 1; break; #endif #ifdef UNICORN_HAS_ARM64 case UC_ARCH_ARM64: + if ((mode & ~UC_MODE_ARM_MASK) || + (mode & UC_MODE_BIG_ENDIAN)) { + free(uc); + return UC_ERR_MODE; + } uc->init_arch = arm64_uc_init; break; #endif #if defined(UNICORN_HAS_MIPS) || defined(UNICORN_HAS_MIPSEL) || defined(UNICORN_HAS_MIPS64) || defined(UNICORN_HAS_MIPS64EL) case UC_ARCH_MIPS: + if ((mode & ~UC_MODE_MIPS_MASK) || + !(mode & (UC_MODE_MIPS32|UC_MODE_MIPS64))) { + free(uc); + return UC_ERR_MODE; + } if (mode & UC_MODE_BIG_ENDIAN) { #ifdef UNICORN_HAS_MIPS if (mode & UC_MODE_MIPS32) @@ -225,7 +242,13 @@ uc_err uc_open(uc_arch arch, uc_mode mode, uc_engine **result) #ifdef UNICORN_HAS_SPARC case UC_ARCH_SPARC: - if (mode & UC_MODE_64) + if ((mode & ~UC_MODE_SPARC_MASK) || + !(mode & UC_MODE_BIG_ENDIAN) || + !(mode & (UC_MODE_SPARC32|UC_MODE_SPARC64))) { + free(uc); + return UC_ERR_MODE; + } + if (mode & UC_MODE_SPARC64) uc->init_arch = sparc64_uc_init; else uc->init_arch = sparc_uc_init; @@ -245,9 +268,6 @@ uc_err uc_open(uc_arch arch, uc_mode mode, uc_engine **result) if (uc->reg_reset) uc->reg_reset(uc); - uc->hook_size = HOOK_SIZE; - uc->hook_callbacks = calloc(1, sizeof(uc->hook_callbacks[0]) * HOOK_SIZE); - return UC_ERR_OK; } else { return UC_ERR_ARCH; @@ -259,6 +279,8 @@ UNICORN_EXPORT uc_err uc_close(uc_engine *uc) { int i; + struct list_item *cur; + struct hook *hook; if (uc->release) uc->release(uc->tcg_ctx); @@ -277,7 +299,23 @@ uc_err uc_close(uc_engine *uc) free(uc->ram_list.dirty_memory[i]); } - free(uc->hook_callbacks); + // TODO: remove uc->root (created with object_new()) + uc->root->free(uc->root); + + // free hooks and hook lists + for (i = 0; i < UC_HOOK_MAX; i++) { + cur = uc->hook[i].head; + // hook can be in more than one list + // so we refcount to know when to free + while (cur) { + hook = (struct hook *)cur->data; + if (--hook->refs == 0) { + free(hook); + } + cur = cur->next; + } + list_clear(&uc->hook[i]); + } free(uc->mapped_blocks); @@ -438,12 +476,20 @@ static void enable_emu_timer(uc_engine *uc, uint64_t timeout) uc, QEMU_THREAD_JOINABLE); } +static void hook_count_cb(struct uc_struct *uc, uint64_t address, uint32_t size, void *user_data) +{ + // count this instruction. ah ah ah. + uc->emu_counter++; + + if (uc->emu_counter > uc->emu_count) + uc_emu_stop(uc); +} + UNICORN_EXPORT uc_err uc_emu_start(uc_engine* uc, uint64_t begin, uint64_t until, uint64_t timeout, size_t count) { // reset the counter uc->emu_counter = 0; - uc->stop_request = false; uc->invalid_error = UC_ERR_OK; uc->block_full = false; uc->emulation_done = false; @@ -473,14 +519,7 @@ uc_err uc_emu_start(uc_engine* uc, uint64_t begin, uint64_t until, uint64_t time break; case UC_ARCH_ARM: - switch(uc->mode) { - default: - break; - case UC_MODE_THUMB: - case UC_MODE_ARM: - uc_reg_write(uc, UC_ARM_REG_R15, &begin); - break; - } + uc_reg_write(uc, UC_ARM_REG_R15, &begin); break; case UC_ARCH_ARM64: @@ -498,9 +537,20 @@ uc_err uc_emu_start(uc_engine* uc, uint64_t begin, uint64_t until, uint64_t time break; } + uc->stop_request = false; + uc->emu_count = count; - if (count > 0) { - uc->hook_insn = true; + // remove count hook if counting isn't necessary + if (count <= 0 && uc->count_hook != 0) { + uc_hook_del(uc, uc->count_hook); + uc->count_hook = 0; + } + // set up count hook to count instructions. + if (count > 0 && uc->count_hook == 0) { + uc_err err = uc_hook_add(uc, &uc->count_hook, UC_HOOK_CODE, hook_count_cb, NULL); + if (err != UC_ERR_OK) { + return err; + } } uc->addr_end = until; @@ -539,37 +589,6 @@ uc_err uc_emu_stop(uc_engine *uc) return UC_ERR_OK; } - -static int _hook_code(uc_engine *uc, int type, uint64_t begin, uint64_t end, - void *callback, void *user_data, uc_hook *hh) -{ - int i; - - i = hook_add(uc, type, begin, end, callback, user_data); - if (i == 0) - return UC_ERR_NOMEM; - - *hh = i; - - return UC_ERR_OK; -} - - -static uc_err _hook_mem_access(uc_engine *uc, uc_hook_type type, - uint64_t begin, uint64_t end, - void *callback, void *user_data, uc_hook *hh) -{ - int i; - - i = hook_add(uc, type, begin, end, callback, user_data); - if (i == 0) - return UC_ERR_NOMEM; - - *hh = i; - - return UC_ERR_OK; -} - // find if a memory range overlaps with existing mapped regions static bool memory_overlap(struct uc_struct *uc, uint64_t begin, size_t size) { @@ -599,26 +618,6 @@ static uc_err mem_map(uc_engine *uc, uint64_t address, size_t size, uint32_t per { MemoryRegion **regions; - if (size == 0) - // invalid memory mapping - return UC_ERR_ARG; - - // address cannot wrapp around - if (address + size - 1 < address) - return UC_ERR_ARG; - - // address must be aligned to uc->target_page_size - if ((address & uc->target_page_align) != 0) - return UC_ERR_ARG; - - // size must be multiple of uc->target_page_size - if ((size & uc->target_page_align) != 0) - return UC_ERR_ARG; - - // check for only valid permissions - if ((perms & ~UC_PROT_ALL) != 0) - return UC_ERR_ARG; - // this area overlaps existing mapped regions? if (memory_overlap(uc, address, size)) return UC_ERR_MAP; @@ -641,19 +640,52 @@ static uc_err mem_map(uc_engine *uc, uint64_t address, size_t size, uint32_t per return UC_ERR_OK; } +static uc_err mem_map_check(uc_engine *uc, uint64_t address, size_t size, uint32_t perms) +{ + if (size == 0) + // invalid memory mapping + return UC_ERR_ARG; + + // address cannot wrapp around + if (address + size - 1 < address) + return UC_ERR_ARG; + + // address must be aligned to uc->target_page_size + if ((address & uc->target_page_align) != 0) + return UC_ERR_ARG; + + // size must be multiple of uc->target_page_size + if ((size & uc->target_page_align) != 0) + return UC_ERR_ARG; + + // check for only valid permissions + if ((perms & ~UC_PROT_ALL) != 0) + return UC_ERR_ARG; + + return UC_ERR_OK; +} + UNICORN_EXPORT uc_err uc_mem_map(uc_engine *uc, uint64_t address, size_t size, uint32_t perms) { + uc_err res; + if (uc->mem_redirect) { address = uc->mem_redirect(address); } + res = mem_map_check(uc, address, size, perms); + if (res) + return res; + return mem_map(uc, address, size, perms, uc->memory_map(uc, address, size, perms)); } UNICORN_EXPORT uc_err uc_mem_map_ptr(uc_engine *uc, uint64_t address, size_t size, uint32_t perms, void *ptr) { + uc_err res; + if (ptr == NULL) return UC_ERR_ARG; @@ -661,6 +693,10 @@ uc_err uc_mem_map_ptr(uc_engine *uc, uint64_t address, size_t size, uint32_t per address = uc->mem_redirect(address); } + res = mem_map_check(uc, address, size, perms); + if (res) + return res; + return mem_map(uc, address, size, UC_PROT_ALL, uc->memory_map_ptr(uc, address, size, perms, ptr)); } @@ -787,6 +823,7 @@ uc_err uc_mem_protect(struct uc_struct *uc, uint64_t address, size_t size, uint3 MemoryRegion *mr; uint64_t addr = address; size_t count, len; + bool remove_exec = false; if (size == 0) // trivial case, no change @@ -823,12 +860,22 @@ uc_err uc_mem_protect(struct uc_struct *uc, uint64_t address, size_t size, uint3 return UC_ERR_NOMEM; mr = memory_mapping(uc, addr); + // will this remove EXEC permission? + if (((mr->perms & UC_PROT_EXEC) != 0) && ((perms & UC_PROT_EXEC) == 0)) + remove_exec = true; mr->perms = perms; uc->readonly_mem(mr, (perms & UC_PROT_WRITE) == 0); count += len; addr += len; } + + // if EXEC permission is removed, then quit TB and continue at the same place + if (remove_exec) { + uc->quit_request = true; + uc_emu_stop(uc); + } + return UC_ERR_OK; } @@ -911,206 +958,141 @@ MemoryRegion *memory_mapping(struct uc_struct* uc, uint64_t address) return NULL; } -static uc_err _hook_mem_invalid(struct uc_struct* uc, int type, uc_cb_eventmem_t callback, - void *user_data, uc_hook *evh) -{ - size_t i; - - // only one event handler at the same time - if ((type & UC_HOOK_MEM_READ_UNMAPPED) != 0 && (uc->hook_mem_read_idx != 0)) - return UC_ERR_HOOK_EXIST; - - if ((type & UC_HOOK_MEM_READ_PROT) != 0 && (uc->hook_mem_read_prot_idx != 0)) - return UC_ERR_HOOK_EXIST; - - if ((type & UC_HOOK_MEM_WRITE_UNMAPPED) != 0 && (uc->hook_mem_write_idx != 0)) - return UC_ERR_HOOK_EXIST; - - if ((type & UC_HOOK_MEM_WRITE_PROT) != 0 && (uc->hook_mem_write_prot_idx != 0)) - return UC_ERR_HOOK_EXIST; - - if ((type & UC_HOOK_MEM_FETCH_UNMAPPED) != 0 && (uc->hook_mem_fetch_idx != 0)) - return UC_ERR_HOOK_EXIST; - - if ((type & UC_HOOK_MEM_FETCH_PROT) != 0 && (uc->hook_mem_fetch_prot_idx != 0)) - return UC_ERR_HOOK_EXIST; - - i = hook_find_new(uc); - if (i) { - uc->hook_callbacks[i].callback = callback; - uc->hook_callbacks[i].user_data = user_data; - *evh = i; - if (type & UC_HOOK_MEM_READ_UNMAPPED) - uc->hook_mem_read_idx = i; - if (type & UC_HOOK_MEM_READ_PROT) - uc->hook_mem_read_prot_idx = i; - if (type & UC_HOOK_MEM_WRITE_UNMAPPED) - uc->hook_mem_write_idx = i; - if (type & UC_HOOK_MEM_WRITE_PROT) - uc->hook_mem_write_prot_idx = i; - if (type & UC_HOOK_MEM_FETCH_UNMAPPED) - uc->hook_mem_fetch_idx = i; - if (type & UC_HOOK_MEM_FETCH_PROT) - uc->hook_mem_fetch_prot_idx = i; - return UC_ERR_OK; - } else - return UC_ERR_NOMEM; -} - - -static uc_err _hook_intr(struct uc_struct* uc, void *callback, - void *user_data, uc_hook *evh) -{ - size_t i; - - // only one event handler at the same time - if (uc->hook_intr_idx) - return UC_ERR_HOOK_EXIST; - - i = hook_find_new(uc); - if (i) { - uc->hook_callbacks[i].callback = callback; - uc->hook_callbacks[i].user_data = user_data; - *evh = i; - uc->hook_intr_idx = i; - return UC_ERR_OK; - } else - return UC_ERR_NOMEM; -} - - -static uc_err _hook_insn(struct uc_struct *uc, unsigned int insn_id, void *callback, - void *user_data, uc_hook *evh) -{ - size_t i; - - switch(uc->arch) { - default: break; - case UC_ARCH_X86: - switch(insn_id) { - default: break; - case UC_X86_INS_OUT: - // only one event handler at the same time - if (uc->hook_out_idx) - return UC_ERR_HOOK_EXIST; - - i = hook_find_new(uc); - if (i) { - uc->hook_callbacks[i].callback = callback; - uc->hook_callbacks[i].user_data = user_data; - *evh = i; - uc->hook_out_idx = i; - return UC_ERR_OK; - } else - return UC_ERR_NOMEM; - case UC_X86_INS_IN: - // only one event handler at the same time - if (uc->hook_in_idx) - return UC_ERR_HOOK_EXIST; - - i = hook_find_new(uc); - if (i) { - uc->hook_callbacks[i].callback = callback; - uc->hook_callbacks[i].user_data = user_data; - *evh = i; - uc->hook_in_idx = i; - return UC_ERR_OK; - } else - return UC_ERR_NOMEM; - case UC_X86_INS_SYSCALL: - case UC_X86_INS_SYSENTER: - // only one event handler at the same time - if (uc->hook_syscall_idx) - return UC_ERR_HOOK_EXIST; - - i = hook_find_new(uc); - if (i) { - uc->hook_callbacks[i].callback = callback; - uc->hook_callbacks[i].user_data = user_data; - *evh = i; - uc->hook_syscall_idx = i; - return UC_ERR_OK; - } else - return UC_ERR_NOMEM; - } - break; - } - - return UC_ERR_OK; -} - UNICORN_EXPORT uc_err uc_hook_add(uc_engine *uc, uc_hook *hh, int type, void *callback, void *user_data, ...) { va_list valist; int ret = UC_ERR_OK; - int id; - uint64_t begin, end; va_start(valist, user_data); - if (type & UC_HOOK_MEM_READ_UNMAPPED) - ret = _hook_mem_invalid(uc, UC_HOOK_MEM_READ_UNMAPPED, callback, user_data, hh); + struct hook *hook = calloc(1, sizeof(struct hook)); + if (hook == NULL) { + return UC_ERR_NOMEM; + } + hook->type = type; + hook->callback = callback; + hook->user_data = user_data; + hook->refs = 0; + *hh = (uc_hook)hook; - if (type & UC_HOOK_MEM_WRITE_UNMAPPED) - ret = _hook_mem_invalid(uc, UC_HOOK_MEM_WRITE_UNMAPPED, callback, user_data, hh); - - if (type & UC_HOOK_MEM_FETCH_UNMAPPED) - ret = _hook_mem_invalid(uc, UC_HOOK_MEM_FETCH_UNMAPPED, callback, user_data, hh); - - if (type & UC_HOOK_MEM_READ_PROT) - ret = _hook_mem_invalid(uc, UC_HOOK_MEM_READ_PROT, callback, user_data, hh); - - if (type & UC_HOOK_MEM_WRITE_PROT) - ret = _hook_mem_invalid(uc, UC_HOOK_MEM_WRITE_PROT, callback, user_data, hh); - - if (type & UC_HOOK_MEM_FETCH_PROT) - ret = _hook_mem_invalid(uc, UC_HOOK_MEM_FETCH_PROT, callback, user_data, hh); - - switch(type) { - default: - break; - case UC_HOOK_INTR: - ret = _hook_intr(uc, callback, user_data, hh); - break; - case UC_HOOK_INSN: - id = va_arg(valist, int); - ret = _hook_insn(uc, id, callback, user_data, hh); - break; - case UC_HOOK_CODE: - begin = va_arg(valist, uint64_t); - end = va_arg(valist, uint64_t); - ret = _hook_code(uc, UC_HOOK_CODE, begin, end, callback, user_data, hh); - break; - case UC_HOOK_BLOCK: - begin = va_arg(valist, uint64_t); - end = va_arg(valist, uint64_t); - ret = _hook_code(uc, UC_HOOK_BLOCK, begin, end, callback, user_data, hh); - break; - case UC_HOOK_MEM_READ: - begin = va_arg(valist, uint64_t); - end = va_arg(valist, uint64_t); - ret = _hook_mem_access(uc, UC_HOOK_MEM_READ, begin, end, callback, user_data, hh); - break; - case UC_HOOK_MEM_WRITE: - begin = va_arg(valist, uint64_t); - end = va_arg(valist, uint64_t); - ret = _hook_mem_access(uc, UC_HOOK_MEM_WRITE, begin, end, callback, user_data, hh); - break; - case UC_HOOK_MEM_READ | UC_HOOK_MEM_WRITE: - begin = va_arg(valist, uint64_t); - end = va_arg(valist, uint64_t); - ret = _hook_mem_access(uc, UC_HOOK_MEM_READ | UC_HOOK_MEM_WRITE, begin, end, callback, user_data, hh); - break; + // everybody but HOOK_INSN gets begin/end, so exit early here. + if (type & UC_HOOK_INSN) { + hook->insn = va_arg(valist, int); + hook->begin = 1; + hook->end = 0; + if (list_append(&uc->hook[UC_HOOK_INSN_IDX], hook) == NULL) { + free(hook); + return UC_ERR_NOMEM; + } + hook->refs++; + return UC_ERR_OK; } + hook->begin = va_arg(valist, uint64_t); + hook->end = va_arg(valist, uint64_t); va_end(valist); + int i = 0; + while ((type >> i) > 0) { + if ((type >> i) & 1) { + // TODO: invalid hook error? + if (i < UC_HOOK_MAX) { + if (list_append(&uc->hook[i], hook) == NULL) { + if (hook->refs == 0) { + free(hook); + } + return UC_ERR_NOMEM; + } + hook->refs++; + } + } + i++; + } + + // we didn't use the hook + // TODO: return an error? + if (hook->refs == 0) { + free(hook); + } + return ret; } UNICORN_EXPORT uc_err uc_hook_del(uc_engine *uc, uc_hook hh) { - return hook_del(uc, hh); + int i; + struct hook *hook; + for (i = 0; i < UC_HOOK_MAX; i++) { + if (list_remove(&uc->hook[i], (void *)hh)) { + hook = (struct hook *)hh; + if (--hook->refs == 0) { + free(hook); + } + } + } + return UC_ERR_OK; +} + +// TCG helper +void helper_uc_tracecode(int32_t size, uc_hook_type type, void *handle, int64_t address); +void helper_uc_tracecode(int32_t size, uc_hook_type type, void *handle, int64_t address) +{ + struct uc_struct *uc = handle; + struct list_item *cur = uc->hook[type].head; + struct hook *hook; + + // sync PC in CPUArchState with address + if (uc->set_pc) { + uc->set_pc(uc, address); + } + + while (cur != NULL && !uc->stop_request) { + hook = (struct hook *)cur->data; + if (HOOK_BOUND_CHECK(hook, address)) { + ((uc_cb_hookcode_t)hook->callback)(uc, address, size, hook->user_data); + } + cur = cur->next; + } +} + +UNICORN_EXPORT +uint32_t uc_mem_regions(uc_engine *uc, uc_mem_region **regions, uint32_t *count) +{ + uint32_t i; + uc_mem_region *r = NULL; + + *count = uc->mapped_block_count; + + if (*count) { + r = malloc(*count * sizeof(uc_mem_region)); + if (r == NULL) { + // out of memory + return UC_ERR_NOMEM; + } + } + + for (i = 0; i < *count; i++) { + r[i].begin = uc->mapped_blocks[i]->addr; + r[i].end = uc->mapped_blocks[i]->end - 1; + r[i].perms = uc->mapped_blocks[i]->perms; + } + + *regions = r; + + return UC_ERR_OK; +} + +UNICORN_EXPORT +uc_err uc_query(uc_engine *uc, uc_query_type type, size_t *result) +{ + switch(uc->arch) { + case UC_ARCH_ARM: + return uc->query(uc, type, result); + default: + return UC_ERR_ARG; + } + + return UC_ERR_OK; }