mirror of
https://github.com/rizinorg/cutter.git
synced 2025-01-18 02:25:26 +00:00
Xref for function variables in disassembly view (#2297)
This commit is contained in:
parent
cf96775b74
commit
f2a867ca28
@ -28,6 +28,7 @@ Q_GLOBAL_STATIC(CutterCore, uniqueInstance)
|
|||||||
|
|
||||||
namespace RJsonKey {
|
namespace RJsonKey {
|
||||||
R_JSON_KEY(addr);
|
R_JSON_KEY(addr);
|
||||||
|
R_JSON_KEY(addrs);
|
||||||
R_JSON_KEY(addr_end);
|
R_JSON_KEY(addr_end);
|
||||||
R_JSON_KEY(arrow);
|
R_JSON_KEY(arrow);
|
||||||
R_JSON_KEY(baddr);
|
R_JSON_KEY(baddr);
|
||||||
@ -476,6 +477,18 @@ QJsonDocument CutterCore::cmdj(const char *str)
|
|||||||
return doc;
|
return doc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QJsonDocument CutterCore::cmdjAt(const char *str, RVA address)
|
||||||
|
{
|
||||||
|
QJsonDocument res;
|
||||||
|
RVA oldOffset = getOffset();
|
||||||
|
seekSilent(address);
|
||||||
|
|
||||||
|
res = cmdj(str);
|
||||||
|
|
||||||
|
seekSilent(oldOffset);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
QString CutterCore::cmdTask(const QString &str)
|
QString CutterCore::cmdTask(const QString &str)
|
||||||
{
|
{
|
||||||
R2Task task(str);
|
R2Task task(str);
|
||||||
@ -3459,6 +3472,37 @@ BlockStatistics CutterCore::getBlockStatistics(unsigned int blocksCount)
|
|||||||
return blockStats;
|
return blockStats;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QList<XrefDescription> CutterCore::getXRefsForVariable(QString variableName, bool findWrites, RVA offset)
|
||||||
|
{
|
||||||
|
QList<XrefDescription> xrefList = QList<XrefDescription>();
|
||||||
|
QJsonArray xrefsArray;
|
||||||
|
if (findWrites) {
|
||||||
|
xrefsArray = cmdjAt("afvWj", offset).array();
|
||||||
|
} else {
|
||||||
|
xrefsArray = cmdjAt("afvRj", offset).array();
|
||||||
|
}
|
||||||
|
for (const QJsonValue &value : xrefsArray) {
|
||||||
|
QJsonObject xrefObject = value.toObject();
|
||||||
|
QString name = xrefObject[RJsonKey::name].toString();
|
||||||
|
if (name == variableName) {
|
||||||
|
QJsonArray addressArray = xrefObject[RJsonKey::addrs].toArray();
|
||||||
|
for (const QJsonValue &address : addressArray) {
|
||||||
|
XrefDescription xref;
|
||||||
|
RVA addr = address.toVariant().toULongLong();
|
||||||
|
xref.from = addr;
|
||||||
|
xref.to = addr;
|
||||||
|
if (findWrites) {
|
||||||
|
xref.from_str = RAddressString(addr);
|
||||||
|
} else {
|
||||||
|
xref.to_str = RAddressString(addr);
|
||||||
|
}
|
||||||
|
xrefList << xref;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return xrefList;
|
||||||
|
}
|
||||||
|
|
||||||
QList<XrefDescription> CutterCore::getXRefs(RVA addr, bool to, bool whole_function,
|
QList<XrefDescription> CutterCore::getXRefs(RVA addr, bool to, bool whole_function,
|
||||||
const QString &filterType)
|
const QString &filterType)
|
||||||
{
|
{
|
||||||
|
@ -109,6 +109,7 @@ public:
|
|||||||
|
|
||||||
QJsonDocument cmdj(const char *str);
|
QJsonDocument cmdj(const char *str);
|
||||||
QJsonDocument cmdj(const QString &str) { return cmdj(str.toUtf8().constData()); }
|
QJsonDocument cmdj(const QString &str) { return cmdj(str.toUtf8().constData()); }
|
||||||
|
QJsonDocument cmdjAt(const char *str, RVA address);
|
||||||
QStringList cmdList(const char *str) { return cmd(str).split(QLatin1Char('\n'), CUTTER_QT_SKIP_EMPTY_PARTS); }
|
QStringList cmdList(const char *str) { return cmd(str).split(QLatin1Char('\n'), CUTTER_QT_SKIP_EMPTY_PARTS); }
|
||||||
QStringList cmdList(const QString &str) { return cmdList(str.toUtf8().constData()); }
|
QStringList cmdList(const QString &str) { return cmdList(str.toUtf8().constData()); }
|
||||||
QString cmdTask(const QString &str);
|
QString cmdTask(const QString &str);
|
||||||
@ -567,7 +568,17 @@ public:
|
|||||||
QList<QJsonObject> getRegisterRefs(int depth = 6);
|
QList<QJsonObject> getRegisterRefs(int depth = 6);
|
||||||
QVector<RegisterRefValueDescription> getRegisterRefValues();
|
QVector<RegisterRefValueDescription> getRegisterRefValues();
|
||||||
QList<VariableDescription> getVariables(RVA at);
|
QList<VariableDescription> getVariables(RVA at);
|
||||||
|
/**
|
||||||
|
* @brief Fetches all the writes or reads to the specified local variable 'variableName'
|
||||||
|
* in the function in which the specified offset is a part of.
|
||||||
|
* @param variableName Name of the local variable.
|
||||||
|
* @param findWrites If this is true, then locations at which modification happen to the specified
|
||||||
|
* local variable is fetched. Else, the locations at which the local is variable is read is fetched.
|
||||||
|
* @param offset An offset in the function in which the specified local variable exist.
|
||||||
|
* @return A list of XrefDescriptions that contains details of all the writes or reads that happen to the
|
||||||
|
* variable 'variableName'.
|
||||||
|
*/
|
||||||
|
QList<XrefDescription> getXRefsForVariable(QString variableName, bool findWrites, RVA offset);
|
||||||
QList<XrefDescription> getXRefs(RVA addr, bool to, bool whole_function,
|
QList<XrefDescription> getXRefs(RVA addr, bool to, bool whole_function,
|
||||||
const QString &filterType = QString());
|
const QString &filterType = QString());
|
||||||
|
|
||||||
|
@ -138,12 +138,14 @@ void XrefsDialog::updateLabels(QString name)
|
|||||||
ui->label_xFrom->setText(tr("X-Refs from %1:").arg(name));
|
ui->label_xFrom->setText(tr("X-Refs from %1:").arg(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void XrefsDialog::updateLabelsForVariables(QString name)
|
||||||
|
{
|
||||||
|
ui->label_xTo->setText(tr("Writes to %1").arg(name));
|
||||||
|
ui->label_xFrom->setText(tr("Reads from %1").arg(name));
|
||||||
|
}
|
||||||
|
|
||||||
void XrefsDialog::fillRefsForAddress(RVA addr, QString name, bool whole_function)
|
void XrefsDialog::fillRefsForAddress(RVA addr, QString name, bool whole_function)
|
||||||
{
|
{
|
||||||
TempConfig tempConfig;
|
|
||||||
tempConfig.set("scr.html", false);
|
|
||||||
tempConfig.set("scr.color", COLOR_MODE_DISABLED);
|
|
||||||
|
|
||||||
setWindowTitle(tr("X-Refs for %1").arg(name));
|
setWindowTitle(tr("X-Refs for %1").arg(name));
|
||||||
updateLabels(name);
|
updateLabels(name);
|
||||||
|
|
||||||
@ -160,6 +162,27 @@ void XrefsDialog::fillRefsForAddress(RVA addr, QString name, bool whole_function
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void XrefsDialog::fillRefsForVariable(QString nameOfVariable, RVA offset)
|
||||||
|
{
|
||||||
|
setWindowTitle(tr("X-Refs for %1").arg(nameOfVariable));
|
||||||
|
updateLabelsForVariables(nameOfVariable);
|
||||||
|
|
||||||
|
// Initialize Model
|
||||||
|
toModel.readForVariable(nameOfVariable, true, offset);
|
||||||
|
fromModel.readForVariable(nameOfVariable, false, offset);
|
||||||
|
// Hide irrelevant column 1: which shows type
|
||||||
|
ui->fromTreeWidget->hideColumn(XrefModel::Columns::TYPE);
|
||||||
|
ui->toTreeWidget->hideColumn(XrefModel::Columns::TYPE);
|
||||||
|
// Adjust columns to content
|
||||||
|
qhelpers::adjustColumns(ui->fromTreeWidget, fromModel.columnCount(), 0);
|
||||||
|
qhelpers::adjustColumns(ui->toTreeWidget, toModel.columnCount(), 0);
|
||||||
|
|
||||||
|
// Automatically select the first line
|
||||||
|
if (!qhelpers::selectFirstItem(ui->toTreeWidget)) {
|
||||||
|
qhelpers::selectFirstItem(ui->fromTreeWidget);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
QString XrefModel::xrefTypeString(const QString &type)
|
QString XrefModel::xrefTypeString(const QString &type)
|
||||||
{
|
{
|
||||||
if (type == "CODE") {
|
if (type == "CODE") {
|
||||||
@ -188,6 +211,14 @@ void XrefModel::readForOffset(RVA offset, bool to, bool whole_function)
|
|||||||
endResetModel();
|
endResetModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void XrefModel::readForVariable(QString nameOfVariable, bool write, RVA offset)
|
||||||
|
{
|
||||||
|
beginResetModel();
|
||||||
|
this->to = write;
|
||||||
|
xrefs = Core()->getXRefsForVariable(nameOfVariable, write, offset);
|
||||||
|
endResetModel();
|
||||||
|
}
|
||||||
|
|
||||||
int XrefModel::rowCount(const QModelIndex &parent) const
|
int XrefModel::rowCount(const QModelIndex &parent) const
|
||||||
{
|
{
|
||||||
Q_UNUSED(parent)
|
Q_UNUSED(parent)
|
||||||
|
@ -19,7 +19,7 @@ public:
|
|||||||
|
|
||||||
XrefModel(QObject *parent = nullptr);
|
XrefModel(QObject *parent = nullptr);
|
||||||
void readForOffset(RVA offset, bool to, bool whole_function);
|
void readForOffset(RVA offset, bool to, bool whole_function);
|
||||||
|
void readForVariable(QString nameOfVariable, bool write, RVA offset);
|
||||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||||
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
|
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||||
|
|
||||||
@ -48,6 +48,13 @@ public:
|
|||||||
~XrefsDialog();
|
~XrefsDialog();
|
||||||
|
|
||||||
void fillRefsForAddress(RVA addr, QString name, bool whole_function);
|
void fillRefsForAddress(RVA addr, QString name, bool whole_function);
|
||||||
|
/**
|
||||||
|
* @brief Initializes toModel and fromModel with the details about the references to the specified
|
||||||
|
* local variable 'nameOfVariable'.
|
||||||
|
* @param nameOfVarible Name of the local variable for which the references are being initialized.
|
||||||
|
* @param offset An offset in the function in which the specified local variable exist.
|
||||||
|
*/
|
||||||
|
void fillRefsForVariable(QString nameOfVariable, RVA offset);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
QString normalizeAddr(const QString &addr) const;
|
QString normalizeAddr(const QString &addr) const;
|
||||||
@ -69,6 +76,7 @@ private:
|
|||||||
std::unique_ptr<Ui::XrefsDialog> ui;
|
std::unique_ptr<Ui::XrefsDialog> ui;
|
||||||
|
|
||||||
void updateLabels(QString name);
|
void updateLabels(QString name);
|
||||||
|
void updateLabelsForVariables(QString name);
|
||||||
void updatePreview(RVA addr);
|
void updatePreview(RVA addr);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -39,6 +39,7 @@ DisassemblyContextMenu::DisassemblyContextMenu(QWidget *parent, MainWindow *main
|
|||||||
actionRenameUsedHere(this),
|
actionRenameUsedHere(this),
|
||||||
actionSetFunctionVarTypes(this),
|
actionSetFunctionVarTypes(this),
|
||||||
actionXRefs(this),
|
actionXRefs(this),
|
||||||
|
actionXRefsForVariables(this),
|
||||||
actionDisplayOptions(this),
|
actionDisplayOptions(this),
|
||||||
actionDeleteComment(this),
|
actionDeleteComment(this),
|
||||||
actionDeleteFlag(this),
|
actionDeleteFlag(this),
|
||||||
@ -142,6 +143,10 @@ DisassemblyContextMenu::DisassemblyContextMenu(QWidget *parent, MainWindow *main
|
|||||||
SLOT(on_actionXRefs_triggered()), getXRefSequence());
|
SLOT(on_actionXRefs_triggered()), getXRefSequence());
|
||||||
addAction(&actionXRefs);
|
addAction(&actionXRefs);
|
||||||
|
|
||||||
|
initAction(&actionXRefsForVariables, tr("X-Refs for local variables"),
|
||||||
|
SLOT(on_actionXRefsForVariables_triggered()), QKeySequence({Qt::SHIFT + Qt::Key_X}));
|
||||||
|
addAction(&actionXRefsForVariables);
|
||||||
|
|
||||||
initAction(&actionDisplayOptions, tr("Show Options"),
|
initAction(&actionDisplayOptions, tr("Show Options"),
|
||||||
SLOT(on_actionDisplayOptions_triggered()), getDisplayOptionsSequence());
|
SLOT(on_actionDisplayOptions_triggered()), getDisplayOptionsSequence());
|
||||||
|
|
||||||
@ -165,6 +170,8 @@ DisassemblyContextMenu::DisassemblyContextMenu(QWidget *parent, MainWindow *main
|
|||||||
|
|
||||||
connect(this, &DisassemblyContextMenu::aboutToShow,
|
connect(this, &DisassemblyContextMenu::aboutToShow,
|
||||||
this, &DisassemblyContextMenu::aboutToShowSlot);
|
this, &DisassemblyContextMenu::aboutToShowSlot);
|
||||||
|
connect(this, &DisassemblyContextMenu::aboutToHide,
|
||||||
|
this, &DisassemblyContextMenu::aboutToHideSlot);
|
||||||
}
|
}
|
||||||
|
|
||||||
DisassemblyContextMenu::~DisassemblyContextMenu()
|
DisassemblyContextMenu::~DisassemblyContextMenu()
|
||||||
@ -559,6 +566,17 @@ void DisassemblyContextMenu::aboutToShowSlot()
|
|||||||
pluginAction->setData(QVariant::fromValue(offset));
|
pluginAction->setData(QVariant::fromValue(offset));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isLocalVar = isHighlightedWordLocalVar();
|
||||||
|
actionXRefsForVariables.setVisible(isLocalVar);
|
||||||
|
if (isLocalVar) {
|
||||||
|
actionXRefsForVariables.setText(tr("X-Refs for %1").arg(curHighlightedWord));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DisassemblyContextMenu::aboutToHideSlot()
|
||||||
|
{
|
||||||
|
actionXRefsForVariables.setVisible(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
QKeySequence DisassemblyContextMenu::getCopySequence() const
|
QKeySequence DisassemblyContextMenu::getCopySequence() const
|
||||||
@ -880,6 +898,15 @@ void DisassemblyContextMenu::on_actionXRefs_triggered()
|
|||||||
dialog.exec();
|
dialog.exec();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DisassemblyContextMenu::on_actionXRefsForVariables_triggered()
|
||||||
|
{
|
||||||
|
if (isHighlightedWordLocalVar()) {
|
||||||
|
XrefsDialog dialog(mainWindow, nullptr);
|
||||||
|
dialog.fillRefsForVariable(curHighlightedWord, offset);
|
||||||
|
dialog.exec();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void DisassemblyContextMenu::on_actionDisplayOptions_triggered()
|
void DisassemblyContextMenu::on_actionDisplayOptions_triggered()
|
||||||
{
|
{
|
||||||
PreferencesDialog dialog(this->window());
|
PreferencesDialog dialog(this->window());
|
||||||
@ -1081,3 +1108,14 @@ void DisassemblyContextMenu::initAction(QAction *action, QString name,
|
|||||||
action->setShortcuts(keySequenceList);
|
action->setShortcuts(keySequenceList);
|
||||||
action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
|
action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool DisassemblyContextMenu::isHighlightedWordLocalVar()
|
||||||
|
{
|
||||||
|
QList<VariableDescription> variables = Core()->getVariables(offset);
|
||||||
|
for (const VariableDescription &var : variables) {
|
||||||
|
if (var.name == curHighlightedWord) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
@ -29,6 +29,7 @@ public slots:
|
|||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void aboutToShowSlot();
|
void aboutToShowSlot();
|
||||||
|
void aboutToHideSlot();
|
||||||
|
|
||||||
void on_actionEditFunction_triggered();
|
void on_actionEditFunction_triggered();
|
||||||
void on_actionEditInstruction_triggered();
|
void on_actionEditInstruction_triggered();
|
||||||
@ -46,6 +47,7 @@ private slots:
|
|||||||
void on_actionRenameUsedHere_triggered();
|
void on_actionRenameUsedHere_triggered();
|
||||||
void on_actionSetFunctionVarTypes_triggered();
|
void on_actionSetFunctionVarTypes_triggered();
|
||||||
void on_actionXRefs_triggered();
|
void on_actionXRefs_triggered();
|
||||||
|
void on_actionXRefsForVariables_triggered();
|
||||||
void on_actionDisplayOptions_triggered();
|
void on_actionDisplayOptions_triggered();
|
||||||
|
|
||||||
void on_actionDeleteComment_triggered();
|
void on_actionDeleteComment_triggered();
|
||||||
@ -131,6 +133,7 @@ private:
|
|||||||
QAction actionRenameUsedHere;
|
QAction actionRenameUsedHere;
|
||||||
QAction actionSetFunctionVarTypes;
|
QAction actionSetFunctionVarTypes;
|
||||||
QAction actionXRefs;
|
QAction actionXRefs;
|
||||||
|
QAction actionXRefsForVariables;
|
||||||
QAction actionDisplayOptions;
|
QAction actionDisplayOptions;
|
||||||
|
|
||||||
QAction actionDeleteComment;
|
QAction actionDeleteComment;
|
||||||
@ -203,6 +206,13 @@ private:
|
|||||||
void addBreakpointMenu();
|
void addBreakpointMenu();
|
||||||
void addDebugMenu();
|
void addDebugMenu();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Checks if the currently highlighted word in the disassembly widget
|
||||||
|
* is a local variable or function paramter.
|
||||||
|
* @return Return true if the highlighted word is the name of a local variable or function parameter,
|
||||||
|
* return false otherwise.
|
||||||
|
*/
|
||||||
|
bool isHighlightedWordLocalVar();
|
||||||
struct ThingUsedHere {
|
struct ThingUsedHere {
|
||||||
QString name;
|
QString name;
|
||||||
RVA offset;
|
RVA offset;
|
||||||
|
Loading…
Reference in New Issue
Block a user