Merge branch 'dev' into fix-stringcolumn-resizing

This commit is contained in:
Anton Kochkov 2023-06-27 17:22:37 +08:00 committed by GitHub
commit 312e4d596e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
291 changed files with 13534 additions and 7174 deletions

View File

@ -1,4 +1,4 @@
version: '2.0-git-{build}'
version: '2.2.0-git-{build}'
image: 'Visual Studio 2017'
clone_depth: 1
@ -30,7 +30,6 @@ install:
before_build:
- cmd: git submodule update --init --recursive
- scripts\prepare_breakpad.bat
# Build config
build_script:
@ -48,7 +47,6 @@ build_script:
-DCUTTER_PACKAGE_RZ_GHIDRA=ON
-DCUTTER_PACKAGE_JSDEC=ON
-DCUTTER_ENABLE_DEPENDENCY_DOWNLOADS=ON
-DCUTTER_ENABLE_CRASH_REPORTS=ON
-DCMAKE_PREFIX_PATH=%CUTTER_DEPS%\\pyside
-DCPACK_PACKAGE_FILE_NAME=%PACKAGE_NAME%
-G Ninja
@ -80,7 +78,8 @@ for:
-
branches:
only:
- master
- dev
- stable
-
skip_non_tags: true

View File

@ -2,9 +2,9 @@ blank_issues_enabled: false
contact_links:
- name: Questions Telegram
url: https://t.me/r2cutter
url: https://t.me/cutter_re
about: Please ask questions about Cutter here or one of the other community channels, not in the issue tracker.
- name: Questions IRC
url: https://web.libera.chat/#cutter
about: "#cutter on https://web.libera.chat/"
about: "#cutter on https://web.libera.chat/"

View File

@ -1,273 +0,0 @@
name: Cutter CI
on:
push:
branches:
- master
tags:
- v*
- upload-test*
pull_request:
branches:
- master
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-18.04, ubuntu-16.04, macos-latest, windows-2016]
python-version: [3.7.x]
system-deps: [false]
cc-override: [default]
cxx-override: [default]
include:
- os: windows-2016
package: true
- os: ubuntu-16.04 # ensure that Cutter can be built at least in basic config on Ubuntu 16.04 using sytem libraries
python-version: 3.5.10
system-deps: true
cc-override: '/usr/bin/gcc-5'
cxx-override: '/usr/bin/g++-5'
- os: ubuntu-16.04 # release package build
system-deps: false
package: true
# Prevent one job from pausing the rest
fail-fast: false
steps:
- uses: actions/checkout@v2
with:
submodules: recursive
persist-credentials: false
- name: apt dependencies
if: contains(matrix.os, 'ubuntu')
run: |
sudo apt-get install libgraphviz-dev mesa-common-dev libxkbcommon-x11-dev libclang-8-dev llvm-8
if [[ "${{ matrix.os }}" = "ubuntu-16.04" ]]
then
sudo apt-get install ninja-build/xenial-backports libxcb1-dev libxkbcommon-dev libxcb-*-dev
if [[ "${{ matrix.system-deps }}" = "true" ]]
then
sudo apt-get install cmake
sudo apt-get install --allow-downgrades \
g++-5=5.4.\* \
gcc-5=5.4.\* \
gcc-5-base=5.4.\* \
libstdc++-5-dev=5.4.\* \
cpp-5=5.4.\* \
libgcc-5-dev=5.4.\* \
libasan2=5.4.\* \
libmpx0=5.4.\*
fi
# make sure cmake that was just installed is at the front of path before
# additional software installed by GitHub
echo /usr/bin >> $GITHUB_PATH
else
sudo apt-get install ninja-build
fi
if [[ "${{ matrix.system-deps }}" = "true" ]]
then
sudo apt-get install qt5-default libqt5svg5-dev qttools5-dev qttools5-dev-tools
fi
- uses: actions/setup-python@v1
with:
python-version: ${{ matrix.python-version }}
- name: homebrew dependencies
if: contains(matrix.os, 'macos')
run: |
cd scripts
rm '/usr/local/bin/2to3' # symlink to some kind of existing python2.7 installation conflicts with brew python3 which gets installed as indirect dependency
brew update --preinstall # temporary workaround for https://github.com/Homebrew/homebrew-bundle/issues/751
brew bundle
brew install coreutils
brew install pkg-config
- name: py dependencies
run: |
# 0.56.1 doesn't work with python 3.5 on Ubuntu 16.04
pip install meson==0.56.0
- name: Prepare package id
shell: bash
run: |
if [[ "${{ startsWith(github.event.ref, 'refs/tags')}}" = "true" ]]
then
PACKAGE_ID="${{ github.event.ref }}"
else
PACKAGE_ID="git-`date "+%Y-%m-%d"`-${{ format('{0}', github.sha) }}"
fi
PACKAGE_ID=${PACKAGE_ID##refs/tags/}
echo PACKAGE_ID=$PACKAGE_ID >> $GITHUB_ENV
- name: cmake ubuntu
if: contains(matrix.os, 'ubuntu')
run: |
if [[ "${{ matrix.system-deps }}" = "false" ]]
then
scripts/fetch_deps.sh
source cutter-deps/env.sh
export PKG_CONFIG_PATH="$CUTTER_DEPS_PYTHON_PREFIX/lib/pkgconfig:${PKG_CONFIG_PATH:-}"
export LD_LIBRARY_PATH="`llvm-config --libdir`:$LD_LIBRARY_PATH"
fi
set -euo pipefail #TODO: move to top once cutter-deps doesn't fail
if [[ "${{ matrix.cc-override }}" != "default" ]]
then
export CC="${{matrix.cc-override}}"
export CXX="${{matrix.cxx-override}}"
fi
source scripts/prepare_breakpad_linux.sh
export PKG_CONFIG_PATH="$CUSTOM_BREAKPAD_PREFIX/lib/pkgconfig:${PKG_CONFIG_PATH:-}" #
mkdir build
cd build
cmake --version
if [[ "${{ matrix.system-deps }}" = "false" ]]
then
cmake \
-G Ninja \
-DCMAKE_BUILD_TYPE=Release \
-DCUTTER_ENABLE_PYTHON=ON \
-DPYTHON_LIBRARY="$CUTTER_DEPS_PYTHON_PREFIX/lib/libpython3.9.so" \
-DPYTHON_INCLUDE_DIR="$CUTTER_DEPS_PYTHON_PREFIX/include/python3.9" \
-DPYTHON_EXECUTABLE="$CUTTER_DEPS_PYTHON_PREFIX/bin/python3" \
-DCUTTER_ENABLE_PYTHON_BINDINGS=ON \
-DCUTTER_ENABLE_GRAPHVIZ=OFF \
-DCUTTER_ENABLE_CRASH_REPORTS=ON \
-DCUTTER_USE_BUNDLED_RIZIN=ON \
-DCUTTER_APPIMAGE_BUILD=ON \
-DCUTTER_ENABLE_PACKAGING=ON \
-DCUTTER_ENABLE_DEPENDENCY_DOWNLOADS=ON \
-DCUTTER_PACKAGE_RZ_GHIDRA=ON \
-DCMAKE_INSTALL_PREFIX=appdir/usr \
-DCMAKE_INSTALL_RPATH_USE_LINK_PATH=ON \
..
else
cmake \
-G Ninja \
-DCMAKE_BUILD_TYPE=Release \
-DCUTTER_USE_BUNDLED_RIZIN=ON \
..
fi
ninja
if [[ "${{ matrix.package || false }}" = "true" ]]
then
export CUTTER_VERSION=$(python ../scripts/get_version.py)
export VERSION=$CUTTER_VERSION
ninja install
"../scripts/appimage_embed_python.sh" appdir
APP_PREFIX=`pwd`/appdir/usr
# export LD_LIBRARY_PATH=${APP_PREFIX}/lib:$Shiboken2_ROOT/lib
export PATH=$PATH:${APP_PREFIX}/bin
"../scripts/jsdec.sh" --prefix=`pwd`/appdir/usr
wget -c "https://github.com/probonopd/linuxdeployqt/releases/download/6/linuxdeployqt-6-x86_64.AppImage"
chmod a+x linuxdeployqt*.AppImage
rm -fv "../cutter-deps/qt/plugins/imageformats/libqjp2.so"
# ./linuxdeployqt*.AppImage ./appdir/usr/share/applications/*.desktop -executable=./appdir/usr/bin/python3 -bundle-non-qt-libs -no-strip -exclude-libs=libnss3.so,libnssutil3.so,libqjp2.so -ignore-glob=usr/lib/python3.9/**/* -verbose=2
# exclude librzghidra cutter plugin because cutter and rz plugin is loaded manuallly as they are plugins linuxdeployqt doesn't know this
./linuxdeployqt*.AppImage ./appdir/usr/share/applications/*.desktop \
-executable=./appdir/usr/bin/python3 \
-appimage \
-no-strip -exclude-libs=libnss3.so,libnssutil3.so,libqjp2.so \
-ignore-glob=usr/lib/python3.9/**/* \
-ignore-glob=usr/share/rizin/cutter/plugins/native/librz_ghidra_cutter.so \
-verbose=2
find ./appdir -executable -type f -exec ldd {} \; | grep " => /usr" | cut -d " " -f 2-3 | sort | uniq
export APPIMAGE_FILE="Cutter-${PACKAGE_ID}-x64.Linux.AppImage"
mv Cutter-*-x86_64.AppImage "$APPIMAGE_FILE"
echo PACKAGE_NAME=$APPIMAGE_FILE >> $GITHUB_ENV
echo UPLOAD_ASSET_TYPE=application/x-executable >> $GITHUB_ENV
fi
- name: cmake macos
shell: bash
if: contains(matrix.os, 'macos')
run: |
export MACOSX_DEPLOYMENT_TARGET=10.14
scripts/fetch_deps.sh
source cutter-deps/env.sh
set -euo pipefail
export PATH=/usr/local/opt/llvm/bin:$PATH
source scripts/prepare_breakpad_macos.sh
mkdir build
cd build
PACKAGE_NAME=Cutter-${PACKAGE_ID}-x64.macOS
cmake \
-DCMAKE_BUILD_TYPE=Release \
-DPYTHON_LIBRARY="$CUTTER_DEPS_PYTHON_PREFIX/lib/libpython3.9.dylib" \
-DPYTHON_INCLUDE_DIR="$CUTTER_DEPS_PYTHON_PREFIX/include/python3.9" \
-DPYTHON_EXECUTABLE="$CUTTER_DEPS_PYTHON_PREFIX/bin/python3" \
-DCUTTER_ENABLE_PYTHON=ON \
-DCUTTER_ENABLE_PYTHON_BINDINGS=ON \
-DCUTTER_ENABLE_CRASH_REPORTS=ON \
-DCUTTER_USE_BUNDLED_RIZIN=ON \
-DCUTTER_ENABLE_PACKAGING=ON \
-DCUTTER_PACKAGE_DEPENDENCIES=ON \
-DCUTTER_ENABLE_DEPENDENCY_DOWNLOADS=ON \
-DCUTTER_PACKAGE_RZ_GHIDRA=ON \
-DCUTTER_PACKAGE_JSDEC=ON \
-DCPACK_PACKAGE_FILE_NAME="$PACKAGE_NAME" \
-DCMAKE_FRAMEWORK_PATH="$BREAKPAD_FRAMEWORK_DIR" \
-DCPACK_BUNDLE_APPLE_CERT_APP="-" \
.. && \
make -j4;
make package
export CUTTER_VERSION=$(python3 ../scripts/get_version.py)
echo PACKAGE_NAME=${PACKAGE_NAME}.dmg >> $GITHUB_ENV
echo UPLOAD_ASSET_TYPE=application/x-apple-diskimage >> $GITHUB_ENV
- name: windows dependencies
if: contains(matrix.os, 'windows')
shell: bash
run: |
pip install ninja
scripts/fetch_deps.sh
choco install winflexbison3
- name: windows cmake
if: contains(matrix.os, 'windows')
shell: cmd
run: |
set ARCH=x64
set CUTTER_DEPS=%CD%\cutter-deps
set PATH=%CD%\cutter-deps\qt\bin;%PATH%
call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x64
call scripts\prepare_breakpad.bat
cd
mkdir build
cd build
set PACKAGE_NAME=cutter-%PACKAGE_ID%-x64.Windows
cmake ^
-DCMAKE_BUILD_TYPE=Release ^
-DCUTTER_USE_BUNDLED_RIZIN=ON ^
-DCUTTER_ENABLE_PYTHON=ON ^
-DCUTTER_ENABLE_PYTHON_BINDINGS=ON ^
-DCUTTER_ENABLE_PACKAGING=ON ^
-DCUTTER_PACKAGE_DEPENDENCIES=ON ^
-DCUTTER_PACKAGE_RZ_GHIDRA=ON ^
-DCUTTER_PACKAGE_JSDEC=ON ^
-DCUTTER_ENABLE_DEPENDENCY_DOWNLOADS=ON ^
-DCUTTER_ENABLE_CRASH_REPORTS=ON ^
-DCMAKE_PREFIX_PATH="%CUTTER_DEPS%\pyside" ^
-DCPACK_PACKAGE_FILE_NAME=%PACKAGE_NAME% ^
-G Ninja ^
..
cmake --build . --config Release
cmake --build . --config Release --target package
echo PACKAGE_NAME=%PACKAGE_NAME%.zip >> %GITHUB_ENV%
echo UPLOAD_ASSET_TYPE=application/zip >> %GITHUB_ENV%
- uses: actions/upload-artifact@v2
if: env.PACKAGE_NAME != null
with:
name: ${{ env.PACKAGE_NAME }}
path: build/${{ env.PACKAGE_NAME }}
- name: Get release
if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags')
id: get_release
uses: karliss/get-release@23b8b7144dd5b0c9d6942b2fb78bd9ae71546d03
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Upload release assets
if: steps.get_release.outputs.upload_url != null && env.PACKAGE_NAME != null
uses: actions/upload-release-asset@v1.0.2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.get_release.outputs.upload_url }}
asset_path: build/${{ env.PACKAGE_NAME }}
asset_name: ${{ env.PACKAGE_NAME }}
asset_content_type: ${{ env.UPLOAD_ASSET_TYPE }}

450
.github/workflows/ci.yml vendored Normal file
View File

@ -0,0 +1,450 @@
name: CI
on:
push:
branches:
- dev
- stable
tags:
- v*
- upload-test*
pull_request:
branches:
- dev
- stable
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
build-linux:
name: ${{ matrix.name }}
runs-on: ubuntu-latest
container:
image: ${{ matrix.image }}
options: --privileged
strategy:
matrix:
name: [
linux-x86_64,
linux-x86_64-system-deps,
linux-x86_64-qt6-system-deps,
tarball
]
include:
- name: linux-x86_64-system-deps # ensure that Cutter can be built at least in basic config on Ubuntu 18.04 using sytem libraries
image: ubuntu:18.04
python-version: 3.6.x
system-deps: true
package: false
tarball: false
cc-override: '/usr/bin/gcc-7'
cxx-override: '/usr/bin/g++-7'
- name: linux-x86_64-qt6-system-deps # ensure that Cutter can be built at least in basic config on Ubuntu 22.04 using sytem libraries
image: ubuntu:22.04
python-version: 3.10.x
system-deps: true
package: false
tarball: false
cc-override: '/usr/bin/gcc-12'
cxx-override: '/usr/bin/g++-12'
- name: linux-x86_64
image: ubuntu:18.04
python-version: 3.6.x
system-deps: false
package: true
tarball: false
cc-override: default
cxx-override: default
- name: tarball
python-version: 3.6.x
image: ubuntu:20.04
system-deps: false
package: false
tarball: true
# Prevent one job from pausing the rest
fail-fast: false
steps:
- name: set timezone
run: |
# Fix timezone on ubuntu to prevent user input request during the apt-get phase.
export TZ=UTC
ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
- name: install latest git and cmake
shell: bash
run: |
set -e
apt-get -y update
echo "Using image: ${{ matrix.image }}"
export GIT_VERSION="git-2.36.1"
export CMAKE_VERSION="3.25.3"
apt-get -y install wget libcurl4-gnutls-dev libexpat1-dev gettext libz-dev libssl-dev build-essential
wget "https://www.kernel.org/pub/software/scm/git/$GIT_VERSION.tar.gz"
tar -zxf "$GIT_VERSION.tar.gz"
# build.
make -C "$GIT_VERSION" prefix=/usr install -j > "$GIT_VERSION/build.log"
# ensure git is installed.
git version
wget "https://github.com/Kitware/CMake/releases/download/v$CMAKE_VERSION/cmake-$CMAKE_VERSION-linux-x86_64.sh"
bash ./cmake-$CMAKE_VERSION-linux-x86_64.sh --skip-license --prefix=/usr
# ensure cmake is installed.
cmake --version
# cleanup dev environment.
rm -rf "$GIT_VERSION.tar.gz" "$GIT_VERSION" cmake-$CMAKE_VERSION-linux-x86_64.sh
unset CMAKE_VERSION
unset GIT_VERSION
- uses: actions/checkout@v3
with:
submodules: recursive
persist-credentials: false
- name: apt cutter dependencies
shell: bash
run: |
# install needed packages
apt-get -y install libgraphviz-dev \
mesa-common-dev \
libxkbcommon-x11-dev \
ninja-build \
python3-pip \
curl \
libpcre2-dev \
libfuse2 \
pkg-config
if [ "${{ matrix.image }}" = "ubuntu:18.04" ]; then
# install additional packages needed for appimage
apt-get -y install gcc-7 \
libglu1-mesa-dev \
freeglut3-dev \
mesa-common-dev
fi
if [ "${{ matrix.image }}" = "ubuntu:18.04" ] || [ "${{ matrix.image }}" = "ubuntu:20.04" ]; then
# install additional packages needed for appimage
apt-get -y install libxcb1-dev \
libxkbcommon-dev \
libxcb-*-dev \
libegl1 \
libclang-8-dev \
llvm-8
ln -s /usr/bin/llvm-config-8 /usr/bin/llvm-config
fi
if [ "${{ matrix.image }}" = "ubuntu:18.04" ] && [ "${{ matrix.system-deps }}" = "true" ]; then
apt-get -y install qt5-default \
libqt5svg5-dev \
qttools5-dev \
qttools5-dev-tools
fi
if [ "${{ matrix.image }}" = "ubuntu:22.04" ]; then
apt-get -y install libclang-12-dev \
llvm-12 \
qt6-base-dev \
qt6-tools-dev \
qt6-tools-dev-tools \
libqt6svg6-dev \
libqt6core5compat6-dev \
libqt6svgwidgets6 \
qt6-l10n-tools \
gcc-12 \
g++-12
fi
- uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: py dependencies
run: |
# https://github.com/rizinorg/cutter/runs/7170222817?check_suite_focus=true
python3 -m pip install meson==0.61.5
- name: Prepare package id
shell: bash
run: |
if [ "${{ startsWith(github.event.ref, 'refs/tags')}}" = "true" ]
then
PACKAGE_ID="${{ github.event.ref }}"
else
PACKAGE_ID="git-`date "+%Y-%m-%d"`-${{ format('{0}', github.sha) }}"
fi
PACKAGE_ID=${PACKAGE_ID##refs/tags/}
echo PACKAGE_ID=$PACKAGE_ID >> $GITHUB_ENV
- name: cmake ubuntu
shell: bash
run: |
if [ "${{ matrix.system-deps }}" = "false" ]
then
scripts/fetch_deps.sh
. cutter-deps/env.sh
export PKG_CONFIG_PATH="$CUTTER_DEPS_PYTHON_PREFIX/lib/pkgconfig:${PKG_CONFIG_PATH:-}"
export LD_LIBRARY_PATH="`llvm-config --libdir`:$LD_LIBRARY_PATH"
fi
set -e #TODO: move to top once cutter-deps doesn't fail
if [ "${{ matrix.cc-override }}" != "default" ]
then
export CC="${{matrix.cc-override}}"
export CXX="${{matrix.cxx-override}}"
fi
mkdir build
cd build
if [ "${{ matrix.system-deps }}" = "false" ]
then
cmake \
-G Ninja \
-DCMAKE_BUILD_TYPE=Release \
-DCUTTER_ENABLE_PYTHON=ON \
-DPYTHON_LIBRARY="$CUTTER_DEPS_PYTHON_PREFIX/lib/libpython3.9.so" \
-DPYTHON_INCLUDE_DIR="$CUTTER_DEPS_PYTHON_PREFIX/include/python3.9" \
-DPYTHON_EXECUTABLE="$CUTTER_DEPS_PYTHON_PREFIX/bin/python3" \
-DCUTTER_ENABLE_PYTHON_BINDINGS=ON \
-DCUTTER_ENABLE_GRAPHVIZ=ON \
-DCUTTER_USE_BUNDLED_RIZIN=ON \
-DCUTTER_APPIMAGE_BUILD=ON \
-DCUTTER_ENABLE_PACKAGING=ON \
-DCUTTER_ENABLE_KSYNTAXHIGHLIGHTING=OFF \
-DCUTTER_ENABLE_SIGDB=ON \
-DCUTTER_ENABLE_DEPENDENCY_DOWNLOADS=ON \
-DCUTTER_PACKAGE_RZ_GHIDRA=ON \
-DCUTTER_PACKAGE_JSDEC=ON \
-DCUTTER_PACKAGE_RZ_LIBSWIFT=ON \
-DCUTTER_PACKAGE_RZ_LIBYARA=ON \
-DCUTTER_PACKAGE_RZ_SILHOUETTE=ON \
-DCMAKE_INSTALL_PREFIX=appdir/usr \
-DCMAKE_INSTALL_RPATH_USE_LINK_PATH=ON \
..
elif [ "${{ matrix.image }}" = "ubuntu:22.04" ]
then
cmake \
-G Ninja \
-DCMAKE_BUILD_TYPE=Release \
-DCUTTER_QT6=ON \
-DCUTTER_USE_BUNDLED_RIZIN=ON \
..
else
cmake \
-G Ninja \
-DCMAKE_BUILD_TYPE=Release \
-DCUTTER_USE_BUNDLED_RIZIN=ON \
..
fi
ninja
if [ "${{ matrix.package }}" = "true" ]
then
export CUTTER_VERSION=$(python ../scripts/get_version.py)
export VERSION=$CUTTER_VERSION
ninja install
"../scripts/appimage_embed_python.sh" appdir
APP_PREFIX=`pwd`/appdir/usr
export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$APP_PREFIX/lib/rizin/plugins"
export PATH=$PATH:${APP_PREFIX}/bin
wget -c "https://github.com/probonopd/linuxdeployqt/releases/download/continuous/linuxdeployqt-continuous-x86_64.AppImage"
chmod a+x linuxdeployqt*.AppImage
rm -fv "../cutter-deps/qt/plugins/imageformats/libqjp2.so"
./linuxdeployqt*.AppImage ./appdir/usr/share/applications/*.desktop \
-executable=./appdir/usr/bin/python3 \
-appimage \
-no-strip -exclude-libs=libnss3.so,libnssutil3.so,libqjp2.so \
-ignore-glob=usr/lib/python3.9/**/* \
-verbose=2
find ./appdir -executable -type f -exec ldd {} \; | grep " => /usr" | cut -d " " -f 2-3 | sort | uniq
export APPIMAGE_FILE="Cutter-${PACKAGE_ID}-Linux-x86_64.AppImage"
mv Cutter-*-x86_64.AppImage "$APPIMAGE_FILE"
echo PACKAGE_NAME=$APPIMAGE_FILE >> $GITHUB_ENV
echo PACKAGE_PATH=build/$APPIMAGE_FILE >> $GITHUB_ENV
echo UPLOAD_ASSET_TYPE=application/x-executable >> $GITHUB_ENV
fi
- name: Create tarball
if: matrix.tarball
shell: bash
run: |
scripts/tarball.sh "Cutter-${PACKAGE_ID}"
echo PACKAGE_NAME=Cutter-${PACKAGE_ID}-src.tar.gz >> $GITHUB_ENV
echo PACKAGE_PATH=Cutter-${PACKAGE_ID}-src.tar.gz >> $GITHUB_ENV
echo UPLOAD_ASSET_TYPE=application/gzip >> $GITHUB_ENV
- uses: actions/upload-artifact@v3
if: env.PACKAGE_NAME != null
with:
name: ${{ env.PACKAGE_NAME }}
path: ${{ env.PACKAGE_PATH }}
- name: Get release
if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags')
id: get_release
uses: rizinorg/gha-get-release@c8074dd5d13ddd0a194d8c9205a1466973c7dc0d
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Upload release assets
if: steps.get_release.outputs.upload_url != null && env.PACKAGE_NAME != null
uses: actions/upload-release-asset@v1.0.2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.get_release.outputs.upload_url }}
asset_path: ${{ env.PACKAGE_PATH }}
asset_name: ${{ env.PACKAGE_NAME }}
asset_content_type: ${{ env.UPLOAD_ASSET_TYPE }}
build:
name: ${{ matrix.name }}
runs-on: ${{ matrix.os }}
strategy:
matrix:
name: [
macos-x86_64,
windows-x86_64,
]
include:
- name: windows-x86_64
os: windows-2019
package: true
system-deps: false
python-version: 3.7.x
- name: macos-x86_64
os: macos-latest
python-version: 3.7.x
system-deps: false
package: true
cc-override: default
cxx-override: default
# Prevent one job from pausing the rest
fail-fast: false
steps:
- uses: actions/checkout@v3
with:
submodules: recursive
persist-credentials: false
- uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: homebrew dependencies
if: contains(matrix.os, 'macos')
run: |
cd scripts
rm /usr/local/bin/2to3* # symlink to some kind of existing python2.7 installation conflicts with brew python3 which gets installed as indirect dependency
brew bundle
- name: py dependencies
run: |
python3 -m pip install -U pip==21.3.1
pip install meson==0.61.5 # https://github.com/rizinorg/cutter/runs/7170222817?check_suite_focus=true
- name: Prepare package id
shell: bash
run: |
if [[ "${{ startsWith(github.event.ref, 'refs/tags')}}" = "true" ]]
then
PACKAGE_ID="${{ github.event.ref }}"
else
PACKAGE_ID="git-`date "+%Y-%m-%d"`-${{ format('{0}', github.sha) }}"
fi
PACKAGE_ID=${PACKAGE_ID##refs/tags/}
echo PACKAGE_ID=$PACKAGE_ID >> $GITHUB_ENV
- name: cmake macos
shell: bash
if: contains(matrix.os, 'macos')
run: |
export MACOSX_DEPLOYMENT_TARGET=10.14
scripts/fetch_deps.sh
source cutter-deps/env.sh
set -euo pipefail
export PATH=/usr/local/opt/llvm/bin:$PATH
mkdir build
cd build
PACKAGE_NAME=Cutter-${PACKAGE_ID}-macOS-x86_64
cmake \
-DCMAKE_BUILD_TYPE=Release \
-DPYTHON_LIBRARY="$CUTTER_DEPS_PYTHON_PREFIX/lib/libpython3.9.dylib" \
-DPYTHON_INCLUDE_DIR="$CUTTER_DEPS_PYTHON_PREFIX/include/python3.9" \
-DPYTHON_EXECUTABLE="$CUTTER_DEPS_PYTHON_PREFIX/bin/python3" \
-DCUTTER_ENABLE_PYTHON=ON \
-DCUTTER_ENABLE_PYTHON_BINDINGS=ON \
-DCUTTER_USE_BUNDLED_RIZIN=ON \
-DCUTTER_ENABLE_PACKAGING=ON \
-DCUTTER_ENABLE_SIGDB=ON \
-DCUTTER_PACKAGE_DEPENDENCIES=ON \
-DCUTTER_ENABLE_DEPENDENCY_DOWNLOADS=ON \
-DCUTTER_PACKAGE_RZ_GHIDRA=ON \
-DCUTTER_PACKAGE_JSDEC=ON \
-DCUTTER_PACKAGE_RZ_LIBSWIFT=ON \
-DCUTTER_PACKAGE_RZ_LIBYARA=ON \
-DCUTTER_PACKAGE_RZ_SILHOUETTE=ON \
-DCPACK_PACKAGE_FILE_NAME="$PACKAGE_NAME" \
-DCPACK_BUNDLE_APPLE_CERT_APP="-" \
.. && \
make -j4;
make package
export CUTTER_VERSION=$(python3 ../scripts/get_version.py)
echo PACKAGE_NAME=${PACKAGE_NAME}.dmg >> $GITHUB_ENV
echo PACKAGE_PATH=build/${PACKAGE_NAME}.dmg >> $GITHUB_ENV
echo UPLOAD_ASSET_TYPE=application/x-apple-diskimage >> $GITHUB_ENV
- name: windows dependencies
if: contains(matrix.os, 'windows')
shell: bash
run: |
pip install ninja
scripts/fetch_deps.sh
choco install winflexbison3
- name: windows cmake
if: contains(matrix.os, 'windows')
shell: cmd
run: |
set ARCH=x64
set CUTTER_DEPS=%CD%\cutter-deps
set PATH=%CD%\cutter-deps\qt\bin;%PATH%
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x64
cd
mkdir build
cd build
set PACKAGE_NAME=Cutter-%PACKAGE_ID%-Windows-x86_64
cmake ^
-DCMAKE_BUILD_TYPE=Release ^
-DCUTTER_USE_BUNDLED_RIZIN=ON ^
-DCUTTER_ENABLE_PYTHON=ON ^
-DCUTTER_ENABLE_PYTHON_BINDINGS=ON ^
-DCUTTER_ENABLE_PACKAGING=ON ^
-DCUTTER_ENABLE_SIGDB=ON ^
-DCUTTER_PACKAGE_DEPENDENCIES=ON ^
-DCUTTER_PACKAGE_RZ_GHIDRA=ON ^
-DCUTTER_PACKAGE_RZ_LIBSWIFT=ON ^
-DCUTTER_PACKAGE_RZ_LIBYARA=ON ^
-DCUTTER_PACKAGE_RZ_SILHOUETTE=ON ^
-DCUTTER_PACKAGE_JSDEC=ON ^
-DCUTTER_ENABLE_DEPENDENCY_DOWNLOADS=ON ^
-DCMAKE_PREFIX_PATH="%CUTTER_DEPS%\pyside" ^
-DCPACK_PACKAGE_FILE_NAME=%PACKAGE_NAME% ^
-G Ninja ^
..
cmake --build . --config Release
cmake --build . --config Release --target package
echo PACKAGE_NAME=%PACKAGE_NAME%.zip >> %GITHUB_ENV%
echo PACKAGE_PATH=build/%PACKAGE_NAME%.zip >> %GITHUB_ENV%
echo UPLOAD_ASSET_TYPE=application/zip >> %GITHUB_ENV%
- uses: actions/upload-artifact@v3
if: env.PACKAGE_NAME != null
with:
name: ${{ env.PACKAGE_NAME }}
path: ${{ env.PACKAGE_PATH }}
- name: Get release
if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags')
id: get_release
uses: rizinorg/gha-get-release@c8074dd5d13ddd0a194d8c9205a1466973c7dc0d
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Upload release assets
if: steps.get_release.outputs.upload_url != null && env.PACKAGE_NAME != null
uses: actions/upload-release-asset@v1.0.2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.get_release.outputs.upload_url }}
asset_path: ${{ env.PACKAGE_PATH }}
asset_name: ${{ env.PACKAGE_NAME }}
asset_content_type: ${{ env.UPLOAD_ASSET_TYPE }}

