Merge branch 'dev' into uc-py-next
This commit is contained in:
4
.github/workflows/build-uc2.yml
vendored
4
.github/workflows/build-uc2.yml
vendored
@@ -328,9 +328,9 @@ jobs:
|
|||||||
brew install p7zip cmake ninja
|
brew install p7zip cmake ninja
|
||||||
mkdir build
|
mkdir build
|
||||||
mkdir instdir
|
mkdir instdir
|
||||||
cmake . -DCMAKE_TOOLCHAIN_FILE="$ANDROID_HOME/ndk/25.0.8775105/build/cmake/android.toolchain.cmake" \
|
cmake . -DCMAKE_TOOLCHAIN_FILE="$ANDROID_NDK/build/cmake/android.toolchain.cmake" \
|
||||||
-DANDROID_PLATFORM=android-28 \
|
-DANDROID_PLATFORM=android-28 \
|
||||||
-DANDROID_NDK="$ANDROID_HOME/ndk/25.0.8775105" \
|
-DANDROID_NDK="$ANDROID_NDK" \
|
||||||
-DANDROID_ABI=${{ matrix.config.arch }} \
|
-DANDROID_ABI=${{ matrix.config.arch }} \
|
||||||
-DOLP_SDK_ENABLE_TESTING=NO \
|
-DOLP_SDK_ENABLE_TESTING=NO \
|
||||||
-DOLP_SDK_BUILD_EXAMPLES=ON \
|
-DOLP_SDK_BUILD_EXAMPLES=ON \
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ ROOT_DIR = os.path.dirname(os.path.realpath(__file__))
|
|||||||
LIBS_DIR = os.path.join(ROOT_DIR, 'unicorn', 'lib')
|
LIBS_DIR = os.path.join(ROOT_DIR, 'unicorn', 'lib')
|
||||||
HEADERS_DIR = os.path.join(ROOT_DIR, 'unicorn', 'include')
|
HEADERS_DIR = os.path.join(ROOT_DIR, 'unicorn', 'include')
|
||||||
SRC_DIR = os.path.join(ROOT_DIR, 'src')
|
SRC_DIR = os.path.join(ROOT_DIR, 'src')
|
||||||
UC_DIR = os.path.join(ROOT_DIR, '../..')
|
UC_DIR = SRC_DIR if os.path.exists(SRC_DIR) else os.path.join(ROOT_DIR, '../..')
|
||||||
BUILD_DIR = os.path.join(UC_DIR, 'build_python')
|
BUILD_DIR = os.path.join(UC_DIR, 'build_python')
|
||||||
|
|
||||||
VERSION = "2.0.0"
|
VERSION = "2.0.0"
|
||||||
@@ -60,6 +60,9 @@ def copy_sources():
|
|||||||
shutil.copytree(os.path.join(ROOT_DIR, '../../include'), os.path.join(SRC_DIR, 'include/'))
|
shutil.copytree(os.path.join(ROOT_DIR, '../../include'), os.path.join(SRC_DIR, 'include/'))
|
||||||
# make -> configure -> clean -> clean tests fails unless tests is present
|
# make -> configure -> clean -> clean tests fails unless tests is present
|
||||||
shutil.copytree(os.path.join(ROOT_DIR, '../../tests'), os.path.join(SRC_DIR, 'tests/'))
|
shutil.copytree(os.path.join(ROOT_DIR, '../../tests'), os.path.join(SRC_DIR, 'tests/'))
|
||||||
|
shutil.copytree(os.path.join(ROOT_DIR, '../../samples'), os.path.join(SRC_DIR, 'samples/'))
|
||||||
|
shutil.copytree(os.path.join(ROOT_DIR, '../../glib_compat'), os.path.join(SRC_DIR, 'glib_compat/'))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# remove site-specific configuration file
|
# remove site-specific configuration file
|
||||||
# might not exist
|
# might not exist
|
||||||
@@ -69,6 +72,7 @@ def copy_sources():
|
|||||||
|
|
||||||
src.extend(glob.glob(os.path.join(ROOT_DIR, "../../*.[ch]")))
|
src.extend(glob.glob(os.path.join(ROOT_DIR, "../../*.[ch]")))
|
||||||
src.extend(glob.glob(os.path.join(ROOT_DIR, "../../*.mk")))
|
src.extend(glob.glob(os.path.join(ROOT_DIR, "../../*.mk")))
|
||||||
|
src.extend(glob.glob(os.path.join(ROOT_DIR, "../../*.cmake")))
|
||||||
|
|
||||||
src.extend(glob.glob(os.path.join(ROOT_DIR, "../../LICENSE*")))
|
src.extend(glob.glob(os.path.join(ROOT_DIR, "../../LICENSE*")))
|
||||||
src.extend(glob.glob(os.path.join(ROOT_DIR, "../../README.md")))
|
src.extend(glob.glob(os.path.join(ROOT_DIR, "../../README.md")))
|
||||||
|
|||||||
@@ -315,7 +315,6 @@ class RegStateManager:
|
|||||||
|
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
def _do_reg_write(self, reg_id: int, reg_obj) -> int:
|
def _do_reg_write(self, reg_id: int, reg_obj) -> int:
|
||||||
"""Private register write implementation.
|
"""Private register write implementation.
|
||||||
Must be implemented by the mixin object
|
Must be implemented by the mixin object
|
||||||
@@ -323,7 +322,6 @@ class RegStateManager:
|
|||||||
|
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
def _reg_read(self, reg_id: int, regtype, *args):
|
def _reg_read(self, reg_id: int, regtype, *args):
|
||||||
"""Register read helper method.
|
"""Register read helper method.
|
||||||
"""
|
"""
|
||||||
@@ -336,7 +334,6 @@ class RegStateManager:
|
|||||||
|
|
||||||
return reg.value
|
return reg.value
|
||||||
|
|
||||||
|
|
||||||
def _reg_write(self, reg_id: int, regtype, value) -> None:
|
def _reg_write(self, reg_id: int, regtype, value) -> None:
|
||||||
"""Register write helper method.
|
"""Register write helper method.
|
||||||
"""
|
"""
|
||||||
@@ -347,7 +344,6 @@ class RegStateManager:
|
|||||||
if status != uc.UC_ERR_OK:
|
if status != uc.UC_ERR_OK:
|
||||||
raise UcError(status, reg_id)
|
raise UcError(status, reg_id)
|
||||||
|
|
||||||
|
|
||||||
def reg_read(self, reg_id: int, aux: Any = None):
|
def reg_read(self, reg_id: int, aux: Any = None):
|
||||||
"""Read architectural register value.
|
"""Read architectural register value.
|
||||||
|
|
||||||
@@ -362,7 +358,6 @@ class RegStateManager:
|
|||||||
|
|
||||||
return self._reg_read(reg_id, self._DEFAULT_REGTYPE)
|
return self._reg_read(reg_id, self._DEFAULT_REGTYPE)
|
||||||
|
|
||||||
|
|
||||||
def reg_write(self, reg_id: int, value) -> None:
|
def reg_write(self, reg_id: int, value) -> None:
|
||||||
"""Write to architectural register.
|
"""Write to architectural register.
|
||||||
|
|
||||||
@@ -439,7 +434,6 @@ class Uc(RegStateManager):
|
|||||||
|
|
||||||
return (uc_maj, uc_min) == (bnd_maj, bnd_min)
|
return (uc_maj, uc_min) == (bnd_maj, bnd_min)
|
||||||
|
|
||||||
|
|
||||||
def __new__(cls, arch: int, mode: int):
|
def __new__(cls, arch: int, mode: int):
|
||||||
# verify version compatibility with the core before doing anything
|
# verify version compatibility with the core before doing anything
|
||||||
if not Uc.__is_compliant():
|
if not Uc.__is_compliant():
|
||||||
@@ -480,7 +474,6 @@ class Uc(RegStateManager):
|
|||||||
# return the appropriate unicorn subclass type
|
# return the appropriate unicorn subclass type
|
||||||
return super(Uc, cls).__new__(subclass)
|
return super(Uc, cls).__new__(subclass)
|
||||||
|
|
||||||
|
|
||||||
def __init__(self, arch: int, mode: int) -> None:
|
def __init__(self, arch: int, mode: int) -> None:
|
||||||
"""Initialize a Unicorn engine instance.
|
"""Initialize a Unicorn engine instance.
|
||||||
|
|
||||||
@@ -511,7 +504,6 @@ class Uc(RegStateManager):
|
|||||||
# this instance undergoes garbage collection.
|
# this instance undergoes garbage collection.
|
||||||
self.__finalizer = weakref.finalize(self, Uc.release_handle, self._uch)
|
self.__finalizer = weakref.finalize(self, Uc.release_handle, self._uch)
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def release_handle(uch: uc_engine) -> None:
|
def release_handle(uch: uc_engine) -> None:
|
||||||
# this method and its arguments must not have any reference to the Uc instance being
|
# this method and its arguments must not have any reference to the Uc instance being
|
||||||
@@ -568,7 +560,6 @@ class Uc(RegStateManager):
|
|||||||
if status != uc.UC_ERR_OK:
|
if status != uc.UC_ERR_OK:
|
||||||
raise UcError(status)
|
raise UcError(status)
|
||||||
|
|
||||||
|
|
||||||
###########################
|
###########################
|
||||||
# CPU state accessors #
|
# CPU state accessors #
|
||||||
###########################
|
###########################
|
||||||
@@ -580,7 +571,6 @@ class Uc(RegStateManager):
|
|||||||
|
|
||||||
return uclib.uc_reg_read(self._uch, reg_id, reg_obj)
|
return uclib.uc_reg_read(self._uch, reg_id, reg_obj)
|
||||||
|
|
||||||
|
|
||||||
def _do_reg_write(self, reg_id: int, reg_obj) -> int:
|
def _do_reg_write(self, reg_id: int, reg_obj) -> int:
|
||||||
"""Private register write implementation.
|
"""Private register write implementation.
|
||||||
Do not call directly.
|
Do not call directly.
|
||||||
@@ -588,7 +578,6 @@ class Uc(RegStateManager):
|
|||||||
|
|
||||||
return uclib.uc_reg_write(self._uch, reg_id, reg_obj)
|
return uclib.uc_reg_write(self._uch, reg_id, reg_obj)
|
||||||
|
|
||||||
|
|
||||||
###########################
|
###########################
|
||||||
# Memory management #
|
# Memory management #
|
||||||
###########################
|
###########################
|
||||||
@@ -611,7 +600,6 @@ class Uc(RegStateManager):
|
|||||||
if status != uc.UC_ERR_OK:
|
if status != uc.UC_ERR_OK:
|
||||||
raise UcError(status)
|
raise UcError(status)
|
||||||
|
|
||||||
|
|
||||||
def mem_map_ptr(self, address: int, size: int, perms: int, ptr: int) -> None:
|
def mem_map_ptr(self, address: int, size: int, perms: int, ptr: int) -> None:
|
||||||
"""Map a memory range and point to existing data on host memory.
|
"""Map a memory range and point to existing data on host memory.
|
||||||
|
|
||||||
@@ -631,7 +619,6 @@ class Uc(RegStateManager):
|
|||||||
if status != uc.UC_ERR_OK:
|
if status != uc.UC_ERR_OK:
|
||||||
raise UcError(status)
|
raise UcError(status)
|
||||||
|
|
||||||
|
|
||||||
def mem_unmap(self, address: int, size: int) -> None:
|
def mem_unmap(self, address: int, size: int) -> None:
|
||||||
"""Reclaim a mapped memory range.
|
"""Reclaim a mapped memory range.
|
||||||
|
|
||||||
@@ -652,7 +639,6 @@ class Uc(RegStateManager):
|
|||||||
# might be splitted by 'map_protect' after they were mapped, so the
|
# might be splitted by 'map_protect' after they were mapped, so the
|
||||||
# (start, end) tuple may not be suitable for retrieving the callbacks
|
# (start, end) tuple may not be suitable for retrieving the callbacks
|
||||||
|
|
||||||
|
|
||||||
def mem_protect(self, address: int, size: int, perms: int = uc.UC_PROT_ALL) -> None:
|
def mem_protect(self, address: int, size: int, perms: int = uc.UC_PROT_ALL) -> None:
|
||||||
"""Modify access protection bitmask of a mapped memory range.
|
"""Modify access protection bitmask of a mapped memory range.
|
||||||
|
|
||||||
@@ -671,7 +657,6 @@ class Uc(RegStateManager):
|
|||||||
if status != uc.UC_ERR_OK:
|
if status != uc.UC_ERR_OK:
|
||||||
raise UcError(status)
|
raise UcError(status)
|
||||||
|
|
||||||
|
|
||||||
def mmio_map(self, address: int, size: int,
|
def mmio_map(self, address: int, size: int,
|
||||||
read_cb: Optional[UC_MMIO_READ_TYPE], user_data_read: Any,
|
read_cb: Optional[UC_MMIO_READ_TYPE], user_data_read: Any,
|
||||||
write_cb: Optional[UC_MMIO_WRITE_TYPE], user_data_write: Any) -> None:
|
write_cb: Optional[UC_MMIO_WRITE_TYPE], user_data_write: Any) -> None:
|
||||||
@@ -702,7 +687,6 @@ class Uc(RegStateManager):
|
|||||||
|
|
||||||
self._mmio_callbacks[(rng_starts, rng_ends)] = (read_cb_fptr, write_cb_fptr)
|
self._mmio_callbacks[(rng_starts, rng_ends)] = (read_cb_fptr, write_cb_fptr)
|
||||||
|
|
||||||
|
|
||||||
def mem_regions(self) -> Iterator[Tuple[int, int, int]]:
|
def mem_regions(self) -> Iterator[Tuple[int, int, int]]:
|
||||||
"""Iterate through mapped memory regions.
|
"""Iterate through mapped memory regions.
|
||||||
|
|
||||||
@@ -725,7 +709,6 @@ class Uc(RegStateManager):
|
|||||||
finally:
|
finally:
|
||||||
uclib.uc_free(regions)
|
uclib.uc_free(regions)
|
||||||
|
|
||||||
|
|
||||||
def mem_read(self, address: int, size: int) -> bytearray:
|
def mem_read(self, address: int, size: int) -> bytearray:
|
||||||
"""Read data from emulated memory subsystem.
|
"""Read data from emulated memory subsystem.
|
||||||
|
|
||||||
@@ -746,7 +729,6 @@ class Uc(RegStateManager):
|
|||||||
|
|
||||||
return bytearray(data)
|
return bytearray(data)
|
||||||
|
|
||||||
|
|
||||||
def mem_write(self, address: int, data: bytes) -> None:
|
def mem_write(self, address: int, data: bytes) -> None:
|
||||||
"""Write data to emulated memory subsystem.
|
"""Write data to emulated memory subsystem.
|
||||||
|
|
||||||
@@ -763,7 +745,6 @@ class Uc(RegStateManager):
|
|||||||
if status != uc.UC_ERR_OK:
|
if status != uc.UC_ERR_OK:
|
||||||
raise UcError(status, address, size)
|
raise UcError(status, address, size)
|
||||||
|
|
||||||
|
|
||||||
###########################
|
###########################
|
||||||
# Event hooks management #
|
# Event hooks management #
|
||||||
###########################
|
###########################
|
||||||
@@ -794,7 +775,6 @@ class Uc(RegStateManager):
|
|||||||
|
|
||||||
return handle.value
|
return handle.value
|
||||||
|
|
||||||
|
|
||||||
def hook_add(self, htype: int, callback: Callable, user_data: Any = None, begin: int = 1, end: int = 0, aux1: int = 0, aux2: int = 0) -> int:
|
def hook_add(self, htype: int, callback: Callable, user_data: Any = None, begin: int = 1, end: int = 0, aux1: int = 0, aux2: int = 0) -> int:
|
||||||
"""Hook emulated events of a certain type.
|
"""Hook emulated events of a certain type.
|
||||||
|
|
||||||
@@ -913,7 +893,6 @@ class Uc(RegStateManager):
|
|||||||
|
|
||||||
return self.__do_hook_add(htype, fptr, begin, end, *aux)
|
return self.__do_hook_add(htype, fptr, begin, end, *aux)
|
||||||
|
|
||||||
|
|
||||||
def hook_del(self, handle: int) -> None:
|
def hook_del(self, handle: int) -> None:
|
||||||
"""Remove an existing hook.
|
"""Remove an existing hook.
|
||||||
|
|
||||||
@@ -929,7 +908,6 @@ class Uc(RegStateManager):
|
|||||||
|
|
||||||
del self._callbacks[handle]
|
del self._callbacks[handle]
|
||||||
|
|
||||||
|
|
||||||
def query(self, prop: int) -> int:
|
def query(self, prop: int) -> int:
|
||||||
"""Query an internal Unicorn property.
|
"""Query an internal Unicorn property.
|
||||||
|
|
||||||
@@ -947,7 +925,6 @@ class Uc(RegStateManager):
|
|||||||
|
|
||||||
return result.value
|
return result.value
|
||||||
|
|
||||||
|
|
||||||
def context_save(self) -> UcContext:
|
def context_save(self) -> UcContext:
|
||||||
context = UcContext(self._uch, self._arch, self._mode)
|
context = UcContext(self._uch, self._arch, self._mode)
|
||||||
status = uclib.uc_context_save(self._uch, context.context)
|
status = uclib.uc_context_save(self._uch, context.context)
|
||||||
@@ -957,21 +934,18 @@ class Uc(RegStateManager):
|
|||||||
|
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
def context_update(self, context: UcContext) -> None:
|
def context_update(self, context: UcContext) -> None:
|
||||||
status = uclib.uc_context_save(self._uch, context.context)
|
status = uclib.uc_context_save(self._uch, context.context)
|
||||||
|
|
||||||
if status != uc.UC_ERR_OK:
|
if status != uc.UC_ERR_OK:
|
||||||
raise UcError(status)
|
raise UcError(status)
|
||||||
|
|
||||||
|
|
||||||
def context_restore(self, context: UcContext) -> None:
|
def context_restore(self, context: UcContext) -> None:
|
||||||
status = uclib.uc_context_restore(self._uch, context.context)
|
status = uclib.uc_context_restore(self._uch, context.context)
|
||||||
|
|
||||||
if status != uc.UC_ERR_OK:
|
if status != uc.UC_ERR_OK:
|
||||||
raise UcError(status)
|
raise UcError(status)
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def __ctl_encode(ctl: int, op: int, nargs: int) -> int:
|
def __ctl_encode(ctl: int, op: int, nargs: int) -> int:
|
||||||
assert nargs and (nargs & ~0b1111) == 0
|
assert nargs and (nargs & ~0b1111) == 0
|
||||||
@@ -979,7 +953,6 @@ class Uc(RegStateManager):
|
|||||||
|
|
||||||
return (op << 30) | (nargs << 26) | ctl
|
return (op << 30) | (nargs << 26) | ctl
|
||||||
|
|
||||||
|
|
||||||
def ctl(self, ctl: int, op: int, *args):
|
def ctl(self, ctl: int, op: int, *args):
|
||||||
code = Uc.__ctl_encode(ctl, op, len(args))
|
code = Uc.__ctl_encode(ctl, op, len(args))
|
||||||
|
|
||||||
@@ -988,10 +961,8 @@ class Uc(RegStateManager):
|
|||||||
if status != uc.UC_ERR_OK:
|
if status != uc.UC_ERR_OK:
|
||||||
raise UcError(status)
|
raise UcError(status)
|
||||||
|
|
||||||
|
|
||||||
Arg = Tuple[Type, Optional[int]]
|
Arg = Tuple[Type, Optional[int]]
|
||||||
|
|
||||||
|
|
||||||
def __ctl_r(self, ctl: int, arg0: Arg):
|
def __ctl_r(self, ctl: int, arg0: Arg):
|
||||||
atype, _ = arg0
|
atype, _ = arg0
|
||||||
carg = atype()
|
carg = atype()
|
||||||
@@ -1000,13 +971,11 @@ class Uc(RegStateManager):
|
|||||||
|
|
||||||
return carg.value
|
return carg.value
|
||||||
|
|
||||||
|
|
||||||
def __ctl_w(self, ctl: int, *args: Arg):
|
def __ctl_w(self, ctl: int, *args: Arg):
|
||||||
cargs = (atype(avalue) for atype, avalue in args)
|
cargs = (atype(avalue) for atype, avalue in args)
|
||||||
|
|
||||||
self.ctl(ctl, uc.UC_CTL_IO_WRITE, *cargs)
|
self.ctl(ctl, uc.UC_CTL_IO_WRITE, *cargs)
|
||||||
|
|
||||||
|
|
||||||
def __ctl_wr(self, ctl: int, arg0: Arg, arg1: Arg):
|
def __ctl_wr(self, ctl: int, arg0: Arg, arg1: Arg):
|
||||||
atype, avalue = arg0
|
atype, avalue = arg0
|
||||||
carg0 = atype(avalue)
|
carg0 = atype(avalue)
|
||||||
@@ -1018,49 +987,41 @@ class Uc(RegStateManager):
|
|||||||
|
|
||||||
return carg1
|
return carg1
|
||||||
|
|
||||||
|
|
||||||
def ctl_get_mode(self) -> int:
|
def ctl_get_mode(self) -> int:
|
||||||
return self.__ctl_r(uc.UC_CTL_UC_MODE,
|
return self.__ctl_r(uc.UC_CTL_UC_MODE,
|
||||||
(ctypes.c_int, None)
|
(ctypes.c_int, None)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def ctl_get_page_size(self) -> int:
|
def ctl_get_page_size(self) -> int:
|
||||||
return self.__ctl_r(uc.UC_CTL_UC_PAGE_SIZE,
|
return self.__ctl_r(uc.UC_CTL_UC_PAGE_SIZE,
|
||||||
(ctypes.c_uint32, None)
|
(ctypes.c_uint32, None)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def ctl_set_page_size(self, val: int) -> None:
|
def ctl_set_page_size(self, val: int) -> None:
|
||||||
self.__ctl_w(uc.UC_CTL_UC_PAGE_SIZE,
|
self.__ctl_w(uc.UC_CTL_UC_PAGE_SIZE,
|
||||||
(ctypes.c_uint32, val)
|
(ctypes.c_uint32, val)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def ctl_get_arch(self) -> int:
|
def ctl_get_arch(self) -> int:
|
||||||
return self.__ctl_r(uc.UC_CTL_UC_ARCH,
|
return self.__ctl_r(uc.UC_CTL_UC_ARCH,
|
||||||
(ctypes.c_int, None)
|
(ctypes.c_int, None)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def ctl_get_timeout(self) -> int:
|
def ctl_get_timeout(self) -> int:
|
||||||
return self.__ctl_r(uc.UC_CTL_UC_TIMEOUT,
|
return self.__ctl_r(uc.UC_CTL_UC_TIMEOUT,
|
||||||
(ctypes.c_uint64, None)
|
(ctypes.c_uint64, None)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def ctl_exits_enabled(self, val: bool) -> None:
|
def ctl_exits_enabled(self, val: bool) -> None:
|
||||||
self.__ctl_w(uc.UC_CTL_UC_USE_EXITS,
|
self.__ctl_w(uc.UC_CTL_UC_USE_EXITS,
|
||||||
(ctypes.c_int, val)
|
(ctypes.c_int, val)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def ctl_get_exits_cnt(self) -> int:
|
def ctl_get_exits_cnt(self) -> int:
|
||||||
return self.__ctl_r(uc.UC_CTL_UC_EXITS_CNT,
|
return self.__ctl_r(uc.UC_CTL_UC_EXITS_CNT,
|
||||||
(ctypes.c_size_t, None)
|
(ctypes.c_size_t, None)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def ctl_get_exits(self) -> Sequence[int]:
|
def ctl_get_exits(self) -> Sequence[int]:
|
||||||
l = self.ctl_get_exits_cnt()
|
l = self.ctl_get_exits_cnt()
|
||||||
arr = (ctypes.c_uint64 * l)()
|
arr = (ctypes.c_uint64 * l)()
|
||||||
@@ -1069,7 +1030,6 @@ class Uc(RegStateManager):
|
|||||||
|
|
||||||
return tuple(i for i in arr)
|
return tuple(i for i in arr)
|
||||||
|
|
||||||
|
|
||||||
def ctl_set_exits(self, exits: Sequence[int]) -> None:
|
def ctl_set_exits(self, exits: Sequence[int]) -> None:
|
||||||
arr = (ctypes.c_uint64 * len(exits))()
|
arr = (ctypes.c_uint64 * len(exits))()
|
||||||
|
|
||||||
@@ -1078,33 +1038,28 @@ class Uc(RegStateManager):
|
|||||||
|
|
||||||
self.ctl(uc.UC_CTL_UC_EXITS, uc.UC_CTL_IO_WRITE, ctypes.cast(arr, ctypes.c_void_p), ctypes.c_size_t(len(exits)))
|
self.ctl(uc.UC_CTL_UC_EXITS, uc.UC_CTL_IO_WRITE, ctypes.cast(arr, ctypes.c_void_p), ctypes.c_size_t(len(exits)))
|
||||||
|
|
||||||
|
|
||||||
def ctl_get_cpu_model(self) -> int:
|
def ctl_get_cpu_model(self) -> int:
|
||||||
return self.__ctl_r(uc.UC_CTL_CPU_MODEL,
|
return self.__ctl_r(uc.UC_CTL_CPU_MODEL,
|
||||||
(ctypes.c_int, None)
|
(ctypes.c_int, None)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def ctl_set_cpu_model(self, val: int) -> None:
|
def ctl_set_cpu_model(self, val: int) -> None:
|
||||||
self.__ctl_w(uc.UC_CTL_CPU_MODEL,
|
self.__ctl_w(uc.UC_CTL_CPU_MODEL,
|
||||||
(ctypes.c_int, val)
|
(ctypes.c_int, val)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def ctl_remove_cache(self, addr: int, end: int) -> None:
|
def ctl_remove_cache(self, addr: int, end: int) -> None:
|
||||||
self.__ctl_w(uc.UC_CTL_TB_REMOVE_CACHE,
|
self.__ctl_w(uc.UC_CTL_TB_REMOVE_CACHE,
|
||||||
(ctypes.c_uint64, addr),
|
(ctypes.c_uint64, addr),
|
||||||
(ctypes.c_uint64, end)
|
(ctypes.c_uint64, end)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def ctl_request_cache(self, addr: int):
|
def ctl_request_cache(self, addr: int):
|
||||||
return self.__ctl_wr(uc.UC_CTL_TB_REQUEST_CACHE,
|
return self.__ctl_wr(uc.UC_CTL_TB_REQUEST_CACHE,
|
||||||
(ctypes.c_uint64, addr),
|
(ctypes.c_uint64, addr),
|
||||||
(uc_tb, None)
|
(uc_tb, None)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def ctl_flush_tb(self) -> None:
|
def ctl_flush_tb(self) -> None:
|
||||||
self.__ctl_w(uc.UC_CTL_TB_FLUSH)
|
self.__ctl_w(uc.UC_CTL_TB_FLUSH)
|
||||||
|
|
||||||
@@ -1139,7 +1094,6 @@ class UcContext(RegStateManager):
|
|||||||
def mode(self) -> int:
|
def mode(self) -> int:
|
||||||
return self._mode
|
return self._mode
|
||||||
|
|
||||||
|
|
||||||
# RegStateManager mixin method implementation
|
# RegStateManager mixin method implementation
|
||||||
def _do_reg_read(self, reg_id: int, reg_obj) -> int:
|
def _do_reg_read(self, reg_id: int, reg_obj) -> int:
|
||||||
"""Private register read implementation.
|
"""Private register read implementation.
|
||||||
@@ -1147,7 +1101,6 @@ class UcContext(RegStateManager):
|
|||||||
|
|
||||||
return uclib.uc_context_reg_read(self._context, reg_id, reg_obj)
|
return uclib.uc_context_reg_read(self._context, reg_id, reg_obj)
|
||||||
|
|
||||||
|
|
||||||
# RegStateManager mixin method implementation
|
# RegStateManager mixin method implementation
|
||||||
def _do_reg_write(self, reg_id: int, reg_obj) -> int:
|
def _do_reg_write(self, reg_id: int, reg_obj) -> int:
|
||||||
"""Private register write implementation.
|
"""Private register write implementation.
|
||||||
@@ -1155,12 +1108,10 @@ class UcContext(RegStateManager):
|
|||||||
|
|
||||||
return uclib.uc_context_reg_write(self._context, reg_id, reg_obj)
|
return uclib.uc_context_reg_write(self._context, reg_id, reg_obj)
|
||||||
|
|
||||||
|
|
||||||
# Make UcContext picklable
|
# Make UcContext picklable
|
||||||
def __getstate__(self):
|
def __getstate__(self):
|
||||||
return bytes(self), self.size, self.arch, self.mode
|
return bytes(self), self.size, self.arch, self.mode
|
||||||
|
|
||||||
|
|
||||||
def __setstate__(self, state) -> None:
|
def __setstate__(self, state) -> None:
|
||||||
context, size, arch, mode = state
|
context, size, arch, mode = state
|
||||||
|
|
||||||
@@ -1172,11 +1123,9 @@ class UcContext(RegStateManager):
|
|||||||
# __init__ won't be invoked, so we are safe to set it here.
|
# __init__ won't be invoked, so we are safe to set it here.
|
||||||
self._to_free = False
|
self._to_free = False
|
||||||
|
|
||||||
|
|
||||||
def __bytes__(self) -> bytes:
|
def __bytes__(self) -> bytes:
|
||||||
return ctypes.string_at(self.context, self.size)
|
return ctypes.string_at(self.context, self.size)
|
||||||
|
|
||||||
|
|
||||||
def __del__(self) -> None:
|
def __del__(self) -> None:
|
||||||
# We need this property since we shouldn't free it if the object is constructed from pickled bytes.
|
# We need this property since we shouldn't free it if the object is constructed from pickled bytes.
|
||||||
if self._to_free:
|
if self._to_free:
|
||||||
|
|||||||
@@ -128,3 +128,17 @@ mkdir build; cd build
|
|||||||
cmake .. -DCMAKE_C_COMPILER=gcc-arm-linux-gnueabihf
|
cmake .. -DCMAKE_C_COMPILER=gcc-arm-linux-gnueabihf
|
||||||
make
|
make
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Building from vcpkg
|
||||||
|
|
||||||
|
The Unicorn port in vcpkg is kept up to date by Microsoft team members and community contributors. The url of vcpkg is: https://github.com/Microsoft/vcpkg . You can download and install unicorn using the vcpkg dependency manager:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/Microsoft/vcpkg.git
|
||||||
|
cd vcpkg
|
||||||
|
./bootstrap-vcpkg.sh # ./bootstrap-vcpkg.bat for Windows
|
||||||
|
./vcpkg integrate install
|
||||||
|
./vcpkg install unicorn
|
||||||
|
```
|
||||||
|
|
||||||
|
If the version is out of date, please [create an issue or pull request](https://github.com/Microsoft/vcpkg) on the vcpkg repository.
|
||||||
|
|||||||
13
docs/FAQ.md
13
docs/FAQ.md
@@ -30,6 +30,13 @@ On x86, all available instructions are: `in` `out` `syscall` `sysenter` `cpuid`.
|
|||||||
|
|
||||||
If you are still using Unicorn1, please upgrade to Unicorn2 for better support.
|
If you are still using Unicorn1, please upgrade to Unicorn2 for better support.
|
||||||
|
|
||||||
|
## Memory hooks get called multiple times for a single instruction
|
||||||
|
|
||||||
|
There are several possibilities, e.g.:
|
||||||
|
|
||||||
|
- The instruction might access memory multiple times like `rep stos` in x86.
|
||||||
|
- The address to access is bad-aligned and thus the MMU emulation will split the access into several aligned memory access. In worst cases on some arch, it leads to byte by byte access.
|
||||||
|
|
||||||
## I can't recover from unmapped read/write even I return `true` in the hook, why?
|
## I can't recover from unmapped read/write even I return `true` in the hook, why?
|
||||||
|
|
||||||
This is a minor change in memory hooks behavior between Unicorn1 and Unicorn2. To gracefully recover from memory read/write error, you have to map the invalid memory before you return true.
|
This is a minor change in memory hooks behavior between Unicorn1 and Unicorn2. To gracefully recover from memory read/write error, you have to map the invalid memory before you return true.
|
||||||
@@ -38,9 +45,11 @@ It is due to the fact that, if users return `true` without memory mapping set up
|
|||||||
|
|
||||||
See the [sample](https://github.com/unicorn-engine/unicorn/blob/c05fbb7e63aed0b60fc2888e08beceb17bce8ac4/samples/sample_x86.c#L1379-L1393) for details.
|
See the [sample](https://github.com/unicorn-engine/unicorn/blob/c05fbb7e63aed0b60fc2888e08beceb17bce8ac4/samples/sample_x86.c#L1379-L1393) for details.
|
||||||
|
|
||||||
## My MIPS emulation gets weird read/write error and CPU exceptions.
|
## My emulation gets weird read/write error and CPU exceptions.
|
||||||
|
|
||||||
Note you might have an address that falls in MIPS `kseg` segments. In that case, MMU is bypassed and you have to make sure the corresponding physical memory is mapped. See [#217](https://github.com/unicorn-engine/unicorn/issues/217), [#1371](https://github.com/unicorn-engine/unicorn/issues/1371), [#1550](https://github.com/unicorn-engine/unicorn/issues/1371).
|
For MIPS, you might have an address that falls in MIPS `kseg` segments. In that case, MMU is bypassed and you have to make sure the corresponding physical memory is mapped. See [#217](https://github.com/unicorn-engine/unicorn/issues/217), [#1371](https://github.com/unicorn-engine/unicorn/issues/1371), [#1550](https://github.com/unicorn-engine/unicorn/issues/1371).
|
||||||
|
|
||||||
|
For ARM, you might have an address that falls in some non-executable segments. For example, for m-class ARM cpu, some memory area is not executable according to [the ARM document](https://developer.arm.com/documentation/ddi0403/d/System-Level-Architecture/System-Address-Map/The-system-address-map?lang=en).
|
||||||
|
|
||||||
## KeyboardInterrupt is not raised during `uc.emu_start`
|
## KeyboardInterrupt is not raised during `uc.emu_start`
|
||||||
|
|
||||||
|
|||||||
@@ -1087,8 +1087,12 @@ RAMBlock *qemu_ram_alloc_from_ptr(struct uc_struct *uc, ram_addr_t size, void *h
|
|||||||
RAMBlock *new_block;
|
RAMBlock *new_block;
|
||||||
ram_addr_t max_size = size;
|
ram_addr_t max_size = size;
|
||||||
|
|
||||||
|
// Don't resize pre-alloced memory as they are given by users.
|
||||||
|
if (!host) {
|
||||||
size = HOST_PAGE_ALIGN(uc, size);
|
size = HOST_PAGE_ALIGN(uc, size);
|
||||||
max_size = HOST_PAGE_ALIGN(uc, max_size);
|
max_size = HOST_PAGE_ALIGN(uc, max_size);
|
||||||
|
}
|
||||||
|
|
||||||
new_block = g_malloc0(sizeof(*new_block));
|
new_block = g_malloc0(sizeof(*new_block));
|
||||||
if (new_block == NULL)
|
if (new_block == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|||||||
@@ -32,40 +32,40 @@
|
|||||||
* converted to a non-qualified type just by applying a binary operator.
|
* converted to a non-qualified type just by applying a binary operator.
|
||||||
*/
|
*/
|
||||||
#define typeof_strip_qual(expr) \
|
#define typeof_strip_qual(expr) \
|
||||||
typeof( \
|
__typeof__( \
|
||||||
__builtin_choose_expr( \
|
__builtin_choose_expr( \
|
||||||
__builtin_types_compatible_p(typeof(expr), bool) || \
|
__builtin_types_compatible_p(__typeof__(expr), bool) || \
|
||||||
__builtin_types_compatible_p(typeof(expr), const bool) || \
|
__builtin_types_compatible_p(__typeof__(expr), const bool) || \
|
||||||
__builtin_types_compatible_p(typeof(expr), volatile bool) || \
|
__builtin_types_compatible_p(__typeof__(expr), volatile bool) || \
|
||||||
__builtin_types_compatible_p(typeof(expr), const volatile bool), \
|
__builtin_types_compatible_p(__typeof__(expr), const volatile bool), \
|
||||||
(bool)1, \
|
(bool)1, \
|
||||||
__builtin_choose_expr( \
|
__builtin_choose_expr( \
|
||||||
__builtin_types_compatible_p(typeof(expr), signed char) || \
|
__builtin_types_compatible_p(__typeof__(expr), signed char) || \
|
||||||
__builtin_types_compatible_p(typeof(expr), const signed char) || \
|
__builtin_types_compatible_p(__typeof__(expr), const signed char) || \
|
||||||
__builtin_types_compatible_p(typeof(expr), volatile signed char) || \
|
__builtin_types_compatible_p(__typeof__(expr), volatile signed char) || \
|
||||||
__builtin_types_compatible_p(typeof(expr), const volatile signed char), \
|
__builtin_types_compatible_p(__typeof__(expr), const volatile signed char), \
|
||||||
(signed char)1, \
|
(signed char)1, \
|
||||||
__builtin_choose_expr( \
|
__builtin_choose_expr( \
|
||||||
__builtin_types_compatible_p(typeof(expr), unsigned char) || \
|
__builtin_types_compatible_p(__typeof__(expr), unsigned char) || \
|
||||||
__builtin_types_compatible_p(typeof(expr), const unsigned char) || \
|
__builtin_types_compatible_p(__typeof__(expr), const unsigned char) || \
|
||||||
__builtin_types_compatible_p(typeof(expr), volatile unsigned char) || \
|
__builtin_types_compatible_p(__typeof__(expr), volatile unsigned char) || \
|
||||||
__builtin_types_compatible_p(typeof(expr), const volatile unsigned char), \
|
__builtin_types_compatible_p(__typeof__(expr), const volatile unsigned char), \
|
||||||
(unsigned char)1, \
|
(unsigned char)1, \
|
||||||
__builtin_choose_expr( \
|
__builtin_choose_expr( \
|
||||||
__builtin_types_compatible_p(typeof(expr), signed short) || \
|
__builtin_types_compatible_p(__typeof__(expr), signed short) || \
|
||||||
__builtin_types_compatible_p(typeof(expr), const signed short) || \
|
__builtin_types_compatible_p(__typeof__(expr), const signed short) || \
|
||||||
__builtin_types_compatible_p(typeof(expr), volatile signed short) || \
|
__builtin_types_compatible_p(__typeof__(expr), volatile signed short) || \
|
||||||
__builtin_types_compatible_p(typeof(expr), const volatile signed short), \
|
__builtin_types_compatible_p(__typeof__(expr), const volatile signed short), \
|
||||||
(signed short)1, \
|
(signed short)1, \
|
||||||
__builtin_choose_expr( \
|
__builtin_choose_expr( \
|
||||||
__builtin_types_compatible_p(typeof(expr), unsigned short) || \
|
__builtin_types_compatible_p(__typeof__(expr), unsigned short) || \
|
||||||
__builtin_types_compatible_p(typeof(expr), const unsigned short) || \
|
__builtin_types_compatible_p(__typeof__(expr), const unsigned short) || \
|
||||||
__builtin_types_compatible_p(typeof(expr), volatile unsigned short) || \
|
__builtin_types_compatible_p(__typeof__(expr), volatile unsigned short) || \
|
||||||
__builtin_types_compatible_p(typeof(expr), const volatile unsigned short), \
|
__builtin_types_compatible_p(__typeof__(expr), const volatile unsigned short), \
|
||||||
(unsigned short)1, \
|
(unsigned short)1, \
|
||||||
(expr)+0))))))
|
(expr)+0))))))
|
||||||
|
|
||||||
#ifdef __ATOMIC_RELAXED
|
#if defined(__ATOMIC_RELAXED) && !(defined(_MSC_VER) && defined(__clang__))
|
||||||
/* For C11 atomic ops */
|
/* For C11 atomic ops */
|
||||||
|
|
||||||
/* Sanity check that the size of an atomic operation isn't "overly large".
|
/* Sanity check that the size of an atomic operation isn't "overly large".
|
||||||
|
|||||||
@@ -146,7 +146,9 @@ static inline Int128 bswap128(Int128 a)
|
|||||||
#else /* !CONFIG_INT128 */
|
#else /* !CONFIG_INT128 */
|
||||||
|
|
||||||
typedef struct Int128 Int128;
|
typedef struct Int128 Int128;
|
||||||
|
#if !(defined(_MSC_VER) && defined(__clang__))
|
||||||
typedef Int128 __int128_t;
|
typedef Int128 __int128_t;
|
||||||
|
#endif
|
||||||
|
|
||||||
struct Int128 {
|
struct Int128 {
|
||||||
uint64_t lo;
|
uint64_t lo;
|
||||||
|
|||||||
@@ -1061,7 +1061,11 @@ void helper_store_msr(CPUPPCState *env, target_ulong val)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if defined(TARGET_PPC64)
|
#if defined(TARGET_PPC64)
|
||||||
|
#if defined(_MSC_VER) && defined(__clang__)
|
||||||
|
void helper_pminsn(CPUPPCState *env, uint32_t insn)
|
||||||
|
#else
|
||||||
void helper_pminsn(CPUPPCState *env, powerpc_pm_insn_t insn)
|
void helper_pminsn(CPUPPCState *env, powerpc_pm_insn_t insn)
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
CPUState *cs;
|
CPUState *cs;
|
||||||
|
|
||||||
|
|||||||
@@ -251,15 +251,22 @@ static void test_uc_hook_cached_uaf(void)
|
|||||||
uc_hook h;
|
uc_hook h;
|
||||||
uint64_t count = 0;
|
uint64_t count = 0;
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
void *callback = mmap(NULL, 4096, PROT_READ | PROT_WRITE | PROT_EXEC,
|
// Apple Silicon does not allow RWX pages.
|
||||||
|
void *callback = mmap(NULL, 4096, PROT_READ | PROT_WRITE,
|
||||||
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||||
|
TEST_CHECK(callback != (void*)-1);
|
||||||
#else
|
#else
|
||||||
void *callback = VirtualAlloc(NULL, 4096, MEM_RESERVE | MEM_COMMIT,
|
void *callback = VirtualAlloc(NULL, 4096, MEM_RESERVE | MEM_COMMIT,
|
||||||
PAGE_EXECUTE_READWRITE);
|
PAGE_EXECUTE_READWRITE);
|
||||||
|
TEST_CHECK(callback != NULL);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
memcpy(callback, (void *)test_uc_hook_cached_cb, 4096);
|
memcpy(callback, (void *)test_uc_hook_cached_cb, 4096);
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
TEST_CHECK(mprotect(callback, 4096, PROT_READ | PROT_EXEC) == 0);
|
||||||
|
#endif
|
||||||
|
|
||||||
uc_common_setup(&uc, UC_ARCH_X86, UC_MODE_32, code, sizeof(code) - 1);
|
uc_common_setup(&uc, UC_ARCH_X86, UC_MODE_32, code, sizeof(code) - 1);
|
||||||
|
|
||||||
OK(uc_hook_add(uc, &h, UC_HOOK_CODE, (void *)callback, (void *)&count, 1,
|
OK(uc_hook_add(uc, &h, UC_HOOK_CODE, (void *)callback, (void *)&count, 1,
|
||||||
@@ -273,8 +280,16 @@ static void test_uc_hook_cached_uaf(void)
|
|||||||
// This will clear deleted hooks and SHOULD clear cache.
|
// This will clear deleted hooks and SHOULD clear cache.
|
||||||
OK(uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 0));
|
OK(uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 0));
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
TEST_CHECK(mprotect(callback, 4096, PROT_READ | PROT_WRITE) == 0);
|
||||||
|
#endif
|
||||||
|
|
||||||
memset(callback, 0, 4096);
|
memset(callback, 0, 4096);
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
TEST_CHECK(mprotect(callback, 4096, PROT_READ | PROT_EXEC) == 0);
|
||||||
|
#endif
|
||||||
|
|
||||||
// Now hooks are deleted and thus this will trigger a UAF
|
// Now hooks are deleted and thus this will trigger a UAF
|
||||||
OK(uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 0));
|
OK(uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 0));
|
||||||
|
|
||||||
|
|||||||
@@ -184,8 +184,15 @@ static void test_map_big_memory(void)
|
|||||||
|
|
||||||
OK(uc_open(UC_ARCH_X86, UC_MODE_64, &uc));
|
OK(uc_open(UC_ARCH_X86, UC_MODE_64, &uc));
|
||||||
|
|
||||||
|
#if defined(_WIN32) || defined(__WIN32__) || defined(__WINDOWS__)
|
||||||
|
uint64_t requested_size = 0xfffffffffffff000; // assume 4K page size
|
||||||
|
#else
|
||||||
|
long ps = sysconf(_SC_PAGESIZE);
|
||||||
|
uint64_t requested_size = (uint64_t)(-ps);
|
||||||
|
#endif
|
||||||
|
|
||||||
uc_assert_err(UC_ERR_NOMEM,
|
uc_assert_err(UC_ERR_NOMEM,
|
||||||
uc_mem_map(uc, 0x0, 0xfffffffffffff000, UC_PROT_ALL));
|
uc_mem_map(uc, 0x0, requested_size, UC_PROT_ALL));
|
||||||
|
|
||||||
OK(uc_close(uc));
|
OK(uc_close(uc));
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user