mirror of
https://github.com/rizinorg/cutter.git
synced 2024-12-21 12:26:11 +00:00
Merge 'dev' branch into stable
This commit is contained in:
commit
341d42c3a2
@ -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
|
||||
|
4
.github/ISSUE_TEMPLATE/config.yml
vendored
4
.github/ISSUE_TEMPLATE/config.yml
vendored
@ -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/"
|
||||
|
53
.github/workflows/ci.yml
vendored
53
.github/workflows/ci.yml
vendored
@ -23,6 +23,7 @@ jobs:
|
||||
name: [
|
||||
linux-x86_64,
|
||||
linux-x86_64-system-deps,
|
||||
linux-x86_64-qt6-system-deps,
|
||||
macos-x86_64,
|
||||
windows-x86_64,
|
||||
tarball
|
||||
@ -39,6 +40,12 @@ jobs:
|
||||
system-deps: true
|
||||
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
|
||||
os: ubuntu-22.04
|
||||
python-version: 3.10.x
|
||||
system-deps: true
|
||||
cc-override: '/usr/bin/gcc-12'
|
||||
cxx-override: '/usr/bin/g++-12'
|
||||
- name: linux-x86_64
|
||||
os: ubuntu-18.04
|
||||
python-version: 3.7.x
|
||||
@ -61,7 +68,7 @@ jobs:
|
||||
# Prevent one job from pausing the rest
|
||||
fail-fast: false
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
persist-credentials: false
|
||||
@ -69,32 +76,34 @@ jobs:
|
||||
if: contains(matrix.os, 'ubuntu')
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install libgraphviz-dev mesa-common-dev libxkbcommon-x11-dev libclang-8-dev llvm-8 ninja-build
|
||||
if [[ "${{ matrix.os }}" = "ubuntu-18.04" ]]
|
||||
sudo apt-get install libgraphviz-dev mesa-common-dev libxkbcommon-x11-dev ninja-build
|
||||
if [[ "${{ matrix.os }}" = "ubuntu-18.04" || "${{ matrix.os }}" = "ubuntu-20.04" ]]
|
||||
then
|
||||
# install additional packages needed for appimage
|
||||
sudo apt-get install libxcb1-dev libxkbcommon-dev libxcb-*-dev libegl1
|
||||
sudo apt-get install libxcb1-dev libxkbcommon-dev libxcb-*-dev libegl1 libclang-8-dev llvm-8
|
||||
fi
|
||||
if [[ "${{ matrix.system-deps }}" = "true" ]]
|
||||
if [[ "${{ matrix.os }}" = "ubuntu-18.04" && "${{ matrix.system-deps }}" = "true" ]]
|
||||
then
|
||||
sudo apt-get install qt5-default libqt5svg5-dev qttools5-dev qttools5-dev-tools
|
||||
fi
|
||||
- uses: actions/setup-python@v1
|
||||
if [[ "${{ matrix.os }}" = "ubuntu-22.04" ]]
|
||||
then
|
||||
sudo apt-get install libclang-12-dev llvm-12 qt6-base-dev qt6-tools-dev \
|
||||
qt6-tools-dev-tools libqt6svg6-dev libqt6core5compat6-dev libqt6svgwidgets6 qt6-l10n-tools
|
||||
fi
|
||||
- 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 update --preinstall # temporary workaround for https://github.com/Homebrew/homebrew-bundle/issues/751
|
||||
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
|
||||
brew install coreutils
|
||||
brew install pkg-config
|
||||
- name: py dependencies
|
||||
run: |
|
||||
python3 -m pip install -U pip==21.3.1
|
||||
pip install meson
|
||||
pip install meson==0.61.5 # https://github.com/rizinorg/cutter/runs/7170222817?check_suite_focus=true
|
||||
- name: Prepare package id
|
||||
shell: bash
|
||||
run: |
|
||||
@ -123,8 +132,6 @@ jobs:
|
||||
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
|
||||
@ -138,8 +145,7 @@ jobs:
|
||||
-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_ENABLE_GRAPHVIZ=ON \
|
||||
-DCUTTER_USE_BUNDLED_RIZIN=ON \
|
||||
-DCUTTER_APPIMAGE_BUILD=ON \
|
||||
-DCUTTER_ENABLE_PACKAGING=ON \
|
||||
@ -152,6 +158,14 @@ jobs:
|
||||
-DCMAKE_INSTALL_PREFIX=appdir/usr \
|
||||
-DCMAKE_INSTALL_RPATH_USE_LINK_PATH=ON \
|
||||
..
|
||||
elif [[ "${{ matrix.os }}" = "ubuntu-22.04" ]]
|
||||
then
|
||||
cmake \
|
||||
-G Ninja \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCUTTER_QT6=ON \
|
||||
-DCUTTER_USE_BUNDLED_RIZIN=ON \
|
||||
..
|
||||
else
|
||||
cmake \
|
||||
-G Ninja \
|
||||
@ -194,7 +208,6 @@ jobs:
|
||||
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}-macOS-x86_64
|
||||
@ -205,7 +218,6 @@ jobs:
|
||||
-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_ENABLE_SIGDB=ON \
|
||||
@ -216,7 +228,6 @@ jobs:
|
||||
-DCUTTER_PACKAGE_RZ_LIBSWIFT=ON \
|
||||
-DCUTTER_PACKAGE_RZ_LIBYARA=ON \
|
||||
-DCPACK_PACKAGE_FILE_NAME="$PACKAGE_NAME" \
|
||||
-DCMAKE_FRAMEWORK_PATH="$BREAKPAD_FRAMEWORK_DIR" \
|
||||
-DCPACK_BUNDLE_APPLE_CERT_APP="-" \
|
||||
.. && \
|
||||
make -j4;
|
||||
@ -240,7 +251,6 @@ jobs:
|
||||
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
|
||||
call scripts\prepare_breakpad.bat
|
||||
cd
|
||||
mkdir build
|
||||
cd build
|
||||
@ -258,7 +268,6 @@ jobs:
|
||||
-DCUTTER_PACKAGE_RZ_LIBYARA=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 ^
|
||||
@ -276,7 +285,7 @@ jobs:
|
||||
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@v2
|
||||
- uses: actions/upload-artifact@v3
|
||||
if: env.PACKAGE_NAME != null
|
||||
with:
|
||||
name: ${{ env.PACKAGE_NAME }}
|
||||
@ -284,7 +293,7 @@ jobs:
|
||||
- name: Get release
|
||||
if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags')
|
||||
id: get_release
|
||||
uses: karliss/get-release@23b8b7144dd5b0c9d6942b2fb78bd9ae71546d03
|
||||
uses: rizinorg/gha-get-release@c8074dd5d13ddd0a194d8c9205a1466973c7dc0d
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Upload release assets
|
||||
|
8
.github/workflows/coverity-scan.yml
vendored
8
.github/workflows/coverity-scan.yml
vendored
@ -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:
|
||||
|
4
.github/workflows/docs.yml
vendored
4
.github/workflows/docs.yml
vendored
@ -7,9 +7,9 @@ on:
|
||||
|
||||
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
|
||||
|
4
.github/workflows/linter.yml
vendored
4
.github/workflows/linter.yml
vendored
@ -16,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:
|
||||
@ -33,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
|
||||
|
@ -12,7 +12,6 @@ pipeline:
|
||||
- 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
|
||||
- source scripts/prepare_breakpad_macos.sh
|
||||
- cmake -Bbuild -GNinja
|
||||
-DCMAKE_BUILD_TYPE=Release
|
||||
-DPYTHON_LIBRARY="$$CUTTER_DEPS_PYTHON_PREFIX/lib/libpython3.9.dylib"
|
||||
@ -20,7 +19,6 @@ pipeline:
|
||||
-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_ENABLE_SIGDB=ON
|
||||
@ -31,7 +29,6 @@ pipeline:
|
||||
-DCUTTER_PACKAGE_RZ_LIBSWIFT=ON
|
||||
-DCUTTER_PACKAGE_RZ_LIBYARA=ON
|
||||
-DCPACK_PACKAGE_FILE_NAME="$$PACKAGE_NAME"
|
||||
-DCMAKE_FRAMEWORK_PATH="$$BREAKPAD_FRAMEWORK_DIR"
|
||||
-DCPACK_BUNDLE_APPLE_CERT_APP="-"
|
||||
- ninja -C build
|
||||
package:
|
||||
|
@ -15,7 +15,6 @@ option(CUTTER_USE_ADDITIONAL_RIZIN_PATHS "Search rizin in additional paths which
|
||||
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_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)
|
||||
@ -26,8 +25,8 @@ option(CUTTER_ENABLE_PACKAGING "Enable building platform-specific packages for d
|
||||
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_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_JSDEC "Compile and install jsdec during install step." OFF)
|
||||
OPTION(CUTTER_QT6 "Use QT6" OFF)
|
||||
|
||||
@ -39,9 +38,33 @@ set(CUTTER_VERSION_MAJOR 2)
|
||||
set(CUTTER_VERSION_MINOR 1)
|
||||
set(CUTTER_VERSION_PATCH 2)
|
||||
|
||||
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)
|
||||
|
||||
@ -130,7 +153,6 @@ if(CUTTER_USE_BUNDLED_RIZIN)
|
||||
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}")
|
||||
|
@ -6,7 +6,6 @@ Cutter is a free and open-source reverse engineering platform powered by [rizin]
|
||||
|
||||
[![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/dev?svg=true)](https://ci.appveyor.com/project/rizinorg/cutter/branch/dev)
|
||||
[![Total alerts](https://img.shields.io/lgtm/alerts/g/rizinorg/cutter.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/rizinorg/cutter/alerts/)
|
||||
|
||||
![Screenshot](https://raw.githubusercontent.com/rizinorg/cutter/dev/docs/source/images/screenshot.png)
|
||||
|
||||
@ -23,9 +22,11 @@ Cutter release binaries for all major platforms (Linux, macOS, Windows) can be d
|
||||
- **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
|
||||
|
||||
|
@ -40,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")
|
||||
@ -56,14 +57,14 @@ endif()
|
||||
|
||||
# TODO: This version number should be fetched automatically
|
||||
# instead of being hardcoded.
|
||||
set (Rizin_VERSION 0.4)
|
||||
set (Rizin_VERSION 0.5)
|
||||
|
||||
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_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})
|
||||
|
@ -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)
|
||||
|
8
dist/CMakeLists.txt
vendored
8
dist/CMakeLists.txt
vendored
@ -77,10 +77,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)
|
||||
@ -168,9 +164,9 @@ if(CUTTER_PACKAGE_RZ_GHIDRA)
|
||||
# installed Cutter.
|
||||
ExternalProject_Add(rz-ghidra
|
||||
GIT_REPOSITORY https://github.com/rizinorg/rz-ghidra
|
||||
GIT_TAG v0.4.0
|
||||
#GIT_TAG v0.3.0
|
||||
#GIT_TAG c7a50a2e7c0a95cd52b167c9ee0fa1805223f08e
|
||||
#GIT_TAG dev
|
||||
GIT_TAG dev
|
||||
#GIT_SHALLOW ON # disable this line when using commit hash
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
|
8
dist/MacOSSetupBundle.cmake.in
vendored
8
dist/MacOSSetupBundle.cmake.in
vendored
@ -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@")
|
||||
|
||||
@ -82,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()
|
||||
|
2
dist/bundle_python.ps1
vendored
2
dist/bundle_python.ps1
vendored
@ -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"
|
||||
|
||||
|
@ -63,6 +63,9 @@ 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::
|
||||
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:
|
||||
|
||||
::
|
||||
@ -113,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.
|
||||
@ -252,7 +254,6 @@ Note that there are some major building options available:
|
||||
|
||||
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.
|
||||
|
||||
@ -271,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
|
||||
|
@ -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
|
Binary file not shown.
Before Width: | Height: | Size: 29 KiB |
Binary file not shown.
Before Width: | Height: | Size: 14 KiB |
2
rizin
2
rizin
@ -1 +1 @@
|
||||
Subproject commit 9023f8b997db210cef3b9a25cf1748fbc94942ed
|
||||
Subproject commit 12898a365e70c22892d78abdd2627e7269533c5f
|
@ -3,4 +3,6 @@ brew "ccache"
|
||||
brew "openssl"
|
||||
brew "xz"
|
||||
brew "llvm"
|
||||
brew "meson"
|
||||
brew "meson"
|
||||
brew "coreutils"
|
||||
brew "pkg-config"
|
||||
|
@ -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',
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
@ -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
@ -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%
|
@ -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 ..
|
@ -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
|
@ -5,6 +5,7 @@ set(SOURCES
|
||||
Main.cpp
|
||||
core/Cutter.cpp
|
||||
core/CutterJson.cpp
|
||||
core/RizinCpp.cpp
|
||||
dialogs/EditStringDialog.cpp
|
||||
dialogs/WriteCommandsDialogs.cpp
|
||||
widgets/DisassemblerGraphView.cpp
|
||||
@ -72,7 +73,6 @@ set(SOURCES
|
||||
widgets/CutterTreeWidget.cpp
|
||||
widgets/GraphWidget.cpp
|
||||
widgets/OverviewWidget.cpp
|
||||
common/JsonTreeItem.cpp
|
||||
common/JsonModel.cpp
|
||||
dialogs/VersionInfoDialog.cpp
|
||||
widgets/FlirtWidget.cpp
|
||||
@ -155,6 +155,7 @@ set(HEADER_FILES
|
||||
core/CutterCommon.h
|
||||
core/CutterDescriptions.h
|
||||
core/CutterJson.h
|
||||
core/RizinCpp.h
|
||||
dialogs/EditStringDialog.h
|
||||
dialogs/WriteCommandsDialogs.h
|
||||
widgets/DisassemblerGraphView.h
|
||||
@ -222,7 +223,6 @@ set(HEADER_FILES
|
||||
widgets/CutterTreeWidget.h
|
||||
widgets/GraphWidget.h
|
||||
widgets/OverviewWidget.h
|
||||
common/JsonTreeItem.h
|
||||
common/JsonModel.h
|
||||
dialogs/VersionInfoDialog.h
|
||||
widgets/FlirtWidget.h
|
||||
@ -262,7 +262,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
|
||||
@ -393,10 +392,6 @@ if (CUTTER_ENABLE_PYTHON)
|
||||
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)
|
||||
set(BINDINGS_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/bindings")
|
||||
set(BINDINGS_BUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}/bindings")
|
||||
@ -442,8 +437,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 ${OPTIONS} ${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 ..
|
||||
@ -452,6 +450,9 @@ set_target_properties(Cutter PROPERTIES
|
||||
CXX_VISIBILITY_PRESET hidden)
|
||||
target_compile_definitions(Cutter PRIVATE CUTTER_SOURCE_BUILD)
|
||||
|
||||
# Set Cutter as the startup project in Visual Studio
|
||||
set_property(DIRECTORY ${PROJECT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT Cutter)
|
||||
|
||||
set(CUTTER_INCLUDE_DIRECTORIES core widgets common plugins menus .)
|
||||
foreach(_dir ${CUTTER_INCLUDE_DIRECTORIES})
|
||||
target_include_directories(Cutter PUBLIC
|
||||
@ -465,19 +466,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)
|
||||
|
@ -1,5 +1,4 @@
|
||||
#include "common/PythonManager.h"
|
||||
#include "common/CrashHandler.h"
|
||||
#include "CutterApplication.h"
|
||||
#include "plugins/PluginManager.h"
|
||||
#include "CutterConfig.h"
|
||||
|
15
src/Main.cpp
15
src/Main.cpp
@ -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>
|
||||
@ -55,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
|
||||
@ -74,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.
|
||||
|
@ -8,26 +8,18 @@
|
||||
|
||||
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());
|
||||
CutterJson docu = Core()->getFileInfo();
|
||||
CutterJson coreObj = docu["core"];
|
||||
CutterJson binObj = docu["bin"];
|
||||
if (binObj.size()) {
|
||||
format = coreObj["format"].toString();
|
||||
arch = binObj["arch"].toString();
|
||||
if (binObj["type"].valid()) {
|
||||
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* Obtained from:\n"
|
||||
|
@ -201,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.
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
@ -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
|
@ -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)
|
||||
{
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
@ -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
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -120,13 +120,6 @@ static void updateOwnedCharPtr(char *&variable, const QString &newValue)
|
||||
variable = strdup(data.data());
|
||||
}
|
||||
|
||||
static QString fromOwnedCharPtr(char *str)
|
||||
{
|
||||
QString result(str ? str : "");
|
||||
rz_mem_free(str);
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool reg_sync(RzCore *core, RzRegisterType type, bool write)
|
||||
{
|
||||
if (rz_core_is_debug(core)) {
|
||||
@ -377,6 +370,37 @@ QString CutterCore::cmd(const char *str)
|
||||
return o;
|
||||
}
|
||||
|
||||
QString CutterCore::getFunctionExecOut(const std::function<bool(RzCore *)> &fcn, const RVA addr)
|
||||
{
|
||||
CORE_LOCK();
|
||||
|
||||
RVA offset = core->offset;
|
||||
seekSilent(addr);
|
||||
QString o = {};
|
||||
rz_cons_push();
|
||||
bool is_pipe = core->is_pipe;
|
||||
core->is_pipe = true;
|
||||
|
||||
if (!fcn(core)) {
|
||||
core->is_pipe = is_pipe;
|
||||
rz_cons_pop();
|
||||
goto clean_return;
|
||||
}
|
||||
|
||||
core->is_pipe = is_pipe;
|
||||
rz_cons_filter();
|
||||
o = rz_cons_get_buffer();
|
||||
|
||||
rz_cons_pop();
|
||||
rz_cons_echo(NULL);
|
||||
|
||||
clean_return:
|
||||
if (offset != core->offset) {
|
||||
seekSilent(offset);
|
||||
}
|
||||
return o;
|
||||
}
|
||||
|
||||
bool CutterCore::isRedirectableDebugee()
|
||||
{
|
||||
if (!currentlyDebugging || currentlyAttachedToPID != -1) {
|
||||
@ -406,48 +430,6 @@ bool CutterCore::isDebugTaskInProgress()
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CutterCore::asyncCmdEsil(const char *command, QSharedPointer<RizinTask> &task)
|
||||
{
|
||||
asyncCmd(command, task);
|
||||
|
||||
if (task.isNull()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
connect(task.data(), &RizinCmdTask::finished, task.data(), [this, task]() {
|
||||
QString res = qobject_cast<RizinCmdTask *>(task.data())->getResult();
|
||||
|
||||
if (res.contains(QStringLiteral("[ESIL] Stopped execution in an invalid instruction"))) {
|
||||
msgBox.showMessage("Stopped when attempted to run an invalid instruction. You can "
|
||||
"disable this in Preferences");
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CutterCore::asyncCmd(const char *str, QSharedPointer<RizinTask> &task)
|
||||
{
|
||||
if (!task.isNull()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CORE_LOCK();
|
||||
|
||||
RVA offset = core->offset;
|
||||
|
||||
task = QSharedPointer<RizinTask>(new RizinCmdTask(str, true));
|
||||
connect(task.data(), &RizinTask::finished, task.data(), [this, offset, task]() {
|
||||
CORE_LOCK();
|
||||
|
||||
if (offset != core->offset) {
|
||||
updateSeek();
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CutterCore::asyncTask(std::function<void *(RzCore *)> fcn, QSharedPointer<RizinTask> &task)
|
||||
{
|
||||
if (!task.isNull()) {
|
||||
@ -468,6 +450,13 @@ bool CutterCore::asyncTask(std::function<void *(RzCore *)> fcn, QSharedPointer<R
|
||||
return true;
|
||||
}
|
||||
|
||||
void CutterCore::functionTask(std::function<void *(RzCore *)> fcn)
|
||||
{
|
||||
auto task = std::unique_ptr<RizinTask>(new RizinFunctionTask(std::move(fcn), true));
|
||||
task->startTask();
|
||||
task->joinTask();
|
||||
}
|
||||
|
||||
QString CutterCore::cmdRawAt(const char *cmd, RVA address)
|
||||
{
|
||||
QString res;
|
||||
@ -486,8 +475,8 @@ QString CutterCore::cmdRaw(const char *cmd)
|
||||
CORE_LOCK();
|
||||
rz_cons_push();
|
||||
|
||||
// rz_cmd_call does not return the output of the command
|
||||
rz_cmd_call(core->rcmd, cmd);
|
||||
// rz_core_cmd does not return the output of the command
|
||||
rz_core_cmd(core, cmd, 0);
|
||||
|
||||
// we grab the output straight from rz_cons
|
||||
res = rz_cons_get_buffer();
|
||||
@ -510,18 +499,6 @@ CutterJson CutterCore::cmdj(const char *str)
|
||||
return parseJson(res, str);
|
||||
}
|
||||
|
||||
CutterJson CutterCore::cmdjAt(const char *str, RVA address)
|
||||
{
|
||||
CutterJson res;
|
||||
RVA oldOffset = getOffset();
|
||||
seekSilent(address);
|
||||
|
||||
res = cmdj(str);
|
||||
|
||||
seekSilent(oldOffset);
|
||||
return res;
|
||||
}
|
||||
|
||||
QString CutterCore::cmdTask(const QString &str)
|
||||
{
|
||||
RizinCmdTask task(str);
|
||||
@ -530,14 +507,6 @@ QString CutterCore::cmdTask(const QString &str)
|
||||
return task.getResult();
|
||||
}
|
||||
|
||||
CutterJson CutterCore::cmdjTask(const QString &str)
|
||||
{
|
||||
RizinCmdTask task(str);
|
||||
task.startTask();
|
||||
task.joinTask();
|
||||
return task.getResultJson();
|
||||
}
|
||||
|
||||
CutterJson CutterCore::parseJson(char *res, const char *cmd)
|
||||
{
|
||||
if (!res) {
|
||||
@ -687,7 +656,7 @@ bool CutterCore::mapFile(QString path, RVA mapaddr)
|
||||
{
|
||||
CORE_LOCK();
|
||||
RVA addr = mapaddr != RVA_INVALID ? mapaddr : 0;
|
||||
ut64 baddr = Core()->getFileInfo()["bin"]["baddr"].toUt64();
|
||||
ut64 baddr = rz_bin_get_baddr(core->bin);
|
||||
if (rz_core_file_open(core, path.toUtf8().constData(), RZ_PERM_RX, addr)) {
|
||||
rz_core_bin_load(core, path.toUtf8().constData(), baddr);
|
||||
} else {
|
||||
@ -746,34 +715,31 @@ void CutterCore::delFlag(const QString &name)
|
||||
emit flagsChanged();
|
||||
}
|
||||
|
||||
PRzAnalysisBytes CutterCore::getRzAnalysisBytesSingle(RVA addr)
|
||||
{
|
||||
CORE_LOCK();
|
||||
ut8 buf[128];
|
||||
rz_io_read_at(core->io, addr, buf, sizeof(buf));
|
||||
|
||||
auto seek = seekTemp(addr);
|
||||
auto vec = fromOwned(rz_core_analysis_bytes(core, buf, sizeof(buf), 1));
|
||||
|
||||
auto ab = vec && rz_pvector_len(vec.get()) > 0
|
||||
? reinterpret_cast<RzAnalysisBytes *>(rz_pvector_pop_front(vec.get()))
|
||||
: nullptr;
|
||||
return { ab, rz_analysis_bytes_free };
|
||||
}
|
||||
|
||||
QString CutterCore::getInstructionBytes(RVA addr)
|
||||
{
|
||||
auto ret = (char *)Core()->returnAtSeek(
|
||||
[&]() {
|
||||
CORE_LOCK();
|
||||
RzPVector *vec = rz_core_analysis_bytes(core, core->block, (int)core->blocksize, 1);
|
||||
auto *ab = static_cast<RzAnalysisBytes *>(rz_pvector_head(vec));
|
||||
char *str = strdup(ab->bytes);
|
||||
rz_pvector_free(vec);
|
||||
return str;
|
||||
},
|
||||
addr);
|
||||
return fromOwnedCharPtr(ret);
|
||||
auto ab = getRzAnalysisBytesSingle(addr);
|
||||
return ab ? ab->bytes : "";
|
||||
}
|
||||
|
||||
QString CutterCore::getInstructionOpcode(RVA addr)
|
||||
{
|
||||
auto ret = (char *)Core()->returnAtSeek(
|
||||
[&]() {
|
||||
CORE_LOCK();
|
||||
RzPVector *vec = rz_core_analysis_bytes(core, core->block, (int)core->blocksize, 1);
|
||||
auto *ab = static_cast<RzAnalysisBytes *>(rz_pvector_head(vec));
|
||||
char *str = strdup(ab->opcode);
|
||||
rz_pvector_free(vec);
|
||||
return str;
|
||||
},
|
||||
addr);
|
||||
return fromOwnedCharPtr(ret);
|
||||
auto ab = getRzAnalysisBytesSingle(addr);
|
||||
return ab ? ab->opcode : "";
|
||||
}
|
||||
|
||||
void CutterCore::editInstruction(RVA addr, const QString &inst, bool fillWithNops)
|
||||
@ -790,14 +756,20 @@ void CutterCore::editInstruction(RVA addr, const QString &inst, bool fillWithNop
|
||||
void CutterCore::nopInstruction(RVA addr)
|
||||
{
|
||||
CORE_LOCK();
|
||||
applyAtSeek([&]() { rz_core_hack(core, "nop"); }, addr);
|
||||
{
|
||||
auto seek = seekTemp(addr);
|
||||
rz_core_hack(core, "nop");
|
||||
}
|
||||
emit instructionChanged(addr);
|
||||
}
|
||||
|
||||
void CutterCore::jmpReverse(RVA addr)
|
||||
{
|
||||
CORE_LOCK();
|
||||
applyAtSeek([&]() { rz_core_hack(core, "recj"); }, addr);
|
||||
{
|
||||
auto seek = seekTemp(addr);
|
||||
rz_core_hack(core, "recj");
|
||||
}
|
||||
emit instructionChanged(addr);
|
||||
}
|
||||
|
||||
@ -879,8 +851,8 @@ QString CutterCore::getString(RVA addr, uint64_t len, RzStrEnc encoding, bool es
|
||||
opt.length = len;
|
||||
opt.encoding = encoding;
|
||||
opt.escape_nl = escape_nl;
|
||||
char *s = (char *)returnAtSeek([&]() { return rz_str_stringify_raw_buffer(&opt, NULL); }, addr);
|
||||
return fromOwnedCharPtr(s);
|
||||
auto seek = seekTemp(addr);
|
||||
return fromOwnedCharPtr(rz_str_stringify_raw_buffer(&opt, NULL));
|
||||
}
|
||||
|
||||
QString CutterCore::getMetaString(RVA addr)
|
||||
@ -964,12 +936,11 @@ void CutterCore::applyStructureOffset(const QString &structureOffset, RVA offset
|
||||
offset = getOffset();
|
||||
}
|
||||
|
||||
applyAtSeek(
|
||||
[&]() {
|
||||
CORE_LOCK();
|
||||
rz_core_analysis_hint_set_offset(core, structureOffset.toUtf8().constData());
|
||||
},
|
||||
offset);
|
||||
{
|
||||
CORE_LOCK();
|
||||
auto seek = seekTemp(offset);
|
||||
rz_core_analysis_hint_set_offset(core, structureOffset.toUtf8().constData());
|
||||
}
|
||||
emit instructionChanged(offset);
|
||||
}
|
||||
|
||||
@ -1055,24 +1026,20 @@ RVA CutterCore::prevOpAddr(RVA startAddr, int count)
|
||||
RVA CutterCore::nextOpAddr(RVA startAddr, int count)
|
||||
{
|
||||
CORE_LOCK();
|
||||
auto seek = seekTemp(startAddr);
|
||||
auto vec =
|
||||
fromOwned(rz_core_analysis_bytes(core, core->block, (int)core->blocksize, count + 1));
|
||||
|
||||
CutterJson array =
|
||||
Core()->cmdj("pdj " + QString::number(count + 1) + " @ " + QString::number(startAddr));
|
||||
if (!array.size()) {
|
||||
return startAddr + 1;
|
||||
RVA addr = startAddr + 1;
|
||||
if (!vec) {
|
||||
return addr;
|
||||
}
|
||||
|
||||
CutterJson instValue = array.last();
|
||||
if (instValue.type() != RZ_JSON_OBJECT) {
|
||||
return startAddr + 1;
|
||||
auto ab = reinterpret_cast<RzAnalysisBytes *>(rz_pvector_tail(vec.get()));
|
||||
if (!(ab && ab->op)) {
|
||||
return addr;
|
||||
}
|
||||
|
||||
RVA offset = instValue[RJsonKey::offset].toRVA();
|
||||
if (offset == RVA_INVALID) {
|
||||
return startAddr + 1;
|
||||
}
|
||||
|
||||
return offset;
|
||||
addr = ab->op->addr;
|
||||
return addr;
|
||||
}
|
||||
|
||||
RVA CutterCore::getOffset()
|
||||
@ -1296,7 +1263,8 @@ QString CutterCore::disassemble(const QByteArray &data)
|
||||
|
||||
QString CutterCore::disassembleSingleInstruction(RVA addr)
|
||||
{
|
||||
return cmdRawAt("pi 1", addr).simplified();
|
||||
auto ab = getRzAnalysisBytesSingle(addr);
|
||||
return QString(ab->disasm).simplified();
|
||||
}
|
||||
|
||||
RzAnalysisFunction *CutterCore::functionIn(ut64 addr)
|
||||
@ -1370,16 +1338,6 @@ QString CutterCore::flagAt(RVA addr)
|
||||
return core->flags->realnames && f->realname ? f->realname : f->name;
|
||||
}
|
||||
|
||||
void CutterCore::cmdEsil(const char *command)
|
||||
{
|
||||
// use cmd and not cmdRaw because of unexpected commands
|
||||
QString res = cmd(command);
|
||||
if (res.contains(QStringLiteral("[ESIL] Stopped execution in an invalid instruction"))) {
|
||||
msgBox.showMessage("Stopped when attempted to run an invalid instruction. You can disable "
|
||||
"this in Preferences");
|
||||
}
|
||||
}
|
||||
|
||||
void CutterCore::createFunctionAt(RVA addr)
|
||||
{
|
||||
createFunctionAt(addr, "");
|
||||
@ -1400,19 +1358,8 @@ void CutterCore::createFunctionAt(RVA addr, QString name)
|
||||
|
||||
RVA CutterCore::getOffsetJump(RVA addr)
|
||||
{
|
||||
auto rva = (RVA *)Core()->returnAtSeek(
|
||||
[&]() {
|
||||
CORE_LOCK();
|
||||
RzPVector *vec = rz_core_analysis_bytes(core, core->block, (int)core->blocksize, 1);
|
||||
auto *ab = static_cast<RzAnalysisBytes *>(rz_pvector_head(vec));
|
||||
RVA *rva = new RVA(ab->op->jump);
|
||||
rz_pvector_free(vec);
|
||||
return rva;
|
||||
},
|
||||
addr);
|
||||
RVA ret = *rva;
|
||||
delete rva;
|
||||
return ret;
|
||||
auto ab = getRzAnalysisBytesSingle(addr);
|
||||
return ab && ab->op ? ab->op->jump : RVA_INVALID;
|
||||
}
|
||||
|
||||
QList<Decompiler *> CutterCore::getDecompilers()
|
||||
@ -1440,17 +1387,7 @@ bool CutterCore::registerDecompiler(Decompiler *decompiler)
|
||||
return true;
|
||||
}
|
||||
|
||||
CutterJson CutterCore::getFileInfo()
|
||||
{
|
||||
return cmdj("ij");
|
||||
}
|
||||
|
||||
CutterJson CutterCore::getFileVersionInfo()
|
||||
{
|
||||
return cmdj("iVj");
|
||||
}
|
||||
|
||||
QString CutterCore::getSignatureInfo()
|
||||
CutterJson CutterCore::getSignatureInfo()
|
||||
{
|
||||
CORE_LOCK();
|
||||
RzBinFile *cur = rz_bin_cur(core->bin);
|
||||
@ -1462,7 +1399,17 @@ QString CutterCore::getSignatureInfo()
|
||||
if (!signature) {
|
||||
return {};
|
||||
}
|
||||
return fromOwnedCharPtr(signature);
|
||||
return parseJson(signature, nullptr);
|
||||
}
|
||||
|
||||
bool CutterCore::existsFileInfo()
|
||||
{
|
||||
CORE_LOCK();
|
||||
const RzBinInfo *info = rz_bin_get_info(core->bin);
|
||||
if (!(info && info->rclass)) {
|
||||
return false;
|
||||
}
|
||||
return strncmp("pe", info->rclass, 2) == 0 || strncmp("elf", info->rclass, 3) == 0;
|
||||
}
|
||||
|
||||
// Utility function to check if a telescoped item exists and add it with prefixes to the desc
|
||||
@ -1846,18 +1793,7 @@ QList<VariableDescription> CutterCore::getVariables(RVA at)
|
||||
}
|
||||
for (auto var : CutterPVector<RzAnalysisVar>(&fcn->vars)) {
|
||||
VariableDescription desc;
|
||||
switch (var->kind) {
|
||||
case RZ_ANALYSIS_VAR_KIND_BPV:
|
||||
desc.refType = VariableDescription::RefType::BP;
|
||||
break;
|
||||
case RZ_ANALYSIS_VAR_KIND_SPV:
|
||||
desc.refType = VariableDescription::RefType::SP;
|
||||
break;
|
||||
case RZ_ANALYSIS_VAR_KIND_REG:
|
||||
default:
|
||||
desc.refType = VariableDescription::RefType::Reg;
|
||||
break;
|
||||
}
|
||||
desc.storageType = var->storage.type;
|
||||
if (!var->name || !var->type) {
|
||||
continue;
|
||||
}
|
||||
@ -2139,9 +2075,15 @@ void CutterCore::attachDebug(int pid)
|
||||
offsetPriorDebugging = getOffset();
|
||||
}
|
||||
|
||||
QString attach_command = currentlyOpenFile.isEmpty() ? "o" : "oodf";
|
||||
// attach to process with dbg plugin
|
||||
asyncCmd("e cfg.debug=true;" + attach_command + " dbg://" + QString::number(pid), debugTask);
|
||||
CORE_LOCK();
|
||||
setConfig("cfg.debug", true);
|
||||
auto uri = rz_str_newf("dbg://%d", pid);
|
||||
if (currentlyOpenFile.isEmpty()) {
|
||||
rz_core_file_open_load(core, uri, 0, RZ_PERM_R, false);
|
||||
} else {
|
||||
rz_core_file_reopen_remote_debug(core, uri, 0);
|
||||
}
|
||||
free(uri);
|
||||
|
||||
emit debugTaskStateChanged();
|
||||
|
||||
@ -2195,7 +2137,8 @@ void CutterCore::stopDebug()
|
||||
|
||||
CORE_LOCK();
|
||||
if (currentlyEmulating) {
|
||||
cmdEsil("aeim- ; aei-");
|
||||
rz_core_analysis_esil_init_mem_del(core, NULL, UT64_MAX, UT32_MAX);
|
||||
rz_core_analysis_esil_deinit(core);
|
||||
resetWriteCache();
|
||||
rz_core_debug_clear_register_flags(core);
|
||||
rz_core_analysis_esil_trace_stop(core);
|
||||
@ -2919,11 +2862,9 @@ bool CutterCore::isGraphEmpty()
|
||||
return emptyGraph;
|
||||
}
|
||||
|
||||
void CutterCore::getOpcodes()
|
||||
void CutterCore::getRegs()
|
||||
{
|
||||
CORE_LOCK();
|
||||
this->opcodes = cmdList("?O");
|
||||
|
||||
this->regs = {};
|
||||
const RzList *rs = rz_reg_get_list(getReg(), RZ_REG_TYPE_ANY);
|
||||
if (!rs) {
|
||||
@ -3099,12 +3040,8 @@ QList<FunctionDescription> CutterCore::getAllFunctions()
|
||||
FunctionDescription function;
|
||||
function.offset = fcn->addr;
|
||||
function.linearSize = rz_analysis_function_linear_size(fcn);
|
||||
function.nargs = rz_analysis_var_count(core->analysis, fcn, 'b', 1)
|
||||
+ rz_analysis_var_count(core->analysis, fcn, 'r', 1)
|
||||
+ rz_analysis_var_count(core->analysis, fcn, 's', 1);
|
||||
function.nlocals = rz_analysis_var_count(core->analysis, fcn, 'b', 0)
|
||||
+ rz_analysis_var_count(core->analysis, fcn, 'r', 0)
|
||||
+ rz_analysis_var_count(core->analysis, fcn, 's', 0);
|
||||
function.nargs = rz_analysis_arg_count(fcn);
|
||||
function.nlocals = rz_analysis_var_local_count(fcn);
|
||||
function.nbbs = rz_list_length(fcn->bbs);
|
||||
function.calltype = fcn->cc ? QString::fromUtf8(fcn->cc) : QString();
|
||||
function.name = fcn->name ? QString::fromUtf8(fcn->name) : QString();
|
||||
@ -3179,21 +3116,38 @@ QList<ImportDescription> CutterCore::getAllImports()
|
||||
QList<ExportDescription> CutterCore::getAllExports()
|
||||
{
|
||||
CORE_LOCK();
|
||||
QList<ExportDescription> ret;
|
||||
|
||||
for (CutterJson exportObject : cmdj("iEj")) {
|
||||
ExportDescription exp;
|
||||
|
||||
exp.vaddr = exportObject[RJsonKey::vaddr].toRVA();
|
||||
exp.paddr = exportObject[RJsonKey::paddr].toRVA();
|
||||
exp.size = exportObject[RJsonKey::size].toRVA();
|
||||
exp.type = exportObject[RJsonKey::type].toString();
|
||||
exp.name = exportObject[RJsonKey::name].toString();
|
||||
exp.flag_name = exportObject[RJsonKey::flagname].toString();
|
||||
|
||||
ret << exp;
|
||||
RzBinFile *bf = rz_bin_cur(core->bin);
|
||||
if (!bf) {
|
||||
return {};
|
||||
}
|
||||
const RzList *symbols = rz_bin_object_get_symbols(bf->o);
|
||||
if (!symbols) {
|
||||
return {};
|
||||
}
|
||||
|
||||
QString lang = getConfigi("bin.demangle") ? getConfig("bin.lang") : "";
|
||||
bool va = core->io->va || core->bin->is_debugger;
|
||||
|
||||
QList<ExportDescription> ret;
|
||||
for (const auto &symbol : CutterRzList<RzBinSymbol>(symbols)) {
|
||||
if (!(symbol->name && rz_core_sym_is_export(symbol))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
RzBinSymNames sn = {};
|
||||
rz_core_sym_name_init(core, &sn, symbol, lang.isEmpty() ? NULL : lang.toUtf8().constData());
|
||||
|
||||
ExportDescription exportDescription;
|
||||
exportDescription.vaddr = rva(bf->o, symbol->paddr, symbol->vaddr, va);
|
||||
exportDescription.paddr = symbol->paddr;
|
||||
exportDescription.size = symbol->size;
|
||||
exportDescription.type = symbol->type;
|
||||
exportDescription.name = sn.symbolname;
|
||||
exportDescription.flag_name = sn.nameflag;
|
||||
ret << exportDescription;
|
||||
|
||||
rz_core_sym_name_fini(&sn);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -3349,22 +3303,37 @@ QList<RelocDescription> CutterCore::getAllRelocs()
|
||||
|
||||
QList<StringDescription> CutterCore::getAllStrings()
|
||||
{
|
||||
return parseStringsJson(cmdjTask("izzj"));
|
||||
}
|
||||
CORE_LOCK();
|
||||
RzBinFile *bf = rz_bin_cur(core->bin);
|
||||
if (!bf) {
|
||||
return {};
|
||||
}
|
||||
RzBinObject *obj = rz_bin_cur_object(core->bin);
|
||||
if (!obj) {
|
||||
return {};
|
||||
}
|
||||
RzList *l = rz_core_bin_whole_strings(core, bf);
|
||||
if (!l) {
|
||||
return {};
|
||||
}
|
||||
|
||||
int va = core->io->va || core->bin->is_debugger;
|
||||
RzStrEscOptions opt = {};
|
||||
opt.show_asciidot = false;
|
||||
opt.esc_bslash = true;
|
||||
opt.esc_double_quotes = true;
|
||||
|
||||
QList<StringDescription> CutterCore::parseStringsJson(const CutterJson &doc)
|
||||
{
|
||||
QList<StringDescription> ret;
|
||||
for (const auto &str : CutterRzList<RzBinString>(l)) {
|
||||
auto section = obj ? rz_bin_get_section_at(obj, str->paddr, 0) : NULL;
|
||||
|
||||
for (CutterJson value : doc) {
|
||||
StringDescription string;
|
||||
|
||||
string.string = value[RJsonKey::string].toString();
|
||||
string.vaddr = value[RJsonKey::vaddr].toRVA();
|
||||
string.type = value[RJsonKey::type].toString();
|
||||
string.size = value[RJsonKey::size].toUt64();
|
||||
string.length = value[RJsonKey::length].toUt64();
|
||||
string.section = value[RJsonKey::section].toString();
|
||||
string.string = rz_str_escape_utf8_keep_printable(str->string, &opt);
|
||||
string.vaddr = obj ? rva(obj, str->paddr, str->vaddr, va) : str->paddr;
|
||||
string.type = rz_str_enc_as_string(str->type);
|
||||
string.size = str->size;
|
||||
string.length = str->length;
|
||||
string.section = section ? section->name : "";
|
||||
|
||||
ret << string;
|
||||
}
|
||||
@ -3881,7 +3850,7 @@ QList<TypeDescription> CutterCore::getBaseType(RzBaseTypeKind kind, const char *
|
||||
|
||||
exp.type = type->name;
|
||||
exp.size = rz_type_db_base_get_bitsize(core->analysis->typedb, type);
|
||||
exp.format = rz_type_format(core->analysis->typedb, type->name);
|
||||
exp.format = rz_base_type_as_format(core->analysis->typedb, type);
|
||||
exp.category = tr(category);
|
||||
types << exp;
|
||||
}
|
||||
@ -3923,8 +3892,7 @@ QString CutterCore::getTypeAsC(QString name)
|
||||
return output;
|
||||
}
|
||||
char *earg = rz_cmd_escape_arg(name.toUtf8().constData(), RZ_CMD_ESCAPE_ONE_ARG);
|
||||
// TODO: use API for `tc` command once available
|
||||
QString result = cmd(QString("tc %1").arg(earg));
|
||||
QString result = fromOwnedCharPtr(rz_core_types_as_c(core, earg, true));
|
||||
free(earg);
|
||||
return result;
|
||||
}
|
||||
@ -3979,22 +3947,34 @@ QList<SearchDescription> CutterCore::getAllSearch(QString searchFor, QString spa
|
||||
QList<XrefDescription> CutterCore::getXRefsForVariable(QString variableName, bool findWrites,
|
||||
RVA offset)
|
||||
{
|
||||
CORE_LOCK();
|
||||
auto fcn = functionIn(offset);
|
||||
if (!fcn) {
|
||||
return {};
|
||||
}
|
||||
const auto typ =
|
||||
findWrites ? RZ_ANALYSIS_VAR_ACCESS_TYPE_WRITE : RZ_ANALYSIS_VAR_ACCESS_TYPE_READ;
|
||||
QList<XrefDescription> xrefList = QList<XrefDescription>();
|
||||
for (CutterJson xrefObject : cmdjAt(findWrites ? "afvWj" : "afvRj", offset)) {
|
||||
QString name = xrefObject[RJsonKey::name].toString();
|
||||
if (name == variableName) {
|
||||
for (CutterJson address : xrefObject[RJsonKey::addrs]) {
|
||||
XrefDescription xref;
|
||||
RVA addr = address.toRVA();
|
||||
xref.from = addr;
|
||||
xref.to = addr;
|
||||
if (findWrites) {
|
||||
xref.from_str = RzAddressString(addr);
|
||||
} else {
|
||||
xref.to_str = RzAddressString(addr);
|
||||
}
|
||||
xrefList << xref;
|
||||
for (const auto &v : CutterPVector<RzAnalysisVar>(&fcn->vars)) {
|
||||
if (variableName != v->name) {
|
||||
continue;
|
||||
}
|
||||
RzAnalysisVarAccess *acc;
|
||||
CutterRzVectorForeach(&v->accesses, acc, RzAnalysisVarAccess)
|
||||
{
|
||||
if (!(acc->type & typ)) {
|
||||
continue;
|
||||
}
|
||||
XrefDescription xref;
|
||||
RVA addr = fcn->addr + acc->offset;
|
||||
xref.from = addr;
|
||||
xref.to = addr;
|
||||
if (findWrites) {
|
||||
xref.from_str = RzAddressString(addr);
|
||||
} else {
|
||||
xref.to_str = RzAddressString(addr);
|
||||
}
|
||||
xrefList << xref;
|
||||
}
|
||||
}
|
||||
return xrefList;
|
||||
@ -4138,18 +4118,33 @@ void CutterCore::loadPDB(const QString &file)
|
||||
|
||||
QList<DisassemblyLine> CutterCore::disassembleLines(RVA offset, int lines)
|
||||
{
|
||||
CutterJson array = cmdj(QString("pdJ ") + QString::number(lines) + QString(" @ ")
|
||||
+ QString::number(offset));
|
||||
QList<DisassemblyLine> r;
|
||||
|
||||
for (CutterJson object : array) {
|
||||
DisassemblyLine line;
|
||||
line.offset = object[RJsonKey::offset].toRVA();
|
||||
line.text = ansiEscapeToHtml(object[RJsonKey::text].toString());
|
||||
line.arrow = object[RJsonKey::arrow].toRVA();
|
||||
r << line;
|
||||
CORE_LOCK();
|
||||
auto vec = fromOwned(
|
||||
rz_pvector_new(reinterpret_cast<RzPVectorFree>(rz_analysis_disasm_text_free)));
|
||||
if (!vec) {
|
||||
return {};
|
||||
}
|
||||
|
||||
RzCoreDisasmOptions options = {};
|
||||
options.cbytes = 1;
|
||||
options.vec = vec.get();
|
||||
{
|
||||
auto restoreSeek = seekTemp(offset);
|
||||
if (rz_cons_singleton()->is_html) {
|
||||
rz_cons_singleton()->is_html = false;
|
||||
rz_cons_singleton()->was_html = true;
|
||||
}
|
||||
rz_core_print_disasm(core, offset, core->block, core->blocksize, lines, NULL, &options);
|
||||
}
|
||||
|
||||
QList<DisassemblyLine> r;
|
||||
for (const auto &t : CutterPVector<RzAnalysisDisasmText>(vec.get())) {
|
||||
DisassemblyLine line;
|
||||
line.offset = t->offset;
|
||||
line.text = ansiEscapeToHtml(t->text);
|
||||
line.arrow = t->arrow;
|
||||
r << line;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
@ -4161,28 +4156,36 @@ QList<DisassemblyLine> CutterCore::disassembleLines(RVA offset, int lines)
|
||||
*/
|
||||
QString CutterCore::hexdump(RVA address, int size, HexdumpFormats format)
|
||||
{
|
||||
QString command = "px";
|
||||
CORE_LOCK();
|
||||
char *res = nullptr;
|
||||
switch (format) {
|
||||
case HexdumpFormats::Normal:
|
||||
res = rz_core_print_hexdump_or_hexdiff_str(core, RZ_OUTPUT_MODE_STANDARD, address, size,
|
||||
false);
|
||||
break;
|
||||
case HexdumpFormats::Half:
|
||||
command += "h";
|
||||
res = rz_core_print_dump_str(core, RZ_OUTPUT_MODE_STANDARD, address, 2, size,
|
||||
RZ_CORE_PRINT_FORMAT_TYPE_HEXADECIMAL);
|
||||
break;
|
||||
case HexdumpFormats::Word:
|
||||
command += "w";
|
||||
res = rz_core_print_dump_str(core, RZ_OUTPUT_MODE_STANDARD, address, 4, size,
|
||||
RZ_CORE_PRINT_FORMAT_TYPE_HEXADECIMAL);
|
||||
break;
|
||||
case HexdumpFormats::Quad:
|
||||
command += "q";
|
||||
res = rz_core_print_dump_str(core, RZ_OUTPUT_MODE_STANDARD, address, 8, size,
|
||||
RZ_CORE_PRINT_FORMAT_TYPE_HEXADECIMAL);
|
||||
break;
|
||||
case HexdumpFormats::Signed:
|
||||
command += "d";
|
||||
res = rz_core_print_dump_str(core, RZ_OUTPUT_MODE_STANDARD, address, 1, size,
|
||||
RZ_CORE_PRINT_FORMAT_TYPE_INTEGER);
|
||||
break;
|
||||
case HexdumpFormats::Octal:
|
||||
command += "o";
|
||||
res = rz_core_print_dump_str(core, RZ_OUTPUT_MODE_STANDARD, address, 1, size,
|
||||
RZ_CORE_PRINT_FORMAT_TYPE_OCTAL);
|
||||
break;
|
||||
}
|
||||
|
||||
return cmdRawAt(QString("%1 %2").arg(command).arg(size), address);
|
||||
return fromOwnedCharPtr(res);
|
||||
}
|
||||
|
||||
QByteArray CutterCore::hexStringToBytes(const QString &hex)
|
||||
@ -4212,10 +4215,9 @@ void CutterCore::loadScript(const QString &scriptname)
|
||||
triggerRefreshAll();
|
||||
}
|
||||
|
||||
QString CutterCore::getRizinVersionReadable()
|
||||
QString CutterCore::getRizinVersionReadable(const char *program)
|
||||
{
|
||||
return QString("%1 (%2)").arg(QString::fromUtf8(RZ_VERSION),
|
||||
QString::fromUtf8(RZ_GITTIP).left(7));
|
||||
return fromOwnedCharPtr(rz_version_str(program));
|
||||
}
|
||||
|
||||
QString CutterCore::getVersionInformation()
|
||||
@ -4238,7 +4240,7 @@ QString CutterCore::getVersionInformation()
|
||||
{ "rz_crypto", &rz_crypto_version },
|
||||
{ "rz_bp", &rz_bp_version },
|
||||
{ "rz_debug", &rz_debug_version },
|
||||
{ "rz_msg_digest", &rz_msg_digest_version },
|
||||
{ "rz_hash", &rz_hash_version },
|
||||
{ "rz_io", &rz_io_version },
|
||||
#if !USE_LIB_MAGIC
|
||||
{ "rz_magic", &rz_magic_version },
|
||||
@ -4252,7 +4254,8 @@ QString CutterCore::getVersionInformation()
|
||||
/* ... */
|
||||
{ NULL, NULL }
|
||||
};
|
||||
versionInfo.append(QString("%1 rz\n").arg(getRizinVersionReadable()));
|
||||
versionInfo.append(getRizinVersionReadable());
|
||||
versionInfo.append("\n");
|
||||
for (i = 0; vcs[i].name; i++) {
|
||||
struct vcs_t *v = &vcs[i];
|
||||
const char *name = v->callback();
|
||||
@ -4316,7 +4319,7 @@ QString CutterCore::ansiEscapeToHtml(const QString &text)
|
||||
int len;
|
||||
char *html = rz_cons_html_filter(text.toUtf8().constData(), &len);
|
||||
if (!html) {
|
||||
return QString();
|
||||
return {};
|
||||
}
|
||||
QString r = QString::fromUtf8(html, len);
|
||||
rz_mem_free(html);
|
||||
@ -4491,7 +4494,6 @@ QByteArray CutterCore::ioRead(RVA addr, int len)
|
||||
/* Zero-copy */
|
||||
array.resize(len);
|
||||
if (!rz_io_read_at(core->io, addr, (uint8_t *)array.data(), len)) {
|
||||
qWarning() << "Can't read data" << addr << len;
|
||||
array.fill(0xff);
|
||||
}
|
||||
|
||||
@ -4501,19 +4503,80 @@ QByteArray CutterCore::ioRead(RVA addr, int len)
|
||||
QStringList CutterCore::getConfigVariableSpaces(const QString &key)
|
||||
{
|
||||
CORE_LOCK();
|
||||
QStringList stringList;
|
||||
for (const auto &node : CutterRzList<RzConfigNode>(core->config->nodes)) {
|
||||
stringList.push_back(node->name);
|
||||
RzList *list = rz_core_config_in_space(core, key.toUtf8().constData());
|
||||
if (!list) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!key.isEmpty()) {
|
||||
stringList = stringList.filter(QRegularExpression(QString("^%0\\..*").arg(key)));
|
||||
std::transform(stringList.begin(), stringList.end(), stringList.begin(),
|
||||
[](const QString &x) { return x.split('.').last(); });
|
||||
} else {
|
||||
std::transform(stringList.begin(), stringList.end(), stringList.begin(),
|
||||
[](const QString &x) { return x.split('.').first(); });
|
||||
QStringList stringList;
|
||||
for (const auto &x : CutterRzList<char>(list)) {
|
||||
stringList << x;
|
||||
}
|
||||
stringList.removeDuplicates();
|
||||
rz_list_free(list);
|
||||
return stringList;
|
||||
}
|
||||
|
||||
char *CutterCore::getTextualGraphAt(RzCoreGraphType type, RzCoreGraphFormat format, RVA address)
|
||||
{
|
||||
CORE_LOCK();
|
||||
char *string = nullptr;
|
||||
RzGraph *graph = rz_core_graph(core, type, address);
|
||||
if (!graph) {
|
||||
if (address == RVA_INVALID) {
|
||||
qWarning() << "Cannot get global graph";
|
||||
} else {
|
||||
qWarning() << "Cannot get graph at " << RzAddressString(address);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
core->graph->is_callgraph = type == RZ_CORE_GRAPH_TYPE_FUNCALL;
|
||||
|
||||
switch (format) {
|
||||
case RZ_CORE_GRAPH_FORMAT_CMD: {
|
||||
string = rz_graph_drawable_to_cmd(graph);
|
||||
break;
|
||||
}
|
||||
case RZ_CORE_GRAPH_FORMAT_DOT: {
|
||||
string = rz_core_graph_to_dot_str(core, graph);
|
||||
break;
|
||||
}
|
||||
case RZ_CORE_GRAPH_FORMAT_JSON:
|
||||
/* fall-thru */
|
||||
case RZ_CORE_GRAPH_FORMAT_JSON_DISASM: {
|
||||
string = rz_graph_drawable_to_json_str(graph, true);
|
||||
break;
|
||||
}
|
||||
case RZ_CORE_GRAPH_FORMAT_GML: {
|
||||
string = rz_graph_drawable_to_gml(graph);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
rz_graph_free(graph);
|
||||
|
||||
if (!string) {
|
||||
qWarning() << "Failed to generate graph";
|
||||
}
|
||||
|
||||
return string;
|
||||
}
|
||||
|
||||
void CutterCore::writeGraphvizGraphToFile(QString path, QString format, RzCoreGraphType type,
|
||||
RVA address)
|
||||
{
|
||||
TempConfig tempConfig;
|
||||
tempConfig.set("scr.color", false);
|
||||
tempConfig.set("graph.gv.format", format);
|
||||
|
||||
CORE_LOCK();
|
||||
auto filepath = path.toUtf8();
|
||||
|
||||
if (!rz_core_graph_write(core, address, type, filepath)) {
|
||||
if (address == RVA_INVALID) {
|
||||
qWarning() << "Cannot get global graph";
|
||||
} else {
|
||||
qWarning() << "Cannot get graph at " << RzAddressString(address);
|
||||
}
|
||||
}
|
||||
}
|
@ -17,6 +17,7 @@
|
||||
#include <QMutex>
|
||||
#include <QDir>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
class AsyncTaskManager;
|
||||
class BasicInstructionHighlighter;
|
||||
@ -31,6 +32,7 @@ class RizinTaskDialog;
|
||||
#include "common/Helpers.h"
|
||||
|
||||
#include <rz_project.h>
|
||||
#include <memory>
|
||||
|
||||
#define Core() (CutterCore::instance())
|
||||
|
||||
@ -59,6 +61,8 @@ struct CUTTER_EXPORT RegisterRef
|
||||
QString name;
|
||||
};
|
||||
|
||||
using PRzAnalysisBytes = std::unique_ptr<RzAnalysisBytes, decltype(rz_analysis_bytes_free) *>;
|
||||
|
||||
class CUTTER_EXPORT CutterCore : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
@ -81,6 +85,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
|
||||
@ -90,21 +98,6 @@ 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.
|
||||
*/
|
||||
bool asyncCmd(const char *str, QSharedPointer<RizinTask> &task);
|
||||
bool asyncCmd(const QString &str, QSharedPointer<RizinTask> &task)
|
||||
{
|
||||
return asyncCmd(str.toUtf8().constData(), task);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief send a task to Rizin
|
||||
@ -112,6 +105,7 @@ public:
|
||||
* @return execute successful?
|
||||
*/
|
||||
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
|
||||
@ -148,56 +142,41 @@ public:
|
||||
return cmdRawAt(str.toUtf8().constData(), address);
|
||||
}
|
||||
|
||||
void applyAtSeek(std::function<void()> fn, RVA address)
|
||||
class SeekReturn
|
||||
{
|
||||
RVA oldOffset = getOffset();
|
||||
seekSilent(address);
|
||||
fn();
|
||||
seekSilent(oldOffset);
|
||||
}
|
||||
RVA returnAddress;
|
||||
bool empty = true;
|
||||
|
||||
void *returnAtSeek(std::function<void *()> fn, RVA address)
|
||||
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)
|
||||
{
|
||||
RVA oldOffset = getOffset();
|
||||
SeekReturn returner(getOffset());
|
||||
seekSilent(address);
|
||||
void *ret = fn();
|
||||
seekSilent(oldOffset);
|
||||
return ret;
|
||||
return returner;
|
||||
}
|
||||
|
||||
CutterJson cmdj(const char *str);
|
||||
CutterJson cmdj(const QString &str) { return cmdj(str.toUtf8().constData()); }
|
||||
CutterJson cmdjAt(const char *str, RVA address);
|
||||
QStringList cmdList(const char *str)
|
||||
{
|
||||
return cmd(str).split(QLatin1Char('\n'), CUTTER_QT_SKIP_EMPTY_PARTS);
|
||||
}
|
||||
QStringList cmdList(const QString &str) { return cmdList(str.toUtf8().constData()); }
|
||||
QString cmdTask(const QString &str);
|
||||
CutterJson 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<RizinTask> &task);
|
||||
bool asyncCmdEsil(const QString &command, QSharedPointer<RizinTask> &task)
|
||||
{
|
||||
return asyncCmdEsil(command.toUtf8().constData(), task);
|
||||
}
|
||||
QString getRizinVersionReadable();
|
||||
|
||||
QString getRizinVersionReadable(const char *program = nullptr);
|
||||
QString getVersionInformation();
|
||||
|
||||
CutterJson parseJson(char *res, const char *cmd = nullptr);
|
||||
@ -257,6 +236,7 @@ public:
|
||||
void triggerFlagsChanged();
|
||||
|
||||
/* Edition functions */
|
||||
PRzAnalysisBytes getRzAnalysisBytesSingle(RVA addr);
|
||||
QString getInstructionBytes(RVA addr);
|
||||
QString getInstructionOpcode(RVA addr);
|
||||
void editInstruction(RVA addr, const QString &inst, bool fillWithNops = false);
|
||||
@ -569,14 +549,12 @@ public:
|
||||
bool registerDecompiler(Decompiler *decompiler);
|
||||
|
||||
RVA getOffsetJump(RVA addr);
|
||||
CutterJson getFileInfo();
|
||||
QString getSignatureInfo();
|
||||
CutterJson getFileVersionInfo();
|
||||
CutterJson getSignatureInfo();
|
||||
bool existsFileInfo();
|
||||
void setGraphEmpty(bool empty);
|
||||
bool isGraphEmpty();
|
||||
|
||||
void getOpcodes();
|
||||
QList<QString> opcodes;
|
||||
void getRegs();
|
||||
QList<QString> regs;
|
||||
void setSettings();
|
||||
|
||||
@ -688,8 +666,6 @@ public:
|
||||
QList<XrefDescription> getXRefs(RVA addr, bool to, bool whole_function,
|
||||
const QString &filterType = QString());
|
||||
|
||||
QList<StringDescription> parseStringsJson(const CutterJson &doc);
|
||||
|
||||
void handleREvent(int type, void *data);
|
||||
|
||||
/* Signals related */
|
||||
@ -744,6 +720,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();
|
||||
|
||||
|
@ -7,6 +7,7 @@
|
||||
|
||||
#include "rz_core.h"
|
||||
#include <QString>
|
||||
#include "RizinCpp.h"
|
||||
|
||||
// Workaround for compile errors on Windows
|
||||
#ifdef Q_OS_WIN
|
||||
@ -14,103 +15,6 @@
|
||||
# undef max
|
||||
#endif // Q_OS_WIN
|
||||
|
||||
// Rizin list iteration macros
|
||||
#define CutterRzListForeach(list, it, type, x) \
|
||||
if (list) \
|
||||
for (it = list->head; it && ((x = static_cast<type *>(it->data))); it = it->n)
|
||||
|
||||
#define CutterRzVectorForeach(vec, it, type) \
|
||||
if ((vec) && (vec)->a) \
|
||||
for (it = (type *)(vec)->a; \
|
||||
(char *)it != (char *)(vec)->a + ((vec)->len * (vec)->elem_size); \
|
||||
it = (type *)((char *)it + (vec)->elem_size))
|
||||
|
||||
template<typename T>
|
||||
class CutterPVector
|
||||
{
|
||||
private:
|
||||
const RzPVector *const vec;
|
||||
|
||||
public:
|
||||
class iterator : public std::iterator<std::input_iterator_tag, T *>
|
||||
{
|
||||
private:
|
||||
T **p;
|
||||
|
||||
public:
|
||||
iterator(T **p) : p(p) {}
|
||||
iterator(const iterator &o) : p(o.p) {}
|
||||
iterator &operator++()
|
||||
{
|
||||
p++;
|
||||
return *this;
|
||||
}
|
||||
iterator operator++(int)
|
||||
{
|
||||
iterator tmp(*this);
|
||||
operator++();
|
||||
return tmp;
|
||||
}
|
||||
bool operator==(const iterator &rhs) const { return p == rhs.p; }
|
||||
bool operator!=(const iterator &rhs) const { return p != rhs.p; }
|
||||
T *operator*() { return *p; }
|
||||
};
|
||||
|
||||
CutterPVector(const RzPVector *vec) : vec(vec) {}
|
||||
iterator begin() const { return iterator(reinterpret_cast<T **>(vec->v.a)); }
|
||||
iterator end() const { return iterator(reinterpret_cast<T **>(vec->v.a) + vec->v.len); }
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class CutterRzList
|
||||
{
|
||||
private:
|
||||
const RzList *const list;
|
||||
|
||||
public:
|
||||
class iterator : public std::iterator<std::input_iterator_tag, T *>
|
||||
{
|
||||
private:
|
||||
RzListIter *iter;
|
||||
|
||||
public:
|
||||
explicit iterator(RzListIter *iter) : iter(iter) {}
|
||||
iterator(const iterator &o) : iter(o.iter) {}
|
||||
iterator &operator++()
|
||||
{
|
||||
if (!iter) {
|
||||
return *this;
|
||||
}
|
||||
iter = iter->n;
|
||||
return *this;
|
||||
}
|
||||
iterator operator++(int)
|
||||
{
|
||||
iterator tmp(*this);
|
||||
operator++();
|
||||
return tmp;
|
||||
}
|
||||
bool operator==(const iterator &rhs) const { return iter == rhs.iter; }
|
||||
bool operator!=(const iterator &rhs) const { return iter != rhs.iter; }
|
||||
T *operator*()
|
||||
{
|
||||
if (!iter) {
|
||||
return nullptr;
|
||||
}
|
||||
return reinterpret_cast<T *>(iter->data);
|
||||
}
|
||||
};
|
||||
|
||||
explicit CutterRzList(const RzList *l) : list(l) {}
|
||||
iterator begin() const
|
||||
{
|
||||
if (!list) {
|
||||
return iterator(nullptr);
|
||||
}
|
||||
return iterator(list->head);
|
||||
}
|
||||
iterator end() const { return iterator(nullptr); }
|
||||
};
|
||||
|
||||
// Global information for Cutter
|
||||
#define APPNAME "Cutter"
|
||||
|
@ -355,8 +355,7 @@ struct RefDescription
|
||||
|
||||
struct VariableDescription
|
||||
{
|
||||
enum class RefType { SP, BP, Reg };
|
||||
RefType refType;
|
||||
RzAnalysisVarStorageType storageType;
|
||||
QString name;
|
||||
QString type;
|
||||
};
|
||||
|
@ -70,7 +70,6 @@ public:
|
||||
iterator end() const { return iterator(nullptr, nullptr); }
|
||||
|
||||
bool toBool() const { return value && value->type == RZ_JSON_BOOLEAN && value->num.u_value; }
|
||||
QString toJson() const { return rz_json_as_string(value); }
|
||||
st64 toSt64() const { return value && value->type == RZ_JSON_INTEGER ? value->num.s_value : 0; }
|
||||
ut64 toUt64() const { return value && value->type == RZ_JSON_INTEGER ? value->num.u_value : 0; }
|
||||
|
||||
|
@ -149,7 +149,7 @@ void MainWindow::initUI()
|
||||
&MainWindow::addExtraDisassembly);
|
||||
connect(ui->actionExtraHexdump, &QAction::triggered, this, &MainWindow::addExtraHexdump);
|
||||
connect(ui->actionCommitChanges, &QAction::triggered, this,
|
||||
[this]() { Core()->commitWriteCache(); });
|
||||
[]() { Core()->commitWriteCache(); });
|
||||
ui->actionCommitChanges->setEnabled(false);
|
||||
connect(Core(), &CutterCore::ioCacheChanged, ui->actionCommitChanges, &QAction::setEnabled);
|
||||
|
||||
@ -637,7 +637,7 @@ bool MainWindow::openProject(const QString &file)
|
||||
|
||||
void MainWindow::finalizeOpen()
|
||||
{
|
||||
core->getOpcodes();
|
||||
core->getRegs();
|
||||
core->updateSeek();
|
||||
refreshAll();
|
||||
// Add fortune message
|
||||
@ -1086,6 +1086,12 @@ MemoryDockWidget *MainWindow::addNewMemoryWidget(MemoryWidgetType type, RVA addr
|
||||
case MemoryWidgetType::Decompiler:
|
||||
memoryWidget = new DecompilerWidget(this);
|
||||
break;
|
||||
case MemoryWidgetType::CallGraph:
|
||||
memoryWidget = new CallGraphWidget(this, false);
|
||||
break;
|
||||
case MemoryWidgetType::GlobalCallGraph:
|
||||
memoryWidget = new CallGraphWidget(this, true);
|
||||
break;
|
||||
}
|
||||
auto seekable = memoryWidget->getSeekable();
|
||||
seekable->setSynchronization(synchronized);
|
||||
@ -1108,7 +1114,7 @@ void MainWindow::initBackForwardMenu()
|
||||
connect(button, &QWidget::customContextMenuRequested, button,
|
||||
[menu, button](const QPoint &pos) { menu->exec(button->mapToGlobal(pos)); });
|
||||
|
||||
QFontMetrics metrics(fontMetrics());
|
||||
QFontMetrics metrics(font());
|
||||
// Roughly 10-16 lines depending on padding size, no need to calculate more precisely
|
||||
menu->setMaximumHeight(metrics.lineSpacing() * 20);
|
||||
|
||||
@ -1692,35 +1698,55 @@ void MainWindow::on_actionImportPDB_triggered()
|
||||
}
|
||||
}
|
||||
|
||||
#define TYPE_BIG_ENDIAN(type, big_endian) big_endian ? type##_BE : type##_LE
|
||||
|
||||
void MainWindow::on_actionExport_as_code_triggered()
|
||||
{
|
||||
QStringList filters;
|
||||
QMap<QString, QString> cmdMap;
|
||||
QMap<QString, RzLangByteArrayType> typMap;
|
||||
const bool big_endian = Core()->getConfigb("big_endian");
|
||||
|
||||
filters << tr("C uin8_t array (*.c)");
|
||||
cmdMap[filters.last()] = "pc";
|
||||
typMap[filters.last()] = RZ_LANG_BYTE_ARRAY_C_CPP_BYTES;
|
||||
filters << tr("C uin16_t array (*.c)");
|
||||
cmdMap[filters.last()] = "pch";
|
||||
typMap[filters.last()] = TYPE_BIG_ENDIAN(RZ_LANG_BYTE_ARRAY_C_CPP_HALFWORDS, big_endian);
|
||||
filters << tr("C uin32_t array (*.c)");
|
||||
cmdMap[filters.last()] = "pcw";
|
||||
typMap[filters.last()] = TYPE_BIG_ENDIAN(RZ_LANG_BYTE_ARRAY_C_CPP_WORDS, big_endian);
|
||||
filters << tr("C uin64_t array (*.c)");
|
||||
cmdMap[filters.last()] = "pcd";
|
||||
filters << tr("C string (*.c)");
|
||||
cmdMap[filters.last()] = "pcs";
|
||||
filters << tr("Shell-script that reconstructs the bin (*.sh)");
|
||||
cmdMap[filters.last()] = "pcS";
|
||||
typMap[filters.last()] = TYPE_BIG_ENDIAN(RZ_LANG_BYTE_ARRAY_C_CPP_DOUBLEWORDS, big_endian);
|
||||
|
||||
filters << tr("Go array (*.go)");
|
||||
typMap[filters.last()] = RZ_LANG_BYTE_ARRAY_GOLANG;
|
||||
filters << tr("Java array (*.java)");
|
||||
typMap[filters.last()] = RZ_LANG_BYTE_ARRAY_JAVA;
|
||||
filters << tr("JSON array (*.json)");
|
||||
cmdMap[filters.last()] = "pcj";
|
||||
filters << tr("JavaScript array (*.js)");
|
||||
cmdMap[filters.last()] = "pcJ";
|
||||
typMap[filters.last()] = RZ_LANG_BYTE_ARRAY_JSON;
|
||||
filters << tr("Kotlin array (*.kt)");
|
||||
typMap[filters.last()] = RZ_LANG_BYTE_ARRAY_KOTLIN;
|
||||
|
||||
filters << tr("Javascript array (*.js)");
|
||||
typMap[filters.last()] = RZ_LANG_BYTE_ARRAY_NODEJS;
|
||||
filters << tr("ObjectiveC array (*.m)");
|
||||
typMap[filters.last()] = RZ_LANG_BYTE_ARRAY_OBJECTIVE_C;
|
||||
filters << tr("Python array (*.py)");
|
||||
cmdMap[filters.last()] = "pcp";
|
||||
typMap[filters.last()] = RZ_LANG_BYTE_ARRAY_PYTHON;
|
||||
filters << tr("Rust array (*.rs)");
|
||||
typMap[filters.last()] = RZ_LANG_BYTE_ARRAY_RUST;
|
||||
|
||||
filters << tr("Swift array (*.swift)");
|
||||
typMap[filters.last()] = RZ_LANG_BYTE_ARRAY_SWIFT;
|
||||
filters << tr("Print 'wx' Rizin commands (*.rz)");
|
||||
cmdMap[filters.last()] = "pc*";
|
||||
typMap[filters.last()] = RZ_LANG_BYTE_ARRAY_RIZIN;
|
||||
filters << tr("Shell-script that reconstructs the bin (*.sh)");
|
||||
typMap[filters.last()] = RZ_LANG_BYTE_ARRAY_BASH;
|
||||
filters << tr("GAS .byte blob (*.asm, *.s)");
|
||||
cmdMap[filters.last()] = "pca";
|
||||
filters << tr(".bytes with instructions in comments (*.txt)");
|
||||
cmdMap[filters.last()] = "pcA";
|
||||
typMap[filters.last()] = RZ_LANG_BYTE_ARRAY_ASM;
|
||||
|
||||
filters << tr("Yara (*.yar)");
|
||||
typMap[filters.last()] = RZ_LANG_BYTE_ARRAY_YARA;
|
||||
/* special case */
|
||||
QString instructionsInComments = tr(".bytes with instructions in comments (*.txt)");
|
||||
filters << instructionsInComments;
|
||||
|
||||
QFileDialog dialog(this, tr("Export as code"));
|
||||
dialog.setAcceptMode(QFileDialog::AcceptSave);
|
||||
@ -1736,13 +1762,25 @@ void MainWindow::on_actionExport_as_code_triggered()
|
||||
qWarning() << "Can't open file";
|
||||
return;
|
||||
}
|
||||
|
||||
TempConfig tempConfig;
|
||||
tempConfig.set("io.va", false);
|
||||
QTextStream fileOut(&file);
|
||||
QString &cmd = cmdMap[dialog.selectedNameFilter()];
|
||||
auto ps = core->seekTemp(0);
|
||||
auto rc = core->core();
|
||||
const auto size = static_cast<int>(rz_io_fd_size(rc->io, rc->file->fd));
|
||||
auto buffer = std::vector<ut8>(size);
|
||||
if (!rz_io_read_at(Core()->core()->io, 0, buffer.data(), size)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Use cmd because cmdRaw would not handle such input
|
||||
fileOut << Core()->cmd(cmd + " $s @ 0");
|
||||
|
||||
|
||||
auto string = fromOwned(
|
||||
dialog.selectedNameFilter() != instructionsInComments
|
||||
? rz_lang_byte_array(buffer.data(), size, typMap[dialog.selectedNameFilter()])
|
||||
: rz_core_print_bytes_with_inst(rc, buffer.data(), 0, size));
|
||||
fileOut << string.get();
|
||||
}
|
||||
|
||||
void MainWindow::on_actionApplySigFromFile_triggered()
|
||||
|
2
src/core/RizinCpp.cpp
Normal file
2
src/core/RizinCpp.cpp
Normal file
@ -0,0 +1,2 @@
|
||||
#include "RizinCpp.h"
|
||||
|
168
src/core/RizinCpp.h
Normal file
168
src/core/RizinCpp.h
Normal file
@ -0,0 +1,168 @@
|
||||
/** \file RizinCpp.h
|
||||
* Various utilities for easier and safer interactions with Rizin
|
||||
* from C++ code.
|
||||
*/
|
||||
#ifndef RIZINCPP_H
|
||||
#define RIZINCPP_H
|
||||
|
||||
#include "rz_core.h"
|
||||
#include <QString>
|
||||
#include <memory>
|
||||
|
||||
static inline QString fromOwnedCharPtr(char *str)
|
||||
{
|
||||
QString result(str ? str : "");
|
||||
rz_mem_free(str);
|
||||
return result;
|
||||
}
|
||||
|
||||
template<class T, class F>
|
||||
std::unique_ptr<T, F *> fromOwned(T *data, F *freeFunction)
|
||||
{
|
||||
return std::unique_ptr<T, F *> { data, freeFunction };
|
||||
}
|
||||
|
||||
static inline std::unique_ptr<char, decltype(&rz_mem_free)> fromOwned(char *text)
|
||||
{
|
||||
return { text, rz_mem_free };
|
||||
}
|
||||
|
||||
template<class T, void (*func)(T *)>
|
||||
class FreeBinder
|
||||
{
|
||||
public:
|
||||
void operator()(T *data) { func(data); }
|
||||
};
|
||||
|
||||
template<class T, void (*func)(T *)>
|
||||
using UniquePtrC = std::unique_ptr<T, FreeBinder<T, func>>;
|
||||
|
||||
template<typename T, void (*func)(T)>
|
||||
using UniquePtrCP = UniquePtrC<typename std::remove_pointer<T>::type, func>;
|
||||
|
||||
static inline auto fromOwned(RZ_OWN RzPVector *data)
|
||||
-> UniquePtrCP<decltype(data), &rz_pvector_free>
|
||||
{
|
||||
return { data, {} };
|
||||
}
|
||||
|
||||
static inline auto fromOwned(RZ_OWN RzList *data)
|
||||
-> UniquePtrCP<decltype(data), &rz_list_free>
|
||||
{
|
||||
return { data, {} };
|
||||
}
|
||||
|
||||
// Rizin list iteration macros
|
||||
// deprecated, prefer using CutterPVector and CutterRzList instead
|
||||
#define CutterRzListForeach(list, it, type, x) \
|
||||
if (list) \
|
||||
for (it = list->head; it && ((x = static_cast<type *>(it->data))); it = it->n)
|
||||
|
||||
#define CutterRzVectorForeach(vec, it, type) \
|
||||
if ((vec) && (vec)->a) \
|
||||
for (it = (type *)(vec)->a; \
|
||||
(char *)it != (char *)(vec)->a + ((vec)->len * (vec)->elem_size); \
|
||||
it = (type *)((char *)it + (vec)->elem_size))
|
||||
|
||||
template<typename T>
|
||||
class CutterPVector
|
||||
{
|
||||
private:
|
||||
const RzPVector *const vec;
|
||||
|
||||
public:
|
||||
class iterator
|
||||
{
|
||||
public:
|
||||
using iterator_category = std::input_iterator_tag;
|
||||
using value_type = T *;
|
||||
using difference_type = ptrdiff_t;
|
||||
using pointer = T **;
|
||||
using reference = T *&;
|
||||
|
||||
private:
|
||||
T **p;
|
||||
|
||||
public:
|
||||
iterator(T **p) : p(p) {}
|
||||
iterator(const iterator &o) : p(o.p) {}
|
||||
iterator &operator++()
|
||||
{
|
||||
p++;
|
||||
return *this;
|
||||
}
|
||||
iterator operator++(int)
|
||||
{
|
||||
iterator tmp(*this);
|
||||
operator++();
|
||||
return tmp;
|
||||
}
|
||||
bool operator==(const iterator &rhs) const { return p == rhs.p; }
|
||||
bool operator!=(const iterator &rhs) const { return p != rhs.p; }
|
||||
T *operator*() { return *p; }
|
||||
};
|
||||
|
||||
CutterPVector(const RzPVector *vec) : vec(vec) {}
|
||||
iterator begin() const { return iterator(reinterpret_cast<T **>(vec->v.a)); }
|
||||
iterator end() const { return iterator(reinterpret_cast<T **>(vec->v.a) + vec->v.len); }
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class CutterRzList
|
||||
{
|
||||
private:
|
||||
const RzList *const list;
|
||||
|
||||
public:
|
||||
class iterator
|
||||
{
|
||||
public:
|
||||
using iterator_category = std::input_iterator_tag;
|
||||
using value_type = T *;
|
||||
using difference_type = ptrdiff_t;
|
||||
using pointer = T **;
|
||||
using reference = T *&;
|
||||
|
||||
private:
|
||||
RzListIter *iter;
|
||||
|
||||
public:
|
||||
explicit iterator(RzListIter *iter) : iter(iter) {}
|
||||
iterator(const iterator &o) : iter(o.iter) {}
|
||||
iterator &operator++()
|
||||
{
|
||||
if (!iter) {
|
||||
return *this;
|
||||
}
|
||||
iter = iter->n;
|
||||
return *this;
|
||||
}
|
||||
iterator operator++(int)
|
||||
{
|
||||
iterator tmp(*this);
|
||||
operator++();
|
||||
return tmp;
|
||||
}
|
||||
bool operator==(const iterator &rhs) const { return iter == rhs.iter; }
|
||||
bool operator!=(const iterator &rhs) const { return iter != rhs.iter; }
|
||||
T *operator*()
|
||||
{
|
||||
if (!iter) {
|
||||
return nullptr;
|
||||
}
|
||||
return reinterpret_cast<T *>(iter->data);
|
||||
}
|
||||
};
|
||||
|
||||
explicit CutterRzList(const RzList *l) : list(l) {}
|
||||
iterator begin() const
|
||||
{
|
||||
if (!list) {
|
||||
return iterator(nullptr);
|
||||
}
|
||||
return iterator(list->head);
|
||||
}
|
||||
iterator end() const { return iterator(nullptr); }
|
||||
};
|
||||
|
||||
#endif // RIZINCPP_H
|
@ -1,4 +1,3 @@
|
||||
#include "rz_version.h"
|
||||
#include "core/Cutter.h"
|
||||
#include "AboutDialog.h"
|
||||
|
||||
@ -7,7 +6,6 @@
|
||||
#include "common/Configuration.h"
|
||||
#include "common/BugReporting.h"
|
||||
|
||||
|
||||
#include <QUrl>
|
||||
#include <QTimer>
|
||||
#include <QEventLoop>
|
||||
|
@ -86,11 +86,7 @@ bool EditMethodDialog::inputValid()
|
||||
|
||||
QString EditMethodDialog::convertRealNameToName(const QString &realName)
|
||||
{
|
||||
std::unique_ptr<const char, void (*)(const char *)> sanitizedCString(
|
||||
rz_str_sanitize_sdb_key(realName.toUtf8().constData()),
|
||||
[](const char *s) { rz_mem_free((void*)s); });
|
||||
|
||||
return QString(sanitizedCString.get());
|
||||
return fromOwnedCharPtr(rz_str_sanitize_sdb_key(realName.toUtf8().constData()));
|
||||
}
|
||||
|
||||
void EditMethodDialog::setClass(const QString &className)
|
||||
|
@ -22,7 +22,6 @@ TypesInteractionDialog::TypesInteractionDialog(QWidget *parent, bool readOnly)
|
||||
syntaxHighLighter = Config()->createSyntaxHighlighter(ui->plainTextEdit->document());
|
||||
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
|
||||
ui->plainTextEdit->setReadOnly(readOnly);
|
||||
this->typeName = "";
|
||||
}
|
||||
|
||||
TypesInteractionDialog::~TypesInteractionDialog() {}
|
||||
@ -64,10 +63,21 @@ void TypesInteractionDialog::done(int r)
|
||||
{
|
||||
if (r == QDialog::Accepted) {
|
||||
RzCoreLocked core(Core());
|
||||
bool edited = rz_type_db_edit_base_type(
|
||||
bool success;
|
||||
if (!typeName.isEmpty()) {
|
||||
success = rz_type_db_edit_base_type(
|
||||
core->analysis->typedb, this->typeName.toUtf8().constData(),
|
||||
ui->plainTextEdit->toPlainText().toUtf8().constData());
|
||||
if (edited) {
|
||||
} else {
|
||||
char *error_msg = NULL;
|
||||
success = rz_type_parse_string_stateless(core->analysis->typedb->parser,
|
||||
ui->plainTextEdit->toPlainText().toUtf8().constData(), &error_msg) == 0;
|
||||
if (error_msg) {
|
||||
RZ_LOG_ERROR("%s\n", error_msg);
|
||||
rz_mem_free(error_msg);
|
||||
}
|
||||
}
|
||||
if (success) {
|
||||
emit newTypesLoaded();
|
||||
QDialog::done(r);
|
||||
return;
|
||||
|
@ -23,145 +23,213 @@ VersionInfoDialog::~VersionInfoDialog() {}
|
||||
|
||||
void VersionInfoDialog::fillVersionInfo()
|
||||
{
|
||||
|
||||
CutterJson doc = Core()->getFileVersionInfo();
|
||||
|
||||
RzCoreLocked core(Core());
|
||||
const RzBinInfo *info = rz_bin_get_info(core->bin);
|
||||
if (!info || !info->rclass) {
|
||||
return;
|
||||
}
|
||||
// Case ELF
|
||||
if (doc["verneed"].valid()) {
|
||||
CutterJson verneed = doc["verneed"].first();
|
||||
CutterJson versym = doc["versym"].first();
|
||||
|
||||
if (strncmp("elf", info->rclass, 3) == 0) {
|
||||
// Set labels
|
||||
ui->leftLabel->setText("Version symbols");
|
||||
ui->rightLabel->setText("Version need");
|
||||
|
||||
// Left tree
|
||||
QTreeWidgetItem *secNameItemL = new QTreeWidgetItem();
|
||||
secNameItemL->setText(0, "Section name:");
|
||||
secNameItemL->setText(1, versym["section_name"].toString());
|
||||
ui->leftTreeWidget->addTopLevelItem(secNameItemL);
|
||||
Sdb *sdb = sdb_ns_path(core->sdb, "bin/cur/info/versioninfo/versym", 0);
|
||||
if (!sdb) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Left tree
|
||||
QTreeWidgetItem *addrItemL = new QTreeWidgetItem();
|
||||
addrItemL->setText(0, "Address:");
|
||||
addrItemL->setText(1, RzAddressString(versym["address"].toRVA()));
|
||||
addrItemL->setText(1, RzAddressString(sdb_num_get(sdb, "addr", 0)));
|
||||
ui->leftTreeWidget->addTopLevelItem(addrItemL);
|
||||
|
||||
QTreeWidgetItem *offItemL = new QTreeWidgetItem();
|
||||
offItemL->setText(0, "Offset:");
|
||||
offItemL->setText(1, RzAddressString(versym["offset"].toRVA()));
|
||||
offItemL->setText(1, RzAddressString(sdb_num_get(sdb, "offset", 0)));
|
||||
ui->leftTreeWidget->addTopLevelItem(offItemL);
|
||||
|
||||
QTreeWidgetItem *linkItemL = new QTreeWidgetItem();
|
||||
linkItemL->setText(0, "Link:");
|
||||
linkItemL->setText(1, QString::number(versym["link"].toRVA()));
|
||||
ui->leftTreeWidget->addTopLevelItem(linkItemL);
|
||||
|
||||
QTreeWidgetItem *linkNameItemL = new QTreeWidgetItem();
|
||||
linkNameItemL->setText(0, "Link section name:");
|
||||
linkNameItemL->setText(1, versym["link_section_name"].toString());
|
||||
ui->leftTreeWidget->addTopLevelItem(linkNameItemL);
|
||||
|
||||
QTreeWidgetItem *entriesItemL = new QTreeWidgetItem();
|
||||
entriesItemL->setText(0, "Entries:");
|
||||
for (CutterJson obj : versym["entries"]) {
|
||||
QTreeWidgetItem *tempItem = new QTreeWidgetItem();
|
||||
tempItem->setText(0, RzAddressString(obj["idx"].toRVA()));
|
||||
tempItem->setText(1, obj["value"].toString());
|
||||
entriesItemL->addChild(tempItem);
|
||||
const ut64 num_entries = sdb_num_get(sdb, "num_entries", 0);
|
||||
for (size_t i = 0; i < num_entries; ++i) {
|
||||
auto key = QString("entry%0").arg(i);
|
||||
const char *const value = sdb_const_get(sdb, key.toStdString().c_str(), 0);
|
||||
if (!value) {
|
||||
continue;
|
||||
}
|
||||
auto item = new QTreeWidgetItem();
|
||||
item->setText(0, RzAddressString(i));
|
||||
item->setText(1, value);
|
||||
entriesItemL->addChild(item);
|
||||
}
|
||||
ui->leftTreeWidget->addTopLevelItem(entriesItemL);
|
||||
|
||||
// Adjust columns to content
|
||||
qhelpers::adjustColumns(ui->leftTreeWidget, 0);
|
||||
sdb = sdb_ns_path(core->sdb, "bin/cur/info/versioninfo/verneed", 0);
|
||||
|
||||
// Right tree
|
||||
QTreeWidgetItem *secNameItemR = new QTreeWidgetItem();
|
||||
secNameItemR->setText(0, "Section name:");
|
||||
secNameItemR->setText(1, verneed["section_name"].toString());
|
||||
ui->rightTreeWidget->addTopLevelItem(secNameItemR);
|
||||
|
||||
QTreeWidgetItem *addrItemR = new QTreeWidgetItem();
|
||||
addrItemR->setText(0, "Address:");
|
||||
addrItemR->setText(1, RzAddressString(verneed["address"].toRVA()));
|
||||
addrItemR->setText(1, RzAddressString(sdb_num_get(sdb, "addr", 0)));
|
||||
ui->rightTreeWidget->addTopLevelItem(addrItemR);
|
||||
|
||||
QTreeWidgetItem *offItemR = new QTreeWidgetItem();
|
||||
offItemR->setText(0, "Offset:");
|
||||
offItemR->setText(1, RzAddressString(verneed["offset"].toRVA()));
|
||||
offItemR->setText(1, RzAddressString(sdb_num_get(sdb, "offset", 0)));
|
||||
ui->rightTreeWidget->addTopLevelItem(offItemR);
|
||||
|
||||
QTreeWidgetItem *linkItemR = new QTreeWidgetItem();
|
||||
linkItemR->setText(0, "Link:");
|
||||
linkItemR->setText(1, QString::number(verneed["link"].toSt64()));
|
||||
ui->rightTreeWidget->addTopLevelItem(linkItemR);
|
||||
|
||||
QTreeWidgetItem *linkNameItemR = new QTreeWidgetItem();
|
||||
linkNameItemR->setText(0, "Link section name:");
|
||||
linkNameItemR->setText(1, verneed["link_section_name"].toString());
|
||||
ui->rightTreeWidget->addTopLevelItem(linkNameItemR);
|
||||
|
||||
QTreeWidgetItem *entriesItemR = new QTreeWidgetItem();
|
||||
entriesItemR->setText(0, "Entries:");
|
||||
for (CutterJson parentObj : verneed["entries"]) {
|
||||
QTreeWidgetItem *parentItem = new QTreeWidgetItem();
|
||||
QString parentString;
|
||||
parentItem->setText(0, RzAddressString(parentObj["idx"].toRVA()));
|
||||
parentString.append("Version: " + QString::number(parentObj["vn_version"].toSt64())
|
||||
+ "\t");
|
||||
parentString.append("File: " + parentObj["file_name"].toString());
|
||||
parentItem->setText(1, parentString);
|
||||
for (size_t num_version = 0;; num_version++) {
|
||||
auto path_version =
|
||||
QString("bin/cur/info/versioninfo/verneed/version%0").arg(num_version);
|
||||
sdb = sdb_ns_path(core->sdb, path_version.toStdString().c_str(), 0);
|
||||
if (!sdb) {
|
||||
break;
|
||||
}
|
||||
const char *filename = sdb_const_get(sdb, "file_name", 0);
|
||||
auto *parentItem = new QTreeWidgetItem();
|
||||
parentItem->setText(0, RzAddressString(sdb_num_get(sdb, "idx", 0)));
|
||||
parentItem->setText(1,
|
||||
QString("Version: %0\t"
|
||||
"File: %1")
|
||||
.arg(QString::number(sdb_num_get(sdb, "vn_version", 0)),
|
||||
QString(filename)));
|
||||
|
||||
for (CutterJson childObj : parentObj["vernaux"]) {
|
||||
QTreeWidgetItem *childItem = new QTreeWidgetItem();
|
||||
QString childString;
|
||||
childItem->setText(0, RzAddressString(childObj["idx"].toRVA()));
|
||||
childString.append("Name: " + childObj["name"].toString() + "\t");
|
||||
childString.append("Flags: " + childObj["flags"].toString() + "\t");
|
||||
childString.append("Version: " + QString::number(childObj["version"].toSt64()));
|
||||
int num_vernaux = 0;
|
||||
while (true) {
|
||||
auto path_vernaux =
|
||||
QString("%0/vernaux%1").arg(path_version, QString::number(num_vernaux++));
|
||||
sdb = sdb_ns_path(core->sdb, path_vernaux.toStdString().c_str(), 0);
|
||||
if (!sdb) {
|
||||
break;
|
||||
}
|
||||
|
||||
auto *childItem = new QTreeWidgetItem();
|
||||
childItem->setText(0, RzAddressString(sdb_num_get(sdb, "idx", 0)));
|
||||
QString childString =
|
||||
QString("Name: %0\t"
|
||||
"Flags: %1\t"
|
||||
"Version: %2\t")
|
||||
.arg(sdb_const_get(sdb, "name", 0), sdb_const_get(sdb, "flags", 0),
|
||||
QString::number(sdb_num_get(sdb, "version", 0)));
|
||||
childItem->setText(1, childString);
|
||||
parentItem->addChild(childItem);
|
||||
}
|
||||
entriesItemR->addChild(parentItem);
|
||||
}
|
||||
ui->rightTreeWidget->addTopLevelItem(entriesItemR);
|
||||
|
||||
ui->rightTreeWidget->addTopLevelItem(entriesItemR);
|
||||
// Adjust columns to content
|
||||
qhelpers::adjustColumns(ui->rightTreeWidget, 0);
|
||||
|
||||
}
|
||||
|
||||
// Case PE
|
||||
else if (doc["VS_FIXEDFILEINFO"].valid()) {
|
||||
CutterJson vs = doc["VS_FIXEDFILEINFO"];
|
||||
CutterJson strings = doc["StringTable"];
|
||||
|
||||
else if (strncmp("pe", info->rclass, 2) == 0) {
|
||||
// Set labels
|
||||
ui->leftLabel->setText("VS Fixed file info");
|
||||
ui->rightLabel->setText("String table");
|
||||
Sdb *sdb = NULL;
|
||||
|
||||
// Left tree
|
||||
for (CutterJson property : vs) {
|
||||
QTreeWidgetItem *tempItem = new QTreeWidgetItem();
|
||||
tempItem->setText(0, property.key());
|
||||
if (property.type() == RZ_JSON_INTEGER)
|
||||
tempItem->setText(1, RzHexString(property.toRVA()));
|
||||
else
|
||||
tempItem->setText(1, property.toString());
|
||||
ui->leftTreeWidget->addTopLevelItem(tempItem);
|
||||
|
||||
// Adjust columns to content
|
||||
qhelpers::adjustColumns(ui->leftTreeWidget, 0);
|
||||
auto path_version = QString("bin/cur/info/vs_version_info/VS_VERSIONINFO%0").arg(0);
|
||||
auto path_fixedfileinfo = QString("%0/fixed_file_info").arg(path_version);
|
||||
sdb = sdb_ns_path(core->sdb, path_fixedfileinfo.toStdString().c_str(), 0);
|
||||
if (!sdb) {
|
||||
return;
|
||||
}
|
||||
ut32 file_version_ms = sdb_num_get(sdb, "FileVersionMS", 0);
|
||||
ut32 file_version_ls = sdb_num_get(sdb, "FileVersionLS", 0);
|
||||
auto file_version = QString("%0.%1.%2.%3")
|
||||
.arg(file_version_ms >> 16)
|
||||
.arg(file_version_ms & 0xFFFF)
|
||||
.arg(file_version_ls >> 16)
|
||||
.arg(file_version_ls & 0xFFFF);
|
||||
ut32 product_version_ms = sdb_num_get(sdb, "ProductVersionMS", 0);
|
||||
ut32 product_version_ls = sdb_num_get(sdb, "ProductVersionLS", 0);
|
||||
auto product_version = QString("%0.%1.%2.%3")
|
||||
.arg(product_version_ms >> 16)
|
||||
.arg(product_version_ms & 0xFFFF)
|
||||
.arg(product_version_ls >> 16)
|
||||
.arg(product_version_ls & 0xFFFF);
|
||||
|
||||
auto item = new QTreeWidgetItem();
|
||||
item->setText(0, "Signature");
|
||||
item->setText(1, RzHexString(sdb_num_get(sdb, "Signature", 0)));
|
||||
ui->leftTreeWidget->addTopLevelItem(item);
|
||||
|
||||
item = new QTreeWidgetItem();
|
||||
item->setText(0, "StrucVersion");
|
||||
item->setText(1, RzHexString(sdb_num_get(sdb, "StrucVersion", 0)));
|
||||
ui->leftTreeWidget->addTopLevelItem(item);
|
||||
|
||||
item = new QTreeWidgetItem();
|
||||
item->setText(0, "FileVersion");
|
||||
item->setText(1, file_version);
|
||||
ui->leftTreeWidget->addTopLevelItem(item);
|
||||
|
||||
item = new QTreeWidgetItem();
|
||||
item->setText(0, "ProductVersion");
|
||||
item->setText(1, product_version);
|
||||
ui->leftTreeWidget->addTopLevelItem(item);
|
||||
|
||||
item = new QTreeWidgetItem();
|
||||
item->setText(0, "FileFlagsMask");
|
||||
item->setText(1, RzHexString(sdb_num_get(sdb, "FileFlagsMask", 0)));
|
||||
ui->leftTreeWidget->addTopLevelItem(item);
|
||||
|
||||
item = new QTreeWidgetItem();
|
||||
item->setText(0, "FileFlags");
|
||||
item->setText(1, RzHexString(sdb_num_get(sdb, "FileFlags", 0)));
|
||||
ui->leftTreeWidget->addTopLevelItem(item);
|
||||
|
||||
item = new QTreeWidgetItem();
|
||||
item->setText(0, "FileOS");
|
||||
item->setText(1, RzHexString(sdb_num_get(sdb, "FileOS", 0)));
|
||||
ui->leftTreeWidget->addTopLevelItem(item);
|
||||
|
||||
item = new QTreeWidgetItem();
|
||||
item->setText(0, "FileType");
|
||||
item->setText(1, RzHexString(sdb_num_get(sdb, "FileType", 0)));
|
||||
ui->leftTreeWidget->addTopLevelItem(item);
|
||||
|
||||
item = new QTreeWidgetItem();
|
||||
item->setText(0, "FileSubType");
|
||||
item->setText(1, RzHexString(sdb_num_get(sdb, "FileSubType", 0)));
|
||||
ui->leftTreeWidget->addTopLevelItem(item);
|
||||
|
||||
// Adjust columns to content
|
||||
qhelpers::adjustColumns(ui->leftTreeWidget, 0);
|
||||
|
||||
// Right tree
|
||||
for (CutterJson property : strings) {
|
||||
QTreeWidgetItem *tempItem = new QTreeWidgetItem();
|
||||
tempItem->setText(0, property.key());
|
||||
tempItem->setText(1, property.toString());
|
||||
ui->rightTreeWidget->addTopLevelItem(tempItem);
|
||||
|
||||
// Adjust columns to content
|
||||
qhelpers::adjustColumns(ui->rightTreeWidget, 0);
|
||||
for (int num_stringtable = 0;; num_stringtable++) {
|
||||
auto path_stringtable = QString("%0/string_file_info/stringtable%1")
|
||||
.arg(path_version)
|
||||
.arg(num_stringtable);
|
||||
sdb = sdb_ns_path(core->sdb, path_stringtable.toStdString().c_str(), 0);
|
||||
if (!sdb) {
|
||||
break;
|
||||
}
|
||||
for (int num_string = 0; sdb; num_string++) {
|
||||
auto path_string = QString("%0/string%1").arg(path_stringtable).arg(num_string);
|
||||
sdb = sdb_ns_path(core->sdb, path_string.toStdString().c_str(), 0);
|
||||
if (!sdb) {
|
||||
continue;
|
||||
}
|
||||
int lenkey = 0;
|
||||
int lenval = 0;
|
||||
ut8 *key_utf16 = sdb_decode(sdb_const_get(sdb, "key", 0), &lenkey);
|
||||
ut8 *val_utf16 = sdb_decode(sdb_const_get(sdb, "value", 0), &lenval);
|
||||
item = new QTreeWidgetItem();
|
||||
item->setText(0, QString::fromUtf16(reinterpret_cast<const ushort *>(key_utf16)));
|
||||
item->setText(1, QString::fromUtf16(reinterpret_cast<const ushort *>(val_utf16)));
|
||||
ui->rightTreeWidget->addTopLevelItem(item);
|
||||
free(key_utf16);
|
||||
free(val_utf16);
|
||||
}
|
||||
}
|
||||
qhelpers::adjustColumns(ui->rightTreeWidget, 0);
|
||||
}
|
||||
}
|
||||
|
@ -131,8 +131,18 @@ void XrefsDialog::updatePreview(RVA addr)
|
||||
tempConfig.set("asm.lines", false);
|
||||
tempConfig.set("asm.bytes", false);
|
||||
|
||||
// Use cmd because cmRaw cannot handle the output properly. Why?
|
||||
QString disas = Core()->cmd("pd--20 @ " + QString::number(addr));
|
||||
QString disas = Core()->getFunctionExecOut(
|
||||
[](RzCore *core) {
|
||||
ut64 offset = core->offset;
|
||||
if (!rz_core_prevop_addr(core, core->offset, 20, &offset)) {
|
||||
offset = rz_core_prevop_addr_force(core, core->offset, 20);
|
||||
}
|
||||
rz_core_seek(core, offset, true);
|
||||
rz_core_print_disasm(core, core->offset, core->block, (int)core->blocksize, 40,
|
||||
NULL, NULL);
|
||||
return true;
|
||||
},
|
||||
addr);
|
||||
ui->previewTextEdit->document()->setHtml(disas);
|
||||
|
||||
// Does it make any sense?
|
||||
|
@ -69,6 +69,11 @@ DecompilerContextMenu::DecompilerContextMenu(QWidget *parent, MainWindow *mainWi
|
||||
|
||||
DecompilerContextMenu::~DecompilerContextMenu() {}
|
||||
|
||||
QWidget *DecompilerContextMenu::parentForDialog()
|
||||
{
|
||||
return parentWidget();
|
||||
}
|
||||
|
||||
void DecompilerContextMenu::setAnnotationHere(RzCodeAnnotation *annotation)
|
||||
{
|
||||
annotationHere = annotation;
|
||||
@ -404,14 +409,15 @@ void DecompilerContextMenu::actionRenameThingHereTriggered()
|
||||
RzAnalysisFunction *func = Core()->functionAt(func_addr);
|
||||
if (func == NULL) {
|
||||
QString function_name = QInputDialog::getText(
|
||||
this, tr("Define this function at %2").arg(RzAddressString(func_addr)),
|
||||
parentForDialog(),
|
||||
tr("Define this function at %2").arg(RzAddressString(func_addr)),
|
||||
tr("Function name:"), QLineEdit::Normal, currentName, &ok);
|
||||
if (ok && !function_name.isEmpty()) {
|
||||
Core()->createFunctionAt(func_addr, function_name);
|
||||
}
|
||||
} else {
|
||||
QString newName = QInputDialog::getText(
|
||||
this->mainWindow, tr("Rename function %2").arg(currentName),
|
||||
parentForDialog(), tr("Rename function %2").arg(currentName),
|
||||
tr("Function name:"), QLineEdit::Normal, currentName, &ok);
|
||||
if (ok && !newName.isEmpty()) {
|
||||
Core()->renameFunction(func_addr, newName);
|
||||
@ -421,16 +427,16 @@ void DecompilerContextMenu::actionRenameThingHereTriggered()
|
||||
RVA var_addr = annotationHere->reference.offset;
|
||||
RzFlagItem *flagDetails = rz_flag_get_i(core->flags, var_addr);
|
||||
if (flagDetails) {
|
||||
QString newName = QInputDialog::getText(this, tr("Rename %2").arg(flagDetails->name),
|
||||
tr("Enter name"), QLineEdit::Normal,
|
||||
flagDetails->name, &ok);
|
||||
QString newName = QInputDialog::getText(
|
||||
parentForDialog(), tr("Rename %2").arg(flagDetails->name), tr("Enter name"),
|
||||
QLineEdit::Normal, flagDetails->name, &ok);
|
||||
if (ok && !newName.isEmpty()) {
|
||||
Core()->renameFlag(flagDetails->name, newName);
|
||||
}
|
||||
} else {
|
||||
QString newName = QInputDialog::getText(
|
||||
this, tr("Add name to %2").arg(curHighlightedWord), tr("Enter name"),
|
||||
QLineEdit::Normal, curHighlightedWord, &ok);
|
||||
parentForDialog(), tr("Add name to %2").arg(curHighlightedWord),
|
||||
tr("Enter name"), QLineEdit::Normal, curHighlightedWord, &ok);
|
||||
if (ok && !newName.isEmpty()) {
|
||||
Core()->addFlag(var_addr, newName, 1);
|
||||
}
|
||||
@ -439,14 +445,14 @@ void DecompilerContextMenu::actionRenameThingHereTriggered()
|
||||
if (!variablePresentInRizin()) {
|
||||
// Show can't rename this variable dialog
|
||||
QMessageBox::critical(
|
||||
this,
|
||||
parentForDialog(),
|
||||
tr("Rename local variable %1").arg(QString(annotationHere->variable.name)),
|
||||
tr("Can't rename this variable. "
|
||||
"Only local variables defined in disassembly can be renamed."));
|
||||
return;
|
||||
}
|
||||
QString oldName(annotationHere->variable.name);
|
||||
QString newName = QInputDialog::getText(this, tr("Rename %2").arg(oldName),
|
||||
QString newName = QInputDialog::getText(parentForDialog(), tr("Rename %2").arg(oldName),
|
||||
tr("Enter name"), QLineEdit::Normal, oldName, &ok);
|
||||
if (ok && !newName.isEmpty()) {
|
||||
Core()->renameFunctionVariable(newName, oldName, decompiledFunctionAddress);
|
||||
@ -465,13 +471,14 @@ void DecompilerContextMenu::actionEditFunctionVariablesTriggered()
|
||||
return;
|
||||
} else if (!variablePresentInRizin()) {
|
||||
QMessageBox::critical(
|
||||
this, tr("Edit local variable %1").arg(QString(annotationHere->variable.name)),
|
||||
parentForDialog(),
|
||||
tr("Edit local variable %1").arg(QString(annotationHere->variable.name)),
|
||||
tr("Can't edit this variable. "
|
||||
"Only local variables defined in disassembly can be edited."));
|
||||
return;
|
||||
}
|
||||
EditVariablesDialog dialog(decompiledFunctionAddress, QString(annotationHere->variable.name),
|
||||
this);
|
||||
parentForDialog());
|
||||
dialog.exec();
|
||||
}
|
||||
|
||||
|
@ -111,6 +111,12 @@ private:
|
||||
QAction actionSetPC;
|
||||
|
||||
// Private Functions
|
||||
|
||||
/**
|
||||
* \return widget that should be used as parent for presenting dialogs
|
||||
*/
|
||||
QWidget *parentForDialog();
|
||||
|
||||
/**
|
||||
* @brief Sets the shortcut context in all the actions contained
|
||||
* in the specified QMenu to Qt::WidgetWithChildrenShortcut.
|
||||
|
@ -313,34 +313,33 @@ void DisassemblyContextMenu::addDebugMenu()
|
||||
|
||||
QVector<DisassemblyContextMenu::ThingUsedHere> DisassemblyContextMenu::getThingUsedHere(RVA offset)
|
||||
{
|
||||
QVector<ThingUsedHere> result;
|
||||
const CutterJson array = Core()->cmdj("anj @ " + QString::number(offset));
|
||||
result.reserve(array.size());
|
||||
for (const auto &thing : array) {
|
||||
auto obj = thing;
|
||||
RVA offset = obj["offset"].toRVA();
|
||||
QString name;
|
||||
|
||||
// If real names display is enabled, show flag's real name instead of full flag name
|
||||
if (Config()->getConfigBool("asm.flags.real") && obj["realname"].valid()) {
|
||||
name = obj["realname"].toString();
|
||||
} else {
|
||||
name = obj["name"].toString();
|
||||
}
|
||||
|
||||
QString typeString = obj["type"].toString();
|
||||
ThingUsedHere::Type type = ThingUsedHere::Type::Address;
|
||||
if (typeString == "var") {
|
||||
type = ThingUsedHere::Type::Var;
|
||||
} else if (typeString == "flag") {
|
||||
type = ThingUsedHere::Type::Flag;
|
||||
} else if (typeString == "function") {
|
||||
type = ThingUsedHere::Type::Function;
|
||||
} else if (typeString == "address") {
|
||||
type = ThingUsedHere::Type::Address;
|
||||
}
|
||||
result.push_back(ThingUsedHere { name, offset, type });
|
||||
RzCoreLocked core(Core());
|
||||
auto p = fromOwned(
|
||||
rz_core_analysis_name(core, offset), rz_core_analysis_name_free);
|
||||
if (!p) {
|
||||
return {};
|
||||
}
|
||||
|
||||
QVector<ThingUsedHere> result;
|
||||
ThingUsedHere th;
|
||||
th.offset = p->offset;
|
||||
th.name = Config()->getConfigBool("asm.flags.real") && p->realname ? p->realname : p->name;
|
||||
switch (p->type) {
|
||||
case RZ_CORE_ANALYSIS_NAME_TYPE_FLAG:
|
||||
th.type = ThingUsedHere::Type::Flag;
|
||||
break;
|
||||
case RZ_CORE_ANALYSIS_NAME_TYPE_FUNCTION:
|
||||
th.type = ThingUsedHere::Type::Function;
|
||||
break;
|
||||
case RZ_CORE_ANALYSIS_NAME_TYPE_VAR:
|
||||
th.type = ThingUsedHere::Type::Var;
|
||||
break;
|
||||
case RZ_CORE_ANALYSIS_NAME_TYPE_ADDRESS:
|
||||
default:
|
||||
th.type = ThingUsedHere::Type::Address;
|
||||
break;
|
||||
}
|
||||
result.push_back(th);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -482,13 +481,7 @@ void DisassemblyContextMenu::setupRenaming()
|
||||
void DisassemblyContextMenu::aboutToShowSlot()
|
||||
{
|
||||
// check if set immediate base menu makes sense
|
||||
RzPVector *vec = (RzPVector *)Core()->returnAtSeek(
|
||||
[&]() {
|
||||
RzCoreLocked core(Core());
|
||||
return rz_core_analysis_bytes(core, core->block, (int)core->blocksize, 1);
|
||||
},
|
||||
offset);
|
||||
auto *ab = static_cast<RzAnalysisBytes *>(rz_pvector_head(vec));
|
||||
auto ab = Core()->getRzAnalysisBytesSingle(offset);
|
||||
|
||||
bool immBase = ab && ab->op && (ab->op->val || ab->op->ptr);
|
||||
setBaseMenu->menuAction()->setVisible(immBase);
|
||||
@ -514,7 +507,6 @@ void DisassemblyContextMenu::aboutToShowSlot()
|
||||
}
|
||||
}
|
||||
}
|
||||
rz_pvector_free(vec);
|
||||
|
||||
if (memBaseReg.isEmpty()) {
|
||||
// hide structure offset menu
|
||||
@ -532,7 +524,7 @@ void DisassemblyContextMenu::aboutToShowSlot()
|
||||
continue;
|
||||
}
|
||||
structureOffsetMenu->addAction("[" + memBaseReg + " + " + ty->path + "]")
|
||||
->setData(ty->path);
|
||||
->setData(QString(ty->path));
|
||||
}
|
||||
rz_list_free(typeoffs);
|
||||
}
|
||||
@ -726,18 +718,13 @@ void DisassemblyContextMenu::on_actionNopInstruction_triggered()
|
||||
|
||||
void DisassemblyContextMenu::showReverseJmpQuery()
|
||||
{
|
||||
QString type;
|
||||
|
||||
CutterJson array = Core()->cmdj("pdj 1 @ " + RzAddressString(offset));
|
||||
if (!array.size()) {
|
||||
actionJmpReverse.setVisible(false);
|
||||
auto ab = Core()->getRzAnalysisBytesSingle(offset);
|
||||
if (!(ab && ab->op)) {
|
||||
return;
|
||||
}
|
||||
|
||||
type = array.first()["type"].toString();
|
||||
if (type == "cjmp") {
|
||||
if (ab->op->type == RZ_ANALYSIS_OP_TYPE_CJMP) {
|
||||
actionJmpReverse.setVisible(true);
|
||||
} else {
|
||||
actionJmpReverse.setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1036,18 +1023,15 @@ void DisassemblyContextMenu::on_actionEditFunction_triggered()
|
||||
|
||||
if (dialog.exec()) {
|
||||
QString new_name = dialog.getNameText();
|
||||
Core()->renameFunction(fcn->addr, new_name);
|
||||
rz_core_analysis_function_rename(core, fcn->addr, new_name.toStdString().c_str());
|
||||
QString new_start_addr = dialog.getStartAddrText();
|
||||
fcn->addr = Core()->math(new_start_addr);
|
||||
QString new_stack_size = dialog.getStackSizeText();
|
||||
fcn->stack = int(Core()->math(new_stack_size));
|
||||
|
||||
const char *ccSelected = dialog.getCallConSelected().toUtf8().constData();
|
||||
if (RZ_STR_ISEMPTY(ccSelected)) {
|
||||
return;
|
||||
}
|
||||
if (rz_analysis_cc_exist(core->analysis, ccSelected)) {
|
||||
fcn->cc = rz_str_constpool_get(&core->analysis->constpool, ccSelected);
|
||||
QByteArray newCC = dialog.getCallConSelected().toUtf8();
|
||||
if (!newCC.isEmpty() && rz_analysis_cc_exist(core->analysis, newCC.constData())) {
|
||||
fcn->cc = rz_str_constpool_get(&core->analysis->constpool, newCC.constData());
|
||||
}
|
||||
|
||||
emit Core()->functionsChanged();
|
||||
|
@ -133,6 +133,10 @@ QString PluginManager::getUserPluginsDirectory() const
|
||||
void PluginManager::loadNativePlugins(const QDir &directory)
|
||||
{
|
||||
for (const QString &fileName : directory.entryList(QDir::Files)) {
|
||||
if (!QLibrary::isLibrary(fileName)) {
|
||||
// Reduce amount of warnings, by not attempting files which are obviously not plugins
|
||||
continue;
|
||||
}
|
||||
QPluginLoader pluginLoader(directory.absoluteFilePath(fileName));
|
||||
QObject *plugin = pluginLoader.instance();
|
||||
if (!plugin) {
|
||||
|
@ -2,6 +2,7 @@ cmake_minimum_required(VERSION 3.12)
|
||||
project(cutter-sample-plugin)
|
||||
|
||||
find_package(Cutter REQUIRED)
|
||||
find_package(Rizin REQUIRED)
|
||||
set(CUTTER_INSTALL_PLUGDIR "${Cutter_USER_PLUGINDIR}" CACHE STRING "Directory to install Cutter plugin into")
|
||||
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
@ -9,5 +10,5 @@ set(CMAKE_AUTOMOC ON)
|
||||
add_library(sample_plugin MODULE
|
||||
CutterSamplePlugin.h
|
||||
CutterSamplePlugin.cpp)
|
||||
target_link_libraries(sample_plugin PRIVATE Cutter::Cutter)
|
||||
target_link_libraries(sample_plugin PRIVATE Cutter::Cutter Rizin::Core)
|
||||
install(TARGETS sample_plugin DESTINATION "${CUTTER_INSTALL_PLUGDIR}")
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include <common/TempConfig.h>
|
||||
#include <common/Configuration.h>
|
||||
#include <MainWindow.h>
|
||||
#include <librz/rz_core.h>
|
||||
|
||||
void CutterSamplePlugin::setupPlugin() {}
|
||||
|
||||
@ -46,25 +47,21 @@ CutterSamplePluginWidget::CutterSamplePluginWidget(MainWindow *main) : CutterDoc
|
||||
void CutterSamplePluginWidget::on_seekChanged(RVA addr)
|
||||
{
|
||||
Q_UNUSED(addr);
|
||||
QString res;
|
||||
{
|
||||
TempConfig tempConfig;
|
||||
tempConfig.set("scr.color", 0);
|
||||
res = Core()->cmd("?E `pi 1`");
|
||||
}
|
||||
RzCoreLocked core(Core());
|
||||
TempConfig tempConfig;
|
||||
tempConfig.set("scr.color", 0);
|
||||
QString disasm = Core()->disassembleSingleInstruction(Core()->getOffset());
|
||||
QString res = fromOwnedCharPtr(rz_core_clippy(core, disasm.toUtf8().constData()));
|
||||
text->setText(res);
|
||||
}
|
||||
|
||||
void CutterSamplePluginWidget::on_buttonClicked()
|
||||
{
|
||||
RzCoreLocked core(Core());
|
||||
char *fortune = rz_core_fortune_get_random(core);
|
||||
auto fortune = fromOwned(rz_core_fortune_get_random(core));
|
||||
if (!fortune) {
|
||||
return;
|
||||
}
|
||||
// cmdRaw can be used to execute single raw commands
|
||||
// this is especially good for user-controlled input
|
||||
QString res = Core()->cmdRaw("?E " + QString::fromUtf8(fortune));
|
||||
QString res = fromOwnedCharPtr(rz_core_clippy(core, fortune.get()));
|
||||
text->setText(res);
|
||||
rz_mem_free(fortune);
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ class FortuneWidget(cutter.CutterDockWidget):
|
||||
self.show()
|
||||
|
||||
def generate_fortune(self):
|
||||
fortune = cutter.cmd("fo").replace("\n", "")
|
||||
fortune = cutter.cmd("fortune").replace("\n", "")
|
||||
res = cutter.core().cmdRaw(f"?E {fortune}")
|
||||
self.text.setText(res)
|
||||
|
||||
@ -44,7 +44,7 @@ class FortuneWidget(cutter.CutterDockWidget):
|
||||
class CutterSamplePlugin(cutter.CutterPlugin):
|
||||
name = "Sample Plugin"
|
||||
description = "A sample plugin written in python."
|
||||
version = "1.1"
|
||||
version = "1.2"
|
||||
author = "Cutter developers"
|
||||
|
||||
# Override CutterPlugin methods
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 974298653ba71b958e1b6c83f6011f5fefff6236
|
||||
Subproject commit 41c0c778b942577749ea2fed117e48a2cf3892df
|
@ -7,9 +7,11 @@
|
||||
#include <QJsonObject>
|
||||
|
||||
CallGraphWidget::CallGraphWidget(MainWindow *main, bool global)
|
||||
: AddressableDockWidget(main), graphView(new CallGraphView(this, main, global)), global(global)
|
||||
: MemoryDockWidget(MemoryWidgetType::CallGraph, main),
|
||||
graphView(new CallGraphView(this, main, global)),
|
||||
global(global)
|
||||
{
|
||||
setObjectName(main->getUniqueObjectName("CallGraphWidget"));
|
||||
setObjectName(main ? main->getUniqueObjectName(getWidgetType()) : getWidgetType());
|
||||
this->setWindowTitle(getWindowTitle());
|
||||
connect(seekable, &CutterSeekable::seekableSeekChanged, this, &CallGraphWidget::onSeekChanged);
|
||||
|
||||
@ -23,6 +25,11 @@ QString CallGraphWidget::getWindowTitle() const
|
||||
return global ? tr("Global Callgraph") : tr("Callgraph");
|
||||
}
|
||||
|
||||
QString CallGraphWidget::getWidgetType() const
|
||||
{
|
||||
return global ? tr("GlobalCallgraph") : tr("Callgraph");
|
||||
}
|
||||
|
||||
void CallGraphWidget::onSeekChanged(RVA address)
|
||||
{
|
||||
if (auto function = Core()->functionIn(address)) {
|
||||
@ -37,6 +44,7 @@ CallGraphView::CallGraphView(CutterDockWidget *parent, MainWindow *main, bool gl
|
||||
refreshDeferrer.registerFor(parent);
|
||||
connect(&refreshDeferrer, &RefreshDeferrer::refreshNow, this, &CallGraphView::refreshView);
|
||||
connect(Core(), &CutterCore::refreshAll, this, &SimpleTextGraphView::refreshView);
|
||||
connect(Core(), &CutterCore::functionRenamed, this, &CallGraphView::refreshView);
|
||||
}
|
||||
|
||||
void CallGraphView::showExportDialog()
|
||||
@ -47,17 +55,14 @@ void CallGraphView::showExportDialog()
|
||||
} else {
|
||||
defaultName = QString("callgraph_%1").arg(RzAddressString(address));
|
||||
}
|
||||
showExportGraphDialog(defaultName, global ? "agC" : "agc", address);
|
||||
showExportGraphDialog(defaultName, RZ_CORE_GRAPH_TYPE_FUNCALL, global ? RVA_INVALID : address);
|
||||
}
|
||||
|
||||
void CallGraphView::showAddress(RVA address)
|
||||
{
|
||||
if (global) {
|
||||
auto addressMappingIt = addressMapping.find(address);
|
||||
if (addressMappingIt != addressMapping.end()) {
|
||||
selectBlockWithId(addressMappingIt->second);
|
||||
showBlock(blocks[addressMappingIt->second]);
|
||||
}
|
||||
selectBlockWithId(address);
|
||||
showBlock(blocks[address]);
|
||||
} else if (address != this->address) {
|
||||
this->address = address;
|
||||
refreshView();
|
||||
@ -72,53 +77,70 @@ void CallGraphView::refreshView()
|
||||
SimpleTextGraphView::refreshView();
|
||||
}
|
||||
|
||||
static inline bool isBetween(ut64 a, ut64 x, ut64 b)
|
||||
{
|
||||
return (a == UT64_MAX || a <= x) && (b == UT64_MAX || x <= b);
|
||||
}
|
||||
|
||||
void CallGraphView::loadCurrentGraph()
|
||||
{
|
||||
blockContent.clear();
|
||||
blocks.clear();
|
||||
|
||||
CutterJson nodes = Core()->cmdj(global ? "agCj" : QString("agcj @ %1").arg(address));
|
||||
const ut64 from = Core()->getConfigi("graph.from");
|
||||
const ut64 to = Core()->getConfigi("graph.to");
|
||||
const bool usenames = Core()->getConfigb("graph.json.usenames");
|
||||
|
||||
QHash<QString, uint64_t> idMapping;
|
||||
auto edges = std::unordered_set<ut64> {};
|
||||
auto addFunction = [&](RzAnalysisFunction *fcn) {
|
||||
GraphLayout::GraphBlock block;
|
||||
block.entry = fcn->addr;
|
||||
|
||||
auto getId = [&](const QString &name) -> uint64_t {
|
||||
auto nextId = idMapping.size();
|
||||
auto &itemId = idMapping[name];
|
||||
if (idMapping.size() != nextId) {
|
||||
itemId = nextId;
|
||||
auto xrefs = fromOwned(rz_analysis_function_get_xrefs_from(fcn));
|
||||
auto calls = std::unordered_set<ut64>();
|
||||
for (const auto &xref : CutterRzList<RzAnalysisXRef>(xrefs.get())) {
|
||||
const auto x = xref->to;
|
||||
if (!(xref->type == RZ_ANALYSIS_XREF_TYPE_CALL && calls.find(x) == calls.end())) {
|
||||
continue;
|
||||
}
|
||||
calls.insert(x);
|
||||
block.edges.emplace_back(x);
|
||||
edges.insert(x);
|
||||
}
|
||||
return itemId;
|
||||
|
||||
QString name = usenames ? fcn->name : RzAddressString(fcn->addr);
|
||||
addBlock(std::move(block), name, fcn->addr);
|
||||
};
|
||||
|
||||
for (CutterJson block : nodes) {
|
||||
QString name = block["name"].toString();
|
||||
|
||||
auto edges = block["imports"];
|
||||
GraphLayout::GraphBlock layoutBlock;
|
||||
layoutBlock.entry = getId(name);
|
||||
for (auto edge : edges) {
|
||||
auto targetName = edge.toString();
|
||||
auto targetId = getId(targetName);
|
||||
layoutBlock.edges.emplace_back(targetId);
|
||||
if (global) {
|
||||
for (const auto &fcn : CutterRzList<RzAnalysisFunction>(Core()->core()->analysis->fcns)) {
|
||||
if (!isBetween(from, fcn->addr, to)) {
|
||||
continue;
|
||||
}
|
||||
addFunction(fcn);
|
||||
}
|
||||
} else {
|
||||
const auto &fcn = Core()->functionIn(address);
|
||||
if (fcn) {
|
||||
addFunction(fcn);
|
||||
}
|
||||
|
||||
// it would be good if address came directly from json instead of having to lookup by name
|
||||
addBlock(std::move(layoutBlock), name, Core()->num(name));
|
||||
}
|
||||
for (auto it = idMapping.begin(), end = idMapping.end(); it != end; ++it) {
|
||||
if (blocks.find(it.value()) == blocks.end()) {
|
||||
GraphLayout::GraphBlock block;
|
||||
block.entry = it.value();
|
||||
addBlock(std::move(block), it.key(), Core()->num(it.key()));
|
||||
|
||||
for (const auto &x : edges) {
|
||||
if (blockContent.find(x) != blockContent.end()) {
|
||||
continue;
|
||||
}
|
||||
GraphLayout::GraphBlock block;
|
||||
block.entry = x;
|
||||
QString flagName = Core()->flagAt(x);
|
||||
QString name = usenames
|
||||
? (!flagName.isEmpty() ? flagName : QString("unk.%0").arg(RzAddressString(x)))
|
||||
: RzAddressString(x);
|
||||
addBlock(std::move(block), name, x);
|
||||
}
|
||||
if (blockContent.empty() && !global) {
|
||||
addBlock({}, RzAddressString(address), address);
|
||||
}
|
||||
|
||||
addressMapping.clear();
|
||||
for (auto &it : blockContent) {
|
||||
addressMapping[it.second.address] = it.first;
|
||||
const auto name = RzAddressString(address);
|
||||
addBlock({}, name, address);
|
||||
}
|
||||
|
||||
computeGraphPlacement();
|
||||
|
@ -2,7 +2,7 @@
|
||||
#define CALL_GRAPH_WIDGET_H
|
||||
|
||||
#include "core/Cutter.h"
|
||||
#include "AddressableDockWidget.h"
|
||||
#include "MemoryDockWidget.h"
|
||||
#include "widgets/SimpleTextGraphView.h"
|
||||
#include "common/RefreshDeferrer.h"
|
||||
|
||||
@ -22,7 +22,6 @@ public:
|
||||
protected:
|
||||
bool global; ///< is this a global or function callgraph
|
||||
RVA address = RVA_INVALID; ///< function address if this is not a global callgraph
|
||||
std::unordered_map<RVA, ut64> addressMapping; ///< mapping from addresses to block id
|
||||
void loadCurrentGraph() override;
|
||||
void restoreCurrentBlock() override;
|
||||
|
||||
@ -31,7 +30,7 @@ private:
|
||||
RVA lastLoadedAddress = RVA_INVALID;
|
||||
};
|
||||
|
||||
class CallGraphWidget : public AddressableDockWidget
|
||||
class CallGraphWidget : public MemoryDockWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
@ -39,6 +38,8 @@ public:
|
||||
explicit CallGraphWidget(MainWindow *main, bool global);
|
||||
~CallGraphWidget();
|
||||
|
||||
QString getWidgetType() const;
|
||||
|
||||
protected:
|
||||
QString getWindowTitle() const override;
|
||||
|
||||
|
@ -48,8 +48,9 @@ void ColorOptionDelegate::paint(QPainter *painter, const QStyleOptionViewItem &o
|
||||
|
||||
ColorOption currCO = index.data(Qt::UserRole).value<ColorOption>();
|
||||
|
||||
QFontMetrics fm = QFontMetrics(painter->font());
|
||||
int penWidth = painter->pen().width();
|
||||
int fontHeight = painter->fontMetrics().height();
|
||||
int fontHeight = fm.height();
|
||||
QPoint tl = option.rect.topLeft();
|
||||
|
||||
QRect optionNameRect;
|
||||
@ -126,9 +127,9 @@ void ColorOptionDelegate::paint(QPainter *painter, const QStyleOptionViewItem &o
|
||||
|
||||
painter->setPen(qApp->palette().text().color());
|
||||
|
||||
QString name =
|
||||
painter->fontMetrics().elidedText(optionInfoMap__[currCO.optionName].displayingtext,
|
||||
Qt::ElideRight, optionNameRect.width());
|
||||
QFontMetrics fm2 = QFontMetrics(painter->font());
|
||||
QString name = fm2.elidedText(optionInfoMap__[currCO.optionName].displayingtext,
|
||||
Qt::ElideRight, optionNameRect.width());
|
||||
painter->drawText(optionNameRect, name);
|
||||
|
||||
QPainterPath roundedOptionRect;
|
||||
@ -155,7 +156,8 @@ void ColorOptionDelegate::paint(QPainter *painter, const QStyleOptionViewItem &o
|
||||
painter->setPen(currCO.color);
|
||||
painter->fillPath(roundedColorRect, currCO.color);
|
||||
|
||||
QString desc = painter->fontMetrics().elidedText(
|
||||
QFontMetrics fm3 = QFontMetrics(painter->font());
|
||||
QString desc = fm3.elidedText(
|
||||
currCO.optionName + ": " + optionInfoMap__[currCO.optionName].info, Qt::ElideRight,
|
||||
descTextRect.width());
|
||||
painter->setPen(qApp->palette().text().color());
|
||||
@ -197,7 +199,8 @@ QPixmap ColorOptionDelegate::getPixmapFromSvg(const QString &fileName, const QCo
|
||||
data.replace(QRegularExpression("#[0-9a-fA-F]{6}"), QString("%1").arg(after.name()));
|
||||
|
||||
QSvgRenderer svgRenderer(data.toUtf8());
|
||||
QPixmap pix(QSize(qApp->fontMetrics().height(), qApp->fontMetrics().height()));
|
||||
QFontMetrics fm = QFontMetrics(qApp->font());
|
||||
QPixmap pix(QSize(fm.height(), fm.height()));
|
||||
pix.fill(Qt::transparent);
|
||||
|
||||
QPainter pixPainter(&pix);
|
||||
|
@ -6,8 +6,14 @@ ComboQuickFilterView::ComboQuickFilterView(QWidget *parent)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
debounceTimer = new QTimer(this);
|
||||
debounceTimer->setSingleShot(true);
|
||||
|
||||
connect(debounceTimer, &QTimer::timeout, this,
|
||||
[this]() { emit filterTextChanged(ui->lineEdit->text()); });
|
||||
|
||||
connect(ui->lineEdit, &QLineEdit::textChanged, this,
|
||||
[this](const QString &text) { emit filterTextChanged(text); });
|
||||
[this](const QString &text) { debounceTimer->start(150); });
|
||||
}
|
||||
|
||||
ComboQuickFilterView::~ComboQuickFilterView()
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
#include <QWidget>
|
||||
#include <QComboBox>
|
||||
#include <QTimer>
|
||||
|
||||
namespace Ui {
|
||||
class ComboQuickFilterView;
|
||||
@ -32,6 +33,7 @@ signals:
|
||||
|
||||
private:
|
||||
Ui::ComboQuickFilterView *ui;
|
||||
QTimer *debounceTimer;
|
||||
};
|
||||
|
||||
#endif // COMBOQUICKFILTERVIEW_H
|
||||
|
@ -152,7 +152,7 @@ void CutterGraphView::zoomReset()
|
||||
|
||||
void CutterGraphView::showExportDialog()
|
||||
{
|
||||
showExportGraphDialog("graph", "", RVA_INVALID);
|
||||
showExportGraphDialog("global_funcall", RZ_CORE_GRAPH_TYPE_FUNCALL, RVA_INVALID);
|
||||
}
|
||||
|
||||
void CutterGraphView::updateColors()
|
||||
@ -318,12 +318,12 @@ void CutterGraphView::mouseMoveEvent(QMouseEvent *event)
|
||||
emit graphMoved();
|
||||
}
|
||||
|
||||
void CutterGraphView::exportGraph(QString filePath, GraphExportType type, QString graphCommand,
|
||||
RVA address)
|
||||
void CutterGraphView::exportGraph(QString filePath, GraphExportType exportType,
|
||||
RzCoreGraphType graphType, RVA address)
|
||||
{
|
||||
bool graphTransparent = Config()->getBitmapTransparentState();
|
||||
double graphScaleFactor = Config()->getBitmapExportScaleFactor();
|
||||
switch (type) {
|
||||
switch (exportType) {
|
||||
case GraphExportType::Png:
|
||||
this->saveAsBitmap(filePath, "png", graphScaleFactor, graphTransparent);
|
||||
break;
|
||||
@ -335,56 +335,55 @@ void CutterGraphView::exportGraph(QString filePath, GraphExportType type, QStrin
|
||||
break;
|
||||
|
||||
case GraphExportType::GVDot:
|
||||
exportRzTextGraph(filePath, graphCommand + "d", address);
|
||||
exportRzTextGraph(filePath, graphType, RZ_CORE_GRAPH_FORMAT_DOT, address);
|
||||
break;
|
||||
case GraphExportType::RzJson:
|
||||
exportRzTextGraph(filePath, graphCommand + "j", address);
|
||||
exportRzTextGraph(filePath, graphType, RZ_CORE_GRAPH_FORMAT_JSON, address);
|
||||
break;
|
||||
case GraphExportType::RzGml:
|
||||
exportRzTextGraph(filePath, graphCommand + "g", address);
|
||||
break;
|
||||
case GraphExportType::RzSDBKeyValue:
|
||||
exportRzTextGraph(filePath, graphCommand + "k", address);
|
||||
exportRzTextGraph(filePath, graphType, RZ_CORE_GRAPH_FORMAT_GML, address);
|
||||
break;
|
||||
|
||||
case GraphExportType::GVJson:
|
||||
exportRizinGraphvizGraph(filePath, "json", graphCommand, address);
|
||||
Core()->writeGraphvizGraphToFile(filePath, "json", graphType, address);
|
||||
break;
|
||||
case GraphExportType::GVGif:
|
||||
exportRizinGraphvizGraph(filePath, "gif", graphCommand, address);
|
||||
Core()->writeGraphvizGraphToFile(filePath, "gif", graphType, address);
|
||||
break;
|
||||
case GraphExportType::GVPng:
|
||||
exportRizinGraphvizGraph(filePath, "png", graphCommand, address);
|
||||
Core()->writeGraphvizGraphToFile(filePath, "png", graphType, address);
|
||||
break;
|
||||
case GraphExportType::GVJpeg:
|
||||
exportRizinGraphvizGraph(filePath, "jpg", graphCommand, address);
|
||||
Core()->writeGraphvizGraphToFile(filePath, "jpg", graphType, address);
|
||||
break;
|
||||
case GraphExportType::GVPostScript:
|
||||
exportRizinGraphvizGraph(filePath, "ps", graphCommand, address);
|
||||
Core()->writeGraphvizGraphToFile(filePath, "ps", graphType, address);
|
||||
break;
|
||||
case GraphExportType::GVSvg:
|
||||
exportRizinGraphvizGraph(filePath, "svg", graphCommand, address);
|
||||
Core()->writeGraphvizGraphToFile(filePath, "svg", graphType, address);
|
||||
break;
|
||||
case GraphExportType::GVPdf:
|
||||
Core()->writeGraphvizGraphToFile(filePath, "pdf", graphType, address);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CutterGraphView::exportRizinGraphvizGraph(QString filePath, QString type, QString graphCommand,
|
||||
RVA address)
|
||||
void CutterGraphView::exportRzTextGraph(QString filePath, RzCoreGraphType type,
|
||||
RzCoreGraphFormat format, RVA address)
|
||||
{
|
||||
TempConfig tempConfig;
|
||||
tempConfig.set("graph.gv.format", type);
|
||||
qWarning() << Core()->cmdRawAt(QString("%0w \"%1\"").arg(graphCommand).arg(filePath), address);
|
||||
}
|
||||
|
||||
void CutterGraphView::exportRzTextGraph(QString filePath, QString graphCommand, RVA address)
|
||||
{
|
||||
QFile file(filePath);
|
||||
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
||||
qWarning() << "Can't open file";
|
||||
char *string = Core()->getTextualGraphAt(type, format, address);
|
||||
if (!string) {
|
||||
return;
|
||||
}
|
||||
QTextStream fileOut(&file);
|
||||
fileOut << Core()->cmdRawAt(QString("%0").arg(graphCommand), address);
|
||||
|
||||
QFile file(filePath);
|
||||
if (file.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
||||
QTextStream fileOut(&file);
|
||||
fileOut << string;
|
||||
} else {
|
||||
qWarning() << "Can't open or create file: " << filePath;
|
||||
}
|
||||
free(string);
|
||||
}
|
||||
|
||||
bool CutterGraphView::graphIsBitamp(CutterGraphView::GraphExportType type)
|
||||
@ -403,40 +402,39 @@ bool CutterGraphView::graphIsBitamp(CutterGraphView::GraphExportType type)
|
||||
|
||||
Q_DECLARE_METATYPE(CutterGraphView::GraphExportType);
|
||||
|
||||
void CutterGraphView::showExportGraphDialog(QString defaultName, QString graphCommand, RVA address)
|
||||
void CutterGraphView::showExportGraphDialog(QString defaultName, RzCoreGraphType type, RVA address)
|
||||
{
|
||||
qWarning() << defaultName << " - " << type << " addr " << RzAddressString(address);
|
||||
QVector<MultitypeFileSaveDialog::TypeDescription> types = {
|
||||
{ tr("PNG (*.png)"), "png", QVariant::fromValue(GraphExportType::Png) },
|
||||
{ tr("JPEG (*.jpg)"), "jpg", QVariant::fromValue(GraphExportType::Jpeg) },
|
||||
{ tr("SVG (*.svg)"), "svg", QVariant::fromValue(GraphExportType::Svg) }
|
||||
};
|
||||
|
||||
bool rzGraphExports = !graphCommand.isEmpty();
|
||||
if (rzGraphExports) {
|
||||
types.append({
|
||||
{ tr("Graphviz dot (*.dot)"), "dot", QVariant::fromValue(GraphExportType::GVDot) },
|
||||
{ tr("Graph Modelling Language (*.gml)"), "gml",
|
||||
QVariant::fromValue(GraphExportType::RzGml) },
|
||||
{ tr("RZ JSON (*.json)"), "json", QVariant::fromValue(GraphExportType::RzJson) },
|
||||
{ tr("SDB key-value (*.txt)"), "txt",
|
||||
QVariant::fromValue(GraphExportType::RzSDBKeyValue) },
|
||||
});
|
||||
bool hasGraphviz = !QStandardPaths::findExecutable("dot").isEmpty()
|
||||
|| !QStandardPaths::findExecutable("xdot").isEmpty();
|
||||
if (hasGraphviz) {
|
||||
types.append({ { tr("Graphviz json (*.json)"), "json",
|
||||
QVariant::fromValue(GraphExportType::GVJson) },
|
||||
{ tr("Graphviz gif (*.gif)"), "gif",
|
||||
QVariant::fromValue(GraphExportType::GVGif) },
|
||||
{ tr("Graphviz png (*.png)"), "png",
|
||||
QVariant::fromValue(GraphExportType::GVPng) },
|
||||
{ tr("Graphviz jpg (*.jpg)"), "jpg",
|
||||
QVariant::fromValue(GraphExportType::GVJpeg) },
|
||||
{ tr("Graphviz PostScript (*.ps)"), "ps",
|
||||
QVariant::fromValue(GraphExportType::GVPostScript) },
|
||||
{ tr("Graphviz svg (*.svg)"), "svg",
|
||||
QVariant::fromValue(GraphExportType::GVSvg) } });
|
||||
}
|
||||
types.append({
|
||||
{ tr("Graphviz dot (*.dot)"), "dot", QVariant::fromValue(GraphExportType::GVDot) },
|
||||
{ tr("Graph Modelling Language (*.gml)"), "gml",
|
||||
QVariant::fromValue(GraphExportType::RzGml) },
|
||||
{ tr("RZ JSON (*.json)"), "json", QVariant::fromValue(GraphExportType::RzJson) },
|
||||
});
|
||||
|
||||
bool hasGraphviz = !QStandardPaths::findExecutable("dot").isEmpty()
|
||||
|| !QStandardPaths::findExecutable("xdot").isEmpty();
|
||||
if (hasGraphviz) {
|
||||
types.append({ { tr("Graphviz json (*.json)"), "json",
|
||||
QVariant::fromValue(GraphExportType::GVJson) },
|
||||
{ tr("Graphviz gif (*.gif)"), "gif",
|
||||
QVariant::fromValue(GraphExportType::GVGif) },
|
||||
{ tr("Graphviz png (*.png)"), "png",
|
||||
QVariant::fromValue(GraphExportType::GVPng) },
|
||||
{ tr("Graphviz jpg (*.jpg)"), "jpg",
|
||||
QVariant::fromValue(GraphExportType::GVJpeg) },
|
||||
{ tr("Graphviz PostScript (*.ps)"), "ps",
|
||||
QVariant::fromValue(GraphExportType::GVPostScript) },
|
||||
{ tr("Graphviz svg (*.svg)"), "svg",
|
||||
QVariant::fromValue(GraphExportType::GVSvg) },
|
||||
{ tr("Graphviz pdf (*.pdf)"), "pdf",
|
||||
QVariant::fromValue(GraphExportType::GVPdf) } });
|
||||
}
|
||||
|
||||
MultitypeFileSaveDialog dialog(this, tr("Export Graph"));
|
||||
@ -470,5 +468,5 @@ void CutterGraphView::showExportGraphDialog(QString defaultName, QString graphCo
|
||||
}
|
||||
|
||||
QString filePath = dialog.selectedFiles().first();
|
||||
exportGraph(filePath, exportType, graphCommand, address);
|
||||
exportGraph(filePath, exportType, type, address);
|
||||
}
|
||||
|
@ -32,48 +32,38 @@ public:
|
||||
GVJpeg,
|
||||
GVPostScript,
|
||||
GVSvg,
|
||||
GVPdf,
|
||||
RzGml,
|
||||
RzSDBKeyValue,
|
||||
RzJson
|
||||
};
|
||||
/**
|
||||
* @brief Export graph to a file in the specified format
|
||||
* @param filePath
|
||||
* @param type export type, GV* and Rz* types require \p graphCommand
|
||||
* @param graphCommand rizin graph printing command without type, not required for direct image
|
||||
* export
|
||||
* @param address object address for commands like agf
|
||||
* @param filePath - output file path
|
||||
* @param exportType - export type, GV* and Rz* types require \p graphCommand
|
||||
* @param graphType - graph type, example RZ_CORE_GRAPH_TYPE_FUNCALL or
|
||||
* RZ_CORE_GRAPH_TYPE_IMPORT
|
||||
* @param address - object address (if global set it to RVA_INVALID)
|
||||
*/
|
||||
void exportGraph(QString filePath, GraphExportType type, QString graphCommand = "",
|
||||
void exportGraph(QString filePath, GraphExportType exportType, RzCoreGraphType graphType,
|
||||
RVA address = RVA_INVALID);
|
||||
/**
|
||||
* @brief Export image using rizin ag*w command and graphviz.
|
||||
* Requires graphviz dot executable in the path.
|
||||
*
|
||||
* @param filePath output file path
|
||||
* @param type image format as expected by "e graph.gv.format"
|
||||
* @param graphCommand rizin command without type, for example agf
|
||||
* @param address object address if required by command
|
||||
*/
|
||||
void exportRizinGraphvizGraph(QString filePath, QString type, QString graphCommand,
|
||||
RVA address);
|
||||
|
||||
/**
|
||||
* @brief Export graph in one of the text formats supported by rizin json, gml, SDB key-value
|
||||
* @param filePath output file path
|
||||
* @param graphCommand graph command including the format, example "agfd" or "agfg"
|
||||
* @param address object address if required by command
|
||||
* @param filePath - output file path
|
||||
* @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 - object address (if global set it to RVA_INVALID)
|
||||
*/
|
||||
void exportRzTextGraph(QString filePath, QString graphCommand, RVA address);
|
||||
void exportRzTextGraph(QString filePath, RzCoreGraphType type, RzCoreGraphFormat format,
|
||||
RVA address);
|
||||
static bool graphIsBitamp(GraphExportType type);
|
||||
/**
|
||||
* @brief Show graph export dialog.
|
||||
* @param defaultName - default file name in the export dialog
|
||||
* @param graphCommand - rizin graph commmand with graph type and without export type, for
|
||||
* example afC. Leave empty for non-rizin graphs. In such case only direct image export will be
|
||||
* available.
|
||||
* @param address - object address if relevant for \p graphCommand
|
||||
* @param type - graph type, example RZ_CORE_GRAPH_TYPE_FUNCALL or RZ_CORE_GRAPH_TYPE_IMPORT
|
||||
* @param address - object address (if global set it to RVA_INVALID)
|
||||
*/
|
||||
void showExportGraphDialog(QString defaultName, QString graphCommand = "",
|
||||
void showExportGraphDialog(QString defaultName, RzCoreGraphType type,
|
||||
RVA address = RVA_INVALID);
|
||||
|
||||
public slots:
|
||||
|
@ -4,8 +4,13 @@
|
||||
CutterTreeView::CutterTreeView(QWidget *parent) : QTreeView(parent), ui(new Ui::CutterTreeView())
|
||||
{
|
||||
ui->setupUi(this);
|
||||
this->setSelectionMode(QAbstractItemView::ExtendedSelection);
|
||||
this->setUniformRowHeights(true);
|
||||
applyCutterStyle(this);
|
||||
}
|
||||
|
||||
CutterTreeView::~CutterTreeView() {}
|
||||
|
||||
void CutterTreeView::applyCutterStyle(QTreeView *view)
|
||||
{
|
||||
view->setSelectionMode(QAbstractItemView::ExtendedSelection);
|
||||
view->setUniformRowHeights(true);
|
||||
}
|
||||
|
@ -19,6 +19,8 @@ public:
|
||||
explicit CutterTreeView(QWidget *parent = nullptr);
|
||||
~CutterTreeView();
|
||||
|
||||
static void applyCutterStyle(QTreeView *view);
|
||||
|
||||
private:
|
||||
std::unique_ptr<Ui::CutterTreeView> ui;
|
||||
};
|
||||
|
@ -2,7 +2,6 @@
|
||||
#include "ui_Dashboard.h"
|
||||
#include "common/Helpers.h"
|
||||
#include "common/JsonModel.h"
|
||||
#include "common/JsonTreeItem.h"
|
||||
#include "common/TempConfig.h"
|
||||
#include "dialogs/VersionInfoDialog.h"
|
||||
|
||||
@ -32,24 +31,27 @@ Dashboard::~Dashboard() {}
|
||||
|
||||
void Dashboard::updateContents()
|
||||
{
|
||||
CutterJson docu = Core()->getFileInfo();
|
||||
CutterJson item = docu["core"];
|
||||
CutterJson item2 = docu["bin"];
|
||||
RzCoreLocked core(Core());
|
||||
int fd = rz_io_fd_get_current(core->io);
|
||||
RzIODesc *desc = rz_io_desc_get(core->io, fd);
|
||||
setPlainText(this->ui->modeEdit, desc ? rz_str_rwx_i(desc->perm & RZ_PERM_RWX) : "");
|
||||
|
||||
setPlainText(this->ui->modeEdit, item["mode"].toString());
|
||||
setPlainText(this->ui->compilationDateEdit, item2["compiled"].toString());
|
||||
|
||||
if (!item2["relro"].toString().isEmpty()) {
|
||||
QString relro = item2["relro"].toString().section(QLatin1Char(' '), 0, 0);
|
||||
relro[0] = relro[0].toUpper();
|
||||
setPlainText(this->ui->relroEdit, relro);
|
||||
} else {
|
||||
setPlainText(this->ui->relroEdit, "N/A");
|
||||
RzBinFile *bf = rz_bin_cur(core->bin);
|
||||
if (bf) {
|
||||
setPlainText(this->ui->compilationDateEdit, rz_core_bin_get_compile_time(bf));
|
||||
if (bf->o) {
|
||||
char *relco_buf = sdb_get(bf->o->kv, "elf.relro", 0);
|
||||
if (RZ_STR_ISNOTEMPTY(relco_buf)) {
|
||||
QString relro = QString(relco_buf).section(QLatin1Char(' '), 0, 0);
|
||||
relro[0] = relro[0].toUpper();
|
||||
setPlainText(this->ui->relroEdit, relro);
|
||||
} else {
|
||||
setPlainText(this->ui->relroEdit, "N/A");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add file hashes, analysis info and libraries
|
||||
RzCoreLocked core(Core());
|
||||
RzBinFile *bf = rz_bin_cur(core->bin);
|
||||
RzBinInfo *binInfo = rz_bin_get_info(core->bin);
|
||||
|
||||
setPlainText(ui->fileEdit, binInfo ? binInfo->file : "");
|
||||
@ -111,82 +113,68 @@ void Dashboard::updateContents()
|
||||
hashesLayout->addRow(new QLabel(label), hashLineEdit);
|
||||
}
|
||||
|
||||
CutterJson analinfo = Core()->cmdj("aaij");
|
||||
setPlainText(ui->functionsLineEdit, QString::number(analinfo["fcns"].toSt64()));
|
||||
setPlainText(ui->xRefsLineEdit, QString::number(analinfo["xrefs"].toSt64()));
|
||||
setPlainText(ui->callsLineEdit, QString::number(analinfo["calls"].toSt64()));
|
||||
setPlainText(ui->stringsLineEdit, QString::number(analinfo["strings"].toSt64()));
|
||||
setPlainText(ui->symbolsLineEdit, QString::number(analinfo["symbols"].toSt64()));
|
||||
setPlainText(ui->importsLineEdit, QString::number(analinfo["imports"].toSt64()));
|
||||
setPlainText(ui->coverageLineEdit, QString::number(analinfo["covrage"].toSt64()) + " bytes");
|
||||
setPlainText(ui->codeSizeLineEdit, QString::number(analinfo["codesz"].toSt64()) + " bytes");
|
||||
setPlainText(ui->percentageLineEdit, QString::number(analinfo["percent"].toSt64()) + "%");
|
||||
st64 fcns = rz_list_length(core->analysis->fcns);
|
||||
st64 strs = rz_flag_count(core->flags, "str.*");
|
||||
st64 syms = rz_flag_count(core->flags, "sym.*");
|
||||
st64 imps = rz_flag_count(core->flags, "sym.imp.*");
|
||||
st64 code = rz_core_analysis_code_count(core);
|
||||
st64 covr = rz_core_analysis_coverage_count(core);
|
||||
st64 call = rz_core_analysis_calls_count(core);
|
||||
ut64 xrfs = rz_analysis_xrefs_count(core->analysis);
|
||||
double precentage = (code > 0) ? (covr * 100.0 / code) : 0;
|
||||
|
||||
// dunno: why not label->setText(lines.join("\n")?
|
||||
while (ui->verticalLayout_2->count() > 0) {
|
||||
QLayoutItem *item = ui->verticalLayout_2->takeAt(0);
|
||||
if (item != nullptr) {
|
||||
QWidget *w = item->widget();
|
||||
if (w != nullptr) {
|
||||
w->deleteLater();
|
||||
}
|
||||
|
||||
delete item;
|
||||
}
|
||||
}
|
||||
setPlainText(ui->functionsLineEdit, QString::number(fcns));
|
||||
setPlainText(ui->xRefsLineEdit, QString::number(xrfs));
|
||||
setPlainText(ui->callsLineEdit, QString::number(call));
|
||||
setPlainText(ui->stringsLineEdit, QString::number(strs));
|
||||
setPlainText(ui->symbolsLineEdit, QString::number(syms));
|
||||
setPlainText(ui->importsLineEdit, QString::number(imps));
|
||||
setPlainText(ui->coverageLineEdit, QString::number(covr) + " bytes");
|
||||
setPlainText(ui->codeSizeLineEdit, QString::number(code) + " bytes");
|
||||
setPlainText(ui->percentageLineEdit, QString::number(precentage) + "%");
|
||||
|
||||
ui->libraryList->setPlainText("");
|
||||
const RzList *libs = bf ? rz_bin_object_get_libs(bf->o) : nullptr;
|
||||
if (libs) {
|
||||
QString libText;
|
||||
bool first = true;
|
||||
for (const auto &lib : CutterRzList<char>(libs)) {
|
||||
auto *label = new QLabel(this);
|
||||
label->setText(lib);
|
||||
label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
|
||||
label->setTextInteractionFlags(Qt::TextSelectableByMouse);
|
||||
ui->verticalLayout_2->addWidget(label);
|
||||
if (!first) {
|
||||
libText.append("\n");
|
||||
}
|
||||
libText.append(lib);
|
||||
first = false;
|
||||
}
|
||||
ui->libraryList->setPlainText(libText);
|
||||
}
|
||||
|
||||
QSpacerItem *spacer = new QSpacerItem(1, 1, QSizePolicy::Fixed, QSizePolicy::Expanding);
|
||||
ui->verticalLayout_2->addSpacerItem(spacer);
|
||||
|
||||
// Check if signature info and version info available
|
||||
if (Core()->getSignatureInfo().isEmpty()) {
|
||||
if (!Core()->getSignatureInfo().size()) {
|
||||
ui->certificateButton->setEnabled(false);
|
||||
}
|
||||
if (!Core()->getFileVersionInfo().size()) {
|
||||
ui->versioninfoButton->setEnabled(false);
|
||||
}
|
||||
ui->versioninfoButton->setEnabled(Core()->existsFileInfo());
|
||||
}
|
||||
|
||||
void Dashboard::on_certificateButton_clicked()
|
||||
{
|
||||
static QDialog *viewDialog = nullptr;
|
||||
static CutterTreeView *view = nullptr;
|
||||
static JsonModel *model = nullptr;
|
||||
static QString qstrCertificates;
|
||||
if (!viewDialog) {
|
||||
viewDialog = new QDialog(this);
|
||||
view = new CutterTreeView(viewDialog);
|
||||
model = new JsonModel();
|
||||
qstrCertificates = Core()->getSignatureInfo();
|
||||
}
|
||||
if (!viewDialog->isVisible()) {
|
||||
std::string strCertificates = qstrCertificates.toUtf8().constData();
|
||||
model->loadJson(QByteArray::fromStdString(strCertificates));
|
||||
view->setModel(model);
|
||||
view->expandAll();
|
||||
view->resize(900, 600);
|
||||
QSizePolicy sizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||
sizePolicy.setHorizontalStretch(0);
|
||||
sizePolicy.setVerticalStretch(0);
|
||||
sizePolicy.setHeightForWidth(view->sizePolicy().hasHeightForWidth());
|
||||
viewDialog->setSizePolicy(sizePolicy);
|
||||
viewDialog->setMinimumSize(QSize(900, 600));
|
||||
viewDialog->setMaximumSize(QSize(900, 600));
|
||||
viewDialog->setSizeGripEnabled(false);
|
||||
viewDialog->setWindowTitle("Certificates");
|
||||
viewDialog->show();
|
||||
}
|
||||
QDialog dialog(this);
|
||||
auto view = new QTreeWidget(&dialog);
|
||||
view->setHeaderLabels({ tr("Key"), tr("Value") });
|
||||
view->addTopLevelItem(Cutter::jsonTreeWidgetItem(QString("<%1>").arg(tr("root")),
|
||||
Core()->getSignatureInfo()));
|
||||
CutterTreeView::applyCutterStyle(view);
|
||||
view->expandAll();
|
||||
view->resize(900, 600);
|
||||
QSizePolicy sizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||
sizePolicy.setHorizontalStretch(0);
|
||||
sizePolicy.setVerticalStretch(0);
|
||||
sizePolicy.setHeightForWidth(view->sizePolicy().hasHeightForWidth());
|
||||
dialog.setSizePolicy(sizePolicy);
|
||||
dialog.setMinimumSize(QSize(900, 600));
|
||||
dialog.setMaximumSize(QSize(900, 600));
|
||||
dialog.setSizeGripEnabled(false);
|
||||
dialog.setWindowTitle("Certificates");
|
||||
dialog.exec();
|
||||
}
|
||||
|
||||
void Dashboard::on_versioninfoButton_clicked()
|
||||
|
@ -42,7 +42,7 @@
|
||||
<enum>QFrame::Plain</enum>
|
||||
</property>
|
||||
<property name="verticalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarAlwaysOff</enum>
|
||||
<enum>Qt::ScrollBarAsNeeded</enum>
|
||||
</property>
|
||||
<property name="horizontalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarAlwaysOff</enum>
|
||||
@ -1331,10 +1331,33 @@
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2"/>
|
||||
<widget class="QPlainTextEdit" name="libraryList">
|
||||
<property name="horizontalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarAlwaysOff</enum>
|
||||
</property>
|
||||
<property name="lineWrapMode">
|
||||
<enum>QPlainTextEdit::NoWrap</enum>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
|
@ -109,14 +109,14 @@ DisassemblerGraphView::DisassemblerGraphView(QWidget *parent, CutterSeekable *se
|
||||
if (c.isValid()) {
|
||||
bbh->highlight(currBlockEntry, c);
|
||||
}
|
||||
Config()->colorsUpdated();
|
||||
emit Config()->colorsUpdated();
|
||||
});
|
||||
|
||||
actionUnhighlight.setText(tr("Unhighlight block"));
|
||||
connect(&actionUnhighlight, &QAction::triggered, this, [this]() {
|
||||
auto bbh = Core()->getBBHighlighter();
|
||||
bbh->clear(blockForAddress(this->seekable->getOffset())->entry);
|
||||
Config()->colorsUpdated();
|
||||
emit Config()->colorsUpdated();
|
||||
});
|
||||
|
||||
QAction *highlightBI = new QAction(this);
|
||||
@ -162,15 +162,15 @@ void DisassemblerGraphView::connectSeekChanged(bool disconn)
|
||||
|
||||
DisassemblerGraphView::~DisassemblerGraphView()
|
||||
{
|
||||
for (QShortcut *shortcut : shortcuts) {
|
||||
delete shortcut;
|
||||
}
|
||||
qDeleteAll(shortcuts);
|
||||
shortcuts.clear();
|
||||
}
|
||||
|
||||
void DisassemblerGraphView::refreshView()
|
||||
{
|
||||
CutterGraphView::refreshView();
|
||||
loadCurrentGraph();
|
||||
breakpoints = Core()->getBreakpointsAddresses();
|
||||
emit viewRefreshed();
|
||||
}
|
||||
|
||||
@ -182,13 +182,6 @@ void DisassemblerGraphView::loadCurrentGraph()
|
||||
.set("asm.lines", false)
|
||||
.set("asm.lines.fcn", false);
|
||||
|
||||
CutterJson functions;
|
||||
RzAnalysisFunction *fcn = Core()->functionIn(seekable->getOffset());
|
||||
if (fcn) {
|
||||
currentFcnAddr = fcn->addr;
|
||||
functions = Core()->cmdj("agJ " + RzAddressString(fcn->addr));
|
||||
}
|
||||
|
||||
disassembly_blocks.clear();
|
||||
blocks.clear();
|
||||
|
||||
@ -197,7 +190,19 @@ void DisassemblerGraphView::loadCurrentGraph()
|
||||
highlight_token = nullptr;
|
||||
}
|
||||
|
||||
emptyGraph = !functions.size();
|
||||
RzAnalysisFunction *fcn = Core()->functionIn(seekable->getOffset());
|
||||
|
||||
windowTitle = tr("Graph");
|
||||
if (fcn && RZ_STR_ISNOTEMPTY(fcn->name)) {
|
||||
currentFcnAddr = fcn->addr;
|
||||
auto fcnName = fromOwned(rz_str_escape_utf8_for_json(fcn->name, -1));
|
||||
windowTitle += QString("(%0)").arg(fcnName.get());
|
||||
} else {
|
||||
windowTitle += "(Empty)";
|
||||
}
|
||||
emit nameChanged(windowTitle);
|
||||
|
||||
emptyGraph = !fcn;
|
||||
if (emptyGraph) {
|
||||
// If there's no function to print, just add a message
|
||||
if (!emptyText) {
|
||||
@ -213,31 +218,20 @@ void DisassemblerGraphView::loadCurrentGraph()
|
||||
}
|
||||
// Refresh global "empty graph" variable so other widget know there is nothing to show here
|
||||
Core()->setGraphEmpty(emptyGraph);
|
||||
setEntry(fcn ? fcn->addr : RVA_INVALID);
|
||||
|
||||
CutterJson func = functions.first();
|
||||
|
||||
windowTitle = tr("Graph");
|
||||
QString funcName = func["name"].toString().trimmed();
|
||||
if (emptyGraph) {
|
||||
windowTitle += " (Empty)";
|
||||
} else if (!funcName.isEmpty()) {
|
||||
windowTitle += " (" + funcName + ")";
|
||||
if (!fcn) {
|
||||
return;
|
||||
}
|
||||
emit nameChanged(windowTitle);
|
||||
|
||||
RVA entry = func["offset"].toRVA();
|
||||
|
||||
setEntry(entry);
|
||||
for (CutterJson block : func["blocks"]) {
|
||||
RVA block_entry = block["offset"].toRVA();
|
||||
RVA block_size = block["size"].toRVA();
|
||||
RVA block_fail = block["fail"].toRVA();
|
||||
RVA block_jump = block["jump"].toRVA();
|
||||
for (const auto &bbi : CutterRzList<RzAnalysisBlock>(fcn->bbs)) {
|
||||
RVA bbiFail = bbi->fail;
|
||||
RVA bbiJump = bbi->jump;
|
||||
|
||||
DisassemblyBlock db;
|
||||
GraphBlock gb;
|
||||
gb.entry = block_entry;
|
||||
db.entry = block_entry;
|
||||
gb.entry = bbi->addr;
|
||||
db.entry = bbi->addr;
|
||||
if (Config()->getGraphBlockEntryOffset()) {
|
||||
// QColor(0,0,0,0) is transparent
|
||||
db.header_text = Text("[" + RzAddressString(db.entry) + "]", ConfigColor("offset"),
|
||||
@ -245,50 +239,67 @@ void DisassemblerGraphView::loadCurrentGraph()
|
||||
}
|
||||
db.true_path = RVA_INVALID;
|
||||
db.false_path = RVA_INVALID;
|
||||
if (block_fail) {
|
||||
db.false_path = block_fail;
|
||||
gb.edges.emplace_back(block_fail);
|
||||
if (bbiFail) {
|
||||
db.false_path = bbiFail;
|
||||
gb.edges.emplace_back(bbiFail);
|
||||
}
|
||||
if (block_jump) {
|
||||
if (block_fail) {
|
||||
db.true_path = block_jump;
|
||||
if (bbiJump) {
|
||||
if (bbiFail) {
|
||||
db.true_path = bbiJump;
|
||||
}
|
||||
gb.edges.emplace_back(block_jump);
|
||||
gb.edges.emplace_back(bbiJump);
|
||||
}
|
||||
|
||||
CutterJson switchOp = block["switchop"];
|
||||
if (switchOp.size()) {
|
||||
for (CutterJson caseOp : switchOp["cases"]) {
|
||||
RVA caseJump = caseOp["jump"].toRVA();
|
||||
if (caseJump == RVA_INVALID) {
|
||||
RzAnalysisSwitchOp *switchOp = bbi->switch_op;
|
||||
if (switchOp) {
|
||||
for (const auto &caseOp : CutterRzList<RzAnalysisCaseOp>(switchOp->cases)) {
|
||||
if (caseOp->jump == RVA_INVALID) {
|
||||
continue;
|
||||
}
|
||||
gb.edges.emplace_back(caseJump);
|
||||
gb.edges.emplace_back(caseOp->jump);
|
||||
}
|
||||
}
|
||||
|
||||
CutterJson opArray = block["ops"];
|
||||
CutterJson::iterator iterator = opArray.begin();
|
||||
while (iterator != opArray.end()) {
|
||||
CutterJson op = *iterator;
|
||||
Instr i;
|
||||
i.addr = op["offset"].toUt64();
|
||||
RzCoreLocked core(Core());
|
||||
std::unique_ptr<ut8[]> buf { new ut8[bbi->size] };
|
||||
if (!buf) {
|
||||
break;
|
||||
}
|
||||
rz_io_read_at(core->io, bbi->addr, buf.get(), (int)bbi->size);
|
||||
|
||||
++iterator;
|
||||
auto vec = fromOwned(
|
||||
rz_pvector_new(reinterpret_cast<RzPVectorFree>(rz_analysis_disasm_text_free)));
|
||||
if (!vec) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (iterator != opArray.end()) {
|
||||
RzCoreDisasmOptions options = {};
|
||||
options.vec = vec.get();
|
||||
options.cbytes = 1;
|
||||
rz_core_print_disasm(core, bbi->addr, buf.get(), (int)bbi->size, (int)bbi->size, NULL,
|
||||
&options);
|
||||
|
||||
auto vecVisitor = CutterPVector<RzAnalysisDisasmText>(vec.get());
|
||||
auto iter = vecVisitor.begin();
|
||||
while (iter != vecVisitor.end()) {
|
||||
RzAnalysisDisasmText *op = *iter;
|
||||
Instr instr;
|
||||
instr.addr = op->offset;
|
||||
|
||||
++iter;
|
||||
if (iter != vecVisitor.end()) {
|
||||
// get instruction size from distance to next instruction ...
|
||||
RVA nextOffset = (*iterator)["offset"].toRVA();
|
||||
i.size = nextOffset - i.addr;
|
||||
RVA nextOffset = (*iter)->offset;
|
||||
instr.size = nextOffset - instr.addr;
|
||||
} else {
|
||||
// or to the end of the block.
|
||||
i.size = (block_entry + block_size) - i.addr;
|
||||
instr.size = (bbi->addr + bbi->size) - instr.addr;
|
||||
}
|
||||
|
||||
QTextDocument textDoc;
|
||||
textDoc.setHtml(CutterCore::ansiEscapeToHtml(op["text"].toString()));
|
||||
textDoc.setHtml(CutterCore::ansiEscapeToHtml(op->text));
|
||||
|
||||
i.plainText = textDoc.toPlainText();
|
||||
instr.plainText = textDoc.toPlainText();
|
||||
|
||||
RichTextPainter::List richText = RichTextPainter::fromTextDocument(textDoc);
|
||||
// Colors::colorizeAssembly(richText, textDoc.toPlainText(), 0);
|
||||
@ -296,23 +307,19 @@ void DisassemblerGraphView::loadCurrentGraph()
|
||||
bool cropped;
|
||||
int blockLength = Config()->getGraphBlockMaxChars()
|
||||
+ Core()->getConfigb("asm.bytes") * 24 + Core()->getConfigb("asm.emu") * 10;
|
||||
i.text = Text(RichTextPainter::cropped(richText, blockLength, "...", &cropped));
|
||||
instr.text = Text(RichTextPainter::cropped(richText, blockLength, "...", &cropped));
|
||||
if (cropped)
|
||||
i.fullText = richText;
|
||||
instr.fullText = richText;
|
||||
else
|
||||
i.fullText = Text();
|
||||
db.instrs.push_back(i);
|
||||
instr.fullText = Text();
|
||||
db.instrs.push_back(instr);
|
||||
}
|
||||
disassembly_blocks[db.entry] = db;
|
||||
prepareGraphNode(gb);
|
||||
|
||||
addBlock(gb);
|
||||
}
|
||||
cleanupEdges(blocks);
|
||||
|
||||
if (func["blocks"].size()) {
|
||||
computeGraphPlacement();
|
||||
}
|
||||
computeGraphPlacement();
|
||||
}
|
||||
|
||||
DisassemblerGraphView::EdgeConfigurationMapping DisassemblerGraphView::getEdgeConfigurations()
|
||||
@ -365,8 +372,6 @@ void DisassemblerGraphView::drawBlock(QPainter &p, GraphView::GraphBlock &block,
|
||||
p.setFont(Config()->getFont());
|
||||
p.drawRect(blockRect);
|
||||
|
||||
breakpoints = Core()->getBreakpointsAddresses();
|
||||
|
||||
// Render node
|
||||
DisassemblyBlock &db = disassembly_blocks[block.entry];
|
||||
bool block_selected = false;
|
||||
@ -887,6 +892,11 @@ void DisassemblerGraphView::contextMenuEvent(QContextMenuEvent *event)
|
||||
|
||||
void DisassemblerGraphView::showExportDialog()
|
||||
{
|
||||
if (currentFcnAddr == RVA_INVALID) {
|
||||
qWarning() << "Cannot find current function.";
|
||||
return;
|
||||
}
|
||||
|
||||
QString defaultName = "graph";
|
||||
if (auto f = Core()->functionIn(currentFcnAddr)) {
|
||||
QString functionName = f->name;
|
||||
@ -897,7 +907,7 @@ void DisassemblerGraphView::showExportDialog()
|
||||
defaultName = functionName;
|
||||
}
|
||||
}
|
||||
showExportGraphDialog(defaultName, "agf", currentFcnAddr);
|
||||
showExportGraphDialog(defaultName, RZ_CORE_GRAPH_TYPE_BLOCK_FUN, currentFcnAddr);
|
||||
}
|
||||
|
||||
void DisassemblerGraphView::blockDoubleClicked(GraphView::GraphBlock &block, QMouseEvent *event,
|
||||
|
@ -211,7 +211,7 @@ QString DisassemblyWidget::getWidgetType()
|
||||
|
||||
QFontMetrics DisassemblyWidget::getFontMetrics()
|
||||
{
|
||||
return mDisasTextEdit->fontMetrics();
|
||||
return QFontMetrics(mDisasTextEdit->font());
|
||||
}
|
||||
|
||||
QList<DisassemblyLine> DisassemblyWidget::getLines()
|
||||
|
@ -231,7 +231,15 @@ QVariant FunctionModel::data(const QModelIndex &index, int role) const
|
||||
|
||||
QStringList disasmPreview =
|
||||
Core()->getDisassemblyPreview(function.offset, kMaxTooltipDisasmPreviewLines);
|
||||
const QStringList &summary = Core()->cmdList(QString("pdsf @ %1").arg(function.offset));
|
||||
QStringList summary {};
|
||||
{
|
||||
auto seeker = Core()->seekTemp(function.offset);
|
||||
auto strings = fromOwnedCharPtr(
|
||||
rz_core_print_disasm_strings(Core()->core(), RZ_CORE_DISASM_STRINGS_MODE_FUNCTION,
|
||||
0, NULL));
|
||||
summary = strings.split('\n', CUTTER_QT_SKIP_EMPTY_PARTS);
|
||||
}
|
||||
|
||||
const QFont &fnt = Config()->getFont();
|
||||
QFontMetrics fm { fnt };
|
||||
|
||||
@ -245,7 +253,7 @@ QVariant FunctionModel::data(const QModelIndex &index, int role) const
|
||||
}
|
||||
}
|
||||
if (disasmPreview.isEmpty() && highlights.isEmpty())
|
||||
return QVariant();
|
||||
return {};
|
||||
|
||||
QString toolTipContent =
|
||||
QString("<html><div style=\"font-family: %1; font-size: %2pt; white-space: "
|
||||
@ -287,7 +295,7 @@ QVariant FunctionModel::data(const QModelIndex &index, int role) const
|
||||
return importAddresses->contains(function.offset);
|
||||
|
||||
default:
|
||||
return QVariant();
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
@ -564,7 +572,18 @@ void FunctionsWidget::refreshTree()
|
||||
importAddresses.insert(import.plt);
|
||||
}
|
||||
|
||||
mainAdress = (ut64)Core()->cmdj("iMj")["vaddr"].toUt64();
|
||||
mainAdress = RVA_INVALID;
|
||||
RzCoreLocked core(Core());
|
||||
RzBinFile *bf = rz_bin_cur(core->bin);
|
||||
if (bf) {
|
||||
const RzBinAddr *binmain =
|
||||
rz_bin_object_get_special_symbol(bf->o, RZ_BIN_SPECIAL_SYMBOL_MAIN);
|
||||
if (binmain) {
|
||||
int va = core->io->va || core->bin->is_debugger;
|
||||
mainAdress = va ? rz_bin_object_addr_with_base(bf->o, binmain->vaddr)
|
||||
: binmain->paddr;
|
||||
}
|
||||
}
|
||||
|
||||
functionModel->updateCurrentIndex();
|
||||
functionModel->endResetModel();
|
||||
|
@ -703,7 +703,10 @@ void GraphView::mouseDoubleClickEvent(QMouseEvent *event)
|
||||
|
||||
void GraphView::keyPressEvent(QKeyEvent *event)
|
||||
{
|
||||
// for scrolling with arrow keys
|
||||
const int delta = static_cast<int>(30.0 / current_scale);
|
||||
// for scrolling with pgup/pgdown keys
|
||||
const int delta2 = static_cast<int>(100.0 / current_scale);
|
||||
int dx = 0, dy = 0;
|
||||
switch (event->key()) {
|
||||
case Qt::Key_Up:
|
||||
@ -718,6 +721,12 @@ void GraphView::keyPressEvent(QKeyEvent *event)
|
||||
case Qt::Key_Right:
|
||||
dx = delta;
|
||||
break;
|
||||
case Qt::Key_PageUp:
|
||||
dy = -delta2;
|
||||
break;
|
||||
case Qt::Key_PageDown:
|
||||
dy = delta2;
|
||||
break;
|
||||
default:
|
||||
QAbstractScrollArea::keyPressEvent(event);
|
||||
return;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -14,7 +14,7 @@ struct BasicCursor
|
||||
{
|
||||
uint64_t address;
|
||||
bool pastEnd;
|
||||
BasicCursor(uint64_t pos) : address(pos), pastEnd(false) {}
|
||||
explicit BasicCursor(uint64_t pos) : address(pos), pastEnd(false) {}
|
||||
BasicCursor() : address(0), pastEnd(false) {}
|
||||
BasicCursor &operator+=(int64_t offset)
|
||||
{
|
||||
@ -35,6 +35,14 @@ struct BasicCursor
|
||||
*this += int64_t(offset);
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool moveChecked(int offset)
|
||||
{
|
||||
auto oldAddress = address;
|
||||
*this += offset;
|
||||
return address - oldAddress == uint64_t(offset);
|
||||
}
|
||||
|
||||
BasicCursor &operator+=(uint64_t offset)
|
||||
{
|
||||
if (uint64_t(offset) > (UINT64_MAX - address)) {
|
||||
@ -46,7 +54,10 @@ struct BasicCursor
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
bool operator<(const BasicCursor &r) { return address < r.address || (pastEnd < r.pastEnd); }
|
||||
bool operator<(const BasicCursor &r) const
|
||||
{
|
||||
return address < r.address || (pastEnd < r.pastEnd);
|
||||
}
|
||||
};
|
||||
|
||||
struct HexCursor
|
||||
@ -74,9 +85,10 @@ struct HexCursor
|
||||
class AbstractData
|
||||
{
|
||||
public:
|
||||
virtual ~AbstractData() {}
|
||||
virtual ~AbstractData() = default;
|
||||
virtual void fetch(uint64_t addr, int len) = 0;
|
||||
virtual bool copy(void *out, uint64_t adr, size_t len) = 0;
|
||||
virtual bool write(const uint8_t *in, uint64_t adr, size_t len) = 0;
|
||||
virtual uint64_t maxIndex() = 0;
|
||||
virtual uint64_t minIndex() = 0;
|
||||
};
|
||||
@ -86,7 +98,7 @@ class BufferData : public AbstractData
|
||||
public:
|
||||
BufferData() { m_buffer.fill(0, 1); }
|
||||
|
||||
BufferData(const QByteArray &buffer)
|
||||
explicit BufferData(const QByteArray &buffer)
|
||||
{
|
||||
if (buffer.isEmpty()) {
|
||||
m_buffer.fill(0, 1);
|
||||
@ -95,7 +107,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
~BufferData() override {}
|
||||
~BufferData() override = default;
|
||||
|
||||
void fetch(uint64_t, int) override {}
|
||||
|
||||
@ -109,6 +121,16 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
bool write(const uint8_t *in, uint64_t addr, size_t len) override
|
||||
{
|
||||
if (addr < static_cast<uint64_t>(m_buffer.size())
|
||||
&& (static_cast<uint64_t>(m_buffer.size()) - addr) < len) {
|
||||
memcpy(m_buffer.data() + addr, in, len);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t maxIndex() override { return m_buffer.size() - 1; }
|
||||
|
||||
private:
|
||||
@ -118,8 +140,8 @@ private:
|
||||
class MemoryData : public AbstractData
|
||||
{
|
||||
public:
|
||||
MemoryData() {}
|
||||
~MemoryData() override {}
|
||||
MemoryData() = default;
|
||||
~MemoryData() override = default;
|
||||
static constexpr size_t BLOCK_SIZE = 4096;
|
||||
|
||||
void fetch(uint64_t address, int length) override
|
||||
@ -144,10 +166,11 @@ public:
|
||||
|
||||
bool copy(void *out, uint64_t addr, size_t len) override
|
||||
{
|
||||
if (addr < m_firstBlockAddr || addr > m_lastValidAddr
|
||||
|| (m_lastValidAddr - addr + 1)
|
||||
< len /* do not merge with last check to handle overflows */
|
||||
|| m_blocks.isEmpty()) {
|
||||
if (addr < m_firstBlockAddr
|
||||
|| addr > m_lastValidAddr
|
||||
/* do not merge with previous check to handle overflows */
|
||||
|| (m_lastValidAddr - addr + 1) < len || m_blocks.isEmpty()) {
|
||||
memset(out, 0xff, len);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -165,9 +188,47 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual uint64_t maxIndex() override { return m_lastValidAddr; }
|
||||
void writeToCache(const uint8_t *in, uint64_t adr, size_t len)
|
||||
{
|
||||
if (adr < m_firstBlockAddr) {
|
||||
uint64_t prefix = m_firstBlockAddr - adr;
|
||||
if (prefix <= len) {
|
||||
return;
|
||||
}
|
||||
in = in + prefix;
|
||||
adr += prefix;
|
||||
len -= prefix;
|
||||
}
|
||||
if (adr > m_lastValidAddr) {
|
||||
return;
|
||||
}
|
||||
int offset = (int)(adr - m_firstBlockAddr);
|
||||
int blockId = offset / BLOCK_SIZE;
|
||||
int blockOffset = offset % BLOCK_SIZE;
|
||||
while (len > 0 && blockId < m_blocks.size()) {
|
||||
size_t l = BLOCK_SIZE - blockOffset;
|
||||
l = std::min(l, len);
|
||||
memcpy(m_blocks[blockId].data() + blockOffset, in, l);
|
||||
len -= l;
|
||||
blockOffset = 0;
|
||||
adr += l;
|
||||
in += l;
|
||||
blockId += 1;
|
||||
}
|
||||
}
|
||||
|
||||
virtual uint64_t minIndex() override { return m_firstBlockAddr; }
|
||||
bool write(const uint8_t *in, uint64_t adr, size_t len) override
|
||||
{
|
||||
RzCoreLocked core(Core());
|
||||
rz_core_write_at(core, adr, in, len);
|
||||
writeToCache(in, adr, len);
|
||||
emit Core()->instructionChanged(adr);
|
||||
return true;
|
||||
}
|
||||
|
||||
uint64_t maxIndex() override { return std::numeric_limits<uint64_t>::max(); }
|
||||
|
||||
uint64_t minIndex() override { return m_firstBlockAddr; }
|
||||
|
||||
private:
|
||||
QVector<QByteArray> m_blocks;
|
||||
@ -178,7 +239,11 @@ private:
|
||||
class HexSelection
|
||||
{
|
||||
public:
|
||||
HexSelection() { m_empty = true; }
|
||||
HexSelection()
|
||||
{
|
||||
m_empty = true;
|
||||
m_start = m_end = 0;
|
||||
}
|
||||
|
||||
inline void init(BasicCursor addr)
|
||||
{
|
||||
@ -189,7 +254,8 @@ public:
|
||||
void set(uint64_t start, uint64_t end)
|
||||
{
|
||||
m_empty = false;
|
||||
m_init = m_start = start;
|
||||
m_init = BasicCursor(start);
|
||||
m_start = start;
|
||||
m_end = end;
|
||||
}
|
||||
|
||||
@ -219,7 +285,7 @@ public:
|
||||
|
||||
bool contains(uint64_t pos) const { return !m_empty && m_start <= pos && pos <= m_end; }
|
||||
|
||||
uint64_t size()
|
||||
uint64_t size() const
|
||||
{
|
||||
uint64_t size = 0;
|
||||
if (!isEmpty())
|
||||
@ -227,9 +293,9 @@ public:
|
||||
return size;
|
||||
}
|
||||
|
||||
inline bool isEmpty() { return m_empty; }
|
||||
inline uint64_t start() { return m_start; }
|
||||
inline uint64_t end() { return m_end; }
|
||||
inline bool isEmpty() const { return m_empty; }
|
||||
inline uint64_t start() const { return m_start; }
|
||||
inline uint64_t end() const { return m_end; }
|
||||
|
||||
private:
|
||||
BasicCursor m_init;
|
||||
@ -244,7 +310,7 @@ class HexWidget : public QScrollArea
|
||||
|
||||
public:
|
||||
explicit HexWidget(QWidget *parent = nullptr);
|
||||
~HexWidget();
|
||||
~HexWidget() override = default;
|
||||
|
||||
void setMonospaceFont(const QFont &font);
|
||||
|
||||
@ -258,10 +324,12 @@ public:
|
||||
ItemFormatFloat
|
||||
};
|
||||
enum class ColumnMode { Fixed, PowerOf2 };
|
||||
enum class EditWordState { Read, WriteNotStarted, WriteNotEdited, WriteEdited };
|
||||
enum class HexNavigationMode { Words, WordChar, AnyChar };
|
||||
|
||||
void setItemSize(int nbytes);
|
||||
void setItemFormat(ItemFormat format);
|
||||
void setItemEndianess(bool bigEndian);
|
||||
void setItemEndianness(bool bigEndian);
|
||||
void setItemGroupSize(int size);
|
||||
/**
|
||||
* @brief Sets line size in bytes.
|
||||
@ -292,7 +360,7 @@ public slots:
|
||||
void refresh();
|
||||
void updateColors();
|
||||
signals:
|
||||
void selectionChanged(Selection selection);
|
||||
void selectionChanged(HexWidget::Selection selection);
|
||||
void positionChanged(RVA start);
|
||||
|
||||
protected:
|
||||
@ -300,10 +368,12 @@ protected:
|
||||
void resizeEvent(QResizeEvent *event) override;
|
||||
void mouseMoveEvent(QMouseEvent *event) override;
|
||||
void mousePressEvent(QMouseEvent *event) override;
|
||||
void mouseDoubleClickEvent(QMouseEvent *event) override;
|
||||
void mouseReleaseEvent(QMouseEvent *event) override;
|
||||
void wheelEvent(QWheelEvent *event) override;
|
||||
void keyPressEvent(QKeyEvent *event) override;
|
||||
void contextMenuEvent(QContextMenuEvent *event) override;
|
||||
bool event(QEvent *event) override;
|
||||
|
||||
private slots:
|
||||
void onCursorBlinked();
|
||||
@ -311,13 +381,13 @@ private slots:
|
||||
void copy();
|
||||
void copyAddress();
|
||||
void onRangeDialogAccepted();
|
||||
void on_actionAddComment_triggered();
|
||||
void on_actionDeleteComment_triggered();
|
||||
void onActionAddCommentTriggered();
|
||||
void onActionDeleteCommentTriggered();
|
||||
|
||||
// Write command slots
|
||||
void w_writeString();
|
||||
void w_increaseDecrease();
|
||||
void w_writeBytes();
|
||||
void w_writeBytes();
|
||||
void w_writeZeros();
|
||||
void w_write64();
|
||||
void w_writeRandom();
|
||||
@ -326,6 +396,9 @@ private slots:
|
||||
void w_writeWideString();
|
||||
void w_writeCString();
|
||||
|
||||
void onKeyboardEditTriggered(bool enabled);
|
||||
void onKeyboardEditChanged(bool enabled);
|
||||
|
||||
private:
|
||||
void updateItemLength();
|
||||
void updateCounts();
|
||||
@ -338,12 +411,15 @@ private:
|
||||
void updateMetrics();
|
||||
void updateAreasPosition();
|
||||
void updateAreasHeight();
|
||||
void moveCursor(int offset, bool select = false);
|
||||
enum class OverflowMove { Clamp, Ignore };
|
||||
bool moveCursor(int offset, bool select = false,
|
||||
OverflowMove overflowMove = OverflowMove::Clamp);
|
||||
void moveCursorKeepEditOffset(int byteOffset, bool select, OverflowMove overflowMove);
|
||||
void setCursorAddr(BasicCursor addr, bool select = false);
|
||||
void updateCursorMeta();
|
||||
void setCursorOnAscii(bool ascii);
|
||||
bool isItemDifferentAt(uint64_t address);
|
||||
const QColor itemColor(uint8_t byte);
|
||||
QColor itemColor(uint8_t byte);
|
||||
QVariant readItem(int offset, QColor *color = nullptr);
|
||||
QString renderItem(int offset, QColor *color = nullptr);
|
||||
QChar renderAscii(int offset, QColor *color = nullptr);
|
||||
@ -359,12 +435,13 @@ private:
|
||||
/**
|
||||
* @brief Convert mouse position to address.
|
||||
* @param point mouse position in widget
|
||||
* @param middle start next position from middle of symbol. Use middle=true for vertical cursror
|
||||
* @param middle start next position from middle of symbol. Use middle=true for vertical cursor
|
||||
* position between symbols, middle=false for insert mode cursor and getting symbol under
|
||||
* cursor.
|
||||
* @return
|
||||
*/
|
||||
BasicCursor screenPosToAddr(const QPoint &point, bool middle = false) const;
|
||||
BasicCursor screenPosToAddr(const QPoint &point, bool middle = false,
|
||||
int *wordOffset = nullptr) const;
|
||||
BasicCursor asciiPosToAddr(const QPoint &point, bool middle = false) const;
|
||||
BasicCursor currentAreaPosToAddr(const QPoint &point, bool middle = false) const;
|
||||
BasicCursor mousePosToAddr(const QPoint &point, bool middle = false) const;
|
||||
@ -412,6 +489,27 @@ private:
|
||||
inline uint64_t lastVisibleAddr() const { return (startAddress - 1) + bytesPerScreen(); }
|
||||
|
||||
const QRectF ¤tArea() const { return cursorOnAscii ? asciiArea : itemArea; }
|
||||
bool isFixedWidth() const;
|
||||
|
||||
bool canKeyboardEdit();
|
||||
bool flushCurrentlyEditedWord();
|
||||
bool finishEditingWord(bool force = true);
|
||||
void maybeFlushCharEdit();
|
||||
void cancelEditedWord();
|
||||
void startEditWord();
|
||||
bool validCharForEdit(QChar digit);
|
||||
void movePrevEditCharAny();
|
||||
void typeOverwriteModeChar(QChar c);
|
||||
HexNavigationMode defaultNavigationMode();
|
||||
void refreshWordEditState();
|
||||
bool parseWord(QString word, uint8_t *buf, size_t bufferSize) const;
|
||||
bool handleAsciiWrite(QKeyEvent *event);
|
||||
bool handleNumberWrite(QKeyEvent *event);
|
||||
|
||||
void writeZeros(uint64_t address, uint64_t length);
|
||||
|
||||
void hideWarningRect();
|
||||
void showWarningRect(QRectF rect);
|
||||
|
||||
bool cursorEnabled;
|
||||
bool cursorOnAscii;
|
||||
@ -436,14 +534,13 @@ private:
|
||||
ItemFormat itemFormat;
|
||||
|
||||
bool itemBigEndian;
|
||||
QString itemPrefix;
|
||||
|
||||
int visibleLines;
|
||||
uint64_t startAddress;
|
||||
qreal charWidth;
|
||||
int byteWidth;
|
||||
qreal lineHeight;
|
||||
int addrCharLen;
|
||||
int addrAreaWidth;
|
||||
QFont monospaceFont;
|
||||
|
||||
bool showHeader;
|
||||
@ -460,6 +557,7 @@ private:
|
||||
QColor b0x7fColor;
|
||||
QColor b0xffColor;
|
||||
QColor printableColor;
|
||||
QColor warningColor;
|
||||
|
||||
HexdumpRangeDialog rangeDialog;
|
||||
|
||||
@ -479,14 +577,30 @@ private:
|
||||
QAction *actionCopyAddress;
|
||||
QAction *actionComment;
|
||||
QAction *actionDeleteComment;
|
||||
QAction *actionSetFlag;
|
||||
QAction *actionSelectRange;
|
||||
QAction *actionKeyboardEdit;
|
||||
QList<QAction *> actionsWriteString;
|
||||
QList<QAction *> actionsWriteOther;
|
||||
|
||||
std::unique_ptr<AbstractData> oldData;
|
||||
std::unique_ptr<AbstractData> data;
|
||||
IOModesController ioModesController;
|
||||
|
||||
int editWordPos = 0;
|
||||
QString editWord;
|
||||
EditWordState editWordState = EditWordState::Read;
|
||||
HexNavigationMode navigationMode = HexNavigationMode::Words;
|
||||
enum class EarlyEditFlush {
|
||||
OnFinish,
|
||||
EditNibble,
|
||||
EditFixedWidthChar,
|
||||
/* AllFormats(not implemented) */
|
||||
};
|
||||
EarlyEditFlush earlyEditFlush = EarlyEditFlush::EditFixedWidthChar;
|
||||
|
||||
bool warningRectVisible = false;
|
||||
QRectF warningRect;
|
||||
QTimer warningTimer;
|
||||
};
|
||||
|
||||
#endif // HEXWIDGET_H
|
||||
|
@ -144,7 +144,6 @@ void HexdumpWidget::initParsing()
|
||||
ui->parseTypeComboBox->addItem(tr("String"), "pcs");
|
||||
ui->parseTypeComboBox->addItem(tr("Assembler"), "pca");
|
||||
ui->parseTypeComboBox->addItem(tr("C bytes"), "pc");
|
||||
ui->parseTypeComboBox->addItem(tr("C bytes with instructions"), "pci");
|
||||
ui->parseTypeComboBox->addItem(tr("C half-words (2 byte)"), "pch");
|
||||
ui->parseTypeComboBox->addItem(tr("C words (4 byte)"), "pcw");
|
||||
ui->parseTypeComboBox->addItem(tr("C dwords (8 byte)"), "pcd");
|
||||
@ -240,28 +239,28 @@ void HexdumpWidget::updateParseWindow(RVA start_address, int size)
|
||||
|
||||
ui->hexDisasTextEdit->setPlainText(
|
||||
selectedCommand != "" ? Core()->cmdRawAt(
|
||||
QString("%1 %2").arg(selectedCommand).arg(size), start_address)
|
||||
QString("%1 @! %2").arg(selectedCommand).arg(size), start_address)
|
||||
: "");
|
||||
} else {
|
||||
// Fill the information tab hashes and entropy
|
||||
RzMsgDigestSize digest_size = 0;
|
||||
RzHashSize digest_size = 0;
|
||||
RzCoreLocked core(Core());
|
||||
ut64 old_offset = core->offset;
|
||||
rz_core_seek(core, start_address, true);
|
||||
ut8 *block = core->block;
|
||||
char *digest = rz_msg_digest_calculate_small_block_string("md5", block, size, &digest_size, false);
|
||||
char *digest = rz_hash_cfg_calculate_small_block_string(core->hash, "md5", block, size, &digest_size, false);
|
||||
ui->bytesMD5->setText(QString(digest));
|
||||
free(digest);
|
||||
digest = rz_msg_digest_calculate_small_block_string("sha1", block, size, &digest_size, false);
|
||||
digest = rz_hash_cfg_calculate_small_block_string(core->hash, "sha1", block, size, &digest_size, false);
|
||||
ui->bytesSHA1->setText(QString(digest));
|
||||
free(digest);
|
||||
digest = rz_msg_digest_calculate_small_block_string("sha256", block, size, &digest_size, false);
|
||||
digest = rz_hash_cfg_calculate_small_block_string(core->hash, "sha256", block, size, &digest_size, false);
|
||||
ui->bytesSHA256->setText(QString(digest));
|
||||
free(digest);
|
||||
digest = rz_msg_digest_calculate_small_block_string("crc32", block, size, &digest_size, false);
|
||||
digest = rz_hash_cfg_calculate_small_block_string(core->hash, "crc32", block, size, &digest_size, false);
|
||||
ui->bytesCRC32->setText(QString(digest));
|
||||
free(digest);
|
||||
digest = rz_msg_digest_calculate_small_block_string("entropy", block, size, &digest_size, false);
|
||||
digest = rz_hash_cfg_calculate_small_block_string(core->hash, "entropy", block, size, &digest_size, false);
|
||||
ui->bytesEntropy->setText(QString(digest));
|
||||
free(digest);
|
||||
rz_core_seek(core, old_offset, true);
|
||||
|
@ -28,6 +28,7 @@ private:
|
||||
const QRegularExpression banned = QRegularExpression(
|
||||
QStringLiteral("\\A(\\w\\.)*(system|strcpy|strcpyA|strcpyW|wcscpy|_tcscpy|_mbscpy|"
|
||||
"StrCpy|StrCpyA|StrCpyW|lstrcpy|lstrcpyA|lstrcpyW"
|
||||
"DCIEnum|DCIOpenProvider|DCISendCommand|DCIBeginAccess"
|
||||
"|_tccpy|_mbccpy|_ftcscpy|strcat|strcatA|strcatW|wcscat|_tcscat|_mbscat|"
|
||||
"StrCat|StrCatA|StrCatW|lstrcat|lstrcatA|"
|
||||
"lstrcatW|StrCatBuff|StrCatBuffA|StrCatBuffW|StrCatChainW|_tccat|_"
|
||||
@ -51,7 +52,7 @@ private:
|
||||
"ui64tow|_ultoa|_ultot|_ultow|CharToOem|CharToOemA|CharToOemW|"
|
||||
"OemToChar|OemToCharA|OemToCharW|CharToOemBuffA|CharToOemBuffW|alloca|_"
|
||||
"alloca|strlen|wcslen|_mbslen|_mbstrlen|StrLen|lstrlen|"
|
||||
"ChangeWindowMessageFilter)\\z"));
|
||||
"ChangeWindowMessageFilter|ChangeWindowMessageFilterEx)\\z"));
|
||||
QList<ImportDescription> imports;
|
||||
|
||||
public:
|
||||
|
@ -7,7 +7,7 @@
|
||||
#include <QAction>
|
||||
|
||||
/* Disassembly/Graph/Hexdump/Decompiler view priority */
|
||||
enum class MemoryWidgetType { Disassembly, Graph, Hexdump, Decompiler };
|
||||
enum class MemoryWidgetType { Disassembly, Graph, Hexdump, Decompiler, CallGraph, GlobalCallGraph };
|
||||
|
||||
class CUTTER_EXPORT MemoryDockWidget : public AddressableDockWidget
|
||||
{
|
||||
|
@ -7,10 +7,16 @@ QuickFilterView::QuickFilterView(QWidget *parent, bool defaultOn)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
debounceTimer = new QTimer(this);
|
||||
debounceTimer->setSingleShot(true);
|
||||
|
||||
connect(ui->closeFilterButton, &QAbstractButton::clicked, this, &QuickFilterView::closeFilter);
|
||||
|
||||
connect(debounceTimer, &QTimer::timeout, this,
|
||||
[this]() { emit filterTextChanged(ui->filterLineEdit->text()); });
|
||||
|
||||
connect(ui->filterLineEdit, &QLineEdit::textChanged, this,
|
||||
[this](const QString &text) { emit filterTextChanged(text); });
|
||||
[this](const QString &text) { debounceTimer->start(150); });
|
||||
|
||||
if (!defaultOn) {
|
||||
closeFilter();
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include <memory>
|
||||
|
||||
#include <QWidget>
|
||||
#include <QTimer>
|
||||
|
||||
namespace Ui {
|
||||
class QuickFilterView;
|
||||
@ -31,6 +32,7 @@ signals:
|
||||
|
||||
private:
|
||||
std::unique_ptr<Ui::QuickFilterView> ui;
|
||||
QTimer *debounceTimer;
|
||||
};
|
||||
|
||||
#endif // QUICKFILTERVIEW_H
|
||||
|
@ -254,6 +254,7 @@ void SearchWidget::refreshSearchspaces()
|
||||
ui->searchspaceCombo->clear();
|
||||
ui->searchspaceCombo->addItem(tr("asm code"), QVariant("/acj"));
|
||||
ui->searchspaceCombo->addItem(tr("string"), QVariant("/j"));
|
||||
ui->searchspaceCombo->addItem(tr("string (case insensitive)"), QVariant("/ij"));
|
||||
ui->searchspaceCombo->addItem(tr("hex string"), QVariant("/xj"));
|
||||
ui->searchspaceCombo->addItem(tr("ROP gadgets"), QVariant("/Rj"));
|
||||
ui->searchspaceCombo->addItem(tr("32bit value"), QVariant("/vj"));
|
||||
@ -301,13 +302,16 @@ void SearchWidget::updatePlaceholderText(int index)
|
||||
case 1: // string
|
||||
ui->filterLineEdit->setPlaceholderText("foobar");
|
||||
break;
|
||||
case 2: // hex string
|
||||
case 2: // string (case insensitive)
|
||||
ui->filterLineEdit->setPlaceholderText("FooBar");
|
||||
break;
|
||||
case 3: // hex string
|
||||
ui->filterLineEdit->setPlaceholderText("deadbeef");
|
||||
break;
|
||||
case 3: // ROP gadgets
|
||||
case 4: // ROP gadgets
|
||||
ui->filterLineEdit->setPlaceholderText("pop,,pop");
|
||||
break;
|
||||
case 4: // 32bit value
|
||||
case 5: // 32bit value
|
||||
ui->filterLineEdit->setPlaceholderText("0xdeadbeef");
|
||||
break;
|
||||
default:
|
||||
|
@ -119,8 +119,9 @@ void SimpleTextGraphView::drawBlock(QPainter &p, GraphView::GraphBlock &block, b
|
||||
|
||||
p.setPen(palette().color(QPalette::WindowText));
|
||||
// Render node text
|
||||
auto x = block.x + padding;
|
||||
int y = block.y + padding + p.fontMetrics().ascent();
|
||||
QFontMetrics fm = QFontMetrics(p.font());
|
||||
auto x = block.x + padding / 2;
|
||||
int y = block.y + padding / 2 + fm.ascent();
|
||||
p.drawText(QPoint(x, y), content.text);
|
||||
}
|
||||
|
||||
|
@ -259,6 +259,10 @@ void TypesWidget::showTypesContextMenu(const QPoint &pt)
|
||||
|
||||
void TypesWidget::on_actionExport_Types_triggered()
|
||||
{
|
||||
char *str = rz_core_types_as_c_all(Core()->core(), true);
|
||||
if (!str) {
|
||||
return;
|
||||
}
|
||||
QString filename =
|
||||
QFileDialog::getSaveFileName(this, tr("Save File"), Config()->getRecentFolder());
|
||||
if (filename.isEmpty()) {
|
||||
@ -272,8 +276,8 @@ void TypesWidget::on_actionExport_Types_triggered()
|
||||
return;
|
||||
}
|
||||
QTextStream fileOut(&file);
|
||||
// TODO: use API for `tc` command once available
|
||||
fileOut << Core()->cmd("tc");
|
||||
fileOut << str;
|
||||
free(str);
|
||||
file.close();
|
||||
}
|
||||
|
||||
@ -289,7 +293,6 @@ void TypesWidget::on_actionLoad_New_Types_triggered()
|
||||
TypesInteractionDialog dialog(this);
|
||||
connect(&dialog, &TypesInteractionDialog::newTypesLoaded, this, &TypesWidget::refreshTypes);
|
||||
dialog.setWindowTitle(tr("Load New Types"));
|
||||
dialog.setTypeName(t.type);
|
||||
dialog.exec();
|
||||
}
|
||||
|
||||
|
@ -21,8 +21,7 @@ VisualNavbar::VisualNavbar(MainWindow *main, QWidget *parent)
|
||||
graphicsView(new QGraphicsView),
|
||||
seekGraphicsItem(nullptr),
|
||||
PCGraphicsItem(nullptr),
|
||||
main(main),
|
||||
stats(nullptr, rz_core_analysis_stats_free)
|
||||
main(main)
|
||||
{
|
||||
Q_UNUSED(parent);
|
||||
|
||||
@ -119,7 +118,7 @@ void VisualNavbar::fetchStats()
|
||||
|
||||
RzCoreLocked core(Core());
|
||||
stats.reset(nullptr);
|
||||
RzList *list = rz_core_get_boundaries_prot(core, -1, NULL, "search");
|
||||
auto list = fromOwned(rz_core_get_boundaries_prot(core, -1, NULL, "search"));
|
||||
if (!list) {
|
||||
return;
|
||||
}
|
||||
@ -127,7 +126,7 @@ void VisualNavbar::fetchStats()
|
||||
RzIOMap *map;
|
||||
ut64 from = UT64_MAX;
|
||||
ut64 to = 0;
|
||||
CutterRzListForeach (list, iter, RzIOMap, map) {
|
||||
CutterRzListForeach (list.get(), iter, RzIOMap, map) {
|
||||
ut64 f = rz_itv_begin(map->itv);
|
||||
ut64 t = rz_itv_end(map->itv);
|
||||
if (f < from) {
|
||||
@ -137,7 +136,6 @@ void VisualNavbar::fetchStats()
|
||||
to = t;
|
||||
}
|
||||
}
|
||||
rz_list_free(list);
|
||||
to--; // rz_core_analysis_get_stats takes inclusive ranges
|
||||
if (to < from) {
|
||||
return;
|
||||
|
@ -47,7 +47,7 @@ private:
|
||||
QGraphicsRectItem *PCGraphicsItem;
|
||||
MainWindow *main;
|
||||
|
||||
std::unique_ptr<RzCoreAnalysisStats, decltype(&rz_core_analysis_stats_free)> stats;
|
||||
UniquePtrC<RzCoreAnalysisStats, &rz_core_analysis_stats_free> stats;
|
||||
unsigned int statsWidth = 0;
|
||||
unsigned int previousWidth = 0;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user