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' image: 'Visual Studio 2017'
clone_depth: 1 clone_depth: 1

View File

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

View File

@ -26,27 +26,34 @@ jobs:
- '**.c' - '**.c'
- '**.h' - '**.h'
- '.github/workflows/linter.yml' - '.github/workflows/linter.yml'
- 'scripts/clang-format.py'
- '_clang-format'
clang-format: clang-format:
needs: changes needs: changes
runs-on: ubuntu-20.04 runs-on: ubuntu-22.04
if: ${{ needs.changes.outputs.clang-format == 'true' }} if: ${{ needs.changes.outputs.clang-format == 'true' }}
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v3 uses: actions/checkout@v3
- name: Install wget - name: Install wget, software-properties-common, lsb-release (dependencies of LLVM install script)
run: sudo apt --assume-yes install wget run: sudo apt --assume-yes install wget software-properties-common lsb-release
- name: Install automatic llvm (stable branch) - name: Uninstall old conflicting packages
run: sudo bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)" run: sudo apt purge --assume-yes --auto-remove llvm python3-lldb-14 llvm-14
- name: Install clang-format-11 - name: Install automatic LLVM 16
run: sudo apt --assume-yes install clang-format-11 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 - name: Install gitpython
run: sudo pip install gitpython run: sudo pip install gitpython
- name: Run clang-format - name: Run clang-format
run: | 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 # Local gdb files
.gdb_history .gdb_history
.gdbinit .gdbinit
# Kdevelop
.kdev/
*.kdev4

View File

@ -28,6 +28,7 @@ pipeline:
-DCUTTER_PACKAGE_JSDEC=ON -DCUTTER_PACKAGE_JSDEC=ON
-DCUTTER_PACKAGE_RZ_LIBSWIFT=ON -DCUTTER_PACKAGE_RZ_LIBSWIFT=ON
-DCUTTER_PACKAGE_RZ_LIBYARA=ON -DCUTTER_PACKAGE_RZ_LIBYARA=ON
-DCUTTER_PACKAGE_RZ_SILHOUETTE=ON
-DCPACK_PACKAGE_FILE_NAME="$$PACKAGE_NAME" -DCPACK_PACKAGE_FILE_NAME="$$PACKAGE_NAME"
-DCPACK_BUNDLE_APPLE_CERT_APP="-" -DCPACK_BUNDLE_APPLE_CERT_APP="-"
- ninja -C build - 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.\ 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) 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 "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) 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_KSYNTAXHIGHLIGHTING "Use KSyntaxHighlighting" AUTO)
tri_option(CUTTER_ENABLE_GRAPHVIZ "Enable use of graphviz for graph layout" 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_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_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_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_PACKAGE_JSDEC "Compile and install jsdec during install step." OFF)
OPTION(CUTTER_QT6 "Use QT6" OFF) OPTION(CUTTER_QT6 "Use QT6" OFF)
@ -36,7 +37,7 @@ endif()
set(CUTTER_VERSION_MAJOR 2) set(CUTTER_VERSION_MAJOR 2)
set(CUTTER_VERSION_MINOR 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}") set(CUTTER_VERSION "${CUTTER_VERSION_MAJOR}.${CUTTER_VERSION_MINOR}.${CUTTER_VERSION_PATCH}")
@ -99,6 +100,24 @@ if(CUTTER_ENABLE_PYTHON)
add_definitions(-DCUTTER_ENABLE_PYTHON) add_definitions(-DCUTTER_ENABLE_PYTHON)
if(CUTTER_ENABLE_PYTHON_BINDINGS) if(CUTTER_ENABLE_PYTHON_BINDINGS)
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()
# 5.12.3 => 5.12 # 5.12.3 => 5.12
if("${Qt5_VERSION}" MATCHES "^([0-9]+\\.[0-9]+)\\.[0-9]+") if("${Qt5_VERSION}" MATCHES "^([0-9]+\\.[0-9]+)\\.[0-9]+")
set(Shiboken2_VERSION_REQUIRED "${CMAKE_MATCH_1}") set(Shiboken2_VERSION_REQUIRED "${CMAKE_MATCH_1}")
@ -107,19 +126,21 @@ if(CUTTER_ENABLE_PYTHON)
endif() endif()
find_package(Shiboken2 "${Shiboken2_VERSION_REQUIRED}" REQUIRED) find_package(Shiboken2 "${Shiboken2_VERSION_REQUIRED}" REQUIRED)
find_package(PySide2 "${Shiboken2_VERSION_REQUIRED}" REQUIRED) find_package(PySide2 "${Shiboken2_VERSION_REQUIRED}" REQUIRED)
get_target_property(PYSIDE_INCLUDE_DIRS PySide2::pyside2 INTERFACE_INCLUDE_DIRECTORIES)
endif()
get_target_property(PYSIDE_INCLUDE_DIR PySide2::pyside2 INTERFACE_INCLUDE_DIRECTORIES) foreach(_dir IN LISTS PYSIDE_INCLUDE_DIRS)
list(GET PYSIDE_INCLUDE_DIR 0 PYSIDE_INCLUDE_DIR) include_directories(${_dir}
include_directories(${PYSIDE_INCLUDE_DIR} ${_dir}/QtCore
${PYSIDE_INCLUDE_DIR}/QtCore ${_dir}/QtGui
${PYSIDE_INCLUDE_DIR}/QtGui ${_dir}/QtWidgets)
${PYSIDE_INCLUDE_DIR}/QtWidgets) endforeach()
add_definitions(-DCUTTER_ENABLE_PYTHON_BINDINGS) add_definitions(-DCUTTER_ENABLE_PYTHON_BINDINGS)
endif() endif()
endif() endif()
if(CUTTER_ENABLE_KSYNTAXHIGHLIGHTING) if(CUTTER_ENABLE_KSYNTAXHIGHLIGHTING AND (NOT CUTTER_QT6))
if(CUTTER_ENABLE_KSYNTAXHIGHLIGHTING STREQUAL AUTO) if(CUTTER_ENABLE_KSYNTAXHIGHLIGHTING STREQUAL AUTO)
find_package(KF5SyntaxHighlighting) find_package(KF5SyntaxHighlighting)
if(KF5SyntaxHighlighting_FOUND) if(KF5SyntaxHighlighting_FOUND)
@ -132,6 +153,9 @@ if(CUTTER_ENABLE_KSYNTAXHIGHLIGHTING)
set(KSYNTAXHIGHLIGHTING_STATUS ON) set(KSYNTAXHIGHLIGHTING_STATUS ON)
endif() endif()
else() else()
if(CUTTER_ENABLE_KSYNTAXHIGHLIGHTING AND CUTTER_QT6)
message(WARNING "KSyntaxHighlighting has been disabled because not supported in QT6")
endif()
set(KSYNTAXHIGHLIGHTING_STATUS OFF) set(KSYNTAXHIGHLIGHTING_STATUS OFF)
endif() endif()
@ -161,6 +185,7 @@ message(STATUS "- Package Dependencies: ${CUTTER_PACKAGE_DEPENDENCIES}")
message(STATUS "- Package RzGhidra: ${CUTTER_PACKAGE_RZ_GHIDRA}") message(STATUS "- Package RzGhidra: ${CUTTER_PACKAGE_RZ_GHIDRA}")
message(STATUS "- Package RzLibSwift: ${CUTTER_PACKAGE_RZ_LIBSWIFT}") message(STATUS "- Package RzLibSwift: ${CUTTER_PACKAGE_RZ_LIBSWIFT}")
message(STATUS "- Package RzLibYara: ${CUTTER_PACKAGE_RZ_LIBYARA}") 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 "- Package JSDec: ${CUTTER_PACKAGE_JSDEC}")
message(STATUS "- QT6: ${CUTTER_QT6}") message(STATUS "- QT6: ${CUTTER_QT6}")
message(STATUS "") message(STATUS "")

View File

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

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/ro/cutter_ro.ts
translations/ru/cutter_ru.ts translations/ru/cutter_ru.ts
translations/tr/cutter_tr.ts translations/tr/cutter_tr.ts
translations/uk/cutter_uk.ts
translations/zh-CN/cutter_zh.ts translations/zh-CN/cutter_zh.ts
) )
# translations/ko/cutter_ko.ts problems with fonts # 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) if(WIN32)
set(CPACK_GENERATOR "ZIP") set(CPACK_GENERATOR "ZIP")
set(RIZIN_INSTALL_PLUGDIR "lib/plugins") set(RIZIN_INSTALL_PLUGDIR "lib/rizin/plugins")
if (CUTTER_PACKAGE_DEPENDENCIES) if (CUTTER_PACKAGE_DEPENDENCIES)
if (CUTTER_ENABLE_PYTHON) if (CUTTER_ENABLE_PYTHON)
@ -59,6 +59,18 @@ if(WIN32)
endif() endif()
") ")
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() endif()
################################################ ################################################
@ -150,6 +162,18 @@ if(CUTTER_ENABLE_DEPENDENCY_DOWNLOADS AND (NOT WIN32))
endif() endif()
") ")
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() endif()
################################################ ################################################
@ -164,9 +188,9 @@ if(CUTTER_PACKAGE_RZ_GHIDRA)
# installed Cutter. # installed Cutter.
ExternalProject_Add(rz-ghidra ExternalProject_Add(rz-ghidra
GIT_REPOSITORY https://github.com/rizinorg/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 c7a50a2e7c0a95cd52b167c9ee0fa1805223f08e
#GIT_TAG dev GIT_TAG dev
#GIT_SHALLOW ON # disable this line when using commit hash #GIT_SHALLOW ON # disable this line when using commit hash
CONFIGURE_COMMAND "" CONFIGURE_COMMAND ""
BUILD_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 git clone https://github.com/rizinorg/jsdec.git --depth 1 --branch master
} }
cd jsdec 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 ninja -C build install
$ErrorActionPreference = 'Stop' $ErrorActionPreference = 'Stop'
$pathdll = "$dist\lib\plugins\core_pdd.dll" $pathdll = "$dist\lib\rizin\plugins\core_pdd.dll"
if(![System.IO.File]::Exists($pathdll)) { if(![System.IO.File]::Exists($pathdll)) {
type build\meson-logs\meson-log.txt type build\meson-logs\meson-log.txt
ls "$dist\lib\rizin\plugins\"
throw (New-Object System.IO.FileNotFoundException("File not found: $pathdll", $pathdll)) 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 cd libswift
& meson.exe --buildtype=release --prefix=$dist build & meson.exe --buildtype=release --prefix=$dist build
ninja -C build install ninja -C build install
$pathdll = "$dist/lib/plugins/swift.dll" $pathdll = "$dist\lib\rizin\plugins\swift.dll"
if(![System.IO.File]::Exists($pathdll)) { if(![System.IO.File]::Exists($pathdll)) {
type build/meson-logs/meson-log.txt 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)) 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 git -C rz_libyara submodule update
} }
cd rz_libyara 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 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)) { if(![System.IO.File]::Exists($pathdll)) {
type build/meson-logs/meson-log.txt 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)) 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 cd cutter-plugin
mkdir build 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_USE_BUNDLED_RIZIN`` automatically compile Rizin from submodule (Enabled by default).
* ``CUTTER_ENABLE_PYTHON`` compile with Python support. * ``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_KSYNTAXHIGHLIGHTING`` use KSyntaxHighlighting for code highlighting.
* ``CUTTER_ENABLE_GRAPHVIZ`` enable Graphviz for graph layouts. * ``CUTTER_ENABLE_GRAPHVIZ`` enable Graphviz for graph layouts.
* ``CUTTER_EXTRA_PLUGIN_DIRS`` List of addition plugin locations. Useful when preparing package for Linux distros that have strict package layout rules. * ``CUTTER_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 # The short X.Y version
version = '2.2' version = '2.2'
# The full version, including a2lpha/beta/rc tags # The full version, including a2lpha/beta/rc tags
release = '2.2.1' release = '2.2.0'
# -- General configuration --------------------------------------------------- # -- General configuration ---------------------------------------------------

