XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFESystem

【Rust】XFE系统,基于Rust和汇编

公开
关注 0 Fork 0 Star 0
UTF-8
using Mono.Cecil;
using Mono.Cecil.Cil;

internal static class EarlyBootConsoleRuntimePatchChecks
{
    public static void Run(Action<string, bool> check)
    {
        using var assembly = Fixture();
        var console = assembly.MainModule.Types.Single(type => type.Name == "EarlyGop");
        var originalPutChar = console.Methods.Single(method => method.Name == "PutChar");
        var originalBody = originalPutChar.Body.Instructions.ToArray();
        check("EarlyConsoleFirstApplyAddsExports", !EarlyBootConsoleRuntimePatch.Apply(assembly));
        check("EarlyConsoleSecondApplyVerifiesWithoutDuplicates", EarlyBootConsoleRuntimePatch.Apply(assembly) && console.Methods.Count == 3);
        check("EarlyConsoleOriginalWriterUnchanged", originalBody.SequenceEqual(originalPutChar.Body.Instructions));
        var writer = console.Methods.Single(method => method.Name == EarlyBootConsoleRuntimePatch.WriteCharName);
        int[] inputs = [int.MinValue, -65526, -1, 0, 9, 10, 13, 31, 32, 65, 126, 127, 255, 65546, int.MaxValue];
        check("EarlyConsoleAsciiAndLineBreaksOnly", inputs.All(value => Simulate(writer, value) ==
            (value is '\n' or '\r' or >= 0x20 and <= 0x7e ? value : '?')));
        check("EarlyConsoleExportsAvoidAllocationAndExceptions", console.Methods.Where(method => method.Name != "PutChar").All(method =>
            method.Body.Variables.Count == 0 && method.Body.ExceptionHandlers.Count == 0 &&
            method.Body.Instructions.All(instruction => instruction.OpCode.Code is not (Code.Newobj or Code.Newarr or Code.Throw or Code.Callvirt))));

        using (var serialized = new MemoryStream())
        {
            assembly.Write(serialized);
            serialized.Position = 0;
            using var reloaded = AssemblyDefinition.ReadAssembly(serialized);
            check("EarlyConsoleRoundtripIdempotence", EarlyBootConsoleRuntimePatch.Apply(reloaded));
        }
        using (var partial = Fixture())
        {
            EarlyBootConsoleRuntimePatch.Apply(partial);
            var type = partial.MainModule.Types.Single(type => type.Name == "EarlyGop");
            type.Methods.Remove(type.Methods.Single(method => method.Name == EarlyBootConsoleRuntimePatch.WriteCharName));
            check("EarlyConsolePartialPatchRejected", Rejects(() => EarlyBootConsoleRuntimePatch.Apply(partial)));
        }
        using (var altered = Fixture())
        {
            EarlyBootConsoleRuntimePatch.Apply(altered);
            var type = altered.MainModule.Types.Single(type => type.Name == "EarlyGop");
            type.Methods.Single(method => method.Name == EarlyBootConsoleRuntimePatch.WriteCharName).Body.Instructions[10].Operand = 255;
            check("EarlyConsoleAlteredRangeRejected", Rejects(() => EarlyBootConsoleRuntimePatch.Apply(altered)));
        }
        using (var altered = Fixture())
        {
            var type = altered.MainModule.Types.Single(type => type.Name == "EarlyGop");
            type.Fields.Single().FieldType = altered.MainModule.TypeSystem.Int32;
            check("EarlyConsoleChangedFieldContractRejected", Rejects(() => EarlyBootConsoleRuntimePatch.Apply(altered)));
        }
        using (var altered = Fixture())
        {
            var type = altered.MainModule.Types.Single(type => type.Name == "EarlyGop");
            type.Methods.Single().Parameters[0].ParameterType = altered.MainModule.TypeSystem.Int32;
            check("EarlyConsoleChangedWriterContractRejected", Rejects(() => EarlyBootConsoleRuntimePatch.Apply(altered)));
        }
        using (var altered = Fixture())
        {
            EarlyBootConsoleRuntimePatch.Apply(altered);
            var type = altered.MainModule.Types.Single(type => type.Name == "EarlyGop");
            var method = type.Methods.Single(method => method.Name == EarlyBootConsoleRuntimePatch.WriteCharName);
            method.Body.Instructions[2].Operand = method.Body.Instructions[^1];
            check("EarlyConsoleAlteredBranchRejected", Rejects(() => EarlyBootConsoleRuntimePatch.Apply(altered)));
        }
    }

    private static AssemblyDefinition Fixture()
    {
        var assembly = AssemblyDefinition.CreateAssembly(new AssemblyNameDefinition("EarlyConsoleFixture", new Version(1, 0)),
            "EarlyConsoleFixture", ModuleKind.Dll);
        var module = assembly.MainModule;
        var type = new TypeDefinition("Cosmos.Kernel.Core.IO", "EarlyGop", TypeAttributes.NotPublic | TypeAttributes.Abstract | TypeAttributes.Sealed,
            module.TypeSystem.Object);
        module.Types.Add(type);
        var enabled = new FieldDefinition("Enabled", FieldAttributes.Public | FieldAttributes.Static, module.TypeSystem.Boolean);
        type.Fields.Add(enabled);
        var putChar = new MethodDefinition("PutChar", MethodAttributes.Public | MethodAttributes.Static, module.TypeSystem.Void);
        putChar.Parameters.Add(new("character", ParameterAttributes.None, module.TypeSystem.Char));
        type.Methods.Add(putChar);
        var il = putChar.Body.GetILProcessor();
        il.Append(il.Create(OpCodes.Ldsfld, enabled)); il.Append(il.Create(OpCodes.Pop)); il.Append(il.Create(OpCodes.Ret));
        return assembly;
    }

    // Interpret the small integer/branch prefix rather than execute a hardware
    // framebuffer writer on the host. The output is the argument to PutChar.
    private static int Simulate(MethodDefinition method, int value)
    {
        var stack = new Stack<int>();
        var instructions = method.Body.Instructions;
        for (int index = 0, budget = 50; budget-- > 0; index++)
        {
            var instruction = instructions[index];
            switch (instruction.OpCode.Code)
            {
                case Code.Ldarg_0: stack.Push(value); break;
                case Code.Ldc_I4: stack.Push((int)instruction.Operand); break;
                case Code.Beq:
                case Code.Blt_Un:
                case Code.Bgt_Un:
                    int right = stack.Pop(), left = stack.Pop();
                    bool taken = instruction.OpCode.Code switch
                    {
                        Code.Beq => left == right,
                        Code.Blt_Un => unchecked((uint)left) < unchecked((uint)right),
                        _ => unchecked((uint)left) > unchecked((uint)right)
                    };
                    if (taken) index = instructions.IndexOf((Instruction)instruction.Operand) - 1;
                    break;
                case Code.Br: index = instructions.IndexOf((Instruction)instruction.Operand) - 1; break;
                case Code.Conv_U2: stack.Push(unchecked((ushort)stack.Pop())); break;
                case Code.Call: return stack.Pop();
                default: throw new InvalidOperationException("Unexpected early console simulation instruction.");
            }
        }
        throw new InvalidOperationException("The early console simulation exceeded its bounded instruction budget.");
    }

    private static bool Rejects(Action action)
    {
        try { action(); return false; }
        catch (InvalidOperationException) { return true; }
    }
}