From 68ec5a3da18ee8f281b2926a037ba40ac2d1dab5 Mon Sep 17 00:00:00 2001 From: Anton Kochkov Date: Wed, 22 Feb 2023 23:09:24 +0800 Subject: [PATCH 01/50] Fix attaching debugger (#3139) * Fix attaching debugger * Fix deadlock on attach Co-authored-by: wargio --- src/core/Cutter.cpp | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/src/core/Cutter.cpp b/src/core/Cutter.cpp index dad33c8e..7a18898c 100644 --- a/src/core/Cutter.cpp +++ b/src/core/Cutter.cpp @@ -2075,16 +2075,23 @@ void CutterCore::attachDebug(int pid) offsetPriorDebugging = getOffset(); } - CORE_LOCK(); - setConfig("cfg.debug", true); - auto uri = rz_str_newf("dbg://%d", pid); - if (currentlyOpenFile.isEmpty()) { - rz_core_file_open_load(core, uri, 0, RZ_PERM_R, false); - } else { - rz_core_file_reopen_remote_debug(core, uri, 0); + if (!asyncTask( + [&](RzCore *core) { + // cannot use setConfig because core is + // already locked, which causes a deadlock + rz_config_set_b(core->config, "cfg.debug", true); + auto uri = rz_str_newf("dbg://%d", pid); + if (currentlyOpenFile.isEmpty()) { + rz_core_file_open_load(core, uri, 0, RZ_PERM_R, false); + } else { + rz_core_file_reopen_remote_debug(core, uri, 0); + } + free(uri); + return nullptr; + }, + debugTask)) { + return; } - free(uri); - emit debugTaskStateChanged(); connect(debugTask.data(), &RizinTask::finished, this, [this, pid]() { @@ -2144,7 +2151,10 @@ void CutterCore::stopDebug() rz_core_analysis_esil_trace_stop(core); currentlyEmulating = false; } else { - rz_core_debug_process_close(core); + // ensure we have opened a file. + if (core->io->desc) { + rz_core_debug_process_close(core); + } currentlyAttachedToPID = -1; } @@ -4579,4 +4589,4 @@ void CutterCore::writeGraphvizGraphToFile(QString path, QString format, RzCoreGr qWarning() << "Cannot get graph at " << RzAddressString(address); } } -} \ No newline at end of file +} From e69a007b8f52eea012417c5bfe576e935bf9044c Mon Sep 17 00:00:00 2001 From: Yappa Date: Sun, 19 Feb 2023 23:10:11 -0800 Subject: [PATCH 02/50] Fix "New Function" Dialog (#3102) --- src/menus/DisassemblyContextMenu.cpp | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/menus/DisassemblyContextMenu.cpp b/src/menus/DisassemblyContextMenu.cpp index ab54328d..918a63fa 100644 --- a/src/menus/DisassemblyContextMenu.cpp +++ b/src/menus/DisassemblyContextMenu.cpp @@ -314,8 +314,7 @@ void DisassemblyContextMenu::addDebugMenu() QVector DisassemblyContextMenu::getThingUsedHere(RVA offset) { RzCoreLocked core(Core()); - auto p = fromOwned( - rz_core_analysis_name(core, offset), rz_core_analysis_name_free); + auto p = fromOwned(rz_core_analysis_name(core, offset), rz_core_analysis_name_free); if (!p) { return {}; } @@ -799,7 +798,6 @@ void DisassemblyContextMenu::on_actionAddComment_triggered() void DisassemblyContextMenu::on_actionAnalyzeFunction_triggered() { - bool ok; RVA flagOffset; QString name = Core()->nearestFlag(offset, &flagOffset); if (name.isEmpty() || flagOffset != offset) { @@ -812,12 +810,20 @@ void DisassemblyContextMenu::on_actionAnalyzeFunction_triggered() } // Create dialog - QString functionName = - QInputDialog::getText(this, tr("New function at %1").arg(RzAddressString(offset)), - tr("Function name:"), QLineEdit::Normal, name, &ok); + QInputDialog inputDialog(this->mainWindow); + inputDialog.resize(500, 100); + inputDialog.setWindowTitle(tr("New function at %1").arg(RzAddressString(offset))); + inputDialog.setLabelText(tr("Function name:")); + inputDialog.setTextValue(name); + inputDialog.setWindowFlags(Qt::Window | Qt::WindowMinimizeButtonHint); - // If user accepted - if (ok && !functionName.isEmpty()) { + if (inputDialog.exec() != QDialog::Accepted) { + return; + } + + QString functionName = inputDialog.textValue().trimmed(); + + if (!functionName.isEmpty()) { Core()->createFunctionAt(offset, functionName); } } From 63125b16c73474969fae8df6b7ca7203bc627e40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=A4rkl?= Date: Sun, 12 Mar 2023 18:54:00 +0100 Subject: [PATCH 03/50] Unify parents of dialogs shown by DisassemblyContextMenu (#3148) Analogous to 2d7fd02a62dd3405d5405a997a8263c6e7706d67, parents of dialogs shown from the DisassemblyContextMenu are consistent now and windowing issues when lauched through shortcuts are fixed. --- src/menus/DecompilerContextMenu.cpp | 2 +- src/menus/DisassemblyContextMenu.cpp | 31 ++++++++++++++++------------ src/menus/DisassemblyContextMenu.h | 5 +++++ 3 files changed, 24 insertions(+), 14 deletions(-) diff --git a/src/menus/DecompilerContextMenu.cpp b/src/menus/DecompilerContextMenu.cpp index 2dd6907a..1de95217 100644 --- a/src/menus/DecompilerContextMenu.cpp +++ b/src/menus/DecompilerContextMenu.cpp @@ -387,7 +387,7 @@ void DecompilerContextMenu::actionCopyReferenceAddressTriggered() void DecompilerContextMenu::actionAddCommentTriggered() { - CommentsDialog::addOrEditComment(this->firstOffsetInLine, this); + CommentsDialog::addOrEditComment(this->firstOffsetInLine, parentForDialog()); } void DecompilerContextMenu::actionDeleteCommentTriggered() diff --git a/src/menus/DisassemblyContextMenu.cpp b/src/menus/DisassemblyContextMenu.cpp index 918a63fa..40b33599 100644 --- a/src/menus/DisassemblyContextMenu.cpp +++ b/src/menus/DisassemblyContextMenu.cpp @@ -161,6 +161,11 @@ DisassemblyContextMenu::DisassemblyContextMenu(QWidget *parent, MainWindow *main DisassemblyContextMenu::~DisassemblyContextMenu() {} +QWidget *DisassemblyContextMenu::parentForDialog() +{ + return parentWidget(); +} + void DisassemblyContextMenu::addSetBaseMenu() { setBaseMenu = addMenu(tr("Set Immediate Base to...")); @@ -690,7 +695,7 @@ void DisassemblyContextMenu::on_actionEditInstruction_triggered() if (!ioModesController.prepareForWriting()) { return; } - EditInstructionDialog e(EDIT_TEXT, this); + EditInstructionDialog e(EDIT_TEXT, parentForDialog()); e.setWindowTitle(tr("Edit Instruction at %1").arg(RzAddressString(offset))); QString oldInstructionOpcode = Core()->getInstructionOpcode(offset); @@ -740,7 +745,7 @@ void DisassemblyContextMenu::on_actionEditBytes_triggered() if (!ioModesController.prepareForWriting()) { return; } - EditInstructionDialog e(EDIT_BYTES, this); + EditInstructionDialog e(EDIT_BYTES, parentForDialog()); e.setWindowTitle(tr("Edit Bytes at %1").arg(RzAddressString(offset))); QString oldBytes = Core()->getInstructionBytes(offset); @@ -774,9 +779,9 @@ void DisassemblyContextMenu::on_actionAdvancedBreakpoint_triggered() { int index = Core()->breakpointIndexAt(offset); if (index >= 0) { - BreakpointsDialog::editBreakpoint(Core()->getBreakpointAt(offset), this); + BreakpointsDialog::editBreakpoint(Core()->getBreakpointAt(offset), parentForDialog()); } else { - BreakpointsDialog::createNewBreakpoint(offset, this); + BreakpointsDialog::createNewBreakpoint(offset, parentForDialog()); } } @@ -793,7 +798,7 @@ void DisassemblyContextMenu::on_actionSetPC_triggered() void DisassemblyContextMenu::on_actionAddComment_triggered() { - CommentsDialog::addOrEditComment(offset, this); + CommentsDialog::addOrEditComment(offset, parentForDialog()); } void DisassemblyContextMenu::on_actionAnalyzeFunction_triggered() @@ -810,7 +815,7 @@ void DisassemblyContextMenu::on_actionAnalyzeFunction_triggered() } // Create dialog - QInputDialog inputDialog(this->mainWindow); + QInputDialog inputDialog(parentForDialog()); inputDialog.resize(500, 100); inputDialog.setWindowTitle(tr("New function at %1").arg(RzAddressString(offset))); inputDialog.setLabelText(tr("Function name:")); @@ -839,12 +844,12 @@ void DisassemblyContextMenu::on_actionRename_triggered() Core()->renameFunction(doRenameInfo.addr, newName); } } else if (doRenameAction == RENAME_FLAG || doRenameAction == RENAME_ADD_FLAG) { - FlagDialog dialog(doRenameInfo.addr, this->mainWindow); + FlagDialog dialog(doRenameInfo.addr, parentForDialog()); ok = dialog.exec(); } else if (doRenameAction == RENAME_LOCAL) { RzAnalysisFunction *fcn = Core()->functionIn(offset); if (fcn) { - EditVariablesDialog dialog(fcn->addr, curHighlightedWord, this->mainWindow); + EditVariablesDialog dialog(fcn->addr, curHighlightedWord, parentForDialog()); if (!dialog.empty()) { // Don't show the dialog if there are no variables ok = dialog.exec(); @@ -873,7 +878,7 @@ void DisassemblyContextMenu::on_actionSetFunctionVarTypes_triggered() return; } - EditVariablesDialog dialog(fcn->addr, curHighlightedWord, this->mainWindow); + EditVariablesDialog dialog(fcn->addr, curHighlightedWord, parentForDialog()); if (dialog.empty()) { // don't show the dialog if there are no variables return; } @@ -898,7 +903,7 @@ void DisassemblyContextMenu::on_actionXRefsForVariables_triggered() void DisassemblyContextMenu::on_actionDisplayOptions_triggered() { - PreferencesDialog dialog(this->window()); + PreferencesDialog dialog(parentForDialog()); dialog.showSection(PreferencesDialog::Section::Disassembly); dialog.exec(); } @@ -920,7 +925,7 @@ void DisassemblyContextMenu::on_actionSetAsStringRemove_triggered() void DisassemblyContextMenu::on_actionSetAsStringAdvanced_triggered() { - EditStringDialog dialog(parentWidget()); + EditStringDialog dialog(parentForDialog()); const int predictedStrSize = Core()->getString(offset).size(); dialog.setStringSizeValue(predictedStrSize); dialog.setStringStartAddress(offset); @@ -970,7 +975,7 @@ void DisassemblyContextMenu::on_actionSetToData_triggered() void DisassemblyContextMenu::on_actionSetToDataEx_triggered() { - SetToDataDialog dialog(offset, this->window()); + SetToDataDialog dialog(offset, parentForDialog()); if (!dialog.exec()) { return; } @@ -1000,7 +1005,7 @@ void DisassemblyContextMenu::on_actionDeleteFunction_triggered() void DisassemblyContextMenu::on_actionEditFunction_triggered() { RzCore *core = Core()->core(); - EditFunctionDialog dialog(mainWindow); + EditFunctionDialog dialog(parentForDialog()); RzAnalysisFunction *fcn = rz_analysis_get_fcn_in(core->analysis, offset, 0); if (fcn) { diff --git a/src/menus/DisassemblyContextMenu.h b/src/menus/DisassemblyContextMenu.h index 158b1ec7..669990a6 100644 --- a/src/menus/DisassemblyContextMenu.h +++ b/src/menus/DisassemblyContextMenu.h @@ -168,6 +168,11 @@ private: QMenu *pluginMenu = nullptr; QAction *pluginActionMenuAction = nullptr; + /** + * \return widget that should be used as parent for presenting dialogs + */ + QWidget *parentForDialog(); + // For creating anonymous entries (that are always visible) QAction *addAnonymousAction(QString name, const char *slot, QKeySequence shortcut); From 1c1355df1f555900d41d361cf8757f5e31f6930e Mon Sep 17 00:00:00 2001 From: Anton Kochkov Date: Wed, 22 Feb 2023 00:00:00 +0800 Subject: [PATCH 04/50] Bump version to v2.2.0 --- .appveyor.yml | 2 +- CMakeLists.txt | 4 ++-- docs/source/conf.py | 4 ++-- src/re.rizin.cutter.appdata.xml | 1 + 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 375d116e..ef25be60 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -1,4 +1,4 @@ -version: '2.1.2-git-{build}' +version: '2.2.0-git-{build}' image: 'Visual Studio 2017' clone_depth: 1 diff --git a/CMakeLists.txt b/CMakeLists.txt index aeb35127..8645b267 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,8 +35,8 @@ if(NOT CUTTER_ENABLE_PYTHON) endif() set(CUTTER_VERSION_MAJOR 2) -set(CUTTER_VERSION_MINOR 1) -set(CUTTER_VERSION_PATCH 2) +set(CUTTER_VERSION_MINOR 2) +set(CUTTER_VERSION_PATCH 0) set(CUTTER_VERSION "${CUTTER_VERSION_MAJOR}.${CUTTER_VERSION_MINOR}.${CUTTER_VERSION_PATCH}") diff --git a/docs/source/conf.py b/docs/source/conf.py index 640b7f9b..34af39b1 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -24,9 +24,9 @@ copyright = '2020, The Cutter Developers' author = 'The Cutter Developers' # The short X.Y version -version = '2.1' +version = '2.2' # The full version, including a2lpha/beta/rc tags -release = '2.1.2' +release = '2.2.0' # -- General configuration --------------------------------------------------- diff --git a/src/re.rizin.cutter.appdata.xml b/src/re.rizin.cutter.appdata.xml index e8e0de5e..9ef364ba 100644 --- a/src/re.rizin.cutter.appdata.xml +++ b/src/re.rizin.cutter.appdata.xml @@ -25,6 +25,7 @@ xarkes + From af970e29d798ced25fbaf85a65923220fcaef2f8 Mon Sep 17 00:00:00 2001 From: Anton Kochkov Date: Tue, 7 Mar 2023 10:47:22 +0800 Subject: [PATCH 05/50] Update Rizin to the latest `dev` --- rizin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rizin b/rizin index 12898a36..3a7d5116 160000 --- a/rizin +++ b/rizin @@ -1 +1 @@ -Subproject commit 12898a365e70c22892d78abdd2627e7269533c5f +Subproject commit 3a7d5116244beb678ad9950bb9dd27d28ed2691f From d3ee310a211b2e04eb3761299ea3fb90a9b7687e Mon Sep 17 00:00:00 2001 From: Anton Kochkov Date: Mon, 13 Mar 2023 13:39:54 +0800 Subject: [PATCH 06/50] Set Rizin version to 0.6 --- cmake/BundledRizin.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/BundledRizin.cmake b/cmake/BundledRizin.cmake index 85ddd5b5..10816039 100644 --- a/cmake/BundledRizin.cmake +++ b/cmake/BundledRizin.cmake @@ -57,7 +57,7 @@ endif() # TODO: This version number should be fetched automatically # instead of being hardcoded. -set (Rizin_VERSION 0.5) +set (Rizin_VERSION 0.6) set (RZ_LIBS rz_core rz_config rz_cons rz_io rz_util rz_flag rz_asm rz_debug rz_hash rz_bin rz_lang rz_il rz_analysis rz_parse rz_bp rz_egg rz_reg From 1f133741ab0c1475fa48fffc24d1eb4b71dfe532 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=A4rkl?= Date: Thu, 30 Mar 2023 19:33:31 +0200 Subject: [PATCH 07/50] Make ClassesWidget a ListDockWidget (#3152) Adds Quick Filter and generic address-based context menu entries to the existing ClassesWidget. The original ClassesWidget.ui is not used anymore as the layout is provided by ListDockWidget and only adjusted. The AddressableItemContextMenu may now also optionally be shown when there no currently selected item by using setShowItemContextMenuWithoutAddress(). This is used e.g. to display the "Create Class" option when nothing is present in the list. Fixes #2237 Co-authored-by: Tristan Crawford --- src/CMakeLists.txt | 1 - src/common/AddressableItemModel.cpp | 8 +- src/menus/AddressableItemContextMenu.cpp | 7 +- src/widgets/AddressableItemList.h | 19 ++- src/widgets/ClassesWidget.cpp | 176 ++++++++++++++--------- src/widgets/ClassesWidget.h | 41 +++--- src/widgets/ClassesWidget.ui | 148 ------------------- src/widgets/ListDockWidget.cpp | 1 - 8 files changed, 160 insertions(+), 241 deletions(-) delete mode 100644 src/widgets/ClassesWidget.ui diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f340ee6e..dd8f524e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -338,7 +338,6 @@ set(UI_FILES dialogs/preferences/InitializationFileEditor.ui widgets/QuickFilterView.ui widgets/DecompilerWidget.ui - widgets/ClassesWidget.ui widgets/VTablesWidget.ui widgets/TypesWidget.ui widgets/SearchWidget.ui diff --git a/src/common/AddressableItemModel.cpp b/src/common/AddressableItemModel.cpp index d36fa594..120b4f38 100644 --- a/src/common/AddressableItemModel.cpp +++ b/src/common/AddressableItemModel.cpp @@ -13,11 +13,17 @@ AddressableFilterProxyModel::AddressableFilterProxyModel(AddressableItemModelI * RVA AddressableFilterProxyModel::address(const QModelIndex &index) const { + if (!addressableSourceModel) { + return RVA_INVALID; + } return addressableSourceModel->address(this->mapToSource(index)); } QString AddressableFilterProxyModel::name(const QModelIndex &index) const { + if (!addressableSourceModel) { + return QString(); + } return addressableSourceModel->name(this->mapToSource(index)); } @@ -28,6 +34,6 @@ void AddressableFilterProxyModel::setSourceModel(QAbstractItemModel *) void AddressableFilterProxyModel::setSourceModel(AddressableItemModelI *sourceModel) { - ParentClass::setSourceModel(sourceModel->asItemModel()); + ParentClass::setSourceModel(sourceModel ? sourceModel->asItemModel() : nullptr); addressableSourceModel = sourceModel; } diff --git a/src/menus/AddressableItemContextMenu.cpp b/src/menus/AddressableItemContextMenu.cpp index 0a82a0f7..8a1d3c7f 100644 --- a/src/menus/AddressableItemContextMenu.cpp +++ b/src/menus/AddressableItemContextMenu.cpp @@ -112,7 +112,8 @@ void AddressableItemContextMenu::aboutToShowSlot() void AddressableItemContextMenu::setHasTarget(bool hasTarget) { this->hasTarget = hasTarget; - for (const auto &action : this->actions()) { - action->setEnabled(hasTarget); - } + actionShowInMenu->setEnabled(hasTarget); + actionCopyAddress->setEnabled(hasTarget); + actionShowXrefs->setEnabled(hasTarget); + actionAddcomment->setEnabled(hasTarget); } diff --git a/src/widgets/AddressableItemList.h b/src/widgets/AddressableItemList.h index 75cd832b..73df3d62 100644 --- a/src/widgets/AddressableItemList.h +++ b/src/widgets/AddressableItemList.h @@ -57,16 +57,30 @@ public: itemContextMenu = menu; } + /** + * If this is set to true, the context menu will also be shown if no item + * is currently selected. + */ + void setShowItemContextMenuWithoutAddress(bool val) { showItemContextMenuWithoutAddress = val; } + protected: virtual void showItemContextMenu(const QPoint &pt) { + if (!itemContextMenu) { + return; + } auto index = this->currentIndex(); - if (index.isValid() && itemContextMenu) { + if (index.isValid()) { auto offset = addressableModel->address(index); auto name = addressableModel->name(index); itemContextMenu->setTarget(offset, name); - itemContextMenu->exec(this->mapToGlobal(pt)); + } else { + if (!showItemContextMenuWithoutAddress) { + return; + } + itemContextMenu->clearTarget(); } + itemContextMenu->exec(this->mapToGlobal(pt)); } virtual void onItemActivated(const QModelIndex &index) @@ -90,6 +104,7 @@ protected: } private: + bool showItemContextMenuWithoutAddress = false; AddressableItemModelI *addressableModel = nullptr; AddressableItemContextMenu *itemContextMenu = nullptr; MainWindow *mainWindow = nullptr; diff --git a/src/widgets/ClassesWidget.cpp b/src/widgets/ClassesWidget.cpp index 42c461ad..5689bc7c 100644 --- a/src/widgets/ClassesWidget.cpp +++ b/src/widgets/ClassesWidget.cpp @@ -1,6 +1,6 @@ #include "ClassesWidget.h" #include "core/MainWindow.h" -#include "ui_ClassesWidget.h" +#include "ui_ListDockWidget.h" #include "common/Helpers.h" #include "common/SvgIconEngine.h" #include "dialogs/EditMethodDialog.h" @@ -9,6 +9,8 @@ #include #include #include +#include +#include QVariant ClassesModel::headerData(int section, Qt::Orientation, int role) const { @@ -33,6 +35,17 @@ QVariant ClassesModel::headerData(int section, Qt::Orientation, int role) const } } +RVA ClassesModel::address(const QModelIndex &index) const +{ + QVariant v = data(index, OffsetRole); + return v.isValid() ? v.toULongLong() : RVA_INVALID; +} + +QString ClassesModel::name(const QModelIndex &index) const +{ + return data(index, NameRole).toString(); +} + BinClassesModel::BinClassesModel(QObject *parent) : ClassesModel(parent) {} void BinClassesModel::setClasses(const QList &classes) @@ -526,12 +539,17 @@ QVariant AnalysisClassesModel::data(const QModelIndex &index, int role) const } ClassesSortFilterProxyModel::ClassesSortFilterProxyModel(QObject *parent) - : QSortFilterProxyModel(parent) + : AddressableFilterProxyModel(nullptr, parent) { + setFilterCaseSensitivity(Qt::CaseInsensitive); + setSortCaseSensitivity(Qt::CaseInsensitive); } bool ClassesSortFilterProxyModel::filterAcceptsRow(int row, const QModelIndex &parent) const { + if (parent.isValid()) + return true; + QModelIndex index = sourceModel()->index(row, 0, parent); return qhelpers::filterStringContains(index.data(ClassesModel::NameRole).toString(), this); } @@ -576,23 +594,63 @@ bool ClassesSortFilterProxyModel::hasChildren(const QModelIndex &parent) const return !parent.isValid() || !parent.parent().isValid(); } -ClassesWidget::ClassesWidget(MainWindow *main) : CutterDockWidget(main), ui(new Ui::ClassesWidget) +ClassesWidget::ClassesWidget(MainWindow *main) + : ListDockWidget(main), + seekToVTableAction(tr("Seek to VTable"), this), + editMethodAction(tr("Edit Method"), this), + addMethodAction(tr("Add Method"), this), + newClassAction(tr("Create new Class"), this), + renameClassAction(tr("Rename Class"), this), + deleteClassAction(tr("Delete Class"), this) { - ui->setupUi(this); + setWindowTitle(tr("Classes")); + setObjectName("ClassesWidget"); - ui->classesTreeView->setIconSize(QSize(10, 10)); + ui->treeView->setIconSize(QSize(10, 10)); proxy_model = new ClassesSortFilterProxyModel(this); - ui->classesTreeView->setModel(proxy_model); - ui->classesTreeView->sortByColumn(ClassesModel::TYPE, Qt::AscendingOrder); - ui->classesTreeView->setContextMenuPolicy(Qt::CustomContextMenu); + setModels(proxy_model); - ui->classSourceCombo->setCurrentIndex(1); + classSourceCombo = new QComboBox(this); + // User an intermediate single-child layout to contain the combo box, otherwise + // when the combo box is inserted directly, the entire vertical layout gets a + // weird horizontal padding on macOS. + QBoxLayout *comboLayout = new QBoxLayout(QBoxLayout::Direction::LeftToRight, nullptr); + comboLayout->addWidget(classSourceCombo); + ui->verticalLayout->insertLayout(ui->verticalLayout->indexOf(ui->quickFilterView), comboLayout); + classSourceCombo->addItem(tr("Binary Info (Fixed)")); + classSourceCombo->addItem(tr("Analysis (Editable)")); + classSourceCombo->setCurrentIndex(1); - connect(ui->classSourceCombo, &QComboBox::currentIndexChanged, this, + connect(classSourceCombo, &QComboBox::currentIndexChanged, this, &ClassesWidget::refreshClasses); - connect(ui->classesTreeView, &QTreeView::customContextMenuRequested, this, - &ClassesWidget::showContextMenu); + + connect(&seekToVTableAction, &QAction::triggered, this, + &ClassesWidget::seekToVTableActionTriggered); + connect(&editMethodAction, &QAction::triggered, this, + &ClassesWidget::editMethodActionTriggered); + connect(&addMethodAction, &QAction::triggered, this, &ClassesWidget::addMethodActionTriggered); + connect(&newClassAction, &QAction::triggered, this, &ClassesWidget::newClassActionTriggered); + connect(&renameClassAction, &QAction::triggered, this, + &ClassesWidget::renameClassActionTriggered); + connect(&deleteClassAction, &QAction::triggered, this, + &ClassesWidget::deleteClassActionTriggered); + + // Build context menu like this: + // class-related actions + // -- classesMethodsSeparator + // method-related actions + // -- separator + // default actions from AddressableItemList + auto contextMenu = ui->treeView->getItemContextMenu(); + contextMenu->insertSeparator(contextMenu->actions().first()); + contextMenu->insertActions(contextMenu->actions().first(), + { &addMethodAction, &editMethodAction, &seekToVTableAction }); + classesMethodsSeparator = contextMenu->insertSeparator(contextMenu->actions().first()); + contextMenu->insertActions(classesMethodsSeparator, + { &newClassAction, &renameClassAction, &deleteClassAction }); + connect(contextMenu, &QMenu::aboutToShow, this, &ClassesWidget::updateActions); + ui->treeView->setShowItemContextMenuWithoutAddress(true); refreshClasses(); } @@ -601,7 +659,7 @@ ClassesWidget::~ClassesWidget() {} ClassesWidget::Source ClassesWidget::getSource() { - switch (ui->classSourceCombo->currentIndex()) { + switch (classSourceCombo->currentIndex()) { case 0: return Source::BIN; default: @@ -614,88 +672,68 @@ void ClassesWidget::refreshClasses() switch (getSource()) { case Source::BIN: if (!bin_model) { - proxy_model->setSourceModel(nullptr); + proxy_model->setSourceModel(static_cast(nullptr)); delete analysis_model; analysis_model = nullptr; bin_model = new BinClassesModel(this); - proxy_model->setSourceModel(bin_model); + proxy_model->setSourceModel(static_cast(bin_model)); } bin_model->setClasses(Core()->getAllClassesFromBin()); break; case Source::ANALYSIS: if (!analysis_model) { - proxy_model->setSourceModel(nullptr); + proxy_model->setSourceModel(static_cast(nullptr)); delete bin_model; bin_model = nullptr; analysis_model = new AnalysisClassesModel(this); - proxy_model->setSourceModel(analysis_model); + proxy_model->setSourceModel(static_cast(analysis_model)); } break; } - qhelpers::adjustColumns(ui->classesTreeView, 3, 0); + qhelpers::adjustColumns(ui->treeView, 3, 0); - ui->classesTreeView->setColumnWidth(0, 200); + ui->treeView->setColumnWidth(0, 200); } -void ClassesWidget::on_classesTreeView_doubleClicked(const QModelIndex &index) +void ClassesWidget::updateActions() { - if (!index.isValid()) - return; + bool isAnalysis = !!analysis_model; + newClassAction.setVisible(isAnalysis); + addMethodAction.setVisible(isAnalysis); - QVariant offsetData = index.data(ClassesModel::OffsetRole); - if (!offsetData.isValid()) { - return; - } - RVA offset = offsetData.value(); - Core()->seekAndShow(offset); -} - -void ClassesWidget::showContextMenu(const QPoint &pt) -{ - if (!analysis_model) { - // no context menu for bin classes - return; + bool rowIsAnalysisClass = false; + bool rowIsAnalysisMethod = false; + QModelIndex index = ui->treeView->selectionModel()->currentIndex(); + if (isAnalysis && index.isValid()) { + auto type = static_cast(index.data(ClassesModel::TypeRole).toInt()); + rowIsAnalysisClass = type == ClassesModel::RowType::Class; + rowIsAnalysisMethod = type == ClassesModel::RowType::Method; } - QModelIndex index = ui->classesTreeView->selectionModel()->currentIndex(); - if (!index.isValid()) { - return; - } - auto type = static_cast(index.data(ClassesModel::TypeRole).toInt()); + renameClassAction.setVisible(rowIsAnalysisClass); + deleteClassAction.setVisible(rowIsAnalysisClass); - QMenu menu(ui->classesTreeView); - - menu.addAction(ui->newClassAction); - - if (type == ClassesModel::RowType::Class) { - menu.addAction(ui->renameClassAction); - menu.addAction(ui->deleteClassAction); - } - - menu.addSeparator(); - - menu.addAction(ui->addMethodAction); - - if (type == ClassesModel::RowType::Method) { - menu.addAction(ui->editMethodAction); + classesMethodsSeparator->setVisible(rowIsAnalysisClass || rowIsAnalysisMethod); + editMethodAction.setVisible(rowIsAnalysisMethod); + bool rowHasVTable = false; + if (rowIsAnalysisMethod) { QString className = index.parent().data(ClassesModel::NameRole).toString(); QString methodName = index.data(ClassesModel::NameRole).toString(); AnalysisMethodDescription desc; if (Core()->getAnalysisMethod(className, methodName, &desc)) { if (desc.vtableOffset >= 0) { - menu.addAction(ui->seekToVTableAction); + rowHasVTable = true; } } } - - menu.exec(ui->classesTreeView->mapToGlobal(pt)); + seekToVTableAction.setVisible(rowHasVTable); } -void ClassesWidget::on_seekToVTableAction_triggered() +void ClassesWidget::seekToVTableActionTriggered() { - QModelIndex index = ui->classesTreeView->selectionModel()->currentIndex(); + QModelIndex index = ui->treeView->selectionModel()->currentIndex(); QString className = index.parent().data(ClassesModel::NameRole).toString(); QList vtables = Core()->getAnalysisClassVTables(className); @@ -714,9 +752,9 @@ void ClassesWidget::on_seekToVTableAction_triggered() Core()->seekAndShow(vtables[0].addr + desc.vtableOffset); } -void ClassesWidget::on_addMethodAction_triggered() +void ClassesWidget::addMethodActionTriggered() { - QModelIndex index = ui->classesTreeView->selectionModel()->currentIndex(); + QModelIndex index = ui->treeView->selectionModel()->currentIndex(); if (!index.isValid()) { return; } @@ -732,9 +770,9 @@ void ClassesWidget::on_addMethodAction_triggered() EditMethodDialog::newMethod(className, QString(), this); } -void ClassesWidget::on_editMethodAction_triggered() +void ClassesWidget::editMethodActionTriggered() { - QModelIndex index = ui->classesTreeView->selectionModel()->currentIndex(); + QModelIndex index = ui->treeView->selectionModel()->currentIndex(); if (!index.isValid() || index.data(ClassesModel::TypeRole).toInt() != static_cast(ClassesModel::RowType::Method)) { @@ -745,7 +783,7 @@ void ClassesWidget::on_editMethodAction_triggered() EditMethodDialog::editMethod(className, methName, this); } -void ClassesWidget::on_newClassAction_triggered() +void ClassesWidget::newClassActionTriggered() { bool ok; QString name = QInputDialog::getText(this, tr("Create new Class"), tr("Class Name:"), @@ -755,9 +793,9 @@ void ClassesWidget::on_newClassAction_triggered() } } -void ClassesWidget::on_deleteClassAction_triggered() +void ClassesWidget::deleteClassActionTriggered() { - QModelIndex index = ui->classesTreeView->selectionModel()->currentIndex(); + QModelIndex index = ui->treeView->selectionModel()->currentIndex(); if (!index.isValid() || index.data(ClassesModel::TypeRole).toInt() != static_cast(ClassesModel::RowType::Class)) { @@ -772,9 +810,9 @@ void ClassesWidget::on_deleteClassAction_triggered() Core()->deleteClass(className); } -void ClassesWidget::on_renameClassAction_triggered() +void ClassesWidget::renameClassActionTriggered() { - QModelIndex index = ui->classesTreeView->selectionModel()->currentIndex(); + QModelIndex index = ui->treeView->selectionModel()->currentIndex(); if (!index.isValid() || index.data(ClassesModel::TypeRole).toInt() != static_cast(ClassesModel::RowType::Class)) { diff --git a/src/widgets/ClassesWidget.h b/src/widgets/ClassesWidget.h index bfe9034e..4b13b740 100644 --- a/src/widgets/ClassesWidget.h +++ b/src/widgets/ClassesWidget.h @@ -5,6 +5,7 @@ #include "core/Cutter.h" #include "CutterDockWidget.h" +#include "widgets/ListDockWidget.h" #include #include @@ -21,7 +22,7 @@ class ClassesWidget; /** * @brief Common abstract base class for Bin and Anal classes models */ -class ClassesModel : public QAbstractItemModel +class ClassesModel : public AddressableItemModel<> { public: enum Columns { NAME = 0, REAL_NAME, TYPE, OFFSET, VTABLE, COUNT }; @@ -69,10 +70,13 @@ public: */ static const int RealNameRole = Qt::UserRole + 4; - explicit ClassesModel(QObject *parent = nullptr) : QAbstractItemModel(parent) {} + explicit ClassesModel(QObject *parent = nullptr) : AddressableItemModel(parent) {} QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; + + RVA address(const QModelIndex &index) const override; + QString name(const QModelIndex &index) const override; }; Q_DECLARE_METATYPE(ClassesModel::RowType) @@ -163,7 +167,7 @@ public slots: void classAttrsChanged(const QString &cls); }; -class ClassesSortFilterProxyModel : public QSortFilterProxyModel +class ClassesSortFilterProxyModel : public AddressableFilterProxyModel { Q_OBJECT @@ -176,7 +180,7 @@ protected: bool hasChildren(const QModelIndex &parent = QModelIndex()) const override; }; -class ClassesWidget : public CutterDockWidget +class ClassesWidget : public ListDockWidget { Q_OBJECT @@ -185,29 +189,34 @@ public: ~ClassesWidget(); private slots: - void on_classesTreeView_doubleClicked(const QModelIndex &index); - - void on_seekToVTableAction_triggered(); - void on_addMethodAction_triggered(); - void on_editMethodAction_triggered(); - void on_newClassAction_triggered(); - void on_deleteClassAction_triggered(); - void on_renameClassAction_triggered(); - - void showContextMenu(const QPoint &pt); + void seekToVTableActionTriggered(); + void editMethodActionTriggered(); + void addMethodActionTriggered(); + void newClassActionTriggered(); + void renameClassActionTriggered(); + void deleteClassActionTriggered(); void refreshClasses(); + void updateActions(); private: enum class Source { BIN, ANALYSIS }; Source getSource(); - std::unique_ptr ui; - BinClassesModel *bin_model = nullptr; AnalysisClassesModel *analysis_model = nullptr; ClassesSortFilterProxyModel *proxy_model; + + QComboBox *classSourceCombo; + + QAction seekToVTableAction; + QAction editMethodAction; + QAction addMethodAction; + QAction newClassAction; + QAction renameClassAction; + QAction deleteClassAction; + QAction *classesMethodsSeparator; }; #endif // CLASSESWIDGET_H diff --git a/src/widgets/ClassesWidget.ui b/src/widgets/ClassesWidget.ui deleted file mode 100644 index 42839c0d..00000000 --- a/src/widgets/ClassesWidget.ui +++ /dev/null @@ -1,148 +0,0 @@ - - - ClassesWidget - - - - 0 - 0 - 400 - 300 - - - - Classes - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - CutterTreeView::item -{ - padding-top: 1px; - padding-bottom: 1px; -} - - - QFrame::NoFrame - - - 0 - - - true - - - false - - - - - - - 10 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - Source: - - - - - - - - 0 - 0 - - - - - Binary Info (Fixed) - - - - - Analysis (Editable) - - - - - - - - - - - Seek to VTable - - - - - Edit Method - - - - - Add Method - - - - - Create new Class - - - - - Rename Class - - - - - Delete Class - - - - - - CutterTreeView - QTreeView -
widgets/CutterTreeView.h
- 1 -
-
- - -
diff --git a/src/widgets/ListDockWidget.cpp b/src/widgets/ListDockWidget.cpp index e8e46ee1..d61a0e1f 100644 --- a/src/widgets/ListDockWidget.cpp +++ b/src/widgets/ListDockWidget.cpp @@ -2,7 +2,6 @@ #include "ui_ListDockWidget.h" #include "core/MainWindow.h" #include "common/Helpers.h" -#include "menus/AddressableItemContextMenu.h" #include #include From fcd504d87f917b93b91ab164b5d53ebb8c2eef49 Mon Sep 17 00:00:00 2001 From: Anton Kochkov Date: Mon, 10 Apr 2023 15:58:34 +0800 Subject: [PATCH 08/50] Update rizin to the latest "dev", translations (#3160) --- rizin | 2 +- src/translations | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rizin b/rizin index 3a7d5116..9ab709bc 160000 --- a/rizin +++ b/rizin @@ -1 +1 @@ -Subproject commit 3a7d5116244beb678ad9950bb9dd27d28ed2691f +Subproject commit 9ab709bc34843f04ffde3b63322c809596123e77 diff --git a/src/translations b/src/translations index 41c0c778..30081249 160000 --- a/src/translations +++ b/src/translations @@ -1 +1 @@ -Subproject commit 41c0c778b942577749ea2fed117e48a2cf3892df +Subproject commit 30081249a58830c3a49838cd4d6d2d6d14a68636 From 3166843dff30f4dd8e77b0836ce481bb54b67e51 Mon Sep 17 00:00:00 2001 From: Giovanni <561184+wargio@users.noreply.github.com> Date: Tue, 11 Apr 2023 11:35:52 +0800 Subject: [PATCH 09/50] Add rz-silhouette to cutter builds (#3161) --- .github/workflows/ci.yml | 3 +++ .woodpecker/macos-arm64.yml | 1 + CMakeLists.txt | 2 ++ dist/CMakeLists.txt | 24 ++++++++++++++++++++++++ dist/bundle_rz_silhouette.ps1 | 17 +++++++++++++++++ scripts/rz-silhouette.sh | 17 +++++++++++++++++ 6 files changed, 64 insertions(+) create mode 100644 dist/bundle_rz_silhouette.ps1 create mode 100755 scripts/rz-silhouette.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 757189d2..983d2b5b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -155,6 +155,7 @@ jobs: -DCUTTER_PACKAGE_JSDEC=ON \ -DCUTTER_PACKAGE_RZ_LIBSWIFT=ON \ -DCUTTER_PACKAGE_RZ_LIBYARA=ON \ + -DCUTTER_PACKAGE_RZ_SILHOUETTE=ON \ -DCMAKE_INSTALL_PREFIX=appdir/usr \ -DCMAKE_INSTALL_RPATH_USE_LINK_PATH=ON \ .. @@ -227,6 +228,7 @@ jobs: -DCUTTER_PACKAGE_JSDEC=ON \ -DCUTTER_PACKAGE_RZ_LIBSWIFT=ON \ -DCUTTER_PACKAGE_RZ_LIBYARA=ON \ + -DCUTTER_PACKAGE_RZ_SILHOUETTE=ON \ -DCPACK_PACKAGE_FILE_NAME="$PACKAGE_NAME" \ -DCPACK_BUNDLE_APPLE_CERT_APP="-" \ .. && \ @@ -266,6 +268,7 @@ jobs: -DCUTTER_PACKAGE_RZ_GHIDRA=ON ^ -DCUTTER_PACKAGE_RZ_LIBSWIFT=ON ^ -DCUTTER_PACKAGE_RZ_LIBYARA=ON ^ + -DCUTTER_PACKAGE_RZ_SILHOUETTE=ON ^ -DCUTTER_PACKAGE_JSDEC=ON ^ -DCUTTER_ENABLE_DEPENDENCY_DOWNLOADS=ON ^ -DCMAKE_PREFIX_PATH="%CUTTER_DEPS%\pyside" ^ diff --git a/.woodpecker/macos-arm64.yml b/.woodpecker/macos-arm64.yml index bbff7e68..e2690181 100644 --- a/.woodpecker/macos-arm64.yml +++ b/.woodpecker/macos-arm64.yml @@ -28,6 +28,7 @@ pipeline: -DCUTTER_PACKAGE_JSDEC=ON -DCUTTER_PACKAGE_RZ_LIBSWIFT=ON -DCUTTER_PACKAGE_RZ_LIBYARA=ON + -DCUTTER_PACKAGE_RZ_SILHOUETTE=ON -DCPACK_PACKAGE_FILE_NAME="$$PACKAGE_NAME" -DCPACK_BUNDLE_APPLE_CERT_APP="-" - ninja -C build diff --git a/CMakeLists.txt b/CMakeLists.txt index 8645b267..60cd6c53 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,6 +27,7 @@ option(CUTTER_PACKAGE_DEPENDENCIES "During install step include the third party option(CUTTER_PACKAGE_RZ_GHIDRA "Compile and install rz-ghidra during install step." OFF) option(CUTTER_PACKAGE_RZ_LIBSWIFT "Compile and install rz-libswift demangler during the install step." OFF) option(CUTTER_PACKAGE_RZ_LIBYARA "Compile and install rz-libyara during the install step." OFF) +option(CUTTER_PACKAGE_RZ_SILHOUETTE "Compile and install rz-silhouette during the install step." OFF) option(CUTTER_PACKAGE_JSDEC "Compile and install jsdec during install step." OFF) OPTION(CUTTER_QT6 "Use QT6" OFF) @@ -161,6 +162,7 @@ message(STATUS "- Package Dependencies: ${CUTTER_PACKAGE_DEPENDENCIES}") message(STATUS "- Package RzGhidra: ${CUTTER_PACKAGE_RZ_GHIDRA}") message(STATUS "- Package RzLibSwift: ${CUTTER_PACKAGE_RZ_LIBSWIFT}") message(STATUS "- Package RzLibYara: ${CUTTER_PACKAGE_RZ_LIBYARA}") +message(STATUS "- Package RzSilhouette: ${CUTTER_PACKAGE_RZ_SILHOUETTE}") message(STATUS "- Package JSDec: ${CUTTER_PACKAGE_JSDEC}") message(STATUS "- QT6: ${CUTTER_QT6}") message(STATUS "") diff --git a/dist/CMakeLists.txt b/dist/CMakeLists.txt index 956fbd18..822942fd 100644 --- a/dist/CMakeLists.txt +++ b/dist/CMakeLists.txt @@ -59,6 +59,18 @@ if(WIN32) endif() ") endif() + if (CUTTER_PACKAGE_RZ_SILHOUETTE AND CUTTER_ENABLE_DEPENDENCY_DOWNLOADS) + install(CODE " + set(ENV{RZ_PREFIX} \"\${CMAKE_INSTALL_PREFIX}\") + set(ENV{PATH} \"\${CMAKE_INSTALL_PREFIX};\$ENV{PATH}\") + execute_process(COMMAND powershell \"${CMAKE_CURRENT_SOURCE_DIR}/bundle_rz_silhouette.ps1\" \"\${CMAKE_INSTALL_PREFIX}\" + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + RESULT_VARIABLE SCRIPT_RESULT) + if (SCRIPT_RESULT) + message(FATAL_ERROR \"Failed to package rz-silhouette (returned \${SCRIPT_RESULT})\") + endif() + ") + endif() endif() ################################################ @@ -150,6 +162,18 @@ if(CUTTER_ENABLE_DEPENDENCY_DOWNLOADS AND (NOT WIN32)) endif() ") endif() + if (CUTTER_PACKAGE_RZ_SILHOUETTE) + install(CODE " + execute_process(COMMAND + \"${CMAKE_CURRENT_SOURCE_DIR}/../scripts/rz-silhouette.sh\" + \"\${CMAKE_INSTALL_PREFIX}\" \"${YARA_PLUGIN_OPTIONS}\" + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + RESULT_VARIABLE SCRIPT_RESULT) + if (SCRIPT_RESULT) + message(FATAL_ERROR \"Failed to package rz-silhouette (returned \${SCRIPT_RESULT})\") + endif() + ") + endif() endif() ################################################ diff --git a/dist/bundle_rz_silhouette.ps1 b/dist/bundle_rz_silhouette.ps1 new file mode 100644 index 00000000..4aeeb2b9 --- /dev/null +++ b/dist/bundle_rz_silhouette.ps1 @@ -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/plugins/rz_silhouette.dll" +if(![System.IO.File]::Exists($pathdll)) { + type build/meson-logs/meson-log.txt + ls "$dist/lib/plugins/" + throw (New-Object System.IO.FileNotFoundException("File not found: $pathdll", $pathdll)) +} +Remove-Item -Recurse -Force $dist/lib/plugins/rz_silhouette.lib diff --git a/scripts/rz-silhouette.sh b/scripts/rz-silhouette.sh new file mode 100755 index 00000000..f2f595e5 --- /dev/null +++ b/scripts/rz-silhouette.sh @@ -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 From b27ee987da92042d3d7e377d19c5aa00705b2924 Mon Sep 17 00:00:00 2001 From: xarkes Date: Thu, 4 May 2023 06:13:57 +0200 Subject: [PATCH 10/50] Update MapFileDialog.cpp (#3165) --- src/dialogs/MapFileDialog.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dialogs/MapFileDialog.cpp b/src/dialogs/MapFileDialog.cpp index dfcd2647..24cbbf7a 100644 --- a/src/dialogs/MapFileDialog.cpp +++ b/src/dialogs/MapFileDialog.cpp @@ -33,7 +33,7 @@ void MapFileDialog::on_buttonBox_accepted() } if (!Core()->mapFile(filePath, mapAddress)) { - QMessageBox::critical(this, tr("Map new file file"), tr("Failed to map a new file")); + QMessageBox::critical(this, tr("Map new file"), tr("Failed to map a new file")); return; } close(); From cf0f7025903064cb18291ca1b89ca7064667f984 Mon Sep 17 00:00:00 2001 From: xarkes Date: Thu, 4 May 2023 06:14:13 +0200 Subject: [PATCH 11/50] Update IOModesController.cpp (#3166) --- src/common/IOModesController.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/IOModesController.cpp b/src/common/IOModesController.cpp index 26f0801f..a3ba1edc 100644 --- a/src/common/IOModesController.cpp +++ b/src/common/IOModesController.cpp @@ -91,7 +91,7 @@ bool IOModesController::askCommitUnsavedChanges() // Check if there are uncommitted changes if (!allChangesComitted()) { QMessageBox::StandardButton ret = QMessageBox::question( - NULL, QObject::tr("Uncomitted changes"), + NULL, QObject::tr("Uncommitted changes"), QObject::tr("It seems that you have changes or patches that are not committed to " "the file.\n" "Do you want to commit them now?"), From 68b3cb100414b5e911e755ce737eec0e6209e92f Mon Sep 17 00:00:00 2001 From: Anton Kochkov Date: Thu, 4 May 2023 15:35:29 +0800 Subject: [PATCH 12/50] Update Japanese, Spanish, Ukranian translations (#3167) --- src/translations | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/translations b/src/translations index 30081249..433de885 160000 --- a/src/translations +++ b/src/translations @@ -1 +1 @@ -Subproject commit 30081249a58830c3a49838cd4d6d2d6d14a68636 +Subproject commit 433de8859d1b1853b52e2e82cf75786d09943efb From 8c89dfde8b2f1d78b5c70e0f85f0475943993428 Mon Sep 17 00:00:00 2001 From: Lucas Hosseini Date: Sat, 6 May 2023 03:53:40 +0200 Subject: [PATCH 13/50] Show C type definition in tooltip in Types widget (#3169) --- src/widgets/TypesWidget.cpp | 13 +++++++++++++ src/widgets/TypesWidget.h | 5 +++++ 2 files changed, 18 insertions(+) diff --git a/src/widgets/TypesWidget.cpp b/src/widgets/TypesWidget.cpp index 3891314e..c31a50d6 100644 --- a/src/widgets/TypesWidget.cpp +++ b/src/widgets/TypesWidget.cpp @@ -14,6 +14,17 @@ TypesModel::TypesModel(QList *types, QObject *parent) { } +QVariant TypesModel::toolTipValue(const QModelIndex &index) const +{ + TypeDescription t = index.data(TypesModel::TypeDescriptionRole).value(); + + if (t.category == "Primitive") { + return QVariant(); + } + + return Core()->getTypeAsC(t.type).trimmed(); +} + int TypesModel::rowCount(const QModelIndex &) const { return types->count(); @@ -45,6 +56,8 @@ QVariant TypesModel::data(const QModelIndex &index, int role) const default: return QVariant(); } + case Qt::ToolTipRole: + return toolTipValue(index); case TypeDescriptionRole: return QVariant::fromValue(exp); default: diff --git a/src/widgets/TypesWidget.h b/src/widgets/TypesWidget.h index dec3ece5..53037790 100644 --- a/src/widgets/TypesWidget.h +++ b/src/widgets/TypesWidget.h @@ -30,6 +30,11 @@ class TypesModel : public QAbstractListModel private: QList *types; + /** + * @brief Returns a description of the type for the given index + */ + QVariant toolTipValue(const QModelIndex &index) const; + public: enum Columns { TYPE = 0, SIZE, CATEGORY, FORMAT, COUNT }; static const int TypeDescriptionRole = Qt::UserRole; From 882f34048132083fbb3f6b1c0ef6fcf8b58c8a84 Mon Sep 17 00:00:00 2001 From: Lucas Hosseini Date: Tue, 9 May 2023 03:48:09 +0200 Subject: [PATCH 14/50] Only jump to reference when double-clicking with left mouse button. (#3174) --- src/widgets/DisassemblyWidget.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/widgets/DisassemblyWidget.cpp b/src/widgets/DisassemblyWidget.cpp index 4c18b57f..6cafb532 100644 --- a/src/widgets/DisassemblyWidget.cpp +++ b/src/widgets/DisassemblyWidget.cpp @@ -623,10 +623,12 @@ bool DisassemblyWidget::eventFilter(QObject *obj, QEvent *event) && (obj == mDisasTextEdit || obj == mDisasTextEdit->viewport())) { QMouseEvent *mouseEvent = static_cast(event); - const QTextCursor &cursor = mDisasTextEdit->cursorForPosition(mouseEvent->pos()); - jumpToOffsetUnderCursor(cursor); + if (mouseEvent->button() == Qt::LeftButton) { + const QTextCursor &cursor = mDisasTextEdit->cursorForPosition(mouseEvent->pos()); + jumpToOffsetUnderCursor(cursor); - return true; + return true; + } } else if (Config()->getPreviewValue() && event->type() == QEvent::ToolTip && obj == mDisasTextEdit->viewport()) { From b63ea5b3e4d6829331e5aca2de015cdd4f184379 Mon Sep 17 00:00:00 2001 From: Lucas Hosseini Date: Tue, 9 May 2023 03:49:43 +0200 Subject: [PATCH 15/50] Disable expandsOnDoubleClick for Functions widget. (#3172) closes #2788 --- src/widgets/FunctionsWidget.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/widgets/FunctionsWidget.cpp b/src/widgets/FunctionsWidget.cpp index 61dad3fe..8c439b1d 100644 --- a/src/widgets/FunctionsWidget.cpp +++ b/src/widgets/FunctionsWidget.cpp @@ -508,6 +508,7 @@ FunctionsWidget::FunctionsWidget(MainWindow *main) functionProxyModel = new FunctionSortFilterProxyModel(functionModel, this); setModels(functionProxyModel); ui->treeView->sortByColumn(FunctionModel::NameColumn, Qt::AscendingOrder); + ui->treeView->setExpandsOnDoubleClick(false); titleContextMenu = new QMenu(this); auto viewTypeGroup = new QActionGroup(titleContextMenu); From beec78b4e206b88f7cb8e0b1e0180a5069d6ce54 Mon Sep 17 00:00:00 2001 From: Lucas Hosseini Date: Thu, 11 May 2023 03:16:06 +0200 Subject: [PATCH 16/50] Keep topOffset history in disassembly widget. (#3171) This commit adds support for a `topOffset` history (on top of the rizin-managed history), that keeps track of the offset of the top instruction in the disassembly widget. closes #2970 --- src/common/CutterSeekable.cpp | 12 ++++++------ src/common/CutterSeekable.h | 12 ++++++++---- src/core/Cutter.cpp | 8 ++++---- src/core/Cutter.h | 7 +++++-- src/widgets/DisassemblyWidget.cpp | 20 +++++++++++++++++--- src/widgets/DisassemblyWidget.h | 5 ++++- 6 files changed, 44 insertions(+), 20 deletions(-) diff --git a/src/common/CutterSeekable.cpp b/src/common/CutterSeekable.cpp index f8b0805a..12ef7b7b 100644 --- a/src/common/CutterSeekable.cpp +++ b/src/common/CutterSeekable.cpp @@ -13,18 +13,18 @@ CutterSeekable::~CutterSeekable() {} void CutterSeekable::setSynchronization(bool sync) { synchronized = sync; - onCoreSeekChanged(Core()->getOffset()); + onCoreSeekChanged(Core()->getOffset(), CutterCore::SeekHistoryType::New); emit syncChanged(); } -void CutterSeekable::onCoreSeekChanged(RVA addr) +void CutterSeekable::onCoreSeekChanged(RVA addr, CutterCore::SeekHistoryType type) { if (synchronized && widgetOffset != addr) { - updateSeek(addr, true); + updateSeek(addr, type, true); } } -void CutterSeekable::updateSeek(RVA addr, bool localOnly) +void CutterSeekable::updateSeek(RVA addr, CutterCore::SeekHistoryType type, bool localOnly) { previousOffset = widgetOffset; widgetOffset = addr; @@ -32,7 +32,7 @@ void CutterSeekable::updateSeek(RVA addr, bool localOnly) Core()->seek(addr); } - emit seekableSeekChanged(addr); + emit seekableSeekChanged(addr, type); } void CutterSeekable::seekPrev() @@ -40,7 +40,7 @@ void CutterSeekable::seekPrev() if (synchronized) { Core()->seekPrev(); } else { - this->seek(previousOffset); + this->seek(previousOffset, CutterCore::SeekHistoryType::Undo); } } diff --git a/src/common/CutterSeekable.h b/src/common/CutterSeekable.h index b8663cbe..85af3de7 100644 --- a/src/common/CutterSeekable.h +++ b/src/common/CutterSeekable.h @@ -19,8 +19,12 @@ public: * signal will be emitted. * In any case, CutterSeekable::seekableSeekChanged is emitted. * @param addr the location to seek at. + * @param type the type of seek wrt history (Undo, Redo, or New) */ - void seek(RVA addr) { updateSeek(addr, false); } + void seek(RVA addr, CutterCore::SeekHistoryType type = CutterCore::SeekHistoryType::New) + { + updateSeek(addr, type, false); + } /** * @brief setSynchronization sets @@ -67,7 +71,7 @@ private slots: /** * @brief onCoreSeekChanged */ - void onCoreSeekChanged(RVA addr); + void onCoreSeekChanged(RVA addr, CutterCore::SeekHistoryType type); private: /** @@ -91,9 +95,9 @@ private: * @brief internal method for changing the seek * @param localOnly whether the seek should be updated globally if synchronized */ - void updateSeek(RVA addr, bool localOnly); + void updateSeek(RVA addr, CutterCore::SeekHistoryType type, bool localOnly); signals: - void seekableSeekChanged(RVA addr); + void seekableSeekChanged(RVA addr, CutterCore::SeekHistoryType type); void syncChanged(); }; diff --git a/src/core/Cutter.cpp b/src/core/Cutter.cpp index 7a18898c..ebbd2142 100644 --- a/src/core/Cutter.cpp +++ b/src/core/Cutter.cpp @@ -1002,19 +1002,19 @@ void CutterCore::seekPrev() { CORE_LOCK(); rz_core_seek_undo(core); - updateSeek(); + updateSeek(SeekHistoryType::Undo); } void CutterCore::seekNext() { CORE_LOCK(); rz_core_seek_redo(core); - updateSeek(); + updateSeek(SeekHistoryType::Redo); } -void CutterCore::updateSeek() +void CutterCore::updateSeek(SeekHistoryType type) { - emit seekChanged(getOffset()); + emit seekChanged(getOffset(), type); } RVA CutterCore::prevOpAddr(RVA startAddr, int count) diff --git a/src/core/Cutter.h b/src/core/Cutter.h index 5138807d..dd9e67c8 100644 --- a/src/core/Cutter.h +++ b/src/core/Cutter.h @@ -172,6 +172,8 @@ public: return returner; } + enum class SeekHistoryType { New, Undo, Redo }; + CutterJson cmdj(const char *str); CutterJson cmdj(const QString &str) { return cmdj(str.toUtf8().constData()); } QString cmdTask(const QString &str); @@ -324,7 +326,7 @@ public: void seekSilent(QString thing) { seekSilent(math(thing)); } void seekPrev(); void seekNext(); - void updateSeek(); + void updateSeek(SeekHistoryType type = SeekHistoryType::New); /** * @brief Raise a memory widget showing current offset, prefer last active * memory widget. @@ -794,8 +796,9 @@ signals: /** * @brief seekChanged is emitted each time Rizin's seek value is modified * @param offset + * @param historyType */ - void seekChanged(RVA offset); + void seekChanged(RVA offset, SeekHistoryType type = SeekHistoryType::New); void toggleDebugView(); diff --git a/src/widgets/DisassemblyWidget.cpp b/src/widgets/DisassemblyWidget.cpp index 6cafb532..4d3dbe2b 100644 --- a/src/widgets/DisassemblyWidget.cpp +++ b/src/widgets/DisassemblyWidget.cpp @@ -327,6 +327,7 @@ void DisassemblyWidget::scrollInstructions(int count) } refreshDisasm(offset); + topOffsetHistory[topOffsetHistoryPos] = offset; } bool DisassemblyWidget::updateMaxLines() @@ -660,19 +661,32 @@ QString DisassemblyWidget::getWindowTitle() const return tr("Disassembly"); } -void DisassemblyWidget::on_seekChanged(RVA offset) +void DisassemblyWidget::on_seekChanged(RVA offset, CutterCore::SeekHistoryType type) { + if (type == CutterCore::SeekHistoryType::New) { + // Erase previous history past this point. + topOffsetHistory.erase(topOffsetHistory.begin() + topOffsetHistoryPos + 1, + topOffsetHistory.end()); + topOffsetHistory.push_back(offset); + topOffsetHistoryPos = topOffsetHistory.size() - 1; + } else if (type == CutterCore::SeekHistoryType::Undo) { + --topOffsetHistoryPos; + } else if (type == CutterCore::SeekHistoryType::Redo) { + ++topOffsetHistoryPos; + } if (!seekFromCursor) { cursorLineOffset = 0; cursorCharOffset = 0; } - if (topOffset != RVA_INVALID && offset >= topOffset && offset <= bottomOffset) { + if (topOffset != RVA_INVALID && offset >= topOffset && offset <= bottomOffset + && type == CutterCore::SeekHistoryType::New) { // if the line with the seek offset is currently visible, just move the cursor there updateCursorPosition(); + topOffsetHistory[topOffsetHistoryPos] = topOffset; } else { // otherwise scroll there - refreshDisasm(offset); + refreshDisasm(topOffsetHistory[topOffsetHistoryPos]); } mCtxMenu->setOffset(offset); } diff --git a/src/widgets/DisassemblyWidget.h b/src/widgets/DisassemblyWidget.h index 1ab16216..05a47579 100644 --- a/src/widgets/DisassemblyWidget.h +++ b/src/widgets/DisassemblyWidget.h @@ -51,7 +51,7 @@ public slots: QList getLines(); protected slots: - void on_seekChanged(RVA offset); + void on_seekChanged(RVA offset, CutterCore::SeekHistoryType type); void refreshIfInRange(RVA offset); void refreshDisasm(RVA offset = RVA_INVALID); @@ -87,6 +87,9 @@ private: void keyPressEvent(QKeyEvent *event) override; QString getWindowTitle() const override; + int topOffsetHistoryPos = 0; + QList topOffsetHistory; + QList breakpoints; void setupFonts(); From 5a12f7c6267afbdf5a6e0cdd4a0a5b2d78fc2807 Mon Sep 17 00:00:00 2001 From: Giovanni <561184+wargio@users.noreply.github.com> Date: Thu, 11 May 2023 13:44:33 +0800 Subject: [PATCH 17/50] Fix 'Rizin Graph' widget (#3179) --- src/widgets/RizinGraphWidget.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/widgets/RizinGraphWidget.cpp b/src/widgets/RizinGraphWidget.cpp index 346cf012..b228e4a4 100644 --- a/src/widgets/RizinGraphWidget.cpp +++ b/src/widgets/RizinGraphWidget.cpp @@ -96,7 +96,7 @@ void GenericRizinGraphView::loadCurrentGraph() return; } - CutterJson functionsDoc = Core()->cmdj(QString("%1j").arg(graphCommand)); + CutterJson functionsDoc = Core()->cmdj(QString("%1 json").arg(graphCommand)); auto nodes = functionsDoc["nodes"]; for (CutterJson block : nodes) { From 3e402c225e4dbf7ea6521930eb6fc571de3c0c1c Mon Sep 17 00:00:00 2001 From: Anton Kochkov Date: Fri, 12 May 2023 14:48:27 +0800 Subject: [PATCH 18/50] Update Hindi, French, Russian translations (#3182) --- src/translations | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/translations b/src/translations index 433de885..e8fc5ca1 160000 --- a/src/translations +++ b/src/translations @@ -1 +1 @@ -Subproject commit 433de8859d1b1853b52e2e82cf75786d09943efb +Subproject commit e8fc5ca1acd70fd82a2ac9ac02b0261e57703250 From f1a421c9f696d33ef1b7aa6fe84a271a05556af8 Mon Sep 17 00:00:00 2001 From: Lucas Hosseini Date: Fri, 12 May 2023 13:52:41 +0200 Subject: [PATCH 19/50] Fix disassembly scroll history. (#3183) Avoid calling `erase()` when `topOffsetHistoryPos + 1 >= `topOffsetHistory.size()`. closes #3181 --- src/widgets/DisassemblyWidget.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/widgets/DisassemblyWidget.cpp b/src/widgets/DisassemblyWidget.cpp index 4d3dbe2b..ebbbfa0d 100644 --- a/src/widgets/DisassemblyWidget.cpp +++ b/src/widgets/DisassemblyWidget.cpp @@ -665,8 +665,10 @@ void DisassemblyWidget::on_seekChanged(RVA offset, CutterCore::SeekHistoryType t { if (type == CutterCore::SeekHistoryType::New) { // Erase previous history past this point. - topOffsetHistory.erase(topOffsetHistory.begin() + topOffsetHistoryPos + 1, - topOffsetHistory.end()); + 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) { From a6a766785226c1330e87ac4eebb6bf945c535083 Mon Sep 17 00:00:00 2001 From: Lucas Hosseini Date: Fri, 12 May 2023 23:57:35 +0200 Subject: [PATCH 20/50] Keep scroll history in decompiler widget. (#3177) * Keep scroll history in decompiler widget. This commit adds a `scrollHistory` on top of the rizin-managed history, that keeps track of the scroll position within decompiled functions. * Fix clearing history upon init. --- src/widgets/DecompilerWidget.cpp | 35 +++++++++++++++++++------------- src/widgets/DecompilerWidget.h | 6 +++--- 2 files changed, 24 insertions(+), 17 deletions(-) diff --git a/src/widgets/DecompilerWidget.cpp b/src/widgets/DecompilerWidget.cpp index 9414f118..60586192 100644 --- a/src/widgets/DecompilerWidget.cpp +++ b/src/widgets/DecompilerWidget.cpp @@ -26,8 +26,7 @@ DecompilerWidget::DecompilerWidget(MainWindow *main) ui(new Ui::DecompilerWidget), decompilerBusy(false), seekFromCursor(false), - scrollerHorizontal(0), - scrollerVertical(0), + historyPos(0), previousFunctionAddr(RVA_INVALID), decompiledFunctionAddr(RVA_INVALID), code(Decompiler::makeWarning(tr("Choose an offset and refresh to get decompiled code")), @@ -311,13 +310,6 @@ QTextCursor DecompilerWidget::getCursorForAddress(RVA addr) void DecompilerWidget::decompilationFinished(RzAnnotatedCode *codeDecompiled) { - bool isDisplayReset = false; - if (previousFunctionAddr == decompiledFunctionAddr) { - scrollerHorizontal = ui->textEdit->horizontalScrollBar()->sliderPosition(); - scrollerVertical = ui->textEdit->verticalScrollBar()->sliderPosition(); - isDisplayReset = true; - } - ui->progressLabel->setVisible(false); ui->decompilerComboBox->setEnabled(decompilerSelectionEnabled); @@ -354,10 +346,8 @@ void DecompilerWidget::decompilationFinished(RzAnnotatedCode *codeDecompiled) } } - if (isDisplayReset) { - ui->textEdit->horizontalScrollBar()->setSliderPosition(scrollerHorizontal); - ui->textEdit->verticalScrollBar()->setSliderPosition(scrollerVertical); - } + ui->textEdit->horizontalScrollBar()->setSliderPosition(scrollHistory[historyPos].first); + ui->textEdit->verticalScrollBar()->setSliderPosition(scrollHistory[historyPos].second); } void DecompilerWidget::setAnnotationsAtCursor(size_t pos) @@ -416,11 +406,28 @@ void DecompilerWidget::cursorPositionChanged() updateSelection(); } -void DecompilerWidget::seekChanged() +void DecompilerWidget::seekChanged(RVA /* addr */, CutterCore::SeekHistoryType type) { if (seekFromCursor) { return; } + + if (!scrollHistory.empty()) { // History is empty upon init. + scrollHistory[historyPos] = { ui->textEdit->horizontalScrollBar()->sliderPosition(), + ui->textEdit->verticalScrollBar()->sliderPosition() }; + } + if (type == CutterCore::SeekHistoryType::New) { + // Erase previous history past this point. + if (scrollHistory.size() > historyPos + 1) { + scrollHistory.erase(scrollHistory.begin() + historyPos + 1, scrollHistory.end()); + } + scrollHistory.push_back({ 0, 0 }); + historyPos = scrollHistory.size() - 1; + } else if (type == CutterCore::SeekHistoryType::Undo) { + --historyPos; + } else if (type == CutterCore::SeekHistoryType::Redo) { + ++historyPos; + } RVA fcnAddr = Core()->getFunctionStart(seekable->getOffset()); if (fcnAddr == RVA_INVALID || fcnAddr != decompiledFunctionAddr) { doRefresh(); diff --git a/src/widgets/DecompilerWidget.h b/src/widgets/DecompilerWidget.h index 015525af..ed5c7f0c 100644 --- a/src/widgets/DecompilerWidget.h +++ b/src/widgets/DecompilerWidget.h @@ -53,7 +53,7 @@ private slots: * - Seek changed to an offset contained in the decompiled function. * - Auto-refresh is disabled. */ - void seekChanged(); + void seekChanged(RVA /* addr */, CutterCore::SeekHistoryType type); void decompilationFinished(RzAnnotatedCode *code); private: @@ -72,8 +72,8 @@ private: bool decompilerBusy; bool seekFromCursor; - int scrollerHorizontal; - int scrollerVertical; + int historyPos; + QVector> scrollHistory; RVA previousFunctionAddr; RVA decompiledFunctionAddr; std::unique_ptr code; From 3ccccae291f98b5947bae2829b5fbed48db17140 Mon Sep 17 00:00:00 2001 From: Giovanni <561184+wargio@users.noreply.github.com> Date: Sat, 13 May 2023 10:22:05 +0800 Subject: [PATCH 21/50] Fix broken English in UI (#3184) --- src/common/Configuration.cpp | 4 ++-- src/common/DisassemblyPreview.cpp | 7 ------- src/dialogs/InitialOptionsDialog.ui | 2 +- src/dialogs/NewFileDialog.cpp | 2 +- src/dialogs/preferences/AsmOptionsWidget.ui | 4 ++-- src/menus/DisassemblyContextMenu.cpp | 2 +- src/widgets/ColorThemeListView.cpp | 3 +-- src/widgets/HexWidget.cpp | 4 ++-- 8 files changed, 10 insertions(+), 18 deletions(-) diff --git a/src/common/Configuration.cpp b/src/common/Configuration.cpp index 7fc76715..12af053f 100644 --- a/src/common/Configuration.cpp +++ b/src/common/Configuration.cpp @@ -143,8 +143,8 @@ Configuration::Configuration() : QObject(), nativePalette(qApp->palette()) mPtr = this; if (!s.isWritable()) { QMessageBox::critical( - nullptr, tr("Critical!"), - tr("!!! Settings are not writable! Make sure you have a write access to \"%1\"") + nullptr, tr("Critical Error!"), + tr("Settings are not writable! Make sure you have a write access to \"%1\".") .arg(s.fileName())); } #ifdef CUTTER_ENABLE_KSYNTAXHIGHLIGHTING diff --git a/src/common/DisassemblyPreview.cpp b/src/common/DisassemblyPreview.cpp index e80e6eaa..4861c769 100644 --- a/src/common/DisassemblyPreview.cpp +++ b/src/common/DisassemblyPreview.cpp @@ -44,13 +44,6 @@ bool DisassemblyPreview::showDisasPreview(QWidget *parent, const QPoint &pointOf } RVA offsetTo = refs.at(0).to; // This is the offset we want to preview - - if (Q_UNLIKELY(offsetFrom != refs.at(0).from)) { - qWarning() << QObject::tr("offsetFrom (%1) differs from refs.at(0).from (%(2))") - .arg(offsetFrom) - .arg(refs.at(0).from); - } - /* * Only if the offset we point *to* is different from the one the cursor is currently * on *and* the former is a valid offset, we are allowed to get a preview of offsetTo diff --git a/src/dialogs/InitialOptionsDialog.ui b/src/dialogs/InitialOptionsDialog.ui index e3db9f83..38316893 100644 --- a/src/dialogs/InitialOptionsDialog.ui +++ b/src/dialogs/InitialOptionsDialog.ui @@ -323,7 +323,7 @@ - Auto Exp + Experimental Qt::AlignCenter diff --git a/src/dialogs/NewFileDialog.cpp b/src/dialogs/NewFileDialog.cpp index cde5fc85..58d0d391 100644 --- a/src/dialogs/NewFileDialog.cpp +++ b/src/dialogs/NewFileDialog.cpp @@ -287,7 +287,7 @@ void NewFileDialog::fillIOPluginsList() { ui->ioPlugin->clear(); ui->ioPlugin->addItem("file://"); - ui->ioPlugin->setItemData(0, tr("Open a file with no extra treatment."), Qt::ToolTipRole); + ui->ioPlugin->setItemData(0, tr("Open a file without additional options/settings."), Qt::ToolTipRole); int index = 1; QList ioPlugins = Core()->getRIOPluginDescriptions(); diff --git a/src/dialogs/preferences/AsmOptionsWidget.ui b/src/dialogs/preferences/AsmOptionsWidget.ui index e96421a4..f3f14952 100644 --- a/src/dialogs/preferences/AsmOptionsWidget.ui +++ b/src/dialogs/preferences/AsmOptionsWidget.ui @@ -142,7 +142,7 @@ - Tabs before assembly (asm.tabs.off): + The number of tabulate spaces after the offset (asm.tabs.off): Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse @@ -479,7 +479,7 @@ - Substitute variables (asm.sub.var) + Substitute variables in disassembly (asm.sub.var) diff --git a/src/menus/DisassemblyContextMenu.cpp b/src/menus/DisassemblyContextMenu.cpp index 40b33599..300283cc 100644 --- a/src/menus/DisassemblyContextMenu.cpp +++ b/src/menus/DisassemblyContextMenu.cpp @@ -168,7 +168,7 @@ QWidget *DisassemblyContextMenu::parentForDialog() void DisassemblyContextMenu::addSetBaseMenu() { - setBaseMenu = addMenu(tr("Set Immediate Base to...")); + setBaseMenu = addMenu(tr("Set base of immediate value to..")); initAction(&actionSetBaseBinary, tr("Binary")); setBaseMenu->addAction(&actionSetBaseBinary); diff --git a/src/widgets/ColorThemeListView.cpp b/src/widgets/ColorThemeListView.cpp index 4f56ba17..84df6d67 100644 --- a/src/widgets/ColorThemeListView.cpp +++ b/src/widgets/ColorThemeListView.cpp @@ -400,8 +400,7 @@ const QMap optionInfoMap__ = { { "fname", { QObject::tr("Color of names of functions"), QObject::tr("Function name") } }, { "floc", { QObject::tr("Color of function location"), QObject::tr("Function location") } }, { "fline", - { QObject::tr( - "Color of ascii line in left side that shows what opcodes are belong to function"), + { QObject::tr("Color of the line which shows which opcodes belongs to a function"), QObject::tr("Function line") } }, { "flag", { QObject::tr("Color of flags (similar to bookmarks for offset)"), QObject::tr("Flag") } }, diff --git a/src/widgets/HexWidget.cpp b/src/widgets/HexWidget.cpp index d933b944..c9d09a71 100644 --- a/src/widgets/HexWidget.cpp +++ b/src/widgets/HexWidget.cpp @@ -160,7 +160,7 @@ HexWidget::HexWidget(QWidget *parent) connect(actionWriteCString, &QAction::triggered, this, &HexWidget::w_writeCString); actionsWriteString.append(actionWriteCString); - QAction *actionWrite64 = new QAction(tr("Write De\\Encoded Base64 string"), this); + QAction *actionWrite64 = new QAction(tr("Write a decoded or encoded Base64 string"), this); connect(actionWrite64, &QAction::triggered, this, &HexWidget::w_write64); actionsWriteString.append(actionWrite64); @@ -1407,7 +1407,7 @@ void HexWidget::w_writeRandom() } bool ok = false; - int nbytes = QInputDialog::getInt(this, tr("Write random"), tr("Number of bytes:"), size, 1, + int nbytes = QInputDialog::getInt(this, tr("Write random bytes"), tr("Number of bytes:"), size, 1, 0x7FFFFFFF, 1, &ok); if (!ok) { return; From a5fa4103b24178189608e212b8f3e046b4f00b65 Mon Sep 17 00:00:00 2001 From: Lucas Hosseini Date: Sun, 14 May 2023 06:59:13 +0200 Subject: [PATCH 22/50] Get rid of stale jump arrows in disassembly widget. (#3175) This commit clears arrows from edited instructions, in order to avoid stale arrows to remain drawn. closes #3114 --- src/widgets/DisassemblyWidget.cpp | 17 ++++++++++++++++- src/widgets/DisassemblyWidget.h | 2 ++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/widgets/DisassemblyWidget.cpp b/src/widgets/DisassemblyWidget.cpp index ebbbfa0d..59adbaa7 100644 --- a/src/widgets/DisassemblyWidget.cpp +++ b/src/widgets/DisassemblyWidget.cpp @@ -132,7 +132,7 @@ DisassemblyWidget::DisassemblyWidget(MainWindow *main) connect(Core(), &CutterCore::functionRenamed, this, [this]() { refreshDisasm(); }); connect(Core(), SIGNAL(varsChanged()), this, SLOT(refreshDisasm())); connect(Core(), SIGNAL(asmOptionsChanged()), this, SLOT(refreshDisasm())); - connect(Core(), &CutterCore::instructionChanged, this, &DisassemblyWidget::refreshIfInRange); + connect(Core(), &CutterCore::instructionChanged, this, &DisassemblyWidget::instructionChanged); connect(Core(), &CutterCore::breakpointsChanged, this, &DisassemblyWidget::refreshIfInRange); connect(Core(), SIGNAL(refreshCodeViews()), this, SLOT(refreshDisasm())); @@ -226,6 +226,12 @@ void DisassemblyWidget::refreshIfInRange(RVA offset) } } +void DisassemblyWidget::instructionChanged(RVA offset) +{ + leftPanel->clearArrowFrom(offset); + refreshDisasm(); +} + void DisassemblyWidget::refreshDisasm(RVA offset) { if (!disasmRefresh->attemptRefresh(offset == RVA_INVALID ? nullptr : new RVA(offset))) { @@ -1006,3 +1012,12 @@ void DisassemblyLeftPanel::paintEvent(QPaintEvent *event) lastBeginOffset = lines.first().offset; } + +void DisassemblyLeftPanel::clearArrowFrom(RVA offset) +{ + auto it = std::find_if(arrows.begin(), arrows.end(), + [&](const Arrow &it) { return it.jmpFromOffset() == offset; }); + if (it != arrows.end()) { + arrows.erase(it); + } +} diff --git a/src/widgets/DisassemblyWidget.h b/src/widgets/DisassemblyWidget.h index 05a47579..8dadd4d4 100644 --- a/src/widgets/DisassemblyWidget.h +++ b/src/widgets/DisassemblyWidget.h @@ -53,6 +53,7 @@ public slots: protected slots: void on_seekChanged(RVA offset, CutterCore::SeekHistoryType type); void refreshIfInRange(RVA offset); + void instructionChanged(RVA offset); void refreshDisasm(RVA offset = RVA_INVALID); bool updateMaxLines(); @@ -156,6 +157,7 @@ public: DisassemblyLeftPanel(DisassemblyWidget *disas); void paintEvent(QPaintEvent *event) override; void wheelEvent(QWheelEvent *event) override; + void clearArrowFrom(RVA offset); private: DisassemblyWidget *disas; From 9bfe0c4e1df4c92762a6a24819aa5fd664013445 Mon Sep 17 00:00:00 2001 From: Anton Kochkov Date: Mon, 15 May 2023 20:37:45 +0800 Subject: [PATCH 23/50] Use Ubuntu 18.04 docker image (#3180) Co-authored-by: wargio --- .github/workflows/ci.yml | 263 ++++++++++++++++++++++++++++++--------- src/CMakeLists.txt | 2 +- 2 files changed, 202 insertions(+), 63 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 983d2b5b..a6d725ab 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,100 +14,164 @@ on: - dev - stable +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: - build: + build-linux: name: ${{ matrix.name }} - runs-on: ${{ matrix.os }} + runs-on: ubuntu-latest + container: + image: ${{ matrix.image }} + options: --privileged strategy: matrix: name: [ linux-x86_64, linux-x86_64-system-deps, linux-x86_64-qt6-system-deps, - macos-x86_64, - windows-x86_64, tarball ] include: - - name: windows-x86_64 - os: windows-2019 - package: true - system-deps: false - python-version: 3.7.x - name: linux-x86_64-system-deps # ensure that Cutter can be built at least in basic config on Ubuntu 18.04 using sytem libraries - os: ubuntu-18.04 + image: ubuntu:18.04 python-version: 3.6.x system-deps: true + package: false + tarball: false cc-override: '/usr/bin/gcc-7' cxx-override: '/usr/bin/g++-7' - name: linux-x86_64-qt6-system-deps # ensure that Cutter can be built at least in basic config on Ubuntu 22.04 using sytem libraries - os: ubuntu-22.04 + image: ubuntu:22.04 python-version: 3.10.x system-deps: true + package: false + tarball: false cc-override: '/usr/bin/gcc-12' cxx-override: '/usr/bin/g++-12' - name: linux-x86_64 - os: ubuntu-18.04 - python-version: 3.7.x - system-deps: false - package: true - cc-override: default - cxx-override: default - - name: macos-x86_64 - os: macos-latest - python-version: 3.7.x + image: ubuntu:18.04 + python-version: 3.6.x system-deps: false package: true + tarball: false cc-override: default cxx-override: default - name: tarball - python-version: 3.7.x - os: ubuntu-20.04 + python-version: 3.6.x + image: ubuntu:20.04 system-deps: false + package: false tarball: true # Prevent one job from pausing the rest fail-fast: false steps: + - name: set timezone + run: | + # Fix timezone on ubuntu to prevent user input request during the apt-get phase. + export TZ=UTC + ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone + + - name: install latest git and cmake + shell: bash + run: | + set -e + apt-get -y update + echo "Using image: ${{ matrix.image }}" + + export GIT_VERSION="git-2.36.1" + export CMAKE_VERSION="3.25.3" + + apt-get -y install wget libcurl4-gnutls-dev libexpat1-dev gettext libz-dev libssl-dev build-essential + + wget "https://www.kernel.org/pub/software/scm/git/$GIT_VERSION.tar.gz" + tar -zxf "$GIT_VERSION.tar.gz" + + # build. + make -C "$GIT_VERSION" prefix=/usr install -j > "$GIT_VERSION/build.log" + + # ensure git is installed. + git version + + wget "https://github.com/Kitware/CMake/releases/download/v$CMAKE_VERSION/cmake-$CMAKE_VERSION-linux-x86_64.sh" + bash ./cmake-$CMAKE_VERSION-linux-x86_64.sh --skip-license --prefix=/usr + + # ensure cmake is installed. + cmake --version + + # cleanup dev environment. + rm -rf "$GIT_VERSION.tar.gz" "$GIT_VERSION" cmake-$CMAKE_VERSION-linux-x86_64.sh + unset CMAKE_VERSION + unset GIT_VERSION + - uses: actions/checkout@v3 with: submodules: recursive persist-credentials: false - - name: apt dependencies - if: contains(matrix.os, 'ubuntu') + + - name: apt cutter dependencies + shell: bash run: | - sudo apt-get update - sudo apt-get install libgraphviz-dev mesa-common-dev libxkbcommon-x11-dev ninja-build - if [[ "${{ matrix.os }}" = "ubuntu-18.04" || "${{ matrix.os }}" = "ubuntu-20.04" ]] - then + # install needed packages + apt-get -y install libgraphviz-dev \ + mesa-common-dev \ + libxkbcommon-x11-dev \ + ninja-build \ + python3-pip \ + curl \ + libpcre2-dev \ + libfuse2 \ + pkg-config + + if [ "${{ matrix.image }}" = "ubuntu:18.04" ]; then # install additional packages needed for appimage - sudo apt-get install libxcb1-dev libxkbcommon-dev libxcb-*-dev libegl1 libclang-8-dev llvm-8 + apt-get -y install gcc-7 \ + libglu1-mesa-dev \ + freeglut3-dev \ + mesa-common-dev + fi - if [[ "${{ matrix.os }}" = "ubuntu-18.04" && "${{ matrix.system-deps }}" = "true" ]] - then - sudo apt-get install qt5-default libqt5svg5-dev qttools5-dev qttools5-dev-tools + if [ "${{ matrix.image }}" = "ubuntu:18.04" ] || [ "${{ matrix.image }}" = "ubuntu:20.04" ]; then + # install additional packages needed for appimage + apt-get -y install libxcb1-dev \ + libxkbcommon-dev \ + libxcb-*-dev \ + libegl1 \ + libclang-8-dev \ + llvm-8 + ln -s /usr/bin/llvm-config-8 /usr/bin/llvm-config fi - if [[ "${{ matrix.os }}" = "ubuntu-22.04" ]] - then - sudo apt-get install libclang-12-dev llvm-12 qt6-base-dev qt6-tools-dev \ - qt6-tools-dev-tools libqt6svg6-dev libqt6core5compat6-dev libqt6svgwidgets6 qt6-l10n-tools + if [ "${{ matrix.image }}" = "ubuntu:18.04" ] && [ "${{ matrix.system-deps }}" = "true" ]; then + apt-get -y install qt5-default \ + libqt5svg5-dev \ + qttools5-dev \ + qttools5-dev-tools + fi + if [ "${{ matrix.image }}" = "ubuntu:22.04" ]; then + apt-get -y install libclang-12-dev \ + llvm-12 \ + qt6-base-dev \ + qt6-tools-dev \ + qt6-tools-dev-tools \ + libqt6svg6-dev \ + libqt6core5compat6-dev \ + libqt6svgwidgets6 \ + qt6-l10n-tools \ + gcc-12 \ + g++-12 fi - uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - - name: homebrew dependencies - if: contains(matrix.os, 'macos') - run: | - cd scripts - rm /usr/local/bin/2to3* # symlink to some kind of existing python2.7 installation conflicts with brew python3 which gets installed as indirect dependency - brew bundle - name: py dependencies run: | - python3 -m pip install -U pip==21.3.1 - pip install meson==0.61.5 # https://github.com/rizinorg/cutter/runs/7170222817?check_suite_focus=true + # https://github.com/rizinorg/cutter/runs/7170222817?check_suite_focus=true + python3 -m pip install meson==0.61.5 - name: Prepare package id shell: bash run: | - if [[ "${{ startsWith(github.event.ref, 'refs/tags')}}" = "true" ]] + if [ "${{ startsWith(github.event.ref, 'refs/tags')}}" = "true" ] then PACKAGE_ID="${{ github.event.ref }}" else @@ -116,17 +180,17 @@ jobs: PACKAGE_ID=${PACKAGE_ID##refs/tags/} echo PACKAGE_ID=$PACKAGE_ID >> $GITHUB_ENV - name: cmake ubuntu - if: contains(matrix.os, 'ubuntu') + shell: bash run: | - if [[ "${{ matrix.system-deps }}" = "false" ]] + if [ "${{ matrix.system-deps }}" = "false" ] then scripts/fetch_deps.sh - source cutter-deps/env.sh + . cutter-deps/env.sh export PKG_CONFIG_PATH="$CUTTER_DEPS_PYTHON_PREFIX/lib/pkgconfig:${PKG_CONFIG_PATH:-}" export LD_LIBRARY_PATH="`llvm-config --libdir`:$LD_LIBRARY_PATH" fi - set -euo pipefail #TODO: move to top once cutter-deps doesn't fail - if [[ "${{ matrix.cc-override }}" != "default" ]] + set -e #TODO: move to top once cutter-deps doesn't fail + if [ "${{ matrix.cc-override }}" != "default" ] then export CC="${{matrix.cc-override}}" export CXX="${{matrix.cxx-override}}" @@ -134,8 +198,7 @@ jobs: mkdir build cd build - cmake --version - if [[ "${{ matrix.system-deps }}" = "false" ]] + if [ "${{ matrix.system-deps }}" = "false" ] then cmake \ -G Ninja \ @@ -149,6 +212,7 @@ jobs: -DCUTTER_USE_BUNDLED_RIZIN=ON \ -DCUTTER_APPIMAGE_BUILD=ON \ -DCUTTER_ENABLE_PACKAGING=ON \ + -DCUTTER_ENABLE_KSYNTAXHIGHLIGHTING=OFF \ -DCUTTER_ENABLE_SIGDB=ON \ -DCUTTER_ENABLE_DEPENDENCY_DOWNLOADS=ON \ -DCUTTER_PACKAGE_RZ_GHIDRA=ON \ @@ -159,7 +223,7 @@ jobs: -DCMAKE_INSTALL_PREFIX=appdir/usr \ -DCMAKE_INSTALL_RPATH_USE_LINK_PATH=ON \ .. - elif [[ "${{ matrix.os }}" = "ubuntu-22.04" ]] + elif [ "${{ matrix.image }}" = "ubuntu:22.04" ] then cmake \ -G Ninja \ @@ -175,7 +239,7 @@ jobs: .. fi ninja - if [[ "${{ matrix.package || false }}" = "true" ]] + if [ "${{ matrix.package }}" = "true" ] then export CUTTER_VERSION=$(python ../scripts/get_version.py) export VERSION=$CUTTER_VERSION @@ -200,6 +264,89 @@ jobs: echo PACKAGE_PATH=build/$APPIMAGE_FILE >> $GITHUB_ENV echo UPLOAD_ASSET_TYPE=application/x-executable >> $GITHUB_ENV fi + - name: Create tarball + if: matrix.tarball + shell: bash + run: | + scripts/tarball.sh "Cutter-${PACKAGE_ID}" + echo PACKAGE_NAME=Cutter-${PACKAGE_ID}-src.tar.gz >> $GITHUB_ENV + echo PACKAGE_PATH=Cutter-${PACKAGE_ID}-src.tar.gz >> $GITHUB_ENV + echo UPLOAD_ASSET_TYPE=application/gzip >> $GITHUB_ENV + - uses: actions/upload-artifact@v3 + if: env.PACKAGE_NAME != null + with: + name: ${{ env.PACKAGE_NAME }} + path: ${{ env.PACKAGE_PATH }} + - name: Get release + if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags') + id: get_release + uses: rizinorg/gha-get-release@c8074dd5d13ddd0a194d8c9205a1466973c7dc0d + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Upload release assets + if: steps.get_release.outputs.upload_url != null && env.PACKAGE_NAME != null + uses: actions/upload-release-asset@v1.0.2 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.get_release.outputs.upload_url }} + asset_path: ${{ env.PACKAGE_PATH }} + asset_name: ${{ env.PACKAGE_NAME }} + asset_content_type: ${{ env.UPLOAD_ASSET_TYPE }} + + build: + name: ${{ matrix.name }} + runs-on: ${{ matrix.os }} + strategy: + matrix: + name: [ + macos-x86_64, + windows-x86_64, + ] + include: + - name: windows-x86_64 + os: windows-2019 + package: true + system-deps: false + python-version: 3.7.x + - name: macos-x86_64 + os: macos-latest + python-version: 3.7.x + system-deps: false + package: true + cc-override: default + cxx-override: default + # Prevent one job from pausing the rest + fail-fast: false + steps: + - uses: actions/checkout@v3 + with: + submodules: recursive + persist-credentials: false + - uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + - name: homebrew dependencies + if: contains(matrix.os, 'macos') + run: | + cd scripts + rm /usr/local/bin/2to3* # symlink to some kind of existing python2.7 installation conflicts with brew python3 which gets installed as indirect dependency + brew bundle + - name: py dependencies + run: | + python3 -m pip install -U pip==21.3.1 + pip install meson==0.61.5 # https://github.com/rizinorg/cutter/runs/7170222817?check_suite_focus=true + - name: Prepare package id + shell: bash + run: | + if [[ "${{ startsWith(github.event.ref, 'refs/tags')}}" = "true" ]] + then + PACKAGE_ID="${{ github.event.ref }}" + else + PACKAGE_ID="git-`date "+%Y-%m-%d"`-${{ format('{0}', github.sha) }}" + fi + PACKAGE_ID=${PACKAGE_ID##refs/tags/} + echo PACKAGE_ID=$PACKAGE_ID >> $GITHUB_ENV - name: cmake macos shell: bash if: contains(matrix.os, 'macos') @@ -280,14 +427,6 @@ jobs: echo PACKAGE_NAME=%PACKAGE_NAME%.zip >> %GITHUB_ENV% echo PACKAGE_PATH=build/%PACKAGE_NAME%.zip >> %GITHUB_ENV% echo UPLOAD_ASSET_TYPE=application/zip >> %GITHUB_ENV% - - name: Create tarball - if: matrix.tarball - shell: bash - run: | - scripts/tarball.sh "Cutter-${PACKAGE_ID}" - echo PACKAGE_NAME=Cutter-${PACKAGE_ID}-src.tar.gz >> $GITHUB_ENV - echo PACKAGE_PATH=Cutter-${PACKAGE_ID}-src.tar.gz >> $GITHUB_ENV - echo UPLOAD_ASSET_TYPE=application/gzip >> $GITHUB_ENV - uses: actions/upload-artifact@v3 if: env.PACKAGE_NAME != null with: diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index dd8f524e..19018beb 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -517,7 +517,7 @@ if(CUTTER_ENABLE_PYTHON) endif() configure_file("${BINDINGS_SRC_DIR}/bindings.txt.in" "${BINDINGS_BUILD_DIR}/bindings.txt") - add_compile_definitions(WIN32_LEAN_AND_MEAN) + add_definitions(-DWIN32_LEAN_AND_MEAN) endif() endif() From 48c8e0c44d4d2a563087171384df7efe8c0b86dc Mon Sep 17 00:00:00 2001 From: Anton Kochkov Date: Sun, 29 May 2022 16:41:54 +0800 Subject: [PATCH 24/50] Allow building Python bindings with Qt6 --- CMakeLists.txt | 29 +++++++++++++++++++++-------- docs/source/building.rst | 2 +- src/CMakeLists.txt | 14 ++++++++++++-- 3 files changed, 34 insertions(+), 11 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 60cd6c53..8145e362 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,7 +14,7 @@ option(CUTTER_USE_BUNDLED_RIZIN "Use rizin from ./rizin submodule instead of sea option(CUTTER_USE_ADDITIONAL_RIZIN_PATHS "Search rizin in additional paths which are not part of default system library paths.\ Disable this option if you are linking against rizin pacakged as proper system library or in a custom path and additional are paths causing problems." ON) option(CUTTER_ENABLE_PYTHON "Enable Python integration. Requires Python >= ${CUTTER_PYTHON_MIN}." OFF) -option(CUTTER_ENABLE_PYTHON_BINDINGS "Enable generating Python bindings with Shiboken2. Unused if CUTTER_ENABLE_PYTHON=OFF." OFF) +option(CUTTER_ENABLE_PYTHON_BINDINGS "Enable generating Python bindings with Shiboken. Unused if CUTTER_ENABLE_PYTHON=OFF." OFF) option(CUTTER_APPIMAGE_BUILD "Enable Appimage specific changes. Doesn't cause building of Appimage itself." OFF) tri_option(CUTTER_ENABLE_KSYNTAXHIGHLIGHTING "Use KSyntaxHighlighting" AUTO) tri_option(CUTTER_ENABLE_GRAPHVIZ "Enable use of graphviz for graph layout" AUTO) @@ -100,16 +100,29 @@ if(CUTTER_ENABLE_PYTHON) add_definitions(-DCUTTER_ENABLE_PYTHON) if(CUTTER_ENABLE_PYTHON_BINDINGS) - # 5.12.3 => 5.12 - if("${Qt5_VERSION}" MATCHES "^([0-9]+\\.[0-9]+)\\.[0-9]+") - set(Shiboken2_VERSION_REQUIRED "${CMAKE_MATCH_1}") + if (CUTTER_QT6) + # 6.12.3 => 6.12 + if("${Qt6_VERSION}" MATCHES "^([0-9]+\\.[0-9]+)\\.[0-9]+") + set(Shiboken6_VERSION_REQUIRED "${CMAKE_MATCH_1}") + else() + message(FATAL_ERROR "Failed to recognize Qt version") + endif() + find_package(Shiboken6 "${Shiboken6_VERSION_REQUIRED}" REQUIRED) + find_package(Shiboken6Tools "${Shiboken6_VERSION_REQUIRED}" REQUIRED) + find_package(PySide6 "${Shiboken6_VERSION_REQUIRED}" REQUIRED) + get_target_property(PYSIDE_INCLUDE_DIR PySide6::pyside6 INTERFACE_INCLUDE_DIRECTORIES) else() - message(FATAL_ERROR "Failed to recognize Qt version") + # 5.12.3 => 5.12 + if("${Qt5_VERSION}" MATCHES "^([0-9]+\\.[0-9]+)\\.[0-9]+") + set(Shiboken2_VERSION_REQUIRED "${CMAKE_MATCH_1}") + else() + message(FATAL_ERROR "Failed to recognize Qt version") + endif() + find_package(Shiboken2 "${Shiboken2_VERSION_REQUIRED}" REQUIRED) + find_package(PySide2 "${Shiboken2_VERSION_REQUIRED}" REQUIRED) + get_target_property(PYSIDE_INCLUDE_DIR PySide2::pyside2 INTERFACE_INCLUDE_DIRECTORIES) endif() - find_package(Shiboken2 "${Shiboken2_VERSION_REQUIRED}" REQUIRED) - find_package(PySide2 "${Shiboken2_VERSION_REQUIRED}" REQUIRED) - get_target_property(PYSIDE_INCLUDE_DIR PySide2::pyside2 INTERFACE_INCLUDE_DIRECTORIES) list(GET PYSIDE_INCLUDE_DIR 0 PYSIDE_INCLUDE_DIR) include_directories(${PYSIDE_INCLUDE_DIR} ${PYSIDE_INCLUDE_DIR}/QtCore diff --git a/docs/source/building.rst b/docs/source/building.rst index 924e638e..762cdd31 100644 --- a/docs/source/building.rst +++ b/docs/source/building.rst @@ -247,7 +247,7 @@ Note that there are some major building options available: * ``CUTTER_USE_BUNDLED_RIZIN`` automatically compile Rizin from submodule (Enabled by default). * ``CUTTER_ENABLE_PYTHON`` compile with Python support. -* ``CUTTER_ENABLE_PYTHON_BINDINGS`` automatically generate Python Bindings with Shiboken2, required for Python plugins! +* ``CUTTER_ENABLE_PYTHON_BINDINGS`` automatically generate Python Bindings with Shiboken, required for Python plugins! * ``CUTTER_ENABLE_KSYNTAXHIGHLIGHTING`` use KSyntaxHighlighting for code highlighting. * ``CUTTER_ENABLE_GRAPHVIZ`` enable Graphviz for graph layouts. * ``CUTTER_EXTRA_PLUGIN_DIRS`` List of addition plugin locations. Useful when preparing package for Linux distros that have strict package layout rules. diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 19018beb..404f5d14 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -408,8 +408,14 @@ if(CUTTER_ENABLE_PYTHON_BINDINGS) list(APPEND SHIBOKEN_OPTIONS --avoid-protected-hack) endif() + if (CUTTER_QT6) + set(SHIBOKEN_COMMAND Shiboken6::shiboken6) + else() + set(SHIBOKEN_COMMAND Shiboken2::shiboken2) + endif() + 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" IMPLICIT_DEPENDS CXX "${CMAKE_CURRENT_SOURCE_DIR}/bindings/bindings.h" COMMENT "Generating Python bindings with shiboken2") @@ -487,7 +493,11 @@ if(CUTTER_ENABLE_PYTHON) endif() target_link_libraries(Cutter PRIVATE ${PYTHON_LIBRARIES}) if(CUTTER_ENABLE_PYTHON_BINDINGS) - target_link_libraries(Cutter PRIVATE Shiboken2::libshiboken PySide2::pyside2) + if (CUTTER_QT6) + target_link_libraries(Cutter PRIVATE Shiboken6::libshiboken PySide6::pyside6) + else() + target_link_libraries(Cutter PRIVATE Shiboken2::libshiboken PySide2::pyside2) + endif() get_target_property(RAW_BINDINGS_INCLUDE_DIRS Cutter INCLUDE_DIRECTORIES) if(NOT CUTTER_USE_BUNDLED_RIZIN) From e78a1fe9ed7292abbdbd49db09250b290bf4565c Mon Sep 17 00:00:00 2001 From: Anton Kochkov Date: Mon, 13 Mar 2023 14:45:54 +0800 Subject: [PATCH 25/50] Add FindPySide6 CMake module --- cmake/FindPySide6.cmake | 68 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 cmake/FindPySide6.cmake diff --git a/cmake/FindPySide6.cmake b/cmake/FindPySide6.cmake new file mode 100644 index 00000000..d072918c --- /dev/null +++ b/cmake/FindPySide6.cmake @@ -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) From 553a8eef5c16198c8427163b9324fe227ddaa374 Mon Sep 17 00:00:00 2001 From: Anton Kochkov Date: Mon, 13 Feb 2023 17:41:48 +0800 Subject: [PATCH 26/50] Specify include paths for Shiboken --- src/CMakeLists.txt | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 404f5d14..1533719e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -386,6 +386,8 @@ set(QRC_FILES themes/lightstyle/light.qrc ) +set(CUTTER_INCLUDE_DIRECTORIES core widgets common plugins menus .) + if (CUTTER_ENABLE_PYTHON) list(APPEND SOURCES common/QtResImporter.cpp common/PythonManager.cpp common/PythonAPI.cpp) list(APPEND HEADER_FILES common/QtResImporter.h common/PythonManager.h common/PythonAPI.h) @@ -403,7 +405,33 @@ if(CUTTER_ENABLE_PYTHON_BINDINGS) include_directories("${BINDINGS_BUILD_DIR}/CutterBindings") + set(SHIBOKEN_INCLUDE_DIRS "") + if(APPLE AND _qt6Core_install_prefix) + list(APPEND BINDINGS_INCLUDE_DIRS "${_qt6Core_install_prefix}/include") + list(APPEND BINDINGS_INCLUDE_DIRS "${_qt6Core_install_prefix}/include/QtCore") + list(APPEND BINDINGS_INCLUDE_DIRS "${_qt6Core_install_prefix}/include/QtGui") + list(APPEND BINDINGS_INCLUDE_DIRS "${_qt6Core_install_prefix}/include/QtWidgets") + endif() + + if (CUTTER_QT6) + list(APPEND SHIBOKEN_INCLUDE_DIRS ${Qt6Core_INCLUDE_DIRS} ${Qt6Widgets_INCLUDE_DIRS} ${Qt6Gui_INCLUDE_DIRS}) + else() + list(APPEND SHIBOKEN_INCLUDE_DIRS ${Qt5Core_INCLUDE_DIRS} ${Qt5Widgets_INCLUDE_DIRS} ${Qt5Gui_INCLUDE_DIRS}) + endif() + + foreach(_dir ${CUTTER_INCLUDE_DIRECTORIES}) + list(APPEND SHIBOKEN_INCLUDE_DIRS + $ + $ + ) + endforeach() + list(APPEND SHIBOKEN_INCLUDE_DIRS ${Rizin_INCLUDE_DIRS}) + if (NOT WIN32) + string(REPLACE ";" ":" SHIBOKEN_INCLUDE_DIRS "${SHIBOKEN_INCLUDE_DIRS}") + endif() + set(SHIBOKEN_OPTIONS) + list(APPEND SHIBOKEN_OPTIONS --include-paths="${SHIBOKEN_INCLUDE_DIRS}") if (WIN32) list(APPEND SHIBOKEN_OPTIONS --avoid-protected-hack) endif() @@ -458,7 +486,6 @@ target_compile_definitions(Cutter PRIVATE CUTTER_SOURCE_BUILD) # Set Cutter as the startup project in Visual Studio set_property(DIRECTORY ${PROJECT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT Cutter) -set(CUTTER_INCLUDE_DIRECTORIES core widgets common plugins menus .) foreach(_dir ${CUTTER_INCLUDE_DIRECTORIES}) target_include_directories(Cutter PUBLIC $ @@ -519,7 +546,11 @@ if(CUTTER_ENABLE_PYTHON) list(APPEND BINDINGS_INCLUDE_DIRS "${_qt5Core_install_prefix}/include/QtGui") list(APPEND BINDINGS_INCLUDE_DIRS "${_qt5Core_install_prefix}/include/QtWidgets") endif() - list(APPEND BINDINGS_INCLUDE_DIRS ${Qt5Core_INCLUDE_DIRS} ${Qt5Widgets_INCLUDE_DIRS} ${Qt5Gui_INCLUDE_DIRS}) + if (CUTTER_QT6) + list(APPEND BINDINGS_INCLUDE_DIRS ${Qt6Core_INCLUDE_DIRS} ${Qt6Widgets_INCLUDE_DIRS} ${Qt6Gui_INCLUDE_DIRS}) + else() + list(APPEND BINDINGS_INCLUDE_DIRS ${Qt5Core_INCLUDE_DIRS} ${Qt5Widgets_INCLUDE_DIRS} ${Qt5Gui_INCLUDE_DIRS}) + endif() list(APPEND BINDINGS_INCLUDE_DIRS ${Rizin_INCLUDE_DIRS}) list(APPEND BINDINGS_INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}") if (NOT WIN32) From 93f88263a0b606579b74d3aef798998804e4d3c5 Mon Sep 17 00:00:00 2001 From: Anton Kochkov Date: Thu, 16 Feb 2023 12:42:32 +0800 Subject: [PATCH 27/50] Address Python and PySide API changes --- CMakeLists.txt | 2 ++ src/common/PythonManager.cpp | 7 ++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8145e362..d567a49e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -111,6 +111,8 @@ if(CUTTER_ENABLE_PYTHON) find_package(Shiboken6Tools "${Shiboken6_VERSION_REQUIRED}" REQUIRED) find_package(PySide6 "${Shiboken6_VERSION_REQUIRED}" REQUIRED) get_target_property(PYSIDE_INCLUDE_DIR PySide6::pyside6 INTERFACE_INCLUDE_DIRECTORIES) + # Check the presence of "pysidecleanup.h" + CHECK_INCLUDE_FILE_CXX("pysidecleanup.h" HAVE_PYSIDECLEANUP) else() # 5.12.3 => 5.12 if("${Qt5_VERSION}" MATCHES "^([0-9]+\\.[0-9]+)\\.[0-9]+") diff --git a/src/common/PythonManager.cpp b/src/common/PythonManager.cpp index 5b2ecd16..8e38a24b 100644 --- a/src/common/PythonManager.cpp +++ b/src/common/PythonManager.cpp @@ -13,6 +13,10 @@ #ifdef CUTTER_ENABLE_PYTHON_BINDINGS # include # include +#ifdef HAVE_PYSIDECLEANUP + // This header is introduced in PySide 6 +# include +#endif # include #endif @@ -72,6 +76,7 @@ void PythonManager::initialize() PyImport_AppendInittab("CutterBindings", &PyInit_CutterBindings); #endif Py_Initialize(); + // This function is deprecated does nothing starting from Python 3.9 PyEval_InitThreads(); pyThreadStateCounter = 1; // we have the thread now => 1 @@ -159,7 +164,7 @@ void PythonManager::addPythonPath(char *path) if (!append) { return; } - PyEval_CallFunction(append, "(s)", path); + PyObject_CallFunction(append, "(s)", path); saveThread(); } From aa40f6945823d08e171f5e16159d895e8cbd5ccc Mon Sep 17 00:00:00 2001 From: Anton Kochkov Date: Thu, 16 Feb 2023 12:43:03 +0800 Subject: [PATCH 28/50] Specify PySide version in bindings template --- CMakeLists.txt | 4 ++++ src/CMakeLists.txt | 18 ++++++++++-------- src/bindings/bindings.txt.in | 4 ++-- src/bindings/{bindings.xml => bindings.xml.in} | 2 +- src/bindings/src_list.py | 2 +- 5 files changed, 18 insertions(+), 12 deletions(-) rename src/bindings/{bindings.xml => bindings.xml.in} (97%) diff --git a/CMakeLists.txt b/CMakeLists.txt index d567a49e..fa5bbbe7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -110,9 +110,13 @@ if(CUTTER_ENABLE_PYTHON) 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_DIR PySide6::pyside6 INTERFACE_INCLUDE_DIRECTORIES) # Check the presence of "pysidecleanup.h" + include(CheckIncludeFileCXX) + set(CMAKE_REQUIRED_INCLUDES "${PYSIDE_INCLUDE_DIR};${LIBSHIBOKEN_INCLUDE_DIRS}") CHECK_INCLUDE_FILE_CXX("pysidecleanup.h" HAVE_PYSIDECLEANUP) + add_compile_definitions("HAVE_PYSIDECLEANUP=${HAVE_PYSIDECLEANUP}") else() # 5.12.3 => 5.12 if("${Qt5_VERSION}" MATCHES "^([0-9]+\\.[0-9]+)\\.[0-9]+") diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1533719e..009b2962 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -394,10 +394,18 @@ if (CUTTER_ENABLE_PYTHON) endif() if(CUTTER_ENABLE_PYTHON_BINDINGS) + if (CUTTER_QT6) + set(PYSIDE_NAME PySide6) + set(SHIBOKEN_COMMAND Shiboken6::shiboken6) + else() + set(PYSIDE_NAME PySide2) + set(SHIBOKEN_COMMAND Shiboken2::shiboken2) + endif() + set(BINDINGS_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/bindings") set(BINDINGS_BUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}/bindings") - configure_file("${BINDINGS_SRC_DIR}/bindings.xml" "${BINDINGS_BUILD_DIR}/bindings.xml" COPYONLY) # trigger reconfigure if file changes + configure_file("${BINDINGS_SRC_DIR}/bindings.xml.in" "${BINDINGS_BUILD_DIR}/bindings.xml") execute_process(COMMAND "${PYTHON_EXECUTABLE}" "${BINDINGS_SRC_DIR}/src_list.py" cmake "${BINDINGS_BUILD_DIR}" OUTPUT_VARIABLE BINDINGS_SOURCE) @@ -436,15 +444,9 @@ if(CUTTER_ENABLE_PYTHON_BINDINGS) list(APPEND SHIBOKEN_OPTIONS --avoid-protected-hack) endif() - if (CUTTER_QT6) - set(SHIBOKEN_COMMAND Shiboken6::shiboken6) - else() - set(SHIBOKEN_COMMAND Shiboken2::shiboken2) - endif() - add_custom_command(OUTPUT ${BINDINGS_SOURCE} 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" COMMENT "Generating Python bindings with shiboken2") else() diff --git a/src/bindings/bindings.txt.in b/src/bindings/bindings.txt.in index dd478889..5eb103ee 100644 --- a/src/bindings/bindings.txt.in +++ b/src/bindings/bindings.txt.in @@ -3,7 +3,7 @@ generator-set = shiboken header-file = ${BINDINGS_SRC_DIR}/bindings.h -typesystem-file = ${BINDINGS_SRC_DIR}/bindings.xml +typesystem-file = ${BINDINGS_BUILD_DIR}/bindings.xml output-directory = ${BINDINGS_BUILD_DIR} @@ -14,4 +14,4 @@ typesystem-paths = ${PYSIDE_TYPESYSTEMS} enable-parent-ctor-heuristic enable-pyside-extensions enable-return-value-heuristic -use-isnull-as-nb_nonzero \ No newline at end of file +use-isnull-as-nb_nonzero diff --git a/src/bindings/bindings.xml b/src/bindings/bindings.xml.in similarity index 97% rename from src/bindings/bindings.xml rename to src/bindings/bindings.xml.in index 36386558..0fa194a9 100644 --- a/src/bindings/bindings.xml +++ b/src/bindings/bindings.xml.in @@ -30,7 +30,7 @@ PyErr_Print(); return QString(); } - PythonToCppFunc pythonToCpp = Shiboken::Conversions::isPythonToCppConvertible(SbkPySide2_QtCoreTypeConverters[SBK_QSTRING_IDX], pyResult); + PythonToCppFunc pythonToCpp = Shiboken::Conversions::isPythonToCppConvertible(Sbk${PYSIDE_NAME}_QtCoreTypeConverters[SBK_QSTRING_IDX], pyResult); if (!pythonToCpp) { Shiboken::warning(PyExc_RuntimeWarning, 2, "Invalid return value for plugin metadata VAR_NAME, expected %s, got %s.", "QString", Py_TYPE(pyResult)->tp_name); return ::QString(); diff --git a/src/bindings/src_list.py b/src/bindings/src_list.py index 3175b39e..69e06f5a 100755 --- a/src/bindings/src_list.py +++ b/src/bindings/src_list.py @@ -9,7 +9,7 @@ script_path = os.path.dirname(os.path.realpath(__file__)) def get_cpp_files_gen(args, include_package=True): - ts_tree = et.parse(os.path.join(script_path, "bindings.xml")) + ts_tree = et.parse(os.path.join(script_path, "bindings.xml.in")) ts_root = ts_tree.getroot() package = ts_root.attrib["package"] From da7fc439db8b76ec10a3af460d94fb422b014ddb Mon Sep 17 00:00:00 2001 From: Anton Kochkov Date: Fri, 17 Feb 2023 21:50:37 +0800 Subject: [PATCH 29/50] Rename PYSIDE_INCLUDE_DIR to PYSIDE_INCLUDE_DIRS --- CMakeLists.txt | 17 +++++++++-------- cmake/FindPySide2.cmake | 2 +- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fa5bbbe7..38f18f17 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -111,10 +111,10 @@ if(CUTTER_ENABLE_PYTHON) 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_DIR PySide6::pyside6 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_DIR};${LIBSHIBOKEN_INCLUDE_DIRS}") + 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() @@ -126,14 +126,15 @@ if(CUTTER_ENABLE_PYTHON) endif() find_package(Shiboken2 "${Shiboken2_VERSION_REQUIRED}" REQUIRED) find_package(PySide2 "${Shiboken2_VERSION_REQUIRED}" REQUIRED) - get_target_property(PYSIDE_INCLUDE_DIR PySide2::pyside2 INTERFACE_INCLUDE_DIRECTORIES) + get_target_property(PYSIDE_INCLUDE_DIRS PySide2::pyside2 INTERFACE_INCLUDE_DIRECTORIES) endif() - list(GET PYSIDE_INCLUDE_DIR 0 PYSIDE_INCLUDE_DIR) - include_directories(${PYSIDE_INCLUDE_DIR} - ${PYSIDE_INCLUDE_DIR}/QtCore - ${PYSIDE_INCLUDE_DIR}/QtGui - ${PYSIDE_INCLUDE_DIR}/QtWidgets) + foreach(_dir IN LISTS PYSIDE_INCLUDE_DIRS) + include_directories(${_dir} + ${_dir}/QtCore + ${_dir}/QtGui + ${_dir}/QtWidgets) + endforeach() add_definitions(-DCUTTER_ENABLE_PYTHON_BINDINGS) endif() diff --git a/cmake/FindPySide2.cmake b/cmake/FindPySide2.cmake index 1b6faf40..d4843f97 100644 --- a/cmake/FindPySide2.cmake +++ b/cmake/FindPySide2.cmake @@ -65,4 +65,4 @@ else() INTERFACE_LINK_LIBRARIES "${PYSIDE_LIBRARY}") endif() -mark_as_advanced(PYSIDE_INCLUDE_DIR PYSIDE_LIBRARY PYSIDE_BINARY) \ No newline at end of file +mark_as_advanced(PYSIDE_INCLUDE_DIR PYSIDE_LIBRARY PYSIDE_BINARY) From 44917603fa48412a0471d2bba9888367ff83bf24 Mon Sep 17 00:00:00 2001 From: wargio Date: Thu, 11 May 2023 12:59:02 +0800 Subject: [PATCH 30/50] Disable KSyntaxHighlighting on Qt6 --- CMakeLists.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 38f18f17..771bddf1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -140,7 +140,7 @@ if(CUTTER_ENABLE_PYTHON) endif() endif() -if(CUTTER_ENABLE_KSYNTAXHIGHLIGHTING) +if(CUTTER_ENABLE_KSYNTAXHIGHLIGHTING AND (NOT CUTTER_QT6)) if(CUTTER_ENABLE_KSYNTAXHIGHLIGHTING STREQUAL AUTO) find_package(KF5SyntaxHighlighting) if(KF5SyntaxHighlighting_FOUND) @@ -153,6 +153,9 @@ if(CUTTER_ENABLE_KSYNTAXHIGHLIGHTING) set(KSYNTAXHIGHLIGHTING_STATUS ON) endif() else() + if(CUTTER_ENABLE_KSYNTAXHIGHLIGHTING AND CUTTER_QT6) + message(WARNING "KSyntaxHighlighting has been disabled because not supported in QT6") + endif() set(KSYNTAXHIGHLIGHTING_STATUS OFF) endif() From 4742003ff48c677156d76ca394dcbf9da3f96927 Mon Sep 17 00:00:00 2001 From: Giovanni <561184+wargio@users.noreply.github.com> Date: Tue, 20 Jun 2023 16:40:57 +0800 Subject: [PATCH 31/50] Update rizin & translation submodules (#3194) * Fix plugins paths for windows builds due recent changes in rizin * Disable openssl support for yara win * Disable use_sys_yara in linux/osx/win builds --- dist/CMakeLists.txt | 2 +- dist/bundle_jsdec.ps1 | 7 ++++--- dist/bundle_rz_libswift.ps1 | 6 +++--- dist/bundle_rz_libyara.ps1 | 8 ++++---- dist/bundle_rz_silhouette.ps1 | 6 +++--- rizin | 2 +- scripts/rz-libyara.sh | 2 +- src/CutterApplication.cpp | 13 +++++++------ src/core/Cutter.cpp | 19 +++---------------- src/core/CutterDescriptions.h | 1 - src/translations | 2 +- src/widgets/ComboQuickFilterView.cpp | 2 +- src/widgets/QuickFilterView.cpp | 2 +- 13 files changed, 30 insertions(+), 42 deletions(-) diff --git a/dist/CMakeLists.txt b/dist/CMakeLists.txt index 822942fd..2fe5b671 100644 --- a/dist/CMakeLists.txt +++ b/dist/CMakeLists.txt @@ -7,7 +7,7 @@ unset(RZ_GHIDRA_PREFIX_PATH) if(WIN32) set(CPACK_GENERATOR "ZIP") - set(RIZIN_INSTALL_PLUGDIR "lib/plugins") + set(RIZIN_INSTALL_PLUGDIR "lib/rizin/plugins") if (CUTTER_PACKAGE_DEPENDENCIES) if (CUTTER_ENABLE_PYTHON) diff --git a/dist/bundle_jsdec.ps1 b/dist/bundle_jsdec.ps1 index 762d7b11..d6162346 100644 --- a/dist/bundle_jsdec.ps1 +++ b/dist/bundle_jsdec.ps1 @@ -5,12 +5,13 @@ if (-not (Test-Path -Path 'jsdec' -PathType Container)) { git clone https://github.com/rizinorg/jsdec.git --depth 1 --branch master } cd jsdec -& meson.exe --buildtype=release -Dc_args=-DDUK_USE_DATE_NOW_WINDOWS -Djsc_folder=".." --prefix=$dist p build +& meson.exe --buildtype=release -Dc_args=-DDUK_USE_DATE_NOW_WINDOWS -Djsc_folder=".." --prefix="$dist" p build ninja -C build install $ErrorActionPreference = 'Stop' -$pathdll = "$dist\lib\plugins\core_pdd.dll" +$pathdll = "$dist\lib\rizin\plugins\core_pdd.dll" if(![System.IO.File]::Exists($pathdll)) { type build\meson-logs\meson-log.txt + ls "$dist\lib\rizin\plugins\" throw (New-Object System.IO.FileNotFoundException("File not found: $pathdll", $pathdll)) } -Remove-Item -Recurse -Force $dist\lib\plugins\core_pdd.lib +Remove-Item -Recurse -Force "$dist\lib\rizin\plugins\core_pdd.lib" diff --git a/dist/bundle_rz_libswift.ps1 b/dist/bundle_rz_libswift.ps1 index 9fec607c..9c0b3c32 100644 --- a/dist/bundle_rz_libswift.ps1 +++ b/dist/bundle_rz_libswift.ps1 @@ -7,10 +7,10 @@ if (-not (Test-Path -Path 'libswift' -PathType Container)) { cd libswift & meson.exe --buildtype=release --prefix=$dist build ninja -C build install -$pathdll = "$dist/lib/plugins/swift.dll" +$pathdll = "$dist\lib\rizin\plugins\swift.dll" if(![System.IO.File]::Exists($pathdll)) { type build/meson-logs/meson-log.txt - ls "$dist/lib/plugins/" + ls "$dist\lib\rizin\plugins\" throw (New-Object System.IO.FileNotFoundException("File not found: $pathdll", $pathdll)) } -Remove-Item -Recurse -Force $dist/lib/plugins/swift.lib +Remove-Item -Recurse -Force "$dist\lib\rizin\plugins\swift.lib" diff --git a/dist/bundle_rz_libyara.ps1 b/dist/bundle_rz_libyara.ps1 index c3309b94..984913d6 100644 --- a/dist/bundle_rz_libyara.ps1 +++ b/dist/bundle_rz_libyara.ps1 @@ -8,15 +8,15 @@ if (-not (Test-Path -Path 'rz_libyara' -PathType Container)) { git -C rz_libyara submodule update } cd rz_libyara -& meson.exe --buildtype=release --prefix=$dist build +& meson.exe --buildtype=release --prefix=$dist -Duse_sys_yara=disabled -Denable_openssl=false build ninja -C build install -$pathdll = "$dist/lib/plugins/rz_yara.dll" +$pathdll = "$dist\lib\rizin\plugins\rz_yara.dll" if(![System.IO.File]::Exists($pathdll)) { type build/meson-logs/meson-log.txt - ls "$dist/lib/plugins/" + ls "$dist\lib\rizin\plugins\" throw (New-Object System.IO.FileNotFoundException("File not found: $pathdll", $pathdll)) } -Remove-Item -Recurse -Force $dist/lib/plugins/rz_yara.lib +Remove-Item -Recurse -Force "$dist\lib\rizin\plugins\rz_yara.lib" cd cutter-plugin mkdir build diff --git a/dist/bundle_rz_silhouette.ps1 b/dist/bundle_rz_silhouette.ps1 index 4aeeb2b9..4cf04668 100644 --- a/dist/bundle_rz_silhouette.ps1 +++ b/dist/bundle_rz_silhouette.ps1 @@ -8,10 +8,10 @@ if (-not (Test-Path -Path 'rz-silhouette' -PathType Container)) { cd rz-silhouette & meson.exe --buildtype=release --prefix=$dist build ninja -C build install -$pathdll = "$dist/lib/plugins/rz_silhouette.dll" +$pathdll = "$dist\lib\rizin\plugins\rz_silhouette.dll" if(![System.IO.File]::Exists($pathdll)) { type build/meson-logs/meson-log.txt - ls "$dist/lib/plugins/" + ls "$dist\lib\rizin\plugins\" throw (New-Object System.IO.FileNotFoundException("File not found: $pathdll", $pathdll)) } -Remove-Item -Recurse -Force $dist/lib/plugins/rz_silhouette.lib +Remove-Item -Recurse -Force "$dist\lib\rizin\plugins\rz_silhouette.lib" diff --git a/rizin b/rizin index 9ab709bc..9c6feafd 160000 --- a/rizin +++ b/rizin @@ -1 +1 @@ -Subproject commit 9ab709bc34843f04ffde3b63322c809596123e77 +Subproject commit 9c6feafd4733903fca0cdad50b3bd213ab2b6228 diff --git a/scripts/rz-libyara.sh b/scripts/rz-libyara.sh index f316f2c5..5fdb303c 100755 --- a/scripts/rz-libyara.sh +++ b/scripts/rz-libyara.sh @@ -15,7 +15,7 @@ fi cd rz_libyara -meson --buildtype=release --pkg-config-path="$INSTALL_PREFIX/lib/pkgconfig" --prefix="$INSTALL_PREFIX" build +meson --buildtype=release --pkg-config-path="$INSTALL_PREFIX/lib/pkgconfig" --prefix="$INSTALL_PREFIX" -Duse_sys_yara=disabled build ninja -C build install cd cutter-plugin diff --git a/src/CutterApplication.cpp b/src/CutterApplication.cpp index 6f0612b4..0c758500 100644 --- a/src/CutterApplication.cpp +++ b/src/CutterApplication.cpp @@ -33,9 +33,9 @@ // has RZ_GITTAP defined and uses it in rz_core_version(). // After that, RZ_GITTAP is not defined anymore and RZ_VERSION is used. #ifdef RZ_GITTAP -#define CUTTER_COMPILE_TIME_RZ_VERSION "" RZ_GITTAP +# define CUTTER_COMPILE_TIME_RZ_VERSION "" RZ_GITTAP #else -#define CUTTER_COMPILE_TIME_RZ_VERSION "" RZ_VERSION +# define CUTTER_COMPILE_TIME_RZ_VERSION "" RZ_VERSION #endif CutterApplication::CutterApplication(int &argc, char **argv) : QApplication(argc, argv) @@ -162,7 +162,8 @@ CutterApplication::CutterApplication(int &argc, char **argv) : QApplication(argc appdir.cdUp(); // appdir auto sleighHome = appdir; - sleighHome.cd("lib/rizin/plugins/rz_ghidra_sleigh/"); // appdir/lib/rizin/plugins/rz_ghidra_sleigh/ + // appdir/lib/rizin/plugins/rz_ghidra_sleigh/ + sleighHome.cd("lib/rizin/plugins/rz_ghidra_sleigh/"); Core()->setConfig("ghidra.sleighhome", sleighHome.absolutePath()); } #endif @@ -174,8 +175,8 @@ CutterApplication::CutterApplication(int &argc, char **argv) : QApplication(argc rzprefix.cd("Resources"); // Contents/Resources/ auto sleighHome = rzprefix; - sleighHome.cd( - "lib/rizin/plugins/rz_ghidra_sleigh"); // Contents/Resources/lib/rizin/plugins/rz_ghidra_sleigh + // Contents/Resources/lib/rizin/plugins/rz_ghidra_sleigh + sleighHome.cd("lib/rizin/plugins/rz_ghidra_sleigh"); Core()->setConfig("ghidra.sleighhome", sleighHome.absolutePath()); } #endif @@ -183,7 +184,7 @@ CutterApplication::CutterApplication(int &argc, char **argv) : QApplication(argc #if defined(Q_OS_WIN) && defined(CUTTER_ENABLE_PACKAGING) { auto sleighHome = QDir(QCoreApplication::applicationDirPath()); - sleighHome.cd("lib/plugins/rz_ghidra_sleigh"); + sleighHome.cd("lib/rizin/plugins/rz_ghidra_sleigh"); Core()->setConfig("ghidra.sleighhome", sleighHome.absolutePath()); } #endif diff --git a/src/core/Cutter.cpp b/src/core/Cutter.cpp index ebbd2142..db144cb4 100644 --- a/src/core/Cutter.cpp +++ b/src/core/Cutter.cpp @@ -3084,8 +3084,6 @@ QList CutterCore::getAllImports() RzBinImport *import; RzListIter *iter; bool va = core->io->va || core->bin->is_debugger; - int bin_demangle = getConfigi("bin.demangle"); - int keep_lib = getConfigi("bin.demangle.libs"); CutterRzListForeach (imports, iter, RzBinImport, import) { if (RZ_STR_ISEMPTY(import->name)) { continue; @@ -3099,13 +3097,6 @@ QList CutterCore::getAllImports() if (RZ_STR_ISNOTEMPTY(import->classname)) { name = QString("%1.%2").arg(import->classname, import->name); } - if (bin_demangle) { - char *dname = rz_bin_demangle(bf, NULL, name.toUtf8().constData(), - importDescription.plt, keep_lib); - if (dname) { - name = fromOwnedCharPtr(dname); - } - } if (core->bin->prefix) { name = QString("%1.%2").arg(core->bin->prefix, name); } @@ -3135,7 +3126,6 @@ QList CutterCore::getAllExports() return {}; } - QString lang = getConfigi("bin.demangle") ? getConfig("bin.lang") : ""; bool va = core->io->va || core->bin->is_debugger; QList ret; @@ -3145,7 +3135,7 @@ QList CutterCore::getAllExports() } RzBinSymNames sn = {}; - rz_core_sym_name_init(core, &sn, symbol, lang.isEmpty() ? NULL : lang.toUtf8().constData()); + rz_core_sym_name_init(&sn, symbol); ExportDescription exportDescription; exportDescription.vaddr = rva(bf->o, symbol->paddr, symbol->vaddr, va); @@ -3543,19 +3533,18 @@ QList CutterCore::getAllClassesFromBin() RzListIter *iter, *iter2, *iter3; RzBinClass *c; RzBinSymbol *sym; - RzBinField *f; + RzBinClassField *f; CutterRzListForeach (cs, iter, RzBinClass, c) { BinClassDescription classDescription; classDescription.name = c->name; classDescription.addr = c->addr; - classDescription.index = c->index; CutterRzListForeach (c->methods, iter2, RzBinSymbol, sym) { BinClassMethodDescription methodDescription; methodDescription.name = sym->name; methodDescription.addr = sym->vaddr; classDescription.methods << methodDescription; } - CutterRzListForeach (c->fields, iter3, RzBinField, f) { + CutterRzListForeach (c->fields, iter3, RzBinClassField, f) { BinClassFieldDescription fieldDescription; fieldDescription.name = f->name; fieldDescription.addr = f->vaddr; @@ -3591,7 +3580,6 @@ QList CutterCore::getAllClassesFromFlags() } desc->name = match.captured(1); desc->addr = item.offset; - desc->index = RVA_INVALID; continue; } @@ -3605,7 +3593,6 @@ QList CutterCore::getAllClassesFromFlags() BinClassDescription cls; cls.name = tr("Unknown (%1)").arg(className); cls.addr = RVA_INVALID; - cls.index = 0; ret << cls; classDesc = &ret.last(); classesCache[className] = classDesc; diff --git a/src/core/CutterDescriptions.h b/src/core/CutterDescriptions.h index aed9b508..20a476fc 100644 --- a/src/core/CutterDescriptions.h +++ b/src/core/CutterDescriptions.h @@ -239,7 +239,6 @@ struct BinClassDescription QString name; RVA addr = RVA_INVALID; RVA vtableAddr = RVA_INVALID; - ut64 index = 0; QList baseClasses; QList methods; QList fields; diff --git a/src/translations b/src/translations index e8fc5ca1..5ac7217b 160000 --- a/src/translations +++ b/src/translations @@ -1 +1 @@ -Subproject commit e8fc5ca1acd70fd82a2ac9ac02b0261e57703250 +Subproject commit 5ac7217b12623c7bfd0378380f923cf694ccee22 diff --git a/src/widgets/ComboQuickFilterView.cpp b/src/widgets/ComboQuickFilterView.cpp index a7bcef60..04580584 100644 --- a/src/widgets/ComboQuickFilterView.cpp +++ b/src/widgets/ComboQuickFilterView.cpp @@ -13,7 +13,7 @@ ComboQuickFilterView::ComboQuickFilterView(QWidget *parent) [this]() { emit filterTextChanged(ui->lineEdit->text()); }); connect(ui->lineEdit, &QLineEdit::textChanged, this, - [this](const QString &text) { debounceTimer->start(150); }); + [this]() { debounceTimer->start(150); }); } ComboQuickFilterView::~ComboQuickFilterView() diff --git a/src/widgets/QuickFilterView.cpp b/src/widgets/QuickFilterView.cpp index 1f04c5b9..bf130333 100644 --- a/src/widgets/QuickFilterView.cpp +++ b/src/widgets/QuickFilterView.cpp @@ -16,7 +16,7 @@ QuickFilterView::QuickFilterView(QWidget *parent, bool defaultOn) [this]() { emit filterTextChanged(ui->filterLineEdit->text()); }); connect(ui->filterLineEdit, &QLineEdit::textChanged, this, - [this](const QString &text) { debounceTimer->start(150); }); + [this]() { debounceTimer->start(150); }); if (!defaultOn) { closeFilter(); From 579ac236b6a7f2ae3b86d5270b1f4fb13fabd27d Mon Sep 17 00:00:00 2001 From: wargio Date: Mon, 12 Jun 2023 19:35:02 +0800 Subject: [PATCH 32/50] Implement RzIL statement graph --- src/widgets/RizinGraphWidget.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/widgets/RizinGraphWidget.cpp b/src/widgets/RizinGraphWidget.cpp index b228e4a4..380657ee 100644 --- a/src/widgets/RizinGraphWidget.cpp +++ b/src/widgets/RizinGraphWidget.cpp @@ -27,6 +27,7 @@ RizinGraphWidget::RizinGraphWidget(MainWindow *main) { 'r', tr("References graph (agr)") }, { 'R', tr("Global references graph (agR)") }, { 'x', tr("Cross references graph (agx)") }, + { 'I', tr("RzIL statement graph (agI)") }, { 'g', tr("Custom graph (agg)") }, { ' ', tr("User command") }, }; From cf14fd10066fcd6a3af1cdad30bf3e3ba0c4f0db Mon Sep 17 00:00:00 2001 From: Giovanni <561184+wargio@users.noreply.github.com> Date: Sun, 25 Jun 2023 11:42:23 +0800 Subject: [PATCH 33/50] Add Tools > BaseFind + command line options (#3198) * Add Tools > BaseFind * Rewritten how cutter parses and stores the initial options * Update docs and fixed comments * Add missing endianness option --- docs/source/user-docs/command-line.rst | 38 ++- rizin | 2 +- src/CMakeLists.txt | 11 + src/CutterApplication.cpp | 153 +++++++++++- src/CutterApplication.h | 4 + src/core/Basefind.cpp | 109 ++++++++ src/core/Basefind.h | 47 ++++ src/core/Cutter.cpp | 6 + src/core/Cutter.h | 4 + src/core/CutterDescriptions.h | 14 ++ src/core/MainWindow.cpp | 25 +- src/core/MainWindow.h | 1 + src/core/MainWindow.ui | 12 + src/dialogs/CommentsDialog.cpp | 2 + src/dialogs/InitialOptionsDialog.cpp | 54 +++- src/dialogs/InitialOptionsDialog.h | 2 +- src/tools/basefind/BaseFindDialog.cpp | 97 ++++++++ src/tools/basefind/BaseFindDialog.h | 38 +++ src/tools/basefind/BaseFindDialog.ui | 246 +++++++++++++++++++ src/tools/basefind/BaseFindResultsDialog.cpp | 161 ++++++++++++ src/tools/basefind/BaseFindResultsDialog.h | 68 +++++ src/tools/basefind/BaseFindResultsDialog.ui | 70 ++++++ src/tools/basefind/BaseFindSearchDialog.cpp | 66 +++++ src/tools/basefind/BaseFindSearchDialog.h | 41 ++++ src/tools/basefind/BaseFindSearchDialog.ui | 112 +++++++++ 25 files changed, 1362 insertions(+), 21 deletions(-) create mode 100644 src/core/Basefind.cpp create mode 100644 src/core/Basefind.h create mode 100644 src/tools/basefind/BaseFindDialog.cpp create mode 100644 src/tools/basefind/BaseFindDialog.h create mode 100644 src/tools/basefind/BaseFindDialog.ui create mode 100644 src/tools/basefind/BaseFindResultsDialog.cpp create mode 100644 src/tools/basefind/BaseFindResultsDialog.h create mode 100644 src/tools/basefind/BaseFindResultsDialog.ui create mode 100644 src/tools/basefind/BaseFindSearchDialog.cpp create mode 100644 src/tools/basefind/BaseFindSearchDialog.h create mode 100644 src/tools/basefind/BaseFindSearchDialog.ui diff --git a/docs/source/user-docs/command-line.rst b/docs/source/user-docs/command-line.rst index ceea4d11..39d3904e 100644 --- a/docs/source/user-docs/command-line.rst +++ b/docs/source/user-docs/command-line.rst @@ -40,21 +40,45 @@ Options **2** aaaa (experimental) +.. option:: -a, --arch + + Sets a specific architecture name. + +.. option:: -b, --bits + + Sets a specific architecture bits. + +.. option:: -c, --cpu + + Sets a specific CPU. + +.. option:: -o, --os + + Sets a specific operating system. + +.. option:: -e, --endian + + Sets the endianness (big or little). + .. option:: -F, --format - Force using a specific file format (bin plugin) + Force using a specific file format (bin plugin). .. option:: -B, --base - Load binary at a specific base address + Load binary at a specific base address. + +.. option:: -m, --map + + Map the binary at a specific address. .. option:: -i - Run script file + Run script file. .. option:: -p, --project - Load project file + Load project file. .. option:: -w, --writemode @@ -62,9 +86,13 @@ Options When used together with -A/--analysis , it will open a file directly in write mode without any further dialog or confirmation. +.. option:: -P, --phymode + + Disables virtual addressing. + .. option:: --pythonhome - PYTHONHOME to use for the embedded python interpreter + PYTHONHOME to use for the embedded python interpreter. .. option:: --no-output-redirect diff --git a/rizin b/rizin index 9c6feafd..6bfc67a2 160000 --- a/rizin +++ b/rizin @@ -1 +1 @@ -Subproject commit 9c6feafd4733903fca0cdad50b3bd213ab2b6228 +Subproject commit 6bfc67a2868e07ab89ff931b96dfd3bc65e7371e diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 009b2962..0d433df0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -6,6 +6,7 @@ set(SOURCES core/Cutter.cpp core/CutterJson.cpp core/RizinCpp.cpp + core/Basefind.cpp dialogs/EditStringDialog.cpp dialogs/WriteCommandsDialogs.cpp widgets/DisassemblerGraphView.cpp @@ -149,6 +150,9 @@ set(SOURCES dialogs/GlibcHeapBinsDialog.cpp widgets/HeapBinsGraphView.cpp dialogs/ArenaInfoDialog.cpp + tools/basefind/BaseFindDialog.cpp + tools/basefind/BaseFindSearchDialog.cpp + tools/basefind/BaseFindResultsDialog.cpp ) set(HEADER_FILES core/Cutter.h @@ -156,6 +160,7 @@ set(HEADER_FILES core/CutterDescriptions.h core/CutterJson.h core/RizinCpp.h + core/Basefind.h dialogs/EditStringDialog.h dialogs/WriteCommandsDialogs.h widgets/DisassemblerGraphView.h @@ -308,6 +313,9 @@ set(HEADER_FILES dialogs/GlibcHeapBinsDialog.h widgets/HeapBinsGraphView.h dialogs/ArenaInfoDialog.h + tools/basefind/BaseFindDialog.h + tools/basefind/BaseFindSearchDialog.h + tools/basefind/BaseFindResultsDialog.h ) set(UI_FILES dialogs/AboutDialog.ui @@ -377,6 +385,9 @@ set(UI_FILES widgets/GlibcHeapWidget.ui dialogs/GlibcHeapBinsDialog.ui dialogs/ArenaInfoDialog.ui + tools/basefind/BaseFindDialog.ui + tools/basefind/BaseFindSearchDialog.ui + tools/basefind/BaseFindResultsDialog.ui ) set(QRC_FILES resources.qrc diff --git a/src/CutterApplication.cpp b/src/CutterApplication.cpp index 0c758500..79a48b59 100644 --- a/src/CutterApplication.cpp +++ b/src/CutterApplication.cpp @@ -296,6 +296,89 @@ bool CutterApplication::loadTranslations() return false; } +QStringList CutterApplication::getArgs() const +{ + auto &options = clOptions.fileOpenOptions; + + QStringList args; + switch (clOptions.analysisLevel) { + case AutomaticAnalysisLevel::None: + args.push_back("-A"); + args.push_back("0"); + break; + case AutomaticAnalysisLevel::AAA: + args.push_back("-A"); + args.push_back("1"); + break; + case AutomaticAnalysisLevel::AAAA: + args.push_back("-A"); + args.push_back("2"); + break; + default: + break; + } + + if (!options.useVA) { + args.push_back("-P"); + } + if (options.writeEnabled) { + args.push_back("-w"); + } + if (!options.script.isEmpty()) { + args.push_back("-i"); + args.push_back(options.script); + } + if (!options.projectFile.isEmpty()) { + args.push_back("-p"); + args.push_back(options.projectFile); + } + if (!options.arch.isEmpty()) { + args.push_back("-a"); + args.push_back(options.arch); + } + if (options.bits > 0) { + args.push_back("-b"); + args.push_back(QString::asprintf("%d", options.bits)); + } + if (!options.cpu.isEmpty()) { + args.push_back("-c"); + args.push_back(options.cpu); + } + if (!options.os.isEmpty()) { + args.push_back("-o"); + args.push_back(options.os); + } + + switch (options.endian) { + case InitialOptions::Endianness::Little: + args.push_back("-e"); + args.push_back("little"); + break; + case InitialOptions::Endianness::Big: + args.push_back("-e"); + args.push_back("big"); + break; + default: + break; + } + if (!options.forceBinPlugin.isEmpty()) { + args.push_back("-F"); + args.push_back(options.forceBinPlugin); + } + if (options.binLoadAddr != RVA_INVALID) { + args.push_back("-B"); + args.push_back(RzAddressString(options.binLoadAddr)); + } + if (options.mapAddr != RVA_INVALID) { + args.push_back("-m"); + args.push_back(RzAddressString(options.mapAddr)); + } + if (!options.filename.isEmpty()) { + args.push_back(options.filename); + } + return args; +} + bool CutterApplication::parseCommandLineOptions() { // Keep this function in sync with documentation @@ -315,6 +398,27 @@ bool CutterApplication::parseCommandLineOptions() QObject::tr("level")); cmd_parser.addOption(analOption); + QCommandLineOption archOption({ "a", "arch" }, QObject::tr("Sets a specific architecture name"), + QObject::tr("arch")); + cmd_parser.addOption(archOption); + + QCommandLineOption bitsOption({ "b", "bits" }, QObject::tr("Sets a specific architecture bits"), + QObject::tr("bits")); + cmd_parser.addOption(bitsOption); + + QCommandLineOption cpuOption({ "c", "cpu" }, QObject::tr("Sets a specific CPU"), + QObject::tr("cpu")); + cmd_parser.addOption(cpuOption); + + QCommandLineOption osOption({ "o", "os" }, QObject::tr("Sets a specific operating system"), + QObject::tr("os")); + cmd_parser.addOption(osOption); + + QCommandLineOption endianOption({ "e", "endian" }, + QObject::tr("Sets the endianness (big or little)"), + QObject::tr("big|little")); + cmd_parser.addOption(endianOption); + QCommandLineOption formatOption({ "F", "format" }, QObject::tr("Force using a specific file format (bin plugin)"), QObject::tr("name")); @@ -325,6 +429,11 @@ bool CutterApplication::parseCommandLineOptions() QObject::tr("base address")); cmd_parser.addOption(baddrOption); + QCommandLineOption maddrOption({ "m", "map" }, + QObject::tr("Map the binary at a specific address"), + QObject::tr("map address")); + cmd_parser.addOption(maddrOption); + QCommandLineOption scriptOption("i", QObject::tr("Run script file"), QObject::tr("file")); cmd_parser.addOption(scriptOption); @@ -336,6 +445,10 @@ bool CutterApplication::parseCommandLineOptions() QObject::tr("Open file in write mode")); cmd_parser.addOption(writeModeOption); + QCommandLineOption phyModeOption({ "P", "phymode" }, + QObject::tr("Disables virtual addressing")); + cmd_parser.addOption(phyModeOption); + QCommandLineOption pythonHomeOption( "pythonhome", QObject::tr("PYTHONHOME to use for embedded python interpreter"), "PYTHONHOME"); @@ -397,15 +510,21 @@ bool CutterApplication::parseCommandLineOptions() return false; } - InitialOptions options; if (!opts.args.isEmpty()) { opts.fileOpenOptions.filename = opts.args[0]; opts.fileOpenOptions.forceBinPlugin = cmd_parser.value(formatOption); if (cmd_parser.isSet(baddrOption)) { - bool ok; + bool ok = false; RVA baddr = cmd_parser.value(baddrOption).toULongLong(&ok, 0); if (ok) { - options.binLoadAddr = baddr; + opts.fileOpenOptions.binLoadAddr = baddr; + } + } + if (cmd_parser.isSet(maddrOption)) { + bool ok = false; + RVA maddr = cmd_parser.value(maddrOption).toULongLong(&ok, 0); + if (ok) { + opts.fileOpenOptions.mapAddr = maddr; } } switch (opts.analysisLevel) { @@ -422,8 +541,36 @@ bool CutterApplication::parseCommandLineOptions() break; } opts.fileOpenOptions.script = cmd_parser.value(scriptOption); + opts.fileOpenOptions.arch = cmd_parser.value(archOption); + opts.fileOpenOptions.cpu = cmd_parser.value(cpuOption); + opts.fileOpenOptions.os = cmd_parser.value(osOption); + if (cmd_parser.isSet(bitsOption)) { + bool ok = false; + int bits = cmd_parser.value(bitsOption).toInt(&ok, 10); + if (ok && bits > 0) { + opts.fileOpenOptions.bits = bits; + } + } + if (cmd_parser.isSet(endianOption)) { + QString endian = cmd_parser.value(endianOption).toLower(); + opts.fileOpenOptions.endian = InitialOptions::Endianness::Auto; + if (endian == "little") { + opts.fileOpenOptions.endian = InitialOptions::Endianness::Little; + } else if (endian == "big") { + opts.fileOpenOptions.endian = InitialOptions::Endianness::Big; + } else { + fprintf(stderr, "%s\n", + QObject::tr("Invalid Endianness. You can only set it to `big` or `little`.") + .toLocal8Bit() + .constData()); + return false; + } + } else { + opts.fileOpenOptions.endian = InitialOptions::Endianness::Auto; + } opts.fileOpenOptions.writeEnabled = cmd_parser.isSet(writeModeOption); + opts.fileOpenOptions.useVA = !cmd_parser.isSet(phyModeOption); } opts.fileOpenOptions.projectFile = cmd_parser.value(projectOption); diff --git a/src/CutterApplication.h b/src/CutterApplication.h index cd27a26a..e4ed041c 100644 --- a/src/CutterApplication.h +++ b/src/CutterApplication.h @@ -33,6 +33,10 @@ public: void launchNewInstance(const QStringList &args = {}); + InitialOptions getInitialOptions() const { return clOptions.fileOpenOptions; } + void setInitialOptions(const InitialOptions &options) { clOptions.fileOpenOptions = options; } + QStringList getArgs() const; + protected: bool event(QEvent *e); diff --git a/src/core/Basefind.cpp b/src/core/Basefind.cpp new file mode 100644 index 00000000..8cc39035 --- /dev/null +++ b/src/core/Basefind.cpp @@ -0,0 +1,109 @@ +#include "Basefind.h" + +bool Basefind::threadCallback(const RzBaseFindThreadInfo *info, void *user) +{ + auto th = reinterpret_cast(user); + return th->updateProgress(info); +} + +Basefind::Basefind(CutterCore *core) + : core(core), + scores(nullptr), + continue_run(true) +#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) + , + mutex(QMutex::Recursive) +#endif +{ + memset(&options, 0, sizeof(RzBaseFindOpt)); +} + +Basefind::~Basefind() +{ + cancel(); + wait(); + rz_list_free(scores); +} + +bool Basefind::setOptions(const RzBaseFindOpt *opts) +{ + mutex.lock(); + options.max_threads = opts->max_threads; + options.pointer_size = opts->pointer_size; + options.start_address = opts->start_address; + options.end_address = opts->end_address; + options.alignment = opts->alignment; + options.min_score = opts->min_score; + options.min_string_len = opts->min_string_len; + mutex.unlock(); + + if (options.start_address >= options.end_address) { + qWarning() << "Start address is >= end address"; + return false; + } else if (options.alignment < RZ_BASEFIND_BASE_ALIGNMENT) { + qWarning() << "Alignment must be at least " + << QString::asprintf("0x%x", RZ_BASEFIND_BASE_ALIGNMENT); + return false; + } else if (options.min_score < 1) { + qWarning() << "Min score must be at least 1"; + return false; + } else if (options.min_string_len < 1) { + qWarning() << "Min string length must be at least 1"; + return false; + } + return true; +} + +void Basefind::run() +{ + qRegisterMetaType(); + + 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 Basefind::results() +{ + QList 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; +} diff --git a/src/core/Basefind.h b/src/core/Basefind.h new file mode 100644 index 00000000..ded42fd1 --- /dev/null +++ b/src/core/Basefind.h @@ -0,0 +1,47 @@ +#ifndef CUTTER_BASEFIND_CORE_H +#define CUTTER_BASEFIND_CORE_H + +#include +#include + +#include "Cutter.h" +#include "CutterDescriptions.h" +#include + +class CutterCore; + +class Basefind : public QThread +{ + Q_OBJECT + +public: + explicit Basefind(CutterCore *core); + virtual ~Basefind(); + + void run(); + bool setOptions(const RzBaseFindOpt *opts); + QList 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 diff --git a/src/core/Cutter.cpp b/src/core/Cutter.cpp index db144cb4..4e8f06f4 100644 --- a/src/core/Cutter.cpp +++ b/src/core/Cutter.cpp @@ -4577,3 +4577,9 @@ void CutterCore::writeGraphvizGraphToFile(QString path, QString format, RzCoreGr } } } + +bool CutterCore::rebaseBin(RVA base_address) +{ + CORE_LOCK(); + return rz_core_bin_rebase(core, base_address); +} diff --git a/src/core/Cutter.h b/src/core/Cutter.h index dd9e67c8..14fbfde7 100644 --- a/src/core/Cutter.h +++ b/src/core/Cutter.h @@ -4,6 +4,7 @@ #include "core/CutterCommon.h" #include "core/CutterDescriptions.h" #include "core/CutterJson.h" +#include "core/Basefind.h" #include "common/BasicInstructionHighlighter.h" #include @@ -69,6 +70,7 @@ class CUTTER_EXPORT CutterCore : public QObject friend class RzCoreLocked; friend class RizinTask; + friend class Basefind; public: explicit CutterCore(QObject *parent = nullptr); @@ -556,6 +558,8 @@ public: void setGraphEmpty(bool empty); bool isGraphEmpty(); + bool rebaseBin(RVA base_address); + void getRegs(); QList regs; void setSettings(); diff --git a/src/core/CutterDescriptions.h b/src/core/CutterDescriptions.h index 20a476fc..5f311be5 100644 --- a/src/core/CutterDescriptions.h +++ b/src/core/CutterDescriptions.h @@ -385,6 +385,18 @@ struct Arena ut64 max_system_mem; }; +struct BasefindCoreStatusDescription +{ + size_t index; + ut32 percentage; +}; + +struct BasefindResultDescription +{ + RVA candidate; + ut32 score; +}; + Q_DECLARE_METATYPE(FunctionDescription) Q_DECLARE_METATYPE(ImportDescription) Q_DECLARE_METATYPE(ExportDescription) @@ -423,5 +435,7 @@ Q_DECLARE_METATYPE(BreakpointDescription::PositionType) Q_DECLARE_METATYPE(ProcessDescription) Q_DECLARE_METATYPE(RefDescription) Q_DECLARE_METATYPE(VariableDescription) +Q_DECLARE_METATYPE(BasefindCoreStatusDescription) +Q_DECLARE_METATYPE(BasefindResultDescription) #endif // DESCRIPTIONS_H diff --git a/src/core/MainWindow.cpp b/src/core/MainWindow.cpp index e873b0d7..8a6edf24 100644 --- a/src/core/MainWindow.cpp +++ b/src/core/MainWindow.cpp @@ -115,6 +115,9 @@ #include #include +// Tools +#include "tools/basefind/BaseFindDialog.h" + #define PROJECT_FILE_FILTER tr("Rizin Project (*.rzdb)") template @@ -1087,11 +1090,11 @@ MemoryDockWidget *MainWindow::addNewMemoryWidget(MemoryWidgetType type, RVA addr memoryWidget = new DecompilerWidget(this); break; case MemoryWidgetType::CallGraph: - memoryWidget = new CallGraphWidget(this, false); - break; + memoryWidget = new CallGraphWidget(this, false); + break; case MemoryWidgetType::GlobalCallGraph: - memoryWidget = new CallGraphWidget(this, true); - break; + memoryWidget = new CallGraphWidget(this, true); + break; } auto seekable = memoryWidget->getSeekable(); seekable->setSynchronization(synchronized); @@ -1637,6 +1640,12 @@ void MainWindow::on_actionTabs_triggered() setTabLocation(); } +void MainWindow::on_actionBaseFind_triggered() +{ + auto dialog = new BaseFindDialog(this); + dialog->show(); +} + void MainWindow::on_actionAbout_triggered() { AboutDialog *a = new AboutDialog(this); @@ -1774,12 +1783,10 @@ void MainWindow::on_actionExport_as_code_triggered() return; } - - auto string = fromOwned( - dialog.selectedNameFilter() != instructionsInComments - ? rz_lang_byte_array(buffer.data(), size, typMap[dialog.selectedNameFilter()]) - : rz_core_print_bytes_with_inst(rc, buffer.data(), 0, size)); + dialog.selectedNameFilter() != instructionsInComments + ? rz_lang_byte_array(buffer.data(), size, typMap[dialog.selectedNameFilter()]) + : rz_core_print_bytes_with_inst(rc, buffer.data(), 0, size)); fileOut << string.get(); } diff --git a/src/core/MainWindow.h b/src/core/MainWindow.h index ce1bdceb..733e7ea5 100644 --- a/src/core/MainWindow.h +++ b/src/core/MainWindow.h @@ -152,6 +152,7 @@ public slots: void toggleOverview(bool visibility, GraphWidget *targetGraph); private slots: + void on_actionBaseFind_triggered(); void on_actionAbout_triggered(); void on_actionIssue_triggered(); void documentationClicked(); diff --git a/src/core/MainWindow.ui b/src/core/MainWindow.ui index 2af046cc..0cf17945 100644 --- a/src/core/MainWindow.ui +++ b/src/core/MainWindow.ui @@ -128,6 +128,12 @@ + + + Tools + + + Help @@ -184,6 +190,7 @@ + @@ -233,6 +240,11 @@ Zen mode + + + BaseFind + + About diff --git a/src/dialogs/CommentsDialog.cpp b/src/dialogs/CommentsDialog.cpp index f5cc1544..4d362d4f 100644 --- a/src/dialogs/CommentsDialog.cpp +++ b/src/dialogs/CommentsDialog.cpp @@ -1,6 +1,8 @@ #include "CommentsDialog.h" #include "ui_CommentsDialog.h" +#include + #include "core/Cutter.h" CommentsDialog::CommentsDialog(QWidget *parent) : QDialog(parent), ui(new Ui::CommentsDialog) diff --git a/src/dialogs/InitialOptionsDialog.cpp b/src/dialogs/InitialOptionsDialog.cpp index 94e43f89..06157cc8 100644 --- a/src/dialogs/InitialOptionsDialog.cpp +++ b/src/dialogs/InitialOptionsDialog.cpp @@ -14,6 +14,7 @@ #include "core/Cutter.h" #include "common/AnalysisTask.h" +#include "CutterApplication.h" InitialOptionsDialog::InitialOptionsDialog(MainWindow *main) : QDialog(nullptr), // parent must not be main @@ -179,9 +180,56 @@ void InitialOptionsDialog::loadOptions(const InitialOptions &options) ui->entry_loadOffset->setText(RzAddressString(options.binLoadAddr)); } + if (options.mapAddr != RVA_INVALID) { + ui->entry_mapOffset->setText(RzAddressString(options.mapAddr)); + } + + ui->vaCheckBox->setChecked(options.useVA); ui->writeCheckBox->setChecked(options.writeEnabled); - // TODO: all other options should also be applied to the ui + if (!options.arch.isNull() && !options.arch.isEmpty()) { + ui->archComboBox->setCurrentText(options.arch); + } + + if (!options.cpu.isNull() && !options.cpu.isEmpty()) { + ui->cpuComboBox->setCurrentText(options.cpu); + } + + if (options.bits > 0) { + ui->bitsComboBox->setCurrentText(QString::asprintf("%d", options.bits)); + } + + if (!options.os.isNull() && !options.os.isEmpty()) { + ui->kernelComboBox->setCurrentText(options.os); + } + + if (!options.forceBinPlugin.isNull() && !options.forceBinPlugin.isEmpty()) { + ui->formatComboBox->setCurrentText(options.forceBinPlugin); + } + + if (!options.loadBinInfo) { + ui->binCheckBox->setChecked(false); + } + + ui->writeCheckBox->setChecked(options.writeEnabled); + + switch (options.endian) { + case InitialOptions::Endianness::Little: + ui->endiannessComboBox->setCurrentIndex(1); + break; + case InitialOptions::Endianness::Big: + ui->endiannessComboBox->setCurrentIndex(2); + break; + default: + break; + } + + ui->demangleCheckBox->setChecked(options.demangle); + + if (!options.pdbFile.isNull() && !options.pdbFile.isEmpty()) { + ui->pdbCheckBox->setChecked(true); + ui->pdbLineEdit->setText(options.pdbFile); + } } void InitialOptionsDialog::setTooltipWithConfigHelp(QWidget *w, const char *config) @@ -246,7 +294,7 @@ QList InitialOptionsDialog::getSelectedAdvancedAnalCmds() co return advanced; } -void InitialOptionsDialog::setupAndStartAnalysis(/*int level, QList advanced*/) +void InitialOptionsDialog::setupAndStartAnalysis() { InitialOptions options; @@ -322,6 +370,8 @@ void InitialOptionsDialog::setupAndStartAnalysis(/*int level, QList adv Core()->getAsyncTaskManager()->start(analysisTaskPtr); done(0); + + static_cast(qApp)->setInitialOptions(options); } void InitialOptionsDialog::on_okButton_clicked() diff --git a/src/dialogs/InitialOptionsDialog.h b/src/dialogs/InitialOptionsDialog.h index 90de4394..74a80a7f 100644 --- a/src/dialogs/InitialOptionsDialog.h +++ b/src/dialogs/InitialOptionsDialog.h @@ -20,7 +20,7 @@ public: explicit InitialOptionsDialog(MainWindow *main); ~InitialOptionsDialog(); - void setupAndStartAnalysis(/*int level, QList advanced*/); + void setupAndStartAnalysis(); private slots: void on_okButton_clicked(); diff --git a/src/tools/basefind/BaseFindDialog.cpp b/src/tools/basefind/BaseFindDialog.cpp new file mode 100644 index 00000000..17adc133 --- /dev/null +++ b/src/tools/basefind/BaseFindDialog.cpp @@ -0,0 +1,97 @@ +#include "BaseFindDialog.h" +#include "ui_BaseFindDialog.h" + +#include "BaseFindSearchDialog.h" + +#include +#include + +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() {} diff --git a/src/tools/basefind/BaseFindDialog.h b/src/tools/basefind/BaseFindDialog.h new file mode 100644 index 00000000..3142eb88 --- /dev/null +++ b/src/tools/basefind/BaseFindDialog.h @@ -0,0 +1,38 @@ +#ifndef BASEFIND_DIALOG_H +#define BASEFIND_DIALOG_H + +#include +#include +#include + +#include + +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; +}; + +#endif // BASEFIND_DIALOG_H diff --git a/src/tools/basefind/BaseFindDialog.ui b/src/tools/basefind/BaseFindDialog.ui new file mode 100644 index 00000000..1f3fd2d4 --- /dev/null +++ b/src/tools/basefind/BaseFindDialog.ui @@ -0,0 +1,246 @@ + + + BaseFindDialog + + + Qt::NonModal + + + + 0 + 0 + 373 + 348 + + + + + 0 + 0 + + + + BaseFind + + + + QLayout::SetMinimumSize + + + + + QLayout::SetMinimumSize + + + + + + 0 + 0 + + + + Cores: + + + + + + + + + + + 0 + 0 + + + + Pointer Size: + + + + + + + + 32 + + + + + 64 + + + + + + + + Start Address: + + + + + + + + 382 + 16777215 + + + + + + + + + + + End Address: + + + + + + + + 382 + 16777215 + + + + + + + + Alignment: + + + + + + + + 382 + 16777215 + + + + + + + + Min String Length: + + + + + + + + 382 + 16777215 + + + + 4 + + + 999999999 + + + + + + + Min Score: + + + + + + + + 382 + 16777215 + + + + 1 + + + 999999999 + + + + + + + + + QLayout::SetMinimumSize + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + Remove item + + + + + Remove all + + + Remove all + + + + + + + buttonBox + accepted() + BaseFindDialog + accept() + + + 248 + 234 + + + 157 + 274 + + + + + buttonBox + rejected() + BaseFindDialog + reject() + + + 316 + 240 + + + 286 + 254 + + + + + diff --git a/src/tools/basefind/BaseFindResultsDialog.cpp b/src/tools/basefind/BaseFindResultsDialog.cpp new file mode 100644 index 00000000..993031b4 --- /dev/null +++ b/src/tools/basefind/BaseFindResultsDialog.cpp @@ -0,0 +1,161 @@ +#include "BaseFindResultsDialog.h" +#include "ui_BaseFindResultsDialog.h" + +#include +#include + +#include +#include + +BaseFindResultsModel::BaseFindResultsModel(QList *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 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(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(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() {} diff --git a/src/tools/basefind/BaseFindResultsDialog.h b/src/tools/basefind/BaseFindResultsDialog.h new file mode 100644 index 00000000..2d9367eb --- /dev/null +++ b/src/tools/basefind/BaseFindResultsDialog.h @@ -0,0 +1,68 @@ +#ifndef BASEFIND_RESULTS_DIALOG_H +#define BASEFIND_RESULTS_DIALOG_H + +#include +#include +#include +#include + +#include + +class BaseFindResultsDialog; + +namespace Ui { +class BaseFindResultsDialog; +} + +class BaseFindResultsModel : public QAbstractListModel +{ + Q_OBJECT + + friend BaseFindResultsDialog; + +public: + enum Column { ScoreColumn = 0, CandidateColumn, ColumnCount }; + + BaseFindResultsModel(QList *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 *list; +}; + +class BaseFindResultsDialog : public QDialog +{ + Q_OBJECT + +public: + explicit BaseFindResultsDialog(QList 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 list; + std::unique_ptr ui; + BaseFindResultsModel *model; + QMenu *blockMenu; + QAction *actionCopyCandidate; + QAction *actionSetLoadAddr; + QAction *actionSetMapAddr; + RVA candidate; +}; + +#endif // BASEFIND_RESULTS_DIALOG_H diff --git a/src/tools/basefind/BaseFindResultsDialog.ui b/src/tools/basefind/BaseFindResultsDialog.ui new file mode 100644 index 00000000..543082d0 --- /dev/null +++ b/src/tools/basefind/BaseFindResultsDialog.ui @@ -0,0 +1,70 @@ + + + BaseFindResultsDialog + + + Qt::NonModal + + + + 0 + 0 + 582 + 382 + + + + + 0 + 0 + + + + BaseFind Results + + + + QLayout::SetMinimumSize + + + + + QLayout::SetMinimumSize + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close + + + + + + + + + + + buttonBox + rejected() + BaseFindResultsDialog + reject() + + + 316 + 240 + + + 286 + 254 + + + + + diff --git a/src/tools/basefind/BaseFindSearchDialog.cpp b/src/tools/basefind/BaseFindSearchDialog.cpp new file mode 100644 index 00000000..bc29af4f --- /dev/null +++ b/src/tools/basefind/BaseFindSearchDialog.cpp @@ -0,0 +1,66 @@ +#include "BaseFindSearchDialog.h" +#include "ui_BaseFindSearchDialog.h" + +#include "BaseFindResultsDialog.h" + +#include +#include + +#include +#include + +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(); +} diff --git a/src/tools/basefind/BaseFindSearchDialog.h b/src/tools/basefind/BaseFindSearchDialog.h new file mode 100644 index 00000000..bd59d6cb --- /dev/null +++ b/src/tools/basefind/BaseFindSearchDialog.h @@ -0,0 +1,41 @@ +#ifndef BASEFIND_SEARCH_DIALOG_H +#define BASEFIND_SEARCH_DIALOG_H + +#include +#include +#include +#include + +#include + +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 bars; + std::unique_ptr basefind; + std::unique_ptr ui; +}; + +#endif // BASEFIND_SEARCH_DIALOG_H diff --git a/src/tools/basefind/BaseFindSearchDialog.ui b/src/tools/basefind/BaseFindSearchDialog.ui new file mode 100644 index 00000000..053cd78d --- /dev/null +++ b/src/tools/basefind/BaseFindSearchDialog.ui @@ -0,0 +1,112 @@ + + + BaseFindSearchDialog + + + Qt::NonModal + + + + 0 + 0 + 582 + 382 + + + + + 0 + 0 + + + + Searching for base address + + + + QLayout::SetMinimumSize + + + + + QLayout::SetMinimumSize + + + + + + 0 + 0 + + + + QAbstractScrollArea::AdjustToContents + + + true + + + + + 0 + 0 + 564 + 320 + + + + + 0 + 0 + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel + + + + + + + + + Remove item + + + + + Remove all + + + Remove all + + + + + + + buttonBox + rejected() + BaseFindSearchDialog + reject() + + + 316 + 240 + + + 286 + 254 + + + + + From 61710535bf11ba9acb336bd0d5c757fafce78110 Mon Sep 17 00:00:00 2001 From: Anton Kochkov Date: Thu, 29 Jun 2023 17:12:59 +0800 Subject: [PATCH 34/50] Update Rizin to latest dev (#3201) --- rizin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rizin b/rizin index 6bfc67a2..09ec12d6 160000 --- a/rizin +++ b/rizin @@ -1 +1 @@ -Subproject commit 6bfc67a2868e07ab89ff931b96dfd3bc65e7371e +Subproject commit 09ec12d6078cdfbb7cfde23262a13cd03056f850 From b757f853506df8aa34e08208b3dc5d22c3357db0 Mon Sep 17 00:00:00 2001 From: Anton Kochkov Date: Fri, 7 Jul 2023 09:24:19 +0800 Subject: [PATCH 35/50] Add few missing tr() string wrappers (#3204) --- src/core/Basefind.cpp | 8 ++++---- src/core/Cutter.cpp | 10 +++++----- src/core/MainWindow.cpp | 4 ++-- src/widgets/Dashboard.cpp | 2 +- src/widgets/GraphView.cpp | 2 +- src/widgets/SearchWidget.cpp | 4 ++-- src/widgets/SectionsWidget.cpp | 2 +- 7 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/core/Basefind.cpp b/src/core/Basefind.cpp index 8cc39035..f1fdd4c7 100644 --- a/src/core/Basefind.cpp +++ b/src/core/Basefind.cpp @@ -38,17 +38,17 @@ bool Basefind::setOptions(const RzBaseFindOpt *opts) mutex.unlock(); if (options.start_address >= options.end_address) { - qWarning() << "Start address is >= end address"; + qWarning() << tr("Start address is >= end address"); return false; } else if (options.alignment < RZ_BASEFIND_BASE_ALIGNMENT) { - qWarning() << "Alignment must be at least " + qWarning() << tr("Alignment must be at least ") << QString::asprintf("0x%x", RZ_BASEFIND_BASE_ALIGNMENT); return false; } else if (options.min_score < 1) { - qWarning() << "Min score must be at least 1"; + qWarning() << tr("Min score must be at least 1"); return false; } else if (options.min_string_len < 1) { - qWarning() << "Min string length must be at least 1"; + qWarning() << tr("Min string length must be at least 1"); return false; } return true; diff --git a/src/core/Cutter.cpp b/src/core/Cutter.cpp index 4e8f06f4..930921dd 100644 --- a/src/core/Cutter.cpp +++ b/src/core/Cutter.cpp @@ -271,7 +271,7 @@ void CutterCore::loadCutterRC() if (!cutterRCFileInfo.exists() || !cutterRCFileInfo.isFile()) { continue; } - qInfo() << "Loading initialization file from " << cutterRCFilePath; + qInfo() << tr("Loading initialization file from ") << cutterRCFilePath; rz_core_cmd_file(core, cutterRCFilePath.toUtf8().constData()); } } @@ -284,7 +284,7 @@ void CutterCore::loadDefaultCutterRC() if (!cutterRCFileInfo.exists() || !cutterRCFileInfo.isFile()) { return; } - qInfo() << "Loading initialization file from " << cutterRCFilePath; + qInfo() << tr("Loading initialization file from ") << cutterRCFilePath; rz_core_cmd_file(core, cutterRCFilePath.toUtf8().constData()); } @@ -4553,7 +4553,7 @@ char *CutterCore::getTextualGraphAt(RzCoreGraphType type, RzCoreGraphFormat form rz_graph_free(graph); if (!string) { - qWarning() << "Failed to generate graph"; + qWarning() << tr("Failed to generate graph"); } return string; @@ -4571,9 +4571,9 @@ void CutterCore::writeGraphvizGraphToFile(QString path, QString format, RzCoreGr if (!rz_core_graph_write(core, address, type, filepath)) { if (address == RVA_INVALID) { - qWarning() << "Cannot get global graph"; + qWarning() << tr("Cannot get global graph"); } else { - qWarning() << "Cannot get graph at " << RzAddressString(address); + qWarning() << tr("Cannot get graph at ") << RzAddressString(address); } } } diff --git a/src/core/MainWindow.cpp b/src/core/MainWindow.cpp index 8a6edf24..3e259734 100644 --- a/src/core/MainWindow.cpp +++ b/src/core/MainWindow.cpp @@ -271,7 +271,7 @@ void MainWindow::initUI() readSettings(); // Display tooltip for the Analyze Program action - ui->actionAnalyze->setToolTip("Analyze the program using Rizin's \"aaa\" command"); + ui->actionAnalyze->setToolTip(tr("Analyze the program using Rizin's \"aaa\" command")); ui->menuFile->setToolTipsVisible(true); } @@ -1768,7 +1768,7 @@ void MainWindow::on_actionExport_as_code_triggered() QFile file(dialog.selectedFiles()[0]); if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { - qWarning() << "Can't open file"; + qWarning() << tr("Can't open file"); return; } diff --git a/src/widgets/Dashboard.cpp b/src/widgets/Dashboard.cpp index 1df65b32..3636492b 100644 --- a/src/widgets/Dashboard.cpp +++ b/src/widgets/Dashboard.cpp @@ -173,7 +173,7 @@ void Dashboard::on_certificateButton_clicked() dialog.setMinimumSize(QSize(900, 600)); dialog.setMaximumSize(QSize(900, 600)); dialog.setSizeGripEnabled(false); - dialog.setWindowTitle("Certificates"); + dialog.setWindowTitle(tr("Certificates")); dialog.exec(); } diff --git a/src/widgets/GraphView.cpp b/src/widgets/GraphView.cpp index 54b4b438..9fcca211 100644 --- a/src/widgets/GraphView.cpp +++ b/src/widgets/GraphView.cpp @@ -438,7 +438,7 @@ void GraphView::saveAsSvg(QString path) generator.setFileName(path); generator.setSize(QSize(width, height)); generator.setViewBox(QRect(0, 0, width, height)); - generator.setTitle("Cutter graph export"); + generator.setTitle(tr("Cutter graph export")); QPainter p; p.begin(&generator); paint(p, QPoint(0, 0), QRect(0, 0, width, height), 1.0, false); diff --git a/src/widgets/SearchWidget.cpp b/src/widgets/SearchWidget.cpp index 0fcb9f8e..86242dd6 100644 --- a/src/widgets/SearchWidget.cpp +++ b/src/widgets/SearchWidget.cpp @@ -322,12 +322,12 @@ void SearchWidget::updatePlaceholderText(int index) void SearchWidget::disableSearch() { ui->searchButton->setEnabled(false); - ui->searchButton->setText("Searching..."); + ui->searchButton->setText(tr("Searching...")); qApp->processEvents(); } void SearchWidget::enableSearch() { ui->searchButton->setEnabled(true); - ui->searchButton->setText("Search"); + ui->searchButton->setText(tr("Search")); } diff --git a/src/widgets/SectionsWidget.cpp b/src/widgets/SectionsWidget.cpp index 548b997d..c4a6317a 100644 --- a/src/widgets/SectionsWidget.cpp +++ b/src/widgets/SectionsWidget.cpp @@ -164,7 +164,7 @@ bool SectionsProxyModel::lessThan(const QModelIndex &left, const QModelIndex &ri SectionsWidget::SectionsWidget(MainWindow *main) : ListDockWidget(main) { setObjectName("SectionsWidget"); - setWindowTitle(QStringLiteral("Sections")); + setWindowTitle(tr("Sections")); setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); sectionsRefreshDeferrer = createRefreshDeferrer([this]() { refreshSections(); }); From d1da807de1fc5e8ee5f46f07d7acc6fb8d973a87 Mon Sep 17 00:00:00 2001 From: Anton Kochkov Date: Fri, 21 Jul 2023 16:13:26 +0800 Subject: [PATCH 36/50] Update Rizin to latest dev (#3207) --- rizin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rizin b/rizin index 09ec12d6..831c472f 160000 --- a/rizin +++ b/rizin @@ -1 +1 @@ -Subproject commit 09ec12d6078cdfbb7cfde23262a13cd03056f850 +Subproject commit 831c472f099dc76012273b7ab5667769aec66f1a From 7418f9c76c7dcd374f8d31d84f8d96e02c17801d Mon Sep 17 00:00:00 2001 From: frmdstryr Date: Sun, 30 Jul 2023 04:37:52 -0400 Subject: [PATCH 37/50] Show disassembly var tooltips (#3206) --- .gitignore | 4 + rizin | 2 +- src/common/Configuration.cpp | 10 + src/common/Configuration.h | 6 + src/common/DisassemblyPreview.cpp | 72 +++++ src/common/DisassemblyPreview.h | 8 + src/core/Cutter.cpp | 6 + src/core/CutterDescriptions.h | 1 + src/dialogs/preferences/AsmOptionsWidget.cpp | 8 + src/dialogs/preferences/AsmOptionsWidget.ui | 281 ++++++++++--------- src/widgets/DisassemblerGraphView.cpp | 49 ++-- src/widgets/DisassemblyWidget.cpp | 16 +- 12 files changed, 301 insertions(+), 162 deletions(-) diff --git a/.gitignore b/.gitignore index 4acf8d2e..f572d83c 100644 --- a/.gitignore +++ b/.gitignore @@ -94,3 +94,7 @@ docs/source/_build # Local gdb files .gdb_history .gdbinit + +# Kdevelop +.kdev/ +*.kdev4 diff --git a/rizin b/rizin index 831c472f..a3ad6221 160000 --- a/rizin +++ b/rizin @@ -1 +1 @@ -Subproject commit 831c472f099dc76012273b7ab5667769aec66f1a +Subproject commit a3ad6221fb2c727bd180709c764f1a23326f7f52 diff --git a/src/common/Configuration.cpp b/src/common/Configuration.cpp index 12af053f..5f186fcf 100644 --- a/src/common/Configuration.cpp +++ b/src/common/Configuration.cpp @@ -783,6 +783,16 @@ bool Configuration::getPreviewValue() const return s.value("asm.preview").toBool(); } +void Configuration::setShowVarTooltips(bool enabled) +{ + s.setValue("showVarTooltips", enabled); +} + +bool Configuration::getShowVarTooltips() const +{ + return s.value("showVarTooltips").toBool(); +} + bool Configuration::getGraphBlockEntryOffset() { return s.value("graphBlockEntryOffset", true).value(); diff --git a/src/common/Configuration.h b/src/common/Configuration.h index b6324705..9aae6573 100644 --- a/src/common/Configuration.h +++ b/src/common/Configuration.h @@ -215,6 +215,12 @@ public: void setPreviewValue(bool checked); bool getPreviewValue() const; + /** + * @brief Show tooltips for known values of registers, variables, and memory when debugging + */ + void setShowVarTooltips(bool enabled); + bool getShowVarTooltips() const; + /** * @brief Recently opened binaries, as shown in NewFileDialog. */ diff --git a/src/common/DisassemblyPreview.cpp b/src/common/DisassemblyPreview.cpp index 4861c769..8ce5c9cf 100644 --- a/src/common/DisassemblyPreview.cpp +++ b/src/common/DisassemblyPreview.cpp @@ -82,3 +82,75 @@ RVA DisassemblyPreview::readDisassemblyOffset(QTextCursor tc) return userData->line.offset; } + +typedef struct mmio_lookup_context +{ + QString selected; + RVA mmio_address; +} mmio_lookup_context_t; + +static bool lookup_mmio_addr_cb(void *user, const ut64 key, const void *value) +{ + mmio_lookup_context_t *ctx = (mmio_lookup_context_t *)user; + if (ctx->selected == (const char *)value) { + ctx->mmio_address = key; + return false; + } + return true; +} + +bool DisassemblyPreview::showDebugValueTooltip(QWidget *parent, const QPoint &pointOfEvent, + const QString &selectedText, const RVA offset) +{ + if (selectedText.isEmpty()) + return false; + + if (selectedText.at(0).isLetter()) { + { + const auto registerRefs = Core()->getRegisterRefValues(); + for (auto ® : 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; +} diff --git a/src/common/DisassemblyPreview.h b/src/common/DisassemblyPreview.h index 81e67ee8..2d7be0d1 100644 --- a/src/common/DisassemblyPreview.h +++ b/src/common/DisassemblyPreview.h @@ -41,5 +41,13 @@ bool showDisasPreview(QWidget *parent, const QPoint &pointOfEvent, const RVA off * @return The disassembly offset of the hovered asm text */ RVA readDisassemblyOffset(QTextCursor tc); + +/** + * @brief Show a QToolTip that shows the value of the highlighted register, variable, or memory + * @return True if the tooltip is shown + */ +bool showDebugValueTooltip(QWidget *parent, const QPoint &pointOfEvent, const QString &selectedText, + const RVA offset); + } #endif diff --git a/src/core/Cutter.cpp b/src/core/Cutter.cpp index 930921dd..93f15ec6 100644 --- a/src/core/Cutter.cpp +++ b/src/core/Cutter.cpp @@ -1804,6 +1804,12 @@ QList CutterCore::getVariables(RVA at) } desc.type = QString::fromUtf8(tn); rz_mem_free(tn); + + if (char *v = rz_core_analysis_var_display(core, var, false)) { + desc.value = QString::fromUtf8(v).trimmed(); + rz_mem_free(v); + } + ret.push_back(desc); } return ret; diff --git a/src/core/CutterDescriptions.h b/src/core/CutterDescriptions.h index 5f311be5..31e40261 100644 --- a/src/core/CutterDescriptions.h +++ b/src/core/CutterDescriptions.h @@ -357,6 +357,7 @@ struct VariableDescription RzAnalysisVarStorageType storageType; QString name; QString type; + QString value; }; struct RegisterRefValueDescription diff --git a/src/dialogs/preferences/AsmOptionsWidget.cpp b/src/dialogs/preferences/AsmOptionsWidget.cpp index dd5cb7a0..63f12215 100644 --- a/src/dialogs/preferences/AsmOptionsWidget.cpp +++ b/src/dialogs/preferences/AsmOptionsWidget.cpp @@ -65,6 +65,12 @@ AsmOptionsWidget::AsmOptionsWidget(PreferencesDialog *dialog) &AsmOptionsWidget::relOffCheckBoxToggled); connect(Core(), &CutterCore::asmOptionsChanged, this, &AsmOptionsWidget::updateAsmOptionsFromVars); + + connect(ui->varTooltipsCheckBox, &QCheckBox::toggled, [this](bool checked) { + Config()->setShowVarTooltips(checked); + triggerAsmOptionsChanged(); + }); + updateAsmOptionsFromVars(); } @@ -138,6 +144,8 @@ void AsmOptionsWidget::updateAsmOptionsFromVars() ui->previewCheckBox->setChecked(Config()->getPreviewValue()); ui->previewCheckBox->blockSignals(false); + qhelpers::setCheckedWithoutSignals(ui->varTooltipsCheckBox, Config()->getShowVarTooltips()); + QList::iterator confCheckbox; // Set the value for each checkbox in "checkboxes" as it exists in the configuration diff --git a/src/dialogs/preferences/AsmOptionsWidget.ui b/src/dialogs/preferences/AsmOptionsWidget.ui index f3f14952..ab4bcf5d 100644 --- a/src/dialogs/preferences/AsmOptionsWidget.ui +++ b/src/dialogs/preferences/AsmOptionsWidget.ui @@ -48,8 +48,8 @@ 0 0 - 582 - 766 + 686 + 886 @@ -65,51 +65,114 @@ Disassembly - - - - true - + + + + + Lowercase + + + + + Uppercase (asm.ucase) + + + + + Capitalize (asm.capitalize) + + - - - - 100 - - - 5 - - - - - + + - Align bytes to the left (asm.lbytes) - - - - - - - Show preview when hovering: - - - - - - - Syntax (asm.syntax): + Tabs in assembly (asm.tabs): Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - + + - Flags (asm.reloff.flags) + Display the bytes of each instruction (asm.bytes) + + + + + + + Show Disassembly as: + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 10 + + + + + + + + Display flags' real name (asm.flags.real) + + + + + + + + + Show offsets relative to: + + + + + + + Functions (asm.reloff) + + + + + + + + + The number of tabulate spaces after the offset (asm.tabs.off): + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + Indent disassembly based on reflines depth (asm.indent) + + + + + + + Show offsets (asm.offset) @@ -132,23 +195,47 @@ - - + + - Show offsets (asm.offset) + Show empty line after every basic block (asm.bb.line) - - + + - The number of tabulate spaces after the offset (asm.tabs.off): + Show preview when hovering + + + + + + + Syntax (asm.syntax): Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + Separate bytes with whitespace (asm.bytes.space) + + + + + + + 100 + + + 5 + + + @@ -163,13 +250,13 @@ - + - Separate bytes with whitespace (asm.bytes.space) + Align bytes to the left (asm.lbytes) - + 100 @@ -179,107 +266,27 @@ - - + + - Display the bytes of each instruction (asm.bytes) + Flags (asm.reloff.flags) - - - - - Lowercase - - - - - Uppercase (asm.ucase) - - - - - Capitalize (asm.capitalize) - - - - - - - - Show empty line after every basic block (asm.bb.line) - - - - - - - - - Show offsets relative to: - - - - - - - Functions (asm.reloff) - - - - - - - - - Qt::Vertical - - - QSizePolicy::Fixed - - - - 20 - 10 - - - - - - - - Show Disassembly as: - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + true - + - Tabs in assembly (asm.tabs): - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - - - - - - Indent disassembly based on reflines depth (asm.indent) - - - - - - - Display flags' real name (asm.flags.real) + Show known variable values when hovering @@ -415,8 +422,8 @@ 0 0 - 454 - 286 + 518 + 326 diff --git a/src/widgets/DisassemblerGraphView.cpp b/src/widgets/DisassemblerGraphView.cpp index 69f87e5b..acb732ae 100644 --- a/src/widgets/DisassemblerGraphView.cpp +++ b/src/widgets/DisassemblerGraphView.cpp @@ -534,33 +534,44 @@ GraphView::EdgeConfiguration DisassemblerGraphView::edgeConfiguration(GraphView: bool DisassemblerGraphView::eventFilter(QObject *obj, QEvent *event) { - if (event->type() == QEvent::Type::ToolTip && Config()->getGraphPreview()) { + if ((Config()->getGraphPreview() || Config()->getShowVarTooltips()) + && event->type() == QEvent::Type::ToolTip) { QHelpEvent *helpEvent = static_cast(event); QPoint pointOfEvent = helpEvent->globalPos(); QPoint point = viewToLogicalCoordinates(helpEvent->pos()); - GraphBlock *block = getBlockContaining(point); + if (auto block = getBlockContaining(point)) { + // Get pos relative to start of block + QPoint pos = point - QPoint(block->x, block->y); - if (block == nullptr) { - return false; - } + // offsetFrom is the address which on top the cursor triggered this + RVA offsetFrom = RVA_INVALID; - // offsetFrom is the address which on top the cursor triggered this - RVA offsetFrom = RVA_INVALID; + /* + * getAddrForMouseEvent() doesn't work for jmps, like + * getInstrForMouseEvent() with false as a 3rd argument. + */ + Instr *inst = getInstrForMouseEvent(*block, &pos, true); + if (inst != nullptr) { + offsetFrom = inst->addr; + } - /* - * getAddrForMouseEvent() doesn't work for jmps, like - * getInstrForMouseEvent() with false as a 3rd argument. - */ - Instr *inst = getInstrForMouseEvent(*block, &point, true); - if (inst != nullptr) { - offsetFrom = inst->addr; - } - - // Don't preview anything for a small scale - if (getViewScale() >= 0.8) { - return DisassemblyPreview::showDisasPreview(this, pointOfEvent, offsetFrom); + // Don't preview anything for a small scale + if (getViewScale() >= 0.8) { + if (Config()->getGraphPreview() + && DisassemblyPreview::showDisasPreview(this, pointOfEvent, offsetFrom)) { + return true; + } + if (Config()->getShowVarTooltips() && inst) { + auto token = getToken(inst, pos.x()); + if (token + && DisassemblyPreview::showDebugValueTooltip(this, pointOfEvent, + token->content, offsetFrom)) { + return true; + } + } + } } } return CutterGraphView::eventFilter(obj, event); diff --git a/src/widgets/DisassemblyWidget.cpp b/src/widgets/DisassemblyWidget.cpp index 59adbaa7..499bb442 100644 --- a/src/widgets/DisassemblyWidget.cpp +++ b/src/widgets/DisassemblyWidget.cpp @@ -636,17 +636,23 @@ bool DisassemblyWidget::eventFilter(QObject *obj, QEvent *event) return true; } - } else if (Config()->getPreviewValue() - && event->type() == QEvent::ToolTip - && obj == mDisasTextEdit->viewport()) { + } else if ((Config()->getPreviewValue() || Config()->getShowVarTooltips()) + && event->type() == QEvent::ToolTip && obj == mDisasTextEdit->viewport()) { QHelpEvent *helpEvent = static_cast(event); - auto cursorForWord = mDisasTextEdit->cursorForPosition(helpEvent->pos()); cursorForWord.select(QTextCursor::WordUnderCursor); RVA offsetFrom = DisassemblyPreview::readDisassemblyOffset(cursorForWord); - return DisassemblyPreview::showDisasPreview(this, helpEvent->globalPos(), offsetFrom); + if (Config()->getPreviewValue() + && DisassemblyPreview::showDisasPreview(this, helpEvent->globalPos(), offsetFrom)) { + return true; + } + if (Config()->getShowVarTooltips() + && DisassemblyPreview::showDebugValueTooltip( + this, helpEvent->globalPos(), cursorForWord.selectedText(), offsetFrom)) { + return true; + } } return MemoryDockWidget::eventFilter(obj, event); From de0e55d684553ff9284ed8b5908ef39cb6a6511b Mon Sep 17 00:00:00 2001 From: Anton Kochkov Date: Wed, 2 Aug 2023 14:30:05 +0800 Subject: [PATCH 38/50] python: use proper way to return None (#3209) --- src/common/PythonAPI.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/common/PythonAPI.cpp b/src/common/PythonAPI.cpp index dcad8e08..a09160f3 100644 --- a/src/common/PythonAPI.cpp +++ b/src/common/PythonAPI.cpp @@ -32,7 +32,7 @@ PyObject *api_refresh(PyObject *self, PyObject *args) Q_UNUSED(self); Q_UNUSED(args); Core()->triggerRefreshAll(); - return Py_None; + Py_RETURN_NONE; } PyObject *api_message(PyObject *self, PyObject *args, PyObject *kwargs) @@ -46,8 +46,7 @@ PyObject *api_message(PyObject *self, PyObject *args, PyObject *kwargs) return NULL; } Core()->message(QString(message), debug); - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } PyMethodDef CutterMethods[] = { From 08d5c7aed7294e49c53857feb255d7984d8e82d2 Mon Sep 17 00:00:00 2001 From: Anton Kochkov Date: Wed, 2 Aug 2023 15:21:53 +0800 Subject: [PATCH 39/50] python: disable deprecated API for newer versions (#3210) --- src/common/PythonManager.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/common/PythonManager.cpp b/src/common/PythonManager.cpp index 8e38a24b..3928936a 100644 --- a/src/common/PythonManager.cpp +++ b/src/common/PythonManager.cpp @@ -77,7 +77,9 @@ void PythonManager::initialize() #endif Py_Initialize(); // This function is deprecated does nothing starting from Python 3.9 +#if (PY_MAJOR_VERSION <= 3) && (PY_MICRO_VERSION < 9) PyEval_InitThreads(); +#endif pyThreadStateCounter = 1; // we have the thread now => 1 RegQtResImporter(); From 6a48b97402e40805aa27746e3daed520b1965f64 Mon Sep 17 00:00:00 2001 From: Anton Kochkov Date: Wed, 2 Aug 2023 18:03:52 +0800 Subject: [PATCH 40/50] Enable Ukrainian translation (#3213) --- cmake/Translations.cmake | 1 + 1 file changed, 1 insertion(+) diff --git a/cmake/Translations.cmake b/cmake/Translations.cmake index 266b5706..c8e5dfce 100644 --- a/cmake/Translations.cmake +++ b/cmake/Translations.cmake @@ -14,6 +14,7 @@ set(TS_FILES translations/ro/cutter_ro.ts translations/ru/cutter_ru.ts translations/tr/cutter_tr.ts + translations/uk/cutter_uk.ts translations/zh-CN/cutter_zh.ts ) # translations/ko/cutter_ko.ts problems with fonts From 3bcafe96251f6e41a8a54b736d44fe36bd515c91 Mon Sep 17 00:00:00 2001 From: Giovanni <561184+wargio@users.noreply.github.com> Date: Wed, 2 Aug 2023 23:04:28 +0800 Subject: [PATCH 41/50] Bump rizin dev (#3212) --- rizin | 2 +- src/core/Cutter.cpp | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/rizin b/rizin index a3ad6221..fe8ed0bf 160000 --- a/rizin +++ b/rizin @@ -1 +1 @@ -Subproject commit a3ad6221fb2c727bd180709c764f1a23326f7f52 +Subproject commit fe8ed0bfbe6f66a907e83e3f842840d1960e7fc5 diff --git a/src/core/Cutter.cpp b/src/core/Cutter.cpp index 93f15ec6..f9b0c3e4 100644 --- a/src/core/Cutter.cpp +++ b/src/core/Cutter.cpp @@ -3133,6 +3133,7 @@ QList CutterCore::getAllExports() } bool va = core->io->va || core->bin->is_debugger; + bool demangle = rz_config_get_b(core->config, "bin.demangle"); QList ret; for (const auto &symbol : CutterRzList(symbols)) { @@ -3141,7 +3142,7 @@ QList CutterCore::getAllExports() } RzBinSymNames sn = {}; - rz_core_sym_name_init(&sn, symbol); + rz_core_sym_name_init(&sn, symbol, demangle); ExportDescription exportDescription; exportDescription.vaddr = rva(bf->o, symbol->paddr, symbol->vaddr, va); From 3e74b9ad6769d00cc67d6c1817e591eea96a7e47 Mon Sep 17 00:00:00 2001 From: Giovanni <561184+wargio@users.noreply.github.com> Date: Thu, 3 Aug 2023 20:36:34 +0800 Subject: [PATCH 42/50] Fix arrow line calculation when disasm line is has multiple newlines. (#3217) --- src/core/Cutter.cpp | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/core/Cutter.cpp b/src/core/Cutter.cpp index f9b0c3e4..1c7803f1 100644 --- a/src/core/Cutter.cpp +++ b/src/core/Cutter.cpp @@ -4143,11 +4143,20 @@ QList CutterCore::disassembleLines(RVA offset, int lines) QList r; for (const auto &t : CutterPVector(vec.get())) { - DisassemblyLine line; - line.offset = t->offset; - line.text = ansiEscapeToHtml(t->text); - line.arrow = t->arrow; - r << line; + QString text = t->text; + QStringList tokens = text.split('\n'); + // text might contain multiple lines + // so we split them and keep only one + // arrow/jump to addr. + for (const auto &tok : tokens) { + DisassemblyLine line; + line.offset = t->offset; + line.text = ansiEscapeToHtml(tok); + line.arrow = t->arrow; + r << line; + // only the first one. + t->arrow = RVA_INVALID; + } } return r; } From b3181b5493b4951c863b3fea96863ce6df5a78ce Mon Sep 17 00:00:00 2001 From: Anton Kochkov Date: Thu, 3 Aug 2023 14:22:45 +0800 Subject: [PATCH 43/50] Add clang-format script --- scripts/clang-format.py | 123 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100755 scripts/clang-format.py diff --git a/scripts/clang-format.py b/scripts/clang-format.py new file mode 100755 index 00000000..36efbf57 --- /dev/null +++ b/scripts/clang-format.py @@ -0,0 +1,123 @@ +#!/usr/bin/env python3 +# +# SPDX-FileCopyrightText: 2021 Anton Kochkov +# 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() From 0d9851c1e0c601c2438b9e5122e2c1063ad26d5b Mon Sep 17 00:00:00 2001 From: Anton Kochkov Date: Thu, 3 Aug 2023 14:28:19 +0800 Subject: [PATCH 44/50] ci: run clang-format.py script --- .github/workflows/linter.yml | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index c9488b8e..3ec9d658 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -26,27 +26,34 @@ jobs: - '**.c' - '**.h' - '.github/workflows/linter.yml' + - 'scripts/clang-format.py' + - '_clang-format' clang-format: needs: changes - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 if: ${{ needs.changes.outputs.clang-format == 'true' }} steps: - name: Checkout repository uses: actions/checkout@v3 - - name: Install wget - run: sudo apt --assume-yes install wget + - name: Install wget, software-properties-common, lsb-release (dependencies of LLVM install script) + run: sudo apt --assume-yes install wget software-properties-common lsb-release - - name: Install automatic llvm (stable branch) - run: sudo bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)" + - name: Uninstall old conflicting packages + run: sudo apt purge --assume-yes --auto-remove llvm python3-lldb-14 llvm-14 - - name: Install clang-format-11 - run: sudo apt --assume-yes install clang-format-11 + - name: Install automatic LLVM 16 + run: wget https://apt.llvm.org/llvm.sh -O /tmp/llvm-install.sh; chmod +x /tmp/llvm-install.sh; sudo /tmp/llvm-install.sh 16 + + - name: Install clang-format-16 + run: sudo apt --assume-yes install clang-format-16 - name: Install gitpython run: sudo pip install gitpython - name: Run clang-format run: | - find ./src -regex '.*\.\(cpp\|h\|c\)' -exec clang-format -style=file --dry-run --Werror {} \; + sudo update-alternatives --install /usr/bin/clang-format clang-format /usr/bin/clang-format-16 160 + clang-format --version + python scripts/clang-format.py --check --verbose From 041118dbd75b4e01e03a6a7b1840bc34808f6986 Mon Sep 17 00:00:00 2001 From: Anton Kochkov Date: Thu, 3 Aug 2023 14:34:37 +0800 Subject: [PATCH 45/50] Reformat the code --- src/Main.cpp | 8 ++++---- src/common/HighDpiPixmap.cpp | 4 ++-- src/common/PythonManager.cpp | 8 ++++---- src/common/SettingsUpgrade.h | 3 ++- src/core/CutterCommon.h | 1 - src/core/RizinCpp.cpp | 1 - src/core/RizinCpp.h | 3 +-- src/dialogs/EditMethodDialog.h | 2 +- src/dialogs/GlibcHeapBinsDialog.cpp | 3 +-- src/dialogs/NewFileDialog.cpp | 3 ++- src/dialogs/TypesInteractionDialog.cpp | 10 ++++++---- src/widgets/ColorThemeComboBox.cpp | 3 +-- src/widgets/ColorThemeListView.cpp | 10 +++++----- src/widgets/ComboQuickFilterView.cpp | 3 +-- src/widgets/DebugActions.cpp | 2 +- src/widgets/FunctionsWidget.cpp | 5 ++--- src/widgets/HeapBinsGraphView.cpp | 3 +-- src/widgets/HexWidget.cpp | 4 ++-- src/widgets/HexdumpWidget.cpp | 15 ++++++++++----- src/widgets/SectionsWidget.h | 3 ++- src/widgets/VisualNavbar.cpp | 3 ++- 21 files changed, 50 insertions(+), 47 deletions(-) diff --git a/src/Main.cpp b/src/Main.cpp index 8650b94e..2c4a62ff 100644 --- a/src/Main.cpp +++ b/src/Main.cpp @@ -13,7 +13,7 @@ * @brief Attempt to connect to a parent console and configure outputs. */ #ifdef Q_OS_WIN -#include +# include static void connectToConsole() { @@ -67,9 +67,9 @@ int main(int argc, char *argv[]) #endif QCoreApplication::setApplicationName("cutter"); - // Importing settings after setting rename, needs separate handling in addition to regular version to version upgrade. - if (Cutter::shouldOfferSettingImport()) - { + // Importing settings after setting rename, needs separate handling in addition to regular + // version to version upgrade. + if (Cutter::shouldOfferSettingImport()) { Cutter::showSettingImportDialog(argc, argv); } diff --git a/src/common/HighDpiPixmap.cpp b/src/common/HighDpiPixmap.cpp index bd7ca1be..c21cd2dd 100644 --- a/src/common/HighDpiPixmap.cpp +++ b/src/common/HighDpiPixmap.cpp @@ -16,8 +16,8 @@ static qreal GetDevicePixelRatio(qreal devicePixelRatio) } HighDpiPixmap::HighDpiPixmap(int width, int height, qreal devicePixelRatio) - : QPixmap(int(width *GetDevicePixelRatio(devicePixelRatio)), - int(height *GetDevicePixelRatio(devicePixelRatio))) + : QPixmap(int(width * GetDevicePixelRatio(devicePixelRatio)), + int(height * GetDevicePixelRatio(devicePixelRatio))) { setDevicePixelRatio(GetDevicePixelRatio(devicePixelRatio)); } diff --git a/src/common/PythonManager.cpp b/src/common/PythonManager.cpp index 3928936a..eb43b018 100644 --- a/src/common/PythonManager.cpp +++ b/src/common/PythonManager.cpp @@ -13,10 +13,10 @@ #ifdef CUTTER_ENABLE_PYTHON_BINDINGS # include # include -#ifdef HAVE_PYSIDECLEANUP - // This header is introduced in PySide 6 -# include -#endif +# ifdef HAVE_PYSIDECLEANUP +// This header is introduced in PySide 6 +# include +# endif # include #endif diff --git a/src/common/SettingsUpgrade.h b/src/common/SettingsUpgrade.h index 5e445abd..80541673 100644 --- a/src/common/SettingsUpgrade.h +++ b/src/common/SettingsUpgrade.h @@ -7,7 +7,8 @@ namespace Cutter { void initializeSettings(); /** - * @brief Check if Cutter should offer importing settings from version that can't be directly updated. + * @brief Check if Cutter should offer importing settings from version that can't be directly + * updated. * @return True if this is first time running Cutter and r2 based Cutter <= 1.12 settings exist. */ bool shouldOfferSettingImport(); diff --git a/src/core/CutterCommon.h b/src/core/CutterCommon.h index a118cd00..20183e55 100644 --- a/src/core/CutterCommon.h +++ b/src/core/CutterCommon.h @@ -15,7 +15,6 @@ # undef max #endif // Q_OS_WIN - // Global information for Cutter #define APPNAME "Cutter" diff --git a/src/core/RizinCpp.cpp b/src/core/RizinCpp.cpp index 7418a3f9..594dfff6 100644 --- a/src/core/RizinCpp.cpp +++ b/src/core/RizinCpp.cpp @@ -1,2 +1 @@ #include "RizinCpp.h" - diff --git a/src/core/RizinCpp.h b/src/core/RizinCpp.h index e39f964b..85381717 100644 --- a/src/core/RizinCpp.h +++ b/src/core/RizinCpp.h @@ -46,8 +46,7 @@ static inline auto fromOwned(RZ_OWN RzPVector *data) return { data, {} }; } -static inline auto fromOwned(RZ_OWN RzList *data) - -> UniquePtrCP +static inline auto fromOwned(RZ_OWN RzList *data) -> UniquePtrCP { return { data, {} }; } diff --git a/src/dialogs/EditMethodDialog.h b/src/dialogs/EditMethodDialog.h index 1f388b89..7da8aab3 100644 --- a/src/dialogs/EditMethodDialog.h +++ b/src/dialogs/EditMethodDialog.h @@ -74,7 +74,7 @@ private: QString fixedClass; bool inputValid(); - static QString convertRealNameToName(const QString& realName); + static QString convertRealNameToName(const QString &realName); }; #endif // EDITMETHODDIALOG_H diff --git a/src/dialogs/GlibcHeapBinsDialog.cpp b/src/dialogs/GlibcHeapBinsDialog.cpp index aea3cdd1..1de43487 100644 --- a/src/dialogs/GlibcHeapBinsDialog.cpp +++ b/src/dialogs/GlibcHeapBinsDialog.cpp @@ -49,8 +49,7 @@ void GlibcHeapBinsDialog::setChainInfo(int index) RzHeapChunkListItem *item; RzList *chunks = binsModel->getChunks(index); QString chainInfo; - CutterRzListForeach(chunks, iter, RzHeapChunkListItem, item) - { + CutterRzListForeach (chunks, iter, RzHeapChunkListItem, item) { chainInfo += " → " + RzAddressString(item->addr); } diff --git a/src/dialogs/NewFileDialog.cpp b/src/dialogs/NewFileDialog.cpp index 58d0d391..94bcae74 100644 --- a/src/dialogs/NewFileDialog.cpp +++ b/src/dialogs/NewFileDialog.cpp @@ -287,7 +287,8 @@ void NewFileDialog::fillIOPluginsList() { ui->ioPlugin->clear(); ui->ioPlugin->addItem("file://"); - ui->ioPlugin->setItemData(0, tr("Open a file without additional options/settings."), Qt::ToolTipRole); + ui->ioPlugin->setItemData(0, tr("Open a file without additional options/settings."), + Qt::ToolTipRole); int index = 1; QList ioPlugins = Core()->getRIOPluginDescriptions(); diff --git a/src/dialogs/TypesInteractionDialog.cpp b/src/dialogs/TypesInteractionDialog.cpp index be363a30..5b528005 100644 --- a/src/dialogs/TypesInteractionDialog.cpp +++ b/src/dialogs/TypesInteractionDialog.cpp @@ -66,12 +66,14 @@ void TypesInteractionDialog::done(int r) bool success; if (!typeName.isEmpty()) { success = rz_type_db_edit_base_type( - core->analysis->typedb, this->typeName.toUtf8().constData(), - ui->plainTextEdit->toPlainText().toUtf8().constData()); + core->analysis->typedb, this->typeName.toUtf8().constData(), + ui->plainTextEdit->toPlainText().toUtf8().constData()); } else { char *error_msg = NULL; - success = rz_type_parse_string_stateless(core->analysis->typedb->parser, - ui->plainTextEdit->toPlainText().toUtf8().constData(), &error_msg) == 0; + success = rz_type_parse_string_stateless( + core->analysis->typedb->parser, + ui->plainTextEdit->toPlainText().toUtf8().constData(), &error_msg) + == 0; if (error_msg) { RZ_LOG_ERROR("%s\n", error_msg); rz_mem_free(error_msg); diff --git a/src/widgets/ColorThemeComboBox.cpp b/src/widgets/ColorThemeComboBox.cpp index 35b638ec..4293895c 100644 --- a/src/widgets/ColorThemeComboBox.cpp +++ b/src/widgets/ColorThemeComboBox.cpp @@ -22,8 +22,7 @@ void ColorThemeComboBox::updateFromConfig(bool interfaceThemeChanged) clear(); for (const QString &theme : themes) { - if (ThemeWorker().isCustomTheme(theme) - || !Configuration::relevantThemes[theme] + if (ThemeWorker().isCustomTheme(theme) || !Configuration::relevantThemes[theme] || (Configuration::cutterInterfaceThemesList()[curInterfaceThemeIndex].flag & Configuration::relevantThemes[theme])) { addItem(theme); diff --git a/src/widgets/ColorThemeListView.cpp b/src/widgets/ColorThemeListView.cpp index 84df6d67..c662a530 100644 --- a/src/widgets/ColorThemeListView.cpp +++ b/src/widgets/ColorThemeListView.cpp @@ -128,8 +128,8 @@ void ColorOptionDelegate::paint(QPainter *painter, const QStyleOptionViewItem &o painter->setPen(qApp->palette().text().color()); QFontMetrics fm2 = QFontMetrics(painter->font()); - QString name = fm2.elidedText(optionInfoMap__[currCO.optionName].displayingtext, - Qt::ElideRight, optionNameRect.width()); + QString name = fm2.elidedText(optionInfoMap__[currCO.optionName].displayingtext, Qt::ElideRight, + optionNameRect.width()); painter->drawText(optionNameRect, name); QPainterPath roundedOptionRect; @@ -157,9 +157,9 @@ void ColorOptionDelegate::paint(QPainter *painter, const QStyleOptionViewItem &o painter->fillPath(roundedColorRect, currCO.color); QFontMetrics fm3 = QFontMetrics(painter->font()); - QString desc = fm3.elidedText( - currCO.optionName + ": " + optionInfoMap__[currCO.optionName].info, Qt::ElideRight, - descTextRect.width()); + QString desc = + fm3.elidedText(currCO.optionName + ": " + optionInfoMap__[currCO.optionName].info, + Qt::ElideRight, descTextRect.width()); painter->setPen(qApp->palette().text().color()); painter->setBrush(qApp->palette().text()); painter->drawText(descTextRect, desc); diff --git a/src/widgets/ComboQuickFilterView.cpp b/src/widgets/ComboQuickFilterView.cpp index 04580584..ec7574b5 100644 --- a/src/widgets/ComboQuickFilterView.cpp +++ b/src/widgets/ComboQuickFilterView.cpp @@ -12,8 +12,7 @@ ComboQuickFilterView::ComboQuickFilterView(QWidget *parent) connect(debounceTimer, &QTimer::timeout, this, [this]() { emit filterTextChanged(ui->lineEdit->text()); }); - connect(ui->lineEdit, &QLineEdit::textChanged, this, - [this]() { debounceTimer->start(150); }); + connect(ui->lineEdit, &QLineEdit::textChanged, this, [this]() { debounceTimer->start(150); }); } ComboQuickFilterView::~ComboQuickFilterView() diff --git a/src/widgets/DebugActions.cpp b/src/widgets/DebugActions.cpp index 8745cb86..76295793 100644 --- a/src/widgets/DebugActions.cpp +++ b/src/widgets/DebugActions.cpp @@ -300,7 +300,7 @@ void DebugActions::onAttachedRemoteDebugger(bool successfully) // TODO(#2829): Investigate why this is happening if (remoteDialog == nullptr) return; - + if (!successfully) { QMessageBox msgBox; msgBox.setText(tr("Error connecting.")); diff --git a/src/widgets/FunctionsWidget.cpp b/src/widgets/FunctionsWidget.cpp index 8c439b1d..24d0d129 100644 --- a/src/widgets/FunctionsWidget.cpp +++ b/src/widgets/FunctionsWidget.cpp @@ -234,9 +234,8 @@ QVariant FunctionModel::data(const QModelIndex &index, int role) const QStringList summary {}; { auto seeker = Core()->seekTemp(function.offset); - auto strings = fromOwnedCharPtr( - rz_core_print_disasm_strings(Core()->core(), RZ_CORE_DISASM_STRINGS_MODE_FUNCTION, - 0, NULL)); + auto strings = fromOwnedCharPtr(rz_core_print_disasm_strings( + Core()->core(), RZ_CORE_DISASM_STRINGS_MODE_FUNCTION, 0, NULL)); summary = strings.split('\n', CUTTER_QT_SKIP_EMPTY_PARTS); } diff --git a/src/widgets/HeapBinsGraphView.cpp b/src/widgets/HeapBinsGraphView.cpp index 0e61ae02..c81c7ae3 100644 --- a/src/widgets/HeapBinsGraphView.cpp +++ b/src/widgets/HeapBinsGraphView.cpp @@ -36,8 +36,7 @@ void HeapBinsGraphView::loadCurrentGraph() || QString(heapBin->type) == QString("Tcache"); // store info about the chunks in a vector for easy access - CutterRzListForeach(heapBin->chunks, iter, RzHeapChunkListItem, item) - { + CutterRzListForeach (heapBin->chunks, iter, RzHeapChunkListItem, item) { GraphHeapChunk graphHeapChunk; graphHeapChunk.addr = item->addr; RzHeapChunkSimple *chunkInfo = Core()->getHeapChunk(item->addr); diff --git a/src/widgets/HexWidget.cpp b/src/widgets/HexWidget.cpp index c9d09a71..c89337c6 100644 --- a/src/widgets/HexWidget.cpp +++ b/src/widgets/HexWidget.cpp @@ -1407,8 +1407,8 @@ void HexWidget::w_writeRandom() } bool ok = false; - int nbytes = QInputDialog::getInt(this, tr("Write random bytes"), tr("Number of bytes:"), size, 1, - 0x7FFFFFFF, 1, &ok); + int nbytes = QInputDialog::getInt(this, tr("Write random bytes"), tr("Number of bytes:"), size, + 1, 0x7FFFFFFF, 1, &ok); if (!ok) { return; } diff --git a/src/widgets/HexdumpWidget.cpp b/src/widgets/HexdumpWidget.cpp index e772da5f..306582d9 100644 --- a/src/widgets/HexdumpWidget.cpp +++ b/src/widgets/HexdumpWidget.cpp @@ -248,19 +248,24 @@ void HexdumpWidget::updateParseWindow(RVA start_address, int size) ut64 old_offset = core->offset; rz_core_seek(core, start_address, true); ut8 *block = core->block; - char *digest = rz_hash_cfg_calculate_small_block_string(core->hash, "md5", block, size, &digest_size, false); + char *digest = rz_hash_cfg_calculate_small_block_string(core->hash, "md5", block, size, + &digest_size, false); ui->bytesMD5->setText(QString(digest)); free(digest); - digest = rz_hash_cfg_calculate_small_block_string(core->hash, "sha1", block, size, &digest_size, false); + digest = rz_hash_cfg_calculate_small_block_string(core->hash, "sha1", block, size, + &digest_size, false); ui->bytesSHA1->setText(QString(digest)); free(digest); - digest = rz_hash_cfg_calculate_small_block_string(core->hash, "sha256", block, size, &digest_size, false); + digest = rz_hash_cfg_calculate_small_block_string(core->hash, "sha256", block, size, + &digest_size, false); ui->bytesSHA256->setText(QString(digest)); free(digest); - digest = rz_hash_cfg_calculate_small_block_string(core->hash, "crc32", block, size, &digest_size, false); + digest = rz_hash_cfg_calculate_small_block_string(core->hash, "crc32", block, size, + &digest_size, false); ui->bytesCRC32->setText(QString(digest)); free(digest); - digest = rz_hash_cfg_calculate_small_block_string(core->hash, "entropy", block, size, &digest_size, false); + digest = rz_hash_cfg_calculate_small_block_string(core->hash, "entropy", block, size, + &digest_size, false); ui->bytesEntropy->setText(QString(digest)); free(digest); rz_core_seek(core, old_offset, true); diff --git a/src/widgets/SectionsWidget.h b/src/widgets/SectionsWidget.h index 8fcdafcf..7a20b18f 100644 --- a/src/widgets/SectionsWidget.h +++ b/src/widgets/SectionsWidget.h @@ -55,7 +55,8 @@ public: 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; + 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; diff --git a/src/widgets/VisualNavbar.cpp b/src/widgets/VisualNavbar.cpp index 3807fe0b..0a3f1409 100644 --- a/src/widgets/VisualNavbar.cpp +++ b/src/widgets/VisualNavbar.cpp @@ -140,7 +140,8 @@ void VisualNavbar::fetchStats() if (to < from) { return; } - stats.reset(rz_core_analysis_get_stats(core, from, to, RZ_MAX(1, (to + 1 - from) / blocksCount))); + stats.reset( + rz_core_analysis_get_stats(core, from, to, RZ_MAX(1, (to + 1 - from) / blocksCount))); } enum class DataType : int { Empty, Code, String, Symbol, Count }; From 3d30892a308fdae0425483f999975d868afaf44f Mon Sep 17 00:00:00 2001 From: Anton Kochkov Date: Fri, 4 Aug 2023 10:17:35 +0800 Subject: [PATCH 46/50] Add Global Vars widget, dialog, context menus (#3203) * Add global variables support - Add Globals widget - Add global variable add/modify dialog - Add "Add at" context submenu in Disassembly widget Co-authored-by: Giovanni <561184+wargio@users.noreply.github.com> --------- Co-authored-by: Giovanni <561184+wargio@users.noreply.github.com> --- src/CMakeLists.txt | 5 + src/core/Cutter.cpp | 123 +++++++++++++- src/core/Cutter.h | 10 ++ src/core/CutterDescriptions.h | 8 + src/core/MainWindow.cpp | 3 + src/core/MainWindow.h | 2 + src/dialogs/GlobalVariableDialog.cpp | 71 ++++++++ src/dialogs/GlobalVariableDialog.h | 32 ++++ src/dialogs/GlobalVariableDialog.ui | 100 +++++++++++ src/menus/DisassemblyContextMenu.cpp | 43 ++++- src/menus/DisassemblyContextMenu.h | 4 + src/widgets/DecompilerWidget.cpp | 1 + src/widgets/DisassemblerGraphView.cpp | 1 + src/widgets/DisassemblyWidget.cpp | 1 + src/widgets/GlobalsWidget.cpp | 228 ++++++++++++++++++++++++++ src/widgets/GlobalsWidget.h | 89 ++++++++++ src/widgets/GlobalsWidget.ui | 107 ++++++++++++ src/widgets/VisualNavbar.cpp | 1 + 18 files changed, 823 insertions(+), 6 deletions(-) create mode 100644 src/dialogs/GlobalVariableDialog.cpp create mode 100644 src/dialogs/GlobalVariableDialog.h create mode 100644 src/dialogs/GlobalVariableDialog.ui create mode 100644 src/widgets/GlobalsWidget.cpp create mode 100644 src/widgets/GlobalsWidget.h create mode 100644 src/widgets/GlobalsWidget.ui diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0d433df0..1a753548 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -18,6 +18,7 @@ set(SOURCES dialogs/CommentsDialog.cpp dialogs/EditInstructionDialog.cpp dialogs/FlagDialog.cpp + dialogs/GlobalVariableDialog.cpp dialogs/RemoteDebugDialog.cpp dialogs/NativeDebugDialog.cpp dialogs/XrefsDialog.cpp @@ -36,6 +37,7 @@ set(SOURCES widgets/ExportsWidget.cpp widgets/FlagsWidget.cpp widgets/FunctionsWidget.cpp + widgets/GlobalsWidget.cpp widgets/ImportsWidget.cpp widgets/Omnibar.cpp widgets/RelocsWidget.cpp @@ -172,6 +174,7 @@ set(HEADER_FILES dialogs/CommentsDialog.h dialogs/EditInstructionDialog.h dialogs/FlagDialog.h + dialogs/GlobalVariableDialog.h dialogs/RemoteDebugDialog.h dialogs/NativeDebugDialog.h dialogs/XrefsDialog.h @@ -327,6 +330,7 @@ set(UI_FILES dialogs/CommentsDialog.ui dialogs/EditInstructionDialog.ui dialogs/FlagDialog.ui + dialogs/GlobalVariableDialog.ui dialogs/RemoteDebugDialog.ui dialogs/NativeDebugDialog.ui dialogs/XrefsDialog.ui @@ -338,6 +342,7 @@ set(UI_FILES widgets/Dashboard.ui widgets/EntrypointWidget.ui widgets/FlagsWidget.ui + widgets/GlobalsWidget.ui widgets/StringsWidget.ui widgets/HexdumpWidget.ui dialogs/preferences/PreferencesDialog.ui diff --git a/src/core/Cutter.cpp b/src/core/Cutter.cpp index 1c7803f1..c1ce9d21 100644 --- a/src/core/Cutter.cpp +++ b/src/core/Cutter.cpp @@ -1815,6 +1815,32 @@ QList CutterCore::getVariables(RVA at) return ret; } +QList CutterCore::getAllGlobals() +{ + CORE_LOCK(); + RzListIter *it; + + QList 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 CutterCore::getRegisterRefValues() { QVector result; @@ -4022,6 +4048,99 @@ QList CutterCore::getXRefs(RVA addr, bool to, bool whole_functi return xrefList; } +void CutterCore::addGlobalVariable(RVA offset, QString name, QString typ) +{ + name = sanitizeStringForCommand(name); + CORE_LOCK(); + char *errmsg = NULL; + RzType *globType = rz_type_parse_string_single(core->analysis->typedb->parser, + typ.toStdString().c_str(), &errmsg); + if (errmsg) { + qWarning() << tr("Error parsing type: \"%1\" message: ").arg(typ) << errmsg; + free(errmsg); + return; + } + if (!rz_analysis_var_global_create(core->analysis, name.toStdString().c_str(), globType, + offset)) { + qWarning() << tr("Error creating global variable: \"%1\"").arg(name); + return; + } + + emit globalVarsChanged(); +} + +void CutterCore::modifyGlobalVariable(RVA offset, QString name, QString typ) +{ + name = sanitizeStringForCommand(name); + CORE_LOCK(); + RzAnalysisVarGlobal *glob = rz_analysis_var_global_get_byaddr_at(core->analysis, offset); + if (!glob) { + return; + } + // Compare if the name is not the same - also rename it + if (name.compare(glob->name)) { + rz_analysis_var_global_rename(core->analysis, glob->name, name.toStdString().c_str()); + } + char *errmsg = NULL; + RzType *globType = rz_type_parse_string_single(core->analysis->typedb->parser, + typ.toStdString().c_str(), &errmsg); + if (errmsg) { + qWarning() << tr("Error parsing type: \"%1\" message: ").arg(typ) << errmsg; + free(errmsg); + return; + } + rz_analysis_var_global_set_type(glob, globType); + + emit globalVarsChanged(); +} + +void CutterCore::delGlobalVariable(QString name) +{ + name = sanitizeStringForCommand(name); + CORE_LOCK(); + rz_analysis_var_global_delete_byname(core->analysis, name.toStdString().c_str()); + + emit globalVarsChanged(); +} + +void CutterCore::delGlobalVariable(RVA offset) +{ + CORE_LOCK(); + rz_analysis_var_global_delete_byaddr_at(core->analysis, offset); + + emit globalVarsChanged(); +} + +QString CutterCore::getGlobalVariableType(QString name) +{ + name = sanitizeStringForCommand(name); + CORE_LOCK(); + RzAnalysisVarGlobal *glob = + rz_analysis_var_global_get_byname(core->analysis, name.toStdString().c_str()); + if (!glob) { + return QString(""); + } + const char *gtype = rz_type_as_string(core->analysis->typedb, glob->type); + if (!gtype) { + return QString(""); + } + return QString(gtype); +} + +QString CutterCore::getGlobalVariableType(RVA offset) +{ + CORE_LOCK(); + RzAnalysisVarGlobal *glob = rz_analysis_var_global_get_byaddr_at(core->analysis, offset); + if (!glob) { + return QString(""); + } + const char *gtype = rz_type_as_string(core->analysis->typedb, glob->type); + if (!gtype) { + return QString(""); + } + return QString(gtype); +} + void CutterCore::addFlag(RVA offset, QString name, RVA size) { name = sanitizeStringForCommand(name); @@ -4536,9 +4655,9 @@ char *CutterCore::getTextualGraphAt(RzCoreGraphType type, RzCoreGraphFormat form RzGraph *graph = rz_core_graph(core, type, address); if (!graph) { if (address == RVA_INVALID) { - qWarning() << "Cannot get global graph"; + qWarning() << tr("Cannot get global graph"); } else { - qWarning() << "Cannot get graph at " << RzAddressString(address); + qWarning() << tr("Cannot get graph at ") << RzAddressString(address); } return nullptr; } diff --git a/src/core/Cutter.h b/src/core/Cutter.h index 14fbfde7..ff2b4682 100644 --- a/src/core/Cutter.h +++ b/src/core/Cutter.h @@ -239,6 +239,14 @@ public: QString nearestFlag(RVA offset, RVA *flagOffsetOut); void triggerFlagsChanged(); + /* Global Variables */ + void addGlobalVariable(RVA offset, QString name, QString typ); + void delGlobalVariable(QString name); + void delGlobalVariable(RVA offset); + void modifyGlobalVariable(RVA offset, QString name, QString typ); + QString getGlobalVariableType(QString name); + QString getGlobalVariableType(RVA offset); + /* Edition functions */ PRzAnalysisBytes getRzAnalysisBytesSingle(RVA addr); QString getInstructionBytes(RVA addr); @@ -584,6 +592,7 @@ public: QList getAllExports(); QList getAllSymbols(); QList getAllHeaders(); + QList getAllGlobals(); QList getSignaturesDB(); QList getAllComments(const QString &filterType); QList getAllRelocs(); @@ -750,6 +759,7 @@ signals: void functionRenamed(const RVA offset, const QString &new_name); void varsChanged(); + void globalVarsChanged(); void functionsChanged(); void flagsChanged(); void commentsChanged(RVA addr); diff --git a/src/core/CutterDescriptions.h b/src/core/CutterDescriptions.h index 31e40261..bb04e12f 100644 --- a/src/core/CutterDescriptions.h +++ b/src/core/CutterDescriptions.h @@ -360,6 +360,13 @@ struct VariableDescription QString value; }; +struct GlobalDescription +{ + RVA addr; + QString type; + QString name; +}; + struct RegisterRefValueDescription { QString name; @@ -407,6 +414,7 @@ Q_DECLARE_METATYPE(RelocDescription) Q_DECLARE_METATYPE(StringDescription) Q_DECLARE_METATYPE(FlagspaceDescription) Q_DECLARE_METATYPE(FlagDescription) +Q_DECLARE_METATYPE(GlobalDescription) Q_DECLARE_METATYPE(XrefDescription) Q_DECLARE_METATYPE(EntrypointDescription) Q_DECLARE_METATYPE(RzBinPluginDescription) diff --git a/src/core/MainWindow.cpp b/src/core/MainWindow.cpp index 3e259734..e7364d43 100644 --- a/src/core/MainWindow.cpp +++ b/src/core/MainWindow.cpp @@ -31,6 +31,7 @@ #include "widgets/DisassemblerGraphView.h" #include "widgets/GraphView.h" #include "widgets/GraphWidget.h" +#include "widgets/GlobalsWidget.h" #include "widgets/OverviewWidget.h" #include "widgets/OverviewView.h" #include "widgets/FunctionsWidget.h" @@ -401,6 +402,7 @@ void MainWindow::initDocks() sectionsDock = new SectionsWidget(this), segmentsDock = new SegmentsWidget(this), symbolsDock = new SymbolsWidget(this), + globalsDock = new GlobalsWidget(this), vTablesDock = new VTablesWidget(this), flirtDock = new FlirtWidget(this), rzGraphDock = new RizinGraphWidget(this), @@ -905,6 +907,7 @@ void MainWindow::restoreDocks() tabifyDockWidget(dashboardDock, headersDock); tabifyDockWidget(dashboardDock, flirtDock); tabifyDockWidget(dashboardDock, symbolsDock); + tabifyDockWidget(dashboardDock, globalsDock); tabifyDockWidget(dashboardDock, classesDock); tabifyDockWidget(dashboardDock, resourcesDock); tabifyDockWidget(dashboardDock, vTablesDock); diff --git a/src/core/MainWindow.h b/src/core/MainWindow.h index 733e7ea5..72a265b8 100644 --- a/src/core/MainWindow.h +++ b/src/core/MainWindow.h @@ -26,6 +26,7 @@ class FunctionsWidget; class ImportsWidget; class ExportsWidget; class SymbolsWidget; +class GlobalsWidget; class RelocsWidget; class CommentsWidget; class StringsWidget; @@ -240,6 +241,7 @@ private: TypesWidget *typesDock = nullptr; SearchWidget *searchDock = nullptr; SymbolsWidget *symbolsDock = nullptr; + GlobalsWidget *globalsDock = nullptr; RelocsWidget *relocsDock = nullptr; CommentsWidget *commentsDock = nullptr; StringsWidget *stringsDock = nullptr; diff --git a/src/dialogs/GlobalVariableDialog.cpp b/src/dialogs/GlobalVariableDialog.cpp new file mode 100644 index 00000000..83877318 --- /dev/null +++ b/src/dialogs/GlobalVariableDialog.cpp @@ -0,0 +1,71 @@ +#include "GlobalVariableDialog.h" +#include "ui_GlobalVariableDialog.h" + +#include +#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); +} diff --git a/src/dialogs/GlobalVariableDialog.h b/src/dialogs/GlobalVariableDialog.h new file mode 100644 index 00000000..5100b367 --- /dev/null +++ b/src/dialogs/GlobalVariableDialog.h @@ -0,0 +1,32 @@ +#ifndef GLOBALVARIABLEDIALOG_H +#define GLOBALVARIABLEDIALOG_H + +#include +#include +#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; + RVA offset; + QString globalVariableName; + RVA globalVariableOffset; + QString typ; +}; + +#endif // GLOBALVARIABLEDIALOG_H diff --git a/src/dialogs/GlobalVariableDialog.ui b/src/dialogs/GlobalVariableDialog.ui new file mode 100644 index 00000000..c45ea8a5 --- /dev/null +++ b/src/dialogs/GlobalVariableDialog.ui @@ -0,0 +1,100 @@ + + + GlobalVariableDialog + + + + 0 + 0 + 452 + 121 + + + + Add Global Variable + + + + + + Add global variable at + + + + + + + 12 + + + + + + 75 + true + + + + Name: + + + + + + + false + + + + + + + + + + + 100 + 16777215 + + + + int + + + false + + + + + + + + + + + 75 + true + + + + Type: + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + diff --git a/src/menus/DisassemblyContextMenu.cpp b/src/menus/DisassemblyContextMenu.cpp index 300283cc..b2cb760d 100644 --- a/src/menus/DisassemblyContextMenu.cpp +++ b/src/menus/DisassemblyContextMenu.cpp @@ -3,6 +3,7 @@ #include "dialogs/EditInstructionDialog.h" #include "dialogs/CommentsDialog.h" #include "dialogs/FlagDialog.h" +#include "dialogs/GlobalVariableDialog.h" #include "dialogs/XrefsDialog.h" #include "dialogs/EditVariablesDialog.h" #include "dialogs/SetToDataDialog.h" @@ -34,6 +35,7 @@ DisassemblyContextMenu::DisassemblyContextMenu(QWidget *parent, MainWindow *main actionAnalyzeFunction(this), actionEditFunction(this), actionRename(this), + actionGlobalVar(this), actionSetFunctionVarTypes(this), actionXRefs(this), actionXRefsForVariables(this), @@ -83,10 +85,6 @@ DisassemblyContextMenu::DisassemblyContextMenu(QWidget *parent, MainWindow *main getCommentSequence()); addAction(&actionAddComment); - initAction(&actionRename, tr("Rename or add flag"), SLOT(on_actionRename_triggered()), - getRenameSequence()); - addAction(&actionRename); - initAction(&actionSetFunctionVarTypes, tr("Re-type Local Variables"), SLOT(on_actionSetFunctionVarTypes_triggered()), getRetypeSequence()); addAction(&actionSetFunctionVarTypes); @@ -112,6 +110,8 @@ DisassemblyContextMenu::DisassemblyContextMenu(QWidget *parent, MainWindow *main addSeparator(); + addAddAtMenu(); + addSetBaseMenu(); addSetBitsMenu(); @@ -166,6 +166,19 @@ QWidget *DisassemblyContextMenu::parentForDialog() return parentWidget(); } +void DisassemblyContextMenu::addAddAtMenu() +{ + setAsMenu = addMenu(tr("Add at...")); + + initAction(&actionRename, tr("Rename or add flag"), SLOT(on_actionRename_triggered()), + getRenameSequence()); + setAsMenu->addAction(&actionRename); + + initAction(&actionGlobalVar, tr("Modify or add global variable"), + SLOT(on_actionGlobalVar_triggered()), getGlobalVarSequence()); + setAsMenu->addAction(&actionGlobalVar); +} + void DisassemblyContextMenu::addSetBaseMenu() { setBaseMenu = addMenu(tr("Set base of immediate value to..")); @@ -479,7 +492,12 @@ void DisassemblyContextMenu::setupRenaming() // Now, build the renaming menu and show it buildRenameMenu(tuh); + + auto name = RzAddressString(tuh->offset); + actionGlobalVar.setText(tr("Add or change global variable at %1 (used here)").arg(name)); + actionRename.setVisible(true); + actionGlobalVar.setVisible(true); } void DisassemblyContextMenu::aboutToShowSlot() @@ -655,6 +673,11 @@ QKeySequence DisassemblyContextMenu::getRenameSequence() const return { Qt::Key_N }; } +QKeySequence DisassemblyContextMenu::getGlobalVarSequence() const +{ + return { Qt::Key_G }; +} + QKeySequence DisassemblyContextMenu::getRetypeSequence() const { return { Qt::Key_Y }; @@ -868,6 +891,18 @@ void DisassemblyContextMenu::on_actionRename_triggered() } } +void DisassemblyContextMenu::on_actionGlobalVar_triggered() +{ + bool ok = false; + GlobalVariableDialog dialog(doRenameInfo.addr, parentForDialog()); + ok = dialog.exec(); + + if (ok) { + // Rebuild menu in case the user presses the rename shortcut directly before clicking + setupRenaming(); + } +} + void DisassemblyContextMenu::on_actionSetFunctionVarTypes_triggered() { RzAnalysisFunction *fcn = Core()->functionIn(offset); diff --git a/src/menus/DisassemblyContextMenu.h b/src/menus/DisassemblyContextMenu.h index 669990a6..177944e6 100644 --- a/src/menus/DisassemblyContextMenu.h +++ b/src/menus/DisassemblyContextMenu.h @@ -45,6 +45,7 @@ private slots: void on_actionAddComment_triggered(); void on_actionAnalyzeFunction_triggered(); void on_actionRename_triggered(); + void on_actionGlobalVar_triggered(); void on_actionSetFunctionVarTypes_triggered(); void on_actionXRefs_triggered(); void on_actionXRefsForVariables_triggered(); @@ -78,6 +79,7 @@ private: QKeySequence getCopySequence() const; QKeySequence getCommentSequence() const; QKeySequence getCopyAddressSequence() const; + QKeySequence getGlobalVarSequence() const; QKeySequence getSetToCodeSequence() const; QKeySequence getSetAsStringSequence() const; QKeySequence getSetAsStringAdvanced() const; @@ -118,6 +120,7 @@ private: QAction actionXRefs; QAction actionXRefsForVariables; QAction actionDisplayOptions; + QAction actionGlobalVar; QAction actionDeleteComment; QAction actionDeleteFlag; @@ -190,6 +193,7 @@ private: void addSetAsMenu(); void addSetToDataMenu(); void addEditMenu(); + void addAddAtMenu(); void addBreakpointMenu(); void addDebugMenu(); diff --git a/src/widgets/DecompilerWidget.cpp b/src/widgets/DecompilerWidget.cpp index 60586192..c54ad6f9 100644 --- a/src/widgets/DecompilerWidget.cpp +++ b/src/widgets/DecompilerWidget.cpp @@ -90,6 +90,7 @@ DecompilerWidget::DecompilerWidget(MainWindow *main) connect(Core(), &CutterCore::varsChanged, this, &DecompilerWidget::doRefresh); connect(Core(), &CutterCore::functionsChanged, this, &DecompilerWidget::doRefresh); connect(Core(), &CutterCore::flagsChanged, this, &DecompilerWidget::doRefresh); + connect(Core(), &CutterCore::globalVarsChanged, this, &DecompilerWidget::doRefresh); connect(Core(), &CutterCore::commentsChanged, this, &DecompilerWidget::refreshIfChanged); connect(Core(), &CutterCore::instructionChanged, this, &DecompilerWidget::refreshIfChanged); connect(Core(), &CutterCore::refreshCodeViews, this, &DecompilerWidget::doRefresh); diff --git a/src/widgets/DisassemblerGraphView.cpp b/src/widgets/DisassemblerGraphView.cpp index acb732ae..82da0866 100644 --- a/src/widgets/DisassemblerGraphView.cpp +++ b/src/widgets/DisassemblerGraphView.cpp @@ -52,6 +52,7 @@ DisassemblerGraphView::DisassemblerGraphView(QWidget *parent, CutterSeekable *se connect(Core(), &CutterCore::commentsChanged, this, &DisassemblerGraphView::refreshView); connect(Core(), &CutterCore::functionRenamed, this, &DisassemblerGraphView::refreshView); connect(Core(), &CutterCore::flagsChanged, this, &DisassemblerGraphView::refreshView); + connect(Core(), &CutterCore::globalVarsChanged, this, &DisassemblerGraphView::refreshView); connect(Core(), &CutterCore::varsChanged, this, &DisassemblerGraphView::refreshView); connect(Core(), &CutterCore::instructionChanged, this, &DisassemblerGraphView::refreshView); connect(Core(), &CutterCore::breakpointsChanged, this, &DisassemblerGraphView::refreshView); diff --git a/src/widgets/DisassemblyWidget.cpp b/src/widgets/DisassemblyWidget.cpp index 499bb442..946a2d35 100644 --- a/src/widgets/DisassemblyWidget.cpp +++ b/src/widgets/DisassemblyWidget.cpp @@ -128,6 +128,7 @@ DisassemblyWidget::DisassemblyWidget(MainWindow *main) connect(Core(), &CutterCore::commentsChanged, this, [this]() { refreshDisasm(); }); connect(Core(), SIGNAL(flagsChanged()), this, SLOT(refreshDisasm())); + connect(Core(), SIGNAL(globalVarsChanged()), this, SLOT(refreshDisasm())); connect(Core(), SIGNAL(functionsChanged()), this, SLOT(refreshDisasm())); connect(Core(), &CutterCore::functionRenamed, this, [this]() { refreshDisasm(); }); connect(Core(), SIGNAL(varsChanged()), this, SLOT(refreshDisasm())); diff --git a/src/widgets/GlobalsWidget.cpp b/src/widgets/GlobalsWidget.cpp new file mode 100644 index 00000000..f0d9bfc8 --- /dev/null +++ b/src/widgets/GlobalsWidget.cpp @@ -0,0 +1,228 @@ +#include "GlobalsWidget.h" +#include "ui_GlobalsWidget.h" +#include "core/MainWindow.h" +#include "common/Helpers.h" +#include "dialogs/GlobalVariableDialog.h" + +#include +#include + +GlobalsModel::GlobalsModel(QList *globals, QObject *parent) + : AddressableItemModel(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(); + + return qhelpers::filterStringContains(global.name, this); +} + +bool GlobalsProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const +{ + auto leftGlobal = left.data(GlobalsModel::GlobalDescriptionRole).value(); + auto rightGlobal = right.data(GlobalsModel::GlobalDescriptionRole).value(); + + 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); +} diff --git a/src/widgets/GlobalsWidget.h b/src/widgets/GlobalsWidget.h new file mode 100644 index 00000000..89dc41ba --- /dev/null +++ b/src/widgets/GlobalsWidget.h @@ -0,0 +1,89 @@ +#ifndef GLOBALSWIDGET_H +#define GLOBALSWIDGET_H + +#include +#include +#include + +#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 +{ + Q_OBJECT + + friend GlobalsWidget; + +private: + QList *globals; + +public: + enum Column { AddressColumn = 0, TypeColumn, NameColumn, CommentColumn, ColumnCount }; + enum Role { GlobalDescriptionRole = Qt::UserRole }; + + GlobalsModel(QList *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; + + QList globals; + GlobalsModel *globalsModel; + GlobalsProxyModel *globalsProxyModel; + CutterTreeWidget *tree; + + QAction *actionEditGlobal; + QAction *actionDeleteGlobal; +}; + +#endif // GLOBALSWIDGET_H diff --git a/src/widgets/GlobalsWidget.ui b/src/widgets/GlobalsWidget.ui new file mode 100644 index 00000000..ca8c8548 --- /dev/null +++ b/src/widgets/GlobalsWidget.ui @@ -0,0 +1,107 @@ + + + GlobalsWidget + + + + 0 + 0 + 400 + 300 + + + + Global Variables + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + CutterTreeView::item +{ + padding-top: 1px; + padding-bottom: 1px; +} + + + QFrame::NoFrame + + + 0 + + + 8 + + + true + + + + + + + + 0 + 0 + + + + + + + + + Edit Global Variable + + + Edit Global Variable + + + + + Delete Global Variable + + + Delete Global Variable + + + + + + CutterTreeView + QTreeView +
widgets/CutterTreeView.h
+ 1 +
+ + ComboQuickFilterView + QWidget +
widgets/ComboQuickFilterView.h
+ 1 +
+
+ + +
diff --git a/src/widgets/VisualNavbar.cpp b/src/widgets/VisualNavbar.cpp index 0a3f1409..53d93826 100644 --- a/src/widgets/VisualNavbar.cpp +++ b/src/widgets/VisualNavbar.cpp @@ -50,6 +50,7 @@ VisualNavbar::VisualNavbar(MainWindow *main, QWidget *parent) connect(Core(), &CutterCore::refreshAll, this, &VisualNavbar::fetchAndPaintData); connect(Core(), &CutterCore::functionsChanged, this, &VisualNavbar::fetchAndPaintData); connect(Core(), &CutterCore::flagsChanged, this, &VisualNavbar::fetchAndPaintData); + connect(Core(), &CutterCore::globalVarsChanged, this, &VisualNavbar::fetchAndPaintData); graphicsScene = new QGraphicsScene(this); From 66629d6288a4fc490d4c7e94ef910a75565a6c96 Mon Sep 17 00:00:00 2001 From: Anton Kochkov Date: Fri, 4 Aug 2023 11:43:03 +0800 Subject: [PATCH 47/50] Update Rizin to latest dev (#3218) --- rizin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rizin b/rizin index fe8ed0bf..48b08805 160000 --- a/rizin +++ b/rizin @@ -1 +1 @@ -Subproject commit fe8ed0bfbe6f66a907e83e3f842840d1960e7fc5 +Subproject commit 48b088056236254356fdde46f28d1cbe8bc28316 From 77aba60152bd3e0b163714bfda5462115c4de3b2 Mon Sep 17 00:00:00 2001 From: Anton Kochkov Date: Fri, 4 Aug 2023 12:51:07 +0800 Subject: [PATCH 48/50] Update translations (#3219) --- src/translations | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/translations b/src/translations index 5ac7217b..74e2dbe6 160000 --- a/src/translations +++ b/src/translations @@ -1 +1 @@ -Subproject commit 5ac7217b12623c7bfd0378380f923cf694ccee22 +Subproject commit 74e2dbe6771c9a2d1b7a727e52fa73eed565a5aa From dccbc7363b6b0bfffae83a0196cc8e51d2045b14 Mon Sep 17 00:00:00 2001 From: Anton Kochkov Date: Sat, 5 Aug 2023 09:46:46 +0800 Subject: [PATCH 49/50] Update translations --- src/translations | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/translations b/src/translations index 74e2dbe6..53b42f08 160000 --- a/src/translations +++ b/src/translations @@ -1 +1 @@ -Subproject commit 74e2dbe6771c9a2d1b7a727e52fa73eed565a5aa +Subproject commit 53b42f0854479f36170356c13d5eec4be3182444 From a0f5eca191b67a7ce3b7370fba89b7e5bf50d97b Mon Sep 17 00:00:00 2001 From: Anton Kochkov Date: Sat, 5 Aug 2023 09:51:11 +0800 Subject: [PATCH 50/50] Fix compiler warning --- src/menus/DisassemblyContextMenu.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/menus/DisassemblyContextMenu.h b/src/menus/DisassemblyContextMenu.h index 177944e6..e6c0773d 100644 --- a/src/menus/DisassemblyContextMenu.h +++ b/src/menus/DisassemblyContextMenu.h @@ -116,11 +116,11 @@ private: QAction actionAnalyzeFunction; QAction actionEditFunction; QAction actionRename; + QAction actionGlobalVar; QAction actionSetFunctionVarTypes; QAction actionXRefs; QAction actionXRefsForVariables; QAction actionDisplayOptions; - QAction actionGlobalVar; QAction actionDeleteComment; QAction actionDeleteFlag;