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;

/// <summary>
/// Single-CPU context virtualization for the pinned runtime's otherwise global
/// exception dispatcher. All storage is allocated before a thread is runnable;
/// the interrupt-side switch contains field copies and two tiny native calls.
/// </summary>
internal static class ThreadEhRuntimePatch
{
    public const string SwitchName = "XfeSwitchThreadEhV1";
    public const string FieldPrefix = "_xfeThreadEhV1_";
    private static readonly string[] StateNames = ["s_isHandlingException", "s_activeCatchFramePointers", "s_activeCatchHandlers", "s_activeCatchExInfos", "s_activeCatchCount", "s_xfeEhTraceTypeCountV1", "s_xfeEhTraceTypesV1"];

    public static bool Apply(AssemblyDefinition assembly)
    {
        var module = assembly.MainModule;
        var eh = module.Types.Single(type => type.FullName == "Cosmos.Kernel.Core.Runtime.ExceptionHandling.ExceptionHelper");
        var thread = module.Types.Single(type => type.FullName == "Cosmos.Kernel.Core.Scheduler.SchedulerThread");
        var cpu = module.Types.Single(type => type.FullName == "Cosmos.Kernel.Core.Scheduler.PerCpuState");
        var setter = cpu.Methods.Single(method => method.Name == "set_CurrentThread");
        var current = cpu.Fields.Single(field => field.Name == "<CurrentThread>k__BackingField");
        var constructor = thread.Methods.Single(method => method.IsConstructor && !method.IsStatic);
        var originals = StateNames.Select(name => eh.Fields.Single(field => field.Name == name)).ToArray();
        var existing = eh.Methods.SingleOrDefault(method => method.Name == SwitchName);
        if (existing is not null)
        {
            if (thread.Fields.Count(field => field.Name.StartsWith(FieldPrefix, StringComparison.Ordinal)) != StateNames.Length + 1 ||
                setter.Body.Instructions.Count(instruction => instruction.Operand is MethodReference method && method.FullName == existing.FullName) != 1 ||
                existing.Body.Instructions.Any(instruction => instruction.OpCode.Code is Code.Newobj or Code.Newarr or Code.Throw) ||
                originals.Any(field => field.IsInitOnly) ||
                constructor.Body.Instructions.Count(instruction => instruction.OpCode == OpCodes.Newarr) != 4 ||
                eh.Methods.Count(method => method.Name == "XfeThreadEhContractVersionV1" || method.Name == "XfeCurrentSchedulerThreadIdV1") != 2)
                throw new InvalidOperationException("Unexpected per-thread EH patch shape.");
            return true;
        }
        if (constructor.Body.Instructions.Count != 3 || constructor.Body.Instructions[^1].OpCode != OpCodes.Ret ||
            setter.Body.Instructions.Count != 4 || setter.Body.Instructions[2].OpCode != OpCodes.Stfld ||
            setter.Body.Instructions[2].Operand is not FieldReference stored || stored.FullName != current.FullName)
            throw new InvalidOperationException("Pinned scheduler constructor/current-thread setter changed; refusing to guess.");

        var state = originals.Select(field => new FieldDefinition(FieldPrefix + field.Name, FieldAttributes.Assembly, field.FieldType)).ToArray();
        foreach (var field in state) thread.Fields.Add(field);
        var nativeHead = new FieldDefinition(FieldPrefix + "NativeExInfoHead", FieldAttributes.Assembly, module.TypeSystem.UIntPtr);
        thread.Fields.Add(nativeHead);
        // The NativeAOT optimizer must not treat the currently installed arrays
        // as immutable globals: they change at each actual thread switch.
        foreach (var field in originals) field.Attributes &= ~FieldAttributes.InitOnly;

        var ctorIl = constructor.Body.GetILProcessor();
        var ctorReturn = constructor.Body.Instructions[^1];
        for (int index = 0; index < state.Length; index++)
            if (state[index].FieldType is ArrayType array)
            {
                ctorIl.InsertBefore(ctorReturn, ctorIl.Create(OpCodes.Ldarg_0));
                ctorIl.InsertBefore(ctorReturn, ctorIl.Create(OpCodes.Ldc_I4, StateNames[index] == "s_xfeEhTraceTypesV1" ? EhRuntimePatch.TraceLimit : EhRuntimePatch.Limit));
                ctorIl.InsertBefore(ctorReturn, ctorIl.Create(OpCodes.Newarr, array.ElementType));
                ctorIl.InsertBefore(ctorReturn, ctorIl.Create(OpCodes.Stfld, state[index]));
            }
        constructor.Body.MaxStackSize = 2;

        var getHead = Import("XfeGetNativeExInfoHeadV1", "XfeRuntimeGetExInfoHead", module.TypeSystem.UIntPtr);
        var setHead = Import("XfeSetNativeExInfoHeadV1", "XfeRuntimeSetExInfoHead", module.TypeSystem.Void);
        setHead.Parameters.Add(new("value", ParameterAttributes.None, module.TypeSystem.UIntPtr));
        var change = new MethodDefinition(SwitchName, MethodAttributes.Assembly | MethodAttributes.Static | MethodAttributes.HideBySig, module.TypeSystem.Void);
        change.Parameters.Add(new("previous", ParameterAttributes.None, thread));
        change.Parameters.Add(new("next", ParameterAttributes.None, thread));
        eh.Methods.Add(change);
        var il = change.Body.GetILProcessor();
        var end = il.Create(OpCodes.Ret);
        var realSwitch = il.Create(OpCodes.Ldarg_0);
        il.Append(il.Create(OpCodes.Ldarg_1)); il.Append(il.Create(OpCodes.Brfalse, end));
        il.Append(il.Create(OpCodes.Ldarg_0)); il.Append(il.Create(OpCodes.Ldarg_1)); il.Append(il.Create(OpCodes.Beq, end));
        il.Append(il.Create(OpCodes.Ldarg_0)); il.Append(il.Create(OpCodes.Brtrue, realSwitch));
        // The first null->idle assignment adopts the already running boot
        // thread's state instead of resetting a live boot exception chain.
        Save(1); il.Append(il.Create(OpCodes.Br, end));
        il.Append(realSwitch); il.Append(il.Create(OpCodes.Pop));
        Save(0);
        for (int index = 0; index < state.Length; index++)
        { il.Append(il.Create(OpCodes.Ldarg_1)); il.Append(il.Create(OpCodes.Ldfld, state[index])); il.Append(il.Create(OpCodes.Stsfld, originals[index])); }
        il.Append(il.Create(OpCodes.Ldarg_1)); il.Append(il.Create(OpCodes.Ldfld, nativeHead)); il.Append(il.Create(OpCodes.Call, setHead));
        il.Append(end); change.Body.MaxStackSize = 2;

        var setterIl = setter.Body.GetILProcessor(); var first = setter.Body.Instructions[0];
        setterIl.InsertBefore(first, setterIl.Create(OpCodes.Ldarg_0)); setterIl.InsertBefore(first, setterIl.Create(OpCodes.Ldfld, current));
        setterIl.InsertBefore(first, setterIl.Create(OpCodes.Ldarg_1)); setterIl.InsertBefore(first, setterIl.Create(OpCodes.Call, change));
        setter.Body.MaxStackSize = Math.Max(2, setter.Body.MaxStackSize);
        var version = new MethodDefinition("XfeThreadEhContractVersionV1", MethodAttributes.Assembly | MethodAttributes.Static | MethodAttributes.HideBySig, module.TypeSystem.Int32);
        var export = new CustomAttribute(module.ImportReference(typeof(System.Runtime.InteropServices.UnmanagedCallersOnlyAttribute).GetConstructor(Type.EmptyTypes)!));
        export.Fields.Add(new("EntryPoint", new CustomAttributeArgument(module.TypeSystem.String, "XfeRuntimeThreadEhContractVersion")));
        version.CustomAttributes.Add(export); eh.Methods.Add(version);
        var versionIl = version.Body.GetILProcessor(); var disabled = versionIl.Create(OpCodes.Ldc_I4_0);
        var scheduler = module.Types.Single(type => type.FullName == "Cosmos.Kernel.Core.Scheduler.SchedulerManager");
        versionIl.Append(versionIl.Create(OpCodes.Call, scheduler.Methods.Single(method => method.Name == "get_IsRunning")));
        versionIl.Append(versionIl.Create(OpCodes.Brfalse, disabled));
        versionIl.Append(versionIl.Create(OpCodes.Call, scheduler.Methods.Single(method => method.Name == "get_CpuCount")));
        versionIl.Append(versionIl.Create(OpCodes.Ldc_I4_1)); versionIl.Append(versionIl.Create(OpCodes.Bne_Un, disabled));
        versionIl.Append(versionIl.Create(OpCodes.Ldc_I4_1)); versionIl.Append(versionIl.Create(OpCodes.Ret));
        versionIl.Append(disabled); versionIl.Append(versionIl.Create(OpCodes.Ret)); version.Body.MaxStackSize = 2;
        var identity = new MethodDefinition("XfeCurrentSchedulerThreadIdV1", MethodAttributes.Assembly | MethodAttributes.Static | MethodAttributes.HideBySig, module.TypeSystem.Int32);
        var identityExport = new CustomAttribute(module.ImportReference(typeof(System.Runtime.InteropServices.UnmanagedCallersOnlyAttribute).GetConstructor(Type.EmptyTypes)!));
        identityExport.Fields.Add(new("EntryPoint", new CustomAttributeArgument(module.TypeSystem.String, "XfeRuntimeCurrentSchedulerThreadId")));
        identity.CustomAttributes.Add(identityExport); eh.Methods.Add(identity);
        var identityIl = identity.Body.GetILProcessor(); var haveCpu = identityIl.Create(OpCodes.Callvirt, cpu.Methods.Single(method => method.Name == "get_CurrentThread"));
        var haveThread = identityIl.Create(OpCodes.Callvirt, thread.Methods.Single(method => method.Name == "get_Id"));
        identityIl.Append(identityIl.Create(OpCodes.Call, scheduler.Methods.Single(method => method.Name == "get_CurrentCpuState")));
        identityIl.Append(identityIl.Create(OpCodes.Dup)); identityIl.Append(identityIl.Create(OpCodes.Brtrue, haveCpu));
        identityIl.Append(identityIl.Create(OpCodes.Pop)); identityIl.Append(identityIl.Create(OpCodes.Ldc_I4_0)); identityIl.Append(identityIl.Create(OpCodes.Ret));
        identityIl.Append(haveCpu); identityIl.Append(identityIl.Create(OpCodes.Dup)); identityIl.Append(identityIl.Create(OpCodes.Brtrue, haveThread));
        identityIl.Append(identityIl.Create(OpCodes.Pop)); identityIl.Append(identityIl.Create(OpCodes.Ldc_I4_0)); identityIl.Append(identityIl.Create(OpCodes.Ret));
        identityIl.Append(haveThread); identityIl.Append(identityIl.Create(OpCodes.Ret)); identity.Body.MaxStackSize = 2;
        return false;

        void Save(int argument)
        {
            for (int index = 0; index < state.Length; index++)
            { il.Append(il.Create(argument == 0 ? OpCodes.Ldarg_0 : OpCodes.Ldarg_1)); il.Append(il.Create(OpCodes.Ldsfld, originals[index])); il.Append(il.Create(OpCodes.Stfld, state[index])); }
            il.Append(il.Create(argument == 0 ? OpCodes.Ldarg_0 : OpCodes.Ldarg_1)); il.Append(il.Create(OpCodes.Call, getHead)); il.Append(il.Create(OpCodes.Stfld, nativeHead));
        }
        MethodDefinition Import(string name, string symbol, TypeReference returns)
        {
            var method = new MethodDefinition(name, MethodAttributes.Private | MethodAttributes.Static | MethodAttributes.HideBySig | MethodAttributes.PInvokeImpl, returns)
            { ImplAttributes = MethodImplAttributes.PreserveSig };
            var library = module.ModuleReferences.SingleOrDefault(reference => reference.Name == "libSystem.Native");
            if (library is null) { library = new ModuleReference("libSystem.Native"); module.ModuleReferences.Add(library); }
            method.PInvokeInfo = new PInvokeInfo(PInvokeAttributes.CallConvCdecl | PInvokeAttributes.NoMangle, symbol, library);
            method.CustomAttributes.Add(new CustomAttribute(module.ImportReference(typeof(System.Runtime.InteropServices.SuppressGCTransitionAttribute).GetConstructor(Type.EmptyTypes)!)));
            eh.Methods.Add(method); return method;
        }
    }
}