up
Some checks failed
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
Notify Smoke Test / Notify Unit Tests (push) Has been cancelled
Notify Smoke Test / Notifier Service Tests (push) Has been cancelled
Notify Smoke Test / Notification Smoke Test (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
Scanner Analyzers / Discover Analyzers (push) Has been cancelled
Scanner Analyzers / Build Analyzers (push) Has been cancelled
Scanner Analyzers / Test Language Analyzers (push) Has been cancelled
Scanner Analyzers / Validate Test Fixtures (push) Has been cancelled
Scanner Analyzers / Verify Deterministic Output (push) Has been cancelled
Signals CI & Image / signals-ci (push) Has been cancelled
Signals Reachability Scoring & Events / reachability-smoke (push) Has been cancelled
Signals Reachability Scoring & Events / sign-and-upload (push) Has been cancelled
Manifest Integrity / Validate Schema Integrity (push) Has been cancelled
Manifest Integrity / Validate Contract Documents (push) Has been cancelled
Manifest Integrity / Validate Pack Fixtures (push) Has been cancelled
Manifest Integrity / Audit SHA256SUMS Files (push) Has been cancelled
Manifest Integrity / Verify Merkle Roots (push) Has been cancelled
devportal-offline / build-offline (push) Has been cancelled
Mirror Thin Bundle Sign & Verify / mirror-sign (push) Has been cancelled

This commit is contained in:
StellaOps Bot
2025-12-13 18:08:55 +02:00
parent 6e45066e37
commit f1a39c4ce3
234 changed files with 24038 additions and 6910 deletions

View File

@@ -65,10 +65,11 @@ internal sealed class NativeCallgraphBuilder
.ThenBy(e => e.CallSiteOffset)
.ToImmutableArray();
// Sort roots per CONTRACT-INIT-ROOTS-401: by phase (numeric), then order, then target ID
var roots = _roots
.OrderBy(r => r.BinaryPath)
.ThenBy(r => r.Phase)
.OrderBy(r => (int)r.Phase)
.ThenBy(r => r.Order)
.ThenBy(r => r.TargetId, StringComparer.Ordinal)
.ToImmutableArray();
var unknowns = _unknowns
@@ -130,34 +131,34 @@ internal sealed class NativeCallgraphBuilder
private void AddSyntheticRoots(ElfFile elf)
{
// Find and add _start
AddRootIfExists(elf, "_start", NativeRootType.Start, "load", 0);
// Find and add _start (load phase)
AddRootIfExists(elf, "_start", NativeRootType.Start, "entry_point", NativeRootPhase.Load, 0);
// Find and add _init
AddRootIfExists(elf, "_init", NativeRootType.Init, "init", 0);
// Find and add _init (init phase, before init_array)
AddRootIfExists(elf, "_init", NativeRootType.Init, "DT_INIT", NativeRootPhase.Init, 0);
// Find and add _fini
AddRootIfExists(elf, "_fini", NativeRootType.Fini, "fini", 0);
// Find and add _fini (fini phase)
AddRootIfExists(elf, "_fini", NativeRootType.Fini, "DT_FINI", NativeRootPhase.Fini, 0);
// Find and add main
AddRootIfExists(elf, "main", NativeRootType.Main, "main", 0);
// Find and add main (main phase)
AddRootIfExists(elf, "main", NativeRootType.Main, "main", NativeRootPhase.Main, 0);
// Add preinit_array entries
// Add preinit_array entries (preinit phase)
for (var i = 0; i < elf.PreInitArraySymbols.Length; i++)
{
var symName = elf.PreInitArraySymbols[i];
AddRootByName(elf, symName, NativeRootType.PreInitArray, "preinit", i);
AddRootByName(elf, symName, NativeRootType.PreInitArray, "preinit_array", NativeRootPhase.PreInit, i);
}
// Add init_array entries
// Add init_array entries (init phase, order starts after DT_INIT)
for (var i = 0; i < elf.InitArraySymbols.Length; i++)
{
var symName = elf.InitArraySymbols[i];
AddRootByName(elf, symName, NativeRootType.InitArray, "init", i);
AddRootByName(elf, symName, NativeRootType.InitArray, "init_array", NativeRootPhase.Init, i + 1);
}
}
private void AddRootIfExists(ElfFile elf, string symbolName, NativeRootType rootType, string phase, int order)
private void AddRootIfExists(ElfFile elf, string symbolName, NativeRootType rootType, string source, NativeRootPhase phase, int order)
{
var sym = elf.Symbols.Concat(elf.DynamicSymbols)
.FirstOrDefault(s => s.Name == symbolName && s.Type == ElfSymbolType.Func);
@@ -170,18 +171,23 @@ internal sealed class NativeCallgraphBuilder
var binding = sym.Binding.ToString().ToLowerInvariant();
var symbolId = NativeGraphIdentifiers.ComputeSymbolId(sym.Name, sym.Value, sym.Size, binding);
var rootId = NativeGraphIdentifiers.ComputeRootId(symbolId, rootType, order);
// Use CONTRACT-INIT-ROOTS-401 compliant root ID format
var rootId = NativeGraphIdentifiers.ComputeRootId(phase, order, symbolId);
_roots.Add(new NativeSyntheticRoot(
RootId: rootId,
TargetId: symbolId,
RootType: rootType,
Source: source,
BinaryPath: elf.Path,
BuildId: elf.BuildId,
Phase: phase,
Order: order));
Order: order,
IsResolved: true,
TargetAddress: sym.Value));
}
private void AddRootByName(ElfFile elf, string symbolName, NativeRootType rootType, string phase, int order)
private void AddRootByName(ElfFile elf, string symbolName, NativeRootType rootType, string source, NativeRootPhase phase, int order)
{
// Check if it's a hex address placeholder
if (symbolName.StartsWith("func_0x", StringComparison.Ordinal))
@@ -191,14 +197,28 @@ internal sealed class NativeCallgraphBuilder
_unknowns.Add(new NativeUnknown(
UnknownId: unknownId,
UnknownType: NativeUnknownType.UnresolvedTarget,
SourceId: $"{elf.Path}:{phase}:{order}",
SourceId: $"{elf.Path}:{source}:{order}",
Name: symbolName,
Reason: "Init array entry could not be resolved to a symbol",
BinaryPath: elf.Path));
// Still add an unresolved root per CONTRACT-INIT-ROOTS-401
var unresolvedRootId = $"root:{phase.ToString().ToLowerInvariant()}:{order}:unknown:{symbolName}";
_roots.Add(new NativeSyntheticRoot(
RootId: unresolvedRootId,
TargetId: $"unknown:{symbolName}",
RootType: rootType,
Source: source,
BinaryPath: elf.Path,
BuildId: elf.BuildId,
Phase: phase,
Order: order,
IsResolved: false,
TargetAddress: null));
return;
}
AddRootIfExists(elf, symbolName, rootType, phase, order);
AddRootIfExists(elf, symbolName, rootType, source, phase, order);
}
private void AddRelocationEdges(ElfFile elf)

