merge master & fix conflicts

This commit is contained in:
Nguyen Anh Quynh 2020-11-17 13:07:34 +08:00
commit fb667f5cdc
32 changed files with 550 additions and 142 deletions

67
.github/workflows/python-publish.yml vendored Normal file
View File

@ -0,0 +1,67 @@
name: PyPI 📦 Distribution
on: [push]
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [windows-latest, macos-latest, ubuntu-latest]
platform: [x32, x64]
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.x'
- name: Set up MSVC
if: matrix.os == 'windows-latest'
uses: microsoft/setup-msbuild@v1
- name: Install dependencies
run: |
pip install setuptools wheel
- name: Build distribution 📦
shell: bash
run: |
if [ ${{ matrix.platform }} == 'x32' ] && [ ${{ matrix.os }} == 'windows-latest' ]; then
cd bindings/python && python setup.py build -p win32 sdist bdist_wheel -p win32
rm dist/*.tar.gz
elif [ ${{ matrix.platform }} == 'x32' ] && [ ${{ matrix.os }} == 'ubuntu-latest' ]; then
docker run --rm -v `pwd`/:/work dockcross/manylinux1-x86 > ./dockcross
chmod +x ./dockcross
./dockcross bindings/python/build_wheel.sh
elif [ ${{ matrix.platform }} == 'x64' ] && [ ${{ matrix.os }} == 'ubuntu-latest' ]; then
docker run --rm -v `pwd`/:/work dockcross/manylinux1-x64 > ./dockcross
chmod +x ./dockcross
./dockcross bindings/python/build_wheel.sh
elif [ ${{ matrix.platform }} == 'x32' ] && [ ${{ matrix.os }} == 'macos-latest' ]; then
cd bindings/python && python setup.py sdist
else
cd bindings/python && python setup.py bdist_wheel
fi
- uses: actions/upload-artifact@v2
with:
path: ${{ github.workspace }}/bindings/python/dist/*
publish:
needs: [build]
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags')
steps:
- uses: actions/download-artifact@v2
with:
name: artifact
path: dist
- name: Publish distribution 📦 to PyPI
uses: pypa/gh-action-pypi-publish@master
with:
user: __token__
password: ${{ secrets.pypi_pass }}

View File

@ -16,23 +16,31 @@ compiler:
- gcc - gcc
os: os:
- linux - linux
- osx
- windows - windows
arch:
- amd64
- arm64
matrix: matrix:
fast_finish: true fast_finish: true
include: exclude:
- name: "Linux arm64 clang C" - os: windows
arch: arm64 arch: arm64
os: linux include:
- name: "Compiler: clang C"
os: osx
osx_image: xcode10.1
python: 3.7
compiler: clang compiler: clang
language: c language: c
env: env:
- PATH=$PATH:/usr/local/opt/binutils/bin - PATH=$PATH:/usr/local/opt/binutils/bin
script: make header && make && make -C tests/unit test && make -C tests/regress test script: make header && make && make -C tests/unit test && make -C tests/regress test
- name: "Linux arm64 gcc C" - name: "Compiler: gcc C"
arch: arm64 os: osx
os: linux osx_image: xcode10.1
python: 3.7
compiler: gcc compiler: gcc
language: c language: c
env: env:
@ -125,6 +133,42 @@ matrix:
- cp libunicorn.* ../ - cp libunicorn.* ../
- make -C ../tests/unit test && make -C ../tests/regress test - make -C ../tests/unit test && make -C ../tests/regress test
- name: "Linux Cmake Static 32bit"
os: linux
compiler: gcc
env:
- CFLAGS="-m32" LDFLAGS="-m32" LDFLAGS_STATIC="-m32" UNICORN_QEMU_FLAGS="--cpu=i386"
- PATH=$PATH:/usr/local/opt/binutils/bin
script:
- mkdir build
- cd build
- cmake -DCMAKE_BUILD_TYPE=Release -DUNICORN_ARCH=x86 -DUNICORN_BUILD_SHARED=OFF .. && make -j8
# temporarily disable test for static build
# - cp libunicorn.* ../
# - make -C ../tests/unit test && make -C ../tests/regress test
addons:
apt:
packages:
- lib32ncurses5-dev
- lib32z1-dev
- libpthread-stubs0-dev
- lib32gcc-4.8-dev
- libc6-dev-i386
- gcc-multilib
- libcmocka-dev:i386
- name: "Linux Cmake Static 64bit"
os: linux
compiler: gcc
env:
- PATH=$PATH:/usr/local/opt/binutils/bin
script:
- mkdir build
- cd build
- cmake -DCMAKE_BUILD_TYPE=Release -DUNICORN_BUILD_SHARED=OFF .. && make -j8
# - cp libunicorn.* ../
# - make -C ../tests/unit test && make -C ../tests/regress test
- name: "MacOSX brew" - name: "MacOSX brew"
os: osx os: osx
osx_image: xcode10.1 osx_image: xcode10.1
@ -214,12 +258,14 @@ matrix:
- export LDFLAGS="-m32" - export LDFLAGS="-m32"
- export LDFLAGS_STATIC="-m32" - export LDFLAGS_STATIC="-m32"
- export UNICORN_QEMU_FLAGS="--cpu=i386" - export UNICORN_QEMU_FLAGS="--cpu=i386"
before_cache: # before_cache:
- $msys2 pacman --sync --clean --noconfirm # - $msys2 pacman --sync --clean --noconfirm
cache: # cache:
directories: # timeout:
- $HOME/AppData/Local/Temp/chocolatey # 1000
- /C/tools/msys64 # directories:
# - $HOME/AppData/Local/Temp/chocolatey
# - /C/tools/msys64
script: script:
- $shell make header; $shell make; cp unicorn.dll /C/Windows/SysWOW64/; $shell make test - $shell make header; $shell make; cp unicorn.dll /C/Windows/SysWOW64/; $shell make test
@ -256,12 +302,14 @@ matrix:
- export CC=x86_64-w64-mingw32-gcc - export CC=x86_64-w64-mingw32-gcc
- export AR=gcc-ar - export AR=gcc-ar
- export RANLIB=gcc-ranlib - export RANLIB=gcc-ranlib
before_cache: # before_cache:
- $msys2 pacman --sync --clean --noconfirm # - $msys2 pacman --sync --clean --noconfirm
cache: # cache:
directories: # timeout:
- $HOME/AppData/Local/Temp/chocolatey # 1000
- /C/tools/msys64 # directories:
# - $HOME/AppData/Local/Temp/chocolatey
# - /C/tools/msys64
script: script:
- $shell make header; $shell make; cp unicorn.dll /C/Windows/System32/; $shell make test - $shell make header; $shell make; cp unicorn.dll /C/Windows/System32/; $shell make test
addons: addons:

View File

@ -881,6 +881,10 @@ if (MSVC)
qemu/util/oslib-win32.c qemu/util/oslib-win32.c
qemu/util/qemu-thread-win32.c qemu/util/qemu-thread-win32.c
) )
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
enable_language(ASM_MASM)
set(UNICORN_SRCS ${UNICORN_SRCS} qemu/util/setjmp-wrapper-win32.asm)
endif()
else() else()
set(UNICORN_SRCS set(UNICORN_SRCS
${UNICORN_SRCS_COMMON} ${UNICORN_SRCS_COMMON}
@ -985,6 +989,7 @@ if(NOT MSVC)
file(GLOB UNICORN_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/include/unicorn/*.h) file(GLOB UNICORN_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/include/unicorn/*.h)
install(TARGETS unicorn install(TARGETS unicorn
RUNTIME DESTINATION bin RUNTIME DESTINATION bin
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
) )
install(FILES ${UNICORN_HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/unicorn) install(FILES ${UNICORN_HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/unicorn)

View File

@ -74,3 +74,4 @@ Huitao Chen (chenhuitao) & KaiJern Lau (xwings): Python3 support for building
Kevin Foo (chfl4gs): Travis-CI migration Kevin Foo (chfl4gs): Travis-CI migration
Simon Gorchakov: PowerPC target Simon Gorchakov: PowerPC target
Stuart Dootson (studoot): MSVC compatibility with PowerPC target support Stuart Dootson (studoot): MSVC compatibility with PowerPC target support
Ziqiao Kong (lazymio): uc_context_free() API and various bug fix & improvement.

View File

@ -1,5 +1,50 @@
This file details the changelog of Unicorn Engine. This file details the changelog of Unicorn Engine.
-----------------------------------
[Version 1.0.2]: October 21st, 2020
- Fix Java binding compilation
- Enable building for ARM little-endian only (ignore big-endian)
------------------------------------
[Version 1.0.2-rc6]: Sept 24th, 2020
- Add uc_context_free() API
- Fix context saving/retoring API (core & Python binding)
------------------------------------
[Version 1.0.2-rc5]: Sept 22nd, 2020
- Add cmake option to build Unicorn as a static library
- Fix error handling of mmap()
- uc_emu_start() can be reentrant
- Fix naming conflicts when built with systemd
- Fix setjmp/longjmp on native Windows
- Fix enabled hooks even after deleting them
- X86:
- Fix 64bit fstenv
- Fix IP value of 16bit mode
- ARM:
- Fix APSR handling
- Python: Remove UC_ERR_TIMEOUT
-----------------------------------
[Version 1.0.2-rc4]: May 29th, 2020
- No longer require Python to build
- Fix recursive UC_HOOK_MEM callbacks for cross pages access
- Remove UC_ERR_TIMEOUT, so timeout on uc_emu_start() is not considered error
- Added UC_QUERY_TIMEOUT to query exit reason
- Fix UAF when deleting hook while in hook callback
- Ensure that hooks are unaffected by a request to stop emulation.
- Fix block hooks being called twice after an early exit from execution.
- Fix binding install on python2 (MacOS)
- X86:
- Support read/write STn registers
- Support read/write X64 base regs
- ARM64:
- Support some new registers
---------------------------------- ----------------------------------
[Version 1.0.1]: April 20th, 2017 [Version 1.0.1]: April 20th, 2017

7
SPONSORS.TXT Normal file
View File

@ -0,0 +1,7 @@
* Version 1.0.2 - October 21st, 2020
Release 1.0.2 was sponsored by the following companies (in no particular order).
- Catena Cyber: https://catenacyber.fr
- Grayshift: https://grayshift.com
- Google: https://google.com

View File

@ -34,3 +34,6 @@ More bindings created & maintained externally by community are available as foll
- pharo-unicorn: Pharo binding (by Guille Polito) - pharo-unicorn: Pharo binding (by Guille Polito)
https://github.com/guillep/pharo-unicorn https://github.com/guillep/pharo-unicorn
- Unicorn.js: JavaScript binding (by Alexandro Sanchez)
https://github.com/AlexAltea/unicorn.js

View File

@ -3,7 +3,7 @@
JAVA_HOME := $(shell jrunscript -e 'java.lang.System.out.println(java.lang.System.getProperty("java.home"));') JAVA_HOME := $(shell jrunscript -e 'java.lang.System.out.println(java.lang.System.getProperty("java.home"));')
JAVA_INC := $(shell realpath $(JAVA_HOME)/../include) JAVA_INC := $(shell realpath $(JAVA_HOME)/include)
JAVA_PLATFORM_INC := $(shell dirname `find $(JAVA_INC) -name jni_md.h`) JAVA_PLATFORM_INC := $(shell dirname `find $(JAVA_INC) -name jni_md.h`)

View File

@ -108,7 +108,7 @@ type
@user_data: user data passed to tracing APIs @user_data: user data passed to tracing APIs
@return: return true to continue, or false to stop program (due to invalid memory). @return: return true to continue, or false to stop program (due to invalid memory).
NOTE: returning true to continue execution will only work if if the accessed NOTE: returning true to continue execution will only work if the accessed
memory is made accessible with the correct permissions during the hook. memory is made accessible with the correct permissions during the hook.
In the event of a UC_MEM_READ_UNMAPPED or UC_MEM_WRITE_UNMAPPED callback, In the event of a UC_MEM_READ_UNMAPPED or UC_MEM_WRITE_UNMAPPED callback,

14
bindings/python/build_wheel.sh Executable file
View File

@ -0,0 +1,14 @@
#!/bin/bash
set -e -x
cd bindings/python
# Compile wheels
if [ -f /opt/python/cp36-cp36m/bin/python ];then
/opt/python/cp36-cp36m/bin/python setup.py bdist_wheel
else
python3 setup.py bdist_wheel
fi
cd dist
auditwheel repair *.whl
mv -f wheelhouse/*.whl .

View File

@ -45,7 +45,7 @@ def test_arm64():
# tracing all basic blocks with customized callback # tracing all basic blocks with customized callback
mu.hook_add(UC_HOOK_BLOCK, hook_block) mu.hook_add(UC_HOOK_BLOCK, hook_block)
# tracing all instructions with customized callback # tracing one instruction at ADDRESS with customized callback
mu.hook_add(UC_HOOK_CODE, hook_code, begin=ADDRESS, end=ADDRESS) mu.hook_add(UC_HOOK_CODE, hook_code, begin=ADDRESS, end=ADDRESS)
# emulate machine code in infinite time # emulate machine code in infinite time

View File

@ -4,7 +4,7 @@
from __future__ import print_function from __future__ import print_function
from unicorn import * from unicorn import *
from unicorn.x86_const import * from unicorn.x86_const import *
import pickle
X86_CODE32 = b"\x41\x4a\x66\x0f\xef\xc1" # INC ecx; DEC edx; PXOR xmm0, xmm1 X86_CODE32 = b"\x41\x4a\x66\x0f\xef\xc1" # INC ecx; DEC edx; PXOR xmm0, xmm1
X86_CODE32_LOOP = b"\x41\x4a\xeb\xfe" # INC ecx; DEC edx; JMP self-loop X86_CODE32_LOOP = b"\x41\x4a\xeb\xfe" # INC ecx; DEC edx; JMP self-loop
@ -384,15 +384,8 @@ def test_i386_loop():
print(">>> EDX = 0x%x" %r_edx) print(">>> EDX = 0x%x" %r_edx)
except UcError as e: except UcError as e:
# timeout is acceptable in this case print("ERROR: %s" % e)
if e.errno == UC_ERR_TIMEOUT:
print(">>> Emulation done. Below is the CPU context")
r_ecx = mu.reg_read(UC_X86_REG_ECX)
r_edx = mu.reg_read(UC_X86_REG_EDX)
print(">>> ECX = 0x%x" %r_ecx)
print(">>> EDX = 0x%x" %r_edx)
else:
print("ERROR: %s" % e)
# Test X86 32 bit with IN/OUT instruction # Test X86 32 bit with IN/OUT instruction
def test_i386_inout(): def test_i386_inout():
@ -460,11 +453,17 @@ def test_i386_context_save():
print(">>> Saving CPU context") print(">>> Saving CPU context")
saved_context = mu.context_save() saved_context = mu.context_save()
print(">>> Pickling CPU context")
pickled_saved_context = pickle.dumps(saved_context)
print(">>> Running emulation for the second time") print(">>> Running emulation for the second time")
mu.emu_start(address, address+1) mu.emu_start(address, address+1)
print(">>> Emulation done. Below is the CPU context") print(">>> Emulation done. Below is the CPU context")
print(">>> EAX = 0x%x" %(mu.reg_read(UC_X86_REG_EAX))) print(">>> EAX = 0x%x" %(mu.reg_read(UC_X86_REG_EAX)))
print(">>> Unpickling CPU context")
saved_context = pickle.loads(pickled_saved_context)
print(">>> CPU context restored. Below is the CPU context") print(">>> CPU context restored. Below is the CPU context")
mu.context_restore(saved_context) mu.context_restore(saved_context)
print(">>> EAX = 0x%x" %(mu.reg_read(UC_X86_REG_EAX))) print(">>> EAX = 0x%x" %(mu.reg_read(UC_X86_REG_EAX)))

View File

@ -15,6 +15,7 @@ from distutils.util import get_platform
from distutils.command.build import build from distutils.command.build import build
from distutils.command.sdist import sdist from distutils.command.sdist import sdist
from setuptools.command.bdist_egg import bdist_egg from setuptools.command.bdist_egg import bdist_egg
from setuptools.command.develop import develop
SYSTEM = sys.platform SYSTEM = sys.platform
@ -22,7 +23,7 @@ SYSTEM = sys.platform
IS_64BITS = platform.architecture()[0] == '64bit' IS_64BITS = platform.architecture()[0] == '64bit'
# are we building from the repository or from a source distribution? # are we building from the repository or from a source distribution?
ROOT_DIR = os.path.dirname(os.path.realpath(__file__)) ROOT_DIR = os.path.dirname(os.path.abspath(__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')
@ -62,9 +63,12 @@ if SYSTEM == 'darwin':
LIBRARY_FILE = "libunicorn.dylib" LIBRARY_FILE = "libunicorn.dylib"
MAC_LIBRARY_FILE = "libunicorn*.dylib" MAC_LIBRARY_FILE = "libunicorn*.dylib"
STATIC_LIBRARY_FILE = None STATIC_LIBRARY_FILE = None
elif SYSTEM in ('win32', 'cygwin'): elif SYSTEM == 'win32':
LIBRARY_FILE = "unicorn.dll" LIBRARY_FILE = "unicorn.dll"
STATIC_LIBRARY_FILE = "unicorn.lib" STATIC_LIBRARY_FILE = "unicorn.lib"
elif SYSTEM == 'cygwin':
LIBRARY_FILE = "cygunicorn.dll"
STATIC_LIBRARY_FILE = None
else: else:
LIBRARY_FILE = "libunicorn.so" LIBRARY_FILE = "libunicorn.so"
STATIC_LIBRARY_FILE = None STATIC_LIBRARY_FILE = None
@ -140,16 +144,22 @@ def build_libraries():
os.chdir(BUILD_DIR) os.chdir(BUILD_DIR)
try: try:
subprocess.check_call(['msbuild', '/help']) subprocess.check_call(['msbuild', '-ver'])
except: except:
has_msbuild = False has_msbuild = False
else: else:
has_msbuild = True has_msbuild = True
if has_msbuild and SYSTEM == 'win32': if has_msbuild and SYSTEM == 'win32':
plat = 'Win32' if platform.architecture()[0] == '32bit' else 'x64' if platform.architecture()[0] == '32bit':
plat = 'Win32'
elif 'win32' in sys.argv:
plat = 'Win32'
else:
plat = 'x64'
conf = 'Debug' if os.getenv('DEBUG', '') else 'Release' conf = 'Debug' if os.getenv('DEBUG', '') else 'Release'
subprocess.call(['msbuild', '-m', '-p:Platform=' + plat, '-p:Configuration=' + conf], cwd=os.path.join(BUILD_DIR, 'msvc')) subprocess.call(['msbuild', 'unicorn.sln', '-m', '-p:Platform=' + plat, '-p:Configuration=' + conf], cwd=os.path.join(BUILD_DIR, 'msvc'))
obj_dir = os.path.join(BUILD_DIR, 'msvc', plat, conf) obj_dir = os.path.join(BUILD_DIR, 'msvc', plat, conf)
shutil.copy(os.path.join(obj_dir, LIBRARY_FILE), LIBS_DIR) shutil.copy(os.path.join(obj_dir, LIBRARY_FILE), LIBS_DIR)
@ -159,12 +169,7 @@ def build_libraries():
new_env = dict(os.environ) new_env = dict(os.environ)
new_env['UNICORN_BUILD_CORE_ONLY'] = 'yes' new_env['UNICORN_BUILD_CORE_ONLY'] = 'yes'
cmd = ['sh', './make.sh'] cmd = ['sh', './make.sh']
if SYSTEM == "cygwin": if SYSTEM == "win32":
if IS_64BITS:
cmd.append('cygwin-mingw64')
else:
cmd.append('cygwin-mingw32')
elif SYSTEM == "win32":
if IS_64BITS: if IS_64BITS:
cmd.append('cross-win64') cmd.append('cross-win64')
else: else:
@ -191,7 +196,6 @@ def build_libraries():
sys.exit(1) sys.exit(1)
os.chdir(cwd) os.chdir(cwd)
class custom_sdist(sdist): class custom_sdist(sdist):
def run(self): def run(self):
clean_bins() clean_bins()
@ -207,6 +211,12 @@ class custom_build(build):
build_libraries() build_libraries()
return build.run(self) return build.run(self)
class custom_develop(develop):
def run(self):
log.info("Building C extensions")
build_libraries()
return develop.run(self)
class custom_bdist_egg(bdist_egg): class custom_bdist_egg(bdist_egg):
def run(self): def run(self):
self.run_command('build') self.run_command('build')
@ -215,10 +225,6 @@ class custom_bdist_egg(bdist_egg):
def dummy_src(): def dummy_src():
return [] return []
cmdclass = {}
cmdclass['build'] = custom_build
cmdclass['sdist'] = custom_sdist
cmdclass['bdist_egg'] = custom_bdist_egg
if 'bdist_wheel' in sys.argv and '--plat-name' not in sys.argv: if 'bdist_wheel' in sys.argv and '--plat-name' not in sys.argv:
idx = sys.argv.index('bdist_wheel') + 1 idx = sys.argv.index('bdist_wheel') + 1
@ -239,20 +245,24 @@ if 'bdist_wheel' in sys.argv and '--plat-name' not in sys.argv:
# https://www.python.org/dev/peps/pep-0425/ # https://www.python.org/dev/peps/pep-0425/
sys.argv.insert(idx + 1, name.replace('.', '_').replace('-', '_')) sys.argv.insert(idx + 1, name.replace('.', '_').replace('-', '_'))
try:
from setuptools.command.develop import develop
class custom_develop(develop):
def run(self):
log.info("Building C extensions")
build_libraries()
return develop.run(self)
cmdclass['develop'] = custom_develop long_desc = '''
except ImportError: Unicorn is a lightweight, multi-platform, multi-architecture CPU emulator framework
print("Proper 'develop' support unavailable.") based on [QEMU](https://qemu.org).
def join_all(src, files): Unicorn offers some unparalleled features:
return tuple(os.path.join(src, f) for f in files)
- Multi-architecture: ARM, ARM64 (ARMv8), M68K, MIPS, SPARC, and X86 (16, 32, 64-bit)
- Clean/simple/lightweight/intuitive architecture-neutral API
- Implemented in pure C language, with bindings for Crystal, Clojure, Visual Basic, Perl, Rust, Ruby, Python, Java, .NET, Go, Delphi/Free Pascal, Haskell, Pharo, and Lua.
- Native support for Windows & *nix (with Mac OSX, Linux, *BSD & Solaris confirmed)
- High performance via Just-In-Time compilation
- Support for fine-grained instrumentation at various levels
- Thread-safety by design
- Distributed under free software license GPLv2
Further information is available at https://www.unicorn-engine.org
'''
long_desc = ''' long_desc = '''
Unicorn is a lightweight, multi-platform, multi-architecture CPU emulator framework Unicorn is a lightweight, multi-platform, multi-architecture CPU emulator framework
@ -282,17 +292,17 @@ setup(
description='Unicorn CPU emulator engine', description='Unicorn CPU emulator engine',
long_description=long_desc, long_description=long_desc,
long_description_content_type="text/markdown", long_description_content_type="text/markdown",
url='http://www.unicorn-engine.org', url='https://www.unicorn-engine.org',
classifiers=[ classifiers=[
'License :: OSI Approved :: BSD License', 'License :: OSI Approved :: BSD License',
'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2',
'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3',
], ],
requires=['ctypes'], requires=['ctypes'],
cmdclass=cmdclass, cmdclass={'build': custom_build, 'develop': custom_develop, 'sdist': custom_sdist, 'bdist_egg': custom_bdist_egg},
zip_safe=True, zip_safe=False,
include_package_data=True, include_package_data=True,
is_pure=True, is_pure=False,
package_data={ package_data={
'unicorn': ['lib/*', 'include/unicorn/*'] 'unicorn': ['lib/*', 'include/unicorn/*']
} }

View File

@ -139,6 +139,7 @@ _setup_prototype(_uc, "uc_free", ucerr, ctypes.c_void_p)
_setup_prototype(_uc, "uc_context_save", ucerr, uc_engine, uc_context) _setup_prototype(_uc, "uc_context_save", ucerr, uc_engine, uc_context)
_setup_prototype(_uc, "uc_context_restore", ucerr, uc_engine, uc_context) _setup_prototype(_uc, "uc_context_restore", ucerr, uc_engine, uc_context)
_setup_prototype(_uc, "uc_context_size", ctypes.c_size_t, uc_engine) _setup_prototype(_uc, "uc_context_size", ctypes.c_size_t, uc_engine)
_setup_prototype(_uc, "uc_context_free", ucerr, uc_context)
_setup_prototype(_uc, "uc_mem_regions", ucerr, uc_engine, ctypes.POINTER(ctypes.POINTER(_uc_mem_region)), ctypes.POINTER(ctypes.c_uint32)) _setup_prototype(_uc, "uc_mem_regions", ucerr, uc_engine, ctypes.POINTER(ctypes.POINTER(_uc_mem_region)), ctypes.POINTER(ctypes.c_uint32))
# uc_hook_add is special due to variable number of arguments # uc_hook_add is special due to variable number of arguments
@ -604,7 +605,7 @@ class Uc(object):
return context return context
def context_update(self, context): def context_update(self, context):
status = _uc.uc_context_save(self._uch, context) status = _uc.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)
@ -628,16 +629,40 @@ class Uc(object):
_uc.uc_free(regions) _uc.uc_free(regions)
class UcContext(ctypes.Structure): class UcContext:
def __init__(self, h): def __init__(self, h):
self.context = uc_context() self._context = uc_context()
self._size = _uc.uc_context_size(h)
status = _uc.uc_context_alloc(h, ctypes.byref(self.context)) self._to_free = True
status = _uc.uc_context_alloc(h, ctypes.byref(self._context))
if status != uc.UC_ERR_OK: if status != uc.UC_ERR_OK:
raise UcError(status) raise UcError(status)
@property
def context(self):
return self._context
@property
def size(self):
return self._size
# Make UcContext picklable
def __getstate__(self):
return (bytes(self), self.size)
def __setstate__(self, state):
self._size = state[1]
self._context = ctypes.cast(ctypes.create_string_buffer(state[0], self._size), uc_context)
# __init__ won'e be invoked, so we are safe to set it here.
self._to_free = False
def __bytes__(self):
return ctypes.string_at(self.context, self.size)
def __del__(self): def __del__(self):
_uc.uc_free(self.context) # We need this property since we shouldn't free it if the object is constructed from pickled bytes.
if self._to_free:
_uc.uc_context_free(self._context)
# print out debugging info # print out debugging info

View File

@ -123,7 +123,7 @@ enum uc_hook_idx {
// if statement to check hook bounds // if statement to check hook bounds
#define HOOK_BOUND_CHECK(hh, addr) \ #define HOOK_BOUND_CHECK(hh, addr) \
((((addr) >= (hh)->begin && (addr) <= (hh)->end) \ ((((addr) >= (hh)->begin && (addr) <= (hh)->end) \
|| (hh)->begin > (hh)->end)) || (hh)->begin > (hh)->end) && !((hh)->to_delete))
#define HOOK_EXISTS(uc, idx) ((uc)->hook[idx##_IDX].head != NULL) #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) #define HOOK_EXISTS_BOUNDED(uc, idx, addr) _hook_exists_bounded((uc)->hook[idx##_IDX].head, addr)
@ -227,13 +227,15 @@ struct uc_struct {
uint32_t target_page_align; uint32_t target_page_align;
uint64_t next_pc; // save next PC for some special cases uint64_t next_pc; // save next PC for some special cases
bool hook_insert; // insert new hook at begin of the hook list (append by default) bool hook_insert; // insert new hook at begin of the hook list (append by default)
struct list saved_contexts; // The contexts saved by this uc_struct.
}; };
// Metadata stub for the variable-size cpu context used with uc_context_*() // Metadata stub for the variable-size cpu context used with uc_context_*()
// We also save cpu->jmp_env, so emulation can be reentrant // We also save cpu->jmp_env, so emulation can be reentrant
struct uc_context { struct uc_context {
size_t context_size; // size of the real internal context structure size_t context_size; // size of the real internal context structure
unsigned int jmp_env_size; // size of cpu->jmp_env size_t jmp_env_size; // size of cpu->jmp_env
struct uc_struct* uc; // the uc_struct which creates this context
char data[0]; // context + cpu->jmp_env char data[0]; // context + cpu->jmp_env
}; };

View File

@ -301,7 +301,7 @@ typedef void (*uc_cb_hookmem_t)(uc_engine *uc, uc_mem_type type,
@user_data: user data passed to tracing APIs @user_data: user data passed to tracing APIs
@return: return true to continue, or false to stop program (due to invalid memory). @return: return true to continue, or false to stop program (due to invalid memory).
NOTE: returning true to continue execution will only work if if the accessed NOTE: returning true to continue execution will only work if the accessed
memory is made accessible with the correct permissions during the hook. memory is made accessible with the correct permissions during the hook.
In the event of a UC_MEM_READ_UNMAPPED or UC_MEM_WRITE_UNMAPPED callback, In the event of a UC_MEM_READ_UNMAPPED or UC_MEM_WRITE_UNMAPPED callback,
@ -414,7 +414,7 @@ UNICORN_EXPORT
uc_err uc_query(uc_engine *uc, uc_query_type type, size_t *result); uc_err uc_query(uc_engine *uc, uc_query_type type, size_t *result);
/* /*
Report the last error number when some API function fail. Report the last error number when some API function fails.
Like glibc's errno, uc_errno might not retain its old value once accessed. Like glibc's errno, uc_errno might not retain its old value once accessed.
@uc: handle returned by uc_open() @uc: handle returned by uc_open()
@ -526,7 +526,7 @@ uc_err uc_mem_read(uc_engine *uc, uint64_t address, void *bytes, size_t size);
@uc: handle returned by uc_open() @uc: handle returned by uc_open()
@begin: address where emulation starts @begin: address where emulation starts
@until: address where emulation stops (i.e when this address is hit) @until: address where emulation stops (i.e. when this address is hit)
@timeout: duration to emulate the code (in microseconds). When this value is 0, @timeout: duration to emulate the code (in microseconds). When this value is 0,
we will emulate the code in infinite time, until the code is finished. we will emulate the code in infinite time, until the code is finished.
@count: the number of instructions to be emulated. When this value is 0, @count: the number of instructions to be emulated. When this value is 0,
@ -556,12 +556,12 @@ uc_err uc_emu_stop(uc_engine *uc);
@uc: handle returned by uc_open() @uc: handle returned by uc_open()
@hh: hook handle returned from this registration. To be used in uc_hook_del() API @hh: hook handle returned from this registration. To be used in uc_hook_del() API
@type: hook type @type: hook type, refer to uc_hook_type enum
@callback: callback to be run when instruction is hit @callback: callback to be run when instruction is hit
@user_data: user-defined data. This will be passed to callback function in its @user_data: user-defined data. This will be passed to callback function in its
last argument @user_data last argument @user_data
@begin: start address of the area where the callback is effect (inclusive) @begin: start address of the area where the callback is in effect (inclusive)
@end: end address of the area where the callback is effect (inclusive) @end: end address of the area where the callback is in effect (inclusive)
NOTE 1: the callback is called only if related address is in range [@begin, @end] NOTE 1: the callback is called only if related address is in range [@begin, @end]
NOTE 2: if @begin > @end, callback is called whenever this hook type is triggered NOTE 2: if @begin > @end, callback is called whenever this hook type is triggered
@...: variable arguments (depending on @type) @...: variable arguments (depending on @type)
@ -578,7 +578,7 @@ uc_err uc_hook_add(uc_engine *uc, uc_hook *hh, int type, void *callback,
Unregister (remove) a hook callback. Unregister (remove) a hook callback.
This API removes the hook callback registered by uc_hook_add(). This API removes the hook callback registered by uc_hook_add().
NOTE: this should be called only when you no longer want to trace. NOTE: this should be called only when you no longer want to trace.
After this, @hh is invalid, and nolonger usable. After this, @hh is invalid, and no longer usable.
@uc: handle returned by uc_open() @uc: handle returned by uc_open()
@hh: handle returned by uc_hook_add() @hh: handle returned by uc_hook_add()
@ -605,7 +605,7 @@ typedef enum uc_prot {
@address: starting address of the new memory region to be mapped in. @address: starting address of the new memory region to be mapped in.
This address must be aligned to 4KB, or this will return with UC_ERR_ARG error. This address must be aligned to 4KB, or this will return with UC_ERR_ARG error.
@size: size of the new memory region to be mapped in. @size: size of the new memory region to be mapped in.
This size must be multiple of 4KB, or this will return with UC_ERR_ARG error. This size must be a multiple of 4KB, or this will return with UC_ERR_ARG error.
@perms: Permissions for the newly mapped region. @perms: Permissions for the newly mapped region.
This must be some combination of UC_PROT_READ | UC_PROT_WRITE | UC_PROT_EXEC, This must be some combination of UC_PROT_READ | UC_PROT_WRITE | UC_PROT_EXEC,
or this will return with UC_ERR_ARG error. or this will return with UC_ERR_ARG error.
@ -624,12 +624,12 @@ uc_err uc_mem_map(uc_engine *uc, uint64_t address, size_t size, uint32_t perms);
@address: starting address of the new memory region to be mapped in. @address: starting address of the new memory region to be mapped in.
This address must be aligned to 4KB, or this will return with UC_ERR_ARG error. This address must be aligned to 4KB, or this will return with UC_ERR_ARG error.
@size: size of the new memory region to be mapped in. @size: size of the new memory region to be mapped in.
This size must be multiple of 4KB, or this will return with UC_ERR_ARG error. This size must be a multiple of 4KB, or this will return with UC_ERR_ARG error.
@perms: Permissions for the newly mapped region. @perms: Permissions for the newly mapped region.
This must be some combination of UC_PROT_READ | UC_PROT_WRITE | UC_PROT_EXEC, This must be some combination of UC_PROT_READ | UC_PROT_WRITE | UC_PROT_EXEC,
or this will return with UC_ERR_ARG error. or this will return with UC_ERR_ARG error.
@ptr: pointer to host memory backing the newly mapped memory. This host memory is @ptr: pointer to host memory backing the newly mapped memory. This host memory is
expected to be an equal or larger size than provided, and be mapped with at expected to be of equal or larger size than provided, and be mapped with at
least PROT_READ | PROT_WRITE. If it is not, the resulting behavior is undefined. 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 @return UC_ERR_OK on success, or other value on failure (refer to uc_err enum
@ -646,7 +646,7 @@ uc_err uc_mem_map_ptr(uc_engine *uc, uint64_t address, size_t size, uint32_t per
@address: starting address of the memory region to be unmapped. @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. 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. @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. This size must be a 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 @return UC_ERR_OK on success, or other value on failure (refer to uc_err enum
for detailed error). for detailed error).
@ -662,7 +662,7 @@ uc_err uc_mem_unmap(uc_engine *uc, uint64_t address, size_t size);
@address: starting address of the memory region to be modified. @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. 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. @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. This size must be a multiple of 4KB, or this will return with UC_ERR_ARG error.
@perms: New permissions for the mapped region. @perms: New permissions for the mapped region.
This must be some combination of UC_PROT_READ | UC_PROT_WRITE | UC_PROT_EXEC, This must be some combination of UC_PROT_READ | UC_PROT_WRITE | UC_PROT_EXEC,
or this will return with UC_ERR_ARG error. or this will return with UC_ERR_ARG error.
@ -676,8 +676,8 @@ uc_err uc_mem_protect(uc_engine *uc, uint64_t address, size_t size, uint32_t per
/* /*
Retrieve all memory regions mapped by uc_mem_map() and uc_mem_map_ptr() 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 This API allocates memory for @regions, and user must free this memory later
by free() to avoid leaking memory. by uc_free() to avoid leaking memory.
NOTE: memory regions may be splitted by uc_mem_unmap() NOTE: memory regions may be split by uc_mem_unmap()
@uc: handle returned by uc_open() @uc: handle returned by uc_open()
@regions: pointer to an array of uc_mem_region struct. This is allocated by @regions: pointer to an array of uc_mem_region struct. This is allocated by
@ -697,9 +697,9 @@ uc_err uc_mem_regions(uc_engine *uc, uc_mem_region **regions, uint32_t *count);
differing arches or modes. differing arches or modes.
@uc: handle returned by uc_open() @uc: handle returned by uc_open()
@context: pointer to a uc_engine*. This will be updated with the pointer to @context: pointer to a uc_context*. This will be updated with the pointer to
the new context on successful return of this function. the new context on successful return of this function.
Later, this allocated memory must be freed with uc_free(). Later, this allocated memory must be freed with uc_context_free().
@return UC_ERR_OK on success, or other value on failure (refer to uc_err enum @return UC_ERR_OK on success, or other value on failure (refer to uc_err enum
for detailed error). for detailed error).
@ -708,10 +708,12 @@ UNICORN_EXPORT
uc_err uc_context_alloc(uc_engine *uc, uc_context **context); uc_err uc_context_alloc(uc_engine *uc, uc_context **context);
/* /*
Free the memory allocated by uc_context_alloc & uc_mem_regions. Free the memory allocated by uc_mem_regions.
WARNING: After Unicorn 1.0.1rc5, the memory allocated by uc_context_alloc should
be freed by uc_context_free(). Calling uc_free() may still work, but the result
is **undefined**.
@mem: memory allocated by uc_context_alloc (returned in *context), or @mem: memory allocated by uc_mem_regions (returned in *regions).
by uc_mem_regions (returned in *regions)
@return UC_ERR_OK on success, or other value on failure (refer to uc_err enum @return UC_ERR_OK on success, or other value on failure (refer to uc_err enum
for detailed error). for detailed error).
@ -739,7 +741,7 @@ uc_err uc_context_save(uc_engine *uc, uc_context *context);
state saved by uc_context_save(). state saved by uc_context_save().
@uc: handle returned by uc_open() @uc: handle returned by uc_open()
@buffer: handle returned by uc_context_alloc that has been used with uc_context_save @context: handle returned by uc_context_alloc that has been used with uc_context_save
@return UC_ERR_OK on success, or other value on failure (refer to uc_err enum @return UC_ERR_OK on success, or other value on failure (refer to uc_err enum
for detailed error). for detailed error).
@ -759,6 +761,18 @@ uc_err uc_context_restore(uc_engine *uc, uc_context *context);
UNICORN_EXPORT UNICORN_EXPORT
size_t uc_context_size(uc_engine *uc); size_t uc_context_size(uc_engine *uc);
/*
Free the context allocated by uc_context_alloc().
@context: handle returned by uc_context_alloc()
@return UC_ERR_OK on success, or other value on failure (refer to uc_err enum
for detailed error).
*/
UNICORN_EXPORT
uc_err uc_context_free(uc_context *context);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@ -53,6 +53,7 @@
</PropertyGroup> </PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings"> <ImportGroup Label="ExtensionSettings">
<Import Project="$(VCTargetsPath)\BuildCustomizations\masm.props" />
</ImportGroup> </ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
@ -357,7 +358,16 @@ copy "$(SolutionDir)..\include\unicorn\*.h" "$(SolutionDir)distro\include\unicor
<ClInclude Include="..\qapi-types.h" /> <ClInclude Include="..\qapi-types.h" />
<ClInclude Include="..\qapi-visit.h" /> <ClInclude Include="..\qapi-visit.h" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<MASM Include="..\..\..\qemu\util\setjmp-wrapper-win32.asm">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</ExcludedFromBuild>
</MASM>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets"> <ImportGroup Label="ExtensionTargets">
<Import Project="$(VCTargetsPath)\BuildCustomizations\masm.targets" />
</ImportGroup> </ImportGroup>
</Project> </Project>

