Debugger: Implement function/instruction nop restore

This commit is contained in:
Ty Lamontagne 2023-10-09 21:02:07 -04:00 committed by Connor McLaughlin
parent 4269f16bcd
commit 03a7c745c9
2 changed files with 121 additions and 49 deletions

View File

@ -37,56 +37,11 @@ DisassemblyWidget::DisassemblyWidget(QWidget* parent)
{
ui.setupUi(this);
CreateCustomContextMenu();
connect(this, &DisassemblyWidget::customContextMenuRequested, this, &DisassemblyWidget::customMenuRequested);
}
DisassemblyWidget::~DisassemblyWidget() = default;
void DisassemblyWidget::CreateCustomContextMenu()
{
if (m_contextMenu)
return; // ???
m_contextMenu = new QMenu(this);
QAction* action = 0;
m_contextMenu->addAction(action = new QAction(tr("Copy Address"), this));
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextCopyAddress);
m_contextMenu->addAction(action = new QAction(tr("Copy Instruction Hex"), this));
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextCopyInstructionHex);
m_contextMenu->addAction(action = new QAction(tr("Copy Instruction Text"), this));
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextCopyInstructionText);
// TODO: Disassemble to file. Do people use that?
m_contextMenu->addSeparator();
m_contextMenu->addAction(action = new QAction(tr("Assemble new Instruction(s)"), this));
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextAssembleInstruction);
m_contextMenu->addAction(action = new QAction(tr("NOP Instruction(s)"), this));
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextNoopInstruction);
m_contextMenu->addSeparator();
m_contextMenu->addAction(action = new QAction(tr("Run to Cursor"), this));
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextRunToCursor);
m_contextMenu->addAction(action = new QAction(tr("Jump to Cursor"), this));
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextJumpToCursor);
m_contextMenu->addAction(action = new QAction(tr("Toggle Breakpoint"), this));
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextToggleBreakpoint);
m_contextMenu->addAction(action = new QAction(tr("Follow Branch"), this));
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextFollowBranch);
m_contextMenu->addSeparator();
m_contextMenu->addAction(action = new QAction(tr("Go to Address"), this));
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextGoToAddress);
m_contextMenu->addAction(action = new QAction(tr("Go to in Memory View"), this));
connect(action, &QAction::triggered, this, [this]() { gotoInMemory(m_selectedAddressStart); });
m_contextMenu->addSeparator();
m_contextMenu->addAction(action = new QAction(tr("Add Function"), this));
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextAddFunction);
m_contextMenu->addAction(action = new QAction(tr("Rename Function"), this));
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextRenameFunction);
m_contextMenu->addAction(action = new QAction(tr("Remove Function"), this));
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextRemoveFunction);
m_contextMenu->addAction(action = new QAction(tr("Stub (NOP) Function"), this));
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextStubFunction);
}
void DisassemblyWidget::contextCopyAddress()
{
QGuiApplication::clipboard()->setText(FetchSelectionInfo(SelectionInfo::ADDRESS));
@ -133,6 +88,7 @@ void DisassemblyWidget::contextAssembleInstruction()
Host::RunOnCPUThread([this, start = m_selectedAddressStart, end = m_selectedAddressEnd, cpu = m_cpu, val = encodedInstruction] {
for (u32 i = start; i <= end; i += 4)
{
this->m_nopedInstructions.insert({i, cpu->read32(i)});
cpu->write32(i, val);
}
QtHost::RunOnUIThread([this] { VMUpdate(); });
@ -145,12 +101,28 @@ void DisassemblyWidget::contextNoopInstruction()
Host::RunOnCPUThread([this, start = m_selectedAddressStart, end = m_selectedAddressEnd, cpu = m_cpu] {
for (u32 i = start; i <= end; i += 4)
{
this->m_nopedInstructions.insert({i, cpu->read32(i)});
cpu->write32(i, 0x00);
}
QtHost::RunOnUIThread([this] { VMUpdate(); });
});
}
void DisassemblyWidget::contextRestoreInstruction()
{
Host::RunOnCPUThread([this, start = m_selectedAddressStart, end = m_selectedAddressEnd, cpu = m_cpu] {
for (u32 i = start; i <= end; i += 4)
{
if (this->m_nopedInstructions.find(i) != this->m_nopedInstructions.end())
{
cpu->write32(i, this->m_nopedInstructions[i]);
this->m_nopedInstructions.erase(i);
}
}
QtHost::RunOnUIThread([this] { VMUpdate(); });
});
}
void DisassemblyWidget::contextRunToCursor()
{
Host::RunOnCPUThread([&] {
@ -317,6 +289,7 @@ void DisassemblyWidget::contextStubFunction()
if (curFuncAddress != SymbolMap::INVALID_ADDRESS)
{
Host::RunOnCPUThread([this, curFuncAddress, cpu = m_cpu] {
this->m_stubbedFunctions.insert({curFuncAddress, {cpu->read32(curFuncAddress), cpu->read32(curFuncAddress + 4)}});
cpu->write32(curFuncAddress, 0x03E00008); // jr $ra
cpu->write32(curFuncAddress + 4, 0x00000000); // nop
QtHost::RunOnUIThread([this] { VMUpdate(); });
@ -328,6 +301,23 @@ void DisassemblyWidget::contextStubFunction()
}
}
void DisassemblyWidget::contextRestoreFunction()
{
const u32 curFuncAddress = m_cpu->GetSymbolMap().GetFunctionStart(m_selectedAddressStart);
if (curFuncAddress != SymbolMap::INVALID_ADDRESS && m_stubbedFunctions.find(curFuncAddress) != m_stubbedFunctions.end())
{
Host::RunOnCPUThread([this, curFuncAddress, cpu = m_cpu] {
cpu->write32(curFuncAddress, std::get<0>(this->m_stubbedFunctions[curFuncAddress]));
cpu->write32(curFuncAddress + 4, std::get<1>(this->m_stubbedFunctions[curFuncAddress]));
this->m_stubbedFunctions.erase(curFuncAddress);
QtHost::RunOnUIThread([this] { VMUpdate(); });
});
}
else
{
QMessageBox::warning(this, tr("Restore Function Error"), tr("No function / symbol is currently selected."));
}
}
void DisassemblyWidget::SetCpu(DebugInterface* cpu)
{
m_cpu = cpu;
@ -639,7 +629,58 @@ void DisassemblyWidget::customMenuRequested(QPoint pos)
if (!m_cpu->isAlive())
return;
m_contextMenu->popup(this->mapToGlobal(pos));
QMenu* contextMenu = new QMenu(this);
QAction* action = 0;
contextMenu->addAction(action = new QAction(tr("Copy Address"), this));
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextCopyAddress);
contextMenu->addAction(action = new QAction(tr("Copy Instruction Hex"), this));
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextCopyInstructionHex);
contextMenu->addAction(action = new QAction(tr("Copy Instruction Text"), this));
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextCopyInstructionText);
contextMenu->addSeparator();
if (AddressCanRestore(m_selectedAddressStart, m_selectedAddressEnd))
{
contextMenu->addAction(action = new QAction(tr("Restore Instruction(s)"), this));
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextRestoreInstruction);
}
contextMenu->addAction(action = new QAction(tr("Assemble new Instruction(s)"), this));
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextAssembleInstruction);
contextMenu->addAction(action = new QAction(tr("NOP Instruction(s)"), this));
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextNoopInstruction);
contextMenu->addSeparator();
contextMenu->addAction(action = new QAction(tr("Run to Cursor"), this));
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextRunToCursor);
contextMenu->addAction(action = new QAction(tr("Jump to Cursor"), this));
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextJumpToCursor);
contextMenu->addAction(action = new QAction(tr("Toggle Breakpoint"), this));
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextToggleBreakpoint);
contextMenu->addAction(action = new QAction(tr("Follow Branch"), this));
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextFollowBranch);
contextMenu->addSeparator();
contextMenu->addAction(action = new QAction(tr("Go to Address"), this));
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextGoToAddress);
contextMenu->addAction(action = new QAction(tr("Go to in Memory View"), this));
connect(action, &QAction::triggered, this, [this]() { gotoInMemory(m_selectedAddressStart); });
contextMenu->addSeparator();
contextMenu->addAction(action = new QAction(tr("Add Function"), this));
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextAddFunction);
contextMenu->addAction(action = new QAction(tr("Rename Function"), this));
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextRenameFunction);
contextMenu->addAction(action = new QAction(tr("Remove Function"), this));
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextRemoveFunction);
if (FunctionCanRestore(m_selectedAddressStart))
{
contextMenu->addAction(action = new QAction(tr("Restore Function"), this));
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextRestoreFunction);
}
else
{
contextMenu->addAction(action = new QAction(tr("Stub (NOP) Function"), this));
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextStubFunction);
}
contextMenu->setAttribute(Qt::WA_DeleteOnClose);
contextMenu->popup(this->mapToGlobal(pos));
}
inline QString DisassemblyWidget::DisassemblyStringFromAddress(u32 address, QFont font, u32 pc)
@ -756,3 +797,29 @@ void DisassemblyWidget::gotoAddress(u32 address)
this->repaint();
this->setFocus();
}
bool DisassemblyWidget::AddressCanRestore(u32 start, u32 end)
{
for (u32 i = start; i <= end; i += 4)
{
if (this->m_nopedInstructions.find(i) != this->m_nopedInstructions.end())
{
return true;
}
}
return false;
}
bool DisassemblyWidget::FunctionCanRestore(u32 address)
{
u32 funcStartAddress = m_cpu->GetSymbolMap().GetFunctionStart(address);
if (funcStartAddress != SymbolMap::INVALID_ADDRESS)
{
if (m_stubbedFunctions.find(funcStartAddress) != this->m_stubbedFunctions.end())
{
return true;
}
}
return false;
}

