Refactor Decompiled Code Structure (#1717)

This commit is contained in:
Florian Märkl 2019-08-27 17:27:39 +02:00 committed by GitHub
parent 448350c83d
commit 13e18f77de
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 90 additions and 149 deletions

View File

@ -5,6 +5,42 @@
#include <QJsonObject> #include <QJsonObject>
#include <QJsonArray> #include <QJsonArray>
ut64 AnnotatedCode::OffsetForPosition(size_t pos) const
{
size_t closestPos = SIZE_MAX;
ut64 closestOffset = UT64_MAX;
for (const auto &annotation : annotations) {
if (annotation.type != CodeAnnotation::Type::Offset || annotation.start > pos || annotation.end <= pos) {
continue;
}
if (closestPos != SIZE_MAX && closestPos >= annotation.start) {
continue;
}
closestPos = annotation.start;
closestOffset = annotation.offset.offset;
}
return closestOffset;
}
size_t AnnotatedCode::PositionForOffset(ut64 offset) const
{
size_t closestPos = SIZE_MAX;
ut64 closestOffset = UT64_MAX;
for (const auto &annotation : annotations) {
if (annotation.type != CodeAnnotation::Type::Offset || annotation.offset.offset > offset) {
continue;
}
if (closestOffset != UT64_MAX && closestOffset >= annotation.offset.offset) {
continue;
}
closestPos = annotation.start;
closestOffset = annotation.offset.offset;
}
return closestPos;
}
Decompiler::Decompiler(const QString &id, const QString &name, QObject *parent) Decompiler::Decompiler(const QString &id, const QString &name, QObject *parent)
: QObject(parent), : QObject(parent),
id(id), id(id),
@ -22,9 +58,9 @@ bool R2DecDecompiler::isAvailable()
return Core()->cmdList("e cmd.pdc=?").contains(QStringLiteral("pdd")); return Core()->cmdList("e cmd.pdc=?").contains(QStringLiteral("pdd"));
} }
DecompiledCode R2DecDecompiler::decompileAt(RVA addr) AnnotatedCode R2DecDecompiler::decompileAt(RVA addr)
{ {
DecompiledCode code; AnnotatedCode code = {};
QString s; QString s;
QJsonObject json = Core()->cmdj("pddj @ " + QString::number(addr)).object(); QJsonObject json = Core()->cmdj("pddj @ " + QString::number(addr)).object();
@ -36,31 +72,32 @@ DecompiledCode R2DecDecompiler::decompileAt(RVA addr)
if (!line.isString()) { if (!line.isString()) {
continue; continue;
} }
code.lines.append(DecompiledCode::Line(line.toString())); code.code.append(line.toString() + "\n");
} }
auto linesArray = json["lines"].toArray(); auto linesArray = json["lines"].toArray();
code.lines.reserve(code.lines.size() + linesArray.size());
for (const auto &line : linesArray) { for (const auto &line : linesArray) {
QJsonObject lineObject = line.toObject(); QJsonObject lineObject = line.toObject();
if (lineObject.isEmpty()) { if (lineObject.isEmpty()) {
continue; continue;
} }
DecompiledCode::Line codeLine; CodeAnnotation annotation = {};
codeLine.str = lineObject["str"].toString(); annotation.type = CodeAnnotation::Type::Offset;
annotation.start = code.code.length();
code.code.append(lineObject["str"].toString() + "\n");
annotation.end = code.code.length();
bool ok; bool ok;
codeLine.addr = lineObject["offset"].toVariant().toULongLong(&ok); annotation.offset.offset = lineObject["offset"].toVariant().toULongLong(&ok);
if (!ok) { if (ok) {
codeLine.addr = RVA_INVALID; code.annotations.push_back(annotation);
} }
code.lines.append(codeLine);
} }
for (const auto &line : json["errors"].toArray()) { for (const auto &line : json["errors"].toArray()) {
if (!line.isString()) { if (!line.isString()) {
continue; continue;
} }
code.lines.append(DecompiledCode::Line(line.toString())); code.code.append(line.toString() + "\n");
} }
return code; return code;

View File

