Xref for function variables in disassembly view (#2297)

This commit is contained in:
NIRMAL MANOJ C 2020-07-29 01:19:50 +05:30 committed by GitHub
parent cf96775b74
commit f2a867ca28
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 148 additions and 6 deletions

View File

@ -28,6 +28,7 @@ Q_GLOBAL_STATIC(CutterCore, uniqueInstance)
namespace RJsonKey {
R_JSON_KEY(addr);
R_JSON_KEY(addrs);
R_JSON_KEY(addr_end);
R_JSON_KEY(arrow);
R_JSON_KEY(baddr);
@ -476,6 +477,18 @@ QJsonDocument CutterCore::cmdj(const char *str)
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)
{
R2Task task(str);
@ -3459,6 +3472,37 @@ BlockStatistics CutterCore::getBlockStatistics(unsigned int blocksCount)
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,
const QString &filterType)
{

View File

@ -109,6 +109,7 @@ public:
QJsonDocument cmdj(const char *str);
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 QString &str) { return cmdList(str.toUtf8().constData()); }
QString cmdTask(const QString &str);
@ -567,7 +568,17 @@ public:
QList<QJsonObject> getRegisterRefs(int depth = 6);
QVector<RegisterRefValueDescription> getRegisterRefValues();
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,
const QString &filterType = QString());

View File

@ -138,12 +138,14 @@ void XrefsDialog::updateLabels(QString 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)
{
TempConfig tempConfig;
tempConfig.set("scr.html", false);
tempConfig.set("scr.color", COLOR_MODE_DISABLED);
setWindowTitle(tr("X-Refs for %1").arg(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)
{
if (type == "CODE") {
@ -188,6 +211,14 @@ void XrefModel::readForOffset(RVA offset, bool to, bool whole_function)
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
{
Q_UNUSED(parent)

View File

@ -19,7 +19,7 @@ public:
XrefModel(QObject *parent = nullptr);
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 columnCount(const QModelIndex &parent = QModelIndex()) const override;
@ -48,6 +48,13 @@ public:
~XrefsDialog();
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:
QString normalizeAddr(const QString &addr) const;
@ -69,6 +76,7 @@ private:
std::unique_ptr<Ui::XrefsDialog> ui;
void updateLabels(QString name);
void updateLabelsForVariables(QString name);
void updatePreview(RVA addr);
};

View File

@ -39,6 +39,7 @@ DisassemblyContextMenu::DisassemblyContextMenu(QWidget *parent, MainWindow *main
actionRenameUsedHere(this),
actionSetFunctionVarTypes(this),
actionXRefs(this),
actionXRefsForVariables(this),
actionDisplayOptions(this),
actionDeleteComment(this),
actionDeleteFlag(this),
@ -142,6 +143,10 @@ DisassemblyContextMenu::DisassemblyContextMenu(QWidget *parent, MainWindow *main
SLOT(on_actionXRefs_triggered()), getXRefSequence());
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"),
SLOT(on_actionDisplayOptions_triggered()), getDisplayOptionsSequence());
@ -165,6 +170,8 @@ DisassemblyContextMenu::DisassemblyContextMenu(QWidget *parent, MainWindow *main
connect(this, &DisassemblyContextMenu::aboutToShow,
this, &DisassemblyContextMenu::aboutToShowSlot);
connect(this, &DisassemblyContextMenu::aboutToHide,
this, &DisassemblyContextMenu::aboutToHideSlot);
}
DisassemblyContextMenu::~DisassemblyContextMenu()
@ -559,6 +566,17 @@ void DisassemblyContextMenu::aboutToShowSlot()
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
@ -880,6 +898,15 @@ void DisassemblyContextMenu::on_actionXRefs_triggered()
dialog.exec();
}
void DisassemblyContextMenu::on_actionXRefsForVariables_triggered()
{
if (isHighlightedWordLocalVar()) {
XrefsDialog dialog(mainWindow, nullptr);
dialog.fillRefsForVariable(curHighlightedWord, offset);
dialog.exec();
}
}
void DisassemblyContextMenu::on_actionDisplayOptions_triggered()
{
PreferencesDialog dialog(this->window());
@ -1081,3 +1108,14 @@ void DisassemblyContextMenu::initAction(QAction *action, QString name,
action->setShortcuts(keySequenceList);
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;
}

View File

@ -29,6 +29,7 @@ public slots:
private slots:
void aboutToShowSlot();
void aboutToHideSlot();
void on_actionEditFunction_triggered();
void on_actionEditInstruction_triggered();
@ -46,6 +47,7 @@ private slots:
void on_actionRenameUsedHere_triggered();
void on_actionSetFunctionVarTypes_triggered();
void on_actionXRefs_triggered();
void on_actionXRefsForVariables_triggered();
void on_actionDisplayOptions_triggered();
void on_actionDeleteComment_triggered();
@ -131,6 +133,7 @@ private:
QAction actionRenameUsedHere;
QAction actionSetFunctionVarTypes;
QAction actionXRefs;
QAction actionXRefsForVariables;
QAction actionDisplayOptions;
QAction actionDeleteComment;
@ -203,6 +206,13 @@ private:
void addBreakpointMenu();
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 {
QString name;
RVA offset;