Add debug dialogs (#1903)

This commit is contained in:
yossizap 2019-12-11 11:26:54 +00:00 committed by Itay Cohen
parent ef97c84351
commit 519cd5dabd
11 changed files with 541 additions and 249 deletions

View File

@ -268,6 +268,7 @@ SOURCES += \
dialogs/FlagDialog.cpp \ dialogs/FlagDialog.cpp \
dialogs/RenameDialog.cpp \ dialogs/RenameDialog.cpp \
dialogs/RemoteDebugDialog.cpp \ dialogs/RemoteDebugDialog.cpp \
dialogs/NativeDebugDialog.cpp \
dialogs/XrefsDialog.cpp \ dialogs/XrefsDialog.cpp \
core/MainWindow.cpp \ core/MainWindow.cpp \
common/Helpers.cpp \ common/Helpers.cpp \
@ -403,6 +404,7 @@ HEADERS += \
dialogs/FlagDialog.h \ dialogs/FlagDialog.h \
dialogs/RenameDialog.h \ dialogs/RenameDialog.h \
dialogs/RemoteDebugDialog.h \ dialogs/RemoteDebugDialog.h \
dialogs/NativeDebugDialog.h \
dialogs/XrefsDialog.h \ dialogs/XrefsDialog.h \
common/Helpers.h \ common/Helpers.h \
common/HexAsciiHighlighter.h \ common/HexAsciiHighlighter.h \
@ -536,6 +538,7 @@ FORMS += \
dialogs/FlagDialog.ui \ dialogs/FlagDialog.ui \
dialogs/RenameDialog.ui \ dialogs/RenameDialog.ui \
dialogs/RemoteDebugDialog.ui \ dialogs/RemoteDebugDialog.ui \
dialogs/NativeDebugDialog.ui \
dialogs/XrefsDialog.ui \ dialogs/XrefsDialog.ui \
dialogs/NewfileDialog.ui \ dialogs/NewfileDialog.ui \
dialogs/InitialOptionsDialog.ui \ dialogs/InitialOptionsDialog.ui \

View File

@ -340,12 +340,12 @@ bool CutterCore::isDebugTaskInProgress()
return false; return false;
} }
void CutterCore::asyncCmdEsil(const char *command, QSharedPointer<R2Task> &task) bool CutterCore::asyncCmdEsil(const char *command, QSharedPointer<R2Task> &task)
{ {
asyncCmd(command, task); asyncCmd(command, task);
if (task.isNull()) { if (task.isNull()) {
return; return false;
} }
connect(task.data(), &R2Task::finished, this, [this, task] () { connect(task.data(), &R2Task::finished, this, [this, task] () {
@ -355,12 +355,14 @@ void CutterCore::asyncCmdEsil(const char *command, QSharedPointer<R2Task> &task)
msgBox.showMessage("Stopped when attempted to run an invalid instruction. You can disable this in Preferences"); msgBox.showMessage("Stopped when attempted to run an invalid instruction. You can disable this in Preferences");
} }
}); });
return true;
} }
void CutterCore::asyncCmd(const char *str, QSharedPointer<R2Task> &task) bool CutterCore::asyncCmd(const char *str, QSharedPointer<R2Task> &task)
{ {
if (!task.isNull()) { if (!task.isNull()) {
return; return false;
} }
CORE_LOCK(); CORE_LOCK();
@ -376,7 +378,7 @@ void CutterCore::asyncCmd(const char *str, QSharedPointer<R2Task> &task)
} }
}); });
task->startTask(); return true;
} }
QString CutterCore::cmdRaw(const QString &str) QString CutterCore::cmdRaw(const QString &str)
@ -1200,42 +1202,43 @@ void CutterCore::setRegister(QString regName, QString regValue)
void CutterCore::setCurrentDebugThread(int tid) void CutterCore::setCurrentDebugThread(int tid)
{ {
asyncCmd("dpt=" + QString::number(tid), debugTask); if (!asyncCmd("dpt=" + QString::number(tid), debugTask)) {
if (!debugTask.isNull()) {
emit debugTaskStateChanged();
connect(debugTask.data(), &R2Task::finished, this, [this] () {
debugTask.clear();
emit registersChanged();
emit refreshCodeViews();
emit stackChanged();
syncAndSeekProgramCounter();
emit switchedThread();
emit debugTaskStateChanged();
});
}
}
void CutterCore::setCurrentDebugProcess(int pid)
{
if (!currentlyDebugging) {
return; return;
} }
emit debugTaskStateChanged(); emit debugTaskStateChanged();
asyncCmd("dp=" + QString::number(pid), debugTask); connect(debugTask.data(), &R2Task::finished, this, [this] () {
if (!debugTask.isNull()) { debugTask.clear();
emit registersChanged();
emit refreshCodeViews();
emit stackChanged();
syncAndSeekProgramCounter();
emit switchedThread();
emit debugTaskStateChanged(); emit debugTaskStateChanged();
connect(debugTask.data(), &R2Task::finished, this, [this] () { });
debugTask.clear();
emit registersChanged(); debugTask->startTask();
emit refreshCodeViews(); }
emit stackChanged();
emit flagsChanged(); void CutterCore::setCurrentDebugProcess(int pid)
syncAndSeekProgramCounter(); {
emit switchedProcess(); if (!currentlyDebugging || !asyncCmd("dp=" + QString::number(pid), debugTask)) {
emit debugTaskStateChanged(); return;
});
} }
emit debugTaskStateChanged();
connect(debugTask.data(), &R2Task::finished, this, [this] () {
debugTask.clear();
emit registersChanged();
emit refreshCodeViews();
emit stackChanged();
emit flagsChanged();
syncAndSeekProgramCounter();
emit switchedProcess();
emit debugTaskStateChanged();
});
debugTask->startTask();
} }
void CutterCore::startDebug() void CutterCore::startDebug()
@ -1244,17 +1247,38 @@ void CutterCore::startDebug()
offsetPriorDebugging = getOffset(); offsetPriorDebugging = getOffset();
} }
currentlyOpenFile = getConfig("file.path"); currentlyOpenFile = getConfig("file.path");
cmd("ood");
emit registersChanged(); if (!asyncCmd("ood", debugTask)) {
if (!currentlyDebugging) { return;
setConfig("asm.flags", false);
currentlyDebugging = true;
emit changeDebugView();
emit flagsChanged();
emit refreshCodeViews();
} }
emit stackChanged();
emit debugTaskStateChanged(); emit debugTaskStateChanged();
connect(debugTask.data(), &R2Task::finished, this, [this] () {
if (debugTaskDialog) {
delete debugTaskDialog;
}
debugTask.clear();
emit registersChanged();
if (!currentlyDebugging) {
setConfig("asm.flags", false);
currentlyDebugging = true;
emit changeDebugView();
emit flagsChanged();
emit refreshCodeViews();
}
emit stackChanged();
emit debugTaskStateChanged();
});
debugTaskDialog = new R2TaskDialog(debugTask);
debugTaskDialog->setBreakOnClose(true);
debugTaskDialog->setAttribute(Qt::WA_DeleteOnClose);
debugTaskDialog->setDesc(tr("Starting native debug..."));
debugTaskDialog->show();
debugTask->startTask();
} }
void CutterCore::startEmulation() void CutterCore::startEmulation()
@ -1262,22 +1286,41 @@ void CutterCore::startEmulation()
if (!currentlyDebugging) { if (!currentlyDebugging) {
offsetPriorDebugging = getOffset(); offsetPriorDebugging = getOffset();
} }
// clear registers, init esil state, stack, progcounter at current seek // clear registers, init esil state, stack, progcounter at current seek
cmd("aei; aeim; aeip"); asyncCmd("aei; aeim; aeip", debugTask);
if (!currentlyDebugging || !currentlyEmulating) {
// prevent register flags from appearing during debug/emul
setConfig("asm.flags", false);
// allows to view self-modifying code changes or other binary changes
setConfig("io.cache", true);
currentlyDebugging = true;
currentlyEmulating = true;
emit changeDebugView();
emit flagsChanged();
}
emit registersChanged();
emit stackChanged();
emit refreshCodeViews();
emit debugTaskStateChanged(); emit debugTaskStateChanged();
connect(debugTask.data(), &R2Task::finished, this, [this] () {
if (debugTaskDialog) {
delete debugTaskDialog;
}
debugTask.clear();
if (!currentlyDebugging || !currentlyEmulating) {
// prevent register flags from appearing during debug/emul
setConfig("asm.flags", false);
// allows to view self-modifying code changes or other binary changes
setConfig("io.cache", true);
currentlyDebugging = true;
currentlyEmulating = true;
emit changeDebugView();
emit flagsChanged();
}
emit registersChanged();
emit stackChanged();
emit refreshCodeViews();
emit debugTaskStateChanged();
});
debugTaskDialog = new R2TaskDialog(debugTask);
debugTaskDialog->setBreakOnClose(true);
debugTaskDialog->setAttribute(Qt::WA_DeleteOnClose);
debugTaskDialog->setDesc(tr("Starting emulation..."));
debugTaskDialog->show();
debugTask->startTask();
} }
void CutterCore::attachRemote(const QString &uri) void CutterCore::attachRemote(const QString &uri)
@ -1329,8 +1372,10 @@ void CutterCore::attachRemote(const QString &uri)
debugTaskDialog = new R2TaskDialog(debugTask); debugTaskDialog = new R2TaskDialog(debugTask);
debugTaskDialog->setBreakOnClose(true); debugTaskDialog->setBreakOnClose(true);
debugTaskDialog->setAttribute(Qt::WA_DeleteOnClose); debugTaskDialog->setAttribute(Qt::WA_DeleteOnClose);
debugTaskDialog->setDesc("Connecting to: " + uri); debugTaskDialog->setDesc(tr("Connecting to: ") + uri);
debugTaskDialog->show(); debugTaskDialog->show();
debugTask->startTask();
} }
void CutterCore::attachDebug(int pid) void CutterCore::attachDebug(int pid)
@ -1340,21 +1385,34 @@ void CutterCore::attachDebug(int pid)
} }
// attach to process with dbg plugin // attach to process with dbg plugin
cmd("o-*; e cfg.debug = true; o+ dbg://" + QString::number(pid)); asyncCmd("o-*; e cfg.debug = true; o+ dbg://" + QString::number(pid), debugTask);
QString programCounterValue = cmd("dr?`drn PC`").trimmed();
seekAndShow(programCounterValue);
emit registersChanged();
if (!currentlyDebugging || !currentlyEmulating) {
// prevent register flags from appearing during debug/emul
setConfig("asm.flags", false);
currentlyDebugging = true;
currentlyOpenFile = getConfig("file.path");
currentlyAttachedToPID = pid;
emit flagsChanged();
emit changeDebugView();
}
emit debugTaskStateChanged(); emit debugTaskStateChanged();
connect(debugTask.data(), &R2Task::finished, this, [this, pid] () {
if (debugTaskDialog) {
delete debugTaskDialog;
}
debugTask.clear();
syncAndSeekProgramCounter();
if (!currentlyDebugging || !currentlyEmulating) {
// prevent register flags from appearing during debug/emul
setConfig("asm.flags", false);
currentlyDebugging = true;
currentlyOpenFile = getConfig("file.path");
currentlyAttachedToPID = pid;
emit flagsChanged();
emit changeDebugView();
}
});
debugTaskDialog = new R2TaskDialog(debugTask);
debugTaskDialog->setBreakOnClose(true);
debugTaskDialog->setAttribute(Qt::WA_DeleteOnClose);
debugTaskDialog->setDesc(tr("Attaching to process (") + QString::number(pid) + ")...");
debugTaskDialog->show();
debugTask->startTask();
} }
void CutterCore::suspendDebug() void CutterCore::suspendDebug()
@ -1364,33 +1422,45 @@ void CutterCore::suspendDebug()
void CutterCore::stopDebug() void CutterCore::stopDebug()
{ {
if (currentlyDebugging) { if (!currentlyDebugging) {
currentlyDebugging = false; return;
emit debugTaskStateChanged(); }
if (currentlyEmulating) {
cmd("aeim-; aei-; wcr; .ar-"); if (!debugTask.isNull()) {
currentlyEmulating = false; suspendDebug();
} else if (currentlyAttachedToPID != -1) { }
cmd(QString("dp- %1; o %2; .ar-").arg(QString::number(currentlyAttachedToPID), currentlyOpenFile));
currentlyAttachedToPID = -1; currentlyDebugging = false;
} else { emit debugTaskStateChanged();
cmd("dk 9; oo; .ar-");
// close ptrace file descriptors left open if (currentlyEmulating) {
QJsonArray openFilesArray = cmdj("oj").array();; cmdEsil("aeim-; aei-; wcr; .ar-");
for (QJsonValue value : openFilesArray) { currentlyEmulating = false;
QJsonObject openFile = value.toObject(); } else if (currentlyAttachedToPID != -1) {
QString URI = openFile["uri"].toString(); cmd(QString("dp- %1; o %2; .ar-").arg(
if (URI.contains("ptrace")) { QString::number(currentlyAttachedToPID), currentlyOpenFile));
cmd("o-" + QString::number(openFile["fd"].toInt())); currentlyAttachedToPID = -1;
} } else {
QString ptraceFiles = "";
// close ptrace file descriptors left open
QJsonArray openFilesArray = cmdj("oj").array();;
for (QJsonValue value : openFilesArray) {
QJsonObject openFile = value.toObject();
QString URI = openFile["uri"].toString();
if (URI.contains("ptrace")) {
ptraceFiles += "o-" + QString::number(openFile["fd"].toInt()) + ";";
} }
} }
seekAndShow(offsetPriorDebugging); cmd("dk 9; oo; .ar-;" + ptraceFiles);
setConfig("asm.flags", true);
setConfig("io.cache", false);
emit flagsChanged();
emit changeDefinedView();
} }
syncAndSeekProgramCounter();
setConfig("asm.flags", true);
setConfig("io.cache", false);
emit flagsChanged();
emit changeDefinedView();
offsetPriorDebugging = getOffset();
emit debugTaskStateChanged();
} }
void CutterCore::syncAndSeekProgramCounter() void CutterCore::syncAndSeekProgramCounter()
@ -1402,137 +1472,183 @@ void CutterCore::syncAndSeekProgramCounter()
void CutterCore::continueDebug() void CutterCore::continueDebug()
{ {
if (currentlyDebugging) { if (!currentlyDebugging) {
if (currentlyEmulating) { return;
asyncCmdEsil("aec", debugTask); }
} else {
asyncCmd("dc", debugTask); if (currentlyEmulating) {
if (!asyncCmdEsil("aec", debugTask)) {
return;
} }
if (!debugTask.isNull()) { } else {
emit debugTaskStateChanged(); if (!asyncCmd("dc", debugTask)) {
connect(debugTask.data(), &R2Task::finished, this, [this] () { return;
debugTask.clear();
syncAndSeekProgramCounter();
emit registersChanged();
emit refreshCodeViews();
emit debugTaskStateChanged();
});
} }
} }
emit debugTaskStateChanged();
connect(debugTask.data(), &R2Task::finished, this, [this] () {
debugTask.clear();
syncAndSeekProgramCounter();
emit registersChanged();
emit refreshCodeViews();
emit debugTaskStateChanged();
});
debugTask->startTask();
} }
void CutterCore::continueUntilDebug(QString offset) void CutterCore::continueUntilDebug(QString offset)
{ {
if (!currentlyDebugging) {
return;
}
if (currentlyDebugging) { if (currentlyEmulating) {
if (currentlyEmulating) { if (!asyncCmdEsil("aecu " + offset, debugTask)) {
asyncCmdEsil("aecu " + offset, debugTask); return;
} else {
asyncCmd("dcu " + offset, debugTask);
} }
if (!debugTask.isNull()) { } else {
emit debugTaskStateChanged(); if (!asyncCmd("dcu " + offset, debugTask)) {
connect(debugTask.data(), &R2Task::finished, this, [this] () { return;
debugTask.clear();
syncAndSeekProgramCounter();
emit registersChanged();
emit stackChanged();
emit refreshCodeViews();
emit debugTaskStateChanged();
});
} }
} }
emit debugTaskStateChanged();
connect(debugTask.data(), &R2Task::finished, this, [this] () {
debugTask.clear();
syncAndSeekProgramCounter();
emit registersChanged();
emit stackChanged();
emit refreshCodeViews();
emit debugTaskStateChanged();
});
debugTask->startTask();
} }
void CutterCore::continueUntilCall() void CutterCore::continueUntilCall()
{ {
if (currentlyDebugging) { if (!currentlyDebugging) {
if (currentlyEmulating) { return;
asyncCmdEsil("aecc", debugTask); }
} else {
asyncCmd("dcc", debugTask); if (currentlyEmulating) {
if (!asyncCmdEsil("aecc", debugTask)) {
return;
} }
if (!debugTask.isNull()) { } else {
emit debugTaskStateChanged(); if (!asyncCmd("dcc", debugTask)) {
connect(debugTask.data(), &R2Task::finished, this, [this] () { return;
debugTask.clear();
syncAndSeekProgramCounter();
emit debugTaskStateChanged();
});
} }
} }
emit debugTaskStateChanged();
connect(debugTask.data(), &R2Task::finished, this, [this] () {
debugTask.clear();
syncAndSeekProgramCounter();
emit debugTaskStateChanged();
});
debugTask->startTask();
} }
void CutterCore::continueUntilSyscall() void CutterCore::continueUntilSyscall()
{ {
if (currentlyDebugging) { if (!currentlyDebugging) {
if (currentlyEmulating) { return;
asyncCmdEsil("aecs", debugTask); }
} else {
asyncCmd("dcs", debugTask); if (currentlyEmulating) {
if (!asyncCmdEsil("aecs", debugTask)) {
return;
} }
if (!debugTask.isNull()) { } else {
emit debugTaskStateChanged(); if (!asyncCmd("dcs", debugTask)) {
connect(debugTask.data(), &R2Task::finished, this, [this] () { return;
debugTask.clear();
syncAndSeekProgramCounter();
emit debugTaskStateChanged();
});
} }
} }
emit debugTaskStateChanged();
connect(debugTask.data(), &R2Task::finished, this, [this] () {
debugTask.clear();
syncAndSeekProgramCounter();
emit debugTaskStateChanged();
});
debugTask->startTask();
} }
void CutterCore::stepDebug() void CutterCore::stepDebug()
{ {
if (currentlyDebugging) { if (!currentlyDebugging) {
if (currentlyEmulating) { return;
asyncCmdEsil("aes", debugTask); }
} else {
asyncCmd("ds", debugTask); if (currentlyEmulating) {
if (!asyncCmdEsil("aes", debugTask)) {
return;
} }
if (!debugTask.isNull()) { } else {
emit debugTaskStateChanged(); if (!asyncCmd("ds", debugTask)) {
connect(debugTask.data(), &R2Task::finished, this, [this] () { return;
debugTask.clear();
syncAndSeekProgramCounter();
emit debugTaskStateChanged();
});
} }
} }
emit debugTaskStateChanged();
connect(debugTask.data(), &R2Task::finished, this, [this] () {
debugTask.clear();
syncAndSeekProgramCounter();
emit debugTaskStateChanged();
});
debugTask->startTask();
} }
void CutterCore::stepOverDebug() void CutterCore::stepOverDebug()
{ {
if (currentlyDebugging) { if (!currentlyDebugging) {
if (currentlyEmulating) { return;
asyncCmdEsil("aeso", debugTask); }
} else {
asyncCmd("dso", debugTask); if (currentlyEmulating) {
if (!asyncCmdEsil("aeso", debugTask)) {
return;
} }
if (!debugTask.isNull()) { } else {
emit debugTaskStateChanged(); if (!asyncCmd("dso", debugTask)) {
connect(debugTask.data(), &R2Task::finished, this, [this] () { return;
debugTask.clear();
syncAndSeekProgramCounter();
emit debugTaskStateChanged();
});
} }
} }
emit debugTaskStateChanged();
connect(debugTask.data(), &R2Task::finished, this, [this] () {
debugTask.clear();
syncAndSeekProgramCounter();
emit debugTaskStateChanged();
});
debugTask->startTask();
} }
void CutterCore::stepOutDebug() void CutterCore::stepOutDebug()
{ {
if (currentlyDebugging) { if (!currentlyDebugging) {
emit debugTaskStateChanged(); return;
asyncCmd("dsf", debugTask);
if (!debugTask.isNull()) {
connect(debugTask.data(), &R2Task::finished, this, [this] () {
debugTask.clear();
syncAndSeekProgramCounter();
emit debugTaskStateChanged();
});
}
} }
emit debugTaskStateChanged();
if (!asyncCmd("dsf", debugTask)) {
return;
}
connect(debugTask.data(), &R2Task::finished, this, [this] () {
debugTask.clear();
syncAndSeekProgramCounter();
emit debugTaskStateChanged();
});
debugTask->startTask();
} }
QStringList CutterCore::getDebugPlugins() QStringList CutterCore::getDebugPlugins()

