Debugger: Replace SymbolMap class with new SymbolGuardian class

This new class uses the CCC library I added in the last commit and
parses the symbol tables on a worker thread.
This commit is contained in:
chaoticgd 2024-08-26 20:23:03 +01:00 committed by Ty
parent 87b03fdc28
commit 44b50bee26
30 changed files with 1134 additions and 1319 deletions

View File

@ -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<QTreeWidgetItem*> functions;
for (const auto& sym : module.exports)
{
if (!QString(sym.name.c_str()).toLower().contains(filter))
continue;
QString symbolName = QString(sym.name.c_str());
QTreeWidgetItem* functionItem = new QTreeWidgetItem(moduleItem, QStringList(QString("%0 %1").arg(FilledQStringFromValue(sym.address, 16)).arg(symbolName)));
functionItem->setData(0, Qt::UserRole, sym.address);
functions.append(functionItem);
}
moduleItem->addChildren(functions);
if (!filter.isEmpty() && functions.size())
{
moduleItem->setExpanded(true);
m_ui.treeModules->insertTopLevelItem(0, moduleItem);
}
else if (filter.isEmpty())
{
m_ui.treeModules->insertTopLevelItem(0, moduleItem);
}
else
{
delete moduleItem;
}
}
}
}
void CpuWidget::updateThreads()
{
m_threadModel.refreshData();
@ -694,145 +611,11 @@ void CpuWidget::onThreadListDoubleClick(const QModelIndex& index)
m_ui.tabWidget->setCurrentWidget(m_ui.tab_memory);
break;
default: // Default to PC
m_ui.disassemblyWidget->gotoAddress(m_ui.threadList->model()->data(m_ui.threadList->model()->index(index.row(), ThreadModel::ThreadColumns::PC), Qt::UserRole).toUInt());
m_ui.disassemblyWidget->gotoAddressAndSetFocus(m_ui.threadList->model()->data(m_ui.threadList->model()->index(index.row(), ThreadModel::ThreadColumns::PC), Qt::UserRole).toUInt());
break;
}
}
void CpuWidget::onFuncListContextMenu(QPoint pos)
{
if (!m_funclistContextMenu)
m_funclistContextMenu = new QMenu(m_ui.listFunctions);
else
m_funclistContextMenu->clear();
if (m_ui.listFunctions->selectedItems().count() && m_ui.listFunctions->selectedItems().first()->data(Qt::UserRole).isValid())
{
QAction* copyName = new QAction(tr("Copy Function Name"), m_ui.listFunctions);
connect(copyName, &QAction::triggered, [this] {
// We only store the address in the widget item
// Resolve the function name by fetching the symbolmap and filtering the address
const QListWidgetItem* selectedItem = m_ui.listFunctions->selectedItems().first();
const QString functionName = QString(m_cpu.GetSymbolMap().GetLabelName(selectedItem->data(Qt::UserRole).toUInt()).c_str());
QApplication::clipboard()->setText(functionName);
});
m_funclistContextMenu->addAction(copyName);
QAction* copyAddress = new QAction(tr("Copy Function Address"), m_ui.listFunctions);
connect(copyAddress, &QAction::triggered, [this] {
const QString addressString = FilledQStringFromValue(m_ui.listFunctions->selectedItems().first()->data(Qt::UserRole).toUInt(), 16);
QApplication::clipboard()->setText(addressString);
});
m_funclistContextMenu->addAction(copyAddress);
m_funclistContextMenu->addSeparator();
QAction* gotoDisasm = new QAction(tr("Go to in Disassembly"), m_ui.listFunctions);
connect(gotoDisasm, &QAction::triggered, [this] {
m_ui.disassemblyWidget->gotoAddress(m_ui.listFunctions->selectedItems().first()->data(Qt::UserRole).toUInt());
});
m_funclistContextMenu->addAction(gotoDisasm);
QAction* gotoMemory = new QAction(tr("Go to in Memory View"), m_ui.listFunctions);
connect(gotoMemory, &QAction::triggered, [this] {
m_ui.memoryviewWidget->gotoAddress(m_ui.listFunctions->selectedItems().first()->data(Qt::UserRole).toUInt());
m_ui.tabWidget->setCurrentWidget(m_ui.tab_memory);
});
m_funclistContextMenu->addAction(gotoMemory);
m_funclistContextMenu->addSeparator();
}
if (m_cpu.getCpuType() == BREAKPOINT_IOP)
{
QAction* moduleViewAction = new QAction(tr("Module Tree"), m_ui.listFunctions);
moduleViewAction->setCheckable(true);
moduleViewAction->setChecked(m_moduleView);
connect(moduleViewAction, &QAction::triggered, [this] {
m_moduleView = !m_moduleView;
m_ui.treeModules->setVisible(m_moduleView);
m_ui.listFunctions->setVisible(!m_moduleView);
updateFunctionList();
});
m_funclistContextMenu->addAction(moduleViewAction);
}
m_funclistContextMenu->popup(m_ui.listFunctions->viewport()->mapToGlobal(pos));
}
void CpuWidget::onFuncListDoubleClick(QListWidgetItem* item)
{
m_ui.disassemblyWidget->gotoAddress(item->data(Qt::UserRole).toUInt());
}
void CpuWidget::onModuleTreeContextMenu(QPoint pos)
{
if (!m_moduleTreeContextMenu)
m_moduleTreeContextMenu = new QMenu(m_ui.treeModules);
else
m_moduleTreeContextMenu->clear();
if (m_ui.treeModules->selectedItems().count() && m_ui.treeModules->selectedItems().first()->data(0, Qt::UserRole).isValid())
{
QAction* copyName = new QAction(tr("Copy Function Name"), m_ui.treeModules);
connect(copyName, &QAction::triggered, [this] {
QApplication::clipboard()->setText(m_cpu.GetSymbolMap().GetLabelName(m_ui.treeModules->selectedItems().first()->data(0, Qt::UserRole).toUInt()).c_str());
});
m_moduleTreeContextMenu->addAction(copyName);
QAction* copyAddress = new QAction(tr("Copy Function Address"), m_ui.treeModules);
connect(copyAddress, &QAction::triggered, [this] {
const QString addressString = FilledQStringFromValue(m_ui.treeModules->selectedItems().first()->data(0, Qt::UserRole).toUInt(), 16);
QApplication::clipboard()->setText(addressString);
});
m_moduleTreeContextMenu->addAction(copyAddress);
m_moduleTreeContextMenu->addSeparator();
QAction* gotoDisasm = new QAction(tr("Go to in Disassembly"), m_ui.treeModules);
connect(gotoDisasm, &QAction::triggered, [this] {
m_ui.disassemblyWidget->gotoAddress(m_ui.treeModules->selectedItems().first()->data(0, Qt::UserRole).toUInt());
});
m_moduleTreeContextMenu->addAction(gotoDisasm);
QAction* gotoMemory = new QAction(tr("Go to in Memory View"), m_ui.treeModules);
connect(gotoMemory, &QAction::triggered, [this] {
m_ui.memoryviewWidget->gotoAddress(m_ui.treeModules->selectedItems().first()->data(0, Qt::UserRole).toUInt());
m_ui.tabWidget->setCurrentWidget(m_ui.tab_memory);
});
m_moduleTreeContextMenu->addAction(gotoMemory);
}
m_moduleTreeContextMenu->addSeparator();
QAction* moduleViewAction = new QAction(tr("Module Tree"), m_ui.treeModules);
moduleViewAction->setCheckable(true);
moduleViewAction->setChecked(m_moduleView);
connect(moduleViewAction, &QAction::triggered, [this] {
m_moduleView = !m_moduleView;
m_ui.treeModules->setVisible(m_moduleView);
m_ui.listFunctions->setVisible(!m_moduleView);
updateFunctionList();
});
m_moduleTreeContextMenu->addAction(moduleViewAction);
m_moduleTreeContextMenu->popup(m_ui.treeModules->viewport()->mapToGlobal(pos));
}
void CpuWidget::onModuleTreeDoubleClick(QTreeWidgetItem* item)
{
if (item->data(0, Qt::UserRole).isValid())
{
m_ui.disassemblyWidget->gotoAddress(item->data(0, Qt::UserRole).toUInt());
}
}
void CpuWidget::updateStackFrames()
{
m_stackModel.refreshData();
@ -873,14 +656,14 @@ void CpuWidget::onStackListDoubleClick(const QModelIndex& index)
{
case StackModel::StackModel::ENTRY:
case StackModel::StackModel::ENTRY_LABEL:
m_ui.disassemblyWidget->gotoAddress(m_ui.stackList->model()->data(m_ui.stackList->model()->index(index.row(), StackModel::StackColumns::ENTRY), Qt::UserRole).toUInt());
m_ui.disassemblyWidget->gotoAddressAndSetFocus(m_ui.stackList->model()->data(m_ui.stackList->model()->index(index.row(), StackModel::StackColumns::ENTRY), Qt::UserRole).toUInt());
break;
case StackModel::StackModel::SP:
m_ui.memoryviewWidget->gotoAddress(m_ui.stackList->model()->data(index, Qt::UserRole).toUInt());
m_ui.tabWidget->setCurrentWidget(m_ui.tab_memory);
break;
default: // Default to PC
m_ui.disassemblyWidget->gotoAddress(m_ui.stackList->model()->data(m_ui.stackList->model()->index(index.row(), StackModel::StackColumns::PC), Qt::UserRole).toUInt());
m_ui.disassemblyWidget->gotoAddressAndSetFocus(m_ui.stackList->model()->data(m_ui.stackList->model()->index(index.row(), StackModel::StackColumns::PC), Qt::UserRole).toUInt());
break;
}
}

