diff --git a/src/Cutter.pro b/src/Cutter.pro index 7772f4e5..4e892f03 100644 --- a/src/Cutter.pro +++ b/src/Cutter.pro @@ -268,6 +268,7 @@ SOURCES += \ dialogs/FlagDialog.cpp \ dialogs/RenameDialog.cpp \ dialogs/RemoteDebugDialog.cpp \ + dialogs/NativeDebugDialog.cpp \ dialogs/XrefsDialog.cpp \ core/MainWindow.cpp \ common/Helpers.cpp \ @@ -403,6 +404,7 @@ HEADERS += \ dialogs/FlagDialog.h \ dialogs/RenameDialog.h \ dialogs/RemoteDebugDialog.h \ + dialogs/NativeDebugDialog.h \ dialogs/XrefsDialog.h \ common/Helpers.h \ common/HexAsciiHighlighter.h \ @@ -536,6 +538,7 @@ FORMS += \ dialogs/FlagDialog.ui \ dialogs/RenameDialog.ui \ dialogs/RemoteDebugDialog.ui \ + dialogs/NativeDebugDialog.ui \ dialogs/XrefsDialog.ui \ dialogs/NewfileDialog.ui \ dialogs/InitialOptionsDialog.ui \ diff --git a/src/core/Cutter.cpp b/src/core/Cutter.cpp index f0db880c..049f6359 100644 --- a/src/core/Cutter.cpp +++ b/src/core/Cutter.cpp @@ -340,12 +340,12 @@ bool CutterCore::isDebugTaskInProgress() return false; } -void CutterCore::asyncCmdEsil(const char *command, QSharedPointer &task) +bool CutterCore::asyncCmdEsil(const char *command, QSharedPointer &task) { asyncCmd(command, task); if (task.isNull()) { - return; + return false; } connect(task.data(), &R2Task::finished, this, [this, task] () { @@ -355,12 +355,14 @@ void CutterCore::asyncCmdEsil(const char *command, QSharedPointer &task) 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 &task) +bool CutterCore::asyncCmd(const char *str, QSharedPointer &task) { if (!task.isNull()) { - return; + return false; } CORE_LOCK(); @@ -376,7 +378,7 @@ void CutterCore::asyncCmd(const char *str, QSharedPointer &task) } }); - task->startTask(); + return true; } QString CutterCore::cmdRaw(const QString &str) @@ -1200,42 +1202,43 @@ void CutterCore::setRegister(QString regName, QString regValue) void CutterCore::setCurrentDebugThread(int tid) { - 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) { + if (!asyncCmd("dpt=" + QString::number(tid), debugTask)) { return; } emit debugTaskStateChanged(); - asyncCmd("dp=" + QString::number(pid), debugTask); - if (!debugTask.isNull()) { + connect(debugTask.data(), &R2Task::finished, this, [this] () { + debugTask.clear(); + emit registersChanged(); + emit refreshCodeViews(); + emit stackChanged(); + syncAndSeekProgramCounter(); + emit switchedThread(); 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::setCurrentDebugProcess(int pid) +{ + if (!currentlyDebugging || !asyncCmd("dp=" + QString::number(pid), debugTask)) { + 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() @@ -1244,17 +1247,38 @@ void CutterCore::startDebug() offsetPriorDebugging = getOffset(); } currentlyOpenFile = getConfig("file.path"); - cmd("ood"); - emit registersChanged(); - if (!currentlyDebugging) { - setConfig("asm.flags", false); - currentlyDebugging = true; - emit changeDebugView(); - emit flagsChanged(); - emit refreshCodeViews(); + + if (!asyncCmd("ood", debugTask)) { + return; } - emit stackChanged(); + 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() @@ -1262,22 +1286,41 @@ void CutterCore::startEmulation() if (!currentlyDebugging) { offsetPriorDebugging = getOffset(); } + // clear registers, init esil state, stack, progcounter at current seek - cmd("aei; aeim; aeip"); - 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(); + asyncCmd("aei; aeim; aeip", debugTask); + 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) @@ -1329,8 +1372,10 @@ void CutterCore::attachRemote(const QString &uri) debugTaskDialog = new R2TaskDialog(debugTask); debugTaskDialog->setBreakOnClose(true); debugTaskDialog->setAttribute(Qt::WA_DeleteOnClose); - debugTaskDialog->setDesc("Connecting to: " + uri); + debugTaskDialog->setDesc(tr("Connecting to: ") + uri); debugTaskDialog->show(); + + debugTask->startTask(); } void CutterCore::attachDebug(int pid) @@ -1340,21 +1385,34 @@ void CutterCore::attachDebug(int pid) } // attach to process with dbg plugin - cmd("o-*; e cfg.debug = true; o+ dbg://" + QString::number(pid)); - 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(); - } - + asyncCmd("o-*; e cfg.debug = true; o+ dbg://" + QString::number(pid), debugTask); 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() @@ -1364,33 +1422,45 @@ void CutterCore::suspendDebug() void CutterCore::stopDebug() { - if (currentlyDebugging) { - currentlyDebugging = false; - emit debugTaskStateChanged(); - if (currentlyEmulating) { - cmd("aeim-; aei-; wcr; .ar-"); - currentlyEmulating = false; - } else if (currentlyAttachedToPID != -1) { - cmd(QString("dp- %1; o %2; .ar-").arg(QString::number(currentlyAttachedToPID), currentlyOpenFile)); - currentlyAttachedToPID = -1; - } else { - cmd("dk 9; oo; .ar-"); - // 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")) { - cmd("o-" + QString::number(openFile["fd"].toInt())); - } + if (!currentlyDebugging) { + return; + } + + if (!debugTask.isNull()) { + suspendDebug(); + } + + currentlyDebugging = false; + emit debugTaskStateChanged(); + + if (currentlyEmulating) { + cmdEsil("aeim-; aei-; wcr; .ar-"); + currentlyEmulating = false; + } else if (currentlyAttachedToPID != -1) { + cmd(QString("dp- %1; o %2; .ar-").arg( + QString::number(currentlyAttachedToPID), currentlyOpenFile)); + 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); - setConfig("asm.flags", true); - setConfig("io.cache", false); - emit flagsChanged(); - emit changeDefinedView(); + cmd("dk 9; oo; .ar-;" + ptraceFiles); } + + syncAndSeekProgramCounter(); + setConfig("asm.flags", true); + setConfig("io.cache", false); + emit flagsChanged(); + emit changeDefinedView(); + offsetPriorDebugging = getOffset(); + emit debugTaskStateChanged(); } void CutterCore::syncAndSeekProgramCounter() @@ -1402,137 +1472,183 @@ void CutterCore::syncAndSeekProgramCounter() void CutterCore::continueDebug() { - if (currentlyDebugging) { - if (currentlyEmulating) { - asyncCmdEsil("aec", debugTask); - } else { - asyncCmd("dc", debugTask); + if (!currentlyDebugging) { + return; + } + + if (currentlyEmulating) { + if (!asyncCmdEsil("aec", debugTask)) { + return; } - if (!debugTask.isNull()) { - emit debugTaskStateChanged(); - connect(debugTask.data(), &R2Task::finished, this, [this] () { - debugTask.clear(); - syncAndSeekProgramCounter(); - emit registersChanged(); - emit refreshCodeViews(); - emit debugTaskStateChanged(); - }); + } else { + if (!asyncCmd("dc", debugTask)) { + return; } } + + 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) { + if (!currentlyDebugging) { + return; + } - if (currentlyDebugging) { - if (currentlyEmulating) { - asyncCmdEsil("aecu " + offset, debugTask); - } else { - asyncCmd("dcu " + offset, debugTask); + if (currentlyEmulating) { + if (!asyncCmdEsil("aecu " + offset, debugTask)) { + return; } - if (!debugTask.isNull()) { - emit debugTaskStateChanged(); - connect(debugTask.data(), &R2Task::finished, this, [this] () { - debugTask.clear(); - syncAndSeekProgramCounter(); - emit registersChanged(); - emit stackChanged(); - emit refreshCodeViews(); - emit debugTaskStateChanged(); - }); + } else { + if (!asyncCmd("dcu " + offset, debugTask)) { + return; } } + + 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() { - if (currentlyDebugging) { - if (currentlyEmulating) { - asyncCmdEsil("aecc", debugTask); - } else { - asyncCmd("dcc", debugTask); + if (!currentlyDebugging) { + return; + } + + if (currentlyEmulating) { + if (!asyncCmdEsil("aecc", debugTask)) { + return; } - if (!debugTask.isNull()) { - emit debugTaskStateChanged(); - connect(debugTask.data(), &R2Task::finished, this, [this] () { - debugTask.clear(); - syncAndSeekProgramCounter(); - emit debugTaskStateChanged(); - }); + } else { + if (!asyncCmd("dcc", debugTask)) { + return; } } + + emit debugTaskStateChanged(); + connect(debugTask.data(), &R2Task::finished, this, [this] () { + debugTask.clear(); + syncAndSeekProgramCounter(); + emit debugTaskStateChanged(); + }); + + debugTask->startTask(); } void CutterCore::continueUntilSyscall() { - if (currentlyDebugging) { - if (currentlyEmulating) { - asyncCmdEsil("aecs", debugTask); - } else { - asyncCmd("dcs", debugTask); + if (!currentlyDebugging) { + return; + } + + if (currentlyEmulating) { + if (!asyncCmdEsil("aecs", debugTask)) { + return; } - if (!debugTask.isNull()) { - emit debugTaskStateChanged(); - connect(debugTask.data(), &R2Task::finished, this, [this] () { - debugTask.clear(); - syncAndSeekProgramCounter(); - emit debugTaskStateChanged(); - }); + } else { + if (!asyncCmd("dcs", debugTask)) { + return; } } + + emit debugTaskStateChanged(); + connect(debugTask.data(), &R2Task::finished, this, [this] () { + debugTask.clear(); + syncAndSeekProgramCounter(); + emit debugTaskStateChanged(); + }); + + debugTask->startTask(); } void CutterCore::stepDebug() { - if (currentlyDebugging) { - if (currentlyEmulating) { - asyncCmdEsil("aes", debugTask); - } else { - asyncCmd("ds", debugTask); + if (!currentlyDebugging) { + return; + } + + if (currentlyEmulating) { + if (!asyncCmdEsil("aes", debugTask)) { + return; } - if (!debugTask.isNull()) { - emit debugTaskStateChanged(); - connect(debugTask.data(), &R2Task::finished, this, [this] () { - debugTask.clear(); - syncAndSeekProgramCounter(); - emit debugTaskStateChanged(); - }); + } else { + if (!asyncCmd("ds", debugTask)) { + return; } } + + emit debugTaskStateChanged(); + connect(debugTask.data(), &R2Task::finished, this, [this] () { + debugTask.clear(); + syncAndSeekProgramCounter(); + emit debugTaskStateChanged(); + }); + + debugTask->startTask(); } void CutterCore::stepOverDebug() { - if (currentlyDebugging) { - if (currentlyEmulating) { - asyncCmdEsil("aeso", debugTask); - } else { - asyncCmd("dso", debugTask); + if (!currentlyDebugging) { + return; + } + + if (currentlyEmulating) { + if (!asyncCmdEsil("aeso", debugTask)) { + return; } - if (!debugTask.isNull()) { - emit debugTaskStateChanged(); - connect(debugTask.data(), &R2Task::finished, this, [this] () { - debugTask.clear(); - syncAndSeekProgramCounter(); - emit debugTaskStateChanged(); - }); + } else { + if (!asyncCmd("dso", debugTask)) { + return; } } + + emit debugTaskStateChanged(); + connect(debugTask.data(), &R2Task::finished, this, [this] () { + debugTask.clear(); + syncAndSeekProgramCounter(); + emit debugTaskStateChanged(); + }); + + debugTask->startTask(); } void CutterCore::stepOutDebug() { - if (currentlyDebugging) { - emit debugTaskStateChanged(); - asyncCmd("dsf", debugTask); - if (!debugTask.isNull()) { - connect(debugTask.data(), &R2Task::finished, this, [this] () { - debugTask.clear(); - syncAndSeekProgramCounter(); - emit debugTaskStateChanged(); - }); - } + if (!currentlyDebugging) { + return; } + + emit debugTaskStateChanged(); + if (!asyncCmd("dsf", debugTask)) { + return; + } + + connect(debugTask.data(), &R2Task::finished, this, [this] () { + debugTask.clear(); + syncAndSeekProgramCounter(); + emit debugTaskStateChanged(); + }); + + debugTask->startTask(); } QStringList CutterCore::getDebugPlugins() diff --git a/src/core/Cutter.h b/src/core/Cutter.h index b0168919..de5a745f 100644 --- a/src/core/Cutter.h +++ b/src/core/Cutter.h @@ -66,10 +66,11 @@ public: * @note connect to the &R2Task::finished signal to add your own logic once * the command is finished. Use task->getResult()/getResultJson() for the * 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. */ - void asyncCmd(const char *str, QSharedPointer &task); - void asyncCmd(const QString &str, QSharedPointer &task) { return asyncCmd(str.toUtf8().constData(), task); } + bool asyncCmd(const char *str, QSharedPointer &task); + bool asyncCmd(const QString &str, QSharedPointer &task) { return asyncCmd(str.toUtf8().constData(), task); } QString cmdRaw(const QString &str); QJsonDocument cmdj(const char *str); 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 * the command is finished. Use task->getResult()/getResultJson() for the * 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. */ - void asyncCmdEsil(const char *command, QSharedPointer &task); - void asyncCmdEsil(const QString &command, QSharedPointer &task) { return asyncCmdEsil(command.toUtf8().constData(), task); } + bool asyncCmdEsil(const char *command, QSharedPointer &task); + bool asyncCmdEsil(const QString &command, QSharedPointer &task) { return asyncCmdEsil(command.toUtf8().constData(), task); } QString getVersionInformation(); QJsonDocument parseJson(const char *res, const char *cmd = nullptr); diff --git a/src/dialogs/NativeDebugDialog.cpp b/src/dialogs/NativeDebugDialog.cpp new file mode 100644 index 00000000..2dea139a --- /dev/null +++ b/src/dialogs/NativeDebugDialog.cpp @@ -0,0 +1,25 @@ +#include "NativeDebugDialog.h" +#include "ui_NativeDebugDialog.h" + +#include + +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(); +} diff --git a/src/dialogs/NativeDebugDialog.h b/src/dialogs/NativeDebugDialog.h new file mode 100644 index 00000000..2fb0dd4c --- /dev/null +++ b/src/dialogs/NativeDebugDialog.h @@ -0,0 +1,29 @@ +#ifndef NATIVEDEBUGDIALOG_H +#define NATIVEDEBUGDIALOG_H + +#include +#include + +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; +}; + +#endif // NATIVE_DEBUG_DIALOG diff --git a/src/dialogs/NativeDebugDialog.ui b/src/dialogs/NativeDebugDialog.ui new file mode 100644 index 00000000..de06833b --- /dev/null +++ b/src/dialogs/NativeDebugDialog.ui @@ -0,0 +1,100 @@ + + + NativeDebugDialog + + + Qt::NonModal + + + + 0 + 0 + 320 + 101 + + + + Native debugging configuration + + + + + 10 + 10 + 301 + 81 + + + + + + + Command line arguments: + + + + + + + + 382 + 16777215 + + + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + + buttonBox + accepted() + NativeDebugDialog + accept() + + + 248 + 234 + + + 157 + 274 + + + + + buttonBox + rejected() + NativeDebugDialog + reject() + + + 316 + 240 + + + 286 + 254 + + + + + diff --git a/src/dialogs/preferences/DebugOptionsWidget.cpp b/src/dialogs/preferences/DebugOptionsWidget.cpp index 39935141..2cf04e10 100644 --- a/src/dialogs/preferences/DebugOptionsWidget.cpp +++ b/src/dialogs/preferences/DebugOptionsWidget.cpp @@ -37,11 +37,6 @@ void DebugOptionsWidget::updateDebugPlugin() connect(ui->pluginComboBox, SIGNAL(currentIndexChanged(const QString &)), this, 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"); ui->stackSize->setText(stackSize); ui->stackSize->setPlaceholderText(stackSize); @@ -52,13 +47,6 @@ void DebugOptionsWidget::updateDebugPlugin() 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) { Core()->setDebugPlugin(plugin); diff --git a/src/dialogs/preferences/DebugOptionsWidget.h b/src/dialogs/preferences/DebugOptionsWidget.h index 54a163e1..367c4e9b 100644 --- a/src/dialogs/preferences/DebugOptionsWidget.h +++ b/src/dialogs/preferences/DebugOptionsWidget.h @@ -24,7 +24,6 @@ private: private slots: void updateDebugPlugin(); - void updateDebugArgs(); void updateStackAddr(); void updateStackSize(); void on_pluginComboBox_currentIndexChanged(const QString &index); diff --git a/src/dialogs/preferences/DebugOptionsWidget.ui b/src/dialogs/preferences/DebugOptionsWidget.ui index 71c3713d..8d1b9e74 100644 --- a/src/dialogs/preferences/DebugOptionsWidget.ui +++ b/src/dialogs/preferences/DebugOptionsWidget.ui @@ -33,30 +33,13 @@ - - - Program Arguments: - - - - - - - - 0 - 0 - - - - - ESIL stack address: - + @@ -66,14 +49,14 @@ - + ESIL stack size: - + diff --git a/src/widgets/DebugActions.cpp b/src/widgets/DebugActions.cpp index 6ca5009f..0911d81f 100644 --- a/src/widgets/DebugActions.cpp +++ b/src/widgets/DebugActions.cpp @@ -1,6 +1,7 @@ #include "DebugActions.h" #include "core/MainWindow.h" #include "dialogs/AttachProcDialog.h" +#include "dialogs/NativeDebugDialog.h" #include "common/Configuration.h" #include "common/Helpers.h" @@ -19,24 +20,22 @@ DebugActions::DebugActions(QToolBar *toolBar, MainWindow *main) : // setIconSize(QSize(16, 16)); // define icons - QIcon startDebugIcon = QIcon(":/img/icons/play_light_debug.svg"); QIcon startEmulIcon = QIcon(":/img/icons/play_light_emul.svg"); QIcon startAttachIcon = QIcon(":/img/icons/play_light_attach.svg"); QIcon startRemoteIcon = QIcon(":/img/icons/play_light_remote.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"); + startDebugIcon = QIcon(":/img/icons/play_light_debug.svg"); continueIcon = QIcon(":/img/icons/media-skip-forward_light.svg"); suspendIcon = QIcon(":/img/icons/media-suspend_light.svg"); // define action labels - QString startDebugLabel = tr("Start debug"); QString startEmulLabel = tr("Start emulation"); QString startAttachLabel = tr("Attach to process"); QString startRemoteLabel = tr("Connect to a remote debugger"); QString stopDebugLabel = tr("Stop debug"); QString stopEmulLabel = tr("Stop emulation"); - QString restartDebugLabel = tr("Restart program"); QString restartEmulLabel = tr("Restart emulation"); QString continueUMLabel = tr("Continue until main"); QString continueUCLabel = tr("Continue until call"); @@ -44,8 +43,10 @@ DebugActions::DebugActions(QToolBar *toolBar, MainWindow *main) : QString stepLabel = tr("Step"); QString stepOverLabel = tr("Step over"); QString stepOutLabel = tr("Step out"); - suspendLabel = tr("Suspend process"); + suspendLabel = tr("Suspend the process"); continueLabel = tr("Continue"); + restartDebugLabel = tr("Restart program"); + startDebugLabel = tr("Start debug"); // define actions actionStart = new QAction(startDebugIcon, startDebugLabel, this); @@ -107,7 +108,7 @@ DebugActions::DebugActions(QToolBar *toolBar, MainWindow *main) : // necessary to avoid staying stuck toggleActions = {actionStepOver, actionStep, actionStepOut, actionContinueUntilMain, actionContinueUntilCall, actionContinueUntilSyscall}; - toggleConnectionActions = {actionAttach, actionStart, actionStartRemote, actionStartEmul}; + toggleConnectionActions = {actionAttach, actionStartRemote, actionStartEmul}; connect(Core(), &CutterCore::debugTaskStateChanged, this, [ = ]() { bool disableToolbar = Core()->isDebugTaskInProgress(); @@ -146,25 +147,7 @@ DebugActions::DebugActions(QToolBar *toolBar, MainWindow *main) : setAllActionsVisible(false); }); connect(actionStep, &QAction::triggered, Core(), &CutterCore::stepDebug); - connect(actionStart, &QAction::triggered, [ = ]() { - // 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(actionStart, &QAction::triggered, this, &DebugActions::startDebug); connect(actionAttach, &QAction::triggered, this, &DebugActions::attachProcessDialog); 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() { QString mainAddr = Core()->cmd("?v sym.main"); @@ -227,7 +222,8 @@ void DebugActions::attachRemoteDebugger() actionStop->setText(stopAttachLabel); } -void DebugActions::onAttachedRemoteDebugger(bool successfully) { +void DebugActions::onAttachedRemoteDebugger(bool successfully) +{ if (!successfully) { QMessageBox msgBox; msgBox.setText(tr("Error connecting.")); @@ -241,6 +237,8 @@ void DebugActions::onAttachedRemoteDebugger(bool successfully) { void DebugActions::attachRemoteDialog() { + showDebugWarning(); + if (!remoteDialog) { remoteDialog = new RemoteDebugDialog(main); } @@ -261,6 +259,8 @@ void DebugActions::attachRemoteDialog() void DebugActions::attachProcessDialog() { + showDebugWarning(); + AttachProcDialog dialog(main); bool success = false; while (!success) { @@ -293,6 +293,44 @@ void DebugActions::attachProcess(int 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) { for (QAction *action : allActions) { @@ -320,4 +358,4 @@ void DebugActions::chooseThemeIcons() qhelpers::setThemeIcons(kSupportedIconsNames, [](void *obj, const QIcon &icon) { static_cast(obj)->setIcon(icon); }); -} \ No newline at end of file +} diff --git a/src/widgets/DebugActions.h b/src/widgets/DebugActions.h index ff03b37e..c786b073 100644 --- a/src/widgets/DebugActions.h +++ b/src/widgets/DebugActions.h @@ -32,11 +32,15 @@ public: QAction *actionStop; QAction *actionAllContinues; - // Continue and suspend interchange during runtime + // Continue/suspend and start/restart interchange during runtime QIcon continueIcon; QIcon suspendIcon; + QIcon restartIcon; + QIcon startDebugIcon; QString suspendLabel; QString continueLabel; + QString restartDebugLabel; + QString startDebugLabel; // Stop and Detach interchange during runtime QIcon detachIcon; @@ -52,9 +56,14 @@ private: QToolButton *continueUntilButton; RemoteDebugDialog *remoteDialog = nullptr; MainWindow *main; + bool acceptedDebugWarning = false; + + // TODO: Remove once debug is stable + void showDebugWarning(); private slots: void continueUntilMain(); + void startDebug(); void attachProcessDialog(); void attachProcess(int pid); void attachRemoteDialog();