View File

@ -7,16 +7,16 @@ jobs:
latest:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
with:
submodules: recursive
- uses: actions/setup-python@v1
- uses: actions/setup-python@v4
with:
python-version: 3.7.x
python-version: 3.9.x
- name: Download Coverity Build Tool
run: |
wget -q https://scan.coverity.com/download/cxx/linux64 --post-data "token=$TOKEN&project=radareorg%2Fcutter" -O cov-analysis-linux64.tar.gz
wget -q https://scan.coverity.com/download/cxx/linux64 --post-data "token=$TOKEN&project=rizinorg%2Fcutter" -O cov-analysis-linux64.tar.gz
mkdir cov-analysis-linux64
tar xzf cov-analysis-linux64.tar.gz --strip 1 -C cov-analysis-linux64
env:

View File

@ -3,13 +3,13 @@ name: Docs
on:
push:
branches:
- master
- dev
jobs:
deploy:
runs-on: ubuntu-20.04
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
with:
submodules: recursive
- name: install dependencies

View File

@ -3,10 +3,12 @@ name: "Linter"
on:
push:
branches:
- master
- dev
- stable
pull_request:
branches:
- master
- dev
- stable
jobs:
changes:
@ -14,7 +16,7 @@ jobs:
outputs:
clang-format: ${{ steps.filter.outputs.clang-format }}
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- uses: dorny/paths-filter@v2
id: filter
with:
@ -31,7 +33,7 @@ jobs:
if: ${{ needs.changes.outputs.clang-format == 'true' }}
steps:
- name: Checkout repository
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: Install wget
run: sudo apt --assume-yes install wget

View File

@ -0,0 +1,47 @@
platform: darwin/arm64
pipeline:
fetch-deps:
image: /bin/bash
commands:
- scripts/fetch_deps.sh
build:
image: /bin/bash
commands:
- set -e
- export PACKAGE_ID=${CI_COMMIT_TAG=git-`date "+%Y-%m-%d"`-${CI_COMMIT_SHA}}
- export PACKAGE_NAME=Cutter-$${PACKAGE_ID}-macOS-arm64
- source cutter-deps/env.sh
- cmake -Bbuild -GNinja
-DCMAKE_BUILD_TYPE=Release
-DPYTHON_LIBRARY="$$CUTTER_DEPS_PYTHON_PREFIX/lib/libpython3.9.dylib"
-DPYTHON_INCLUDE_DIR="$$CUTTER_DEPS_PYTHON_PREFIX/include/python3.9"
-DPYTHON_EXECUTABLE="$$CUTTER_DEPS_PYTHON_PREFIX/bin/python3"
-DCUTTER_ENABLE_PYTHON=ON
-DCUTTER_ENABLE_PYTHON_BINDINGS=ON
-DCUTTER_USE_BUNDLED_RIZIN=ON
-DCUTTER_ENABLE_PACKAGING=ON
-DCUTTER_ENABLE_SIGDB=ON
-DCUTTER_PACKAGE_DEPENDENCIES=ON
-DCUTTER_ENABLE_DEPENDENCY_DOWNLOADS=ON
-DCUTTER_PACKAGE_RZ_GHIDRA=ON
-DCUTTER_PACKAGE_JSDEC=ON
-DCUTTER_PACKAGE_RZ_LIBSWIFT=ON
-DCUTTER_PACKAGE_RZ_LIBYARA=ON
-DCUTTER_PACKAGE_RZ_SILHOUETTE=ON
-DCPACK_PACKAGE_FILE_NAME="$$PACKAGE_NAME"
-DCPACK_BUNDLE_APPLE_CERT_APP="-"
- ninja -C build
package:
image: /bin/bash
commands:
- source cutter-deps/env.sh
- ninja -C build package
deploy:
when:
event: tag
tag: v*
image: /bin/bash
commands:
- gh release upload "${CI_COMMIT_TAG}" build/Cutter-*.dmg
secrets: [ github_token ]

View File