View File

@ -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;
};

View File

@ -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();
}

View File

@ -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:

View File

@ -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();

View File

@ -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:

View File

@ -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,

View File

@ -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)

View File

@ -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

View File

@ -2,7 +2,7 @@
// SPDX-License-Identifier: GPL-3.0+
#include "Breakpoints.h"
#include "SymbolMap.h"
#include "SymbolGuardian.h"
#include "MIPSAnalyst.h"
#include <cstdio>
#include "R5900.h"

View File

@ -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<u16>(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<u32> DebugInterface::getCallerStackPointer(const ccc::Function& currentFunction)
{
u32 sp = getRegister(EECAT_GPR, 29);
u32 pc = getPC();
if (pc != currentFunction.address().value)
{
std::optional<u32> stack_frame_size = getStackFrameSize(currentFunction);
if (!stack_frame_size.has_value())
return std::nullopt;
sp += *stack_frame_size;
}
return sp;
}
std::optional<u32> DebugInterface::getStackFrameSize(const ccc::Function& function)
{
s32 stack_frame_size = function.stack_frame_size;
if (stack_frame_size < 0)
{
// The stack frame size isn't stored in the symbol table, so we try
// to extract it from the code by checking for an instruction at the
// start of the current function that is in the form of
// "addui $sp, $sp, frame_size" instead.
u32 instruction = read32(function.address().value);
if ((instruction & 0xffff0000) == 0x27bd0000)
stack_frame_size = -(instruction & 0xffff);
if (stack_frame_size < 0)
return std::nullopt;
}
return (u32)stack_frame_size;
}
bool DebugInterface::initExpression(const char* exp, PostfixExpression& dest)
{
MipsExpressionFunctions funcs(this);
@ -375,6 +416,14 @@ void R5900DebugInterface::write8(u32 address, u8 value)
memWrite8(address, value);
}
void R5900DebugInterface::write16(u32 address, u16 value)
{
if (!isValidAddress(address))
return;
memWrite16(address, value);
}
void R5900DebugInterface::write32(u32 address, u32 value)
{
if (!isValidAddress(address))
@ -383,6 +432,21 @@ void R5900DebugInterface::write32(u32 address, u32 value)
memWrite32(address, value);
}
void R5900DebugInterface::write64(u32 address, u64 value)
{
if (!isValidAddress(address))
return;
memWrite64(address, value);
}
void R5900DebugInterface::write128(u32 address, u128 value)
{
if (!isValidAddress(address))
return;
memWrite128(address, value);
}
int R5900DebugInterface::getRegisterCategoryCount()
{
@ -727,9 +791,9 @@ u32 R5900DebugInterface::getCycles()
return cpuRegs.cycle;
}
SymbolMap& R5900DebugInterface::GetSymbolMap() const
SymbolGuardian& R5900DebugInterface::GetSymbolGuardian() const
{
return R5900SymbolMap;
return R5900SymbolGuardian;
}
std::vector<std::unique_ptr<BiosThread>> R5900DebugInterface::GetThreadList() const
@ -788,7 +852,6 @@ u32 R3000DebugInterface::read32(u32 address, bool& valid)
if (!(valid = isValidAddress(address)))
return -1;
return iopMemRead32(address);
}
u64 R3000DebugInterface::read64(u32 address)
@ -815,6 +878,14 @@ void R3000DebugInterface::write8(u32 address, u8 value)
iopMemWrite8(address, value);
}
void R3000DebugInterface::write16(u32 address, u16 value)
{
if (!isValidAddress(address))
return;
iopMemWrite16(address, value);
}
void R3000DebugInterface::write32(u32 address, u32 value)
{
if (!isValidAddress(address))
@ -823,6 +894,26 @@ void R3000DebugInterface::write32(u32 address, u32 value)
iopMemWrite32(address, value);
}
void R3000DebugInterface::write64(u32 address, u64 value)
{
if (!isValidAddress(address))
return;
iopMemWrite32(address + 0, value);
iopMemWrite32(address + 4, value >> 32);
}
void R3000DebugInterface::write128(u32 address, u128 value)
{
if (!isValidAddress(address))
return;
iopMemWrite32(address + 0x0, value._u32[0]);
iopMemWrite32(address + 0x4, value._u32[1]);
iopMemWrite32(address + 0x8, value._u32[2]);
iopMemWrite32(address + 0xc, value._u32[3]);
}
int R3000DebugInterface::getRegisterCategoryCount()
{
return IOPCAT_COUNT;
@ -1019,9 +1110,9 @@ u32 R3000DebugInterface::getCycles()
return psxRegs.cycle;
}
SymbolMap& R3000DebugInterface::GetSymbolMap() const
SymbolGuardian& R3000DebugInterface::GetSymbolGuardian() const
{
return R3000SymbolMap;
return R3000SymbolGuardian;
}
std::vector<std::unique_ptr<BiosThread>> R3000DebugInterface::GetThreadList() const

View File

@ -5,7 +5,7 @@
#include "DebugTools/BiosDebugData.h"
#include "MemoryTypes.h"
#include "ExpressionParser.h"
#include "SymbolMap.h"
#include "SymbolGuardian.h"
#include <string>
@ -51,7 +51,10 @@ public:
virtual u64 read64(u32 address, bool& valid) = 0;
virtual u128 read128(u32 address) = 0;
virtual void write8(u32 address, u8 value) = 0;
virtual void write16(u32 address, u16 value) = 0;
virtual void write32(u32 address, u32 value) = 0;
virtual void write64(u32 address, u64 value) = 0;
virtual void write128(u32 address, u128 value) = 0;
// register stuff
virtual int getRegisterCategoryCount() = 0;
@ -73,7 +76,7 @@ public:
virtual bool isValidAddress(u32 address) = 0;
virtual u32 getCycles() = 0;
virtual BreakPointCpu getCpuType() = 0;
[[nodiscard]] virtual SymbolMap& GetSymbolMap() const = 0;
[[nodiscard]] virtual SymbolGuardian& GetSymbolGuardian() const = 0;
[[nodiscard]] virtual std::vector<std::unique_ptr<BiosThread>> GetThreadList() const = 0;
bool initExpression(const char* exp, PostfixExpression& dest);
@ -84,6 +87,9 @@ public:
void resumeCpu();
char* stringFromPointer(u32 p);
std::optional<u32> getCallerStackPointer(const ccc::Function& currentFunction);
std::optional<u32> getStackFrameSize(const ccc::Function& currentFunction);
static void setPauseOnEntry(bool pauseOnEntry) { m_pause_on_entry = pauseOnEntry; };
static bool getPauseOnEntry() { return m_pause_on_entry; }
@ -104,7 +110,10 @@ public:
u64 read64(u32 address, bool& valid) override;
u128 read128(u32 address) override;
void write8(u32 address, u8 value) override;
void write16(u32 address, u16 value) override;
void write32(u32 address, u32 value) override;
void write64(u32 address, u64 value) override;
void write128(u32 address, u128 value) override;
// register stuff
int getRegisterCategoryCount() override;
@ -121,7 +130,7 @@ public:
bool getCPCOND0() override;
void setPc(u32 newPc) override;
void setRegister(int cat, int num, u128 newValue) override;
[[nodiscard]] SymbolMap& GetSymbolMap() const override;
[[nodiscard]] SymbolGuardian& GetSymbolGuardian() const override;
[[nodiscard]] std::vector<std::unique_ptr<BiosThread>> GetThreadList() const override;
std::string disasm(u32 address, bool simplify) override;
@ -144,7 +153,10 @@ public:
u64 read64(u32 address, bool& valid) override;
u128 read128(u32 address) override;
void write8(u32 address, u8 value) override;
void write16(u32 address, u16 value) override;
void write32(u32 address, u32 value) override;
void write64(u32 address, u64 value) override;
void write128(u32 address, u128 value) override;
// register stuff
int getRegisterCategoryCount() override;
@ -161,7 +173,7 @@ public:
bool getCPCOND0() override;
void setPc(u32 newPc) override;
void setRegister(int cat, int num, u128 newValue) override;
[[nodiscard]] SymbolMap& GetSymbolMap() const override;
[[nodiscard]] SymbolGuardian& GetSymbolGuardian() const override;
[[nodiscard]] std::vector<std::unique_ptr<BiosThread>> GetThreadList() const override;
std::string disasm(u32 address, bool simplify) override;

View File

