diff --git a/pcsx2-qt/Debugger/CpuWidget.cpp b/pcsx2-qt/Debugger/CpuWidget.cpp index a39d9ada0b..584ab0ff02 100644 --- a/pcsx2-qt/Debugger/CpuWidget.cpp +++ b/pcsx2-qt/Debugger/CpuWidget.cpp @@ -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()) - { - m_ui.registerWidget->update(); - m_ui.disassemblyWidget->update(); - m_ui.memoryviewWidget->update(); - m_ui.memorySearchWidget->update(); - } + 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 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; } } diff --git a/pcsx2-qt/Debugger/CpuWidget.h b/pcsx2-qt/Debugger/CpuWidget.h index e17f85a3bb..a24e28fc22 100644 --- a/pcsx2-qt/Debugger/CpuWidget.h +++ b/pcsx2-qt/Debugger/CpuWidget.h @@ -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; }; diff --git a/pcsx2-qt/Debugger/DisassemblyWidget.cpp b/pcsx2-qt/Debugger/DisassemblyWidget.cpp index 3aeeaaa5e9..615744fbfc 100644 --- a/pcsx2-qt/Debugger/DisassemblyWidget.cpp +++ b/pcsx2-qt/Debugger/DisassemblyWidget.cpp @@ -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) - { - bool ok; - QString funcName = QInputDialog::getText(this, tr("Rename Function"), tr("Function name"), QLineEdit::Normal, m_cpu->GetSymbolMap().GetLabelName(curFuncAddress).c_str(), &ok); - if (!ok) - return; + const FunctionInfo curFunc = m_cpu->GetSymbolGuardian().FunctionOverlappingAddress(m_selectedAddressStart); - if (funcName.isEmpty()) - { - QMessageBox::warning(this, tr("Rename Function Error"), tr("Function name cannot be nothing.")); - } - else - { - m_cpu->GetSymbolMap().SetLabelName(funcName.toLocal8Bit().constData(), curFuncAddress); - m_cpu->GetSymbolMap().SortSymbols(); - this->repaint(); - } - } - else + 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 newName = QInputDialog::getText(this, tr("Rename Function"), tr("Function name"), QLineEdit::Normal, oldName, &ok); + if (!ok) + return; + + if (newName.isEmpty()) + { + QMessageBox::warning(this, tr("Rename Function Error"), tr("Function name cannot be nothing.")); + return; + } + + 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 - 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(); - }); - } + 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(); + }); } void DisassemblyWidget::contextRestoreFunction() { - const u32 curFuncAddress = m_cpu->GetSymbolMap().GetFunctionStart(m_selectedAddressStart); - if (curFuncAddress != SymbolMap::INVALID_ADDRESS && m_stubbedFunctions.find(curFuncAddress) != m_stubbedFunctions.end()) + 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; + }); + + auto stub = m_stubbedFunctions.find(address); + if (stub != 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(); - }); - } - else if (m_stubbedFunctions.find(m_selectedAddressStart) != 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(); } diff --git a/pcsx2-qt/Debugger/DisassemblyWidget.h b/pcsx2-qt/Debugger/DisassemblyWidget.h index b8f3a67f43..d6ba1318c5 100644 --- a/pcsx2-qt/Debugger/DisassemblyWidget.h +++ b/pcsx2-qt/Debugger/DisassemblyWidget.h @@ -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: diff --git a/pcsx2-qt/Debugger/Models/BreakpointModel.cpp b/pcsx2-qt/Debugger/Models/BreakpointModel.cpp index eb8736e8ab..a3e76438cf 100644 --- a/pcsx2-qt/Debugger/Models/BreakpointModel.cpp +++ b/pcsx2-qt/Debugger/Models/BreakpointModel.cpp @@ -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(); diff --git a/pcsx2-qt/Debugger/Models/StackModel.cpp b/pcsx2-qt/Debugger/Models/StackModel.cpp index aff9dc62b1..8684d17152 100644 --- a/pcsx2-qt/Debugger/Models/StackModel.cpp +++ b/pcsx2-qt/Debugger/Models/StackModel.cpp @@ -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: diff --git a/pcsx2-qt/Debugger/Models/StackModel.h b/pcsx2-qt/Debugger/Models/StackModel.h index 7a37dc05e0..423a1b656c 100644 --- a/pcsx2-qt/Debugger/Models/StackModel.h +++ b/pcsx2-qt/Debugger/Models/StackModel.h @@ -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, diff --git a/pcsx2/CDVD/CDVDcommon.cpp b/pcsx2/CDVD/CDVDcommon.cpp index fb7df53449..d8a011f273 100644 --- a/pcsx2/CDVD/CDVDcommon.cpp +++ b/pcsx2/CDVD/CDVDcommon.cpp @@ -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) diff --git a/pcsx2/CMakeLists.txt b/pcsx2/CMakeLists.txt index 1d43e5aefb..fd013f4581 100644 --- a/pcsx2/CMakeLists.txt +++ b/pcsx2/CMakeLists.txt @@ -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 diff --git a/pcsx2/DebugTools/Breakpoints.cpp b/pcsx2/DebugTools/Breakpoints.cpp index 3ad6eb0e1b..1f0c142178 100644 --- a/pcsx2/DebugTools/Breakpoints.cpp +++ b/pcsx2/DebugTools/Breakpoints.cpp @@ -2,7 +2,7 @@ // SPDX-License-Identifier: GPL-3.0+ #include "Breakpoints.h" -#include "SymbolMap.h" +#include "SymbolGuardian.h" #include "MIPSAnalyst.h" #include #include "R5900.h" diff --git a/pcsx2/DebugTools/DebugInterface.cpp b/pcsx2/DebugTools/DebugInterface.cpp index 5865008f23..90768d476f 100644 --- a/pcsx2/DebugTools/DebugInterface.cpp +++ b/pcsx2/DebugTools/DebugInterface.cpp @@ -12,7 +12,6 @@ #include "R3000A.h" #include "IopMem.h" -#include "SymbolMap.h" #include "VMManager.h" #include "common/StringUtil.h" @@ -27,16 +26,16 @@ R3000DebugInterface r3000Debug; enum ReferenceIndexType { - REF_INDEX_PC = 32, - REF_INDEX_HI = 33, - REF_INDEX_LO = 34, + REF_INDEX_PC = 32, + REF_INDEX_HI = 33, + REF_INDEX_LO = 34, REF_INDEX_OPTARGET = 0x800, - REF_INDEX_OPSTORE = 0x1000, - REF_INDEX_OPLOAD = 0x2000, - REF_INDEX_IS_OPSL = REF_INDEX_OPTARGET | REF_INDEX_OPSTORE | REF_INDEX_OPLOAD, - REF_INDEX_FPU = 0x4000, - REF_INDEX_FPU_INT = 0x8000, - REF_INDEX_VFPU = 0x10000, + REF_INDEX_OPSTORE = 0x1000, + REF_INDEX_OPLOAD = 0x2000, + REF_INDEX_IS_OPSL = REF_INDEX_OPTARGET | REF_INDEX_OPSTORE | REF_INDEX_OPLOAD, + REF_INDEX_FPU = 0x4000, + REF_INDEX_FPU_INT = 0x8000, + REF_INDEX_VFPU = 0x10000, REF_INDEX_VFPU_INT = 0x20000, REF_INDEX_IS_FLOAT = REF_INDEX_FPU | REF_INDEX_VFPU, @@ -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) @@ -131,7 +132,7 @@ public: const R5900::OPCODE& opcode = R5900::GetInstruction(OP); if (opcode.flags & IS_MEMORY) { - // Fetch the address in the base register + // Fetch the address in the base register u32 target = cpuRegs.GPR.r[(OP >> 21) & 0x1F].UD[0]; // Add the offset (lower 16 bits) target += static_cast(OP); @@ -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 DebugInterface::getCallerStackPointer(const ccc::Function& currentFunction) +{ + u32 sp = getRegister(EECAT_GPR, 29); + u32 pc = getPC(); + + if (pc != currentFunction.address().value) + { + std::optional stack_frame_size = getStackFrameSize(currentFunction); + if (!stack_frame_size.has_value()) + return std::nullopt; + + sp += *stack_frame_size; + } + + return sp; +} + +std::optional 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> 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> R3000DebugInterface::GetThreadList() const diff --git a/pcsx2/DebugTools/DebugInterface.h b/pcsx2/DebugTools/DebugInterface.h index 591cecb7cd..f628222007 100644 --- a/pcsx2/DebugTools/DebugInterface.h +++ b/pcsx2/DebugTools/DebugInterface.h @@ -5,7 +5,7 @@ #include "DebugTools/BiosDebugData.h" #include "MemoryTypes.h" #include "ExpressionParser.h" -#include "SymbolMap.h" +#include "SymbolGuardian.h" #include @@ -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> GetThreadList() const = 0; bool initExpression(const char* exp, PostfixExpression& dest); @@ -84,6 +87,9 @@ public: void resumeCpu(); char* stringFromPointer(u32 p); + std::optional getCallerStackPointer(const ccc::Function& currentFunction); + std::optional 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> 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> GetThreadList() const override; std::string disasm(u32 address, bool simplify) override; diff --git a/pcsx2/DebugTools/DisassemblyManager.cpp b/pcsx2/DebugTools/DisassemblyManager.cpp index af02c1ef26..c745f74429 100644 --- a/pcsx2/DebugTools/DisassemblyManager.cpp +++ b/pcsx2/DebugTools/DisassemblyManager.cpp @@ -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((address+3) & ~3,cpu->GetSymbolMap().GetNextSymbolAddress(address,ST_ALL)); + u32 next = std::min((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& 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 diff --git a/pcsx2/DebugTools/DisassemblyManager.h b/pcsx2/DebugTools/DisassemblyManager.h index 74e30455ff..3bee4baa88 100644 --- a/pcsx2/DebugTools/DisassemblyManager.h +++ b/pcsx2/DebugTools/DisassemblyManager.h @@ -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 { diff --git a/pcsx2/DebugTools/MIPSAnalyst.cpp b/pcsx2/DebugTools/MIPSAnalyst.cpp index 42a90bdc9a..15dcdbf429 100644 --- a/pcsx2/DebugTools/MIPSAnalyst.cpp +++ b/pcsx2/DebugTools/MIPSAnalyst.cpp @@ -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 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 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; - - // 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); + 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); + + 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; @@ -312,13 +307,45 @@ namespace MIPSAnalyst currentFunction.end = addr + 4; functions.push_back(currentFunction); + + ccc::Result source = database.get_symbol_source("Analysis"); + if(!source->valid()) + return; - 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); + 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 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; } } diff --git a/pcsx2/DebugTools/MIPSAnalyst.h b/pcsx2/DebugTools/MIPSAnalyst.h index e5a534d6d9..8af1605e90 100644 --- a/pcsx2/DebugTools/MIPSAnalyst.h +++ b/pcsx2/DebugTools/MIPSAnalyst.h @@ -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 }; diff --git a/pcsx2/DebugTools/MipsStackWalk.cpp b/pcsx2/DebugTools/MipsStackWalk.cpp index 1eb980e268..d604753624 100644 --- a/pcsx2/DebugTools/MipsStackWalk.cpp +++ b/pcsx2/DebugTools/MipsStackWalk.cpp @@ -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; - } - return INVALIDTARGET; + FunctionInfo function = cpu->GetSymbolGuardian().FunctionOverlappingAddress(pc); + if (!function.address.valid()) + return INVALIDTARGET; + + return function.address.value; } bool IsSWInstr(const R5900::OPCODE& op) diff --git a/pcsx2/DebugTools/SymbolGuardian.cpp b/pcsx2/DebugTools/SymbolGuardian.cpp new file mode 100644 index 0000000000..a2f7b87385 --- /dev/null +++ b/pcsx2/DebugTools/SymbolGuardian.cpp @@ -0,0 +1,560 @@ +// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team +// SPDX-License-Identifier: GPL-3.0+ + +#include "SymbolGuardian.h" + +#include +#include +#include +#include + +#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 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 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 symbol = database.data_types.create_symbol(default_type.name, *source, nullptr); + if (!symbol.success()) + return; + + std::unique_ptr type = std::make_unique(); + 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 symbol = database.data_types.create_symbol(name, source, nullptr); + if (!symbol.success()) + return; + + std::unique_ptr type = std::make_unique(); + 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 elf, std::string elf_file_name, const std::string& nocash_path) +{ + ccc::Result parsed_elf = ccc::ElfFile::parse(std::move(elf)); + if (!parsed_elf.success()) + { + ccc::report_error(parsed_elf.error()); + return; + } + + std::unique_ptr symbol_file = + std::make_unique(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>> 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 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 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 scalar_type = std::make_unique(); + 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 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 array = std::make_unique(); + 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 function = database.functions.create_symbol(value, address, *source, nullptr); + if (!function.success()) + { + fclose(f); + return false; + } + + (*function)->set_size(size); + } + else + { + ccc::Result 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> text = elf.get_array_virtual( + 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 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; +} diff --git a/pcsx2/DebugTools/SymbolGuardian.h b/pcsx2/DebugTools/SymbolGuardian.h new file mode 100644 index 0000000000..e1d165b7cb --- /dev/null +++ b/pcsx2/DebugTools/SymbolGuardian.h @@ -0,0 +1,112 @@ +// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team +// SPDX-License-Identifier: GPL-3.0+ + +#pragma once + +#include +#include +#include +#include +#include + +#include +#include + +#include "common/Pcsx2Types.h" + +class DebugInterface; + +struct SymbolInfo +{ + std::optional 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; + using ReadWriteCallback = std::function; + + // 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 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 m_load_queue; + std::mutex m_load_queue_lock; +}; + +extern SymbolGuardian R5900SymbolGuardian; +extern SymbolGuardian R3000SymbolGuardian; diff --git a/pcsx2/DebugTools/SymbolMap.cpp b/pcsx2/DebugTools/SymbolMap.cpp deleted file mode 100644 index a628aaeb1f..0000000000 --- a/pcsx2/DebugTools/SymbolMap.cpp +++ /dev/null @@ -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 - -SymbolMap R5900SymbolMap; -SymbolMap R3000SymbolMap; - -void SymbolMap::SortSymbols() -{ - std::lock_guard guard(m_lock); - AssignFunctionIndices(); -} - -void SymbolMap::Clear() -{ - std::lock_guard guard(m_lock); - functions.clear(); - labels.clear(); - data.clear(); - modules.clear(); -} - - -bool SymbolMap::LoadNocashSym(const std::string& filename) -{ - std::lock_guard 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 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 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 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 SymbolMap::GetAllSymbols(SymbolType symmask) const -{ - std::vector result; - - if (symmask & ST_FUNCTION) - { - std::lock_guard 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 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 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 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 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 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 guard(m_lock); - auto it = functions.find(address); - if (it == functions.end()) - return false; - - return it->second.noReturn; -} - -void SymbolMap::AssignFunctionIndices() -{ - std::lock_guard 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 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 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 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 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 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 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 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 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 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 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 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 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 SymbolMap::GetModules() const -{ - std::lock_guard guard(m_lock); - std::vector result; - for (auto& module : modules) - { - std::vector 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 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 guard(m_lock); - for (auto& module : modules) - { - for (auto& exportEntry : module.second.exports) - { - RemoveFunction(exportEntry.start); - } - } - modules.clear(); -} diff --git a/pcsx2/DebugTools/SymbolMap.h b/pcsx2/DebugTools/SymbolMap.h deleted file mode 100644 index 7dcac06336..0000000000 --- a/pcsx2/DebugTools/SymbolMap.h +++ /dev/null @@ -1,152 +0,0 @@ -// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team -// SPDX-License-Identifier: GPL-3.0+ - -#pragma once - -#include -#include -#include -#include -#include - -#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 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 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 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 exports; - }; - - std::map functions; - std::map labels; - std::map data; - std::multimap modules; - - mutable std::recursive_mutex m_lock; -}; - -extern SymbolMap R5900SymbolMap; -extern SymbolMap R3000SymbolMap; diff --git a/pcsx2/Elfheader.cpp b/pcsx2/Elfheader.cpp index 193bd6d99f..bc0ed03440 100644 --- a/pcsx2/Elfheader.cpp +++ b/pcsx2/Elfheader.cpp @@ -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); - } - } - } } } diff --git a/pcsx2/Elfheader.h b/pcsx2/Elfheader.h index 3bc996fab0..71414e4021 100644 --- a/pcsx2/Elfheader.h +++ b/pcsx2/Elfheader.h @@ -117,6 +117,7 @@ public: ~ElfObject(); __fi const std::vector& GetData() const { return data; } + __fi std::vector ReleaseData() const { return std::move(data); } __fi const ELF_HEADER& GetHeader() const { return *reinterpret_cast(data.data()); } __fi u32 GetSize() const { return static_cast(data.size()); } diff --git a/pcsx2/IopBios.cpp b/pcsx2/IopBios.cpp index fed0913da1..4f24d4d3ab 100644 --- a/pcsx2/IopBios.cpp +++ b/pcsx2/IopBios.cpp @@ -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); + + R3000SymbolGuardian.ReadWrite([&](ccc::SymbolDatabase& database) { + ccc::Result 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 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 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; - if (R3000SymbolMap.AddModule(modname, version)) - { 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 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() diff --git a/pcsx2/R3000AInterpreter.cpp b/pcsx2/R3000AInterpreter.cpp index 578f0a6746..ff4daafaf0 100644 --- a/pcsx2/R3000AInterpreter.cpp +++ b/pcsx2/R3000AInterpreter.cpp @@ -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; diff --git a/pcsx2/R5900.cpp b/pcsx2/R5900.cpp index 259be933a6..da3e80f055 100644 --- a/pcsx2/R5900.cpp +++ b/pcsx2/R5900.cpp @@ -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" diff --git a/pcsx2/VMManager.cpp b/pcsx2/VMManager.cpp index 7867b125a4..01d08c11b9 100644 --- a/pcsx2/VMManager.cpp +++ b/pcsx2/VMManager.cpp @@ -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() diff --git a/pcsx2/pcsx2.vcxproj b/pcsx2/pcsx2.vcxproj index dc9e0e8cc7..1ff70c03c6 100644 --- a/pcsx2/pcsx2.vcxproj +++ b/pcsx2/pcsx2.vcxproj @@ -153,7 +153,7 @@ - + @@ -597,7 +597,7 @@ - + diff --git a/pcsx2/pcsx2.vcxproj.filters b/pcsx2/pcsx2.vcxproj.filters index 41eae2d858..25e5a352e3 100644 --- a/pcsx2/pcsx2.vcxproj.filters +++ b/pcsx2/pcsx2.vcxproj.filters @@ -728,9 +728,6 @@ System\ISO - - System\Ps2\Debug - System\Ps2\Debug @@ -1395,6 +1392,9 @@ System\Ps2\Iop\SIO\PAD + + System\Ps2\Debug + System\Ps2\USB\usb-eyetoy @@ -1661,9 +1661,6 @@ System\Ps2\EmotionEngine\VU\Dynarec\microVU - - System\Ps2\Debug - System\Ps2\Debug @@ -2336,6 +2333,9 @@ System\Ps2\Iop\SIO\PAD + + System\Ps2\Debug + System\Ps2\USB\usb-eyetoy @@ -2410,4 +2410,4 @@ System\Ps2\GS - \ No newline at end of file + diff --git a/pcsx2/x86/iR3000A.cpp b/pcsx2/x86/iR3000A.cpp index ac0139a8e4..291428a203 100644 --- a/pcsx2/x86/iR3000A.cpp +++ b/pcsx2/x86/iR3000A.cpp @@ -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