Files
git.stella-ops.org/src/BinaryIndex/__Libraries/StellaOps.BinaryIndex.Builders/SymbolDiff/SymbolTableDiffAnalyzer.cs
2026-02-01 21:37:40 +02:00

807 lines
28 KiB
C#

// -----------------------------------------------------------------------------
// SymbolTableDiffAnalyzer.cs
// Sprint: SPRINT_20260106_001_003_BINDEX_symbol_table_diff
// Tasks: SYM-007 to SYM-015 - Implement symbol table diff analyzer
// Description: Symbol table diff analyzer implementation
// -----------------------------------------------------------------------------
using Microsoft.Extensions.Logging;
using System.Globalization;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
namespace StellaOps.BinaryIndex.Builders.SymbolDiff;
/// <summary>
/// Analyzes symbol table differences between two binaries.
/// </summary>
public sealed class SymbolTableDiffAnalyzer : ISymbolTableDiffAnalyzer
{
private readonly ISymbolExtractor _symbolExtractor;
private readonly INameDemangler _nameDemangler;
private readonly TimeProvider _timeProvider;
private readonly ILogger<SymbolTableDiffAnalyzer> _logger;
public SymbolTableDiffAnalyzer(
ISymbolExtractor symbolExtractor,
INameDemangler nameDemangler,
TimeProvider timeProvider,
ILogger<SymbolTableDiffAnalyzer> logger)
{
_symbolExtractor = symbolExtractor;
_nameDemangler = nameDemangler;
_timeProvider = timeProvider;
_logger = logger;
}
/// <inheritdoc />
public async Task<SymbolTableDiff> ComputeDiffAsync(
string basePath,
string targetPath,
SymbolDiffOptions? options = null,
CancellationToken ct = default)
{
options ??= new SymbolDiffOptions();
var now = _timeProvider.GetUtcNow();
_logger.LogDebug("Computing symbol diff between {Base} and {Target}", basePath, targetPath);
// Extract symbol tables
var baseTable = await ExtractSymbolTableAsync(basePath, ct);
var targetTable = await ExtractSymbolTableAsync(targetPath, ct);
// Compute symbol changes
var exports = ComputeSymbolChanges(
baseTable.Exports,
targetTable.Exports,
options);
var imports = ComputeSymbolChanges(
baseTable.Imports,
targetTable.Imports,
options);
// Compute version diff
var versions = ComputeVersionDiff(baseTable, targetTable);
// Compute dynamic linking diff
DynamicLinkingDiff? dynamic = null;
if (options.IncludeDynamicLinking)
{
dynamic = ComputeDynamicLinkingDiff(baseTable, targetTable);
}
// Create diff without ID first
var diffWithoutId = new SymbolTableDiff
{
DiffId = string.Empty, // Placeholder
Base = baseTable.Binary,
Target = targetTable.Binary,
Exports = exports,
Imports = imports,
Versions = versions,
Dynamic = dynamic,
AbiCompatibility = new AbiCompatibility
{
Level = AbiCompatibilityLevel.FullyCompatible,
Score = 1.0,
IsBackwardCompatible = true,
IsForwardCompatible = true,
BreakingChanges = [],
Warnings = [],
Summary = new AbiSummary()
},
ComputedAt = now
};
// Assess ABI compatibility
var abiCompatibility = AssessAbiCompatibility(diffWithoutId);
// Compute content-addressed ID
var diffId = ComputeDiffId(baseTable.Binary, targetTable.Binary, exports, imports);
var diff = diffWithoutId with
{
DiffId = diffId,
AbiCompatibility = abiCompatibility
};
_logger.LogInformation(
"Symbol diff complete: {ExportsAdded} exports added, {ExportsRemoved} removed, {Level}",
exports.Counts.Added,
exports.Counts.Removed,
abiCompatibility.Level);
return diff;
}
/// <inheritdoc />
public async Task<SymbolTable> ExtractSymbolTableAsync(
string binaryPath,
CancellationToken ct = default)
{
return await _symbolExtractor.ExtractAsync(binaryPath, ct);
}
/// <inheritdoc />
public AbiCompatibility AssessAbiCompatibility(SymbolTableDiff diff)
{
var breakingChanges = new List<AbiBreakingChange>();
var warnings = new List<AbiWarning>();
// Removed exports are breaking
foreach (var removed in diff.Exports.Removed)
{
breakingChanges.Add(new AbiBreakingChange
{
Type = AbiBreakType.SymbolRemoved,
Severity = removed.Binding == SymbolBinding.Weak ? ChangeSeverity.Low : ChangeSeverity.High,
Symbol = removed.Name,
Description = $"Exported symbol '{removed.DemangledName ?? removed.Name}' was removed",
Impact = "Code linking against this symbol will fail at runtime",
Mitigation = "Provide symbol alias or versioned symbol for backward compatibility"
});
}
// Modified exports with size changes
foreach (var modified in diff.Exports.Modified)
{
if (modified.IsAbiBreaking)
{
foreach (var change in modified.Changes.Where(c => c.Severity >= ChangeSeverity.High))
{
breakingChanges.Add(new AbiBreakingChange
{
Type = DetermineBreakType(change),
Severity = change.Severity,
Symbol = modified.Name,
Description = $"Symbol '{modified.DemangledName ?? modified.Name}' {change.Attribute} changed from {change.BaseValue} to {change.TargetValue}",
Details = $"Attribute: {change.Attribute}"
});
}
}
}
// Version removals
foreach (var removed in diff.Versions.DefinitionsRemoved)
{
if (!removed.IsBase)
{
breakingChanges.Add(new AbiBreakingChange
{
Type = AbiBreakType.VersionRemoved,
Severity = ChangeSeverity.High,
Symbol = removed.Name,
Description = $"Version definition '{removed.Name}' was removed"
});
}
}
// Added exports are warnings
foreach (var added in diff.Exports.Added)
{
warnings.Add(new AbiWarning
{
Type = AbiWarningType.SymbolAdded,
Symbol = added.Name,
Message = $"New exported symbol: {added.DemangledName ?? added.Name}"
});
}
// Renames are warnings
foreach (var rename in diff.Exports.Renamed)
{
warnings.Add(new AbiWarning
{
Type = AbiWarningType.SymbolRenamed,
Symbol = rename.BaseName,
Message = $"Symbol renamed from '{rename.BaseDemangled ?? rename.BaseName}' to '{rename.TargetDemangled ?? rename.TargetName}'"
});
}
// Calculate compatibility level and score
var level = DetermineCompatibilityLevel(breakingChanges);
var score = CalculateCompatibilityScore(diff, breakingChanges);
return new AbiCompatibility
{
Level = level,
Score = score,
IsBackwardCompatible = breakingChanges.Count == 0,
IsForwardCompatible = diff.Exports.Added.Count == 0,
BreakingChanges = breakingChanges,
Warnings = warnings,
Summary = new AbiSummary
{
TotalExportsBase = diff.Exports.Counts.TotalBase,
TotalExportsTarget = diff.Exports.Counts.TotalTarget,
ExportsAdded = diff.Exports.Counts.Added,
ExportsRemoved = diff.Exports.Counts.Removed,
ExportsModified = diff.Exports.Counts.Modified,
BreakingChangesCount = breakingChanges.Count,
WarningsCount = warnings.Count,
CompatibilityPercentage = score * 100
}
};
}
// SYM-009, SYM-010: Compute symbol changes
private SymbolChangeSummary ComputeSymbolChanges(
IReadOnlyList<ExtractedSymbol> baseSymbols,
IReadOnlyList<ExtractedSymbol> targetSymbols,
SymbolDiffOptions options)
{
var baseByName = baseSymbols.ToDictionary(s => s.Name, s => s);
var targetByName = targetSymbols.ToDictionary(s => s.Name, s => s);
var added = new List<SymbolChange>();
var removed = new List<SymbolChange>();
var modified = new List<SymbolModification>();
var renamed = new List<SymbolRename>();
var unchanged = 0;
// Find added symbols
foreach (var target in targetSymbols)
{
if (!baseByName.ContainsKey(target.Name))
{
added.Add(MapToSymbolChange(target));
}
}
// Find removed and modified symbols
foreach (var baseSymbol in baseSymbols)
{
if (!targetByName.TryGetValue(baseSymbol.Name, out var targetSymbol))
{
removed.Add(MapToSymbolChange(baseSymbol));
}
else
{
var modification = DetectModification(baseSymbol, targetSymbol);
if (modification is not null)
{
modified.Add(modification);
}
else
{
unchanged++;
}
}
}
// Detect renames (removed symbols that match added symbols via fingerprint)
if (options.DetectRenames)
{
var detectedRenames = DetectRenames(
removed,
added,
options.RenameSimilarityThreshold);
renamed.AddRange(detectedRenames);
// Remove renamed from added/removed
var renamedBaseNames = new HashSet<string>(detectedRenames.Select(r => r.BaseName));
var renamedTargetNames = new HashSet<string>(detectedRenames.Select(r => r.TargetName));
removed.RemoveAll(r => renamedBaseNames.Contains(r.Name));
added.RemoveAll(a => renamedTargetNames.Contains(a.Name));
}
return new SymbolChangeSummary
{
Added = added,
Removed = removed,
Modified = modified,
Renamed = renamed,
Counts = new SymbolChangeCounts
{
Added = added.Count,
Removed = removed.Count,
Modified = modified.Count,
Renamed = renamed.Count,
Unchanged = unchanged,
TotalBase = baseSymbols.Count,
TotalTarget = targetSymbols.Count
}
};
}
// SYM-011: Compute version diff
private VersionMapDiff ComputeVersionDiff(SymbolTable baseTable, SymbolTable targetTable)
{
var baseDefs = baseTable.VersionDefinitions.ToDictionary(v => v.Name);
var targetDefs = targetTable.VersionDefinitions.ToDictionary(v => v.Name);
var defsAdded = targetTable.VersionDefinitions
.Where(v => !baseDefs.ContainsKey(v.Name))
.ToList();
var defsRemoved = baseTable.VersionDefinitions
.Where(v => !targetDefs.ContainsKey(v.Name))
.ToList();
var baseReqs = baseTable.VersionRequirements
.ToDictionary(r => $"{r.Library}@{r.Version}");
var targetReqs = targetTable.VersionRequirements
.ToDictionary(r => $"{r.Library}@{r.Version}");
var reqsAdded = targetTable.VersionRequirements
.Where(r => !baseReqs.ContainsKey($"{r.Library}@{r.Version}"))
.ToList();
var reqsRemoved = baseTable.VersionRequirements
.Where(r => !targetReqs.ContainsKey($"{r.Library}@{r.Version}"))
.ToList();
// Detect version assignment changes
var assignmentChanges = new List<VersionAssignmentChange>();
var baseExports = baseTable.Exports.Where(e => e.Version is not null).ToDictionary(e => e.Name);
foreach (var target in targetTable.Exports.Where(e => e.Version is not null))
{
if (baseExports.TryGetValue(target.Name, out var baseExport))
{
if (baseExport.Version != target.Version)
{
assignmentChanges.Add(new VersionAssignmentChange
{
SymbolName = target.Name,
BaseVersion = baseExport.Version,
TargetVersion = target.Version,
IsAbiBreaking = true // Version changes can be breaking
});
}
}
}
return new VersionMapDiff
{
DefinitionsAdded = defsAdded,
DefinitionsRemoved = defsRemoved,
RequirementsAdded = reqsAdded,
RequirementsRemoved = reqsRemoved,
AssignmentsChanged = assignmentChanges,
Counts = new VersionChangeCounts
{
DefinitionsAdded = defsAdded.Count,
DefinitionsRemoved = defsRemoved.Count,
RequirementsAdded = reqsAdded.Count,
RequirementsRemoved = reqsRemoved.Count,
AssignmentsChanged = assignmentChanges.Count
}
};
}
// SYM-012: Compute dynamic linking diff
private DynamicLinkingDiff ComputeDynamicLinkingDiff(SymbolTable baseTable, SymbolTable targetTable)
{
return new DynamicLinkingDiff
{
Got = ComputeGotDiff(baseTable.GotEntries ?? [], targetTable.GotEntries ?? []),
Plt = ComputePltDiff(baseTable.PltEntries ?? [], targetTable.PltEntries ?? []),
Rpath = ComputeRpathDiff(baseTable, targetTable),
Needed = ComputeNeededDiff(baseTable.NeededLibraries, targetTable.NeededLibraries)
};
}
private GotDiff ComputeGotDiff(IReadOnlyList<GotEntry> baseEntries, IReadOnlyList<GotEntry> targetEntries)
{
var baseBySymbol = baseEntries.ToDictionary(e => e.SymbolName);
var targetBySymbol = targetEntries.ToDictionary(e => e.SymbolName);
var added = targetEntries.Where(e => !baseBySymbol.ContainsKey(e.SymbolName)).ToList();
var removed = baseEntries.Where(e => !targetBySymbol.ContainsKey(e.SymbolName)).ToList();
var modified = new List<GotEntryModification>();
foreach (var baseEntry in baseEntries)
{
if (targetBySymbol.TryGetValue(baseEntry.SymbolName, out var targetEntry))
{
if (baseEntry.Type != targetEntry.Type || baseEntry.Address != targetEntry.Address)
{
modified.Add(new GotEntryModification
{
SymbolName = baseEntry.SymbolName,
BaseAddress = baseEntry.Address,
TargetAddress = targetEntry.Address,
BaseType = baseEntry.Type,
TargetType = targetEntry.Type
});
}
}
}
return new GotDiff
{
EntriesAdded = added,
EntriesRemoved = removed,
EntriesModified = modified,
BaseCount = baseEntries.Count,
TargetCount = targetEntries.Count
};
}
private PltDiff ComputePltDiff(IReadOnlyList<PltEntry> baseEntries, IReadOnlyList<PltEntry> targetEntries)
{
var baseBySymbol = baseEntries.ToDictionary(e => e.SymbolName);
var targetBySymbol = targetEntries.ToDictionary(e => e.SymbolName);
var added = targetEntries.Where(e => !baseBySymbol.ContainsKey(e.SymbolName)).ToList();
var removed = baseEntries.Where(e => !targetBySymbol.ContainsKey(e.SymbolName)).ToList();
var reordered = new List<PltReorder>();
foreach (var baseEntry in baseEntries)
{
if (targetBySymbol.TryGetValue(baseEntry.SymbolName, out var targetEntry))
{
if (baseEntry.Index != targetEntry.Index)
{
reordered.Add(new PltReorder
{
SymbolName = baseEntry.SymbolName,
BaseIndex = baseEntry.Index,
TargetIndex = targetEntry.Index
});
}
}
}
return new PltDiff
{
EntriesAdded = added,
EntriesRemoved = removed,
EntriesReordered = reordered,
BaseCount = baseEntries.Count,
TargetCount = targetEntries.Count
};
}
private RpathDiff ComputeRpathDiff(SymbolTable baseTable, SymbolTable targetTable)
{
var basePaths = new HashSet<string>(
(baseTable.Rpath ?? []).Concat(baseTable.Runpath ?? []));
var targetPaths = new HashSet<string>(
(targetTable.Rpath ?? []).Concat(targetTable.Runpath ?? []));
return new RpathDiff
{
RpathBase = baseTable.Rpath,
RpathTarget = targetTable.Rpath,
RunpathBase = baseTable.Runpath,
RunpathTarget = targetTable.Runpath,
PathsAdded = targetPaths.Except(basePaths).ToList(),
PathsRemoved = basePaths.Except(targetPaths).ToList(),
HasChanges = !basePaths.SetEquals(targetPaths)
};
}
private NeededDiff ComputeNeededDiff(IReadOnlyList<string> baseLibs, IReadOnlyList<string> targetLibs)
{
var baseSet = new HashSet<string>(baseLibs);
var targetSet = new HashSet<string>(targetLibs);
return new NeededDiff
{
LibrariesAdded = targetSet.Except(baseSet).ToList(),
LibrariesRemoved = baseSet.Except(targetSet).ToList(),
BaseLibraries = baseLibs,
TargetLibraries = targetLibs
};
}
// SYM-013: Detect renames via fingerprint matching
private IReadOnlyList<SymbolRename> DetectRenames(
List<SymbolChange> removed,
List<SymbolChange> added,
double threshold)
{
var renames = new List<SymbolRename>();
// Only consider symbols with fingerprints
var removedWithFp = removed.Where(r => r.Fingerprint is not null).ToList();
var addedWithFp = added.Where(a => a.Fingerprint is not null).ToList();
foreach (var removedSymbol in removedWithFp)
{
// Find best match in added
SymbolChange? bestMatch = null;
double bestSimilarity = 0;
foreach (var addedSymbol in addedWithFp)
{
var similarity = ComputeFingerprintSimilarity(
removedSymbol.Fingerprint!,
addedSymbol.Fingerprint!);
if (similarity >= threshold && similarity > bestSimilarity)
{
bestMatch = addedSymbol;
bestSimilarity = similarity;
}
}
if (bestMatch is not null)
{
renames.Add(new SymbolRename
{
BaseName = removedSymbol.Name,
TargetName = bestMatch.Name,
BaseDemangled = removedSymbol.DemangledName,
TargetDemangled = bestMatch.DemangledName,
Fingerprint = removedSymbol.Fingerprint!,
Similarity = bestSimilarity,
Confidence = DetermineRenameConfidence(bestSimilarity)
});
// Remove matched from consideration
addedWithFp.Remove(bestMatch);
}
}
return renames;
}
// SYM-015: Compute content-addressed diff ID
private string ComputeDiffId(
BinaryRef baseRef,
BinaryRef targetRef,
SymbolChangeSummary exports,
SymbolChangeSummary imports)
{
var canonical = new
{
base_sha256 = baseRef.Sha256,
target_sha256 = targetRef.Sha256,
exports_added = exports.Added.Select(e => e.Name).OrderBy(n => n, StringComparer.Ordinal),
exports_removed = exports.Removed.Select(e => e.Name).OrderBy(n => n, StringComparer.Ordinal),
imports_added = imports.Added.Select(i => i.Name).OrderBy(n => n, StringComparer.Ordinal),
imports_removed = imports.Removed.Select(i => i.Name).OrderBy(n => n, StringComparer.Ordinal)
};
var json = JsonSerializer.Serialize(canonical, new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower,
WriteIndented = false
});
var hash = SHA256.HashData(Encoding.UTF8.GetBytes(json));
return $"sha256:{Convert.ToHexString(hash).ToLowerInvariant()}";
}
// Helper methods
private static SymbolChange MapToSymbolChange(ExtractedSymbol symbol)
{
return new SymbolChange
{
Name = symbol.Name,
DemangledName = symbol.DemangledName,
Type = symbol.Type,
Binding = symbol.Binding,
Visibility = symbol.Visibility,
Section = symbol.Section,
Address = symbol.Address,
Size = symbol.Size,
Version = symbol.Version,
Fingerprint = symbol.Fingerprint
};
}
private static SymbolModification? DetectModification(ExtractedSymbol baseSymbol, ExtractedSymbol targetSymbol)
{
var changes = new List<AttributeChange>();
if (baseSymbol.Type != targetSymbol.Type)
{
changes.Add(new AttributeChange
{
Attribute = "type",
BaseValue = baseSymbol.Type.ToString(),
TargetValue = targetSymbol.Type.ToString(),
Severity = ChangeSeverity.High
});
}
if (baseSymbol.Size != targetSymbol.Size)
{
changes.Add(new AttributeChange
{
Attribute = "size",
BaseValue = baseSymbol.Size.ToString(CultureInfo.InvariantCulture),
TargetValue = targetSymbol.Size.ToString(CultureInfo.InvariantCulture),
Severity = targetSymbol.Size < baseSymbol.Size ? ChangeSeverity.High : ChangeSeverity.Low
});
}
if (baseSymbol.Visibility != targetSymbol.Visibility)
{
var severityFromVisibility = (baseSymbol.Visibility, targetSymbol.Visibility) switch
{
(SymbolVisibility.Default, SymbolVisibility.Hidden) => ChangeSeverity.High,
(SymbolVisibility.Protected, SymbolVisibility.Hidden) => ChangeSeverity.High,
_ => ChangeSeverity.Medium
};
changes.Add(new AttributeChange
{
Attribute = "visibility",
BaseValue = baseSymbol.Visibility.ToString(),
TargetValue = targetSymbol.Visibility.ToString(),
Severity = severityFromVisibility
});
}
if (baseSymbol.Binding != targetSymbol.Binding)
{
changes.Add(new AttributeChange
{
Attribute = "binding",
BaseValue = baseSymbol.Binding.ToString(),
TargetValue = targetSymbol.Binding.ToString(),
Severity = ChangeSeverity.Medium
});
}
if (changes.Count == 0)
{
return null;
}
return new SymbolModification
{
Name = baseSymbol.Name,
DemangledName = baseSymbol.DemangledName ?? targetSymbol.DemangledName,
Base = new SymbolAttributes
{
Type = baseSymbol.Type,
Binding = baseSymbol.Binding,
Visibility = baseSymbol.Visibility,
Section = baseSymbol.Section,
Address = baseSymbol.Address,
Size = baseSymbol.Size,
Version = baseSymbol.Version,
Fingerprint = baseSymbol.Fingerprint
},
Target = new SymbolAttributes
{
Type = targetSymbol.Type,
Binding = targetSymbol.Binding,
Visibility = targetSymbol.Visibility,
Section = targetSymbol.Section,
Address = targetSymbol.Address,
Size = targetSymbol.Size,
Version = targetSymbol.Version,
Fingerprint = targetSymbol.Fingerprint
},
Changes = changes,
IsAbiBreaking = changes.Any(c => c.Severity >= ChangeSeverity.High)
};
}
private static double ComputeFingerprintSimilarity(string fp1, string fp2)
{
if (fp1 == fp2) return 1.0;
// Simple Jaccard similarity on hex characters
var set1 = new HashSet<char>(fp1);
var set2 = new HashSet<char>(fp2);
var intersection = set1.Intersect(set2).Count();
var union = set1.Union(set2).Count();
return union == 0 ? 0 : (double)intersection / union;
}
private static RenameConfidence DetermineRenameConfidence(double similarity)
{
return similarity switch
{
>= 0.95 => RenameConfidence.VeryHigh,
>= 0.85 => RenameConfidence.High,
>= 0.75 => RenameConfidence.Medium,
>= 0.65 => RenameConfidence.Low,
_ => RenameConfidence.VeryLow
};
}
private static AbiBreakType DetermineBreakType(AttributeChange change)
{
return change.Attribute switch
{
"type" => AbiBreakType.SymbolTypeChanged,
"size" => AbiBreakType.SymbolSizeChanged,
"visibility" => AbiBreakType.VisibilityReduced,
"binding" => AbiBreakType.BindingChanged,
_ => AbiBreakType.SymbolTypeChanged
};
}
private static AbiCompatibilityLevel DetermineCompatibilityLevel(List<AbiBreakingChange> breaks)
{
if (breaks.Count == 0)
{
return AbiCompatibilityLevel.FullyCompatible;
}
var criticalCount = breaks.Count(b => b.Severity == ChangeSeverity.Critical);
var highCount = breaks.Count(b => b.Severity == ChangeSeverity.High);
if (criticalCount > 0 || highCount >= 10)
{
return AbiCompatibilityLevel.Incompatible;
}
if (highCount >= 3)
{
return AbiCompatibilityLevel.MajorIncompatibility;
}
if (highCount >= 1)
{
return AbiCompatibilityLevel.MinorIncompatibility;
}
return AbiCompatibilityLevel.CompatibleWithWarnings;
}
private static double CalculateCompatibilityScore(SymbolTableDiff diff, List<AbiBreakingChange> breaks)
{
if (diff.Exports.Counts.TotalBase == 0)
{
return 1.0;
}
var removedWeight = diff.Exports.Counts.Removed * 0.5;
var breakingWeight = breaks.Sum(b => b.Severity switch
{
ChangeSeverity.Critical => 1.0,
ChangeSeverity.High => 0.5,
ChangeSeverity.Medium => 0.2,
_ => 0.1
});
var penalty = (removedWeight + breakingWeight) / diff.Exports.Counts.TotalBase;
return Math.Max(0, 1.0 - penalty);
}
}
/// <summary>
/// Interface for extracting symbols from binaries.
/// </summary>
public interface ISymbolExtractor
{
/// <summary>
/// Extracts symbol table from a binary.
/// </summary>
Task<SymbolTable> ExtractAsync(string binaryPath, CancellationToken ct = default);
}
/// <summary>
/// Interface for demangling C++/Rust names.
/// </summary>
public interface INameDemangler
{
/// <summary>
/// Demangles a symbol name.
/// </summary>
string? Demangle(string mangledName);
/// <summary>
/// Detects the mangling scheme.
/// </summary>
ManglingScheme DetectScheme(string name);
}
/// <summary>
/// Name mangling scheme.
/// </summary>
public enum ManglingScheme
{
None,
ItaniumCxx,
MicrosoftCxx,
Rust,
Swift,
Unknown
}