Debugger: Allow loading symbols conditionally and with a base address

This commit is contained in:
chaoticgd 2024-11-12 23:03:04 +00:00 committed by Ty
parent efb43ac7f9
commit cce0ae4369
11 changed files with 511 additions and 299 deletions

View File

@ -100,8 +100,9 @@ Result<std::unique_ptr<SymbolTable>> create_elf_symbol_table(
Result<ModuleHandle> import_symbol_tables(
SymbolDatabase& database,
std::string module_name,
const std::vector<std::unique_ptr<SymbolTable>>& symbol_tables,
std::string module_name,
Address base_address,
u32 importer_flags,
DemanglerFunctions demangler,
const std::atomic_bool* interrupt)
@ -109,7 +110,8 @@ Result<ModuleHandle> import_symbol_tables(
Result<SymbolSourceHandle> module_source = database.get_symbol_source("Symbol Table Importer");
CCC_RETURN_IF_ERROR(module_source);
Result<Module*> module_symbol = database.modules.create_symbol(std::move(module_name), *module_source, nullptr);
Result<Module*> module_symbol = database.modules.create_symbol(
std::move(module_name), base_address, *module_source, nullptr);
CCC_RETURN_IF_ERROR(module_symbol);
ModuleHandle module_handle = (*module_symbol)->handle();

View File

@ -71,8 +71,9 @@ Result<std::unique_ptr<SymbolTable>> create_elf_symbol_table(
// and to generate a module handle.
Result<ModuleHandle> import_symbol_tables(
SymbolDatabase& database,
std::string module_name,
const std::vector<std::unique_ptr<SymbolTable>>& symbol_tables,
std::string module_name,
Address base_address,
u32 importer_flags,
DemanglerFunctions demangler,
const std::atomic_bool* interrupt);

View File

@ -104,7 +104,7 @@ DebugAnalysisSettingsWidget::DebugAnalysisSettingsWidget(SettingsWindow* dialog,
else
{
m_ui.symbolFileLabel->hide();
m_ui.symbolFileList->hide();
m_ui.symbolFileTable->hide();
m_ui.importSymbolFileButtons->hide();
}
@ -165,18 +165,19 @@ void DebugAnalysisSettingsWidget::parseSettingsFromWidgets(Pcsx2Config::DebugAna
output.DemangleSymbols = m_ui.demangleSymbols->isChecked();
output.DemangleParameters = m_ui.demangleParameters->isChecked();
for (int i = 0; i < m_ui.symbolFileList->count(); i++)
for (int i = 0; i < m_symbol_file_model->rowCount(); i++)
{
DebugExtraSymbolFile& file = output.ExtraSymbolFiles.emplace_back();
file.Path = m_ui.symbolFileList->item(i)->text().toStdString();
file.Path = m_symbol_file_model->item(i, PATH_COLUMN)->text().toStdString();
file.BaseAddress = m_symbol_file_model->item(i, BASE_ADDRESS_COLUMN)->text().toStdString();
file.Condition = m_symbol_file_model->item(i, CONDITION_COLUMN)->text().toStdString();
}
output.FunctionScanMode = static_cast<DebugFunctionScanMode>(m_ui.functionScanMode->currentIndex());
output.CustomFunctionScanRange = m_ui.customAddressRange->isChecked();
output.FunctionScanStartAddress = m_ui.addressRangeStart->text().toStdString();
output.FunctionScanEndAddress = m_ui.addressRangeEnd->text().toStdString();
output.GenerateFunctionHashes = m_ui.grayOutOverwrittenFunctions->isChecked();
}
void DebugAnalysisSettingsWidget::setupSymbolSourceGrid()
@ -187,27 +188,12 @@ void DebugAnalysisSettingsWidget::setupSymbolSourceGrid()
{
// Add symbol sources for which the user has already selected whether or
// not they should be cleared.
int existing_symbol_source_count;
if (m_dialog)
existing_symbol_source_count = m_dialog->getEffectiveIntValue("Debugger/Analysis/SymbolSources", "Count", 0);
else
existing_symbol_source_count = Host::GetIntSettingValue("Debugger/Analysis/SymbolSources", "Count", 0);
int existing_symbol_source_count = getIntSettingValue("Debugger/Analysis/SymbolSources", "Count", 0);
for (int i = 0; i < existing_symbol_source_count; i++)
{
std::string section = "Debugger/Analysis/SymbolSources/" + std::to_string(i);
std::string name;
if (m_dialog)
name = m_dialog->getEffectiveStringValue(section.c_str(), "Name", "");
else
name = Host::GetStringSettingValue(section.c_str(), "Name", "");
bool value;
if (m_dialog)
value = m_dialog->getEffectiveBoolValue(section.c_str(), "ClearDuringAnalysis", false);
else
value = Host::GetBoolSettingValue(section.c_str(), "ClearDuringAnalysis", false);
std::string name = getStringSettingValue(section.c_str(), "Name", "");
bool value = getBoolSettingValue(section.c_str(), "ClearDuringAnalysis", false);
SymbolSourceTemp& source = m_symbol_sources[name];
source.previous_value = value;
@ -320,45 +306,100 @@ void DebugAnalysisSettingsWidget::saveSymbolSources()
void DebugAnalysisSettingsWidget::setupSymbolFileList()
{
int extra_symbol_file_count;
if (m_dialog)
extra_symbol_file_count = m_dialog->getEffectiveIntValue("Debugger/Analysis/ExtraSymbolFiles", "Count", 0);
else
extra_symbol_file_count = Host::GetIntSettingValue("Debugger/Analysis/ExtraSymbolFiles", "Count", 0);
m_symbol_file_model = new QStandardItemModel(0, SYMBOL_FILE_COLUMN_COUNT, m_ui.symbolFileTable);
QStringList headers;
headers.emplace_back(tr("Path"));
headers.emplace_back(tr("Base Address"));
headers.emplace_back(tr("Condition"));
m_symbol_file_model->setHorizontalHeaderLabels(headers);
m_ui.symbolFileTable->setModel(m_symbol_file_model);
m_ui.symbolFileTable->horizontalHeader()->setSectionResizeMode(PATH_COLUMN, QHeaderView::Stretch);
m_ui.symbolFileTable->horizontalHeader()->setSectionResizeMode(BASE_ADDRESS_COLUMN, QHeaderView::Fixed);
m_ui.symbolFileTable->horizontalHeader()->setSectionResizeMode(CONDITION_COLUMN, QHeaderView::Fixed);
m_ui.symbolFileTable->verticalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
int extra_symbol_file_count = getIntSettingValue("Debugger/Analysis/ExtraSymbolFiles", "Count", 0);
for (int i = 0; i < extra_symbol_file_count; i++)
{
std::string section = "Debugger/Analysis/ExtraSymbolFiles/" + std::to_string(i);
std::string path;
if (m_dialog)
path = m_dialog->getEffectiveStringValue(section.c_str(), "Path", "");
else
path = Host::GetStringSettingValue(section.c_str(), "Path", "");
m_ui.symbolFileList->addItem(QString::fromStdString(path));
int row = m_symbol_file_model->rowCount();
if (!m_symbol_file_model->insertRow(row))
continue;
QStandardItem* path_item = new QStandardItem();
path_item->setText(QString::fromStdString(getStringSettingValue(section.c_str(), "Path", "")));
m_symbol_file_model->setItem(row, PATH_COLUMN, path_item);
QStandardItem* base_address_item = new QStandardItem();
base_address_item->setText(QString::fromStdString(getStringSettingValue(section.c_str(), "BaseAddress")));
m_symbol_file_model->setItem(row, BASE_ADDRESS_COLUMN, base_address_item);
QStandardItem* condition_item = new QStandardItem();
condition_item->setText(QString::fromStdString(getStringSettingValue(section.c_str(), "Condition")));
m_symbol_file_model->setItem(row, CONDITION_COLUMN, condition_item);
}
connect(m_ui.addSymbolFile, &QPushButton::clicked, this, &DebugAnalysisSettingsWidget::addSymbolFile);
connect(m_ui.removeSymbolFile, &QPushButton::clicked, this, &DebugAnalysisSettingsWidget::removeSymbolFile);
connect(m_ui.symbolFileTable->selectionModel(), &QItemSelectionModel::selectionChanged,
this, &DebugAnalysisSettingsWidget::updateEnabledStates);
connect(m_symbol_file_model, &QStandardItemModel::dataChanged,
this, &DebugAnalysisSettingsWidget::saveSymbolFiles);
connect(m_symbol_file_model, &QStandardItemModel::dataChanged,
this, &DebugAnalysisSettingsWidget::updateEnabledStates);
}
void DebugAnalysisSettingsWidget::addSymbolFile()
{
QString path = QDir::toNativeSeparators(QFileDialog::getOpenFileName(this, tr("Add Symbol File")));
if (path.isEmpty())
std::string path = Path::ToNativePath(QFileDialog::getOpenFileName(this, tr("Add Symbol File")).toStdString());
if (path.empty())
return;
m_ui.symbolFileList->addItem(path);
std::string relative_path = Path::MakeRelative(path, EmuFolders::GameSettings);
if (!relative_path.starts_with(".."))
path = std::move(relative_path);
int row = m_symbol_file_model->rowCount();
if (!m_symbol_file_model->insertRow(row))
return;
QStandardItem* path_item = new QStandardItem();
path_item->setText(QString::fromStdString(path));
m_symbol_file_model->setItem(row, PATH_COLUMN, path_item);
QStandardItem* base_address_item = new QStandardItem();
base_address_item->setText("");
m_symbol_file_model->setItem(row, BASE_ADDRESS_COLUMN, base_address_item);
QStandardItem* condition_item = new QStandardItem();
condition_item->setText("");
m_symbol_file_model->setItem(row, CONDITION_COLUMN, condition_item);
saveSymbolFiles();
updateEnabledStates();
}
void DebugAnalysisSettingsWidget::removeSymbolFile()
{
for (QListWidgetItem* item : m_ui.symbolFileList->selectedItems())
delete item;
QItemSelectionModel* selection_model = m_ui.symbolFileTable->selectionModel();
if (!selection_model)
return;
while (!selection_model->selectedIndexes().isEmpty())
{
QModelIndex index = selection_model->selectedIndexes().first();
m_symbol_file_model->removeRow(index.row(), index.parent());
}
saveSymbolFiles();
updateEnabledStates();
}
void DebugAnalysisSettingsWidget::saveSymbolFiles()
@ -380,17 +421,24 @@ void DebugAnalysisSettingsWidget::saveSymbolFiles()
sif->RemoveSection("Debugger/Analysis/ExtraSymbolFiles");
if (m_ui.symbolFileList->count() == 0)
if (m_symbol_file_model->rowCount() == 0)
return;
// Make new configuration entries.
sif->SetIntValue("Debugger/Analysis/ExtraSymbolFiles", "Count", m_ui.symbolFileList->count());
sif->SetIntValue("Debugger/Analysis/ExtraSymbolFiles", "Count", m_symbol_file_model->rowCount());
for (int i = 0; i < m_ui.symbolFileList->count(); i++)
for (int i = 0; i < m_symbol_file_model->rowCount(); i++)
{
std::string section = "Debugger/Analysis/ExtraSymbolFiles/" + std::to_string(i);
std::string path = m_ui.symbolFileList->item(i)->text().toStdString();
sif->SetStringValue(section.c_str(), "Path", path.c_str());
if (QStandardItem* path_item = m_symbol_file_model->item(i, PATH_COLUMN))
sif->SetStringValue(section.c_str(), "Path", path_item->text().toStdString().c_str());
if (QStandardItem* base_address_item = m_symbol_file_model->item(i, BASE_ADDRESS_COLUMN))
sif->SetStringValue(section.c_str(), "BaseAddress", base_address_item->text().toStdString().c_str());
if (QStandardItem* condition_item = m_symbol_file_model->item(i, CONDITION_COLUMN))
sif->SetStringValue(section.c_str(), "Condition", condition_item->text().toStdString().c_str());
}
QtHost::SaveGameSettings(sif, true);
@ -423,5 +471,34 @@ void DebugAnalysisSettingsWidget::updateEnabledStates()
m_ui.symbolSourceScrollArea->setEnabled(!m_ui.automaticallyClearSymbols->isChecked());
m_ui.symbolSourceErrorMessage->setEnabled(!m_ui.automaticallyClearSymbols->isChecked());
m_ui.demangleParameters->setEnabled(m_ui.demangleSymbols->isChecked());
m_ui.removeSymbolFile->setEnabled(
m_ui.symbolFileTable->selectionModel() && m_ui.symbolFileTable->selectionModel()->hasSelection());
m_ui.customAddressRangeLineEdits->setEnabled(m_ui.customAddressRange->isChecked());
}
std::string DebugAnalysisSettingsWidget::getStringSettingValue(
const char* section, const char* key, const char* default_value)
{
if (m_dialog)
return m_dialog->getEffectiveStringValue(section, key, default_value);
return Host::GetStringSettingValue(section, key, default_value);
}
bool DebugAnalysisSettingsWidget::getBoolSettingValue(
const char* section, const char* key, bool default_value)
{
if (m_dialog)
return m_dialog->getEffectiveBoolValue(section, key, default_value);
return Host::GetBoolSettingValue(section, key, default_value);
}
int DebugAnalysisSettingsWidget::getIntSettingValue(
const char* section, const char* key, int default_value)
{
if (m_dialog)
return m_dialog->getEffectiveIntValue(section, key, default_value);
return Host::GetIntSettingValue(section, key, default_value);
}

View File

@ -6,6 +6,7 @@
#include "Config.h"
#include <QtGui/QStandardItemModel>
#include <QtWidgets/QDialog>
class SettingsWindow;
@ -16,7 +17,7 @@ class DebugAnalysisSettingsWidget : public QWidget
public:
// Create a widget that will discard any settings changed after it is
// closed, for use in the dialog opened by the "Reanalyze" button.
// closed, for use in the dialog opened by the "Analyze" button.
DebugAnalysisSettingsWidget(QWidget* parent = nullptr);
// Create a widget that will write back any settings changed to the config
@ -42,6 +43,10 @@ protected:
void updateEnabledStates();
std::string getStringSettingValue(const char* section, const char* key, const char* default_value = "");
bool getBoolSettingValue(const char* section, const char* key, bool default_value = false);
int getIntSettingValue(const char* section, const char* key, int default_value = 0);
struct SymbolSourceTemp
{
QCheckBox* check_box = nullptr;
@ -49,8 +54,18 @@ protected:
bool modified_by_user = false;
};
enum SymbolFileColumn
{
PATH_COLUMN = 0,
BASE_ADDRESS_COLUMN = 1,
CONDITION_COLUMN = 2,
SYMBOL_FILE_COLUMN_COUNT = 3
};
SettingsWindow* m_dialog = nullptr;
std::map<std::string, SymbolSourceTemp> m_symbol_sources;
QStandardItemModel* m_symbol_file_model;
Ui::DebugAnalysisSettingsWidget m_ui;
};

View File

@ -171,7 +171,7 @@
</widget>
</item>
<item>
<widget class="QListWidget" name="symbolFileList">
<widget class="QTableView" name="symbolFileTable">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
@ -184,9 +184,27 @@
<height>100</height>
</size>
</property>
<property name="sortingEnabled">
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="textElideMode">
<enum>Qt::ElideLeft</enum>
</property>
<property name="wordWrap">
<bool>false</bool>
</property>
<attribute name="horizontalHeaderHighlightSections">
<bool>false</bool>
</attribute>
<attribute name="horizontalHeaderStretchLastSection">
<bool>true</bool>
</attribute>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
</widget>
</item>
<item>

View File

@ -208,6 +208,8 @@ struct DebugSymbolSource
struct DebugExtraSymbolFile
{
std::string Path;
std::string BaseAddress;
std::string Condition;
friend auto operator<=>(const DebugExtraSymbolFile& lhs, const DebugExtraSymbolFile& rhs) = default;
};
@ -1276,7 +1278,7 @@ struct Pcsx2Config
EnableGameFixes : 1, // enables automatic game fixes
SaveStateOnShutdown : 1, // default value for saving state on shutdown
EnableDiscordPresence : 1, // enables discord rich presence integration
UseSavestateSelector: 1,
UseSavestateSelector : 1,
InhibitScreensaver : 1,
BackupSavestate : 1,
McdFolderAutoManage : 1,

View File

@ -34,227 +34,6 @@ enum ReferenceIndexType
REF_INDEX_VFPU = 0x10000,
REF_INDEX_VFPU_INT = 0x20000,
REF_INDEX_IS_FLOAT = REF_INDEX_FPU | REF_INDEX_VFPU,
};
class MipsExpressionFunctions : public IExpressionFunctions
{
public:
explicit MipsExpressionFunctions(DebugInterface* cpu, bool enumerateSymbols)
: m_cpu(cpu)
{
if (!enumerateSymbols)
return;
m_cpu->GetSymbolGuardian().Read([&](const ccc::SymbolDatabase& database) {
for (const ccc::Function& function : database.functions)
m_mangled_function_names_to_handles.emplace(function.mangled_name(), function.handle());
for (const ccc::GlobalVariable& global : database.global_variables)
m_mangled_global_names_to_handles.emplace(global.mangled_name(), global.handle());
});
}
virtual bool parseReference(char* str, u64& referenceIndex)
{
for (int i = 0; i < 32; i++)
{
char reg[8];
std::snprintf(reg, std::size(reg), "r%d", i);
if (StringUtil::Strcasecmp(str, reg) == 0 || StringUtil::Strcasecmp(str, m_cpu->getRegisterName(0, i)) == 0)
{
referenceIndex = i;
return true;
}
std::snprintf(reg, std::size(reg), "f%d", i);
if (StringUtil::Strcasecmp(str, reg) == 0)
{
referenceIndex = i | REF_INDEX_FPU;
return true;
}
}
if (StringUtil::Strcasecmp(str, "pc") == 0)
{
referenceIndex = REF_INDEX_PC;
return true;
}
if (StringUtil::Strcasecmp(str, "hi") == 0)
{
referenceIndex = REF_INDEX_HI;
return true;
}
if (StringUtil::Strcasecmp(str, "lo") == 0)
{
referenceIndex = REF_INDEX_LO;
return true;
}
if (StringUtil::Strcasecmp(str, "target") == 0)
{
referenceIndex = REF_INDEX_OPTARGET;
return true;
}
if (StringUtil::Strcasecmp(str, "load") == 0)
{
referenceIndex = REF_INDEX_OPLOAD;
return true;
}
if (StringUtil::Strcasecmp(str, "store") == 0)
{
referenceIndex = REF_INDEX_OPSTORE;
return true;
}
return false;
}
virtual bool parseSymbol(char* str, u64& symbolValue)
{
bool success = false;
m_cpu->GetSymbolGuardian().Read([&](const ccc::SymbolDatabase& database) {
std::string name = str;
// Check for mangled function names.
auto function_iterator = m_mangled_function_names_to_handles.find(name);
if (function_iterator != m_mangled_function_names_to_handles.end())
{
const ccc::Function* function = database.functions.symbol_from_handle(function_iterator->second);
if (function && function->address().valid())
{
symbolValue = function->address().value;
success = true;
return;
}
}
// Check for mangled global variable names.
auto global_iterator = m_mangled_global_names_to_handles.find(name);
if (global_iterator != m_mangled_global_names_to_handles.end())
{
const ccc::GlobalVariable* global = database.global_variables.symbol_from_handle(global_iterator->second);
if (global && global->address().valid())
{
symbolValue = global->address().value;
success = true;
return;
}
}
// Check for regular unmangled names.
const ccc::Symbol* symbol = database.symbol_with_name(name);
if (symbol && symbol->address().valid())
{
symbolValue = symbol->address().value;
success = true;
return;
}
});
return success;
}
virtual u64 getReferenceValue(u64 referenceIndex)
{
if (referenceIndex < 32)
return m_cpu->getRegister(0, referenceIndex)._u64[0];
if (referenceIndex == REF_INDEX_PC)
return m_cpu->getPC();
if (referenceIndex == REF_INDEX_HI)
return m_cpu->getHI()._u64[0];
if (referenceIndex == REF_INDEX_LO)
return m_cpu->getLO()._u64[0];
if (referenceIndex & REF_INDEX_IS_OPSL)
{
const u32 OP = m_cpu->read32(m_cpu->getPC());
const R5900::OPCODE& opcode = R5900::GetInstruction(OP);
if (opcode.flags & IS_MEMORY)
{
// 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);
if (referenceIndex & REF_INDEX_OPTARGET)
{
return target;
}
else if (referenceIndex & REF_INDEX_OPLOAD)
{
return (opcode.flags & IS_LOAD) ? target : 0;
}
else if (referenceIndex & REF_INDEX_OPSTORE)
{
return (opcode.flags & IS_STORE) ? target : 0;
}
}
return 0;
}
if (referenceIndex & REF_INDEX_FPU)
{
return m_cpu->getRegister(EECAT_FPR, referenceIndex & 0x1F)._u64[0];
}
return -1;
}
virtual ExpressionType getReferenceType(u64 referenceIndex)
{
if (referenceIndex & REF_INDEX_IS_FLOAT)
{
return EXPR_TYPE_FLOAT;
}
return EXPR_TYPE_UINT;
}
virtual bool getMemoryValue(u32 address, int size, u64& dest, std::string& error)
{
switch (size)
{
case 1:
case 2:
case 4:
case 8:
break;
default:
error = StringUtil::StdStringFromFormat(
TRANSLATE("ExpressionParser", "Invalid memory access size %d."), size);
return false;
}
if (address % size)
{
error = TRANSLATE("ExpressionParser", "Invalid memory access (unaligned).");
return false;
}
switch (size)
{
case 1:
dest = m_cpu->read8(address);
break;
case 2:
dest = m_cpu->read16(address);
break;
case 4:
dest = m_cpu->read32(address);
break;
case 8:
dest = m_cpu->read64(address);
break;
}
return true;
}
protected:
DebugInterface* m_cpu;
std::map<std::string, ccc::FunctionHandle> m_mangled_function_names_to_handles;
std::map<std::string, ccc::GlobalVariableHandle> m_mangled_global_names_to_handles;
};
//
@ -369,13 +148,13 @@ bool DebugInterface::evaluateExpression(const char* expression, u64& dest, std::
bool DebugInterface::initExpression(const char* exp, PostfixExpression& dest, std::string& error)
{
MipsExpressionFunctions funcs(this, true);
MipsExpressionFunctions funcs(this, nullptr, true);
return initPostfixExpression(exp, &funcs, dest, error);
}
bool DebugInterface::parseExpression(PostfixExpression& exp, u64& dest, std::string& error)
{
MipsExpressionFunctions funcs(this, false);
MipsExpressionFunctions funcs(this, nullptr, false);
return parsePostfixExpression(exp, &funcs, dest, error);
}
@ -1306,3 +1085,239 @@ u64 ElfMemoryReader::read64(u32 address, bool& valid)
return *result;
}
//
// MipsExpressionFunctions
//
MipsExpressionFunctions::MipsExpressionFunctions(
DebugInterface* cpu, const ccc::SymbolDatabase* symbolDatabase, bool shouldEnumerateSymbols)
: m_cpu(cpu)
, m_database(symbolDatabase)
{
if (!shouldEnumerateSymbols)
return;
if (symbolDatabase)
{
enumerateSymbols(*symbolDatabase);
}
else
{
m_cpu->GetSymbolGuardian().Read([&](const ccc::SymbolDatabase& database) {
enumerateSymbols(database);
});
}
}
void MipsExpressionFunctions::enumerateSymbols(const ccc::SymbolDatabase& database)
{
// TODO: Add mangled symbol name maps to CCC and remove this.
for (const ccc::Function& function : database.functions)
m_mangled_function_names_to_handles.emplace(function.mangled_name(), function.handle());
for (const ccc::GlobalVariable& global : database.global_variables)
m_mangled_global_names_to_handles.emplace(global.mangled_name(), global.handle());
}
bool MipsExpressionFunctions::parseReference(char* str, u64& referenceIndex)
{
for (int i = 0; i < 32; i++)
{
char reg[8];
std::snprintf(reg, std::size(reg), "r%d", i);
if (StringUtil::Strcasecmp(str, reg) == 0 || StringUtil::Strcasecmp(str, m_cpu->getRegisterName(0, i)) == 0)
{
referenceIndex = i;
return true;
}
std::snprintf(reg, std::size(reg), "f%d", i);
if (StringUtil::Strcasecmp(str, reg) == 0)
{
referenceIndex = i | REF_INDEX_FPU;
return true;
}
}
if (StringUtil::Strcasecmp(str, "pc") == 0)
{
referenceIndex = REF_INDEX_PC;
return true;
}
if (StringUtil::Strcasecmp(str, "hi") == 0)
{
referenceIndex = REF_INDEX_HI;
return true;
}
if (StringUtil::Strcasecmp(str, "lo") == 0)
{
referenceIndex = REF_INDEX_LO;
return true;
}
if (StringUtil::Strcasecmp(str, "target") == 0)
{
referenceIndex = REF_INDEX_OPTARGET;
return true;
}
if (StringUtil::Strcasecmp(str, "load") == 0)
{
referenceIndex = REF_INDEX_OPLOAD;
return true;
}
if (StringUtil::Strcasecmp(str, "store") == 0)
{
referenceIndex = REF_INDEX_OPSTORE;
return true;
}
return false;
}
bool MipsExpressionFunctions::parseSymbol(char* str, u64& symbolValue)
{
if (m_database)
return parseSymbol(str, symbolValue, *m_database);
bool success = false;
m_cpu->GetSymbolGuardian().Read([&](const ccc::SymbolDatabase& database) {
success = parseSymbol(str, symbolValue, database);
});
return success;
}
bool MipsExpressionFunctions::parseSymbol(char* str, u64& symbolValue, const ccc::SymbolDatabase& database)
{
std::string name = str;
// Check for mangled function names.
auto function_iterator = m_mangled_function_names_to_handles.find(name);
if (function_iterator != m_mangled_function_names_to_handles.end())
{
const ccc::Function* function = database.functions.symbol_from_handle(function_iterator->second);
if (function && function->address().valid())
{
symbolValue = function->address().value;
return true;
}
}
// Check for mangled global variable names.
auto global_iterator = m_mangled_global_names_to_handles.find(name);
if (global_iterator != m_mangled_global_names_to_handles.end())
{
const ccc::GlobalVariable* global = database.global_variables.symbol_from_handle(global_iterator->second);
if (global && global->address().valid())
{
symbolValue = global->address().value;
return true;
}
}
// Check for regular unmangled names.
const ccc::Symbol* symbol = database.symbol_with_name(name);
if (symbol && symbol->address().valid())
{
symbolValue = symbol->address().value;
return true;
}
return false;
}
u64 MipsExpressionFunctions::getReferenceValue(u64 referenceIndex)
{
if (referenceIndex < 32)
return m_cpu->getRegister(0, referenceIndex)._u64[0];
if (referenceIndex == REF_INDEX_PC)
return m_cpu->getPC();
if (referenceIndex == REF_INDEX_HI)
return m_cpu->getHI()._u64[0];
if (referenceIndex == REF_INDEX_LO)
return m_cpu->getLO()._u64[0];
if (referenceIndex & REF_INDEX_IS_OPSL)
{
const u32 OP = m_cpu->read32(m_cpu->getPC());
const R5900::OPCODE& opcode = R5900::GetInstruction(OP);
if (opcode.flags & IS_MEMORY)
{
// 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);
if (referenceIndex & REF_INDEX_OPTARGET)
{
return target;
}
else if (referenceIndex & REF_INDEX_OPLOAD)
{
return (opcode.flags & IS_LOAD) ? target : 0;
}
else if (referenceIndex & REF_INDEX_OPSTORE)
{
return (opcode.flags & IS_STORE) ? target : 0;
}
}
return 0;
}
if (referenceIndex & REF_INDEX_FPU)
{
return m_cpu->getRegister(EECAT_FPR, referenceIndex & 0x1F)._u64[0];
}
return -1;
}
ExpressionType MipsExpressionFunctions::getReferenceType(u64 referenceIndex)
{
if (referenceIndex & REF_INDEX_IS_FLOAT)
{
return EXPR_TYPE_FLOAT;
}
return EXPR_TYPE_UINT;
}
bool MipsExpressionFunctions::getMemoryValue(u32 address, int size, u64& dest, std::string& error)
{
switch (size)
{
case 1:
case 2:
case 4:
case 8:
break;
default:
error = StringUtil::StdStringFromFormat(
TRANSLATE("ExpressionParser", "Invalid memory access size %d."), size);
return false;
}
if (address % size)
{
error = TRANSLATE("ExpressionParser", "Invalid memory access (unaligned).");
return false;
}
switch (size)
{
case 1:
dest = m_cpu->read8(address);
break;
case 2:
dest = m_cpu->read16(address);
break;
case 4:
dest = m_cpu->read32(address);
break;
case 8:
dest = m_cpu->read64(address);
break;
}
return true;
}

View File

@ -2,8 +2,8 @@
// SPDX-License-Identifier: GPL-3.0+
#pragma once
#include "DebugTools/BiosDebugData.h"
#include "MemoryTypes.h"
#include "BiosDebugData.h"
#include "ExpressionParser.h"
#include "SymbolGuardian.h"
#include "SymbolImporter.h"
@ -210,5 +210,26 @@ protected:
const ccc::ElfFile& m_elf;
};
class MipsExpressionFunctions : public IExpressionFunctions
{
public:
MipsExpressionFunctions(
DebugInterface* cpu, const ccc::SymbolDatabase* symbolDatabase, bool shouldEnumerateSymbols);
bool parseReference(char* str, u64& referenceIndex) override;
bool parseSymbol(char* str, u64& symbolValue) override;
u64 getReferenceValue(u64 referenceIndex) override;
ExpressionType getReferenceType(u64 referenceIndex) override;
bool getMemoryValue(u32 address, int size, u64& dest, std::string& error) override;
protected:
void enumerateSymbols(const ccc::SymbolDatabase& database);
bool parseSymbol(char* str, u64& symbolValue, const ccc::SymbolDatabase& database);
DebugInterface* m_cpu;
const ccc::SymbolDatabase* m_database;
std::map<std::string, ccc::FunctionHandle> m_mangled_function_names_to_handles;
std::map<std::string, ccc::GlobalVariableHandle> m_mangled_global_names_to_handles;
};
extern R5900DebugInterface r5900Debug;
extern R3000DebugInterface r3000Debug;

View File

@ -276,13 +276,6 @@ void SymbolImporter::ImportSymbols(
const std::map<std::string, ccc::DataTypeHandle>& builtin_types,
const std::atomic_bool* interrupt)
{
ccc::DemanglerFunctions demangler;
if (options.DemangleSymbols)
{
demangler.cplus_demangle = cplus_demangle;
demangler.cplus_demangle_opname = cplus_demangle_opname;
}
u32 importer_flags =
ccc::NO_MEMBER_FUNCTIONS |
ccc::NO_OPTIMIZED_OUT_FUNCTIONS |
@ -291,6 +284,13 @@ void SymbolImporter::ImportSymbols(
if (options.DemangleParameters)
importer_flags |= ccc::DEMANGLE_PARAMETERS;
ccc::DemanglerFunctions demangler;
if (options.DemangleSymbols)
{
demangler.cplus_demangle = cplus_demangle;
demangler.cplus_demangle_opname = cplus_demangle_opname;
}
if (options.ImportSymbolsFromELF)
{
ccc::Result<std::vector<std::unique_ptr<ccc::SymbolTable>>> symbol_tables = elf.get_all_symbol_tables();
@ -301,7 +301,7 @@ void SymbolImporter::ImportSymbols(
else
{
ccc::Result<ccc::ModuleHandle> module_handle = ccc::import_symbol_tables(
database, elf.name(), *symbol_tables, importer_flags, demangler, interrupt);
database, *symbol_tables, elf.name(), ccc::Address(), importer_flags, demangler, interrupt);
if (!module_handle.success())
{
ccc::report_error(module_handle.error());
@ -311,7 +311,7 @@ void SymbolImporter::ImportSymbols(
if (!nocash_path.empty() && options.ImportSymFileFromDefaultLocation)
{
ccc::Result<bool> nocash_result = ImportNocashSymbols(database, nocash_path, builtin_types);
ccc::Result<bool> nocash_result = ImportNocashSymbols(database, nocash_path, 0, builtin_types);
if (!nocash_result.success())
{
Console.Error("Failed to import symbol file '%s': %s",
@ -319,14 +319,64 @@ void SymbolImporter::ImportSymbols(
}
}
ImportExtraSymbols(database, options, builtin_types, importer_flags, demangler, interrupt);
Console.WriteLn("Imported %d symbols.", database.symbol_count());
return;
}
void SymbolImporter::ImportExtraSymbols(
ccc::SymbolDatabase& database,
const Pcsx2Config::DebugAnalysisOptions& options,
const std::map<std::string, ccc::DataTypeHandle>& builtin_types,
u32 importer_flags,
const ccc::DemanglerFunctions& demangler,
const std::atomic_bool* interrupt)
{
MipsExpressionFunctions expression_functions(&r5900Debug, &database, true);
for (const DebugExtraSymbolFile& extra_symbol_file : options.ExtraSymbolFiles)
{
if (*interrupt)
return;
if (StringUtil::EndsWithNoCase(extra_symbol_file.Path, ".sym"))
std::string path = Path::ToNativePath(extra_symbol_file.Path);
if (!Path::IsAbsolute(path))
path = Path::Combine(EmuFolders::GameSettings, path);
if (!extra_symbol_file.Condition.empty())
{
ccc::Result<bool> nocash_result = ImportNocashSymbols(database, extra_symbol_file.Path, builtin_types);
u64 expression_result = 0;
std::string error;
if (!parseExpression(extra_symbol_file.Condition.c_str(), &expression_functions, expression_result, error))
{
Console.Error("Failed to parse condition expression '%s' while importing extra symbol file '%s': %s.",
extra_symbol_file.Condition.c_str(), path.c_str(), error.c_str());
}
if (!expression_result)
continue;
}
ccc::Address base_address;
if (!extra_symbol_file.BaseAddress.empty())
{
u64 expression_result = 0;
std::string error;
if (!parseExpression(extra_symbol_file.BaseAddress.c_str(), &expression_functions, expression_result, error))
{
Console.Error("Failed to parse base address expression '%s' while importing extra symbol file '%s': %s.",
extra_symbol_file.BaseAddress.c_str(), path.c_str(), error.c_str());
}
base_address = static_cast<u32>(expression_result);
}
if (StringUtil::EndsWithNoCase(path, ".sym"))
{
ccc::Result<bool> nocash_result = ImportNocashSymbols(
database, path, base_address.get_or_zero(), builtin_types);
if (!nocash_result.success())
{
Console.Error("Failed to import symbol file '%s': %s",
@ -334,29 +384,30 @@ void SymbolImporter::ImportSymbols(
}
if (!*nocash_result)
Console.Error("Cannot open symbol file '%s'.", extra_symbol_file.Path.c_str());
Console.Error("Cannot open symbol file '%s'.", path.c_str());
continue;
}
Error error;
std::optional<std::vector<u8>> image = FileSystem::ReadBinaryFile(extra_symbol_file.Path.c_str());
std::optional<std::vector<u8>> image = FileSystem::ReadBinaryFile(path.c_str());
if (!image.has_value())
{
Console.Error("Failed to read extra symbol file '%s'.", extra_symbol_file.Path.c_str());
Console.Error("Failed to read extra symbol file '%s'.", path.c_str());
continue;
}
std::string file_name(Path::GetFileName(extra_symbol_file.Path));
std::string file_name(Path::GetFileName(path));
ccc::Result<std::unique_ptr<ccc::SymbolFile>> symbol_file = ccc::parse_symbol_file(std::move(*image), file_name.c_str());
ccc::Result<std::unique_ptr<ccc::SymbolFile>> symbol_file = ccc::parse_symbol_file(
std::move(*image), file_name.c_str());
if (!symbol_file.success())
{
ccc::report_error(symbol_file.error());
continue;
}
ccc::Result<std::vector<std::unique_ptr<ccc::SymbolTable>>> symbol_tables = (*symbol_file)->get_all_symbol_tables();
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());
@ -364,22 +415,19 @@ void SymbolImporter::ImportSymbols(
}
ccc::Result<ccc::ModuleHandle> module_handle = ccc::import_symbol_tables(
database, elf.name(), *symbol_tables, importer_flags, demangler, interrupt);
database, *symbol_tables, (*symbol_file)->name(), base_address, importer_flags, demangler, interrupt);
if (!module_handle.success())
{
ccc::report_error(module_handle.error());
continue;
}
}
Console.WriteLn("Imported %d symbols.", database.symbol_count());
return;
}
ccc::Result<bool> SymbolImporter::ImportNocashSymbols(
ccc::SymbolDatabase& database,
const std::string& file_path,
u32 base_address,
const std::map<std::string, ccc::DataTypeHandle>& builtin_types)
{
auto file = FileSystem::OpenManagedCFile(file_path.c_str(), "r");
@ -405,6 +453,8 @@ ccc::Result<bool> SymbolImporter::ImportNocashSymbols(
if (address == 0 && strcmp(value, "0") == 0)
continue;
address += base_address;
if (value[0] == '.')
{
// data directives

View File

@ -46,9 +46,18 @@ public:
const std::map<std::string, ccc::DataTypeHandle>& builtin_types,
const std::atomic_bool* interrupt);
static void ImportExtraSymbols(
ccc::SymbolDatabase& database,
const Pcsx2Config::DebugAnalysisOptions& options,
const std::map<std::string, ccc::DataTypeHandle>& builtin_types,
u32 importer_flags,
const ccc::DemanglerFunctions& demangler,
const std::atomic_bool* interrupt);
static ccc::Result<bool> ImportNocashSymbols(
ccc::SymbolDatabase& database,
const std::string& file_path,
u32 base_address,
const std::map<std::string, ccc::DataTypeHandle>& builtin_types);
static std::unique_ptr<ccc::ast::Node> GetBuiltInType(

View File

@ -1625,6 +1625,8 @@ void Pcsx2Config::DebugAnalysisOptions::LoadSave(SettingsWrapper& wrap)
file = ExtraSymbolFiles[i];
SettingsWrapEntryEx(file.Path, "Path");
SettingsWrapEntryEx(file.BaseAddress, "BaseAddress");
SettingsWrapEntryEx(file.Condition, "Condition");
if (wrap.IsLoading())
ExtraSymbolFiles.emplace_back(std::move(file));