View File

@@ -0,0 +1,281 @@
namespace StellaOps.Scanner.Analyzers.Native.Internal.Demangle;
/// <summary>
/// Composite demangler that tries multiple demanglers in order.
/// Per DECISION-NATIVE-TOOLCHAIN-401: per-language managed demanglers with native fallback.
/// </summary>
internal sealed class CompositeDemangler : ISymbolDemangler
{
private readonly ISymbolDemangler[] _demanglers;
public CompositeDemangler(params ISymbolDemangler[] demanglers)
{
_demanglers = demanglers;
}
/// <summary>
/// Creates a default composite demangler with built-in demanglers.
/// </summary>
public static CompositeDemangler CreateDefault() =>
new(
new ItaniumAbiDemangler(),
new RustDemangler(),
new HeuristicDemangler());
public bool TryDemangle(string mangledName, out DemangleResult result)
{
if (string.IsNullOrEmpty(mangledName))
{
result = DemangleResult.Failed(mangledName ?? string.Empty, "Empty symbol name");
return false;
}
foreach (var demangler in _demanglers)
{
if (demangler.TryDemangle(mangledName, out result))
{
return true;
}
}
result = DemangleResult.Failed(mangledName, "No demangler recognized the symbol format");
return false;
}
}
/// <summary>
/// Itanium C++ ABI demangler (GCC/Clang style).
/// </summary>
internal sealed class ItaniumAbiDemangler : ISymbolDemangler
{
public bool TryDemangle(string mangledName, out DemangleResult result)
{
result = default!;
// Itanium ABI symbols start with _Z
if (!mangledName.StartsWith("_Z", StringComparison.Ordinal))
{
return false;
}
// Basic demangling for common patterns
// Full implementation would use a proper parser or external library
var demangled = TryParseItaniumSymbol(mangledName);
if (demangled is not null)
{
result = DemangleResult.Success(mangledName, demangled, DemangleSource.ItaniumAbi);
return true;
}
// Return the mangled name with heuristic confidence if we recognized but couldn't parse
result = DemangleResult.Heuristic(mangledName, mangledName, 0.6);
return true;
}
private static string? TryParseItaniumSymbol(string mangled)
{
// Simple pattern matching for common cases
// Full implementation would use a complete Itanium ABI parser
if (mangled.StartsWith("_ZN", StringComparison.Ordinal))
{
// Nested name: _ZN<length>name<length>name...E<signature>
return ParseNestedName(mangled);
}
if (mangled.StartsWith("_Z", StringComparison.Ordinal))
{
// Simple name: _Z<length>name<signature>
return ParseSimpleName(mangled);
}
return null;
}
private static string? ParseNestedName(string mangled)
{
// Basic nested name parsing: _ZN4Foo3BarE -> Foo::Bar
var components = new List<string>();
var pos = 3; // Skip "_ZN"
while (pos < mangled.Length)
{
if (mangled[pos] == 'E')
{
break; // End of nested name
}
// Read length
var lengthStart = pos;
while (pos < mangled.Length && char.IsDigit(mangled[pos]))
{
pos++;
}
if (pos == lengthStart)
{
break;
}
var length = int.Parse(mangled[lengthStart..pos]);
if (pos + length > mangled.Length)
{
break;
}
components.Add(mangled.Substring(pos, length));
pos += length;
}
if (components.Count == 0)
{
return null;
}
return string.Join("::", components);
}
private static string? ParseSimpleName(string mangled)
{
// Basic simple name parsing: _Z3foo -> foo
var pos = 2; // Skip "_Z"
// Read length
var lengthStart = pos;
while (pos < mangled.Length && char.IsDigit(mangled[pos]))
{
pos++;
}
if (pos == lengthStart)
{
return null;
}
var length = int.Parse(mangled[lengthStart..pos]);
if (pos + length > mangled.Length)
{
return null;
}
return mangled.Substring(pos, length);
}
}
/// <summary>
/// Rust symbol demangler.
/// </summary>
internal sealed class RustDemangler : ISymbolDemangler
{
public bool TryDemangle(string mangledName, out DemangleResult result)
{
result = default!;
// Rust symbols start with _ZN or _R (new scheme) with specific patterns
// Legacy: _ZN...17h<hash>E (contains 17h followed by hex hash)
// v0: _R...
if (!mangledName.StartsWith("_ZN", StringComparison.Ordinal) &&
!mangledName.StartsWith("_R", StringComparison.Ordinal))
{
return false;
}
// Check for Rust hash pattern (17h followed by 16 hex chars)
var hashIndex = mangledName.IndexOf("17h", StringComparison.Ordinal);
if (hashIndex < 0 && !mangledName.StartsWith("_R", StringComparison.Ordinal))
{
return false;
}
// Basic demangling - strip hash suffix for legacy format
var demangled = TryParseRustSymbol(mangledName);
if (demangled is not null)
{
result = DemangleResult.Success(mangledName, demangled, DemangleSource.Rust);
return true;
}
// Heuristic if we recognized the pattern
result = DemangleResult.Heuristic(mangledName, mangledName, 0.5);
return true;
}
private static string? TryParseRustSymbol(string mangled)
{
// Simple pattern: extract components before hash
if (!mangled.StartsWith("_ZN", StringComparison.Ordinal))
{
return null;
}
var hashIndex = mangled.IndexOf("17h", StringComparison.Ordinal);
var endIndex = hashIndex > 0 ? hashIndex : mangled.Length - 1;
var components = new List<string>();
var pos = 3; // Skip "_ZN"
while (pos < endIndex)
{
if (mangled[pos] == 'E')
{
break;
}
var lengthStart = pos;
while (pos < endIndex && char.IsDigit(mangled[pos]))
{
pos++;
}
if (pos == lengthStart)
{
break;
}
var length = int.Parse(mangled[lengthStart..pos]);
if (pos + length > mangled.Length)
{
break;
}
components.Add(mangled.Substring(pos, length));
pos += length;
}
if (components.Count == 0)
{
return null;
}
return string.Join("::", components);
}
}
/// <summary>
/// Heuristic demangler for unrecognized formats.
/// </summary>
internal sealed class HeuristicDemangler : ISymbolDemangler
{
public bool TryDemangle(string mangledName, out DemangleResult result)
{
// If the name doesn't look mangled, return it as-is with high confidence
if (!LooksMangled(mangledName))
{
result = DemangleResult.Success(mangledName, mangledName, DemangleSource.Heuristic);
return true;
}
// Otherwise return the mangled name with low confidence
result = DemangleResult.Failed(mangledName, "Unrecognized mangling scheme");
return false;
}
private static bool LooksMangled(string name) =>
// Common mangling prefixes
name.StartsWith("_Z", StringComparison.Ordinal) ||
name.StartsWith("?", StringComparison.Ordinal) || // MSVC
name.StartsWith("_R", StringComparison.Ordinal) || // Rust v0
name.StartsWith("__Z", StringComparison.Ordinal) || // macOS Itanium
name.Contains("@@", StringComparison.Ordinal); // MSVC decorated
}