@ -30,7 +30,7 @@ static u32 computeHash(u32 address, u32 size)
}
static void parseDisasm(SymbolMap& map, const char* disasm, char* opcode, char* arguments, size_t arguments_size, bool insertSymbols)
static void parseDisasm(SymbolGuardian& guardian, const char* disasm, char* opcode, char* arguments, size_t arguments_size, bool insertSymbols)
{
if (*disasm == '(')
{
@ -64,7 +64,7 @@ static void parseDisasm(SymbolMap& map, const char* disasm, char* opcode, char*
u32 branchTarget;
sscanf(disasm+3,"0x%08x",&branchTarget);
const std::string addressSymbol = map.GetLabelName(branchTarget);
const std::string addressSymbol = guardian.SymbolStartingAtAddress(branchTarget).name;
if (!addressSymbol.empty() && insertSymbols)
{
arguments += std::snprintf(arguments, arguments_size - (arguments - arguments_start), "%s",addressSymbol.c_str());
@ -147,19 +147,50 @@ void DisassemblyManager::analyze(u32 address, u32 size = 1024)
continue;
}
SymbolInfo info;
if (!cpu->GetSymbolMap().GetSymbolInfo(&info,address,ST_ALL))
SymbolInfo info = cpu->GetSymbolGuardian().SymbolOverlappingAddress(
address, ccc::FUNCTION | ccc::GLOBAL_VARIABLE | ccc::LOCAL_VARIABLE);
if (info.descriptor.has_value())
{
switch (*info.descriptor)
{
case ccc::SymbolDescriptor::FUNCTION:
{
DisassemblyFunction* function = new DisassemblyFunction(cpu,info.address.value,info.size);
entries[info.address.value] = function;
address = info.address.value + info.size;
break;
}
case ccc::SymbolDescriptor::GLOBAL_VARIABLE:
{
DisassemblyData* data = new DisassemblyData(cpu,info.address.value,info.size,DATATYPE_WORD);
entries[info.address.value] = data;
address = info.address.value+info.size;
break;
}
case ccc::SymbolDescriptor::LOCAL_VARIABLE:
{
DisassemblyData* data = new DisassemblyData(cpu,info.address.value,info.size,DATATYPE_WORD);
entries[info.address.value] = data;
address = info.address.value+info.size;
break;
}
default:
break;
}
} else {
if (address % 4)
{
u32 next = std::min<u32>((address+3) & ~3,cpu->GetSymbolMap().GetNextSymbolAddress(address,ST_ALL));
u32 next = std::min<u32>((address+3) & ~3,cpu->GetSymbolGuardian().SymbolAfterAddress(
address, ccc::FUNCTION | ccc::GLOBAL_VARIABLE | ccc::LOCAL_VARIABLE).address.value);
DisassemblyData* data = new DisassemblyData(cpu,address,next-address,DATATYPE_BYTE);
entries[address] = data;
address = next;
continue;
}
u32 next = cpu->GetSymbolMap().GetNextSymbolAddress(address,ST_ALL);
u32 next = cpu->GetSymbolGuardian().SymbolAfterAddress(
address, ccc::FUNCTION | ccc::GLOBAL_VARIABLE | ccc::LOCAL_VARIABLE).address.value;
if ((next % 4) && next != 0xFFFFFFFF)
{
@ -181,26 +212,6 @@ void DisassemblyManager::analyze(u32 address, u32 size = 1024)
address = next;
continue;
}
switch (info.type)
{
case ST_FUNCTION:
{
DisassemblyFunction* function = new DisassemblyFunction(cpu,info.address,info.size);
entries[info.address] = function;
address = info.address+info.size;
}
break;
case ST_DATA:
{
DisassemblyData* data = new DisassemblyData(cpu,info.address,info.size,cpu->GetSymbolMap().GetDataType(info.address));
entries[info.address] = data;
address = info.address+info.size;
}
break;
default:
break;
}
}
}
@ -401,7 +412,7 @@ bool DisassemblyFunction::disassemble(u32 address, DisassemblyLineInfo& dest, bo
if (it == entries.end())
return false;
return it->second->disassemble(address,dest,simplify, simplify);
return it->second->disassemble(address,dest,insertSymbols,simplify);
}
void DisassemblyFunction::getBranchLines(u32 start, u32 size, std::vector<BranchLine>& dest)
@ -529,21 +540,23 @@ void DisassemblyFunction::load()
u32 funcPos = address;
u32 funcEnd = address+size;
u32 nextData = cpu->GetSymbolMap().GetNextSymbolAddress(funcPos-1,ST_DATA);
SymbolInfo nextData = cpu->GetSymbolGuardian().SymbolAfterAddress(
funcPos-1, ccc::GLOBAL_VARIABLE | ccc::LOCAL_VARIABLE);
u32 opcodeSequenceStart = funcPos;
while (funcPos < funcEnd)
{
if (funcPos == nextData)
if (funcPos == nextData.address.value && nextData.size > 0)
{
if (opcodeSequenceStart != funcPos)
addOpcodeSequence(opcodeSequenceStart,funcPos);
DisassemblyData* data = new DisassemblyData(cpu,funcPos,cpu->GetSymbolMap().GetDataSize(funcPos),cpu->GetSymbolMap().GetDataType(funcPos));
DisassemblyData* data = new DisassemblyData(cpu,funcPos,nextData.size,DATATYPE_WORD);
entries[funcPos] = data;
lineAddresses.push_back(funcPos);
funcPos += data->getTotalSize();
nextData = cpu->GetSymbolMap().GetNextSymbolAddress(funcPos-1,ST_DATA);
nextData = cpu->GetSymbolGuardian().SymbolAfterAddress(funcPos-1,
ccc::GLOBAL_VARIABLE | ccc::LOCAL_VARIABLE);
opcodeSequenceStart = funcPos;
continue;
}
@ -593,7 +606,7 @@ void DisassemblyFunction::load()
*/
#if 0
// lui
if (MIPS_GET_OP(opInfo.encodedOpcode) == 0x0F && funcPos < funcEnd && funcPos != nextData)
if (MIPS_GET_OP(opInfo.encodedOpcode) == 0x0F && funcPos < funcEnd && funcPos != nextData.address.value)
{
u32 next = cpu->read32(funcPos);
@ -706,7 +719,7 @@ bool DisassemblyOpcode::disassemble(u32 address, DisassemblyLineInfo& dest, bool
char opcode[64],arguments[256];
std::string dis = cpu->disasm(address,simplify);
parseDisasm(cpu->GetSymbolMap(),dis.c_str(),opcode,arguments,std::size(arguments),insertSymbols);
parseDisasm(cpu->GetSymbolGuardian(),dis.c_str(),opcode,arguments,std::size(arguments),insertSymbols);
dest.type = DISTYPE_OPCODE;
dest.name = opcode;
dest.params = arguments;
@ -783,7 +796,7 @@ bool DisassemblyMacro::disassemble(u32 address, DisassemblyLineInfo& dest, bool
case MACRO_LI:
dest.name = name;
addressSymbol = cpu->GetSymbolMap().GetLabelName(immediate);
addressSymbol = cpu->GetSymbolGuardian().SymbolStartingAtAddress(immediate).name;
if (!addressSymbol.empty() && insertSymbols)
{
std::snprintf(buffer,std::size(buffer),"%s,%s",cpu->getRegisterName(0,rt),addressSymbol.c_str());
@ -799,7 +812,7 @@ bool DisassemblyMacro::disassemble(u32 address, DisassemblyLineInfo& dest, bool
case MACRO_MEMORYIMM:
dest.name = name;
addressSymbol = cpu->GetSymbolMap().GetLabelName(immediate);
addressSymbol = cpu->GetSymbolGuardian().SymbolStartingAtAddress(immediate).name;
if (!addressSymbol.empty() && insertSymbols)
{
std::snprintf(buffer,std::size(buffer),"%s,%s",cpu->getRegisterName(0,rt),addressSymbol.c_str());
@ -994,7 +1007,7 @@ void DisassemblyData::createLines()
case DATATYPE_WORD:
{
value = memRead32(pos);
const std::string label = cpu->GetSymbolMap().GetLabelName(value);
const std::string label = cpu->GetSymbolGuardian().SymbolStartingAtAddress(value).name;
if (!label.empty())
std::snprintf(buffer,std::size(buffer),"%s",label.c_str());
else

View File

@ -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
{

View File

@ -4,13 +4,10 @@
#include "MIPSAnalyst.h"
#include "Debug.h"
#include "DebugInterface.h"
#include "SymbolMap.h"
#include "DebugInterface.h"
#include "R5900.h"
#include "R5900OpcodeTables.h"
static std::vector<MIPSAnalyst::AnalyzedFunction> functions;
#define MIPS_MAKE_J(addr) (0x08000000 | ((addr)>>2))
#define MIPS_MAKE_JAL(addr) (0x0C000000 | ((addr)>>2))
#define MIPS_MAKE_JR_RA() (0x03e00008)
@ -117,12 +114,6 @@ namespace MIPSAnalyst
return INVALIDTARGET;
}
static const char *DefaultFunctionName(char buffer[256], u32 startAddr) {
std::snprintf(buffer, 256, "z_un_%08x", startAddr);
return buffer;
}
static u32 ScanAheadForJumpback(u32 fromAddr, u32 knownStart, u32 knownEnd) {
static const u32 MAX_AHEAD_SCAN = 0x1000;
// Maybe a bit high... just to make sure we don't get confused by recursive tail recursion.
@ -183,7 +174,8 @@ namespace MIPSAnalyst
return furthestJumpbackAddr;
}
void ScanForFunctions(SymbolMap& map, u32 startAddr, u32 endAddr, bool insertSymbols) {
void ScanForFunctions(ccc::SymbolDatabase& database, u32 startAddr, u32 endAddr) {
std::vector<MIPSAnalyst::AnalyzedFunction> functions;
AnalyzedFunction currentFunction = {startAddr};
u32 furthestBranch = 0;
@ -192,19 +184,14 @@ namespace MIPSAnalyst
bool isStraightLeaf = true;
bool suspectedNoReturn = false;
functions.clear();
u32 addr;
for (addr = startAddr; addr <= endAddr; addr += 4) {
// Use pre-existing symbol map info if available. May be more reliable.
SymbolInfo syminfo;
if (map.GetSymbolInfo(&syminfo, addr, ST_FUNCTION)) {
addr = syminfo.address + syminfo.size - 4;
// 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<ccc::SymbolSourceHandle> 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<ccc::Function*> symbol_result = database.functions.create_symbol(
std::move(name), function.start, *source, nullptr);
if (!symbol_result.success())
return;
symbol = *symbol_result;
}
if (symbol->size() == 0) {
symbol->set_size(function.end - function.start + 4);
}
symbol->is_no_return = function.suspectedNoReturn;
}
}

View File

@ -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 };

View File

@ -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)

View File

@ -0,0 +1,560 @@
// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
#include "SymbolGuardian.h"
#include <ccc/ast.h>
#include <ccc/elf.h>
#include <ccc/importer_flags.h>
#include <ccc/symbol_file.h>
#include "common/Console.h"
#include "common/FileSystem.h"
#include "common/StringUtil.h"
#include "common/Threading.h"
#include "DebugInterface.h"
#include "MIPSAnalyst.h"
#include "Host.h"
#include "VMManager.h"
SymbolGuardian R5900SymbolGuardian;
SymbolGuardian R3000SymbolGuardian;
struct DefaultBuiltInType
{
const char* name;
ccc::ast::BuiltInClass bclass;
};
static const std::vector<DefaultBuiltInType> DEFAULT_BUILT_IN_TYPES = {
{"char", ccc::ast::BuiltInClass::UNQUALIFIED_8},
{"signed char", ccc::ast::BuiltInClass::SIGNED_8},
{"unsigned char", ccc::ast::BuiltInClass::UNSIGNED_8},
{"short", ccc::ast::BuiltInClass::SIGNED_16},
{"unsigned short", ccc::ast::BuiltInClass::UNSIGNED_16},
{"int", ccc::ast::BuiltInClass::SIGNED_32},
{"unsigned int", ccc::ast::BuiltInClass::UNSIGNED_32},
{"unsigned", ccc::ast::BuiltInClass::UNSIGNED_32},
{"long", ccc::ast::BuiltInClass::SIGNED_64},
{"unsigned long", ccc::ast::BuiltInClass::UNSIGNED_64},
{"long long", ccc::ast::BuiltInClass::SIGNED_64},
{"unsigned long long", ccc::ast::BuiltInClass::UNSIGNED_64},
{"bool", ccc::ast::BuiltInClass::BOOL_8},
{"float", ccc::ast::BuiltInClass::FLOAT_32},
{"double", ccc::ast::BuiltInClass::FLOAT_64},
{"void", ccc::ast::BuiltInClass::VOID_TYPE},
{"s8", ccc::ast::BuiltInClass::SIGNED_8},
{"u8", ccc::ast::BuiltInClass::UNSIGNED_8},
{"s16", ccc::ast::BuiltInClass::SIGNED_16},
{"u16", ccc::ast::BuiltInClass::UNSIGNED_16},
{"s32", ccc::ast::BuiltInClass::SIGNED_32},
{"u32", ccc::ast::BuiltInClass::UNSIGNED_32},
{"s64", ccc::ast::BuiltInClass::SIGNED_64},
{"u64", ccc::ast::BuiltInClass::UNSIGNED_64},
{"s128", ccc::ast::BuiltInClass::SIGNED_128},
{"u128", ccc::ast::BuiltInClass::UNSIGNED_128},
{"f32", ccc::ast::BuiltInClass::FLOAT_32},
{"f64", ccc::ast::BuiltInClass::FLOAT_64},
};
static void error_callback(const ccc::Error& error, ccc::ErrorLevel level)
{
switch (level)
{
case ccc::ERROR_LEVEL_ERROR:
Console.Error("Error while importing symbol table: %s", error.message.c_str());
break;
case ccc::ERROR_LEVEL_WARNING:
Console.Warning("Warning while importing symbol table: %s", error.message.c_str());
break;
}
}
SymbolGuardian::SymbolGuardian()
{
ccc::set_custom_error_callback(error_callback);
}
SymbolGuardian::~SymbolGuardian()
{
}
void SymbolGuardian::Read(ReadCallback callback) const noexcept
{
m_big_symbol_lock.lock_shared();
callback(m_database);
m_big_symbol_lock.unlock_shared();
}
void SymbolGuardian::ReadWrite(ReadWriteCallback callback) noexcept
{
m_big_symbol_lock.lock();
callback(m_database);
m_big_symbol_lock.unlock();
}
void SymbolGuardian::Reset()
{
ShutdownWorkerThread();
ReadWrite([&](ccc::SymbolDatabase& database) {
database.clear();
ccc::Result<ccc::SymbolSourceHandle> source = database.get_symbol_source("Built-in");
if (!source.success())
return;
// Create some built-in data type symbols so that users still have some
// types to use even if there isn't a symbol table loaded. Maybe in the
// future we could add PS2-specific types like DMA tags here too.
for (const DefaultBuiltInType& default_type : DEFAULT_BUILT_IN_TYPES)
{
ccc::Result<ccc::DataType*> symbol = database.data_types.create_symbol(default_type.name, *source, nullptr);
if (!symbol.success())
return;
std::unique_ptr<ccc::ast::BuiltIn> type = std::make_unique<ccc::ast::BuiltIn>();
type->name = default_type.name;
type->size_bytes = ccc::ast::builtin_class_size(default_type.bclass);
type->bclass = default_type.bclass;
(*symbol)->set_type(std::move(type));
}
});
}
static void CreateBuiltInDataType(
ccc::SymbolDatabase& database, ccc::SymbolSourceHandle source, const char* name, ccc::ast::BuiltInClass bclass)
{
ccc::Result<ccc::DataType*> symbol = database.data_types.create_symbol(name, source, nullptr);
if (!symbol.success())
return;
std::unique_ptr<ccc::ast::BuiltIn> type = std::make_unique<ccc::ast::BuiltIn>();
type->name = name;
type->size_bytes = ccc::ast::builtin_class_size(bclass);
type->bclass = bclass;
(*symbol)->set_type(std::move(type));
}
void SymbolGuardian::ImportElf(std::vector<u8> elf, std::string elf_file_name, const std::string& nocash_path)
{
ccc::Result<ccc::ElfFile> parsed_elf = ccc::ElfFile::parse(std::move(elf));
if (!parsed_elf.success())
{
ccc::report_error(parsed_elf.error());
return;
}
std::unique_ptr<ccc::ElfSymbolFile> symbol_file =
std::make_unique<ccc::ElfSymbolFile>(std::move(*parsed_elf), std::move(elf_file_name));
ShutdownWorkerThread();
m_import_thread = std::thread([this, nocash_path, file = std::move(symbol_file)]() {
Threading::SetNameOfCurrentThread("Symbol Worker");
ccc::SymbolDatabase temp_database;
ImportSymbolTables(temp_database, *file, &m_interrupt_import_thread);
if (m_interrupt_import_thread)
return;
ImportNocashSymbols(temp_database, nocash_path);
if (m_interrupt_import_thread)
return;
ComputeOriginalFunctionHashes(temp_database, file->elf());
if (m_interrupt_import_thread)
return;
// Wait for the ELF to be loaded into memory, otherwise the call to
// ScanForFunctions below won't work.
while (true)
{
bool has_booted_elf = false;
Host::RunOnCPUThread([&]() {
has_booted_elf = VMManager::Internal::HasBootedELF();
},
true);
if (has_booted_elf)
break;
std::this_thread::sleep_for(std::chrono::milliseconds(100));
if (m_interrupt_import_thread)
return;
}
Host::RunOnCPUThread([this, &temp_database, &file, nocash_path]() {
ReadWrite([&](ccc::SymbolDatabase& database) {
database.merge_from(temp_database);
const ccc::ElfProgramHeader* entry_segment = file->elf().entry_point_segment();
if (entry_segment)
MIPSAnalyst::ScanForFunctions(database, entry_segment->vaddr, entry_segment->vaddr + entry_segment->filesz);
});
},
true);
});
}
void SymbolGuardian::ShutdownWorkerThread()
{
m_interrupt_import_thread = true;
if (m_import_thread.joinable())
m_import_thread.join();
m_interrupt_import_thread = false;
}
ccc::ModuleHandle SymbolGuardian::ImportSymbolTables(
ccc::SymbolDatabase& database, const ccc::SymbolFile& symbol_file, const std::atomic_bool* interrupt)
{
ccc::Result<std::vector<std::unique_ptr<ccc::SymbolTable>>> symbol_tables = symbol_file.get_all_symbol_tables();
if (!symbol_tables.success())
{
ccc::report_error(symbol_tables.error());
return ccc::ModuleHandle();
}
ccc::DemanglerFunctions demangler;
u32 importer_flags =
ccc::DEMANGLE_PARAMETERS |
ccc::DEMANGLE_RETURN_TYPE |
ccc::NO_MEMBER_FUNCTIONS |
ccc::NO_OPTIMIZED_OUT_FUNCTIONS |
ccc::UNIQUE_FUNCTIONS;
ccc::Result<ccc::ModuleHandle> module_handle = ccc::import_symbol_tables(
database, symbol_file.name(), *symbol_tables, importer_flags, demangler, interrupt);
if (!module_handle.success())
{
ccc::report_error(module_handle.error());
return ccc::ModuleHandle();
}
Console.WriteLn("Imported %d symbols.", database.symbol_count());
return *module_handle;
}
bool SymbolGuardian::ImportNocashSymbols(ccc::SymbolDatabase& database, const std::string& file_name)
{
ccc::Result<ccc::SymbolSourceHandle> source = database.get_symbol_source("Nocash Symbols");
if (!source.success())
return false;
FILE* f = FileSystem::OpenCFile(file_name.c_str(), "r");
if (!f)
return false;
while (!feof(f))
{
char line[256], value[256] = {0};
char* p = fgets(line, 256, f);
if (p == NULL)
break;
if (char* end = strchr(line, '\n'))
*end = '\0';
u32 address;
if (sscanf(line, "%08x %255s", &address, value) != 2)
continue;
if (address == 0 && strcmp(value, "0") == 0)
continue;
if (value[0] == '.')
{
// data directives
char* s = strchr(value, ':');
if (s != NULL)
{
*s = 0;
u32 size = 0;
if (sscanf(s + 1, "%04x", &size) != 1)
continue;
std::unique_ptr<ccc::ast::BuiltIn> scalar_type = std::make_unique<ccc::ast::BuiltIn>();
if (StringUtil::Strcasecmp(value, ".byt") == 0)
{
scalar_type->size_bytes = 1;
scalar_type->bclass = ccc::ast::BuiltInClass::UNSIGNED_8;
}
else if (StringUtil::Strcasecmp(value, ".wrd") == 0)
{
scalar_type->size_bytes = 2;
scalar_type->bclass = ccc::ast::BuiltInClass::UNSIGNED_16;
}
else if (StringUtil::Strcasecmp(value, ".dbl") == 0)
{
scalar_type->size_bytes = 4;
scalar_type->bclass = ccc::ast::BuiltInClass::UNSIGNED_32;
}
else if (StringUtil::Strcasecmp(value, ".asc") == 0)
{
scalar_type->size_bytes = 1;
scalar_type->bclass = ccc::ast::BuiltInClass::UNQUALIFIED_8;
}
else
{
continue;
}
ccc::Result<ccc::GlobalVariable*> global_variable = database.global_variables.create_symbol(
line, address, *source, nullptr);
if (!global_variable.success())
{
fclose(f);
return false;
}
if (scalar_type->size_bytes == (s32)size)
{
(*global_variable)->set_type(std::move(scalar_type));
}
else
{
std::unique_ptr<ccc::ast::Array> array = std::make_unique<ccc::ast::Array>();
array->size_bytes = (s32)size;
array->element_type = std::move(scalar_type);
array->element_count = size / array->element_type->size_bytes;
(*global_variable)->set_type(std::move(array));
}
}
}
else
{ // labels
u32 size = 1;
char* seperator = strchr(value, ',');
if (seperator != NULL)
{
*seperator = 0;
sscanf(seperator + 1, "%08x", &size);
}
if (size != 1)
{
ccc::Result<ccc::Function*> function = database.functions.create_symbol(value, address, *source, nullptr);
if (!function.success())
{
fclose(f);
return false;
}
(*function)->set_size(size);
}
else
{
ccc::Result<ccc::Label*> label = database.labels.create_symbol(value, address, *source, nullptr);
if (!label.success())
{
fclose(f);
return false;
}
}
}
}
fclose(f);
return true;
}
void SymbolGuardian::ComputeOriginalFunctionHashes(ccc::SymbolDatabase& database, const ccc::ElfFile& elf)
{
for (ccc::Function& function : database.functions)
{
if (!function.address().valid())
continue;
if (function.size() == 0)
continue;
ccc::Result<std::span<const u32>> text = elf.get_array_virtual<u32>(
function.address().value, function.size() / 4);
if (!text.success())
{
ccc::report_warning(text.error());
break;
}
ccc::FunctionHash hash;
for (u32 instruction : *text)
hash.update(instruction);
function.set_original_hash(hash.get());
}
}
void SymbolGuardian::UpdateFunctionHashes(DebugInterface& cpu)
{
ReadWrite([&](ccc::SymbolDatabase& database) {
for (ccc::Function& function : database.functions)
{
if (!function.address().valid())
continue;
if (function.size() == 0)
continue;
ccc::FunctionHash hash;
for (u32 i = 0; i < function.size() / 4; i++)
hash.update(cpu.read32(function.address().value + i * 4));
function.set_current_hash(hash);
}
for (ccc::SourceFile& source_file : database.source_files)
source_file.check_functions_match(database);
});
}
void SymbolGuardian::ClearIrxModules()
{
ReadWrite([&](ccc::SymbolDatabase& database) {
std::vector<ccc::ModuleHandle> irx_modules;
for (const ccc::Module& module : m_database.modules)
if (module.is_irx)
irx_modules.emplace_back(module.handle());
for (ccc::ModuleHandle module : irx_modules)
m_database.destroy_symbols_from_module(module, false);
});
}
SymbolInfo SymbolGuardian::SymbolStartingAtAddress(
u32 address, u32 descriptors) const
{
SymbolInfo info;
Read([&](const ccc::SymbolDatabase& database) {
ccc::SymbolDescriptor descriptor;
const ccc::Symbol* symbol = database.symbol_starting_at_address(address, descriptors, &descriptor);
if (!symbol)
return;
info.descriptor = descriptor;
info.handle = symbol->raw_handle();
info.name = symbol->name();
info.address = symbol->address();
info.size = symbol->size();
});
return info;
}
SymbolInfo SymbolGuardian::SymbolAfterAddress(
u32 address, u32 descriptors) const
{
SymbolInfo info;
Read([&](const ccc::SymbolDatabase& database) {
ccc::SymbolDescriptor descriptor;
const ccc::Symbol* symbol = database.symbol_after_address(address, descriptors, &descriptor);
if (!symbol)
return;
info.descriptor = descriptor;
info.handle = symbol->raw_handle();
info.name = symbol->name();
info.address = symbol->address();
info.size = symbol->size();
});
return info;
}
SymbolInfo SymbolGuardian::SymbolOverlappingAddress(
u32 address, u32 descriptors) const
{
SymbolInfo info;
Read([&](const ccc::SymbolDatabase& database) {
ccc::SymbolDescriptor descriptor;
const ccc::Symbol* symbol = database.symbol_overlapping_address(address, descriptors, &descriptor);
if (!symbol)
return;
info.descriptor = descriptor;
info.handle = symbol->raw_handle();
info.name = symbol->name();
info.address = symbol->address();
info.size = symbol->size();
});
return info;
}
SymbolInfo SymbolGuardian::SymbolWithName(
const std::string& name, u32 descriptors) const
{
SymbolInfo info;
Read([&](const ccc::SymbolDatabase& database) {
ccc::SymbolDescriptor descriptor;
const ccc::Symbol* symbol = database.symbol_with_name(name, descriptors, &descriptor);
if (!symbol)
return;
info.descriptor = descriptor;
info.handle = symbol->raw_handle();
info.name = symbol->name();
info.address = symbol->address();
info.size = symbol->size();
});
return info;
}
bool SymbolGuardian::FunctionExistsWithStartingAddress(u32 address) const
{
bool exists = false;
Read([&](const ccc::SymbolDatabase& database) {
ccc::FunctionHandle handle = database.functions.first_handle_from_starting_address(address);
exists = handle.valid();
});
return exists;
}
bool SymbolGuardian::FunctionExistsThatOverlapsAddress(u32 address) const
{
bool exists = false;
Read([&](const ccc::SymbolDatabase& database) {
const ccc::Function* function = database.functions.symbol_overlapping_address(address);
exists = function != nullptr;
});
return exists;
}
FunctionInfo SymbolGuardian::FunctionStartingAtAddress(u32 address) const
{
FunctionInfo info;
Read([&](const ccc::SymbolDatabase& database) {
ccc::FunctionHandle handle = database.functions.first_handle_from_starting_address(address);
const ccc::Function* function = database.functions.symbol_from_handle(handle);
if (!function)
return;
info.handle = function->handle();
info.name = function->name();
info.address = function->address();
info.size = function->size();
info.is_no_return = function->is_no_return;
});
return info;
}
FunctionInfo SymbolGuardian::FunctionOverlappingAddress(u32 address) const
{
FunctionInfo info;
Read([&](const ccc::SymbolDatabase& database) {
const ccc::Function* function = database.functions.symbol_overlapping_address(address);
if (!function)
return;
info.handle = function->handle();
info.name = function->name();
info.address = function->address();
info.size = function->size();
info.is_no_return = function->is_no_return;
});
return info;
}

View File

@ -0,0 +1,112 @@
// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
#pragma once
#include <queue>
#include <atomic>
#include <thread>
#include <functional>
#include <shared_mutex>
#include <ccc/symbol_database.h>
#include <ccc/symbol_file.h>
#include "common/Pcsx2Types.h"
class DebugInterface;
struct SymbolInfo
{
std::optional<ccc::SymbolDescriptor> descriptor;
u32 handle = (u32)-1;
std::string name;
ccc::Address address;
u32 size = 0;
};
struct FunctionInfo
{
ccc::FunctionHandle handle;
std::string name;
ccc::Address address;
u32 size = 0;
bool is_no_return = false;
};
struct SymbolGuardian
{
public:
SymbolGuardian();
SymbolGuardian(const SymbolGuardian& rhs) = delete;
SymbolGuardian(SymbolGuardian&& rhs) = delete;
~SymbolGuardian();
SymbolGuardian& operator=(const SymbolGuardian& rhs) = delete;
SymbolGuardian& operator=(SymbolGuardian&& rhs) = delete;
using ReadCallback = std::function<void(const ccc::SymbolDatabase&)>;
using ReadWriteCallback = std::function<void(ccc::SymbolDatabase&)>;
// Take a shared lock on the symbol database and run the callback.
void Read(ReadCallback callback) const noexcept;
// Take an exclusive lock on the symbol database and run the callback.
void ReadWrite(ReadWriteCallback callback) noexcept;
// Delete all stored symbols and create some default built-ins. Should be
// called from the CPU thread.
void Reset();
// Import symbols from the ELF file, nocash symbols, and scan for functions.
// Should be called from the CPU thread.
void ImportElf(std::vector<u8> elf, std::string elf_file_name, const std::string& nocash_path);
// Interrupt the import thread. Should be called from the CPU thread.
void ShutdownWorkerThread();
static ccc::ModuleHandle ImportSymbolTables(
ccc::SymbolDatabase& database, const ccc::SymbolFile& symbol_file, const std::atomic_bool* interrupt);
static bool ImportNocashSymbols(ccc::SymbolDatabase& database, const std::string& file_name);
// Compute original hashes for all the functions based on the code stored in
// the ELF file.
static void ComputeOriginalFunctionHashes(ccc::SymbolDatabase& database, const ccc::ElfFile& elf);
// Compute new hashes for all the functions to check if any of them have
// been overwritten.
void UpdateFunctionHashes(DebugInterface& cpu);
// Delete all symbols from modules that have the "is_irx" flag set.
void ClearIrxModules();
// Copy commonly used attributes of a symbol into a temporary object.
SymbolInfo SymbolStartingAtAddress(
u32 address, u32 descriptors = ccc::ALL_SYMBOL_TYPES) const;
SymbolInfo SymbolAfterAddress(
u32 address, u32 descriptors = ccc::ALL_SYMBOL_TYPES) const;
SymbolInfo SymbolOverlappingAddress(
u32 address, u32 descriptors = ccc::ALL_SYMBOL_TYPES) const;
SymbolInfo SymbolWithName(
const std::string& name, u32 descriptors = ccc::ALL_SYMBOL_TYPES) const;
bool FunctionExistsWithStartingAddress(u32 address) const;
bool FunctionExistsThatOverlapsAddress(u32 address) const;
// Copy commonly used attributes of a function so they can be used by the
// calling thread without needing to keep the lock held.
FunctionInfo FunctionStartingAtAddress(u32 address) const;
FunctionInfo FunctionOverlappingAddress(u32 address) const;
protected:
ccc::SymbolDatabase m_database;
mutable std::shared_mutex m_big_symbol_lock;
std::thread m_import_thread;
std::atomic_bool m_interrupt_import_thread = false;
std::queue<ccc::SymbolDatabase> m_load_queue;
std::mutex m_load_queue_lock;
};
extern SymbolGuardian R5900SymbolGuardian;
extern SymbolGuardian R3000SymbolGuardian;

View File

@ -1,595 +0,0 @@
// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
#include "SymbolMap.h"
#include "common/FileSystem.h"
#include "common/StringUtil.h"
#include <algorithm>
SymbolMap R5900SymbolMap;
SymbolMap R3000SymbolMap;
void SymbolMap::SortSymbols()
{
std::lock_guard<std::recursive_mutex> guard(m_lock);
AssignFunctionIndices();
}
void SymbolMap::Clear()
{
std::lock_guard<std::recursive_mutex> guard(m_lock);
functions.clear();
labels.clear();
data.clear();
modules.clear();
}
bool SymbolMap::LoadNocashSym(const std::string& filename)
{
std::lock_guard<std::recursive_mutex> guard(m_lock);
FILE* f = FileSystem::OpenCFile(filename.c_str(), "r");
if (!f)
return false;
while (!feof(f))
{
char line[256], value[256] = {0};
char* p = fgets(line, 256, f);
if (p == NULL)
break;
u32 address;
if (sscanf(line, "%08X %s", &address, value) != 2)
continue;
if (address == 0 && strcmp(value, "0") == 0)
continue;
if (value[0] == '.')
{
// data directives
char* s = strchr(value, ':');
if (s != NULL)
{
*s = 0;
u32 size = 0;
if (sscanf(s + 1, "%04X", &size) != 1)
continue;
if (StringUtil::Strcasecmp(value, ".byt") == 0)
{
AddData(address, size, DATATYPE_BYTE, 0);
}
else if (StringUtil::Strcasecmp(value, ".wrd") == 0)
{
AddData(address, size, DATATYPE_HALFWORD, 0);
}
else if (StringUtil::Strcasecmp(value, ".dbl") == 0)
{
AddData(address, size, DATATYPE_WORD, 0);
}
else if (StringUtil::Strcasecmp(value, ".asc") == 0)
{
AddData(address, size, DATATYPE_ASCII, 0);
}
}
}
else
{ // labels
int size = 1;
char* seperator = strchr(value, ',');
if (seperator != NULL)
{
*seperator = 0;
sscanf(seperator + 1, "%08X", &size);
}
if (size != 1)
{
AddFunction(value, address, size);
}
else
{
AddLabel(value, address);
}
}
}
fclose(f);
return true;
}
SymbolType SymbolMap::GetSymbolType(u32 address) const
{
std::lock_guard<std::recursive_mutex> guard(m_lock);
if (functions.find(address) != functions.end())
return ST_FUNCTION;
if (data.find(address) != data.end())
return ST_DATA;
return ST_NONE;
}
bool SymbolMap::GetSymbolInfo(SymbolInfo* info, u32 address, SymbolType symmask) const
{
u32 functionAddress = INVALID_ADDRESS;
u32 dataAddress = INVALID_ADDRESS;
if (symmask & ST_FUNCTION)
functionAddress = GetFunctionStart(address);
if (symmask & ST_DATA)
dataAddress = GetDataStart(address);
if (functionAddress == INVALID_ADDRESS || dataAddress == INVALID_ADDRESS)
{
if (functionAddress != INVALID_ADDRESS)
{
if (info != NULL)
{
info->type = ST_FUNCTION;
info->address = functionAddress;
info->size = GetFunctionSize(functionAddress);
}
return true;
}
if (dataAddress != INVALID_ADDRESS)
{
if (info != NULL)
{
info->type = ST_DATA;
info->address = dataAddress;
info->size = GetDataSize(dataAddress);
}
return true;
}
return false;
}
// if both exist, return the function
if (info != NULL)
{
info->type = ST_FUNCTION;
info->address = functionAddress;
info->size = GetFunctionSize(functionAddress);
}
return true;
}
u32 SymbolMap::GetNextSymbolAddress(u32 address, SymbolType symmask)
{
std::lock_guard<std::recursive_mutex> guard(m_lock);
const auto functionEntry = symmask & ST_FUNCTION ? functions.upper_bound(address) : functions.end();
const auto dataEntry = symmask & ST_DATA ? data.upper_bound(address) : data.end();
if (functionEntry == functions.end() && dataEntry == data.end())
return INVALID_ADDRESS;
u32 funcAddress = (functionEntry != functions.end()) ? functionEntry->first : 0xFFFFFFFF;
u32 dataAddress = (dataEntry != data.end()) ? dataEntry->first : 0xFFFFFFFF;
if (funcAddress <= dataAddress)
return funcAddress;
else
return dataAddress;
}
std::string SymbolMap::GetDescription(unsigned int address) const
{
std::lock_guard<std::recursive_mutex> guard(m_lock);
std::string labelName;
u32 funcStart = GetFunctionStart(address);
if (funcStart != INVALID_ADDRESS)
{
labelName = GetLabelName(funcStart);
}
else
{
u32 dataStart = GetDataStart(address);
if (dataStart != INVALID_ADDRESS)
labelName = GetLabelName(dataStart);
}
if (!labelName.empty())
return labelName;
char descriptionTemp[256];
std::snprintf(descriptionTemp, std::size(descriptionTemp), "(%08x)", address);
return descriptionTemp;
}
std::vector<SymbolEntry> SymbolMap::GetAllSymbols(SymbolType symmask) const
{
std::vector<SymbolEntry> result;
if (symmask & ST_FUNCTION)
{
std::lock_guard<std::recursive_mutex> guard(m_lock);
for (auto it = functions.begin(); it != functions.end(); it++)
{
SymbolEntry entry;
entry.address = it->first;
entry.size = GetFunctionSize(entry.address);
entry.name = GetLabelName(entry.address);
result.push_back(entry);
}
}
if (symmask & ST_DATA)
{
std::lock_guard<std::recursive_mutex> guard(m_lock);
for (auto it = data.begin(); it != data.end(); it++)
{
SymbolEntry entry;
entry.address = it->first;
entry.size = GetDataSize(entry.address);
entry.name = GetLabelName(entry.address);
result.push_back(entry);
}
}
return result;
}
void SymbolMap::AddFunction(const std::string& name, u32 address, u32 size, bool noReturn)
{
std::lock_guard<std::recursive_mutex> guard(m_lock);
auto existing = functions.find(address);
if (existing != functions.end())
{
existing->second.size = size;
}
else
{
FunctionEntry func;
func.start = address;
func.size = size;
func.index = (int)functions.size();
func.name = name;
func.noReturn = noReturn;
functions[address] = func;
functions.insert(std::make_pair(address, func));
}
AddLabel(name, address);
}
u32 SymbolMap::GetFunctionStart(u32 address) const
{
std::lock_guard<std::recursive_mutex> guard(m_lock);
auto it = functions.upper_bound(address);
if (it == functions.end())
{
// check last element
auto rit = functions.rbegin();
if (rit != functions.rend())
{
u32 start = rit->first;
u32 size = rit->second.size;
if (start <= address && start + size > address)
return start;
}
// otherwise there's no function that contains this address
return INVALID_ADDRESS;
}
if (it != functions.begin())
{
it--;
u32 start = it->first;
u32 size = it->second.size;
if (start <= address && start + size > address)
return start;
}
return INVALID_ADDRESS;
}
u32 SymbolMap::GetFunctionSize(u32 startAddress) const
{
std::lock_guard<std::recursive_mutex> guard(m_lock);
auto it = functions.find(startAddress);
if (it == functions.end())
return INVALID_ADDRESS;
return it->second.size;
}
int SymbolMap::GetFunctionNum(u32 address) const
{
std::lock_guard<std::recursive_mutex> guard(m_lock);
u32 start = GetFunctionStart(address);
if (start == INVALID_ADDRESS)
return INVALID_ADDRESS;
auto it = functions.find(start);
if (it == functions.end())
return INVALID_ADDRESS;
return it->second.index;
}
bool SymbolMap::GetFunctionNoReturn(u32 address) const
{
std::lock_guard<std::recursive_mutex> guard(m_lock);
auto it = functions.find(address);
if (it == functions.end())
return false;
return it->second.noReturn;
}
void SymbolMap::AssignFunctionIndices()
{
std::lock_guard<std::recursive_mutex> guard(m_lock);
int index = 0;
auto begin = functions.lower_bound(0);
auto end = functions.upper_bound(0xFFFFFFFF);
for (auto it = begin; it != end; ++it)
{
it->second.index = index++;
}
}
bool SymbolMap::SetFunctionSize(u32 startAddress, u32 newSize)
{
std::lock_guard<std::recursive_mutex> guard(m_lock);
auto funcInfo = functions.find(startAddress);
if (funcInfo != functions.end())
{
funcInfo->second.size = newSize;
}
// TODO: check for overlaps
return true;
}
bool SymbolMap::RemoveFunction(u32 startAddress)
{
std::lock_guard<std::recursive_mutex> guard(m_lock);
auto it = functions.find(startAddress);
if (it == functions.end())
return false;
functions.erase(it);
labels.erase(startAddress);
return true;
}
void SymbolMap::AddLabel(const std::string& name, u32 address)
{
std::lock_guard<std::recursive_mutex> guard(m_lock);
auto existing = labels.find(address);
if (existing != labels.end())
{
// Adding a function will automatically call this.
// We don't want to overwrite the name if it's already set because
// label names are added before our functions
}
else
{
LabelEntry label;
label.addr = address;
label.name = name;
labels[address] = label;
}
}
void SymbolMap::SetLabelName(const std::string& name, u32 address)
{
std::lock_guard<std::recursive_mutex> guard(m_lock);
auto labelInfo = labels.find(address);
if (labelInfo == labels.end())
{
AddLabel(name, address);
}
else
{
labelInfo->second.name = name;
}
}
std::string SymbolMap::GetLabelName(u32 address) const
{
std::lock_guard<std::recursive_mutex> guard(m_lock);
auto it = labels.find(address);
if (it == labels.end())
return "";
return it->second.name;
}
bool SymbolMap::GetLabelValue(const std::string& name, u32& dest)
{
std::lock_guard<std::recursive_mutex> guard(m_lock);
for (auto it = labels.begin(); it != labels.end(); it++)
{
if (name == it->second.name)
{
dest = it->first;
return true;
}
}
return false;
}
void SymbolMap::AddData(u32 address, u32 size, DataType type, int moduleIndex)
{
std::lock_guard<std::recursive_mutex> guard(m_lock);
auto existing = data.find(address);
if (existing != data.end())
{
existing->second.size = size;
existing->second.type = type;
}
else
{
DataEntry entry;
entry.start = address;
entry.size = size;
entry.type = type;
data[address] = entry;
}
}
u32 SymbolMap::GetDataStart(u32 address) const
{
std::lock_guard<std::recursive_mutex> guard(m_lock);
auto it = data.upper_bound(address);
if (it == data.end())
{
// check last element
auto rit = data.rbegin();
if (rit != data.rend())
{
u32 start = rit->first;
u32 size = rit->second.size;
if (start <= address && start + size > address)
return start;
}
// otherwise there's no data that contains this address
return INVALID_ADDRESS;
}
if (it != data.begin())
{
it--;
u32 start = it->first;
u32 size = it->second.size;
if (start <= address && start + size > address)
return start;
}
return INVALID_ADDRESS;
}
u32 SymbolMap::GetDataSize(u32 startAddress) const
{
std::lock_guard<std::recursive_mutex> guard(m_lock);
auto it = data.find(startAddress);
if (it == data.end())
return INVALID_ADDRESS;
return it->second.size;
}
DataType SymbolMap::GetDataType(u32 startAddress) const
{
std::lock_guard<std::recursive_mutex> guard(m_lock);
auto it = data.find(startAddress);
if (it == data.end())
return DATATYPE_NONE;
return it->second.type;
}
bool SymbolMap::AddModule(const std::string& name, ModuleVersion version)
{
std::lock_guard<std::recursive_mutex> guard(m_lock);
auto it = modules.find(name);
if (it != modules.end())
{
for (auto [itr, end] = modules.equal_range(name); itr != end; ++itr)
{
// Different major versions, we treat this one as a different module
if (itr->second.version.major != version.major)
continue;
// RegisterLibraryEntries will fail if the new minor ver is <= the old minor ver
// and the major version is the same
if (itr->second.version.minor >= version.minor)
return false;
// Remove the old module and its export table
RemoveModule(name, itr->second.version);
break;
}
}
modules.insert(std::make_pair(name, ModuleEntry{name, version, {}}));
return true;
}
void SymbolMap::AddModuleExport(const std::string& module, ModuleVersion version, const std::string& name, u32 address, u32 size)
{
std::lock_guard<std::recursive_mutex> guard(m_lock);
for (auto [itr, end] = modules.equal_range(module); itr != end; ++itr)
{
if (itr->second.version != version)
continue;
AddFunction(name, address, size);
AddLabel(name, address);
itr->second.exports.push_back({address, size, 0, name});
}
}
std::vector<ModuleInfo> SymbolMap::GetModules() const
{
std::lock_guard<std::recursive_mutex> guard(m_lock);
std::vector<ModuleInfo> result;
for (auto& module : modules)
{
std::vector<SymbolEntry> exports;
for (auto& fun : module.second.exports)
{
exports.push_back({fun.name, fun.start, fun.size});
}
result.push_back({module.second.name, module.second.version, exports});
}
return result;
}
void SymbolMap::RemoveModule(const std::string& name, ModuleVersion version)
{
std::lock_guard<std::recursive_mutex> guard(m_lock);
for (auto [itr, end] = modules.equal_range(name); itr != end; ++itr)
{
if (itr->second.version != version)
continue;
for (auto& exportEntry : itr->second.exports)
{
RemoveFunction(exportEntry.start);
}
modules.erase(itr);
break;
}
}
void SymbolMap::ClearModules()
{
std::lock_guard<std::recursive_mutex> guard(m_lock);
for (auto& module : modules)
{
for (auto& exportEntry : module.second.exports)
{
RemoveFunction(exportEntry.start);
}
}
modules.clear();
}

View File

@ -1,152 +0,0 @@
// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
#pragma once
#include <vector>
#include <set>
#include <map>
#include <string>
#include <mutex>
#include "common/Pcsx2Types.h"
enum SymbolType
{
ST_NONE = 0,
ST_FUNCTION = 1,
ST_DATA = 2,
ST_ALL = 3,
};
struct SymbolInfo
{
SymbolType type;
u32 address;
u32 size;
};
struct SymbolEntry
{
std::string name;
u32 address;
u32 size;
};
struct ModuleVersion
{
u8 major;
u8 minor;
friend auto operator<=>(const ModuleVersion&, const ModuleVersion&) = default;
};
struct ModuleInfo
{
std::string name;
ModuleVersion version;
std::vector<SymbolEntry> exports;
};
enum DataType
{
DATATYPE_NONE,
DATATYPE_BYTE,
DATATYPE_HALFWORD,
DATATYPE_WORD,
DATATYPE_ASCII
};
class SymbolMap
{
public:
SymbolMap() {}
void Clear();
void SortSymbols();
bool LoadNocashSym(const std::string& filename);
SymbolType GetSymbolType(u32 address) const;
bool GetSymbolInfo(SymbolInfo* info, u32 address, SymbolType symmask = ST_FUNCTION) const;
u32 GetNextSymbolAddress(u32 address, SymbolType symmask);
std::string GetDescription(unsigned int address) const;
std::vector<SymbolEntry> GetAllSymbols(SymbolType symmask) const;
void AddFunction(const std::string& name, u32 address, u32 size, bool noReturn = false);
u32 GetFunctionStart(u32 address) const;
int GetFunctionNum(u32 address) const;
bool GetFunctionNoReturn(u32 address) const;
u32 GetFunctionSize(u32 startAddress) const;
bool SetFunctionSize(u32 startAddress, u32 newSize);
bool RemoveFunction(u32 startAddress);
void AddLabel(const std::string& name, u32 address);
std::string GetLabelName(u32 address) const;
void SetLabelName(const std::string& name, u32 address);
bool GetLabelValue(const std::string& name, u32& dest);
void AddData(u32 address, u32 size, DataType type, int moduleIndex = -1);
u32 GetDataStart(u32 address) const;
u32 GetDataSize(u32 startAddress) const;
DataType GetDataType(u32 startAddress) const;
// Module functions for IOP symbols
bool AddModule(const std::string& name, ModuleVersion version);
void AddModuleExport(const std::string& module, ModuleVersion version, const std::string& name, u32 address, u32 size);
std::vector<ModuleInfo> GetModules() const;
void RemoveModule(const std::string& name, ModuleVersion version);
// Clears any modules and their associated exports
// Prefer this over Clear() so we don't clear user defined functions
// In the future we should mark functions as user defined
void ClearModules();
static const u32 INVALID_ADDRESS = (u32)-1;
bool IsEmpty() const { return functions.empty() && labels.empty() && data.empty(); };
private:
void AssignFunctionIndices();
struct FunctionEntry
{
u32 start;
u32 size;
int index;
std::string name;
bool noReturn;
};
struct LabelEntry
{
u32 addr;
std::string name;
};
struct DataEntry
{
DataType type;
u32 start;
u32 size;
};
struct ModuleEntry
{
std::string name;
ModuleVersion version;
// This is duplicated data from the function map
// The issue is that multiple exports can point to the same address
// The address we use as a key... We should use a multimap in the future
std::vector<FunctionEntry> exports;
};
std::map<u32, FunctionEntry> functions;
std::map<u32, LabelEntry> labels;
std::map<u32, DataEntry> data;
std::multimap<std::string, ModuleEntry> modules;
mutable std::recursive_mutex m_lock;
};
extern SymbolMap R5900SymbolMap;
extern SymbolMap R3000SymbolMap;

View File

@ -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, &sections_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);
}
}
}
}
}

View File

@ -117,6 +117,7 @@ public:
~ElfObject();
__fi const std::vector<u8>& GetData() const { return data; }
__fi std::vector<u8> ReleaseData() const { return std::move(data); }
__fi const ELF_HEADER& GetHeader() const { return *reinterpret_cast<const ELF_HEADER*>(data.data()); }
__fi u32 GetSize() const { return static_cast<u32>(data.size()); }

View File

@ -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<ccc::SymbolSourceHandle> source = database.get_symbol_source("IRX Export Table");
if (!source.success())
return;
// Enumerate the module symbols that already exist for this IRX
// module. Really there should only be one.
std::vector<ccc::ModuleHandle> existing_modules;
for (const auto& pair : database.modules.handles_from_name(modname))
{
const ccc::Module* existing_module = database.modules.symbol_from_handle(pair.second);
if (!existing_module || !existing_module->is_irx)
continue;
// Different major versions, we treat this one as a different module.
if (existing_module->version_major != version_major)
continue;
// RegisterLibraryEntries will fail if the new minor ver is <= the old minor ver
// and the major version is the same.
if (existing_module->version_minor >= version_minor)
return;
existing_modules.emplace_back(existing_module->handle());
}
// Destroy the old symbols for this IRX module if any exist.
for (ccc::ModuleHandle existing_module : existing_modules)
database.destroy_symbols_from_module(existing_module, true);
ccc::Result<ccc::Module*> module_symbol = database.modules.create_symbol(modname, *source, nullptr);
if (!module_symbol.success())
return;
(*module_symbol)->is_irx = true;
(*module_symbol)->version_major = version_major;
(*module_symbol)->version_minor = version_minor;
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<ccc::Function*> function = database.functions.create_symbol(qualified_name, funcptr, *source, *module_symbol);
if (!function.success())
return;
index++;
func += 4;
funcptr = iopMemRead32(func);
}
}
});
}
void ReleaseFuncs(u32 a0reg)
{
const std::string modname = iopMemReadString(a0reg + 12, 8);
ModuleVersion version = {iopMemRead8(a0 + 9), iopMemRead8(a0 + 8)};
s32 version_major = iopMemRead8(a0reg + 9);
s32 version_minor = iopMemRead8(a0reg + 8);
DevCon.WriteLn(Color_Gray, "ReleaseLibraryEntries: %8.8s version %x.%02x", modname.data(), version.major, version.minor);
DevCon.WriteLn(Color_Gray, "ReleaseLibraryEntries: %8.8s version %x.%02x", modname.c_str(), version_major, version_minor);
R3000SymbolMap.RemoveModule(modname, version);
R3000SymbolGuardian.ReadWrite([&](ccc::SymbolDatabase& database) {
// Destroy the symbols for the module.
for (const auto& pair : database.modules.handles_from_name(modname))
{
const ccc::Module* existing_module = database.modules.symbol_from_handle(pair.second);
if (!existing_module || !existing_module->is_irx)
continue;
if (existing_module->version_major != version_major || existing_module->version_minor != version_minor)
continue;
database.destroy_symbols_from_module(existing_module->handle(), true);
}
});
}
int RegisterLibraryEntries_HLE()

