mirror of
https://github.com/PCSX2/pcsx2.git
synced 2026-01-31 01:15:24 +01:00
677 lines
19 KiB
C++
677 lines
19 KiB
C++
// SPDX-FileCopyrightText: 2002-2026 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/Path.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 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();
|
|
|
|
if (EmuConfig.DebuggerAnalysis.RunCondition == DebugAnalysisCondition::NEVER)
|
|
{
|
|
m_symbol_table_loaded_on_boot = false;
|
|
return;
|
|
}
|
|
|
|
if (!m_debugger_open && EmuConfig.DebuggerAnalysis.RunCondition == DebugAnalysisCondition::IF_DEBUGGER_IS_OPEN)
|
|
{
|
|
m_symbol_table_loaded_on_boot = false;
|
|
return;
|
|
}
|
|
|
|
AnalyseElf(std::move(elf), elf_file_name, EmuConfig.DebuggerAnalysis, true);
|
|
|
|
m_symbol_table_loaded_on_boot = true;
|
|
}
|
|
|
|
void SymbolImporter::OnElfLoadedInMemory()
|
|
{
|
|
{
|
|
std::lock_guard lock(m_elf_loaded_in_memory_mutex);
|
|
m_elf_loaded_in_memory = true;
|
|
}
|
|
m_elf_loaded_in_memory_condition_variable.notify_one();
|
|
}
|
|
|
|
void SymbolImporter::OnDebuggerOpened()
|
|
{
|
|
m_debugger_open = true;
|
|
|
|
if (EmuConfig.DebuggerAnalysis.RunCondition == DebugAnalysisCondition::NEVER)
|
|
return;
|
|
|
|
if (m_symbol_table_loaded_on_boot)
|
|
return;
|
|
|
|
LoadAndAnalyseElf(EmuConfig.DebuggerAnalysis);
|
|
}
|
|
|
|
void SymbolImporter::OnDebuggerClosed()
|
|
{
|
|
m_debugger_open = false;
|
|
}
|
|
|
|
void SymbolImporter::Reset()
|
|
{
|
|
ShutdownWorkerThread();
|
|
|
|
m_guardian.ReadWrite([&](ccc::SymbolDatabase& database) {
|
|
database.clear();
|
|
m_builtin_types.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));
|
|
|
|
m_builtin_types.emplace(default_type.name, (*symbol)->handle());
|
|
}
|
|
});
|
|
}
|
|
|
|
void SymbolImporter::LoadAndAnalyseElf(Pcsx2Config::DebugAnalysisOptions options)
|
|
{
|
|
if (!VMManager::HasValidVM())
|
|
return;
|
|
|
|
const std::string& elf_path = VMManager::GetCurrentELF();
|
|
|
|
Error error;
|
|
ElfObject elfo;
|
|
if (elf_path.empty() || !cdvdLoadElf(&elfo, elf_path, false, &error))
|
|
{
|
|
if (!elf_path.empty())
|
|
Console.Error(fmt::format("Failed to read ELF for symbol import: {}: {}", elf_path, error.GetDescription()));
|
|
return;
|
|
}
|
|
|
|
AnalyseElf(elfo.ReleaseData(), elf_path, options, false);
|
|
}
|
|
|
|
struct SymbolImporterThreadParameters
|
|
{
|
|
std::vector<u8> elf;
|
|
std::string elf_file_name;
|
|
std::string nocash_path;
|
|
Pcsx2Config::DebugAnalysisOptions options;
|
|
bool wait_until_elf_is_loaded;
|
|
};
|
|
|
|
void SymbolImporter::AnalyseElf(
|
|
std::vector<u8> elf,
|
|
const std::string& elf_file_name,
|
|
Pcsx2Config::DebugAnalysisOptions options,
|
|
bool wait_until_elf_is_loaded)
|
|
{
|
|
if (!VMManager::HasValidVM())
|
|
return;
|
|
|
|
// 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";
|
|
}
|
|
|
|
SymbolImporterThreadParameters parameters;
|
|
parameters.elf = std::move(elf);
|
|
parameters.elf_file_name = elf_file_name;
|
|
parameters.nocash_path = std::move(nocash_path);
|
|
parameters.options = std::move(options);
|
|
parameters.wait_until_elf_is_loaded = wait_until_elf_is_loaded;
|
|
|
|
ShutdownWorkerThread();
|
|
|
|
m_import_thread = std::thread([this, params = std::move(parameters)]() {
|
|
Threading::SetNameOfCurrentThread("Symbol Worker");
|
|
|
|
ccc::Result<ccc::ElfFile> parsed_elf = ccc::ElfFile::parse(std::move(params.elf));
|
|
if (!parsed_elf.success())
|
|
{
|
|
ccc::report_error(parsed_elf.error());
|
|
return;
|
|
}
|
|
|
|
ccc::ElfSymbolFile symbol_file(std::move(*parsed_elf), std::move(params.elf_file_name));
|
|
|
|
ccc::SymbolDatabase temp_database;
|
|
|
|
ImportSymbols(
|
|
temp_database,
|
|
symbol_file,
|
|
params.nocash_path,
|
|
params.options,
|
|
m_builtin_types,
|
|
&m_interrupt_import_thread);
|
|
|
|
if (m_interrupt_import_thread)
|
|
return;
|
|
|
|
if (params.options.GenerateFunctionHashes)
|
|
{
|
|
ElfMemoryReader reader(symbol_file.elf());
|
|
SymbolGuardian::GenerateFunctionHashes(temp_database, reader);
|
|
}
|
|
|
|
if (m_interrupt_import_thread)
|
|
return;
|
|
|
|
if (params.wait_until_elf_is_loaded && params.options.FunctionScanMode == DebugFunctionScanMode::SCAN_MEMORY)
|
|
{
|
|
// Wait for the entry point to start compiling on the CPU thread so
|
|
// we know the functions we want to scan are loaded in memory.
|
|
std::unique_lock lock(m_elf_loaded_in_memory_mutex);
|
|
m_elf_loaded_in_memory_condition_variable.wait(lock,
|
|
[this]() { return m_elf_loaded_in_memory; });
|
|
|
|
if (m_interrupt_import_thread)
|
|
return;
|
|
}
|
|
|
|
m_guardian.ReadWrite([&](ccc::SymbolDatabase& database) {
|
|
ClearExistingSymbols(database, params.options);
|
|
|
|
if (m_interrupt_import_thread)
|
|
return;
|
|
|
|
database.merge_from(temp_database);
|
|
|
|
if (m_interrupt_import_thread)
|
|
return;
|
|
|
|
// The function scanner has to be run on the main database so that
|
|
// functions created before the importer was run are still
|
|
// considered. Otherwise, duplicate functions will be created.
|
|
ScanForFunctions(database, symbol_file, params.options);
|
|
});
|
|
});
|
|
}
|
|
|
|
void SymbolImporter::ShutdownWorkerThread()
|
|
{
|
|
if (m_import_thread.joinable())
|
|
{
|
|
m_interrupt_import_thread = true;
|
|
|
|
// Make sure the import thread is woken up so we can shut it down.
|
|
{
|
|
std::lock_guard lock(m_elf_loaded_in_memory_mutex);
|
|
m_elf_loaded_in_memory = true;
|
|
}
|
|
m_elf_loaded_in_memory_condition_variable.notify_one();
|
|
|
|
m_import_thread.join();
|
|
|
|
m_interrupt_import_thread = false;
|
|
}
|
|
|
|
{
|
|
std::lock_guard lock(m_elf_loaded_in_memory_mutex);
|
|
m_elf_loaded_in_memory = false;
|
|
}
|
|
}
|
|
|
|
void SymbolImporter::ClearExistingSymbols(ccc::SymbolDatabase& database, const Pcsx2Config::DebugAnalysisOptions& options)
|
|
{
|
|
std::vector<ccc::SymbolSourceHandle> sources_to_destroy;
|
|
for (const ccc::SymbolSource& source : database.symbol_sources)
|
|
{
|
|
bool should_destroy = ShouldClearSymbolsFromSourceByDefault(source.name());
|
|
|
|
if (!options.AutomaticallySelectSymbolsToClear)
|
|
for (const DebugSymbolSource& source_config : options.SymbolSources)
|
|
if (source_config.Name == source.name())
|
|
should_destroy = source_config.ClearDuringAnalysis;
|
|
|
|
if (should_destroy)
|
|
sources_to_destroy.emplace_back(source.handle());
|
|
}
|
|
|
|
for (ccc::SymbolSourceHandle handle : sources_to_destroy)
|
|
database.destroy_symbols_from_source(handle, true);
|
|
}
|
|
|
|
bool SymbolImporter::ShouldClearSymbolsFromSourceByDefault(const std::string& source_name)
|
|
{
|
|
return source_name.find("Symbol Table") != std::string::npos ||
|
|
source_name == "ELF Section Headers" ||
|
|
source_name == "Function Scanner" ||
|
|
source_name == "Nocash Symbols";
|
|
}
|
|
|
|
void SymbolImporter::ImportSymbols(
|
|
ccc::SymbolDatabase& database,
|
|
const ccc::ElfSymbolFile& elf,
|
|
const std::string& nocash_path,
|
|
const Pcsx2Config::DebugAnalysisOptions& options,
|
|
const std::map<std::string, ccc::DataTypeHandle>& builtin_types,
|
|
const std::atomic_bool* interrupt)
|
|
{
|
|
u32 importer_flags =
|
|
ccc::NO_MEMBER_FUNCTIONS |
|
|
ccc::NO_OPTIMIZED_OUT_FUNCTIONS |
|
|
ccc::UNIQUE_FUNCTIONS;
|
|
|
|
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();
|
|
if (!symbol_tables.success())
|
|
{
|
|
ccc::report_error(symbol_tables.error());
|
|
}
|
|
else
|
|
{
|
|
ccc::Result<ccc::ModuleHandle> module_handle = ccc::import_symbol_tables(
|
|
database, *symbol_tables, elf.name(), ccc::Address(), importer_flags, demangler, interrupt);
|
|
if (!module_handle.success())
|
|
{
|
|
ccc::report_error(module_handle.error());
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!nocash_path.empty() && options.ImportSymFileFromDefaultLocation)
|
|
{
|
|
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",
|
|
nocash_path.c_str(), nocash_result.error().message.c_str());
|
|
}
|
|
}
|
|
|
|
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;
|
|
|
|
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())
|
|
{
|
|
u64 expression_result = 0;
|
|
std::string error;
|
|
if (!parseExpression(extra_symbol_file.Condition.c_str(), &expression_functions, expression_result, error))
|
|
{
|
|
Console.Error("Failed to evaluate 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 evaluate 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",
|
|
extra_symbol_file.Path.c_str(), nocash_result.error().message.c_str());
|
|
}
|
|
|
|
if (!*nocash_result)
|
|
Console.Error("Cannot open symbol file '%s'.", path.c_str());
|
|
|
|
continue;
|
|
}
|
|
|
|
std::optional<std::vector<u8>> image = FileSystem::ReadBinaryFile(path.c_str());
|
|
if (!image.has_value())
|
|
{
|
|
Console.Error("Failed to read extra symbol file '%s'.", path.c_str());
|
|
continue;
|
|
}
|
|
|
|
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());
|
|
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();
|
|
if (!symbol_tables.success())
|
|
{
|
|
ccc::report_error(symbol_tables.error());
|
|
continue;
|
|
}
|
|
|
|
ccc::Result<ccc::ModuleHandle> module_handle = ccc::import_symbol_tables(
|
|
database, *symbol_tables, (*symbol_file)->name(), base_address, importer_flags, demangler, interrupt);
|
|
if (!module_handle.success())
|
|
{
|
|
ccc::report_error(module_handle.error());
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
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");
|
|
if (!file)
|
|
return false;
|
|
|
|
ccc::Result<ccc::SymbolSourceHandle> source = database.get_symbol_source("Nocash Symbols");
|
|
CCC_RETURN_IF_ERROR(source);
|
|
|
|
while (!feof(file.get()))
|
|
{
|
|
char line[256], value[256] = {0};
|
|
char* p = fgets(line, 256, file.get());
|
|
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;
|
|
|
|
address += base_address;
|
|
|
|
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::Node> type;
|
|
if (StringUtil::Strcasecmp(value, ".byt") == 0)
|
|
type = GetBuiltInType("u8", ccc::ast::BuiltInClass::UNSIGNED_8, builtin_types);
|
|
else if (StringUtil::Strcasecmp(value, ".wrd") == 0)
|
|
type = GetBuiltInType("u16", ccc::ast::BuiltInClass::UNSIGNED_16, builtin_types);
|
|
else if (StringUtil::Strcasecmp(value, ".dbl") == 0)
|
|
type = GetBuiltInType("u32", ccc::ast::BuiltInClass::UNSIGNED_32, builtin_types);
|
|
else if (StringUtil::Strcasecmp(value, ".asc") == 0)
|
|
type = GetBuiltInType("char", ccc::ast::BuiltInClass::UNQUALIFIED_8, builtin_types);
|
|
else
|
|
continue;
|
|
|
|
ccc::Result<ccc::GlobalVariable*> global_variable = database.global_variables.create_symbol(
|
|
line, address, *source, nullptr);
|
|
CCC_RETURN_IF_ERROR(global_variable);
|
|
|
|
if (type->size_bytes == (s32)size)
|
|
{
|
|
(*global_variable)->set_type(std::move(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(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);
|
|
CCC_RETURN_IF_ERROR(function);
|
|
|
|
(*function)->set_size(size);
|
|
}
|
|
else
|
|
{
|
|
ccc::Result<ccc::Label*> label = database.labels.create_symbol(value, address, *source, nullptr);
|
|
CCC_RETURN_IF_ERROR(label);
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
std::unique_ptr<ccc::ast::Node> SymbolImporter::GetBuiltInType(
|
|
const std::string& name,
|
|
ccc::ast::BuiltInClass bclass,
|
|
const std::map<std::string, ccc::DataTypeHandle>& builtin_types)
|
|
{
|
|
auto type = builtin_types.find(name);
|
|
if (type != builtin_types.end())
|
|
{
|
|
std::unique_ptr<ccc::ast::TypeName> type_name = std::make_unique<ccc::ast::TypeName>();
|
|
type_name->size_bytes = ccc::ast::builtin_class_size(bclass);
|
|
type_name->data_type_handle = type->second;
|
|
return type_name;
|
|
}
|
|
|
|
std::unique_ptr<ccc::ast::BuiltIn> built_in = std::make_unique<ccc::ast::BuiltIn>();
|
|
built_in->size_bytes = ccc::ast::builtin_class_size(bclass);
|
|
built_in->bclass = bclass;
|
|
return built_in;
|
|
}
|
|
|
|
void SymbolImporter::ScanForFunctions(
|
|
ccc::SymbolDatabase& database, const ccc::ElfSymbolFile& elf, const Pcsx2Config::DebugAnalysisOptions& options)
|
|
{
|
|
MipsExpressionFunctions expression_functions(&r5900Debug, &database, true);
|
|
|
|
u32 start_address = 0;
|
|
u32 end_address = 0;
|
|
if (options.CustomFunctionScanRange)
|
|
{
|
|
u64 expression_result = 0;
|
|
std::string error;
|
|
|
|
if (!parseExpression(options.FunctionScanStartAddress.c_str(), &expression_functions, expression_result, error))
|
|
{
|
|
Console.Error("Failed to evaluate start address expression '%s' while scanning for functions: %s",
|
|
options.FunctionScanStartAddress.c_str(), error.c_str());
|
|
return;
|
|
}
|
|
|
|
start_address = static_cast<u32>(expression_result);
|
|
|
|
if (!parseExpression(options.FunctionScanEndAddress.c_str(), &expression_functions, expression_result, error))
|
|
{
|
|
Console.Error("Failed to evaluate end address expression '%s' while scanning for functions: %s",
|
|
options.FunctionScanEndAddress.c_str(), error.c_str());
|
|
return;
|
|
}
|
|
|
|
end_address = static_cast<u32>(expression_result);
|
|
}
|
|
else
|
|
{
|
|
const ccc::ElfProgramHeader* entry_segment = elf.elf().entry_point_segment();
|
|
if (!entry_segment)
|
|
return;
|
|
|
|
start_address = entry_segment->vaddr;
|
|
end_address = entry_segment->vaddr + entry_segment->filesz;
|
|
}
|
|
|
|
switch (options.FunctionScanMode)
|
|
{
|
|
case DebugFunctionScanMode::SCAN_ELF:
|
|
{
|
|
ElfMemoryReader reader(elf.elf());
|
|
MIPSAnalyst::ScanForFunctions(database, reader, start_address, end_address, options.GenerateFunctionHashes);
|
|
break;
|
|
}
|
|
case DebugFunctionScanMode::SCAN_MEMORY:
|
|
{
|
|
MIPSAnalyst::ScanForFunctions(database, r5900Debug, start_address, end_address, options.GenerateFunctionHashes);
|
|
break;
|
|
}
|
|
case DebugFunctionScanMode::SKIP:
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|