Merge 'dev' branch into stable

This commit is contained in:
Anton Kochkov 2023-08-05 10:27:27 +08:00
commit 48b35953bb
109 changed files with 3254 additions and 632 deletions

View File

@ -1,4 +1,4 @@
version: '2.2.1-git-{build}'
version: '2.2.0-git-{build}'
image: 'Visual Studio 2017'
clone_depth: 1

View File

@ -219,6 +219,7 @@ jobs:
-DCUTTER_PACKAGE_JSDEC=ON \
-DCUTTER_PACKAGE_RZ_LIBSWIFT=ON \
-DCUTTER_PACKAGE_RZ_LIBYARA=ON \
-DCUTTER_PACKAGE_RZ_SILHOUETTE=ON \
-DCMAKE_INSTALL_PREFIX=appdir/usr \
-DCMAKE_INSTALL_RPATH_USE_LINK_PATH=ON \
..
@ -374,6 +375,7 @@ jobs:
-DCUTTER_PACKAGE_JSDEC=ON \
-DCUTTER_PACKAGE_RZ_LIBSWIFT=ON \
-DCUTTER_PACKAGE_RZ_LIBYARA=ON \
-DCUTTER_PACKAGE_RZ_SILHOUETTE=ON \
-DCPACK_PACKAGE_FILE_NAME="$PACKAGE_NAME" \
-DCPACK_BUNDLE_APPLE_CERT_APP="-" \
.. && \
@ -413,6 +415,7 @@ jobs:
-DCUTTER_PACKAGE_RZ_GHIDRA=ON ^
-DCUTTER_PACKAGE_RZ_LIBSWIFT=ON ^
-DCUTTER_PACKAGE_RZ_LIBYARA=ON ^
-DCUTTER_PACKAGE_RZ_SILHOUETTE=ON ^
-DCUTTER_PACKAGE_JSDEC=ON ^
-DCUTTER_ENABLE_DEPENDENCY_DOWNLOADS=ON ^
-DCMAKE_PREFIX_PATH="%CUTTER_DEPS%\pyside" ^

View File

@ -26,27 +26,34 @@ jobs:
- '**.c'
- '**.h'
- '.github/workflows/linter.yml'
- 'scripts/clang-format.py'
- '_clang-format'
clang-format:
needs: changes
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
if: ${{ needs.changes.outputs.clang-format == 'true' }}
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Install wget
run: sudo apt --assume-yes install wget
- name: Install wget, software-properties-common, lsb-release (dependencies of LLVM install script)
run: sudo apt --assume-yes install wget software-properties-common lsb-release
- name: Install automatic llvm (stable branch)
run: sudo bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)"
- name: Uninstall old conflicting packages
run: sudo apt purge --assume-yes --auto-remove llvm python3-lldb-14 llvm-14
- name: Install clang-format-11
run: sudo apt --assume-yes install clang-format-11
- name: Install automatic LLVM 16
run: wget https://apt.llvm.org/llvm.sh -O /tmp/llvm-install.sh; chmod +x /tmp/llvm-install.sh; sudo /tmp/llvm-install.sh 16
- name: Install clang-format-16
run: sudo apt --assume-yes install clang-format-16
- name: Install gitpython
run: sudo pip install gitpython
- name: Run clang-format
run: |
find ./src -regex '.*\.\(cpp\|h\|c\)' -exec clang-format -style=file --dry-run --Werror {} \;
sudo update-alternatives --install /usr/bin/clang-format clang-format /usr/bin/clang-format-16 160
clang-format --version
python scripts/clang-format.py --check --verbose

4
.gitignore vendored
View File

@ -94,3 +94,7 @@ docs/source/_build
# Local gdb files
.gdb_history
.gdbinit
# Kdevelop
.kdev/
*.kdev4

View File

@ -28,6 +28,7 @@ pipeline:
-DCUTTER_PACKAGE_JSDEC=ON
-DCUTTER_PACKAGE_RZ_LIBSWIFT=ON
-DCUTTER_PACKAGE_RZ_LIBYARA=ON
-DCUTTER_PACKAGE_RZ_SILHOUETTE=ON
-DCPACK_PACKAGE_FILE_NAME="$$PACKAGE_NAME"
-DCPACK_BUNDLE_APPLE_CERT_APP="-"
- ninja -C build

View File

