mirror of
https://github.com/rizinorg/cutter.git
synced 2024-12-24 05:45:27 +00:00
Refactored Decompiler Widget and R2Dec Plugin to use RAnnotatedCode (#2227)
This commit is contained in:
parent
498d2076c8
commit
255ffe1208
@ -5,42 +5,6 @@
|
|||||||
#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),
|
||||||
@ -48,6 +12,11 @@ Decompiler::Decompiler(const QString &id, const QString &name, QObject *parent)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RAnnotatedCode *Decompiler::makeWarning(QString warningMessage){
|
||||||
|
std::string temporary = warningMessage.toStdString();
|
||||||
|
return r_annotated_code_new(strdup(temporary.c_str()));
|
||||||
|
}
|
||||||
|
|
||||||
R2DecDecompiler::R2DecDecompiler(QObject *parent)
|
R2DecDecompiler::R2DecDecompiler(QObject *parent)
|
||||||
: Decompiler("r2dec", "r2dec", parent)
|
: Decompiler("r2dec", "r2dec", parent)
|
||||||
{
|
{
|
||||||
@ -64,26 +33,22 @@ void R2DecDecompiler::decompileAt(RVA addr)
|
|||||||
if (task) {
|
if (task) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
task = new R2Task("pddj @ " + QString::number(addr));
|
task = new R2Task("pddj @ " + QString::number(addr));
|
||||||
connect(task, &R2Task::finished, this, [this]() {
|
connect(task, &R2Task::finished, this, [this]() {
|
||||||
AnnotatedCode code = {};
|
|
||||||
QString s;
|
|
||||||
|
|
||||||
QJsonObject json = task->getResultJson().object();
|
QJsonObject json = task->getResultJson().object();
|
||||||
delete task;
|
delete task;
|
||||||
task = nullptr;
|
task = nullptr;
|
||||||
if (json.isEmpty()) {
|
if (json.isEmpty()) {
|
||||||
code.code = tr("Failed to parse JSON from r2dec");
|
emit finished(Decompiler::makeWarning(tr("Failed to parse JSON from r2dec")));
|
||||||
emit finished(code);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
RAnnotatedCode *code = r_annotated_code_new(nullptr);
|
||||||
|
QString codeString = "";
|
||||||
for (const auto &line : json["log"].toArray()) {
|
for (const auto &line : json["log"].toArray()) {
|
||||||
if (!line.isString()) {
|
if (!line.isString()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
code.code.append(line.toString() + "\n");
|
codeString.append(line.toString() + "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
auto linesArray = json["lines"].toArray();
|
auto linesArray = json["lines"].toArray();
|
||||||
@ -92,25 +57,24 @@ void R2DecDecompiler::decompileAt(RVA addr)
|
|||||||
if (lineObject.isEmpty()) {
|
if (lineObject.isEmpty()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
CodeAnnotation annotation = {};
|
RCodeAnnotation *annotationi = new RCodeAnnotation;
|
||||||
annotation.type = CodeAnnotation::Type::Offset;
|
annotationi->start = codeString.length();
|
||||||
annotation.start = code.code.length();
|
codeString.append(lineObject["str"].toString() + "\n");
|
||||||
code.code.append(lineObject["str"].toString() + "\n");
|
annotationi->end = codeString.length();
|
||||||
annotation.end = code.code.length();
|
|
||||||
bool ok;
|
bool ok;
|
||||||
annotation.offset.offset = lineObject["offset"].toVariant().toULongLong(&ok);
|
annotationi->type = R_CODE_ANNOTATION_TYPE_OFFSET;
|
||||||
if (ok) {
|
annotationi->offset.offset = lineObject["offset"].toVariant().toULongLong(&ok);
|
||||||
code.annotations.push_back(annotation);
|
r_annotated_code_add_annotation(code, annotationi);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto &line : json["errors"].toArray()) {
|
for (const auto &line : json["errors"].toArray()) {
|
||||||
if (!line.isString()) {
|
if (!line.isString()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
code.code.append(line.toString() + "\n");
|
codeString.append(line.toString() + "\n");
|
||||||
}
|
}
|
||||||
|
std::string tmp = codeString.toStdString();
|
||||||
|
code->code = strdup(tmp.c_str());
|
||||||
emit finished(code);
|
emit finished(code);
|
||||||
});
|
});
|
||||||
task->startTask();
|
task->startTask();
|
||||||
|
@ -3,43 +3,11 @@
|
|||||||
|
|
||||||
#include "CutterCommon.h"
|
#include "CutterCommon.h"
|
||||||
#include "R2Task.h"
|
#include "R2Task.h"
|
||||||
|
#include <r_util/r_annotated_code.h>
|
||||||
|
|
||||||
#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
|
|
||||||
*/
|
|
||||||
struct AnnotatedCode
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* The entire decompiled code
|
|
||||||
*/
|
|
||||||
QString code;
|
|
||||||
|
|
||||||
QList<CodeAnnotation> annotations;
|
|
||||||
|
|
||||||
ut64 OffsetForPosition(size_t pos) const;
|
|
||||||
size_t PositionForOffset(ut64 offset) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements a decompiler that can be registered using CutterCore::registerDecompiler()
|
* Implements a decompiler that can be registered using CutterCore::registerDecompiler()
|
||||||
*/
|
*/
|
||||||
@ -55,6 +23,8 @@ public:
|
|||||||
Decompiler(const QString &id, const QString &name, QObject *parent = nullptr);
|
Decompiler(const QString &id, const QString &name, QObject *parent = nullptr);
|
||||||
virtual ~Decompiler() = default;
|
virtual ~Decompiler() = default;
|
||||||
|
|
||||||
|
static RAnnotatedCode *makeWarning(QString warningMessage);
|
||||||
|
|
||||||
QString getId() const { return id; }
|
QString getId() const { return id; }
|
||||||
QString getName() const { return name; }
|
QString getName() const { return name; }
|
||||||
virtual bool isRunning() { return false; }
|
virtual bool isRunning() { return false; }
|
||||||
@ -64,7 +34,7 @@ public:
|
|||||||
virtual void cancel() {}
|
virtual void cancel() {}
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void finished(AnnotatedCode code);
|
void finished(RAnnotatedCode *codeDecompiled);
|
||||||
};
|
};
|
||||||
|
|
||||||
class R2DecDecompiler: public Decompiler
|
class R2DecDecompiler: public Decompiler
|
||||||
|
@ -18,7 +18,8 @@
|
|||||||
DecompilerWidget::DecompilerWidget(MainWindow *main) :
|
DecompilerWidget::DecompilerWidget(MainWindow *main) :
|
||||||
MemoryDockWidget(MemoryWidgetType::Decompiler, main),
|
MemoryDockWidget(MemoryWidgetType::Decompiler, main),
|
||||||
mCtxMenu(new DisassemblyContextMenu(this, main)),
|
mCtxMenu(new DisassemblyContextMenu(this, main)),
|
||||||
ui(new Ui::DecompilerWidget)
|
ui(new Ui::DecompilerWidget),
|
||||||
|
code(Decompiler::makeWarning(tr("Choose an offset and refresh to get decompiled code")), &r_annotated_code_free)
|
||||||
{
|
{
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
|
|
||||||
@ -139,6 +140,45 @@ void DecompilerWidget::updateRefreshButton()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static ut64 offsetForPosition(RAnnotatedCode &codeDecompiled, size_t pos)
|
||||||
|
{
|
||||||
|
size_t closestPos = SIZE_MAX;
|
||||||
|
ut64 closestOffset = UT64_MAX;
|
||||||
|
void *annotationi;
|
||||||
|
r_vector_foreach(&codeDecompiled.annotations, annotationi) {
|
||||||
|
RCodeAnnotation *annotation = (RCodeAnnotation *)annotationi;
|
||||||
|
if (annotation->type != R_CODE_ANNOTATION_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;
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t positionForOffset(RAnnotatedCode &codeDecompiled, ut64 offset)
|
||||||
|
{
|
||||||
|
size_t closestPos = SIZE_MAX;
|
||||||
|
ut64 closestOffset = UT64_MAX;
|
||||||
|
void *annotationi;
|
||||||
|
r_vector_foreach(&codeDecompiled.annotations, annotationi) {
|
||||||
|
RCodeAnnotation *annotation = (RCodeAnnotation *)annotationi;
|
||||||
|
if (annotation->type != R_CODE_ANNOTATION_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;
|
||||||
|
}
|
||||||
|
|
||||||
void DecompilerWidget::doRefresh(RVA addr)
|
void DecompilerWidget::doRefresh(RVA addr)
|
||||||
{
|
{
|
||||||
if (!refreshDeferrer->attemptRefresh(nullptr)) {
|
if (!refreshDeferrer->attemptRefresh(nullptr)) {
|
||||||
@ -183,7 +223,7 @@ void DecompilerWidget::refreshDecompiler()
|
|||||||
|
|
||||||
QTextCursor DecompilerWidget::getCursorForAddress(RVA addr)
|
QTextCursor DecompilerWidget::getCursorForAddress(RVA addr)
|
||||||
{
|
{
|
||||||
size_t pos = code.PositionForOffset(addr);
|
size_t pos = positionForOffset(*code, addr);
|
||||||
if (pos == SIZE_MAX || pos == 0) {
|
if (pos == SIZE_MAX || pos == 0) {
|
||||||
return QTextCursor();
|
return QTextCursor();
|
||||||
}
|
}
|
||||||
@ -193,19 +233,20 @@ QTextCursor DecompilerWidget::getCursorForAddress(RVA addr)
|
|||||||
return cursor;
|
return cursor;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DecompilerWidget::decompilationFinished(AnnotatedCode code)
|
void DecompilerWidget::decompilationFinished(RAnnotatedCode *codeDecompiled)
|
||||||
{
|
{
|
||||||
ui->progressLabel->setVisible(false);
|
ui->progressLabel->setVisible(false);
|
||||||
ui->decompilerComboBox->setEnabled(decompilerSelectionEnabled);
|
ui->decompilerComboBox->setEnabled(decompilerSelectionEnabled);
|
||||||
updateRefreshButton();
|
updateRefreshButton();
|
||||||
|
|
||||||
this->code = code;
|
this->code.reset(codeDecompiled);
|
||||||
if (code.code.isEmpty()) {
|
QString codeString = QString::fromUtf8(this->code->code);
|
||||||
|
if (codeString.isEmpty()) {
|
||||||
ui->textEdit->setPlainText(tr("Cannot decompile at this address (Not a function?)"));
|
ui->textEdit->setPlainText(tr("Cannot decompile at this address (Not a function?)"));
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
connectCursorPositionChanged(true);
|
connectCursorPositionChanged(true);
|
||||||
ui->textEdit->setPlainText(code.code);
|
ui->textEdit->setPlainText(codeString);
|
||||||
connectCursorPositionChanged(false);
|
connectCursorPositionChanged(false);
|
||||||
updateCursorPosition();
|
updateCursorPosition();
|
||||||
highlightPC();
|
highlightPC();
|
||||||
@ -244,7 +285,7 @@ void DecompilerWidget::cursorPositionChanged()
|
|||||||
}
|
}
|
||||||
|
|
||||||
size_t pos = ui->textEdit->textCursor().position();
|
size_t pos = ui->textEdit->textCursor().position();
|
||||||
RVA offset = code.OffsetForPosition(pos);
|
RVA offset = offsetForPosition(*code, 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);
|
||||||
@ -274,7 +315,7 @@ void DecompilerWidget::seekChanged()
|
|||||||
void DecompilerWidget::updateCursorPosition()
|
void DecompilerWidget::updateCursorPosition()
|
||||||
{
|
{
|
||||||
RVA offset = Core()->getOffset();
|
RVA offset = Core()->getOffset();
|
||||||
size_t pos = code.PositionForOffset(offset);
|
size_t pos = positionForOffset(*code, offset);
|
||||||
if (pos == SIZE_MAX) {
|
if (pos == SIZE_MAX) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -334,7 +375,7 @@ void DecompilerWidget::showDisasContextMenu(const QPoint &pt)
|
|||||||
void DecompilerWidget::seekToReference()
|
void DecompilerWidget::seekToReference()
|
||||||
{
|
{
|
||||||
size_t pos = ui->textEdit->textCursor().position();
|
size_t pos = ui->textEdit->textCursor().position();
|
||||||
RVA offset = code.OffsetForPosition(pos);
|
RVA offset = offsetForPosition(*code, pos);
|
||||||
seekable->seekToReference(offset);
|
seekable->seekToReference(offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -352,7 +393,6 @@ bool DecompilerWidget::eventFilter(QObject *obj, QEvent *event)
|
|||||||
return MemoryDockWidget::eventFilter(obj, event);
|
return MemoryDockWidget::eventFilter(obj, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void DecompilerWidget::highlightPC()
|
void DecompilerWidget::highlightPC()
|
||||||
{
|
{
|
||||||
RVA PCAddress = Core()->getProgramCounterValue();
|
RVA PCAddress = Core()->getProgramCounterValue();
|
||||||
|
@ -38,7 +38,7 @@ private slots:
|
|||||||
void decompilerSelected();
|
void decompilerSelected();
|
||||||
void cursorPositionChanged();
|
void cursorPositionChanged();
|
||||||
void seekChanged();
|
void seekChanged();
|
||||||
void decompilationFinished(AnnotatedCode code);
|
void decompilationFinished(RAnnotatedCode *code);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<Ui::DecompilerWidget> ui;
|
std::unique_ptr<Ui::DecompilerWidget> ui;
|
||||||
@ -56,8 +56,7 @@ private:
|
|||||||
bool decompilerWasBusy;
|
bool decompilerWasBusy;
|
||||||
|
|
||||||
RVA decompiledFunctionAddr;
|
RVA decompiledFunctionAddr;
|
||||||
AnnotatedCode code;
|
std::unique_ptr<RAnnotatedCode, void (*)(RAnnotatedCode*)> code;
|
||||||
|
|
||||||
bool seekFromCursor = false;
|
bool seekFromCursor = false;
|
||||||
|
|
||||||
Decompiler *getCurrentDecompiler();
|
Decompiler *getCurrentDecompiler();
|
||||||
@ -99,6 +98,7 @@ private:
|
|||||||
* It will also run when a breakpoint is added, removed or modified.
|
* It will also run when a breakpoint is added, removed or modified.
|
||||||
*/
|
*/
|
||||||
void highlightBreakpoints();
|
void highlightBreakpoints();
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // DECOMPILERWIDGET_H
|
#endif // DECOMPILERWIDGET_H
|
||||||
|
Loading…
Reference in New Issue
Block a user