View File

@ -56,6 +56,7 @@ public slots:
void contextCopyInstructionText();
void contextAssembleInstruction();
void contextNoopInstruction();
void contextRestoreInstruction();
void contextRunToCursor();
void contextJumpToCursor();
void contextToggleBreakpoint();
@ -65,6 +66,7 @@ public slots:
void contextRenameFunction();
void contextRemoveFunction();
void contextStubFunction();
void contextRestoreFunction();
void gotoAddress(u32 address);
@ -76,9 +78,6 @@ signals:
private:
Ui::DisassemblyWidget ui;
QMenu* m_contextMenu = nullptr;
void CreateCustomContextMenu();
DebugInterface* m_cpu;
u32 m_visibleStart = 0x00336318; // The address of the first opcode shown(row 0)
u32 m_visibleRows;
@ -86,6 +85,9 @@ private:
u32 m_selectedAddressEnd = 0;
u32 m_rowHeight = 0;
std::map<u32, u32> m_nopedInstructions;
std::map<u32, std::tuple<u32, u32>> m_stubbedFunctions;
DisassemblyManager m_disassemblyManager;
inline QString DisassemblyStringFromAddress(u32 address, QFont font, u32 pc);
@ -97,4 +99,7 @@ private:
INSTRUCTIONTEXT,
};
QString FetchSelectionInfo(SelectionInfo selInfo);
bool AddressCanRestore(u32 start, u32 end);
bool FunctionCanRestore(u32 address);
};