View File

@ -66,10 +66,11 @@ public:
* @note connect to the &R2Task::finished signal to add your own logic once * @note connect to the &R2Task::finished signal to add your own logic once
* the command is finished. Use task->getResult()/getResultJson() for the * the command is finished. Use task->getResult()/getResultJson() for the
* return value. * return value.
* Once you have setup connections you can start the task with task->startTask()
* If you want to seek to an address, you should use CutterCore::seek. * If you want to seek to an address, you should use CutterCore::seek.
*/ */
void asyncCmd(const char *str, QSharedPointer<R2Task> &task); bool asyncCmd(const char *str, QSharedPointer<R2Task> &task);
void asyncCmd(const QString &str, QSharedPointer<R2Task> &task) { return asyncCmd(str.toUtf8().constData(), task); } bool asyncCmd(const QString &str, QSharedPointer<R2Task> &task) { return asyncCmd(str.toUtf8().constData(), task); }
QString cmdRaw(const QString &str); QString cmdRaw(const QString &str);
QJsonDocument cmdj(const char *str); QJsonDocument cmdj(const char *str);
QJsonDocument cmdj(const QString &str) { return cmdj(str.toUtf8().constData()); } QJsonDocument cmdj(const QString &str) { return cmdj(str.toUtf8().constData()); }
@ -91,10 +92,11 @@ public:
* @note connect to the &R2Task::finished signal to add your own logic once * @note connect to the &R2Task::finished signal to add your own logic once
* the command is finished. Use task->getResult()/getResultJson() for the * the command is finished. Use task->getResult()/getResultJson() for the
* return value. * return value.
* Once you have setup connections you can start the task with task->startTask()
* If you want to seek to an address, you should use CutterCore::seek. * If you want to seek to an address, you should use CutterCore::seek.
*/ */
void asyncCmdEsil(const char *command, QSharedPointer<R2Task> &task); bool asyncCmdEsil(const char *command, QSharedPointer<R2Task> &task);
void asyncCmdEsil(const QString &command, QSharedPointer<R2Task> &task) { return asyncCmdEsil(command.toUtf8().constData(), task); } bool asyncCmdEsil(const QString &command, QSharedPointer<R2Task> &task) { return asyncCmdEsil(command.toUtf8().constData(), task); }
QString getVersionInformation(); QString getVersionInformation();
QJsonDocument parseJson(const char *res, const char *cmd = nullptr); QJsonDocument parseJson(const char *res, const char *cmd = nullptr);

