1 #include "XrefsDialog.h"
2 #include "ui_XrefsDialog.h"
3
4 #include "common/TempConfig.h"
5 #include "common/Helpers.h"
6
7 #include "core/MainWindow.h"
8
9 #include <QJsonArray>
10
XrefsDialog(MainWindow * main,QWidget * parent,bool hideXrefFrom)11 XrefsDialog::XrefsDialog(MainWindow *main, QWidget *parent, bool hideXrefFrom) :
12 QDialog(parent),
13 addr(0),
14 toModel(this),
15 fromModel(this),
16 ui(new Ui::XrefsDialog)
17 {
18 ui->setupUi(this);
19 setWindowFlags(windowFlags() & (~Qt::WindowContextHelpButtonHint));
20
21 ui->toTreeWidget->setMainWindow(main);
22 ui->fromTreeWidget->setMainWindow(main);
23
24 ui->toTreeWidget->setModel(&toModel);
25 ui->fromTreeWidget->setModel(&fromModel);
26
27 // Modify the splitter's location to show more Disassembly instead of empty space. Not possible via Designer
28 ui->splitter->setSizes(QList<int>() << 300 << 400);
29
30 // Increase asm text edit margin
31 QTextDocument *asm_docu = ui->previewTextEdit->document();
32 asm_docu->setDocumentMargin(10);
33
34 setupPreviewColors();
35 setupPreviewFont();
36
37 // Highlight current line
38 connect(ui->previewTextEdit, &QPlainTextEdit::cursorPositionChanged, this, &XrefsDialog::highlightCurrentLine);
39 connect(Config(), &Configuration::fontsUpdated, this, &XrefsDialog::setupPreviewFont);
40 connect(Config(), &Configuration::colorsUpdated, this, &XrefsDialog::setupPreviewColors);
41
42 connect(ui->toTreeWidget->selectionModel(), &QItemSelectionModel::selectionChanged,
43 this, &XrefsDialog::onToTreeWidgetItemSelectionChanged);
44 connect(ui->fromTreeWidget->selectionModel(), &QItemSelectionModel::selectionChanged,
45 this, &XrefsDialog::onFromTreeWidgetItemSelectionChanged);
46
47 // Don't create recursive xref dialogs
48 auto toContextMenu = ui->toTreeWidget->getItemContextMenu();
49 connect(toContextMenu, &AddressableItemContextMenu::xrefsTriggered, this, &QWidget::close);
50 auto fromContextMenu = ui->fromTreeWidget->getItemContextMenu();
51 connect(fromContextMenu, &AddressableItemContextMenu::xrefsTriggered, this, &QWidget::close);
52
53 connect(ui->toTreeWidget, &QAbstractItemView::doubleClicked, this, &QWidget::close);
54 connect(ui->fromTreeWidget, &QAbstractItemView::doubleClicked, this, &QWidget::close);
55
56 connect(Core(), &CutterCore::commentsChanged, this, [this]() {
57 qhelpers::emitColumnChanged(&toModel, XrefModel::COMMENT);
58 qhelpers::emitColumnChanged(&fromModel, XrefModel::COMMENT);
59 });
60
61 if (hideXrefFrom) {
62 hideXrefFromSection();
63 }
64 }
65
~XrefsDialog()66 XrefsDialog::~XrefsDialog() { }
67
normalizeAddr(const QString & addr) const68 QString XrefsDialog::normalizeAddr(const QString &addr) const
69 {
70 QString ret = addr;
71 if (addr.length() < 10) {
72 ret = ret.mid(3).rightJustified(8, QLatin1Char('0'));
73 ret.prepend(QStringLiteral("0x"));
74 }
75 return ret;
76 }
77
setupPreviewFont()78 void XrefsDialog::setupPreviewFont()
79 {
80 ui->previewTextEdit->setFont(Config()->getBaseFont());
81 }
82
setupPreviewColors()83 void XrefsDialog::setupPreviewColors()
84 {
85 ui->previewTextEdit->setStyleSheet(QString("QPlainTextEdit { background-color: %1; color: %2; }")
86 .arg(ConfigColor("gui.background").name())
87 .arg(ConfigColor("btext").name()));
88 }
89
highlightCurrentLine()90 void XrefsDialog::highlightCurrentLine()
91 {
92 QList<QTextEdit::ExtraSelection> extraSelections;
93
94 if (ui->previewTextEdit->isReadOnly()) {
95 QTextEdit::ExtraSelection selection = QTextEdit::ExtraSelection();
96
97 selection.format.setBackground(ConfigColor("lineHighlight"));
98 selection.format.setProperty(QTextFormat::FullWidthSelection, true);
99 selection.cursor = ui->previewTextEdit->textCursor();
100 selection.cursor.clearSelection();
101 extraSelections.append(selection);
102
103 ui->previewTextEdit->setExtraSelections(extraSelections);
104 }
105 }
106
onFromTreeWidgetItemSelectionChanged()107 void XrefsDialog::onFromTreeWidgetItemSelectionChanged()
108 {
109 auto index = ui->fromTreeWidget->currentIndex();
110 if (!ui->fromTreeWidget->selectionModel()->hasSelection() || !index.isValid()) {
111 return;
112 }
113 ui->toTreeWidget->clearSelection();
114 updatePreview(fromModel.address(index));
115 }
116
onToTreeWidgetItemSelectionChanged()117 void XrefsDialog::onToTreeWidgetItemSelectionChanged()
118 {
119 auto index = ui->toTreeWidget->currentIndex();
120 if (!ui->toTreeWidget->selectionModel()->hasSelection() || !index.isValid()) {
121 return;
122 }
123 ui->fromTreeWidget->clearSelection();
124 updatePreview(toModel.address(index));
125 }
126
updatePreview(RVA addr)127 void XrefsDialog::updatePreview(RVA addr)
128 {
129 TempConfig tempConfig;
130 tempConfig.set("scr.html", true);
131 tempConfig.set("scr.color", COLOR_MODE_16M);
132 tempConfig.set("asm.lines", false);
133 tempConfig.set("asm.bytes", false);
134
135 // Use cmd because cmRaw cannot handle the output properly. Why?
136 QString disas = Core()->cmd("pd--20 @ " + QString::number(addr));
137 ui->previewTextEdit->document()->setHtml(disas);
138
139 // Does it make any sense?
140 ui->previewTextEdit->find(normalizeAddr(RAddressString(addr)), QTextDocument::FindBackward);
141 ui->previewTextEdit->moveCursor(QTextCursor::StartOfLine, QTextCursor::MoveAnchor);
142 }
143
updateLabels(QString name)144 void XrefsDialog::updateLabels(QString name)
145 {
146 ui->label_xTo->setText(tr("X-Refs to %1 (%2 results):").arg(name).arg(toModel.rowCount()));
147 ui->label_xFrom->setText(tr("X-Refs from %1 (%2 results):").arg(name).arg(fromModel.rowCount()));
148 }
149
updateLabelsForVariables(QString name)150 void XrefsDialog::updateLabelsForVariables(QString name)
151 {
152 ui->label_xTo->setText(tr("Writes to %1").arg(name));
153 ui->label_xFrom->setText(tr("Reads from %1").arg(name));
154 }
155
hideXrefFromSection()156 void XrefsDialog::hideXrefFromSection()
157 {
158 ui->label_xFrom->hide();
159 ui->fromTreeWidget->hide();
160 }
161
fillRefsForAddress(RVA addr,QString name,bool whole_function)162 void XrefsDialog::fillRefsForAddress(RVA addr, QString name, bool whole_function)
163 {
164 setWindowTitle(tr("X-Refs for %1").arg(name));
165
166 toModel.readForOffset(addr, true, whole_function);
167 fromModel.readForOffset(addr, false, whole_function);
168
169 updateLabels(name);
170
171 // Adjust columns to content
172 qhelpers::adjustColumns(ui->fromTreeWidget, fromModel.columnCount(), 0);
173 qhelpers::adjustColumns(ui->toTreeWidget, toModel.columnCount(), 0);
174
175 // Automatically select the first line
176 if (!qhelpers::selectFirstItem(ui->toTreeWidget)) {
177 qhelpers::selectFirstItem(ui->fromTreeWidget);
178 }
179 }
180
fillRefsForVariable(QString nameOfVariable,RVA offset)181 void XrefsDialog::fillRefsForVariable(QString nameOfVariable, RVA offset)
182 {
183 setWindowTitle(tr("X-Refs for %1").arg(nameOfVariable));
184 updateLabelsForVariables(nameOfVariable);
185
186 // Initialize Model
187 toModel.readForVariable(nameOfVariable, true, offset);
188 fromModel.readForVariable(nameOfVariable, false, offset);
189 // Hide irrelevant column 1: which shows type
190 ui->fromTreeWidget->hideColumn(XrefModel::Columns::TYPE);
191 ui->toTreeWidget->hideColumn(XrefModel::Columns::TYPE);
192 // Adjust columns to content
193 qhelpers::adjustColumns(ui->fromTreeWidget, fromModel.columnCount(), 0);
194 qhelpers::adjustColumns(ui->toTreeWidget, toModel.columnCount(), 0);
195
196 // Automatically select the first line
197 if (!qhelpers::selectFirstItem(ui->toTreeWidget)) {
198 qhelpers::selectFirstItem(ui->fromTreeWidget);
199 }
200 }
201
xrefTypeString(const QString & type)202 QString XrefModel::xrefTypeString(const QString &type)
203 {
204 if (type == "CODE") {
205 return QStringLiteral("Code");
206 } else if (type == "CALL") {
207 return QStringLiteral("Call");
208 } else if (type == "DATA") {
209 return QStringLiteral("Data");
210 } else if (type == "STRING") {
211 return QStringLiteral("String");
212 }
213 return type;
214 }
215
216
XrefModel(QObject * parent)217 XrefModel::XrefModel(QObject *parent)
218 : AddressableItemModel(parent)
219 {
220 }
221
readForOffset(RVA offset,bool to,bool whole_function)222 void XrefModel::readForOffset(RVA offset, bool to, bool whole_function)
223 {
224 beginResetModel();
225 this->to = to;
226 xrefs = Core()->getXRefs(offset, to, whole_function);
227 endResetModel();
228 }
229
readForVariable(QString nameOfVariable,bool write,RVA offset)230 void XrefModel::readForVariable(QString nameOfVariable, bool write, RVA offset)
231 {
232 beginResetModel();
233 this->to = write;
234 xrefs = Core()->getXRefsForVariable(nameOfVariable, write, offset);
235 endResetModel();
236 }
237
rowCount(const QModelIndex & parent) const238 int XrefModel::rowCount(const QModelIndex &parent) const
239 {
240 Q_UNUSED(parent)
241 return xrefs.size();
242 }
243
columnCount(const QModelIndex & parent) const244 int XrefModel::columnCount(const QModelIndex &parent) const
245 {
246 Q_UNUSED(parent)
247 return Columns::COUNT;
248 }
249
data(const QModelIndex & index,int role) const250 QVariant XrefModel::data(const QModelIndex &index, int role) const
251 {
252 if (!index.isValid() || index.row() >= xrefs.count()) {
253 return QVariant();
254 }
255
256 const XrefDescription &xref = xrefs.at(index.row());
257
258 switch (role) {
259 case Qt::DisplayRole:
260 switch (index.column()) {
261 case OFFSET:
262 return to ? xref.from_str : xref.to_str;
263 case TYPE:
264 return xrefTypeString(xref.type);
265 case CODE:
266 if (to || xref.type != "DATA") {
267 return Core()->disassembleSingleInstruction(xref.from);
268 } else {
269 return QString();
270 }
271 case COMMENT:
272 return to ? Core()->getCommentAt(xref.from) : Core()->getCommentAt(xref.to);
273 }
274 return QVariant();
275 case FlagDescriptionRole:
276 return QVariant::fromValue(xref);
277 default:
278 break;
279 }
280 return QVariant();
281 }
282
headerData(int section,Qt::Orientation orientation,int role) const283 QVariant XrefModel::headerData(int section, Qt::Orientation orientation, int role) const
284 {
285 Q_UNUSED(orientation)
286
287 switch (role) {
288 case Qt::DisplayRole:
289 switch (section) {
290 case OFFSET:
291 return tr("Address");
292 case TYPE:
293 return tr("Type");
294 case CODE:
295 return tr("Code");
296 case COMMENT:
297 return tr("Comment");
298 default:
299 return QVariant();
300 }
301 default:
302 return QVariant();
303 }
304 }
305
address(const QModelIndex & index) const306 RVA XrefModel::address(const QModelIndex &index) const
307 {
308 const auto &xref = xrefs.at(index.row());
309 return to ? xref.from : xref.to;
310 }
311