View File

@ -40,21 +40,45 @@ Options
**2** **2**
aaaa (experimental) 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> .. 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> .. 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> .. option:: -i <file>
Run script file Run script file.
.. option:: -p, --project <file> .. option:: -p, --project <file>
Load project file Load project file.
.. option:: -w, --writemode .. option:: -w, --writemode
@ -62,9 +86,13 @@ Options
When used together with -A/--analysis <level>, it will open a file directly When used together with -A/--analysis <level>, it will open a file directly
in write mode without any further dialog or confirmation. in write mode without any further dialog or confirmation.
.. option:: -P, --phymode
Disables virtual addressing.
.. option:: --pythonhome <PYTHONHOME> .. option:: --pythonhome <PYTHONHOME>
PYTHONHOME to use for the embedded python interpreter PYTHONHOME to use for the embedded python interpreter.
.. option:: --no-output-redirect .. 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 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 ninja -C build install
cd cutter-plugin 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/Cutter.cpp
core/CutterJson.cpp core/CutterJson.cpp
core/RizinCpp.cpp core/RizinCpp.cpp
core/Basefind.cpp
dialogs/EditStringDialog.cpp dialogs/EditStringDialog.cpp
dialogs/WriteCommandsDialogs.cpp dialogs/WriteCommandsDialogs.cpp
widgets/DisassemblerGraphView.cpp widgets/DisassemblerGraphView.cpp
@ -17,6 +18,7 @@ set(SOURCES
dialogs/CommentsDialog.cpp dialogs/CommentsDialog.cpp
dialogs/EditInstructionDialog.cpp dialogs/EditInstructionDialog.cpp
dialogs/FlagDialog.cpp dialogs/FlagDialog.cpp
dialogs/GlobalVariableDialog.cpp
dialogs/RemoteDebugDialog.cpp dialogs/RemoteDebugDialog.cpp
dialogs/NativeDebugDialog.cpp dialogs/NativeDebugDialog.cpp
dialogs/XrefsDialog.cpp dialogs/XrefsDialog.cpp
@ -35,6 +37,7 @@ set(SOURCES
widgets/ExportsWidget.cpp widgets/ExportsWidget.cpp
widgets/FlagsWidget.cpp widgets/FlagsWidget.cpp
widgets/FunctionsWidget.cpp widgets/FunctionsWidget.cpp
widgets/GlobalsWidget.cpp
widgets/ImportsWidget.cpp widgets/ImportsWidget.cpp
widgets/Omnibar.cpp widgets/Omnibar.cpp
widgets/RelocsWidget.cpp widgets/RelocsWidget.cpp
@ -149,6 +152,9 @@ set(SOURCES
dialogs/GlibcHeapBinsDialog.cpp dialogs/GlibcHeapBinsDialog.cpp
widgets/HeapBinsGraphView.cpp widgets/HeapBinsGraphView.cpp
dialogs/ArenaInfoDialog.cpp dialogs/ArenaInfoDialog.cpp
tools/basefind/BaseFindDialog.cpp
tools/basefind/BaseFindSearchDialog.cpp
tools/basefind/BaseFindResultsDialog.cpp
) )
set(HEADER_FILES set(HEADER_FILES
core/Cutter.h core/Cutter.h
@ -156,6 +162,7 @@ set(HEADER_FILES
core/CutterDescriptions.h core/CutterDescriptions.h
core/CutterJson.h core/CutterJson.h
core/RizinCpp.h core/RizinCpp.h
core/Basefind.h
dialogs/EditStringDialog.h dialogs/EditStringDialog.h
dialogs/WriteCommandsDialogs.h dialogs/WriteCommandsDialogs.h
widgets/DisassemblerGraphView.h widgets/DisassemblerGraphView.h
@ -167,6 +174,7 @@ set(HEADER_FILES
dialogs/CommentsDialog.h dialogs/CommentsDialog.h
dialogs/EditInstructionDialog.h dialogs/EditInstructionDialog.h
dialogs/FlagDialog.h dialogs/FlagDialog.h
dialogs/GlobalVariableDialog.h
dialogs/RemoteDebugDialog.h dialogs/RemoteDebugDialog.h
dialogs/NativeDebugDialog.h dialogs/NativeDebugDialog.h
dialogs/XrefsDialog.h dialogs/XrefsDialog.h
@ -308,6 +316,9 @@ set(HEADER_FILES
dialogs/GlibcHeapBinsDialog.h dialogs/GlibcHeapBinsDialog.h
widgets/HeapBinsGraphView.h widgets/HeapBinsGraphView.h
dialogs/ArenaInfoDialog.h dialogs/ArenaInfoDialog.h
tools/basefind/BaseFindDialog.h
tools/basefind/BaseFindSearchDialog.h
tools/basefind/BaseFindResultsDialog.h
) )
set(UI_FILES set(UI_FILES
dialogs/AboutDialog.ui dialogs/AboutDialog.ui
@ -319,6 +330,7 @@ set(UI_FILES
dialogs/CommentsDialog.ui dialogs/CommentsDialog.ui
dialogs/EditInstructionDialog.ui dialogs/EditInstructionDialog.ui
dialogs/FlagDialog.ui dialogs/FlagDialog.ui
dialogs/GlobalVariableDialog.ui
dialogs/RemoteDebugDialog.ui dialogs/RemoteDebugDialog.ui
dialogs/NativeDebugDialog.ui dialogs/NativeDebugDialog.ui
dialogs/XrefsDialog.ui dialogs/XrefsDialog.ui
@ -330,6 +342,7 @@ set(UI_FILES
widgets/Dashboard.ui widgets/Dashboard.ui
widgets/EntrypointWidget.ui widgets/EntrypointWidget.ui
widgets/FlagsWidget.ui widgets/FlagsWidget.ui
widgets/GlobalsWidget.ui
widgets/StringsWidget.ui widgets/StringsWidget.ui
widgets/HexdumpWidget.ui widgets/HexdumpWidget.ui
dialogs/preferences/PreferencesDialog.ui dialogs/preferences/PreferencesDialog.ui
@ -338,7 +351,6 @@ set(UI_FILES
dialogs/preferences/InitializationFileEditor.ui dialogs/preferences/InitializationFileEditor.ui
widgets/QuickFilterView.ui widgets/QuickFilterView.ui
widgets/DecompilerWidget.ui widgets/DecompilerWidget.ui
widgets/ClassesWidget.ui
widgets/VTablesWidget.ui widgets/VTablesWidget.ui
widgets/TypesWidget.ui widgets/TypesWidget.ui
widgets/SearchWidget.ui widgets/SearchWidget.ui
@ -378,6 +390,9 @@ set(UI_FILES
widgets/GlibcHeapWidget.ui widgets/GlibcHeapWidget.ui
dialogs/GlibcHeapBinsDialog.ui dialogs/GlibcHeapBinsDialog.ui
dialogs/ArenaInfoDialog.ui dialogs/ArenaInfoDialog.ui
tools/basefind/BaseFindDialog.ui
tools/basefind/BaseFindSearchDialog.ui
tools/basefind/BaseFindResultsDialog.ui
) )
set(QRC_FILES set(QRC_FILES
resources.qrc resources.qrc
@ -387,16 +402,26 @@ set(QRC_FILES
themes/lightstyle/light.qrc themes/lightstyle/light.qrc
) )
set(CUTTER_INCLUDE_DIRECTORIES core widgets common plugins menus .)
if (CUTTER_ENABLE_PYTHON) if (CUTTER_ENABLE_PYTHON)
list(APPEND SOURCES common/QtResImporter.cpp common/PythonManager.cpp common/PythonAPI.cpp) 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) list(APPEND HEADER_FILES common/QtResImporter.h common/PythonManager.h common/PythonAPI.h)
endif() endif()
if(CUTTER_ENABLE_PYTHON_BINDINGS) 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_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/bindings")
set(BINDINGS_BUILD_DIR "${CMAKE_CURRENT_BINARY_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) 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") 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) set(SHIBOKEN_OPTIONS)
list(APPEND SHIBOKEN_OPTIONS --include-paths="${SHIBOKEN_INCLUDE_DIRS}")
if (WIN32) if (WIN32)
list(APPEND SHIBOKEN_OPTIONS --avoid-protected-hack) list(APPEND SHIBOKEN_OPTIONS --avoid-protected-hack)
endif() endif()
add_custom_command(OUTPUT ${BINDINGS_SOURCE} add_custom_command(OUTPUT ${BINDINGS_SOURCE}
COMMAND Shiboken2::shiboken2 --project-file="${BINDINGS_BUILD_DIR}/bindings.txt" ${SHIBOKEN_OPTIONS} ${SHIBOKEN_EXTRA_OPTIONS} COMMAND "${SHIBOKEN_COMMAND}" --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" DEPENDS "${BINDINGS_BUILD_DIR}/bindings.xml" "${BINDINGS_BUILD_DIR}/bindings.txt"
IMPLICIT_DEPENDS CXX "${CMAKE_CURRENT_SOURCE_DIR}/bindings/bindings.h" IMPLICIT_DEPENDS CXX "${CMAKE_CURRENT_SOURCE_DIR}/bindings/bindings.h"
COMMENT "Generating Python bindings with shiboken2") COMMENT "Generating Python bindings with shiboken2")
else() else()
@ -453,7 +504,6 @@ target_compile_definitions(Cutter PRIVATE CUTTER_SOURCE_BUILD)
# Set Cutter as the startup project in Visual Studio # Set Cutter as the startup project in Visual Studio
set_property(DIRECTORY ${PROJECT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT Cutter) 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}) foreach(_dir ${CUTTER_INCLUDE_DIRECTORIES})
target_include_directories(Cutter PUBLIC target_include_directories(Cutter PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/${_dir}> $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/${_dir}>
@ -488,7 +538,11 @@ if(CUTTER_ENABLE_PYTHON)
endif() endif()
target_link_libraries(Cutter PRIVATE ${PYTHON_LIBRARIES}) target_link_libraries(Cutter PRIVATE ${PYTHON_LIBRARIES})
if(CUTTER_ENABLE_PYTHON_BINDINGS) if(CUTTER_ENABLE_PYTHON_BINDINGS)
if (CUTTER_QT6)
target_link_libraries(Cutter PRIVATE Shiboken6::libshiboken PySide6::pyside6)
else()
target_link_libraries(Cutter PRIVATE Shiboken2::libshiboken PySide2::pyside2) target_link_libraries(Cutter PRIVATE Shiboken2::libshiboken PySide2::pyside2)
endif()
get_target_property(RAW_BINDINGS_INCLUDE_DIRS Cutter INCLUDE_DIRECTORIES) get_target_property(RAW_BINDINGS_INCLUDE_DIRS Cutter INCLUDE_DIRECTORIES)
if(NOT CUTTER_USE_BUNDLED_RIZIN) 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/QtGui")
list(APPEND BINDINGS_INCLUDE_DIRS "${_qt5Core_install_prefix}/include/QtWidgets") list(APPEND BINDINGS_INCLUDE_DIRS "${_qt5Core_install_prefix}/include/QtWidgets")
endif() endif()
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}) 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 ${Rizin_INCLUDE_DIRS})
list(APPEND BINDINGS_INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}") list(APPEND BINDINGS_INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}")
if (NOT WIN32) if (NOT WIN32)

View File

@ -33,9 +33,9 @@
// has RZ_GITTAP defined and uses it in rz_core_version(). // has RZ_GITTAP defined and uses it in rz_core_version().
// After that, RZ_GITTAP is not defined anymore and RZ_VERSION is used. // After that, RZ_GITTAP is not defined anymore and RZ_VERSION is used.
#ifdef RZ_GITTAP #ifdef RZ_GITTAP
#define CUTTER_COMPILE_TIME_RZ_VERSION "" RZ_GITTAP # define CUTTER_COMPILE_TIME_RZ_VERSION "" RZ_GITTAP
#else #else
#define CUTTER_COMPILE_TIME_RZ_VERSION "" RZ_VERSION # define CUTTER_COMPILE_TIME_RZ_VERSION "" RZ_VERSION
#endif #endif
CutterApplication::CutterApplication(int &argc, char **argv) : QApplication(argc, argv) CutterApplication::CutterApplication(int &argc, char **argv) : QApplication(argc, argv)
@ -162,7 +162,8 @@ CutterApplication::CutterApplication(int &argc, char **argv) : QApplication(argc
appdir.cdUp(); // appdir appdir.cdUp(); // appdir
auto sleighHome = 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()); Core()->setConfig("ghidra.sleighhome", sleighHome.absolutePath());
} }
#endif #endif
@ -174,8 +175,8 @@ CutterApplication::CutterApplication(int &argc, char **argv) : QApplication(argc
rzprefix.cd("Resources"); // Contents/Resources/ rzprefix.cd("Resources"); // Contents/Resources/
auto sleighHome = rzprefix; auto sleighHome = rzprefix;
sleighHome.cd( // Contents/Resources/lib/rizin/plugins/rz_ghidra_sleigh
"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()); Core()->setConfig("ghidra.sleighhome", sleighHome.absolutePath());
} }
#endif #endif
@ -183,7 +184,7 @@ CutterApplication::CutterApplication(int &argc, char **argv) : QApplication(argc
#if defined(Q_OS_WIN) && defined(CUTTER_ENABLE_PACKAGING) #if defined(Q_OS_WIN) && defined(CUTTER_ENABLE_PACKAGING)
{ {
auto sleighHome = QDir(QCoreApplication::applicationDirPath()); 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()); Core()->setConfig("ghidra.sleighhome", sleighHome.absolutePath());
} }
#endif #endif
@ -295,6 +296,89 @@ bool CutterApplication::loadTranslations()
return false; 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() bool CutterApplication::parseCommandLineOptions()
{ {
// Keep this function in sync with documentation // Keep this function in sync with documentation
@ -314,6 +398,27 @@ bool CutterApplication::parseCommandLineOptions()
QObject::tr("level")); QObject::tr("level"));
cmd_parser.addOption(analOption); 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" }, QCommandLineOption formatOption({ "F", "format" },
QObject::tr("Force using a specific file format (bin plugin)"), QObject::tr("Force using a specific file format (bin plugin)"),
QObject::tr("name")); QObject::tr("name"));
@ -324,6 +429,11 @@ bool CutterApplication::parseCommandLineOptions()
QObject::tr("base address")); QObject::tr("base address"));
cmd_parser.addOption(baddrOption); 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")); QCommandLineOption scriptOption("i", QObject::tr("Run script file"), QObject::tr("file"));
cmd_parser.addOption(scriptOption); cmd_parser.addOption(scriptOption);
@ -335,6 +445,10 @@ bool CutterApplication::parseCommandLineOptions()
QObject::tr("Open file in write mode")); QObject::tr("Open file in write mode"));
cmd_parser.addOption(writeModeOption); cmd_parser.addOption(writeModeOption);
QCommandLineOption phyModeOption({ "P", "phymode" },
QObject::tr("Disables virtual addressing"));
cmd_parser.addOption(phyModeOption);
QCommandLineOption pythonHomeOption( QCommandLineOption pythonHomeOption(
"pythonhome", QObject::tr("PYTHONHOME to use for embedded python interpreter"), "pythonhome", QObject::tr("PYTHONHOME to use for embedded python interpreter"),
"PYTHONHOME"); "PYTHONHOME");
@ -396,15 +510,21 @@ bool CutterApplication::parseCommandLineOptions()
return false; return false;
} }
InitialOptions options;
if (!opts.args.isEmpty()) { if (!opts.args.isEmpty()) {
opts.fileOpenOptions.filename = opts.args[0]; opts.fileOpenOptions.filename = opts.args[0];
opts.fileOpenOptions.forceBinPlugin = cmd_parser.value(formatOption); opts.fileOpenOptions.forceBinPlugin = cmd_parser.value(formatOption);
if (cmd_parser.isSet(baddrOption)) { if (cmd_parser.isSet(baddrOption)) {
bool ok; bool ok = false;
RVA baddr = cmd_parser.value(baddrOption).toULongLong(&ok, 0); RVA baddr = cmd_parser.value(baddrOption).toULongLong(&ok, 0);
if (ok) { 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) { switch (opts.analysisLevel) {
@ -421,8 +541,36 @@ bool CutterApplication::parseCommandLineOptions()
break; break;
} }
opts.fileOpenOptions.script = cmd_parser.value(scriptOption); 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.writeEnabled = cmd_parser.isSet(writeModeOption);
opts.fileOpenOptions.useVA = !cmd_parser.isSet(phyModeOption);
} }
opts.fileOpenOptions.projectFile = cmd_parser.value(projectOption); opts.fileOpenOptions.projectFile = cmd_parser.value(projectOption);

View File

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

View File

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

View File

@ -3,7 +3,7 @@
generator-set = shiboken generator-set = shiboken
header-file = ${BINDINGS_SRC_DIR}/bindings.h 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} output-directory = ${BINDINGS_BUILD_DIR}

View File

@ -30,7 +30,7 @@
PyErr_Print(); PyErr_Print();
return QString(); 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) { 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); 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(); 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): 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() ts_root = ts_tree.getroot()
package = ts_root.attrib["package"] package = ts_root.attrib["package"]

