From 7418f9c76c7dcd374f8d31d84f8d96e02c17801d Mon Sep 17 00:00:00 2001 From: frmdstryr Date: Sun, 30 Jul 2023 04:37:52 -0400 Subject: [PATCH] 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);