mirror of
https://github.com/rizinorg/cutter.git
synced 2024-12-19 19:36:11 +00:00
Register refs widget improvements (#2038)
* Move register refs to the new telescoping function and add an addressable context menu
This commit is contained in:
parent
969ce5ac30
commit
a1b5a41e56
@ -1156,6 +1156,86 @@ QJsonDocument CutterCore::getSignatureInfo()
|
||||
return cmdj("iCj");
|
||||
}
|
||||
|
||||
// Utility function to check if a telescoped item exists and add it with prefixes to the desc
|
||||
static inline const QString appendVar(QString &dst, const QString val, const QString prepend_val,
|
||||
const QString append_val)
|
||||
{
|
||||
if (!val.isEmpty()) {
|
||||
dst += prepend_val + val + append_val;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
RefDescription CutterCore::formatRefDesc(QJsonObject refItem)
|
||||
{
|
||||
RefDescription desc;
|
||||
|
||||
// Ignore empty refs and refs that only contain addr
|
||||
if (refItem.size() <= 1) {
|
||||
return desc;
|
||||
}
|
||||
|
||||
QString str = refItem["string"].toVariant().toString();
|
||||
if (!str.isEmpty()) {
|
||||
desc.ref = str;
|
||||
desc.refColor = ConfigColor("comment");
|
||||
} else {
|
||||
QString type, string;
|
||||
do {
|
||||
desc.ref += " ->";
|
||||
appendVar(desc.ref, refItem["reg"].toVariant().toString(), " @", "");
|
||||
appendVar(desc.ref, refItem["mapname"].toVariant().toString(), " (", ")");
|
||||
appendVar(desc.ref, refItem["section"].toVariant().toString(), " (", ")");
|
||||
appendVar(desc.ref, refItem["func"].toVariant().toString(), " ", "");
|
||||
type = appendVar(desc.ref, refItem["type"].toVariant().toString(), " ", "");
|
||||
appendVar(desc.ref, refItem["perms"].toVariant().toString(), " ", "");
|
||||
appendVar(desc.ref, refItem["asm"].toVariant().toString(), " \"", "\"");
|
||||
string = appendVar(desc.ref, refItem["string"].toVariant().toString(), " ", "");
|
||||
if (!string.isNull()) {
|
||||
// There is no point in adding ascii and addr info after a string
|
||||
break;
|
||||
}
|
||||
if (!refItem["value"].isNull()) {
|
||||
appendVar(desc.ref, RAddressString(refItem["value"].toVariant().toULongLong()), " ", "");
|
||||
}
|
||||
refItem = refItem["ref"].toObject();
|
||||
} while (!refItem.empty());
|
||||
|
||||
// Set the ref's color according to the last item type
|
||||
if (type == "ascii" || !string.isEmpty()) {
|
||||
desc.refColor = ConfigColor("comment");
|
||||
} else if (type == "program") {
|
||||
desc.refColor = ConfigColor("fname");
|
||||
} else if (type == "library") {
|
||||
desc.refColor = ConfigColor("floc");
|
||||
} else if (type == "stack") {
|
||||
desc.refColor = ConfigColor("offset");
|
||||
}
|
||||
}
|
||||
|
||||
return desc;
|
||||
}
|
||||
|
||||
QList<QJsonObject> CutterCore::getRegisterRefs(int depth)
|
||||
{
|
||||
QList<QJsonObject> ret;
|
||||
if (!currentlyDebugging) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
QJsonObject registers = cmdj("drj").object();
|
||||
|
||||
for (const QString &key : registers.keys()) {
|
||||
QJsonObject reg;
|
||||
reg["value"] = registers.value(key);
|
||||
reg["ref"] = getAddrRefs(registers.value(key).toVariant().toULongLong(), depth);
|
||||
reg["name"] = key;
|
||||
ret.append(reg);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
QList<QJsonObject> CutterCore::getStack(int size, int depth)
|
||||
{
|
||||
QList<QJsonObject> stack;
|
||||
@ -1321,26 +1401,6 @@ QJsonDocument CutterCore::getRegisterValues()
|
||||
return cmdj("drj");
|
||||
}
|
||||
|
||||
QList<RegisterRefDescription> CutterCore::getRegisterRefs()
|
||||
{
|
||||
QList<RegisterRefDescription> ret;
|
||||
QJsonArray registerRefArray = cmdj("drrj").array();
|
||||
|
||||
for (const QJsonValue &value : registerRefArray) {
|
||||
QJsonObject regRefObject = value.toObject();
|
||||
|
||||
RegisterRefDescription regRef;
|
||||
|
||||
regRef.reg = regRefObject[RJsonKey::reg].toString();
|
||||
regRef.value = regRefObject[RJsonKey::value].toString();
|
||||
regRef.ref = regRefObject[RJsonKey::ref].toString();
|
||||
|
||||
ret << regRef;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
QList<VariableDescription> CutterCore::getVariables(RVA at)
|
||||
{
|
||||
QList<VariableDescription> ret;
|
||||
|
@ -312,6 +312,11 @@ public:
|
||||
* @param depth telescoping depth
|
||||
*/
|
||||
QJsonObject getAddrRefs(RVA addr, int depth);
|
||||
/**
|
||||
* @brief return a RefDescription with a formatted ref string and configured colors
|
||||
* @param ref the "ref" JSON node from getAddrRefs
|
||||
*/
|
||||
RefDescription formatRefDesc(QJsonObject ref);
|
||||
/**
|
||||
* @brief Get a list of a given process's threads
|
||||
* @param pid The pid of the process, -1 for the currently debugged process
|
||||
@ -525,7 +530,11 @@ public:
|
||||
BlockStatistics getBlockStatistics(unsigned int blocksCount);
|
||||
QList<BreakpointDescription> getBreakpoints();
|
||||
QList<ProcessDescription> getAllProcesses();
|
||||
QList<RegisterRefDescription> getRegisterRefs();
|
||||
/**
|
||||
* @brief returns a list of reg values and their telescoped references
|
||||
* @param depth telescoping depth
|
||||
*/
|
||||
QList<QJsonObject> getRegisterRefs(int depth = 6);
|
||||
QJsonObject getRegisterJson();
|
||||
QList<VariableDescription> getVariables(RVA at);
|
||||
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include <QList>
|
||||
#include <QStringList>
|
||||
#include <QMetaType>
|
||||
#include <QColor>
|
||||
#include "core/CutterCommon.h"
|
||||
|
||||
struct FunctionDescription {
|
||||
@ -308,10 +309,9 @@ struct ProcessDescription {
|
||||
QString path;
|
||||
};
|
||||
|
||||
struct RegisterRefDescription {
|
||||
QString reg;
|
||||
QString value;
|
||||
struct RefDescription {
|
||||
QString ref;
|
||||
QColor refColor;
|
||||
};
|
||||
|
||||
struct VariableDescription {
|
||||
@ -357,7 +357,7 @@ Q_DECLARE_METATYPE(MemoryMapDescription)
|
||||
Q_DECLARE_METATYPE(BreakpointDescription)
|
||||
Q_DECLARE_METATYPE(BreakpointDescription::PositionType)
|
||||
Q_DECLARE_METATYPE(ProcessDescription)
|
||||
Q_DECLARE_METATYPE(RegisterRefDescription)
|
||||
Q_DECLARE_METATYPE(RefDescription)
|
||||
Q_DECLARE_METATYPE(VariableDescription)
|
||||
|
||||
#endif // DESCRIPTIONS_H
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include "core/MainWindow.h"
|
||||
#include "common/Helpers.h"
|
||||
|
||||
#include <QJsonObject>
|
||||
#include <QMenu>
|
||||
#include <QClipboard>
|
||||
#include <QShortcut>
|
||||
@ -38,7 +39,14 @@ QVariant RegisterRefModel::data(const QModelIndex &index, int role) const
|
||||
case ValueColumn:
|
||||
return registerRef.value;
|
||||
case RefColumn:
|
||||
return registerRef.ref;
|
||||
return registerRef.refDesc.ref;
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
case Qt::ForegroundRole:
|
||||
switch (index.column()) {
|
||||
case RefColumn:
|
||||
return registerRef.refDesc.refColor;
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
@ -93,7 +101,7 @@ bool RegisterRefProxyModel::lessThan(const QModelIndex &left, const QModelIndex
|
||||
case RegisterRefModel::RegColumn:
|
||||
return leftRegRef.reg < rightRegRef.reg;
|
||||
case RegisterRefModel::RefColumn:
|
||||
return leftRegRef.ref < rightRegRef.ref;
|
||||
return leftRegRef.refDesc.ref < rightRegRef.refDesc.ref;
|
||||
case RegisterRefModel::ValueColumn:
|
||||
return leftRegRef.value < rightRegRef.value;
|
||||
default:
|
||||
@ -106,7 +114,8 @@ bool RegisterRefProxyModel::lessThan(const QModelIndex &left, const QModelIndex
|
||||
RegisterRefsWidget::RegisterRefsWidget(MainWindow *main, QAction *action) :
|
||||
CutterDockWidget(main, action),
|
||||
ui(new Ui::RegisterRefsWidget),
|
||||
tree(new CutterTreeWidget(this))
|
||||
tree(new CutterTreeWidget(this)),
|
||||
addressableItemContextMenu(this, main)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
@ -122,6 +131,13 @@ RegisterRefsWidget::RegisterRefsWidget(MainWindow *main, QAction *action) :
|
||||
actionCopyValue = new QAction(tr("Copy register value"), this);
|
||||
actionCopyRef = new QAction(tr("Copy register reference"), this);
|
||||
|
||||
addressableItemContextMenu.addAction(actionCopyValue);
|
||||
addressableItemContextMenu.addAction(actionCopyRef);
|
||||
addActions(addressableItemContextMenu.actions());
|
||||
|
||||
connect(ui->registerRefTreeView->selectionModel(), &QItemSelectionModel::currentChanged,
|
||||
this, &RegisterRefsWidget::onCurrentChanged);
|
||||
|
||||
refreshDeferrer = createRefreshDeferrer([this](){
|
||||
refreshRegisterRef();
|
||||
});
|
||||
@ -144,8 +160,8 @@ RegisterRefsWidget::RegisterRefsWidget(MainWindow *main, QAction *action) :
|
||||
copyClip(RegisterRefModel::RefColumn);
|
||||
});
|
||||
ui->registerRefTreeView->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(ui->registerRefTreeView, SIGNAL(customContextMenuRequested(const QPoint &)),
|
||||
this, SLOT(showRegRefContextMenu(const QPoint &)));
|
||||
connect(ui->registerRefTreeView, &QMenu::customContextMenuRequested,
|
||||
this, &RegisterRefsWidget::customMenuRequested);
|
||||
|
||||
connect(ui->quickFilterView, &QuickFilterView::filterTextChanged, this, [this] {
|
||||
tree->showItemsNumber(registerRefProxyModel->rowCount());
|
||||
@ -161,7 +177,19 @@ void RegisterRefsWidget::refreshRegisterRef()
|
||||
}
|
||||
|
||||
registerRefModel->beginResetModel();
|
||||
registerRefs = Core()->getRegisterRefs();
|
||||
|
||||
QList<QJsonObject> regRefs = Core()->getRegisterRefs();
|
||||
registerRefs.clear();
|
||||
for (const QJsonObject ® : regRefs) {
|
||||
RegisterRefDescription desc;
|
||||
|
||||
desc.value = RAddressString(reg["value"].toVariant().toULongLong());
|
||||
desc.reg = reg["name"].toVariant().toString();
|
||||
desc.refDesc = Core()->formatRefDesc(reg["ref"].toObject());
|
||||
|
||||
registerRefs.push_back(desc);
|
||||
}
|
||||
|
||||
registerRefModel->endResetModel();
|
||||
|
||||
ui->registerRefTreeView->resizeColumnToContents(0);
|
||||
@ -183,15 +211,27 @@ void RegisterRefsWidget::on_registerRefTreeView_doubleClicked(const QModelIndex
|
||||
Core()->seekAndShow(item.value);
|
||||
}
|
||||
|
||||
void RegisterRefsWidget::showRegRefContextMenu(const QPoint &pt)
|
||||
void RegisterRefsWidget::customMenuRequested(QPoint pos)
|
||||
{
|
||||
QMenu *menu = new QMenu(ui->registerRefTreeView);
|
||||
menu->clear();
|
||||
menu->addAction(actionCopyValue);
|
||||
menu->addAction(actionCopyRef);
|
||||
addressableItemContextMenu.exec(ui->registerRefTreeView->viewport()->mapToGlobal(pos));
|
||||
}
|
||||
|
||||
menu->exec(ui->registerRefTreeView->viewport()->mapToGlobal(pt));
|
||||
delete menu;
|
||||
void RegisterRefsWidget::onCurrentChanged(const QModelIndex ¤t, const QModelIndex &previous)
|
||||
{
|
||||
Q_UNUSED(current)
|
||||
Q_UNUSED(previous)
|
||||
auto currentIndex = ui->registerRefTreeView->selectionModel()->currentIndex();
|
||||
|
||||
// Use the value column as the offset
|
||||
QString offsetString;
|
||||
if (currentIndex.column() != RegisterRefModel::RefColumn) {
|
||||
offsetString = currentIndex.data().toString();
|
||||
} else {
|
||||
offsetString = currentIndex.sibling(currentIndex.row(), RegisterRefModel::ValueColumn).data().toString();
|
||||
}
|
||||
|
||||
RVA offset = Core()->math(offsetString);
|
||||
addressableItemContextMenu.setTarget(offset);
|
||||
}
|
||||
|
||||
void RegisterRefsWidget::copyClip(int column)
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include "core/Cutter.h"
|
||||
#include "CutterDockWidget.h"
|
||||
#include "CutterTreeWidget.h"
|
||||
#include "menus/AddressableItemContextMenu.h"
|
||||
|
||||
#include <QAbstractListModel>
|
||||
#include <QSortFilterProxyModel>
|
||||
@ -21,6 +22,12 @@ class RegisterRefsWidget;
|
||||
class MainWindow;
|
||||
class QTreeWidgetItem;
|
||||
|
||||
struct RegisterRefDescription {
|
||||
QString reg;
|
||||
QString value;
|
||||
RefDescription refDesc;
|
||||
};
|
||||
Q_DECLARE_METATYPE(RegisterRefDescription)
|
||||
|
||||
class RegisterRefModel: public QAbstractListModel
|
||||
{
|
||||
@ -44,8 +51,6 @@ public:
|
||||
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
|
||||
};
|
||||
|
||||
|
||||
|
||||
class RegisterRefProxyModel : public QSortFilterProxyModel
|
||||
{
|
||||
Q_OBJECT
|
||||
@ -71,7 +76,8 @@ private slots:
|
||||
void on_registerRefTreeView_doubleClicked(const QModelIndex &index);
|
||||
void refreshRegisterRef();
|
||||
void copyClip(int column);
|
||||
void showRegRefContextMenu(const QPoint &pt);
|
||||
void customMenuRequested(QPoint pos);
|
||||
void onCurrentChanged(const QModelIndex ¤t, const QModelIndex &previous);
|
||||
|
||||
private:
|
||||
std::unique_ptr<Ui::RegisterRefsWidget> ui;
|
||||
@ -79,10 +85,12 @@ private:
|
||||
RegisterRefModel *registerRefModel;
|
||||
RegisterRefProxyModel *registerRefProxyModel;
|
||||
QList<RegisterRefDescription> registerRefs;
|
||||
QAction *actionCopyValue;
|
||||
QAction *actionCopyRef;
|
||||
CutterTreeWidget *tree;
|
||||
void setScrollMode();
|
||||
|
||||
RefreshDeferrer *refreshDeferrer;
|
||||
|
||||
QAction *actionCopyValue;
|
||||
QAction *actionCopyRef;
|
||||
AddressableItemContextMenu addressableItemContextMenu;
|
||||
};
|
||||
|
@ -144,16 +144,6 @@ StackModel::StackModel(QObject *parent)
|
||||
{
|
||||
}
|
||||
|
||||
// Utility function to check if a telescoped item exists and add it with prefixes to the desc
|
||||
static inline const QString append_var(QString &dst, const QString val, const QString prepend_val,
|
||||
const QString append_val)
|
||||
{
|
||||
if (!val.isEmpty()) {
|
||||
dst += prepend_val + val + append_val;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
void StackModel::reload()
|
||||
{
|
||||
QList<QJsonObject> stackItems = Core()->getStack();
|
||||
@ -165,47 +155,8 @@ void StackModel::reload()
|
||||
|
||||
item.offset = stackItem["addr"].toVariant().toULongLong();
|
||||
item.value = RAddressString(stackItem["value"].toVariant().toULongLong());
|
||||
item.refDesc = Core()->formatRefDesc(stackItem["ref"].toObject());
|
||||
|
||||
QJsonObject refItem = stackItem["ref"].toObject();
|
||||
if (!refItem.empty()) {
|
||||
QString str = refItem["string"].toVariant().toString();
|
||||
if (!str.isEmpty()) {
|
||||
item.description = str;
|
||||
item.descriptionColor = ConfigColor("comment");
|
||||
} else {
|
||||
QString type, string;
|
||||
do {
|
||||
item.description += " ->";
|
||||
append_var(item.description, refItem["reg"].toVariant().toString(), " @", "");
|
||||
append_var(item.description, refItem["mapname"].toVariant().toString(), " (", ")");
|
||||
append_var(item.description, refItem["section"].toVariant().toString(), " (", ")");
|
||||
append_var(item.description, refItem["func"].toVariant().toString(), " ", "");
|
||||
type = append_var(item.description, refItem["type"].toVariant().toString(), " ", "");
|
||||
append_var(item.description, refItem["perms"].toVariant().toString(), " ", "");
|
||||
append_var(item.description, refItem["asm"].toVariant().toString(), " \"", "\"");
|
||||
string = append_var(item.description, refItem["string"].toVariant().toString(), " ", "");
|
||||
if (!string.isNull()) {
|
||||
// There is no point in adding ascii and addr info after a string
|
||||
break;
|
||||
}
|
||||
if (!refItem["value"].isNull()) {
|
||||
append_var(item.description, RAddressString(refItem["value"].toVariant().toULongLong()), " ", "");
|
||||
}
|
||||
refItem = refItem["ref"].toObject();
|
||||
} while (!refItem.empty());
|
||||
|
||||
// Set the description's color according to the last item type
|
||||
if (type == "ascii" || !string.isEmpty()) {
|
||||
item.descriptionColor = ConfigColor("comment");
|
||||
} else if (type == "program") {
|
||||
item.descriptionColor = ConfigColor("fname");
|
||||
} else if (type == "library") {
|
||||
item.descriptionColor = ConfigColor("floc");
|
||||
} else if (type == "stack") {
|
||||
item.descriptionColor = ConfigColor("offset");
|
||||
}
|
||||
}
|
||||
}
|
||||
values.push_back(item);
|
||||
}
|
||||
endResetModel();
|
||||
@ -236,14 +187,14 @@ QVariant StackModel::data(const QModelIndex &index, int role) const
|
||||
case ValueColumn:
|
||||
return item.value;
|
||||
case DescriptionColumn:
|
||||
return item.description;
|
||||
return item.refDesc.ref;
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
case Qt::ForegroundRole:
|
||||
switch (index.column()) {
|
||||
case DescriptionColumn:
|
||||
return item.descriptionColor;
|
||||
return item.refDesc.refColor;
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
|
@ -22,8 +22,7 @@ public:
|
||||
struct Item {
|
||||
RVA offset;
|
||||
QString value;
|
||||
QString description;
|
||||
QVariant descriptionColor;
|
||||
RefDescription refDesc;
|
||||
};
|
||||
|
||||
enum Column { OffsetColumn = 0, ValueColumn, DescriptionColumn, ColumnCount};
|
||||
|
Loading…
Reference in New Issue
Block a user