View File

@@ -0,0 +1,80 @@
namespace StellaOps.Scanner.Analyzers.Native.Internal.Demangle;
/// <summary>
/// Interface for symbol demangling services.
/// Per DECISION-NATIVE-TOOLCHAIN-401 specification.
/// </summary>
public interface ISymbolDemangler
{
/// <summary>
/// Attempts to demangle a symbol name.
/// </summary>
/// <param name="mangledName">The mangled symbol name.</param>
/// <param name="result">The demangling result if successful.</param>
/// <returns>True if demangling was successful, false otherwise.</returns>
bool TryDemangle(string mangledName, out DemangleResult result);
}
/// <summary>
/// Result of a demangling operation.
/// </summary>
/// <param name="Mangled">Original mangled name.</param>
/// <param name="Demangled">Demangled human-readable name.</param>
/// <param name="Source">Demangling source (e.g., itanium-abi, msvc, rust).</param>
/// <param name="Confidence">Confidence level (1.0 for definite, lower for heuristic).</param>
/// <param name="Error">Error message if demangling partially failed.</param>
public sealed record DemangleResult(
string Mangled,
string? Demangled,
DemangleSource Source,
double Confidence,
string? Error = null)
{
/// <summary>
/// Creates a successful demangling result.
/// </summary>
public static DemangleResult Success(string mangled, string demangled, DemangleSource source) =>
new(mangled, demangled, source, 1.0);
/// <summary>
/// Creates a failed demangling result.
/// </summary>
public static DemangleResult Failed(string mangled, string? error = null) =>
new(mangled, null, DemangleSource.None, 0.3, error);
/// <summary>
/// Creates a heuristic demangling result.
/// </summary>
public static DemangleResult Heuristic(string mangled, string demangled, double confidence) =>
new(mangled, demangled, DemangleSource.Heuristic, confidence);
}
/// <summary>
/// Source of demangling operation per CONTRACT-NATIVE-TOOLCHAIN-401.
/// </summary>
public enum DemangleSource
{
/// <summary>Itanium C++ ABI (GCC/Clang).</summary>
ItaniumAbi,
/// <summary>Microsoft Visual C++.</summary>
Msvc,
/// <summary>Rust mangling.</summary>
Rust,
/// <summary>Swift mangling.</summary>
Swift,
/// <summary>D language mangling.</summary>
D,
/// <summary>Native tool fallback.</summary>
Fallback,
/// <summary>Pattern-based heuristic.</summary>
Heuristic,
/// <summary>No demangling available.</summary>
None,
}

