fix: commit remaining uncommitted source changes for CI

Add extractPdbSymbols declaration to import_pdb.h,
enumerateModules to provider.h, and other pending changes
that were only local.
This commit is contained in:
IChooseYou
2026-03-14 09:36:49 -06:00
committed by IChooseYou
parent 5921af2b4f
commit 009ddc951c
9 changed files with 102 additions and 29 deletions

View File

@@ -185,8 +185,14 @@ void ProcessMemoryProvider::cacheModules()
if ( i == 0 )
m_base = (uint64_t)mi.lpBaseOfDll;
WCHAR modPath[MAX_PATH];
QString fullPath;
if (GetModuleFileNameExW(m_handle, mods[i], modPath, MAX_PATH))
fullPath = QString::fromWCharArray(modPath);
m_modules.append({
QString::fromWCharArray(modName),
fullPath,
(uint64_t)mi.lpBaseOfDll,
(uint64_t)mi.SizeOfImage
});
@@ -194,6 +200,15 @@ void ProcessMemoryProvider::cacheModules()
}
}
QVector<rcx::Provider::ModuleEntry> ProcessMemoryProvider::enumerateModules() const
{
QVector<ModuleEntry> result;
result.reserve(m_modules.size());
for (const auto& m : m_modules)
result.append({m.name, m.fullPath, m.base, m.size});
return result;
}
QVector<rcx::MemoryRegion> ProcessMemoryProvider::enumerateRegions() const
{
QVector<rcx::MemoryRegion> regions;

View File

@@ -43,6 +43,7 @@ public:
void refreshModules() { m_modules.clear(); cacheModules(); }
uint64_t peb() const override { return m_peb; }
QVector<ThreadInfo> tebs() const override;
QVector<ModuleEntry> enumerateModules() const override;
private:
void cacheModules();
@@ -62,6 +63,7 @@ private:
struct ModuleInfo {
QString name;
QString fullPath;
uint64_t base;
uint64_t size;
};

View File

@@ -273,17 +273,32 @@ private:
// Identifier or hex literal disambiguation.
// Scan [a-zA-Z_][a-zA-Z0-9_]*. If it contains any non-hex char → identifier.
// Otherwise → backtrack and parse as hex number.
// WinDbg-style "module!symbol" is scanned as a single identifier token.
// If the identifier is followed by '(', try to parse as a built-in function call.
bool parseIdentifierOrHex(uint64_t& result) {
int start = m_pos;
bool hasNonHex = false;
// Scan full token
// Scan full token, including "module!symbol" as one token
while (!atEnd() && isIdentChar(peek())) {
if (!isHexDigit(peek()))
hasNonHex = true;
advance();
}
// If we hit '!' and the next char is an identifier start, extend the token
// to include the second part (WinDbg module!symbol syntax)
if (!atEnd() && peek() == '!' && m_pos > start) {
int bangPos = m_pos;
advance(); // skip '!'
if (!atEnd() && isIdentStart(peek())) {
hasNonHex = true;
while (!atEnd() && isIdentChar(peek())) {
advance();
}
} else {
m_pos = bangPos; // backtrack — '!' at end isn't module!symbol
}
}
QString token = m_input.mid(start, m_pos - start);

View File

@@ -71,6 +71,7 @@ struct ComposeState {
bool treeLines = false; // draw Unicode tree connectors in indentation
bool braceWrap = false; // opening brace on its own line
bool typeHints = false; // show type inference hints on hex nodes
SymbolLookupFn symbolLookup; // optional PDB symbol lookup callback
QVector<bool> siblingStack; // per-depth: true = more siblings follow at this level
uint64_t currentPtrBase = 0; // absolute addr of current pointer expansion target
@@ -262,7 +263,7 @@ void composeLeaf(ComposeState& state, const NodeTree& tree,
auto suggestions = inferTypes(
reinterpret_cast<const uint8_t*>(b.constData()), sz);
if (!suggestions.isEmpty() && suggestions[0].strength >= 3) {
lm.typeHintStart = lineText.size() + 2; // after " " gap
lm.typeHintStart = kFoldCol + lineText.size() + 2; // after fold prefix + " " gap
lm.typeHintKinds = suggestions[0].kinds;
QString typeName = formatHint(suggestions[0]);
QString preview = formatPreview(
@@ -276,6 +277,13 @@ void composeLeaf(ComposeState& state, const NodeTree& tree,
}
}
// PDB symbol annotation: show symbol name if this address matches a loaded symbol
if (sub == 0 && state.symbolLookup) {
QString sym = state.symbolLookup(absAddr);
if (!sym.isEmpty())
lineText += QStringLiteral(" // ") + sym;
}
state.emitLine(lineText, std::move(lm));
}
}
@@ -1087,12 +1095,13 @@ void composeNode(ComposeState& state, const NodeTree& tree,
ComposeResult compose(const NodeTree& tree, const Provider& prov, uint64_t viewRootId,
bool compactColumns, bool treeLines, bool braceWrap,
bool typeHints) {
bool typeHints, SymbolLookupFn symbolLookup) {
ComposeState state;
state.compactColumns = compactColumns;
state.treeLines = treeLines;
state.braceWrap = braceWrap;
state.typeHints = typeHints;
state.symbolLookup = std::move(symbolLookup);
// Precompute parent→children map
for (int i = 0; i < tree.nodes.size(); i++)

View File

@@ -42,7 +42,8 @@ public:
ComposeResult compose(uint64_t viewRootId = 0, bool compactColumns = false,
bool treeLines = false, bool braceWrap = false,
bool typeHints = false) const;
bool typeHints = false,
SymbolLookupFn symbolLookup = {}) const;
bool save(const QString& path);
bool load(const QString& path);
void loadData(const QString& binaryPath);

View File

@@ -5,6 +5,25 @@
namespace rcx {
// ── PDB Symbol Extraction ──
struct PdbSymbol {
QString name;
uint32_t rva;
};
struct PdbSymbolResult {
QString moduleName; // derived from PDB filename (e.g. "ntoskrnl")
QVector<PdbSymbol> symbols;
};
// Extract public/global symbols (name → RVA) from a PDB file.
// This reads the DBI stream's public and global symbol sub-streams.
PdbSymbolResult extractPdbSymbols(const QString& pdbPath,
QString* errorMsg = nullptr);
// ── PDB Type Import ──
struct PdbTypeInfo {
uint32_t typeIndex; // TPI type index
QString name; // struct/class/union/enum name

View File

@@ -5165,12 +5165,40 @@ void MainWindow::createSymbolsDock() {
m_symbolsTree->setExpandsOnDoubleClick(false);
styleTree(m_symbolsTree);
// Debounced search
// Populate a module item's children (replaces sentinel with real symbols)
auto populateModuleItem = [this](QStandardItem* item) {
if (!item || item->parent()) return;
if (item->rowCount() == 1 && item->child(0)->text().isEmpty()) {
item->removeRows(0, 1);
QString moduleName = item->data(Qt::UserRole).toString();
const auto* set = rcx::SymbolStore::instance().moduleData(moduleName);
if (set) {
static const QIcon symIcon(":/vsicons/symbol-method.svg");
for (const auto& sym : set->rvaToName) {
auto* child = new QStandardItem(symIcon,
QStringLiteral("%1 [0x%2]")
.arg(sym.second)
.arg(sym.first, 8, 16, QLatin1Char('0')));
child->setData(moduleName, Qt::UserRole);
child->setData(sym.first, Qt::UserRole + 1);
child->setData(sym.second, Qt::UserRole + 2);
item->appendRow(child);
}
}
}
};
// Debounced search — force-populate all modules so filter can match children
auto* searchTimer = new QTimer(this);
searchTimer->setSingleShot(true);
searchTimer->setInterval(150);
connect(searchTimer, &QTimer::timeout, this, [this]() {
connect(searchTimer, &QTimer::timeout, this, [this, populateModuleItem]() {
QString text = m_symbolsSearch->text();
if (!text.isEmpty()) {
// Force-populate all modules that still have sentinels
for (int i = 0; i < m_symbolsModel->rowCount(); i++)
populateModuleItem(m_symbolsModel->item(i));
}
m_symbolsProxy->setFilterFixedString(text);
if (!text.isEmpty())
m_symbolsTree->expandAll();
@@ -5184,30 +5212,9 @@ void MainWindow::createSymbolsDock() {
symLayout->addWidget(m_symbolsTree);
// Lazy-load children when a module node is expanded
connect(m_symbolsTree, &QTreeView::expanded, this, [this](const QModelIndex& proxyIdx) {
connect(m_symbolsTree, &QTreeView::expanded, this, [this, populateModuleItem](const QModelIndex& proxyIdx) {
QModelIndex srcIdx = m_symbolsProxy->mapToSource(proxyIdx);
auto* item = m_symbolsModel->itemFromIndex(srcIdx);
if (!item || item->parent()) return;
if (item->rowCount() == 1 && item->child(0)->text().isEmpty()) {
item->removeRows(0, 1);
QString moduleName = item->data(Qt::UserRole).toString();
const auto* set = rcx::SymbolStore::instance().moduleData(moduleName);
if (set) {
for (const auto& sym : set->rvaToName) {
auto* child = new QStandardItem(
QStringLiteral("%1 [0x%2]")
.arg(sym.second)
.arg(sym.first, 8, 16, QLatin1Char('0')));
child->setData(moduleName, Qt::UserRole);
child->setData(sym.first, Qt::UserRole + 1);
child->setData(sym.second, Qt::UserRole + 2);
static const QIcon symIcon(":/vsicons/symbol-method.svg");
child->setIcon(symIcon);
item->appendRow(child);
}
}
}
populateModuleItem(m_symbolsModel->itemFromIndex(srcIdx));
});
// Double-click symbol → navigate to moduleBase + RVA

View File

@@ -87,6 +87,9 @@ public:
struct ThreadInfo { uint64_t tebAddress; uint32_t threadId; };
virtual QVector<ThreadInfo> tebs() const { return {}; }
struct ModuleEntry { QString name; QString fullPath; uint64_t base; uint64_t size; };
virtual QVector<ModuleEntry> enumerateModules() const { return {}; }
// --- Kernel paging capabilities (override in kernel providers) ---
virtual bool hasKernelPaging() const { return false; }
virtual uint64_t getCr3() const { return 0; }

View File

@@ -17,6 +17,7 @@
<file alias="file-binary.svg">vsicons/file-binary.svg</file>
<file alias="debug.svg">vsicons/debug.svg</file>
<file alias="close.svg">vsicons/close.svg</file>
<file alias="cloud-download.svg">vsicons/cloud-download.svg</file>
<file alias="arrow-left.svg">vsicons/arrow-left.svg</file>
<file alias="arrow-right.svg">vsicons/arrow-right.svg</file>
<file alias="split-horizontal.svg">vsicons/split-horizontal.svg</file>
@@ -55,6 +56,7 @@
<file alias="symbol-enum.svg">vsicons/symbol-enum.svg</file>
<file alias="symbol-class.svg">vsicons/symbol-class.svg</file>
<file alias="symbol-variable.svg">vsicons/symbol-variable.svg</file>
<file alias="symbol-method.svg">vsicons/symbol-method.svg</file>
<file alias="server-process.svg">vsicons/server-process.svg</file>
<file alias="remote.svg">vsicons/remote.svg</file>
<file alias="plug.svg">vsicons/plug.svg</file>