View File

@ -499,4 +499,7 @@
<ClInclude Include="..\..\..\include\unicorn\unicorn.h" /> <ClInclude Include="..\..\..\include\unicorn\unicorn.h" />
<ClInclude Include="..\..\..\include\unicorn\x86.h" /> <ClInclude Include="..\..\..\include\unicorn\x86.h" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<MASM Include="..\..\..\qemu\util\setjmp-wrapper-win32.asm" />
</ItemGroup>
</Project> </Project>

View File

@ -169,6 +169,14 @@
<ClInclude Include="..\qapi-types.h" /> <ClInclude Include="..\qapi-types.h" />
<ClInclude Include="..\qapi-visit.h" /> <ClInclude Include="..\qapi-visit.h" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<MASM Include="..\..\..\qemu\util\setjmp-wrapper-win32.asm">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</ExcludedFromBuild>
</MASM>
</ItemGroup>
<PropertyGroup Label="Globals"> <PropertyGroup Label="Globals">
<ProjectGuid>{B6EFD6D7-C2D4-4FBB-B363-2E08CE09CC96}</ProjectGuid> <ProjectGuid>{B6EFD6D7-C2D4-4FBB-B363-2E08CE09CC96}</ProjectGuid>
<Keyword>Win32Proj</Keyword> <Keyword>Win32Proj</Keyword>
@ -204,6 +212,7 @@
</PropertyGroup> </PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings"> <ImportGroup Label="ExtensionSettings">
<Import Project="$(VCTargetsPath)\BuildCustomizations\masm.props" />
</ImportGroup> </ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
@ -363,5 +372,6 @@ copy "$(SolutionDir)..\include\unicorn\*.h" "$(SolutionDir)distro\include\unicor
</ItemDefinitionGroup> </ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets"> <ImportGroup Label="ExtensionTargets">
<Import Project="$(VCTargetsPath)\BuildCustomizations\masm.targets" />
</ImportGroup> </ImportGroup>
</Project> </Project>