View File

@ -0,0 +1,25 @@
#include "NativeDebugDialog.h"
#include "ui_NativeDebugDialog.h"
#include <QMessageBox>
NativeDebugDialog::NativeDebugDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::NativeDebugDialog)
{
ui->setupUi(this);
setWindowFlags(windowFlags() & (~Qt::WindowContextHelpButtonHint));
}
NativeDebugDialog::~NativeDebugDialog() {}
QString NativeDebugDialog::getArgs() const
{
return ui->argEdit->toPlainText();
}
void NativeDebugDialog::setArgs(const QString &args)
{
ui->argEdit->setText(args);
ui->argEdit->selectAll();
}

View File

@ -0,0 +1,29 @@
#ifndef NATIVEDEBUGDIALOG_H
#define NATIVEDEBUGDIALOG_H
#include <QDialog>
#include <memory>
namespace Ui {
class NativeDebugDialog;
}
/**
* @brief Dialog for connecting to native debug
*/
class NativeDebugDialog : public QDialog
{
Q_OBJECT
public:
explicit NativeDebugDialog(QWidget *parent = nullptr);
~NativeDebugDialog();
QString getArgs() const;
void setArgs(const QString &args);
private:
std::unique_ptr<Ui::NativeDebugDialog> ui;
};
#endif // NATIVE_DEBUG_DIALOG

