mirror of
https://github.com/rizinorg/cutter.git
synced 2025-01-31 08:37:26 +00:00
Refactor Decompiled Code Structure (#1717)
This commit is contained in:
parent
448350c83d
commit
13e18f77de
@ -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;
|
||||||
|
@ -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();
|
||||||
};
|
};
|
||||||
|
@ -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);
|
||||||
|
|
||||||
auto it = findLineByOffset(offset);
|
|
||||||
if (it != textLines.end()) {
|
|
||||||
// move back if the offset is identical (so we don't land on closing braces for example)
|
|
||||||
while (it != textLines.begin()) {
|
|
||||||
auto prev = it - 1;
|
|
||||||
if (prev->line.addr != it->line.addr) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
it = prev;
|
|
||||||
}
|
|
||||||
QTextCursor cursor = ui->textEdit->textCursor();
|
QTextCursor cursor = ui->textEdit->textCursor();
|
||||||
cursor.setPosition((*it).position);
|
cursor.setPosition(pos);
|
||||||
ui->textEdit->setTextCursor(cursor);
|
ui->textEdit->setTextCursor(cursor);
|
||||||
updateSelection();
|
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();
|
||||||
|
|
||||||
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));
|
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);
|
||||||
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user