mirror of
https://github.com/NohamR/Reclass.git
synced 2026-05-10 19:59:21 +00:00
fix: add missing symbol store and PDB debug info sources
These files were referenced in CMakeLists.txt and main.cpp but never committed, breaking the CI build.
This commit is contained in:
171
src/symbolstore.cpp
Normal file
171
src/symbolstore.cpp
Normal file
@@ -0,0 +1,171 @@
|
||||
#include "symbolstore.h"
|
||||
#include "providers/provider.h"
|
||||
#include <QDebug>
|
||||
|
||||
namespace rcx {
|
||||
|
||||
uint64_t SymbolStore::getModuleBase(const Provider* provider, const QString& canonical) const {
|
||||
if (!provider)
|
||||
return 0;
|
||||
uint64_t base = provider->symbolToAddress(canonical);
|
||||
if (base == 0)
|
||||
base = provider->symbolToAddress(canonical + QStringLiteral(".exe"));
|
||||
if (base == 0)
|
||||
base = provider->symbolToAddress(canonical + QStringLiteral(".dll"));
|
||||
if (base == 0)
|
||||
base = provider->symbolToAddress(canonical + QStringLiteral(".sys"));
|
||||
return base;
|
||||
}
|
||||
|
||||
int SymbolStore::addModule(const QString& moduleName, const QString& pdbPath,
|
||||
const QVector<QPair<QString, uint32_t>>& symbols) {
|
||||
QString canonical = resolveAlias(moduleName);
|
||||
|
||||
PdbSymbolSet set;
|
||||
set.pdbPath = pdbPath;
|
||||
set.moduleName = canonical;
|
||||
set.nameToRva.reserve(symbols.size());
|
||||
set.rvaToName.reserve(symbols.size());
|
||||
|
||||
for (const auto& sym : symbols) {
|
||||
if (set.nameToRva.contains(sym.first))
|
||||
continue;
|
||||
set.nameToRva.insert(sym.first, sym.second);
|
||||
set.rvaToName.append({sym.second, sym.first});
|
||||
}
|
||||
|
||||
set.sortRvaIndex();
|
||||
int count = set.nameToRva.size();
|
||||
|
||||
// Register the raw module name as an alias if it differs from canonical
|
||||
QString rawLower = moduleName.toLower();
|
||||
if (rawLower.endsWith(QStringLiteral(".exe")) || rawLower.endsWith(QStringLiteral(".dll")) ||
|
||||
rawLower.endsWith(QStringLiteral(".sys")))
|
||||
rawLower = rawLower.left(rawLower.lastIndexOf('.'));
|
||||
if (rawLower != canonical)
|
||||
m_aliases[rawLower] = canonical;
|
||||
|
||||
m_modules[canonical] = std::move(set);
|
||||
|
||||
qDebug() << "[SymbolStore] loaded" << count << "symbols for module" << canonical
|
||||
<< "(from" << pdbPath << ")";
|
||||
return count;
|
||||
}
|
||||
|
||||
void SymbolStore::unloadModule(const QString& moduleName) {
|
||||
QString canonical = resolveAlias(moduleName);
|
||||
m_modules.remove(canonical);
|
||||
}
|
||||
|
||||
uint64_t SymbolStore::resolve(const QString& token, const Provider* provider, bool* ok) const {
|
||||
*ok = false;
|
||||
|
||||
// Check for "module!symbol" syntax
|
||||
int bangIdx = token.indexOf('!');
|
||||
if (bangIdx > 0 && bangIdx < token.size() - 1) {
|
||||
QString modPart = token.left(bangIdx);
|
||||
QString symPart = token.mid(bangIdx + 1);
|
||||
QString canonical = resolveAlias(modPart);
|
||||
|
||||
auto modIt = m_modules.find(canonical);
|
||||
if (modIt == m_modules.end())
|
||||
return 0;
|
||||
|
||||
auto symIt = modIt->nameToRva.find(symPart);
|
||||
if (symIt == modIt->nameToRva.end())
|
||||
return 0;
|
||||
|
||||
uint32_t rva = *symIt;
|
||||
uint64_t moduleBase = getModuleBase(provider, canonical);
|
||||
// Also try the user-supplied module name form
|
||||
if (moduleBase == 0)
|
||||
moduleBase = getModuleBase(provider, modPart);
|
||||
|
||||
*ok = true;
|
||||
return moduleBase + rva;
|
||||
}
|
||||
|
||||
// Bare symbol — search all loaded modules
|
||||
uint32_t foundRva = 0;
|
||||
QString foundModule;
|
||||
int matches = 0;
|
||||
|
||||
for (auto it = m_modules.begin(); it != m_modules.end(); ++it) {
|
||||
auto symIt = it->nameToRva.find(token);
|
||||
if (symIt != it->nameToRva.end()) {
|
||||
foundRva = *symIt;
|
||||
foundModule = it.key();
|
||||
matches++;
|
||||
if (matches > 1)
|
||||
return 0; // ambiguous
|
||||
}
|
||||
}
|
||||
|
||||
if (matches == 1) {
|
||||
uint64_t moduleBase = getModuleBase(provider, foundModule);
|
||||
*ok = true;
|
||||
return moduleBase + foundRva;
|
||||
}
|
||||
|
||||
// Fallback: treat bare token as a module name (e.g. "ntdll" → ntdll base)
|
||||
if (matches == 0) {
|
||||
QString canonical = resolveAlias(token);
|
||||
uint64_t moduleBase = getModuleBase(provider, canonical);
|
||||
if (moduleBase != 0) {
|
||||
*ok = true;
|
||||
return moduleBase;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
QString SymbolStore::getSymbolForAddress(uint64_t addr, const Provider* provider) const {
|
||||
if (m_modules.isEmpty() || !provider)
|
||||
return {};
|
||||
|
||||
for (auto it = m_modules.begin(); it != m_modules.end(); ++it) {
|
||||
const PdbSymbolSet& set = *it;
|
||||
|
||||
uint64_t moduleBase = getModuleBase(provider, set.moduleName);
|
||||
if (moduleBase == 0)
|
||||
continue;
|
||||
|
||||
if (addr < moduleBase)
|
||||
continue;
|
||||
|
||||
uint32_t rva = static_cast<uint32_t>(addr - moduleBase);
|
||||
|
||||
if (set.rvaToName.isEmpty())
|
||||
continue;
|
||||
|
||||
// Binary search: find last entry with RVA <= target
|
||||
auto upper = std::upper_bound(set.rvaToName.begin(), set.rvaToName.end(), rva,
|
||||
[](uint32_t val, const QPair<uint32_t, QString>& entry) {
|
||||
return val < entry.first;
|
||||
});
|
||||
|
||||
if (upper == set.rvaToName.begin())
|
||||
continue;
|
||||
|
||||
--upper;
|
||||
uint32_t displacement = rva - upper->first;
|
||||
|
||||
static constexpr uint32_t kMaxDisplacement = 0x1000;
|
||||
if (displacement > kMaxDisplacement)
|
||||
continue;
|
||||
|
||||
if (displacement == 0)
|
||||
return set.moduleName + QStringLiteral("!") + upper->second;
|
||||
return set.moduleName + QStringLiteral("!") + upper->second
|
||||
+ QStringLiteral("+0x") + QString::number(displacement, 16);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void SymbolStore::addAlias(const QString& alias, const QString& canonicalModule) {
|
||||
m_aliases[alias.toLower()] = canonicalModule.toLower();
|
||||
}
|
||||
|
||||
} // namespace rcx
|
||||
Reference in New Issue
Block a user