View File

@ -498,4 +498,7 @@
<Filter>qemu</Filter> <Filter>qemu</Filter>
</ClInclude> </ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup>
<MASM Include="..\..\..\qemu\util\setjmp-wrapper-win32.asm" />
</ItemGroup>
</Project> </Project>

View File

@ -11,3 +11,4 @@ PKG_EXTRA = 3
# version tag. Examples: rc1, b2, post1 # version tag. Examples: rc1, b2, post1
PKG_TAG = PKG_TAG =
# PKG_TAG = rc6

View File

@ -2382,7 +2382,6 @@ symbols = (
'qemu_get_guest_simple_memory_mapping', 'qemu_get_guest_simple_memory_mapping',
'qemu_get_ram_block', 'qemu_get_ram_block',
'qemu_get_ram_block_host_ptr', 'qemu_get_ram_block_host_ptr',
'qemu_get_ram_fd',
'qemu_get_ram_ptr', 'qemu_get_ram_ptr',
'qemu_host_page_mask', 'qemu_host_page_mask',
'qemu_host_page_size', 'qemu_host_page_size',

View File

@ -205,7 +205,6 @@ static inline uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c)
/* vector definitions */ /* vector definitions */
#ifdef __ALTIVEC__ #ifdef __ALTIVEC__
#include <altivec.h>
/* The altivec.h header says we're allowed to undef these for /* The altivec.h header says we're allowed to undef these for
* C++ compatibility. Here we don't care about C++, but we * C++ compatibility. Here we don't care about C++, but we
* undef them anyway to avoid namespace pollution. * undef them anyway to avoid namespace pollution.
@ -213,6 +212,7 @@ static inline uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c)
#undef vector #undef vector
#undef pixel #undef pixel
#undef bool #undef bool
#include <altivec.h>
#define VECTYPE __vector unsigned char #define VECTYPE __vector unsigned char
#define SPLAT(p) vec_splat(vec_ld(0, p), 0) #define SPLAT(p) vec_splat(vec_ld(0, p), 0)
#define ALL_EQ(v1, v2) vec_all_eq(v1, v2) #define ALL_EQ(v1, v2) vec_all_eq(v1, v2)

View File

@ -56,13 +56,27 @@
# define EWOULDBLOCK WSAEWOULDBLOCK # define EWOULDBLOCK WSAEWOULDBLOCK
#endif #endif
#if defined(_WIN64) && !defined(_MSC_VER) #if defined(_WIN64)
/* On w64, setjmp is implemented by _setjmp which needs a second parameter. /* On w64, setjmp is implemented by _setjmp which needs a second parameter.
* If this parameter is NULL, longjump does no stack unwinding. * If this parameter is NULL, longjump does no stack unwinding.
* That is what we need for QEMU. Passing the value of register rsp (default) * That is what we need for QEMU. Passing the value of register rsp (default)
* lets longjmp try a stack unwinding which will crash with generated code. */ * lets longjmp try a stack unwinding which will crash with generated code. */
#if defined(_MSC_VER) // MSVC
// See qemu/include/utils/setjmp-wrapper-win32.asm for details.
extern int _setjmp_wrapper(jmp_buf);
# undef setjmp
# define setjmp(env) _setjmp_wrapper(env)
#else // MinGW
// Original QEMU patch.
# undef setjmp # undef setjmp
# define setjmp(env) _setjmp(env, NULL) # define setjmp(env) _setjmp(env, NULL)
#endif
#endif #endif
/* QEMU uses sigsetjmp()/siglongjmp() as the portable way to specify /* QEMU uses sigsetjmp()/siglongjmp() as the portable way to specify
* "longjmp and don't touch the signal masks". Since we know that the * "longjmp and don't touch the signal masks". Since we know that the

View File

@ -69,6 +69,9 @@ int arm_reg_read(struct uc_struct *uc, unsigned int *regs, void **vals, int coun
else { else {
switch(regid) { switch(regid) {
case UC_ARM_REG_APSR: case UC_ARM_REG_APSR:
*(int32_t *)value = cpsr_read(&ARM_CPU(uc, mycpu)->env) & (CPSR_NZCV | CPSR_Q | CPSR_GE);
break;
case UC_ARM_REG_APSR_NZCV:
*(int32_t *)value = cpsr_read(&ARM_CPU(uc, mycpu)->env) & CPSR_NZCV; *(int32_t *)value = cpsr_read(&ARM_CPU(uc, mycpu)->env) & CPSR_NZCV;
break; break;
case UC_ARM_REG_CPSR: case UC_ARM_REG_CPSR:
@ -132,6 +135,9 @@ int arm_reg_write(struct uc_struct *uc, unsigned int *regs, void* const* vals, i
else { else {
switch(regid) { switch(regid) {
case UC_ARM_REG_APSR: case UC_ARM_REG_APSR:
cpsr_write(&ARM_CPU(uc, mycpu)->env, *(uint32_t *)value, (CPSR_NZCV | CPSR_Q | CPSR_GE));
break;
case UC_ARM_REG_APSR_NZCV:
cpsr_write(&ARM_CPU(uc, mycpu)->env, *(uint32_t *)value, CPSR_NZCV); cpsr_write(&ARM_CPU(uc, mycpu)->env, *(uint32_t *)value, CPSR_NZCV);
break; break;
case UC_ARM_REG_CPSR: case UC_ARM_REG_CPSR:

View File

@ -1011,16 +1011,7 @@ void helper_fstenv(CPUX86State *env, target_ulong ptr, int data32)
} }
} }
// DFLAG enum: tcg.h, case here to int if (data32) {
if (env->hflags & HF_CS64_MASK) {
cpu_stl_data(env, ptr, env->fpuc);
cpu_stl_data(env, ptr + 4, fpus);
cpu_stl_data(env, ptr + 8, fptag);
cpu_stl_data(env, ptr + 12, (uint32_t)env->fpip); /* fpip */
cpu_stl_data(env, ptr + 20, 0); /* fpcs */
cpu_stl_data(env, ptr + 24, 0); /* fpoo */
cpu_stl_data(env, ptr + 28, 0); /* fpos */
} else if (data32) {
/* 32 bit */ /* 32 bit */
cpu_stl_data(env, ptr, env->fpuc); cpu_stl_data(env, ptr, env->fpuc);
cpu_stl_data(env, ptr + 4, fpus); cpu_stl_data(env, ptr + 4, fpus);

View File

@ -710,102 +710,134 @@ void helper_cvtsq2sd(CPUX86State *env, XMMReg *d, uint64_t val)
#endif #endif
/* float to integer */ /* float to integer */
/*
* x86 mandates that we return the indefinite integer value for the result
* of any float-to-integer conversion that raises the 'invalid' exception.
* Wrap the softfloat functions to get this behaviour.
*/
#define WRAP_FLOATCONV(RETTYPE, FN, FLOATTYPE, INDEFVALUE) \
static inline RETTYPE x86_##FN(FLOATTYPE a, float_status *s) \
{ \
int oldflags, newflags; \
RETTYPE r; \
\
oldflags = get_float_exception_flags(s); \
set_float_exception_flags(0, s); \
r = FN(a, s); \
newflags = get_float_exception_flags(s); \
if (newflags & float_flag_invalid) { \
r = INDEFVALUE; \
} \
set_float_exception_flags(newflags | oldflags, s); \
return r; \
}
WRAP_FLOATCONV(int32_t, float32_to_int32, float32, INT32_MIN)
WRAP_FLOATCONV(int32_t, float32_to_int32_round_to_zero, float32, INT32_MIN)
WRAP_FLOATCONV(int32_t, float64_to_int32, float64, INT32_MIN)
WRAP_FLOATCONV(int32_t, float64_to_int32_round_to_zero, float64, INT32_MIN)
WRAP_FLOATCONV(int64_t, float32_to_int64, float32, INT64_MIN)
WRAP_FLOATCONV(int64_t, float32_to_int64_round_to_zero, float32, INT64_MIN)
WRAP_FLOATCONV(int64_t, float64_to_int64, float64, INT64_MIN)
WRAP_FLOATCONV(int64_t, float64_to_int64_round_to_zero, float64, INT64_MIN)
void helper_cvtps2dq(CPUX86State *env, XMMReg *d, XMMReg *s) void helper_cvtps2dq(CPUX86State *env, XMMReg *d, XMMReg *s)
{ {
d->XMM_L(0) = float32_to_int32(s->XMM_S(0), &env->sse_status); d->XMM_L(0) = x86_float32_to_int32(s->XMM_S(0), &env->sse_status);
d->XMM_L(1) = float32_to_int32(s->XMM_S(1), &env->sse_status); d->XMM_L(1) = x86_float32_to_int32(s->XMM_S(1), &env->sse_status);
d->XMM_L(2) = float32_to_int32(s->XMM_S(2), &env->sse_status); d->XMM_L(2) = x86_float32_to_int32(s->XMM_S(2), &env->sse_status);
d->XMM_L(3) = float32_to_int32(s->XMM_S(3), &env->sse_status); d->XMM_L(3) = x86_float32_to_int32(s->XMM_S(3), &env->sse_status);
} }
void helper_cvtpd2dq(CPUX86State *env, XMMReg *d, XMMReg *s) void helper_cvtpd2dq(CPUX86State *env, XMMReg *d, XMMReg *s)
{ {
d->XMM_L(0) = float64_to_int32(s->XMM_D(0), &env->sse_status); d->XMM_L(0) = x86_float64_to_int32(s->XMM_D(0), &env->sse_status);
d->XMM_L(1) = float64_to_int32(s->XMM_D(1), &env->sse_status); d->XMM_L(1) = x86_float64_to_int32(s->XMM_D(1), &env->sse_status);
d->XMM_Q(1) = 0; d->XMM_Q(1) = 0;
} }
void helper_cvtps2pi(CPUX86State *env, MMXReg *d, XMMReg *s) void helper_cvtps2pi(CPUX86State *env, MMXReg *d, XMMReg *s)
{ {
d->MMX_L(0) = float32_to_int32(s->XMM_S(0), &env->sse_status); d->MMX_L(0) = x86_float32_to_int32(s->XMM_S(0), &env->sse_status);
d->MMX_L(1) = float32_to_int32(s->XMM_S(1), &env->sse_status); d->MMX_L(1) = x86_float32_to_int32(s->XMM_S(1), &env->sse_status);
} }
void helper_cvtpd2pi(CPUX86State *env, MMXReg *d, XMMReg *s) void helper_cvtpd2pi(CPUX86State *env, MMXReg *d, XMMReg *s)
{ {
d->MMX_L(0) = float64_to_int32(s->XMM_D(0), &env->sse_status); d->MMX_L(0) = x86_float64_to_int32(s->XMM_D(0), &env->sse_status);
d->MMX_L(1) = float64_to_int32(s->XMM_D(1), &env->sse_status); d->MMX_L(1) = x86_float64_to_int32(s->XMM_D(1), &env->sse_status);
} }
int32_t helper_cvtss2si(CPUX86State *env, XMMReg *s) int32_t helper_cvtss2si(CPUX86State *env, XMMReg *s)
{ {
return float32_to_int32(s->XMM_S(0), &env->sse_status); return x86_float32_to_int32(s->XMM_S(0), &env->sse_status);
} }
int32_t helper_cvtsd2si(CPUX86State *env, XMMReg *s) int32_t helper_cvtsd2si(CPUX86State *env, XMMReg *s)
{ {
return float64_to_int32(s->XMM_D(0), &env->sse_status); return x86_float64_to_int32(s->XMM_D(0), &env->sse_status);
} }
#ifdef TARGET_X86_64 #ifdef TARGET_X86_64
int64_t helper_cvtss2sq(CPUX86State *env, XMMReg *s) int64_t helper_cvtss2sq(CPUX86State *env, XMMReg *s)
{ {
return float32_to_int64(s->XMM_S(0), &env->sse_status); return x86_float32_to_int64(s->XMM_S(0), &env->sse_status);
} }
int64_t helper_cvtsd2sq(CPUX86State *env, XMMReg *s) int64_t helper_cvtsd2sq(CPUX86State *env, XMMReg *s)
{ {
return float64_to_int64(s->XMM_D(0), &env->sse_status); return x86_float64_to_int64(s->XMM_D(0), &env->sse_status);
} }
#endif #endif
/* float to integer truncated */ /* float to integer truncated */
void helper_cvttps2dq(CPUX86State *env, XMMReg *d, XMMReg *s) void helper_cvttps2dq(CPUX86State *env, XMMReg *d, XMMReg *s)
{ {
d->XMM_L(0) = float32_to_int32_round_to_zero(s->XMM_S(0), &env->sse_status); d->XMM_L(0) = x86_float32_to_int32_round_to_zero(s->XMM_S(0), &env->sse_status);
d->XMM_L(1) = float32_to_int32_round_to_zero(s->XMM_S(1), &env->sse_status); d->XMM_L(1) = x86_float32_to_int32_round_to_zero(s->XMM_S(1), &env->sse_status);
d->XMM_L(2) = float32_to_int32_round_to_zero(s->XMM_S(2), &env->sse_status); d->XMM_L(2) = x86_float32_to_int32_round_to_zero(s->XMM_S(2), &env->sse_status);
d->XMM_L(3) = float32_to_int32_round_to_zero(s->XMM_S(3), &env->sse_status); d->XMM_L(3) = x86_float32_to_int32_round_to_zero(s->XMM_S(3), &env->sse_status);
} }
void helper_cvttpd2dq(CPUX86State *env, XMMReg *d, XMMReg *s) void helper_cvttpd2dq(CPUX86State *env, XMMReg *d, XMMReg *s)
{ {
d->XMM_L(0) = float64_to_int32_round_to_zero(s->XMM_D(0), &env->sse_status); d->XMM_L(0) = x86_float64_to_int32_round_to_zero(s->XMM_D(0), &env->sse_status);
d->XMM_L(1) = float64_to_int32_round_to_zero(s->XMM_D(1), &env->sse_status); d->XMM_L(1) = x86_float64_to_int32_round_to_zero(s->XMM_D(1), &env->sse_status);
d->XMM_Q(1) = 0; d->XMM_Q(1) = 0;
} }
void helper_cvttps2pi(CPUX86State *env, MMXReg *d, XMMReg *s) void helper_cvttps2pi(CPUX86State *env, MMXReg *d, XMMReg *s)
{ {
d->MMX_L(0) = float32_to_int32_round_to_zero(s->XMM_S(0), &env->sse_status); d->MMX_L(0) = x86_float32_to_int32_round_to_zero(s->XMM_S(0), &env->sse_status);
d->MMX_L(1) = float32_to_int32_round_to_zero(s->XMM_S(1), &env->sse_status); d->MMX_L(1) = x86_float32_to_int32_round_to_zero(s->XMM_S(1), &env->sse_status);
} }
void helper_cvttpd2pi(CPUX86State *env, MMXReg *d, XMMReg *s) void helper_cvttpd2pi(CPUX86State *env, MMXReg *d, XMMReg *s)
{ {
d->MMX_L(0) = float64_to_int32_round_to_zero(s->XMM_D(0), &env->sse_status); d->MMX_L(0) = x86_float64_to_int32_round_to_zero(s->XMM_D(0), &env->sse_status);
d->MMX_L(1) = float64_to_int32_round_to_zero(s->XMM_D(1), &env->sse_status); d->MMX_L(1) = x86_float64_to_int32_round_to_zero(s->XMM_D(1), &env->sse_status);
} }
int32_t helper_cvttss2si(CPUX86State *env, XMMReg *s) int32_t helper_cvttss2si(CPUX86State *env, XMMReg *s)
{ {
return float32_to_int32_round_to_zero(s->XMM_S(0), &env->sse_status); return x86_float32_to_int32_round_to_zero(s->XMM_S(0), &env->sse_status);
} }
int32_t helper_cvttsd2si(CPUX86State *env, XMMReg *s) int32_t helper_cvttsd2si(CPUX86State *env, XMMReg *s)
{ {
return float64_to_int32_round_to_zero(s->XMM_D(0), &env->sse_status); return x86_float64_to_int32_round_to_zero(s->XMM_D(0), &env->sse_status);
} }
#ifdef TARGET_X86_64 #ifdef TARGET_X86_64
int64_t helper_cvttss2sq(CPUX86State *env, XMMReg *s) int64_t helper_cvttss2sq(CPUX86State *env, XMMReg *s)
{ {
return float32_to_int64_round_to_zero(s->XMM_S(0), &env->sse_status); return x86_float32_to_int64_round_to_zero(s->XMM_S(0), &env->sse_status);
} }
int64_t helper_cvttsd2sq(CPUX86State *env, XMMReg *s) int64_t helper_cvttsd2sq(CPUX86State *env, XMMReg *s)
{ {
return float64_to_int64_round_to_zero(s->XMM_D(0), &env->sse_status); return x86_float64_to_int64_round_to_zero(s->XMM_D(0), &env->sse_status);
} }
#endif #endif

View File

@ -26,7 +26,12 @@ const int X86_REGS_STORAGE_SIZE = offsetof(CPUX86State, tlb_table);
static void x86_set_pc(struct uc_struct *uc, uint64_t address) static void x86_set_pc(struct uc_struct *uc, uint64_t address)
{ {
((CPUX86State *)uc->cpu->env_ptr)->eip = address; CPUState* cpu = uc->cpu;
int16_t cs = (uint16_t)X86_CPU(uc, cpu)->env.segs[R_CS].selector;
if(uc->mode == UC_MODE_16)
((CPUX86State *)uc->cpu->env_ptr)->eip = address - cs*16;
else
((CPUX86State *)uc->cpu->env_ptr)->eip = address;
} }
static void x86_release(void *ctx) static void x86_release(void *ctx)

View File

@ -0,0 +1,26 @@
EXTERN _setjmp: proc
PUBLIC _setjmp_wrapper
_TEXT SEGMENT
_setjmp_wrapper PROC
; Why do we need this wrapper?
; Short answer: Windows default implementation of setjmp/longjmp is incompatible with generated code.
; A longer answer: https://blog.lazym.io/2020/09/21/Unicorn-Devblog-setjmp-longjmp-on-Windows/.
; From qemu os-win32 comments:
; > On w64, setjmp is implemented by _setjmp which needs a second parameter.
; > If this parameter is NULL, longjump does no stack unwinding.
; > That is what we need for QEMU. Passing the value of register rsp (default)
; > lets longjmp try a stack unwinding which will crash with generated code.
; It's true indeed, but MSVC doesn't has a setjmp signature which receives two arguements.
; Therefore, we add a wrapper to keep the second argument zero.
xor rdx, rdx
jmp _setjmp
_setjmp_wrapper ENDP
_TEXT ENDS
END

View File

@ -737,7 +737,7 @@ static void test_i386_context_save(void)
printf(">>> EAX = 0x%x\n", r_eax); printf(">>> EAX = 0x%x\n", r_eax);
// free the CPU context // free the CPU context
err = uc_free(context); err = uc_context_free(context);
if (err) { if (err) {
printf("Failed on uc_free() with error returned: %u\n", err); printf("Failed on uc_free() with error returned: %u\n", err);
return; return;

View File

@ -0,0 +1,30 @@
#!/usr/bin/python
from unicorn import *
from unicorn.arm_const import *
import regress
class APSRAccess(regress.RegressTest):
def runTest(self):
code = (
b'\x00\x00\xa0\xe1' + # 0: mov r0, r0
b'\x08\x10\x9f\xe5' + # 4: ldr r1, [pc, #8]
b'\x01\xf0\x28\xe1' + # 8: 01 f0 28 e1 msr apsr_nzcvq, r1
b'\x00\x00\xa0\xe1' + # c: mov r0, r0
b'\x00\x00\xa0\xe1' + # 10: mov r0, r0
b'\x00\x00\x00\xff') # 14: data for inst @4
uc = Uc(UC_ARCH_ARM, UC_MODE_ARM)
uc.mem_map(0x1000, 0x1000)
uc.mem_write(0x1000, code) # bxeq lr; mov r0, r0
uc.reg_write(UC_ARM_REG_APSR, 0)
uc.emu_start(0x1000, 0x100c)
self.assertEqual(uc.reg_read(UC_ARM_REG_APSR), 0xf8000000)
self.assertEqual(uc.reg_read(UC_ARM_REG_APSR_NZCV), 0xf0000000)
if __name__ == '__main__':
regress.main()

48
uc.c
View File

@ -185,7 +185,11 @@ uc_err uc_open(uc_arch arch, uc_mode mode, uc_engine **result)
return UC_ERR_MODE; return UC_ERR_MODE;
} }
if (mode & UC_MODE_BIG_ENDIAN) { if (mode & UC_MODE_BIG_ENDIAN) {
#ifdef UNICORN_HAS_ARMEB
uc->init_arch = armeb_uc_init; uc->init_arch = armeb_uc_init;
#else
return UC_ERR_MODE;
#endif
} else { } else {
uc->init_arch = arm_uc_init; uc->init_arch = arm_uc_init;
} }
@ -347,6 +351,16 @@ uc_err uc_close(uc_engine *uc)
free(uc->mapped_blocks); free(uc->mapped_blocks);
// free the saved contexts list and notify them that uc has been closed.
cur = uc->saved_contexts.head;
while (cur != NULL) {
struct list_item *next = cur->next;
struct uc_context *context = (struct uc_context*)cur->data;
context->uc = NULL;
cur = next;
}
list_clear(&uc->saved_contexts);
// finally, free uc itself. // finally, free uc itself.
memset(uc, 0, sizeof(*uc)); memset(uc, 0, sizeof(*uc));
free(uc); free(uc);
@ -1293,7 +1307,13 @@ static size_t cpu_context_size(uc_arch arch, uc_mode mode)
case UC_ARCH_X86: return X86_REGS_STORAGE_SIZE; case UC_ARCH_X86: return X86_REGS_STORAGE_SIZE;
#endif #endif
#ifdef UNICORN_HAS_ARM #ifdef UNICORN_HAS_ARM
case UC_ARCH_ARM: return mode & UC_MODE_BIG_ENDIAN ? ARM_REGS_STORAGE_SIZE_armeb : ARM_REGS_STORAGE_SIZE_arm; case UC_ARCH_ARM: return mode & UC_MODE_BIG_ENDIAN ?
#ifdef UNICORN_HAS_ARMEB
ARM_REGS_STORAGE_SIZE_armeb
#else
0
#endif
: ARM_REGS_STORAGE_SIZE_arm;
#endif #endif
#ifdef UNICORN_HAS_ARM64 #ifdef UNICORN_HAS_ARM64
case UC_ARCH_ARM64: return mode & UC_MODE_BIG_ENDIAN ? ARM64_REGS_STORAGE_SIZE_aarch64eb : ARM64_REGS_STORAGE_SIZE_aarch64; case UC_ARCH_ARM64: return mode & UC_MODE_BIG_ENDIAN ? ARM64_REGS_STORAGE_SIZE_aarch64eb : ARM64_REGS_STORAGE_SIZE_aarch64;
@ -1325,13 +1345,18 @@ UNICORN_EXPORT
uc_err uc_context_alloc(uc_engine *uc, uc_context **context) uc_err uc_context_alloc(uc_engine *uc, uc_context **context)
{ {
struct uc_context **_context = context; struct uc_context **_context = context;
size_t size = cpu_context_size(uc->arch, uc->mode); size_t size = uc_context_size(uc);
*_context = malloc(size); *_context = malloc(size);
if (*_context) { if (*_context) {
(*_context)->jmp_env_size = sizeof(*uc->cpu->jmp_env); (*_context)->jmp_env_size = sizeof(*uc->cpu->jmp_env);
(*_context)->context_size = size - sizeof(uc_context) - (*_context)->jmp_env_size; (*_context)->context_size = cpu_context_size(uc->arch, uc->mode);
return UC_ERR_OK; (*_context)->uc = uc;
if (list_insert(&uc->saved_contexts, *_context)) {
return UC_ERR_OK;
} else {
return UC_ERR_NOMEM;
}
} else { } else {
return UC_ERR_NOMEM; return UC_ERR_NOMEM;
} }
@ -1364,7 +1389,20 @@ UNICORN_EXPORT
uc_err uc_context_restore(uc_engine *uc, uc_context *context) uc_err uc_context_restore(uc_engine *uc, uc_context *context)
{ {
memcpy(uc->cpu->env_ptr, context->data, context->context_size); memcpy(uc->cpu->env_ptr, context->data, context->context_size);
memcpy(uc->cpu->jmp_env, context->data + context->context_size, context->jmp_env_size); if (list_exists(&uc->saved_contexts, context)) {
memcpy(uc->cpu->jmp_env, context->data + context->context_size, context->jmp_env_size);
}
return UC_ERR_OK; return UC_ERR_OK;
} }
UNICORN_EXPORT
uc_err uc_context_free(uc_context *context)
{
uc_engine* uc = context->uc;
// if uc is NULL, it means that uc_engine has been free-ed.
if (uc) {
list_remove(&uc->saved_contexts, context);
}
return uc_free(context);
}