@ -14,8 +14,7 @@ option(CUTTER_USE_BUNDLED_RIZIN "Use rizin from ./rizin submodule instead of sea
option(CUTTER_USE_ADDITIONAL_RIZIN_PATHS "Search rizin in additional paths which are not part of default system library paths.\
Disable this option if you are linking against rizin pacakged as proper system library or in a custom path and additional are paths causing problems." ON)
option(CUTTER_ENABLE_PYTHON "Enable Python integration. Requires Python >= ${CUTTER_PYTHON_MIN}." OFF)
option(CUTTER_ENABLE_PYTHON_BINDINGS "Enable generating Python bindings with Shiboken2. Unused if CUTTER_ENABLE_PYTHON=OFF." OFF)
option(CUTTER_ENABLE_CRASH_REPORTS "Enable crash report system. Unused if CUTTER_ENABLE_CRASH_REPORTS=OFF" OFF)
option(CUTTER_ENABLE_PYTHON_BINDINGS "Enable generating Python bindings with Shiboken. Unused if CUTTER_ENABLE_PYTHON=OFF." OFF)
option(CUTTER_APPIMAGE_BUILD "Enable Appimage specific changes. Doesn't cause building of Appimage itself." OFF)
tri_option(CUTTER_ENABLE_KSYNTAXHIGHLIGHTING "Use KSyntaxHighlighting" AUTO)
tri_option(CUTTER_ENABLE_GRAPHVIZ "Enable use of graphviz for graph layout" AUTO)
@ -23,8 +22,12 @@ set(SHIBOKEN_EXTRA_OPTIONS "" CACHE STRING "Extra options for shiboken generator
set(CUTTER_EXTRA_PLUGIN_DIRS "" CACHE STRING "List of addition plugin locations")
option(CUTTER_ENABLE_DEPENDENCY_DOWNLOADS "Enable downloading of dependencies. Setting to OFF doesn't affect any downloads done by rizin build." OFF)
option(CUTTER_ENABLE_PACKAGING "Enable building platform-specific packages for distributing" OFF)
option(CUTTER_ENABLE_SIGDB "Downloads and installs sigdb (only available when CUTTER_USE_BUNDLED_RIZIN=ON)." OFF)
option(CUTTER_PACKAGE_DEPENDENCIES "During install step include the third party dependencies." OFF)
option(CUTTER_PACKAGE_RZ_GHIDRA "Compile and install rz-ghidra during install step." OFF)
option(CUTTER_PACKAGE_RZ_LIBSWIFT "Compile and install rz-libswift demangler during the install step." OFF)
option(CUTTER_PACKAGE_RZ_LIBYARA "Compile and install rz-libyara during the install step." OFF)
option(CUTTER_PACKAGE_RZ_SILHOUETTE "Compile and install rz-silhouette during the install step." OFF)
option(CUTTER_PACKAGE_JSDEC "Compile and install jsdec during install step." OFF)
OPTION(CUTTER_QT6 "Use QT6" OFF)
@ -33,12 +36,36 @@ if(NOT CUTTER_ENABLE_PYTHON)
endif()
set(CUTTER_VERSION_MAJOR 2)
set(CUTTER_VERSION_MINOR 0)
set(CUTTER_VERSION_PATCH 2)
set(CUTTER_VERSION_MINOR 2)
set(CUTTER_VERSION_PATCH 0)
set(CUTTER_VERSION_FULL "${CUTTER_VERSION_MAJOR}.${CUTTER_VERSION_MINOR}.${CUTTER_VERSION_PATCH}")
set(CUTTER_VERSION "${CUTTER_VERSION_MAJOR}.${CUTTER_VERSION_MINOR}.${CUTTER_VERSION_PATCH}")
project(Cutter VERSION "${CUTTER_VERSION_FULL}")
execute_process(COMMAND git log --pretty=format:'%h' -n 1
OUTPUT_VARIABLE GIT_REV
ERROR_QUIET)
# Check whether we got any revision (which isn't
# always the case, e.g. when someone downloaded a zip file
if ("${GIT_REV}" STREQUAL "")
set(CUTTER_VERSION_FULL "${CUTTER_VERSION}")
else()
execute_process(
COMMAND git rev-parse --abbrev-ref HEAD
OUTPUT_VARIABLE GIT_BRANCH)
string(STRIP "${GIT_REV}" GIT_REV)
string(SUBSTRING "${GIT_REV}" 1 7 GIT_REV)
string(STRIP "${GIT_BRANCH}" GIT_BRANCH)
set(CUTTER_VERSION_FULL "${CUTTER_VERSION}-${GIT_BRANCH}-${GIT_REV}")
endif()
project(Cutter VERSION "${CUTTER_VERSION}")
# Enable solution folder support
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
# Put Qt files in a separate folder
set_property(GLOBAL PROPERTY AUTOGEN_SOURCE_GROUP "Generated Files")
set(CMAKE_CXX_STANDARD 11)
@ -48,24 +75,22 @@ if(CUTTER_USE_BUNDLED_RIZIN)
include(BundledRizin)
set(RIZIN_TARGET Rizin)
else()
find_package(Rizin REQUIRED)
set(RIZIN_TARGET Rizin::librz)
find_package(Rizin COMPONENTS Core REQUIRED)
set(RIZIN_TARGET Rizin::Core)
endif()
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTORCC ON)
set(QT_COMPONENTS Core Widgets Gui Svg Network)
if (CUTTER_QT6)
set(QT_PREFIX Qt6)
list(APPEND QT_COMPONENTS Core5Compat SvgWidgets OpenGLWidgets)
else()
set(QT_PREFIX Qt5)
endif()
find_package(${QT_PREFIX} REQUIRED COMPONENTS Core Widgets Gui Svg Network)
if (CUTTER_QT6)
find_package(${QT_PREFIX} REQUIRED COMPONENTS Core5Compat SvgWidgets OpenGLWidgets)
endif()
find_package(${QT_PREFIX} REQUIRED COMPONENTS ${QT_COMPONENTS})
if(CUTTER_ENABLE_PYTHON)
find_package(PythonInterp REQUIRED)
@ -75,27 +100,47 @@ if(CUTTER_ENABLE_PYTHON)
add_definitions(-DCUTTER_ENABLE_PYTHON)
if(CUTTER_ENABLE_PYTHON_BINDINGS)
# 5.12.3 => 5.12
if("${Qt5_VERSION}" MATCHES "^([0-9]+\\.[0-9]+)\\.[0-9]+")
set(Shiboken2_VERSION_REQUIRED "${CMAKE_MATCH_1}")
if (CUTTER_QT6)
# 6.12.3 => 6.12
if("${Qt6_VERSION}" MATCHES "^([0-9]+\\.[0-9]+)\\.[0-9]+")
set(Shiboken6_VERSION_REQUIRED "${CMAKE_MATCH_1}")
else()
message(FATAL_ERROR "Failed to recognize Qt version")
endif()
find_package(Shiboken6 "${Shiboken6_VERSION_REQUIRED}" REQUIRED)
find_package(Shiboken6Tools "${Shiboken6_VERSION_REQUIRED}" REQUIRED)
find_package(PySide6 "${Shiboken6_VERSION_REQUIRED}" REQUIRED)
get_target_property(LIBSHIBOKEN_INCLUDE_DIRS Shiboken6::libshiboken INTERFACE_INCLUDE_DIRECTORIES)
get_target_property(PYSIDE_INCLUDE_DIRS PySide6::pyside6 INTERFACE_INCLUDE_DIRECTORIES)
# Check the presence of "pysidecleanup.h"
include(CheckIncludeFileCXX)
set(CMAKE_REQUIRED_INCLUDES "${PYSIDE_INCLUDE_DIRS};${LIBSHIBOKEN_INCLUDE_DIRS}")
CHECK_INCLUDE_FILE_CXX("pysidecleanup.h" HAVE_PYSIDECLEANUP)
add_compile_definitions("HAVE_PYSIDECLEANUP=${HAVE_PYSIDECLEANUP}")
else()
message(FATAL_ERROR "Failed to recognize Qt version")
# 5.12.3 => 5.12
if("${Qt5_VERSION}" MATCHES "^([0-9]+\\.[0-9]+)\\.[0-9]+")
set(Shiboken2_VERSION_REQUIRED "${CMAKE_MATCH_1}")
else()
message(FATAL_ERROR "Failed to recognize Qt version")
endif()
find_package(Shiboken2 "${Shiboken2_VERSION_REQUIRED}" REQUIRED)
find_package(PySide2 "${Shiboken2_VERSION_REQUIRED}" REQUIRED)
get_target_property(PYSIDE_INCLUDE_DIRS PySide2::pyside2 INTERFACE_INCLUDE_DIRECTORIES)
endif()
find_package(Shiboken2 "${Shiboken2_VERSION_REQUIRED}" REQUIRED)
find_package(PySide2 "${Shiboken2_VERSION_REQUIRED}" REQUIRED)
get_target_property(PYSIDE_INCLUDE_DIR PySide2::pyside2 INTERFACE_INCLUDE_DIRECTORIES)
list(GET PYSIDE_INCLUDE_DIR 0 PYSIDE_INCLUDE_DIR)
include_directories(${PYSIDE_INCLUDE_DIR}
${PYSIDE_INCLUDE_DIR}/QtCore
${PYSIDE_INCLUDE_DIR}/QtGui
${PYSIDE_INCLUDE_DIR}/QtWidgets)
foreach(_dir IN LISTS PYSIDE_INCLUDE_DIRS)
include_directories(${_dir}
${_dir}/QtCore
${_dir}/QtGui
${_dir}/QtWidgets)
endforeach()
add_definitions(-DCUTTER_ENABLE_PYTHON_BINDINGS)
endif()
endif()
if(CUTTER_ENABLE_KSYNTAXHIGHLIGHTING)
if(CUTTER_ENABLE_KSYNTAXHIGHLIGHTING AND (NOT CUTTER_QT6))
if(CUTTER_ENABLE_KSYNTAXHIGHLIGHTING STREQUAL AUTO)
find_package(KF5SyntaxHighlighting)
if(KF5SyntaxHighlighting_FOUND)
@ -108,6 +153,9 @@ if(CUTTER_ENABLE_KSYNTAXHIGHLIGHTING)
set(KSYNTAXHIGHLIGHTING_STATUS ON)
endif()
else()
if(CUTTER_ENABLE_KSYNTAXHIGHLIGHTING AND CUTTER_QT6)
message(WARNING "KSyntaxHighlighting has been disabled because not supported in QT6")
endif()
set(KSYNTAXHIGHLIGHTING_STATUS OFF)
endif()
@ -124,11 +172,22 @@ message(STATUS "")
message(STATUS "Building Cutter version ${CUTTER_VERSION_FULL}")
message(STATUS "Options:")
message(STATUS "- Bundled rizin: ${CUTTER_USE_BUNDLED_RIZIN}")
if(CUTTER_USE_BUNDLED_RIZIN)
message(STATUS "- Bundled sigdb: ${CUTTER_ENABLE_SIGDB}")
endif()
message(STATUS "- Python: ${CUTTER_ENABLE_PYTHON}")
message(STATUS "- Python Bindings: ${CUTTER_ENABLE_PYTHON_BINDINGS}")
message(STATUS "- Crash Handling: ${CUTTER_ENABLE_CRASH_REPORTS}")
message(STATUS "- KSyntaxHighlighting: ${KSYNTAXHIGHLIGHTING_STATUS}")
message(STATUS "- Graphviz: ${CUTTER_ENABLE_GRAPHVIZ}")
message(STATUS "- Downloads dependencies: ${CUTTER_ENABLE_DEPENDENCY_DOWNLOADS}")
message(STATUS "- Enable Packaging: ${CUTTER_ENABLE_PACKAGING}")
message(STATUS "- Package Dependencies: ${CUTTER_PACKAGE_DEPENDENCIES}")
message(STATUS "- Package RzGhidra: ${CUTTER_PACKAGE_RZ_GHIDRA}")
message(STATUS "- Package RzLibSwift: ${CUTTER_PACKAGE_RZ_LIBSWIFT}")
message(STATUS "- Package RzLibYara: ${CUTTER_PACKAGE_RZ_LIBYARA}")
message(STATUS "- Package RzSilhouette: ${CUTTER_PACKAGE_RZ_SILHOUETTE}")
message(STATUS "- Package JSDec: ${CUTTER_PACKAGE_JSDEC}")
message(STATUS "- QT6: ${CUTTER_QT6}")
message(STATUS "")
add_subdirectory(src)

View File

@ -16,7 +16,7 @@ Please follow our contribution guidelines: https://cutter.re/docs/contributing.h
## Contributing to the documentation
The documentation is something important for newcomers. As of today the documentation can be found [here](https://cutter.re/docs/) and it stands in the [docs](https://github.com/rizinorg/cutter/tree/master/docs) folder.
The documentation is something important for newcomers. As of today the documentation can be found [here](https://cutter.re/docs/) and it stands in the [docs](docs) folder.
The API Reference is automatically generated from the source code, so it is strongly advised to document your code.
Check issues marked as "Documentation" on our issues [list](https://github.com/rizinorg/cutter/issues?q=is%3Aissue+is%3Aopen+label%3ADocumentation).

View File

@ -1,14 +1,13 @@
<img width="150" height="150" align="left" style="float: left; margin: 0 10px 0 0;" alt="Cutter logo" src="https://raw.githubusercontent.com/rizinorg/cutter/master/src/img/cutter.svg?sanitize=true">
<img width="150" height="150" align="left" style="float: left; margin: 0 10px 0 0;" alt="Cutter logo" src="https://raw.githubusercontent.com/rizinorg/cutter/dev/src/img/cutter.svg?sanitize=true">
# Cutter
Cutter is a free and open-source reverse engineering platform powered by [rizin](https://github.com/rizinorg/rizin). It aims at being an advanced and customizable reverse engineering platform while keeping the user experience in mind. Cutter is created by reverse engineers for reverse engineers.
[![Cutter CI](https://github.com/rizinorg/cutter/workflows/Cutter%20CI/badge.svg)](https://github.com/rizinorg/cutter/actions?query=workflow%3A%22Cutter+CI%22)
[![Build status](https://ci.appveyor.com/api/projects/status/tn7kttv55b8wf799/branch/master?svg=true)](https://ci.appveyor.com/project/rizinorg/cutter/branch/master)
[![Total alerts](https://img.shields.io/lgtm/alerts/g/rizinorg/cutter.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/rizinorg/cutter/alerts/)
[![Build status](https://ci.appveyor.com/api/projects/status/tn7kttv55b8wf799/branch/dev?svg=true)](https://ci.appveyor.com/project/rizinorg/cutter/branch/dev)
![Screenshot](https://raw.githubusercontent.com/rizinorg/cutter/master/docs/source/images/screenshot.png)
![Screenshot](https://raw.githubusercontent.com/rizinorg/cutter/dev/docs/source/images/screenshot.png)
## Learn more at [cutter.re](https://cutter.re).
@ -17,15 +16,17 @@ Cutter is a free and open-source reverse engineering platform powered by [rizin]
Cutter release binaries for all major platforms (Linux, macOS, Windows) can be downloaded from [GitHub Releases](https://github.com/rizinorg/cutter/releases).
- **Linux**: Download the `.AppImage` file. Then make it executable and run as below or use [AppImageLauncher](https://github.com/TheAssassin/AppImageLauncher).
- **Linux**: If your distribution provides it, check for `cutter` package in your package manager (or `cutter-re`). If not available there, we have setup repositories in [OBS](https://openbuildservice.org/) for some common distributions. Look at [https://software.opensuse.org/package/cutter-re](https://software.opensuse.org/download/package?package=cutter-re&project=home%3ARizinOrg) and follow the instructions there. Otherwise download the `.AppImage` file from our release, make it executable and run as below or use [AppImageLauncher](https://github.com/TheAssassin/AppImageLauncher).
`chmod +x Cutter*.AppImage; ./Cutter*.AppImage`
- **macOS**: Download the `.dmg` file or use [Homebrew Cask](https://github.com/Homebrew/homebrew-cask):
`brew install --cask cutter`
- **Windows**: Download the `.zip` archive or use [Chocolatey](https://chocolatey.org):
- **Windows**: Download the `.zip` archive, or use either [Chocolatey](https://chocolatey.org) or [Scoop](https://scoop.sh/):
`choco install cutter`
`scoop bucket add extras` followed by `scoop install cutter`
### Build from sources

View File

@ -66,6 +66,7 @@ ForEachMacros:
- Q_FOREVER
- QBENCHMARK
- QBENCHMARK_ONCE
- CutterRzListForeach
IncludeBlocks: Preserve
IncludeCategories:
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'

View File

@ -8,7 +8,7 @@ if(WIN32)
set(RIZIN_INSTALL_DIR "${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>")
endif()
set(RIZIN_INSTALL_BINPATH ".")
set(MESON_OPTIONS "--prefix=${RIZIN_INSTALL_DIR}" "--bindir=${RIZIN_INSTALL_BINPATH}" "-DRZ_INCDIR=include/librz")
set(MESON_OPTIONS "--prefix=${RIZIN_INSTALL_DIR}" "--bindir=${RIZIN_INSTALL_BINPATH}")
else()
set(RIZIN_INSTALL_DIR "${CMAKE_CURRENT_BINARY_DIR}/Rizin-prefix")
set(MESON_OPTIONS "--prefix=${RIZIN_INSTALL_DIR}" --libdir=lib)
@ -18,6 +18,14 @@ if (CUTTER_ENABLE_PACKAGING)
list(APPEND MESON_OPTIONS "-Dportable=true")
endif()
if (CUTTER_ENABLE_SIGDB)
list(APPEND MESON_OPTIONS "-Dinstall_sigdb=true")
endif()
if (CUTTER_PACKAGE_RZ_LIBSWIFT AND CUTTER_ENABLE_DEPENDENCY_DOWNLOADS)
list(APPEND MESON_OPTIONS "-Duse_swift_demangler=false")
endif()
find_program(MESON meson)
if(NOT MESON)
message(FATAL_ERROR "Failed to find meson, which is required to build bundled rizin")
@ -32,6 +40,7 @@ ExternalProject_Add(Rizin-Bundled
SOURCE_DIR "${RIZIN_SOURCE_DIR}"
CONFIGURE_COMMAND "${MESON}" "<SOURCE_DIR>" ${MESON_OPTIONS} && "${MESON}" configure ${MESON_OPTIONS} --buildtype "$<$<CONFIG:Debug>:debug>$<$<NOT:$<CONFIG:Debug>>:release>"
BUILD_COMMAND "${NINJA}"
BUILD_ALWAYS TRUE
INSTALL_COMMAND "${NINJA}" install)
set(Rizin_INCLUDE_DIRS "${RIZIN_INSTALL_DIR}/include/librz" "${RIZIN_INSTALL_DIR}/include/librz/sdb")
@ -46,25 +55,31 @@ else()
link_directories("${RIZIN_INSTALL_DIR}/lib")
endif()
# TODO: This version number should be fetched automatically
# instead of being hardcoded.
set (Rizin_VERSION 0.6)
set (RZ_LIBS rz_core rz_config rz_cons rz_io rz_util rz_flag rz_asm rz_debug
rz_hash rz_bin rz_lang rz_io rz_analysis rz_parse rz_bp rz_egg rz_reg
rz_search rz_syscall rz_socket rz_magic rz_crypto rz_type rz_diff)
rz_hash rz_bin rz_lang rz_il rz_analysis rz_parse rz_bp rz_egg rz_reg
rz_search rz_syscall rz_socket rz_magic rz_crypto rz_type rz_diff rz_sign
rz_demangler)
set (RZ_EXTRA_LIBS rz_main)
set (RZ_BIN rz-agent rz-bin rizin rz-diff rz-find rz-gg rz-hash rz-run rz-asm rz-ax)
set (RZ_BIN rz-bin rizin rz-diff rz-find rz-gg rz-hash rz-run rz-asm rz-ax)
target_link_libraries(Rizin INTERFACE
${RZ_LIBS})
target_include_directories(Rizin INTERFACE
"$<BUILD_INTERFACE:${Rizin_INCLUDE_DIRS}>"
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/librz>")
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/librz>"
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/librz/sdb>")
install(TARGETS Rizin EXPORT CutterTargets)
if (WIN32)
foreach(_lib ${RZ_LIBS} ${RZ_EXTRA_LIBS})
install(FILES "${RIZIN_INSTALL_DIR}/${RZ_INSTALL_BINPATH}/${_lib}.dll" DESTINATION "${CMAKE_INSTALL_BINDIR}")
foreach(_lib ${RZ_LIBS} ${RZ_EXTRA_LIBS})
install(FILES "${RIZIN_INSTALL_DIR}/${_lib}-${Rizin_VERSION}.dll" DESTINATION "${CMAKE_INSTALL_BINDIR}")
endforeach()
foreach(_exe ${RZ_BIN})
install(FILES "${RIZIN_INSTALL_DIR}/${RZ_INSTALL_BINPATH}/${_exe}.exe" DESTINATION "${CMAKE_INSTALL_BINDIR}")
install(FILES "${RIZIN_INSTALL_DIR}/${_exe}.exe" DESTINATION "${CMAKE_INSTALL_BINDIR}")
endforeach()
install(DIRECTORY "${RIZIN_INSTALL_DIR}/share" DESTINATION ".")
install(DIRECTORY "${RIZIN_INSTALL_DIR}/include" DESTINATION "."

View File

@ -1 +0,0 @@
include("${CMAKE_CURRENT_LIST_DIR}/CutterTargets.cmake")

View File

@ -0,0 +1,22 @@
@PACKAGE_INIT@
set(Cutter_RIZIN_BUNDLED @CUTTER_USE_BUNDLED_RIZIN@)
include(CMakeFindDependencyMacro)
find_dependency(@QT_PREFIX@ COMPONENTS @QT_COMPONENTS@)
find_dependency(Rizin COMPONENTS Core)
# Make a best guess for a user location from where plugins can be loaded.
# This can be used in Cutter plugins like
# set(CUTTER_INSTALL_PLUGDIR "${Cutter_USER_PLUGINDIR}" CACHE STRING "Directory to install Cutter plugin into")
# see https://doc.qt.io/qt-5/qstandardpaths.html under AppDataLocation
if(APPLE)
set(Cutter_USER_PLUGINDIR "$ENV{HOME}/Library/Application Support/rizin/cutter/plugins/native")
elseif(WIN32)
file(TO_CMAKE_PATH "$ENV{APPDATA}" Cutter_USER_PLUGINDIR)
set(Cutter_USER_PLUGINDIR "${Cutter_USER_PLUGINDIR}/rizin/cutter/plugins/native")
else()
set(Cutter_USER_PLUGINDIR "$ENV{HOME}/.local/share/rizin/cutter/plugins/native")
endif()
include("${CMAKE_CURRENT_LIST_DIR}/CutterTargets.cmake")

View File

@ -20,4 +20,4 @@ else()
include(GNUInstallDirs)
set(CUTTER_INSTALL_DATADIR "${CMAKE_INSTALL_DATAROOTDIR}/${CUTTER_DIR_NAME}" CACHE PATH "Resource installation directory")
endif()
set(ConfigPackageLocation "${CMAKE_INSTALL_LIBDIR}/Cutter" CACHE PATH "Cmake file install location")
set(CUTTER_INSTALL_CONFIGDIR "${CMAKE_INSTALL_LIBDIR}/cmake/Cutter" CACHE PATH "CMake file install location")

View File

@ -1,65 +0,0 @@
# - Find Breakpad
#
# Breakpad_FOUND - True if Breakpad has been found.
# Breakpad_INCLUDE_DIRS - Breakpad include directory
# Breakpad_LIBRARIES - List of libraries when using Breakpad.
set(Breakpad_LIBRARIES_VARS "")
if(WIN32)
find_path(Breakpad_INCLUDE_DIRS
client/windows/handler/exception_handler.h
HINTS
"${CMAKE_CURRENT_SOURCE_DIR}/Breakpad/src/src")
set(Breakpad_LIBRARY_NAMES
exception_handler
crash_generation_client
common
)
set(Breakpad_LIBRARIES "")
foreach(libname ${Breakpad_LIBRARY_NAMES})
find_library(Breakpad_LIBRARY_${libname}
${libname}
HINTS
"${CMAKE_CURRENT_SOURCE_DIR}/Breakpad/src/src/client/windows/Release/lib"
REQUIRED)
list(APPEND Breakpad_LIBRARIES ${Breakpad_LIBRARY_${libname}})
list(APPEND Breakpad_LIBRARIES_VARS "Breakpad_LIBRARY_${libname}")
endforeach()
set (Breakpad_LINK_LIBRARIES ${Breakpad_LIBRARIES})
set(Breakpad_LIBRARY_DIRS "")
elseif(APPLE)
find_library(Breakpad_LINK_LIBRARIES Breakpad REQUIRED)
set(Breakpad_LIBRARIES ${Breakpad_LINK_LIBRARIES})
# Assumes Breakpad is packed as Framework
set(Breakpad_INCLUDE_DIRS "${Breakpad_LINK_LIBRARIES}/Headers")
else()
set(Breakpad_CMAKE_PREFIX_PATH_TEMP ${CMAKE_PREFIX_PATH})
list(APPEND CMAKE_PREFIX_PATH "${CMAKE_CURRENT_SOURCE_DIR}/Breakpad/prefix")
find_package(PkgConfig REQUIRED)
pkg_search_module(Breakpad REQUIRED breakpad-client)
# reset CMAKE_PREFIX_PATH
set(CMAKE_PREFIX_PATH ${Breakpad_CMAKE_PREFIX_PATH_TEMP})
mark_as_advanced(Breakpad_CMAKE_PREFIX_PATH_TEMP)
endif()
# could be simplified in > cmake 3.11 using pkg_search_module IMPORTED_TARGET [GLOBAL] but this would still be required for windows
add_library(Breakpad::client INTERFACE IMPORTED)
set_target_properties(Breakpad::client PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${Breakpad_INCLUDE_DIRS}")
set_target_properties(Breakpad::client PROPERTIES
INTERFACE_LINK_LIBRARIES "${Breakpad_LINK_LIBRARIES}")
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Breakpad REQUIRED_VARS Breakpad_LIBRARIES Breakpad_INCLUDE_DIRS ${Breakpad_LIBRARIES_VARS})
mark_as_advanced(Breakpad_LIBRARIES_VARS)

View File

@ -65,4 +65,4 @@ else()
INTERFACE_LINK_LIBRARIES "${PYSIDE_LIBRARY}")
endif()
mark_as_advanced(PYSIDE_INCLUDE_DIR PYSIDE_LIBRARY PYSIDE_BINARY)
mark_as_advanced(PYSIDE_INCLUDE_DIR PYSIDE_LIBRARY PYSIDE_BINARY)

68
cmake/FindPySide6.cmake Normal file
View File

@ -0,0 +1,68 @@
set(_module PySide6)
find_package(${_module} ${${_module}_FIND_VERSION} CONFIG QUIET)
set(_lib_target ${_module}::pyside6)
if(NOT ${_module}_FOUND)
include(PythonInfo)
find_python_site_packages(PYTHON_SITE_PACKAGES)
get_python_extension_suffix(PYTHON_EXTENSION_SUFFIX)
find_library(PYSIDE_LIBRARY
NAMES
"pyside6${PYTHON_EXTENSION_SUFFIX}"
"pyside6${PYTHON_EXTENSION_SUFFIX}.${${_module}_FIND_VERSION_MAJOR}.${${_module}_FIND_VERSION_MINOR}"
PATH_SUFFIXES "${PYTHON_SITE_PACKAGES}/PySide6")
find_path(PYSIDE_INCLUDE_DIR
pyside.h
PATH_SUFFIXES "${PYTHON_SITE_PACKAGES}/PySide6/include")
find_path(PYSIDE_TYPESYSTEMS
typesystem_core.xml
PATH_SUFFIXES "${PYTHON_SITE_PACKAGES}/PySide6/typesystems")
endif()
if(TARGET ${_lib_target})
get_target_property(_is_imported ${_lib_target} IMPORTED)
if(_is_imported)
get_target_property(_imported_location ${_lib_target} IMPORTED_LOCATION)
if(NOT _imported_location)
message(STATUS "Target ${_lib_target} does not specify its IMPORTED_LOCATION! Trying to find it ourselves...")
set(_find_args)
if(${_module}_CONFIG)
get_filename_component(_pyside6_lib_dir "${${_module}_CONFIG}/../../../" ABSOLUTE)
set(_find_args PATHS "${_pyside6_lib_dir}")
endif()
find_library(PYSIDE_LIBRARY
NAMES
"pyside6${PYTHON_CONFIG_SUFFIX}"
"pyside6${PYTHON_CONFIG_SUFFIX}.${${_module}_FIND_VERSION_MAJOR}.${${_module}_FIND_VERSION_MINOR}"
${_find_args})
if(NOT PYSIDE_LIBRARY)
set(_message_type WARNING)
if(${_module}_FIND_REQUIRED)
set(_message_type FATAL_ERROR)
endif()
message(${_message_type} "Failed to manually find library for ${_module}")
return()
endif()
message(STATUS "IMPORTED_LOCATION for ${_lib_target} found: ${PYSIDE_LIBRARY}")
set_target_properties(${_lib_target} PROPERTIES IMPORTED_LOCATION "${PYSIDE_LIBRARY}")
endif()
endif()
else()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(${_module}
FOUND_VAR ${_module}_FOUND
REQUIRED_VARS PYSIDE_LIBRARY PYSIDE_INCLUDE_DIR PYSIDE_TYPESYSTEMS
VERSION_VAR ${_module}_VERSION)
add_library(${_module}::pyside6 INTERFACE IMPORTED)
set_target_properties(${_module}::pyside6 PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${PYSIDE_INCLUDE_DIR}"
INTERFACE_LINK_LIBRARIES "${PYSIDE_LIBRARY}")
endif()
mark_as_advanced(PYSIDE_INCLUDE_DIR PYSIDE_LIBRARY PYSIDE_BINARY)

View File

@ -1,117 +0,0 @@
# - Find Rizin (librz)
#
# This module provides the following imported targets, if found:
#
# Rizin::librz
#
# This will define the following variables:
# (but don't use them if you don't know what you are doing, use Rizin::librz)
#
# Rizin_FOUND - True if librz has been found.
# Rizin_INCLUDE_DIRS - librz include directory
# Rizin_LIBRARIES - List of libraries when using librz.
# Rizin_LIBRARY_DIRS - librz library directories
#
# If librz was found using find_library and not pkg-config, the following variables will also be set:
# Rizin_LIBRARY_<name> - Path to library rz_<name>
if(WIN32)
find_path(Rizin_INCLUDE_DIRS
NAMES rz_core.h rz_bin.h rz_util.h
HINTS
"$ENV{HOME}/bin/prefix/rizin/include/librz"
/usr/local/include/libr
/usr/include/librz)
find_path(SDB_INCLUDE_DIR
NAMES sdb.h sdbht.h sdb_version.h
HINTS
"$ENV{HOME}/bin/prefix/rizin/include/librz/sdb"
/usr/local/include/librz/sdb
/usr/include/librz/sdb)
list(APPEND Rizin_INCLUDE_DIRS ${SDB_INCLUDE_DIR})
set(Rizin_LIBRARY_NAMES
core
config
cons
io
util
flag
asm
debug
hash
bin
lang
io
analysis
parse
bp
egg
reg
search
syscall
socket
magic
crypto
type)
set(Rizin_LIBRARIES "")
set(Rizin_LIBRARIES_VARS "")
foreach(libname ${Rizin_LIBRARY_NAMES})
find_library(Rizin_LIBRARY_${libname}
rz_${libname}
HINTS
"$ENV{HOME}/bin/prefix/rizin/lib"
/usr/local/lib
/usr/lib)
list(APPEND Rizin_LIBRARIES ${Rizin_LIBRARY_${libname}})
list(APPEND Rizin_LIBRARIES_VARS "Rizin_LIBRARY_${libname}")
endforeach()
set(Rizin_LIBRARY_DIRS "")
add_library(Rizin::librz UNKNOWN IMPORTED)
set_target_properties(Rizin::librz PROPERTIES
IMPORTED_LOCATION "${Rizin_LIBRARY_core}"
IMPORTED_LINK_INTERFACE_LIBRARIES "${Rizin_LIBRARIES}"
INTERFACE_LINK_DIRECTORIES "${Rizin_LIBRARY_DIRS}"
INTERFACE_INCLUDE_DIRECTORIES "${Rizin_INCLUDE_DIRS}")
set(Rizin_TARGET Rizin::librz)
else()
# support installation locations used by rizin scripts like sys/user.sh and sys/install.sh
if(CUTTER_USE_ADDITIONAL_RIZIN_PATHS)
set(Rizin_CMAKE_PREFIX_PATH_TEMP ${CMAKE_PREFIX_PATH})
list(APPEND CMAKE_PREFIX_PATH "$ENV{HOME}/bin/prefix/rizin") # sys/user.sh
list(APPEND CMAKE_PREFIX_PATH "/usr/local") # sys/install.sh
endif()
find_package(PkgConfig REQUIRED)
if(CMAKE_VERSION VERSION_LESS "3.6")
pkg_search_module(Rizin REQUIRED rz_core)
else()
pkg_search_module(Rizin IMPORTED_TARGET REQUIRED rz_core)
endif()
# reset CMAKE_PREFIX_PATH
if(CUTTER_USE_ADDITIONAL_RIZIN_PATHS)
set(CMAKE_PREFIX_PATH ${Rizin_CMAKE_PREFIX_PATH_TEMP})
endif()
if((TARGET PkgConfig::Rizin) AND (NOT CMAKE_VERSION VERSION_LESS "3.11.0"))
set_target_properties(PkgConfig::Rizin PROPERTIES IMPORTED_GLOBAL ON)
add_library(Rizin::librz ALIAS PkgConfig::Rizin)
set(Rizin_TARGET Rizin::librz)
elseif(Rizin_FOUND)
add_library(Rizin::librz INTERFACE IMPORTED)
set_target_properties(Rizin::librz PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${Rizin_INCLUDE_DIRS}")
set_target_properties(Rizin::librz PROPERTIES
INTERFACE_LINK_LIBRARIES "${Rizin_LIBRARIES}")
set(Rizin_TARGET Rizin::librz)
endif()
endif()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Rizin REQUIRED_VARS Rizin_TARGET Rizin_LIBRARIES Rizin_INCLUDE_DIRS)

120
dist/CMakeLists.txt vendored
View File

@ -7,7 +7,7 @@ unset(RZ_GHIDRA_PREFIX_PATH)
if(WIN32)
set(CPACK_GENERATOR "ZIP")
set(RIZIN_INSTALL_PLUGDIR "lib/plugins")
set(RIZIN_INSTALL_PLUGDIR "lib/rizin/plugins")
if (CUTTER_PACKAGE_DEPENDENCIES)
if (CUTTER_ENABLE_PYTHON)
@ -28,7 +28,46 @@ if(WIN32)
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
RESULT_VARIABLE SCRIPT_RESULT)
if (SCRIPT_RESULT)
message(FATAL_ERROR \"Failed to package jsdec\")
message(FATAL_ERROR \"Failed to package jsdec (returned \${SCRIPT_RESULT})\")
endif()
")
endif()
if (CUTTER_PACKAGE_RZ_LIBSWIFT AND CUTTER_ENABLE_DEPENDENCY_DOWNLOADS)
install(CODE "
set(ENV{RZ_PREFIX} \"\${CMAKE_INSTALL_PREFIX}\")
set(ENV{PATH} \"\${CMAKE_INSTALL_PREFIX};\$ENV{PATH}\")
execute_process(COMMAND powershell \"${CMAKE_CURRENT_SOURCE_DIR}/bundle_rz_libswift.ps1\" \"\${CMAKE_INSTALL_PREFIX}\"
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
RESULT_VARIABLE SCRIPT_RESULT)
if (SCRIPT_RESULT)
message(FATAL_ERROR \"Failed to package rz-libswift (returned \${SCRIPT_RESULT})\")
endif()
")
endif()
if (CUTTER_PACKAGE_RZ_LIBYARA AND CUTTER_ENABLE_DEPENDENCY_DOWNLOADS)
install(CODE "
set(ENV{RZ_PREFIX} \"\${CMAKE_INSTALL_PREFIX}\")
set(ENV{PATH} \"\${CMAKE_INSTALL_PREFIX};\$ENV{PATH}\")
execute_process(COMMAND powershell
\"${CMAKE_CURRENT_SOURCE_DIR}/bundle_rz_libyara.ps1\"
\"\${CMAKE_INSTALL_PREFIX}\"
\"-DCUTTER_INSTALL_PLUGDIR=plugins/native\"
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
RESULT_VARIABLE SCRIPT_RESULT)
if (SCRIPT_RESULT)
message(FATAL_ERROR \"Failed to package rz_libyara (returned \${SCRIPT_RESULT})\")
endif()
")
endif()
if (CUTTER_PACKAGE_RZ_SILHOUETTE AND CUTTER_ENABLE_DEPENDENCY_DOWNLOADS)
install(CODE "
set(ENV{RZ_PREFIX} \"\${CMAKE_INSTALL_PREFIX}\")
set(ENV{PATH} \"\${CMAKE_INSTALL_PREFIX};\$ENV{PATH}\")
execute_process(COMMAND powershell \"${CMAKE_CURRENT_SOURCE_DIR}/bundle_rz_silhouette.ps1\" \"\${CMAKE_INSTALL_PREFIX}\"
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
RESULT_VARIABLE SCRIPT_RESULT)
if (SCRIPT_RESULT)
message(FATAL_ERROR \"Failed to package rz-silhouette (returned \${SCRIPT_RESULT})\")
endif()
")
endif()
@ -50,10 +89,6 @@ if(APPLE)
set(CPACK_DMG_VOLUME_NAME "Cutter")
set(CPACK_BUNDLE_APPLE_ENTITLEMENTS "${CMAKE_CURRENT_SOURCE_DIR}/macos/Entitlements.plist")
set(CPACK_APPLE_BUNDLE_ID "re.rizin.cutter")
if (CUTTER_ENABLE_CRASH_REPORTS)
list(APPEND CPACK_BUNDLE_APPLE_CODESIGN_FILES "/Contents/Frameworks/Breakpad.framework/Versions/Current/Resources/breakpadUtilities.dylib")
endif()
find_program(MACDEPLOYQT_PATH macdeployqt HINTS "${Qt5_DIR}/../../../bin")
if(NOT MACDEPLOYQT_PATH)
@ -80,21 +115,65 @@ if(APPLE)
get_filename_component(QT_PREFIX "${MACDEPLOYQT_PATH}/../.." ABSOLUTE)
list(APPEND RZ_GHIDRA_PREFIX_PATH "${QT_PREFIX}")
set(RIZIN_INSTALL_PLUGDIR "share/rizin/plugins") # escaped backslash on purpose, should be evaluated later
set(RIZIN_INSTALL_PLUGDIR "lib/rizin/plugins") # escaped backslash on purpose, should be evaluated later
endif()
if (CUTTER_PACKAGE_JSDEC AND CUTTER_ENABLE_DEPENDENCY_DOWNLOADS)
################################################
# macOS + Linux
################################################
if(CUTTER_ENABLE_DEPENDENCY_DOWNLOADS AND (NOT WIN32))
if (CUTTER_PACKAGE_JSDEC)
install(CODE "
execute_process(COMMAND \"${CMAKE_CURRENT_SOURCE_DIR}/../scripts/jsdec.sh\" --prefix=\${CMAKE_INSTALL_PREFIX}
\"-Drizin_incdir=\${CMAKE_INSTALL_PREFIX}/include/librz\"
\"-Drizin_libdir=\${CMAKE_INSTALL_PREFIX}/lib\"
execute_process(COMMAND \"${CMAKE_CURRENT_SOURCE_DIR}/../scripts/jsdec.sh\"
--pkg-config-path=\${CMAKE_INSTALL_PREFIX}/lib/pkgconfig --prefix=\${CMAKE_INSTALL_PREFIX}
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
RESULT_VARIABLE SCRIPT_RESULT)
if (SCRIPT_RESULT)
message(FATAL_ERROR \"Failed to package jsdec\")
message(FATAL_ERROR \"Failed to package jsdec (returned \${SCRIPT_RESULT})\")
endif()
")
endif()
if (CUTTER_PACKAGE_RZ_LIBSWIFT)
install(CODE "
execute_process(COMMAND \"${CMAKE_CURRENT_SOURCE_DIR}/../scripts/rz-libswift.sh\"
--pkg-config-path=\${CMAKE_INSTALL_PREFIX}/lib/pkgconfig --prefix=\${CMAKE_INSTALL_PREFIX}
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
RESULT_VARIABLE SCRIPT_RESULT)
if (SCRIPT_RESULT)
message(FATAL_ERROR \"Failed to package rz-libswift (returned \${SCRIPT_RESULT})\")
endif()
")
endif()
if (CUTTER_PACKAGE_RZ_LIBYARA)
if(APPLE)
set (YARA_PLUGIN_OPTIONS "-DCUTTER_INSTALL_PLUGDIR=plugins/native")
else()
set (YARA_PLUGIN_OPTIONS "")
endif()
install(CODE "
execute_process(COMMAND
\"${CMAKE_CURRENT_SOURCE_DIR}/../scripts/rz-libyara.sh\"
\"\${CMAKE_INSTALL_PREFIX}\" \"${YARA_PLUGIN_OPTIONS}\"
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
RESULT_VARIABLE SCRIPT_RESULT)
if (SCRIPT_RESULT)
message(FATAL_ERROR \"Failed to package rz-libyara (returned \${SCRIPT_RESULT})\")
endif()
")
endif()
if (CUTTER_PACKAGE_RZ_SILHOUETTE)
install(CODE "
execute_process(COMMAND
\"${CMAKE_CURRENT_SOURCE_DIR}/../scripts/rz-silhouette.sh\"
\"\${CMAKE_INSTALL_PREFIX}\" \"${YARA_PLUGIN_OPTIONS}\"
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
RESULT_VARIABLE SCRIPT_RESULT)
if (SCRIPT_RESULT)
message(FATAL_ERROR \"Failed to package rz-silhouette (returned \${SCRIPT_RESULT})\")
endif()
")
endif()
endif()
################################################
@ -109,7 +188,8 @@ if(CUTTER_PACKAGE_RZ_GHIDRA)
# installed Cutter.
ExternalProject_Add(rz-ghidra
GIT_REPOSITORY https://github.com/rizinorg/rz-ghidra
#GIT_TAG v0.2.0
#GIT_TAG v0.3.0
#GIT_TAG c7a50a2e7c0a95cd52b167c9ee0fa1805223f08e
GIT_TAG dev
#GIT_SHALLOW ON # disable this line when using commit hash
CONFIGURE_COMMAND ""
@ -117,16 +197,12 @@ if(CUTTER_PACKAGE_RZ_GHIDRA)
INSTALL_COMMAND ""
)
endif()
if (WIN32)
set (GHIDRA_OPTIONS "
\"-DRIZIN_INSTALL_PLUGDIR=\${CMAKE_INSTALL_PREFIX}/${RIZIN_INSTALL_PLUGDIR}\"
-DCUTTER_INSTALL_PLUGDIR=plugins/native")
elseif (APPLE)
if (WIN32 OR APPLE)
set (GHIDRA_OPTIONS "
\"-DRIZIN_INSTALL_PLUGDIR=\${CMAKE_INSTALL_PREFIX}/${RIZIN_INSTALL_PLUGDIR}\"
-DCUTTER_INSTALL_PLUGDIR=plugins/native")
else()
set (GHIDRA_OPTIONS "")
set (GHIDRA_OPTIONS "-DCUTTER_INSTALL_PLUGDIR=\${CMAKE_INSTALL_PREFIX}/share/rizin/cutter/plugins/native")
endif()
install(CODE "
execute_process(
@ -143,14 +219,14 @@ if(CUTTER_PACKAGE_RZ_GHIDRA)
-G Ninja
)
if (SCRIPT_RESULT)
message(FATAL_ERROR \"Failed to configure rz-ghidra\")
message(FATAL_ERROR \"Failed to configure rz-ghidra (returned \${SCRIPT_RESULT})\")
endif()
execute_process(WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/rz-ghidra-prefix/src/rz-ghidra-build
RESULT_VARIABLE SCRIPT_RESULT
COMMAND \${CMAKE_COMMAND} --build . --target install
)
if (SCRIPT_RESULT)
message(FATAL_ERROR \"Failed to install rz-ghidra plugin\")
message(FATAL_ERROR \"Failed to install rz-ghidra plugin (returned \${SCRIPT_RESULT})\")
endif()
")
endif()

View File

@ -3,8 +3,6 @@ include(BundleUtilities)
set(MACDEPLOYQT_PATH "@MACDEPLOYQT_PATH@")
set(INFO_PLIST_PATH "@CPACK_BUNDLE_PLIST@")
set(ADJUST_RIZIN_LIBS "@ADJUST_RIZIN_LIBS@")
set(CUTTER_ENABLE_CRASH_REPORTS "@CUTTER_ENABLE_CRASH_REPORTS@")
set(Breakpad_LINK_LIBRARIES "@Breakpad_LINK_LIBRARIES@")
set(CUTTER_PACKAGE_DEPENDENCIES "@CUTTER_PACKAGE_DEPENDENCIES@")
set(CUTTER_ENABLE_PYTHON "@CUTTER_ENABLE_PYTHON@")
@ -25,7 +23,6 @@ file(COPY "${INFO_PLIST_PATH}" DESTINATION "${BUNDLE_PATH}/Contents")
# replace absolute path from build directory in rizin pkgconfig files with relative ones
file(GLOB RZ_PCFILES "${CMAKE_INSTALL_PREFIX}/lib/pkgconfig/rz_*.pc")
list(APPEND RZ_PCFILES "${CMAKE_INSTALL_PREFIX}/lib/pkgconfig/librz.pc")
foreach (_pcfile ${RZ_PCFILES})
file(READ "${_pcfile}" _text)
string(REGEX REPLACE "^prefix=[^\n]*\n" "prefix=\${pcfiledir}/../..\n" _text "${_text}")
@ -61,6 +58,9 @@ run_or_die(COMMAND install_name_tool
run_or_die(COMMAND install_name_tool
-add_rpath "@executable_path/../Resources/lib"
"${EXECUTABLE_DIR}/cutter")
run_or_die(COMMAND install_name_tool
-add_rpath "@executable_path/../Resources/lib/rizin/plugins"
"${EXECUTABLE_DIR}/cutter")
set(MACDEPLOYQT_COMMAND "${MACDEPLOYQT_PATH}" "${BUNDLE_PATH}" "-verbose=2" "-libpath=${CMAKE_INSTALL_PREFIX}/lib")
message("Running macdeployqt \"${MACDEPLOYQT_COMMAND}\"")
@ -80,9 +80,3 @@ foreach(_lib ${ADJUST_RIZIN_LIBS})
get_filename_component(_name "${_lib}" NAME)
file(REMOVE "${BUNDLE_PATH}/Contents/Frameworks/${_name}")
endforeach()
if (CUTTER_ENABLE_CRASH_REPORTS)
message("Copying Breakpad ${Breakpad_LINK_LIBRARIES}")
set(_breakpad_lib "Versions/A/Breakpad")
copy_resolved_framework_into_bundle("${Breakpad_LINK_LIBRARIES}/${_breakpad_lib}" "${FRAMEWORK_DIR}/Breakpad.framework/${_breakpad_lib}")
endif()

13
dist/bundle_jsdec.ps1 vendored
View File

@ -2,9 +2,16 @@ $dist = $args[0]
$python = Split-Path((Get-Command python.exe).Path)
if (-not (Test-Path -Path 'jsdec' -PathType Container)) {
git clone https://github.com/rizinorg/jsdec.git --branch v0.2.x --depth 1
git clone https://github.com/rizinorg/jsdec.git --depth 1 --branch master
}
cd jsdec
& meson.exe --buildtype=release -Dc_args=-DDUK_USE_DATE_NOW_WINDOWS --prefix=$dist --libdir=lib\plugins --datadir=lib\plugins p build
& meson.exe --buildtype=release -Dc_args=-DDUK_USE_DATE_NOW_WINDOWS -Djsc_folder=".." --prefix="$dist" p build
ninja -C build install
Remove-Item -Recurse -Force $dist\lib\plugins\core_pdd.lib
$ErrorActionPreference = 'Stop'
$pathdll = "$dist\lib\rizin\plugins\core_pdd.dll"
if(![System.IO.File]::Exists($pathdll)) {
type build\meson-logs\meson-log.txt
ls "$dist\lib\rizin\plugins\"
throw (New-Object System.IO.FileNotFoundException("File not found: $pathdll", $pathdll))
}
Remove-Item -Recurse -Force "$dist\lib\rizin\plugins\core_pdd.lib"

View File

@ -2,7 +2,7 @@ $arch = $args[0]
$dist = $args[1]
$py_version = (python --version).Split()[1]
$py_base = "python" + $py_version[0] + $py_version[2]
$py_base = "python" + $py_version.Split('.')[0] + $py_version.Split('.')[1]
$py_platform = If ($arch -eq "x64") {"amd64"} Else {"win32"}
$py_url = "https://www.python.org/ftp/python/${py_version}/python-${py_version}-embed-${py_platform}.zip"

16
dist/bundle_rz_libswift.ps1 vendored Normal file
View File

@ -0,0 +1,16 @@
$dist = $args[0]
$python = Split-Path((Get-Command python.exe).Path)
if (-not (Test-Path -Path 'libswift' -PathType Container)) {
git clone https://github.com/rizinorg/rz-libswift.git --depth 1 libswift
}
cd libswift
& meson.exe --buildtype=release --prefix=$dist build
ninja -C build install
$pathdll = "$dist\lib\rizin\plugins\swift.dll"
if(![System.IO.File]::Exists($pathdll)) {
type build/meson-logs/meson-log.txt
ls "$dist\lib\rizin\plugins\"
throw (New-Object System.IO.FileNotFoundException("File not found: $pathdll", $pathdll))
}
Remove-Item -Recurse -Force "$dist\lib\rizin\plugins\swift.lib"

26
dist/bundle_rz_libyara.ps1 vendored Normal file
View File

@ -0,0 +1,26 @@
$dist = $args[0]
$cmake_opts = $args[1]
$python = Split-Path((Get-Command python.exe).Path)
if (-not (Test-Path -Path 'rz_libyara' -PathType Container)) {
git clone https://github.com/rizinorg/rz-libyara.git --depth 1 rz_libyara
git -C rz_libyara submodule init
git -C rz_libyara submodule update
}
cd rz_libyara
& meson.exe --buildtype=release --prefix=$dist -Duse_sys_yara=disabled -Denable_openssl=false build
ninja -C build install
$pathdll = "$dist\lib\rizin\plugins\rz_yara.dll"
if(![System.IO.File]::Exists($pathdll)) {
type build/meson-logs/meson-log.txt
ls "$dist\lib\rizin\plugins\"
throw (New-Object System.IO.FileNotFoundException("File not found: $pathdll", $pathdll))
}
Remove-Item -Recurse -Force "$dist\lib\rizin\plugins\rz_yara.lib"
cd cutter-plugin
mkdir build
cd build
cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -DRIZIN_INSTALL_PLUGDIR="../build" -DCMAKE_INSTALL_PREFIX="$dist" $cmake_opts ..
ninja
ninja install

17
dist/bundle_rz_silhouette.ps1 vendored Normal file
View File

@ -0,0 +1,17 @@
$dist = $args[0]
$cmake_opts = $args[1]
$python = Split-Path((Get-Command python.exe).Path)
if (-not (Test-Path -Path 'rz-silhouette' -PathType Container)) {
git clone https://github.com/rizinorg/rz-silhouette.git --depth 1 rz-silhouette
}
cd rz-silhouette
& meson.exe --buildtype=release --prefix=$dist build
ninja -C build install
$pathdll = "$dist\lib\rizin\plugins\rz_silhouette.dll"
if(![System.IO.File]::Exists($pathdll)) {
type build/meson-logs/meson-log.txt
ls "$dist\lib\rizin\plugins\"
throw (New-Object System.IO.FileNotFoundException("File not found: $pathdll", $pathdll))
}
Remove-Item -Recurse -Force "$dist\lib\rizin\plugins\rz_silhouette.lib"

View File

@ -7,9 +7,9 @@
<key>CFBundleExecutable</key>
<string>cutter</string>
<key>CFBundleVersion</key>
<string>@FULL_VERSION@</string>
<string>@CUTTER_VERSION_FULL@</string>
<key>CFBundleShortVersionString</key>
<string>@FULL_VERSION@</string>
<string>@CUTTER_VERSION_FULL@</string>
<key>NSHumanReadableCopyright</key>
<string>Licensed under the GPLv3 by the Cutter developers.</string>
<key>CFBundleIconFile</key>

View File

@ -6,7 +6,7 @@ Building
If you just want to use the latest Release version of Cutter, please note
that we provide pre-compiled binaries for Windows, Linux, and macOS on
our `release page <https://github.com/rizinorg/cutter/releases/latest>`_ and
`CI page <https://nightly.link/rizinorg/cutter/workflows/ccpp/master>`_ for latest development builds.
`CI page <https://nightly.link/rizinorg/cutter/workflows/ccpp/dev>`_ for latest development builds.
This page describes how to do a basic build from the command line. If you are planning to modify Cutter it is recommended to also read our :doc:`development environment setup</contributing/code/ide-setup>`.
@ -64,7 +64,22 @@ On Debian-based Linux distributions, all of these essential packages can be inst
sudo apt install build-essential cmake meson libzip-dev zlib1g-dev qt5-default libqt5svg5-dev qttools5-dev qttools5-dev-tools
.. note::
For Ubuntu 18.04 and lower, ``meson`` should be installed with ``pip install --upgrade --user meson``.
On Debian 11 (bullseye) and higher or Ubuntu 22.04 (Jammy) and higher, replace ``qt5-default`` above with ``qtbase5-dev``.
Depending on your configuration you'll might also need the following:
::
# When building with CUTTER_ENABLE_KSYNTAXHIGHLIGHTING (Default)
sudo apt install libkf5syntaxhighlighting-dev
# When building with CUTTER_ENABLE_GRAPHVIZ (Default)
sudo apt install libgraphviz-dev
# when building with CUTTER_ENABLE_PYTHON_BINDINGS
sudo apt install libshiboken2-dev libpyside2-dev qtdeclarative5-dev
.. note::
For Ubuntu 20.04 and lower (or in any case you get an error ``Meson version is x but project requires >=y``), ``meson`` should be installed with ``pip install --upgrade --user meson``.
On Arch-based Linux distributions:
@ -72,6 +87,12 @@ On Arch-based Linux distributions:
sudo pacman -Syu --needed base-devel cmake meson qt5-base qt5-svg qt5-tools
On dnf/yum based distributions:
::
sudo dnf install -y gcc gcc-c++ make cmake meson qt5-qtbase-devel qt5-qtsvg-devel qt5-qttools-devel
Building Steps
~~~~~~~~~~~~~~
@ -95,8 +116,7 @@ If you want to use Cutter with another version of Rizin you can set ``-DCUTTER_U
.. note::
If you are interested in building Cutter with support for Python plugins,
Syntax Highlighting, Crash Reporting and more,
please look at the full list of `CMake Building Options`_.
Syntax Highlighting and more, please look at the full list of `CMake Building Options`_.
After the build process is complete, you should have the ``Cutter`` executable in the **build** dir.
@ -215,7 +235,7 @@ Recommended Way for dev builds
mkdir build
cd build
cmake .. -DCMAKE_PREFIX_PATH=/local/opt/qt5
cmake .. -DCMAKE_PREFIX_PATH=/usr/local/opt/qt5
make
--------------
@ -227,14 +247,13 @@ Note that there are some major building options available:
* ``CUTTER_USE_BUNDLED_RIZIN`` automatically compile Rizin from submodule (Enabled by default).
* ``CUTTER_ENABLE_PYTHON`` compile with Python support.
* ``CUTTER_ENABLE_PYTHON_BINDINGS`` automatically generate Python Bindings with Shiboken2, required for Python plugins!
* ``CUTTER_ENABLE_PYTHON_BINDINGS`` automatically generate Python Bindings with Shiboken, required for Python plugins!
* ``CUTTER_ENABLE_KSYNTAXHIGHLIGHTING`` use KSyntaxHighlighting for code highlighting.
* ``CUTTER_ENABLE_GRAPHVIZ`` enable Graphviz for graph layouts.
* ``CUTTER_EXTRA_PLUGIN_DIRS`` List of addition plugin locations. Useful when preparing package for Linux distros that have strict package layout rules.
Cutter binary release options, not needed for most users and might not work easily outside CI environment:
* ``CUTTER_ENABLE_CRASH_REPORTS`` is used to compile Cutter with crash handling system enabled (Breakpad).
* ``CUTTER_ENABLE_DEPENDENCY_DOWNLOADS`` Enable downloading of dependencies. Setting to OFF doesn't affect any downloads done by Rizin build. This option is used for preparing Cutter binary release packges. Turned off by default.
* ``CUTTER_PACKAGE_DEPENDENCIES`` During install step include the third party dependencies. This option is used for preparing Cutter binary release packges.
@ -253,28 +272,6 @@ Or if one wants to explicitly disable an option:
cmake -B build -DCUTTER_ENABLE_PYTHON=OFF
--------------
Compiling Cutter with Breakpad Support
--------------------------------------
If you want to build Cutter with crash handling system, you will want to first prepare Breakpad.
For this, simply run one of the scripts (according to your OS) from root Cutter directory:
.. code:: sh
source scripts/prepare_breakpad_linux.sh # Linux
source scripts/prepare_breakpad_macos.sh # MacOS
scripts/prepare_breakpad.bat # Windows
Then if you are building on Linux you want to change ``PKG_CONFIG_PATH`` environment variable
so it contains ``$CUSTOM_BREAKPAD_PREFIX/lib/pkgconfig``. For this simply run
.. code:: sh
export PKG_CONFIG_PATH="$CUSTOM_BREAKPAD_PREFIX/lib/pkgconfig:$PKG_CONFIG_PATH"
--------------
Troubleshooting

View File

@ -24,9 +24,9 @@ copyright = '2020, The Cutter Developers'
author = 'The Cutter Developers'
# The short X.Y version
version = '2.0'
version = '2.2'
# The full version, including a2lpha/beta/rc tags
release = '2.0.2'
release = '2.2.0'
# -- General configuration ---------------------------------------------------

View File

@ -1,32 +0,0 @@
Crash Handling System
=====================
Cutter uses `Breakpad <https://github.com/google/breakpad>`__ as a backend
for crash handling.
Crash Handling System is disabled by default to not interfere with developers while debugging.
To enable this system, set the ``CUTTER_ENABLE_CRASH_REPORTS`` build option.
Solution Description
--------------------
There are only 2 source files:
* ``CrashHandler.h``
* ``CrashHandler.cpp``
And the API is very simple: One function, ``initCrashHandler()``, enables the Crash Handling System if
``CUTTER_ENABLE_CRASH_REPORTS`` is true, otherwise it does nothing.
As soon as a signal is raised, ``crashHandler(int signum)`` is called with the signal's code as an argument.
This function first writes a crash dump to the operating system's temporary directory to catch core and
memory state as it was at the moment of the crash.
Then the crash dialog is shown:
.. image :: /images/crash-dialog.png
If the user chooses to create a crash dump, the prepared dump is moved to the directory specified by the user.
And then the success dialog is shown:
.. image :: /images/success-dump-dialog.png

View File

@ -47,7 +47,7 @@ Example:
.. code:: cpp
QJsonArray array = Core()->cmdj("pdj 1 @ main").array();
CutterJson array = Core()->cmdj("pdj 1 @ main");
Seek the Current File
~~~~~~~~~~~~~~~~~~~~~
@ -257,7 +257,7 @@ In order to update one submodule individually, use the following code:
.. code:: sh
cd rizin
git checkout master && git pull
git checkout dev && git pull
cd ..
git add rizin
git commit -m "Update rizin submodule"

View File

@ -4,7 +4,7 @@ Release Procedure
1. Update translations submodule `<https://github.com/rizinorg/cutter-translations>`_
1. The latest archive from Crowdin should already be in the repository, if not make sure to merge any automated Pull Request from Crowdin (e.g. https://github.com/rizinorg/cutter-translations/pull/9)
2. Update submodule in cutter
2. If there is a desire to keep working in the master branch, create branch for the release and do all the following work there.
2. Merge the current state of dev into stable. This can happen even earlier in order to feature-freeze the release while keeping development on dev alive. The rizin submodule on stable should point to a commit of stable in rizin and dev to a commit in dev.
3. Lock rzghidra and rzdec versions downloaded by packaging scripts. Specify a tag or commit hash.
4. Update version
#. appveyor.yml
@ -19,11 +19,11 @@ Release Procedure
6. Create a GitHub release, mark it as pre-release save draft, set the tag to v1.11.0-rc1
7. Wait for packages to build
8. On all operating systems do the `Basic testing procedure`_ to ensure nothing badly broken.
9. If any major problems are found, open an issue and fix them. If a release branch is used fix them in master and cherry pick into release branch. If the amount of changes is sufficiently large repeat from step 3. increasing rc number by one.
9. If any major problems are found, open an issue and fix them in dev and cherry pick into release branch. If the amount of changes is sufficiently large repeat from step 3. increasing rc number by one.
10. Update version to 1.11.0
11. Create tag
12. Create release
* Fill the release notes in the Release description. Preparing release notes can begin earlier. Compare current master or release branch against previous release to find all the changes. Choose the most important ones. Don't duplicate the commit log. Release notes should be a summary for people who don't want to read whole commit log. Group related changes together under titles such as "New features", "Bug Fixes", "Decompiler", "Rizin" and similar.
* Fill the release notes in the Release description. Preparing release notes can begin earlier. Compare current dev branch against previous release to find all the changes. Choose the most important ones. Don't duplicate the commit log. Release notes should be a summary for people who don't want to read whole commit log. Group related changes together under titles such as "New features", "Bug Fixes", "Decompiler", "Rizin" and similar.
13. Prepare announcement tweets and messages to send in the Telegram group, reddit, and others.
14. Close milestone if there was one
@ -33,8 +33,7 @@ Bugfix Release
--------------
The process for bugfix release is similar no normal release procedure described above with some differences.
* Create the branch from the last tag instead of master or reuse the branch from x.y.0 release if it was already created.
* Cherry pick required bugfixes from master into the branch.
* Cherry pick required bugfixes from dev into the stable.
* Increase the third version number x.y.n into x.y.(n+1) .

View File

@ -16,7 +16,7 @@ How can you help?
The following sections suggest ways you can contribute to Cutter's documentation. The list isn't complete as the possibilities are limitless.
The source for this documentation is available in the `docs directory <https://github.com/rizinorg/cutter/tree/master/docs>`_ on Cutter's repository. This source can be generated according to the steps described in the :ref:`building docs page<contributing/docs/building-docs:Building docs>`. When the docs are updated, they are generated and pushed directly to the website at <https://cutter.re/docs>. The source for the website and blog are available on the `cutter.re's repository <https://github.com/rizinorg/cutter.re>`_ and are served from the ``gh-pages`` branch.
The source for this documentation is available in the `docs directory <https://github.com/rizinorg/cutter/tree/dev/docs>`_ on Cutter's repository. This source can be generated according to the steps described in the :ref:`building docs page<contributing/docs/building-docs:Building docs>`. When the docs are updated, they are generated and pushed directly to the website at <https://cutter.re/docs>. The source for the website and blog are available on the `cutter.re's repository <https://github.com/rizinorg/cutter.re>`_ and are served from the ``gh-pages`` branch.
.. tip::
Document what you wished to see. If you are a user of Cutter, try to think what things you would want to see documented when you started using the project. Sometimes, the best contributions are coming from your own needs.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

View File

@ -40,21 +40,45 @@ Options
**2**
aaaa (experimental)
.. option:: -a, --arch <arch>
Sets a specific architecture name.
.. option:: -b, --bits <bits>
Sets a specific architecture bits.
.. option:: -c, --cpu <cpu>
Sets a specific CPU.
.. option:: -o, --os <os>
Sets a specific operating system.
.. option:: -e, --endian <big|little>
Sets the endianness (big or little).
.. option:: -F, --format <name>
Force using a specific file format (bin plugin)
Force using a specific file format (bin plugin).
.. option:: -B, --base <base address>
Load binary at a specific base address
Load binary at a specific base address.
.. option:: -m, --map <map address>
Map the binary at a specific address.
.. option:: -i <file>
Run script file
Run script file.
.. option:: -p, --project <file>
Load project file
Load project file.
.. option:: -w, --writemode
@ -62,9 +86,13 @@ Options
When used together with -A/--analysis <level>, it will open a file directly
in write mode without any further dialog or confirmation.
.. option:: -P, --phymode
Disables virtual addressing.
.. option:: --pythonhome <PYTHONHOME>
PYTHONHOME to use for the embedded python interpreter
PYTHONHOME to use for the embedded python interpreter.
.. option:: --no-output-redirect

View File

@ -103,15 +103,6 @@ Set Structure Offset
**Steps:** -> Structure offset
Link a Type to Address
----------------------------------------
**Description:** You can link type, enum or structure to a specific address. Types, structures and enums can be defined in the Types widget.
**Steps:** Right-click on an instruction and choose ``Link Type to Address``.
**Shortcut:** :kbd:`L`
Show Cross References
----------------------------------------
**Description:** Show X-Refs from and to the specific location. This option will open Cutter's X-Refs dialog in which you will be able to see a list of X-Refs from and to the selected location, in addition to a preview of each cross-reference to quickly inspect the different usages.

View File

@ -89,8 +89,8 @@ Show VTables
**Steps:** Windows -> Info... -> VTables
Show Zignatures
Show Signatures
----------------------------------------
**Description:** Cutter has its own format of signatures, called Zignatures. This widget lists all the loaded Zignatures.
**Description:** Cutter supports the creation and the utilization of signatures. This widget lists all the signatures available to cutter.
**Steps:** Windows -> Info... -> Zignatures
**Steps:** Windows -> Info... -> Signatures

View File

@ -62,8 +62,6 @@ Disassembly View Shortcuts
+-------------+----------------------------------+
| Y | Edit/rename local variables |
+-------------+----------------------------------+
| L | Link a type/struct to address |
+-------------+----------------------------------+
| A | Set current address to String |
+-------------+----------------------------------+
| C | Set current address to Code |

2
rizin

@ -1 +1 @@
Subproject commit 381f22d7cc81bf2eb6663a690a715ff4f8f09373
Subproject commit 6bfc67a2868e07ab89ff931b96dfd3bc65e7371e

View File

@ -3,4 +3,6 @@ brew "ccache"
brew "openssl"
brew "xz"
brew "llvm"
brew "meson"
brew "meson"
brew "coreutils"
brew "pkg-config"

View File

@ -1,34 +0,0 @@
{
'includes': [
'../../build/common.gypi'
],
'targets': [
{
'target_name': 'build_all',
'type': 'none',
'dependencies': [
'./crash_generation/crash_generation.gyp:*',
'./handler/exception_handler.gyp:*',
'./sender/crash_report_sender.gyp:*',
]
},
{
'target_name': 'common',
'type': 'static_library',
'include_dirs': [
'<(DEPTH)',
],
'direct_dependent_settings': {
'include_dirs': [
'<(DEPTH)',
]
},
'sources': [
'<(DEPTH)/common/windows/guid_string.cc',
'<(DEPTH)/common/windows/guid_string.h',
'<(DEPTH)/common/windows/http_upload.h',
'<(DEPTH)/common/windows/string_utils.cc',
]
}
]
}

View File

@ -1,55 +0,0 @@
import sys
import os
import subprocess
import re
import atexit
if len(sys.argv) != 3:
print(f"usage: {sys.argv[0]} [Cutter.AppImage] [symbols dir]")
exit(1)
root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
def store_syms(syms, syms_dir):
m = re.match(b"MODULE ([^ ]+) ([^ ]+) ([^ ]+) (.+)\n.*", syms)
if m is None:
print("Invalid dump_syms output")
return
(modos, modarch, modhash, modname) = m.groups()
modname = modname.decode("utf-8")
modhash = modhash.decode("utf-8")
symdir = os.path.join(syms_dir, modname, modhash)
symfile = f"{modname}.sym"
os.makedirs(symdir)
symfile_path = os.path.join(symdir, symfile)
with open(symfile_path, "wb") as f:
f.write(syms)
print(symfile_path)
def dump_syms(binary, syms_dir):
dump_syms_exec = os.path.join(root, "breakpad/src/tools/linux/dump_syms/dump_syms")
syms = subprocess.run([dump_syms_exec, binary], capture_output=True).stdout
store_syms(syms, syms_dir)
appimage = sys.argv[1]
syms_dst = sys.argv[2]
# stdbuf workaround is needed before https://github.com/AppImage/AppImageKit/commit/e827baa719f5444aeef7202fe1f71c97d4200dde
appimage_p = subprocess.Popen(["stdbuf", "-oL", appimage, "--appimage-mount"], stdout=subprocess.PIPE)
def kill_appimage():
appimage_p.kill()
atexit.register(kill_appimage)
mount_dir = appimage_p.stdout.readline().strip().decode("utf-8")
binaries = [ os.path.join(mount_dir, "usr/bin/cutter") ]
for f in os.scandir(os.path.join(mount_dir, "usr/lib")):
if f.is_dir() or f.is_symlink():
continue
binaries.append(f.path)
for b in binaries:
dump_syms(b, syms_dst)

File diff suppressed because it is too large Load Diff

View File

@ -1,54 +1,55 @@
#!/bin/bash
set -e
cd $(dirname "${BASH_SOURCE[0]}")/..
mkdir -p cutter-deps && cd cutter-deps
LINUX_FILE="cutter-deps-linux.tar.gz"
LINUX_MD5=eb2710548d951823e6b5340c33c8fc99
LINUX_URL=https://github.com/rizinorg/cutter-deps/releases/download/v14/cutter-deps-linux.tar.gz
DEPS_FILE_linux_x86_64=cutter-deps-linux-x86_64.tar.gz
DEPS_SHA256_linux_x86_64=0721c85548bbcf31f6911cdb2227e5efb4a20c34262672d4cd2193db166b2f8c
MACOS_FILE="cutter-deps-macos.tar.gz"
MACOS_MD5=f921c007430eec38b06acef8cc0fe42a
MACOS_URL=https://github.com/rizinorg/cutter-deps/releases/download/v14/cutter-deps-macos.tar.gz
DEPS_FILE_macos_x86_64=cutter-deps-macos-x86_64.tar.gz
DEPS_SHA256_macos_x86_64=0a23fdec3012a8af76675d6f3ff39cf9df9b08c13d1156fb7ffcc0e495c9407f
WIN_FILE="cutter-deps-win.tar.gz"
WIN_MD5=09aa544e62cdd786df3598f1ff340f9e
WIN_URL=https://github.com/rizinorg/cutter-deps/releases/download/v14/cutter-deps-win.tar.gz
DEPS_FILE_macos_arm64=cutter-deps-macos-arm64.tar.gz
DEPS_SHA256_macos_arm64=f9b9a5569bd23c9b5e45836b82aba7576a5c53df4871380a55c370b9d7f88615
DEPS_FILE_win_x86_64=cutter-deps-win-x86_64.tar.gz
DEPS_SHA256_win_x86_64=9ab4e89732a3df0859a26fd5de6d9f3cb80106cbe2539340af831ed298625076
DEPS_BASE_URL=https://github.com/rizinorg/cutter-deps/releases/download/v15
ARCH=x86_64
if [ "$OS" == "Windows_NT" ]; then
FILE="${WIN_FILE}"
MD5="${WIN_MD5}"
URL="${WIN_URL}"
PLATFORM=win
else
UNAME_S="$(uname -s)"
if [ "$UNAME_S" == "Linux" ]; then
FILE="${LINUX_FILE}"
MD5="${LINUX_MD5}"
URL="${LINUX_URL}"
PLATFORM=linux
elif [ "$UNAME_S" == "Darwin" ]; then
FILE="${MACOS_FILE}"
MD5="${MACOS_MD5}"
URL="${MACOS_URL}"
PLATFORM=macos
ARCH=$(uname -m)
else
echo "Unsupported Platform: uname -s => $UNAME_S, \$OS => $OS"
exit 1
fi
fi
curl -L "$URL" -o "$FILE" || exit 1
DEPS_FILE=DEPS_FILE_${PLATFORM}_${ARCH}
DEPS_FILE=${!DEPS_FILE}
DEPS_SHA256=DEPS_SHA256_${PLATFORM}_${ARCH}
DEPS_SHA256=${!DEPS_SHA256}
DEPS_URL=${DEPS_BASE_URL}/${DEPS_FILE}
if [ "$UNAME_S" == "Darwin" ]; then
if [ "$(md5 -r "$FILE")" != "$MD5 $FILE" ]; then \
echo "MD5 mismatch for file $FILE"; \
exit 1; \
else \
echo "$FILE OK"; \
fi
else
echo "$MD5 $FILE" | md5sum -c - || exit 1
SHA256SUM=sha256sum
if ! command -v ${SHA256SUM} &> /dev/null; then
SHA256SUM="shasum -a 256"
fi
tar -xf "$FILE" || exit 1
curl -L "$DEPS_URL" -o "$DEPS_FILE" || exit 1
echo "$DEPS_SHA256 $DEPS_FILE" | ${SHA256SUM} -c - || exit 1
tar -xf "$DEPS_FILE" || exit 1
if [ -f relocate.sh ]; then
./relocate.sh || exit 1

View File

@ -7,13 +7,13 @@ SCRIPTPATH=$(realpath "$(dirname "${BASH_SOURCE[0]}")")
cd "$SCRIPTPATH/.."
if [[ ! -d jsdec ]]; then
git clone https://github.com/rizinorg/jsdec.git --depth 1 --branch v0.2.x
git clone https://github.com/rizinorg/jsdec.git --depth 2 --branch master
fi
cd jsdec
rm -rf build
mkdir build && cd build
meson --buildtype=release --libdir=share/rizin/plugins --datadir=share/rizin/plugins "$@" ../p
meson --buildtype=release -Djsc_folder="../" "$@" ../p
ninja
ninja install

View File

@ -1,33 +0,0 @@
@ECHO OFF
SET ROOT_DIR=%CD%
powershell -Command "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; wget https://storage.googleapis.com/chrome-infra/depot_tools.zip -OutFile depot_tools.zip "
7z -bd x %ROOT_DIR%\depot_tools.zip -odepot_tools
powershell -Command "depot_tools\update_depot_tools"
SET BUFF_PATH=%PATH%
SET DEPOT_TOOLS=%ROOT_DIR%\depot_tools
set PATH=%DEPOT_TOOLS%;%BUFF_PATH%
mkdir %ROOT_DIR%\src\breakpad
CD %ROOT_DIR%\src\breakpad
powershell -Command "fetch breakpad"
powershell -Command "gclient sync"
CD %ROOT_DIR%\src\breakpad\src
powershell -Command "git reset --hard 756daa536ad819eff80172aaab262fb71d1e89fd"
CD %ROOT_DIR%\src\breakpad\src\src\client\windows
DEL %CD%\breakpad_client.gyp
DEL %CD%\breakpad_client.sln
DEL %CD%\common.vcxproj
DEL %CD%\common.vcxproj.filters
DEL %CD%\build_all.vcxproj
COPY %ROOT_DIR%\scripts\breakpad_client.gyp %CD%
CD %ROOT_DIR%\src\breakpad\src\src
SET GYP_MSVS_VERSION=2017
powershell -Command "tools\gyp\gyp.bat --no-circular-check client\windows\breakpad_client.gyp -Dwin_release_RuntimeLibrary=2 -Dwin_debug_RuntimeLibrary=2 -Dplatform=%ARCH% -Dconfiguration=release"
devenv client\windows\breakpad_client.sln /upgrade
set PATH=%BUFF_PATH%
msbuild /m %CD%\client\windows\breakpad_client.sln /p:Configuration=release /p:Platform=%ARCH% || exit /b 1
CD %ROOT_DIR%

View File

@ -1,11 +0,0 @@
#!/bin/sh
set -euo pipefail
git clone https://github.com/google/breakpad.git
cd breakpad
git clone https://chromium.googlesource.com/linux-syscall-support src/third_party/lss
CFLAGS=-w CXXFLAGS=-w ./configure --disable-tools --prefix=`pwd`/prefix && make -j4 && make install || exit 1
export CUSTOM_BREAKPAD_PREFIX="`pwd`/prefix"
cd ..

View File

@ -1,24 +0,0 @@
#!/bin/sh
set -euo pipefail
SCRIPTPATH=$(realpath "$(dirname "${BASH_SOURCE[0]}")")
DIR="$SCRIPTPATH/.."
cd "$DIR"
BREAKPAD_FRAMEWORK_DIR="$DIR/breakpad/framework"
BREAKPAD_DUMP_SYMS_DIR="$DIR/breakpad/bin"
git clone https://github.com/google/breakpad.git
mkdir $BREAKPAD_FRAMEWORK_DIR
mkdir $BREAKPAD_DUMP_SYMS_DIR
cd breakpad
git checkout 4d550cceca107f36c4bc1ea1126b7d32cc50f424
git apply "$SCRIPTPATH/breakpad_macos.patch"
cd src/client/mac/ && xcodebuild -sdk macosx MACOSX_DEPLOYMENT_TARGET=10.14
cp -R build/Release/Breakpad.framework "$BREAKPAD_FRAMEWORK_DIR"
cd $DIR/breakpad
cp -R src/. framework/Breakpad.framework/Headers
export BREAKPAD_FRAMEWORK_DIR=$BREAKPAD_FRAMEWORK_DIR
cd $DIR

19
scripts/rz-libswift.sh Executable file
View File

@ -0,0 +1,19 @@
#!/bin/bash
set -e
SCRIPTPATH=$(realpath "$(dirname "${BASH_SOURCE[0]}")")
cd "$SCRIPTPATH/.."
if [[ ! -d libswift ]]; then
git clone https://github.com/rizinorg/rz-libswift.git --depth 1 libswift
fi
cd libswift
rm -rf build || sleep 0
mkdir build && cd build
meson --buildtype=release "$@" ..
ninja
ninja install

25
scripts/rz-libyara.sh Executable file
View File

@ -0,0 +1,25 @@
#!/bin/bash
set -e
SCRIPTPATH=$(realpath "$(dirname "${BASH_SOURCE[0]}")")
INSTALL_PREFIX="$1"
EXTRA_CMAKE_OPTS="$2"
cd "$SCRIPTPATH/.."
if [[ ! -d rz_libyara ]]; then
git clone https://github.com/rizinorg/rz-libyara.git --depth 1 rz_libyara
git -C rz_libyara submodule init
git -C rz_libyara submodule update
fi
cd rz_libyara
meson --buildtype=release --pkg-config-path="$INSTALL_PREFIX/lib/pkgconfig" --prefix="$INSTALL_PREFIX" -Duse_sys_yara=disabled build
ninja -C build install
cd cutter-plugin
mkdir build && cd build
cmake -G Ninja -DRIZIN_INSTALL_PLUGDIR="../build" -DCMAKE_INSTALL_PREFIX="$INSTALL_PREFIX" $EXTRA_CMAKE_OPTS ..
ninja
ninja install

17
scripts/rz-silhouette.sh Executable file
View File

@ -0,0 +1,17 @@
#!/bin/bash
set -e
SCRIPTPATH=$(realpath "$(dirname "${BASH_SOURCE[0]}")")
INSTALL_PREFIX="$1"
EXTRA_CMAKE_OPTS="$2"
cd "$SCRIPTPATH/.."
if [[ ! -d rz-silhouette ]]; then
git clone https://github.com/rizinorg/rz-silhouette.git --depth 1 rz-silhouette
fi
cd rz-silhouette
meson --buildtype=release --pkg-config-path="$INSTALL_PREFIX/lib/pkgconfig" --prefix="$INSTALL_PREFIX" build
ninja -C build install

32
scripts/tarball.sh Executable file
View File

@ -0,0 +1,32 @@
#!/bin/bash
NAME=${1:-Cutter}
set -xe
cd $(dirname "${BASH_SOURCE[0]}")/..
shopt -s extglob
shopt -s dotglob
mkdir "${NAME}"
cp -r !(${NAME}) "${NAME}"
pushd "${NAME}"
git clean -dxff .
git submodule update --init --recursive
pushd rizin
git clean -dxff .
# Possible option: pre-download all subproject, however this makes the tarball huge.
# As opposed to meson dist used for rizin tarballs, this will not just download the ones
# used in a default build, but all of them, including multiple capstone variants.
# meson subprojects download
popd
pushd src/translations
git clean -dxff .
popd
find . -name ".git*" | xargs rm -rfv
popd
tar -czvf "${NAME}-src.tar.gz" "${NAME}"

View File

@ -4,10 +4,14 @@ configure_file("${CMAKE_CURRENT_SOURCE_DIR}/CutterConfig.h.in"
set(SOURCES
Main.cpp
core/Cutter.cpp
core/CutterJson.cpp
core/RizinCpp.cpp
core/Basefind.cpp
dialogs/EditStringDialog.cpp
dialogs/WriteCommandsDialogs.cpp
widgets/DisassemblerGraphView.cpp
widgets/OverviewView.cpp
common/DisassemblyPreview.cpp
common/RichTextPainter.cpp
dialogs/InitialOptionsDialog.cpp
dialogs/AboutDialog.cpp
@ -24,7 +28,7 @@ set(SOURCES
common/DirectionalComboBox.cpp
dialogs/preferences/AsmOptionsWidget.cpp
dialogs/NewFileDialog.cpp
common/AnalTask.cpp
common/AnalysisTask.cpp
widgets/CommentsWidget.cpp
widgets/ConsoleWidget.cpp
widgets/Dashboard.cpp
@ -41,6 +45,7 @@ set(SOURCES
widgets/SymbolsWidget.cpp
menus/DisassemblyContextMenu.cpp
menus/DecompilerContextMenu.cpp
menus/FlirtContextMenu.cpp
widgets/DisassemblyWidget.cpp
widgets/HexdumpWidget.cpp
common/Configuration.cpp
@ -69,10 +74,9 @@ set(SOURCES
widgets/CutterTreeWidget.cpp
widgets/GraphWidget.cpp
widgets/OverviewWidget.cpp
common/JsonTreeItem.cpp
common/JsonModel.cpp
dialogs/VersionInfoDialog.cpp
widgets/ZignaturesWidget.cpp
widgets/FlirtWidget.cpp
common/AsyncTask.cpp
dialogs/AsyncTaskDialog.cpp
widgets/StackWidget.cpp
@ -109,7 +113,6 @@ set(SOURCES
plugins/PluginManager.cpp
common/BasicBlockHighlighter.cpp
common/BasicInstructionHighlighter.cpp
dialogs/LinkTypeDialog.cpp
widgets/ColorPicker.cpp
common/ColorThemeWorker.cpp
widgets/ColorThemeComboBox.cpp
@ -139,18 +142,25 @@ set(SOURCES
widgets/RizinGraphWidget.cpp
widgets/CallGraph.cpp
widgets/AddressableDockWidget.cpp
dialogs/preferences/AnalOptionsWidget.cpp
dialogs/preferences/AnalysisOptionsWidget.cpp
common/DecompilerHighlighter.cpp
dialogs/GlibcHeapInfoDialog.cpp
widgets/HeapDockWidget.cpp
widgets/GlibcHeapWidget.cpp
dialogs/GlibcHeapBinsDialog.cpp
widgets/HeapBinsGraphView.cpp
dialogs/ArenaInfoDialog.cpp
tools/basefind/BaseFindDialog.cpp
tools/basefind/BaseFindSearchDialog.cpp
tools/basefind/BaseFindResultsDialog.cpp
)
set(HEADER_FILES
core/Cutter.h
core/CutterCommon.h
core/CutterDescriptions.h
core/CutterJson.h
core/RizinCpp.h
core/Basefind.h
dialogs/EditStringDialog.h
dialogs/WriteCommandsDialogs.h
widgets/DisassemblerGraphView.h
@ -172,7 +182,7 @@ set(HEADER_FILES
common/DirectionalComboBox.h
dialogs/InitialOptionsDialog.h
dialogs/NewFileDialog.h
common/AnalTask.h
common/AnalysisTask.h
widgets/CommentsWidget.h
widgets/ConsoleWidget.h
widgets/Dashboard.h
@ -189,6 +199,7 @@ set(HEADER_FILES
widgets/SymbolsWidget.h
menus/DisassemblyContextMenu.h
menus/DecompilerContextMenu.h
menus/FlirtContextMenu.h
widgets/DisassemblyWidget.h
widgets/HexdumpWidget.h
common/Configuration.h
@ -217,10 +228,9 @@ set(HEADER_FILES
widgets/CutterTreeWidget.h
widgets/GraphWidget.h
widgets/OverviewWidget.h
common/JsonTreeItem.h
common/JsonModel.h
dialogs/VersionInfoDialog.h
widgets/ZignaturesWidget.h
widgets/FlirtWidget.h
common/AsyncTask.h
dialogs/AsyncTaskDialog.h
widgets/StackWidget.h
@ -257,7 +267,6 @@ set(HEADER_FILES
common/RunScriptTask.h
common/Json.h
dialogs/EditMethodDialog.h
common/CrashHandler.h
dialogs/TypesInteractionDialog.h
widgets/SdbWidget.h
plugins/PluginManager.h
@ -270,7 +279,6 @@ set(HEADER_FILES
widgets/MemoryDockWidget.h
widgets/ColorThemeListView.h
dialogs/preferences/ColorThemeEditDialog.h
dialogs/LinkTypeDialog.h
common/BugReporting.h
common/HighDpiPixmap.h
widgets/GraphLayout.h
@ -297,13 +305,17 @@ set(HEADER_FILES
widgets/RizinGraphWidget.h
widgets/CallGraph.h
widgets/AddressableDockWidget.h
dialogs/preferences/AnalOptionsWidget.h
dialogs/preferences/AnalysisOptionsWidget.h
common/DecompilerHighlighter.h
dialogs/GlibcHeapInfoDialog.h
widgets/HeapDockWidget.h
widgets/GlibcHeapWidget.h
dialogs/GlibcHeapBinsDialog.h
widgets/HeapBinsGraphView.h
dialogs/ArenaInfoDialog.h
tools/basefind/BaseFindDialog.h
tools/basefind/BaseFindSearchDialog.h
tools/basefind/BaseFindResultsDialog.h
)
set(UI_FILES
dialogs/AboutDialog.ui
@ -334,13 +346,12 @@ set(UI_FILES
dialogs/preferences/InitializationFileEditor.ui
widgets/QuickFilterView.ui
widgets/DecompilerWidget.ui
widgets/ClassesWidget.ui
widgets/VTablesWidget.ui
widgets/TypesWidget.ui
widgets/SearchWidget.ui
dialogs/RizinPluginsDialog.ui
dialogs/VersionInfoDialog.ui
widgets/ZignaturesWidget.ui
widgets/FlirtWidget.ui
dialogs/AsyncTaskDialog.ui
dialogs/RizinTaskDialog.ui
widgets/StackWidget.ui
@ -363,17 +374,20 @@ set(UI_FILES
dialogs/EditMethodDialog.ui
dialogs/TypesInteractionDialog.ui
widgets/SdbWidget.ui
dialogs/LinkTypeDialog.ui
widgets/ColorPicker.ui
dialogs/preferences/ColorThemeEditDialog.ui
widgets/ListDockWidget.ui
dialogs/LayoutManager.ui
widgets/RizinGraphWidget.ui
dialogs/preferences/AnalOptionsWidget.ui
dialogs/preferences/AnalysisOptionsWidget.ui
dialogs/GlibcHeapInfoDialog.ui
widgets/HeapDockWidget.ui
widgets/GlibcHeapWidget.ui
dialogs/GlibcHeapBinsDialog.ui
dialogs/ArenaInfoDialog.ui
tools/basefind/BaseFindDialog.ui
tools/basefind/BaseFindSearchDialog.ui
tools/basefind/BaseFindResultsDialog.ui
)
set(QRC_FILES
resources.qrc
@ -383,20 +397,26 @@ set(QRC_FILES
themes/lightstyle/light.qrc
)
set(CUTTER_INCLUDE_DIRECTORIES core widgets common plugins menus .)
if (CUTTER_ENABLE_PYTHON)
list(APPEND SOURCES common/QtResImporter.cpp common/PythonManager.cpp common/PythonAPI.cpp)
list(APPEND HEADER_FILES common/QtResImporter.h common/PythonManager.h common/PythonAPI.h)
endif()
if(CUTTER_ENABLE_CRASH_REPORTS)
list(APPEND SOURCES common/CrashHandler.cpp)
endif()
if(CUTTER_ENABLE_PYTHON_BINDINGS)
if (CUTTER_QT6)
set(PYSIDE_NAME PySide6)
set(SHIBOKEN_COMMAND Shiboken6::shiboken6)
else()
set(PYSIDE_NAME PySide2)
set(SHIBOKEN_COMMAND Shiboken2::shiboken2)
endif()
set(BINDINGS_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/bindings")
set(BINDINGS_BUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}/bindings")
configure_file("${BINDINGS_SRC_DIR}/bindings.xml" "${BINDINGS_BUILD_DIR}/bindings.xml" COPYONLY) # trigger reconfigure if file changes
configure_file("${BINDINGS_SRC_DIR}/bindings.xml.in" "${BINDINGS_BUILD_DIR}/bindings.xml")
execute_process(COMMAND "${PYTHON_EXECUTABLE}" "${BINDINGS_SRC_DIR}/src_list.py" cmake "${BINDINGS_BUILD_DIR}" OUTPUT_VARIABLE BINDINGS_SOURCE)
@ -404,14 +424,40 @@ if(CUTTER_ENABLE_PYTHON_BINDINGS)
include_directories("${BINDINGS_BUILD_DIR}/CutterBindings")
set(SHIBOKEN_INCLUDE_DIRS "")
if(APPLE AND _qt6Core_install_prefix)
list(APPEND BINDINGS_INCLUDE_DIRS "${_qt6Core_install_prefix}/include")
list(APPEND BINDINGS_INCLUDE_DIRS "${_qt6Core_install_prefix}/include/QtCore")
list(APPEND BINDINGS_INCLUDE_DIRS "${_qt6Core_install_prefix}/include/QtGui")
list(APPEND BINDINGS_INCLUDE_DIRS "${_qt6Core_install_prefix}/include/QtWidgets")
endif()
if (CUTTER_QT6)
list(APPEND SHIBOKEN_INCLUDE_DIRS ${Qt6Core_INCLUDE_DIRS} ${Qt6Widgets_INCLUDE_DIRS} ${Qt6Gui_INCLUDE_DIRS})
else()
list(APPEND SHIBOKEN_INCLUDE_DIRS ${Qt5Core_INCLUDE_DIRS} ${Qt5Widgets_INCLUDE_DIRS} ${Qt5Gui_INCLUDE_DIRS})
endif()
foreach(_dir ${CUTTER_INCLUDE_DIRECTORIES})
list(APPEND SHIBOKEN_INCLUDE_DIRS
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/${_dir}>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/cutter/${_dir}>
)
endforeach()
list(APPEND SHIBOKEN_INCLUDE_DIRS ${Rizin_INCLUDE_DIRS})
if (NOT WIN32)
string(REPLACE ";" ":" SHIBOKEN_INCLUDE_DIRS "${SHIBOKEN_INCLUDE_DIRS}")
endif()
set(SHIBOKEN_OPTIONS)
list(APPEND SHIBOKEN_OPTIONS --include-paths="${SHIBOKEN_INCLUDE_DIRS}")
if (WIN32)
list(APPEND SHIBOKEN_OPTIONS --avoid-protected-hack)
endif()
add_custom_command(OUTPUT ${BINDINGS_SOURCE}
COMMAND Shiboken2::shiboken2 --project-file="${BINDINGS_BUILD_DIR}/bindings.txt" ${SHIBOKEN_OPTIONS} ${SHIBOKEN_EXTRA_OPTIONS}
DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/bindings/bindings.xml" "${BINDINGS_BUILD_DIR}/bindings.txt"
COMMAND "${SHIBOKEN_COMMAND}" --project-file="${BINDINGS_BUILD_DIR}/bindings.txt" ${SHIBOKEN_OPTIONS} ${SHIBOKEN_EXTRA_OPTIONS}
DEPENDS "${BINDINGS_BUILD_DIR}/bindings.xml" "${BINDINGS_BUILD_DIR}/bindings.txt"
IMPLICIT_DEPENDS CXX "${CMAKE_CURRENT_SOURCE_DIR}/bindings/bindings.h"
COMMENT "Generating Python bindings with shiboken2")
else()
@ -426,6 +472,7 @@ endif()
if (WIN32)
set(PLATFORM_RESOURCES "img/cutter.rc")
set(OPTIONS WIN32)
else()
set(PLATFORM_RESOURCES "")
endif()
@ -436,8 +483,11 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU"
set_source_files_properties(${BINDINGS_SOURCE} PROPERTIES COMPILE_FLAGS -w)
endif()
# Make a source group for Visual Studio
set(CUTTER_SOURCES ${OPTIONS} ${UI_FILES} ${QRC_FILES} ${PLATFORM_RESOURCES} ${SOURCES} ${HEADER_FILES})
source_group(TREE "${CMAKE_CURRENT_LIST_DIR}" FILES ${CUTTER_SOURCES})
add_executable(Cutter ${UI_FILES} ${QRC_FILES} ${PLATFORM_RESOURCES} ${SOURCES} ${HEADER_FILES} ${BINDINGS_SOURCE})
add_executable(Cutter ${CUTTER_SOURCES} ${BINDINGS_SOURCE})
set_target_properties(Cutter PROPERTIES
OUTPUT_NAME cutter
RUNTIME_OUTPUT_DIRECTORY ..
@ -446,7 +496,9 @@ set_target_properties(Cutter PROPERTIES
CXX_VISIBILITY_PRESET hidden)
target_compile_definitions(Cutter PRIVATE CUTTER_SOURCE_BUILD)
set(CUTTER_INCLUDE_DIRECTORIES core widgets common plugins menus .)
# Set Cutter as the startup project in Visual Studio
set_property(DIRECTORY ${PROJECT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT Cutter)
foreach(_dir ${CUTTER_INCLUDE_DIRECTORIES})
target_include_directories(Cutter PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/${_dir}>
@ -459,19 +511,6 @@ if (TARGET Graphviz::GVC)
target_compile_definitions(Cutter PRIVATE CUTTER_ENABLE_GRAPHVIZ)
endif()
if(CUTTER_ENABLE_CRASH_REPORTS)
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
target_link_libraries(Cutter PRIVATE Threads::Threads)
add_definitions(-DCUTTER_ENABLE_CRASH_REPORTS)
if (NOT WIN32)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g ")
endif()
find_package(Breakpad REQUIRED)
target_link_libraries(Cutter PRIVATE Breakpad::client)
endif()
target_link_libraries(Cutter PUBLIC ${QT_PREFIX}::Core ${QT_PREFIX}::Widgets ${QT_PREFIX}::Gui PRIVATE ${QT_PREFIX}::Svg ${QT_PREFIX}::Network)
if (CUTTER_QT6)
target_link_libraries(Cutter PUBLIC Qt6::Core5Compat Qt6::SvgWidgets)
@ -494,9 +533,17 @@ if(CUTTER_ENABLE_PYTHON)
endif()
target_link_libraries(Cutter PRIVATE ${PYTHON_LIBRARIES})
if(CUTTER_ENABLE_PYTHON_BINDINGS)
target_link_libraries(Cutter PRIVATE Shiboken2::libshiboken PySide2::pyside2)
if (CUTTER_QT6)
target_link_libraries(Cutter PRIVATE Shiboken6::libshiboken PySide6::pyside6)
else()
target_link_libraries(Cutter PRIVATE Shiboken2::libshiboken PySide2::pyside2)
endif()
get_target_property(RAW_BINDINGS_INCLUDE_DIRS Cutter INCLUDE_DIRECTORIES)
if(NOT CUTTER_USE_BUNDLED_RIZIN)
get_target_property(RAW_RIZIN_INCLUDE_DIRS ${RIZIN_TARGET} INTERFACE_INCLUDE_DIRECTORIES)
list(APPEND RAW_BINDINGS_INCLUDE_DIRS "${RAW_RIZIN_INCLUDE_DIRS}")
endif()
set(BINDINGS_INCLUDE_DIRS "")
foreach(_dir ${RAW_BINDINGS_INCLUDE_DIRS})
string(REGEX REPLACE "\\$<BUILD_INTERFACE:(.*)>" "\\1" _dir ${_dir})
@ -512,7 +559,11 @@ if(CUTTER_ENABLE_PYTHON)
list(APPEND BINDINGS_INCLUDE_DIRS "${_qt5Core_install_prefix}/include/QtGui")
list(APPEND BINDINGS_INCLUDE_DIRS "${_qt5Core_install_prefix}/include/QtWidgets")
endif()
list(APPEND BINDINGS_INCLUDE_DIRS ${Qt5Core_INCLUDE_DIRS} ${Qt5Widgets_INCLUDE_DIRS} ${Qt5Gui_INCLUDE_DIRS})
if (CUTTER_QT6)
list(APPEND BINDINGS_INCLUDE_DIRS ${Qt6Core_INCLUDE_DIRS} ${Qt6Widgets_INCLUDE_DIRS} ${Qt6Gui_INCLUDE_DIRS})
else()
list(APPEND BINDINGS_INCLUDE_DIRS ${Qt5Core_INCLUDE_DIRS} ${Qt5Widgets_INCLUDE_DIRS} ${Qt5Gui_INCLUDE_DIRS})
endif()
list(APPEND BINDINGS_INCLUDE_DIRS ${Rizin_INCLUDE_DIRS})
list(APPEND BINDINGS_INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}")
if (NOT WIN32)
@ -520,7 +571,7 @@ if(CUTTER_ENABLE_PYTHON)
endif()
configure_file("${BINDINGS_SRC_DIR}/bindings.txt.in" "${BINDINGS_BUILD_DIR}/bindings.txt")
add_compile_definitions(WIN32_LEAN_AND_MEAN)
add_definitions(-DWIN32_LEAN_AND_MEAN)
endif()
endif()
@ -552,17 +603,26 @@ install(TARGETS Cutter
EXPORT CutterTargets
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
BUNDLE DESTINATION "." # needs to be tested
ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT Devel
)
ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT Devel)
install(EXPORT CutterTargets
NAMESPACE Cutter::
DESTINATION "${ConfigPackageLocation}"
DESTINATION "${CUTTER_INSTALL_CONFIGDIR}"
COMPONENT Devel)
include(CMakePackageConfigHelpers)
configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/../cmake/CutterConfig.cmake.in
"${CMAKE_CURRENT_BINARY_DIR}/CutterConfig.cmake"
INSTALL_DESTINATION "${CUTTER_INSTALL_CONFIGDIR}"
NO_SET_AND_CHECK_MACRO
NO_CHECK_REQUIRED_COMPONENTS_MACRO)
write_basic_package_version_file(
"${CMAKE_CURRENT_BINARY_DIR}/CutterConfigVersion.cmake"
VERSION "${CUTTER_VERSION_FULL}"
COMPATIBILITY AnyNewerVersion)
install(FILES
../cmake/CutterConfig.cmake
DESTINATION ${ConfigPackageLocation}
COMPONENT Devel
)
"${CMAKE_CURRENT_BINARY_DIR}/CutterConfig.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/CutterConfigVersion.cmake"
DESTINATION ${CUTTER_INSTALL_CONFIGDIR}
COMPONENT Devel)
foreach(_file ${HEADER_FILES})
# Can't use target PUBLIC_HEADER option for installing due to multiple directories
get_filename_component(_header_dir "${_file}" DIRECTORY)
@ -570,8 +630,8 @@ foreach(_file ${HEADER_FILES})
endforeach()
if(UNIX AND NOT APPLE)
install (FILES "img/cutter.svg"
DESTINATION "share/icons/hicolor/scalable/apps/")
install(FILES "img/cutter.svg"
DESTINATION "share/icons/hicolor/scalable/apps/")
install(FILES "re.rizin.cutter.desktop"
DESTINATION "share/applications"
COMPONENT Devel)

View File

@ -1,5 +1,4 @@
#include "common/PythonManager.h"
#include "common/CrashHandler.h"
#include "CutterApplication.h"
#include "plugins/PluginManager.h"
#include "CutterConfig.h"
@ -34,21 +33,24 @@
// has RZ_GITTAP defined and uses it in rz_core_version().
// After that, RZ_GITTAP is not defined anymore and RZ_VERSION is used.
#ifdef RZ_GITTAP
#define CUTTER_COMPILE_TIME_RZ_VERSION "" RZ_GITTAP
# define CUTTER_COMPILE_TIME_RZ_VERSION "" RZ_GITTAP
#else
#define CUTTER_COMPILE_TIME_RZ_VERSION "" RZ_VERSION
# define CUTTER_COMPILE_TIME_RZ_VERSION "" RZ_VERSION
#endif
CutterApplication::CutterApplication(int &argc, char **argv) : QApplication(argc, argv)
{
// Setup application information
setApplicationVersion(CUTTER_VERSION_FULL);
#ifdef Q_OS_MACOS
setWindowIcon(QIcon(":/img/cutter_macos_simple.svg"));
#else
setWindowIcon(QIcon(":/img/cutter.svg"));
#endif
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
setAttribute(Qt::AA_UseHighDpiPixmaps); // always enabled on Qt >= 6.0.0
#endif
setLayoutDirection(Qt::LeftToRight);
// WARN!!! Put initialization code below this line. Code above this line is mandatory to be run
// First
@ -149,7 +151,7 @@ CutterApplication::CutterApplication(int &argc, char **argv) : QApplication(argc
}
mainWindow->displayNewFileDialog();
} else { // filename specified as positional argument
bool askOptions = (clOptions.analLevel != AutomaticAnalysisLevel::Ask)
bool askOptions = (clOptions.analysisLevel != AutomaticAnalysisLevel::Ask)
|| !clOptions.fileOpenOptions.projectFile.isEmpty();
mainWindow->openNewFile(clOptions.fileOpenOptions, askOptions);
}
@ -160,43 +162,29 @@ CutterApplication::CutterApplication(int &argc, char **argv) : QApplication(argc
appdir.cdUp(); // appdir
auto sleighHome = appdir;
sleighHome.cd(
"share/rizin/plugins/rz_ghidra_sleigh"); // appdir/share/rizin/plugins/rz_ghidra_sleigh
// appdir/lib/rizin/plugins/rz_ghidra_sleigh/
sleighHome.cd("lib/rizin/plugins/rz_ghidra_sleigh/");
Core()->setConfig("ghidra.sleighhome", sleighHome.absolutePath());
auto jsdecHome = appdir;
jsdecHome.cd("share/rizin/plugins/jsdec"); // appdir/share/rizin/plugins/jsdec
qputenv("JSDEC_HOME", jsdecHome.absolutePath().toLocal8Bit());
}
#endif
#ifdef Q_OS_MACOS
#if defined(Q_OS_MACOS) && defined(CUTTER_ENABLE_PACKAGING)
{
auto rzprefix = QDir(QCoreApplication::applicationDirPath()); // Contents/MacOS
rzprefix.cdUp(); // Contents
rzprefix.cd("Resources"); // Contents/Resources/rz
rzprefix.cd("Resources"); // Contents/Resources/
auto sleighHome = rzprefix;
sleighHome.cd(
"share/rizin/plugins/rz_ghidra_sleigh"); // Contents/Resources/rz/share/rizin/plugins/rz_ghidra_sleigh
// Contents/Resources/lib/rizin/plugins/rz_ghidra_sleigh
sleighHome.cd("lib/rizin/plugins/rz_ghidra_sleigh");
Core()->setConfig("ghidra.sleighhome", sleighHome.absolutePath());
# ifdef CUTTER_BUNDLE_JSDEC
auto jsdecHome = rzprefix;
jsdecHome.cd(
"share/rizin/plugins/jsdec"); // Contents/Resources/rz/share/rizin/plugins/jsdec
qputenv("JSDEC_HOME", jsdecHome.absolutePath().toLocal8Bit());
# endif
}
#endif
#if defined(Q_OS_WIN) && defined(CUTTER_ENABLE_PACKAGING)
{
# ifdef CUTTER_BUNDLE_JSDEC
qputenv("JSDEC_HOME", "lib\\plugins\\jsdec");
# endif
auto sleighHome = QDir(QCoreApplication::applicationDirPath());
sleighHome.cd("lib/plugins/rz_ghidra_sleigh");
sleighHome.cd("lib/rizin/plugins/rz_ghidra_sleigh");
Core()->setConfig("ghidra.sleighhome", sleighHome.absolutePath());
}
#endif
@ -308,6 +296,89 @@ bool CutterApplication::loadTranslations()
return false;
}
QStringList CutterApplication::getArgs() const
{
auto &options = clOptions.fileOpenOptions;
QStringList args;
switch (clOptions.analysisLevel) {
case AutomaticAnalysisLevel::None:
args.push_back("-A");
args.push_back("0");
break;
case AutomaticAnalysisLevel::AAA:
args.push_back("-A");
args.push_back("1");
break;
case AutomaticAnalysisLevel::AAAA:
args.push_back("-A");
args.push_back("2");
break;
default:
break;
}
if (!options.useVA) {
args.push_back("-P");
}
if (options.writeEnabled) {
args.push_back("-w");
}
if (!options.script.isEmpty()) {
args.push_back("-i");
args.push_back(options.script);
}
if (!options.projectFile.isEmpty()) {
args.push_back("-p");
args.push_back(options.projectFile);
}
if (!options.arch.isEmpty()) {
args.push_back("-a");
args.push_back(options.arch);
}
if (options.bits > 0) {
args.push_back("-b");
args.push_back(QString::asprintf("%d", options.bits));
}
if (!options.cpu.isEmpty()) {
args.push_back("-c");
args.push_back(options.cpu);
}
if (!options.os.isEmpty()) {
args.push_back("-o");
args.push_back(options.os);
}
switch (options.endian) {
case InitialOptions::Endianness::Little:
args.push_back("-e");
args.push_back("little");
break;
case InitialOptions::Endianness::Big:
args.push_back("-e");
args.push_back("big");
break;
default:
break;
}
if (!options.forceBinPlugin.isEmpty()) {
args.push_back("-F");
args.push_back(options.forceBinPlugin);
}
if (options.binLoadAddr != RVA_INVALID) {
args.push_back("-B");
args.push_back(RzAddressString(options.binLoadAddr));
}
if (options.mapAddr != RVA_INVALID) {
args.push_back("-m");
args.push_back(RzAddressString(options.mapAddr));
}
if (!options.filename.isEmpty()) {
args.push_back(options.filename);
}
return args;
}
bool CutterApplication::parseCommandLineOptions()
{
// Keep this function in sync with documentation
@ -327,6 +398,27 @@ bool CutterApplication::parseCommandLineOptions()
QObject::tr("level"));
cmd_parser.addOption(analOption);
QCommandLineOption archOption({ "a", "arch" }, QObject::tr("Sets a specific architecture name"),
QObject::tr("arch"));
cmd_parser.addOption(archOption);
QCommandLineOption bitsOption({ "b", "bits" }, QObject::tr("Sets a specific architecture bits"),
QObject::tr("bits"));
cmd_parser.addOption(bitsOption);
QCommandLineOption cpuOption({ "c", "cpu" }, QObject::tr("Sets a specific CPU"),
QObject::tr("cpu"));
cmd_parser.addOption(cpuOption);
QCommandLineOption osOption({ "o", "os" }, QObject::tr("Sets a specific operating system"),
QObject::tr("os"));
cmd_parser.addOption(osOption);
QCommandLineOption endianOption({ "e", "endian" },
QObject::tr("Sets the endianness (big or little)"),
QObject::tr("big|little"));
cmd_parser.addOption(endianOption);
QCommandLineOption formatOption({ "F", "format" },
QObject::tr("Force using a specific file format (bin plugin)"),
QObject::tr("name"));
@ -337,6 +429,11 @@ bool CutterApplication::parseCommandLineOptions()
QObject::tr("base address"));
cmd_parser.addOption(baddrOption);
QCommandLineOption maddrOption({ "m", "map" },
QObject::tr("Map the binary at a specific address"),
QObject::tr("map address"));
cmd_parser.addOption(maddrOption);
QCommandLineOption scriptOption("i", QObject::tr("Run script file"), QObject::tr("file"));
cmd_parser.addOption(scriptOption);
@ -348,6 +445,10 @@ bool CutterApplication::parseCommandLineOptions()
QObject::tr("Open file in write mode"));
cmd_parser.addOption(writeModeOption);
QCommandLineOption phyModeOption({ "P", "phymode" },
QObject::tr("Disables virtual addressing"));
cmd_parser.addOption(phyModeOption);
QCommandLineOption pythonHomeOption(
"pythonhome", QObject::tr("PYTHONHOME to use for embedded python interpreter"),
"PYTHONHOME");
@ -378,30 +479,30 @@ bool CutterApplication::parseCommandLineOptions()
opts.args = cmd_parser.positionalArguments();
if (cmd_parser.isSet(analOption)) {
bool analLevelSpecified = false;
int analLevel = cmd_parser.value(analOption).toInt(&analLevelSpecified);
bool analysisLevelSpecified = false;
int analysisLevel = cmd_parser.value(analOption).toInt(&analysisLevelSpecified);
if (!analLevelSpecified || analLevel < 0 || analLevel > 2) {
if (!analysisLevelSpecified || analysisLevel < 0 || analysisLevel > 2) {
fprintf(stderr, "%s\n",
QObject::tr("Invalid Analysis Level. May be a value between 0 and 2.")
.toLocal8Bit()
.constData());
return false;
}
switch (analLevel) {
switch (analysisLevel) {
case 0:
opts.analLevel = AutomaticAnalysisLevel::None;
opts.analysisLevel = AutomaticAnalysisLevel::None;
break;
case 1:
opts.analLevel = AutomaticAnalysisLevel::AAA;
opts.analysisLevel = AutomaticAnalysisLevel::AAA;
break;
case 2:
opts.analLevel = AutomaticAnalysisLevel::AAAA;
opts.analysisLevel = AutomaticAnalysisLevel::AAAA;
break;
}
}
if (opts.args.empty() && opts.analLevel != AutomaticAnalysisLevel::Ask) {
if (opts.args.empty() && opts.analysisLevel != AutomaticAnalysisLevel::Ask) {
fprintf(stderr, "%s\n",
QObject::tr("Filename must be specified to start analysis automatically.")
.toLocal8Bit()
@ -409,33 +510,67 @@ bool CutterApplication::parseCommandLineOptions()
return false;
}
InitialOptions options;
if (!opts.args.isEmpty()) {
opts.fileOpenOptions.filename = opts.args[0];
opts.fileOpenOptions.forceBinPlugin = cmd_parser.value(formatOption);
if (cmd_parser.isSet(baddrOption)) {
bool ok;
bool ok = false;
RVA baddr = cmd_parser.value(baddrOption).toULongLong(&ok, 0);
if (ok) {
options.binLoadAddr = baddr;
opts.fileOpenOptions.binLoadAddr = baddr;
}
}
switch (opts.analLevel) {
if (cmd_parser.isSet(maddrOption)) {
bool ok = false;
RVA maddr = cmd_parser.value(maddrOption).toULongLong(&ok, 0);
if (ok) {
opts.fileOpenOptions.mapAddr = maddr;
}
}
switch (opts.analysisLevel) {
case AutomaticAnalysisLevel::Ask:
break;
case AutomaticAnalysisLevel::None:
opts.fileOpenOptions.analCmd = {};
opts.fileOpenOptions.analysisCmd = {};
break;
case AutomaticAnalysisLevel::AAA:
opts.fileOpenOptions.analCmd = { { "aaa", "Auto analysis" } };
opts.fileOpenOptions.analysisCmd = { { "aaa", "Auto analysis" } };
break;
case AutomaticAnalysisLevel::AAAA:
opts.fileOpenOptions.analCmd = { { "aaaa", "Auto analysis (experimental)" } };
opts.fileOpenOptions.analysisCmd = { { "aaaa", "Auto analysis (experimental)" } };
break;
}
opts.fileOpenOptions.script = cmd_parser.value(scriptOption);
opts.fileOpenOptions.arch = cmd_parser.value(archOption);
opts.fileOpenOptions.cpu = cmd_parser.value(cpuOption);
opts.fileOpenOptions.os = cmd_parser.value(osOption);
if (cmd_parser.isSet(bitsOption)) {
bool ok = false;
int bits = cmd_parser.value(bitsOption).toInt(&ok, 10);
if (ok && bits > 0) {
opts.fileOpenOptions.bits = bits;
}
}
if (cmd_parser.isSet(endianOption)) {
QString endian = cmd_parser.value(endianOption).toLower();
opts.fileOpenOptions.endian = InitialOptions::Endianness::Auto;
if (endian == "little") {
opts.fileOpenOptions.endian = InitialOptions::Endianness::Little;
} else if (endian == "big") {
opts.fileOpenOptions.endian = InitialOptions::Endianness::Big;
} else {
fprintf(stderr, "%s\n",
QObject::tr("Invalid Endianness. You can only set it to `big` or `little`.")
.toLocal8Bit()
.constData());
return false;
}
} else {
opts.fileOpenOptions.endian = InitialOptions::Endianness::Auto;
}
opts.fileOpenOptions.writeEnabled = cmd_parser.isSet(writeModeOption);
opts.fileOpenOptions.useVA = !cmd_parser.isSet(phyModeOption);
}
opts.fileOpenOptions.projectFile = cmd_parser.value(projectOption);

View File

@ -13,7 +13,7 @@ enum class AutomaticAnalysisLevel { Ask, None, AAA, AAAA };
struct CutterCommandLineOptions
{
QStringList args;
AutomaticAnalysisLevel analLevel = AutomaticAnalysisLevel::Ask;
AutomaticAnalysisLevel analysisLevel = AutomaticAnalysisLevel::Ask;
InitialOptions fileOpenOptions;
QString pythonHome;
bool outputRedirectionEnabled = true;
@ -33,6 +33,10 @@ public:
void launchNewInstance(const QStringList &args = {});
InitialOptions getInitialOptions() const { return clOptions.fileOpenOptions; }
void setInitialOptions(const InitialOptions &options) { clOptions.fileOpenOptions = options; }
QStringList getArgs() const;
protected:
bool event(QEvent *e);
@ -50,6 +54,7 @@ private:
private:
bool m_FileAlreadyDropped;
CutterCore core;
MainWindow *mainWindow;
CutterCommandLineOptions clOptions;
};

View File

@ -3,7 +3,6 @@
#include "core/MainWindow.h"
#include "common/UpdateWorker.h"
#include "CutterConfig.h"
#include "common/CrashHandler.h"
#include "common/SettingsUpgrade.h"
#include <QJsonObject>
@ -12,27 +11,30 @@
/**
* @brief Attempt to connect to a parent console and configure outputs.
*
* @note Doesn't do anything if the exe wasn't executed from a console.
*/
#ifdef Q_OS_WIN
#include <windows.h>
static void connectToConsole()
{
if (!AttachConsole(ATTACH_PARENT_PROCESS)) {
return;
}
BOOL attached = AttachConsole(ATTACH_PARENT_PROCESS);
// Avoid reconfiguring stderr/stdout if one of them is already connected to a stream.
// Avoid reconfiguring stdin/stderr/stdout if one of them is already connected to a stream.
// This can happen when running with stdout/stderr redirected to a file.
if (0 > fileno(stdout)) {
// Overwrite FD 1 and 2 for the benefit of any code that uses the FDs
if (0 > fileno(stdin)) {
// Overwrite FD 0, FD 1 and 2 for the benefit of any code that uses the FDs
// directly. This is safe because the CRT allocates FDs 0, 1 and
// 2 at startup even if they don't have valid underlying Windows
// handles. This means we won't be overwriting an FD created by
// _open() after startup.
_close(0);
freopen(attached ? "CONIN$" : "NUL", "r+", stdin);
}
if (0 > fileno(stdout)) {
_close(1);
if (freopen("CONOUT$", "a+", stdout)) {
if (freopen(attached ? "CONOUT$" : "NUL", "a+", stdout)) {
// Avoid buffering stdout/stderr since IOLBF is replaced by IOFBF in Win32.
setvbuf(stdout, nullptr, _IONBF, 0);
}
@ -40,7 +42,7 @@ static void connectToConsole()
if (0 > fileno(stderr)) {
_close(2);
if (freopen("CONOUT$", "a+", stderr)) {
if (freopen(attached ? "CONOUT$" : "NUL", "a+", stderr)) {
setvbuf(stderr, nullptr, _IONBF, 0);
}
}
@ -52,17 +54,6 @@ static void connectToConsole()
int main(int argc, char *argv[])
{
#ifdef CUTTER_ENABLE_CRASH_REPORTS
if (argc >= 3 && QString::fromLocal8Bit(argv[1]) == "--start-crash-handler") {
QApplication app(argc, argv);
QString dumpLocation = QString::fromLocal8Bit(argv[2]);
showCrashDialog(dumpLocation);
return 0;
}
initCrashHandler();
#endif
#ifdef Q_OS_WIN
connectToConsole();
#endif
@ -71,6 +62,9 @@ int main(int argc, char *argv[])
qRegisterMetaType<QList<FunctionDescription>>();
QCoreApplication::setOrganizationName("rizin");
#ifndef Q_OS_MACOS // don't set on macOS so that it doesn't affect config path there
QCoreApplication::setOrganizationDomain("rizin.re");
#endif
QCoreApplication::setApplicationName("cutter");
// Importing settings after setting rename, needs separate handling in addition to regular version to version upgrade.

View File

@ -3,7 +3,7 @@
generator-set = shiboken
header-file = ${BINDINGS_SRC_DIR}/bindings.h
typesystem-file = ${BINDINGS_SRC_DIR}/bindings.xml
typesystem-file = ${BINDINGS_BUILD_DIR}/bindings.xml
output-directory = ${BINDINGS_BUILD_DIR}
@ -14,4 +14,4 @@ typesystem-paths = ${PYSIDE_TYPESYSTEMS}
enable-parent-ctor-heuristic
enable-pyside-extensions
enable-return-value-heuristic
use-isnull-as-nb_nonzero
use-isnull-as-nb_nonzero

View File

@ -30,7 +30,7 @@
PyErr_Print();
return QString();
}
PythonToCppFunc pythonToCpp = Shiboken::Conversions::isPythonToCppConvertible(SbkPySide2_QtCoreTypeConverters[SBK_QSTRING_IDX], pyResult);
PythonToCppFunc pythonToCpp = Shiboken::Conversions::isPythonToCppConvertible(Sbk${PYSIDE_NAME}_QtCoreTypeConverters[SBK_QSTRING_IDX], pyResult);
if (!pythonToCpp) {
Shiboken::warning(PyExc_RuntimeWarning, 2, "Invalid return value for plugin metadata VAR_NAME, expected %s, got %s.", "QString", Py_TYPE(pyResult)->tp_name);
return ::QString();

View File

@ -9,7 +9,7 @@ script_path = os.path.dirname(os.path.realpath(__file__))
def get_cpp_files_gen(args, include_package=True):
ts_tree = et.parse(os.path.join(script_path, "bindings.xml"))
ts_tree = et.parse(os.path.join(script_path, "bindings.xml.in"))
ts_root = ts_tree.getroot()
package = ts_root.attrib["package"]

View File

@ -13,11 +13,17 @@ AddressableFilterProxyModel::AddressableFilterProxyModel(AddressableItemModelI *
RVA AddressableFilterProxyModel::address(const QModelIndex &index) const
{
if (!addressableSourceModel) {
return RVA_INVALID;
}
return addressableSourceModel->address(this->mapToSource(index));
}
QString AddressableFilterProxyModel::name(const QModelIndex &index) const
{
if (!addressableSourceModel) {
return QString();
}
return addressableSourceModel->name(this->mapToSource(index));
}
@ -28,6 +34,6 @@ void AddressableFilterProxyModel::setSourceModel(QAbstractItemModel *)
void AddressableFilterProxyModel::setSourceModel(AddressableItemModelI *sourceModel)
{
ParentClass::setSourceModel(sourceModel->asItemModel());
ParentClass::setSourceModel(sourceModel ? sourceModel->asItemModel() : nullptr);
addressableSourceModel = sourceModel;
}

View File

@ -1,32 +1,33 @@
#include "core/Cutter.h"
#include "common/AnalTask.h"
#include "common/AnalysisTask.h"
#include "core/MainWindow.h"
#include "dialogs/InitialOptionsDialog.h"
#include <QJsonArray>
#include <QDebug>
#include <QCheckBox>
AnalTask::AnalTask() : AsyncTask() {}
AnalysisTask::AnalysisTask() : AsyncTask() {}
AnalTask::~AnalTask() {}
AnalysisTask::~AnalysisTask() {}
void AnalTask::interrupt()
void AnalysisTask::interrupt()
{
AsyncTask::interrupt();
rz_cons_singleton()->context->breaked = true;
}
QString AnalTask::getTitle()
QString AnalysisTask::getTitle()
{
// If no file is loaded we consider it's Initial Analysis
QJsonArray openedFiles = Core()->getOpenedFiles();
if (!openedFiles.size()) {
RzCoreLocked core(Core());
RzList *descs = rz_id_storage_list(core->io->files);
if (rz_list_empty(descs)) {
return tr("Initial Analysis");
}
return tr("Analyzing Program");
}
void AnalTask::runTask()
void AnalysisTask::runTask()
{
int perms = RZ_PERM_RX;
if (options.writeEnabled) {
@ -38,8 +39,9 @@ void AnalTask::runTask()
Core()->setConfig("bin.demangle", options.demangle);
// Do not reload the file if already loaded
QJsonArray openedFiles = Core()->getOpenedFiles();
if (!openedFiles.size() && options.filename.length()) {
RzCoreLocked core(Core());
RzList *descs = rz_id_storage_list(core->io->files);
if (rz_list_empty(descs) && options.filename.length()) {
log(tr("Loading the file..."));
openFailed = false;
bool fileLoaded =
@ -62,7 +64,8 @@ void AnalTask::runTask()
}
if (!options.os.isNull()) {
Core()->cmdRaw("e asm.os=" + options.os);
RzCoreLocked core(Core());
rz_config_set(core->config, "asm.os", options.os.toUtf8().constData());
}
if (!options.pdbFile.isNull()) {
@ -76,14 +79,14 @@ void AnalTask::runTask()
if (!options.shellcode.isNull() && options.shellcode.size() / 2 > 0) {
log(tr("Loading shellcode..."));
Core()->cmdRaw("wx " + options.shellcode);
rz_core_write_hexpair(core, core->offset, options.shellcode.toStdString().c_str());
}
if (options.endian != InitialOptions::Endianness::Auto) {
Core()->setEndianness(options.endian == InitialOptions::Endianness::Big);
}
Core()->cmdRaw("fs *");
rz_flag_space_set(core->flags, "*");
if (!options.script.isNull()) {
log(tr("Executing script..."));
@ -94,9 +97,9 @@ void AnalTask::runTask()
return;
}
if (!options.analCmd.empty()) {
if (!options.analysisCmd.empty()) {
log(tr("Executing analysis..."));
for (const CommandDescription &cmd : options.analCmd) {
for (const CommandDescription &cmd : options.analysisCmd) {
if (isInterrupted()) {
return;
}

View File

@ -9,13 +9,13 @@ class CutterCore;
class MainWindow;
class InitialOptionsDialog;
class AnalTask : public AsyncTask
class AnalysisTask : public AsyncTask
{
Q_OBJECT
public:
explicit AnalTask();
~AnalTask();
explicit AnalysisTask();
~AnalysisTask();
QString getTitle() override;

View File

@ -2,21 +2,14 @@
BasicBlockHighlighter::BasicBlockHighlighter() {}
BasicBlockHighlighter::~BasicBlockHighlighter()
{
for (BasicBlockIt itr = bbMap.begin(); itr != bbMap.end(); ++itr) {
delete itr->second;
}
}
/**
* @brief Highlight the basic block at address
*/
void BasicBlockHighlighter::highlight(RVA address, const QColor &color)
{
BasicBlock *block = new BasicBlock;
block->address = address;
block->color = color;
BasicBlock block;
block.address = address;
block.color = color;
bbMap[address] = block;
}
@ -33,13 +26,11 @@ void BasicBlockHighlighter::clear(RVA address)
*
* If there is nothing to highlight at specified address, returns nullptr
*/
BasicBlock *BasicBlockHighlighter::getBasicBlock(RVA address)
BasicBlockHighlighter::BasicBlock *BasicBlockHighlighter::getBasicBlock(RVA address)
{
BasicBlockIt it;
it = bbMap.find(address);
auto it = bbMap.find(address);
if (it != bbMap.end()) {
return it->second;
return &it->second;
}
return nullptr;

View File

@ -6,26 +6,23 @@ class BasicBlockHighlighter;
#include "Cutter.h"
#include <map>
struct BasicBlock
{
RVA address;
QColor color;
};
typedef std::map<RVA, BasicBlock *>::iterator BasicBlockIt;
class BasicBlockHighlighter
{
public:
struct BasicBlock
{
RVA address;
QColor color;
};
BasicBlockHighlighter();
~BasicBlockHighlighter();
void highlight(RVA address, const QColor &color);
void clear(RVA address);
BasicBlock *getBasicBlock(RVA address);
private:
std::map<RVA, BasicBlock *> bbMap;
std::map<RVA, BasicBlock> bbMap;
};
#endif // BASICBLOCKHIGHLIGHTER_H

View File

@ -8,38 +8,35 @@
void openIssue()
{
RzCoreLocked core(Core());
RzBinFile *bf = rz_bin_cur(core->bin);
RzBinInfo *info = rz_bin_get_info(core->bin);
RzBinPlugin *plugin = rz_bin_file_cur_plugin(bf);
QString url, osInfo, format, arch, type;
// Pull in info needed for git issue
osInfo = QSysInfo::productType() + " "
+ (QSysInfo::productVersion() == "unknown" ? "" : QSysInfo::productVersion());
QJsonDocument docu = Core()->getFileInfo();
QJsonObject coreObj = docu.object()["core"].toObject();
QJsonObject binObj = docu.object()["bin"].toObject();
if (!binObj.QJsonObject::isEmpty()) {
format = coreObj["format"].toString();
arch = binObj["arch"].toString();
if (!binObj["type"].isUndefined()) {
type = coreObj["type"].toString();
} else {
type = "N/A";
}
} else {
format = coreObj["format"].toString();
arch = "N/A";
type = "N/A";
}
format = plugin && RZ_STR_ISNOTEMPTY(plugin->name) ? plugin->name : "N/A";
arch = info && RZ_STR_ISNOTEMPTY(info->arch) ? info->arch : "N/A";
type = info && RZ_STR_ISNOTEMPTY(info->type) ? info->type : "N/A";
url = "https://github.com/rizinorg/cutter/issues/new?&body=**Environment information**\n* "
"Operating System: "
+ osInfo + "\n* Cutter version: " + CUTTER_VERSION_FULL + "\n* File format: " + format
+ "\n * Arch: " + arch + "\n * Type: " + type
+ "\n\n**Describe the bug**\nA clear and concise description of what the bug "
"is.\n\n**To Reproduce**\n"
+ osInfo + "\n* Cutter version: " + CUTTER_VERSION_FULL + "\n* Obtained from:\n"
+ " - [ ] Built from source\n - [ ] Downloaded release from Cutter website or GitHub "
"\n"
" - [ ] Distribution repository\n* File format: "
+ format + "\n * Arch: " + arch + "\n * Type: " + type
+ "\n\n**Describe the bug**\n\n<!-- A clear and concise description of what the bug "
"is. -->"
"\n\n**To Reproduce**\n\n"
"Steps to reproduce the behavior:\n1. Go to '...'\n2. Click on '....'\n3. Scroll "
"down to '....'\n"
"4. See error\n\n**Expected behavior**\n"
"A clear and concise description of what you expected to happen.\n\n"
"**Screenshots**\nIf applicable, add screenshots to help explain your problem.\n\n"
"**Additional context**\nAdd any other context about the problem here.";
"4. See error\n\n**Expected behavior**\n\n"
"<!-- A clear and concise description of what you expected to happen. -->\n\n\n"
"**Screenshots**\n\n<!-- If applicable, add screenshots to help explain your "
"problem. -->\n\n\n"
"**Additional context**\n\n<!-- Add any other context about the problem here. -->\n";
QDesktopServices::openUrl(QUrl(url, QUrl::TolerantMode));
}

View File

@ -6,6 +6,7 @@
#include <QJsonArray>
#include <QStandardPaths>
#include <QRegularExpression>
#include <rz_util/rz_path.h>
#include "common/Configuration.h"
@ -29,16 +30,15 @@ const QStringList ColorThemeWorker::rizinUnusedOptions = {
ColorThemeWorker::ColorThemeWorker(QObject *parent) : QObject(parent)
{
char *szThemes = rz_str_home(RZ_HOME_THEMES);
char *szThemes = rz_path_home_prefix(RZ_THEMES);
customRzThemesLocationPath = szThemes;
rz_mem_free(szThemes);
if (!QDir(customRzThemesLocationPath).exists()) {
QDir().mkpath(customRzThemesLocationPath);
}
QDir currDir {
QStringLiteral("%1%2%3").arg(rz_sys_prefix(nullptr)).arg(RZ_SYS_DIR).arg(RZ_THEMES)
};
char *theme_dir = rz_path_prefix(RZ_THEMES);
QDir currDir { theme_dir };
if (currDir.exists()) {
standardRzThemesLocationPath = currDir.absolutePath();
} else {
@ -47,6 +47,7 @@ ColorThemeWorker::ColorThemeWorker(QObject *parent) : QObject(parent)
"Most likely, Rizin is not properly installed.")
.arg(currDir.path()));
}
free(theme_dir);
}
QColor ColorThemeWorker::mergeColors(const QColor &upper, const QColor &lower) const
@ -77,27 +78,15 @@ QString ColorThemeWorker::copy(const QString &srcThemeName, const QString &copyT
return save(getTheme(srcThemeName), copyThemeName);
}
QString ColorThemeWorker::save(const QJsonDocument &theme, const QString &themeName) const
QString ColorThemeWorker::save(const Theme &theme, const QString &themeName) const
{
QFile fOut(QDir(customRzThemesLocationPath).filePath(themeName));
if (!fOut.open(QFile::WriteOnly | QFile::Truncate)) {
return tr("The file <b>%1</b> cannot be opened.").arg(QFileInfo(fOut).filePath());
}
QJsonObject obj = theme.object();
for (auto it = obj.constBegin(); it != obj.constEnd(); it++) {
QJsonArray arr = it.value().toArray();
QColor color;
if (arr.isEmpty()) {
color = it.value().toVariant().value<QColor>();
} else if (arr.size() == 4) {
color = QColor(arr[0].toInt(), arr[1].toInt(), arr[2].toInt(), arr[3].toInt());
} else if (arr.size() == 3) {
color = QColor(arr[0].toInt(), arr[1].toInt(), arr[2].toInt());
} else {
continue;
}
for (auto it = theme.constBegin(); it != theme.constEnd(); it++) {
const QColor &color = it.value();
if (cutterSpecificOptions.contains(it.key())) {
fOut.write(QString("#~%1 rgb:%2\n")
.arg(it.key(), color.name(QColor::HexArgb).remove('#'))
@ -124,24 +113,18 @@ bool ColorThemeWorker::isThemeExist(const QString &name) const
return themes.contains(name);
}
QJsonDocument ColorThemeWorker::getTheme(const QString &themeName) const
ColorThemeWorker::Theme ColorThemeWorker::getTheme(const QString &themeName) const
{
int r, g, b, a;
QVariantMap theme;
Theme theme;
QString curr = Config()->getColorTheme();
if (themeName != curr) {
Core()->cmdRaw(QString("eco %1").arg(themeName));
theme = Core()->cmdj("ecj").object().toVariantMap();
Core()->cmdRaw(QString("eco %1").arg(curr));
RzCoreLocked core(Core());
rz_core_theme_load(core, themeName.toUtf8().constData());
theme = Core()->getTheme();
rz_core_theme_load(core, curr.toUtf8().constData());
} else {
theme = Core()->cmdj("ecj").object().toVariantMap();
}
for (auto it = theme.begin(); it != theme.end(); it++) {
auto arr = it.value().toList();
QColor(arr[0].toInt(), arr[1].toInt(), arr[2].toInt()).getRgb(&r, &g, &b, &a);
theme[it.key()] = QJsonArray({ r, g, b, a });
theme = Core()->getTheme();
}
ColorFlags colorFlags = ColorFlags::DarkFlag;
@ -150,14 +133,13 @@ QJsonDocument ColorThemeWorker::getTheme(const QString &themeName) const
}
for (auto &it : cutterSpecificOptions) {
Configuration::cutterOptionColors[it][colorFlags].getRgb(&r, &g, &b, &a);
theme.insert(it, QJsonArray { r, g, b, a });
theme.insert(it, QColor(Configuration::cutterOptionColors[it][colorFlags]));
}
if (isCustomTheme(themeName)) {
QFile src(QDir(customRzThemesLocationPath).filePath(themeName));
if (!src.open(QFile::ReadOnly)) {
return QJsonDocument();
return {};
}
QStringList sl;
for (auto &line : QString(src.readAll()).split('\n', CUTTER_QT_SKIP_EMPTY_PARTS)) {
@ -167,8 +149,7 @@ QJsonDocument ColorThemeWorker::getTheme(const QString &themeName) const
if (sl.size() != 3 || sl[0][0] == '#') {
continue;
}
QColor(sl[2]).getRgb(&r, &g, &b, &a);
theme.insert(sl[1], QJsonArray({ r, g, b, a }));
theme.insert(sl[1], QColor(sl[2]));
}
}
@ -176,21 +157,7 @@ QJsonDocument ColorThemeWorker::getTheme(const QString &themeName) const
theme.remove(key);
}
// manualy converting instead of using QJsonObject::fromVariantMap because
// Qt < 5.6 QJsonValue.fromVariant doesn't expect QVariant to already contain
// QJson values like QJsonArray.
// https://github.com/qt/qtbase/commit/26237f0a2d8db80024b601f676bbce54d483e672
QJsonObject obj;
for (auto it = theme.begin(); it != theme.end(); it++) {
auto &value = it.value();
if (value.canConvert<QJsonArray>()) {
obj[it.key()] = it.value().value<QJsonArray>();
} else {
obj[it.key()] = QJsonValue::fromVariant(value);
}
}
return QJsonDocument(obj);
return theme;
}
QString ColorThemeWorker::deleteTheme(const QString &themeName) const
@ -276,13 +243,11 @@ bool ColorThemeWorker::isFileTheme(const QString &filePath, bool *ok) const
}
const QString colors = "black|red|white|green|magenta|yellow|cyan|blue|gray|none";
QString options = (Core()->cmdj("ecj").object().keys() << cutterSpecificOptions)
.join('|')
.replace(".", "\\.");
QString options =
(Core()->getThemeKeys() << cutterSpecificOptions).join('|').replace(".", "\\.");
QString pattern = QString("((ec\\s+(%1)\\s+(((rgb:|#)[0-9a-fA-F]{3,8})|(%2))))\\s*")
.arg(options)
.arg(colors);
QString pattern =
QString("((ec\\s+(%1)\\s+(((rgb:|#)[0-9a-fA-F]{3,8})|(%2))))\\s*").arg(options, colors);
// The below construct mimics the behaviour of QRegexP::exactMatch(), which was here before
QRegularExpression regexp("\\A(?:" + pattern + ")\\z");
@ -313,7 +278,7 @@ QStringList ColorThemeWorker::customThemes() const
const QStringList &ColorThemeWorker::getRizinSpecificOptions()
{
if (rizinSpecificOptions.isEmpty()) {
rizinSpecificOptions = Core()->cmdj("ecj").object().keys();
rizinSpecificOptions << Core()->getThemeKeys();
}
return rizinSpecificOptions;
}

View File

@ -5,6 +5,7 @@
#include <QColor>
#include <QObject>
#include "Cutter.h"
#include <QJsonDocument>
#include <QJsonObject>
#define ThemeWorker() (ColorThemeWorker::instance())
@ -17,6 +18,8 @@ class ColorThemeWorker : public QObject
{
Q_OBJECT
public:
typedef QHash<QString, QColor> Theme;
/**
* @brief cutterSpecificOptions is list of all available Cutter-only color options.
*/
@ -53,7 +56,7 @@ public:
* Name of theme to save.
* @return "" on success or error message.
*/
QString save(const QJsonDocument &theme, const QString &themeName) const;
QString save(const Theme &theme, const QString &themeName) const;
/**
* @brief Returns whether or not @a themeName theme is custom (created by user or imported) or
@ -70,12 +73,11 @@ public:
bool isThemeExist(const QString &name) const;
/**
* @brief Returns theme as Json where key is option name and value is array of 3 Ints (Red,
* Green, Blue).
* @brief Returns theme as QHash where key is option name and value is QColor.
* @param themeName
* Theme to get.
*/
QJsonDocument getTheme(const QString &themeName) const;
Theme getTheme(const QString &themeName) const;
/**
* @brief Deletes theme named @a themeName.

View File

@ -7,9 +7,9 @@
#include <QApplication>
#ifdef CUTTER_ENABLE_KSYNTAXHIGHLIGHTING
# include <KSyntaxHighlighting/repository.h>
# include <KSyntaxHighlighting/theme.h>
# include <KSyntaxHighlighting/definition.h>
# include <KSyntaxHighlighting/Repository>
# include <KSyntaxHighlighting/Theme>
# include <KSyntaxHighlighting/Definition>
#endif
#include "common/ColorThemeWorker.h"
@ -21,10 +21,16 @@
* and for light - only light ones.
*/
const QHash<QString, ColorFlags> Configuration::relevantThemes = {
{ "ayu", DarkFlag }, { "consonance", DarkFlag }, { "darkda", DarkFlag },
{ "onedark", DarkFlag }, { "solarized", DarkFlag }, { "zenburn", DarkFlag },
{ "cutter", LightFlag }, { "dark", LightFlag }, { "matrix", LightFlag },
{ "tango", LightFlag }, { "white", LightFlag }
{ "ayu", DarkFlag }, { "basic", DarkFlag }, { "behelit", DarkFlag },
{ "bold", DarkFlag }, { "bright", DarkFlag }, { "consonance", DarkFlag },
{ "darkda", DarkFlag }, { "defragger", DarkFlag }, { "focus", DarkFlag },
{ "gentoo", DarkFlag }, { "lima", DarkFlag }, { "monokai", DarkFlag },
{ "ogray", DarkFlag }, { "onedark", DarkFlag }, { "pink", DarkFlag },
{ "rasta", DarkFlag }, { "sepia", DarkFlag }, { "smyck", DarkFlag },
{ "solarized", DarkFlag }, { "twilight", DarkFlag }, { "white2", DarkFlag },
{ "xvilka", DarkFlag }, { "zenburn", DarkFlag }, { "cga", LightFlag },
{ "cutter", LightFlag }, { "dark", LightFlag }, { "gb", LightFlag },
{ "matrix", LightFlag }, { "tango", LightFlag }, { "white", LightFlag }
};
static const QString DEFAULT_LIGHT_COLOR_THEME = "cutter";
static const QString DEFAULT_DARK_COLOR_THEME = "ayu";
@ -39,7 +45,9 @@ const QHash<QString, QHash<ColorFlags, QColor>> Configuration::cutterOptionColor
{ "gui.item_invalid",
{ { DarkFlag, QColor(0x9b, 0x9b, 0x9b) }, { LightFlag, QColor(0x9b, 0x9b, 0x9b) } } },
{ "gui.main",
{ { DarkFlag, QColor(0x00, 0x80, 0x00) }, { LightFlag, QColor(0x00, 0x80, 0x00) } } },
{ { DarkFlag, QColor(0x21, 0xd8, 0x93) }, { LightFlag, QColor(0x00, 0x80, 0x00) } } },
{ "gui.flirt",
{ { DarkFlag, QColor(0xd8, 0xbb, 0x21) }, { LightFlag, QColor(0xf1, 0xc4, 0x0f) } } },
{ "gui.item_unsafe",
{ { DarkFlag, QColor(0xff, 0x81, 0x7b) }, { LightFlag, QColor(0xff, 0x81, 0x7b) } } },
{ "gui.navbar.seek",
@ -135,8 +143,8 @@ Configuration::Configuration() : QObject(), nativePalette(qApp->palette())
mPtr = this;
if (!s.isWritable()) {
QMessageBox::critical(
nullptr, tr("Critical!"),
tr("!!! Settings are not writable! Make sure you have a write access to \"%1\"")
nullptr, tr("Critical Error!"),
tr("Settings are not writable! Make sure you have a write access to \"%1\".")
.arg(s.fileName()));
}
#ifdef CUTTER_ENABLE_KSYNTAXHIGHLIGHTING
@ -193,7 +201,6 @@ void Configuration::resetAll()
{
// Don't reset all rizin vars, that currently breaks a bunch of stuff.
// settingsFile.remove()+loadInitials() should reset all settings configurable using Cutter GUI.
// Core()->cmdRaw("e-");
Core()->setSettings();
// Delete the file so no extra configuration is in it.
@ -244,8 +251,8 @@ bool Configuration::setLocaleByName(const QString &language)
QLocale::matchingLocales(QLocale::AnyLanguage, QLocale::AnyScript, QLocale::AnyCountry);
for (auto &it : allLocales) {
if (QString::compare(it.nativeLanguageName(), language, Qt::CaseInsensitive) == 0 ||
it.name() == language) {
if (QString::compare(it.nativeLanguageName(), language, Qt::CaseInsensitive) == 0
|| it.name() == language) {
setLocale(it);
return true;
}
@ -281,6 +288,15 @@ void Configuration::loadNativeStylesheet()
f.open(QFile::ReadOnly | QFile::Text);
QTextStream ts(&f);
QString stylesheet = ts.readAll();
#ifdef Q_OS_MACOS
QFile mf(nativeWindowIsDark() ? ":native/native-macos-dark.qss"
: ":native/native-macos-light.qss");
if (mf.exists()) {
mf.open(QFile::ReadOnly | QFile::Text);
QTextStream mts(&mf);
stylesheet += "\n" + mts.readAll();
}
#endif
qApp->setStyleSheet(stylesheet);
}
@ -512,20 +528,16 @@ const QColor Configuration::getColor(const QString &name) const
void Configuration::setColorTheme(const QString &theme)
{
if (theme == "default") {
Core()->cmdRaw("ecd");
rz_cons_pal_init(Core()->core()->cons->context);
s.setValue("theme", "default");
} else {
Core()->cmdRaw(QStringLiteral("eco %1").arg(theme));
rz_core_theme_load(Core()->core(), theme.toUtf8().constData());
s.setValue("theme", theme);
}
QJsonObject colorTheme = ThemeWorker().getTheme(theme).object();
ColorThemeWorker::Theme colorTheme = ThemeWorker().getTheme(theme);
for (auto it = colorTheme.constBegin(); it != colorTheme.constEnd(); it++) {
QJsonArray rgb = it.value().toArray();
if (rgb.size() != 4) {
continue;
}
setColor(it.key(), QColor(rgb[0].toInt(), rgb[1].toInt(), rgb[2].toInt(), rgb[3].toInt()));
setColor(it.key(), it.value());
}
emit colorsUpdated();
@ -652,11 +664,13 @@ QStringList Configuration::getAvailableTranslations()
for (auto i : fileNames) {
QString localeName = i.mid(sizeof("cutter_") - 1, 2); // TODO:#2321 don't asume 2 characters
// language code is sometimes 3 characters, and there could also be language_COUNTRY. Qt supports that.
// language code is sometimes 3 characters, and there could also be language_COUNTRY. Qt
// supports that.
QLocale locale(localeName);
if (locale.language() != QLocale::C) {
currLanguageName = locale.nativeLanguageName();
if (currLanguageName.isEmpty()) { // Qt doesn't have native language name for some languages
if (currLanguageName
.isEmpty()) { // Qt doesn't have native language name for some languages
currLanguageName = QLocale::languageToString(locale.language());
}
if (!currLanguageName.isEmpty()) {
@ -759,6 +773,16 @@ bool Configuration::getOutputRedirectionEnabled() const
return outputRedirectEnabled;
}
void Configuration::setPreviewValue(bool checked)
{
s.setValue("asm.preview", checked);
}
bool Configuration::getPreviewValue() const
{
return s.value("asm.preview").toBool();
}
bool Configuration::getGraphBlockEntryOffset()
{
return s.value("graphBlockEntryOffset", true).value<bool>();
@ -796,3 +820,13 @@ void Configuration::addRecentProject(QString file)
files.prepend(file);
setRecentProjects(files);
}
QString Configuration::getFunctionsWidgetLayout()
{
return s.value("functionsWidgetLayout").toString();
}
void Configuration::setFunctionsWidgetLayout(const QString &layout)
{
s.setValue("functionsWidgetLayout", layout);
}

View File

@ -167,6 +167,17 @@ public:
void setGraphMinFontSize(int sz) { s.setValue("graph.minfontsize", sz); }
/**
* @brief Get the boolean setting for preview in Graph
* @return True if preview checkbox is checked, false otherwise
*/
bool getGraphPreview() { return s.value("graph.preview").toBool(); }
/**
* @brief Set the boolean setting for preview in Graph
* @param checked is a boolean that represents the preview checkbox
*/
void setGraphPreview(bool checked) { s.setValue("graph.preview", checked); }
/**
* @brief Getters and setters for the transaparent option state and scale factor for bitmap
* graph exports.
@ -201,6 +212,9 @@ public:
void setOutputRedirectionEnabled(bool enabled);
bool getOutputRedirectionEnabled() const;
void setPreviewValue(bool checked);
bool getPreviewValue() const;
/**
* @brief Recently opened binaries, as shown in NewFileDialog.
*/
@ -214,6 +228,19 @@ public:
void setRecentProjects(const QStringList &list);
void addRecentProject(QString file);
// Functions Widget Layout
/**
* @brief Get the layout of the Functions widget.
* @return The layout.
*/
QString getFunctionsWidgetLayout();
/**
* @brief Set the layout of the Functions widget
* @param layout The layout of the Functions widget, either horizontal or vertical.
*/
void setFunctionsWidgetLayout(const QString &layout);
public slots:
void refreshFont();
signals:

View File

@ -1,163 +0,0 @@
#include "CrashHandler.h"
#include "BugReporting.h"
#include <QMessageBox>
#include <QPushButton>
#include <QFileDialog>
#include <QStandardPaths>
#include <QTime>
#include <QApplication>
#include <QString>
#include <QFile>
#include <QDir>
#include <QMap>
#include <QProcess>
#if defined(Q_OS_LINUX)
# include "client/linux/handler/exception_handler.h"
#elif defined(Q_OS_WIN32)
# include "client/windows/handler/exception_handler.h"
#elif defined(Q_OS_MACOS)
# include "client/mac/handler/exception_handler.h"
#endif // Q_OS
static google_breakpad::ExceptionHandler *exceptionHandler = nullptr;
static void finishCrashHandler()
{
delete exceptionHandler;
}
#ifdef Q_OS_WIN32
// Called if crash dump was successfully created
// Saves path to file
bool callback(const wchar_t *_dump_dir, const wchar_t *_minidump_id, void *context,
EXCEPTION_POINTERS *exinfo, MDRawAssertionInfo *assertion, bool success)
{
const QDir dir = QString::fromWCharArray(_dump_dir);
const QString id = QString::fromWCharArray(_minidump_id);
QProcess::startDetached(QCoreApplication::applicationFilePath(),
{ "--start-crash-handler", dir.filePath(id + ".dmp") });
_exit(1);
return true;
}
#elif defined(Q_OS_LINUX)
// Called if crash dump was successfully created
// Saves path to file
bool callback(const google_breakpad::MinidumpDescriptor &md, void *context, bool b)
{
QProcess::startDetached(QCoreApplication::applicationFilePath(),
{ "--start-crash-handler", md.path() });
_exit(1);
return true;
}
#elif defined(Q_OS_MACOS)
// Called if crash dump was successfully created
// Saves path to file
bool callback(const char *dump_dir, const char *minidump_id, void *context, bool succeeded)
{
const QDir dir = QString::fromUtf8(dump_dir);
const QString id = QString::fromUtf8(minidump_id);
QProcess::startDetached(QCoreApplication::applicationFilePath(),
{ "--start-crash-handler", dir.filePath(id + ".dmp") });
_exit(1);
return true;
}
#endif // Q_OS
void initCrashHandler()
{
if (exceptionHandler) {
return;
}
// Here will be placed crash dump at the first place
// and then moved if needed
#if defined(Q_OS_LINUX)
static std::string tmpLocation =
QStandardPaths::writableLocation(QStandardPaths::TempLocation).toStdString();
exceptionHandler = new google_breakpad::ExceptionHandler(
google_breakpad::MinidumpDescriptor(tmpLocation), nullptr, callback, nullptr, true, -1);
#elif defined(Q_OS_MACOS)
static std::string tmpLocation =
QStandardPaths::writableLocation(QStandardPaths::TempLocation).toStdString();
exceptionHandler = new google_breakpad::ExceptionHandler(tmpLocation, nullptr, callback,
nullptr, true, nullptr);
#else
static std::wstring tmpLocation =
QStandardPaths::writableLocation(QStandardPaths::TempLocation).toStdWString();
exceptionHandler =
new google_breakpad::ExceptionHandler(tmpLocation, nullptr, callback, nullptr,
google_breakpad::ExceptionHandler::HANDLER_ALL);
#endif
atexit(finishCrashHandler);
}
void showCrashDialog(const QString &dumpFile)
{
QMessageBox mb;
mb.setWindowTitle(QObject::tr("Crash"));
mb.setText(QObject::tr("Cutter received a signal it can't handle and will close.<br/>"
"Would you like to create a crash dump for a bug report?"));
mb.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
mb.button(QMessageBox::Yes)->setText(QObject::tr("Create a Crash Dump"));
mb.button(QMessageBox::No)->setText(QObject::tr("Quit"));
mb.setDefaultButton(QMessageBox::Yes);
bool ok = false;
int ret = mb.exec();
if (ret == QMessageBox::Yes) {
QString dumpSaveFileName;
int placementFailCounter = 0;
do {
placementFailCounter++;
if (placementFailCounter == 4) {
break;
}
dumpSaveFileName = QFileDialog::getSaveFileName(
nullptr, QObject::tr("Choose a directory to save the crash dump in"),
QStandardPaths::writableLocation(QStandardPaths::HomeLocation)
+ QDir::separator() + "Cutter_crash_dump_"
+ QDate::currentDate().toString("dd.MM.yy") + "_"
+ QTime::currentTime().toString("HH.mm.ss") + ".dmp",
QObject::tr("Minidump (*.dmp)"));
if (dumpSaveFileName.isEmpty()) {
return;
}
if (QFile::rename(dumpFile, dumpSaveFileName)) {
ok = true;
break;
}
QMessageBox::critical(nullptr, QObject::tr("Save Crash Dump"),
QObject::tr("Failed to write to %1.<br/>"
"Please make sure you have access to that directory "
"and try again.")
.arg(QFileInfo(dumpSaveFileName).dir().path()));
} while (true);
if (ok) {
QMessageBox info;
info.setWindowTitle(QObject::tr("Success"));
info.setText(QObject::tr("<a href=\"%1\">Crash dump</a> was successfully created.")
.arg(QFileInfo(dumpSaveFileName).dir().path()));
info.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
info.button(QMessageBox::Yes)->setText(QObject::tr("Open an Issue"));
info.button(QMessageBox::No)->setText(QObject::tr("Quit"));
info.setDefaultButton(QMessageBox::Yes);
int ret = info.exec();
if (ret == QMessageBox::Yes) {
openIssue();
}
} else {
QMessageBox::critical(nullptr, QObject::tr("Error"),
QObject::tr("Error occurred during crash dump creation."));
}
} else {
QFile f(dumpFile);
f.remove();
}
}

View File

@ -1,16 +0,0 @@
#ifndef CRASH_HANDLER_H
#define CRASH_HANDLER_H
#include <QString>
/**
* @fn void initCrashHandler()
*
* If CUTTER_ENABLE_CRASH_REPORTS is true, initializes
* crash handling and reporting, otherwise does nothing.
*/
void initCrashHandler();
void showCrashDialog(const QString &dumpFile);
#endif // CRASH_HANDLER_H

View File

@ -13,18 +13,18 @@ CutterSeekable::~CutterSeekable() {}
void CutterSeekable::setSynchronization(bool sync)
{
synchronized = sync;
onCoreSeekChanged(Core()->getOffset());
onCoreSeekChanged(Core()->getOffset(), CutterCore::SeekHistoryType::New);
emit syncChanged();
}
void CutterSeekable::onCoreSeekChanged(RVA addr)
void CutterSeekable::onCoreSeekChanged(RVA addr, CutterCore::SeekHistoryType type)
{
if (synchronized && widgetOffset != addr) {
updateSeek(addr, true);
updateSeek(addr, type, true);
}
}
void CutterSeekable::updateSeek(RVA addr, bool localOnly)
void CutterSeekable::updateSeek(RVA addr, CutterCore::SeekHistoryType type, bool localOnly)
{
previousOffset = widgetOffset;
widgetOffset = addr;
@ -32,7 +32,7 @@ void CutterSeekable::updateSeek(RVA addr, bool localOnly)
Core()->seek(addr);
}
emit seekableSeekChanged(addr);
emit seekableSeekChanged(addr, type);
}
void CutterSeekable::seekPrev()
@ -40,7 +40,7 @@ void CutterSeekable::seekPrev()
if (synchronized) {
Core()->seekPrev();
} else {
this->seek(previousOffset);
this->seek(previousOffset, CutterCore::SeekHistoryType::Undo);
}
}

View File

@ -19,8 +19,12 @@ public:
* signal will be emitted.
* In any case, CutterSeekable::seekableSeekChanged is emitted.
* @param addr the location to seek at.
* @param type the type of seek wrt history (Undo, Redo, or New)
*/
void seek(RVA addr) { updateSeek(addr, false); }
void seek(RVA addr, CutterCore::SeekHistoryType type = CutterCore::SeekHistoryType::New)
{
updateSeek(addr, type, false);
}
/**
* @brief setSynchronization sets
@ -67,7 +71,7 @@ private slots:
/**
* @brief onCoreSeekChanged
*/
void onCoreSeekChanged(RVA addr);
void onCoreSeekChanged(RVA addr, CutterCore::SeekHistoryType type);
private:
/**
@ -91,9 +95,9 @@ private:
* @brief internal method for changing the seek
* @param localOnly whether the seek should be updated globally if synchronized
*/
void updateSeek(RVA addr, bool localOnly);
void updateSeek(RVA addr, CutterCore::SeekHistoryType type, bool localOnly);
signals:
void seekableSeekChanged(RVA addr);
void seekableSeekChanged(RVA addr, CutterCore::SeekHistoryType type);
void syncChanged();
};

View File

@ -10,6 +10,75 @@ Decompiler::Decompiler(const QString &id, const QString &name, QObject *parent)
{
}
static char *jsonToStrdup(const CutterJson &str)
{
const RzJson *j = str.lowLevelValue();
if (!j || j->type != RZ_JSON_STRING) {
return NULL;
}
return rz_str_new(j->str_value);
}
static RzAnnotatedCode *parseJsonCode(CutterJson &json)
{
char *raw_code = jsonToStrdup(json["code"]);
if (!raw_code) {
return NULL;
}
RzAnnotatedCode *code = rz_annotated_code_new(raw_code);
if (!code) {
return NULL;
}
for (const auto &jsonAnnotation : json["annotations"]) {
RzCodeAnnotation annotation = {};
annotation.start = jsonAnnotation["start"].toUt64();
annotation.end = jsonAnnotation["end"].toUt64();
QString type = jsonAnnotation["type"].toString();
if (type == "offset") {
annotation.type = RZ_CODE_ANNOTATION_TYPE_OFFSET;
annotation.offset.offset = jsonAnnotation["offset"].toString().toULongLong();
} else if (type == "function_name") {
annotation.type = RZ_CODE_ANNOTATION_TYPE_FUNCTION_NAME;
annotation.reference.name = jsonToStrdup(jsonAnnotation["name"]);
annotation.reference.offset = jsonAnnotation["offset"].toString().toULongLong();
} else if (type == "global_variable") {
annotation.type = RZ_CODE_ANNOTATION_TYPE_GLOBAL_VARIABLE;
annotation.reference.offset = jsonAnnotation["offset"].toString().toULongLong();
} else if (type == "constant_variable") {
annotation.type = RZ_CODE_ANNOTATION_TYPE_CONSTANT_VARIABLE;
annotation.reference.offset = jsonAnnotation["offset"].toString().toULongLong();
} else if (type == "local_variable") {
annotation.type = RZ_CODE_ANNOTATION_TYPE_LOCAL_VARIABLE;
annotation.variable.name = jsonToStrdup(jsonAnnotation["name"]);
} else if (type == "function_parameter") {
annotation.type = RZ_CODE_ANNOTATION_TYPE_FUNCTION_PARAMETER;
annotation.variable.name = jsonToStrdup(jsonAnnotation["name"]);
} else if (type == "syntax_highlight") {
annotation.type = RZ_CODE_ANNOTATION_TYPE_SYNTAX_HIGHLIGHT;
QString highlightType = jsonAnnotation["syntax_highlight"].toString();
if (highlightType == "keyword") {
annotation.syntax_highlight.type = RZ_SYNTAX_HIGHLIGHT_TYPE_KEYWORD;
} else if (highlightType == "comment") {
annotation.syntax_highlight.type = RZ_SYNTAX_HIGHLIGHT_TYPE_COMMENT;
} else if (highlightType == "datatype") {
annotation.syntax_highlight.type = RZ_SYNTAX_HIGHLIGHT_TYPE_DATATYPE;
} else if (highlightType == "function_name") {
annotation.syntax_highlight.type = RZ_SYNTAX_HIGHLIGHT_TYPE_FUNCTION_NAME;
} else if (highlightType == "function_parameter") {
annotation.syntax_highlight.type = RZ_SYNTAX_HIGHLIGHT_TYPE_FUNCTION_PARAMETER;
} else if (highlightType == "local_variable") {
annotation.syntax_highlight.type = RZ_SYNTAX_HIGHLIGHT_TYPE_LOCAL_VARIABLE;
} else if (highlightType == "constant_variable") {
annotation.syntax_highlight.type = RZ_SYNTAX_HIGHLIGHT_TYPE_CONSTANT_VARIABLE;
} else if (highlightType == "global_variable") {
annotation.syntax_highlight.type = RZ_SYNTAX_HIGHLIGHT_TYPE_GLOBAL_VARIABLE;
}
}
rz_annotated_code_add_annotation(code, &annotation);
}
return code;
}
RzAnnotatedCode *Decompiler::makeWarning(QString warningMessage)
{
std::string temporary = warningMessage.toStdString();
@ -23,7 +92,7 @@ JSDecDecompiler::JSDecDecompiler(QObject *parent) : Decompiler("jsdec", "jsdec",
bool JSDecDecompiler::isAvailable()
{
return Core()->cmdList("es").contains("jsdec");
return Core()->getConfigVariableSpaces().contains("jsdec");
}
void JSDecDecompiler::decompileAt(RVA addr)
@ -31,48 +100,16 @@ void JSDecDecompiler::decompileAt(RVA addr)
if (task) {
return;
}
task = new RizinCmdTask("pddj @ " + QString::number(addr));
task = new RizinCmdTask("pddA @ " + QString::number(addr));
connect(task, &RizinCmdTask::finished, this, [this]() {
QJsonObject json = task->getResultJson().object();
CutterJson json = task->getResultJson();
delete task;
task = nullptr;
if (json.isEmpty()) {
if (!json.size()) {
emit finished(Decompiler::makeWarning(tr("Failed to parse JSON from jsdec")));
return;
}
RzAnnotatedCode *code = rz_annotated_code_new(nullptr);
QString codeString = "";
for (const auto &line : json["log"].toArray()) {
if (!line.isString()) {
continue;
}
codeString.append(line.toString() + "\n");
}
auto linesArray = json["lines"].toArray();
for (const auto &line : linesArray) {
QJsonObject lineObject = line.toObject();
if (lineObject.isEmpty()) {
continue;
}
RzCodeAnnotation annotationi = {};
annotationi.start = codeString.length();
codeString.append(lineObject["str"].toString() + "\n");
annotationi.end = codeString.length();
bool ok;
annotationi.type = RZ_CODE_ANNOTATION_TYPE_OFFSET;
annotationi.offset.offset = lineObject["offset"].toVariant().toULongLong(&ok);
rz_annotated_code_add_annotation(code, &annotationi);
}
for (const auto &line : json["errors"].toArray()) {
if (!line.isString()) {
continue;
}
codeString.append(line.toString() + "\n");
}
std::string tmp = codeString.toStdString();
code->code = strdup(tmp.c_str());
RzAnnotatedCode *code = parseJsonCode(json);
emit finished(code);
});
task->startTask();

View File

@ -49,8 +49,7 @@ void DecompilerHighlighter::highlightBlock(const QString &)
size_t start = block.position();
size_t end = block.position() + block.length();
std::unique_ptr<RzPVector, decltype(&rz_pvector_free)> annotations(
rz_annotated_code_annotations_range(code, start, end), &rz_pvector_free);
auto annotations = fromOwned(rz_annotated_code_annotations_range(code, start, end));
void **iter;
rz_pvector_foreach(annotations.get(), iter)
{

View File

@ -0,0 +1,84 @@
#include "DisassemblyPreview.h"
#include "Configuration.h"
#include "widgets/GraphView.h"
#include <QCoreApplication>
#include <QWidget>
#include <QToolTip>
#include <QProcessEnvironment>
DisassemblyTextBlockUserData::DisassemblyTextBlockUserData(const DisassemblyLine &line)
: line { line }
{
}
DisassemblyTextBlockUserData *getUserData(const QTextBlock &block)
{
QTextBlockUserData *userData = block.userData();
if (!userData) {
return nullptr;
}
return static_cast<DisassemblyTextBlockUserData *>(userData);
}
QString DisassemblyPreview::getToolTipStyleSheet()
{
return QString { "QToolTip { border-width: 1px; max-width: %1px;"
"opacity: 230; background-color: %2;"
"color: %3; border-color: %3;}" }
.arg(400)
.arg(Config()->getColor("gui.tooltip.background").name())
.arg(Config()->getColor("gui.tooltip.foreground").name());
}
bool DisassemblyPreview::showDisasPreview(QWidget *parent, const QPoint &pointOfEvent,
const RVA offsetFrom)
{
QList<XrefDescription> refs = Core()->getXRefs(offsetFrom, false, false);
if (refs.length()) {
if (refs.length() > 1) {
qWarning() << QObject::tr(
"More than one (%1) references here. Weird behaviour expected.")
.arg(refs.length());
}
RVA offsetTo = refs.at(0).to; // This is the offset we want to preview
/*
* Only if the offset we point *to* is different from the one the cursor is currently
* on *and* the former is a valid offset, we are allowed to get a preview of offsetTo
*/
if (offsetTo != offsetFrom && offsetTo != RVA_INVALID) {
QStringList disasmPreview = Core()->getDisassemblyPreview(offsetTo, 10);
// Last check to make sure the returned preview isn't an empty text (QStringList)
if (!disasmPreview.isEmpty()) {
const QFont &fnt = Config()->getFont();
QFontMetrics fm { fnt };
QString tooltip =
QString { "<html><div style=\"font-family: %1; font-size: %2pt; "
"white-space: nowrap;\"><div style=\"margin-bottom: "
"10px;\"><strong>Disassembly Preview</strong>:<br>%3<div>" }
.arg(fnt.family())
.arg(qMax(8, fnt.pointSize() - 1))
.arg(disasmPreview.join("<br>"));
QToolTip::showText(pointOfEvent, tooltip, parent, QRect {}, 3500);
return true;
}
}
}
return false;
}
RVA DisassemblyPreview::readDisassemblyOffset(QTextCursor tc)
{
auto userData = getUserData(tc.block());
if (!userData) {
return RVA_INVALID;
}
return userData->line.offset;
}

View File

@ -0,0 +1,45 @@
#ifndef DISASSEMBLYPREVIEW_H
#define DISASSEMBLYPREVIEW_H
#include <QTextBlockUserData>
#include "core/CutterDescriptions.h"
class QWidget;
class DisassemblyTextBlockUserData : public QTextBlockUserData
{
public:
DisassemblyLine line;
explicit DisassemblyTextBlockUserData(const DisassemblyLine &line);
};
DisassemblyTextBlockUserData *getUserData(const QTextBlock &block);
/**
* @brief Namespace to define relevant functions
*
* @ingroup DisassemblyPreview
*/
namespace DisassemblyPreview {
/*!
* @brief Get the QString that defines the stylesheet for tooltip
* @return A QString for the stylesheet
*/
QString getToolTipStyleSheet();
/*!
* @brief Show a QToolTip that previews the disassembly that is pointed to
* It works for GraphWidget and DisassemblyWidget
* @return True if the tooltip is shown
*/
bool showDisasPreview(QWidget *parent, const QPoint &pointOfEvent, const RVA offsetFrom);
/*!
* @brief Reads the offset for the cursor position
* @return The disassembly offset of the hovered asm text
*/
RVA readDisassemblyOffset(QTextCursor tc);
}
#endif

View File

@ -9,15 +9,6 @@ Highlighter::Highlighter(QTextDocument *parent) : QSyntaxHighlighter(parent)
core = Core();
keywordFormat.setForeground(QColor(65, 131, 215));
for (const QString &pattern : this->core->opcodes) {
rule.pattern.setPattern("\\b" + pattern + "\\b");
rule.pattern.setPatternOptions(QRegularExpression::CaseInsensitiveOption);
rule.format = keywordFormat;
highlightingRules.append(rule);
}
regFormat.setForeground(QColor(236, 100, 75));
for (const QString &pattern : this->core->regs) {

View File

@ -77,17 +77,12 @@ bool IOModesController::prepareForWriting()
bool IOModesController::allChangesComitted()
{
// Get a list of available write changes
QJsonArray changes = Core()->cmdj("wcj").array();
// Check if there is a change which isn't written to the file
for (const QJsonValue &value : changes) {
QJsonObject changeObject = value.toObject();
if (!changeObject["written"].toBool()) {
RzCoreLocked core(Core());
for (auto c : CutterPVector<RzIOCache>(&core->io->cache)) {
if (!c->written) {
return false;
}
}
return true;
}
@ -96,7 +91,7 @@ bool IOModesController::askCommitUnsavedChanges()
// Check if there are uncommitted changes
if (!allChangesComitted()) {
QMessageBox::StandardButton ret = QMessageBox::question(
NULL, QObject::tr("Uncomitted changes"),
NULL, QObject::tr("Uncommitted changes"),
QObject::tr("It seems that you have changes or patches that are not committed to "
"the file.\n"
"Do you want to commit them now?"),
@ -105,7 +100,7 @@ bool IOModesController::askCommitUnsavedChanges()
if (ret == QMessageBox::Save) {
Core()->commitWriteCache();
} else if (ret == QMessageBox::Discard) {
Core()->cmdRaw("wcr");
Core()->resetWriteCache();
emit Core()->refreshCodeViews();
} else if (ret == QMessageBox::Cancel) {
return false;

View File

@ -40,7 +40,7 @@ struct InitialOptions
QString pdbFile;
QString script;
QList<CommandDescription> analCmd = { { "aaa", "Auto analysis" } };
QList<CommandDescription> analysisCmd = { { "aaa", "Auto analysis" } };
QString shellcode;
};

View File

@ -1,123 +1,38 @@
#include "JsonModel.h"
#include <QIODevice>
JsonModel::JsonModel(QObject *parent) : QAbstractItemModel(parent)
QTreeWidgetItem *Cutter::jsonTreeWidgetItem(const QString &key, const CutterJson &json)
{
mRootItem = new JsonTreeItem;
mHeaders.append("key");
mHeaders.append("value");
}
JsonModel::~JsonModel()
{
delete mRootItem;
}
bool JsonModel::load(QIODevice *device)
{
return loadJson(device->readAll());
}
bool JsonModel::loadJson(const QByteArray &json)
{
mDocument = QJsonDocument::fromJson(json);
if (!mDocument.isNull()) {
beginResetModel();
delete mRootItem;
if (mDocument.isArray()) {
mRootItem = JsonTreeItem::load(QJsonValue(mDocument.array()));
} else {
mRootItem = JsonTreeItem::load(QJsonValue(mDocument.object()));
QString val;
switch (json.type()) {
case RZ_JSON_STRING:
val = json.toString();
break;
case RZ_JSON_BOOLEAN:
val = json.toBool() ? "true" : "false";
break;
case RZ_JSON_DOUBLE:
val = QString::number(json.lowLevelValue()->num.dbl_value);
break;
case RZ_JSON_INTEGER:
val = QString::number(json.toUt64());
break;
case RZ_JSON_NULL:
val = "null";
break;
case RZ_JSON_OBJECT:
case RZ_JSON_ARRAY:
break;
}
auto r = new QTreeWidgetItem(QStringList({ key, val }));
if (json.type() == RZ_JSON_ARRAY) {
size_t i = 0;
for (const auto &child : json) {
r->addChild(jsonTreeWidgetItem(QString::number(i++), child));
}
} else if (json.type() == RZ_JSON_OBJECT) {
for (const auto &child : json) {
r->addChild(jsonTreeWidgetItem(child.key(), child));
}
endResetModel();
return true;
}
return false;
}
QVariant JsonModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
JsonTreeItem *item = static_cast<JsonTreeItem *>(index.internalPointer());
if (role == Qt::DisplayRole) {
if (index.column() == 0)
return QString("%1").arg(item->key());
if (index.column() == 1)
return QString("%1").arg(item->value());
}
return QVariant();
}
QVariant JsonModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (role != Qt::DisplayRole)
return QVariant();
if (orientation == Qt::Horizontal) {
return mHeaders.value(section);
} else
return QVariant();
}
QModelIndex JsonModel::index(int row, int column, const QModelIndex &parent) const
{
if (!hasIndex(row, column, parent))
return QModelIndex();
JsonTreeItem *parentItem;
if (!parent.isValid())
parentItem = mRootItem;
else
parentItem = static_cast<JsonTreeItem *>(parent.internalPointer());
JsonTreeItem *childItem = parentItem->child(row);
if (childItem)
return createIndex(row, column, childItem);
else
return QModelIndex();
}
QModelIndex JsonModel::parent(const QModelIndex &index) const
{
if (!index.isValid())
return QModelIndex();
JsonTreeItem *childItem = static_cast<JsonTreeItem *>(index.internalPointer());
JsonTreeItem *parentItem = childItem->parent();
if (parentItem == mRootItem)
return QModelIndex();
return createIndex(parentItem->row(), 0, parentItem);
}
int JsonModel::rowCount(const QModelIndex &parent) const
{
JsonTreeItem *parentItem;
if (parent.column() > 0)
return 0;
if (!parent.isValid())
parentItem = mRootItem;
else
parentItem = static_cast<JsonTreeItem *>(parent.internalPointer());
return parentItem->childCount();
}
int JsonModel::columnCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
return 2;
return r;
}

View File

@ -2,37 +2,13 @@
#ifndef JSONMODEL_H
#define JSONMODEL_H
#include <QAbstractItemModel>
#include <QJsonDocument>
#include <QJsonValue>
#include <QJsonArray>
#include <QJsonObject>
#include <QIcon>
#include <QTreeWidgetItem>
#include "CutterJson.h"
#include "JsonTreeItem.h"
namespace Cutter {
class JsonTreeItem;
QTreeWidgetItem *jsonTreeWidgetItem(const QString &key, const CutterJson &json);
class JsonModel : public QAbstractItemModel
{
public:
explicit JsonModel(QObject *parent = nullptr);
bool load(QIODevice *device);
bool loadJson(const QByteArray &json);
QVariant data(const QModelIndex &index, int role) const Q_DECL_OVERRIDE;
QVariant headerData(int section, Qt::Orientation orientation, int role) const Q_DECL_OVERRIDE;
QModelIndex index(int row, int column,
const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
QModelIndex parent(const QModelIndex &index) const Q_DECL_OVERRIDE;
int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
int columnCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
~JsonModel();
private:
JsonTreeItem *mRootItem;
QJsonDocument mDocument;
QStringList mHeaders;
};
#endif // JSONMODEL_H

View File

@ -1,104 +0,0 @@
#include "JsonTreeItem.h"
JsonTreeItem::JsonTreeItem(JsonTreeItem *parent)
{
mParent = parent;
}
JsonTreeItem::~JsonTreeItem()
{
qDeleteAll(mChilds);
}
void JsonTreeItem::appendChild(JsonTreeItem *item)
{
mChilds.append(item);
}
JsonTreeItem *JsonTreeItem::child(int row)
{
return mChilds.value(row);
}
JsonTreeItem *JsonTreeItem::parent()
{
return mParent;
}
int JsonTreeItem::childCount() const
{
return mChilds.count();
}
int JsonTreeItem::row() const
{
if (mParent)
return mParent->mChilds.indexOf(const_cast<JsonTreeItem *>(this));
return 0;
}
void JsonTreeItem::setKey(const QString &key)
{
mKey = key;
}
void JsonTreeItem::setValue(const QString &value)
{
mValue = value;
}
void JsonTreeItem::setType(const QJsonValue::Type &type)
{
mType = type;
}
QString JsonTreeItem::key() const
{
return mKey;
}
QString JsonTreeItem::value() const
{
return mValue;
}
QJsonValue::Type JsonTreeItem::type() const
{
return mType;
}
JsonTreeItem *JsonTreeItem::load(const QJsonValue &value, JsonTreeItem *parent)
{
JsonTreeItem *rootItem = new JsonTreeItem(parent);
rootItem->setKey("root");
if (value.isObject()) {
// Get all QJsonValue childs
for (const QString &key : value.toObject().keys()) {
QJsonValue v = value.toObject().value(key);
JsonTreeItem *child = load(v, rootItem);
child->setKey(key);
child->setType(v.type());
rootItem->appendChild(child);
}
} else if (value.isArray()) {
// Get all QJsonValue childs
int index = 0;
for (const QJsonValue &v : value.toArray()) {
JsonTreeItem *child = load(v, rootItem);
child->setKey(QString::number(index));
child->setType(v.type());
rootItem->appendChild(child);
++index;
}
} else {
rootItem->setValue(value.toVariant().toString());
rootItem->setType(value.type());
}
return rootItem;
}

View File

@ -1,39 +0,0 @@
#ifndef JSONTREEITEM_H
#define JSONTREEITEM_H
#include <QAbstractItemModel>
#include <QJsonDocument>
#include <QJsonValue>
#include <QJsonArray>
#include <QJsonObject>
#include <QIcon>
#include "JsonModel.h"
class JsonTreeItem
{
public:
JsonTreeItem(JsonTreeItem *parent = nullptr);
~JsonTreeItem();
void appendChild(JsonTreeItem *item);
JsonTreeItem *child(int row);
JsonTreeItem *parent();
int childCount() const;
int row() const;
void setKey(const QString &key);
void setValue(const QString &value);
void setType(const QJsonValue::Type &type);
QString key() const;
QString value() const;
QJsonValue::Type type() const;
static JsonTreeItem *load(const QJsonValue &value, JsonTreeItem *parent = nullptr);
private:
QString mKey;
QString mValue;
QJsonValue::Type mType;
QList<JsonTreeItem *> mChilds;
JsonTreeItem *mParent;
};
#endif // JSONTREEITEM_H

View File

@ -13,6 +13,10 @@
#ifdef CUTTER_ENABLE_PYTHON_BINDINGS
# include <shiboken.h>
# include <pyside.h>
#ifdef HAVE_PYSIDECLEANUP
// This header is introduced in PySide 6
# include <pysidecleanup.h>
#endif
# include <signalmanager.h>
#endif
@ -72,6 +76,7 @@ void PythonManager::initialize()
PyImport_AppendInittab("CutterBindings", &PyInit_CutterBindings);
#endif
Py_Initialize();
// This function is deprecated does nothing starting from Python 3.9
PyEval_InitThreads();
pyThreadStateCounter = 1; // we have the thread now => 1
@ -159,7 +164,7 @@ void PythonManager::addPythonPath(char *path)
if (!append) {
return;
}
PyEval_CallFunction(append, "(s)", path);
PyObject_CallFunction(append, "(s)", path);
saveThread();
}

View File

@ -54,13 +54,15 @@ QString RizinCmdTask::getResult()
return QString::fromUtf8(res);
}
QJsonDocument RizinCmdTask::getResultJson()
CutterJson RizinCmdTask::getResultJson()
{
const char *res = rz_core_cmd_task_get_result(task);
if (!res) {
return QJsonDocument();
return CutterJson();
}
return Core()->parseJson(res, nullptr);
char *copy = static_cast<char *>(rz_mem_alloc(strlen(res) + 1));
strcpy(copy, res);
return Core()->parseJson(copy, nullptr);
}
const char *RizinCmdTask::getResultRaw()

View File

@ -38,7 +38,7 @@ public:
explicit RizinCmdTask(const QString &cmd, bool transient = true);
QString getResult();
QJsonDocument getResultJson();
CutterJson getResultJson();
const char *getResultRaw();
};

View File

@ -16,7 +16,10 @@ void RunScriptTask::runTask()
{
if (!this->fileName.isNull()) {
log(tr("Executing script..."));
Core()->cmdTask(". " + this->fileName);
Core()->functionTask([&](RzCore *core) {
rz_core_run_script(core, this->fileName.toUtf8().constData());
return nullptr;
});
if (isInterrupted()) {
return;
}

View File

@ -181,21 +181,20 @@ void Cutter::initializeSettings()
static void removeObsoleteOptionsFromCustomThemes()
{
const QStringList options = Core()->cmdj("ecj").object().keys()
<< ColorThemeWorker::cutterSpecificOptions;
for (auto theme : Core()->cmdList("eco*")) {
theme = theme.trimmed();
if (!ThemeWorker().isCustomTheme(theme)) {
const QStringList options = Core()->getThemeKeys() << ColorThemeWorker::cutterSpecificOptions;
QStringList themes = Core()->getColorThemes();
for (const auto &themeName : themes) {
if (!ThemeWorker().isCustomTheme(themeName)) {
continue;
}
QJsonObject updatedTheme;
auto sch = ThemeWorker().getTheme(theme).object();
for (const auto &key : sch.keys()) {
if (options.contains(key)) {
updatedTheme.insert(key, sch[key]);
ColorThemeWorker::Theme sch = ThemeWorker().getTheme(themeName);
ColorThemeWorker::Theme updatedTheme;
for (auto it = sch.constBegin(); it != sch.constEnd(); ++it) {
if (options.contains(it.key())) {
updatedTheme.insert(it.key(), it.value());
}
}
ThemeWorker().save(QJsonDocument(updatedTheme), theme);
ThemeWorker().save(updatedTheme, themeName);
}
}

View File

@ -5,7 +5,7 @@
# include "Configuration.h"
# include <KSyntaxHighlighting/theme.h>
# include <KSyntaxHighlighting/Theme>
SyntaxHighlighter::SyntaxHighlighter(QTextDocument *document)
: KSyntaxHighlighting::SyntaxHighlighter(document)

View File

@ -10,7 +10,7 @@
#ifdef CUTTER_ENABLE_KSYNTAXHIGHLIGHTING
# include <KSyntaxHighlighting/syntaxhighlighter.h>
# include <KSyntaxHighlighting/SyntaxHighlighter>
class SyntaxHighlighter : public KSyntaxHighlighting::SyntaxHighlighter
{

View File

@ -19,7 +19,6 @@
* {
* TempConfig tempConfig;
* tempConfig.set("asm.arch", "x86").set("asm.comments", false);
* return Core()->cmdRaw("pd");
* // config automatically restored at the end of scope
* }
* \endcode

View File

@ -52,29 +52,6 @@ void UpdateWorker::checkCurrentVersion(time_t timeoutMs)
pending = true;
}
void UpdateWorker::download(QString filename, QString version)
{
downloadFile.setFileName(filename);
downloadFile.open(QIODevice::WriteOnly);
QNetworkRequest request;
# if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0) && QT_VERSION < QT_VERSION_CHECK(5, 9, 0)
request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
# elif QT_VERSION >= QT_VERSION_CHECK(5, 9, 0)
request.setAttribute(QNetworkRequest::RedirectPolicyAttribute,
QNetworkRequest::RedirectPolicy::NoLessSafeRedirectPolicy);
# endif
QUrl url(QString("https://github.com/rizinorg/cutter/releases/"
"download/v%1/%2")
.arg(version)
.arg(getRepositoryFileName()));
request.setUrl(url);
downloadReply = nm.get(request);
connect(downloadReply, &QNetworkReply::downloadProgress, this, &UpdateWorker::process);
connect(downloadReply, &QNetworkReply::finished, this, &UpdateWorker::serveDownloadFinish);
}
void UpdateWorker::showUpdateDialog(bool showDontCheckForUpdatesButton)
{
QMessageBox mb;
@ -82,69 +59,23 @@ void UpdateWorker::showUpdateDialog(bool showDontCheckForUpdatesButton)
mb.setText(tr("There is an update available for Cutter.<br/>") + "<b>" + tr("Current version:")
+ "</b> " CUTTER_VERSION_FULL "<br/>" + "<b>" + tr("Latest version:") + "</b> "
+ latestVersion.toString() + "<br/><br/>"
+ tr("For update, please check the link:<br/>")
+ tr("To update, please check the link:<br/>")
+ QString("<a href=\"https://github.com/rizinorg/cutter/releases/tag/v%1\">"
"https://github.com/rizinorg/cutter/releases/tag/v%1</a><br/>")
.arg(latestVersion.toString())
+ tr("or click \"Download\" to download latest version of Cutter."));
.arg(latestVersion.toString()));
if (showDontCheckForUpdatesButton) {
mb.setStandardButtons(QMessageBox::Save | QMessageBox::Reset | QMessageBox::Ok);
mb.button(QMessageBox::Reset)->setText(tr("Don't check for updates"));
mb.setStandardButtons(QMessageBox::Reset | QMessageBox::Ok);
mb.button(QMessageBox::Reset)->setText(tr("Don't check for updates automatically"));
} else {
mb.setStandardButtons(QMessageBox::Save | QMessageBox::Ok);
mb.setStandardButtons(QMessageBox::Ok);
}
mb.button(QMessageBox::Save)->setText(tr("Download"));
mb.setDefaultButton(QMessageBox::Ok);
int ret = mb.exec();
if (ret == QMessageBox::Reset) {
Config()->setAutoUpdateEnabled(false);
} else if (ret == QMessageBox::Save) {
QString fullFileName = QFileDialog::getSaveFileName(
nullptr, tr("Choose directory for downloading"),
QStandardPaths::writableLocation(QStandardPaths::HomeLocation) + QDir::separator()
+ getRepositoryFileName(),
QString("%1 (*.%1)").arg(getRepositeryExt()));
if (!fullFileName.isEmpty()) {
QProgressDialog progressDial(tr("Downloading update..."), tr("Cancel"), 0, 100);
connect(this, &UpdateWorker::downloadProcess, &progressDial,
[&progressDial](size_t curr, size_t total) {
progressDial.setValue(100.0f * curr / total);
});
connect(&progressDial, &QProgressDialog::canceled, this, &UpdateWorker::abortDownload);
connect(this, &UpdateWorker::downloadFinished, &progressDial, &QProgressDialog::cancel);
connect(this, &UpdateWorker::downloadFinished, this, [](QString filePath) {
QMessageBox info(QMessageBox::Information, tr("Download finished!"),
tr("Latest version of Cutter was succesfully downloaded!"),
QMessageBox::Yes | QMessageBox::Open | QMessageBox::Ok, nullptr);
info.button(QMessageBox::Open)->setText(tr("Open file"));
info.button(QMessageBox::Yes)->setText(tr("Open download folder"));
int r = info.exec();
if (r == QMessageBox::Open) {
QDesktopServices::openUrl(filePath);
} else if (r == QMessageBox::Yes) {
auto path = filePath.split('/');
path.removeLast();
QDesktopServices::openUrl(path.join('/'));
}
});
download(fullFileName, latestVersion.toString());
// Calling show() before exec() is only way make dialog non-modal
// it seems weird, but it works
progressDial.show();
progressDial.exec();
}
}
}
void UpdateWorker::abortDownload()
{
disconnect(downloadReply, &QNetworkReply::finished, this, &UpdateWorker::serveDownloadFinish);
disconnect(downloadReply, &QNetworkReply::downloadProgress, this, &UpdateWorker::process);
downloadReply->close();
downloadReply->deleteLater();
downloadFile.remove();
}
void UpdateWorker::serveVersionCheckReply()
{
pending = false;
@ -168,51 +99,6 @@ void UpdateWorker::serveVersionCheckReply()
emit checkComplete(versionReply, errStr);
}
void UpdateWorker::serveDownloadFinish()
{
downloadReply->close();
downloadReply->deleteLater();
if (downloadReply->error()) {
emit downloadError(downloadReply->errorString());
} else {
emit downloadFinished(downloadFile.fileName());
}
}
void UpdateWorker::process(size_t bytesReceived, size_t bytesTotal)
{
downloadFile.write(downloadReply->readAll());
emit downloadProcess(bytesReceived, bytesTotal);
}
QString UpdateWorker::getRepositeryExt() const
{
# ifdef Q_OS_LINUX
return "AppImage";
# elif defined(Q_OS_WIN64) || defined(Q_OS_WIN32)
return "zip";
# elif defined(Q_OS_MACOS)
return "dmg";
# endif
}
QString UpdateWorker::getRepositoryFileName() const
{
QString downloadFileName;
# ifdef Q_OS_LINUX
downloadFileName = "Cutter-v%1-x%2.Linux.AppImage";
# elif defined(Q_OS_WIN64) || defined(Q_OS_WIN32)
downloadFileName = "Cutter-v%1-x%2.Windows.zip";
# elif defined(Q_OS_MACOS)
downloadFileName = "Cutter-v%1-x%2.macOS.dmg";
# endif
downloadFileName =
downloadFileName.arg(latestVersion.toString())
.arg(QSysInfo::buildAbi().split('-').at(2).contains("64") ? "64" : "32");
return downloadFileName;
}
QVersionNumber UpdateWorker::currentVersionNumber()
{
return QVersionNumber(CUTTER_VERSION_MAJOR, CUTTER_VERSION_MINOR, CUTTER_VERSION_PATCH);

View File

@ -23,8 +23,7 @@ class QNetworkReply;
/**
* @class UpdateWorker
* @brief The UpdateWorker class is a class providing API to check for current Cutter version
* and download specific version of one.
* @brief The UpdateWorker class is a class providing API to check for current Cutter version.
*/
class UpdateWorker : public QObject
@ -47,23 +46,12 @@ public:
void checkCurrentVersion(time_t timeoutMs);
/**
* @fn void UpdateWorker::download(QDir downloadPath, QString version)
*
* @brief Downloads provided @a version of Cutter into @a downloadDir.
*
* @sa downloadProcess(size_t bytesReceived, size_t bytesTotal)
*/
void download(QString filename, QString version);
/**
* @fn void UpdateWorker::showUpdateDialog()
*
* Shows dialog that allows user to either download latest version of Cutter from website
* or download it by clicking on a button. This dialog also has "Don't check for updates"
* button which disables on-start update checks if @a showDontCheckForUpdatesButton is true.
*
* @sa downloadProcess(size_t bytesReceived, size_t bytesTotal)
* Shows dialog that allows user to download latest version of Cutter from website.
* This dialog also has "Don't check for updates" button which disables on-start update
* checks if @a showDontCheckForUpdatesButton is true.
*/
void showUpdateDialog(bool showDontCheckForUpdatesButton);
@ -73,18 +61,6 @@ public:
*/
static QVersionNumber currentVersionNumber();
public slots:
/**
* @fn void UpdateWorker::abortDownload()
*
* @brief Stops current process of downloading.
*
* @note UpdateWorker::downloadFinished(QString filename) is not send after this function.
*
* @sa download(QDir downloadDir, QString version)
*/
void abortDownload();
signals:
/**
* @fn UpdateWorker::checkComplete(const QString& verson, const QString& errorMsg)
@ -95,46 +71,14 @@ signals:
*/
void checkComplete(const QVersionNumber &currVerson, const QString &errorMsg);
/**
* @fn UpdateWorker::downloadProcess(size_t bytesReceived, size_t bytesTotal)
*
* The signal is emitted each time when some amount of bytes was downloaded.
* May be used as indicator of download progress.
*/
void downloadProcess(size_t bytesReceived, size_t bytesTotal);
/**
* @fn UpdateWorker::downloadFinished(QString filename)
*
* @brief The signal is emitted as soon as downloading completes.
*/
void downloadFinished(QString filename);
/**
* @fn UpdateWorker::downloadError(QString errorStr)
*
* @brief The signal is emitted when error occures during download.
*/
void downloadError(QString errorStr);
private slots:
void serveVersionCheckReply();
void serveDownloadFinish();
void process(size_t bytesReceived, size_t bytesTotal);
private:
QString getRepositeryExt() const;
QString getRepositoryFileName() const;
private:
QNetworkAccessManager nm;
QVersionNumber latestVersion;
QTimer t;
bool pending;
QFile downloadFile;
QNetworkReply *downloadReply;
QNetworkReply *checkReply;
};

109
src/core/Basefind.cpp Normal file
View File

@ -0,0 +1,109 @@
#include "Basefind.h"
bool Basefind::threadCallback(const RzBaseFindThreadInfo *info, void *user)
{
auto th = reinterpret_cast<Basefind *>(user);
return th->updateProgress(info);
}
Basefind::Basefind(CutterCore *core)
: core(core),
scores(nullptr),
continue_run(true)
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
,
mutex(QMutex::Recursive)
#endif
{
memset(&options, 0, sizeof(RzBaseFindOpt));
}
Basefind::~Basefind()
{
cancel();
wait();
rz_list_free(scores);
}
bool Basefind::setOptions(const RzBaseFindOpt *opts)
{
mutex.lock();
options.max_threads = opts->max_threads;
options.pointer_size = opts->pointer_size;
options.start_address = opts->start_address;
options.end_address = opts->end_address;
options.alignment = opts->alignment;
options.min_score = opts->min_score;
options.min_string_len = opts->min_string_len;
mutex.unlock();
if (options.start_address >= options.end_address) {
qWarning() << "Start address is >= end address";
return false;
} else if (options.alignment < RZ_BASEFIND_BASE_ALIGNMENT) {
qWarning() << "Alignment must be at least "
<< QString::asprintf("0x%x", RZ_BASEFIND_BASE_ALIGNMENT);
return false;
} else if (options.min_score < 1) {
qWarning() << "Min score must be at least 1";
return false;
} else if (options.min_string_len < 1) {
qWarning() << "Min string length must be at least 1";
return false;
}
return true;
}
void Basefind::run()
{
qRegisterMetaType<BasefindCoreStatusDescription>();
mutex.lock();
rz_list_free(scores);
scores = nullptr;
continue_run = true;
mutex.unlock();
core->coreMutex.lock();
options.callback = threadCallback;
options.user = this;
scores = rz_basefind(core->core_, &options);
core->coreMutex.unlock();
emit complete();
}
void Basefind::cancel()
{
mutex.lock();
continue_run = false;
mutex.unlock();
}
QList<BasefindResultDescription> Basefind::results()
{
QList<BasefindResultDescription> pairs;
RzListIter *it;
RzBaseFindScore *pair;
CutterRzListForeach (scores, it, RzBaseFindScore, pair) {
BasefindResultDescription desc;
desc.candidate = pair->candidate;
desc.score = pair->score;
pairs.push_back(desc);
}
return pairs;
}
bool Basefind::updateProgress(const RzBaseFindThreadInfo *info)
{
mutex.lock();
BasefindCoreStatusDescription status;
status.index = info->thread_idx;
status.percentage = info->percentage;
emit progress(status);
bool ret = continue_run;
mutex.unlock();
return ret;
}

47
src/core/Basefind.h Normal file
View File

@ -0,0 +1,47 @@
#ifndef CUTTER_BASEFIND_CORE_H
#define CUTTER_BASEFIND_CORE_H
#include <QThread>
#include <QMutex>
#include "Cutter.h"
#include "CutterDescriptions.h"
#include <rz_basefind.h>
class CutterCore;
class Basefind : public QThread
{
Q_OBJECT
public:
explicit Basefind(CutterCore *core);
virtual ~Basefind();
void run();
bool setOptions(const RzBaseFindOpt *opts);
QList<BasefindResultDescription> results();
public slots:
void cancel();
signals:
void progress(BasefindCoreStatusDescription status);
void complete();
private:
CutterCore *const core;
RzList *scores;
bool continue_run;
RzBaseFindOpt options;
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
QMutex mutex;
#else
QRecursiveMutex mutex;
#endif
bool updateProgress(const RzBaseFindThreadInfo *info);
static bool threadCallback(const RzBaseFindThreadInfo *info, void *user);
};
#endif // CUTTER_BASEFIND_CORE_H

File diff suppressed because it is too large Load Diff

View File

@ -3,18 +3,22 @@
#include "core/CutterCommon.h"
#include "core/CutterDescriptions.h"
#include "core/CutterJson.h"
#include "core/Basefind.h"
#include "common/BasicInstructionHighlighter.h"
#include <QMap>
#include <QMenu>
#include <QDebug>
#include <QObject>
#include <QSharedPointer>
#include <QStringList>
#include <QMessageBox>
#include <QJsonDocument>
#include <QErrorMessage>
#include <QMutex>
#include <QDir>
#include <functional>
#include <memory>
class AsyncTaskManager;
class BasicInstructionHighlighter;
@ -22,23 +26,51 @@ class CutterCore;
class Decompiler;
class RizinTask;
class RizinCmdTask;
class RizinFunctionTask;
class RizinTaskDialog;
#include "common/BasicBlockHighlighter.h"
#include "common/Helpers.h"
#include <rz_project.h>
#include <memory>
#define Core() (CutterCore::instance())
class RzCoreLocked;
struct CUTTER_EXPORT AddrRefs
{
RVA addr;
QString mapname;
QString section;
QString reg;
QString fcn;
QString type;
QString asm_op;
QString perms;
ut64 value;
bool has_value;
QString string;
QSharedPointer<AddrRefs> ref;
};
struct CUTTER_EXPORT RegisterRef
{
ut64 value;
AddrRefs ref;
QString name;
};
using PRzAnalysisBytes = std::unique_ptr<RzAnalysisBytes, decltype(rz_analysis_bytes_free) *>;
class CUTTER_EXPORT CutterCore : public QObject
{
Q_OBJECT
friend class RzCoreLocked;
friend class RizinTask;
friend class Basefind;
public:
explicit CutterCore(QObject *parent = nullptr);
@ -55,6 +87,10 @@ public:
RVA getOffset() const { return core_->offset; }
/* Core functions (commands) */
/* Almost the same as core_cmd_raw,
* only executes std::function<bool(RzCore *)> instead of char* */
QString getFunctionExecOut(const std::function<bool(RzCore *)> &fcn,
const RVA addr = RVA_INVALID);
static QString sanitizeStringForCommand(QString s);
/**
* @brief send a command to Rizin
@ -64,21 +100,14 @@ public:
*/
QString cmd(const char *str);
QString cmd(const QString &str) { return cmd(str.toUtf8().constData()); }
/**
* @brief send a command to Rizin asynchronously
* @param str the command you want to execute
* @param task a shared pointer that will be returned with the Rizin command task
* @note connect to the &RizinTask::finished signal to add your own logic once
* the command is finished. Use task->getResult()/getResultJson() for the
* return value.
* Once you have setup connections you can start the task with task->startTask()
* If you want to seek to an address, you should use CutterCore::seek.
* @brief send a task to Rizin
* @param fcn the task you want to execute
* @return execute successful?
*/
bool asyncCmd(const char *str, QSharedPointer<RizinCmdTask> &task);
bool asyncCmd(const QString &str, QSharedPointer<RizinCmdTask> &task)
{
return asyncCmd(str.toUtf8().constData(), task);
}
bool asyncTask(std::function<void *(RzCore *)> fcn, QSharedPointer<RizinTask> &task);
void functionTask(std::function<void *(RzCore *)> fcn);
/**
* @brief Execute a Rizin command \a cmd. By nature, the API
@ -115,43 +144,47 @@ public:
return cmdRawAt(str.toUtf8().constData(), address);
}
QJsonDocument cmdj(const char *str);
QJsonDocument cmdj(const QString &str) { return cmdj(str.toUtf8().constData()); }
QJsonDocument cmdjAt(const char *str, RVA address);
QStringList cmdList(const char *str)
class SeekReturn
{
return cmd(str).split(QLatin1Char('\n'), CUTTER_QT_SKIP_EMPTY_PARTS);
RVA returnAddress;
bool empty = true;
public:
SeekReturn(RVA returnAddress) : returnAddress(returnAddress), empty(false) {}
~SeekReturn()
{
if (!empty) {
Core()->seekSilent(returnAddress);
}
}
SeekReturn(SeekReturn &&from)
{
if (this != &from) {
returnAddress = from.returnAddress;
empty = from.empty;
from.empty = true;
}
};
};
SeekReturn seekTemp(RVA address)
{
SeekReturn returner(getOffset());
seekSilent(address);
return returner;
}
QStringList cmdList(const QString &str) { return cmdList(str.toUtf8().constData()); }
enum class SeekHistoryType { New, Undo, Redo };
CutterJson cmdj(const char *str);
CutterJson cmdj(const QString &str) { return cmdj(str.toUtf8().constData()); }
QString cmdTask(const QString &str);
QJsonDocument cmdjTask(const QString &str);
/**
* @brief send a command to Rizin and check for ESIL errors
* @param command the command you want to execute
* @note If you want to seek to an address, you should use CutterCore::seek.
*/
void cmdEsil(const char *command);
void cmdEsil(const QString &command) { cmdEsil(command.toUtf8().constData()); }
/**
* @brief send a command to Rizin and check for ESIL errors
* @param command the command you want to execute
* @param task a shared pointer that will be returned with the Rizin command task
* @note connect to the &RizinTask::finished signal to add your own logic once
* the command is finished. Use task->getResult()/getResultJson() for the
* return value.
* Once you have setup connections you can start the task with task->startTask()
* If you want to seek to an address, you should use CutterCore::seek.
*/
bool asyncCmdEsil(const char *command, QSharedPointer<RizinCmdTask> &task);
bool asyncCmdEsil(const QString &command, QSharedPointer<RizinCmdTask> &task)
{
return asyncCmdEsil(command.toUtf8().constData(), task);
}
QString getRizinVersionReadable();
QString getRizinVersionReadable(const char *program = nullptr);
QString getVersionInformation();
QJsonDocument parseJson(const char *res, const char *cmd = nullptr);
QJsonDocument parseJson(const char *res, const QString &cmd = QString())
CutterJson parseJson(char *res, const char *cmd = nullptr);
CutterJson parseJson(char *res, const QString &cmd = QString())
{
return parseJson(res, cmd.isNull() ? nullptr : cmd.toLocal8Bit().constData());
}
@ -187,10 +220,9 @@ public:
RVA getFunctionStart(RVA addr);
RVA getFunctionEnd(RVA addr);
RVA getLastFunctionInstruction(RVA addr);
QString cmdFunctionAt(QString addr);
QString cmdFunctionAt(RVA addr);
QString createFunctionAt(RVA addr);
QString createFunctionAt(RVA addr, QString name);
QString flagAt(RVA addr);
void createFunctionAt(RVA addr);
void createFunctionAt(RVA addr, QString name);
QStringList getDisassemblyPreview(RVA address, int num_of_lines);
/* Flags */
@ -208,9 +240,10 @@ public:
void triggerFlagsChanged();
/* Edition functions */
PRzAnalysisBytes getRzAnalysisBytesSingle(RVA addr);
QString getInstructionBytes(RVA addr);
QString getInstructionOpcode(RVA addr);
void editInstruction(RVA addr, const QString &inst);
void editInstruction(RVA addr, const QString &inst, bool fillWithNops = false);
void nopInstruction(RVA addr);
void jmpReverse(RVA addr);
void editBytes(RVA addr, const QString &inst);
@ -233,6 +266,13 @@ public:
* \param addr The address of the array where the string will be applied
*/
void removeString(RVA addr);
/**
* @brief Gets string at address
* That function correspond the 'Cs.' command
* \param addr The address of the string
* @return string at requested address
*/
QString getMetaString(RVA addr);
/**
* @brief Gets string at address
* That function calls the 'ps' command
@ -240,6 +280,7 @@ public:
* @return string at requested address
*/
QString getString(RVA addr);
QString getString(RVA addr, uint64_t len, RzStrEnc encoding, bool escape_nl = false);
void setToData(RVA addr, int size, int repeat = 1);
int sizeofDataMeta(RVA addr);
@ -260,17 +301,18 @@ public:
void applyStructureOffset(const QString &structureOffset, RVA offset = RVA_INVALID);
/* Classes */
QList<QString> getAllAnalClasses(bool sorted);
QList<AnalMethodDescription> getAnalClassMethods(const QString &cls);
QList<AnalBaseClassDescription> getAnalClassBaseClasses(const QString &cls);
QList<AnalVTableDescription> getAnalClassVTables(const QString &cls);
QList<QString> getAllAnalysisClasses(bool sorted);
QList<AnalysisMethodDescription> getAnalysisClassMethods(const QString &cls);
QList<AnalysisBaseClassDescription> getAnalysisClassBaseClasses(const QString &cls);
QList<AnalysisVTableDescription> getAnalysisClassVTables(const QString &cls);
void createNewClass(const QString &cls);
void renameClass(const QString &oldName, const QString &newName);
void deleteClass(const QString &cls);
bool getAnalMethod(const QString &cls, const QString &meth, AnalMethodDescription *desc);
void renameAnalMethod(const QString &className, const QString &oldMethodName,
const QString &newMethodName);
void setAnalMethod(const QString &cls, const AnalMethodDescription &meth);
bool getAnalysisMethod(const QString &cls, const QString &meth,
AnalysisMethodDescription *desc);
void renameAnalysisMethod(const QString &className, const QString &oldMethodName,
const QString &newMethodName);
void setAnalysisMethod(const QString &cls, const AnalysisMethodDescription &meth);
/* File related methods */
bool loadFile(QString path, ut64 baddr = 0LL, ut64 mapaddr = 0LL, int perms = RZ_PERM_R,
@ -278,7 +320,6 @@ public:
bool tryFile(QString path, bool rw);
bool mapFile(QString path, RVA mapaddr);
void loadScript(const QString &scriptname);
QJsonArray getOpenedFiles();
/* Seek functions */
void seek(QString thing);
@ -287,7 +328,7 @@ public:
void seekSilent(QString thing) { seekSilent(math(thing)); }
void seekPrev();
void seekNext();
void updateSeek();
void updateSeek(SeekHistoryType type = SeekHistoryType::New);
/**
* @brief Raise a memory widget showing current offset, prefer last active
* memory widget.
@ -307,6 +348,10 @@ public:
RVA prevOpAddr(RVA startAddr, int count);
RVA nextOpAddr(RVA startAddr, int count);
/* SigDB / Flirt functions */
void applySignature(const QString &filepath);
void createSignature(const QString &filepath);
/* Math functions */
ut64 math(const QString &expr);
ut64 num(const QString &expr);
@ -332,7 +377,12 @@ public:
QString getConfig(const char *k);
QString getConfig(const QString &k) { return getConfig(k.toUtf8().constData()); }
QString getConfigDescription(const char *k);
QList<QString> getColorThemes();
QStringList getConfigOptions(const char *k);
QStringList getColorThemes();
QHash<QString, QColor> getTheme();
QStringList getThemeKeys();
bool setColor(const QString &key, const QString &color);
QStringList getConfigVariableSpaces(const QString &key = "");
/* Assembly\Hexdump related methods */
QByteArray assemble(const QString &code);
@ -356,8 +406,6 @@ public:
bool sdbSet(QString path, QString key, QString val);
/* Debug */
QJsonDocument getRegistersInfo();
QJsonDocument getRegisterValues();
QString getRegisterName(QString registerRole);
RVA getProgramCounterValue();
void setRegister(QString regName, QString regValue);
@ -371,32 +419,25 @@ public:
* @param size number of bytes to scan
* @param depth telescoping depth
*/
QList<QJsonObject> getStack(int size = 0x100, int depth = 6);
QList<AddrRefs> getStack(int size = 0x100, int depth = 6);
/**
* @brief Recursively dereferences pointers starting at the specified address
* up to a given depth
* @param addr telescoping addr
* @param depth telescoping depth
*/
QJsonObject getAddrRefs(RVA addr, int depth);
AddrRefs getAddrRefs(RVA addr, int depth);
/**
* @brief return a RefDescription with a formatted ref string and configured colors
* @param ref the "ref" JSON node from getAddrRefs
*/
RefDescription formatRefDesc(QJsonObject ref);
RefDescription formatRefDesc(const QSharedPointer<AddrRefs> &ref);
/**
* @brief Get a list of a given process's threads
* @param pid The pid of the process, -1 for the currently debugged process
* @return JSON object result of dptj
* @return List of ProcessDescription
*/
QJsonDocument getProcessThreads(int pid);
/**
* @brief Get a list of a given process's child processes
* @param pid The pid of the process, -1 for the currently debugged process
* @return JSON object result of dptj
*/
QJsonDocument getChildProcesses(int pid);
QJsonDocument getBacktrace();
QList<ProcessDescription> getProcessThreads(int pid);
/**
* @brief Get a list of heap chunks
* Uses RZ_API rz_heap_chunks_list to get vector of chunks
@ -419,7 +460,20 @@ public:
* @return RzHeapChunkSimple struct pointer for the heap chunk
*/
RzHeapChunkSimple *getHeapChunk(ut64 addr);
/**
* @brief Get heap bins of an arena with given base address
* (including large, small, fast, unsorted, tcache)
* @param arena_addr Base address of the arena
* @return QVector of non empty RzHeapBin pointers
*/
QVector<RzHeapBin *> getHeapBins(ut64 arena_addr);
/**
* @brief Write the given chunk header to memory
* @param chunkSimple RzHeapChunkSimple pointer of the chunk to be written
* @return true if the write succeeded else false
*/
bool writeHeapChunk(RzHeapChunkSimple *chunkSimple);
int getArchBits();
void startDebug();
void startEmulation();
/**
@ -436,7 +490,7 @@ public:
void continueBackDebug();
void continueUntilCall();
void continueUntilSyscall();
void continueUntilDebug(QString offset);
void continueUntilDebug(ut64 offset);
void stepDebug();
void stepOverDebug();
void stepOutDebug();
@ -499,15 +553,14 @@ public:
bool registerDecompiler(Decompiler *decompiler);
RVA getOffsetJump(RVA addr);
QJsonDocument getFileInfo();
QJsonDocument getSignatureInfo();
QJsonDocument getFileVersionInfo();
QStringList getStats();
CutterJson getSignatureInfo();
bool existsFileInfo();
void setGraphEmpty(bool empty);
bool isGraphEmpty();
void getOpcodes();
QList<QString> opcodes;
bool rebaseBin(RVA base_address);
void getRegs();
QList<QString> regs;
void setSettings();
@ -519,10 +572,10 @@ public:
/* Plugins */
QStringList getAsmPluginNames();
QStringList getAnalPluginNames();
QStringList getAnalysisPluginNames();
/* Widgets */
QList<RzBinPluginDescription> getRBinPluginDescriptions(const QString &type = QString());
QList<RzBinPluginDescription> getBinPluginDescriptions(bool bin = true, bool xtr = true);
QList<RzIOPluginDescription> getRIOPluginDescriptions();
QList<RzCorePluginDescription> getRCorePluginDescriptions();
QList<RzAsmPluginDescription> getRAsmPluginDescriptions();
@ -531,7 +584,7 @@ public:
QList<ExportDescription> getAllExports();
QList<SymbolDescription> getAllSymbols();
QList<HeaderDescription> getAllHeaders();
QList<ZignatureDescription> getAllZignatures();
QList<FlirtDescription> getSignaturesDB();
QList<CommentDescription> getAllComments(const QString &filterType);
QList<RelocDescription> getAllRelocs();
QList<StringDescription> getAllStrings();
@ -577,23 +630,10 @@ public:
/**
* @brief Fetching the C representation of a given Type
* @param name - the name or the type of the given Type / Struct
* @param category - the category of the given Type (Struct, Union, Enum, ...)
* @param name - the name or the type of the given Type
* @return The type decleration as C output
*/
QString getTypeAsC(QString name, QString category);
/**
* @brief Adds new types
* It first uses the rz_parse_c_string() function from Rizin API to parse the
* supplied C file (in the form of a string). If there were errors, they are displayed.
* If there were no errors, it uses sdb_query_lines() function from Rizin API
* to save the parsed types returned by rz_parse_c_string()
* \param str Contains the definition of the data types
* \return returns an empty QString if there was no error, else returns the error
*/
QString addTypes(const char *str);
QString addTypes(const QString &str) { return addTypes(str.toUtf8().constData()); }
QString getTypeAsC(QString name);
/**
* @brief Checks if the given address is mapped to a region
@ -604,14 +644,17 @@ public:
QList<MemoryMapDescription> getMemoryMap();
QList<SearchDescription> getAllSearch(QString searchFor, QString space, QString in);
BlockStatistics getBlockStatistics(unsigned int blocksCount);
QList<BreakpointDescription> getBreakpoints();
QList<ProcessDescription> getAllProcesses();
/**
* @brief Get the right RzReg object based on the cutter state (debugging vs emulating)
*/
RzReg *getReg();
/**
* @brief returns a list of reg values and their telescoped references
* @param depth telescoping depth
*/
QList<QJsonObject> getRegisterRefs(int depth = 6);
QList<RegisterRef> getRegisterRefs(int depth = 6);
QVector<RegisterRefValueDescription> getRegisterRefValues();
QList<VariableDescription> getVariables(RVA at);
/**
@ -629,8 +672,6 @@ public:
QList<XrefDescription> getXRefs(RVA addr, bool to, bool whole_function,
const QString &filterType = QString());
QList<StringDescription> parseStringsJson(const QJsonDocument &doc);
void handleREvent(int type, void *data);
/* Signals related */
@ -667,6 +708,10 @@ public:
* @brief Commit write cache to the file on disk.
*/
void commitWriteCache();
/**
* @brief Reset write cache.
*/
void resetWriteCache();
/**
* @brief Enable or disable Write mode. When the file is opened in write mode, any changes to it
@ -681,6 +726,25 @@ public:
*/
bool isWriteModeEnabled();
/**
* @brief Returns the textual version of global or specific graph.
* @param type Graph type, example RZ_CORE_GRAPH_TYPE_FUNCALL or RZ_CORE_GRAPH_TYPE_IMPORT
* @param format Graph format, example RZ_CORE_GRAPH_FORMAT_DOT or RZ_CORE_GRAPH_FORMAT_GML
* @param address The object address (if global set it to RVA_INVALID)
* @return The textual graph string.
*/
char *getTextualGraphAt(RzCoreGraphType type, RzCoreGraphFormat format, RVA address);
/**
* @brief Writes a graphviz graph to a file.
* @param path The file output path
* @param format The output format (see graph.gv.format)
* @param type The graph type, example RZ_CORE_GRAPH_TYPE_FUNCALL or
* RZ_CORE_GRAPH_TYPE_IMPORT
* @param address The object address (if global set it to RVA_INVALID)
*/
void writeGraphvizGraphToFile(QString path, QString format, RzCoreGraphType type, RVA address);
signals:
void refreshAll();
@ -736,8 +800,9 @@ signals:
/**
* @brief seekChanged is emitted each time Rizin's seek value is modified
* @param offset
* @param historyType
*/
void seekChanged(RVA offset);
void seekChanged(RVA offset, SeekHistoryType type = SeekHistoryType::New);
void toggleDebugView();
@ -771,10 +836,11 @@ private:
bool iocache = false;
BasicInstructionHighlighter biHighlighter;
QSharedPointer<RizinCmdTask> debugTask;
QSharedPointer<RizinTask> debugTask;
RizinTaskDialog *debugTaskDialog;
QVector<QString> getCutterRCFilePaths() const;
QList<TypeDescription> getBaseType(RzBaseTypeKind kind, const char *category);
};
class CUTTER_EXPORT RzCoreLocked

Some files were not shown because too many files have changed in this diff Show More