using Mono.Cecil; using Mono.Cecil.Cil; /// Resolve EH-only funclet lookups to the root method's EH table and address space. internal static class FuncletEhRuntimePatch { public const string HelperName = "XfeResolveEhFuncletRootV1"; 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 lookup = eh.Methods.Single(method => method.Name == "TryGetMethodLSDA"); var existing = eh.Methods.SingleOrDefault(method => method.Name == HelperName); if (existing is not null) { if (lookup.Body.Instructions.Count(instruction => instruction.Operand is MethodReference method && method.FullName == existing.FullName) != 1 || existing.Parameters.Count != 2 || existing.Body.Instructions.Count != 31) throw new InvalidOperationException("Unexpected EH funclet root resolver patch."); return true; } if (lookup.Body.Instructions.Count != 5 || lookup.Body.Instructions[3].Operand is not MethodReference original || original.Name != "TryGetMethodLSDA" || original.DeclaringType.FullName != "Cosmos.Kernel.Core.Runtime.GcInfo.MethodGcInfoLookup") throw new InvalidOperationException("Pinned EH LSDA wrapper changed; refusing to replace it."); var resolve = new MethodDefinition(HelperName, MethodAttributes.Private | MethodAttributes.Static | MethodAttributes.HideBySig, module.TypeSystem.Void); resolve.Parameters.Add(new("rootStart", ParameterAttributes.None, lookup.Parameters[1].ParameterType)); resolve.Parameters.Add(new("rootLsda", ParameterAttributes.None, lookup.Parameters[2].ParameterType)); resolve.Body.InitLocals = true; var cursor = new VariableDefinition(new PointerType(module.TypeSystem.Byte)); resolve.Body.Variables.Add(cursor); eh.Methods.Add(resolve); var il = resolve.Body.GetILProcessor(); var end = il.Create(OpCodes.Ret); il.Append(il.Create(OpCodes.Ldarg_1)); il.Append(il.Create(OpCodes.Ldind_I)); il.Append(il.Create(OpCodes.Ldind_U1)); il.Append(il.Create(OpCodes.Ldc_I4_3)); il.Append(il.Create(OpCodes.And)); il.Append(il.Create(OpCodes.Brfalse, end)); // Same signed relative-pointer format used by the unmodified pinned // GCInfo decoder: LSDA delta is relative to its own int32 slot. il.Append(il.Create(OpCodes.Ldarg_1)); il.Append(il.Create(OpCodes.Ldind_I)); il.Append(il.Create(OpCodes.Ldc_I4_1)); il.Append(il.Create(OpCodes.Add)); il.Append(il.Create(OpCodes.Stloc, cursor)); il.Append(il.Create(OpCodes.Ldarg_1)); il.Append(il.Create(OpCodes.Ldloc, cursor)); il.Append(il.Create(OpCodes.Ldloc, cursor)); il.Append(il.Create(OpCodes.Ldind_I4)); il.Append(il.Create(OpCodes.Conv_I)); il.Append(il.Create(OpCodes.Add)); il.Append(il.Create(OpCodes.Stind_I)); il.Append(il.Create(OpCodes.Ldloc, cursor)); il.Append(il.Create(OpCodes.Ldc_I4_4)); il.Append(il.Create(OpCodes.Add)); il.Append(il.Create(OpCodes.Stloc, cursor)); il.Append(il.Create(OpCodes.Ldarg_0)); il.Append(il.Create(OpCodes.Ldarg_0)); il.Append(il.Create(OpCodes.Ldind_I)); il.Append(il.Create(OpCodes.Ldloc, cursor)); il.Append(il.Create(OpCodes.Ldind_I4)); il.Append(il.Create(OpCodes.Conv_I)); il.Append(il.Create(OpCodes.Sub)); il.Append(il.Create(OpCodes.Stind_I)); il.Append(end); resolve.Body.MaxStackSize = 3; lookup.Body.Instructions.Clear(); var wrapper = lookup.Body.GetILProcessor(); var notFound = wrapper.Create(OpCodes.Ldc_I4_0); wrapper.Append(wrapper.Create(OpCodes.Ldarg_0)); wrapper.Append(wrapper.Create(OpCodes.Ldarg_1)); wrapper.Append(wrapper.Create(OpCodes.Ldarg_2)); wrapper.Append(wrapper.Create(OpCodes.Call, original)); wrapper.Append(wrapper.Create(OpCodes.Brfalse, notFound)); wrapper.Append(wrapper.Create(OpCodes.Ldarg_1)); wrapper.Append(wrapper.Create(OpCodes.Ldarg_2)); wrapper.Append(wrapper.Create(OpCodes.Call, resolve)); wrapper.Append(wrapper.Create(OpCodes.Ldc_I4_1)); wrapper.Append(wrapper.Create(OpCodes.Ret)); wrapper.Append(notFound); wrapper.Append(wrapper.Create(OpCodes.Ret)); lookup.Body.MaxStackSize = 3; return false; } }