mirror of https://github.com/PCSX2/pcsx2.git
Debugger: Split out SymbolImporter into its own class
This commit is contained in:
parent
1e23e7efc4
commit
0fd7e1d7c2
|
@ -5,6 +5,7 @@
|
|||
|
||||
#include <QtWidgets/QStyledItemDelegate>
|
||||
|
||||
#include "DebugTools/DebugInterface.h"
|
||||
#include "DebugTools/SymbolGuardian.h"
|
||||
|
||||
class SymbolTreeValueDelegate : public QStyledItemDelegate
|
||||
|
|
|
@ -61,7 +61,7 @@ void SymbolTreeWidget::reset()
|
|||
|
||||
m_ui.treeView->setColumnHidden(SymbolTreeModel::SIZE, !m_show_size_column || !m_show_size_column->isChecked());
|
||||
|
||||
m_cpu.GetSymbolGuardian().UpdateFunctionHashes(m_cpu);
|
||||
m_cpu.GetSymbolImporter().UpdateFunctionHashes(m_cpu);
|
||||
|
||||
SymbolFilters filters;
|
||||
std::unique_ptr<SymbolTreeNode> root;
|
||||
|
|
|
@ -789,6 +789,7 @@ set(pcsx2DebugToolsSources
|
|||
DebugTools/MipsStackWalk.cpp
|
||||
DebugTools/Breakpoints.cpp
|
||||
DebugTools/SymbolGuardian.cpp
|
||||
DebugTools/SymbolImporter.cpp
|
||||
DebugTools/DisR3000A.cpp
|
||||
DebugTools/DisR5900asm.cpp
|
||||
DebugTools/DisVU0Micro.cpp
|
||||
|
@ -806,6 +807,7 @@ set(pcsx2DebugToolsHeaders
|
|||
DebugTools/MipsStackWalk.h
|
||||
DebugTools/Breakpoints.h
|
||||
DebugTools/SymbolGuardian.h
|
||||
DebugTools/SymbolImporter.h
|
||||
DebugTools/Debug.h
|
||||
DebugTools/DisASM.h
|
||||
DebugTools/DisVUmicro.h
|
||||
|
|
|
@ -894,6 +894,11 @@ SymbolGuardian& R5900DebugInterface::GetSymbolGuardian() const
|
|||
return R5900SymbolGuardian;
|
||||
}
|
||||
|
||||
SymbolImporter& R5900DebugInterface::GetSymbolImporter() const
|
||||
{
|
||||
return R5900SymbolImporter;
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<BiosThread>> R5900DebugInterface::GetThreadList() const
|
||||
{
|
||||
return getEEThreads();
|
||||
|
@ -1213,6 +1218,11 @@ SymbolGuardian& R3000DebugInterface::GetSymbolGuardian() const
|
|||
return R3000SymbolGuardian;
|
||||
}
|
||||
|
||||
SymbolImporter& R3000DebugInterface::GetSymbolImporter() const
|
||||
{
|
||||
return R3000SymbolImporter;
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<BiosThread>> R3000DebugInterface::GetThreadList() const
|
||||
{
|
||||
return getIOPThreads();
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include "MemoryTypes.h"
|
||||
#include "ExpressionParser.h"
|
||||
#include "SymbolGuardian.h"
|
||||
#include "SymbolImporter.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
|
@ -81,8 +82,9 @@ public:
|
|||
virtual bool isValidAddress(u32 address) = 0;
|
||||
virtual u32 getCycles() = 0;
|
||||
virtual BreakPointCpu getCpuType() = 0;
|
||||
[[nodiscard]] virtual SymbolGuardian& GetSymbolGuardian() const = 0;
|
||||
[[nodiscard]] virtual std::vector<std::unique_ptr<BiosThread>> GetThreadList() const = 0;
|
||||
virtual SymbolGuardian& GetSymbolGuardian() const = 0;
|
||||
virtual SymbolImporter& GetSymbolImporter() const = 0;
|
||||
virtual std::vector<std::unique_ptr<BiosThread>> GetThreadList() const = 0;
|
||||
|
||||
bool evaluateExpression(const char* expression, u64& dest);
|
||||
bool initExpression(const char* exp, PostfixExpression& dest);
|
||||
|
@ -136,8 +138,9 @@ public:
|
|||
bool getCPCOND0() override;
|
||||
void setPc(u32 newPc) override;
|
||||
void setRegister(int cat, int num, u128 newValue) override;
|
||||
[[nodiscard]] SymbolGuardian& GetSymbolGuardian() const override;
|
||||
[[nodiscard]] std::vector<std::unique_ptr<BiosThread>> GetThreadList() const override;
|
||||
SymbolGuardian& GetSymbolGuardian() const override;
|
||||
SymbolImporter& GetSymbolImporter() const override;
|
||||
std::vector<std::unique_ptr<BiosThread>> GetThreadList() const override;
|
||||
|
||||
std::string disasm(u32 address, bool simplify) override;
|
||||
bool isValidAddress(u32 address) override;
|
||||
|
@ -178,8 +181,9 @@ public:
|
|||
bool getCPCOND0() override;
|
||||
void setPc(u32 newPc) override;
|
||||
void setRegister(int cat, int num, u128 newValue) override;
|
||||
[[nodiscard]] SymbolGuardian& GetSymbolGuardian() const override;
|
||||
[[nodiscard]] std::vector<std::unique_ptr<BiosThread>> GetThreadList() const override;
|
||||
SymbolGuardian& GetSymbolGuardian() const override;
|
||||
SymbolImporter& GetSymbolImporter() const override;
|
||||
std::vector<std::unique_ptr<BiosThread>> GetThreadList() const override;
|
||||
|
||||
std::string disasm(u32 address, bool simplify) override;
|
||||
bool isValidAddress(u32 address) override;
|
||||
|
|
|
@ -3,402 +3,19 @@
|
|||
|
||||
#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"
|
||||
|
||||
#include <demangle.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();
|
||||
std::shared_lock l(m_big_symbol_lock);
|
||||
callback(m_database);
|
||||
m_big_symbol_lock.unlock_shared();
|
||||
}
|
||||
|
||||
void SymbolGuardian::ReadWrite(ReadWriteCallback callback) noexcept
|
||||
{
|
||||
m_big_symbol_lock.lock();
|
||||
std::unique_lock l(m_big_symbol_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));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
ccc::ElfSymbolFile symbol_file(std::move(*parsed_elf), std::move(elf_file_name));
|
||||
|
||||
ShutdownWorkerThread();
|
||||
|
||||
m_import_thread = std::thread([this, nocash_path, worker_symbol_file = std::move(symbol_file)]() {
|
||||
Threading::SetNameOfCurrentThread("Symbol Worker");
|
||||
|
||||
ccc::SymbolDatabase temp_database;
|
||||
ImportSymbolTables(temp_database, worker_symbol_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, worker_symbol_file.elf());
|
||||
|
||||
if (m_interrupt_import_thread)
|
||||
return;
|
||||
|
||||
ReadWrite([&](ccc::SymbolDatabase& database) {
|
||||
database.merge_from(temp_database);
|
||||
|
||||
if (m_interrupt_import_thread)
|
||||
return;
|
||||
|
||||
const ccc::ElfProgramHeader* entry_segment = worker_symbol_file.elf().entry_point_segment();
|
||||
if (entry_segment)
|
||||
{
|
||||
ElfMemoryReader reader(worker_symbol_file.elf());
|
||||
MIPSAnalyst::ScanForFunctions(database, reader, entry_segment->vaddr, entry_segment->vaddr + entry_segment->filesz);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
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;
|
||||
demangler.cplus_demangle = cplus_demangle;
|
||||
demangler.cplus_demangle_opname = cplus_demangle_opname;
|
||||
|
||||
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(
|
||||
|
@ -531,3 +148,16 @@ FunctionInfo SymbolGuardian::FunctionOverlappingAddress(u32 address) const
|
|||
});
|
||||
return info;
|
||||
}
|
||||
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -8,13 +8,12 @@
|
|||
#include <functional>
|
||||
#include <shared_mutex>
|
||||
|
||||
#include <ccc/ast.h>
|
||||
#include <ccc/symbol_database.h>
|
||||
#include <ccc/symbol_file.h>
|
||||
|
||||
#include "common/Pcsx2Types.h"
|
||||
|
||||
class DebugInterface;
|
||||
|
||||
struct SymbolInfo
|
||||
{
|
||||
std::optional<ccc::SymbolDescriptor> descriptor;
|
||||
|
@ -33,13 +32,15 @@ struct FunctionInfo
|
|||
bool is_no_return = false;
|
||||
};
|
||||
|
||||
struct SymbolGuardian
|
||||
// Guardian of the ancient symbols. This class provides a thread safe API for
|
||||
// accessing the symbol database.
|
||||
class SymbolGuardian
|
||||
{
|
||||
public:
|
||||
SymbolGuardian();
|
||||
SymbolGuardian() = default;
|
||||
SymbolGuardian(const SymbolGuardian& rhs) = delete;
|
||||
SymbolGuardian(SymbolGuardian&& rhs) = delete;
|
||||
~SymbolGuardian();
|
||||
~SymbolGuardian() = default;
|
||||
SymbolGuardian& operator=(const SymbolGuardian& rhs) = delete;
|
||||
SymbolGuardian& operator=(SymbolGuardian&& rhs) = delete;
|
||||
|
||||
|
@ -52,32 +53,6 @@ public:
|
|||
// 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;
|
||||
|
@ -96,12 +71,12 @@ public:
|
|||
FunctionInfo FunctionStartingAtAddress(u32 address) const;
|
||||
FunctionInfo FunctionOverlappingAddress(u32 address) const;
|
||||
|
||||
// Delete all symbols from modules that have the "is_irx" flag set.
|
||||
void ClearIrxModules();
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
extern SymbolGuardian R5900SymbolGuardian;
|
||||
|
|
|
@ -0,0 +1,397 @@
|
|||
// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
#include "SymbolImporter.h"
|
||||
|
||||
#include "CDVD/CDVD.h"
|
||||
#include "DebugInterface.h"
|
||||
#include "Elfheader.h"
|
||||
#include "MIPSAnalyst.h"
|
||||
#include "VMManager.h"
|
||||
|
||||
#include "common/Console.h"
|
||||
#include "common/Error.h"
|
||||
#include "common/FileSystem.h"
|
||||
#include "common/StringUtil.h"
|
||||
#include "common/Threading.h"
|
||||
|
||||
#include <ccc/ast.h>
|
||||
#include <ccc/elf.h>
|
||||
#include <ccc/importer_flags.h>
|
||||
#include <ccc/symbol_file.h>
|
||||
|
||||
#include <demangle.h>
|
||||
|
||||
SymbolImporter R3000SymbolImporter(R3000SymbolGuardian);
|
||||
SymbolImporter R5900SymbolImporter(R5900SymbolGuardian);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
SymbolImporter::SymbolImporter(SymbolGuardian& guardian)
|
||||
: m_guardian(guardian)
|
||||
{
|
||||
ccc::set_custom_error_callback(error_callback);
|
||||
}
|
||||
|
||||
void SymbolImporter::OnElfChanged(std::vector<u8> elf, const std::string& elf_file_name)
|
||||
{
|
||||
Reset();
|
||||
AnalyseElf(std::move(elf), elf_file_name);
|
||||
}
|
||||
|
||||
void SymbolImporter::Reset()
|
||||
{
|
||||
ShutdownWorkerThread();
|
||||
|
||||
m_guardian.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));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void SymbolImporter::AnalyseElf(std::vector<u8> elf, const std::string& elf_file_name)
|
||||
{
|
||||
// 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";
|
||||
}
|
||||
|
||||
ccc::Result<ccc::ElfFile> parsed_elf = ccc::ElfFile::parse(std::move(elf));
|
||||
if (!parsed_elf.success())
|
||||
{
|
||||
ccc::report_error(parsed_elf.error());
|
||||
return;
|
||||
}
|
||||
|
||||
ccc::ElfSymbolFile symbol_file(std::move(*parsed_elf), std::move(elf_file_name));
|
||||
|
||||
ShutdownWorkerThread();
|
||||
|
||||
m_import_thread = std::thread([this, nocash_path, worker_symbol_file = std::move(symbol_file)]() {
|
||||
Threading::SetNameOfCurrentThread("Symbol Worker");
|
||||
|
||||
ccc::SymbolDatabase temp_database;
|
||||
ImportSymbolTables(temp_database, worker_symbol_file, &m_interrupt_import_thread);
|
||||
|
||||
if (m_interrupt_import_thread)
|
||||
return;
|
||||
|
||||
ImportNocashSymbols(temp_database, nocash_path);
|
||||
|
||||
if (m_interrupt_import_thread)
|
||||
return;
|
||||
|
||||
const ccc::ElfProgramHeader* entry_segment = worker_symbol_file.elf().entry_point_segment();
|
||||
if (entry_segment)
|
||||
{
|
||||
ElfMemoryReader reader(worker_symbol_file.elf());
|
||||
MIPSAnalyst::ScanForFunctions(temp_database, reader, entry_segment->vaddr, entry_segment->vaddr + entry_segment->filesz);
|
||||
}
|
||||
|
||||
if (m_interrupt_import_thread)
|
||||
return;
|
||||
|
||||
ComputeOriginalFunctionHashes(temp_database, worker_symbol_file.elf());
|
||||
|
||||
if (m_interrupt_import_thread)
|
||||
return;
|
||||
|
||||
m_guardian.ReadWrite([&](ccc::SymbolDatabase& database) {
|
||||
database.merge_from(temp_database);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void SymbolImporter::ShutdownWorkerThread()
|
||||
{
|
||||
m_interrupt_import_thread = true;
|
||||
if (m_import_thread.joinable())
|
||||
m_import_thread.join();
|
||||
m_interrupt_import_thread = false;
|
||||
}
|
||||
|
||||
|
||||
ccc::ModuleHandle SymbolImporter::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;
|
||||
demangler.cplus_demangle = cplus_demangle;
|
||||
demangler.cplus_demangle_opname = cplus_demangle_opname;
|
||||
|
||||
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 SymbolImporter::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 SymbolImporter::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 SymbolImporter::UpdateFunctionHashes(DebugInterface& cpu)
|
||||
{
|
||||
m_guardian.ReadWrite([&](ccc::SymbolDatabase& database) {
|
||||
for (ccc::Function& function : database.functions)
|
||||
{
|
||||
if (!function.address().valid())
|
||||
continue;
|
||||
|
||||
if (function.size() == 0)
|
||||
continue;
|
||||
|
||||
if (function.original_hash() == 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);
|
||||
});
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "SymbolGuardian.h"
|
||||
|
||||
class DebugInterface;
|
||||
|
||||
struct SymbolImporterOptions
|
||||
{
|
||||
std::vector<ccc::SymbolSourceHandle> symbols_to_destroy;
|
||||
|
||||
};
|
||||
|
||||
class SymbolImporter
|
||||
{
|
||||
public:
|
||||
SymbolImporter(SymbolGuardian& guardian);
|
||||
|
||||
// These functions are used to receive events from the rest of the emulator
|
||||
// that are used to determine when symbol tables should be loaded, and
|
||||
// should be called from the CPU thread.
|
||||
void OnElfChanged(std::vector<u8> elf, const std::string& elf_file_name);
|
||||
|
||||
void AutoAnalyse();
|
||||
|
||||
// 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 AnalyseElf(std::vector<u8> elf, const std::string& elf_file_name);
|
||||
|
||||
// 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);
|
||||
|
||||
protected:
|
||||
SymbolGuardian& m_guardian;
|
||||
|
||||
std::thread m_import_thread;
|
||||
std::atomic_bool m_interrupt_import_thread = false;
|
||||
};
|
||||
|
||||
extern SymbolImporter R3000SymbolImporter;
|
||||
extern SymbolImporter R5900SymbolImporter;
|
|
@ -7,7 +7,7 @@
|
|||
#include "Counters.h"
|
||||
#include "DEV9/DEV9.h"
|
||||
#include "DebugTools/DebugInterface.h"
|
||||
#include "DebugTools/SymbolGuardian.h"
|
||||
#include "DebugTools/SymbolImporter.h"
|
||||
#include "Elfheader.h"
|
||||
#include "FW.h"
|
||||
#include "GS.h"
|
||||
|
@ -447,8 +447,8 @@ void VMManager::Internal::CPUThreadShutdown()
|
|||
// Ensure emulog gets flushed.
|
||||
Log::SetFileOutputLevel(LOGLEVEL_NONE, std::string());
|
||||
|
||||
R3000SymbolGuardian.ShutdownWorkerThread();
|
||||
R5900SymbolGuardian.ShutdownWorkerThread();
|
||||
R3000SymbolImporter.ShutdownWorkerThread();
|
||||
R5900SymbolImporter.ShutdownWorkerThread();
|
||||
}
|
||||
|
||||
void VMManager::Internal::SetFileLogPath(std::string path)
|
||||
|
@ -1154,24 +1154,7 @@ void VMManager::UpdateELFInfo(std::string elf_path)
|
|||
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);
|
||||
R5900SymbolImporter.OnElfChanged(elfo.ReleaseData(), s_elf_path);
|
||||
}
|
||||
|
||||
void VMManager::ClearELFInfo()
|
||||
|
|
|
@ -155,6 +155,7 @@
|
|||
<ClCompile Include="DebugTools\MipsAssemblerTables.cpp" />
|
||||
<ClCompile Include="DebugTools\MipsStackWalk.cpp" />
|
||||
<ClCompile Include="DebugTools\SymbolGuardian.cpp" />
|
||||
<ClCompile Include="DebugTools\SymbolImporter.cpp" />
|
||||
<ClCompile Include="DEV9\AdapterUtils.cpp" />
|
||||
<ClCompile Include="DEV9\ATA\Commands\ATA_Command.cpp" />
|
||||
<ClCompile Include="DEV9\ATA\Commands\ATA_CmdDMA.cpp" />
|
||||
|
@ -600,6 +601,7 @@
|
|||
<ClInclude Include="DebugTools\MipsAssemblerTables.h" />
|
||||
<ClInclude Include="DebugTools\MipsStackWalk.h" />
|
||||
<ClInclude Include="DebugTools\SymbolGuardian.h" />
|
||||
<ClInclude Include="DebugTools\SymbolImporter.h" />
|
||||
<ClInclude Include="DEV9\AdapterUtils.h" />
|
||||
<ClInclude Include="DEV9\ATA\ATA.h" />
|
||||
<ClInclude Include="DEV9\ATA\HddCreate.h" />
|
||||
|
|
|
@ -1398,6 +1398,9 @@
|
|||
<ClCompile Include="DebugTools\SymbolGuardian.cpp">
|
||||
<Filter>System\Ps2\Debug</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="DebugTools\SymbolImporter.cpp">
|
||||
<Filter>System\Ps2\Debug</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="USB\usb-eyetoy\cam-jpeg.cpp">
|
||||
<Filter>System\Ps2\USB\usb-eyetoy</Filter>
|
||||
</ClCompile>
|
||||
|
@ -2342,6 +2345,9 @@
|
|||
<ClInclude Include="DebugTools\SymbolGuardian.h">
|
||||
<Filter>System\Ps2\Debug</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="DebugTools\SymbolImporter.h">
|
||||
<Filter>System\Ps2\Debug</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="USB\usb-eyetoy\cam-jpeg.h">
|
||||
<Filter>System\Ps2\USB\usb-eyetoy</Filter>
|
||||
</ClInclude>
|
||||
|
|
Loading…
Reference in New Issue