View File

@ -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;

View File

@ -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"

View File

@ -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()

View File

@ -153,7 +153,7 @@
<ClCompile Include="DebugTools\MipsAssembler.cpp" />
<ClCompile Include="DebugTools\MipsAssemblerTables.cpp" />
<ClCompile Include="DebugTools\MipsStackWalk.cpp" />
<ClCompile Include="DebugTools\SymbolMap.cpp" />
<ClCompile Include="DebugTools\SymbolGuardian.cpp" />
<ClCompile Include="DEV9\AdapterUtils.cpp" />
<ClCompile Include="DEV9\ATA\Commands\ATA_Command.cpp" />
<ClCompile Include="DEV9\ATA\Commands\ATA_CmdDMA.cpp" />
@ -597,7 +597,7 @@
<ClInclude Include="DebugTools\MipsAssembler.h" />
<ClInclude Include="DebugTools\MipsAssemblerTables.h" />
<ClInclude Include="DebugTools\MipsStackWalk.h" />
<ClInclude Include="DebugTools\SymbolMap.h" />
<ClInclude Include="DebugTools\SymbolGuardian.h" />
<ClInclude Include="DEV9\AdapterUtils.h" />
<ClInclude Include="DEV9\ATA\ATA.h" />
<ClInclude Include="DEV9\ATA\HddCreate.h" />

