319 lines
9.2 KiB
C#
319 lines
9.2 KiB
C#
// Copyright (c) StellaOps. All rights reserved.
|
|
// Licensed under AGPL-3.0-or-later. See LICENSE in the project root.
|
|
|
|
using System.Collections.Immutable;
|
|
|
|
namespace StellaOps.BinaryIndex.Semantic;
|
|
|
|
/// <summary>
|
|
/// A function lifted to intermediate representation.
|
|
/// </summary>
|
|
/// <param name="Name">Function name (may be empty for unnamed functions).</param>
|
|
/// <param name="Address">Start address of the function.</param>
|
|
/// <param name="Statements">IR statements comprising the function body.</param>
|
|
/// <param name="BasicBlocks">Basic blocks in the function.</param>
|
|
/// <param name="Cfg">Control flow graph.</param>
|
|
public sealed record LiftedFunction(
|
|
string Name,
|
|
ulong Address,
|
|
ImmutableArray<IrStatement> Statements,
|
|
ImmutableArray<IrBasicBlock> BasicBlocks,
|
|
ControlFlowGraph Cfg);
|
|
|
|
/// <summary>
|
|
/// A function transformed to Static Single Assignment (SSA) form.
|
|
/// </summary>
|
|
/// <param name="Name">Function name.</param>
|
|
/// <param name="Address">Start address of the function.</param>
|
|
/// <param name="Statements">SSA statements comprising the function body.</param>
|
|
/// <param name="BasicBlocks">SSA basic blocks in the function.</param>
|
|
/// <param name="DefUse">Definition-use chains for dataflow analysis.</param>
|
|
public sealed record SsaFunction(
|
|
string Name,
|
|
ulong Address,
|
|
ImmutableArray<SsaStatement> Statements,
|
|
ImmutableArray<SsaBasicBlock> BasicBlocks,
|
|
DefUseChains DefUse);
|
|
|
|
/// <summary>
|
|
/// An intermediate representation statement.
|
|
/// </summary>
|
|
/// <param name="Id">Unique statement ID within the function.</param>
|
|
/// <param name="Address">Original instruction address.</param>
|
|
/// <param name="Kind">Statement kind.</param>
|
|
/// <param name="Operation">Operation name (e.g., add, sub, load).</param>
|
|
/// <param name="Destination">Destination operand (if any).</param>
|
|
/// <param name="Sources">Source operands.</param>
|
|
/// <param name="Metadata">Additional metadata.</param>
|
|
public sealed record IrStatement(
|
|
int Id,
|
|
ulong Address,
|
|
IrStatementKind Kind,
|
|
string Operation,
|
|
IrOperand? Destination,
|
|
ImmutableArray<IrOperand> Sources,
|
|
ImmutableDictionary<string, object>? Metadata = null);
|
|
|
|
/// <summary>
|
|
/// Kind of IR statement.
|
|
/// </summary>
|
|
public enum IrStatementKind
|
|
{
|
|
/// <summary>Unknown statement kind.</summary>
|
|
Unknown = 0,
|
|
|
|
/// <summary>Assignment: dest = expr.</summary>
|
|
Assign,
|
|
|
|
/// <summary>Binary operation: dest = src1 op src2.</summary>
|
|
BinaryOp,
|
|
|
|
/// <summary>Unary operation: dest = op src.</summary>
|
|
UnaryOp,
|
|
|
|
/// <summary>Memory load: dest = [addr].</summary>
|
|
Load,
|
|
|
|
/// <summary>Memory store: [addr] = src.</summary>
|
|
Store,
|
|
|
|
/// <summary>Unconditional jump.</summary>
|
|
Jump,
|
|
|
|
/// <summary>Conditional jump.</summary>
|
|
ConditionalJump,
|
|
|
|
/// <summary>Function call.</summary>
|
|
Call,
|
|
|
|
/// <summary>Function return.</summary>
|
|
Return,
|
|
|
|
/// <summary>No operation.</summary>
|
|
Nop,
|
|
|
|
/// <summary>PHI node (for SSA form).</summary>
|
|
Phi,
|
|
|
|
/// <summary>System call.</summary>
|
|
Syscall,
|
|
|
|
/// <summary>Interrupt.</summary>
|
|
Interrupt,
|
|
|
|
/// <summary>Cast/type conversion.</summary>
|
|
Cast,
|
|
|
|
/// <summary>Comparison.</summary>
|
|
Compare,
|
|
|
|
/// <summary>Sign/zero extension.</summary>
|
|
Extend
|
|
}
|
|
|
|
/// <summary>
|
|
/// An operand in an IR statement.
|
|
/// </summary>
|
|
/// <param name="Kind">Operand kind.</param>
|
|
/// <param name="Name">Name (for temporaries and registers).</param>
|
|
/// <param name="Value">Constant value (for immediates).</param>
|
|
/// <param name="BitSize">Size in bits.</param>
|
|
/// <param name="IsMemory">Whether this is a memory reference.</param>
|
|
public sealed record IrOperand(
|
|
IrOperandKind Kind,
|
|
string? Name,
|
|
long? Value,
|
|
int BitSize,
|
|
bool IsMemory = false);
|
|
|
|
/// <summary>
|
|
/// Kind of IR operand.
|
|
/// </summary>
|
|
public enum IrOperandKind
|
|
{
|
|
/// <summary>Unknown operand kind.</summary>
|
|
Unknown = 0,
|
|
|
|
/// <summary>CPU register.</summary>
|
|
Register,
|
|
|
|
/// <summary>IR temporary variable.</summary>
|
|
Temporary,
|
|
|
|
/// <summary>Immediate constant value.</summary>
|
|
Immediate,
|
|
|
|
/// <summary>Memory address.</summary>
|
|
Memory,
|
|
|
|
/// <summary>Program counter / instruction pointer.</summary>
|
|
ProgramCounter,
|
|
|
|
/// <summary>Stack pointer.</summary>
|
|
StackPointer,
|
|
|
|
/// <summary>Base pointer / frame pointer.</summary>
|
|
FramePointer,
|
|
|
|
/// <summary>Flags/condition register.</summary>
|
|
Flags,
|
|
|
|
/// <summary>Undefined value (for SSA).</summary>
|
|
Undefined,
|
|
|
|
/// <summary>Label / address reference.</summary>
|
|
Label
|
|
}
|
|
|
|
/// <summary>
|
|
/// A basic block in the intermediate representation.
|
|
/// </summary>
|
|
/// <param name="Id">Unique block ID within the function.</param>
|
|
/// <param name="Label">Block label/name.</param>
|
|
/// <param name="StartAddress">Start address of the block.</param>
|
|
/// <param name="EndAddress">End address of the block (exclusive).</param>
|
|
/// <param name="StatementIds">IDs of statements in this block.</param>
|
|
/// <param name="Predecessors">IDs of predecessor blocks.</param>
|
|
/// <param name="Successors">IDs of successor blocks.</param>
|
|
public sealed record IrBasicBlock(
|
|
int Id,
|
|
string Label,
|
|
ulong StartAddress,
|
|
ulong EndAddress,
|
|
ImmutableArray<int> StatementIds,
|
|
ImmutableArray<int> Predecessors,
|
|
ImmutableArray<int> Successors);
|
|
|
|
/// <summary>
|
|
/// Control flow graph for a function.
|
|
/// </summary>
|
|
/// <param name="EntryBlockId">ID of the entry block.</param>
|
|
/// <param name="ExitBlockIds">IDs of exit blocks.</param>
|
|
/// <param name="Edges">CFG edges.</param>
|
|
public sealed record ControlFlowGraph(
|
|
int EntryBlockId,
|
|
ImmutableArray<int> ExitBlockIds,
|
|
ImmutableArray<CfgEdge> Edges);
|
|
|
|
/// <summary>
|
|
/// An edge in the control flow graph.
|
|
/// </summary>
|
|
/// <param name="SourceBlockId">Source block ID.</param>
|
|
/// <param name="TargetBlockId">Target block ID.</param>
|
|
/// <param name="Kind">Edge kind.</param>
|
|
/// <param name="Condition">Condition for conditional edges.</param>
|
|
public sealed record CfgEdge(
|
|
int SourceBlockId,
|
|
int TargetBlockId,
|
|
CfgEdgeKind Kind,
|
|
string? Condition = null);
|
|
|
|
/// <summary>
|
|
/// Kind of CFG edge.
|
|
/// </summary>
|
|
public enum CfgEdgeKind
|
|
{
|
|
/// <summary>Sequential fall-through.</summary>
|
|
FallThrough,
|
|
|
|
/// <summary>Unconditional jump.</summary>
|
|
Jump,
|
|
|
|
/// <summary>Conditional branch taken.</summary>
|
|
ConditionalTrue,
|
|
|
|
/// <summary>Conditional branch not taken.</summary>
|
|
ConditionalFalse,
|
|
|
|
/// <summary>Function call edge.</summary>
|
|
Call,
|
|
|
|
/// <summary>Function return edge.</summary>
|
|
Return,
|
|
|
|
/// <summary>Indirect jump (computed target).</summary>
|
|
Indirect,
|
|
|
|
/// <summary>Exception/interrupt edge.</summary>
|
|
Exception
|
|
}
|
|
|
|
/// <summary>
|
|
/// An SSA statement with versioned variables.
|
|
/// </summary>
|
|
/// <param name="Id">Unique statement ID within the function.</param>
|
|
/// <param name="Address">Original instruction address.</param>
|
|
/// <param name="Kind">Statement kind.</param>
|
|
/// <param name="Operation">Operation name.</param>
|
|
/// <param name="Destination">Destination SSA variable (if any).</param>
|
|
/// <param name="Sources">Source SSA variables.</param>
|
|
/// <param name="PhiSources">For PHI nodes: mapping from predecessor block to variable version.</param>
|
|
public sealed record SsaStatement(
|
|
int Id,
|
|
ulong Address,
|
|
IrStatementKind Kind,
|
|
string Operation,
|
|
SsaVariable? Destination,
|
|
ImmutableArray<SsaVariable> Sources,
|
|
ImmutableDictionary<int, SsaVariable>? PhiSources = null);
|
|
|
|
/// <summary>
|
|
/// An SSA variable (versioned).
|
|
/// </summary>
|
|
/// <param name="BaseName">Original variable/register name.</param>
|
|
/// <param name="Version">SSA version number.</param>
|
|
/// <param name="BitSize">Size in bits.</param>
|
|
/// <param name="Kind">Variable kind.</param>
|
|
public sealed record SsaVariable(
|
|
string BaseName,
|
|
int Version,
|
|
int BitSize,
|
|
SsaVariableKind Kind);
|
|
|
|
/// <summary>
|
|
/// Kind of SSA variable.
|
|
/// </summary>
|
|
public enum SsaVariableKind
|
|
{
|
|
/// <summary>CPU register.</summary>
|
|
Register,
|
|
|
|
/// <summary>IR temporary.</summary>
|
|
Temporary,
|
|
|
|
/// <summary>Memory location.</summary>
|
|
Memory,
|
|
|
|
/// <summary>Immediate constant.</summary>
|
|
Constant,
|
|
|
|
/// <summary>PHI result.</summary>
|
|
Phi
|
|
}
|
|
|
|
/// <summary>
|
|
/// An SSA basic block.
|
|
/// </summary>
|
|
/// <param name="Id">Unique block ID.</param>
|
|
/// <param name="Label">Block label.</param>
|
|
/// <param name="PhiNodes">PHI nodes at block entry.</param>
|
|
/// <param name="Statements">Non-PHI statements.</param>
|
|
/// <param name="Predecessors">Predecessor block IDs.</param>
|
|
/// <param name="Successors">Successor block IDs.</param>
|
|
public sealed record SsaBasicBlock(
|
|
int Id,
|
|
string Label,
|
|
ImmutableArray<SsaStatement> PhiNodes,
|
|
ImmutableArray<SsaStatement> Statements,
|
|
ImmutableArray<int> Predecessors,
|
|
ImmutableArray<int> Successors);
|
|
|
|
/// <summary>
|
|
/// Definition-use chains for SSA form.
|
|
/// </summary>
|
|
/// <param name="Definitions">Maps variable to its defining statement.</param>
|
|
/// <param name="Uses">Maps variable to statements that use it.</param>
|
|
public sealed record DefUseChains(
|
|
ImmutableDictionary<SsaVariable, int> Definitions,
|
|
ImmutableDictionary<SsaVariable, ImmutableHashSet<int>> Uses);
|