View File

@@ -335,12 +335,13 @@ internal static class ElfReader
return null;
}
return Convert.ToHexString(gnuBuildId.Descriptor.Span).ToLowerInvariant();
var hexBuildId = Convert.ToHexString(gnuBuildId.Descriptor.Span).ToLowerInvariant();
return $"gnu-build-id:{hexBuildId}";
}
private static string FormatCodeId(string buildId)
{
// Format as ELF code-id (same as build-id for ELF)
// For ELF, code-id uses the prefixed build-id directly
return buildId;
}

View File

@@ -89,7 +89,7 @@ internal static class NativeGraphDsseWriter
TargetId: root.TargetId,
RootType: root.RootType.ToString().ToLowerInvariant(),
BinaryPath: root.BinaryPath,
Phase: root.Phase,
Phase: root.Phase.ToString().ToLowerInvariant(),
Order: root.Order);
await WriteLineAsync(writer, record, cancellationToken);
@@ -160,7 +160,7 @@ internal static class NativeGraphDsseWriter
TargetId: r.TargetId,
RootType: r.RootType.ToString().ToLowerInvariant(),
BinaryPath: r.BinaryPath,
Phase: r.Phase,
Phase: r.Phase.ToString().ToLowerInvariant(),
Order: r.Order)).ToArray(),
Unknowns: graph.Unknowns.OrderBy(u => u.UnknownId).Select(u => new NdjsonUnknownPayload(
UnknownId: u.UnknownId,

View File

@@ -92,20 +92,51 @@ public enum NativeEdgeType
/// <summary>
/// A synthetic root in the call graph (entry points that don't have callers).
/// Per CONTRACT-INIT-ROOTS-401 specification.
/// </summary>
/// <param name="RootId">Deterministic root identifier.</param>
/// <param name="RootId">Deterministic root identifier: root:{phase}:{order}:{target_id}.</param>
/// <param name="TargetId">SymbolId of the target function.</param>
/// <param name="RootType">Type of synthetic root.</param>
/// <param name="Source">Source of the root (e.g., init_array, DT_INIT, preinit_array, etc.).</param>
/// <param name="BinaryPath">Path to the containing binary.</param>
/// <param name="Phase">Execution phase (load, init, main, fini).</param>
/// <param name="BuildId">Build-ID of the binary (e.g., gnu-build-id:...).</param>
/// <param name="Phase">Execution phase (load=0, preinit=1, init=2, main=3, fini=4).</param>
/// <param name="Order">Order within the phase (for init arrays).</param>
/// <param name="IsResolved">Whether the target was successfully resolved.</param>
/// <param name="TargetAddress">Address of the target function if available.</param>
public sealed record NativeSyntheticRoot(
string RootId,
string TargetId,
NativeRootType RootType,
string Source,
string BinaryPath,
string Phase,
int Order);
string? BuildId,
NativeRootPhase Phase,
int Order,
bool IsResolved = true,
ulong? TargetAddress = null);
/// <summary>
/// Execution phase for synthetic roots.
/// Ordered by when they execute during program lifecycle.
/// </summary>
public enum NativeRootPhase
{
/// <summary>Dynamic linker resolution phase (order=0).</summary>
Load = 0,
/// <summary>Before dynamic init - DT_PREINIT_ARRAY (order=1).</summary>
PreInit = 1,
/// <summary>During initialization - DT_INIT, init_array (order=2).</summary>
Init = 2,
/// <summary>Program entry - main() (order=3).</summary>
Main = 3,
/// <summary>During termination - DT_FINI, fini_array (order=4).</summary>
Fini = 4,
}
/// <summary>
/// Type of synthetic root.
@@ -238,9 +269,20 @@ internal static class NativeGraphIdentifiers
}
/// <summary>
/// Computes a deterministic root ID.
/// Computes a deterministic root ID following CONTRACT-INIT-ROOTS-401 format.
/// Format: root:{phase}:{order}:{target_id}
/// </summary>
public static string ComputeRootId(string targetId, NativeRootType rootType, int order)
public static string ComputeRootId(NativeRootPhase phase, int order, string targetId)
{
var phaseName = phase.ToString().ToLowerInvariant();
return $"root:{phaseName}:{order}:{targetId}";
}
/// <summary>
/// Computes a deterministic root ID (legacy overload for backwards compatibility).
/// </summary>
[Obsolete("Use ComputeRootId(NativeRootPhase, int, string) for CONTRACT-INIT-ROOTS-401 compliance")]
public static string ComputeRootIdLegacy(string targetId, NativeRootType rootType, int order)
{
var input = $"{targetId}:{rootType}:{order}";
var hash = SHA256.HashData(Encoding.UTF8.GetBytes(input));