mirror of https://github.com/PCSX2/pcsx2.git
Debugger: Replace SymbolMap class with new SymbolGuardian class
This new class uses the CCC library I added in the last commit and parses the symbol tables on a worker thread.
This commit is contained in:
parent
87b03fdc28
commit
44b50bee26
|
@ -12,7 +12,6 @@
|
|||
|
||||
#include "DebugTools/DebugInterface.h"
|
||||
#include "DebugTools/Breakpoints.h"
|
||||
#include "DebugTools/BiosDebugData.h"
|
||||
#include "DebugTools/MipsStackWalk.h"
|
||||
|
||||
#include "QtUtils.h"
|
||||
|
@ -100,27 +99,10 @@ CpuWidget::CpuWidget(QWidget* parent, DebugInterface& cpu)
|
|||
i++;
|
||||
}
|
||||
|
||||
connect(m_ui.tabWidgetRegFunc, &QTabWidget::currentChanged, [this](int i) {if(i == 1){updateFunctionList(true);} });
|
||||
connect(m_ui.listFunctions, &QListWidget::customContextMenuRequested, this, &CpuWidget::onFuncListContextMenu);
|
||||
connect(m_ui.listFunctions, &QListWidget::itemDoubleClicked, this, &CpuWidget::onFuncListDoubleClick);
|
||||
connect(m_ui.treeModules, &QTreeWidget::customContextMenuRequested, this, &CpuWidget::onModuleTreeContextMenu);
|
||||
connect(m_ui.treeModules, &QTreeWidget::itemDoubleClicked, this, &CpuWidget::onModuleTreeDoubleClick);
|
||||
connect(m_ui.btnRefreshFunctions, &QPushButton::clicked, [this] { updateFunctionList(); });
|
||||
connect(m_ui.txtFuncSearch, &QLineEdit::textChanged, [this] { updateFunctionList(); });
|
||||
|
||||
m_ui.disassemblyWidget->SetCpu(&cpu);
|
||||
m_ui.registerWidget->SetCpu(&cpu);
|
||||
m_ui.memoryviewWidget->SetCpu(&cpu);
|
||||
|
||||
if (cpu.getCpuType() == BREAKPOINT_EE)
|
||||
{
|
||||
m_ui.treeModules->setVisible(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ui.treeModules->header()->setSectionResizeMode(0, QHeaderView::ResizeMode::ResizeToContents);
|
||||
m_ui.listFunctions->setVisible(false);
|
||||
}
|
||||
this->repaint();
|
||||
|
||||
m_ui.savedAddressesList->setModel(&m_savedAddressesModel);
|
||||
|
@ -139,9 +121,11 @@ CpuWidget::CpuWidget(QWidget* parent, DebugInterface& cpu)
|
|||
DebuggerSettingsManager::loadGameSettings(&m_savedAddressesModel);
|
||||
|
||||
connect(m_ui.memorySearchWidget, &MemorySearchWidget::addAddressToSavedAddressesList, this, &CpuWidget::addAddressToSavedAddressesList);
|
||||
connect(m_ui.memorySearchWidget, &MemorySearchWidget::goToAddressInDisassemblyView, [this](u32 address) { m_ui.disassemblyWidget->gotoAddress(address); });
|
||||
connect(m_ui.memorySearchWidget, &MemorySearchWidget::goToAddressInDisassemblyView,
|
||||
[this](u32 address) { m_ui.disassemblyWidget->gotoAddress(address, true); });
|
||||
connect(m_ui.memorySearchWidget, &MemorySearchWidget::goToAddressInMemoryView, m_ui.memoryviewWidget, &MemoryViewWidget::gotoAddress);
|
||||
connect(m_ui.memorySearchWidget, &MemorySearchWidget::switchToMemoryViewTab, [this]() { m_ui.tabWidget->setCurrentWidget(m_ui.tab_memory); });
|
||||
connect(m_ui.memorySearchWidget, &MemorySearchWidget::switchToMemoryViewTab,
|
||||
[this]() { m_ui.tabWidget->setCurrentWidget(m_ui.tab_memory); });
|
||||
m_ui.memorySearchWidget->setCpu(&m_cpu);
|
||||
|
||||
m_refreshDebuggerTimer.setInterval(1000);
|
||||
|
@ -153,13 +137,13 @@ CpuWidget::~CpuWidget() = default;
|
|||
|
||||
void CpuWidget::refreshDebugger()
|
||||
{
|
||||
if (m_cpu.isAlive())
|
||||
{
|
||||
if (!m_cpu.isAlive())
|
||||
return;
|
||||
|
||||
m_ui.registerWidget->update();
|
||||
m_ui.disassemblyWidget->update();
|
||||
m_ui.memoryviewWidget->update();
|
||||
m_ui.memorySearchWidget->update();
|
||||
}
|
||||
}
|
||||
|
||||
void CpuWidget::reloadCPUWidgets()
|
||||
|
@ -317,7 +301,7 @@ void CpuWidget::onBPListDoubleClicked(const QModelIndex& index)
|
|||
{
|
||||
if (index.column() == BreakpointModel::OFFSET)
|
||||
{
|
||||
m_ui.disassemblyWidget->gotoAddress(m_bpModel.data(index, BreakpointModel::DataRole).toUInt());
|
||||
m_ui.disassemblyWidget->gotoAddressAndSetFocus(m_bpModel.data(index, BreakpointModel::DataRole).toUInt());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -491,7 +475,7 @@ void CpuWidget::onSavedAddressesListContextMenu(QPoint pos)
|
|||
QAction* goToAddressDisassemblyAction = new QAction(tr("Go to in Disassembly"), m_ui.savedAddressesList);
|
||||
connect(goToAddressDisassemblyAction, &QAction::triggered, this, [this, indexAtPos]() {
|
||||
const QModelIndex rowAddressIndex = m_ui.savedAddressesList->model()->index(indexAtPos.row(), 0, QModelIndex());
|
||||
m_ui.disassemblyWidget->gotoAddress(m_ui.savedAddressesList->model()->data(rowAddressIndex, Qt::UserRole).toUInt());
|
||||
m_ui.disassemblyWidget->gotoAddressAndSetFocus(m_ui.savedAddressesList->model()->data(rowAddressIndex, Qt::UserRole).toUInt());
|
||||
});
|
||||
contextMenu->addAction(goToAddressDisassemblyAction);
|
||||
}
|
||||
|
@ -584,73 +568,6 @@ void CpuWidget::addAddressToSavedAddressesList(u32 address)
|
|||
m_ui.savedAddressesList->edit(m_ui.savedAddressesList->model()->index(rowCount - 1, 1));
|
||||
}
|
||||
|
||||
void CpuWidget::updateFunctionList(bool whenEmpty)
|
||||
{
|
||||
if (!m_cpu.isAlive())
|
||||
return;
|
||||
|
||||
if (m_cpu.getCpuType() == BREAKPOINT_EE || !m_moduleView)
|
||||
{
|
||||
if (whenEmpty && m_ui.listFunctions->count())
|
||||
return;
|
||||
|
||||
m_ui.listFunctions->clear();
|
||||
|
||||
const QString filter = m_ui.txtFuncSearch->text().toLower();
|
||||
for (const auto& symbol : m_cpu.GetSymbolMap().GetAllSymbols(SymbolType::ST_FUNCTION))
|
||||
{
|
||||
QString symbolName = symbol.name.c_str();
|
||||
|
||||
if (filter.size() && !symbolName.toLower().contains(filter))
|
||||
continue;
|
||||
|
||||
QListWidgetItem* item = new QListWidgetItem();
|
||||
|
||||
item->setText(QString("%0 %1").arg(FilledQStringFromValue(symbol.address, 16)).arg(symbolName));
|
||||
|
||||
item->setData(Qt::UserRole, symbol.address);
|
||||
|
||||
m_ui.listFunctions->addItem(item);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const QString filter = m_ui.txtFuncSearch->text().toLower();
|
||||
|
||||
m_ui.treeModules->clear();
|
||||
for (const auto& module : m_cpu.GetSymbolMap().GetModules())
|
||||
{
|
||||
QTreeWidgetItem* moduleItem = new QTreeWidgetItem(m_ui.treeModules, QStringList({QString(module.name.c_str()), QString("%0.%1").arg(module.version.major).arg(module.version.minor), QString::number(module.exports.size())}));
|
||||
QList<QTreeWidgetItem*> functions;
|
||||
for (const auto& sym : module.exports)
|
||||
{
|
||||
if (!QString(sym.name.c_str()).toLower().contains(filter))
|
||||
continue;
|
||||
|
||||
QString symbolName = QString(sym.name.c_str());
|
||||
QTreeWidgetItem* functionItem = new QTreeWidgetItem(moduleItem, QStringList(QString("%0 %1").arg(FilledQStringFromValue(sym.address, 16)).arg(symbolName)));
|
||||
functionItem->setData(0, Qt::UserRole, sym.address);
|
||||
functions.append(functionItem);
|
||||
}
|
||||
moduleItem->addChildren(functions);
|
||||
|
||||
if (!filter.isEmpty() && functions.size())
|
||||
{
|
||||
moduleItem->setExpanded(true);
|
||||
m_ui.treeModules->insertTopLevelItem(0, moduleItem);
|
||||
}
|
||||
else if (filter.isEmpty())
|
||||
{
|
||||
m_ui.treeModules->insertTopLevelItem(0, moduleItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
delete moduleItem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CpuWidget::updateThreads()
|
||||
{
|
||||
m_threadModel.refreshData();
|
||||
|
@ -694,145 +611,11 @@ void CpuWidget::onThreadListDoubleClick(const QModelIndex& index)
|
|||
m_ui.tabWidget->setCurrentWidget(m_ui.tab_memory);
|
||||
break;
|
||||
default: // Default to PC
|
||||
m_ui.disassemblyWidget->gotoAddress(m_ui.threadList->model()->data(m_ui.threadList->model()->index(index.row(), ThreadModel::ThreadColumns::PC), Qt::UserRole).toUInt());
|
||||
m_ui.disassemblyWidget->gotoAddressAndSetFocus(m_ui.threadList->model()->data(m_ui.threadList->model()->index(index.row(), ThreadModel::ThreadColumns::PC), Qt::UserRole).toUInt());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CpuWidget::onFuncListContextMenu(QPoint pos)
|
||||
{
|
||||
if (!m_funclistContextMenu)
|
||||
m_funclistContextMenu = new QMenu(m_ui.listFunctions);
|
||||
else
|
||||
m_funclistContextMenu->clear();
|
||||
|
||||
if (m_ui.listFunctions->selectedItems().count() && m_ui.listFunctions->selectedItems().first()->data(Qt::UserRole).isValid())
|
||||
{
|
||||
QAction* copyName = new QAction(tr("Copy Function Name"), m_ui.listFunctions);
|
||||
connect(copyName, &QAction::triggered, [this] {
|
||||
// We only store the address in the widget item
|
||||
// Resolve the function name by fetching the symbolmap and filtering the address
|
||||
|
||||
const QListWidgetItem* selectedItem = m_ui.listFunctions->selectedItems().first();
|
||||
const QString functionName = QString(m_cpu.GetSymbolMap().GetLabelName(selectedItem->data(Qt::UserRole).toUInt()).c_str());
|
||||
QApplication::clipboard()->setText(functionName);
|
||||
});
|
||||
m_funclistContextMenu->addAction(copyName);
|
||||
|
||||
QAction* copyAddress = new QAction(tr("Copy Function Address"), m_ui.listFunctions);
|
||||
connect(copyAddress, &QAction::triggered, [this] {
|
||||
const QString addressString = FilledQStringFromValue(m_ui.listFunctions->selectedItems().first()->data(Qt::UserRole).toUInt(), 16);
|
||||
QApplication::clipboard()->setText(addressString);
|
||||
});
|
||||
|
||||
m_funclistContextMenu->addAction(copyAddress);
|
||||
|
||||
m_funclistContextMenu->addSeparator();
|
||||
|
||||
QAction* gotoDisasm = new QAction(tr("Go to in Disassembly"), m_ui.listFunctions);
|
||||
connect(gotoDisasm, &QAction::triggered, [this] {
|
||||
m_ui.disassemblyWidget->gotoAddress(m_ui.listFunctions->selectedItems().first()->data(Qt::UserRole).toUInt());
|
||||
});
|
||||
|
||||
m_funclistContextMenu->addAction(gotoDisasm);
|
||||
|
||||
QAction* gotoMemory = new QAction(tr("Go to in Memory View"), m_ui.listFunctions);
|
||||
connect(gotoMemory, &QAction::triggered, [this] {
|
||||
m_ui.memoryviewWidget->gotoAddress(m_ui.listFunctions->selectedItems().first()->data(Qt::UserRole).toUInt());
|
||||
m_ui.tabWidget->setCurrentWidget(m_ui.tab_memory);
|
||||
});
|
||||
|
||||
m_funclistContextMenu->addAction(gotoMemory);
|
||||
|
||||
m_funclistContextMenu->addSeparator();
|
||||
}
|
||||
|
||||
if (m_cpu.getCpuType() == BREAKPOINT_IOP)
|
||||
{
|
||||
QAction* moduleViewAction = new QAction(tr("Module Tree"), m_ui.listFunctions);
|
||||
moduleViewAction->setCheckable(true);
|
||||
moduleViewAction->setChecked(m_moduleView);
|
||||
|
||||
connect(moduleViewAction, &QAction::triggered, [this] {
|
||||
m_moduleView = !m_moduleView;
|
||||
m_ui.treeModules->setVisible(m_moduleView);
|
||||
m_ui.listFunctions->setVisible(!m_moduleView);
|
||||
updateFunctionList();
|
||||
});
|
||||
|
||||
m_funclistContextMenu->addAction(moduleViewAction);
|
||||
}
|
||||
m_funclistContextMenu->popup(m_ui.listFunctions->viewport()->mapToGlobal(pos));
|
||||
}
|
||||
|
||||
void CpuWidget::onFuncListDoubleClick(QListWidgetItem* item)
|
||||
{
|
||||
m_ui.disassemblyWidget->gotoAddress(item->data(Qt::UserRole).toUInt());
|
||||
}
|
||||
|
||||
void CpuWidget::onModuleTreeContextMenu(QPoint pos)
|
||||
{
|
||||
if (!m_moduleTreeContextMenu)
|
||||
m_moduleTreeContextMenu = new QMenu(m_ui.treeModules);
|
||||
else
|
||||
m_moduleTreeContextMenu->clear();
|
||||
|
||||
if (m_ui.treeModules->selectedItems().count() && m_ui.treeModules->selectedItems().first()->data(0, Qt::UserRole).isValid())
|
||||
{
|
||||
QAction* copyName = new QAction(tr("Copy Function Name"), m_ui.treeModules);
|
||||
connect(copyName, &QAction::triggered, [this] {
|
||||
QApplication::clipboard()->setText(m_cpu.GetSymbolMap().GetLabelName(m_ui.treeModules->selectedItems().first()->data(0, Qt::UserRole).toUInt()).c_str());
|
||||
});
|
||||
m_moduleTreeContextMenu->addAction(copyName);
|
||||
|
||||
QAction* copyAddress = new QAction(tr("Copy Function Address"), m_ui.treeModules);
|
||||
connect(copyAddress, &QAction::triggered, [this] {
|
||||
const QString addressString = FilledQStringFromValue(m_ui.treeModules->selectedItems().first()->data(0, Qt::UserRole).toUInt(), 16);
|
||||
QApplication::clipboard()->setText(addressString);
|
||||
});
|
||||
m_moduleTreeContextMenu->addAction(copyAddress);
|
||||
|
||||
m_moduleTreeContextMenu->addSeparator();
|
||||
|
||||
QAction* gotoDisasm = new QAction(tr("Go to in Disassembly"), m_ui.treeModules);
|
||||
connect(gotoDisasm, &QAction::triggered, [this] {
|
||||
m_ui.disassemblyWidget->gotoAddress(m_ui.treeModules->selectedItems().first()->data(0, Qt::UserRole).toUInt());
|
||||
});
|
||||
m_moduleTreeContextMenu->addAction(gotoDisasm);
|
||||
|
||||
QAction* gotoMemory = new QAction(tr("Go to in Memory View"), m_ui.treeModules);
|
||||
connect(gotoMemory, &QAction::triggered, [this] {
|
||||
m_ui.memoryviewWidget->gotoAddress(m_ui.treeModules->selectedItems().first()->data(0, Qt::UserRole).toUInt());
|
||||
m_ui.tabWidget->setCurrentWidget(m_ui.tab_memory);
|
||||
});
|
||||
m_moduleTreeContextMenu->addAction(gotoMemory);
|
||||
}
|
||||
|
||||
m_moduleTreeContextMenu->addSeparator();
|
||||
|
||||
QAction* moduleViewAction = new QAction(tr("Module Tree"), m_ui.treeModules);
|
||||
moduleViewAction->setCheckable(true);
|
||||
moduleViewAction->setChecked(m_moduleView);
|
||||
|
||||
connect(moduleViewAction, &QAction::triggered, [this] {
|
||||
m_moduleView = !m_moduleView;
|
||||
m_ui.treeModules->setVisible(m_moduleView);
|
||||
m_ui.listFunctions->setVisible(!m_moduleView);
|
||||
updateFunctionList();
|
||||
});
|
||||
|
||||
m_moduleTreeContextMenu->addAction(moduleViewAction);
|
||||
|
||||
m_moduleTreeContextMenu->popup(m_ui.treeModules->viewport()->mapToGlobal(pos));
|
||||
}
|
||||
|
||||
void CpuWidget::onModuleTreeDoubleClick(QTreeWidgetItem* item)
|
||||
{
|
||||
if (item->data(0, Qt::UserRole).isValid())
|
||||
{
|
||||
m_ui.disassemblyWidget->gotoAddress(item->data(0, Qt::UserRole).toUInt());
|
||||
}
|
||||
}
|
||||
void CpuWidget::updateStackFrames()
|
||||
{
|
||||
m_stackModel.refreshData();
|
||||
|
@ -873,14 +656,14 @@ void CpuWidget::onStackListDoubleClick(const QModelIndex& index)
|
|||
{
|
||||
case StackModel::StackModel::ENTRY:
|
||||
case StackModel::StackModel::ENTRY_LABEL:
|
||||
m_ui.disassemblyWidget->gotoAddress(m_ui.stackList->model()->data(m_ui.stackList->model()->index(index.row(), StackModel::StackColumns::ENTRY), Qt::UserRole).toUInt());
|
||||
m_ui.disassemblyWidget->gotoAddressAndSetFocus(m_ui.stackList->model()->data(m_ui.stackList->model()->index(index.row(), StackModel::StackColumns::ENTRY), Qt::UserRole).toUInt());
|
||||
break;
|
||||
case StackModel::StackModel::SP:
|
||||
m_ui.memoryviewWidget->gotoAddress(m_ui.stackList->model()->data(index, Qt::UserRole).toUInt());
|
||||
m_ui.tabWidget->setCurrentWidget(m_ui.tab_memory);
|
||||
break;
|
||||
default: // Default to PC
|
||||
m_ui.disassemblyWidget->gotoAddress(m_ui.stackList->model()->data(m_ui.stackList->model()->index(index.row(), StackModel::StackColumns::PC), Qt::UserRole).toUInt());
|
||||
m_ui.disassemblyWidget->gotoAddressAndSetFocus(m_ui.stackList->model()->data(m_ui.stackList->model()->index(index.row(), StackModel::StackColumns::PC), Qt::UserRole).toUInt());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -63,11 +63,6 @@ public slots:
|
|||
void onStackListContextMenu(QPoint pos);
|
||||
void onStackListDoubleClick(const QModelIndex& index);
|
||||
|
||||
void updateFunctionList(bool whenEmpty = false);
|
||||
void onFuncListContextMenu(QPoint pos);
|
||||
void onFuncListDoubleClick(QListWidgetItem* item);
|
||||
void onModuleTreeContextMenu(QPoint pos);
|
||||
void onModuleTreeDoubleClick(QTreeWidgetItem* item);
|
||||
void refreshDebugger();
|
||||
void reloadCPUWidgets();
|
||||
|
||||
|
@ -91,6 +86,4 @@ private:
|
|||
QSortFilterProxyModel m_threadProxyModel;
|
||||
StackModel m_stackModel;
|
||||
SavedAddressesModel m_savedAddressesModel;
|
||||
|
||||
bool m_moduleView = true;
|
||||
};
|
||||
|
|
|
@ -153,9 +153,9 @@ void DisassemblyWidget::contextFollowBranch()
|
|||
if (line.type == DISTYPE_OPCODE || line.type == DISTYPE_MACRO)
|
||||
{
|
||||
if (line.info.isBranch)
|
||||
gotoAddress(line.info.branchTarget);
|
||||
gotoAddressAndSetFocus(line.info.branchTarget);
|
||||
else if (line.info.hasRelevantAddress)
|
||||
gotoAddress(line.info.releventAddress);
|
||||
gotoAddressAndSetFocus(line.info.releventAddress);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -176,144 +176,93 @@ void DisassemblyWidget::contextGoToAddress()
|
|||
return;
|
||||
}
|
||||
|
||||
gotoAddress(targetAddress);
|
||||
gotoAddressAndSetFocus(targetAddress);
|
||||
}
|
||||
|
||||
void DisassemblyWidget::contextAddFunction()
|
||||
{
|
||||
// Get current function
|
||||
const u32 curAddress = m_selectedAddressStart;
|
||||
const u32 curFuncAddr = m_cpu->GetSymbolMap().GetFunctionStart(m_selectedAddressStart);
|
||||
QString optionaldlgText;
|
||||
|
||||
if (curFuncAddr != SymbolMap::INVALID_ADDRESS)
|
||||
{
|
||||
if (curFuncAddr == curAddress) // There is already a function here
|
||||
{
|
||||
QMessageBox::warning(this, tr("Add Function Error"), tr("A function entry point already exists here. Consider renaming instead."));
|
||||
}
|
||||
else
|
||||
{
|
||||
const u32 prevSize = m_cpu->GetSymbolMap().GetFunctionSize(curFuncAddr);
|
||||
u32 newSize = curAddress - curFuncAddr;
|
||||
|
||||
bool ok;
|
||||
QString funcName = QInputDialog::getText(this, tr("Add Function"),
|
||||
tr("Function will be (0x%1) instructions long.\nEnter function name").arg(prevSize - newSize, 0, 16), QLineEdit::Normal, "", &ok);
|
||||
if (!ok)
|
||||
return;
|
||||
|
||||
m_cpu->GetSymbolMap().SetFunctionSize(curFuncAddr, newSize); // End the current function to where we selected
|
||||
newSize = prevSize - newSize;
|
||||
m_cpu->GetSymbolMap().AddFunction(funcName.toLocal8Bit().constData(), curAddress, newSize);
|
||||
m_cpu->GetSymbolMap().SortSymbols();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bool ok;
|
||||
QString funcName = QInputDialog::getText(this, "Add Function",
|
||||
tr("Function will be (0x%1) instructions long.\nEnter function name").arg(m_selectedAddressEnd + 4 - m_selectedAddressStart, 0, 16), QLineEdit::Normal, "", &ok);
|
||||
if (!ok)
|
||||
return;
|
||||
|
||||
m_cpu->GetSymbolMap().AddFunction(funcName.toLocal8Bit().constData(), m_selectedAddressStart, m_selectedAddressEnd + 4 - m_selectedAddressStart);
|
||||
m_cpu->GetSymbolMap().SortSymbols();
|
||||
}
|
||||
}
|
||||
|
||||
void DisassemblyWidget::contextCopyFunctionName()
|
||||
{
|
||||
QGuiApplication::clipboard()->setText(QString::fromStdString(m_cpu->GetSymbolMap().GetLabelName(m_selectedAddressStart)));
|
||||
std::string name = m_cpu->GetSymbolGuardian().FunctionStartingAtAddress(m_selectedAddressStart).name;
|
||||
QGuiApplication::clipboard()->setText(QString::fromStdString(name));
|
||||
}
|
||||
|
||||
void DisassemblyWidget::contextRemoveFunction()
|
||||
{
|
||||
u32 curFuncAddr = m_cpu->GetSymbolMap().GetFunctionStart(m_selectedAddressStart);
|
||||
m_cpu->GetSymbolGuardian().ReadWrite([&](ccc::SymbolDatabase& database) {
|
||||
ccc::Function* curFunc = database.functions.symbol_overlapping_address(m_selectedAddressStart);
|
||||
if (!curFunc)
|
||||
return;
|
||||
|
||||
if (curFuncAddr != SymbolMap::INVALID_ADDRESS)
|
||||
{
|
||||
u32 previousFuncAddr = m_cpu->GetSymbolMap().GetFunctionStart(curFuncAddr - 4);
|
||||
if (previousFuncAddr != SymbolMap::INVALID_ADDRESS)
|
||||
{
|
||||
// Extend the previous function to replace the spot of the function that is going to be removed
|
||||
u32 expandedSize = m_cpu->GetSymbolMap().GetFunctionSize(previousFuncAddr) + m_cpu->GetSymbolMap().GetFunctionSize(curFuncAddr);
|
||||
m_cpu->GetSymbolMap().SetFunctionSize(previousFuncAddr, expandedSize);
|
||||
}
|
||||
ccc::Function* previousFunc = database.functions.symbol_overlapping_address(curFunc->address().value - 4);
|
||||
if (previousFunc)
|
||||
previousFunc->set_size(curFunc->size() + previousFunc->size());
|
||||
|
||||
m_cpu->GetSymbolMap().RemoveFunction(curFuncAddr);
|
||||
m_cpu->GetSymbolMap().SortSymbols();
|
||||
}
|
||||
database.functions.mark_symbol_for_destruction(curFunc->handle(), &database);
|
||||
database.destroy_marked_symbols();
|
||||
});
|
||||
}
|
||||
|
||||
void DisassemblyWidget::contextRenameFunction()
|
||||
{
|
||||
const u32 curFuncAddress = m_cpu->GetSymbolMap().GetFunctionStart(m_selectedAddressStart);
|
||||
if (curFuncAddress != SymbolMap::INVALID_ADDRESS)
|
||||
const FunctionInfo curFunc = m_cpu->GetSymbolGuardian().FunctionOverlappingAddress(m_selectedAddressStart);
|
||||
|
||||
if (!curFunc.address.valid())
|
||||
{
|
||||
QMessageBox::warning(this, tr("Rename Function Error"), tr("No function / symbol is currently selected."));
|
||||
return;
|
||||
}
|
||||
|
||||
QString oldName = QString::fromStdString(curFunc.name);
|
||||
|
||||
bool ok;
|
||||
QString funcName = QInputDialog::getText(this, tr("Rename Function"), tr("Function name"), QLineEdit::Normal, m_cpu->GetSymbolMap().GetLabelName(curFuncAddress).c_str(), &ok);
|
||||
QString newName = QInputDialog::getText(this, tr("Rename Function"), tr("Function name"), QLineEdit::Normal, oldName, &ok);
|
||||
if (!ok)
|
||||
return;
|
||||
|
||||
if (funcName.isEmpty())
|
||||
if (newName.isEmpty())
|
||||
{
|
||||
QMessageBox::warning(this, tr("Rename Function Error"), tr("Function name cannot be nothing."));
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_cpu->GetSymbolMap().SetLabelName(funcName.toLocal8Bit().constData(), curFuncAddress);
|
||||
m_cpu->GetSymbolMap().SortSymbols();
|
||||
this->repaint();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
QMessageBox::warning(this, tr("Rename Function Error"), tr("No function / symbol is currently selected."));
|
||||
}
|
||||
|
||||
m_cpu->GetSymbolGuardian().ReadWrite([&](ccc::SymbolDatabase& database) {
|
||||
database.functions.rename_symbol(curFunc.handle, newName.toStdString());
|
||||
});
|
||||
}
|
||||
|
||||
void DisassemblyWidget::contextStubFunction()
|
||||
{
|
||||
const u32 curFuncAddress = m_cpu->GetSymbolMap().GetFunctionStart(m_selectedAddressStart);
|
||||
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
|
||||
FunctionInfo function = m_cpu->GetSymbolGuardian().FunctionOverlappingAddress(m_selectedAddressStart);
|
||||
u32 address = function.address.valid() ? function.address.value : m_selectedAddressStart;
|
||||
|
||||
Host::RunOnCPUThread([this, address, cpu = m_cpu] {
|
||||
this->m_stubbedFunctions.insert({address, {cpu->read32(address), cpu->read32(address + 4)}});
|
||||
cpu->write32(address, 0x03E00008); // jr ra
|
||||
cpu->write32(address + 4, 0x00000000); // nop
|
||||
emit VMUpdate();
|
||||
});
|
||||
}
|
||||
else // Stub the current opcode instead
|
||||
{
|
||||
Host::RunOnCPUThread([this, cpu = m_cpu] {
|
||||
this->m_stubbedFunctions.insert({m_selectedAddressStart, {cpu->read32(m_selectedAddressStart), cpu->read32(m_selectedAddressStart + 4)}});
|
||||
cpu->write32(m_selectedAddressStart, 0x03E00008); // jr $ra
|
||||
cpu->write32(m_selectedAddressStart + 4, 0x00000000); // nop
|
||||
emit VMUpdate();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
emit VMUpdate();
|
||||
u32 address = m_selectedAddressStart;
|
||||
m_cpu->GetSymbolGuardian().Read([&](const ccc::SymbolDatabase& database) {
|
||||
const ccc::Function* function = database.functions.symbol_overlapping_address(m_selectedAddressStart);
|
||||
if (function)
|
||||
address = function->address().value;
|
||||
});
|
||||
}
|
||||
else if (m_stubbedFunctions.find(m_selectedAddressStart) != m_stubbedFunctions.end())
|
||||
|
||||
auto stub = m_stubbedFunctions.find(address);
|
||||
if (stub != m_stubbedFunctions.end())
|
||||
{
|
||||
Host::RunOnCPUThread([this, cpu = m_cpu] {
|
||||
cpu->write32(m_selectedAddressStart, std::get<0>(this->m_stubbedFunctions[m_selectedAddressStart]));
|
||||
cpu->write32(m_selectedAddressStart + 4, std::get<1>(this->m_stubbedFunctions[m_selectedAddressStart]));
|
||||
this->m_stubbedFunctions.erase(m_selectedAddressStart);
|
||||
Host::RunOnCPUThread([this, address, cpu = m_cpu, stub] {
|
||||
auto [first_instruction, second_instruction] = stub->second;
|
||||
cpu->write32(address, first_instruction);
|
||||
cpu->write32(address + 4, second_instruction);
|
||||
this->m_stubbedFunctions.erase(address);
|
||||
emit VMUpdate();
|
||||
});
|
||||
}
|
||||
|
@ -641,7 +590,7 @@ void DisassemblyWidget::keyPressEvent(QKeyEvent* event)
|
|||
contextFollowBranch();
|
||||
break;
|
||||
case Qt::Key_Left:
|
||||
gotoAddress(m_cpu->getPC());
|
||||
gotoAddressAndSetFocus(m_cpu->getPC());
|
||||
break;
|
||||
case Qt::Key_O:
|
||||
m_showInstructionOpcode = !m_showInstructionOpcode;
|
||||
|
@ -666,7 +615,7 @@ void DisassemblyWidget::customMenuRequested(QPoint pos)
|
|||
contextMenu->addAction(action = new QAction(tr("&Copy Instruction Text"), this));
|
||||
action->setShortcut(QKeySequence(Qt::Key_C));
|
||||
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextCopyInstructionText);
|
||||
if (m_selectedAddressStart == m_cpu->GetSymbolMap().GetFunctionStart(m_selectedAddressStart))
|
||||
if (m_cpu->GetSymbolGuardian().FunctionExistsWithStartingAddress(m_selectedAddressStart))
|
||||
{
|
||||
contextMenu->addAction(action = new QAction(tr("Copy Function Name"), this));
|
||||
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextCopyFunctionName);
|
||||
|
@ -741,13 +690,8 @@ inline QString DisassemblyWidget::DisassemblyStringFromAddress(u32 address, QFon
|
|||
const bool isConditionalMet = line.info.conditionMet;
|
||||
const bool isCurrentPC = m_cpu->getPC() == address;
|
||||
|
||||
bool isFunctionNoReturn = false;
|
||||
|
||||
const std::string addressSymbol = m_cpu->GetSymbolMap().GetLabelName(address);
|
||||
if(m_cpu->GetSymbolMap().GetFunctionStart(address) == address)
|
||||
{
|
||||
isFunctionNoReturn = m_cpu->GetSymbolMap().GetFunctionNoReturn(address);
|
||||
}
|
||||
FunctionInfo function = m_cpu->GetSymbolGuardian().FunctionStartingAtAddress(address);
|
||||
SymbolInfo symbol = m_cpu->GetSymbolGuardian().SymbolStartingAtAddress(address);
|
||||
const bool showOpcode = m_showInstructionOpcode && m_cpu->isAlive();
|
||||
|
||||
QString lineString;
|
||||
|
@ -760,7 +704,7 @@ inline QString DisassemblyWidget::DisassemblyStringFromAddress(u32 address, QFon
|
|||
lineString = QString(" %1 %2 %3 %4 %5 %6");
|
||||
}
|
||||
|
||||
if(isFunctionNoReturn)
|
||||
if (function.is_no_return)
|
||||
{
|
||||
lineString = lineString.arg("NR");
|
||||
}
|
||||
|
@ -769,13 +713,12 @@ inline QString DisassemblyWidget::DisassemblyStringFromAddress(u32 address, QFon
|
|||
lineString = lineString.arg(" ");
|
||||
}
|
||||
|
||||
if (addressSymbol.empty()) // The address wont have symbol text if it's the start of a function for example
|
||||
if (symbol.name.empty())
|
||||
lineString = lineString.arg(address, 8, 16, QChar('0')).toUpper();
|
||||
else
|
||||
{
|
||||
// We want this text elided
|
||||
QFontMetrics metric(font);
|
||||
QString symbolString = QString::fromStdString(addressSymbol);
|
||||
QString symbolString = QString::fromStdString(symbol.name);
|
||||
|
||||
lineString = lineString.arg(metric.elidedText(symbolString, Qt::ElideRight, (selected ? 32 : 7) * font.pointSize()));
|
||||
}
|
||||
|
@ -829,11 +772,11 @@ QColor DisassemblyWidget::GetAddressFunctionColor(u32 address)
|
|||
};
|
||||
}
|
||||
|
||||
const auto funNum = m_cpu->GetSymbolMap().GetFunctionNum(address);
|
||||
if (funNum == -1)
|
||||
return this->palette().text().color();
|
||||
ccc::FunctionHandle handle = m_cpu->GetSymbolGuardian().FunctionOverlappingAddress(address).handle;
|
||||
if (!handle.valid())
|
||||
return palette().text().color();
|
||||
|
||||
return colors[funNum % 6];
|
||||
return colors[handle.value % colors.size()];
|
||||
}
|
||||
|
||||
QString DisassemblyWidget::FetchSelectionInfo(SelectionInfo selInfo)
|
||||
|
@ -861,6 +804,11 @@ QString DisassemblyWidget::FetchSelectionInfo(SelectionInfo selInfo)
|
|||
return infoBlock;
|
||||
}
|
||||
|
||||
void DisassemblyWidget::gotoAddressAndSetFocus(u32 address)
|
||||
{
|
||||
gotoAddress(address, true);
|
||||
}
|
||||
|
||||
void DisassemblyWidget::gotoAddress(u32 address, bool should_set_focus)
|
||||
{
|
||||
const u32 destAddress = address & ~3;
|
||||
|
@ -888,21 +836,9 @@ bool DisassemblyWidget::AddressCanRestore(u32 start, u32 end)
|
|||
|
||||
bool DisassemblyWidget::FunctionCanRestore(u32 address)
|
||||
{
|
||||
u32 funcStartAddress = m_cpu->GetSymbolMap().GetFunctionStart(address);
|
||||
FunctionInfo function = m_cpu->GetSymbolGuardian().FunctionOverlappingAddress(address);
|
||||
if (function.address.valid())
|
||||
address = function.address.value;
|
||||
|
||||
if (funcStartAddress != SymbolMap::INVALID_ADDRESS)
|
||||
{
|
||||
if (m_stubbedFunctions.find(funcStartAddress) != this->m_stubbedFunctions.end())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_stubbedFunctions.find(address) != this->m_stubbedFunctions.end())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return m_stubbedFunctions.find(address) != m_stubbedFunctions.end();
|
||||
}
|
||||
|
|
|
@ -58,7 +58,8 @@ public slots:
|
|||
void contextRestoreFunction();
|
||||
void contextShowOpcode();
|
||||
|
||||
void gotoAddress(u32 address, bool should_set_focus = true);
|
||||
void gotoAddressAndSetFocus(u32 address);
|
||||
void gotoAddress(u32 address, bool should_set_focus);
|
||||
|
||||
void setDemangle(bool demangle) { m_demangleFunctions = demangle; };
|
||||
signals:
|
||||
|
|
|
@ -47,7 +47,7 @@ QVariant BreakpointModel::data(const QModelIndex& index, int role) const
|
|||
case BreakpointColumns::OFFSET:
|
||||
return QtUtils::FilledQStringFromValue(bp->addr, 16);
|
||||
case BreakpointColumns::SIZE_LABEL:
|
||||
return m_cpu.GetSymbolMap().GetLabelName(bp->addr).c_str();
|
||||
return QString::fromStdString(m_cpu.GetSymbolGuardian().FunctionStartingAtAddress(bp->addr).name);
|
||||
case BreakpointColumns::OPCODE:
|
||||
// Note: Fix up the disassemblymanager so we can use it here, instead of calling a function through the disassemblyview (yuck)
|
||||
return m_cpu.disasm(bp->addr, true).c_str();
|
||||
|
@ -100,7 +100,7 @@ QVariant BreakpointModel::data(const QModelIndex& index, int role) const
|
|||
case BreakpointColumns::OFFSET:
|
||||
return bp->addr;
|
||||
case BreakpointColumns::SIZE_LABEL:
|
||||
return m_cpu.GetSymbolMap().GetLabelName(bp->addr).c_str();
|
||||
return QString::fromStdString(m_cpu.GetSymbolGuardian().FunctionStartingAtAddress(bp->addr).name);
|
||||
case BreakpointColumns::OPCODE:
|
||||
// Note: Fix up the disassemblymanager so we can use it here, instead of calling a function through the disassemblyview (yuck)
|
||||
return m_cpu.disasm(bp->addr, false).c_str();
|
||||
|
@ -146,7 +146,7 @@ QVariant BreakpointModel::data(const QModelIndex& index, int role) const
|
|||
case BreakpointColumns::OFFSET:
|
||||
return QtUtils::FilledQStringFromValue(bp->addr, 16);
|
||||
case BreakpointColumns::SIZE_LABEL:
|
||||
return m_cpu.GetSymbolMap().GetLabelName(bp->addr).c_str();
|
||||
return QString::fromStdString(m_cpu.GetSymbolGuardian().FunctionStartingAtAddress(bp->addr).name);
|
||||
case BreakpointColumns::OPCODE:
|
||||
// Note: Fix up the disassemblymanager so we can use it here, instead of calling a function through the disassemblyview (yuck)
|
||||
return m_cpu.disasm(bp->addr, false).c_str();
|
||||
|
|
|
@ -33,7 +33,7 @@ QVariant StackModel::data(const QModelIndex& index, int role) const
|
|||
case StackModel::ENTRY:
|
||||
return QtUtils::FilledQStringFromValue(stackFrame.entry, 16);
|
||||
case StackModel::ENTRY_LABEL:
|
||||
return m_cpu.GetSymbolMap().GetLabelName(stackFrame.entry).c_str();
|
||||
return QString::fromStdString(m_cpu.GetSymbolGuardian().FunctionStartingAtAddress(stackFrame.entry).name);
|
||||
case StackModel::PC:
|
||||
return QtUtils::FilledQStringFromValue(stackFrame.pc, 16);
|
||||
case StackModel::PC_OPCODE:
|
||||
|
@ -52,7 +52,7 @@ QVariant StackModel::data(const QModelIndex& index, int role) const
|
|||
case StackModel::ENTRY:
|
||||
return stackFrame.entry;
|
||||
case StackModel::ENTRY_LABEL:
|
||||
return m_cpu.GetSymbolMap().GetLabelName(stackFrame.entry).c_str();
|
||||
return QString::fromStdString(m_cpu.GetSymbolGuardian().FunctionStartingAtAddress(stackFrame.entry).name);
|
||||
case StackModel::PC:
|
||||
return stackFrame.pc;
|
||||
case StackModel::PC_OPCODE:
|
||||
|
|
|
@ -28,7 +28,7 @@ public:
|
|||
static constexpr QHeaderView::ResizeMode HeaderResizeModes[StackColumns::COLUMN_COUNT] =
|
||||
{
|
||||
QHeaderView::ResizeMode::ResizeToContents,
|
||||
QHeaderView::ResizeMode::ResizeToContents,
|
||||
QHeaderView::ResizeMode::Stretch,
|
||||
QHeaderView::ResizeMode::ResizeToContents,
|
||||
QHeaderView::ResizeMode::Stretch,
|
||||
QHeaderView::ResizeMode::ResizeToContents,
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
#include "CDVD/CDVDcommon.h"
|
||||
#include "CDVD/IsoReader.h"
|
||||
#include "CDVD/IsoFileFormats.h"
|
||||
#include "DebugTools/SymbolMap.h"
|
||||
#include "Config.h"
|
||||
#include "Host.h"
|
||||
#include "IconsFontAwesome5.h"
|
||||
|
@ -287,20 +286,6 @@ void CDVDsys_SetFile(CDVD_SourceType srctype, std::string newfile)
|
|||
#endif
|
||||
|
||||
m_SourceFilename[enum_cast(srctype)] = std::move(newfile);
|
||||
|
||||
// look for symbol file
|
||||
if (R5900SymbolMap.IsEmpty())
|
||||
{
|
||||
std::string symName;
|
||||
std::string::size_type n = m_SourceFilename[enum_cast(srctype)].rfind('.');
|
||||
if (n == std::string::npos)
|
||||
symName = m_SourceFilename[enum_cast(srctype)] + ".sym";
|
||||
else
|
||||
symName = m_SourceFilename[enum_cast(srctype)].substr(0, n) + ".sym";
|
||||
|
||||
R5900SymbolMap.LoadNocashSym(symName.c_str());
|
||||
R5900SymbolMap.SortSymbols();
|
||||
}
|
||||
}
|
||||
|
||||
const std::string& CDVDsys_GetFile(CDVD_SourceType srctype)
|
||||
|
|
|
@ -786,7 +786,7 @@ set(pcsx2DebugToolsSources
|
|||
DebugTools/MipsAssemblerTables.cpp
|
||||
DebugTools/MipsStackWalk.cpp
|
||||
DebugTools/Breakpoints.cpp
|
||||
DebugTools/SymbolMap.cpp
|
||||
DebugTools/SymbolGuardian.cpp
|
||||
DebugTools/DisR3000A.cpp
|
||||
DebugTools/DisR5900asm.cpp
|
||||
DebugTools/DisVU0Micro.cpp
|
||||
|
@ -803,7 +803,7 @@ set(pcsx2DebugToolsHeaders
|
|||
DebugTools/MipsAssemblerTables.h
|
||||
DebugTools/MipsStackWalk.h
|
||||
DebugTools/Breakpoints.h
|
||||
DebugTools/SymbolMap.h
|
||||
DebugTools/SymbolGuardian.h
|
||||
DebugTools/Debug.h
|
||||
DebugTools/DisASM.h
|
||||
DebugTools/DisVUmicro.h
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
#include "Breakpoints.h"
|
||||
#include "SymbolMap.h"
|
||||
#include "SymbolGuardian.h"
|
||||
#include "MIPSAnalyst.h"
|
||||
#include <cstdio>
|
||||
#include "R5900.h"
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
|
||||
#include "R3000A.h"
|
||||
#include "IopMem.h"
|
||||
#include "SymbolMap.h"
|
||||
#include "VMManager.h"
|
||||
|
||||
#include "common/StringUtil.h"
|
||||
|
@ -109,10 +108,12 @@ public:
|
|||
|
||||
virtual bool parseSymbol(char* str, u64& symbolValue)
|
||||
{
|
||||
u32 value;
|
||||
bool result = cpu->GetSymbolMap().GetLabelValue(str, value);
|
||||
symbolValue = value;
|
||||
return result;
|
||||
SymbolInfo symbol = cpu->GetSymbolGuardian().SymbolWithName(std::string(str));
|
||||
if (!symbol.address.valid())
|
||||
return false;
|
||||
|
||||
symbolValue = symbol.address.value;
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual u64 getReferenceValue(u64 referenceIndex)
|
||||
|
@ -238,7 +239,7 @@ void DebugInterface::resumeCpu()
|
|||
|
||||
char* DebugInterface::stringFromPointer(u32 p)
|
||||
{
|
||||
const int BUFFER_LEN = 25;
|
||||
const int BUFFER_LEN = 64;
|
||||
static char buf[BUFFER_LEN] = {0};
|
||||
|
||||
if (!isValidAddress(p))
|
||||
|
@ -267,6 +268,46 @@ char* DebugInterface::stringFromPointer(u32 p)
|
|||
return buf;
|
||||
}
|
||||
|
||||
std::optional<u32> DebugInterface::getCallerStackPointer(const ccc::Function& currentFunction)
|
||||
{
|
||||
u32 sp = getRegister(EECAT_GPR, 29);
|
||||
u32 pc = getPC();
|
||||
|
||||
if (pc != currentFunction.address().value)
|
||||
{
|
||||
std::optional<u32> stack_frame_size = getStackFrameSize(currentFunction);
|
||||
if (!stack_frame_size.has_value())
|
||||
return std::nullopt;
|
||||
|
||||
sp += *stack_frame_size;
|
||||
}
|
||||
|
||||
return sp;
|
||||
}
|
||||
|
||||
std::optional<u32> DebugInterface::getStackFrameSize(const ccc::Function& function)
|
||||
{
|
||||
s32 stack_frame_size = function.stack_frame_size;
|
||||
|
||||
if (stack_frame_size < 0)
|
||||
{
|
||||
// The stack frame size isn't stored in the symbol table, so we try
|
||||
// to extract it from the code by checking for an instruction at the
|
||||
// start of the current function that is in the form of
|
||||
// "addui $sp, $sp, frame_size" instead.
|
||||
|
||||
u32 instruction = read32(function.address().value);
|
||||
|
||||
if ((instruction & 0xffff0000) == 0x27bd0000)
|
||||
stack_frame_size = -(instruction & 0xffff);
|
||||
|
||||
if (stack_frame_size < 0)
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return (u32)stack_frame_size;
|
||||
}
|
||||
|
||||
bool DebugInterface::initExpression(const char* exp, PostfixExpression& dest)
|
||||
{
|
||||
MipsExpressionFunctions funcs(this);
|
||||
|
@ -375,6 +416,14 @@ void R5900DebugInterface::write8(u32 address, u8 value)
|
|||
memWrite8(address, value);
|
||||
}
|
||||
|
||||
void R5900DebugInterface::write16(u32 address, u16 value)
|
||||
{
|
||||
if (!isValidAddress(address))
|
||||
return;
|
||||
|
||||
memWrite16(address, value);
|
||||
}
|
||||
|
||||
void R5900DebugInterface::write32(u32 address, u32 value)
|
||||
{
|
||||
if (!isValidAddress(address))
|
||||
|
@ -383,6 +432,21 @@ void R5900DebugInterface::write32(u32 address, u32 value)
|
|||
memWrite32(address, value);
|
||||
}
|
||||
|
||||
void R5900DebugInterface::write64(u32 address, u64 value)
|
||||
{
|
||||
if (!isValidAddress(address))
|
||||
return;
|
||||
|
||||
memWrite64(address, value);
|
||||
}
|
||||
|
||||
void R5900DebugInterface::write128(u32 address, u128 value)
|
||||
{
|
||||
if (!isValidAddress(address))
|
||||
return;
|
||||
|
||||
memWrite128(address, value);
|
||||
}
|
||||
|
||||
int R5900DebugInterface::getRegisterCategoryCount()
|
||||
{
|
||||
|
@ -727,9 +791,9 @@ u32 R5900DebugInterface::getCycles()
|
|||
return cpuRegs.cycle;
|
||||
}
|
||||
|
||||
SymbolMap& R5900DebugInterface::GetSymbolMap() const
|
||||
SymbolGuardian& R5900DebugInterface::GetSymbolGuardian() const
|
||||
{
|
||||
return R5900SymbolMap;
|
||||
return R5900SymbolGuardian;
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<BiosThread>> R5900DebugInterface::GetThreadList() const
|
||||
|
@ -788,7 +852,6 @@ u32 R3000DebugInterface::read32(u32 address, bool& valid)
|
|||
if (!(valid = isValidAddress(address)))
|
||||
return -1;
|
||||
return iopMemRead32(address);
|
||||
|
||||
}
|
||||
|
||||
u64 R3000DebugInterface::read64(u32 address)
|
||||
|
@ -815,6 +878,14 @@ void R3000DebugInterface::write8(u32 address, u8 value)
|
|||
iopMemWrite8(address, value);
|
||||
}
|
||||
|
||||
void R3000DebugInterface::write16(u32 address, u16 value)
|
||||
{
|
||||
if (!isValidAddress(address))
|
||||
return;
|
||||
|
||||
iopMemWrite16(address, value);
|
||||
}
|
||||
|
||||
void R3000DebugInterface::write32(u32 address, u32 value)
|
||||
{
|
||||
if (!isValidAddress(address))
|
||||
|
@ -823,6 +894,26 @@ void R3000DebugInterface::write32(u32 address, u32 value)
|
|||
iopMemWrite32(address, value);
|
||||
}
|
||||
|
||||
void R3000DebugInterface::write64(u32 address, u64 value)
|
||||
{
|
||||
if (!isValidAddress(address))
|
||||
return;
|
||||
|
||||
iopMemWrite32(address + 0, value);
|
||||
iopMemWrite32(address + 4, value >> 32);
|
||||
}
|
||||
|
||||
void R3000DebugInterface::write128(u32 address, u128 value)
|
||||
{
|
||||
if (!isValidAddress(address))
|
||||
return;
|
||||
|
||||
iopMemWrite32(address + 0x0, value._u32[0]);
|
||||
iopMemWrite32(address + 0x4, value._u32[1]);
|
||||
iopMemWrite32(address + 0x8, value._u32[2]);
|
||||
iopMemWrite32(address + 0xc, value._u32[3]);
|
||||
}
|
||||
|
||||
int R3000DebugInterface::getRegisterCategoryCount()
|
||||
{
|
||||
return IOPCAT_COUNT;
|
||||
|
@ -1019,9 +1110,9 @@ u32 R3000DebugInterface::getCycles()
|
|||
return psxRegs.cycle;
|
||||
}
|
||||
|
||||
SymbolMap& R3000DebugInterface::GetSymbolMap() const
|
||||
SymbolGuardian& R3000DebugInterface::GetSymbolGuardian() const
|
||||
{
|
||||
return R3000SymbolMap;
|
||||
return R3000SymbolGuardian;
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<BiosThread>> R3000DebugInterface::GetThreadList() const
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
#include "DebugTools/BiosDebugData.h"
|
||||
#include "MemoryTypes.h"
|
||||
#include "ExpressionParser.h"
|
||||
#include "SymbolMap.h"
|
||||
#include "SymbolGuardian.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
|
@ -51,7 +51,10 @@ public:
|
|||
virtual u64 read64(u32 address, bool& valid) = 0;
|
||||
virtual u128 read128(u32 address) = 0;
|
||||
virtual void write8(u32 address, u8 value) = 0;
|
||||
virtual void write16(u32 address, u16 value) = 0;
|
||||
virtual void write32(u32 address, u32 value) = 0;
|
||||
virtual void write64(u32 address, u64 value) = 0;
|
||||
virtual void write128(u32 address, u128 value) = 0;
|
||||
|
||||
// register stuff
|
||||
virtual int getRegisterCategoryCount() = 0;
|
||||
|
@ -73,7 +76,7 @@ public:
|
|||
virtual bool isValidAddress(u32 address) = 0;
|
||||
virtual u32 getCycles() = 0;
|
||||
virtual BreakPointCpu getCpuType() = 0;
|
||||
[[nodiscard]] virtual SymbolMap& GetSymbolMap() const = 0;
|
||||
[[nodiscard]] virtual SymbolGuardian& GetSymbolGuardian() const = 0;
|
||||
[[nodiscard]] virtual std::vector<std::unique_ptr<BiosThread>> GetThreadList() const = 0;
|
||||
|
||||
bool initExpression(const char* exp, PostfixExpression& dest);
|
||||
|
@ -84,6 +87,9 @@ public:
|
|||
void resumeCpu();
|
||||
char* stringFromPointer(u32 p);
|
||||
|
||||
std::optional<u32> getCallerStackPointer(const ccc::Function& currentFunction);
|
||||
std::optional<u32> getStackFrameSize(const ccc::Function& currentFunction);
|
||||
|
||||
static void setPauseOnEntry(bool pauseOnEntry) { m_pause_on_entry = pauseOnEntry; };
|
||||
static bool getPauseOnEntry() { return m_pause_on_entry; }
|
||||
|
||||
|
@ -104,7 +110,10 @@ public:
|
|||
u64 read64(u32 address, bool& valid) override;
|
||||
u128 read128(u32 address) override;
|
||||
void write8(u32 address, u8 value) override;
|
||||
void write16(u32 address, u16 value) override;
|
||||
void write32(u32 address, u32 value) override;
|
||||
void write64(u32 address, u64 value) override;
|
||||
void write128(u32 address, u128 value) override;
|
||||
|
||||
// register stuff
|
||||
int getRegisterCategoryCount() override;
|
||||
|
@ -121,7 +130,7 @@ public:
|
|||
bool getCPCOND0() override;
|
||||
void setPc(u32 newPc) override;
|
||||
void setRegister(int cat, int num, u128 newValue) override;
|
||||
[[nodiscard]] SymbolMap& GetSymbolMap() const override;
|
||||
[[nodiscard]] SymbolGuardian& GetSymbolGuardian() const override;
|
||||
[[nodiscard]] std::vector<std::unique_ptr<BiosThread>> GetThreadList() const override;
|
||||
|
||||
std::string disasm(u32 address, bool simplify) override;
|
||||
|
@ -144,7 +153,10 @@ public:
|
|||
u64 read64(u32 address, bool& valid) override;
|
||||
u128 read128(u32 address) override;
|
||||
void write8(u32 address, u8 value) override;
|
||||
void write16(u32 address, u16 value) override;
|
||||
void write32(u32 address, u32 value) override;
|
||||
void write64(u32 address, u64 value) override;
|
||||
void write128(u32 address, u128 value) override;
|
||||
|
||||
// register stuff
|
||||
int getRegisterCategoryCount() override;
|
||||
|
@ -161,7 +173,7 @@ public:
|
|||
bool getCPCOND0() override;
|
||||
void setPc(u32 newPc) override;
|
||||
void setRegister(int cat, int num, u128 newValue) override;
|
||||
[[nodiscard]] SymbolMap& GetSymbolMap() const override;
|
||||
[[nodiscard]] SymbolGuardian& GetSymbolGuardian() const override;
|
||||
[[nodiscard]] std::vector<std::unique_ptr<BiosThread>> GetThreadList() const override;
|
||||
|
||||
std::string disasm(u32 address, bool simplify) override;
|
||||
|
|
|
@ -30,7 +30,7 @@ static u32 computeHash(u32 address, u32 size)
|
|||
}
|
||||
|
||||
|
||||
static void parseDisasm(SymbolMap& map, const char* disasm, char* opcode, char* arguments, size_t arguments_size, bool insertSymbols)
|
||||
static void parseDisasm(SymbolGuardian& guardian, const char* disasm, char* opcode, char* arguments, size_t arguments_size, bool insertSymbols)
|
||||
{
|
||||
if (*disasm == '(')
|
||||
{
|
||||
|
@ -64,7 +64,7 @@ static void parseDisasm(SymbolMap& map, const char* disasm, char* opcode, char*
|
|||
u32 branchTarget;
|
||||
sscanf(disasm+3,"0x%08x",&branchTarget);
|
||||
|
||||
const std::string addressSymbol = map.GetLabelName(branchTarget);
|
||||
const std::string addressSymbol = guardian.SymbolStartingAtAddress(branchTarget).name;
|
||||
if (!addressSymbol.empty() && insertSymbols)
|
||||
{
|
||||
arguments += std::snprintf(arguments, arguments_size - (arguments - arguments_start), "%s",addressSymbol.c_str());
|
||||
|
@ -147,19 +147,50 @@ void DisassemblyManager::analyze(u32 address, u32 size = 1024)
|
|||
continue;
|
||||
}
|
||||
|
||||
SymbolInfo info;
|
||||
if (!cpu->GetSymbolMap().GetSymbolInfo(&info,address,ST_ALL))
|
||||
SymbolInfo info = cpu->GetSymbolGuardian().SymbolOverlappingAddress(
|
||||
address, ccc::FUNCTION | ccc::GLOBAL_VARIABLE | ccc::LOCAL_VARIABLE);
|
||||
|
||||
if (info.descriptor.has_value())
|
||||
{
|
||||
switch (*info.descriptor)
|
||||
{
|
||||
case ccc::SymbolDescriptor::FUNCTION:
|
||||
{
|
||||
DisassemblyFunction* function = new DisassemblyFunction(cpu,info.address.value,info.size);
|
||||
entries[info.address.value] = function;
|
||||
address = info.address.value + info.size;
|
||||
break;
|
||||
}
|
||||
case ccc::SymbolDescriptor::GLOBAL_VARIABLE:
|
||||
{
|
||||
DisassemblyData* data = new DisassemblyData(cpu,info.address.value,info.size,DATATYPE_WORD);
|
||||
entries[info.address.value] = data;
|
||||
address = info.address.value+info.size;
|
||||
break;
|
||||
}
|
||||
case ccc::SymbolDescriptor::LOCAL_VARIABLE:
|
||||
{
|
||||
DisassemblyData* data = new DisassemblyData(cpu,info.address.value,info.size,DATATYPE_WORD);
|
||||
entries[info.address.value] = data;
|
||||
address = info.address.value+info.size;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (address % 4)
|
||||
{
|
||||
u32 next = std::min<u32>((address+3) & ~3,cpu->GetSymbolMap().GetNextSymbolAddress(address,ST_ALL));
|
||||
u32 next = std::min<u32>((address+3) & ~3,cpu->GetSymbolGuardian().SymbolAfterAddress(
|
||||
address, ccc::FUNCTION | ccc::GLOBAL_VARIABLE | ccc::LOCAL_VARIABLE).address.value);
|
||||
DisassemblyData* data = new DisassemblyData(cpu,address,next-address,DATATYPE_BYTE);
|
||||
entries[address] = data;
|
||||
address = next;
|
||||
continue;
|
||||
}
|
||||
|
||||
u32 next = cpu->GetSymbolMap().GetNextSymbolAddress(address,ST_ALL);
|
||||
u32 next = cpu->GetSymbolGuardian().SymbolAfterAddress(
|
||||
address, ccc::FUNCTION | ccc::GLOBAL_VARIABLE | ccc::LOCAL_VARIABLE).address.value;
|
||||
|
||||
if ((next % 4) && next != 0xFFFFFFFF)
|
||||
{
|
||||
|
@ -181,26 +212,6 @@ void DisassemblyManager::analyze(u32 address, u32 size = 1024)
|
|||
address = next;
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (info.type)
|
||||
{
|
||||
case ST_FUNCTION:
|
||||
{
|
||||
DisassemblyFunction* function = new DisassemblyFunction(cpu,info.address,info.size);
|
||||
entries[info.address] = function;
|
||||
address = info.address+info.size;
|
||||
}
|
||||
break;
|
||||
case ST_DATA:
|
||||
{
|
||||
DisassemblyData* data = new DisassemblyData(cpu,info.address,info.size,cpu->GetSymbolMap().GetDataType(info.address));
|
||||
entries[info.address] = data;
|
||||
address = info.address+info.size;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -401,7 +412,7 @@ bool DisassemblyFunction::disassemble(u32 address, DisassemblyLineInfo& dest, bo
|
|||
if (it == entries.end())
|
||||
return false;
|
||||
|
||||
return it->second->disassemble(address,dest,simplify, simplify);
|
||||
return it->second->disassemble(address,dest,insertSymbols,simplify);
|
||||
}
|
||||
|
||||
void DisassemblyFunction::getBranchLines(u32 start, u32 size, std::vector<BranchLine>& dest)
|
||||
|
@ -529,21 +540,23 @@ void DisassemblyFunction::load()
|
|||
u32 funcPos = address;
|
||||
u32 funcEnd = address+size;
|
||||
|
||||
u32 nextData = cpu->GetSymbolMap().GetNextSymbolAddress(funcPos-1,ST_DATA);
|
||||
SymbolInfo nextData = cpu->GetSymbolGuardian().SymbolAfterAddress(
|
||||
funcPos-1, ccc::GLOBAL_VARIABLE | ccc::LOCAL_VARIABLE);
|
||||
u32 opcodeSequenceStart = funcPos;
|
||||
while (funcPos < funcEnd)
|
||||
{
|
||||
if (funcPos == nextData)
|
||||
if (funcPos == nextData.address.value && nextData.size > 0)
|
||||
{
|
||||
if (opcodeSequenceStart != funcPos)
|
||||
addOpcodeSequence(opcodeSequenceStart,funcPos);
|
||||
|
||||
DisassemblyData* data = new DisassemblyData(cpu,funcPos,cpu->GetSymbolMap().GetDataSize(funcPos),cpu->GetSymbolMap().GetDataType(funcPos));
|
||||
DisassemblyData* data = new DisassemblyData(cpu,funcPos,nextData.size,DATATYPE_WORD);
|
||||
entries[funcPos] = data;
|
||||
lineAddresses.push_back(funcPos);
|
||||
funcPos += data->getTotalSize();
|
||||
|
||||
nextData = cpu->GetSymbolMap().GetNextSymbolAddress(funcPos-1,ST_DATA);
|
||||
nextData = cpu->GetSymbolGuardian().SymbolAfterAddress(funcPos-1,
|
||||
ccc::GLOBAL_VARIABLE | ccc::LOCAL_VARIABLE);
|
||||
opcodeSequenceStart = funcPos;
|
||||
continue;
|
||||
}
|
||||
|
@ -593,7 +606,7 @@ void DisassemblyFunction::load()
|
|||
*/
|
||||
#if 0
|
||||
// lui
|
||||
if (MIPS_GET_OP(opInfo.encodedOpcode) == 0x0F && funcPos < funcEnd && funcPos != nextData)
|
||||
if (MIPS_GET_OP(opInfo.encodedOpcode) == 0x0F && funcPos < funcEnd && funcPos != nextData.address.value)
|
||||
{
|
||||
u32 next = cpu->read32(funcPos);
|
||||
|
||||
|
@ -706,7 +719,7 @@ bool DisassemblyOpcode::disassemble(u32 address, DisassemblyLineInfo& dest, bool
|
|||
char opcode[64],arguments[256];
|
||||
|
||||
std::string dis = cpu->disasm(address,simplify);
|
||||
parseDisasm(cpu->GetSymbolMap(),dis.c_str(),opcode,arguments,std::size(arguments),insertSymbols);
|
||||
parseDisasm(cpu->GetSymbolGuardian(),dis.c_str(),opcode,arguments,std::size(arguments),insertSymbols);
|
||||
dest.type = DISTYPE_OPCODE;
|
||||
dest.name = opcode;
|
||||
dest.params = arguments;
|
||||
|
@ -783,7 +796,7 @@ bool DisassemblyMacro::disassemble(u32 address, DisassemblyLineInfo& dest, bool
|
|||
case MACRO_LI:
|
||||
dest.name = name;
|
||||
|
||||
addressSymbol = cpu->GetSymbolMap().GetLabelName(immediate);
|
||||
addressSymbol = cpu->GetSymbolGuardian().SymbolStartingAtAddress(immediate).name;
|
||||
if (!addressSymbol.empty() && insertSymbols)
|
||||
{
|
||||
std::snprintf(buffer,std::size(buffer),"%s,%s",cpu->getRegisterName(0,rt),addressSymbol.c_str());
|
||||
|
@ -799,7 +812,7 @@ bool DisassemblyMacro::disassemble(u32 address, DisassemblyLineInfo& dest, bool
|
|||
case MACRO_MEMORYIMM:
|
||||
dest.name = name;
|
||||
|
||||
addressSymbol = cpu->GetSymbolMap().GetLabelName(immediate);
|
||||
addressSymbol = cpu->GetSymbolGuardian().SymbolStartingAtAddress(immediate).name;
|
||||
if (!addressSymbol.empty() && insertSymbols)
|
||||
{
|
||||
std::snprintf(buffer,std::size(buffer),"%s,%s",cpu->getRegisterName(0,rt),addressSymbol.c_str());
|
||||
|
@ -994,7 +1007,7 @@ void DisassemblyData::createLines()
|
|||
case DATATYPE_WORD:
|
||||
{
|
||||
value = memRead32(pos);
|
||||
const std::string label = cpu->GetSymbolMap().GetLabelName(value);
|
||||
const std::string label = cpu->GetSymbolGuardian().SymbolStartingAtAddress(value).name;
|
||||
if (!label.empty())
|
||||
std::snprintf(buffer,std::size(buffer),"%s",label.c_str());
|
||||
else
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "SymbolMap.h"
|
||||
#include "SymbolGuardian.h"
|
||||
#include "common/Threading.h"
|
||||
#include "common/Pcsx2Types.h"
|
||||
#include "DebugInterface.h"
|
||||
|
@ -94,7 +94,6 @@ private:
|
|||
int num;
|
||||
};
|
||||
|
||||
|
||||
class DisassemblyMacro: public DisassemblyEntry
|
||||
{
|
||||
public:
|
||||
|
@ -124,6 +123,14 @@ private:
|
|||
int dataSize;
|
||||
};
|
||||
|
||||
enum DataType
|
||||
{
|
||||
DATATYPE_NONE,
|
||||
DATATYPE_BYTE,
|
||||
DATATYPE_HALFWORD,
|
||||
DATATYPE_WORD,
|
||||
DATATYPE_ASCII
|
||||
};
|
||||
|
||||
class DisassemblyData: public DisassemblyEntry
|
||||
{
|
||||
|
|
|
@ -4,13 +4,10 @@
|
|||
#include "MIPSAnalyst.h"
|
||||
#include "Debug.h"
|
||||
#include "DebugInterface.h"
|
||||
#include "SymbolMap.h"
|
||||
#include "DebugInterface.h"
|
||||
#include "R5900.h"
|
||||
#include "R5900OpcodeTables.h"
|
||||
|
||||
static std::vector<MIPSAnalyst::AnalyzedFunction> functions;
|
||||
|
||||
#define MIPS_MAKE_J(addr) (0x08000000 | ((addr)>>2))
|
||||
#define MIPS_MAKE_JAL(addr) (0x0C000000 | ((addr)>>2))
|
||||
#define MIPS_MAKE_JR_RA() (0x03e00008)
|
||||
|
@ -117,12 +114,6 @@ namespace MIPSAnalyst
|
|||
return INVALIDTARGET;
|
||||
}
|
||||
|
||||
static const char *DefaultFunctionName(char buffer[256], u32 startAddr) {
|
||||
std::snprintf(buffer, 256, "z_un_%08x", startAddr);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
|
||||
static u32 ScanAheadForJumpback(u32 fromAddr, u32 knownStart, u32 knownEnd) {
|
||||
static const u32 MAX_AHEAD_SCAN = 0x1000;
|
||||
// Maybe a bit high... just to make sure we don't get confused by recursive tail recursion.
|
||||
|
@ -183,7 +174,8 @@ namespace MIPSAnalyst
|
|||
return furthestJumpbackAddr;
|
||||
}
|
||||
|
||||
void ScanForFunctions(SymbolMap& map, u32 startAddr, u32 endAddr, bool insertSymbols) {
|
||||
void ScanForFunctions(ccc::SymbolDatabase& database, u32 startAddr, u32 endAddr) {
|
||||
std::vector<MIPSAnalyst::AnalyzedFunction> functions;
|
||||
AnalyzedFunction currentFunction = {startAddr};
|
||||
|
||||
u32 furthestBranch = 0;
|
||||
|
@ -192,19 +184,14 @@ namespace MIPSAnalyst
|
|||
bool isStraightLeaf = true;
|
||||
bool suspectedNoReturn = false;
|
||||
|
||||
functions.clear();
|
||||
|
||||
u32 addr;
|
||||
for (addr = startAddr; addr <= endAddr; addr += 4) {
|
||||
// Use pre-existing symbol map info if available. May be more reliable.
|
||||
SymbolInfo syminfo;
|
||||
if (map.GetSymbolInfo(&syminfo, addr, ST_FUNCTION)) {
|
||||
addr = syminfo.address + syminfo.size - 4;
|
||||
ccc::FunctionHandle existing_symbol_handle = database.functions.first_handle_from_starting_address(addr);
|
||||
const ccc::Function* existing_symbol = database.functions.symbol_from_handle(existing_symbol_handle);
|
||||
|
||||
// We still need to insert the func for hashing purposes.
|
||||
currentFunction.start = syminfo.address;
|
||||
currentFunction.end = syminfo.address + syminfo.size - 4;
|
||||
functions.push_back(currentFunction);
|
||||
if (existing_symbol && existing_symbol->address().valid() && existing_symbol->size() > 0) {
|
||||
addr = existing_symbol->address().value + existing_symbol->size() - 4;
|
||||
currentFunction.start = addr + 4;
|
||||
furthestBranch = 0;
|
||||
looking = false;
|
||||
|
@ -289,10 +276,18 @@ namespace MIPSAnalyst
|
|||
}
|
||||
}
|
||||
|
||||
// Prevent functions from being generated that overlap with existing
|
||||
// symbols. This is mainly a problem with symbols from SNDLL symbol
|
||||
// tables as they will have a size of zero.
|
||||
ccc::FunctionHandle next_symbol_handle = database.functions.first_handle_from_starting_address(addr+8);
|
||||
const ccc::Function* next_symbol = database.functions.symbol_from_handle(next_symbol_handle);
|
||||
end |= next_symbol != nullptr;
|
||||
|
||||
if (end) {
|
||||
// most functions are aligned to 8 or 16 bytes
|
||||
// add the padding to this one
|
||||
while (((addr+8) % 16) && r5900Debug.read32(addr+8) == 0)
|
||||
// Most functions are aligned to 8 or 16 bytes, so add padding
|
||||
// to this one unless a symbol exists implying a new function
|
||||
// follows immediately.
|
||||
while (next_symbol == nullptr && ((addr+8) % 16) && r5900Debug.read32(addr+8) == 0)
|
||||
addr += 4;
|
||||
|
||||
currentFunction.end = addr + 4;
|
||||
|
@ -313,13 +308,45 @@ namespace MIPSAnalyst
|
|||
currentFunction.end = addr + 4;
|
||||
functions.push_back(currentFunction);
|
||||
|
||||
for (auto iter = functions.begin(); iter != functions.end(); iter++) {
|
||||
iter->size = iter->end - iter->start + 4;
|
||||
if (insertSymbols) {
|
||||
char temp[256];
|
||||
map.AddFunction(DefaultFunctionName(temp, iter->start), iter->start, iter->end - iter->start + 4, iter->suspectedNoReturn);
|
||||
ccc::Result<ccc::SymbolSourceHandle> source = database.get_symbol_source("Analysis");
|
||||
if(!source->valid())
|
||||
return;
|
||||
|
||||
for (const AnalyzedFunction& function : functions) {
|
||||
ccc::FunctionHandle handle = database.functions.first_handle_from_starting_address(function.start);
|
||||
ccc::Function* symbol = database.functions.symbol_from_handle(handle);
|
||||
|
||||
if (!symbol) {
|
||||
std::string name;
|
||||
|
||||
// The SNDLL importer may create label symbols for functions if
|
||||
// they're not in a section named ".text" since it can't
|
||||
// otherwise distinguish between functions and globals.
|
||||
for (auto [address, handle] : database.labels.handles_from_starting_address(function.start)) {
|
||||
ccc::Label* label = database.labels.symbol_from_handle(handle);
|
||||
if (label && !label->is_junk) {
|
||||
name = label->name();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (name.empty()) {
|
||||
name = StringUtil::StdStringFromFormat("z_un_%08x", function.start);
|
||||
}
|
||||
|
||||
ccc::Result<ccc::Function*> symbol_result = database.functions.create_symbol(
|
||||
std::move(name), function.start, *source, nullptr);
|
||||
if (!symbol_result.success())
|
||||
return;
|
||||
symbol = *symbol_result;
|
||||
}
|
||||
|
||||
if (symbol->size() == 0) {
|
||||
symbol->set_size(function.end - function.start + 4);
|
||||
}
|
||||
|
||||
symbol->is_no_return = function.suspectedNoReturn;
|
||||
}
|
||||
}
|
||||
|
||||
MipsOpcodeInfo GetOpcodeInfo(DebugInterface* cpu, u32 address) {
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "SymbolMap.h"
|
||||
#include "SymbolGuardian.h"
|
||||
|
||||
class DebugInterface;
|
||||
|
||||
|
@ -22,7 +22,6 @@ namespace MIPSAnalyst
|
|||
u32 start;
|
||||
u32 end;
|
||||
u64 hash;
|
||||
u32 size;
|
||||
bool isStraightLeaf;
|
||||
bool hasHash;
|
||||
bool suspectedNoReturn;
|
||||
|
@ -30,7 +29,7 @@ namespace MIPSAnalyst
|
|||
char name[64];
|
||||
};
|
||||
|
||||
void ScanForFunctions(SymbolMap& map, u32 startAddr, u32 endAddr, bool insertSymbols);
|
||||
void ScanForFunctions(ccc::SymbolDatabase& database, u32 startAddr, u32 endAddr);
|
||||
|
||||
enum LoadStoreLRType { LOADSTORE_NORMAL, LOADSTORE_LEFT, LOADSTORE_RIGHT };
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
|
||||
#include "MipsStackWalk.h"
|
||||
#include "SymbolMap.h"
|
||||
#include "SymbolGuardian.h"
|
||||
#include "MIPSAnalyst.h"
|
||||
#include "DebugInterface.h"
|
||||
#include "R5900OpcodeTables.h"
|
||||
|
@ -26,12 +26,11 @@ namespace MipsStackWalk
|
|||
|
||||
static u32 GuessEntry(DebugInterface* cpu, u32 pc)
|
||||
{
|
||||
SymbolInfo info;
|
||||
if (cpu->GetSymbolMap().GetSymbolInfo(&info, pc))
|
||||
{
|
||||
return info.address;
|
||||
}
|
||||
FunctionInfo function = cpu->GetSymbolGuardian().FunctionOverlappingAddress(pc);
|
||||
if (!function.address.valid())
|
||||
return INVALIDTARGET;
|
||||
|
||||
return function.address.value;
|
||||
}
|
||||
|
||||
bool IsSWInstr(const R5900::OPCODE& op)
|
||||
|
|
|
@ -0,0 +1,560 @@
|
|||
// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
#include "SymbolGuardian.h"
|
||||
|
||||
#include <ccc/ast.h>
|
||||
#include <ccc/elf.h>
|
||||
#include <ccc/importer_flags.h>
|
||||
#include <ccc/symbol_file.h>
|
||||
|
||||
#include "common/Console.h"
|
||||
#include "common/FileSystem.h"
|
||||
#include "common/StringUtil.h"
|
||||
#include "common/Threading.h"
|
||||
|
||||
#include "DebugInterface.h"
|
||||
#include "MIPSAnalyst.h"
|
||||
#include "Host.h"
|
||||
#include "VMManager.h"
|
||||
|
||||
SymbolGuardian R5900SymbolGuardian;
|
||||
SymbolGuardian R3000SymbolGuardian;
|
||||
|
||||
struct DefaultBuiltInType
|
||||
{
|
||||
const char* name;
|
||||
ccc::ast::BuiltInClass bclass;
|
||||
};
|
||||
|
||||
static const std::vector<DefaultBuiltInType> DEFAULT_BUILT_IN_TYPES = {
|
||||
{"char", ccc::ast::BuiltInClass::UNQUALIFIED_8},
|
||||
{"signed char", ccc::ast::BuiltInClass::SIGNED_8},
|
||||
{"unsigned char", ccc::ast::BuiltInClass::UNSIGNED_8},
|
||||
{"short", ccc::ast::BuiltInClass::SIGNED_16},
|
||||
{"unsigned short", ccc::ast::BuiltInClass::UNSIGNED_16},
|
||||
{"int", ccc::ast::BuiltInClass::SIGNED_32},
|
||||
{"unsigned int", ccc::ast::BuiltInClass::UNSIGNED_32},
|
||||
{"unsigned", ccc::ast::BuiltInClass::UNSIGNED_32},
|
||||
{"long", ccc::ast::BuiltInClass::SIGNED_64},
|
||||
{"unsigned long", ccc::ast::BuiltInClass::UNSIGNED_64},
|
||||
{"long long", ccc::ast::BuiltInClass::SIGNED_64},
|
||||
{"unsigned long long", ccc::ast::BuiltInClass::UNSIGNED_64},
|
||||
{"bool", ccc::ast::BuiltInClass::BOOL_8},
|
||||
{"float", ccc::ast::BuiltInClass::FLOAT_32},
|
||||
{"double", ccc::ast::BuiltInClass::FLOAT_64},
|
||||
{"void", ccc::ast::BuiltInClass::VOID_TYPE},
|
||||
{"s8", ccc::ast::BuiltInClass::SIGNED_8},
|
||||
{"u8", ccc::ast::BuiltInClass::UNSIGNED_8},
|
||||
{"s16", ccc::ast::BuiltInClass::SIGNED_16},
|
||||
{"u16", ccc::ast::BuiltInClass::UNSIGNED_16},
|
||||
{"s32", ccc::ast::BuiltInClass::SIGNED_32},
|
||||
{"u32", ccc::ast::BuiltInClass::UNSIGNED_32},
|
||||
{"s64", ccc::ast::BuiltInClass::SIGNED_64},
|
||||
{"u64", ccc::ast::BuiltInClass::UNSIGNED_64},
|
||||
{"s128", ccc::ast::BuiltInClass::SIGNED_128},
|
||||
{"u128", ccc::ast::BuiltInClass::UNSIGNED_128},
|
||||
{"f32", ccc::ast::BuiltInClass::FLOAT_32},
|
||||
{"f64", ccc::ast::BuiltInClass::FLOAT_64},
|
||||
};
|
||||
|
||||
static void error_callback(const ccc::Error& error, ccc::ErrorLevel level)
|
||||
{
|
||||
switch (level)
|
||||
{
|
||||
case ccc::ERROR_LEVEL_ERROR:
|
||||
Console.Error("Error while importing symbol table: %s", error.message.c_str());
|
||||
break;
|
||||
case ccc::ERROR_LEVEL_WARNING:
|
||||
Console.Warning("Warning while importing symbol table: %s", error.message.c_str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
SymbolGuardian::SymbolGuardian()
|
||||
{
|
||||
ccc::set_custom_error_callback(error_callback);
|
||||
}
|
||||
|
||||
SymbolGuardian::~SymbolGuardian()
|
||||
{
|
||||
}
|
||||
|
||||
void SymbolGuardian::Read(ReadCallback callback) const noexcept
|
||||
{
|
||||
m_big_symbol_lock.lock_shared();
|
||||
callback(m_database);
|
||||
m_big_symbol_lock.unlock_shared();
|
||||
}
|
||||
|
||||
void SymbolGuardian::ReadWrite(ReadWriteCallback callback) noexcept
|
||||
{
|
||||
m_big_symbol_lock.lock();
|
||||
callback(m_database);
|
||||
m_big_symbol_lock.unlock();
|
||||
}
|
||||
|
||||
void SymbolGuardian::Reset()
|
||||
{
|
||||
ShutdownWorkerThread();
|
||||
|
||||
ReadWrite([&](ccc::SymbolDatabase& database) {
|
||||
database.clear();
|
||||
|
||||
ccc::Result<ccc::SymbolSourceHandle> source = database.get_symbol_source("Built-in");
|
||||
if (!source.success())
|
||||
return;
|
||||
|
||||
// Create some built-in data type symbols so that users still have some
|
||||
// types to use even if there isn't a symbol table loaded. Maybe in the
|
||||
// future we could add PS2-specific types like DMA tags here too.
|
||||
for (const DefaultBuiltInType& default_type : DEFAULT_BUILT_IN_TYPES)
|
||||
{
|
||||
ccc::Result<ccc::DataType*> symbol = database.data_types.create_symbol(default_type.name, *source, nullptr);
|
||||
if (!symbol.success())
|
||||
return;
|
||||
|
||||
std::unique_ptr<ccc::ast::BuiltIn> type = std::make_unique<ccc::ast::BuiltIn>();
|
||||
type->name = default_type.name;
|
||||
type->size_bytes = ccc::ast::builtin_class_size(default_type.bclass);
|
||||
type->bclass = default_type.bclass;
|
||||
(*symbol)->set_type(std::move(type));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static void CreateBuiltInDataType(
|
||||
ccc::SymbolDatabase& database, ccc::SymbolSourceHandle source, const char* name, ccc::ast::BuiltInClass bclass)
|
||||
{
|
||||
ccc::Result<ccc::DataType*> symbol = database.data_types.create_symbol(name, source, nullptr);
|
||||
if (!symbol.success())
|
||||
return;
|
||||
|
||||
std::unique_ptr<ccc::ast::BuiltIn> type = std::make_unique<ccc::ast::BuiltIn>();
|
||||
type->name = name;
|
||||
type->size_bytes = ccc::ast::builtin_class_size(bclass);
|
||||
type->bclass = bclass;
|
||||
(*symbol)->set_type(std::move(type));
|
||||
}
|
||||
|
||||
void SymbolGuardian::ImportElf(std::vector<u8> elf, std::string elf_file_name, const std::string& nocash_path)
|
||||
{
|
||||
ccc::Result<ccc::ElfFile> parsed_elf = ccc::ElfFile::parse(std::move(elf));
|
||||
if (!parsed_elf.success())
|
||||
{
|
||||
ccc::report_error(parsed_elf.error());
|
||||
return;
|
||||
}
|
||||
|
||||
std::unique_ptr<ccc::ElfSymbolFile> symbol_file =
|
||||
std::make_unique<ccc::ElfSymbolFile>(std::move(*parsed_elf), std::move(elf_file_name));
|
||||
|
||||
ShutdownWorkerThread();
|
||||
|
||||
m_import_thread = std::thread([this, nocash_path, file = std::move(symbol_file)]() {
|
||||
Threading::SetNameOfCurrentThread("Symbol Worker");
|
||||
|
||||
ccc::SymbolDatabase temp_database;
|
||||
ImportSymbolTables(temp_database, *file, &m_interrupt_import_thread);
|
||||
|
||||
if (m_interrupt_import_thread)
|
||||
return;
|
||||
|
||||
ImportNocashSymbols(temp_database, nocash_path);
|
||||
|
||||
if (m_interrupt_import_thread)
|
||||
return;
|
||||
|
||||
ComputeOriginalFunctionHashes(temp_database, file->elf());
|
||||
|
||||
if (m_interrupt_import_thread)
|
||||
return;
|
||||
|
||||
// Wait for the ELF to be loaded into memory, otherwise the call to
|
||||
// ScanForFunctions below won't work.
|
||||
while (true)
|
||||
{
|
||||
bool has_booted_elf = false;
|
||||
Host::RunOnCPUThread([&]() {
|
||||
has_booted_elf = VMManager::Internal::HasBootedELF();
|
||||
},
|
||||
true);
|
||||
|
||||
if (has_booted_elf)
|
||||
break;
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
|
||||
if (m_interrupt_import_thread)
|
||||
return;
|
||||
}
|
||||
|
||||
Host::RunOnCPUThread([this, &temp_database, &file, nocash_path]() {
|
||||
ReadWrite([&](ccc::SymbolDatabase& database) {
|
||||
database.merge_from(temp_database);
|
||||
|
||||
const ccc::ElfProgramHeader* entry_segment = file->elf().entry_point_segment();
|
||||
if (entry_segment)
|
||||
MIPSAnalyst::ScanForFunctions(database, entry_segment->vaddr, entry_segment->vaddr + entry_segment->filesz);
|
||||
});
|
||||
},
|
||||
true);
|
||||
});
|
||||
}
|
||||
|
||||
void SymbolGuardian::ShutdownWorkerThread()
|
||||
{
|
||||
m_interrupt_import_thread = true;
|
||||
if (m_import_thread.joinable())
|
||||
m_import_thread.join();
|
||||
m_interrupt_import_thread = false;
|
||||
}
|
||||
|
||||
|
||||
ccc::ModuleHandle SymbolGuardian::ImportSymbolTables(
|
||||
ccc::SymbolDatabase& database, const ccc::SymbolFile& symbol_file, const std::atomic_bool* interrupt)
|
||||
{
|
||||
ccc::Result<std::vector<std::unique_ptr<ccc::SymbolTable>>> symbol_tables = symbol_file.get_all_symbol_tables();
|
||||
if (!symbol_tables.success())
|
||||
{
|
||||
ccc::report_error(symbol_tables.error());
|
||||
return ccc::ModuleHandle();
|
||||
}
|
||||
|
||||
ccc::DemanglerFunctions demangler;
|
||||
|
||||
u32 importer_flags =
|
||||
ccc::DEMANGLE_PARAMETERS |
|
||||
ccc::DEMANGLE_RETURN_TYPE |
|
||||
ccc::NO_MEMBER_FUNCTIONS |
|
||||
ccc::NO_OPTIMIZED_OUT_FUNCTIONS |
|
||||
ccc::UNIQUE_FUNCTIONS;
|
||||
|
||||
ccc::Result<ccc::ModuleHandle> module_handle = ccc::import_symbol_tables(
|
||||
database, symbol_file.name(), *symbol_tables, importer_flags, demangler, interrupt);
|
||||
if (!module_handle.success())
|
||||
{
|
||||
ccc::report_error(module_handle.error());
|
||||
return ccc::ModuleHandle();
|
||||
}
|
||||
|
||||
Console.WriteLn("Imported %d symbols.", database.symbol_count());
|
||||
|
||||
return *module_handle;
|
||||
}
|
||||
|
||||
bool SymbolGuardian::ImportNocashSymbols(ccc::SymbolDatabase& database, const std::string& file_name)
|
||||
{
|
||||
ccc::Result<ccc::SymbolSourceHandle> source = database.get_symbol_source("Nocash Symbols");
|
||||
if (!source.success())
|
||||
return false;
|
||||
|
||||
FILE* f = FileSystem::OpenCFile(file_name.c_str(), "r");
|
||||
if (!f)
|
||||
return false;
|
||||
|
||||
while (!feof(f))
|
||||
{
|
||||
char line[256], value[256] = {0};
|
||||
char* p = fgets(line, 256, f);
|
||||
if (p == NULL)
|
||||
break;
|
||||
|
||||
if (char* end = strchr(line, '\n'))
|
||||
*end = '\0';
|
||||
|
||||
u32 address;
|
||||
if (sscanf(line, "%08x %255s", &address, value) != 2)
|
||||
continue;
|
||||
if (address == 0 && strcmp(value, "0") == 0)
|
||||
continue;
|
||||
|
||||
if (value[0] == '.')
|
||||
{
|
||||
// data directives
|
||||
char* s = strchr(value, ':');
|
||||
if (s != NULL)
|
||||
{
|
||||
*s = 0;
|
||||
|
||||
u32 size = 0;
|
||||
if (sscanf(s + 1, "%04x", &size) != 1)
|
||||
continue;
|
||||
|
||||
std::unique_ptr<ccc::ast::BuiltIn> scalar_type = std::make_unique<ccc::ast::BuiltIn>();
|
||||
if (StringUtil::Strcasecmp(value, ".byt") == 0)
|
||||
{
|
||||
scalar_type->size_bytes = 1;
|
||||
scalar_type->bclass = ccc::ast::BuiltInClass::UNSIGNED_8;
|
||||
}
|
||||
else if (StringUtil::Strcasecmp(value, ".wrd") == 0)
|
||||
{
|
||||
scalar_type->size_bytes = 2;
|
||||
scalar_type->bclass = ccc::ast::BuiltInClass::UNSIGNED_16;
|
||||
}
|
||||
else if (StringUtil::Strcasecmp(value, ".dbl") == 0)
|
||||
{
|
||||
scalar_type->size_bytes = 4;
|
||||
scalar_type->bclass = ccc::ast::BuiltInClass::UNSIGNED_32;
|
||||
}
|
||||
else if (StringUtil::Strcasecmp(value, ".asc") == 0)
|
||||
{
|
||||
scalar_type->size_bytes = 1;
|
||||
scalar_type->bclass = ccc::ast::BuiltInClass::UNQUALIFIED_8;
|
||||
}
|
||||
else
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ccc::Result<ccc::GlobalVariable*> global_variable = database.global_variables.create_symbol(
|
||||
line, address, *source, nullptr);
|
||||
if (!global_variable.success())
|
||||
{
|
||||
fclose(f);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (scalar_type->size_bytes == (s32)size)
|
||||
{
|
||||
(*global_variable)->set_type(std::move(scalar_type));
|
||||
}
|
||||
else
|
||||
{
|
||||
std::unique_ptr<ccc::ast::Array> array = std::make_unique<ccc::ast::Array>();
|
||||
array->size_bytes = (s32)size;
|
||||
array->element_type = std::move(scalar_type);
|
||||
array->element_count = size / array->element_type->size_bytes;
|
||||
(*global_variable)->set_type(std::move(array));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // labels
|
||||
u32 size = 1;
|
||||
char* seperator = strchr(value, ',');
|
||||
if (seperator != NULL)
|
||||
{
|
||||
*seperator = 0;
|
||||
sscanf(seperator + 1, "%08x", &size);
|
||||
}
|
||||
|
||||
if (size != 1)
|
||||
{
|
||||
ccc::Result<ccc::Function*> function = database.functions.create_symbol(value, address, *source, nullptr);
|
||||
if (!function.success())
|
||||
{
|
||||
fclose(f);
|
||||
return false;
|
||||
}
|
||||
|
||||
(*function)->set_size(size);
|
||||
}
|
||||
else
|
||||
{
|
||||
ccc::Result<ccc::Label*> label = database.labels.create_symbol(value, address, *source, nullptr);
|
||||
if (!label.success())
|
||||
{
|
||||
fclose(f);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
return true;
|
||||
}
|
||||
|
||||
void SymbolGuardian::ComputeOriginalFunctionHashes(ccc::SymbolDatabase& database, const ccc::ElfFile& elf)
|
||||
{
|
||||
for (ccc::Function& function : database.functions)
|
||||
{
|
||||
if (!function.address().valid())
|
||||
continue;
|
||||
|
||||
if (function.size() == 0)
|
||||
continue;
|
||||
|
||||
ccc::Result<std::span<const u32>> text = elf.get_array_virtual<u32>(
|
||||
function.address().value, function.size() / 4);
|
||||
if (!text.success())
|
||||
{
|
||||
ccc::report_warning(text.error());
|
||||
break;
|
||||
}
|
||||
|
||||
ccc::FunctionHash hash;
|
||||
for (u32 instruction : *text)
|
||||
hash.update(instruction);
|
||||
|
||||
function.set_original_hash(hash.get());
|
||||
}
|
||||
}
|
||||
|
||||
void SymbolGuardian::UpdateFunctionHashes(DebugInterface& cpu)
|
||||
{
|
||||
ReadWrite([&](ccc::SymbolDatabase& database) {
|
||||
for (ccc::Function& function : database.functions)
|
||||
{
|
||||
if (!function.address().valid())
|
||||
continue;
|
||||
|
||||
if (function.size() == 0)
|
||||
continue;
|
||||
|
||||
ccc::FunctionHash hash;
|
||||
for (u32 i = 0; i < function.size() / 4; i++)
|
||||
hash.update(cpu.read32(function.address().value + i * 4));
|
||||
|
||||
function.set_current_hash(hash);
|
||||
}
|
||||
|
||||
for (ccc::SourceFile& source_file : database.source_files)
|
||||
source_file.check_functions_match(database);
|
||||
});
|
||||
}
|
||||
|
||||
void SymbolGuardian::ClearIrxModules()
|
||||
{
|
||||
ReadWrite([&](ccc::SymbolDatabase& database) {
|
||||
std::vector<ccc::ModuleHandle> irx_modules;
|
||||
for (const ccc::Module& module : m_database.modules)
|
||||
if (module.is_irx)
|
||||
irx_modules.emplace_back(module.handle());
|
||||
|
||||
for (ccc::ModuleHandle module : irx_modules)
|
||||
m_database.destroy_symbols_from_module(module, false);
|
||||
});
|
||||
}
|
||||
|
||||
SymbolInfo SymbolGuardian::SymbolStartingAtAddress(
|
||||
u32 address, u32 descriptors) const
|
||||
{
|
||||
SymbolInfo info;
|
||||
Read([&](const ccc::SymbolDatabase& database) {
|
||||
ccc::SymbolDescriptor descriptor;
|
||||
const ccc::Symbol* symbol = database.symbol_starting_at_address(address, descriptors, &descriptor);
|
||||
if (!symbol)
|
||||
return;
|
||||
|
||||
info.descriptor = descriptor;
|
||||
info.handle = symbol->raw_handle();
|
||||
info.name = symbol->name();
|
||||
info.address = symbol->address();
|
||||
info.size = symbol->size();
|
||||
});
|
||||
return info;
|
||||
}
|
||||
|
||||
SymbolInfo SymbolGuardian::SymbolAfterAddress(
|
||||
u32 address, u32 descriptors) const
|
||||
{
|
||||
SymbolInfo info;
|
||||
Read([&](const ccc::SymbolDatabase& database) {
|
||||
ccc::SymbolDescriptor descriptor;
|
||||
const ccc::Symbol* symbol = database.symbol_after_address(address, descriptors, &descriptor);
|
||||
if (!symbol)
|
||||
return;
|
||||
|
||||
info.descriptor = descriptor;
|
||||
info.handle = symbol->raw_handle();
|
||||
info.name = symbol->name();
|
||||
info.address = symbol->address();
|
||||
info.size = symbol->size();
|
||||
});
|
||||
return info;
|
||||
}
|
||||
|
||||
SymbolInfo SymbolGuardian::SymbolOverlappingAddress(
|
||||
u32 address, u32 descriptors) const
|
||||
{
|
||||
SymbolInfo info;
|
||||
Read([&](const ccc::SymbolDatabase& database) {
|
||||
ccc::SymbolDescriptor descriptor;
|
||||
const ccc::Symbol* symbol = database.symbol_overlapping_address(address, descriptors, &descriptor);
|
||||
if (!symbol)
|
||||
return;
|
||||
|
||||
info.descriptor = descriptor;
|
||||
info.handle = symbol->raw_handle();
|
||||
info.name = symbol->name();
|
||||
info.address = symbol->address();
|
||||
info.size = symbol->size();
|
||||
});
|
||||
return info;
|
||||
}
|
||||
|
||||
SymbolInfo SymbolGuardian::SymbolWithName(
|
||||
const std::string& name, u32 descriptors) const
|
||||
{
|
||||
SymbolInfo info;
|
||||
Read([&](const ccc::SymbolDatabase& database) {
|
||||
ccc::SymbolDescriptor descriptor;
|
||||
const ccc::Symbol* symbol = database.symbol_with_name(name, descriptors, &descriptor);
|
||||
if (!symbol)
|
||||
return;
|
||||
|
||||
info.descriptor = descriptor;
|
||||
info.handle = symbol->raw_handle();
|
||||
info.name = symbol->name();
|
||||
info.address = symbol->address();
|
||||
info.size = symbol->size();
|
||||
});
|
||||
return info;
|
||||
}
|
||||
|
||||
bool SymbolGuardian::FunctionExistsWithStartingAddress(u32 address) const
|
||||
{
|
||||
bool exists = false;
|
||||
Read([&](const ccc::SymbolDatabase& database) {
|
||||
ccc::FunctionHandle handle = database.functions.first_handle_from_starting_address(address);
|
||||
exists = handle.valid();
|
||||
});
|
||||
return exists;
|
||||
}
|
||||
|
||||
bool SymbolGuardian::FunctionExistsThatOverlapsAddress(u32 address) const
|
||||
{
|
||||
bool exists = false;
|
||||
Read([&](const ccc::SymbolDatabase& database) {
|
||||
const ccc::Function* function = database.functions.symbol_overlapping_address(address);
|
||||
exists = function != nullptr;
|
||||
});
|
||||
return exists;
|
||||
}
|
||||
|
||||
FunctionInfo SymbolGuardian::FunctionStartingAtAddress(u32 address) const
|
||||
{
|
||||
FunctionInfo info;
|
||||
Read([&](const ccc::SymbolDatabase& database) {
|
||||
ccc::FunctionHandle handle = database.functions.first_handle_from_starting_address(address);
|
||||
const ccc::Function* function = database.functions.symbol_from_handle(handle);
|
||||
if (!function)
|
||||
return;
|
||||
|
||||
info.handle = function->handle();
|
||||
info.name = function->name();
|
||||
info.address = function->address();
|
||||
info.size = function->size();
|
||||
info.is_no_return = function->is_no_return;
|
||||
});
|
||||
return info;
|
||||
}
|
||||
|
||||
FunctionInfo SymbolGuardian::FunctionOverlappingAddress(u32 address) const
|
||||
{
|
||||
FunctionInfo info;
|
||||
Read([&](const ccc::SymbolDatabase& database) {
|
||||
const ccc::Function* function = database.functions.symbol_overlapping_address(address);
|
||||
if (!function)
|
||||
return;
|
||||
|
||||
info.handle = function->handle();
|
||||
info.name = function->name();
|
||||
info.address = function->address();
|
||||
info.size = function->size();
|
||||
info.is_no_return = function->is_no_return;
|
||||
});
|
||||
return info;
|
||||
}
|
|
@ -0,0 +1,112 @@
|
|||
// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <queue>
|
||||
#include <atomic>
|
||||
#include <thread>
|
||||
#include <functional>
|
||||
#include <shared_mutex>
|
||||
|
||||
#include <ccc/symbol_database.h>
|
||||
#include <ccc/symbol_file.h>
|
||||
|
||||
#include "common/Pcsx2Types.h"
|
||||
|
||||
class DebugInterface;
|
||||
|
||||
struct SymbolInfo
|
||||
{
|
||||
std::optional<ccc::SymbolDescriptor> descriptor;
|
||||
u32 handle = (u32)-1;
|
||||
std::string name;
|
||||
ccc::Address address;
|
||||
u32 size = 0;
|
||||
};
|
||||
|
||||
struct FunctionInfo
|
||||
{
|
||||
ccc::FunctionHandle handle;
|
||||
std::string name;
|
||||
ccc::Address address;
|
||||
u32 size = 0;
|
||||
bool is_no_return = false;
|
||||
};
|
||||
|
||||
struct SymbolGuardian
|
||||
{
|
||||
public:
|
||||
SymbolGuardian();
|
||||
SymbolGuardian(const SymbolGuardian& rhs) = delete;
|
||||
SymbolGuardian(SymbolGuardian&& rhs) = delete;
|
||||
~SymbolGuardian();
|
||||
SymbolGuardian& operator=(const SymbolGuardian& rhs) = delete;
|
||||
SymbolGuardian& operator=(SymbolGuardian&& rhs) = delete;
|
||||
|
||||
using ReadCallback = std::function<void(const ccc::SymbolDatabase&)>;
|
||||
using ReadWriteCallback = std::function<void(ccc::SymbolDatabase&)>;
|
||||
|
||||
// Take a shared lock on the symbol database and run the callback.
|
||||
void Read(ReadCallback callback) const noexcept;
|
||||
|
||||
// Take an exclusive lock on the symbol database and run the callback.
|
||||
void ReadWrite(ReadWriteCallback callback) noexcept;
|
||||
|
||||
// Delete all stored symbols and create some default built-ins. Should be
|
||||
// called from the CPU thread.
|
||||
void Reset();
|
||||
|
||||
// Import symbols from the ELF file, nocash symbols, and scan for functions.
|
||||
// Should be called from the CPU thread.
|
||||
void ImportElf(std::vector<u8> elf, std::string elf_file_name, const std::string& nocash_path);
|
||||
|
||||
// Interrupt the import thread. Should be called from the CPU thread.
|
||||
void ShutdownWorkerThread();
|
||||
|
||||
static ccc::ModuleHandle ImportSymbolTables(
|
||||
ccc::SymbolDatabase& database, const ccc::SymbolFile& symbol_file, const std::atomic_bool* interrupt);
|
||||
static bool ImportNocashSymbols(ccc::SymbolDatabase& database, const std::string& file_name);
|
||||
|
||||
// Compute original hashes for all the functions based on the code stored in
|
||||
// the ELF file.
|
||||
static void ComputeOriginalFunctionHashes(ccc::SymbolDatabase& database, const ccc::ElfFile& elf);
|
||||
|
||||
// Compute new hashes for all the functions to check if any of them have
|
||||
// been overwritten.
|
||||
void UpdateFunctionHashes(DebugInterface& cpu);
|
||||
|
||||
// Delete all symbols from modules that have the "is_irx" flag set.
|
||||
void ClearIrxModules();
|
||||
|
||||
// Copy commonly used attributes of a symbol into a temporary object.
|
||||
SymbolInfo SymbolStartingAtAddress(
|
||||
u32 address, u32 descriptors = ccc::ALL_SYMBOL_TYPES) const;
|
||||
SymbolInfo SymbolAfterAddress(
|
||||
u32 address, u32 descriptors = ccc::ALL_SYMBOL_TYPES) const;
|
||||
SymbolInfo SymbolOverlappingAddress(
|
||||
u32 address, u32 descriptors = ccc::ALL_SYMBOL_TYPES) const;
|
||||
SymbolInfo SymbolWithName(
|
||||
const std::string& name, u32 descriptors = ccc::ALL_SYMBOL_TYPES) const;
|
||||
|
||||
bool FunctionExistsWithStartingAddress(u32 address) const;
|
||||
bool FunctionExistsThatOverlapsAddress(u32 address) const;
|
||||
|
||||
// Copy commonly used attributes of a function so they can be used by the
|
||||
// calling thread without needing to keep the lock held.
|
||||
FunctionInfo FunctionStartingAtAddress(u32 address) const;
|
||||
FunctionInfo FunctionOverlappingAddress(u32 address) const;
|
||||
|
||||
protected:
|
||||
ccc::SymbolDatabase m_database;
|
||||
mutable std::shared_mutex m_big_symbol_lock;
|
||||
|
||||
std::thread m_import_thread;
|
||||
std::atomic_bool m_interrupt_import_thread = false;
|
||||
|
||||
std::queue<ccc::SymbolDatabase> m_load_queue;
|
||||
std::mutex m_load_queue_lock;
|
||||
};
|
||||
|
||||
extern SymbolGuardian R5900SymbolGuardian;
|
||||
extern SymbolGuardian R3000SymbolGuardian;
|
|
@ -1,595 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
#include "SymbolMap.h"
|
||||
|
||||
#include "common/FileSystem.h"
|
||||
#include "common/StringUtil.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
SymbolMap R5900SymbolMap;
|
||||
SymbolMap R3000SymbolMap;
|
||||
|
||||
void SymbolMap::SortSymbols()
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(m_lock);
|
||||
AssignFunctionIndices();
|
||||
}
|
||||
|
||||
void SymbolMap::Clear()
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(m_lock);
|
||||
functions.clear();
|
||||
labels.clear();
|
||||
data.clear();
|
||||
modules.clear();
|
||||
}
|
||||
|
||||
|
||||
bool SymbolMap::LoadNocashSym(const std::string& filename)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(m_lock);
|
||||
FILE* f = FileSystem::OpenCFile(filename.c_str(), "r");
|
||||
if (!f)
|
||||
return false;
|
||||
|
||||
while (!feof(f))
|
||||
{
|
||||
char line[256], value[256] = {0};
|
||||
char* p = fgets(line, 256, f);
|
||||
if (p == NULL)
|
||||
break;
|
||||
|
||||
u32 address;
|
||||
if (sscanf(line, "%08X %s", &address, value) != 2)
|
||||
continue;
|
||||
if (address == 0 && strcmp(value, "0") == 0)
|
||||
continue;
|
||||
|
||||
if (value[0] == '.')
|
||||
{
|
||||
// data directives
|
||||
char* s = strchr(value, ':');
|
||||
if (s != NULL)
|
||||
{
|
||||
*s = 0;
|
||||
|
||||
u32 size = 0;
|
||||
if (sscanf(s + 1, "%04X", &size) != 1)
|
||||
continue;
|
||||
|
||||
if (StringUtil::Strcasecmp(value, ".byt") == 0)
|
||||
{
|
||||
AddData(address, size, DATATYPE_BYTE, 0);
|
||||
}
|
||||
else if (StringUtil::Strcasecmp(value, ".wrd") == 0)
|
||||
{
|
||||
AddData(address, size, DATATYPE_HALFWORD, 0);
|
||||
}
|
||||
else if (StringUtil::Strcasecmp(value, ".dbl") == 0)
|
||||
{
|
||||
AddData(address, size, DATATYPE_WORD, 0);
|
||||
}
|
||||
else if (StringUtil::Strcasecmp(value, ".asc") == 0)
|
||||
{
|
||||
AddData(address, size, DATATYPE_ASCII, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // labels
|
||||
int size = 1;
|
||||
char* seperator = strchr(value, ',');
|
||||
if (seperator != NULL)
|
||||
{
|
||||
*seperator = 0;
|
||||
sscanf(seperator + 1, "%08X", &size);
|
||||
}
|
||||
|
||||
if (size != 1)
|
||||
{
|
||||
AddFunction(value, address, size);
|
||||
}
|
||||
else
|
||||
{
|
||||
AddLabel(value, address);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
return true;
|
||||
}
|
||||
|
||||
SymbolType SymbolMap::GetSymbolType(u32 address) const
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(m_lock);
|
||||
if (functions.find(address) != functions.end())
|
||||
return ST_FUNCTION;
|
||||
if (data.find(address) != data.end())
|
||||
return ST_DATA;
|
||||
return ST_NONE;
|
||||
}
|
||||
|
||||
bool SymbolMap::GetSymbolInfo(SymbolInfo* info, u32 address, SymbolType symmask) const
|
||||
{
|
||||
u32 functionAddress = INVALID_ADDRESS;
|
||||
u32 dataAddress = INVALID_ADDRESS;
|
||||
|
||||
if (symmask & ST_FUNCTION)
|
||||
functionAddress = GetFunctionStart(address);
|
||||
|
||||
if (symmask & ST_DATA)
|
||||
dataAddress = GetDataStart(address);
|
||||
|
||||
if (functionAddress == INVALID_ADDRESS || dataAddress == INVALID_ADDRESS)
|
||||
{
|
||||
if (functionAddress != INVALID_ADDRESS)
|
||||
{
|
||||
if (info != NULL)
|
||||
{
|
||||
info->type = ST_FUNCTION;
|
||||
info->address = functionAddress;
|
||||
info->size = GetFunctionSize(functionAddress);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (dataAddress != INVALID_ADDRESS)
|
||||
{
|
||||
if (info != NULL)
|
||||
{
|
||||
info->type = ST_DATA;
|
||||
info->address = dataAddress;
|
||||
info->size = GetDataSize(dataAddress);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// if both exist, return the function
|
||||
if (info != NULL)
|
||||
{
|
||||
info->type = ST_FUNCTION;
|
||||
info->address = functionAddress;
|
||||
info->size = GetFunctionSize(functionAddress);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
u32 SymbolMap::GetNextSymbolAddress(u32 address, SymbolType symmask)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(m_lock);
|
||||
const auto functionEntry = symmask & ST_FUNCTION ? functions.upper_bound(address) : functions.end();
|
||||
const auto dataEntry = symmask & ST_DATA ? data.upper_bound(address) : data.end();
|
||||
|
||||
if (functionEntry == functions.end() && dataEntry == data.end())
|
||||
return INVALID_ADDRESS;
|
||||
|
||||
u32 funcAddress = (functionEntry != functions.end()) ? functionEntry->first : 0xFFFFFFFF;
|
||||
u32 dataAddress = (dataEntry != data.end()) ? dataEntry->first : 0xFFFFFFFF;
|
||||
|
||||
if (funcAddress <= dataAddress)
|
||||
return funcAddress;
|
||||
else
|
||||
return dataAddress;
|
||||
}
|
||||
|
||||
std::string SymbolMap::GetDescription(unsigned int address) const
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(m_lock);
|
||||
std::string labelName;
|
||||
|
||||
u32 funcStart = GetFunctionStart(address);
|
||||
if (funcStart != INVALID_ADDRESS)
|
||||
{
|
||||
labelName = GetLabelName(funcStart);
|
||||
}
|
||||
else
|
||||
{
|
||||
u32 dataStart = GetDataStart(address);
|
||||
if (dataStart != INVALID_ADDRESS)
|
||||
labelName = GetLabelName(dataStart);
|
||||
}
|
||||
|
||||
if (!labelName.empty())
|
||||
return labelName;
|
||||
|
||||
char descriptionTemp[256];
|
||||
std::snprintf(descriptionTemp, std::size(descriptionTemp), "(%08x)", address);
|
||||
return descriptionTemp;
|
||||
}
|
||||
|
||||
std::vector<SymbolEntry> SymbolMap::GetAllSymbols(SymbolType symmask) const
|
||||
{
|
||||
std::vector<SymbolEntry> result;
|
||||
|
||||
if (symmask & ST_FUNCTION)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(m_lock);
|
||||
for (auto it = functions.begin(); it != functions.end(); it++)
|
||||
{
|
||||
SymbolEntry entry;
|
||||
entry.address = it->first;
|
||||
entry.size = GetFunctionSize(entry.address);
|
||||
entry.name = GetLabelName(entry.address);
|
||||
|
||||
result.push_back(entry);
|
||||
}
|
||||
}
|
||||
|
||||
if (symmask & ST_DATA)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(m_lock);
|
||||
for (auto it = data.begin(); it != data.end(); it++)
|
||||
{
|
||||
SymbolEntry entry;
|
||||
entry.address = it->first;
|
||||
entry.size = GetDataSize(entry.address);
|
||||
entry.name = GetLabelName(entry.address);
|
||||
|
||||
result.push_back(entry);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void SymbolMap::AddFunction(const std::string& name, u32 address, u32 size, bool noReturn)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(m_lock);
|
||||
|
||||
auto existing = functions.find(address);
|
||||
|
||||
if (existing != functions.end())
|
||||
{
|
||||
existing->second.size = size;
|
||||
}
|
||||
else
|
||||
{
|
||||
FunctionEntry func;
|
||||
func.start = address;
|
||||
func.size = size;
|
||||
func.index = (int)functions.size();
|
||||
func.name = name;
|
||||
func.noReturn = noReturn;
|
||||
functions[address] = func;
|
||||
|
||||
functions.insert(std::make_pair(address, func));
|
||||
}
|
||||
|
||||
AddLabel(name, address);
|
||||
}
|
||||
|
||||
u32 SymbolMap::GetFunctionStart(u32 address) const
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(m_lock);
|
||||
auto it = functions.upper_bound(address);
|
||||
if (it == functions.end())
|
||||
{
|
||||
// check last element
|
||||
auto rit = functions.rbegin();
|
||||
if (rit != functions.rend())
|
||||
{
|
||||
u32 start = rit->first;
|
||||
u32 size = rit->second.size;
|
||||
if (start <= address && start + size > address)
|
||||
return start;
|
||||
}
|
||||
// otherwise there's no function that contains this address
|
||||
return INVALID_ADDRESS;
|
||||
}
|
||||
|
||||
if (it != functions.begin())
|
||||
{
|
||||
it--;
|
||||
u32 start = it->first;
|
||||
u32 size = it->second.size;
|
||||
if (start <= address && start + size > address)
|
||||
return start;
|
||||
}
|
||||
|
||||
return INVALID_ADDRESS;
|
||||
}
|
||||
|
||||
u32 SymbolMap::GetFunctionSize(u32 startAddress) const
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(m_lock);
|
||||
auto it = functions.find(startAddress);
|
||||
if (it == functions.end())
|
||||
return INVALID_ADDRESS;
|
||||
|
||||
return it->second.size;
|
||||
}
|
||||
|
||||
int SymbolMap::GetFunctionNum(u32 address) const
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(m_lock);
|
||||
u32 start = GetFunctionStart(address);
|
||||
if (start == INVALID_ADDRESS)
|
||||
return INVALID_ADDRESS;
|
||||
|
||||
auto it = functions.find(start);
|
||||
if (it == functions.end())
|
||||
return INVALID_ADDRESS;
|
||||
|
||||
return it->second.index;
|
||||
}
|
||||
|
||||
bool SymbolMap::GetFunctionNoReturn(u32 address) const
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(m_lock);
|
||||
auto it = functions.find(address);
|
||||
if (it == functions.end())
|
||||
return false;
|
||||
|
||||
return it->second.noReturn;
|
||||
}
|
||||
|
||||
void SymbolMap::AssignFunctionIndices()
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(m_lock);
|
||||
int index = 0;
|
||||
auto begin = functions.lower_bound(0);
|
||||
auto end = functions.upper_bound(0xFFFFFFFF);
|
||||
for (auto it = begin; it != end; ++it)
|
||||
{
|
||||
it->second.index = index++;
|
||||
}
|
||||
}
|
||||
|
||||
bool SymbolMap::SetFunctionSize(u32 startAddress, u32 newSize)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(m_lock);
|
||||
|
||||
auto funcInfo = functions.find(startAddress);
|
||||
if (funcInfo != functions.end())
|
||||
{
|
||||
funcInfo->second.size = newSize;
|
||||
}
|
||||
|
||||
// TODO: check for overlaps
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SymbolMap::RemoveFunction(u32 startAddress)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(m_lock);
|
||||
|
||||
auto it = functions.find(startAddress);
|
||||
if (it == functions.end())
|
||||
return false;
|
||||
|
||||
functions.erase(it);
|
||||
|
||||
labels.erase(startAddress);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SymbolMap::AddLabel(const std::string& name, u32 address)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(m_lock);
|
||||
|
||||
auto existing = labels.find(address);
|
||||
|
||||
if (existing != labels.end())
|
||||
{
|
||||
// Adding a function will automatically call this.
|
||||
// We don't want to overwrite the name if it's already set because
|
||||
// label names are added before our functions
|
||||
}
|
||||
else
|
||||
{
|
||||
LabelEntry label;
|
||||
label.addr = address;
|
||||
label.name = name;
|
||||
|
||||
labels[address] = label;
|
||||
}
|
||||
}
|
||||
|
||||
void SymbolMap::SetLabelName(const std::string& name, u32 address)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(m_lock);
|
||||
auto labelInfo = labels.find(address);
|
||||
if (labelInfo == labels.end())
|
||||
{
|
||||
AddLabel(name, address);
|
||||
}
|
||||
else
|
||||
{
|
||||
labelInfo->second.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
std::string SymbolMap::GetLabelName(u32 address) const
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(m_lock);
|
||||
auto it = labels.find(address);
|
||||
if (it == labels.end())
|
||||
return "";
|
||||
|
||||
return it->second.name;
|
||||
}
|
||||
|
||||
bool SymbolMap::GetLabelValue(const std::string& name, u32& dest)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(m_lock);
|
||||
for (auto it = labels.begin(); it != labels.end(); it++)
|
||||
{
|
||||
if (name == it->second.name)
|
||||
{
|
||||
dest = it->first;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void SymbolMap::AddData(u32 address, u32 size, DataType type, int moduleIndex)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(m_lock);
|
||||
|
||||
auto existing = data.find(address);
|
||||
|
||||
if (existing != data.end())
|
||||
{
|
||||
existing->second.size = size;
|
||||
existing->second.type = type;
|
||||
}
|
||||
else
|
||||
{
|
||||
DataEntry entry;
|
||||
entry.start = address;
|
||||
entry.size = size;
|
||||
entry.type = type;
|
||||
|
||||
data[address] = entry;
|
||||
}
|
||||
}
|
||||
|
||||
u32 SymbolMap::GetDataStart(u32 address) const
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(m_lock);
|
||||
auto it = data.upper_bound(address);
|
||||
if (it == data.end())
|
||||
{
|
||||
// check last element
|
||||
auto rit = data.rbegin();
|
||||
|
||||
if (rit != data.rend())
|
||||
{
|
||||
u32 start = rit->first;
|
||||
u32 size = rit->second.size;
|
||||
if (start <= address && start + size > address)
|
||||
return start;
|
||||
}
|
||||
// otherwise there's no data that contains this address
|
||||
return INVALID_ADDRESS;
|
||||
}
|
||||
|
||||
if (it != data.begin())
|
||||
{
|
||||
it--;
|
||||
u32 start = it->first;
|
||||
u32 size = it->second.size;
|
||||
if (start <= address && start + size > address)
|
||||
return start;
|
||||
}
|
||||
|
||||
return INVALID_ADDRESS;
|
||||
}
|
||||
|
||||
u32 SymbolMap::GetDataSize(u32 startAddress) const
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(m_lock);
|
||||
auto it = data.find(startAddress);
|
||||
if (it == data.end())
|
||||
return INVALID_ADDRESS;
|
||||
return it->second.size;
|
||||
}
|
||||
|
||||
DataType SymbolMap::GetDataType(u32 startAddress) const
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(m_lock);
|
||||
auto it = data.find(startAddress);
|
||||
if (it == data.end())
|
||||
return DATATYPE_NONE;
|
||||
return it->second.type;
|
||||
}
|
||||
|
||||
bool SymbolMap::AddModule(const std::string& name, ModuleVersion version)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(m_lock);
|
||||
auto it = modules.find(name);
|
||||
if (it != modules.end())
|
||||
{
|
||||
for (auto [itr, end] = modules.equal_range(name); itr != end; ++itr)
|
||||
{
|
||||
// Different major versions, we treat this one as a different module
|
||||
if (itr->second.version.major != version.major)
|
||||
continue;
|
||||
|
||||
// RegisterLibraryEntries will fail if the new minor ver is <= the old minor ver
|
||||
// and the major version is the same
|
||||
if (itr->second.version.minor >= version.minor)
|
||||
return false;
|
||||
|
||||
// Remove the old module and its export table
|
||||
RemoveModule(name, itr->second.version);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
modules.insert(std::make_pair(name, ModuleEntry{name, version, {}}));
|
||||
return true;
|
||||
}
|
||||
|
||||
void SymbolMap::AddModuleExport(const std::string& module, ModuleVersion version, const std::string& name, u32 address, u32 size)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(m_lock);
|
||||
for (auto [itr, end] = modules.equal_range(module); itr != end; ++itr)
|
||||
{
|
||||
if (itr->second.version != version)
|
||||
continue;
|
||||
|
||||
AddFunction(name, address, size);
|
||||
AddLabel(name, address);
|
||||
itr->second.exports.push_back({address, size, 0, name});
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<ModuleInfo> SymbolMap::GetModules() const
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(m_lock);
|
||||
std::vector<ModuleInfo> result;
|
||||
for (auto& module : modules)
|
||||
{
|
||||
std::vector<SymbolEntry> exports;
|
||||
for (auto& fun : module.second.exports)
|
||||
{
|
||||
exports.push_back({fun.name, fun.start, fun.size});
|
||||
}
|
||||
result.push_back({module.second.name, module.second.version, exports});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void SymbolMap::RemoveModule(const std::string& name, ModuleVersion version)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(m_lock);
|
||||
for (auto [itr, end] = modules.equal_range(name); itr != end; ++itr)
|
||||
{
|
||||
if (itr->second.version != version)
|
||||
continue;
|
||||
|
||||
for (auto& exportEntry : itr->second.exports)
|
||||
{
|
||||
RemoveFunction(exportEntry.start);
|
||||
}
|
||||
|
||||
modules.erase(itr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void SymbolMap::ClearModules()
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(m_lock);
|
||||
for (auto& module : modules)
|
||||
{
|
||||
for (auto& exportEntry : module.second.exports)
|
||||
{
|
||||
RemoveFunction(exportEntry.start);
|
||||
}
|
||||
}
|
||||
modules.clear();
|
||||
}
|
|
@ -1,152 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <mutex>
|
||||
|
||||
#include "common/Pcsx2Types.h"
|
||||
|
||||
enum SymbolType
|
||||
{
|
||||
ST_NONE = 0,
|
||||
ST_FUNCTION = 1,
|
||||
ST_DATA = 2,
|
||||
ST_ALL = 3,
|
||||
};
|
||||
|
||||
struct SymbolInfo
|
||||
{
|
||||
SymbolType type;
|
||||
u32 address;
|
||||
u32 size;
|
||||
};
|
||||
|
||||
struct SymbolEntry
|
||||
{
|
||||
std::string name;
|
||||
u32 address;
|
||||
u32 size;
|
||||
};
|
||||
|
||||
struct ModuleVersion
|
||||
{
|
||||
u8 major;
|
||||
u8 minor;
|
||||
|
||||
friend auto operator<=>(const ModuleVersion&, const ModuleVersion&) = default;
|
||||
};
|
||||
|
||||
struct ModuleInfo
|
||||
{
|
||||
std::string name;
|
||||
ModuleVersion version;
|
||||
std::vector<SymbolEntry> exports;
|
||||
};
|
||||
|
||||
enum DataType
|
||||
{
|
||||
DATATYPE_NONE,
|
||||
DATATYPE_BYTE,
|
||||
DATATYPE_HALFWORD,
|
||||
DATATYPE_WORD,
|
||||
DATATYPE_ASCII
|
||||
};
|
||||
|
||||
class SymbolMap
|
||||
{
|
||||
public:
|
||||
SymbolMap() {}
|
||||
void Clear();
|
||||
void SortSymbols();
|
||||
|
||||
bool LoadNocashSym(const std::string& filename);
|
||||
|
||||
SymbolType GetSymbolType(u32 address) const;
|
||||
bool GetSymbolInfo(SymbolInfo* info, u32 address, SymbolType symmask = ST_FUNCTION) const;
|
||||
u32 GetNextSymbolAddress(u32 address, SymbolType symmask);
|
||||
std::string GetDescription(unsigned int address) const;
|
||||
std::vector<SymbolEntry> GetAllSymbols(SymbolType symmask) const;
|
||||
|
||||
void AddFunction(const std::string& name, u32 address, u32 size, bool noReturn = false);
|
||||
u32 GetFunctionStart(u32 address) const;
|
||||
int GetFunctionNum(u32 address) const;
|
||||
bool GetFunctionNoReturn(u32 address) const;
|
||||
u32 GetFunctionSize(u32 startAddress) const;
|
||||
bool SetFunctionSize(u32 startAddress, u32 newSize);
|
||||
bool RemoveFunction(u32 startAddress);
|
||||
|
||||
void AddLabel(const std::string& name, u32 address);
|
||||
std::string GetLabelName(u32 address) const;
|
||||
void SetLabelName(const std::string& name, u32 address);
|
||||
bool GetLabelValue(const std::string& name, u32& dest);
|
||||
|
||||
void AddData(u32 address, u32 size, DataType type, int moduleIndex = -1);
|
||||
u32 GetDataStart(u32 address) const;
|
||||
u32 GetDataSize(u32 startAddress) const;
|
||||
DataType GetDataType(u32 startAddress) const;
|
||||
|
||||
// Module functions for IOP symbols
|
||||
|
||||
bool AddModule(const std::string& name, ModuleVersion version);
|
||||
void AddModuleExport(const std::string& module, ModuleVersion version, const std::string& name, u32 address, u32 size);
|
||||
std::vector<ModuleInfo> GetModules() const;
|
||||
void RemoveModule(const std::string& name, ModuleVersion version);
|
||||
// Clears any modules and their associated exports
|
||||
// Prefer this over Clear() so we don't clear user defined functions
|
||||
// In the future we should mark functions as user defined
|
||||
void ClearModules();
|
||||
|
||||
static const u32 INVALID_ADDRESS = (u32)-1;
|
||||
|
||||
bool IsEmpty() const { return functions.empty() && labels.empty() && data.empty(); };
|
||||
|
||||
private:
|
||||
void AssignFunctionIndices();
|
||||
|
||||
struct FunctionEntry
|
||||
{
|
||||
u32 start;
|
||||
u32 size;
|
||||
int index;
|
||||
std::string name;
|
||||
bool noReturn;
|
||||
};
|
||||
|
||||
struct LabelEntry
|
||||
{
|
||||
u32 addr;
|
||||
std::string name;
|
||||
};
|
||||
|
||||
struct DataEntry
|
||||
{
|
||||
DataType type;
|
||||
u32 start;
|
||||
u32 size;
|
||||
};
|
||||
|
||||
struct ModuleEntry
|
||||
{
|
||||
std::string name;
|
||||
ModuleVersion version;
|
||||
// This is duplicated data from the function map
|
||||
// The issue is that multiple exports can point to the same address
|
||||
// The address we use as a key... We should use a multimap in the future
|
||||
std::vector<FunctionEntry> exports;
|
||||
};
|
||||
|
||||
std::map<u32, FunctionEntry> functions;
|
||||
std::map<u32, LabelEntry> labels;
|
||||
std::map<u32, DataEntry> data;
|
||||
std::multimap<std::string, ModuleEntry> modules;
|
||||
|
||||
mutable std::recursive_mutex m_lock;
|
||||
};
|
||||
|
||||
extern SymbolMap R5900SymbolMap;
|
||||
extern SymbolMap R3000SymbolMap;
|
|
@ -4,7 +4,7 @@
|
|||
#include "Elfheader.h"
|
||||
#include "CDVD/IsoReader.h"
|
||||
#include "DebugTools/Debug.h"
|
||||
#include "DebugTools/SymbolMap.h"
|
||||
#include "DebugTools/SymbolGuardian.h"
|
||||
|
||||
#include "common/Error.h"
|
||||
#include "common/FileSystem.h"
|
||||
|
@ -303,8 +303,6 @@ void ElfObject::LoadSectionHeaders()
|
|||
const u32 section_names_offset = secthead[(header.e_shstrndx == 0xffff ? 0 : header.e_shstrndx)].sh_offset;
|
||||
const u8* sections_names = data.data() + section_names_offset;
|
||||
|
||||
int i_st = -1, i_dt = -1;
|
||||
|
||||
for( int i = 0 ; i < header.e_shnum ; i++ )
|
||||
{
|
||||
ELF_LOG( "ELF32 Section Header [%x] %s", i, §ions_names[ secthead[ i ].sh_name ] );
|
||||
|
@ -340,33 +338,6 @@ void ElfObject::LoadSectionHeaders()
|
|||
ELF_LOG("info: %08x", secthead[i].sh_info);
|
||||
ELF_LOG("addralign: %08x", secthead[i].sh_addralign);
|
||||
ELF_LOG("entsize: %08x", secthead[i].sh_entsize);
|
||||
// dump symbol table
|
||||
|
||||
if (secthead[ i ].sh_type == 0x02)
|
||||
{
|
||||
i_st = i;
|
||||
i_dt = secthead[i].sh_link;
|
||||
}
|
||||
}
|
||||
|
||||
if ((i_st >= 0) && (i_dt >= 0))
|
||||
{
|
||||
if (secthead[i_dt].sh_offset < data.size() &&
|
||||
secthead[i_st].sh_offset < data.size())
|
||||
{
|
||||
const char* SymNames = (char*)(data.data() + secthead[i_dt].sh_offset);
|
||||
Elf32_Sym* eS = (Elf32_Sym*)(data.data() + secthead[i_st].sh_offset);
|
||||
Console.WriteLn("found %d symbols", secthead[i_st].sh_size / sizeof(Elf32_Sym));
|
||||
|
||||
R5900SymbolMap.Clear();
|
||||
for (uint i = 1; i < (secthead[i_st].sh_size / sizeof(Elf32_Sym)); i++)
|
||||
{
|
||||
if ((eS[i].st_value != 0) && (ELF32_ST_TYPE(eS[i].st_info) == 2))
|
||||
{
|
||||
R5900SymbolMap.AddLabel(&SymNames[eS[i].st_name], eS[i].st_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -117,6 +117,7 @@ public:
|
|||
~ElfObject();
|
||||
|
||||
__fi const std::vector<u8>& GetData() const { return data; }
|
||||
__fi std::vector<u8> ReleaseData() const { return std::move(data); }
|
||||
__fi const ELF_HEADER& GetHeader() const { return *reinterpret_cast<const ELF_HEADER*>(data.data()); }
|
||||
__fi u32 GetSize() const { return static_cast<u32>(data.size()); }
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
#include "Common.h"
|
||||
#include "DebugTools/SymbolMap.h"
|
||||
#include "DebugTools/SymbolGuardian.h"
|
||||
#include "IopBios.h"
|
||||
#include "IopMem.h"
|
||||
#include "R3000A.h"
|
||||
|
@ -1053,40 +1053,94 @@ namespace R3000A
|
|||
void LoadFuncs(u32 a0reg)
|
||||
{
|
||||
const std::string modname = iopMemReadString(a0reg + 12, 8);
|
||||
ModuleVersion version = {iopMemRead8(a0 + 9), iopMemRead8(a0 + 8)};
|
||||
DevCon.WriteLn(Color_Gray, "RegisterLibraryEntries: %8.8s version %x.%02x", modname.data(), version.major, version.minor);
|
||||
s32 version_major = iopMemRead8(a0reg + 9);
|
||||
s32 version_minor = iopMemRead8(a0reg + 8);
|
||||
DevCon.WriteLn(Color_Gray, "RegisterLibraryEntries: %8.8s version %x.%02x", modname.data(), version_major, version_minor);
|
||||
|
||||
if (R3000SymbolMap.AddModule(modname, version))
|
||||
R3000SymbolGuardian.ReadWrite([&](ccc::SymbolDatabase& database) {
|
||||
ccc::Result<ccc::SymbolSourceHandle> source = database.get_symbol_source("IRX Export Table");
|
||||
if (!source.success())
|
||||
return;
|
||||
|
||||
// Enumerate the module symbols that already exist for this IRX
|
||||
// module. Really there should only be one.
|
||||
std::vector<ccc::ModuleHandle> existing_modules;
|
||||
for (const auto& pair : database.modules.handles_from_name(modname))
|
||||
{
|
||||
const ccc::Module* existing_module = database.modules.symbol_from_handle(pair.second);
|
||||
if (!existing_module || !existing_module->is_irx)
|
||||
continue;
|
||||
|
||||
// Different major versions, we treat this one as a different module.
|
||||
if (existing_module->version_major != version_major)
|
||||
continue;
|
||||
|
||||
// RegisterLibraryEntries will fail if the new minor ver is <= the old minor ver
|
||||
// and the major version is the same.
|
||||
if (existing_module->version_minor >= version_minor)
|
||||
return;
|
||||
|
||||
existing_modules.emplace_back(existing_module->handle());
|
||||
}
|
||||
|
||||
// Destroy the old symbols for this IRX module if any exist.
|
||||
for (ccc::ModuleHandle existing_module : existing_modules)
|
||||
database.destroy_symbols_from_module(existing_module, true);
|
||||
|
||||
ccc::Result<ccc::Module*> module_symbol = database.modules.create_symbol(modname, *source, nullptr);
|
||||
if (!module_symbol.success())
|
||||
return;
|
||||
|
||||
(*module_symbol)->is_irx = true;
|
||||
(*module_symbol)->version_major = version_major;
|
||||
(*module_symbol)->version_minor = version_minor;
|
||||
|
||||
u32 func = a0reg + 20;
|
||||
u32 funcptr = iopMemRead32(func);
|
||||
u32 index = 0;
|
||||
while (funcptr != 0)
|
||||
{
|
||||
const std::string funcname = std::string(irxImportFuncname(modname, index));
|
||||
if (!funcname.empty())
|
||||
{
|
||||
R3000SymbolMap.AddModuleExport(modname, version, fmt::format("{}[{:02}]::{}", modname, index, funcname).c_str(), funcptr, 0);
|
||||
}
|
||||
const char* unqualified_name = irxImportFuncname(modname, index);
|
||||
|
||||
std::string qualified_name;
|
||||
if (unqualified_name && unqualified_name[0] != '\0')
|
||||
qualified_name = fmt::format("{}[{:02}]::{}", modname, index, unqualified_name);
|
||||
else
|
||||
{
|
||||
R3000SymbolMap.AddModuleExport(modname, version, fmt::format("{}[{:02}]::unkn_{:02}", modname, index, index).c_str(), funcptr, 0);
|
||||
}
|
||||
qualified_name = fmt::format("{}[{:02}]::unkn_{:02}", modname, index, index);
|
||||
|
||||
ccc::Result<ccc::Function*> function = database.functions.create_symbol(qualified_name, funcptr, *source, *module_symbol);
|
||||
if (!function.success())
|
||||
return;
|
||||
|
||||
index++;
|
||||
func += 4;
|
||||
funcptr = iopMemRead32(func);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void ReleaseFuncs(u32 a0reg)
|
||||
{
|
||||
const std::string modname = iopMemReadString(a0reg + 12, 8);
|
||||
ModuleVersion version = {iopMemRead8(a0 + 9), iopMemRead8(a0 + 8)};
|
||||
s32 version_major = iopMemRead8(a0reg + 9);
|
||||
s32 version_minor = iopMemRead8(a0reg + 8);
|
||||
|
||||
DevCon.WriteLn(Color_Gray, "ReleaseLibraryEntries: %8.8s version %x.%02x", modname.data(), version.major, version.minor);
|
||||
DevCon.WriteLn(Color_Gray, "ReleaseLibraryEntries: %8.8s version %x.%02x", modname.c_str(), version_major, version_minor);
|
||||
|
||||
R3000SymbolMap.RemoveModule(modname, version);
|
||||
R3000SymbolGuardian.ReadWrite([&](ccc::SymbolDatabase& database) {
|
||||
// Destroy the symbols for the module.
|
||||
for (const auto& pair : database.modules.handles_from_name(modname))
|
||||
{
|
||||
const ccc::Module* existing_module = database.modules.symbol_from_handle(pair.second);
|
||||
if (!existing_module || !existing_module->is_irx)
|
||||
continue;
|
||||
|
||||
if (existing_module->version_major != version_major || existing_module->version_minor != version_minor)
|
||||
continue;
|
||||
|
||||
database.destroy_symbols_from_module(existing_module->handle(), true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
int RegisterLibraryEntries_HLE()
|
||||
|
|
|
@ -238,7 +238,7 @@ static void doBranch(s32 tar) {
|
|||
if(tar == 0x890)
|
||||
{
|
||||
DevCon.WriteLn(Color_Gray, "[R3000 Debugger] Branch to 0x890 (SYSMEM). Clearing modules.");
|
||||
R3000SymbolMap.ClearModules();
|
||||
R3000SymbolGuardian.ClearIrxModules();
|
||||
}
|
||||
|
||||
branch2 = iopIsDelaySlot = true;
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
|
||||
#include "DebugTools/Breakpoints.h"
|
||||
#include "DebugTools/MIPSAnalyst.h"
|
||||
#include "DebugTools/SymbolMap.h"
|
||||
#include "DebugTools/SymbolGuardian.h"
|
||||
#include "R5900OpcodeTables.h"
|
||||
|
||||
#include "fmt/format.h"
|
||||
|
|
|
@ -7,8 +7,7 @@
|
|||
#include "Counters.h"
|
||||
#include "DEV9/DEV9.h"
|
||||
#include "DebugTools/DebugInterface.h"
|
||||
#include "DebugTools/MIPSAnalyst.h"
|
||||
#include "DebugTools/SymbolMap.h"
|
||||
#include "DebugTools/SymbolGuardian.h"
|
||||
#include "Elfheader.h"
|
||||
#include "FW.h"
|
||||
#include "GS.h"
|
||||
|
@ -447,6 +446,9 @@ void VMManager::Internal::CPUThreadShutdown()
|
|||
|
||||
// Ensure emulog gets flushed.
|
||||
Log::SetFileOutputLevel(LOGLEVEL_NONE, std::string());
|
||||
|
||||
R3000SymbolGuardian.ShutdownWorkerThread();
|
||||
R5900SymbolGuardian.ShutdownWorkerThread();
|
||||
}
|
||||
|
||||
void VMManager::Internal::SetFileLogPath(std::string path)
|
||||
|
@ -1128,9 +1130,6 @@ void VMManager::HandleELFChange(bool verbose_patches_if_changed)
|
|||
Console.WriteLn(Color_StrongOrange, fmt::format("ELF changed, active CRC {:08X} ({})", crc_to_report, s_elf_path));
|
||||
Patch::ReloadPatches(s_disc_serial, crc_to_report, false, false, false, verbose_patches_if_changed);
|
||||
ApplyCoreSettings();
|
||||
|
||||
MIPSAnalyst::ScanForFunctions(
|
||||
R5900SymbolMap, s_elf_text_range.first, s_elf_text_range.first + s_elf_text_range.second, true);
|
||||
}
|
||||
|
||||
void VMManager::UpdateELFInfo(std::string elf_path)
|
||||
|
@ -1154,6 +1153,25 @@ void VMManager::UpdateELFInfo(std::string elf_path)
|
|||
s_elf_entry_point = elfo.GetEntryPoint();
|
||||
s_elf_text_range = elfo.GetTextRange();
|
||||
s_elf_path = std::move(elf_path);
|
||||
|
||||
R5900SymbolGuardian.Reset();
|
||||
|
||||
// Search for a .sym file to load symbols from.
|
||||
std::string nocash_path;
|
||||
CDVD_SourceType source_type = CDVDsys_GetSourceType();
|
||||
if (source_type == CDVD_SourceType::Iso)
|
||||
{
|
||||
std::string iso_file_path = CDVDsys_GetFile(source_type);
|
||||
|
||||
std::string::size_type n = iso_file_path.rfind('.');
|
||||
if (n == std::string::npos)
|
||||
nocash_path = iso_file_path + ".sym";
|
||||
else
|
||||
nocash_path = iso_file_path.substr(0, n) + ".sym";
|
||||
}
|
||||
|
||||
// Load the symbols stored in the ELF file.
|
||||
R5900SymbolGuardian.ImportElf(elfo.ReleaseData(), s_elf_path, nocash_path);
|
||||
}
|
||||
|
||||
void VMManager::ClearELFInfo()
|
||||
|
|
|
@ -153,7 +153,7 @@
|
|||
<ClCompile Include="DebugTools\MipsAssembler.cpp" />
|
||||
<ClCompile Include="DebugTools\MipsAssemblerTables.cpp" />
|
||||
<ClCompile Include="DebugTools\MipsStackWalk.cpp" />
|
||||
<ClCompile Include="DebugTools\SymbolMap.cpp" />
|
||||
<ClCompile Include="DebugTools\SymbolGuardian.cpp" />
|
||||
<ClCompile Include="DEV9\AdapterUtils.cpp" />
|
||||
<ClCompile Include="DEV9\ATA\Commands\ATA_Command.cpp" />
|
||||
<ClCompile Include="DEV9\ATA\Commands\ATA_CmdDMA.cpp" />
|
||||
|
@ -597,7 +597,7 @@
|
|||
<ClInclude Include="DebugTools\MipsAssembler.h" />
|
||||
<ClInclude Include="DebugTools\MipsAssemblerTables.h" />
|
||||
<ClInclude Include="DebugTools\MipsStackWalk.h" />
|
||||
<ClInclude Include="DebugTools\SymbolMap.h" />
|
||||
<ClInclude Include="DebugTools\SymbolGuardian.h" />
|
||||
<ClInclude Include="DEV9\AdapterUtils.h" />
|
||||
<ClInclude Include="DEV9\ATA\ATA.h" />
|
||||
<ClInclude Include="DEV9\ATA\HddCreate.h" />
|
||||
|
|
|
@ -728,9 +728,6 @@
|
|||
<ClCompile Include="CDVD\BlockdumpFileReader.cpp">
|
||||
<Filter>System\ISO</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="DebugTools\SymbolMap.cpp">
|
||||
<Filter>System\Ps2\Debug</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="DebugTools\DebugInterface.cpp">
|
||||
<Filter>System\Ps2\Debug</Filter>
|
||||
</ClCompile>
|
||||
|
@ -1395,6 +1392,9 @@
|
|||
<ClCompile Include="SIO\Pad\PadPopn.cpp">
|
||||
<Filter>System\Ps2\Iop\SIO\PAD</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="DebugTools\SymbolGuardian.cpp">
|
||||
<Filter>System\Ps2\Debug</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="USB\usb-eyetoy\cam-jpeg.cpp">
|
||||
<Filter>System\Ps2\USB\usb-eyetoy</Filter>
|
||||
</ClCompile>
|
||||
|
@ -1661,9 +1661,6 @@
|
|||
<ClInclude Include="x86\microVU_Profiler.h">
|
||||
<Filter>System\Ps2\EmotionEngine\VU\Dynarec\microVU</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="DebugTools\SymbolMap.h">
|
||||
<Filter>System\Ps2\Debug</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="DebugTools\DebugInterface.h">
|
||||
<Filter>System\Ps2\Debug</Filter>
|
||||
</ClInclude>
|
||||
|
@ -2336,6 +2333,9 @@
|
|||
<Filter>System\Ps2\Iop\SIO\PAD</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="SupportURLs.h" />
|
||||
<ClInclude Include="DebugTools\SymbolGuardian.h">
|
||||
<Filter>System\Ps2\Debug</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="USB\usb-eyetoy\cam-jpeg.h">
|
||||
<Filter>System\Ps2\USB\usb-eyetoy</Filter>
|
||||
</ClInclude>
|
||||
|
|
|
@ -1544,7 +1544,7 @@ static void iopRecRecompile(const u32 startpc)
|
|||
if(startpc == 0x890)
|
||||
{
|
||||
DevCon.WriteLn(Color_Gray, "[R3000 Debugger] Branch to 0x890 (SYSMEM). Clearing modules.");
|
||||
R3000SymbolMap.ClearModules();
|
||||
R3000SymbolGuardian.ClearIrxModules();
|
||||
}
|
||||
|
||||
// Inject IRX hack
|
||||
|
|
Loading…
Reference in New Issue