@ -6,34 +6,37 @@
#include <QString> #include <QString>
#include <QObject> #include <QObject>
struct CodeAnnotation
{
size_t start;
size_t end;
enum class Type { Offset };
Type type;
union
{
struct
{
ut64 offset;
} offset;
};
};
/** /**
* Describes the result of a Decompilation Process with optional metadata * Describes the result of a Decompilation Process with optional metadata
*/ */
struct DecompiledCode { struct AnnotatedCode
{
/** /**
* A single line of decompiled code * The entire decompiled code
*/ */
struct Line QString code;
{
QString str;
/** QList<CodeAnnotation> annotations;
* Offset of the original instruction
*/
RVA addr;
Line() ut64 OffsetForPosition(size_t pos) const;
{ size_t PositionForOffset(ut64 offset) const;
this->addr = RVA_INVALID;
}
explicit Line(const QString &str, RVA addr = RVA_INVALID)
{
this->str = str;
this->addr = addr;
}
};
QList<Line> lines = {};
}; };
/** /**
@ -54,7 +57,7 @@ public:
QString getId() const { return id; } QString getId() const { return id; }
QString getName() const { return name; } QString getName() const { return name; }
virtual DecompiledCode decompileAt(RVA addr) =0; virtual AnnotatedCode decompileAt(RVA addr) =0;
}; };
class R2DecDecompiler: public Decompiler class R2DecDecompiler: public Decompiler
@ -63,7 +66,7 @@ class R2DecDecompiler: public Decompiler
public: public:
explicit R2DecDecompiler(QObject *parent = nullptr); explicit R2DecDecompiler(QObject *parent = nullptr);
DecompiledCode decompileAt(RVA addr) override; AnnotatedCode decompileAt(RVA addr) override;
static bool isAvailable(); static bool isAvailable();
}; };

View File

@ -14,27 +14,6 @@
#include <QObject> #include <QObject>
#include <QTextBlockUserData> #include <QTextBlockUserData>
/**
* Represents a single line of decompiled code as part of the displayed text,
* including the position inside the QTextDocument
*/
struct DecompiledCodeTextLine
{
/**
* position inside the QTextDocument
*/
int position;
DecompiledCode::Line line;
DecompiledCodeTextLine(int position, const DecompiledCode::Line &line)
{
this->position = position;
this->line = line;
}
};
PseudocodeWidget::PseudocodeWidget(MainWindow *main, QAction *action) : PseudocodeWidget::PseudocodeWidget(MainWindow *main, QAction *action) :
MemoryDockWidget(MemoryWidgetType::Pseudocode, main, action), MemoryDockWidget(MemoryWidgetType::Pseudocode, main, action),
mCtxMenu(new DisassemblyContextMenu(this, main)), mCtxMenu(new DisassemblyContextMenu(this, main)),
@ -104,24 +83,14 @@ void PseudocodeWidget::doRefresh(RVA addr)
return; return;
} }
DecompiledCode decompiledCode = dec->decompileAt(addr); code = dec->decompileAt(addr);
if (code.code.isEmpty()) {
textLines = {};
textLines.reserve(decompiledCode.lines.size());
if (decompiledCode.lines.isEmpty()) {
ui->textEdit->setPlainText(tr("Cannot decompile at") + " " + RAddressString( ui->textEdit->setPlainText(tr("Cannot decompile at") + " " + RAddressString(
addr) + " " + tr("(Not a function?)")); addr) + " " + tr("(Not a function?)"));
return; return;
} else { } else {
connectCursorPositionChanged(true); connectCursorPositionChanged(true);
ui->textEdit->document()->clear(); ui->textEdit->setPlainText(code.code);
QTextCursor cursor(ui->textEdit->document());
for (const DecompiledCode::Line &line : decompiledCode.lines) {
textLines.append(DecompiledCodeTextLine(cursor.position(), line));
// Can't use cursor.block()->setUserData() here, because the Syntax Highlighter will mess it up.
cursor.insertText(line.str + "\n");
}
connectCursorPositionChanged(false); connectCursorPositionChanged(false);
seekChanged(); seekChanged();
} }
@ -148,7 +117,8 @@ void PseudocodeWidget::connectCursorPositionChanged(bool disconnect)
void PseudocodeWidget::cursorPositionChanged() void PseudocodeWidget::cursorPositionChanged()
{ {
RVA offset = getOffsetAtLine(ui->textEdit->textCursor()); size_t pos = ui->textEdit->textCursor().position();
RVA offset = code.OffsetForPosition(pos);
if (offset != RVA_INVALID && offset != Core()->getOffset()) { if (offset != RVA_INVALID && offset != Core()->getOffset()) {
seekFromCursor = true; seekFromCursor = true;
Core()->seek(offset); Core()->seek(offset);
@ -169,61 +139,18 @@ void PseudocodeWidget::seekChanged()
void PseudocodeWidget::updateCursorPosition() void PseudocodeWidget::updateCursorPosition()
{ {
RVA offset = Core()->getOffset(); RVA offset = Core()->getOffset();
size_t pos = code.PositionForOffset(offset);
if (pos == SIZE_MAX) {
return;
}
connectCursorPositionChanged(true); connectCursorPositionChanged(true);
QTextCursor cursor = ui->textEdit->textCursor();
auto it = findLineByOffset(offset); cursor.setPosition(pos);
if (it != textLines.end()) { ui->textEdit->setTextCursor(cursor);
// move back if the offset is identical (so we don't land on closing braces for example) updateSelection();
while (it != textLines.begin()) {
auto prev = it - 1;
if (prev->line.addr != it->line.addr) {
break;
}
it = prev;
}
QTextCursor cursor = ui->textEdit->textCursor();
cursor.setPosition((*it).position);
ui->textEdit->setTextCursor(cursor);
updateSelection();
}
connectCursorPositionChanged(false); connectCursorPositionChanged(false);
} }
QList<DecompiledCodeTextLine>::iterator PseudocodeWidget::findLine(int position)
{
return std::upper_bound(textLines.begin(), textLines.end(), position,
[](int pos, const DecompiledCodeTextLine &line) {
return pos < line.position;
});
}
QList<DecompiledCodeTextLine>::iterator PseudocodeWidget::findLineByOffset(RVA offset)
{
auto it = textLines.begin();
auto candidate = it;
for (; it != textLines.end(); it++) {
RVA lineOffset = it->line.addr;
if (lineOffset != RVA_INVALID && lineOffset > offset) {
break;
}
if (candidate->line.addr == RVA_INVALID || (lineOffset != RVA_INVALID && lineOffset > candidate->line.addr)) {
candidate = it;
}
}
return candidate;
}
RVA PseudocodeWidget::getOffsetAtLine(const QTextCursor &tc)
{
auto it = findLine(tc.position());
if (it == textLines.begin()) {
return RVA_INVALID;
}
it--;
return (*it).line.addr;
}
void PseudocodeWidget::setupFonts() void PseudocodeWidget::setupFonts()
{ {
QFont font = Config()->getFont(); QFont font = Config()->getFont();
@ -236,20 +163,7 @@ void PseudocodeWidget::updateSelection()
// Highlight the current line // Highlight the current line
auto cursor = ui->textEdit->textCursor(); auto cursor = ui->textEdit->textCursor();
extraSelections.append(createLineHighlightSelection(cursor));
RVA cursorOffset = getOffsetAtLine(cursor);
if (cursorOffset != RVA_INVALID) {
for (auto it = findLineByOffset(cursorOffset);
it != textLines.end() && it->line.addr != RVA_INVALID && it->line.addr <= cursorOffset;
it++) {
auto lineCursor = cursor;
lineCursor.setPosition(it->position);
extraSelections.append(createLineHighlightSelection(lineCursor));
}
} else {
// if the cursor position has no valid offset, just highlight the line
extraSelections.append(createLineHighlightSelection(cursor));
}
// Highlight all the words in the document same as the current one // Highlight all the words in the document same as the current one
cursor.select(QTextCursor::WordUnderCursor); cursor.select(QTextCursor::WordUnderCursor);

View File

@ -5,6 +5,7 @@
#include "core/Cutter.h" #include "core/Cutter.h"
#include "MemoryDockWidget.h" #include "MemoryDockWidget.h"
#include "Decompiler.h"
namespace Ui { namespace Ui {
class PseudocodeWidget; class PseudocodeWidget;
@ -41,10 +42,7 @@ private:
QSyntaxHighlighter *syntaxHighlighter; QSyntaxHighlighter *syntaxHighlighter;
/** AnnotatedCode code;
* Index of all lines that are currently displayed, ordered by the position in the text
*/
QList<DecompiledCodeTextLine> textLines;
bool seekFromCursor = false; bool seekFromCursor = false;
@ -54,17 +52,6 @@ private:
void connectCursorPositionChanged(bool disconnect); void connectCursorPositionChanged(bool disconnect);
void updateCursorPosition(); void updateCursorPosition();
/**
* @return Iterator to the first line that is after position or last if not found.
*/
QList<DecompiledCodeTextLine>::iterator findLine(int position);
/**
* @return Iterator to the first line that is considered to contain offset
*/
QList<DecompiledCodeTextLine>::iterator findLineByOffset(RVA offset);
RVA getOffsetAtLine(const QTextCursor &tc);
QString getWindowTitle() const override; QString getWindowTitle() const override;
}; };