View File

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

View File

@ -783,6 +783,16 @@ bool Configuration::getPreviewValue() const
return s.value("asm.preview").toBool(); 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() bool Configuration::getGraphBlockEntryOffset()
{ {
return s.value("graphBlockEntryOffset", true).value<bool>(); return s.value("graphBlockEntryOffset", true).value<bool>();

View File

@ -215,6 +215,12 @@ public:
void setPreviewValue(bool checked); void setPreviewValue(bool checked);
bool getPreviewValue() const; 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. * @brief Recently opened binaries, as shown in NewFileDialog.
*/ */

View File

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

View File

@ -19,8 +19,12 @@ public:
* signal will be emitted. * signal will be emitted.
* In any case, CutterSeekable::seekableSeekChanged is emitted. * In any case, CutterSeekable::seekableSeekChanged is emitted.
* @param addr the location to seek at. * @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 * @brief setSynchronization sets
@ -67,7 +71,7 @@ private slots:
/** /**
* @brief onCoreSeekChanged * @brief onCoreSeekChanged
*/ */
void onCoreSeekChanged(RVA addr); void onCoreSeekChanged(RVA addr, CutterCore::SeekHistoryType type);
private: private:
/** /**
@ -91,9 +95,9 @@ private:
* @brief internal method for changing the seek * @brief internal method for changing the seek
* @param localOnly whether the seek should be updated globally if synchronized * @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: signals:
void seekableSeekChanged(RVA addr); void seekableSeekChanged(RVA addr, CutterCore::SeekHistoryType type);
void syncChanged(); void syncChanged();
}; };

View File

@ -82,3 +82,75 @@ RVA DisassemblyPreview::readDisassemblyOffset(QTextCursor tc)
return userData->line.offset; 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 * @return The disassembly offset of the hovered asm text
*/ */
RVA readDisassemblyOffset(QTextCursor tc); 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 #endif

View File

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

View File

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

View File

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

View File

@ -7,7 +7,8 @@
namespace Cutter { namespace Cutter {
void initializeSettings(); 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. * @return True if this is first time running Cutter and r2 based Cutter <= 1.12 settings exist.
*/ */
bool shouldOfferSettingImport(); 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()) { if (!cutterRCFileInfo.exists() || !cutterRCFileInfo.isFile()) {
continue; continue;
} }
qInfo() << "Loading initialization file from " << cutterRCFilePath; qInfo() << tr("Loading initialization file from ") << cutterRCFilePath;
rz_core_cmd_file(core, cutterRCFilePath.toUtf8().constData()); rz_core_cmd_file(core, cutterRCFilePath.toUtf8().constData());
} }
} }
@ -284,7 +284,7 @@ void CutterCore::loadDefaultCutterRC()
if (!cutterRCFileInfo.exists() || !cutterRCFileInfo.isFile()) { if (!cutterRCFileInfo.exists() || !cutterRCFileInfo.isFile()) {
return; return;
} }
qInfo() << "Loading initialization file from " << cutterRCFilePath; qInfo() << tr("Loading initialization file from ") << cutterRCFilePath;
rz_core_cmd_file(core, cutterRCFilePath.toUtf8().constData()); rz_core_cmd_file(core, cutterRCFilePath.toUtf8().constData());
} }
@ -1002,19 +1002,19 @@ void CutterCore::seekPrev()
{ {
CORE_LOCK(); CORE_LOCK();
rz_core_seek_undo(core); rz_core_seek_undo(core);
updateSeek(); updateSeek(SeekHistoryType::Undo);
} }
void CutterCore::seekNext() void CutterCore::seekNext()
{ {
CORE_LOCK(); CORE_LOCK();
rz_core_seek_redo(core); 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) RVA CutterCore::prevOpAddr(RVA startAddr, int count)
@ -1804,11 +1804,43 @@ QList<VariableDescription> CutterCore::getVariables(RVA at)
} }
desc.type = QString::fromUtf8(tn); desc.type = QString::fromUtf8(tn);
rz_mem_free(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); ret.push_back(desc);
} }
return ret; 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> CutterCore::getRegisterRefValues()
{ {
QVector<RegisterRefValueDescription> result; QVector<RegisterRefValueDescription> result;
@ -3084,8 +3116,6 @@ QList<ImportDescription> CutterCore::getAllImports()
RzBinImport *import; RzBinImport *import;
RzListIter *iter; RzListIter *iter;
bool va = core->io->va || core->bin->is_debugger; 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) { CutterRzListForeach (imports, iter, RzBinImport, import) {
if (RZ_STR_ISEMPTY(import->name)) { if (RZ_STR_ISEMPTY(import->name)) {
continue; continue;
@ -3099,13 +3129,6 @@ QList<ImportDescription> CutterCore::getAllImports()
if (RZ_STR_ISNOTEMPTY(import->classname)) { if (RZ_STR_ISNOTEMPTY(import->classname)) {
name = QString("%1.%2").arg(import->classname, import->name); 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) { if (core->bin->prefix) {
name = QString("%1.%2").arg(core->bin->prefix, name); name = QString("%1.%2").arg(core->bin->prefix, name);
} }
@ -3135,8 +3158,8 @@ QList<ExportDescription> CutterCore::getAllExports()
return {}; return {};
} }
QString lang = getConfigi("bin.demangle") ? getConfig("bin.lang") : "";
bool va = core->io->va || core->bin->is_debugger; bool va = core->io->va || core->bin->is_debugger;
bool demangle = rz_config_get_b(core->config, "bin.demangle");
QList<ExportDescription> ret; QList<ExportDescription> ret;
for (const auto &symbol : CutterRzList<RzBinSymbol>(symbols)) { for (const auto &symbol : CutterRzList<RzBinSymbol>(symbols)) {
@ -3145,7 +3168,7 @@ QList<ExportDescription> CutterCore::getAllExports()
} }
RzBinSymNames sn = {}; 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 exportDescription;
exportDescription.vaddr = rva(bf->o, symbol->paddr, symbol->vaddr, va); exportDescription.vaddr = rva(bf->o, symbol->paddr, symbol->vaddr, va);
@ -3543,19 +3566,18 @@ QList<BinClassDescription> CutterCore::getAllClassesFromBin()
RzListIter *iter, *iter2, *iter3; RzListIter *iter, *iter2, *iter3;
RzBinClass *c; RzBinClass *c;
RzBinSymbol *sym; RzBinSymbol *sym;
RzBinField *f; RzBinClassField *f;
CutterRzListForeach (cs, iter, RzBinClass, c) { CutterRzListForeach (cs, iter, RzBinClass, c) {
BinClassDescription classDescription; BinClassDescription classDescription;
classDescription.name = c->name; classDescription.name = c->name;
classDescription.addr = c->addr; classDescription.addr = c->addr;
classDescription.index = c->index;
CutterRzListForeach (c->methods, iter2, RzBinSymbol, sym) { CutterRzListForeach (c->methods, iter2, RzBinSymbol, sym) {
BinClassMethodDescription methodDescription; BinClassMethodDescription methodDescription;
methodDescription.name = sym->name; methodDescription.name = sym->name;
methodDescription.addr = sym->vaddr; methodDescription.addr = sym->vaddr;
classDescription.methods << methodDescription; classDescription.methods << methodDescription;
} }
CutterRzListForeach (c->fields, iter3, RzBinField, f) { CutterRzListForeach (c->fields, iter3, RzBinClassField, f) {
BinClassFieldDescription fieldDescription; BinClassFieldDescription fieldDescription;
fieldDescription.name = f->name; fieldDescription.name = f->name;
fieldDescription.addr = f->vaddr; fieldDescription.addr = f->vaddr;
@ -3591,7 +3613,6 @@ QList<BinClassDescription> CutterCore::getAllClassesFromFlags()
} }
desc->name = match.captured(1); desc->name = match.captured(1);
desc->addr = item.offset; desc->addr = item.offset;
desc->index = RVA_INVALID;
continue; continue;
} }
@ -3605,7 +3626,6 @@ QList<BinClassDescription> CutterCore::getAllClassesFromFlags()
BinClassDescription cls; BinClassDescription cls;
cls.name = tr("Unknown (%1)").arg(className); cls.name = tr("Unknown (%1)").arg(className);
cls.addr = RVA_INVALID; cls.addr = RVA_INVALID;
cls.index = 0;
ret << cls; ret << cls;
classDesc = &ret.last(); classDesc = &ret.last();
classesCache[className] = classDesc; classesCache[className] = classDesc;
@ -4028,6 +4048,99 @@ QList<XrefDescription> CutterCore::getXRefs(RVA addr, bool to, bool whole_functi
return xrefList; 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) void CutterCore::addFlag(RVA offset, QString name, RVA size)
{ {
name = sanitizeStringForCommand(name); name = sanitizeStringForCommand(name);
@ -4149,11 +4262,20 @@ QList<DisassemblyLine> CutterCore::disassembleLines(RVA offset, int lines)
QList<DisassemblyLine> r; QList<DisassemblyLine> r;
for (const auto &t : CutterPVector<RzAnalysisDisasmText>(vec.get())) { for (const auto &t : CutterPVector<RzAnalysisDisasmText>(vec.get())) {
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; DisassemblyLine line;
line.offset = t->offset; line.offset = t->offset;
line.text = ansiEscapeToHtml(t->text); line.text = ansiEscapeToHtml(tok);
line.arrow = t->arrow; line.arrow = t->arrow;
r << line; r << line;
// only the first one.
t->arrow = RVA_INVALID;
}
} }
return r; return r;
} }
@ -4533,9 +4655,9 @@ char *CutterCore::getTextualGraphAt(RzCoreGraphType type, RzCoreGraphFormat form
RzGraph *graph = rz_core_graph(core, type, address); RzGraph *graph = rz_core_graph(core, type, address);
if (!graph) { if (!graph) {
if (address == RVA_INVALID) { if (address == RVA_INVALID) {
qWarning() << "Cannot get global graph"; qWarning() << tr("Cannot get global graph");
} else { } else {
qWarning() << "Cannot get graph at " << RzAddressString(address); qWarning() << tr("Cannot get graph at ") << RzAddressString(address);
} }
return nullptr; return nullptr;
} }
@ -4566,7 +4688,7 @@ char *CutterCore::getTextualGraphAt(RzCoreGraphType type, RzCoreGraphFormat form
rz_graph_free(graph); rz_graph_free(graph);
if (!string) { if (!string) {
qWarning() << "Failed to generate graph"; qWarning() << tr("Failed to generate graph");
} }
return string; return string;
@ -4584,9 +4706,15 @@ void CutterCore::writeGraphvizGraphToFile(QString path, QString format, RzCoreGr
if (!rz_core_graph_write(core, address, type, filepath)) { if (!rz_core_graph_write(core, address, type, filepath)) {
if (address == RVA_INVALID) { if (address == RVA_INVALID) {
qWarning() << "Cannot get global graph"; qWarning() << tr("Cannot get global graph");
} else { } 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/CutterCommon.h"
#include "core/CutterDescriptions.h" #include "core/CutterDescriptions.h"
#include "core/CutterJson.h" #include "core/CutterJson.h"
#include "core/Basefind.h"
#include "common/BasicInstructionHighlighter.h" #include "common/BasicInstructionHighlighter.h"
#include <QMap> #include <QMap>
@ -69,6 +70,7 @@ class CUTTER_EXPORT CutterCore : public QObject
friend class RzCoreLocked; friend class RzCoreLocked;
friend class RizinTask; friend class RizinTask;
friend class Basefind;
public: public:
explicit CutterCore(QObject *parent = nullptr); explicit CutterCore(QObject *parent = nullptr);
@ -172,6 +174,8 @@ public:
return returner; return returner;
} }
enum class SeekHistoryType { New, Undo, Redo };
CutterJson cmdj(const char *str); CutterJson cmdj(const char *str);
CutterJson cmdj(const QString &str) { return cmdj(str.toUtf8().constData()); } CutterJson cmdj(const QString &str) { return cmdj(str.toUtf8().constData()); }
QString cmdTask(const QString &str); QString cmdTask(const QString &str);
@ -235,6 +239,14 @@ public:
QString nearestFlag(RVA offset, RVA *flagOffsetOut); QString nearestFlag(RVA offset, RVA *flagOffsetOut);
void triggerFlagsChanged(); 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 */ /* Edition functions */
PRzAnalysisBytes getRzAnalysisBytesSingle(RVA addr); PRzAnalysisBytes getRzAnalysisBytesSingle(RVA addr);
QString getInstructionBytes(RVA addr); QString getInstructionBytes(RVA addr);
@ -324,7 +336,7 @@ public:
void seekSilent(QString thing) { seekSilent(math(thing)); } void seekSilent(QString thing) { seekSilent(math(thing)); }
void seekPrev(); void seekPrev();
void seekNext(); void seekNext();
void updateSeek(); void updateSeek(SeekHistoryType type = SeekHistoryType::New);
/** /**
* @brief Raise a memory widget showing current offset, prefer last active * @brief Raise a memory widget showing current offset, prefer last active
* memory widget. * memory widget.
@ -554,6 +566,8 @@ public:
void setGraphEmpty(bool empty); void setGraphEmpty(bool empty);
bool isGraphEmpty(); bool isGraphEmpty();
bool rebaseBin(RVA base_address);
void getRegs(); void getRegs();
QList<QString> regs; QList<QString> regs;
void setSettings(); void setSettings();
@ -578,6 +592,7 @@ public:
QList<ExportDescription> getAllExports(); QList<ExportDescription> getAllExports();
QList<SymbolDescription> getAllSymbols(); QList<SymbolDescription> getAllSymbols();
QList<HeaderDescription> getAllHeaders(); QList<HeaderDescription> getAllHeaders();
QList<GlobalDescription> getAllGlobals();
QList<FlirtDescription> getSignaturesDB(); QList<FlirtDescription> getSignaturesDB();
QList<CommentDescription> getAllComments(const QString &filterType); QList<CommentDescription> getAllComments(const QString &filterType);
QList<RelocDescription> getAllRelocs(); QList<RelocDescription> getAllRelocs();
@ -744,6 +759,7 @@ signals:
void functionRenamed(const RVA offset, const QString &new_name); void functionRenamed(const RVA offset, const QString &new_name);
void varsChanged(); void varsChanged();
void globalVarsChanged();
void functionsChanged(); void functionsChanged();
void flagsChanged(); void flagsChanged();
void commentsChanged(RVA addr); void commentsChanged(RVA addr);
@ -794,8 +810,9 @@ signals:
/** /**
* @brief seekChanged is emitted each time Rizin's seek value is modified * @brief seekChanged is emitted each time Rizin's seek value is modified
* @param offset * @param offset
* @param historyType
*/ */
void seekChanged(RVA offset); void seekChanged(RVA offset, SeekHistoryType type = SeekHistoryType::New);
void toggleDebugView(); void toggleDebugView();

View File

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

View File

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

View File

@ -31,6 +31,7 @@
#include "widgets/DisassemblerGraphView.h" #include "widgets/DisassemblerGraphView.h"
#include "widgets/GraphView.h" #include "widgets/GraphView.h"
#include "widgets/GraphWidget.h" #include "widgets/GraphWidget.h"
#include "widgets/GlobalsWidget.h"
#include "widgets/OverviewWidget.h" #include "widgets/OverviewWidget.h"
#include "widgets/OverviewView.h" #include "widgets/OverviewView.h"
#include "widgets/FunctionsWidget.h" #include "widgets/FunctionsWidget.h"
@ -115,6 +116,9 @@
#include <QGraphicsScene> #include <QGraphicsScene>
#include <QGraphicsView> #include <QGraphicsView>
// Tools
#include "tools/basefind/BaseFindDialog.h"
#define PROJECT_FILE_FILTER tr("Rizin Project (*.rzdb)") #define PROJECT_FILE_FILTER tr("Rizin Project (*.rzdb)")
template<class T> template<class T>
@ -268,7 +272,7 @@ void MainWindow::initUI()
readSettings(); readSettings();
// Display tooltip for the Analyze Program action // 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); ui->menuFile->setToolTipsVisible(true);
} }
@ -398,6 +402,7 @@ void MainWindow::initDocks()
sectionsDock = new SectionsWidget(this), sectionsDock = new SectionsWidget(this),
segmentsDock = new SegmentsWidget(this), segmentsDock = new SegmentsWidget(this),
symbolsDock = new SymbolsWidget(this), symbolsDock = new SymbolsWidget(this),
globalsDock = new GlobalsWidget(this),
vTablesDock = new VTablesWidget(this), vTablesDock = new VTablesWidget(this),
flirtDock = new FlirtWidget(this), flirtDock = new FlirtWidget(this),
rzGraphDock = new RizinGraphWidget(this), rzGraphDock = new RizinGraphWidget(this),
@ -902,6 +907,7 @@ void MainWindow::restoreDocks()
tabifyDockWidget(dashboardDock, headersDock); tabifyDockWidget(dashboardDock, headersDock);
tabifyDockWidget(dashboardDock, flirtDock); tabifyDockWidget(dashboardDock, flirtDock);
tabifyDockWidget(dashboardDock, symbolsDock); tabifyDockWidget(dashboardDock, symbolsDock);
tabifyDockWidget(dashboardDock, globalsDock);
tabifyDockWidget(dashboardDock, classesDock); tabifyDockWidget(dashboardDock, classesDock);
tabifyDockWidget(dashboardDock, resourcesDock); tabifyDockWidget(dashboardDock, resourcesDock);
tabifyDockWidget(dashboardDock, vTablesDock); tabifyDockWidget(dashboardDock, vTablesDock);
@ -1637,6 +1643,12 @@ void MainWindow::on_actionTabs_triggered()
setTabLocation(); setTabLocation();
} }
void MainWindow::on_actionBaseFind_triggered()
{
auto dialog = new BaseFindDialog(this);
dialog->show();
}
void MainWindow::on_actionAbout_triggered() void MainWindow::on_actionAbout_triggered()
{ {
AboutDialog *a = new AboutDialog(this); AboutDialog *a = new AboutDialog(this);
@ -1759,7 +1771,7 @@ void MainWindow::on_actionExport_as_code_triggered()
QFile file(dialog.selectedFiles()[0]); QFile file(dialog.selectedFiles()[0]);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
qWarning() << "Can't open file"; qWarning() << tr("Can't open file");
return; return;
} }
@ -1774,8 +1786,6 @@ void MainWindow::on_actionExport_as_code_triggered()
return; return;
} }
auto string = fromOwned( auto string = fromOwned(
dialog.selectedNameFilter() != instructionsInComments dialog.selectedNameFilter() != instructionsInComments
? rz_lang_byte_array(buffer.data(), size, typMap[dialog.selectedNameFilter()]) ? rz_lang_byte_array(buffer.data(), size, typMap[dialog.selectedNameFilter()])

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -49,8 +49,7 @@ void GlibcHeapBinsDialog::setChainInfo(int index)
RzHeapChunkListItem *item; RzHeapChunkListItem *item;
RzList *chunks = binsModel->getChunks(index); RzList *chunks = binsModel->getChunks(index);
QString chainInfo; QString chainInfo;
CutterRzListForeach(chunks, iter, RzHeapChunkListItem, item) CutterRzListForeach (chunks, iter, RzHeapChunkListItem, item) {
{
chainInfo += "" + RzAddressString(item->addr); 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 "core/Cutter.h"
#include "common/AnalysisTask.h" #include "common/AnalysisTask.h"
#include "CutterApplication.h"
InitialOptionsDialog::InitialOptionsDialog(MainWindow *main) InitialOptionsDialog::InitialOptionsDialog(MainWindow *main)
: QDialog(nullptr), // parent must not be 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)); 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); 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) void InitialOptionsDialog::setTooltipWithConfigHelp(QWidget *w, const char *config)
@ -246,7 +294,7 @@ QList<CommandDescription> InitialOptionsDialog::getSelectedAdvancedAnalCmds() co
return advanced; return advanced;
} }
void InitialOptionsDialog::setupAndStartAnalysis(/*int level, QList<QString> advanced*/) void InitialOptionsDialog::setupAndStartAnalysis()
{ {
InitialOptions options; InitialOptions options;
@ -322,6 +370,8 @@ void InitialOptionsDialog::setupAndStartAnalysis(/*int level, QList<QString> adv
Core()->getAsyncTaskManager()->start(analysisTaskPtr); Core()->getAsyncTaskManager()->start(analysisTaskPtr);
done(0); done(0);
static_cast<CutterApplication *>(qApp)->setInitialOptions(options);
} }
void InitialOptionsDialog::on_okButton_clicked() void InitialOptionsDialog::on_okButton_clicked()

View File

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

View File

@ -287,7 +287,8 @@ void NewFileDialog::fillIOPluginsList()
{ {
ui->ioPlugin->clear(); ui->ioPlugin->clear();
ui->ioPlugin->addItem("file://"); 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; int index = 1;
QList<RzIOPluginDescription> ioPlugins = Core()->getRIOPluginDescriptions(); QList<RzIOPluginDescription> ioPlugins = Core()->getRIOPluginDescriptions();

View File

@ -70,8 +70,10 @@ void TypesInteractionDialog::done(int r)
ui->plainTextEdit->toPlainText().toUtf8().constData()); ui->plainTextEdit->toPlainText().toUtf8().constData());
} else { } else {
char *error_msg = NULL; char *error_msg = NULL;
success = rz_type_parse_string_stateless(core->analysis->typedb->parser, success = rz_type_parse_string_stateless(
ui->plainTextEdit->toPlainText().toUtf8().constData(), &error_msg) == 0; core->analysis->typedb->parser,
ui->plainTextEdit->toPlainText().toUtf8().constData(), &error_msg)
== 0;
if (error_msg) { if (error_msg) {
RZ_LOG_ERROR("%s\n", error_msg); RZ_LOG_ERROR("%s\n", error_msg);
rz_mem_free(error_msg); rz_mem_free(error_msg);

View File

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

View File

@ -48,8 +48,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>582</width> <width>686</width>
<height>766</height> <height>886</height>
</rect> </rect>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_2"> <layout class="QVBoxLayout" name="verticalLayout_2">
@ -65,51 +65,114 @@
<string>Disassembly</string> <string>Disassembly</string>
</property> </property>
<layout class="QGridLayout" name="gridLayout"> <layout class="QGridLayout" name="gridLayout">
<item row="12" column="2"> <item row="5" column="2">
<widget class="QSpinBox" name="nbytesSpinBox"> <widget class="QComboBox" name="caseComboBox">
<property name="enabled"> <item>
<bool>true</bool>
</property>
</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">
<property name="text"> <property name="text">
<string>Align bytes to the left (asm.lbytes)</string> <string>Lowercase</string>
</property> </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> </widget>
</item> </item>
<item row="15" column="1"> <item row="17" column="1">
<widget class="QCheckBox" name="previewCheckBox"> <widget class="QLabel" name="asmTabsLabel">
<property name="text"> <property name="text">
<string>Show preview when hovering:</string> <string>Tabs in assembly (asm.tabs):</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLabel" name="syntaxLabel">
<property name="text">
<string>Syntax (asm.syntax):</string>
</property> </property>
<property name="textInteractionFlags"> <property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set> <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property> </property>
</widget> </widget>
</item> </item>
<item row="8" column="2"> <item row="11" column="1">
<widget class="QCheckBox" name="relOffFlagsCheckBox"> <widget class="QCheckBox" name="bytesCheckBox">
<property name="text"> <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> </property>
</widget> </widget>
</item> </item>
@ -132,23 +195,47 @@
</item> </item>
</widget> </widget>
</item> </item>
<item row="7" column="1"> <item row="14" column="1" colspan="2">
<widget class="QCheckBox" name="offsetCheckBox"> <widget class="QCheckBox" name="bblineCheckBox">
<property name="text"> <property name="text">
<string>Show offsets (asm.offset)</string> <string>Show empty line after every basic block (asm.bb.line)</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="17" column="1"> <item row="15" column="1">
<widget class="QLabel" name="asmTabsOffLabel"> <widget class="QCheckBox" name="previewCheckBox">
<property name="text"> <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>
<property name="textInteractionFlags"> <property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set> <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property> </property>
</widget> </widget>
</item> </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"> <item row="12" column="1">
<widget class="QLabel" name="nbytesLabel"> <widget class="QLabel" name="nbytesLabel">
<property name="enabled"> <property name="enabled">
@ -163,13 +250,13 @@
</widget> </widget>
</item> </item>
<item row="20" column="1" colspan="2"> <item row="20" column="1" colspan="2">
<widget class="QCheckBox" name="bytespaceCheckBox"> <widget class="QCheckBox" name="lbytesCheckBox">
<property name="text"> <property name="text">
<string>Separate bytes with whitespace (asm.bytes.space)</string> <string>Align bytes to the left (asm.lbytes)</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="16" column="2"> <item row="17" column="2">
<widget class="QSpinBox" name="asmTabsSpinBox"> <widget class="QSpinBox" name="asmTabsSpinBox">
<property name="maximum"> <property name="maximum">
<number>100</number> <number>100</number>
@ -179,107 +266,27 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="11" column="1"> <item row="8" column="2">
<widget class="QCheckBox" name="bytesCheckBox"> <widget class="QCheckBox" name="relOffFlagsCheckBox">
<property name="text"> <property name="text">
<string>Display the bytes of each instruction (asm.bytes)</string> <string>Flags (asm.reloff.flags)</string>
</property> </property>
</widget> </widget>
</item> </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"> <item row="4" column="2">
<widget class="QComboBox" name="syntaxComboBox"/> <widget class="QComboBox" name="syntaxComboBox"/>
</item> </item>
<item row="0" column="1" colspan="2"> <item row="12" column="2">
<spacer name="verticalSpacer_3"> <widget class="QSpinBox" name="nbytesSpinBox">
<property name="orientation"> <property name="enabled">
<enum>Qt::Vertical</enum> <bool>true</bool>
</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>
</property> </property>
</widget> </widget>
</item> </item>
<item row="16" column="1"> <item row="16" column="1">
<widget class="QLabel" name="asmTabsLabel"> <widget class="QCheckBox" name="varTooltipsCheckBox">
<property name="text"> <property name="text">
<string>Tabs in assembly (asm.tabs):</string> <string>Show known variable values when hovering</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>
</property> </property>
</widget> </widget>
</item> </item>
@ -415,8 +422,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>454</width> <width>518</width>
<height>286</height> <height>326</height>
</rect> </rect>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_6"> <layout class="QVBoxLayout" name="verticalLayout_6">

View File

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

View File

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

View File

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

View File

@ -45,6 +45,7 @@ private slots:
void on_actionAddComment_triggered(); void on_actionAddComment_triggered();
void on_actionAnalyzeFunction_triggered(); void on_actionAnalyzeFunction_triggered();
void on_actionRename_triggered(); void on_actionRename_triggered();
void on_actionGlobalVar_triggered();
void on_actionSetFunctionVarTypes_triggered(); void on_actionSetFunctionVarTypes_triggered();
void on_actionXRefs_triggered(); void on_actionXRefs_triggered();
void on_actionXRefsForVariables_triggered(); void on_actionXRefsForVariables_triggered();
@ -78,6 +79,7 @@ private:
QKeySequence getCopySequence() const; QKeySequence getCopySequence() const;
QKeySequence getCommentSequence() const; QKeySequence getCommentSequence() const;
QKeySequence getCopyAddressSequence() const; QKeySequence getCopyAddressSequence() const;
QKeySequence getGlobalVarSequence() const;
QKeySequence getSetToCodeSequence() const; QKeySequence getSetToCodeSequence() const;
QKeySequence getSetAsStringSequence() const; QKeySequence getSetAsStringSequence() const;
QKeySequence getSetAsStringAdvanced() const; QKeySequence getSetAsStringAdvanced() const;
@ -114,6 +116,7 @@ private:
QAction actionAnalyzeFunction; QAction actionAnalyzeFunction;
QAction actionEditFunction; QAction actionEditFunction;
QAction actionRename; QAction actionRename;
QAction actionGlobalVar;
QAction actionSetFunctionVarTypes; QAction actionSetFunctionVarTypes;
QAction actionXRefs; QAction actionXRefs;
QAction actionXRefsForVariables; QAction actionXRefsForVariables;
@ -168,6 +171,11 @@ private:
QMenu *pluginMenu = nullptr; QMenu *pluginMenu = nullptr;
QAction *pluginActionMenuAction = 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) // For creating anonymous entries (that are always visible)
QAction *addAnonymousAction(QString name, const char *slot, QKeySequence shortcut); QAction *addAnonymousAction(QString name, const char *slot, QKeySequence shortcut);
@ -185,6 +193,7 @@ private:
void addSetAsMenu(); void addSetAsMenu();
void addSetToDataMenu(); void addSetToDataMenu();
void addEditMenu(); void addEditMenu();
void addAddAtMenu();
void addBreakpointMenu(); void addBreakpointMenu();
void addDebugMenu(); void addDebugMenu();

View File

@ -25,7 +25,6 @@
<update_contact>xarkes</update_contact> <update_contact>xarkes</update_contact>
<releases> <releases>
<release version="2.2.1" date="2023-05-15" />
<release version="2.2.0" date="2023-02-22" /> <release version="2.2.0" date="2023-02-22" />
<release version="2.1.2" date="2022-09-11" /> <release version="2.1.2" date="2022-09-11" />
<release version="2.1.1" date="2022-09-10" /> <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; 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: protected:
virtual void showItemContextMenu(const QPoint &pt) virtual void showItemContextMenu(const QPoint &pt)
{ {
if (!itemContextMenu) {
return;
}
auto index = this->currentIndex(); auto index = this->currentIndex();
if (index.isValid() && itemContextMenu) { if (index.isValid()) {
auto offset = addressableModel->address(index); auto offset = addressableModel->address(index);
auto name = addressableModel->name(index); auto name = addressableModel->name(index);
itemContextMenu->setTarget(offset, name); 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) virtual void onItemActivated(const QModelIndex &index)
@ -90,6 +104,7 @@ protected:
} }
private: private:
bool showItemContextMenuWithoutAddress = false;
AddressableItemModelI *addressableModel = nullptr; AddressableItemModelI *addressableModel = nullptr;
AddressableItemContextMenu *itemContextMenu = nullptr; AddressableItemContextMenu *itemContextMenu = nullptr;
MainWindow *mainWindow = nullptr; MainWindow *mainWindow = nullptr;

View File

@ -1,6 +1,6 @@
#include "ClassesWidget.h" #include "ClassesWidget.h"
#include "core/MainWindow.h" #include "core/MainWindow.h"
#include "ui_ClassesWidget.h" #include "ui_ListDockWidget.h"
#include "common/Helpers.h" #include "common/Helpers.h"
#include "common/SvgIconEngine.h" #include "common/SvgIconEngine.h"
#include "dialogs/EditMethodDialog.h" #include "dialogs/EditMethodDialog.h"
@ -9,6 +9,8 @@
#include <QMenu> #include <QMenu>
#include <QMouseEvent> #include <QMouseEvent>
#include <QInputDialog> #include <QInputDialog>
#include <QShortcut>
#include <QComboBox>
QVariant ClassesModel::headerData(int section, Qt::Orientation, int role) const 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) {} BinClassesModel::BinClassesModel(QObject *parent) : ClassesModel(parent) {}
void BinClassesModel::setClasses(const QList<BinClassDescription> &classes) void BinClassesModel::setClasses(const QList<BinClassDescription> &classes)
@ -526,12 +539,17 @@ QVariant AnalysisClassesModel::data(const QModelIndex &index, int role) const
} }
ClassesSortFilterProxyModel::ClassesSortFilterProxyModel(QObject *parent) ClassesSortFilterProxyModel::ClassesSortFilterProxyModel(QObject *parent)
: QSortFilterProxyModel(parent) : AddressableFilterProxyModel(nullptr, parent)
{ {
setFilterCaseSensitivity(Qt::CaseInsensitive);
setSortCaseSensitivity(Qt::CaseInsensitive);
} }
bool ClassesSortFilterProxyModel::filterAcceptsRow(int row, const QModelIndex &parent) const bool ClassesSortFilterProxyModel::filterAcceptsRow(int row, const QModelIndex &parent) const
{ {
if (parent.isValid())
return true;
QModelIndex index = sourceModel()->index(row, 0, parent); QModelIndex index = sourceModel()->index(row, 0, parent);
return qhelpers::filterStringContains(index.data(ClassesModel::NameRole).toString(), this); 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(); 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); proxy_model = new ClassesSortFilterProxyModel(this);
ui->classesTreeView->setModel(proxy_model); setModels(proxy_model);
ui->classesTreeView->sortByColumn(ClassesModel::TYPE, Qt::AscendingOrder);
ui->classesTreeView->setContextMenuPolicy(Qt::CustomContextMenu);
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); &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(); refreshClasses();
} }
@ -601,7 +659,7 @@ ClassesWidget::~ClassesWidget() {}
ClassesWidget::Source ClassesWidget::getSource() ClassesWidget::Source ClassesWidget::getSource()
{ {
switch (ui->classSourceCombo->currentIndex()) { switch (classSourceCombo->currentIndex()) {
case 0: case 0:
return Source::BIN; return Source::BIN;
default: default:
@ -614,88 +672,68 @@ void ClassesWidget::refreshClasses()
switch (getSource()) { switch (getSource()) {
case Source::BIN: case Source::BIN:
if (!bin_model) { if (!bin_model) {
proxy_model->setSourceModel(nullptr); proxy_model->setSourceModel(static_cast<AddressableItemModelI *>(nullptr));
delete analysis_model; delete analysis_model;
analysis_model = nullptr; analysis_model = nullptr;
bin_model = new BinClassesModel(this); bin_model = new BinClassesModel(this);
proxy_model->setSourceModel(bin_model); proxy_model->setSourceModel(static_cast<AddressableItemModelI *>(bin_model));
} }
bin_model->setClasses(Core()->getAllClassesFromBin()); bin_model->setClasses(Core()->getAllClassesFromBin());
break; break;
case Source::ANALYSIS: case Source::ANALYSIS:
if (!analysis_model) { if (!analysis_model) {
proxy_model->setSourceModel(nullptr); proxy_model->setSourceModel(static_cast<AddressableItemModelI *>(nullptr));
delete bin_model; delete bin_model;
bin_model = nullptr; bin_model = nullptr;
analysis_model = new AnalysisClassesModel(this); analysis_model = new AnalysisClassesModel(this);
proxy_model->setSourceModel(analysis_model); proxy_model->setSourceModel(static_cast<AddressableItemModelI *>(analysis_model));
} }
break; 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()) bool isAnalysis = !!analysis_model;
return; newClassAction.setVisible(isAnalysis);
addMethodAction.setVisible(isAnalysis);
QVariant offsetData = index.data(ClassesModel::OffsetRole); bool rowIsAnalysisClass = false;
if (!offsetData.isValid()) { bool rowIsAnalysisMethod = false;
return; QModelIndex index = ui->treeView->selectionModel()->currentIndex();
} if (isAnalysis && index.isValid()) {
RVA offset = offsetData.value<RVA>();
Core()->seekAndShow(offset);
}
void ClassesWidget::showContextMenu(const QPoint &pt)
{
if (!analysis_model) {
// no context menu for bin classes
return;
}
QModelIndex index = ui->classesTreeView->selectionModel()->currentIndex();
if (!index.isValid()) {
return;
}
auto type = static_cast<ClassesModel::RowType>(index.data(ClassesModel::TypeRole).toInt()); auto type = static_cast<ClassesModel::RowType>(index.data(ClassesModel::TypeRole).toInt());
rowIsAnalysisClass = type == ClassesModel::RowType::Class;
QMenu menu(ui->classesTreeView); rowIsAnalysisMethod = type == ClassesModel::RowType::Method;
menu.addAction(ui->newClassAction);
if (type == ClassesModel::RowType::Class) {
menu.addAction(ui->renameClassAction);
menu.addAction(ui->deleteClassAction);
} }
menu.addSeparator(); renameClassAction.setVisible(rowIsAnalysisClass);
deleteClassAction.setVisible(rowIsAnalysisClass);
menu.addAction(ui->addMethodAction); classesMethodsSeparator->setVisible(rowIsAnalysisClass || rowIsAnalysisMethod);
if (type == ClassesModel::RowType::Method) {
menu.addAction(ui->editMethodAction);
editMethodAction.setVisible(rowIsAnalysisMethod);
bool rowHasVTable = false;
if (rowIsAnalysisMethod) {
QString className = index.parent().data(ClassesModel::NameRole).toString(); QString className = index.parent().data(ClassesModel::NameRole).toString();
QString methodName = index.data(ClassesModel::NameRole).toString(); QString methodName = index.data(ClassesModel::NameRole).toString();
AnalysisMethodDescription desc; AnalysisMethodDescription desc;
if (Core()->getAnalysisMethod(className, methodName, &desc)) { if (Core()->getAnalysisMethod(className, methodName, &desc)) {
if (desc.vtableOffset >= 0) { if (desc.vtableOffset >= 0) {
menu.addAction(ui->seekToVTableAction); rowHasVTable = true;
} }
} }
} }
seekToVTableAction.setVisible(rowHasVTable);
menu.exec(ui->classesTreeView->mapToGlobal(pt));
} }
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(); QString className = index.parent().data(ClassesModel::NameRole).toString();
QList<AnalysisVTableDescription> vtables = Core()->getAnalysisClassVTables(className); QList<AnalysisVTableDescription> vtables = Core()->getAnalysisClassVTables(className);
@ -714,9 +752,9 @@ void ClassesWidget::on_seekToVTableAction_triggered()
Core()->seekAndShow(vtables[0].addr + desc.vtableOffset); 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()) { if (!index.isValid()) {
return; return;
} }
@ -732,9 +770,9 @@ void ClassesWidget::on_addMethodAction_triggered()
EditMethodDialog::newMethod(className, QString(), this); 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() if (!index.isValid()
|| index.data(ClassesModel::TypeRole).toInt() || index.data(ClassesModel::TypeRole).toInt()
!= static_cast<int>(ClassesModel::RowType::Method)) { != static_cast<int>(ClassesModel::RowType::Method)) {
@ -745,7 +783,7 @@ void ClassesWidget::on_editMethodAction_triggered()
EditMethodDialog::editMethod(className, methName, this); EditMethodDialog::editMethod(className, methName, this);
} }
void ClassesWidget::on_newClassAction_triggered() void ClassesWidget::newClassActionTriggered()
{ {
bool ok; bool ok;
QString name = QInputDialog::getText(this, tr("Create new Class"), tr("Class Name:"), 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() if (!index.isValid()
|| index.data(ClassesModel::TypeRole).toInt() || index.data(ClassesModel::TypeRole).toInt()
!= static_cast<int>(ClassesModel::RowType::Class)) { != static_cast<int>(ClassesModel::RowType::Class)) {
@ -772,9 +810,9 @@ void ClassesWidget::on_deleteClassAction_triggered()
Core()->deleteClass(className); 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() if (!index.isValid()
|| index.data(ClassesModel::TypeRole).toInt() || index.data(ClassesModel::TypeRole).toInt()
!= static_cast<int>(ClassesModel::RowType::Class)) { != static_cast<int>(ClassesModel::RowType::Class)) {

View File

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

View File

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

View File

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

View File

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

View File

@ -26,8 +26,7 @@ DecompilerWidget::DecompilerWidget(MainWindow *main)
ui(new Ui::DecompilerWidget), ui(new Ui::DecompilerWidget),
decompilerBusy(false), decompilerBusy(false),
seekFromCursor(false), seekFromCursor(false),
scrollerHorizontal(0), historyPos(0),
scrollerVertical(0),
previousFunctionAddr(RVA_INVALID), previousFunctionAddr(RVA_INVALID),
decompiledFunctionAddr(RVA_INVALID), decompiledFunctionAddr(RVA_INVALID),
code(Decompiler::makeWarning(tr("Choose an offset and refresh to get decompiled code")), 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::varsChanged, this, &DecompilerWidget::doRefresh);
connect(Core(), &CutterCore::functionsChanged, this, &DecompilerWidget::doRefresh); connect(Core(), &CutterCore::functionsChanged, this, &DecompilerWidget::doRefresh);
connect(Core(), &CutterCore::flagsChanged, 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::commentsChanged, this, &DecompilerWidget::refreshIfChanged);
connect(Core(), &CutterCore::instructionChanged, this, &DecompilerWidget::refreshIfChanged); connect(Core(), &CutterCore::instructionChanged, this, &DecompilerWidget::refreshIfChanged);
connect(Core(), &CutterCore::refreshCodeViews, this, &DecompilerWidget::doRefresh); connect(Core(), &CutterCore::refreshCodeViews, this, &DecompilerWidget::doRefresh);
@ -311,13 +311,6 @@ QTextCursor DecompilerWidget::getCursorForAddress(RVA addr)
void DecompilerWidget::decompilationFinished(RzAnnotatedCode *codeDecompiled) 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->progressLabel->setVisible(false);
ui->decompilerComboBox->setEnabled(decompilerSelectionEnabled); ui->decompilerComboBox->setEnabled(decompilerSelectionEnabled);
@ -354,10 +347,8 @@ void DecompilerWidget::decompilationFinished(RzAnnotatedCode *codeDecompiled)
} }
} }
if (isDisplayReset) { ui->textEdit->horizontalScrollBar()->setSliderPosition(scrollHistory[historyPos].first);
ui->textEdit->horizontalScrollBar()->setSliderPosition(scrollerHorizontal); ui->textEdit->verticalScrollBar()->setSliderPosition(scrollHistory[historyPos].second);
ui->textEdit->verticalScrollBar()->setSliderPosition(scrollerVertical);
}
} }
void DecompilerWidget::setAnnotationsAtCursor(size_t pos) void DecompilerWidget::setAnnotationsAtCursor(size_t pos)
@ -416,11 +407,28 @@ void DecompilerWidget::cursorPositionChanged()
updateSelection(); updateSelection();
} }
void DecompilerWidget::seekChanged() void DecompilerWidget::seekChanged(RVA /* addr */, CutterCore::SeekHistoryType type)
{ {
if (seekFromCursor) { if (seekFromCursor) {
return; 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()); RVA fcnAddr = Core()->getFunctionStart(seekable->getOffset());
if (fcnAddr == RVA_INVALID || fcnAddr != decompiledFunctionAddr) { if (fcnAddr == RVA_INVALID || fcnAddr != decompiledFunctionAddr) {
doRefresh(); doRefresh();

View File

@ -53,7 +53,7 @@ private slots:
* - Seek changed to an offset contained in the decompiled function. * - Seek changed to an offset contained in the decompiled function.
* - Auto-refresh is disabled. * - Auto-refresh is disabled.
*/ */
void seekChanged(); void seekChanged(RVA /* addr */, CutterCore::SeekHistoryType type);
void decompilationFinished(RzAnnotatedCode *code); void decompilationFinished(RzAnnotatedCode *code);
private: private:
@ -72,8 +72,8 @@ private:
bool decompilerBusy; bool decompilerBusy;
bool seekFromCursor; bool seekFromCursor;
int scrollerHorizontal; int historyPos;
int scrollerVertical; QVector<QPair<int, int>> scrollHistory;
RVA previousFunctionAddr; RVA previousFunctionAddr;
RVA decompiledFunctionAddr; RVA decompiledFunctionAddr;
std::unique_ptr<RzAnnotatedCode, void (*)(RzAnnotatedCode *)> code; 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::commentsChanged, this, &DisassemblerGraphView::refreshView);
connect(Core(), &CutterCore::functionRenamed, this, &DisassemblerGraphView::refreshView); connect(Core(), &CutterCore::functionRenamed, this, &DisassemblerGraphView::refreshView);
connect(Core(), &CutterCore::flagsChanged, 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::varsChanged, this, &DisassemblerGraphView::refreshView);
connect(Core(), &CutterCore::instructionChanged, this, &DisassemblerGraphView::refreshView); connect(Core(), &CutterCore::instructionChanged, this, &DisassemblerGraphView::refreshView);
connect(Core(), &CutterCore::breakpointsChanged, this, &DisassemblerGraphView::refreshView); connect(Core(), &CutterCore::breakpointsChanged, this, &DisassemblerGraphView::refreshView);
@ -534,17 +535,16 @@ GraphView::EdgeConfiguration DisassemblerGraphView::edgeConfiguration(GraphView:
bool DisassemblerGraphView::eventFilter(QObject *obj, QEvent *event) 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); QHelpEvent *helpEvent = static_cast<QHelpEvent *>(event);
QPoint pointOfEvent = helpEvent->globalPos(); QPoint pointOfEvent = helpEvent->globalPos();
QPoint point = viewToLogicalCoordinates(helpEvent->pos()); QPoint point = viewToLogicalCoordinates(helpEvent->pos());
GraphBlock *block = getBlockContaining(point); if (auto block = getBlockContaining(point)) {
// Get pos relative to start of block
if (block == nullptr) { QPoint pos = point - QPoint(block->x, block->y);
return false;
}
// offsetFrom is the address which on top the cursor triggered this // offsetFrom is the address which on top the cursor triggered this
RVA offsetFrom = RVA_INVALID; RVA offsetFrom = RVA_INVALID;
@ -553,14 +553,26 @@ bool DisassemblerGraphView::eventFilter(QObject *obj, QEvent *event)
* getAddrForMouseEvent() doesn't work for jmps, like * getAddrForMouseEvent() doesn't work for jmps, like
* getInstrForMouseEvent() with false as a 3rd argument. * getInstrForMouseEvent() with false as a 3rd argument.
*/ */
Instr *inst = getInstrForMouseEvent(*block, &point, true); Instr *inst = getInstrForMouseEvent(*block, &pos, true);
if (inst != nullptr) { if (inst != nullptr) {
offsetFrom = inst->addr; offsetFrom = inst->addr;
} }
// Don't preview anything for a small scale // Don't preview anything for a small scale
if (getViewScale() >= 0.8) { if (getViewScale() >= 0.8) {
return DisassemblyPreview::showDisasPreview(this, pointOfEvent, offsetFrom); 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); return CutterGraphView::eventFilter(obj, event);

View File

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

View File

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

View File

@ -234,9 +234,8 @@ QVariant FunctionModel::data(const QModelIndex &index, int role) const
QStringList summary {}; QStringList summary {};
{ {
auto seeker = Core()->seekTemp(function.offset); auto seeker = Core()->seekTemp(function.offset);
auto strings = fromOwnedCharPtr( auto strings = fromOwnedCharPtr(rz_core_print_disasm_strings(
rz_core_print_disasm_strings(Core()->core(), RZ_CORE_DISASM_STRINGS_MODE_FUNCTION, Core()->core(), RZ_CORE_DISASM_STRINGS_MODE_FUNCTION, 0, NULL));
0, NULL));
summary = strings.split('\n', CUTTER_QT_SKIP_EMPTY_PARTS); summary = strings.split('\n', CUTTER_QT_SKIP_EMPTY_PARTS);
} }
@ -508,6 +507,7 @@ FunctionsWidget::FunctionsWidget(MainWindow *main)
functionProxyModel = new FunctionSortFilterProxyModel(functionModel, this); functionProxyModel = new FunctionSortFilterProxyModel(functionModel, this);
setModels(functionProxyModel); setModels(functionProxyModel);
ui->treeView->sortByColumn(FunctionModel::NameColumn, Qt::AscendingOrder); ui->treeView->sortByColumn(FunctionModel::NameColumn, Qt::AscendingOrder);
ui->treeView->setExpandsOnDoubleClick(false);
titleContextMenu = new QMenu(this); titleContextMenu = new QMenu(this);
auto viewTypeGroup = new QActionGroup(titleContextMenu); 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.setFileName(path);
generator.setSize(QSize(width, height)); generator.setSize(QSize(width, height));
generator.setViewBox(QRect(0, 0, width, height)); generator.setViewBox(QRect(0, 0, width, height));
generator.setTitle("Cutter graph export"); generator.setTitle(tr("Cutter graph export"));
QPainter p; QPainter p;
p.begin(&generator); p.begin(&generator);
paint(p, QPoint(0, 0), QRect(0, 0, width, height), 1.0, false); 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"); || QString(heapBin->type) == QString("Tcache");
// store info about the chunks in a vector for easy access // 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 graphHeapChunk;
graphHeapChunk.addr = item->addr; graphHeapChunk.addr = item->addr;
RzHeapChunkSimple *chunkInfo = Core()->getHeapChunk(item->addr); RzHeapChunkSimple *chunkInfo = Core()->getHeapChunk(item->addr);

View File

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

View File

@ -248,19 +248,24 @@ void HexdumpWidget::updateParseWindow(RVA start_address, int size)
ut64 old_offset = core->offset; ut64 old_offset = core->offset;
rz_core_seek(core, start_address, true); rz_core_seek(core, start_address, true);
ut8 *block = core->block; 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)); ui->bytesMD5->setText(QString(digest));
free(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)); ui->bytesSHA1->setText(QString(digest));
free(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)); ui->bytesSHA256->setText(QString(digest));
free(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)); ui->bytesCRC32->setText(QString(digest));
free(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)); ui->bytesEntropy->setText(QString(digest));
free(digest); free(digest);
rz_core_seek(core, old_offset, true); rz_core_seek(core, old_offset, true);

View File

@ -2,7 +2,6 @@
#include "ui_ListDockWidget.h" #include "ui_ListDockWidget.h"
#include "core/MainWindow.h" #include "core/MainWindow.h"
#include "common/Helpers.h" #include "common/Helpers.h"
#include "menus/AddressableItemContextMenu.h"
#include <QMenu> #include <QMenu>
#include <QResizeEvent> #include <QResizeEvent>

View File

@ -16,7 +16,7 @@ QuickFilterView::QuickFilterView(QWidget *parent, bool defaultOn)
[this]() { emit filterTextChanged(ui->filterLineEdit->text()); }); [this]() { emit filterTextChanged(ui->filterLineEdit->text()); });
connect(ui->filterLineEdit, &QLineEdit::textChanged, this, connect(ui->filterLineEdit, &QLineEdit::textChanged, this,
[this](const QString &text) { debounceTimer->start(150); }); [this]() { debounceTimer->start(150); });
if (!defaultOn) { if (!defaultOn) {
closeFilter(); closeFilter();

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