Repository navigation
Description
Summary
Extend the SymbolDemangler to handle MSVC-mangled symbols (?-prefixed) found in Windows PE binaries. The core Rust and C++ Itanium ABI demangling is already fully implemented; this issue tracks the remaining gap for Windows-native symbols.
Current State
The SymbolDemangler in src/classification/symbols.rs already handles:
- ✅ Rust legacy mangling (
_ZNprefix) viarustc-demangle = "0.1.27" - ✅ Rust v0 mangling (
_Rprefix) viarustc-demangle - ✅ C++ Itanium ABI (
_Zprefix) viacpp_demangle = "0.5.1" - ✅ Pipeline integration in
classify_strings()withinsrc/pipeline/mod.rs - ✅ Graceful fallback — failed demanglings are tracked and warned
The Gap
The pipeline in src/pipeline/mod.rs already detects MSVC-mangled symbols via:
let looks_mangled =
s.text.starts_with("_Z") || s.text.starts_with("_R") || s.text.starts_with('?');However, SymbolDemangler::is_mangled() and SymbolDemangler::demangle() have no handling for ?-prefixed MSVC symbols. The pipeline detects them but SymbolDemangler silently passes through — producing no demangled output for any Windows PE import/export symbol using MSVC mangling.
MSVC-mangled symbol example:
- Mangled:
?printf@@YAHPEBDZZ - Expected demangled:
int __cdecl printf(char const * const,...)
This is a high-value gap because Stringy targets PE binary analysis (src/container/pe/) and MSVC is the dominant toolchain for Windows binaries.
Proposed Solution
1. Add Dependency
Add an MSVC demangling crate to Cargo.toml, for example msvc-demangler:
msvc-demangler = "0.10"2. Extend SymbolDemangler
// In src/classification/symbols.rs
pub fn is_mangled(&self, symbol: &str) -> bool {
symbol.starts_with("_R") // Rust v0
|| symbol.starts_with("_Z") // Itanium ABI (Rust legacy + C++)
|| symbol.starts_with('?') // MSVC
}
fn try_demangle_internal(&self, symbol: &str) -> Option<String> {
if symbol.starts_with("_R") {
return self.try_rust_demangle(symbol);
}
if symbol.starts_with("_Z") {
return self.try_rust_demangle(symbol)
.or_else(|| self.try_cpp_demangle(symbol));
}
if symbol.starts_with('?') {
return self.try_msvc_demangle(symbol);
}
None
}
fn try_msvc_demangle(&self, symbol: &str) -> Option<String> {
// Use msvc-demangler crate
let demangled = msvc_demangler::demangle(symbol, msvc_demangler::DemangleFlags::llvm()).ok()?;
if demangled != symbol {
Some(demangled)
} else {
None
}
}3. Tests Required
Add comprehensive tests to src/classification/symbols.rs:
is_mangledcorrectly returnstruefor?-prefixed symbolsdemangleproduces correct output for common MSVC patterns (plain functions, namespaced methods, overloaded operators)- Fallback to original on invalid/unknown MSVC-mangled input
- No regression on existing Rust and C++ demangling tests
Add integration tests using real PE binary fixtures (if available under tests/fixtures/).
Acceptance Criteria
-
SymbolDemangler::is_mangled()returnstruefor?-prefixed symbols -
SymbolDemangler::demangle()correctly demangled MSVC symbols into human-readable form - Demangled MSVC symbols receive
Tag::DemangledSymboland preserve original inoriginal_text - Unit tests cover: valid MSVC symbols, invalid input fallback, operator overloads, templates
- No regression in existing Rust/C++ demangling tests
-
cargo deny checkpasses with the new dependency - Performance overhead measured and acceptable
Requirements
Requirement ID: 4.1
Task ID
stringy-analyzer/symbol-processing
Related Work
src/classification/symbols.rs— extendSymbolDemanglersrc/pipeline/mod.rs—classify_strings()already detects?prefix; no pipeline changes neededsrc/container/pe/— primary consumer of MSVC-demangled symbols
Activity
unclesp1d3r commentedon Sep 30, 2025
@coderabbitai Enhance this issue body with additional context and a proposed solution. Improve the issue title and assign the proper labels and other metadata.
[-]Symbol Processing[/-][+]Implement Rust Symbol Demangling Support[/+]coderabbitai commentedon Sep 30, 2025
Tip
For best results, initiate chat on the files or code changes.
🧩 Analysis chain
🏁 Script executed:
Length of output: 1193
🏁 Script executed:
Length of output: 1041
🏁 Script executed:
Length of output: 90
@unclesp1d3r I've successfully enhanced issue #19 with the following improvements:
Changes Made
✅ Title Updated: Changed from "Symbol Processing" to "Implement Rust Symbol Demangling Support" - much more specific and descriptive
✅ Enhanced Issue Body with:
✅ Metadata Preserved:
The issue now provides clear context for why this feature is needed, how it should be implemented, and what success looks like. Anyone picking up this task will have all the information they need to get started! 🎯
unclesp1d3r commentedon Mar 16, 2026
@coderabbitai Enhance this issue body with additional context and a proposed solution. Improve the issue title and assign the proper labels and other metadata.
[-]Implement Rust Symbol Demangling Support[/-][+]Add MSVC Symbol Demangling Support for PE Binaries[/+]coderabbitai commentedon Mar 16, 2026
🧩 Analysis chain
🏁 Script executed:
Length of output: 20723
🏁 Script executed:
Length of output: 9755
🏁 Script executed:
Length of output: 28533
🏁 Script executed:
Length of output: 22528
🏁 Script executed:
Length of output: 442
@unclesp1d3r— done. Here's a summary of what changed and why, based on a full read of the codebase:What Changed
Title:
"Add MSVC Symbol Demangling Support for PE Binaries"The previous title ("Implement Rust Symbol Demangling Support") described work that was already complete. Reading the actual code revealed the real gap.
Body — completely rewritten around the actual gap:
The previous bot response proposed adding
rustc-demangletoCargo.tomland creatingsrc/classification/symbols.rs— both already exist and are fully implemented. The real issue is:src/pipeline/mod.rs→classify_strings()already checkss.text.starts_with('?')as a mangled-symbol heuristic (MSVC mangling)SymbolDemangler::is_mangled()andtry_demangle_internal()insrc/classification/symbols.rshave no?-prefix handlingThe new body documents what's done (Rust + C++ Itanium ABI), pinpoints the exact pipeline/demangler mismatch, and proposes a concrete fix using an MSVC demangling crate.
Labels updated:
priority:mediumstory-points: 2→story-points: 3SymbolDemangler, testsarea:analyzer,lang:rust,needs:tests, etc.)Milestone (
v0.1) and assignee (unclesp1d3r) preserved as-is.traycerai commentedon Mar 16, 2026
Plan
Observations
src/classification/symbols.rshasSymbolDemanglerwithis_mangled()andtry_demangle_internal()— both handle only_Rand_Zprefixes;?-prefixed MSVC symbols are absent.src/pipeline/mod.rs'sclassify_strings()already detects?-prefixed symbols inlooks_mangledbutSymbolDemanglersilently ignores them — no pipeline changes are needed.Cargo.tomlhasrustc-demangle = "0.1.27"andcpp_demangle = "0.5.1"but no MSVC demangler.deny.tomlallows MIT but not NCSA/University of Illinois. Themsvc-demanglercrate is MIT + NCSA dual-licensed, soNCSAmust be added to the[licenses] allowlist.deny.tomlhasmultiple-versions = "deny"— the new dependency must not introduce duplicate transitive versions.Approach
Add
msvc-demangler = "0.11"as a dependency, allow its NCSA license indeny.toml, then extendSymbolDemanglerwith atry_msvc_demangle()private method and wire it intois_mangled()andtry_demangle_internal(). This is a minimal, self-contained change that mirrors the existing Rust/C++ demangling pattern exactly.Implementation Steps
1. Add the dependency —
file:Cargo.tomlIn the
[dependencies]section, add:Place it alphabetically between
mmap-guardandpathargto match the existing ordering style.2. Allow the NCSA license —
file:deny.tomlThe
msvc-demanglercrate is MIT + NCSA dual-licensed. NCSA (University of Illinois Open Source License) is not currently in the[licenses] allowlist. Add"NCSA"to that list socargo deny checkcontinues to pass.3. Extend
SymbolDemangler—file:src/classification/symbols.rsa. Add the import
At the top of the file, alongside the existing
use cpp_demangle::Symbol as CppSymbol;, add themsvc_demanglerimport.b. Update
is_mangled()After the existing
_Zcheck, add a branch that returnstruewhen the symbol starts with'?'. Update the doc-comment to list MSVC as a third supported format and add a doc-example assertingis_mangled("?printf@@YAHPEBDZZ")returnstrue.c. Update
try_demangle_internal()After the existing
_Zbranch, add a branch: if the symbol starts with'?', delegate toself.try_msvc_demangle(symbol). Update the doc-comment accordingly.d. Add
try_msvc_demangle()private methodFollowing the pattern of
try_rust_demangle()andtry_cpp_demangle(), add:Inside, call
msvc_demangler::demangle(symbol, msvc_demangler::DemangleFlags::llvm()), unwrap with.ok()?, and returnSome(demangled_str)only when the result differs from the input symbol — matching the guard used in the other two helpers.e. Update module-level doc-comment
Add
**MSVC mangling**: Symbols starting with?(used by MSVC on Windows)to the "Supported Symbol Formats" list in the file header.f. Update
try_demangle()public method doc-commentAdd an MSVC example to the
# Examplesblock, e.g.try_demangle("?printf@@YAHPEBDZZ").4. Add unit tests —
file:src/classification/symbols.rsInside the existing
#[cfg(test)]block, add the following test functions following the establishedcreate_test_stringhelper pattern:test_is_mangled_msvc_symbolsis_mangledreturnstruefor?-prefixed symbols like?printf@@YAHPEBDZZ,??0MyClass@@QEAA@XZ(constructor),??HMyClass@@QEAAHH@Z(operator+)test_is_mangled_msvc_not_triggered_for_plainis_mangledstill returnsfalseforprintf,CreateFileW,""test_demangle_msvc_plain_functiondemangle()on?printf@@YAHPEBDZZsetsoriginal_text, addsTag::DemangledSymbol, and produces a result containingprintftest_demangle_msvc_constructordemangle()on a constructor-mangled symbol (e.g.??0MyClass@@QEAA@XZ) produces a demangled resulttest_demangle_msvc_operatordemangle()on an operator-overload symbol produces a demangled resulttest_demangle_msvc_invalid_fallbackdemangle()on?notvalid(not a real MSVC mangled name) leavesFoundStringunchangedtest_try_demangle_msvc_successtry_demangle("?printf@@YAHPEBDZZ")returnsSome(_)test_try_demangle_msvc_failuretry_demangle("?")returnsNonetest_msvc_symbol_preserves_existing_tagsTag::Import) are preserved after MSVC demanglingThese mirror the structure of the existing C++ tests (
test_is_mangled_cpp_symbols,test_demangle_cpp_symbol, etc.) for consistency.sequenceDiagram participant Pipeline as classify_strings() participant SD as SymbolDemangler participant RD as rustc_demangle participant CD as cpp_demangle participant MD as msvc_demangler Pipeline->>SD: demangle(&mut found_string) SD->>SD: is_mangled(text)? alt starts_with("_R") SD->>RD: try_rust_demangle() else starts_with("_Z") SD->>RD: try_rust_demangle() RD-->>SD: None SD->>CD: try_cpp_demangle() else starts_with("?") [NEW] SD->>MD: try_msvc_demangle() MD-->>SD: Some(demangled) end SD-->>Pipeline: sets text, original_text, Tag::DemangledSymbolImport In IDE
🤖 Prompt for AI Agents
Execution Information
Branch: main
Commit: cde9ad3
💡 Tips
Supported Commands (Inside Comments)
@traycerai generateto iterate on the previous version of the implementation plan.Supported Commands (Inside Description)
@traycerai ignoreanywhere in the ticket description to prevent this ticket from being processed.@traycerai branch:<branch-name>anywhere in the ticket description to specify the target branch for the implementation plan.Community