View File

@ -0,0 +1,100 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>NativeDebugDialog</class>
<widget class="QDialog" name="NativeDebugDialog">
<property name="windowModality">
<enum>Qt::NonModal</enum>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>320</width>
<height>101</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true">Native debugging configuration</string>
</property>
<widget class="QWidget" name="verticalLayoutWidget">
<property name="geometry">
<rect>
<x>10</x>
<y>10</y>
<width>301</width>
<height>81</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="argText">
<property name="text">
<string>Command line arguments:</string>
</property>
</widget>
</item>
<item>
<widget class="QTextEdit" name="argEdit">
<property name="maximumSize">
<size>
<width>382</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string notr="true"/>
</property>
<property name="placeholderText">
<string notr="true"/>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>NativeDebugDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>234</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>NativeDebugDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>240</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>254</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -37,11 +37,6 @@ void DebugOptionsWidget::updateDebugPlugin()
connect(ui->pluginComboBox, SIGNAL(currentIndexChanged(const QString &)), this, connect(ui->pluginComboBox, SIGNAL(currentIndexChanged(const QString &)), this,
SLOT(on_pluginComboBox_currentIndexChanged(const QString &))); SLOT(on_pluginComboBox_currentIndexChanged(const QString &)));
QString debugArgs = Core()->getConfig("dbg.args");
ui->debugArgs->setText(debugArgs);
ui->debugArgs->setPlaceholderText(debugArgs);
connect(ui->debugArgs, &QLineEdit::editingFinished, this, &DebugOptionsWidget::updateDebugArgs);
QString stackSize = Core()->getConfig("esil.stack.size"); QString stackSize = Core()->getConfig("esil.stack.size");
ui->stackSize->setText(stackSize); ui->stackSize->setText(stackSize);
ui->stackSize->setPlaceholderText(stackSize); ui->stackSize->setPlaceholderText(stackSize);
@ -52,13 +47,6 @@ void DebugOptionsWidget::updateDebugPlugin()
connect(ui->stackSize, &QLineEdit::editingFinished, this, &DebugOptionsWidget::updateStackSize); connect(ui->stackSize, &QLineEdit::editingFinished, this, &DebugOptionsWidget::updateStackSize);
} }
void DebugOptionsWidget::updateDebugArgs()
{
QString newArgs = ui->debugArgs->text();
Core()->setConfig("dbg.args", newArgs);
ui->debugArgs->setPlaceholderText(newArgs);
}
void DebugOptionsWidget::on_pluginComboBox_currentIndexChanged(const QString &plugin) void DebugOptionsWidget::on_pluginComboBox_currentIndexChanged(const QString &plugin)
{ {
Core()->setDebugPlugin(plugin); Core()->setDebugPlugin(plugin);

View File

@ -24,7 +24,6 @@ private:
private slots: private slots:
void updateDebugPlugin(); void updateDebugPlugin();
void updateDebugArgs();
void updateStackAddr(); void updateStackAddr();
void updateStackSize(); void updateStackSize();
void on_pluginComboBox_currentIndexChanged(const QString &index); void on_pluginComboBox_currentIndexChanged(const QString &index);

View File

@ -33,30 +33,13 @@
<widget class="QComboBox" name="pluginComboBox"/> <widget class="QComboBox" name="pluginComboBox"/>
</item> </item>
<item row="1" column="0"> <item row="1" column="0">
<widget class="QLabel" name="argsLabel">
<property name="text">
<string>Program Arguments:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="debugArgs">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="esilstackAddr"> <widget class="QLabel" name="esilstackAddr">
<property name="text"> <property name="text">
<string>ESIL stack address:</string> <string>ESIL stack address:</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="1"> <item row="1" column="1">
<widget class="QLineEdit" name="stackAddr"> <widget class="QLineEdit" name="stackAddr">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed"> <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
@ -66,14 +49,14 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="3" column="0"> <item row="2" column="0">
<widget class="QLabel" name="esilStackSize"> <widget class="QLabel" name="esilStackSize">
<property name="text"> <property name="text">
<string>ESIL stack size:</string> <string>ESIL stack size:</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="3" column="1"> <item row="2" column="1">
<widget class="QLineEdit" name="stackSize"> <widget class="QLineEdit" name="stackSize">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed"> <sizepolicy hsizetype="Preferred" vsizetype="Fixed">

View File

@ -1,6 +1,7 @@
#include "DebugActions.h" #include "DebugActions.h"
#include "core/MainWindow.h" #include "core/MainWindow.h"
#include "dialogs/AttachProcDialog.h" #include "dialogs/AttachProcDialog.h"
#include "dialogs/NativeDebugDialog.h"
#include "common/Configuration.h" #include "common/Configuration.h"
#include "common/Helpers.h" #include "common/Helpers.h"
@ -19,24 +20,22 @@ DebugActions::DebugActions(QToolBar *toolBar, MainWindow *main) :
// setIconSize(QSize(16, 16)); // setIconSize(QSize(16, 16));
// define icons // define icons
QIcon startDebugIcon = QIcon(":/img/icons/play_light_debug.svg");
QIcon startEmulIcon = QIcon(":/img/icons/play_light_emul.svg"); QIcon startEmulIcon = QIcon(":/img/icons/play_light_emul.svg");
QIcon startAttachIcon = QIcon(":/img/icons/play_light_attach.svg"); QIcon startAttachIcon = QIcon(":/img/icons/play_light_attach.svg");
QIcon startRemoteIcon = QIcon(":/img/icons/play_light_remote.svg"); QIcon startRemoteIcon = QIcon(":/img/icons/play_light_remote.svg");
stopIcon = QIcon(":/img/icons/media-stop_light.svg"); stopIcon = QIcon(":/img/icons/media-stop_light.svg");
QIcon restartIcon = QIcon(":/img/icons/spin_light.svg"); restartIcon = QIcon(":/img/icons/spin_light.svg");
detachIcon = QIcon(":/img/icons/detach_debugger.svg"); detachIcon = QIcon(":/img/icons/detach_debugger.svg");
startDebugIcon = QIcon(":/img/icons/play_light_debug.svg");
continueIcon = QIcon(":/img/icons/media-skip-forward_light.svg"); continueIcon = QIcon(":/img/icons/media-skip-forward_light.svg");
suspendIcon = QIcon(":/img/icons/media-suspend_light.svg"); suspendIcon = QIcon(":/img/icons/media-suspend_light.svg");
// define action labels // define action labels
QString startDebugLabel = tr("Start debug");
QString startEmulLabel = tr("Start emulation"); QString startEmulLabel = tr("Start emulation");
QString startAttachLabel = tr("Attach to process"); QString startAttachLabel = tr("Attach to process");
QString startRemoteLabel = tr("Connect to a remote debugger"); QString startRemoteLabel = tr("Connect to a remote debugger");
QString stopDebugLabel = tr("Stop debug"); QString stopDebugLabel = tr("Stop debug");
QString stopEmulLabel = tr("Stop emulation"); QString stopEmulLabel = tr("Stop emulation");
QString restartDebugLabel = tr("Restart program");
QString restartEmulLabel = tr("Restart emulation"); QString restartEmulLabel = tr("Restart emulation");
QString continueUMLabel = tr("Continue until main"); QString continueUMLabel = tr("Continue until main");
QString continueUCLabel = tr("Continue until call"); QString continueUCLabel = tr("Continue until call");
@ -44,8 +43,10 @@ DebugActions::DebugActions(QToolBar *toolBar, MainWindow *main) :
QString stepLabel = tr("Step"); QString stepLabel = tr("Step");
QString stepOverLabel = tr("Step over"); QString stepOverLabel = tr("Step over");
QString stepOutLabel = tr("Step out"); QString stepOutLabel = tr("Step out");
suspendLabel = tr("Suspend process"); suspendLabel = tr("Suspend the process");
continueLabel = tr("Continue"); continueLabel = tr("Continue");
restartDebugLabel = tr("Restart program");
startDebugLabel = tr("Start debug");
// define actions // define actions
actionStart = new QAction(startDebugIcon, startDebugLabel, this); actionStart = new QAction(startDebugIcon, startDebugLabel, this);
@ -107,7 +108,7 @@ DebugActions::DebugActions(QToolBar *toolBar, MainWindow *main) :
// necessary to avoid staying stuck // necessary to avoid staying stuck
toggleActions = {actionStepOver, actionStep, actionStepOut, actionContinueUntilMain, toggleActions = {actionStepOver, actionStep, actionStepOut, actionContinueUntilMain,
actionContinueUntilCall, actionContinueUntilSyscall}; actionContinueUntilCall, actionContinueUntilSyscall};
toggleConnectionActions = {actionAttach, actionStart, actionStartRemote, actionStartEmul}; toggleConnectionActions = {actionAttach, actionStartRemote, actionStartEmul};
connect(Core(), &CutterCore::debugTaskStateChanged, this, [ = ]() { connect(Core(), &CutterCore::debugTaskStateChanged, this, [ = ]() {
bool disableToolbar = Core()->isDebugTaskInProgress(); bool disableToolbar = Core()->isDebugTaskInProgress();
@ -146,25 +147,7 @@ DebugActions::DebugActions(QToolBar *toolBar, MainWindow *main) :
setAllActionsVisible(false); setAllActionsVisible(false);
}); });
connect(actionStep, &QAction::triggered, Core(), &CutterCore::stepDebug); connect(actionStep, &QAction::triggered, Core(), &CutterCore::stepDebug);
connect(actionStart, &QAction::triggered, [ = ]() { connect(actionStart, &QAction::triggered, this, &DebugActions::startDebug);
// check if file is executable before starting debug
QString filename = Core()->getConfig("file.path").section(QLatin1Char(' '), 0, 0);
QFileInfo info(filename);
if (!Core()->currentlyDebugging && !info.isExecutable()) {
QMessageBox msgBox;
msgBox.setText(tr("File '%1' does not have executable permissions.").arg(filename));
msgBox.exec();
return;
}
setAllActionsVisible(true);
actionAttach->setVisible(false);
actionStartRemote->setVisible(false);
actionStartEmul->setVisible(false);
actionStart->setText(restartDebugLabel);
actionStart->setIcon(restartIcon);
setButtonVisibleIfMainExists();
Core()->startDebug();
});
connect(actionAttach, &QAction::triggered, this, &DebugActions::attachProcessDialog); connect(actionAttach, &QAction::triggered, this, &DebugActions::attachProcessDialog);
connect(actionStartRemote, &QAction::triggered, this, &DebugActions::attachRemoteDialog); connect(actionStartRemote, &QAction::triggered, this, &DebugActions::attachRemoteDialog);
@ -210,6 +193,18 @@ void DebugActions::setButtonVisibleIfMainExists()
} }
} }
void DebugActions::showDebugWarning()
{
if (!acceptedDebugWarning) {
acceptedDebugWarning = true;
QMessageBox msgBox;
msgBox.setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::LinksAccessibleByMouse);
msgBox.setText(tr("Debug is currently in beta.\n") +
tr("If you encounter any problems or have suggestions, please submit an issue to https://github.com/radareorg/cutter/issues"));
msgBox.exec();
}
}
void DebugActions::continueUntilMain() void DebugActions::continueUntilMain()
{ {
QString mainAddr = Core()->cmd("?v sym.main"); QString mainAddr = Core()->cmd("?v sym.main");
@ -227,7 +222,8 @@ void DebugActions::attachRemoteDebugger()
actionStop->setText(stopAttachLabel); actionStop->setText(stopAttachLabel);
} }
void DebugActions::onAttachedRemoteDebugger(bool successfully) { void DebugActions::onAttachedRemoteDebugger(bool successfully)
{
if (!successfully) { if (!successfully) {
QMessageBox msgBox; QMessageBox msgBox;
msgBox.setText(tr("Error connecting.")); msgBox.setText(tr("Error connecting."));
@ -241,6 +237,8 @@ void DebugActions::onAttachedRemoteDebugger(bool successfully) {
void DebugActions::attachRemoteDialog() void DebugActions::attachRemoteDialog()
{ {
showDebugWarning();
if (!remoteDialog) { if (!remoteDialog) {
remoteDialog = new RemoteDebugDialog(main); remoteDialog = new RemoteDebugDialog(main);
} }
@ -261,6 +259,8 @@ void DebugActions::attachRemoteDialog()
void DebugActions::attachProcessDialog() void DebugActions::attachProcessDialog()
{ {
showDebugWarning();
AttachProcDialog dialog(main); AttachProcDialog dialog(main);
bool success = false; bool success = false;
while (!success) { while (!success) {
@ -293,6 +293,44 @@ void DebugActions::attachProcess(int pid)
Core()->attachDebug(pid); Core()->attachDebug(pid);
} }
void DebugActions::startDebug()
{
// check if file is executable before starting debug
QString filename = Core()->getConfig("file.path").section(QLatin1Char(' '), 0, 0);
QFileInfo info(filename);
if (!Core()->currentlyDebugging && !info.isExecutable()) {
QMessageBox msgBox;
msgBox.setText(tr("File '%1' does not have executable permissions.").arg(filename));
msgBox.exec();
return;
}
showDebugWarning();
NativeDebugDialog dialog(main);
dialog.setArgs(Core()->getConfig("dbg.args"));
QString args;
if (dialog.exec()) {
args = dialog.getArgs();
} else {
return;
}
// Update dbg.args with the new args
Core()->setConfig("dbg.args", args);
setAllActionsVisible(true);
actionAttach->setVisible(false);
actionStartRemote->setVisible(false);
actionStartEmul->setVisible(false);
actionStart->setText(restartDebugLabel);
actionStart->setIcon(restartIcon);
setButtonVisibleIfMainExists();
Core()->startDebug();
}
void DebugActions::setAllActionsVisible(bool visible) void DebugActions::setAllActionsVisible(bool visible)
{ {
for (QAction *action : allActions) { for (QAction *action : allActions) {

View File

@ -32,11 +32,15 @@ public:
QAction *actionStop; QAction *actionStop;
QAction *actionAllContinues; QAction *actionAllContinues;
// Continue and suspend interchange during runtime // Continue/suspend and start/restart interchange during runtime
QIcon continueIcon; QIcon continueIcon;
QIcon suspendIcon; QIcon suspendIcon;
QIcon restartIcon;
QIcon startDebugIcon;
QString suspendLabel; QString suspendLabel;
QString continueLabel; QString continueLabel;
QString restartDebugLabel;
QString startDebugLabel;
// Stop and Detach interchange during runtime // Stop and Detach interchange during runtime
QIcon detachIcon; QIcon detachIcon;
@ -52,9 +56,14 @@ private:
QToolButton *continueUntilButton; QToolButton *continueUntilButton;
RemoteDebugDialog *remoteDialog = nullptr; RemoteDebugDialog *remoteDialog = nullptr;
MainWindow *main; MainWindow *main;
bool acceptedDebugWarning = false;
// TODO: Remove once debug is stable
void showDebugWarning();
private slots: private slots:
void continueUntilMain(); void continueUntilMain();
void startDebug();
void attachProcessDialog(); void attachProcessDialog();
void attachProcess(int pid); void attachProcess(int pid);
void attachRemoteDialog(); void attachRemoteDialog();