@ -14,7 +14,7 @@ option(CUTTER_USE_BUNDLED_RIZIN "Use rizin from ./rizin submodule instead of sea
option(CUTTER_USE_ADDITIONAL_RIZIN_PATHS "Search rizin in additional paths which are not part of default system library paths.\
Disable this option if you are linking against rizin pacakged as proper system library or in a custom path and additional are paths causing problems." ON)
option(CUTTER_ENABLE_PYTHON "Enable Python integration. Requires Python >= ${CUTTER_PYTHON_MIN}." OFF)
option(CUTTER_ENABLE_PYTHON_BINDINGS "Enable generating Python bindings with Shiboken2. Unused if CUTTER_ENABLE_PYTHON=OFF." OFF)
option(CUTTER_ENABLE_PYTHON_BINDINGS "Enable generating Python bindings with Shiboken. Unused if CUTTER_ENABLE_PYTHON=OFF." OFF)
option(CUTTER_APPIMAGE_BUILD "Enable Appimage specific changes. Doesn't cause building of Appimage itself." OFF)
tri_option(CUTTER_ENABLE_KSYNTAXHIGHLIGHTING "Use KSyntaxHighlighting" AUTO)
tri_option(CUTTER_ENABLE_GRAPHVIZ "Enable use of graphviz for graph layout" AUTO)
@ -27,6 +27,7 @@ option(CUTTER_PACKAGE_DEPENDENCIES "During install step include the third party
option(CUTTER_PACKAGE_RZ_GHIDRA "Compile and install rz-ghidra during install step." OFF)
option(CUTTER_PACKAGE_RZ_LIBSWIFT "Compile and install rz-libswift demangler during the install step." OFF)
option(CUTTER_PACKAGE_RZ_LIBYARA "Compile and install rz-libyara during the install step." OFF)
option(CUTTER_PACKAGE_RZ_SILHOUETTE "Compile and install rz-silhouette during the install step." OFF)
option(CUTTER_PACKAGE_JSDEC "Compile and install jsdec during install step." OFF)
OPTION(CUTTER_QT6 "Use QT6" OFF)
@ -36,7 +37,7 @@ endif()
set(CUTTER_VERSION_MAJOR 2)
set(CUTTER_VERSION_MINOR 2)
set(CUTTER_VERSION_PATCH 1)
set(CUTTER_VERSION_PATCH 0)
set(CUTTER_VERSION "${CUTTER_VERSION_MAJOR}.${CUTTER_VERSION_MINOR}.${CUTTER_VERSION_PATCH}")
@ -99,27 +100,47 @@ if(CUTTER_ENABLE_PYTHON)
add_definitions(-DCUTTER_ENABLE_PYTHON)
if(CUTTER_ENABLE_PYTHON_BINDINGS)
# 5.12.3 => 5.12
if("${Qt5_VERSION}" MATCHES "^([0-9]+\\.[0-9]+)\\.[0-9]+")
set(Shiboken2_VERSION_REQUIRED "${CMAKE_MATCH_1}")
if (CUTTER_QT6)
# 6.12.3 => 6.12
if("${Qt6_VERSION}" MATCHES "^([0-9]+\\.[0-9]+)\\.[0-9]+")
set(Shiboken6_VERSION_REQUIRED "${CMAKE_MATCH_1}")
else()
message(FATAL_ERROR "Failed to recognize Qt version")
endif()
find_package(Shiboken6 "${Shiboken6_VERSION_REQUIRED}" REQUIRED)
find_package(Shiboken6Tools "${Shiboken6_VERSION_REQUIRED}" REQUIRED)
find_package(PySide6 "${Shiboken6_VERSION_REQUIRED}" REQUIRED)
get_target_property(LIBSHIBOKEN_INCLUDE_DIRS Shiboken6::libshiboken INTERFACE_INCLUDE_DIRECTORIES)
get_target_property(PYSIDE_INCLUDE_DIRS PySide6::pyside6 INTERFACE_INCLUDE_DIRECTORIES)
# Check the presence of "pysidecleanup.h"
include(CheckIncludeFileCXX)
set(CMAKE_REQUIRED_INCLUDES "${PYSIDE_INCLUDE_DIRS};${LIBSHIBOKEN_INCLUDE_DIRS}")
CHECK_INCLUDE_FILE_CXX("pysidecleanup.h" HAVE_PYSIDECLEANUP)
add_compile_definitions("HAVE_PYSIDECLEANUP=${HAVE_PYSIDECLEANUP}")
else()
message(FATAL_ERROR "Failed to recognize Qt version")
# 5.12.3 => 5.12
if("${Qt5_VERSION}" MATCHES "^([0-9]+\\.[0-9]+)\\.[0-9]+")
set(Shiboken2_VERSION_REQUIRED "${CMAKE_MATCH_1}")
else()
message(FATAL_ERROR "Failed to recognize Qt version")
endif()
find_package(Shiboken2 "${Shiboken2_VERSION_REQUIRED}" REQUIRED)
find_package(PySide2 "${Shiboken2_VERSION_REQUIRED}" REQUIRED)
get_target_property(PYSIDE_INCLUDE_DIRS PySide2::pyside2 INTERFACE_INCLUDE_DIRECTORIES)
endif()
find_package(Shiboken2 "${Shiboken2_VERSION_REQUIRED}" REQUIRED)
find_package(PySide2 "${Shiboken2_VERSION_REQUIRED}" REQUIRED)
get_target_property(PYSIDE_INCLUDE_DIR PySide2::pyside2 INTERFACE_INCLUDE_DIRECTORIES)
list(GET PYSIDE_INCLUDE_DIR 0 PYSIDE_INCLUDE_DIR)
include_directories(${PYSIDE_INCLUDE_DIR}
${PYSIDE_INCLUDE_DIR}/QtCore
${PYSIDE_INCLUDE_DIR}/QtGui
${PYSIDE_INCLUDE_DIR}/QtWidgets)
foreach(_dir IN LISTS PYSIDE_INCLUDE_DIRS)
include_directories(${_dir}
${_dir}/QtCore
${_dir}/QtGui
${_dir}/QtWidgets)
endforeach()
add_definitions(-DCUTTER_ENABLE_PYTHON_BINDINGS)
endif()
endif()
if(CUTTER_ENABLE_KSYNTAXHIGHLIGHTING)
if(CUTTER_ENABLE_KSYNTAXHIGHLIGHTING AND (NOT CUTTER_QT6))
if(CUTTER_ENABLE_KSYNTAXHIGHLIGHTING STREQUAL AUTO)
find_package(KF5SyntaxHighlighting)
if(KF5SyntaxHighlighting_FOUND)
@ -132,6 +153,9 @@ if(CUTTER_ENABLE_KSYNTAXHIGHLIGHTING)
set(KSYNTAXHIGHLIGHTING_STATUS ON)
endif()
else()
if(CUTTER_ENABLE_KSYNTAXHIGHLIGHTING AND CUTTER_QT6)
message(WARNING "KSyntaxHighlighting has been disabled because not supported in QT6")
endif()
set(KSYNTAXHIGHLIGHTING_STATUS OFF)
endif()
@ -161,6 +185,7 @@ message(STATUS "- Package Dependencies: ${CUTTER_PACKAGE_DEPENDENCIES}")
message(STATUS "- Package RzGhidra: ${CUTTER_PACKAGE_RZ_GHIDRA}")
message(STATUS "- Package RzLibSwift: ${CUTTER_PACKAGE_RZ_LIBSWIFT}")
message(STATUS "- Package RzLibYara: ${CUTTER_PACKAGE_RZ_LIBYARA}")
message(STATUS "- Package RzSilhouette: ${CUTTER_PACKAGE_RZ_SILHOUETTE}")
message(STATUS "- Package JSDec: ${CUTTER_PACKAGE_JSDEC}")
message(STATUS "- QT6: ${CUTTER_QT6}")
message(STATUS "")

View File

@ -57,7 +57,7 @@ endif()
# TODO: This version number should be fetched automatically
# instead of being hardcoded.
set (Rizin_VERSION 0.5)
set (Rizin_VERSION 0.6)
set (RZ_LIBS rz_core rz_config rz_cons rz_io rz_util rz_flag rz_asm rz_debug
rz_hash rz_bin rz_lang rz_il rz_analysis rz_parse rz_bp rz_egg rz_reg

View File

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

68
cmake/FindPySide6.cmake Normal file
View File

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

View File

@ -14,6 +14,7 @@ set(TS_FILES
translations/ro/cutter_ro.ts
translations/ru/cutter_ru.ts
translations/tr/cutter_tr.ts
translations/uk/cutter_uk.ts
translations/zh-CN/cutter_zh.ts
)
# translations/ko/cutter_ko.ts problems with fonts

30
dist/CMakeLists.txt vendored
View File

@ -7,7 +7,7 @@ unset(RZ_GHIDRA_PREFIX_PATH)
if(WIN32)
set(CPACK_GENERATOR "ZIP")
set(RIZIN_INSTALL_PLUGDIR "lib/plugins")
set(RIZIN_INSTALL_PLUGDIR "lib/rizin/plugins")
if (CUTTER_PACKAGE_DEPENDENCIES)
if (CUTTER_ENABLE_PYTHON)
@ -59,6 +59,18 @@ if(WIN32)
endif()
")
endif()
if (CUTTER_PACKAGE_RZ_SILHOUETTE AND CUTTER_ENABLE_DEPENDENCY_DOWNLOADS)
install(CODE "
set(ENV{RZ_PREFIX} \"\${CMAKE_INSTALL_PREFIX}\")
set(ENV{PATH} \"\${CMAKE_INSTALL_PREFIX};\$ENV{PATH}\")
execute_process(COMMAND powershell \"${CMAKE_CURRENT_SOURCE_DIR}/bundle_rz_silhouette.ps1\" \"\${CMAKE_INSTALL_PREFIX}\"
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
RESULT_VARIABLE SCRIPT_RESULT)
if (SCRIPT_RESULT)
message(FATAL_ERROR \"Failed to package rz-silhouette (returned \${SCRIPT_RESULT})\")
endif()
")
endif()
endif()
################################################
@ -150,6 +162,18 @@ if(CUTTER_ENABLE_DEPENDENCY_DOWNLOADS AND (NOT WIN32))
endif()
")
endif()
if (CUTTER_PACKAGE_RZ_SILHOUETTE)
install(CODE "
execute_process(COMMAND
\"${CMAKE_CURRENT_SOURCE_DIR}/../scripts/rz-silhouette.sh\"
\"\${CMAKE_INSTALL_PREFIX}\" \"${YARA_PLUGIN_OPTIONS}\"
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
RESULT_VARIABLE SCRIPT_RESULT)
if (SCRIPT_RESULT)
message(FATAL_ERROR \"Failed to package rz-silhouette (returned \${SCRIPT_RESULT})\")
endif()
")
endif()
endif()
################################################
@ -164,9 +188,9 @@ if(CUTTER_PACKAGE_RZ_GHIDRA)
# installed Cutter.
ExternalProject_Add(rz-ghidra
GIT_REPOSITORY https://github.com/rizinorg/rz-ghidra
GIT_TAG v0.5.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 ""

View File

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

View File

@ -7,10 +7,10 @@ if (-not (Test-Path -Path 'libswift' -PathType Container)) {
cd libswift
& meson.exe --buildtype=release --prefix=$dist build
ninja -C build install
$pathdll = "$dist/lib/plugins/swift.dll"
$pathdll = "$dist\lib\rizin\plugins\swift.dll"
if(![System.IO.File]::Exists($pathdll)) {
type build/meson-logs/meson-log.txt
ls "$dist/lib/plugins/"
ls "$dist\lib\rizin\plugins\"
throw (New-Object System.IO.FileNotFoundException("File not found: $pathdll", $pathdll))
}
Remove-Item -Recurse -Force $dist/lib/plugins/swift.lib
Remove-Item -Recurse -Force "$dist\lib\rizin\plugins\swift.lib"

View File

@ -8,15 +8,15 @@ if (-not (Test-Path -Path 'rz_libyara' -PathType Container)) {
git -C rz_libyara submodule update
}
cd rz_libyara
& meson.exe --buildtype=release --prefix=$dist build
& meson.exe --buildtype=release --prefix=$dist -Duse_sys_yara=disabled -Denable_openssl=false build
ninja -C build install
$pathdll = "$dist/lib/plugins/rz_yara.dll"
$pathdll = "$dist\lib\rizin\plugins\rz_yara.dll"
if(![System.IO.File]::Exists($pathdll)) {
type build/meson-logs/meson-log.txt
ls "$dist/lib/plugins/"
ls "$dist\lib\rizin\plugins\"
throw (New-Object System.IO.FileNotFoundException("File not found: $pathdll", $pathdll))
}
Remove-Item -Recurse -Force $dist/lib/plugins/rz_yara.lib
Remove-Item -Recurse -Force "$dist\lib\rizin\plugins\rz_yara.lib"
cd cutter-plugin
mkdir build

17
dist/bundle_rz_silhouette.ps1 vendored Normal file
View File

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

View File

@ -247,7 +247,7 @@ Note that there are some major building options available:
* ``CUTTER_USE_BUNDLED_RIZIN`` automatically compile Rizin from submodule (Enabled by default).
* ``CUTTER_ENABLE_PYTHON`` compile with Python support.
* ``CUTTER_ENABLE_PYTHON_BINDINGS`` automatically generate Python Bindings with Shiboken2, required for Python plugins!
* ``CUTTER_ENABLE_PYTHON_BINDINGS`` automatically generate Python Bindings with Shiboken, required for Python plugins!
* ``CUTTER_ENABLE_KSYNTAXHIGHLIGHTING`` use KSyntaxHighlighting for code highlighting.
* ``CUTTER_ENABLE_GRAPHVIZ`` enable Graphviz for graph layouts.
* ``CUTTER_EXTRA_PLUGIN_DIRS`` List of addition plugin locations. Useful when preparing package for Linux distros that have strict package layout rules.

View File

@ -26,7 +26,7 @@ author = 'The Cutter Developers'
# The short X.Y version
version = '2.2'
# The full version, including a2lpha/beta/rc tags
release = '2.2.1'
release = '2.2.0'
# -- General configuration ---------------------------------------------------

View File

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

2
rizin

@ -1 +1 @@
Subproject commit e2646b53b0f1aba8202f98b22d345ae73ad0e35f
Subproject commit 48b088056236254356fdde46f28d1cbe8bc28316

123
scripts/clang-format.py Executable file
View File

@ -0,0 +1,123 @@
#!/usr/bin/env python3
#
# SPDX-FileCopyrightText: 2021 Anton Kochkov <anton.kochkov@gmail.com>
# SPDX-License-Identifier: LGPL-3.0-only
import argparse
import glob
import itertools
import subprocess
import sys
from git import Repo
dirlist = [
"src",
]
skiplist = [
"translations",
]
patterns = ["*.cpp", "*.h", "*.hpp"]
def should_scan(filename):
return any(directory in filename for directory in dirlist) and any(
pattern[1:] in filename for pattern in patterns
)
def skip(filename):
return any(skipfile in filename for skipfile in skiplist)
def get_matching_files():
for directory, pattern in itertools.product(dirlist, patterns):
for filename in glob.iglob(directory + "/**/" + pattern, recursive=True):
if not skip(filename):
yield filename
def get_edited_files(args):
repo = Repo()
for diff in repo.index.diff(args.diff):
filename = diff.a_path
if should_scan(filename) and not skip(filename):
yield filename
def build_command(clangformat, check, filenames, verbose):
cmd = [clangformat, "--style=file"]
if verbose:
cmd += ["--verbose"]
if check:
cmd += ["--Werror", "--dry-run"]
else:
cmd += ["-i"]
return cmd + filenames
def format_files(args, files):
if len(files) == 0:
print("No C files to format.")
sys.exit(0)
cmd = build_command(args.clang_format, args.check, files, args.verbose)
r = subprocess.run(cmd, check=False)
sys.exit(r.returncode)
def get_file(args):
filename = args.file
if should_scan(filename) and not skip(filename):
return [filename]
return []
def get_files(args):
if args.diff:
return get_edited_files(args)
if args.file:
return get_file(args)
return get_matching_files()
def process(args):
files = get_files(args)
format_files(args, list(files))
def parse():
parser = argparse.ArgumentParser(description="Clang format the rizin project")
parser.add_argument(
"-C", "--clang-format", default="clang-format", help="path of clang-format"
)
parser.add_argument(
"-c", "--check", action="store_true", help="enable the check mode"
)
parser.add_argument(
"-v", "--verbose", action="store_true", help="use verbose output"
)
parser.add_argument("-f", "--file", help="formats (or checks) only the given file")
parser.add_argument(
"-d",
"--diff",
type=str,
default=None,
help="format all modified file related to branch",
)
return parser.parse_args()
def main():
args = parse()
process(args)
if __name__ == "__main__":
main()

View File

@ -15,7 +15,7 @@ fi
cd rz_libyara
meson --buildtype=release --pkg-config-path="$INSTALL_PREFIX/lib/pkgconfig" --prefix="$INSTALL_PREFIX" build
meson --buildtype=release --pkg-config-path="$INSTALL_PREFIX/lib/pkgconfig" --prefix="$INSTALL_PREFIX" -Duse_sys_yara=disabled build
ninja -C build install
cd cutter-plugin

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

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

View File

@ -6,6 +6,7 @@ set(SOURCES
core/Cutter.cpp
core/CutterJson.cpp
core/RizinCpp.cpp
core/Basefind.cpp
dialogs/EditStringDialog.cpp
dialogs/WriteCommandsDialogs.cpp
widgets/DisassemblerGraphView.cpp
@ -17,6 +18,7 @@ set(SOURCES
dialogs/CommentsDialog.cpp
dialogs/EditInstructionDialog.cpp
dialogs/FlagDialog.cpp
dialogs/GlobalVariableDialog.cpp
dialogs/RemoteDebugDialog.cpp
dialogs/NativeDebugDialog.cpp
dialogs/XrefsDialog.cpp
@ -35,6 +37,7 @@ set(SOURCES
widgets/ExportsWidget.cpp
widgets/FlagsWidget.cpp
widgets/FunctionsWidget.cpp
widgets/GlobalsWidget.cpp
widgets/ImportsWidget.cpp
widgets/Omnibar.cpp
widgets/RelocsWidget.cpp
@ -149,6 +152,9 @@ set(SOURCES
dialogs/GlibcHeapBinsDialog.cpp
widgets/HeapBinsGraphView.cpp
dialogs/ArenaInfoDialog.cpp
tools/basefind/BaseFindDialog.cpp
tools/basefind/BaseFindSearchDialog.cpp
tools/basefind/BaseFindResultsDialog.cpp
)
set(HEADER_FILES
core/Cutter.h
@ -156,6 +162,7 @@ set(HEADER_FILES
core/CutterDescriptions.h
core/CutterJson.h
core/RizinCpp.h
core/Basefind.h
dialogs/EditStringDialog.h
dialogs/WriteCommandsDialogs.h
widgets/DisassemblerGraphView.h
@ -167,6 +174,7 @@ set(HEADER_FILES
dialogs/CommentsDialog.h
dialogs/EditInstructionDialog.h
dialogs/FlagDialog.h
dialogs/GlobalVariableDialog.h
dialogs/RemoteDebugDialog.h
dialogs/NativeDebugDialog.h
dialogs/XrefsDialog.h
@ -308,6 +316,9 @@ set(HEADER_FILES
dialogs/GlibcHeapBinsDialog.h
widgets/HeapBinsGraphView.h
dialogs/ArenaInfoDialog.h
tools/basefind/BaseFindDialog.h
tools/basefind/BaseFindSearchDialog.h
tools/basefind/BaseFindResultsDialog.h
)
set(UI_FILES
dialogs/AboutDialog.ui
@ -319,6 +330,7 @@ set(UI_FILES
dialogs/CommentsDialog.ui
dialogs/EditInstructionDialog.ui
dialogs/FlagDialog.ui
dialogs/GlobalVariableDialog.ui
dialogs/RemoteDebugDialog.ui
dialogs/NativeDebugDialog.ui
dialogs/XrefsDialog.ui
@ -330,6 +342,7 @@ set(UI_FILES
widgets/Dashboard.ui
widgets/EntrypointWidget.ui
widgets/FlagsWidget.ui
widgets/GlobalsWidget.ui
widgets/StringsWidget.ui
widgets/HexdumpWidget.ui
dialogs/preferences/PreferencesDialog.ui
@ -338,7 +351,6 @@ set(UI_FILES
dialogs/preferences/InitializationFileEditor.ui
widgets/QuickFilterView.ui
widgets/DecompilerWidget.ui
widgets/ClassesWidget.ui
widgets/VTablesWidget.ui
widgets/TypesWidget.ui
widgets/SearchWidget.ui
@ -378,6 +390,9 @@ set(UI_FILES
widgets/GlibcHeapWidget.ui
dialogs/GlibcHeapBinsDialog.ui
dialogs/ArenaInfoDialog.ui
tools/basefind/BaseFindDialog.ui
tools/basefind/BaseFindSearchDialog.ui
tools/basefind/BaseFindResultsDialog.ui
)
set(QRC_FILES
resources.qrc
@ -387,16 +402,26 @@ set(QRC_FILES
themes/lightstyle/light.qrc
)
set(CUTTER_INCLUDE_DIRECTORIES core widgets common plugins menus .)
if (CUTTER_ENABLE_PYTHON)
list(APPEND SOURCES common/QtResImporter.cpp common/PythonManager.cpp common/PythonAPI.cpp)
list(APPEND HEADER_FILES common/QtResImporter.h common/PythonManager.h common/PythonAPI.h)
endif()
if(CUTTER_ENABLE_PYTHON_BINDINGS)
if (CUTTER_QT6)
set(PYSIDE_NAME PySide6)
set(SHIBOKEN_COMMAND Shiboken6::shiboken6)
else()
set(PYSIDE_NAME PySide2)
set(SHIBOKEN_COMMAND Shiboken2::shiboken2)
endif()
set(BINDINGS_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/bindings")
set(BINDINGS_BUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}/bindings")
configure_file("${BINDINGS_SRC_DIR}/bindings.xml" "${BINDINGS_BUILD_DIR}/bindings.xml" COPYONLY) # trigger reconfigure if file changes
configure_file("${BINDINGS_SRC_DIR}/bindings.xml.in" "${BINDINGS_BUILD_DIR}/bindings.xml")
execute_process(COMMAND "${PYTHON_EXECUTABLE}" "${BINDINGS_SRC_DIR}/src_list.py" cmake "${BINDINGS_BUILD_DIR}" OUTPUT_VARIABLE BINDINGS_SOURCE)
@ -404,14 +429,40 @@ if(CUTTER_ENABLE_PYTHON_BINDINGS)
include_directories("${BINDINGS_BUILD_DIR}/CutterBindings")
set(SHIBOKEN_INCLUDE_DIRS "")
if(APPLE AND _qt6Core_install_prefix)
list(APPEND BINDINGS_INCLUDE_DIRS "${_qt6Core_install_prefix}/include")
list(APPEND BINDINGS_INCLUDE_DIRS "${_qt6Core_install_prefix}/include/QtCore")
list(APPEND BINDINGS_INCLUDE_DIRS "${_qt6Core_install_prefix}/include/QtGui")
list(APPEND BINDINGS_INCLUDE_DIRS "${_qt6Core_install_prefix}/include/QtWidgets")
endif()
if (CUTTER_QT6)
list(APPEND SHIBOKEN_INCLUDE_DIRS ${Qt6Core_INCLUDE_DIRS} ${Qt6Widgets_INCLUDE_DIRS} ${Qt6Gui_INCLUDE_DIRS})
else()
list(APPEND SHIBOKEN_INCLUDE_DIRS ${Qt5Core_INCLUDE_DIRS} ${Qt5Widgets_INCLUDE_DIRS} ${Qt5Gui_INCLUDE_DIRS})
endif()
foreach(_dir ${CUTTER_INCLUDE_DIRECTORIES})
list(APPEND SHIBOKEN_INCLUDE_DIRS
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/${_dir}>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/cutter/${_dir}>
)
endforeach()
list(APPEND SHIBOKEN_INCLUDE_DIRS ${Rizin_INCLUDE_DIRS})
if (NOT WIN32)
string(REPLACE ";" ":" SHIBOKEN_INCLUDE_DIRS "${SHIBOKEN_INCLUDE_DIRS}")
endif()
set(SHIBOKEN_OPTIONS)
list(APPEND SHIBOKEN_OPTIONS --include-paths="${SHIBOKEN_INCLUDE_DIRS}")
if (WIN32)
list(APPEND SHIBOKEN_OPTIONS --avoid-protected-hack)
endif()
add_custom_command(OUTPUT ${BINDINGS_SOURCE}
COMMAND Shiboken2::shiboken2 --project-file="${BINDINGS_BUILD_DIR}/bindings.txt" ${SHIBOKEN_OPTIONS} ${SHIBOKEN_EXTRA_OPTIONS}
DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/bindings/bindings.xml" "${BINDINGS_BUILD_DIR}/bindings.txt"
COMMAND "${SHIBOKEN_COMMAND}" --project-file="${BINDINGS_BUILD_DIR}/bindings.txt" ${SHIBOKEN_OPTIONS} ${SHIBOKEN_EXTRA_OPTIONS}
DEPENDS "${BINDINGS_BUILD_DIR}/bindings.xml" "${BINDINGS_BUILD_DIR}/bindings.txt"
IMPLICIT_DEPENDS CXX "${CMAKE_CURRENT_SOURCE_DIR}/bindings/bindings.h"
COMMENT "Generating Python bindings with shiboken2")
else()
@ -453,7 +504,6 @@ 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
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/${_dir}>
@ -488,7 +538,11 @@ if(CUTTER_ENABLE_PYTHON)
endif()
target_link_libraries(Cutter PRIVATE ${PYTHON_LIBRARIES})
if(CUTTER_ENABLE_PYTHON_BINDINGS)
target_link_libraries(Cutter PRIVATE Shiboken2::libshiboken PySide2::pyside2)
if (CUTTER_QT6)
target_link_libraries(Cutter PRIVATE Shiboken6::libshiboken PySide6::pyside6)
else()
target_link_libraries(Cutter PRIVATE Shiboken2::libshiboken PySide2::pyside2)
endif()
get_target_property(RAW_BINDINGS_INCLUDE_DIRS Cutter INCLUDE_DIRECTORIES)
if(NOT CUTTER_USE_BUNDLED_RIZIN)
@ -510,7 +564,11 @@ if(CUTTER_ENABLE_PYTHON)
list(APPEND BINDINGS_INCLUDE_DIRS "${_qt5Core_install_prefix}/include/QtGui")
list(APPEND BINDINGS_INCLUDE_DIRS "${_qt5Core_install_prefix}/include/QtWidgets")
endif()
list(APPEND BINDINGS_INCLUDE_DIRS ${Qt5Core_INCLUDE_DIRS} ${Qt5Widgets_INCLUDE_DIRS} ${Qt5Gui_INCLUDE_DIRS})
if (CUTTER_QT6)
list(APPEND BINDINGS_INCLUDE_DIRS ${Qt6Core_INCLUDE_DIRS} ${Qt6Widgets_INCLUDE_DIRS} ${Qt6Gui_INCLUDE_DIRS})
else()
list(APPEND BINDINGS_INCLUDE_DIRS ${Qt5Core_INCLUDE_DIRS} ${Qt5Widgets_INCLUDE_DIRS} ${Qt5Gui_INCLUDE_DIRS})
endif()
list(APPEND BINDINGS_INCLUDE_DIRS ${Rizin_INCLUDE_DIRS})
list(APPEND BINDINGS_INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}")
if (NOT WIN32)

View File

@ -33,9 +33,9 @@
// has RZ_GITTAP defined and uses it in rz_core_version().
// After that, RZ_GITTAP is not defined anymore and RZ_VERSION is used.
#ifdef RZ_GITTAP
#define CUTTER_COMPILE_TIME_RZ_VERSION "" RZ_GITTAP
# define CUTTER_COMPILE_TIME_RZ_VERSION "" RZ_GITTAP
#else
#define CUTTER_COMPILE_TIME_RZ_VERSION "" RZ_VERSION
# define CUTTER_COMPILE_TIME_RZ_VERSION "" RZ_VERSION
#endif
CutterApplication::CutterApplication(int &argc, char **argv) : QApplication(argc, argv)
@ -162,7 +162,8 @@ CutterApplication::CutterApplication(int &argc, char **argv) : QApplication(argc
appdir.cdUp(); // appdir
auto sleighHome = appdir;
sleighHome.cd("lib/rizin/plugins/rz_ghidra_sleigh/"); // appdir/lib/rizin/plugins/rz_ghidra_sleigh/
// appdir/lib/rizin/plugins/rz_ghidra_sleigh/
sleighHome.cd("lib/rizin/plugins/rz_ghidra_sleigh/");
Core()->setConfig("ghidra.sleighhome", sleighHome.absolutePath());
}
#endif
@ -174,8 +175,8 @@ CutterApplication::CutterApplication(int &argc, char **argv) : QApplication(argc
rzprefix.cd("Resources"); // Contents/Resources/
auto sleighHome = rzprefix;
sleighHome.cd(
"lib/rizin/plugins/rz_ghidra_sleigh"); // Contents/Resources/lib/rizin/plugins/rz_ghidra_sleigh
// Contents/Resources/lib/rizin/plugins/rz_ghidra_sleigh
sleighHome.cd("lib/rizin/plugins/rz_ghidra_sleigh");
Core()->setConfig("ghidra.sleighhome", sleighHome.absolutePath());
}
#endif
@ -183,7 +184,7 @@ CutterApplication::CutterApplication(int &argc, char **argv) : QApplication(argc
#if defined(Q_OS_WIN) && defined(CUTTER_ENABLE_PACKAGING)
{
auto sleighHome = QDir(QCoreApplication::applicationDirPath());
sleighHome.cd("lib/plugins/rz_ghidra_sleigh");
sleighHome.cd("lib/rizin/plugins/rz_ghidra_sleigh");
Core()->setConfig("ghidra.sleighhome", sleighHome.absolutePath());
}
#endif
@ -295,6 +296,89 @@ bool CutterApplication::loadTranslations()
return false;
}
QStringList CutterApplication::getArgs() const
{
auto &options = clOptions.fileOpenOptions;
QStringList args;
switch (clOptions.analysisLevel) {
case AutomaticAnalysisLevel::None:
args.push_back("-A");
args.push_back("0");
break;
case AutomaticAnalysisLevel::AAA:
args.push_back("-A");
args.push_back("1");
break;
case AutomaticAnalysisLevel::AAAA:
args.push_back("-A");
args.push_back("2");
break;
default:
break;
}
if (!options.useVA) {
args.push_back("-P");
}
if (options.writeEnabled) {
args.push_back("-w");
}
if (!options.script.isEmpty()) {
args.push_back("-i");
args.push_back(options.script);
}
if (!options.projectFile.isEmpty()) {
args.push_back("-p");
args.push_back(options.projectFile);
}
if (!options.arch.isEmpty()) {
args.push_back("-a");
args.push_back(options.arch);
}
if (options.bits > 0) {
args.push_back("-b");
args.push_back(QString::asprintf("%d", options.bits));
}
if (!options.cpu.isEmpty()) {
args.push_back("-c");
args.push_back(options.cpu);
}
if (!options.os.isEmpty()) {
args.push_back("-o");
args.push_back(options.os);
}
switch (options.endian) {
case InitialOptions::Endianness::Little:
args.push_back("-e");
args.push_back("little");
break;
case InitialOptions::Endianness::Big:
args.push_back("-e");
args.push_back("big");
break;
default:
break;
}
if (!options.forceBinPlugin.isEmpty()) {
args.push_back("-F");
args.push_back(options.forceBinPlugin);
}
if (options.binLoadAddr != RVA_INVALID) {
args.push_back("-B");
args.push_back(RzAddressString(options.binLoadAddr));
}
if (options.mapAddr != RVA_INVALID) {
args.push_back("-m");
args.push_back(RzAddressString(options.mapAddr));
}
if (!options.filename.isEmpty()) {
args.push_back(options.filename);
}
return args;
}
bool CutterApplication::parseCommandLineOptions()
{
// Keep this function in sync with documentation
@ -314,6 +398,27 @@ bool CutterApplication::parseCommandLineOptions()
QObject::tr("level"));
cmd_parser.addOption(analOption);
QCommandLineOption archOption({ "a", "arch" }, QObject::tr("Sets a specific architecture name"),
QObject::tr("arch"));
cmd_parser.addOption(archOption);
QCommandLineOption bitsOption({ "b", "bits" }, QObject::tr("Sets a specific architecture bits"),
QObject::tr("bits"));
cmd_parser.addOption(bitsOption);
QCommandLineOption cpuOption({ "c", "cpu" }, QObject::tr("Sets a specific CPU"),
QObject::tr("cpu"));
cmd_parser.addOption(cpuOption);
QCommandLineOption osOption({ "o", "os" }, QObject::tr("Sets a specific operating system"),
QObject::tr("os"));
cmd_parser.addOption(osOption);
QCommandLineOption endianOption({ "e", "endian" },
QObject::tr("Sets the endianness (big or little)"),
QObject::tr("big|little"));
cmd_parser.addOption(endianOption);
QCommandLineOption formatOption({ "F", "format" },
QObject::tr("Force using a specific file format (bin plugin)"),
QObject::tr("name"));
@ -324,6 +429,11 @@ bool CutterApplication::parseCommandLineOptions()
QObject::tr("base address"));
cmd_parser.addOption(baddrOption);
QCommandLineOption maddrOption({ "m", "map" },
QObject::tr("Map the binary at a specific address"),
QObject::tr("map address"));
cmd_parser.addOption(maddrOption);
QCommandLineOption scriptOption("i", QObject::tr("Run script file"), QObject::tr("file"));
cmd_parser.addOption(scriptOption);
@ -335,6 +445,10 @@ bool CutterApplication::parseCommandLineOptions()
QObject::tr("Open file in write mode"));
cmd_parser.addOption(writeModeOption);
QCommandLineOption phyModeOption({ "P", "phymode" },
QObject::tr("Disables virtual addressing"));
cmd_parser.addOption(phyModeOption);
QCommandLineOption pythonHomeOption(
"pythonhome", QObject::tr("PYTHONHOME to use for embedded python interpreter"),
"PYTHONHOME");
@ -396,15 +510,21 @@ bool CutterApplication::parseCommandLineOptions()
return false;
}
InitialOptions options;
if (!opts.args.isEmpty()) {
opts.fileOpenOptions.filename = opts.args[0];
opts.fileOpenOptions.forceBinPlugin = cmd_parser.value(formatOption);
if (cmd_parser.isSet(baddrOption)) {
bool ok;
bool ok = false;
RVA baddr = cmd_parser.value(baddrOption).toULongLong(&ok, 0);
if (ok) {
options.binLoadAddr = baddr;
opts.fileOpenOptions.binLoadAddr = baddr;
}
}
if (cmd_parser.isSet(maddrOption)) {
bool ok = false;
RVA maddr = cmd_parser.value(maddrOption).toULongLong(&ok, 0);
if (ok) {
opts.fileOpenOptions.mapAddr = maddr;
}
}
switch (opts.analysisLevel) {
@ -421,8 +541,36 @@ bool CutterApplication::parseCommandLineOptions()
break;
}
opts.fileOpenOptions.script = cmd_parser.value(scriptOption);
opts.fileOpenOptions.arch = cmd_parser.value(archOption);
opts.fileOpenOptions.cpu = cmd_parser.value(cpuOption);
opts.fileOpenOptions.os = cmd_parser.value(osOption);
if (cmd_parser.isSet(bitsOption)) {
bool ok = false;
int bits = cmd_parser.value(bitsOption).toInt(&ok, 10);
if (ok && bits > 0) {
opts.fileOpenOptions.bits = bits;
}
}
if (cmd_parser.isSet(endianOption)) {
QString endian = cmd_parser.value(endianOption).toLower();
opts.fileOpenOptions.endian = InitialOptions::Endianness::Auto;
if (endian == "little") {
opts.fileOpenOptions.endian = InitialOptions::Endianness::Little;
} else if (endian == "big") {
opts.fileOpenOptions.endian = InitialOptions::Endianness::Big;
} else {
fprintf(stderr, "%s\n",
QObject::tr("Invalid Endianness. You can only set it to `big` or `little`.")
.toLocal8Bit()
.constData());
return false;
}
} else {
opts.fileOpenOptions.endian = InitialOptions::Endianness::Auto;
}
opts.fileOpenOptions.writeEnabled = cmd_parser.isSet(writeModeOption);
opts.fileOpenOptions.useVA = !cmd_parser.isSet(phyModeOption);
}
opts.fileOpenOptions.projectFile = cmd_parser.value(projectOption);

View File

@ -33,6 +33,10 @@ public:
void launchNewInstance(const QStringList &args = {});
InitialOptions getInitialOptions() const { return clOptions.fileOpenOptions; }
void setInitialOptions(const InitialOptions &options) { clOptions.fileOpenOptions = options; }
QStringList getArgs() const;
protected:
bool event(QEvent *e);

View File

@ -13,7 +13,7 @@
* @brief Attempt to connect to a parent console and configure outputs.
*/
#ifdef Q_OS_WIN
#include <windows.h>
# include <windows.h>
static void connectToConsole()
{
@ -67,9 +67,9 @@ int main(int argc, char *argv[])
#endif
QCoreApplication::setApplicationName("cutter");
// Importing settings after setting rename, needs separate handling in addition to regular version to version upgrade.
if (Cutter::shouldOfferSettingImport())
{
// Importing settings after setting rename, needs separate handling in addition to regular
// version to version upgrade.
if (Cutter::shouldOfferSettingImport()) {
Cutter::showSettingImportDialog(argc, argv);
}

View File

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

View File

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

View File

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

View File

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

View File

@ -783,6 +783,16 @@ bool Configuration::getPreviewValue() const
return s.value("asm.preview").toBool();
}
void Configuration::setShowVarTooltips(bool enabled)
{
s.setValue("showVarTooltips", enabled);
}
bool Configuration::getShowVarTooltips() const
{
return s.value("showVarTooltips").toBool();
}
bool Configuration::getGraphBlockEntryOffset()
{
return s.value("graphBlockEntryOffset", true).value<bool>();

View File

@ -215,6 +215,12 @@ public:
void setPreviewValue(bool checked);
bool getPreviewValue() const;
/**
* @brief Show tooltips for known values of registers, variables, and memory when debugging
*/
void setShowVarTooltips(bool enabled);
bool getShowVarTooltips() const;
/**
* @brief Recently opened binaries, as shown in NewFileDialog.
*/

View File

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

View File

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

View File

@ -82,3 +82,75 @@ RVA DisassemblyPreview::readDisassemblyOffset(QTextCursor tc)
return userData->line.offset;
}
typedef struct mmio_lookup_context
{
QString selected;
RVA mmio_address;
} mmio_lookup_context_t;
static bool lookup_mmio_addr_cb(void *user, const ut64 key, const void *value)
{
mmio_lookup_context_t *ctx = (mmio_lookup_context_t *)user;
if (ctx->selected == (const char *)value) {
ctx->mmio_address = key;
return false;
}
return true;
}
bool DisassemblyPreview::showDebugValueTooltip(QWidget *parent, const QPoint &pointOfEvent,
const QString &selectedText, const RVA offset)
{
if (selectedText.isEmpty())
return false;
if (selectedText.at(0).isLetter()) {
{
const auto registerRefs = Core()->getRegisterRefValues();
for (auto &reg : registerRefs) {
if (reg.name == selectedText) {
auto msg = QString("reg %1 = %2").arg(reg.name, reg.value);
QToolTip::showText(pointOfEvent, msg, parent);
return true;
}
}
}
if (offset != RVA_INVALID) {
auto vars = Core()->getVariables(offset);
for (auto &var : vars) {
if (var.name == selectedText) {
auto msg = QString("var %1 = %2").arg(var.name, var.value);
QToolTip::showText(pointOfEvent, msg, parent);
return true;
}
}
}
{
// Lookup MMIO address
mmio_lookup_context_t ctx;
ctx.selected = selectedText;
ctx.mmio_address = RVA_INVALID;
auto core = Core()->core();
RzPlatformTarget *arch_target = core->analysis->arch_target;
if (arch_target && arch_target->profile) {
ht_up_foreach(arch_target->profile->registers_mmio, lookup_mmio_addr_cb, &ctx);
}
if (ctx.mmio_address != RVA_INVALID) {
int len = 8; // TODO: Determine proper len of mmio address for the cpu
if (char *r = rz_core_print_hexdump_or_hexdiff_str(core, RZ_OUTPUT_MODE_STANDARD,
ctx.mmio_address, len, false)) {
auto val = QString::fromUtf8(r).trimmed().split("\n").last();
auto msg = QString("mmio %1 %2").arg(selectedText, val);
free(r);
QToolTip::showText(pointOfEvent, msg, parent);
return true;
}
}
}
}
// Else show preview for value?
return false;
}

View File

@ -41,5 +41,13 @@ bool showDisasPreview(QWidget *parent, const QPoint &pointOfEvent, const RVA off
* @return The disassembly offset of the hovered asm text
*/
RVA readDisassemblyOffset(QTextCursor tc);
/**
* @brief Show a QToolTip that shows the value of the highlighted register, variable, or memory
* @return True if the tooltip is shown
*/
bool showDebugValueTooltip(QWidget *parent, const QPoint &pointOfEvent, const QString &selectedText,
const RVA offset);
}
#endif

View File

@ -16,8 +16,8 @@ static qreal GetDevicePixelRatio(qreal devicePixelRatio)
}
HighDpiPixmap::HighDpiPixmap(int width, int height, qreal devicePixelRatio)
: QPixmap(int(width *GetDevicePixelRatio(devicePixelRatio)),
int(height *GetDevicePixelRatio(devicePixelRatio)))
: QPixmap(int(width * GetDevicePixelRatio(devicePixelRatio)),
int(height * GetDevicePixelRatio(devicePixelRatio)))
{
setDevicePixelRatio(GetDevicePixelRatio(devicePixelRatio));
}

View File

@ -32,7 +32,7 @@ PyObject *api_refresh(PyObject *self, PyObject *args)
Q_UNUSED(self);
Q_UNUSED(args);
Core()->triggerRefreshAll();
return Py_None;
Py_RETURN_NONE;
}
PyObject *api_message(PyObject *self, PyObject *args, PyObject *kwargs)
@ -46,8 +46,7 @@ PyObject *api_message(PyObject *self, PyObject *args, PyObject *kwargs)
return NULL;
}
Core()->message(QString(message), debug);
Py_INCREF(Py_None);
return Py_None;
Py_RETURN_NONE;
}
PyMethodDef CutterMethods[] = {

View File

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

View File

@ -7,7 +7,8 @@
namespace Cutter {
void initializeSettings();
/**
* @brief Check if Cutter should offer importing settings from version that can't be directly updated.
* @brief Check if Cutter should offer importing settings from version that can't be directly
* updated.
* @return True if this is first time running Cutter and r2 based Cutter <= 1.12 settings exist.
*/
bool shouldOfferSettingImport();

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

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

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

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

View File

@ -271,7 +271,7 @@ void CutterCore::loadCutterRC()
if (!cutterRCFileInfo.exists() || !cutterRCFileInfo.isFile()) {
continue;
}
qInfo() << "Loading initialization file from " << cutterRCFilePath;
qInfo() << tr("Loading initialization file from ") << cutterRCFilePath;
rz_core_cmd_file(core, cutterRCFilePath.toUtf8().constData());
}
}
@ -284,7 +284,7 @@ void CutterCore::loadDefaultCutterRC()
if (!cutterRCFileInfo.exists() || !cutterRCFileInfo.isFile()) {
return;
}
qInfo() << "Loading initialization file from " << cutterRCFilePath;
qInfo() << tr("Loading initialization file from ") << cutterRCFilePath;
rz_core_cmd_file(core, cutterRCFilePath.toUtf8().constData());
}
@ -1002,19 +1002,19 @@ void CutterCore::seekPrev()
{
CORE_LOCK();
rz_core_seek_undo(core);
updateSeek();
updateSeek(SeekHistoryType::Undo);
}
void CutterCore::seekNext()
{
CORE_LOCK();
rz_core_seek_redo(core);
updateSeek();
updateSeek(SeekHistoryType::Redo);
}
void CutterCore::updateSeek()
void CutterCore::updateSeek(SeekHistoryType type)
{
emit seekChanged(getOffset());
emit seekChanged(getOffset(), type);
}
RVA CutterCore::prevOpAddr(RVA startAddr, int count)
@ -1804,11 +1804,43 @@ QList<VariableDescription> CutterCore::getVariables(RVA at)
}
desc.type = QString::fromUtf8(tn);
rz_mem_free(tn);
if (char *v = rz_core_analysis_var_display(core, var, false)) {
desc.value = QString::fromUtf8(v).trimmed();
rz_mem_free(v);
}
ret.push_back(desc);
}
return ret;
}
QList<GlobalDescription> CutterCore::getAllGlobals()
{
CORE_LOCK();
RzListIter *it;
QList<GlobalDescription> ret;
RzAnalysisVarGlobal *glob;
if (core && core->analysis && core->analysis->typedb) {
const RzList *globals = rz_analysis_var_global_get_all(core->analysis);
CutterRzListForeach (globals, it, RzAnalysisVarGlobal, glob) {
const char *gtype = rz_type_as_string(core->analysis->typedb, glob->type);
if (!gtype) {
continue;
}
GlobalDescription global;
global.addr = glob->addr;
global.name = QString(glob->name);
global.type = QString(gtype);
ret << global;
}
}
return ret;
}
QVector<RegisterRefValueDescription> CutterCore::getRegisterRefValues()
{
QVector<RegisterRefValueDescription> result;
@ -3084,8 +3116,6 @@ QList<ImportDescription> CutterCore::getAllImports()
RzBinImport *import;
RzListIter *iter;
bool va = core->io->va || core->bin->is_debugger;
int bin_demangle = getConfigi("bin.demangle");
int keep_lib = getConfigi("bin.demangle.libs");
CutterRzListForeach (imports, iter, RzBinImport, import) {
if (RZ_STR_ISEMPTY(import->name)) {
continue;
@ -3099,13 +3129,6 @@ QList<ImportDescription> CutterCore::getAllImports()
if (RZ_STR_ISNOTEMPTY(import->classname)) {
name = QString("%1.%2").arg(import->classname, import->name);
}
if (bin_demangle) {
char *dname = rz_bin_demangle(bf, NULL, name.toUtf8().constData(),
importDescription.plt, keep_lib);
if (dname) {
name = fromOwnedCharPtr(dname);
}
}
if (core->bin->prefix) {
name = QString("%1.%2").arg(core->bin->prefix, name);
}
@ -3135,8 +3158,8 @@ QList<ExportDescription> CutterCore::getAllExports()
return {};
}
QString lang = getConfigi("bin.demangle") ? getConfig("bin.lang") : "";
bool va = core->io->va || core->bin->is_debugger;
bool demangle = rz_config_get_b(core->config, "bin.demangle");
QList<ExportDescription> ret;
for (const auto &symbol : CutterRzList<RzBinSymbol>(symbols)) {
@ -3145,7 +3168,7 @@ QList<ExportDescription> CutterCore::getAllExports()
}
RzBinSymNames sn = {};
rz_core_sym_name_init(core, &sn, symbol, lang.isEmpty() ? NULL : lang.toUtf8().constData());
rz_core_sym_name_init(&sn, symbol, demangle);
ExportDescription exportDescription;
exportDescription.vaddr = rva(bf->o, symbol->paddr, symbol->vaddr, va);
@ -3543,19 +3566,18 @@ QList<BinClassDescription> CutterCore::getAllClassesFromBin()
RzListIter *iter, *iter2, *iter3;
RzBinClass *c;
RzBinSymbol *sym;
RzBinField *f;
RzBinClassField *f;
CutterRzListForeach (cs, iter, RzBinClass, c) {
BinClassDescription classDescription;
classDescription.name = c->name;
classDescription.addr = c->addr;
classDescription.index = c->index;
CutterRzListForeach (c->methods, iter2, RzBinSymbol, sym) {
BinClassMethodDescription methodDescription;
methodDescription.name = sym->name;
methodDescription.addr = sym->vaddr;
classDescription.methods << methodDescription;
}
CutterRzListForeach (c->fields, iter3, RzBinField, f) {
CutterRzListForeach (c->fields, iter3, RzBinClassField, f) {
BinClassFieldDescription fieldDescription;
fieldDescription.name = f->name;
fieldDescription.addr = f->vaddr;
@ -3591,7 +3613,6 @@ QList<BinClassDescription> CutterCore::getAllClassesFromFlags()
}
desc->name = match.captured(1);
desc->addr = item.offset;
desc->index = RVA_INVALID;
continue;
}
@ -3605,7 +3626,6 @@ QList<BinClassDescription> CutterCore::getAllClassesFromFlags()
BinClassDescription cls;
cls.name = tr("Unknown (%1)").arg(className);
cls.addr = RVA_INVALID;
cls.index = 0;
ret << cls;
classDesc = &ret.last();
classesCache[className] = classDesc;
@ -4028,6 +4048,99 @@ QList<XrefDescription> CutterCore::getXRefs(RVA addr, bool to, bool whole_functi
return xrefList;
}
void CutterCore::addGlobalVariable(RVA offset, QString name, QString typ)
{
name = sanitizeStringForCommand(name);
CORE_LOCK();
char *errmsg = NULL;
RzType *globType = rz_type_parse_string_single(core->analysis->typedb->parser,
typ.toStdString().c_str(), &errmsg);
if (errmsg) {
qWarning() << tr("Error parsing type: \"%1\" message: ").arg(typ) << errmsg;
free(errmsg);
return;
}
if (!rz_analysis_var_global_create(core->analysis, name.toStdString().c_str(), globType,
offset)) {
qWarning() << tr("Error creating global variable: \"%1\"").arg(name);
return;
}
emit globalVarsChanged();
}
void CutterCore::modifyGlobalVariable(RVA offset, QString name, QString typ)
{
name = sanitizeStringForCommand(name);
CORE_LOCK();
RzAnalysisVarGlobal *glob = rz_analysis_var_global_get_byaddr_at(core->analysis, offset);
if (!glob) {
return;
}
// Compare if the name is not the same - also rename it
if (name.compare(glob->name)) {
rz_analysis_var_global_rename(core->analysis, glob->name, name.toStdString().c_str());
}
char *errmsg = NULL;
RzType *globType = rz_type_parse_string_single(core->analysis->typedb->parser,
typ.toStdString().c_str(), &errmsg);
if (errmsg) {
qWarning() << tr("Error parsing type: \"%1\" message: ").arg(typ) << errmsg;
free(errmsg);
return;
}
rz_analysis_var_global_set_type(glob, globType);
emit globalVarsChanged();
}
void CutterCore::delGlobalVariable(QString name)
{
name = sanitizeStringForCommand(name);
CORE_LOCK();
rz_analysis_var_global_delete_byname(core->analysis, name.toStdString().c_str());
emit globalVarsChanged();
}
void CutterCore::delGlobalVariable(RVA offset)
{
CORE_LOCK();
rz_analysis_var_global_delete_byaddr_at(core->analysis, offset);
emit globalVarsChanged();
}
QString CutterCore::getGlobalVariableType(QString name)
{
name = sanitizeStringForCommand(name);
CORE_LOCK();
RzAnalysisVarGlobal *glob =
rz_analysis_var_global_get_byname(core->analysis, name.toStdString().c_str());
if (!glob) {
return QString("");
}
const char *gtype = rz_type_as_string(core->analysis->typedb, glob->type);
if (!gtype) {
return QString("");
}
return QString(gtype);
}
QString CutterCore::getGlobalVariableType(RVA offset)
{
CORE_LOCK();
RzAnalysisVarGlobal *glob = rz_analysis_var_global_get_byaddr_at(core->analysis, offset);
if (!glob) {
return QString("");
}
const char *gtype = rz_type_as_string(core->analysis->typedb, glob->type);
if (!gtype) {
return QString("");
}
return QString(gtype);
}
void CutterCore::addFlag(RVA offset, QString name, RVA size)
{
name = sanitizeStringForCommand(name);
@ -4149,11 +4262,20 @@ QList<DisassemblyLine> CutterCore::disassembleLines(RVA offset, int lines)
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;
QString text = t->text;
QStringList tokens = text.split('\n');
// text might contain multiple lines
// so we split them and keep only one
// arrow/jump to addr.
for (const auto &tok : tokens) {
DisassemblyLine line;
line.offset = t->offset;
line.text = ansiEscapeToHtml(tok);
line.arrow = t->arrow;
r << line;
// only the first one.
t->arrow = RVA_INVALID;
}
}
return r;
}
@ -4533,9 +4655,9 @@ char *CutterCore::getTextualGraphAt(RzCoreGraphType type, RzCoreGraphFormat form
RzGraph *graph = rz_core_graph(core, type, address);
if (!graph) {
if (address == RVA_INVALID) {
qWarning() << "Cannot get global graph";
qWarning() << tr("Cannot get global graph");
} else {
qWarning() << "Cannot get graph at " << RzAddressString(address);
qWarning() << tr("Cannot get graph at ") << RzAddressString(address);
}
return nullptr;
}
@ -4566,7 +4688,7 @@ char *CutterCore::getTextualGraphAt(RzCoreGraphType type, RzCoreGraphFormat form
rz_graph_free(graph);
if (!string) {
qWarning() << "Failed to generate graph";
qWarning() << tr("Failed to generate graph");
}
return string;
@ -4584,9 +4706,15 @@ void CutterCore::writeGraphvizGraphToFile(QString path, QString format, RzCoreGr
if (!rz_core_graph_write(core, address, type, filepath)) {
if (address == RVA_INVALID) {
qWarning() << "Cannot get global graph";
qWarning() << tr("Cannot get global graph");
} else {
qWarning() << "Cannot get graph at " << RzAddressString(address);
qWarning() << tr("Cannot get graph at ") << RzAddressString(address);
}
}
}
bool CutterCore::rebaseBin(RVA base_address)
{
CORE_LOCK();
return rz_core_bin_rebase(core, base_address);
}

View File

@ -4,6 +4,7 @@
#include "core/CutterCommon.h"
#include "core/CutterDescriptions.h"
#include "core/CutterJson.h"
#include "core/Basefind.h"
#include "common/BasicInstructionHighlighter.h"
#include <QMap>
@ -69,6 +70,7 @@ class CUTTER_EXPORT CutterCore : public QObject
friend class RzCoreLocked;
friend class RizinTask;
friend class Basefind;
public:
explicit CutterCore(QObject *parent = nullptr);
@ -172,6 +174,8 @@ public:
return returner;
}
enum class SeekHistoryType { New, Undo, Redo };
CutterJson cmdj(const char *str);
CutterJson cmdj(const QString &str) { return cmdj(str.toUtf8().constData()); }
QString cmdTask(const QString &str);
@ -235,6 +239,14 @@ public:
QString nearestFlag(RVA offset, RVA *flagOffsetOut);
void triggerFlagsChanged();
/* Global Variables */
void addGlobalVariable(RVA offset, QString name, QString typ);
void delGlobalVariable(QString name);
void delGlobalVariable(RVA offset);
void modifyGlobalVariable(RVA offset, QString name, QString typ);
QString getGlobalVariableType(QString name);
QString getGlobalVariableType(RVA offset);
/* Edition functions */
PRzAnalysisBytes getRzAnalysisBytesSingle(RVA addr);
QString getInstructionBytes(RVA addr);
@ -324,7 +336,7 @@ public:
void seekSilent(QString thing) { seekSilent(math(thing)); }
void seekPrev();
void seekNext();
void updateSeek();
void updateSeek(SeekHistoryType type = SeekHistoryType::New);
/**
* @brief Raise a memory widget showing current offset, prefer last active
* memory widget.
@ -554,6 +566,8 @@ public:
void setGraphEmpty(bool empty);
bool isGraphEmpty();
bool rebaseBin(RVA base_address);
void getRegs();
QList<QString> regs;
void setSettings();
@ -578,6 +592,7 @@ public:
QList<ExportDescription> getAllExports();
QList<SymbolDescription> getAllSymbols();
QList<HeaderDescription> getAllHeaders();
QList<GlobalDescription> getAllGlobals();
QList<FlirtDescription> getSignaturesDB();
QList<CommentDescription> getAllComments(const QString &filterType);
QList<RelocDescription> getAllRelocs();
@ -744,6 +759,7 @@ signals:
void functionRenamed(const RVA offset, const QString &new_name);
void varsChanged();
void globalVarsChanged();
void functionsChanged();
void flagsChanged();
void commentsChanged(RVA addr);
@ -794,8 +810,9 @@ signals:
/**
* @brief seekChanged is emitted each time Rizin's seek value is modified
* @param offset
* @param historyType
*/
void seekChanged(RVA offset);
void seekChanged(RVA offset, SeekHistoryType type = SeekHistoryType::New);
void toggleDebugView();

View File

@ -15,7 +15,6 @@
# undef max
#endif // Q_OS_WIN
// Global information for Cutter
#define APPNAME "Cutter"

View File

@ -239,7 +239,6 @@ struct BinClassDescription
QString name;
RVA addr = RVA_INVALID;
RVA vtableAddr = RVA_INVALID;
ut64 index = 0;
QList<BinClassBaseClassDescription> baseClasses;
QList<BinClassMethodDescription> methods;
QList<BinClassFieldDescription> fields;
@ -358,6 +357,14 @@ struct VariableDescription
RzAnalysisVarStorageType storageType;
QString name;
QString type;
QString value;
};
struct GlobalDescription
{
RVA addr;
QString type;
QString name;
};
struct RegisterRefValueDescription
@ -386,6 +393,18 @@ struct Arena
ut64 max_system_mem;
};
struct BasefindCoreStatusDescription
{
size_t index;
ut32 percentage;
};
struct BasefindResultDescription
{
RVA candidate;
ut32 score;
};
Q_DECLARE_METATYPE(FunctionDescription)
Q_DECLARE_METATYPE(ImportDescription)
Q_DECLARE_METATYPE(ExportDescription)
@ -395,6 +414,7 @@ Q_DECLARE_METATYPE(RelocDescription)
Q_DECLARE_METATYPE(StringDescription)
Q_DECLARE_METATYPE(FlagspaceDescription)
Q_DECLARE_METATYPE(FlagDescription)
Q_DECLARE_METATYPE(GlobalDescription)
Q_DECLARE_METATYPE(XrefDescription)
Q_DECLARE_METATYPE(EntrypointDescription)
Q_DECLARE_METATYPE(RzBinPluginDescription)
@ -424,5 +444,7 @@ Q_DECLARE_METATYPE(BreakpointDescription::PositionType)
Q_DECLARE_METATYPE(ProcessDescription)
Q_DECLARE_METATYPE(RefDescription)
Q_DECLARE_METATYPE(VariableDescription)
Q_DECLARE_METATYPE(BasefindCoreStatusDescription)
Q_DECLARE_METATYPE(BasefindResultDescription)
#endif // DESCRIPTIONS_H

View File

@ -31,6 +31,7 @@
#include "widgets/DisassemblerGraphView.h"
#include "widgets/GraphView.h"
#include "widgets/GraphWidget.h"
#include "widgets/GlobalsWidget.h"
#include "widgets/OverviewWidget.h"
#include "widgets/OverviewView.h"
#include "widgets/FunctionsWidget.h"
@ -115,6 +116,9 @@
#include <QGraphicsScene>
#include <QGraphicsView>
// Tools
#include "tools/basefind/BaseFindDialog.h"
#define PROJECT_FILE_FILTER tr("Rizin Project (*.rzdb)")
template<class T>
@ -268,7 +272,7 @@ void MainWindow::initUI()
readSettings();
// Display tooltip for the Analyze Program action
ui->actionAnalyze->setToolTip("Analyze the program using Rizin's \"aaa\" command");
ui->actionAnalyze->setToolTip(tr("Analyze the program using Rizin's \"aaa\" command"));
ui->menuFile->setToolTipsVisible(true);
}
@ -398,6 +402,7 @@ void MainWindow::initDocks()
sectionsDock = new SectionsWidget(this),
segmentsDock = new SegmentsWidget(this),
symbolsDock = new SymbolsWidget(this),
globalsDock = new GlobalsWidget(this),
vTablesDock = new VTablesWidget(this),
flirtDock = new FlirtWidget(this),
rzGraphDock = new RizinGraphWidget(this),
@ -902,6 +907,7 @@ void MainWindow::restoreDocks()
tabifyDockWidget(dashboardDock, headersDock);
tabifyDockWidget(dashboardDock, flirtDock);
tabifyDockWidget(dashboardDock, symbolsDock);
tabifyDockWidget(dashboardDock, globalsDock);
tabifyDockWidget(dashboardDock, classesDock);
tabifyDockWidget(dashboardDock, resourcesDock);
tabifyDockWidget(dashboardDock, vTablesDock);
@ -1087,11 +1093,11 @@ MemoryDockWidget *MainWindow::addNewMemoryWidget(MemoryWidgetType type, RVA addr
memoryWidget = new DecompilerWidget(this);
break;
case MemoryWidgetType::CallGraph:
memoryWidget = new CallGraphWidget(this, false);
break;
memoryWidget = new CallGraphWidget(this, false);
break;
case MemoryWidgetType::GlobalCallGraph:
memoryWidget = new CallGraphWidget(this, true);
break;
memoryWidget = new CallGraphWidget(this, true);
break;
}
auto seekable = memoryWidget->getSeekable();
seekable->setSynchronization(synchronized);
@ -1637,6 +1643,12 @@ void MainWindow::on_actionTabs_triggered()
setTabLocation();
}
void MainWindow::on_actionBaseFind_triggered()
{
auto dialog = new BaseFindDialog(this);
dialog->show();
}
void MainWindow::on_actionAbout_triggered()
{
AboutDialog *a = new AboutDialog(this);
@ -1759,7 +1771,7 @@ void MainWindow::on_actionExport_as_code_triggered()
QFile file(dialog.selectedFiles()[0]);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
qWarning() << "Can't open file";
qWarning() << tr("Can't open file");
return;
}
@ -1774,12 +1786,10 @@ void MainWindow::on_actionExport_as_code_triggered()
return;
}
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));
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();
}

View File

@ -26,6 +26,7 @@ class FunctionsWidget;
class ImportsWidget;
class ExportsWidget;
class SymbolsWidget;
class GlobalsWidget;
class RelocsWidget;
class CommentsWidget;
class StringsWidget;
@ -152,6 +153,7 @@ public slots:
void toggleOverview(bool visibility, GraphWidget *targetGraph);
private slots:
void on_actionBaseFind_triggered();
void on_actionAbout_triggered();
void on_actionIssue_triggered();
void documentationClicked();
@ -239,6 +241,7 @@ private:
TypesWidget *typesDock = nullptr;
SearchWidget *searchDock = nullptr;
SymbolsWidget *symbolsDock = nullptr;
GlobalsWidget *globalsDock = nullptr;
RelocsWidget *relocsDock = nullptr;
CommentsWidget *commentsDock = nullptr;
StringsWidget *stringsDock = nullptr;

View File

@ -128,6 +128,12 @@
<addaction name="actionSaveLayout"/>
<addaction name="menuLayouts"/>
</widget>
<widget class="QMenu" name="menuTools">
<property name="title">
<string>Tools</string>
</property>
<addaction name="actionBaseFind"/>
</widget>
<widget class="QMenu" name="menuHelp">
<property name="title">
<string>Help</string>
@ -184,6 +190,7 @@
<addaction name="menuView"/>
<addaction name="menuWindows"/>
<addaction name="menuDebug"/>
<addaction name="menuTools"/>
<addaction name="menuHelp"/>
</widget>
<widget class="QToolBar" name="mainToolBar">
@ -233,6 +240,11 @@
<string>Zen mode</string>
</property>
</action>
<action name="actionBaseFind">
<property name="text">
<string>BaseFind</string>
</property>
</action>
<action name="actionAbout">
<property name="text">
<string>About</string>

View File

@ -1,2 +1 @@
#include "RizinCpp.h"

View File

@ -46,8 +46,7 @@ static inline auto fromOwned(RZ_OWN RzPVector *data)
return { data, {} };
}
static inline auto fromOwned(RZ_OWN RzList *data)
-> UniquePtrCP<decltype(data), &rz_list_free>
static inline auto fromOwned(RZ_OWN RzList *data) -> UniquePtrCP<decltype(data), &rz_list_free>
{
return { data, {} };
}

View File

@ -1,6 +1,8 @@
#include "CommentsDialog.h"
#include "ui_CommentsDialog.h"
#include <QErrorMessage>
#include "core/Cutter.h"
CommentsDialog::CommentsDialog(QWidget *parent) : QDialog(parent), ui(new Ui::CommentsDialog)

View File

@ -74,7 +74,7 @@ private:
QString fixedClass;
bool inputValid();
static QString convertRealNameToName(const QString& realName);
static QString convertRealNameToName(const QString &realName);
};
#endif // EDITMETHODDIALOG_H

View File

@ -49,8 +49,7 @@ void GlibcHeapBinsDialog::setChainInfo(int index)
RzHeapChunkListItem *item;
RzList *chunks = binsModel->getChunks(index);
QString chainInfo;
CutterRzListForeach(chunks, iter, RzHeapChunkListItem, item)
{
CutterRzListForeach (chunks, iter, RzHeapChunkListItem, item) {
chainInfo += "" + RzAddressString(item->addr);
}

View File

@ -0,0 +1,71 @@
#include "GlobalVariableDialog.h"
#include "ui_GlobalVariableDialog.h"
#include <QIntValidator>
#include "core/Cutter.h"
GlobalVariableDialog::GlobalVariableDialog(RVA offset, QWidget *parent)
: QDialog(parent),
ui(new Ui::GlobalVariableDialog),
offset(offset),
globalVariableName(""),
globalVariableOffset(RVA_INVALID)
{
// Setup UI
ui->setupUi(this);
setWindowFlags(windowFlags() & (~Qt::WindowContextHelpButtonHint));
RzAnalysisVarGlobal *globalVariable =
rz_analysis_var_global_get_byaddr_at(Core()->core()->analysis, offset);
if (globalVariable) {
globalVariableName = QString(globalVariable->name);
globalVariableOffset = globalVariable->addr;
}
if (globalVariable) {
ui->nameEdit->setText(globalVariable->name);
QString globalVarType = Core()->getGlobalVariableType(globalVariable->name);
ui->typeEdit->setText(globalVarType);
ui->labelAction->setText(tr("Edit global variable at %1").arg(RzAddressString(offset)));
} else {
ui->labelAction->setText(tr("Add global variable at %1").arg(RzAddressString(offset)));
}
// Connect slots
connect(ui->buttonBox, &QDialogButtonBox::accepted, this,
&GlobalVariableDialog::buttonBoxAccepted);
connect(ui->buttonBox, &QDialogButtonBox::rejected, this,
&GlobalVariableDialog::buttonBoxRejected);
}
GlobalVariableDialog::~GlobalVariableDialog() {}
void GlobalVariableDialog::buttonBoxAccepted()
{
QString name = ui->nameEdit->text();
QString typ = ui->typeEdit->text();
if (name.isEmpty()) {
if (globalVariableOffset != RVA_INVALID) {
// Empty name and global variable exists -> delete the global variable
Core()->delGlobalVariable(globalVariableOffset);
} else {
// GlobalVariable was not existing and we gave an empty name, do nothing
}
} else {
if (globalVariableOffset != RVA_INVALID) {
// Name provided and global variable exists -> rename the global variable
Core()->modifyGlobalVariable(globalVariableOffset, name, typ);
} else {
// Name provided and global variable does not exist -> create the global variable
Core()->addGlobalVariable(offset, name, typ);
}
}
close();
this->setResult(QDialog::Accepted);
}
void GlobalVariableDialog::buttonBoxRejected()
{
close();
this->setResult(QDialog::Rejected);
}

View File

@ -0,0 +1,32 @@
#ifndef GLOBALVARIABLEDIALOG_H
#define GLOBALVARIABLEDIALOG_H
#include <QDialog>
#include <memory>
#include "core/CutterCommon.h"
namespace Ui {
class GlobalVariableDialog;
}
class GlobalVariableDialog : public QDialog
{
Q_OBJECT
public:
explicit GlobalVariableDialog(RVA offset, QWidget *parent = nullptr);
~GlobalVariableDialog();
private slots:
void buttonBoxAccepted();
void buttonBoxRejected();
private:
std::unique_ptr<Ui::GlobalVariableDialog> ui;
RVA offset;
QString globalVariableName;
RVA globalVariableOffset;
QString typ;
};
#endif // GLOBALVARIABLEDIALOG_H

View File

@ -0,0 +1,100 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>GlobalVariableDialog</class>
<widget class="QDialog" name="GlobalVariableDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>452</width>
<height>121</height>
</rect>
</property>
<property name="windowTitle">
<string>Add Global Variable</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="labelAction">
<property name="text">
<string>Add global variable at</string>
</property>
</widget>
</item>
<item>
<layout class="QFormLayout" name="formLayout">
<property name="horizontalSpacing">
<number>12</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="labelName">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Name:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="nameEdit">
<property name="frame">
<bool>false</bool>
</property>
<property name="placeholderText">
<string notr="true"/>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="typeEdit">
<property name="maximumSize">
<size>
<width>100</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>int</string>
</property>
<property name="frame">
<bool>false</bool>
</property>
<property name="placeholderText">
<string notr="true"/>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="labelType">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Type:</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -14,6 +14,7 @@
#include "core/Cutter.h"
#include "common/AnalysisTask.h"
#include "CutterApplication.h"
InitialOptionsDialog::InitialOptionsDialog(MainWindow *main)
: QDialog(nullptr), // parent must not be main
@ -179,9 +180,56 @@ void InitialOptionsDialog::loadOptions(const InitialOptions &options)
ui->entry_loadOffset->setText(RzAddressString(options.binLoadAddr));
}
if (options.mapAddr != RVA_INVALID) {
ui->entry_mapOffset->setText(RzAddressString(options.mapAddr));
}
ui->vaCheckBox->setChecked(options.useVA);
ui->writeCheckBox->setChecked(options.writeEnabled);
// TODO: all other options should also be applied to the ui
if (!options.arch.isNull() && !options.arch.isEmpty()) {
ui->archComboBox->setCurrentText(options.arch);
}
if (!options.cpu.isNull() && !options.cpu.isEmpty()) {
ui->cpuComboBox->setCurrentText(options.cpu);
}
if (options.bits > 0) {
ui->bitsComboBox->setCurrentText(QString::asprintf("%d", options.bits));
}
if (!options.os.isNull() && !options.os.isEmpty()) {
ui->kernelComboBox->setCurrentText(options.os);
}
if (!options.forceBinPlugin.isNull() && !options.forceBinPlugin.isEmpty()) {
ui->formatComboBox->setCurrentText(options.forceBinPlugin);
}
if (!options.loadBinInfo) {
ui->binCheckBox->setChecked(false);
}
ui->writeCheckBox->setChecked(options.writeEnabled);
switch (options.endian) {
case InitialOptions::Endianness::Little:
ui->endiannessComboBox->setCurrentIndex(1);
break;
case InitialOptions::Endianness::Big:
ui->endiannessComboBox->setCurrentIndex(2);
break;
default:
break;
}
ui->demangleCheckBox->setChecked(options.demangle);
if (!options.pdbFile.isNull() && !options.pdbFile.isEmpty()) {
ui->pdbCheckBox->setChecked(true);
ui->pdbLineEdit->setText(options.pdbFile);
}
}
void InitialOptionsDialog::setTooltipWithConfigHelp(QWidget *w, const char *config)
@ -246,7 +294,7 @@ QList<CommandDescription> InitialOptionsDialog::getSelectedAdvancedAnalCmds() co
return advanced;
}
void InitialOptionsDialog::setupAndStartAnalysis(/*int level, QList<QString> advanced*/)
void InitialOptionsDialog::setupAndStartAnalysis()
{
InitialOptions options;
@ -322,6 +370,8 @@ void InitialOptionsDialog::setupAndStartAnalysis(/*int level, QList<QString> adv
Core()->getAsyncTaskManager()->start(analysisTaskPtr);
done(0);
static_cast<CutterApplication *>(qApp)->setInitialOptions(options);
}
void InitialOptionsDialog::on_okButton_clicked()

View File

@ -20,7 +20,7 @@ public:
explicit InitialOptionsDialog(MainWindow *main);
~InitialOptionsDialog();
void setupAndStartAnalysis(/*int level, QList<QString> advanced*/);
void setupAndStartAnalysis();
private slots:
void on_okButton_clicked();

View File

@ -287,7 +287,8 @@ void NewFileDialog::fillIOPluginsList()
{
ui->ioPlugin->clear();
ui->ioPlugin->addItem("file://");
ui->ioPlugin->setItemData(0, tr("Open a file without additional options/settings."), Qt::ToolTipRole);
ui->ioPlugin->setItemData(0, tr("Open a file without additional options/settings."),
Qt::ToolTipRole);
int index = 1;
QList<RzIOPluginDescription> ioPlugins = Core()->getRIOPluginDescriptions();

View File

@ -66,12 +66,14 @@ void TypesInteractionDialog::done(int r)
bool success;
if (!typeName.isEmpty()) {
success = rz_type_db_edit_base_type(
core->analysis->typedb, this->typeName.toUtf8().constData(),
ui->plainTextEdit->toPlainText().toUtf8().constData());
core->analysis->typedb, this->typeName.toUtf8().constData(),
ui->plainTextEdit->toPlainText().toUtf8().constData());
} else {
char *error_msg = NULL;
success = rz_type_parse_string_stateless(core->analysis->typedb->parser,
ui->plainTextEdit->toPlainText().toUtf8().constData(), &error_msg) == 0;
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);

View File

@ -65,6 +65,12 @@ AsmOptionsWidget::AsmOptionsWidget(PreferencesDialog *dialog)
&AsmOptionsWidget::relOffCheckBoxToggled);
connect(Core(), &CutterCore::asmOptionsChanged, this,
&AsmOptionsWidget::updateAsmOptionsFromVars);
connect(ui->varTooltipsCheckBox, &QCheckBox::toggled, [this](bool checked) {
Config()->setShowVarTooltips(checked);
triggerAsmOptionsChanged();
});
updateAsmOptionsFromVars();
}
@ -138,6 +144,8 @@ void AsmOptionsWidget::updateAsmOptionsFromVars()
ui->previewCheckBox->setChecked(Config()->getPreviewValue());
ui->previewCheckBox->blockSignals(false);
qhelpers::setCheckedWithoutSignals(ui->varTooltipsCheckBox, Config()->getShowVarTooltips());
QList<ConfigCheckbox>::iterator confCheckbox;
// Set the value for each checkbox in "checkboxes" as it exists in the configuration

View File

@ -48,8 +48,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>582</width>
<height>766</height>
<width>686</width>
<height>886</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
@ -65,51 +65,114 @@
<string>Disassembly</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="12" column="2">
<widget class="QSpinBox" name="nbytesSpinBox">
<property name="enabled">
<bool>true</bool>
</property>
<item row="5" column="2">
<widget class="QComboBox" name="caseComboBox">
<item>
<property name="text">
<string>Lowercase</string>
</property>
</item>
<item>
<property name="text">
<string>Uppercase (asm.ucase)</string>
</property>
</item>
<item>
<property name="text">
<string>Capitalize (asm.capitalize)</string>
</property>
</item>
</widget>
</item>
<item row="17" column="2">
<widget class="QSpinBox" name="asmTabsOffSpinBox">
<property name="maximum">
<number>100</number>
</property>
<property name="singleStep">
<number>5</number>
</property>
</widget>
</item>
<item row="19" column="1" colspan="2">
<widget class="QCheckBox" name="lbytesCheckBox">
<item row="17" column="1">
<widget class="QLabel" name="asmTabsLabel">
<property name="text">
<string>Align bytes to the left (asm.lbytes)</string>
</property>
</widget>
</item>
<item row="15" column="1">
<widget class="QCheckBox" name="previewCheckBox">
<property name="text">
<string>Show preview when hovering:</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLabel" name="syntaxLabel">
<property name="text">
<string>Syntax (asm.syntax):</string>
<string>Tabs in assembly (asm.tabs):</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="8" column="2">
<widget class="QCheckBox" name="relOffFlagsCheckBox">
<item row="11" column="1">
<widget class="QCheckBox" name="bytesCheckBox">
<property name="text">
<string>Flags (asm.reloff.flags)</string>
<string>Display the bytes of each instruction (asm.bytes)</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLabel" name="label">
<property name="text">
<string>Show Disassembly as:</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="0" column="1" colspan="2">
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>10</height>
</size>
</property>
</spacer>
</item>
<item row="13" column="1">
<widget class="QCheckBox" name="realnameCheckBox">
<property name="text">
<string>Display flags' real name (asm.flags.real)</string>
</property>
</widget>
</item>
<item row="8" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="relOffsetLabel">
<property name="text">
<string>Show offsets relative to:</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="relOffsetCheckBox">
<property name="text">
<string>Functions (asm.reloff)</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="18" column="1">
<widget class="QLabel" name="asmTabsOffLabel">
<property name="text">
<string>The number of tabulate spaces after the offset (asm.tabs.off):</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="19" column="1" colspan="2">
<widget class="QCheckBox" name="indentCheckBox">
<property name="text">
<string>Indent disassembly based on reflines depth (asm.indent)</string>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="QCheckBox" name="offsetCheckBox">
<property name="text">
<string>Show offsets (asm.offset)</string>
</property>
</widget>
</item>
@ -132,23 +195,47 @@
</item>
</widget>
</item>
<item row="7" column="1">
<widget class="QCheckBox" name="offsetCheckBox">
<item row="14" column="1" colspan="2">
<widget class="QCheckBox" name="bblineCheckBox">
<property name="text">
<string>Show offsets (asm.offset)</string>
<string>Show empty line after every basic block (asm.bb.line)</string>
</property>
</widget>
</item>
<item row="17" column="1">
<widget class="QLabel" name="asmTabsOffLabel">
<item row="15" column="1">
<widget class="QCheckBox" name="previewCheckBox">
<property name="text">
<string>The number of tabulate spaces after the offset (asm.tabs.off):</string>
<string>Show preview when hovering</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLabel" name="syntaxLabel">
<property name="text">
<string>Syntax (asm.syntax):</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="21" column="1" colspan="2">
<widget class="QCheckBox" name="bytespaceCheckBox">
<property name="text">
<string>Separate bytes with whitespace (asm.bytes.space)</string>
</property>
</widget>
</item>
<item row="18" column="2">
<widget class="QSpinBox" name="asmTabsOffSpinBox">
<property name="maximum">
<number>100</number>
</property>
<property name="singleStep">
<number>5</number>
</property>
</widget>
</item>
<item row="12" column="1">
<widget class="QLabel" name="nbytesLabel">
<property name="enabled">
@ -163,13 +250,13 @@
</widget>
</item>
<item row="20" column="1" colspan="2">
<widget class="QCheckBox" name="bytespaceCheckBox">
<widget class="QCheckBox" name="lbytesCheckBox">
<property name="text">
<string>Separate bytes with whitespace (asm.bytes.space)</string>
<string>Align bytes to the left (asm.lbytes)</string>
</property>
</widget>
</item>
<item row="16" column="2">
<item row="17" column="2">
<widget class="QSpinBox" name="asmTabsSpinBox">
<property name="maximum">
<number>100</number>
@ -179,107 +266,27 @@
</property>
</widget>
</item>
<item row="11" column="1">
<widget class="QCheckBox" name="bytesCheckBox">
<item row="8" column="2">
<widget class="QCheckBox" name="relOffFlagsCheckBox">
<property name="text">
<string>Display the bytes of each instruction (asm.bytes)</string>
<string>Flags (asm.reloff.flags)</string>
</property>
</widget>
</item>
<item row="5" column="2">
<widget class="QComboBox" name="caseComboBox">
<item>
<property name="text">
<string>Lowercase</string>
</property>
</item>
<item>
<property name="text">
<string>Uppercase (asm.ucase)</string>
</property>
</item>
<item>
<property name="text">
<string>Capitalize (asm.capitalize)</string>
</property>
</item>
</widget>
</item>
<item row="14" column="1" colspan="2">
<widget class="QCheckBox" name="bblineCheckBox">
<property name="text">
<string>Show empty line after every basic block (asm.bb.line)</string>
</property>
</widget>
</item>
<item row="8" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="relOffsetLabel">
<property name="text">
<string>Show offsets relative to:</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="relOffsetCheckBox">
<property name="text">
<string>Functions (asm.reloff)</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="4" column="2">
<widget class="QComboBox" name="syntaxComboBox"/>
</item>
<item row="0" column="1" colspan="2">
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>10</height>
</size>
</property>
</spacer>
</item>
<item row="3" column="1">
<widget class="QLabel" name="label">
<property name="text">
<string>Show Disassembly as:</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
<item row="12" column="2">
<widget class="QSpinBox" name="nbytesSpinBox">
<property name="enabled">
<bool>true</bool>
</property>
</widget>
</item>
<item row="16" column="1">
<widget class="QLabel" name="asmTabsLabel">
<widget class="QCheckBox" name="varTooltipsCheckBox">
<property name="text">
<string>Tabs in assembly (asm.tabs):</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="18" column="1" colspan="2">
<widget class="QCheckBox" name="indentCheckBox">
<property name="text">
<string>Indent disassembly based on reflines depth (asm.indent)</string>
</property>
</widget>
</item>
<item row="13" column="1">
<widget class="QCheckBox" name="realnameCheckBox">
<property name="text">
<string>Display flags' real name (asm.flags.real)</string>
<string>Show known variable values when hovering</string>
</property>
</widget>
</item>
@ -415,8 +422,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>454</width>
<height>286</height>
<width>518</width>
<height>326</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_6">

View File

@ -112,7 +112,8 @@ void AddressableItemContextMenu::aboutToShowSlot()
void AddressableItemContextMenu::setHasTarget(bool hasTarget)
{
this->hasTarget = hasTarget;
for (const auto &action : this->actions()) {
action->setEnabled(hasTarget);
}
actionShowInMenu->setEnabled(hasTarget);
actionCopyAddress->setEnabled(hasTarget);
actionShowXrefs->setEnabled(hasTarget);
actionAddcomment->setEnabled(hasTarget);
}

View File

@ -387,7 +387,7 @@ void DecompilerContextMenu::actionCopyReferenceAddressTriggered()
void DecompilerContextMenu::actionAddCommentTriggered()
{
CommentsDialog::addOrEditComment(this->firstOffsetInLine, this);
CommentsDialog::addOrEditComment(this->firstOffsetInLine, parentForDialog());
}
void DecompilerContextMenu::actionDeleteCommentTriggered()

View File

@ -3,6 +3,7 @@
#include "dialogs/EditInstructionDialog.h"
#include "dialogs/CommentsDialog.h"
#include "dialogs/FlagDialog.h"
#include "dialogs/GlobalVariableDialog.h"
#include "dialogs/XrefsDialog.h"
#include "dialogs/EditVariablesDialog.h"
#include "dialogs/SetToDataDialog.h"
@ -34,6 +35,7 @@ DisassemblyContextMenu::DisassemblyContextMenu(QWidget *parent, MainWindow *main
actionAnalyzeFunction(this),
actionEditFunction(this),
actionRename(this),
actionGlobalVar(this),
actionSetFunctionVarTypes(this),
actionXRefs(this),
actionXRefsForVariables(this),
@ -83,10 +85,6 @@ DisassemblyContextMenu::DisassemblyContextMenu(QWidget *parent, MainWindow *main
getCommentSequence());
addAction(&actionAddComment);
initAction(&actionRename, tr("Rename or add flag"), SLOT(on_actionRename_triggered()),
getRenameSequence());
addAction(&actionRename);
initAction(&actionSetFunctionVarTypes, tr("Re-type Local Variables"),
SLOT(on_actionSetFunctionVarTypes_triggered()), getRetypeSequence());
addAction(&actionSetFunctionVarTypes);
@ -112,6 +110,8 @@ DisassemblyContextMenu::DisassemblyContextMenu(QWidget *parent, MainWindow *main
addSeparator();
addAddAtMenu();
addSetBaseMenu();
addSetBitsMenu();
@ -161,6 +161,24 @@ DisassemblyContextMenu::DisassemblyContextMenu(QWidget *parent, MainWindow *main
DisassemblyContextMenu::~DisassemblyContextMenu() {}
QWidget *DisassemblyContextMenu::parentForDialog()
{
return parentWidget();
}
void DisassemblyContextMenu::addAddAtMenu()
{
setAsMenu = addMenu(tr("Add at..."));
initAction(&actionRename, tr("Rename or add flag"), SLOT(on_actionRename_triggered()),
getRenameSequence());
setAsMenu->addAction(&actionRename);
initAction(&actionGlobalVar, tr("Modify or add global variable"),
SLOT(on_actionGlobalVar_triggered()), getGlobalVarSequence());
setAsMenu->addAction(&actionGlobalVar);
}
void DisassemblyContextMenu::addSetBaseMenu()
{
setBaseMenu = addMenu(tr("Set base of immediate value to.."));
@ -314,8 +332,7 @@ void DisassemblyContextMenu::addDebugMenu()
QVector<DisassemblyContextMenu::ThingUsedHere> DisassemblyContextMenu::getThingUsedHere(RVA offset)
{
RzCoreLocked core(Core());
auto p = fromOwned(
rz_core_analysis_name(core, offset), rz_core_analysis_name_free);
auto p = fromOwned(rz_core_analysis_name(core, offset), rz_core_analysis_name_free);
if (!p) {
return {};
}
@ -475,7 +492,12 @@ void DisassemblyContextMenu::setupRenaming()
// Now, build the renaming menu and show it
buildRenameMenu(tuh);
auto name = RzAddressString(tuh->offset);
actionGlobalVar.setText(tr("Add or change global variable at %1 (used here)").arg(name));
actionRename.setVisible(true);
actionGlobalVar.setVisible(true);
}
void DisassemblyContextMenu::aboutToShowSlot()
@ -651,6 +673,11 @@ QKeySequence DisassemblyContextMenu::getRenameSequence() const
return { Qt::Key_N };
}
QKeySequence DisassemblyContextMenu::getGlobalVarSequence() const
{
return { Qt::Key_G };
}
QKeySequence DisassemblyContextMenu::getRetypeSequence() const
{
return { Qt::Key_Y };
@ -691,7 +718,7 @@ void DisassemblyContextMenu::on_actionEditInstruction_triggered()
if (!ioModesController.prepareForWriting()) {
return;
}
EditInstructionDialog e(EDIT_TEXT, this);
EditInstructionDialog e(EDIT_TEXT, parentForDialog());
e.setWindowTitle(tr("Edit Instruction at %1").arg(RzAddressString(offset)));
QString oldInstructionOpcode = Core()->getInstructionOpcode(offset);
@ -741,7 +768,7 @@ void DisassemblyContextMenu::on_actionEditBytes_triggered()
if (!ioModesController.prepareForWriting()) {
return;
}
EditInstructionDialog e(EDIT_BYTES, this);
EditInstructionDialog e(EDIT_BYTES, parentForDialog());
e.setWindowTitle(tr("Edit Bytes at %1").arg(RzAddressString(offset)));
QString oldBytes = Core()->getInstructionBytes(offset);
@ -775,9 +802,9 @@ void DisassemblyContextMenu::on_actionAdvancedBreakpoint_triggered()
{
int index = Core()->breakpointIndexAt(offset);
if (index >= 0) {
BreakpointsDialog::editBreakpoint(Core()->getBreakpointAt(offset), this);
BreakpointsDialog::editBreakpoint(Core()->getBreakpointAt(offset), parentForDialog());
} else {
BreakpointsDialog::createNewBreakpoint(offset, this);
BreakpointsDialog::createNewBreakpoint(offset, parentForDialog());
}
}
@ -794,12 +821,11 @@ void DisassemblyContextMenu::on_actionSetPC_triggered()
void DisassemblyContextMenu::on_actionAddComment_triggered()
{
CommentsDialog::addOrEditComment(offset, this);
CommentsDialog::addOrEditComment(offset, parentForDialog());
}
void DisassemblyContextMenu::on_actionAnalyzeFunction_triggered()
{
bool ok;
RVA flagOffset;
QString name = Core()->nearestFlag(offset, &flagOffset);
if (name.isEmpty() || flagOffset != offset) {
@ -812,12 +838,20 @@ void DisassemblyContextMenu::on_actionAnalyzeFunction_triggered()
}
// Create dialog
QString functionName =
QInputDialog::getText(this, tr("New function at %1").arg(RzAddressString(offset)),
tr("Function name:"), QLineEdit::Normal, name, &ok);
QInputDialog inputDialog(parentForDialog());
inputDialog.resize(500, 100);
inputDialog.setWindowTitle(tr("New function at %1").arg(RzAddressString(offset)));
inputDialog.setLabelText(tr("Function name:"));
inputDialog.setTextValue(name);
inputDialog.setWindowFlags(Qt::Window | Qt::WindowMinimizeButtonHint);
// If user accepted
if (ok && !functionName.isEmpty()) {
if (inputDialog.exec() != QDialog::Accepted) {
return;
}
QString functionName = inputDialog.textValue().trimmed();
if (!functionName.isEmpty()) {
Core()->createFunctionAt(offset, functionName);
}
}
@ -833,12 +867,12 @@ void DisassemblyContextMenu::on_actionRename_triggered()
Core()->renameFunction(doRenameInfo.addr, newName);
}
} else if (doRenameAction == RENAME_FLAG || doRenameAction == RENAME_ADD_FLAG) {
FlagDialog dialog(doRenameInfo.addr, this->mainWindow);
FlagDialog dialog(doRenameInfo.addr, parentForDialog());
ok = dialog.exec();
} else if (doRenameAction == RENAME_LOCAL) {
RzAnalysisFunction *fcn = Core()->functionIn(offset);
if (fcn) {
EditVariablesDialog dialog(fcn->addr, curHighlightedWord, this->mainWindow);
EditVariablesDialog dialog(fcn->addr, curHighlightedWord, parentForDialog());
if (!dialog.empty()) {
// Don't show the dialog if there are no variables
ok = dialog.exec();
@ -857,6 +891,18 @@ void DisassemblyContextMenu::on_actionRename_triggered()
}
}
void DisassemblyContextMenu::on_actionGlobalVar_triggered()
{
bool ok = false;
GlobalVariableDialog dialog(doRenameInfo.addr, parentForDialog());
ok = dialog.exec();
if (ok) {
// Rebuild menu in case the user presses the rename shortcut directly before clicking
setupRenaming();
}
}
void DisassemblyContextMenu::on_actionSetFunctionVarTypes_triggered()
{
RzAnalysisFunction *fcn = Core()->functionIn(offset);
@ -867,7 +913,7 @@ void DisassemblyContextMenu::on_actionSetFunctionVarTypes_triggered()
return;
}
EditVariablesDialog dialog(fcn->addr, curHighlightedWord, this->mainWindow);
EditVariablesDialog dialog(fcn->addr, curHighlightedWord, parentForDialog());
if (dialog.empty()) { // don't show the dialog if there are no variables
return;
}
@ -892,7 +938,7 @@ void DisassemblyContextMenu::on_actionXRefsForVariables_triggered()
void DisassemblyContextMenu::on_actionDisplayOptions_triggered()
{
PreferencesDialog dialog(this->window());
PreferencesDialog dialog(parentForDialog());
dialog.showSection(PreferencesDialog::Section::Disassembly);
dialog.exec();
}
@ -914,7 +960,7 @@ void DisassemblyContextMenu::on_actionSetAsStringRemove_triggered()
void DisassemblyContextMenu::on_actionSetAsStringAdvanced_triggered()
{
EditStringDialog dialog(parentWidget());
EditStringDialog dialog(parentForDialog());
const int predictedStrSize = Core()->getString(offset).size();
dialog.setStringSizeValue(predictedStrSize);
dialog.setStringStartAddress(offset);
@ -964,7 +1010,7 @@ void DisassemblyContextMenu::on_actionSetToData_triggered()
void DisassemblyContextMenu::on_actionSetToDataEx_triggered()
{
SetToDataDialog dialog(offset, this->window());
SetToDataDialog dialog(offset, parentForDialog());
if (!dialog.exec()) {
return;
}
@ -994,7 +1040,7 @@ void DisassemblyContextMenu::on_actionDeleteFunction_triggered()
void DisassemblyContextMenu::on_actionEditFunction_triggered()
{
RzCore *core = Core()->core();
EditFunctionDialog dialog(mainWindow);
EditFunctionDialog dialog(parentForDialog());
RzAnalysisFunction *fcn = rz_analysis_get_fcn_in(core->analysis, offset, 0);
if (fcn) {

View File

@ -45,6 +45,7 @@ private slots:
void on_actionAddComment_triggered();
void on_actionAnalyzeFunction_triggered();
void on_actionRename_triggered();
void on_actionGlobalVar_triggered();
void on_actionSetFunctionVarTypes_triggered();
void on_actionXRefs_triggered();
void on_actionXRefsForVariables_triggered();
@ -78,6 +79,7 @@ private:
QKeySequence getCopySequence() const;
QKeySequence getCommentSequence() const;
QKeySequence getCopyAddressSequence() const;
QKeySequence getGlobalVarSequence() const;
QKeySequence getSetToCodeSequence() const;
QKeySequence getSetAsStringSequence() const;
QKeySequence getSetAsStringAdvanced() const;
@ -114,6 +116,7 @@ private:
QAction actionAnalyzeFunction;
QAction actionEditFunction;
QAction actionRename;
QAction actionGlobalVar;
QAction actionSetFunctionVarTypes;
QAction actionXRefs;
QAction actionXRefsForVariables;
@ -168,6 +171,11 @@ private:
QMenu *pluginMenu = nullptr;
QAction *pluginActionMenuAction = nullptr;
/**
* \return widget that should be used as parent for presenting dialogs
*/
QWidget *parentForDialog();
// For creating anonymous entries (that are always visible)
QAction *addAnonymousAction(QString name, const char *slot, QKeySequence shortcut);
@ -185,6 +193,7 @@ private:
void addSetAsMenu();
void addSetToDataMenu();
void addEditMenu();
void addAddAtMenu();
void addBreakpointMenu();
void addDebugMenu();

View File

@ -25,7 +25,6 @@
<update_contact>xarkes</update_contact>
<releases>
<release version="2.2.1" date="2023-05-15" />
<release version="2.2.0" date="2023-02-22" />
<release version="2.1.2" date="2022-09-11" />
<release version="2.1.1" date="2022-09-10" />

View File

@ -0,0 +1,97 @@
#include "BaseFindDialog.h"
#include "ui_BaseFindDialog.h"
#include "BaseFindSearchDialog.h"
#include <core/Cutter.h>
#include <rz_th.h>
BaseFindDialog::BaseFindDialog(QWidget *parent) : QDialog(parent), ui(new Ui::BaseFindDialog)
{
ui->setupUi(this);
setWindowFlags(windowFlags() & (~Qt::WindowContextHelpButtonHint));
// Fill in N-thread Combo
size_t n_cores = rz_th_physical_core_number();
ui->nCoresCombo->clear();
for (size_t i = n_cores; i > 0; i--) {
if (n_cores == i) {
ui->nCoresCombo->addItem("All Cores");
continue;
}
ui->nCoresCombo->addItem(QString::number(i));
}
ui->startAddressEdit->setText(Core()->getConfig("basefind.search.start"));
ui->endAddressEdit->setText(Core()->getConfig("basefind.search.end"));
ui->alignmentEdit->setText(Core()->getConfig("basefind.alignment"));
ui->minStrLenEdit->setValue(Core()->getConfigut64("basefind.min.string"));
ui->minScoreEdit->setValue(Core()->getConfigut64("basefind.min.score"));
size_t selected_n_cores = Core()->getConfigut64("basefind.max.threads");
if (selected_n_cores < n_cores && selected_n_cores > 0) {
ui->nCoresCombo->setCurrentIndex(n_cores - selected_n_cores);
}
}
BaseFindDialog::~BaseFindDialog() {}
size_t BaseFindDialog::getNCores() const
{
size_t n_cores = rz_th_physical_core_number();
return n_cores - ui->nCoresCombo->currentIndex();
}
ut32 BaseFindDialog::getPointerSize() const
{
auto index = ui->pointerSizeCombo->currentIndex();
QString value = ui->pointerSizeCombo->itemText(index);
return value.toULong(nullptr, 0);
}
RVA BaseFindDialog::getStartAddress() const
{
QString value = ui->startAddressEdit->text();
return value.toULongLong(nullptr, 0);
}
RVA BaseFindDialog::getEndAddress() const
{
QString value = ui->endAddressEdit->text();
return value.toULongLong(nullptr, 0);
}
RVA BaseFindDialog::getAlignment() const
{
QString value = ui->alignmentEdit->text();
return value.toULongLong(nullptr, 0);
}
ut32 BaseFindDialog::getMinStrLen() const
{
return ui->minStrLenEdit->value();
}
ut32 BaseFindDialog::getMinScore() const
{
return ui->minScoreEdit->value();
}
void BaseFindDialog::on_buttonBox_accepted()
{
RzBaseFindOpt options = {};
options.max_threads = getNCores();
options.pointer_size = getPointerSize();
options.start_address = getStartAddress();
options.end_address = getEndAddress();
options.alignment = getAlignment();
options.min_score = getMinScore();
options.min_string_len = getMinStrLen();
options.callback = nullptr;
options.user = nullptr;
BaseFindSearchDialog *bfs = new BaseFindSearchDialog(parentWidget());
bfs->show(&options);
}
void BaseFindDialog::on_buttonBox_rejected() {}

View File

@ -0,0 +1,38 @@
#ifndef BASEFIND_DIALOG_H
#define BASEFIND_DIALOG_H
#include <QDialog>
#include <QListWidgetItem>
#include <memory>
#include <core/Cutter.h>
namespace Ui {
class BaseFindDialog;
}
class BaseFindDialog : public QDialog
{
Q_OBJECT
public:
explicit BaseFindDialog(QWidget *parent = nullptr);
~BaseFindDialog();
size_t getNCores() const;
ut32 getPointerSize() const;
RVA getStartAddress() const;
RVA getEndAddress() const;
RVA getAlignment() const;
ut32 getMinStrLen() const;
ut32 getMinScore() const;
private slots:
void on_buttonBox_accepted();
void on_buttonBox_rejected();
private:
std::unique_ptr<Ui::BaseFindDialog> ui;
};
#endif // BASEFIND_DIALOG_H

View File

@ -0,0 +1,246 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>BaseFindDialog</class>
<widget class="QDialog" name="BaseFindDialog">
<property name="windowModality">
<enum>Qt::NonModal</enum>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>373</width>
<height>348</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string notr="true">BaseFind</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="sizeConstraint">
<enum>QLayout::SetMinimumSize</enum>
</property>
<item>
<layout class="QGridLayout" name="gridLayout">
<property name="sizeConstraint">
<enum>QLayout::SetMinimumSize</enum>
</property>
<item row="0" column="0">
<widget class="QLabel" name="nCoresLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Cores:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="nCoresCombo"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="pointerSizeLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Pointer Size:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="pointerSizeCombo">
<item>
<property name="text">
<string>32</string>
</property>
</item>
<item>
<property name="text">
<string>64</string>
</property>
</item>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="startAddressLabel">
<property name="text">
<string>Start Address:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="startAddressEdit">
<property name="maximumSize">
<size>
<width>382</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="endAddressLabel">
<property name="text">
<string>End Address:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="endAddressEdit">
<property name="maximumSize">
<size>
<width>382</width>
<height>16777215</height>
</size>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="alignmentLabel">
<property name="text">
<string>Alignment:</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLineEdit" name="alignmentEdit">
<property name="maximumSize">
<size>
<width>382</width>
<height>16777215</height>
</size>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="minStrLenLabel">
<property name="text">
<string>Min String Length:</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QSpinBox" name="minStrLenEdit">
<property name="maximumSize">
<size>
<width>382</width>
<height>16777215</height>
</size>
</property>
<property name="minimum">
<number>4</number>
</property>
<property name="maximum">
<number>999999999</number>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="minScoreLabel">
<property name="text">
<string>Min Score:</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QSpinBox" name="minScoreEdit">
<property name="maximumSize">
<size>
<width>382</width>
<height>16777215</height>
</size>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>999999999</number>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="sizeConstraint">
<enum>QLayout::SetMinimumSize</enum>
</property>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
<action name="actionRemoveItem">
<property name="text">
<string>Remove item</string>
</property>
</action>
<action name="actionRemoveAll">
<property name="text">
<string>Remove all</string>
</property>
<property name="toolTip">
<string>Remove all</string>
</property>
</action>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>BaseFindDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>234</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>BaseFindDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>240</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>254</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -0,0 +1,161 @@
#include "BaseFindResultsDialog.h"
#include "ui_BaseFindResultsDialog.h"
#include <QClipboard>
#include <QMessageBox>
#include <core/Cutter.h>
#include <CutterApplication.h>
BaseFindResultsModel::BaseFindResultsModel(QList<BasefindResultDescription> *list, QObject *parent)
: QAbstractListModel(parent), list(list)
{
}
int BaseFindResultsModel::rowCount(const QModelIndex &) const
{
return list->count();
}
int BaseFindResultsModel::columnCount(const QModelIndex &) const
{
return BaseFindResultsModel::ColumnCount;
}
QVariant BaseFindResultsModel::data(const QModelIndex &index, int role) const
{
if (index.row() >= list->count())
return QVariant();
const BasefindResultDescription &entry = list->at(index.row());
switch (role) {
case Qt::DisplayRole:
switch (index.column()) {
case ScoreColumn:
return QString::asprintf("%u", entry.score);
case CandidateColumn:
return QString::asprintf("%#010llx", entry.candidate);
default:
return QVariant();
}
case Qt::ToolTipRole: {
return QString::asprintf("%#010llx", entry.candidate);
}
default:
return QVariant();
}
}
QVariant BaseFindResultsModel::headerData(int section, Qt::Orientation, int role) const
{
switch (role) {
case Qt::DisplayRole:
switch (section) {
case ScoreColumn:
return tr("Score");
case CandidateColumn:
return tr("Address");
default:
return QVariant();
}
default:
return QVariant();
}
}
BaseFindResultsDialog::BaseFindResultsDialog(QList<BasefindResultDescription> results,
QWidget *parent)
: QDialog(parent), list(results), ui(new Ui::BaseFindResultsDialog)
{
ui->setupUi(this);
setWindowFlags(windowFlags() & (~Qt::WindowContextHelpButtonHint));
model = new BaseFindResultsModel(&list, this);
ui->tableView->setModel(model);
ui->tableView->sortByColumn(BaseFindResultsModel::ScoreColumn, Qt::AscendingOrder);
ui->tableView->verticalHeader()->hide();
ui->tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
ui->tableView->setContextMenuPolicy(Qt::CustomContextMenu);
blockMenu = new QMenu(this);
actionCopyCandidate = new QAction(tr("Copy %1"), this);
actionSetLoadAddr = new QAction(tr("Reopen Cutter with base address as %1"), this);
actionSetMapAddr = new QAction(tr("Reopen Cutter with map address as %1"), this);
connect(ui->tableView, &QWidget::customContextMenuRequested, this,
&BaseFindResultsDialog::showItemContextMenu);
connect(actionCopyCandidate, &QAction::triggered, this,
&BaseFindResultsDialog::onActionCopyLine);
connect(actionSetLoadAddr, &QAction::triggered, this,
&BaseFindResultsDialog::onActionSetLoadAddr);
connect(actionSetMapAddr, &QAction::triggered, this,
&BaseFindResultsDialog::onActionSetMapAddr);
blockMenu->addAction(actionSetLoadAddr);
blockMenu->addAction(actionSetMapAddr);
blockMenu->addAction(actionCopyCandidate);
addActions(blockMenu->actions());
}
void BaseFindResultsDialog::showItemContextMenu(const QPoint &pt)
{
auto index = ui->tableView->currentIndex();
if (index.isValid()) {
const BasefindResultDescription &entry = list.at(index.row());
candidate = entry.candidate;
auto addr = QString::asprintf("%#010llx", candidate);
actionCopyCandidate->setText(tr("Copy %1").arg(addr));
actionSetLoadAddr->setText(tr("Reopen Cutter with base address as %1").arg(addr));
actionSetMapAddr->setText(tr("Reopen Cutter with map address as %1").arg(addr));
blockMenu->exec(this->mapToGlobal(pt));
}
}
void BaseFindResultsDialog::onActionCopyLine()
{
auto clipboard = QApplication::clipboard();
clipboard->setText(QString::asprintf("%#010llx", candidate));
}
void BaseFindResultsDialog::onActionSetLoadAddr()
{
auto cutter = static_cast<CutterApplication *>(qApp);
auto options = cutter->getInitialOptions();
auto oldValue = options.binLoadAddr;
// override options to generate correct args
options.binLoadAddr = candidate;
cutter->setInitialOptions(options);
auto args = cutter->getArgs();
// revert back options
options.binLoadAddr = oldValue;
cutter->setInitialOptions(options);
cutter->launchNewInstance(args);
}
void BaseFindResultsDialog::onActionSetMapAddr()
{
auto cutter = static_cast<CutterApplication *>(qApp);
auto options = cutter->getInitialOptions();
auto oldValue = options.mapAddr;
// override options to generate correct args
options.mapAddr = candidate;
cutter->setInitialOptions(options);
auto args = cutter->getArgs();
// revert back options
options.mapAddr = oldValue;
cutter->setInitialOptions(options);
cutter->launchNewInstance(args);
}
BaseFindResultsDialog::~BaseFindResultsDialog() {}
void BaseFindResultsDialog::on_buttonBox_rejected() {}

View File

@ -0,0 +1,68 @@
#ifndef BASEFIND_RESULTS_DIALOG_H
#define BASEFIND_RESULTS_DIALOG_H
#include <QDialog>
#include <QAbstractListModel>
#include <QSortFilterProxyModel>
#include <memory>
#include <core/Cutter.h>
class BaseFindResultsDialog;
namespace Ui {
class BaseFindResultsDialog;
}
class BaseFindResultsModel : public QAbstractListModel
{
Q_OBJECT
friend BaseFindResultsDialog;
public:
enum Column { ScoreColumn = 0, CandidateColumn, ColumnCount };
BaseFindResultsModel(QList<BasefindResultDescription> *list, QObject *parent = nullptr);
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
int rowCount(const QModelIndex &parent = QModelIndex()) const;
int columnCount(const QModelIndex &parent = QModelIndex()) const;
private:
QList<BasefindResultDescription> *list;
};
class BaseFindResultsDialog : public QDialog
{
Q_OBJECT
public:
explicit BaseFindResultsDialog(QList<BasefindResultDescription> results,
QWidget *parent = nullptr);
~BaseFindResultsDialog();
public slots:
void showItemContextMenu(const QPoint &pt);
private slots:
void on_buttonBox_rejected();
private:
void onActionCopyLine();
void onActionSetLoadAddr();
void onActionSetMapAddr();
QList<BasefindResultDescription> list;
std::unique_ptr<Ui::BaseFindResultsDialog> ui;
BaseFindResultsModel *model;
QMenu *blockMenu;
QAction *actionCopyCandidate;
QAction *actionSetLoadAddr;
QAction *actionSetMapAddr;
RVA candidate;
};
#endif // BASEFIND_RESULTS_DIALOG_H

View File

@ -0,0 +1,70 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>BaseFindResultsDialog</class>
<widget class="QDialog" name="BaseFindResultsDialog">
<property name="windowModality">
<enum>Qt::NonModal</enum>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>582</width>
<height>382</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string notr="true">BaseFind Results</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="sizeConstraint">
<enum>QLayout::SetMinimumSize</enum>
</property>
<item>
<layout class="QVBoxLayout" name="mainVerticalLayout">
<property name="sizeConstraint">
<enum>QLayout::SetMinimumSize</enum>
</property>
<item>
<widget class="QTableView" name="tableView"/>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Close</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>BaseFindResultsDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>240</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>254</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -0,0 +1,66 @@
#include "BaseFindSearchDialog.h"
#include "ui_BaseFindSearchDialog.h"
#include "BaseFindResultsDialog.h"
#include <QLabel>
#include <QFormLayout>
#include <core/Cutter.h>
#include <rz_th.h>
BaseFindSearchDialog::BaseFindSearchDialog(QWidget *parent)
: QDialog(parent), basefind(new Basefind(Core())), ui(new Ui::BaseFindSearchDialog)
{
ui->setupUi(this);
setWindowFlags(windowFlags() & (~Qt::WindowContextHelpButtonHint));
}
BaseFindSearchDialog::~BaseFindSearchDialog() {}
void BaseFindSearchDialog::show(RzBaseFindOpt *opts)
{
size_t n_cores = rz_th_physical_core_number();
if (opts->max_threads > n_cores || opts->max_threads < 1) {
opts->max_threads = n_cores;
}
QFormLayout *layout = new QFormLayout();
ui->scrollAreaWidgetContents->setLayout(layout);
for (ut32 i = 0; i < opts->max_threads; ++i) {
QString label = QString::asprintf("Core %u", i);
QProgressBar *pbar = new QProgressBar(nullptr);
layout->addRow(label, pbar);
pbar->setRange(0, 100);
bars.push_back(pbar);
}
if (!basefind->setOptions(opts)) {
return;
}
connect(this, &BaseFindSearchDialog::cancelSearch, basefind.get(), &Basefind::cancel);
connect(basefind.get(), &Basefind::progress, this, &BaseFindSearchDialog::onProgress);
connect(basefind.get(), &Basefind::complete, this, &BaseFindSearchDialog::onCompletion);
basefind->start();
this->QDialog::show();
}
void BaseFindSearchDialog::onProgress(BasefindCoreStatusDescription status)
{
bars[status.index]->setValue(status.percentage);
}
void BaseFindSearchDialog::onCompletion()
{
auto results = basefind->results();
BaseFindResultsDialog *table = new BaseFindResultsDialog(results, parentWidget());
table->show();
this->close();
}
void BaseFindSearchDialog::on_buttonBox_rejected()
{
emit cancelSearch();
}

View File

@ -0,0 +1,41 @@
#ifndef BASEFIND_SEARCH_DIALOG_H
#define BASEFIND_SEARCH_DIALOG_H
#include <QDialog>
#include <QListWidgetItem>
#include <QProgressBar>
#include <memory>
#include <core/Cutter.h>
namespace Ui {
class BaseFindSearchDialog;
}
class BaseFindSearchDialog : public QDialog
{
Q_OBJECT
public:
explicit BaseFindSearchDialog(QWidget *parent = nullptr);
~BaseFindSearchDialog();
void show(RzBaseFindOpt *opts);
public slots:
void onProgress(BasefindCoreStatusDescription status);
void onCompletion();
signals:
void cancelSearch();
private slots:
void on_buttonBox_rejected();
private:
std::vector<QProgressBar *> bars;
std::unique_ptr<Basefind> basefind;
std::unique_ptr<Ui::BaseFindSearchDialog> ui;
};
#endif // BASEFIND_SEARCH_DIALOG_H

View File

@ -0,0 +1,112 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>BaseFindSearchDialog</class>
<widget class="QDialog" name="BaseFindSearchDialog">
<property name="windowModality">
<enum>Qt::NonModal</enum>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>582</width>
<height>382</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string notr="true">Searching for base address</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="sizeConstraint">
<enum>QLayout::SetMinimumSize</enum>
</property>
<item>
<layout class="QVBoxLayout" name="mainVerticalLayout">
<property name="sizeConstraint">
<enum>QLayout::SetMinimumSize</enum>
</property>
<item>
<widget class="QScrollArea" name="scrollArea">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="sizeAdjustPolicy">
<enum>QAbstractScrollArea::AdjustToContents</enum>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>564</width>
<height>320</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
<action name="actionRemoveItem">
<property name="text">
<string>Remove item</string>
</property>
</action>
<action name="actionRemoveAll">
<property name="text">
<string>Remove all</string>
</property>
<property name="toolTip">
<string>Remove all</string>
</property>
</action>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>BaseFindSearchDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>240</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>254</y>
</hint>
</hints>
</connection>
</connections>
</ui>

@ -1 +1 @@
Subproject commit e8fc5ca1acd70fd82a2ac9ac02b0261e57703250
Subproject commit 53b42f0854479f36170356c13d5eec4be3182444

View File

@ -57,16 +57,30 @@ public:
itemContextMenu = menu;
}
/**
* If this is set to true, the context menu will also be shown if no item
* is currently selected.
*/
void setShowItemContextMenuWithoutAddress(bool val) { showItemContextMenuWithoutAddress = val; }
protected:
virtual void showItemContextMenu(const QPoint &pt)
{
if (!itemContextMenu) {
return;
}
auto index = this->currentIndex();
if (index.isValid() && itemContextMenu) {
if (index.isValid()) {
auto offset = addressableModel->address(index);
auto name = addressableModel->name(index);
itemContextMenu->setTarget(offset, name);
itemContextMenu->exec(this->mapToGlobal(pt));
} else {
if (!showItemContextMenuWithoutAddress) {
return;
}
itemContextMenu->clearTarget();
}
itemContextMenu->exec(this->mapToGlobal(pt));
}
virtual void onItemActivated(const QModelIndex &index)
@ -90,6 +104,7 @@ protected:
}
private:
bool showItemContextMenuWithoutAddress = false;
AddressableItemModelI *addressableModel = nullptr;
AddressableItemContextMenu *itemContextMenu = nullptr;
MainWindow *mainWindow = nullptr;

View File

@ -1,6 +1,6 @@
#include "ClassesWidget.h"
#include "core/MainWindow.h"
#include "ui_ClassesWidget.h"
#include "ui_ListDockWidget.h"
#include "common/Helpers.h"
#include "common/SvgIconEngine.h"
#include "dialogs/EditMethodDialog.h"
@ -9,6 +9,8 @@
#include <QMenu>
#include <QMouseEvent>
#include <QInputDialog>
#include <QShortcut>
#include <QComboBox>
QVariant ClassesModel::headerData(int section, Qt::Orientation, int role) const
{
@ -33,6 +35,17 @@ QVariant ClassesModel::headerData(int section, Qt::Orientation, int role) const
}
}
RVA ClassesModel::address(const QModelIndex &index) const
{
QVariant v = data(index, OffsetRole);
return v.isValid() ? v.toULongLong() : RVA_INVALID;
}
QString ClassesModel::name(const QModelIndex &index) const
{
return data(index, NameRole).toString();
}
BinClassesModel::BinClassesModel(QObject *parent) : ClassesModel(parent) {}
void BinClassesModel::setClasses(const QList<BinClassDescription> &classes)
@ -526,12 +539,17 @@ QVariant AnalysisClassesModel::data(const QModelIndex &index, int role) const
}
ClassesSortFilterProxyModel::ClassesSortFilterProxyModel(QObject *parent)
: QSortFilterProxyModel(parent)
: AddressableFilterProxyModel(nullptr, parent)
{
setFilterCaseSensitivity(Qt::CaseInsensitive);
setSortCaseSensitivity(Qt::CaseInsensitive);
}
bool ClassesSortFilterProxyModel::filterAcceptsRow(int row, const QModelIndex &parent) const
{
if (parent.isValid())
return true;
QModelIndex index = sourceModel()->index(row, 0, parent);
return qhelpers::filterStringContains(index.data(ClassesModel::NameRole).toString(), this);
}
@ -576,23 +594,63 @@ bool ClassesSortFilterProxyModel::hasChildren(const QModelIndex &parent) const
return !parent.isValid() || !parent.parent().isValid();
}
ClassesWidget::ClassesWidget(MainWindow *main) : CutterDockWidget(main), ui(new Ui::ClassesWidget)
ClassesWidget::ClassesWidget(MainWindow *main)
: ListDockWidget(main),
seekToVTableAction(tr("Seek to VTable"), this),
editMethodAction(tr("Edit Method"), this),
addMethodAction(tr("Add Method"), this),
newClassAction(tr("Create new Class"), this),
renameClassAction(tr("Rename Class"), this),
deleteClassAction(tr("Delete Class"), this)
{
ui->setupUi(this);
setWindowTitle(tr("Classes"));
setObjectName("ClassesWidget");
ui->classesTreeView->setIconSize(QSize(10, 10));
ui->treeView->setIconSize(QSize(10, 10));
proxy_model = new ClassesSortFilterProxyModel(this);
ui->classesTreeView->setModel(proxy_model);
ui->classesTreeView->sortByColumn(ClassesModel::TYPE, Qt::AscendingOrder);
ui->classesTreeView->setContextMenuPolicy(Qt::CustomContextMenu);
setModels(proxy_model);
ui->classSourceCombo->setCurrentIndex(1);
classSourceCombo = new QComboBox(this);
// User an intermediate single-child layout to contain the combo box, otherwise
// when the combo box is inserted directly, the entire vertical layout gets a
// weird horizontal padding on macOS.
QBoxLayout *comboLayout = new QBoxLayout(QBoxLayout::Direction::LeftToRight, nullptr);
comboLayout->addWidget(classSourceCombo);
ui->verticalLayout->insertLayout(ui->verticalLayout->indexOf(ui->quickFilterView), comboLayout);
classSourceCombo->addItem(tr("Binary Info (Fixed)"));
classSourceCombo->addItem(tr("Analysis (Editable)"));
classSourceCombo->setCurrentIndex(1);
connect<void (QComboBox::*)(int)>(ui->classSourceCombo, &QComboBox::currentIndexChanged, this,
connect<void (QComboBox::*)(int)>(classSourceCombo, &QComboBox::currentIndexChanged, this,
&ClassesWidget::refreshClasses);
connect(ui->classesTreeView, &QTreeView::customContextMenuRequested, this,
&ClassesWidget::showContextMenu);
connect(&seekToVTableAction, &QAction::triggered, this,
&ClassesWidget::seekToVTableActionTriggered);
connect(&editMethodAction, &QAction::triggered, this,
&ClassesWidget::editMethodActionTriggered);
connect(&addMethodAction, &QAction::triggered, this, &ClassesWidget::addMethodActionTriggered);
connect(&newClassAction, &QAction::triggered, this, &ClassesWidget::newClassActionTriggered);
connect(&renameClassAction, &QAction::triggered, this,
&ClassesWidget::renameClassActionTriggered);
connect(&deleteClassAction, &QAction::triggered, this,
&ClassesWidget::deleteClassActionTriggered);
// Build context menu like this:
// class-related actions
// -- classesMethodsSeparator
// method-related actions
// -- separator
// default actions from AddressableItemList
auto contextMenu = ui->treeView->getItemContextMenu();
contextMenu->insertSeparator(contextMenu->actions().first());
contextMenu->insertActions(contextMenu->actions().first(),
{ &addMethodAction, &editMethodAction, &seekToVTableAction });
classesMethodsSeparator = contextMenu->insertSeparator(contextMenu->actions().first());
contextMenu->insertActions(classesMethodsSeparator,
{ &newClassAction, &renameClassAction, &deleteClassAction });
connect(contextMenu, &QMenu::aboutToShow, this, &ClassesWidget::updateActions);
ui->treeView->setShowItemContextMenuWithoutAddress(true);
refreshClasses();
}
@ -601,7 +659,7 @@ ClassesWidget::~ClassesWidget() {}
ClassesWidget::Source ClassesWidget::getSource()
{
switch (ui->classSourceCombo->currentIndex()) {
switch (classSourceCombo->currentIndex()) {
case 0:
return Source::BIN;
default:
@ -614,88 +672,68 @@ void ClassesWidget::refreshClasses()
switch (getSource()) {
case Source::BIN:
if (!bin_model) {
proxy_model->setSourceModel(nullptr);
proxy_model->setSourceModel(static_cast<AddressableItemModelI *>(nullptr));
delete analysis_model;
analysis_model = nullptr;
bin_model = new BinClassesModel(this);
proxy_model->setSourceModel(bin_model);
proxy_model->setSourceModel(static_cast<AddressableItemModelI *>(bin_model));
}
bin_model->setClasses(Core()->getAllClassesFromBin());
break;
case Source::ANALYSIS:
if (!analysis_model) {
proxy_model->setSourceModel(nullptr);
proxy_model->setSourceModel(static_cast<AddressableItemModelI *>(nullptr));
delete bin_model;
bin_model = nullptr;
analysis_model = new AnalysisClassesModel(this);
proxy_model->setSourceModel(analysis_model);
proxy_model->setSourceModel(static_cast<AddressableItemModelI *>(analysis_model));
}
break;
}
qhelpers::adjustColumns(ui->classesTreeView, 3, 0);
qhelpers::adjustColumns(ui->treeView, 3, 0);
ui->classesTreeView->setColumnWidth(0, 200);
ui->treeView->setColumnWidth(0, 200);
}
void ClassesWidget::on_classesTreeView_doubleClicked(const QModelIndex &index)
void ClassesWidget::updateActions()
{
if (!index.isValid())
return;
bool isAnalysis = !!analysis_model;
newClassAction.setVisible(isAnalysis);
addMethodAction.setVisible(isAnalysis);
QVariant offsetData = index.data(ClassesModel::OffsetRole);
if (!offsetData.isValid()) {
return;
}
RVA offset = offsetData.value<RVA>();
Core()->seekAndShow(offset);
}
void ClassesWidget::showContextMenu(const QPoint &pt)
{
if (!analysis_model) {
// no context menu for bin classes
return;
bool rowIsAnalysisClass = false;
bool rowIsAnalysisMethod = false;
QModelIndex index = ui->treeView->selectionModel()->currentIndex();
if (isAnalysis && index.isValid()) {
auto type = static_cast<ClassesModel::RowType>(index.data(ClassesModel::TypeRole).toInt());
rowIsAnalysisClass = type == ClassesModel::RowType::Class;
rowIsAnalysisMethod = type == ClassesModel::RowType::Method;
}
QModelIndex index = ui->classesTreeView->selectionModel()->currentIndex();
if (!index.isValid()) {
return;
}
auto type = static_cast<ClassesModel::RowType>(index.data(ClassesModel::TypeRole).toInt());
renameClassAction.setVisible(rowIsAnalysisClass);
deleteClassAction.setVisible(rowIsAnalysisClass);
QMenu menu(ui->classesTreeView);
menu.addAction(ui->newClassAction);
if (type == ClassesModel::RowType::Class) {
menu.addAction(ui->renameClassAction);
menu.addAction(ui->deleteClassAction);
}
menu.addSeparator();
menu.addAction(ui->addMethodAction);
if (type == ClassesModel::RowType::Method) {
menu.addAction(ui->editMethodAction);
classesMethodsSeparator->setVisible(rowIsAnalysisClass || rowIsAnalysisMethod);
editMethodAction.setVisible(rowIsAnalysisMethod);
bool rowHasVTable = false;
if (rowIsAnalysisMethod) {
QString className = index.parent().data(ClassesModel::NameRole).toString();
QString methodName = index.data(ClassesModel::NameRole).toString();
AnalysisMethodDescription desc;
if (Core()->getAnalysisMethod(className, methodName, &desc)) {
if (desc.vtableOffset >= 0) {
menu.addAction(ui->seekToVTableAction);
rowHasVTable = true;
}
}
}
menu.exec(ui->classesTreeView->mapToGlobal(pt));
seekToVTableAction.setVisible(rowHasVTable);
}
void ClassesWidget::on_seekToVTableAction_triggered()
void ClassesWidget::seekToVTableActionTriggered()
{
QModelIndex index = ui->classesTreeView->selectionModel()->currentIndex();
QModelIndex index = ui->treeView->selectionModel()->currentIndex();
QString className = index.parent().data(ClassesModel::NameRole).toString();
QList<AnalysisVTableDescription> vtables = Core()->getAnalysisClassVTables(className);
@ -714,9 +752,9 @@ void ClassesWidget::on_seekToVTableAction_triggered()
Core()->seekAndShow(vtables[0].addr + desc.vtableOffset);
}
void ClassesWidget::on_addMethodAction_triggered()
void ClassesWidget::addMethodActionTriggered()
{
QModelIndex index = ui->classesTreeView->selectionModel()->currentIndex();
QModelIndex index = ui->treeView->selectionModel()->currentIndex();
if (!index.isValid()) {
return;
}
@ -732,9 +770,9 @@ void ClassesWidget::on_addMethodAction_triggered()
EditMethodDialog::newMethod(className, QString(), this);
}
void ClassesWidget::on_editMethodAction_triggered()
void ClassesWidget::editMethodActionTriggered()
{
QModelIndex index = ui->classesTreeView->selectionModel()->currentIndex();
QModelIndex index = ui->treeView->selectionModel()->currentIndex();
if (!index.isValid()
|| index.data(ClassesModel::TypeRole).toInt()
!= static_cast<int>(ClassesModel::RowType::Method)) {
@ -745,7 +783,7 @@ void ClassesWidget::on_editMethodAction_triggered()
EditMethodDialog::editMethod(className, methName, this);
}
void ClassesWidget::on_newClassAction_triggered()
void ClassesWidget::newClassActionTriggered()
{
bool ok;
QString name = QInputDialog::getText(this, tr("Create new Class"), tr("Class Name:"),
@ -755,9 +793,9 @@ void ClassesWidget::on_newClassAction_triggered()
}
}
void ClassesWidget::on_deleteClassAction_triggered()
void ClassesWidget::deleteClassActionTriggered()
{
QModelIndex index = ui->classesTreeView->selectionModel()->currentIndex();
QModelIndex index = ui->treeView->selectionModel()->currentIndex();
if (!index.isValid()
|| index.data(ClassesModel::TypeRole).toInt()
!= static_cast<int>(ClassesModel::RowType::Class)) {
@ -772,9 +810,9 @@ void ClassesWidget::on_deleteClassAction_triggered()
Core()->deleteClass(className);
}
void ClassesWidget::on_renameClassAction_triggered()
void ClassesWidget::renameClassActionTriggered()
{
QModelIndex index = ui->classesTreeView->selectionModel()->currentIndex();
QModelIndex index = ui->treeView->selectionModel()->currentIndex();
if (!index.isValid()
|| index.data(ClassesModel::TypeRole).toInt()
!= static_cast<int>(ClassesModel::RowType::Class)) {

View File

@ -5,6 +5,7 @@
#include "core/Cutter.h"
#include "CutterDockWidget.h"
#include "widgets/ListDockWidget.h"
#include <QAbstractListModel>
#include <QSortFilterProxyModel>
@ -21,7 +22,7 @@ class ClassesWidget;
/**
* @brief Common abstract base class for Bin and Anal classes models
*/
class ClassesModel : public QAbstractItemModel
class ClassesModel : public AddressableItemModel<>
{
public:
enum Columns { NAME = 0, REAL_NAME, TYPE, OFFSET, VTABLE, COUNT };
@ -69,10 +70,13 @@ public:
*/
static const int RealNameRole = Qt::UserRole + 4;
explicit ClassesModel(QObject *parent = nullptr) : QAbstractItemModel(parent) {}
explicit ClassesModel(QObject *parent = nullptr) : AddressableItemModel(parent) {}
QVariant headerData(int section, Qt::Orientation orientation,
int role = Qt::DisplayRole) const override;
RVA address(const QModelIndex &index) const override;
QString name(const QModelIndex &index) const override;
};
Q_DECLARE_METATYPE(ClassesModel::RowType)
@ -163,7 +167,7 @@ public slots:
void classAttrsChanged(const QString &cls);
};
class ClassesSortFilterProxyModel : public QSortFilterProxyModel
class ClassesSortFilterProxyModel : public AddressableFilterProxyModel
{
Q_OBJECT
@ -176,7 +180,7 @@ protected:
bool hasChildren(const QModelIndex &parent = QModelIndex()) const override;
};
class ClassesWidget : public CutterDockWidget
class ClassesWidget : public ListDockWidget
{
Q_OBJECT
@ -185,29 +189,34 @@ public:
~ClassesWidget();
private slots:
void on_classesTreeView_doubleClicked(const QModelIndex &index);
void on_seekToVTableAction_triggered();
void on_addMethodAction_triggered();
void on_editMethodAction_triggered();
void on_newClassAction_triggered();
void on_deleteClassAction_triggered();
void on_renameClassAction_triggered();
void showContextMenu(const QPoint &pt);
void seekToVTableActionTriggered();
void editMethodActionTriggered();
void addMethodActionTriggered();
void newClassActionTriggered();
void renameClassActionTriggered();
void deleteClassActionTriggered();
void refreshClasses();
void updateActions();
private:
enum class Source { BIN, ANALYSIS };
Source getSource();
std::unique_ptr<Ui::ClassesWidget> ui;
BinClassesModel *bin_model = nullptr;
AnalysisClassesModel *analysis_model = nullptr;
ClassesSortFilterProxyModel *proxy_model;
QComboBox *classSourceCombo;
QAction seekToVTableAction;
QAction editMethodAction;
QAction addMethodAction;
QAction newClassAction;
QAction renameClassAction;
QAction deleteClassAction;
QAction *classesMethodsSeparator;
};
#endif // CLASSESWIDGET_H

View File

@ -1,148 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ClassesWidget</class>
<widget class="QDockWidget" name="ClassesWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true">Classes</string>
</property>
<widget class="QWidget" name="dockWidgetContents">
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="CutterTreeView" name="classesTreeView">
<property name="styleSheet">
<string notr="true">CutterTreeView::item
{
padding-top: 1px;
padding-bottom: 1px;
}</string>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
<property name="expandsOnDoubleClick">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_17">
<property name="spacing">
<number>10</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="classSourceLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Source:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="classSourceCombo">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<item>
<property name="text">
<string>Binary Info (Fixed)</string>
</property>
</item>
<item>
<property name="text">
<string>Analysis (Editable)</string>
</property>
</item>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<action name="seekToVTableAction">
<property name="text">
<string>Seek to VTable</string>
</property>
</action>
<action name="editMethodAction">
<property name="text">
<string>Edit Method</string>
</property>
</action>
<action name="addMethodAction">
<property name="text">
<string>Add Method</string>
</property>
</action>
<action name="newClassAction">
<property name="text">
<string>Create new Class</string>
</property>
</action>
<action name="renameClassAction">
<property name="text">
<string>Rename Class</string>
</property>
</action>
<action name="deleteClassAction">
<property name="text">
<string>Delete Class</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>
<class>CutterTreeView</class>
<extends>QTreeView</extends>
<header>widgets/CutterTreeView.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@ -22,8 +22,7 @@ void ColorThemeComboBox::updateFromConfig(bool interfaceThemeChanged)
clear();
for (const QString &theme : themes) {
if (ThemeWorker().isCustomTheme(theme)
|| !Configuration::relevantThemes[theme]
if (ThemeWorker().isCustomTheme(theme) || !Configuration::relevantThemes[theme]
|| (Configuration::cutterInterfaceThemesList()[curInterfaceThemeIndex].flag
& Configuration::relevantThemes[theme])) {
addItem(theme);

View File

@ -128,8 +128,8 @@ void ColorOptionDelegate::paint(QPainter *painter, const QStyleOptionViewItem &o
painter->setPen(qApp->palette().text().color());
QFontMetrics fm2 = QFontMetrics(painter->font());
QString name = fm2.elidedText(optionInfoMap__[currCO.optionName].displayingtext,
Qt::ElideRight, optionNameRect.width());
QString name = fm2.elidedText(optionInfoMap__[currCO.optionName].displayingtext, Qt::ElideRight,
optionNameRect.width());
painter->drawText(optionNameRect, name);
QPainterPath roundedOptionRect;
@ -157,9 +157,9 @@ void ColorOptionDelegate::paint(QPainter *painter, const QStyleOptionViewItem &o
painter->fillPath(roundedColorRect, currCO.color);
QFontMetrics fm3 = QFontMetrics(painter->font());
QString desc = fm3.elidedText(
currCO.optionName + ": " + optionInfoMap__[currCO.optionName].info, Qt::ElideRight,
descTextRect.width());
QString desc =
fm3.elidedText(currCO.optionName + ": " + optionInfoMap__[currCO.optionName].info,
Qt::ElideRight, descTextRect.width());
painter->setPen(qApp->palette().text().color());
painter->setBrush(qApp->palette().text());
painter->drawText(descTextRect, desc);

View File

@ -12,8 +12,7 @@ ComboQuickFilterView::ComboQuickFilterView(QWidget *parent)
connect(debounceTimer, &QTimer::timeout, this,
[this]() { emit filterTextChanged(ui->lineEdit->text()); });
connect(ui->lineEdit, &QLineEdit::textChanged, this,
[this](const QString &text) { debounceTimer->start(150); });
connect(ui->lineEdit, &QLineEdit::textChanged, this, [this]() { debounceTimer->start(150); });
}
ComboQuickFilterView::~ComboQuickFilterView()

View File

@ -173,7 +173,7 @@ void Dashboard::on_certificateButton_clicked()
dialog.setMinimumSize(QSize(900, 600));
dialog.setMaximumSize(QSize(900, 600));
dialog.setSizeGripEnabled(false);
dialog.setWindowTitle("Certificates");
dialog.setWindowTitle(tr("Certificates"));
dialog.exec();
}

View File

@ -300,7 +300,7 @@ void DebugActions::onAttachedRemoteDebugger(bool successfully)
// TODO(#2829): Investigate why this is happening
if (remoteDialog == nullptr)
return;
if (!successfully) {
QMessageBox msgBox;
msgBox.setText(tr("Error connecting."));

View File

@ -26,8 +26,7 @@ DecompilerWidget::DecompilerWidget(MainWindow *main)
ui(new Ui::DecompilerWidget),
decompilerBusy(false),
seekFromCursor(false),
scrollerHorizontal(0),
scrollerVertical(0),
historyPos(0),
previousFunctionAddr(RVA_INVALID),
decompiledFunctionAddr(RVA_INVALID),
code(Decompiler::makeWarning(tr("Choose an offset and refresh to get decompiled code")),
@ -91,6 +90,7 @@ DecompilerWidget::DecompilerWidget(MainWindow *main)
connect(Core(), &CutterCore::varsChanged, this, &DecompilerWidget::doRefresh);
connect(Core(), &CutterCore::functionsChanged, this, &DecompilerWidget::doRefresh);
connect(Core(), &CutterCore::flagsChanged, this, &DecompilerWidget::doRefresh);
connect(Core(), &CutterCore::globalVarsChanged, this, &DecompilerWidget::doRefresh);
connect(Core(), &CutterCore::commentsChanged, this, &DecompilerWidget::refreshIfChanged);
connect(Core(), &CutterCore::instructionChanged, this, &DecompilerWidget::refreshIfChanged);
connect(Core(), &CutterCore::refreshCodeViews, this, &DecompilerWidget::doRefresh);
@ -311,13 +311,6 @@ QTextCursor DecompilerWidget::getCursorForAddress(RVA addr)
void DecompilerWidget::decompilationFinished(RzAnnotatedCode *codeDecompiled)
{
bool isDisplayReset = false;
if (previousFunctionAddr == decompiledFunctionAddr) {
scrollerHorizontal = ui->textEdit->horizontalScrollBar()->sliderPosition();
scrollerVertical = ui->textEdit->verticalScrollBar()->sliderPosition();
isDisplayReset = true;
}
ui->progressLabel->setVisible(false);
ui->decompilerComboBox->setEnabled(decompilerSelectionEnabled);
@ -354,10 +347,8 @@ void DecompilerWidget::decompilationFinished(RzAnnotatedCode *codeDecompiled)
}
}
if (isDisplayReset) {
ui->textEdit->horizontalScrollBar()->setSliderPosition(scrollerHorizontal);
ui->textEdit->verticalScrollBar()->setSliderPosition(scrollerVertical);
}
ui->textEdit->horizontalScrollBar()->setSliderPosition(scrollHistory[historyPos].first);
ui->textEdit->verticalScrollBar()->setSliderPosition(scrollHistory[historyPos].second);
}
void DecompilerWidget::setAnnotationsAtCursor(size_t pos)
@ -416,11 +407,28 @@ void DecompilerWidget::cursorPositionChanged()
updateSelection();
}
void DecompilerWidget::seekChanged()
void DecompilerWidget::seekChanged(RVA /* addr */, CutterCore::SeekHistoryType type)
{
if (seekFromCursor) {
return;
}
if (!scrollHistory.empty()) { // History is empty upon init.
scrollHistory[historyPos] = { ui->textEdit->horizontalScrollBar()->sliderPosition(),
ui->textEdit->verticalScrollBar()->sliderPosition() };
}
if (type == CutterCore::SeekHistoryType::New) {
// Erase previous history past this point.
if (scrollHistory.size() > historyPos + 1) {
scrollHistory.erase(scrollHistory.begin() + historyPos + 1, scrollHistory.end());
}
scrollHistory.push_back({ 0, 0 });
historyPos = scrollHistory.size() - 1;
} else if (type == CutterCore::SeekHistoryType::Undo) {
--historyPos;
} else if (type == CutterCore::SeekHistoryType::Redo) {
++historyPos;
}
RVA fcnAddr = Core()->getFunctionStart(seekable->getOffset());
if (fcnAddr == RVA_INVALID || fcnAddr != decompiledFunctionAddr) {
doRefresh();

View File

@ -53,7 +53,7 @@ private slots:
* - Seek changed to an offset contained in the decompiled function.
* - Auto-refresh is disabled.
*/
void seekChanged();
void seekChanged(RVA /* addr */, CutterCore::SeekHistoryType type);
void decompilationFinished(RzAnnotatedCode *code);
private:
@ -72,8 +72,8 @@ private:
bool decompilerBusy;
bool seekFromCursor;
int scrollerHorizontal;
int scrollerVertical;
int historyPos;
QVector<QPair<int, int>> scrollHistory;
RVA previousFunctionAddr;
RVA decompiledFunctionAddr;
std::unique_ptr<RzAnnotatedCode, void (*)(RzAnnotatedCode *)> code;

View File

@ -52,6 +52,7 @@ DisassemblerGraphView::DisassemblerGraphView(QWidget *parent, CutterSeekable *se
connect(Core(), &CutterCore::commentsChanged, this, &DisassemblerGraphView::refreshView);
connect(Core(), &CutterCore::functionRenamed, this, &DisassemblerGraphView::refreshView);
connect(Core(), &CutterCore::flagsChanged, this, &DisassemblerGraphView::refreshView);
connect(Core(), &CutterCore::globalVarsChanged, this, &DisassemblerGraphView::refreshView);
connect(Core(), &CutterCore::varsChanged, this, &DisassemblerGraphView::refreshView);
connect(Core(), &CutterCore::instructionChanged, this, &DisassemblerGraphView::refreshView);
connect(Core(), &CutterCore::breakpointsChanged, this, &DisassemblerGraphView::refreshView);
@ -534,33 +535,44 @@ GraphView::EdgeConfiguration DisassemblerGraphView::edgeConfiguration(GraphView:
bool DisassemblerGraphView::eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::Type::ToolTip && Config()->getGraphPreview()) {
if ((Config()->getGraphPreview() || Config()->getShowVarTooltips())
&& event->type() == QEvent::Type::ToolTip) {
QHelpEvent *helpEvent = static_cast<QHelpEvent *>(event);
QPoint pointOfEvent = helpEvent->globalPos();
QPoint point = viewToLogicalCoordinates(helpEvent->pos());
GraphBlock *block = getBlockContaining(point);
if (auto block = getBlockContaining(point)) {
// Get pos relative to start of block
QPoint pos = point - QPoint(block->x, block->y);
if (block == nullptr) {
return false;
}
// offsetFrom is the address which on top the cursor triggered this
RVA offsetFrom = RVA_INVALID;
// offsetFrom is the address which on top the cursor triggered this
RVA offsetFrom = RVA_INVALID;
/*
* getAddrForMouseEvent() doesn't work for jmps, like
* getInstrForMouseEvent() with false as a 3rd argument.
*/
Instr *inst = getInstrForMouseEvent(*block, &pos, true);
if (inst != nullptr) {
offsetFrom = inst->addr;
}
/*
* getAddrForMouseEvent() doesn't work for jmps, like
* getInstrForMouseEvent() with false as a 3rd argument.
*/
Instr *inst = getInstrForMouseEvent(*block, &point, true);
if (inst != nullptr) {
offsetFrom = inst->addr;
}
// Don't preview anything for a small scale
if (getViewScale() >= 0.8) {
return DisassemblyPreview::showDisasPreview(this, pointOfEvent, offsetFrom);
// Don't preview anything for a small scale
if (getViewScale() >= 0.8) {
if (Config()->getGraphPreview()
&& DisassemblyPreview::showDisasPreview(this, pointOfEvent, offsetFrom)) {
return true;
}
if (Config()->getShowVarTooltips() && inst) {
auto token = getToken(inst, pos.x());
if (token
&& DisassemblyPreview::showDebugValueTooltip(this, pointOfEvent,
token->content, offsetFrom)) {
return true;
}
}
}
}
}
return CutterGraphView::eventFilter(obj, event);

View File

@ -128,6 +128,7 @@ DisassemblyWidget::DisassemblyWidget(MainWindow *main)
connect(Core(), &CutterCore::commentsChanged, this, [this]() { refreshDisasm(); });
connect(Core(), SIGNAL(flagsChanged()), this, SLOT(refreshDisasm()));
connect(Core(), SIGNAL(globalVarsChanged()), this, SLOT(refreshDisasm()));
connect(Core(), SIGNAL(functionsChanged()), this, SLOT(refreshDisasm()));
connect(Core(), &CutterCore::functionRenamed, this, [this]() { refreshDisasm(); });
connect(Core(), SIGNAL(varsChanged()), this, SLOT(refreshDisasm()));
@ -333,6 +334,7 @@ void DisassemblyWidget::scrollInstructions(int count)
}
refreshDisasm(offset);
topOffsetHistory[topOffsetHistoryPos] = offset;
}
bool DisassemblyWidget::updateMaxLines()
@ -629,21 +631,29 @@ bool DisassemblyWidget::eventFilter(QObject *obj, QEvent *event)
&& (obj == mDisasTextEdit || obj == mDisasTextEdit->viewport())) {
QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
const QTextCursor &cursor = mDisasTextEdit->cursorForPosition(mouseEvent->pos());
jumpToOffsetUnderCursor(cursor);
if (mouseEvent->button() == Qt::LeftButton) {
const QTextCursor &cursor = mDisasTextEdit->cursorForPosition(mouseEvent->pos());
jumpToOffsetUnderCursor(cursor);
return true;
} else if (Config()->getPreviewValue()
&& event->type() == QEvent::ToolTip
&& obj == mDisasTextEdit->viewport()) {
return true;
}
} else if ((Config()->getPreviewValue() || Config()->getShowVarTooltips())
&& event->type() == QEvent::ToolTip && obj == mDisasTextEdit->viewport()) {
QHelpEvent *helpEvent = static_cast<QHelpEvent *>(event);
auto cursorForWord = mDisasTextEdit->cursorForPosition(helpEvent->pos());
cursorForWord.select(QTextCursor::WordUnderCursor);
RVA offsetFrom = DisassemblyPreview::readDisassemblyOffset(cursorForWord);
return DisassemblyPreview::showDisasPreview(this, helpEvent->globalPos(), offsetFrom);
if (Config()->getPreviewValue()
&& DisassemblyPreview::showDisasPreview(this, helpEvent->globalPos(), offsetFrom)) {
return true;
}
if (Config()->getShowVarTooltips()
&& DisassemblyPreview::showDebugValueTooltip(
this, helpEvent->globalPos(), cursorForWord.selectedText(), offsetFrom)) {
return true;
}
}
return MemoryDockWidget::eventFilter(obj, event);
@ -664,19 +674,34 @@ QString DisassemblyWidget::getWindowTitle() const
return tr("Disassembly");
}
void DisassemblyWidget::on_seekChanged(RVA offset)
void DisassemblyWidget::on_seekChanged(RVA offset, CutterCore::SeekHistoryType type)
{
if (type == CutterCore::SeekHistoryType::New) {
// Erase previous history past this point.
if (topOffsetHistory.size() > topOffsetHistoryPos + 1) {
topOffsetHistory.erase(topOffsetHistory.begin() + topOffsetHistoryPos + 1,
topOffsetHistory.end());
}
topOffsetHistory.push_back(offset);
topOffsetHistoryPos = topOffsetHistory.size() - 1;
} else if (type == CutterCore::SeekHistoryType::Undo) {
--topOffsetHistoryPos;
} else if (type == CutterCore::SeekHistoryType::Redo) {
++topOffsetHistoryPos;
}
if (!seekFromCursor) {
cursorLineOffset = 0;
cursorCharOffset = 0;
}
if (topOffset != RVA_INVALID && offset >= topOffset && offset <= bottomOffset) {
if (topOffset != RVA_INVALID && offset >= topOffset && offset <= bottomOffset
&& type == CutterCore::SeekHistoryType::New) {
// if the line with the seek offset is currently visible, just move the cursor there
updateCursorPosition();
topOffsetHistory[topOffsetHistoryPos] = topOffset;
} else {
// otherwise scroll there
refreshDisasm(offset);
refreshDisasm(topOffsetHistory[topOffsetHistoryPos]);
}
mCtxMenu->setOffset(offset);
}

View File

@ -51,7 +51,7 @@ public slots:
QList<DisassemblyLine> getLines();
protected slots:
void on_seekChanged(RVA offset);
void on_seekChanged(RVA offset, CutterCore::SeekHistoryType type);
void refreshIfInRange(RVA offset);
void instructionChanged(RVA offset);
void refreshDisasm(RVA offset = RVA_INVALID);
@ -88,6 +88,9 @@ private:
void keyPressEvent(QKeyEvent *event) override;
QString getWindowTitle() const override;
int topOffsetHistoryPos = 0;
QList<RVA> topOffsetHistory;
QList<RVA> breakpoints;
void setupFonts();

View File

@ -234,9 +234,8 @@ QVariant FunctionModel::data(const QModelIndex &index, int role) const
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));
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);
}
@ -508,6 +507,7 @@ FunctionsWidget::FunctionsWidget(MainWindow *main)
functionProxyModel = new FunctionSortFilterProxyModel(functionModel, this);
setModels(functionProxyModel);
ui->treeView->sortByColumn(FunctionModel::NameColumn, Qt::AscendingOrder);
ui->treeView->setExpandsOnDoubleClick(false);
titleContextMenu = new QMenu(this);
auto viewTypeGroup = new QActionGroup(titleContextMenu);

View File

@ -0,0 +1,228 @@
#include "GlobalsWidget.h"
#include "ui_GlobalsWidget.h"
#include "core/MainWindow.h"
#include "common/Helpers.h"
#include "dialogs/GlobalVariableDialog.h"
#include <QMenu>
#include <QShortcut>
GlobalsModel::GlobalsModel(QList<GlobalDescription> *globals, QObject *parent)
: AddressableItemModel<QAbstractListModel>(parent), globals(globals)
{
}
int GlobalsModel::rowCount(const QModelIndex &) const
{
return globals->count();
}
int GlobalsModel::columnCount(const QModelIndex &) const
{
return GlobalsModel::ColumnCount;
}
QVariant GlobalsModel::data(const QModelIndex &index, int role) const
{
if (index.row() >= globals->count()) {
return QVariant();
}
const GlobalDescription &global = globals->at(index.row());
switch (role) {
case Qt::DisplayRole:
switch (index.column()) {
case GlobalsModel::AddressColumn:
return RzAddressString(global.addr);
case GlobalsModel::TypeColumn:
return QString(global.type).trimmed();
case GlobalsModel::NameColumn:
return global.name;
case GlobalsModel::CommentColumn:
return Core()->getCommentAt(global.addr);
default:
return QVariant();
}
case GlobalsModel::GlobalDescriptionRole:
return QVariant::fromValue(global);
default:
return QVariant();
}
}
QVariant GlobalsModel::headerData(int section, Qt::Orientation, int role) const
{
switch (role) {
case Qt::DisplayRole:
switch (section) {
case GlobalsModel::AddressColumn:
return tr("Address");
case GlobalsModel::TypeColumn:
return tr("Type");
case GlobalsModel::NameColumn:
return tr("Name");
case GlobalsModel::CommentColumn:
return tr("Comment");
default:
return QVariant();
}
default:
return QVariant();
}
}
RVA GlobalsModel::address(const QModelIndex &index) const
{
const GlobalDescription &global = globals->at(index.row());
return global.addr;
}
QString GlobalsModel::name(const QModelIndex &index) const
{
const GlobalDescription &global = globals->at(index.row());
return global.name;
}
GlobalsProxyModel::GlobalsProxyModel(GlobalsModel *sourceModel, QObject *parent)
: AddressableFilterProxyModel(sourceModel, parent)
{
setFilterCaseSensitivity(Qt::CaseInsensitive);
setSortCaseSensitivity(Qt::CaseInsensitive);
}
bool GlobalsProxyModel::filterAcceptsRow(int row, const QModelIndex &parent) const
{
QModelIndex index = sourceModel()->index(row, 0, parent);
auto global = index.data(GlobalsModel::GlobalDescriptionRole).value<GlobalDescription>();
return qhelpers::filterStringContains(global.name, this);
}
bool GlobalsProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
{
auto leftGlobal = left.data(GlobalsModel::GlobalDescriptionRole).value<GlobalDescription>();
auto rightGlobal = right.data(GlobalsModel::GlobalDescriptionRole).value<GlobalDescription>();
switch (left.column()) {
case GlobalsModel::AddressColumn:
return leftGlobal.addr < rightGlobal.addr;
case GlobalsModel::TypeColumn:
return leftGlobal.type < rightGlobal.type;
case GlobalsModel::NameColumn:
return leftGlobal.name < rightGlobal.name;
case GlobalsModel::CommentColumn:
return Core()->getCommentAt(leftGlobal.addr) < Core()->getCommentAt(rightGlobal.addr);
default:
break;
}
return false;
}
void GlobalsWidget::editGlobal()
{
QModelIndex index = ui->treeView->currentIndex();
if (!index.isValid()) {
return;
}
RVA globalVariableAddress = globalsModel->address(index);
GlobalVariableDialog dialog(globalVariableAddress, parentWidget());
dialog.exec();
}
void GlobalsWidget::deleteGlobal()
{
QModelIndex index = ui->treeView->currentIndex();
if (!index.isValid()) {
return;
}
RVA globalVariableAddress = globalsModel->address(index);
Core()->delGlobalVariable(globalVariableAddress);
}
void GlobalsWidget::showGlobalsContextMenu(const QPoint &pt)
{
QModelIndex index = ui->treeView->indexAt(pt);
QMenu menu(ui->treeView);
if (index.isValid()) {
menu.addAction(actionEditGlobal);
menu.addAction(actionDeleteGlobal);
}
menu.exec(ui->treeView->mapToGlobal(pt));
}
GlobalsWidget::GlobalsWidget(MainWindow *main)
: CutterDockWidget(main), ui(new Ui::GlobalsWidget), tree(new CutterTreeWidget(this))
{
ui->setupUi(this);
ui->quickFilterView->setLabelText(tr("Category"));
setWindowTitle(tr("Globals"));
setObjectName("GlobalsWidget");
// Add status bar which displays the count
tree->addStatusBar(ui->verticalLayout);
// Set single select mode
ui->treeView->setSelectionMode(QAbstractItemView::SingleSelection);
// Setup up the model and the proxy model
globalsModel = new GlobalsModel(&globals, this);
globalsProxyModel = new GlobalsProxyModel(globalsModel, this);
ui->treeView->setModel(globalsProxyModel);
ui->treeView->sortByColumn(GlobalsModel::AddressColumn, Qt::AscendingOrder);
// Setup custom context menu
connect(ui->treeView, &QWidget::customContextMenuRequested, this,
&GlobalsWidget::showGlobalsContextMenu);
ui->treeView->setContextMenuPolicy(Qt::CustomContextMenu);
connect(ui->quickFilterView, &ComboQuickFilterView::filterTextChanged, globalsProxyModel,
&QSortFilterProxyModel::setFilterWildcard);
connect(ui->quickFilterView, &ComboQuickFilterView::filterTextChanged, this,
[this] { tree->showItemsNumber(globalsProxyModel->rowCount()); });
QShortcut *searchShortcut = new QShortcut(QKeySequence::Find, this);
connect(searchShortcut, &QShortcut::activated, ui->quickFilterView,
&ComboQuickFilterView::showFilter);
searchShortcut->setContext(Qt::WidgetWithChildrenShortcut);
QShortcut *clearShortcut = new QShortcut(QKeySequence(Qt::Key_Escape), this);
connect(clearShortcut, &QShortcut::activated, ui->quickFilterView,
&ComboQuickFilterView::clearFilter);
clearShortcut->setContext(Qt::WidgetWithChildrenShortcut);
actionEditGlobal = new QAction(tr("Edit Global Variable"), this);
actionDeleteGlobal = new QAction(tr("Delete Global Variable"), this);
connect(actionEditGlobal, &QAction::triggered, [this]() { editGlobal(); });
connect(actionDeleteGlobal, &QAction::triggered, [this]() { deleteGlobal(); });
connect(Core(), &CutterCore::globalVarsChanged, this, &GlobalsWidget::refreshGlobals);
connect(Core(), &CutterCore::codeRebased, this, &GlobalsWidget::refreshGlobals);
connect(Core(), &CutterCore::refreshAll, this, &GlobalsWidget::refreshGlobals);
connect(Core(), &CutterCore::commentsChanged, this,
[this]() { qhelpers::emitColumnChanged(globalsModel, GlobalsModel::CommentColumn); });
}
GlobalsWidget::~GlobalsWidget() {}
void GlobalsWidget::refreshGlobals()
{
globalsModel->beginResetModel();
globals = Core()->getAllGlobals();
globalsModel->endResetModel();
qhelpers::adjustColumns(ui->treeView, GlobalsModel::ColumnCount, 0);
}

View File

@ -0,0 +1,89 @@
#ifndef GLOBALSWIDGET_H
#define GLOBALSWIDGET_H
#include <memory>
#include <QAbstractListModel>
#include <QSortFilterProxyModel>
#include "core/Cutter.h"
#include "CutterDockWidget.h"
#include "widgets/ListDockWidget.h"
class MainWindow;
class QTreeWidget;
class GlobalsWidget;
namespace Ui {
class GlobalsWidget;
}
class MainWindow;
class QTreeWidgetItem;
class GlobalsModel : public AddressableItemModel<QAbstractListModel>
{
Q_OBJECT
friend GlobalsWidget;
private:
QList<GlobalDescription> *globals;
public:
enum Column { AddressColumn = 0, TypeColumn, NameColumn, CommentColumn, ColumnCount };
enum Role { GlobalDescriptionRole = Qt::UserRole };
GlobalsModel(QList<GlobalDescription> *exports, QObject *parent = nullptr);
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role) const override;
QVariant headerData(int section, Qt::Orientation orientation,
int role = Qt::DisplayRole) const override;
RVA address(const QModelIndex &index) const override;
QString name(const QModelIndex &index) const override;
};
class GlobalsProxyModel : public AddressableFilterProxyModel
{
Q_OBJECT
public:
GlobalsProxyModel(GlobalsModel *sourceModel, QObject *parent = nullptr);
protected:
bool filterAcceptsRow(int row, const QModelIndex &parent) const override;
bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;
};
class GlobalsWidget : public CutterDockWidget
{
Q_OBJECT
public:
explicit GlobalsWidget(MainWindow *main);
~GlobalsWidget();
private slots:
void refreshGlobals();
void showGlobalsContextMenu(const QPoint &pt);
void editGlobal();
void deleteGlobal();
private:
std::unique_ptr<Ui::GlobalsWidget> ui;
QList<GlobalDescription> globals;
GlobalsModel *globalsModel;
GlobalsProxyModel *globalsProxyModel;
CutterTreeWidget *tree;
QAction *actionEditGlobal;
QAction *actionDeleteGlobal;
};
#endif // GLOBALSWIDGET_H

View File

@ -0,0 +1,107 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>GlobalsWidget</class>
<widget class="QDockWidget" name="GlobalsWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true">Global Variables</string>
</property>
<widget class="QWidget" name="dockWidgetContents">
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="CutterTreeView" name="treeView">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="styleSheet">
<string notr="true">CutterTreeView::item
{
padding-top: 1px;
padding-bottom: 1px;
}</string>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<property name="indentation">
<number>8</number>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="ComboQuickFilterView" name="quickFilterView" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</widget>
<action name="actionEditGlobal">
<property name="text">
<string>Edit Global Variable</string>
</property>
<property name="toolTip">
<string>Edit Global Variable</string>
</property>
</action>
<action name="actionDeleteGlobal">
<property name="text">
<string>Delete Global Variable</string>
</property>
<property name="toolTip">
<string>Delete Global Variable</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>
<class>CutterTreeView</class>
<extends>QTreeView</extends>
<header>widgets/CutterTreeView.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>ComboQuickFilterView</class>
<extends>QWidget</extends>
<header>widgets/ComboQuickFilterView.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@ -438,7 +438,7 @@ void GraphView::saveAsSvg(QString path)
generator.setFileName(path);
generator.setSize(QSize(width, height));
generator.setViewBox(QRect(0, 0, width, height));
generator.setTitle("Cutter graph export");
generator.setTitle(tr("Cutter graph export"));
QPainter p;
p.begin(&generator);
paint(p, QPoint(0, 0), QRect(0, 0, width, height), 1.0, false);

View File

@ -36,8 +36,7 @@ void HeapBinsGraphView::loadCurrentGraph()
|| QString(heapBin->type) == QString("Tcache");
// store info about the chunks in a vector for easy access
CutterRzListForeach(heapBin->chunks, iter, RzHeapChunkListItem, item)
{
CutterRzListForeach (heapBin->chunks, iter, RzHeapChunkListItem, item) {
GraphHeapChunk graphHeapChunk;
graphHeapChunk.addr = item->addr;
RzHeapChunkSimple *chunkInfo = Core()->getHeapChunk(item->addr);

View File

@ -1407,8 +1407,8 @@ void HexWidget::w_writeRandom()
}
bool ok = false;
int nbytes = QInputDialog::getInt(this, tr("Write random bytes"), tr("Number of bytes:"), size, 1,
0x7FFFFFFF, 1, &ok);
int nbytes = QInputDialog::getInt(this, tr("Write random bytes"), tr("Number of bytes:"), size,
1, 0x7FFFFFFF, 1, &ok);
if (!ok) {
return;
}

View File

@ -248,19 +248,24 @@ void HexdumpWidget::updateParseWindow(RVA start_address, int size)
ut64 old_offset = core->offset;
rz_core_seek(core, start_address, true);
ut8 *block = core->block;
char *digest = rz_hash_cfg_calculate_small_block_string(core->hash, "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_hash_cfg_calculate_small_block_string(core->hash, "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_hash_cfg_calculate_small_block_string(core->hash, "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_hash_cfg_calculate_small_block_string(core->hash, "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_hash_cfg_calculate_small_block_string(core->hash, "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);

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