From e9672232cb4ddbf8dc7c06db6231f1eb136b7e45 Mon Sep 17 00:00:00 2001 From: I-Man104 Date: Tue, 10 Mar 2026 22:43:52 +0200 Subject: [PATCH 1/5] decompiler: implemented variable values tooltip in debugging --- src/widgets/DecompilerWidget.cpp | 84 ++++++++++++++++++++++++++++++++ src/widgets/DecompilerWidget.h | 5 ++ 2 files changed, 89 insertions(+) diff --git a/src/widgets/DecompilerWidget.cpp b/src/widgets/DecompilerWidget.cpp index 69799444d0..67ebfab4b0 100644 --- a/src/widgets/DecompilerWidget.cpp +++ b/src/widgets/DecompilerWidget.cpp @@ -20,6 +20,7 @@ #include #include #include +#include DecompilerWidget::DecompilerWidget(MainWindow *main) : MemoryDockWidget(MemoryWidgetType::Decompiler, main), @@ -519,8 +520,91 @@ void DecompilerWidget::seekToReference() seekable->seekToReference(offsetForPosition(pos)); } +void DecompilerWidget::showVariableTooltip(QHelpEvent *event, RzCodeAnnotation *annotation) +{ + if (!annotation->variable.name) { + return; + } + RzCoreLocked core = Core()->lock(); + RzAnalysisFunction *fcn = rz_analysis_get_function_at(core->analysis, decompiledFunctionAddr); + if (!fcn) { + return; + } + RzAnalysisVar *var = rz_analysis_function_get_var_byname(fcn, annotation->variable.name); + QString tooltipContent; + if (var) { + tooltipContent = formatVarValue(var); + } else { + // TODO: track the value of the synthetic/untracked variables and show it in the tooltip. + tooltipContent = QString("%1
(Synthetic/Untracked Variable)") + .arg(QString::fromUtf8(annotation->variable.name)); + } + QToolTip::showText(event->globalPos(), tooltipContent, ui->textEdit); +} + +QString DecompilerWidget::formatVarValue(RzAnalysisVar *var) +{ + RzCoreLocked core = Core()->lock(); + QString typeStr = "unknown data type"; + if (var && var->type) { + char *type = rz_type_as_string(core->analysis->typedb, var->type); + if (type) { + typeStr = QString::fromUtf8(type); + rz_mem_free(type); + } + } + char *rawVal = rz_core_analysis_var_display(core, var, false); + QString displayValue = rawVal ? QString::fromUtf8(rawVal).trimmed() : "??"; + if (rawVal && typeStr.trimmed().contains("*")) { + char *eq = strchr(rawVal, '='); + if (eq) { + QString left = QString::fromUtf8(rawVal, eq - rawVal).trimmed(); + ut64 stackAddr = rz_num_math(core->num, left.toUtf8().constData()); + if (stackAddr != 0) { + ut64 pointedAddr = 0; + int ptrSize = core->analysis->bits / 8; + int pointedAddr_state = rz_io_read_at_mapped(core->io, stackAddr, (ut8*)&pointedAddr, ptrSize); + if (pointedAddr_state > 0 && pointedAddr != 0) { + ut8 buf[256]; + int str_state = rz_io_read_at_mapped(core->io, pointedAddr, buf, sizeof(buf) - 1); + if (str_state != 0) { + buf[sizeof(buf)-1] = 0; + if (isprint(buf[0]) || buf[0] == '\0') { + displayValue += QString("
value: \"%1\"") + .arg(QString::fromUtf8(reinterpret_cast(buf))); + } + } + } + } + } + } + rz_mem_free(rawVal); + return QString("%1 (%2)
Value: %3") + .arg(QString::fromUtf8(var->name), typeStr, displayValue); +} + bool DecompilerWidget::eventFilter(QObject *obj, QEvent *event) { + if (event->type() == QEvent::ToolTip && + Config()->getShowVarTooltips() && + (obj == ui->textEdit || obj == ui->textEdit->viewport())) { + QHelpEvent *helpEvent = static_cast(event); + QTextCursor cursor = ui->textEdit->cursorForPosition(helpEvent->pos()); + size_t pos = cursor.position(); + void *iter; + rz_vector_foreach(&this->code->annotations, iter) { + RzCodeAnnotation *annotation = (RzCodeAnnotation *)iter; + if (pos >= annotation->start && pos < annotation->end) { + if (annotation->type == RZ_CODE_ANNOTATION_TYPE_LOCAL_VARIABLE || + annotation->type == RZ_CODE_ANNOTATION_TYPE_FUNCTION_PARAMETER) { + showVariableTooltip(helpEvent, annotation); + return true; + } + } + } + QToolTip::hideText(); + return true; + } if (event->type() == QEvent::MouseButtonDblClick && (obj == ui->textEdit || obj == ui->textEdit->viewport())) { QMouseEvent *mouseEvent = static_cast(event); diff --git a/src/widgets/DecompilerWidget.h b/src/widgets/DecompilerWidget.h index ed5c7f0cdf..385f000454 100644 --- a/src/widgets/DecompilerWidget.h +++ b/src/widgets/DecompilerWidget.h @@ -1,6 +1,7 @@ #ifndef DECOMPILERWIDGET_H #define DECOMPILERWIDGET_H +#include #include #include @@ -254,6 +255,10 @@ private slots: void setCode(RzAnnotatedCode *code); void setHighlighter(bool annotationBasedHighlighter); + + void showVariableTooltip(QHelpEvent *event, RzCodeAnnotation *annotation); + QString formatVarValue(RzAnalysisVar *var); + }; #endif // DECOMPILERWIDGET_H From 7b81998cb92c1538c9fe57363401ec0451c0a858 Mon Sep 17 00:00:00 2001 From: I-Man104 Date: Thu, 12 Mar 2026 02:51:09 +0200 Subject: [PATCH 2/5] applied clang format --- rizin | 2 +- src/widgets/DecompilerWidget.cpp | 30 +++++++++++++++++------------- src/widgets/DecompilerWidget.h | 1 - 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/rizin b/rizin index 690daa2f54..2718026d27 160000 --- a/rizin +++ b/rizin @@ -1 +1 @@ -Subproject commit 690daa2f545cc70c91de885cf3738427a79f86c1 +Subproject commit 2718026d271c06046bab24d3678965f9e07e9d02 diff --git a/src/widgets/DecompilerWidget.cpp b/src/widgets/DecompilerWidget.cpp index 67ebfab4b0..e5198abd66 100644 --- a/src/widgets/DecompilerWidget.cpp +++ b/src/widgets/DecompilerWidget.cpp @@ -537,7 +537,7 @@ void DecompilerWidget::showVariableTooltip(QHelpEvent *event, RzCodeAnnotation * } else { // TODO: track the value of the synthetic/untracked variables and show it in the tooltip. tooltipContent = QString("%1
(Synthetic/Untracked Variable)") - .arg(QString::fromUtf8(annotation->variable.name)); + .arg(QString::fromUtf8(annotation->variable.name)); } QToolTip::showText(event->globalPos(), tooltipContent, ui->textEdit); } @@ -563,15 +563,19 @@ QString DecompilerWidget::formatVarValue(RzAnalysisVar *var) if (stackAddr != 0) { ut64 pointedAddr = 0; int ptrSize = core->analysis->bits / 8; - int pointedAddr_state = rz_io_read_at_mapped(core->io, stackAddr, (ut8*)&pointedAddr, ptrSize); + int pointedAddr_state = + rz_io_read_at_mapped(core->io, stackAddr, (ut8 *)&pointedAddr, ptrSize); if (pointedAddr_state > 0 && pointedAddr != 0) { ut8 buf[256]; - int str_state = rz_io_read_at_mapped(core->io, pointedAddr, buf, sizeof(buf) - 1); + int str_state = + rz_io_read_at_mapped(core->io, pointedAddr, buf, sizeof(buf) - 1); if (str_state != 0) { - buf[sizeof(buf)-1] = 0; + buf[sizeof(buf) - 1] = 0; if (isprint(buf[0]) || buf[0] == '\0') { - displayValue += QString("
value: \"%1\"") - .arg(QString::fromUtf8(reinterpret_cast(buf))); + displayValue += + QString("
value: \"%1\"") + .arg(QString::fromUtf8( + reinterpret_cast(buf))); } } } @@ -580,23 +584,23 @@ QString DecompilerWidget::formatVarValue(RzAnalysisVar *var) } rz_mem_free(rawVal); return QString("%1 (%2)
Value: %3") - .arg(QString::fromUtf8(var->name), typeStr, displayValue); + .arg(QString::fromUtf8(var->name), typeStr, displayValue); } bool DecompilerWidget::eventFilter(QObject *obj, QEvent *event) { - if (event->type() == QEvent::ToolTip && - Config()->getShowVarTooltips() && - (obj == ui->textEdit || obj == ui->textEdit->viewport())) { + if (event->type() == QEvent::ToolTip && Config()->getShowVarTooltips() + && (obj == ui->textEdit || obj == ui->textEdit->viewport())) { QHelpEvent *helpEvent = static_cast(event); QTextCursor cursor = ui->textEdit->cursorForPosition(helpEvent->pos()); size_t pos = cursor.position(); void *iter; - rz_vector_foreach(&this->code->annotations, iter) { + rz_vector_foreach(&this->code->annotations, iter) + { RzCodeAnnotation *annotation = (RzCodeAnnotation *)iter; if (pos >= annotation->start && pos < annotation->end) { - if (annotation->type == RZ_CODE_ANNOTATION_TYPE_LOCAL_VARIABLE || - annotation->type == RZ_CODE_ANNOTATION_TYPE_FUNCTION_PARAMETER) { + if (annotation->type == RZ_CODE_ANNOTATION_TYPE_LOCAL_VARIABLE + || annotation->type == RZ_CODE_ANNOTATION_TYPE_FUNCTION_PARAMETER) { showVariableTooltip(helpEvent, annotation); return true; } diff --git a/src/widgets/DecompilerWidget.h b/src/widgets/DecompilerWidget.h index 385f000454..3ecf1004d6 100644 --- a/src/widgets/DecompilerWidget.h +++ b/src/widgets/DecompilerWidget.h @@ -258,7 +258,6 @@ private slots: void showVariableTooltip(QHelpEvent *event, RzCodeAnnotation *annotation); QString formatVarValue(RzAnalysisVar *var); - }; #endif // DECOMPILERWIDGET_H From 3a1ebd7105ebc1d5a78c85762a3ec8bc0ccfe380 Mon Sep 17 00:00:00 2001 From: I-Man104 Date: Thu, 12 Mar 2026 02:53:07 +0200 Subject: [PATCH 3/5] refactor to use rz_core_analysis_var_addr and storage types --- src/widgets/DecompilerWidget.cpp | 52 ++++++++++++++------------------ 1 file changed, 22 insertions(+), 30 deletions(-) diff --git a/src/widgets/DecompilerWidget.cpp b/src/widgets/DecompilerWidget.cpp index e5198abd66..555cb4cc9c 100644 --- a/src/widgets/DecompilerWidget.cpp +++ b/src/widgets/DecompilerWidget.cpp @@ -534,10 +534,6 @@ void DecompilerWidget::showVariableTooltip(QHelpEvent *event, RzCodeAnnotation * QString tooltipContent; if (var) { tooltipContent = formatVarValue(var); - } else { - // TODO: track the value of the synthetic/untracked variables and show it in the tooltip. - tooltipContent = QString("%1
(Synthetic/Untracked Variable)") - .arg(QString::fromUtf8(annotation->variable.name)); } QToolTip::showText(event->globalPos(), tooltipContent, ui->textEdit); } @@ -555,36 +551,32 @@ QString DecompilerWidget::formatVarValue(RzAnalysisVar *var) } char *rawVal = rz_core_analysis_var_display(core, var, false); QString displayValue = rawVal ? QString::fromUtf8(rawVal).trimmed() : "??"; - if (rawVal && typeStr.trimmed().contains("*")) { - char *eq = strchr(rawVal, '='); - if (eq) { - QString left = QString::fromUtf8(rawVal, eq - rawVal).trimmed(); - ut64 stackAddr = rz_num_math(core->num, left.toUtf8().constData()); - if (stackAddr != 0) { - ut64 pointedAddr = 0; - int ptrSize = core->analysis->bits / 8; - int pointedAddr_state = - rz_io_read_at_mapped(core->io, stackAddr, (ut8 *)&pointedAddr, ptrSize); - if (pointedAddr_state > 0 && pointedAddr != 0) { - ut8 buf[256]; - int str_state = - rz_io_read_at_mapped(core->io, pointedAddr, buf, sizeof(buf) - 1); - if (str_state != 0) { - buf[sizeof(buf) - 1] = 0; - if (isprint(buf[0]) || buf[0] == '\0') { - displayValue += - QString("
value: \"%1\"") - .arg(QString::fromUtf8( - reinterpret_cast(buf))); - } - } + rz_mem_free(rawVal); + if (typeStr.contains("*")) { + ut64 pointedAddr = 0; + int ptrSize = core->analysis->bits / 8; + bool pointedAddr_state = false; + if (var->storage.type == RZ_ANALYSIS_VAR_STORAGE_REG) { + pointedAddr = rz_debug_reg_get(core->dbg, var->storage.reg); + pointedAddr_state = (pointedAddr != 0); + } else if (var->storage.type == RZ_ANALYSIS_VAR_STORAGE_STACK) { + ut64 stackAddr = rz_core_analysis_var_addr(core, var); + pointedAddr_state = + rz_io_read_at_mapped(core->io, stackAddr, (ut8 *)&pointedAddr, ptrSize); + } + if (pointedAddr_state && pointedAddr) { + ut8 buf[256]; + bool str_state = rz_io_read_at_mapped(core->io, pointedAddr, buf, sizeof(buf) - 1); + if (str_state) { + size_t len = strnlen((const char *)buf, sizeof(buf)); + if (len > 0 && rz_str_is_printable((const char *)buf)) { + QString str = QString::fromUtf8((const char *)buf, len); + displayValue += QString("\nvalue: \"%1\"").arg(str); } } } } - rz_mem_free(rawVal); - return QString("%1 (%2)
Value: %3") - .arg(QString::fromUtf8(var->name), typeStr, displayValue); + return QString("%1 (%2)\nValue: %3").arg(QString::fromUtf8(var->name), typeStr, displayValue); } bool DecompilerWidget::eventFilter(QObject *obj, QEvent *event) From fab42130b534afb003067d3d2dafda1268899cc8 Mon Sep 17 00:00:00 2001 From: I-Man104 Date: Sun, 29 Mar 2026 20:46:30 +0200 Subject: [PATCH 4/5] improve variable tooltip with endian-safe pointer resolution --- src/widgets/DecompilerWidget.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/widgets/DecompilerWidget.cpp b/src/widgets/DecompilerWidget.cpp index 555cb4cc9c..3bded95996 100644 --- a/src/widgets/DecompilerWidget.cpp +++ b/src/widgets/DecompilerWidget.cpp @@ -554,29 +554,31 @@ QString DecompilerWidget::formatVarValue(RzAnalysisVar *var) rz_mem_free(rawVal); if (typeStr.contains("*")) { ut64 pointedAddr = 0; - int ptrSize = core->analysis->bits / 8; - bool pointedAddr_state = false; + const int bits = core->rasm->bits; + const int ptrSize = bits / 8; if (var->storage.type == RZ_ANALYSIS_VAR_STORAGE_REG) { pointedAddr = rz_debug_reg_get(core->dbg, var->storage.reg); - pointedAddr_state = (pointedAddr != 0); } else if (var->storage.type == RZ_ANALYSIS_VAR_STORAGE_STACK) { ut64 stackAddr = rz_core_analysis_var_addr(core, var); - pointedAddr_state = - rz_io_read_at_mapped(core->io, stackAddr, (ut8 *)&pointedAddr, ptrSize); + ut8 ptrBuf[8]; + if (rz_io_read_at_mapped(core->io, stackAddr, ptrBuf, ptrSize)) { + pointedAddr = rz_read_ble(ptrBuf, core->rasm->big_endian, bits); + } } - if (pointedAddr_state && pointedAddr) { + if (pointedAddr) { ut8 buf[256]; bool str_state = rz_io_read_at_mapped(core->io, pointedAddr, buf, sizeof(buf) - 1); if (str_state) { size_t len = strnlen((const char *)buf, sizeof(buf)); if (len > 0 && rz_str_is_printable((const char *)buf)) { - QString str = QString::fromUtf8((const char *)buf, len); + QString str = QString::fromUtf8((const char *)buf, len).toHtmlEscaped(); displayValue += QString("\nvalue: \"%1\"").arg(str); } } } } - return QString("%1 (%2)\nValue: %3").arg(QString::fromUtf8(var->name), typeStr, displayValue); + return QString("%1 (%2)\nValue: %3") + .arg(QString::fromUtf8(var->name).toHtmlEscaped(), typeStr, displayValue); } bool DecompilerWidget::eventFilter(QObject *obj, QEvent *event) From c1cc178e7165184f5095779b80719246786d8ddb Mon Sep 17 00:00:00 2001 From: I-Man104 Date: Mon, 30 Mar 2026 17:41:28 +0200 Subject: [PATCH 5/5] Use Core()->getRegisterRefValue() for safe register access. --- src/widgets/DecompilerWidget.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/widgets/DecompilerWidget.cpp b/src/widgets/DecompilerWidget.cpp index 3bded95996..758ee070e3 100644 --- a/src/widgets/DecompilerWidget.cpp +++ b/src/widgets/DecompilerWidget.cpp @@ -557,7 +557,10 @@ QString DecompilerWidget::formatVarValue(RzAnalysisVar *var) const int bits = core->rasm->bits; const int ptrSize = bits / 8; if (var->storage.type == RZ_ANALYSIS_VAR_STORAGE_REG) { - pointedAddr = rz_debug_reg_get(core->dbg, var->storage.reg); + auto reg = Core()->getRegisterRefValue(QString::fromUtf8(var->storage.reg)); + if (!reg.name.isEmpty()) { + pointedAddr = Core()->math(reg.value); + } } else if (var->storage.type == RZ_ANALYSIS_VAR_STORAGE_STACK) { ut64 stackAddr = rz_core_analysis_var_addr(core, var); ut8 ptrBuf[8];