Files
git.stella-ops.org/src/BinaryIndex/__Tests/StellaOps.BinaryIndex.Semantic.Tests/IrLiftingServiceTests.cs
StellaOps Bot 37e11918e0 save progress
2026-01-06 09:42:20 +02:00

209 lines
6.3 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;
using FluentAssertions;
using Microsoft.Extensions.Logging.Abstractions;
using StellaOps.BinaryIndex.Disassembly;
using Xunit;
namespace StellaOps.BinaryIndex.Semantic.Tests;
[Trait("Category", "Unit")]
public class IrLiftingServiceTests
{
private readonly IrLiftingService _sut;
public IrLiftingServiceTests()
{
_sut = new IrLiftingService(NullLogger<IrLiftingService>.Instance);
}
[Theory]
[InlineData(CpuArchitecture.X86)]
[InlineData(CpuArchitecture.X86_64)]
[InlineData(CpuArchitecture.ARM32)]
[InlineData(CpuArchitecture.ARM64)]
public void SupportsArchitecture_ShouldReturnTrue_ForSupportedArchitectures(CpuArchitecture arch)
{
// Act
var result = _sut.SupportsArchitecture(arch);
// Assert
result.Should().BeTrue();
}
[Theory]
[InlineData(CpuArchitecture.MIPS32)]
[InlineData(CpuArchitecture.RISCV64)]
[InlineData(CpuArchitecture.Unknown)]
public void SupportsArchitecture_ShouldReturnFalse_ForUnsupportedArchitectures(CpuArchitecture arch)
{
// Act
var result = _sut.SupportsArchitecture(arch);
// Assert
result.Should().BeFalse();
}
[Fact]
public async Task LiftToIrAsync_ShouldLiftSimpleInstructions()
{
// Arrange
var instructions = new List<DisassembledInstruction>
{
CreateInstruction(0x1000, "MOV", InstructionKind.Move, "RAX", "RBX"),
CreateInstruction(0x1004, "ADD", InstructionKind.Arithmetic, "RAX", "RCX"),
CreateInstruction(0x1008, "RET", InstructionKind.Return)
};
// Act
var result = await _sut.LiftToIrAsync(
instructions,
"test_func",
0x1000,
CpuArchitecture.X86_64);
// Assert
result.Should().NotBeNull();
result.Name.Should().Be("test_func");
result.Address.Should().Be(0x1000);
result.Statements.Should().HaveCount(3);
result.BasicBlocks.Should().NotBeEmpty();
}
[Fact]
public async Task LiftToIrAsync_ShouldCreateBasicBlocksOnBranches()
{
// Arrange
var instructions = new List<DisassembledInstruction>
{
CreateInstruction(0x1000, "MOV", InstructionKind.Move, "RAX", "0"),
CreateInstruction(0x1004, "CMP", InstructionKind.Compare, "RAX", "10"),
CreateInstruction(0x1008, "JE", InstructionKind.ConditionalBranch, "0x1020"),
CreateInstruction(0x100C, "ADD", InstructionKind.Arithmetic, "RAX", "1"),
CreateInstruction(0x1010, "RET", InstructionKind.Return)
};
// Act
var result = await _sut.LiftToIrAsync(
instructions,
"branch_func",
0x1000,
CpuArchitecture.X86_64);
// Assert
result.BasicBlocks.Should().HaveCountGreaterThan(1);
result.Cfg.Edges.Should().NotBeEmpty();
}
[Fact]
public async Task LiftToIrAsync_ShouldThrow_ForUnsupportedArchitecture()
{
// Arrange
var instructions = new List<DisassembledInstruction>
{
CreateInstruction(0x1000, "NOP", InstructionKind.Nop)
};
// Act
var act = () => _sut.LiftToIrAsync(
instructions,
"test",
0x1000,
CpuArchitecture.MIPS32);
// Assert
await act.Should().ThrowAsync<NotSupportedException>();
}
[Fact]
public async Task TransformToSsaAsync_ShouldVersionVariables()
{
// Arrange
var instructions = new List<DisassembledInstruction>
{
CreateInstruction(0x1000, "MOV", InstructionKind.Move, "RAX", "0"),
CreateInstruction(0x1004, "ADD", InstructionKind.Arithmetic, "RAX", "1"),
CreateInstruction(0x1008, "ADD", InstructionKind.Arithmetic, "RAX", "2"),
CreateInstruction(0x100C, "RET", InstructionKind.Return)
};
var lifted = await _sut.LiftToIrAsync(
instructions,
"ssa_test",
0x1000,
CpuArchitecture.X86_64);
// Act
var ssa = await _sut.TransformToSsaAsync(lifted);
// Assert
ssa.Should().NotBeNull();
ssa.Name.Should().Be("ssa_test");
ssa.Statements.Should().HaveCount(4);
// RAX should have multiple versions
var raxVersions = ssa.Statements
.Where(s => s.Destination?.BaseName == "RAX")
.Select(s => s.Destination!.Version)
.Distinct()
.ToList();
raxVersions.Should().HaveCountGreaterThan(1);
}
[Fact]
public async Task TransformToSsaAsync_ShouldBuildDefUseChains()
{
// Arrange
var instructions = new List<DisassembledInstruction>
{
CreateInstruction(0x1000, "MOV", InstructionKind.Move, "RAX", "0"),
CreateInstruction(0x1004, "ADD", InstructionKind.Arithmetic, "RBX", "RAX"),
CreateInstruction(0x1008, "RET", InstructionKind.Return)
};
var lifted = await _sut.LiftToIrAsync(
instructions,
"defuse_test",
0x1000,
CpuArchitecture.X86_64);
// Act
var ssa = await _sut.TransformToSsaAsync(lifted);
// Assert
ssa.DefUse.Should().NotBeNull();
ssa.DefUse.Definitions.Should().NotBeEmpty();
}
private static DisassembledInstruction CreateInstruction(
ulong address,
string mnemonic,
InstructionKind kind,
params string[] operands)
{
var ops = operands.Select((o, i) =>
{
if (long.TryParse(o, out var val))
{
return new Operand(OperandType.Immediate, o, val);
}
if (o.StartsWith("0x", StringComparison.OrdinalIgnoreCase))
{
return new Operand(OperandType.Address, o);
}
return new Operand(OperandType.Register, o, Register: o);
}).ToImmutableArray();
return new DisassembledInstruction(
address,
[0x90], // NOP placeholder
mnemonic,
string.Join(", ", operands),
kind,
ops);
}
}