merge master & fix conflicts
This commit is contained in:
commit
fb667f5cdc
67
.github/workflows/python-publish.yml
vendored
Normal file
67
.github/workflows/python-publish.yml
vendored
Normal 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 }}
|
86
.travis.yml
86
.travis.yml
@ -16,23 +16,31 @@ compiler:
|
||||
- gcc
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
- windows
|
||||
arch:
|
||||
- amd64
|
||||
- arm64
|
||||
matrix:
|
||||
fast_finish: true
|
||||
include:
|
||||
- name: "Linux arm64 clang C"
|
||||
exclude:
|
||||
- os: windows
|
||||
arch: arm64
|
||||
os: linux
|
||||
include:
|
||||
|
||||
- name: "Compiler: clang C"
|
||||
os: osx
|
||||
osx_image: xcode10.1
|
||||
python: 3.7
|
||||
compiler: clang
|
||||
language: c
|
||||
env:
|
||||
- PATH=$PATH:/usr/local/opt/binutils/bin
|
||||
script: make header && make && make -C tests/unit test && make -C tests/regress test
|
||||
|
||||
- name: "Linux arm64 gcc C"
|
||||
arch: arm64
|
||||
os: linux
|
||||
- name: "Compiler: gcc C"
|
||||
os: osx
|
||||
osx_image: xcode10.1
|
||||
python: 3.7
|
||||
compiler: gcc
|
||||
language: c
|
||||
env:
|
||||
@ -125,6 +133,42 @@ matrix:
|
||||
- cp libunicorn.* ../
|
||||
- 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"
|
||||
os: osx
|
||||
osx_image: xcode10.1
|
||||
@ -214,12 +258,14 @@ matrix:
|
||||
- export LDFLAGS="-m32"
|
||||
- export LDFLAGS_STATIC="-m32"
|
||||
- export UNICORN_QEMU_FLAGS="--cpu=i386"
|
||||
before_cache:
|
||||
- $msys2 pacman --sync --clean --noconfirm
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/AppData/Local/Temp/chocolatey
|
||||
- /C/tools/msys64
|
||||
# before_cache:
|
||||
# - $msys2 pacman --sync --clean --noconfirm
|
||||
# cache:
|
||||
# timeout:
|
||||
# 1000
|
||||
# directories:
|
||||
# - $HOME/AppData/Local/Temp/chocolatey
|
||||
# - /C/tools/msys64
|
||||
script:
|
||||
- $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 AR=gcc-ar
|
||||
- export RANLIB=gcc-ranlib
|
||||
before_cache:
|
||||
- $msys2 pacman --sync --clean --noconfirm
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/AppData/Local/Temp/chocolatey
|
||||
- /C/tools/msys64
|
||||
# before_cache:
|
||||
# - $msys2 pacman --sync --clean --noconfirm
|
||||
# cache:
|
||||
# timeout:
|
||||
# 1000
|
||||
# directories:
|
||||
# - $HOME/AppData/Local/Temp/chocolatey
|
||||
# - /C/tools/msys64
|
||||
script:
|
||||
- $shell make header; $shell make; cp unicorn.dll /C/Windows/System32/; $shell make test
|
||||
addons:
|
||||
|
@ -881,6 +881,10 @@ if (MSVC)
|
||||
qemu/util/oslib-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()
|
||||
set(UNICORN_SRCS
|
||||
${UNICORN_SRCS_COMMON}
|
||||
@ -985,6 +989,7 @@ if(NOT MSVC)
|
||||
file(GLOB UNICORN_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/include/unicorn/*.h)
|
||||
install(TARGETS unicorn
|
||||
RUNTIME DESTINATION bin
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
)
|
||||
install(FILES ${UNICORN_HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/unicorn)
|
||||
|
@ -74,3 +74,4 @@ Huitao Chen (chenhuitao) & KaiJern Lau (xwings): Python3 support for building
|
||||
Kevin Foo (chfl4gs): Travis-CI migration
|
||||
Simon Gorchakov: PowerPC target
|
||||
Stuart Dootson (studoot): MSVC compatibility with PowerPC target support
|
||||
Ziqiao Kong (lazymio): uc_context_free() API and various bug fix & improvement.
|
||||
|
45
ChangeLog
45
ChangeLog
@ -1,5 +1,50 @@
|
||||
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
|
||||
|
||||
|
7
SPONSORS.TXT
Normal file
7
SPONSORS.TXT
Normal 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
|
@ -34,3 +34,6 @@ More bindings created & maintained externally by community are available as foll
|
||||
|
||||
- pharo-unicorn: Pharo binding (by Guille Polito)
|
||||
https://github.com/guillep/pharo-unicorn
|
||||
|
||||
- Unicorn.js: JavaScript binding (by Alexandro Sanchez)
|
||||
https://github.com/AlexAltea/unicorn.js
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
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`)
|
||||
|
||||
|
@ -108,7 +108,7 @@ type
|
||||
@user_data: user data passed to tracing APIs
|
||||
|
||||
@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.
|
||||
|
||||
In the event of a UC_MEM_READ_UNMAPPED or UC_MEM_WRITE_UNMAPPED callback,
|
||||
|
14
bindings/python/build_wheel.sh
Executable file
14
bindings/python/build_wheel.sh
Executable 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 .
|
@ -45,7 +45,7 @@ def test_arm64():
|
||||
# tracing all basic blocks with customized callback
|
||||
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)
|
||||
|
||||
# emulate machine code in infinite time
|
||||
|
@ -4,7 +4,7 @@
|
||||
from __future__ import print_function
|
||||
from unicorn 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_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)
|
||||
|
||||
except UcError as e:
|
||||
# timeout is acceptable in this case
|
||||
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)
|
||||
print("ERROR: %s" % e)
|
||||
|
||||
|
||||
# Test X86 32 bit with IN/OUT instruction
|
||||
def test_i386_inout():
|
||||
@ -460,11 +453,17 @@ def test_i386_context_save():
|
||||
print(">>> Saving CPU context")
|
||||
saved_context = mu.context_save()
|
||||
|
||||
print(">>> Pickling CPU context")
|
||||
pickled_saved_context = pickle.dumps(saved_context)
|
||||
|
||||
print(">>> Running emulation for the second time")
|
||||
mu.emu_start(address, address+1)
|
||||
print(">>> Emulation done. Below is the CPU context")
|
||||
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")
|
||||
mu.context_restore(saved_context)
|
||||
print(">>> EAX = 0x%x" %(mu.reg_read(UC_X86_REG_EAX)))
|
||||
|
@ -15,6 +15,7 @@ from distutils.util import get_platform
|
||||
from distutils.command.build import build
|
||||
from distutils.command.sdist import sdist
|
||||
from setuptools.command.bdist_egg import bdist_egg
|
||||
from setuptools.command.develop import develop
|
||||
|
||||
SYSTEM = sys.platform
|
||||
|
||||
@ -22,7 +23,7 @@ SYSTEM = sys.platform
|
||||
IS_64BITS = platform.architecture()[0] == '64bit'
|
||||
|
||||
# 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')
|
||||
HEADERS_DIR = os.path.join(ROOT_DIR, 'unicorn', 'include')
|
||||
SRC_DIR = os.path.join(ROOT_DIR, 'src')
|
||||
@ -62,9 +63,12 @@ if SYSTEM == 'darwin':
|
||||
LIBRARY_FILE = "libunicorn.dylib"
|
||||
MAC_LIBRARY_FILE = "libunicorn*.dylib"
|
||||
STATIC_LIBRARY_FILE = None
|
||||
elif SYSTEM in ('win32', 'cygwin'):
|
||||
elif SYSTEM == 'win32':
|
||||
LIBRARY_FILE = "unicorn.dll"
|
||||
STATIC_LIBRARY_FILE = "unicorn.lib"
|
||||
elif SYSTEM == 'cygwin':
|
||||
LIBRARY_FILE = "cygunicorn.dll"
|
||||
STATIC_LIBRARY_FILE = None
|
||||
else:
|
||||
LIBRARY_FILE = "libunicorn.so"
|
||||
STATIC_LIBRARY_FILE = None
|
||||
@ -140,16 +144,22 @@ def build_libraries():
|
||||
os.chdir(BUILD_DIR)
|
||||
|
||||
try:
|
||||
subprocess.check_call(['msbuild', '/help'])
|
||||
subprocess.check_call(['msbuild', '-ver'])
|
||||
except:
|
||||
has_msbuild = False
|
||||
else:
|
||||
has_msbuild = True
|
||||
|
||||
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'
|
||||
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)
|
||||
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['UNICORN_BUILD_CORE_ONLY'] = 'yes'
|
||||
cmd = ['sh', './make.sh']
|
||||
if SYSTEM == "cygwin":
|
||||
if IS_64BITS:
|
||||
cmd.append('cygwin-mingw64')
|
||||
else:
|
||||
cmd.append('cygwin-mingw32')
|
||||
elif SYSTEM == "win32":
|
||||
if SYSTEM == "win32":
|
||||
if IS_64BITS:
|
||||
cmd.append('cross-win64')
|
||||
else:
|
||||
@ -191,7 +196,6 @@ def build_libraries():
|
||||
sys.exit(1)
|
||||
os.chdir(cwd)
|
||||
|
||||
|
||||
class custom_sdist(sdist):
|
||||
def run(self):
|
||||
clean_bins()
|
||||
@ -207,6 +211,12 @@ class custom_build(build):
|
||||
build_libraries()
|
||||
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):
|
||||
def run(self):
|
||||
self.run_command('build')
|
||||
@ -215,10 +225,6 @@ class custom_bdist_egg(bdist_egg):
|
||||
def dummy_src():
|
||||
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:
|
||||
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/
|
||||
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
|
||||
except ImportError:
|
||||
print("Proper 'develop' support unavailable.")
|
||||
long_desc = '''
|
||||
Unicorn is a lightweight, multi-platform, multi-architecture CPU emulator framework
|
||||
based on [QEMU](https://qemu.org).
|
||||
|
||||
def join_all(src, files):
|
||||
return tuple(os.path.join(src, f) for f in files)
|
||||
Unicorn offers some unparalleled features:
|
||||
|
||||
- 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 = '''
|
||||
Unicorn is a lightweight, multi-platform, multi-architecture CPU emulator framework
|
||||
@ -282,17 +292,17 @@ setup(
|
||||
description='Unicorn CPU emulator engine',
|
||||
long_description=long_desc,
|
||||
long_description_content_type="text/markdown",
|
||||
url='http://www.unicorn-engine.org',
|
||||
url='https://www.unicorn-engine.org',
|
||||
classifiers=[
|
||||
'License :: OSI Approved :: BSD License',
|
||||
'Programming Language :: Python :: 2',
|
||||
'Programming Language :: Python :: 3',
|
||||
],
|
||||
requires=['ctypes'],
|
||||
cmdclass=cmdclass,
|
||||
zip_safe=True,
|
||||
cmdclass={'build': custom_build, 'develop': custom_develop, 'sdist': custom_sdist, 'bdist_egg': custom_bdist_egg},
|
||||
zip_safe=False,
|
||||
include_package_data=True,
|
||||
is_pure=True,
|
||||
is_pure=False,
|
||||
package_data={
|
||||
'unicorn': ['lib/*', 'include/unicorn/*']
|
||||
}
|
||||
|
@ -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_restore", ucerr, uc_engine, uc_context)
|
||||
_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))
|
||||
|
||||
# uc_hook_add is special due to variable number of arguments
|
||||
@ -604,7 +605,7 @@ class Uc(object):
|
||||
return 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:
|
||||
raise UcError(status)
|
||||
|
||||
@ -628,16 +629,40 @@ class Uc(object):
|
||||
_uc.uc_free(regions)
|
||||
|
||||
|
||||
class UcContext(ctypes.Structure):
|
||||
class UcContext:
|
||||
def __init__(self, h):
|
||||
self.context = uc_context()
|
||||
|
||||
status = _uc.uc_context_alloc(h, ctypes.byref(self.context))
|
||||
self._context = uc_context()
|
||||
self._size = _uc.uc_context_size(h)
|
||||
self._to_free = True
|
||||
status = _uc.uc_context_alloc(h, ctypes.byref(self._context))
|
||||
if status != uc.UC_ERR_OK:
|
||||
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):
|
||||
_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
|
||||
|
@ -123,7 +123,7 @@ enum uc_hook_idx {
|
||||
// if statement to check hook bounds
|
||||
#define HOOK_BOUND_CHECK(hh, addr) \
|
||||
((((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_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;
|
||||
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)
|
||||
struct list saved_contexts; // The contexts saved by this uc_struct.
|
||||
};
|
||||
|
||||
// Metadata stub for the variable-size cpu context used with uc_context_*()
|
||||
// We also save cpu->jmp_env, so emulation can be reentrant
|
||||
struct uc_context {
|
||||
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
|
||||
};
|
||||
|
||||
|
@ -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
|
||||
|
||||
@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.
|
||||
|
||||
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);
|
||||
|
||||
/*
|
||||
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.
|
||||
|
||||
@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()
|
||||
@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,
|
||||
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,
|
||||
@ -556,12 +556,12 @@ uc_err uc_emu_stop(uc_engine *uc);
|
||||
|
||||
@uc: handle returned by uc_open()
|
||||
@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
|
||||
@user_data: user-defined data. This will be passed to callback function in its
|
||||
last argument @user_data
|
||||
@begin: start address of the area where the callback is effect (inclusive)
|
||||
@end: end 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 in effect (inclusive)
|
||||
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
|
||||
@...: 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.
|
||||
This API removes the hook callback registered by uc_hook_add().
|
||||
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()
|
||||
@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.
|
||||
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.
|
||||
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.
|
||||
This must be some combination of UC_PROT_READ | UC_PROT_WRITE | UC_PROT_EXEC,
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
This must be some combination of UC_PROT_READ | UC_PROT_WRITE | UC_PROT_EXEC,
|
||||
or this will return with UC_ERR_ARG error.
|
||||
@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.
|
||||
|
||||
@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.
|
||||
This address must be aligned to 4KB, or this will return with UC_ERR_ARG error.
|
||||
@size: size of the memory region to be modified.
|
||||
This size must be multiple of 4KB, or this will return with UC_ERR_ARG error.
|
||||
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
|
||||
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.
|
||||
This address must be aligned to 4KB, or this will return with UC_ERR_ARG error.
|
||||
@size: size of the memory region to be modified.
|
||||
This size must be multiple of 4KB, or this will return with UC_ERR_ARG error.
|
||||
This size must be a multiple of 4KB, or this will return with UC_ERR_ARG error.
|
||||
@perms: New permissions for the mapped region.
|
||||
This must be some combination of UC_PROT_READ | UC_PROT_WRITE | UC_PROT_EXEC,
|
||||
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()
|
||||
This API allocates memory for @regions, and user must free this memory later
|
||||
by free() to avoid leaking memory.
|
||||
NOTE: memory regions may be splitted by uc_mem_unmap()
|
||||
by uc_free() to avoid leaking memory.
|
||||
NOTE: memory regions may be split by uc_mem_unmap()
|
||||
|
||||
@uc: handle returned by uc_open()
|
||||
@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.
|
||||
|
||||
@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.
|
||||
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
|
||||
for detailed error).
|
||||
@ -708,10 +708,12 @@ UNICORN_EXPORT
|
||||
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
|
||||
by uc_mem_regions (returned in *regions)
|
||||
@mem: memory allocated by uc_mem_regions (returned in *regions).
|
||||
|
||||
@return UC_ERR_OK on success, or other value on failure (refer to uc_err enum
|
||||
for detailed error).
|
||||
@ -739,7 +741,7 @@ uc_err uc_context_save(uc_engine *uc, uc_context *context);
|
||||
state saved by uc_context_save().
|
||||
|
||||
@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
|
||||
for detailed error).
|
||||
@ -759,6 +761,18 @@ uc_err uc_context_restore(uc_engine *uc, uc_context *context);
|
||||
UNICORN_EXPORT
|
||||
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
|
||||
}
|
||||
#endif
|
||||
|
@ -53,6 +53,7 @@
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
<Import Project="$(VCTargetsPath)\BuildCustomizations\masm.props" />
|
||||
</ImportGroup>
|
||||
<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" />
|
||||
@ -357,7 +358,16 @@ copy "$(SolutionDir)..\include\unicorn\*.h" "$(SolutionDir)distro\include\unicor
|
||||
<ClInclude Include="..\qapi-types.h" />
|
||||
<ClInclude Include="..\qapi-visit.h" />
|
||||
</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" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
<Import Project="$(VCTargetsPath)\BuildCustomizations\masm.targets" />
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
</Project>
|
@ -499,4 +499,7 @@
|
||||
<ClInclude Include="..\..\..\include\unicorn\unicorn.h" />
|
||||
<ClInclude Include="..\..\..\include\unicorn\x86.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<MASM Include="..\..\..\qemu\util\setjmp-wrapper-win32.asm" />
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -169,6 +169,14 @@
|
||||
<ClInclude Include="..\qapi-types.h" />
|
||||
<ClInclude Include="..\qapi-visit.h" />
|
||||
</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">
|
||||
<ProjectGuid>{B6EFD6D7-C2D4-4FBB-B363-2E08CE09CC96}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
@ -204,6 +212,7 @@
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
<Import Project="$(VCTargetsPath)\BuildCustomizations\masm.props" />
|
||||
</ImportGroup>
|
||||
<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" />
|
||||
@ -363,5 +372,6 @@ copy "$(SolutionDir)..\include\unicorn\*.h" "$(SolutionDir)distro\include\unicor
|
||||
</ItemDefinitionGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
<Import Project="$(VCTargetsPath)\BuildCustomizations\masm.targets" />
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
</Project>
|
@ -498,4 +498,7 @@
|
||||
<Filter>qemu</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<MASM Include="..\..\..\qemu\util\setjmp-wrapper-win32.asm" />
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -11,3 +11,4 @@ PKG_EXTRA = 3
|
||||
|
||||
# version tag. Examples: rc1, b2, post1
|
||||
PKG_TAG =
|
||||
# PKG_TAG = rc6
|
||||
|
@ -2382,7 +2382,6 @@ symbols = (
|
||||
'qemu_get_guest_simple_memory_mapping',
|
||||
'qemu_get_ram_block',
|
||||
'qemu_get_ram_block_host_ptr',
|
||||
'qemu_get_ram_fd',
|
||||
'qemu_get_ram_ptr',
|
||||
'qemu_host_page_mask',
|
||||
'qemu_host_page_size',
|
||||
|
@ -205,7 +205,6 @@ static inline uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c)
|
||||
|
||||
/* vector definitions */
|
||||
#ifdef __ALTIVEC__
|
||||
#include <altivec.h>
|
||||
/* The altivec.h header says we're allowed to undef these for
|
||||
* C++ compatibility. Here we don't care about C++, but we
|
||||
* 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 pixel
|
||||
#undef bool
|
||||
#include <altivec.h>
|
||||
#define VECTYPE __vector unsigned char
|
||||
#define SPLAT(p) vec_splat(vec_ld(0, p), 0)
|
||||
#define ALL_EQ(v1, v2) vec_all_eq(v1, v2)
|
||||
|
@ -56,13 +56,27 @@
|
||||
# define EWOULDBLOCK WSAEWOULDBLOCK
|
||||
#endif
|
||||
|
||||
#if defined(_WIN64) && !defined(_MSC_VER)
|
||||
#if defined(_WIN64)
|
||||
/* 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. */
|
||||
|
||||
#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
|
||||
# define setjmp(env) _setjmp(env, NULL)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
/* QEMU uses sigsetjmp()/siglongjmp() as the portable way to specify
|
||||
* "longjmp and don't touch the signal masks". Since we know that the
|
||||
|
@ -69,6 +69,9 @@ int arm_reg_read(struct uc_struct *uc, unsigned int *regs, void **vals, int coun
|
||||
else {
|
||||
switch(regid) {
|
||||
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;
|
||||
break;
|
||||
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 {
|
||||
switch(regid) {
|
||||
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);
|
||||
break;
|
||||
case UC_ARM_REG_CPSR:
|
||||
|
@ -1011,16 +1011,7 @@ void helper_fstenv(CPUX86State *env, target_ulong ptr, int data32)
|
||||
}
|
||||
}
|
||||
|
||||
// DFLAG enum: tcg.h, case here to int
|
||||
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) {
|
||||
if (data32) {
|
||||
/* 32 bit */
|
||||
cpu_stl_data(env, ptr, env->fpuc);
|
||||
cpu_stl_data(env, ptr + 4, fpus);
|
||||
|
@ -710,102 +710,134 @@ void helper_cvtsq2sd(CPUX86State *env, XMMReg *d, uint64_t val)
|
||||
#endif
|
||||
|
||||
/* 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)
|
||||
{
|
||||
d->XMM_L(0) = 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(2) = 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(0) = x86_float32_to_int32(s->XMM_S(0), &env->sse_status);
|
||||
d->XMM_L(1) = x86_float32_to_int32(s->XMM_S(1), &env->sse_status);
|
||||
d->XMM_L(2) = x86_float32_to_int32(s->XMM_S(2), &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)
|
||||
{
|
||||
d->XMM_L(0) = 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(0) = x86_float64_to_int32(s->XMM_D(0), &env->sse_status);
|
||||
d->XMM_L(1) = x86_float64_to_int32(s->XMM_D(1), &env->sse_status);
|
||||
d->XMM_Q(1) = 0;
|
||||
}
|
||||
|
||||
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(1) = float32_to_int32(s->XMM_S(1), &env->sse_status);
|
||||
d->MMX_L(0) = x86_float32_to_int32(s->XMM_S(0), &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)
|
||||
{
|
||||
d->MMX_L(0) = 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(0) = x86_float64_to_int32(s->XMM_D(0), &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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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
|
||||
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)
|
||||
{
|
||||
return float64_to_int64(s->XMM_D(0), &env->sse_status);
|
||||
return x86_float64_to_int64(s->XMM_D(0), &env->sse_status);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* float to integer truncated */
|
||||
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(1) = 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(3) = float32_to_int32_round_to_zero(s->XMM_S(3), &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) = x86_float32_to_int32_round_to_zero(s->XMM_S(1), &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) = x86_float32_to_int32_round_to_zero(s->XMM_S(3), &env->sse_status);
|
||||
}
|
||||
|
||||
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(1) = float64_to_int32_round_to_zero(s->XMM_D(1), &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) = x86_float64_to_int32_round_to_zero(s->XMM_D(1), &env->sse_status);
|
||||
d->XMM_Q(1) = 0;
|
||||
}
|
||||
|
||||
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(1) = float32_to_int32_round_to_zero(s->XMM_S(1), &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) = x86_float32_to_int32_round_to_zero(s->XMM_S(1), &env->sse_status);
|
||||
}
|
||||
|
||||
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(1) = float64_to_int32_round_to_zero(s->XMM_D(1), &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) = x86_float64_to_int32_round_to_zero(s->XMM_D(1), &env->sse_status);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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
|
||||
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)
|
||||
{
|
||||
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
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
((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)
|
||||
|
26
qemu/util/setjmp-wrapper-win32.asm
Normal file
26
qemu/util/setjmp-wrapper-win32.asm
Normal 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
|
@ -737,7 +737,7 @@ static void test_i386_context_save(void)
|
||||
printf(">>> EAX = 0x%x\n", r_eax);
|
||||
|
||||
// free the CPU context
|
||||
err = uc_free(context);
|
||||
err = uc_context_free(context);
|
||||
if (err) {
|
||||
printf("Failed on uc_free() with error returned: %u\n", err);
|
||||
return;
|
||||
|
30
tests/regress/arm_apsr_access.py
Normal file
30
tests/regress/arm_apsr_access.py
Normal 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
48
uc.c
@ -185,7 +185,11 @@ uc_err uc_open(uc_arch arch, uc_mode mode, uc_engine **result)
|
||||
return UC_ERR_MODE;
|
||||
}
|
||||
if (mode & UC_MODE_BIG_ENDIAN) {
|
||||
#ifdef UNICORN_HAS_ARMEB
|
||||
uc->init_arch = armeb_uc_init;
|
||||
#else
|
||||
return UC_ERR_MODE;
|
||||
#endif
|
||||
} else {
|
||||
uc->init_arch = arm_uc_init;
|
||||
}
|
||||
@ -347,6 +351,16 @@ uc_err uc_close(uc_engine *uc)
|
||||
|
||||
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.
|
||||
memset(uc, 0, sizeof(*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;
|
||||
#endif
|
||||
#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
|
||||
#ifdef UNICORN_HAS_ARM64
|
||||
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)
|
||||
{
|
||||
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);
|
||||
if (*_context) {
|
||||
(*_context)->jmp_env_size = sizeof(*uc->cpu->jmp_env);
|
||||
(*_context)->context_size = size - sizeof(uc_context) - (*_context)->jmp_env_size;
|
||||
return UC_ERR_OK;
|
||||
(*_context)->context_size = cpu_context_size(uc->arch, uc->mode);
|
||||
(*_context)->uc = uc;
|
||||
if (list_insert(&uc->saved_contexts, *_context)) {
|
||||
return UC_ERR_OK;
|
||||
} else {
|
||||
return UC_ERR_NOMEM;
|
||||
}
|
||||
} else {
|
||||
return UC_ERR_NOMEM;
|
||||
}
|
||||
@ -1364,7 +1389,20 @@ UNICORN_EXPORT
|
||||
uc_err uc_context_restore(uc_engine *uc, uc_context *context)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user