View File

@ -728,9 +728,6 @@
<ClCompile Include="CDVD\BlockdumpFileReader.cpp">
<Filter>System\ISO</Filter>
</ClCompile>
<ClCompile Include="DebugTools\SymbolMap.cpp">
<Filter>System\Ps2\Debug</Filter>
</ClCompile>
<ClCompile Include="DebugTools\DebugInterface.cpp">
<Filter>System\Ps2\Debug</Filter>
</ClCompile>
@ -1395,6 +1392,9 @@
<ClCompile Include="SIO\Pad\PadPopn.cpp">
<Filter>System\Ps2\Iop\SIO\PAD</Filter>
</ClCompile>
<ClCompile Include="DebugTools\SymbolGuardian.cpp">
<Filter>System\Ps2\Debug</Filter>
</ClCompile>
<ClCompile Include="USB\usb-eyetoy\cam-jpeg.cpp">
<Filter>System\Ps2\USB\usb-eyetoy</Filter>
</ClCompile>
@ -1661,9 +1661,6 @@
<ClInclude Include="x86\microVU_Profiler.h">
<Filter>System\Ps2\EmotionEngine\VU\Dynarec\microVU</Filter>
</ClInclude>
<ClInclude Include="DebugTools\SymbolMap.h">
<Filter>System\Ps2\Debug</Filter>
</ClInclude>
<ClInclude Include="DebugTools\DebugInterface.h">
<Filter>System\Ps2\Debug</Filter>
</ClInclude>
@ -2336,6 +2333,9 @@
<Filter>System\Ps2\Iop\SIO\PAD</Filter>
</ClInclude>
<ClInclude Include="SupportURLs.h" />
<ClInclude Include="DebugTools\SymbolGuardian.h">
<Filter>System\Ps2\Debug</Filter>
</ClInclude>
<ClInclude Include="USB\usb-eyetoy\cam-jpeg.h">
<Filter>System\Ps2\USB\usb-eyetoy</Filter>
</ClInclude>
@ -2410,4 +2410,4 @@
<Filter>System\Ps2\GS</Filter>
</Natvis>
</ItemGroup>
</Project>
</Project>

View File

@ -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