using System; using System.Buffers; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Linq; using System.Net; using System.Numerics; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using System.Threading; using Iced.Intel; using Mono.Cecil; using Mono.Cecil.Cil; using MonoMod; using MonoMod.Core.Interop; using MonoMod.Core.Interop.Attributes; using MonoMod.Core.Platforms; using MonoMod.Core.Platforms.Architectures; using MonoMod.Core.Platforms.Architectures.AltEntryFactories; using MonoMod.Core.Platforms.Memory; using MonoMod.Core.Platforms.Runtimes; using MonoMod.Core.Platforms.Systems; using MonoMod.Core.Utils; using MonoMod.Logs; using MonoMod.Utils; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: DefaultDllImportSearchPaths(DllImportSearchPath.SafeDirectories)] [assembly: DisableRuntimeMarshalling] [assembly: CLSCompliant(false)] [assembly: TargetFramework(".NETCoreApp,Version=v8.0", FrameworkDisplayName = ".NET 8.0")] [assembly: AssemblyCompany("0x0ade, DaNike")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyCopyright("Copyright 2026 0x0ade, DaNike")] [assembly: AssemblyDescription("The code functionality for runtime detouring. Use `DetourFactory.Current` to get a DetourFactory capable of installing unique method detours.\n \n WARNING: THIS IS A LOW-LEVEL LIBRARY THAT IS DIFFICULT TO USE ON ITS OWN. If possible, use MonoMod.RuntimeDetour instead.")] [assembly: AssemblyFileVersion("1.3.4.0")] [assembly: AssemblyInformationalVersion("1.3.4+69fdc9deb")] [assembly: AssemblyProduct("MonoMod.Core")] [assembly: AssemblyTitle("MonoMod.Core")] [assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/MonoMod/MonoMod")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.3.4.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] internal class { static () { MMDbgLog.LogVersion(); } } [CompilerGenerated] internal unsafe delegate CoreCLR.CorJitResult <>f__AnonymousDelegate0(nint arg1, nint arg2, CoreCLR.V21.CORINFO_METHOD_INFO* arg3, uint arg4, byte** arg5, uint* arg6); [CompilerGenerated] internal unsafe delegate CoreCLR.CorJitResult <>f__AnonymousDelegate1(nint arg1, nint arg2, CoreCLR.V21.CORINFO_METHOD_INFO* arg3, uint arg4, byte** arg5, uint* arg6, CoreCLR.CorJitResult arg7, CoreCLR.V60.AllocMemArgs* arg8); [CompilerGenerated] internal unsafe delegate void <>f__AnonymousDelegate2(nint arg1, CoreCLR.V60.AllocMemArgs* arg2); namespace MonoMod { internal static class MMDbgLog { [InterpolatedStringHandler] internal ref struct DebugLogSpamStringHandler { internal DebugLogInterpolatedStringHandler handler; public DebugLogSpamStringHandler(int literalLen, int formattedCount, out bool isEnabled) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) handler = new DebugLogInterpolatedStringHandler(literalLen, formattedCount, (LogLevel)0, ref isEnabled); } public override string ToString() { return ((object)Unsafe.As(ref handler)/*cast due to .constrained prefix*/).ToString(); } public string ToStringAndClear() { return ((DebugLogInterpolatedStringHandler)(ref handler)).ToStringAndClear(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendLiteral(string s) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendLiteral(s); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(string? s) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(s); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(string? s, int alignment = 0, string? format = null) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(s, alignment, format); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(ReadOnlySpan s) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(s); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(ReadOnlySpan s, int alignment = 0, string? format = null) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(s, alignment, format); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(T value) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(T value, int alignment) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(value, alignment); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(T value, string? format) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(value, format); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(T value, int alignment, string? format) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(value, alignment, format); } } [InterpolatedStringHandler] internal ref struct DebugLogTraceStringHandler { internal DebugLogInterpolatedStringHandler handler; public DebugLogTraceStringHandler(int literalLen, int formattedCount, out bool isEnabled) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) handler = new DebugLogInterpolatedStringHandler(literalLen, formattedCount, (LogLevel)1, ref isEnabled); } public override string ToString() { return ((object)Unsafe.As(ref handler)/*cast due to .constrained prefix*/).ToString(); } public string ToStringAndClear() { return ((DebugLogInterpolatedStringHandler)(ref handler)).ToStringAndClear(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendLiteral(string s) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendLiteral(s); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(string? s) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(s); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(string? s, int alignment = 0, string? format = null) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(s, alignment, format); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(ReadOnlySpan s) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(s); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(ReadOnlySpan s, int alignment = 0, string? format = null) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(s, alignment, format); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(T value) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(T value, int alignment) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(value, alignment); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(T value, string? format) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(value, format); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(T value, int alignment, string? format) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(value, alignment, format); } } [InterpolatedStringHandler] internal ref struct DebugLogInfoStringHandler { internal DebugLogInterpolatedStringHandler handler; public DebugLogInfoStringHandler(int literalLen, int formattedCount, out bool isEnabled) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) handler = new DebugLogInterpolatedStringHandler(literalLen, formattedCount, (LogLevel)2, ref isEnabled); } public override string ToString() { return ((object)Unsafe.As(ref handler)/*cast due to .constrained prefix*/).ToString(); } public string ToStringAndClear() { return ((DebugLogInterpolatedStringHandler)(ref handler)).ToStringAndClear(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendLiteral(string s) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendLiteral(s); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(string? s) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(s); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(string? s, int alignment = 0, string? format = null) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(s, alignment, format); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(ReadOnlySpan s) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(s); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(ReadOnlySpan s, int alignment = 0, string? format = null) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(s, alignment, format); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(T value) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(T value, int alignment) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(value, alignment); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(T value, string? format) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(value, format); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(T value, int alignment, string? format) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(value, alignment, format); } } [InterpolatedStringHandler] internal ref struct DebugLogWarningStringHandler { internal DebugLogInterpolatedStringHandler handler; public DebugLogWarningStringHandler(int literalLen, int formattedCount, out bool isEnabled) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) handler = new DebugLogInterpolatedStringHandler(literalLen, formattedCount, (LogLevel)3, ref isEnabled); } public override string ToString() { return ((object)Unsafe.As(ref handler)/*cast due to .constrained prefix*/).ToString(); } public string ToStringAndClear() { return ((DebugLogInterpolatedStringHandler)(ref handler)).ToStringAndClear(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendLiteral(string s) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendLiteral(s); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(string? s) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(s); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(string? s, int alignment = 0, string? format = null) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(s, alignment, format); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(ReadOnlySpan s) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(s); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(ReadOnlySpan s, int alignment = 0, string? format = null) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(s, alignment, format); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(T value) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(T value, int alignment) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(value, alignment); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(T value, string? format) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(value, format); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(T value, int alignment, string? format) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(value, alignment, format); } } [InterpolatedStringHandler] internal ref struct DebugLogErrorStringHandler { internal DebugLogInterpolatedStringHandler handler; public DebugLogErrorStringHandler(int literalLen, int formattedCount, out bool isEnabled) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) handler = new DebugLogInterpolatedStringHandler(literalLen, formattedCount, (LogLevel)4, ref isEnabled); } public override string ToString() { return ((object)Unsafe.As(ref handler)/*cast due to .constrained prefix*/).ToString(); } public string ToStringAndClear() { return ((DebugLogInterpolatedStringHandler)(ref handler)).ToStringAndClear(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendLiteral(string s) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendLiteral(s); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(string? s) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(s); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(string? s, int alignment = 0, string? format = null) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(s, alignment, format); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(ReadOnlySpan s) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(s); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(ReadOnlySpan s, int alignment = 0, string? format = null) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(s, alignment, format); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(T value) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(T value, int alignment) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(value, alignment); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(T value, string? format) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(value, format); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendFormatted(T value, int alignment, string? format) { ((DebugLogInterpolatedStringHandler)(ref handler)).AppendFormatted(value, alignment, format); } } public static bool IsWritingLog => DebugLog.IsWritingLog; [ModuleInitializer] internal static void LogVersion() { Info("Version 1.3.4"); } public static void Log(LogLevel level, string message) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) DebugLog.Log("MonoMod.Core", level, message); } public static void Log(LogLevel level, [InterpolatedStringHandlerArgument("level")] ref DebugLogInterpolatedStringHandler message) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) DebugLog.Log("MonoMod.Core", level, ref message); } public static void Spam(string message) { Log((LogLevel)0, message); } public static void Spam(ref DebugLogSpamStringHandler message) { Log((LogLevel)0, ref message.handler); } public static void Trace(string message) { Log((LogLevel)1, message); } public static void Trace(ref DebugLogTraceStringHandler message) { Log((LogLevel)1, ref message.handler); } public static void Info(string message) { Log((LogLevel)2, message); } public static void Info(ref DebugLogInfoStringHandler message) { Log((LogLevel)2, ref message.handler); } public static void Warning(string message) { Log((LogLevel)3, message); } public static void Warning(ref DebugLogWarningStringHandler message) { Log((LogLevel)3, ref message.handler); } public static void Error(string message) { Log((LogLevel)4, message); } public static void Error(ref DebugLogErrorStringHandler message) { Log((LogLevel)4, ref message.handler); } } internal static class MultiTargetShims { public static TypeReference GetConstraintType(this GenericParameterConstraint constraint) { return constraint.ConstraintType; } } } namespace MonoMod.SourceGen.Attributes { [AttributeUsage(AttributeTargets.Class)] internal sealed class EmitILOverloadsAttribute : Attribute { public EmitILOverloadsAttribute(string filename, string kind) { } } internal static class ILOverloadKind { public const string Cursor = "ILCursor"; public const string Matcher = "ILMatcher"; } } namespace MonoMod.Core { [CLSCompliant(true)] public interface ICoreDetour : ICoreDetourBase, IDisposable { MethodBase Source { get; } MethodBase Target { get; } } public interface ICoreDetourWithClone : ICoreDetour, ICoreDetourBase, IDisposable { MethodInfo? SourceMethodClone { get; } DynamicMethodDefinition? SourceMethodCloneIL { get; } } public interface ICoreDetourBase : IDisposable { bool IsApplied { get; } void Apply(); void Undo(); } public interface ICoreNativeDetour : ICoreDetourBase, IDisposable { nint Source { get; } nint Target { get; } bool HasOrigEntrypoint { get; } nint OrigEntrypoint { get; } } [CLSCompliant(true)] public interface IDetourFactory { bool SupportsNativeDetourOrigEntrypoint { get; } ICoreDetour CreateDetour(CreateDetourRequest request); ICoreNativeDetour CreateNativeDetour(CreateNativeDetourRequest request); } [CLSCompliant(true)] public readonly record struct CreateDetourRequest { public MethodBase Source { get; init; } public MethodBase Target { get; init; } public bool ApplyByDefault { get; init; } public bool CreateSourceCloneIfNotILClone { get; init; } public CreateDetourRequest(MethodBase Source, MethodBase Target) { CreateSourceCloneIfNotILClone = false; this.Source = Source; this.Target = Target; ApplyByDefault = true; } [CompilerGenerated] public void Deconstruct(out MethodBase Source, out MethodBase Target) { Source = this.Source; Target = this.Target; } } [CLSCompliant(true)] public readonly record struct CreateNativeDetourRequest(nint Source, nint Target) { public bool ApplyByDefault { get; init; } = true; } [CLSCompliant(true)] public static class DetourFactory { private static object currentLock = new object(); private static IDetourFactory? lazyDefault; private static IDetourFactory? lazyCurrent; public unsafe static IDetourFactory Default => Helpers.GetOrInitWithLock(ref lazyDefault, currentLock, (delegate*)(&CreateDefault)); public unsafe static IDetourFactory Current => Helpers.GetOrInitWithLock(ref lazyCurrent, currentLock, (delegate*)(&CreateCurrent)); private static IDetourFactory CreateDefault() { return new PlatformTripleDetourFactory(PlatformTriple.Current); } private static IDetourFactory CreateCurrent() { return Default; } [EditorBrowsable(EditorBrowsableState.Never)] public static void SetCurrentFactory(Func creator) { Helpers.ThrowIfArgumentNull>(creator, "creator"); lock (currentLock) { lazyCurrent = creator(Current); } } public static ICoreDetour CreateDetour(this IDetourFactory factory, MethodBase source, MethodBase target, bool applyByDefault = true) { Helpers.ThrowIfArgumentNull(factory, "factory"); return factory.CreateDetour(new CreateDetourRequest(source, target) { ApplyByDefault = applyByDefault }); } public static ICoreNativeDetour CreateNativeDetour(this IDetourFactory factory, nint source, nint target, bool applyByDefault = true) { Helpers.ThrowIfArgumentNull(factory, "factory"); return factory.CreateNativeDetour(new CreateNativeDetourRequest(source, target) { ApplyByDefault = applyByDefault }); } } internal static class AssemblyInfo { public const string AssemblyName = "MonoMod.Core"; public const string AssemblyVersion = "1.3.4"; } } namespace MonoMod.Core.Utils { [Flags] public enum AddressKind { Rel32 = 0, Rel64 = 2, Abs32 = 1, Abs64 = 3, PrecodeFixupThunkRel32 = 4, PrecodeFixupThunkRel64 = 6, PrecodeFixupThunkAbs32 = 5, PrecodeFixupThunkAbs64 = 7, Indirect = 8, ConstantAddr = 0x10 } public static class AddressKindExtensions { public const AddressKind IsAbsoluteField = AddressKind.Abs32; public const AddressKind Is64BitField = AddressKind.Rel64; public const AddressKind IsPrecodeFixupField = AddressKind.PrecodeFixupThunkRel32; public const AddressKind IsIndirectField = AddressKind.Indirect; public const AddressKind IsConstantField = AddressKind.ConstantAddr; [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsRelative(this AddressKind value) { return (value & AddressKind.Abs32) == 0; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsAbsolute(this AddressKind value) { return !value.IsRelative(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool Is32Bit(this AddressKind value) { return (value & AddressKind.Rel64) == 0; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool Is64Bit(this AddressKind value) { return !value.Is32Bit(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsPrecodeFixup(this AddressKind value) { return (value & AddressKind.PrecodeFixupThunkRel32) != 0; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsIndirect(this AddressKind value) { return (value & AddressKind.Indirect) != 0; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsConstant(this AddressKind value) { return (value & AddressKind.ConstantAddr) != 0; } public static void Validate(this AddressKind value, [CallerArgumentExpression("value")] string argName = "") { if ((value & ~(AddressKind.PrecodeFixupThunkAbs64 | AddressKind.Indirect | AddressKind.ConstantAddr)) != AddressKind.Rel32) { throw new ArgumentOutOfRangeException(argName); } } public static string FastToString(this AddressKind value) { FormatInterpolatedStringHandler val = default(FormatInterpolatedStringHandler); ((FormatInterpolatedStringHandler)(ref val))..ctor(0, 4); ((FormatInterpolatedStringHandler)(ref val)).AppendFormatted(value.IsPrecodeFixup() ? "PrecodeFixupThunk" : ""); ((FormatInterpolatedStringHandler)(ref val)).AppendFormatted(value.IsRelative() ? "Rel" : "Abs"); ((FormatInterpolatedStringHandler)(ref val)).AppendFormatted(value.Is32Bit() ? "32" : "64"); ((FormatInterpolatedStringHandler)(ref val)).AppendFormatted(value.IsIndirect() ? "Indirect" : ""); return DebugFormatter.Format(ref val); } } public readonly struct AddressMeaning : IEquatable { public AddressKind Kind { get; } public int RelativeToOffset { get; } public ulong ConstantValue { get; } public AddressMeaning(AddressKind kind) { ConstantValue = 0uL; kind.Validate("kind"); if (!kind.IsAbsolute()) { throw new ArgumentOutOfRangeException("kind"); } Kind = kind; RelativeToOffset = 0; } public AddressMeaning(AddressKind kind, int relativeOffset) { ConstantValue = 0uL; kind.Validate("kind"); if (!kind.IsRelative()) { throw new ArgumentOutOfRangeException("kind"); } if (relativeOffset < 0) { throw new ArgumentOutOfRangeException("relativeOffset"); } Kind = kind; RelativeToOffset = relativeOffset; } public AddressMeaning(AddressKind kind, int relativeOffset, ulong constantValue) { kind.Validate("kind"); if (!kind.IsRelative()) { throw new ArgumentOutOfRangeException("kind"); } if (relativeOffset < 0) { throw new ArgumentOutOfRangeException("relativeOffset"); } Kind = kind; RelativeToOffset = relativeOffset; ConstantValue = constantValue; } private unsafe static nint DoProcessAddress(AddressKind kind, nint basePtr, int offset, ulong constantValue, ulong address) { if (kind.IsConstant()) { address = constantValue; } nint num; if (kind.IsAbsolute()) { num = (nint)address; } else { long num2 = (kind.Is32Bit() ? Unsafe.As(ref address) : Unsafe.As(ref address)); num = (nint)(basePtr + offset + num2); } if (kind.IsIndirect()) { num = *(nint*)num; } return num; } public nint ProcessAddress(nint basePtr, int offset, ulong address) { return DoProcessAddress(Kind, basePtr, offset + RelativeToOffset, ConstantValue, address); } public override bool Equals(object? obj) { if (obj is AddressMeaning other) { return Equals(other); } return false; } public bool Equals(AddressMeaning other) { if (Kind == other.Kind && RelativeToOffset == other.RelativeToOffset) { return ConstantValue == other.ConstantValue; } return false; } public override string ToString() { FormatInterpolatedStringHandler val = default(FormatInterpolatedStringHandler); ((FormatInterpolatedStringHandler)(ref val))..ctor(38, 3); ((FormatInterpolatedStringHandler)(ref val)).AppendLiteral("AddressMeaning("); ((FormatInterpolatedStringHandler)(ref val)).AppendFormatted(Kind.FastToString()); ((FormatInterpolatedStringHandler)(ref val)).AppendLiteral(", offset: "); ((FormatInterpolatedStringHandler)(ref val)).AppendFormatted(RelativeToOffset); ((FormatInterpolatedStringHandler)(ref val)).AppendLiteral(", constant: "); ((FormatInterpolatedStringHandler)(ref val)).AppendFormatted(ConstantValue); ((FormatInterpolatedStringHandler)(ref val)).AppendLiteral(")"); return DebugFormatter.Format(ref val); } public override int GetHashCode() { return HashCode.Combine(Kind, RelativeToOffset, ConstantValue); } public static bool operator ==(AddressMeaning left, AddressMeaning right) { return left.Equals(right); } public static bool operator !=(AddressMeaning left, AddressMeaning right) { return !(left == right); } } public sealed class BytePattern { private enum SegmentKind { Literal, MaskedLiteral, Any, AnyRepeating, Address } private record struct PatternSegment(int Start, int Length, SegmentKind Kind) { public ReadOnlySpan SliceOf(ReadOnlySpan span) { return span.Slice(Start, Length); } public ReadOnlyMemory SliceOf(ReadOnlyMemory mem) { return mem.Slice(Start, Length); } } private readonly record struct ComputeSegmentsResult(PatternSegment[] Segments, int MinLen, int AddrBytes); private const ushort MaskMask = 65280; public const byte BAnyValue = 0; public const ushort SAnyValue = 65280; public const byte BAnyRepeatingValue = 1; public const ushort SAnyRepeatingValue = 65281; public const byte BAddressValue = 2; public const ushort SAddressValue = 65282; private readonly ReadOnlyMemory pattern; private readonly ReadOnlyMemory bitmask; private readonly PatternSegment[] segments; private (ReadOnlyMemory Bytes, int Offset)? lazyFirstLiteralSegment; public int AddressBytes { get; } public int MinLength { get; } public AddressMeaning AddressMeaning { get; } public bool MustMatchAtStart { get; } public (ReadOnlyMemory Bytes, int Offset) FirstLiteralSegment { get { (ReadOnlyMemory, int) valueOrDefault = lazyFirstLiteralSegment.GetValueOrDefault(); if (!lazyFirstLiteralSegment.HasValue) { valueOrDefault = GetFirstLiteralSegment(); lazyFirstLiteralSegment = valueOrDefault; return valueOrDefault; } return valueOrDefault; } } public BytePattern(AddressMeaning meaning, params ushort[] pattern) : this(meaning, mustMatchAtStart: false, pattern.AsMemory()) { } public BytePattern(AddressMeaning meaning, bool mustMatchAtStart, params ushort[] pattern) : this(meaning, mustMatchAtStart, pattern.AsMemory()) { } public BytePattern(AddressMeaning meaning, ReadOnlyMemory pattern) : this(meaning, mustMatchAtStart: false, pattern) { } public BytePattern(AddressMeaning meaning, bool mustMatchAtStart, ReadOnlyMemory pattern) { AddressMeaning = meaning; MustMatchAtStart = mustMatchAtStart; ComputeSegmentsFromShort(pattern).Deconstruct(out PatternSegment[] Segments, out int MinLen, out int AddrBytes); segments = Segments; MinLength = MinLen; AddressBytes = AddrBytes; Memory memory = new byte[pattern.Length * 2].AsMemory(); Memory memory2 = memory.Slice(0, pattern.Length); Memory memory3 = memory.Slice(pattern.Length); for (int i = 0; i < pattern.Length; i++) { ushort num = pattern.Span[i]; byte b = (byte)((num & 0xFF00) >> 8); byte b2 = (byte)(num & -65281); if ((b == 0 || b == byte.MaxValue) ? true : false) { b = (byte)(~b); } memory2.Span[i] = (byte)(b2 & b); memory3.Span[i] = b; } this.pattern = memory2; bitmask = memory3; } public BytePattern(AddressMeaning meaning, ReadOnlyMemory mask, ReadOnlyMemory pattern) : this(meaning, mustMatchAtStart: false, mask, pattern) { } public BytePattern(AddressMeaning meaning, bool mustMatchAtStart, ReadOnlyMemory mask, ReadOnlyMemory pattern) { AddressMeaning = meaning; MustMatchAtStart = mustMatchAtStart; ComputeSegmentsFromMaskPattern(mask, pattern).Deconstruct(out PatternSegment[] Segments, out int MinLen, out int AddrBytes); segments = Segments; MinLength = MinLen; AddressBytes = AddrBytes; this.pattern = pattern; bitmask = mask; } private unsafe static ComputeSegmentsResult ComputeSegmentsFromShort(ReadOnlyMemory pattern) { return ComputeSegmentsCore((delegate*, int, SegmentKind>)(&KindForShort), pattern.Length, pattern); static SegmentKind KindForShort(ReadOnlyMemory readOnlyMemory, int idx) { ushort num = readOnlyMemory.Span[idx]; switch (num & 0xFF00) { case 0: return SegmentKind.Literal; case 65280: { int num2 = num & 0xFF; return num2 switch { 0 => SegmentKind.Any, 1 => SegmentKind.AnyRepeating, 2 => SegmentKind.Address, _ => throw new ArgumentException($"Pattern contained unknown special value {num2:x2}", "pattern"), }; } default: return SegmentKind.MaskedLiteral; } } } private unsafe static ComputeSegmentsResult ComputeSegmentsFromMaskPattern(ReadOnlyMemory mask, ReadOnlyMemory pattern) { if (mask.Length < pattern.Length) { throw new ArgumentException("Mask buffer shorter than pattern", "mask"); } return ComputeSegmentsCore((delegate*<(ReadOnlyMemory, ReadOnlyMemory), int, SegmentKind>)(&KindForIdx), pattern.Length, (mask, pattern)); static SegmentKind KindForIdx((ReadOnlyMemory mask, ReadOnlyMemory pattern) t, int idx) { switch (t.mask.Span[idx]) { case 0: { byte b = t.pattern.Span[idx]; return b switch { 0 => SegmentKind.Any, 1 => SegmentKind.AnyRepeating, 2 => SegmentKind.Address, _ => throw new ArgumentException($"Pattern contained unknown special value {b:x2}", "pattern"), }; } case byte.MaxValue: return SegmentKind.Literal; default: return SegmentKind.MaskedLiteral; } } } private unsafe static ComputeSegmentsResult ComputeSegmentsCore(delegate* kindForIdx, int patternLength, TPattern pattern) { if (patternLength == 0) { throw new ArgumentException("Pattern cannot be empty", "pattern"); } int num = 0; SegmentKind segmentKind = SegmentKind.AnyRepeating; int num2 = 0; int num3 = 0; int num4 = 0; int num5 = -1; for (int i = 0; i < patternLength; i++) { SegmentKind segmentKind2 = kindForIdx(pattern, i); int num6 = num4; num4 = num6 + segmentKind2 switch { SegmentKind.Literal => 1, SegmentKind.MaskedLiteral => 1, SegmentKind.Any => 1, SegmentKind.AnyRepeating => 0, SegmentKind.Address => 1, _ => 0, }; if (segmentKind2 != segmentKind) { if (num5 < 0) { num5 = i; } num++; num2 = 1; } else { num2++; } if (segmentKind2 == SegmentKind.Address) { num3++; } segmentKind = segmentKind2; } if (num > 0 && segmentKind == SegmentKind.AnyRepeating) { num--; } if (num == 0 || num4 <= 0) { throw new ArgumentException("Pattern has no meaningful segments", "pattern"); } PatternSegment[] array = new PatternSegment[num]; num = 0; segmentKind = SegmentKind.AnyRepeating; num2 = 0; for (int j = num5; j < patternLength; j++) { if (num > array.Length) { break; } SegmentKind num7 = kindForIdx(pattern, j); if (num7 != segmentKind) { if (num > 0) { array[num - 1] = new PatternSegment(j - num2, num2, segmentKind); if (num > 1 && segmentKind == SegmentKind.Any && array[num - 2].Kind == SegmentKind.AnyRepeating) { Helpers.Swap(ref array[num - 2], ref array[num - 1]); } } num++; num2 = 1; } else { num2++; } segmentKind = num7; } if (segmentKind != SegmentKind.AnyRepeating && num > 0) { array[num - 1] = new PatternSegment(patternLength - num2, num2, segmentKind); } return new ComputeSegmentsResult(array, num4, num3); } public bool TryMatchAt(ReadOnlySpan data, out ulong address, out int length) { if (data.Length < MinLength) { length = 0; address = 0uL; return false; } ReadOnlySpan span = pattern.Span; Span addrBuf = stackalloc byte[8]; bool result = TryMatchAtImpl(span, data, addrBuf, out length, 0); address = Unsafe.ReadUnaligned(in addrBuf[0]); return result; } public bool TryMatchAt(ReadOnlySpan data, Span addrBuf, out int length) { if (data.Length < MinLength) { length = 0; return false; } ReadOnlySpan span = pattern.Span; return TryMatchAtImpl(span, data, addrBuf, out length, 0); } private bool TryMatchAtImpl(ReadOnlySpan patternSpan, ReadOnlySpan data, Span addrBuf, out int length, int startAtSegment) { int num = 0; int num2 = startAtSegment; while (true) { if (num2 < segments.Length) { PatternSegment patternSegment = segments[num2]; switch (patternSegment.Kind) { case SegmentKind.Literal: if (data.Length - num >= patternSegment.Length) { ReadOnlySpan span = patternSegment.SliceOf(patternSpan); if (span.SequenceEqual(data.Slice(num, span.Length))) { num += patternSegment.Length; goto IL_0192; } } break; case SegmentKind.MaskedLiteral: if (data.Length - num >= patternSegment.Length) { ReadOnlySpan readOnlySpan2 = patternSegment.SliceOf(patternSpan); ReadOnlySpan readOnlySpan3 = patternSegment.SliceOf(bitmask.Span); if (Helpers.MaskedSequenceEqual(readOnlySpan2, data.Slice(num, readOnlySpan2.Length), readOnlySpan3)) { num += patternSegment.Length; goto IL_0192; } } break; case SegmentKind.Any: if (data.Length - num >= patternSegment.Length) { num += patternSegment.Length; goto IL_0192; } break; case SegmentKind.Address: if (data.Length - num >= patternSegment.Length) { ReadOnlySpan readOnlySpan = data.Slice(num, Math.Min(patternSegment.Length, addrBuf.Length)); readOnlySpan.CopyTo(addrBuf); addrBuf = addrBuf.Slice(Math.Min(addrBuf.Length, readOnlySpan.Length)); num += patternSegment.Length; goto IL_0192; } break; case SegmentKind.AnyRepeating: { int offset; int length2; bool result = ScanForNextLiteral(patternSpan, data.Slice(num), addrBuf, out offset, out length2, num2); length = num + offset + length2; return result; } default: throw new InvalidOperationException(); } break; } length = num; return true; IL_0192: num2++; } length = 0; return false; } public bool TryFindMatch(ReadOnlySpan data, out ulong address, out int offset, out int length) { if (data.Length < MinLength) { length = (offset = 0); address = 0uL; return false; } ReadOnlySpan span = pattern.Span; Span addrBuf = stackalloc byte[8]; bool result; if (MustMatchAtStart) { offset = 0; result = TryMatchAtImpl(span, data, addrBuf, out length, 0); } else { result = ScanForNextLiteral(span, data, addrBuf, out offset, out length, 0); } address = Unsafe.ReadUnaligned(in addrBuf[0]); return result; } public bool TryFindMatch(ReadOnlySpan data, Span addrBuf, out int offset, out int length) { if (data.Length < MinLength) { length = (offset = 0); return false; } ReadOnlySpan span = pattern.Span; if (MustMatchAtStart) { offset = 0; return TryMatchAtImpl(span, data, addrBuf, out length, 0); } return ScanForNextLiteral(span, data, addrBuf, out offset, out length, 0); } private bool ScanForNextLiteral(ReadOnlySpan patternSpan, ReadOnlySpan data, Span addrBuf, out int offset, out int length, int segmentIndex) { var (patternSegment, num) = GetNextLiteralSegment(segmentIndex); if (num + patternSegment.Length > data.Length) { offset = (length = 0); return false; } int num2 = 0; while (true) { int num3 = data.Slice(num + num2).IndexOf(patternSegment.SliceOf(patternSpan)); if (num3 < 0) { offset = (length = 0); return false; } if (TryMatchAtImpl(patternSpan, data.Slice(offset = num2 + num3), addrBuf, out length, segmentIndex)) { break; } num2 += num3 + 1; } return true; } private (ReadOnlyMemory Bytes, int Offset) GetFirstLiteralSegment() { var (patternSegment, item) = GetNextLiteralSegment(0); return (Bytes: patternSegment.SliceOf(pattern), Offset: item); } private (PatternSegment Segment, int LiteralOffset) GetNextLiteralSegment(int segmentIndexId) { if (segmentIndexId < 0 || segmentIndexId >= segments.Length) { throw new ArgumentOutOfRangeException("segmentIndexId"); } int num = 0; while (segmentIndexId < segments.Length) { PatternSegment item = segments[segmentIndexId]; if (item.Kind == SegmentKind.Literal) { return (Segment: item, LiteralOffset: num); } SegmentKind kind = item.Kind; if (((uint)(kind - 1) <= 1u || kind == SegmentKind.Address) ? true : false) { num += item.Length; } else if (item.Kind != SegmentKind.AnyRepeating) { throw new InvalidOperationException("Unknown segment kind"); } segmentIndexId++; } return (Segment: default(PatternSegment), LiteralOffset: num); } } public sealed class BytePatternCollection : IEnumerable, IEnumerable { private struct HomogenousPatternCollection { public BytePattern[]?[] Lut; public readonly int Offset; public int MinLength; public HomogenousPatternCollection(int offs) { Offset = offs; Lut = null; MinLength = int.MaxValue; } public void AddFirstBytes(ref FirstByteCollection bytes) { for (int i = 0; i < Lut.Length; i++) { if (Lut[i] != null) { bytes.Add((byte)i); } } } } private ref struct FirstByteCollection { private Span firstByteStore; private Span byteIndicies; private int firstBytesRecorded; public const int SingleAllocationSize = 512; public ReadOnlySpan FirstBytes => firstByteStore.Slice(0, firstBytesRecorded); public FirstByteCollection(Span store) : this(store.Slice(0, 256), store.Slice(256, 256)) { } public FirstByteCollection(Span store, Span indicies) { firstByteStore = store; byteIndicies = indicies; firstBytesRecorded = 0; byteIndicies.Fill(byte.MaxValue); } public void Add(byte value) { ref byte reference = ref byteIndicies[value]; if (reference == byte.MaxValue) { reference = (byte)firstBytesRecorded; firstByteStore[reference] = value; firstBytesRecorded = Math.Min(firstBytesRecorded + 1, 256); } } } private readonly HomogenousPatternCollection[] patternCollections; private readonly BytePattern[]? emptyPatterns; private ReadOnlyMemory? lazyPossibleFirstBytes; public int MinLength { get; } public int MaxMinLength { get; } public int MaxAddressLength { get; } private ReadOnlyMemory PossibleFirstBytes { get { ReadOnlyMemory valueOrDefault = lazyPossibleFirstBytes.GetValueOrDefault(); if (!lazyPossibleFirstBytes.HasValue) { valueOrDefault = GetPossibleFirstBytes(); lazyPossibleFirstBytes = valueOrDefault; return valueOrDefault; } return valueOrDefault; } } public BytePatternCollection(ReadOnlyMemory patterns) { int minLength; int maxMinLength; int maxAddrLength; (HomogenousPatternCollection[], BytePattern[]) tuple = ComputeLut(patterns, out minLength, out maxMinLength, out maxAddrLength); patternCollections = tuple.Item1; emptyPatterns = tuple.Item2; MinLength = minLength; MaxMinLength = maxMinLength; MaxAddressLength = maxAddrLength; Helpers.Assert(MinLength > 0, (string)null, "MinLength > 0"); } public BytePatternCollection(params BytePattern?[] patterns) : this(patterns.AsMemory()) { } public IEnumerator GetEnumerator() { for (int i = 0; i < patternCollections.Length; i++) { BytePattern[]?[] coll = patternCollections[i].Lut; for (int j = 0; j < coll.Length; j++) { if (coll[j] != null) { BytePattern[] array = coll[j]; for (int k = 0; k < array.Length; k++) { yield return array[k]; } } } } if (emptyPatterns != null) { BytePattern[] array = emptyPatterns; for (int i = 0; i < array.Length; i++) { yield return array[i]; } } } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } private static (HomogenousPatternCollection[], BytePattern[]?) ComputeLut(ReadOnlyMemory patterns, out int minLength, out int maxMinLength, out int maxAddrLength) { if (patterns.Length == 0) { minLength = 0; maxMinLength = 0; maxAddrLength = 0; return (ArrayEx.Empty(), null); } Span span = stackalloc int[256]; minLength = int.MaxValue; maxMinLength = int.MinValue; maxAddrLength = 0; int[][] array = null; int num = 0; int num2 = 0; for (int i = 0; i < patterns.Length; i++) { BytePattern bytePattern = patterns.Span[i]; if (bytePattern == null) { continue; } if (bytePattern.MinLength < minLength) { minLength = bytePattern.MinLength; } if (bytePattern.MinLength > maxMinLength) { maxMinLength = bytePattern.MinLength; } if (bytePattern.AddressBytes > maxAddrLength) { maxAddrLength = bytePattern.AddressBytes; } var (readOnlyMemory, num3) = bytePattern.FirstLiteralSegment; if (readOnlyMemory.Length == 0) { num++; continue; } num2 = 1; if (num3 == 0) { span[readOnlyMemory.Span[0]]++; continue; } if (array == null || array.Length < num3) { Array.Resize(ref array, num3); } ref int[] reference = ref array[num3 - 1]; if (reference == null) { reference = new int[256]; } reference[readOnlyMemory.Span[0]]++; } if (array != null) { int[][] array2 = array; for (int j = 0; j < array2.Length; j++) { if (array2[j] != null) { num2++; } } } BytePattern[] array3 = ((num > 0) ? new BytePattern[num] : null); int num4 = 0; HomogenousPatternCollection[] array4 = new HomogenousPatternCollection[num2]; int num5 = 1; array4[0] = new HomogenousPatternCollection(0); for (int k = 0; k < patterns.Length; k++) { BytePattern bytePattern2 = patterns.Span[k]; if (bytePattern2 == null) { continue; } var (readOnlyMemory2, num6) = bytePattern2.FirstLiteralSegment; if (readOnlyMemory2.Length == 0) { array3[num4++] = bytePattern2; continue; } int num7 = -1; for (int l = 0; l < array4.Length; l++) { if (array4[l].Offset == num6) { num7 = l; break; } } if (num7 == -1) { num7 = num5++; array4[num7] = new HomogenousPatternCollection(num6); } ReadOnlySpan arrayCounts = ((num6 == 0) ? span : array[num6 - 1].AsSpan()); AddToPatternCollection(ref array4[num7], arrayCounts, bytePattern2); if (num7 > 0 && array4[num7 - 1].Offset > array4[num7].Offset) { Helpers.Swap(ref array4[num7 - 1], ref array4[num7]); } } return (array4, array3); static void AddToPatternCollection(ref HomogenousPatternCollection collection, ReadOnlySpan readOnlySpan, BytePattern pattern) { ReadOnlyMemory item = pattern.FirstLiteralSegment.Bytes; if (collection.Lut == null) { BytePattern[][] array5 = new BytePattern[256][]; for (int m = 0; m < readOnlySpan.Length; m++) { if (readOnlySpan[m] > 0) { array5[m] = new BytePattern[readOnlySpan[m]]; } } collection.Lut = array5; } BytePattern[]? obj = collection.Lut[item.Span[0]]; int num8 = Array.IndexOf(obj, null); obj[num8] = pattern; if (pattern.MinLength < collection.MinLength) { collection.MinLength = pattern.MinLength; } } } public bool TryMatchAt(ReadOnlySpan data, out ulong address, [MaybeNullWhen(false)] out BytePattern matchingPattern, out int length) { if (data.Length < MinLength) { length = 0; address = 0uL; matchingPattern = null; return false; } Span addrBuf = stackalloc byte[8]; bool result = TryMatchAt(data, addrBuf, out matchingPattern, out length); address = Unsafe.ReadUnaligned(in addrBuf[0]); return result; } public bool TryMatchAt(ReadOnlySpan data, Span addrBuf, [MaybeNullWhen(false)] out BytePattern matchingPattern, out int length) { if (data.Length < MinLength) { length = 0; matchingPattern = null; return false; } for (int i = 0; i < patternCollections.Length; i++) { ref HomogenousPatternCollection reference = ref patternCollections[i]; if (data.Length < reference.Offset + reference.MinLength) { continue; } byte b = data[reference.Offset]; BytePattern[] array = reference.Lut[b]; if (array == null) { continue; } BytePattern[] array2 = array; foreach (BytePattern bytePattern in array2) { if (bytePattern.TryMatchAt(data, addrBuf, out length)) { matchingPattern = bytePattern; return true; } } } if (emptyPatterns != null) { BytePattern[] array2 = emptyPatterns; foreach (BytePattern bytePattern2 in array2) { if (bytePattern2.TryMatchAt(data, addrBuf, out length)) { matchingPattern = bytePattern2; return true; } } } matchingPattern = null; length = 0; return false; } public bool TryFindMatch(ReadOnlySpan data, out ulong address, [MaybeNullWhen(false)] out BytePattern matchingPattern, out int offset, out int length) { if (data.Length < MinLength) { length = (offset = 0); address = 0uL; matchingPattern = null; return false; } Span addrBuf = stackalloc byte[8]; bool result = TryFindMatch(data, addrBuf, out matchingPattern, out offset, out length); address = Unsafe.ReadUnaligned(in addrBuf[0]); return result; } public bool TryFindMatch(ReadOnlySpan data, Span addrBuf, [MaybeNullWhen(false)] out BytePattern matchingPattern, out int offset, out int length) { if (data.Length < MinLength) { length = (offset = 0); matchingPattern = null; return false; } ReadOnlySpan span = PossibleFirstBytes.Span; int num = 0; while (true) { int num2 = data.Slice(num).IndexOfAny(span); if (num2 < 0) { break; } offset = num + num2; byte b = data[offset]; for (int i = 0; i < patternCollections.Length; i++) { ref HomogenousPatternCollection reference = ref patternCollections[i]; if (offset < reference.Offset || data.Length < offset + reference.MinLength) { continue; } BytePattern[] array = reference.Lut[b]; if (array == null) { continue; } BytePattern[] array2 = array; foreach (BytePattern bytePattern in array2) { if ((offset == 0 || !bytePattern.MustMatchAtStart) && bytePattern.TryMatchAt(data.Slice(offset - reference.Offset), addrBuf, out length)) { offset -= reference.Offset; matchingPattern = bytePattern; return true; } } } num = offset + 1; } if (emptyPatterns != null) { BytePattern[] array2 = emptyPatterns; foreach (BytePattern bytePattern2 in array2) { if (bytePattern2.TryFindMatch(data, addrBuf, out offset, out length)) { matchingPattern = bytePattern2; return true; } } } matchingPattern = null; offset = 0; length = 0; return false; } private ReadOnlyMemory GetPossibleFirstBytes() { Memory memory = new byte[512].AsMemory(); FirstByteCollection bytes = new FirstByteCollection(memory.Span); for (int i = 0; i < patternCollections.Length; i++) { patternCollections[i].AddFirstBytes(ref bytes); } return memory.Slice(0, bytes.FirstBytes.Length); } } internal static class IcedExtensions { [Obsolete("This method is not supported.", true)] public static string FormatInsns(this IList insns) { throw new NotSupportedException(); } [Obsolete("This method is not supported.", true)] public static string FormatInsns(this InstructionList insns) { throw new NotSupportedException(); } } public interface IInitialize { void Initialize(); } public interface IInitialize { void Initialize(T value); } internal struct StringParser { internal delegate T ParseRawFunc(string buffer, ref int startIndex, ref int endIndex); private readonly string _buffer; private readonly char _separator; private readonly bool _skipEmpty; private int _startIndex; private int _endIndex; public StringParser(string buffer, char separator, bool skipEmpty = false) { _buffer = Helpers.ThrowIfNull(buffer, "buffer"); _separator = separator; _skipEmpty = skipEmpty; _startIndex = -1; _endIndex = -1; } public bool MoveNext() { if (_buffer == null) { throw new InvalidOperationException(); } do { if (_endIndex >= _buffer.Length) { _startIndex = _endIndex; return false; } int num = _buffer.IndexOf(_separator, _endIndex + 1); _startIndex = _endIndex + 1; _endIndex = ((num >= 0) ? num : _buffer.Length); } while (_skipEmpty && _endIndex < _startIndex + 1); return true; } public void MoveNextOrFail() { if (!MoveNext()) { ThrowForInvalidData(); } } public string MoveAndExtractNext() { MoveNextOrFail(); return _buffer.Substring(_startIndex, _endIndex - _startIndex); } public string MoveAndExtractNextInOuterParens() { MoveNextOrFail(); if (_buffer[_startIndex] != '(') { ThrowForInvalidData(); } int num = _buffer.LastIndexOf(')'); if (num == -1 || num < _startIndex) { ThrowForInvalidData(); } string result = _buffer.Substring(_startIndex + 1, num - _startIndex - 1); _endIndex = num + 1; return result; } public string ExtractCurrent() { if (_buffer == null || _startIndex == -1) { throw new InvalidOperationException(); } return _buffer.Substring(_startIndex, _endIndex - _startIndex); } public unsafe int ParseNextInt32() { MoveNextOrFail(); bool flag = false; int num = 0; fixed (char* ptr = _buffer.AsSpan()) { char* ptr2 = ptr + _startIndex; char* ptr3 = ptr + _endIndex; if (ptr2 == ptr3) { ThrowForInvalidData(); } if (*ptr2 == '-') { flag = true; ptr2++; if (ptr2 == ptr3) { ThrowForInvalidData(); } } for (; ptr2 != ptr3; ptr2++) { int num2 = *ptr2 - 48; if (num2 < 0 || num2 > 9) { ThrowForInvalidData(); } num = checked(flag ? (num * 10 - num2) : (num * 10 + num2)); } } return num; } public unsafe long ParseNextInt64() { MoveNextOrFail(); bool flag = false; long num = 0L; fixed (char* ptr = _buffer.AsSpan()) { char* ptr2 = ptr + _startIndex; char* ptr3 = ptr + _endIndex; if (ptr2 == ptr3) { ThrowForInvalidData(); } if (*ptr2 == '-') { flag = true; ptr2++; if (ptr2 == ptr3) { ThrowForInvalidData(); } } for (; ptr2 != ptr3; ptr2++) { int num2 = *ptr2 - 48; if (num2 < 0 || num2 > 9) { ThrowForInvalidData(); } num = checked(flag ? (num * 10 - num2) : (num * 10 + num2)); } } return num; } public unsafe uint ParseNextUInt32() { MoveNextOrFail(); if (_startIndex == _endIndex) { ThrowForInvalidData(); } uint num = 0u; fixed (char* ptr = _buffer.AsSpan()) { char* ptr2 = ptr + _startIndex; for (char* ptr3 = ptr + _endIndex; ptr2 != ptr3; ptr2++) { int num2 = *ptr2 - 48; if (num2 < 0 || num2 > 9) { ThrowForInvalidData(); } num = (uint)checked(num * 10 + num2); } } return num; } public unsafe ulong ParseNextUInt64() { MoveNextOrFail(); ulong num = 0uL; fixed (char* ptr = _buffer.AsSpan()) { char* ptr2 = ptr + _startIndex; for (char* ptr3 = ptr + _endIndex; ptr2 != ptr3; ptr2++) { int num2 = *ptr2 - 48; if (num2 < 0 || num2 > 9) { ThrowForInvalidData(); } num = checked(num * 10 + (ulong)num2); } } return num; } public char ParseNextChar() { MoveNextOrFail(); if (_endIndex - _startIndex != 1) { ThrowForInvalidData(); } return _buffer[_startIndex]; } internal T ParseRaw(ParseRawFunc selector) { MoveNextOrFail(); return selector(_buffer, ref _startIndex, ref _endIndex); } public string ExtractCurrentToEnd() { if (_buffer == null || _startIndex == -1) { throw new InvalidOperationException(); } return _buffer.Substring(_startIndex); } private static void ThrowForInvalidData() { throw new FormatException(); } } } namespace MonoMod.Core.Platforms { public enum TypeClassification { InRegister, ByReference, OnStack } public delegate TypeClassification Classifier(Type type, bool isReturn); public enum SpecialArgumentKind { ThisPointer, ReturnBuffer, GenericContext, UserArguments } public readonly record struct Abi(ReadOnlyMemory ArgumentOrder, Classifier Classifier, bool ReturnsReturnBuffer) { public TypeClassification Classify(Type type, bool isReturn) { Helpers.ThrowIfArgumentNull(type, "type"); if (type == typeof(void)) { return TypeClassification.InRegister; } if (!type.IsValueType) { return TypeClassification.InRegister; } if (type.IsPointer) { return TypeClassification.InRegister; } if (type.IsByRef) { return TypeClassification.InRegister; } return Classifier(type, isReturn); } } [Flags] public enum ArchitectureFeature { None = 0, FixedInstructionSize = 1, Immediate64 = 2, CreateAltEntryPoint = 4 } public readonly struct FeatureFlags : IEquatable { public ArchitectureFeature Architecture { get; } public SystemFeature System { get; } public RuntimeFeature Runtime { get; } public FeatureFlags(ArchitectureFeature archFlags, SystemFeature sysFlags, RuntimeFeature runtimeFlags) { Runtime = runtimeFlags; Architecture = archFlags; System = sysFlags; } public bool Has(RuntimeFeature feature) { return (Runtime & feature) == feature; } public bool Has(ArchitectureFeature feature) { return (Architecture & feature) == feature; } public bool Has(SystemFeature feature) { return (System & feature) == feature; } public override bool Equals(object? obj) { if (obj is FeatureFlags other) { return Equals(other); } return false; } public bool Equals(FeatureFlags other) { if (Runtime == other.Runtime && Architecture == other.Architecture) { return System == other.System; } return false; } public override int GetHashCode() { return HashCode.Combine(Runtime, Architecture, System); } public override string ToString() { return $"({Architecture})({System})({Runtime})"; } public static bool operator ==(FeatureFlags left, FeatureFlags right) { return left.Equals(right); } public static bool operator !=(FeatureFlags left, FeatureFlags right) { return !(left == right); } } public interface IAltEntryFactory { nint CreateAlternateEntrypoint(nint entrypoint, int minLength, out IDisposable? handle); } public interface IArchitecture { ArchitectureKind Target { get; } ArchitectureFeature Features { get; } BytePatternCollection KnownMethodThunks { get; } IAltEntryFactory AltEntryFactory { get; } NativeDetourInfo ComputeDetourInfo(nint from, nint target, int maxSizeHint = -1); int GetDetourBytes(NativeDetourInfo info, Span buffer, out IDisposable? allocationHandle); NativeDetourInfo ComputeRetargetInfo(NativeDetourInfo detour, nint target, int maxSizeHint = -1); int GetRetargetBytes(NativeDetourInfo original, NativeDetourInfo retarget, Span buffer, out IDisposable? allocationHandle, out bool needsRepatch, out bool disposeOldAlloc); ReadOnlyMemory CreateNativeVtableProxyStubs(nint vtableBase, int vtableSize); IAllocatedMemory CreateSpecialEntryStub(nint target, nint argument); } public interface INativeDetourKind { int Size { get; } } public readonly record struct NativeDetourInfo(nint From, nint To, INativeDetourKind InternalKind, IDisposable? InternalData) { public int Size => InternalKind.Size; } public interface IControlFlowGuard { bool IsSupported { get; } int TargetAlignmentRequirement { get; } unsafe void RegisterValidIndirectCallTargets(void* memoryRegionStart, nint memoryRegionLength, ReadOnlySpan validTargetsInMemoryRegion); } public interface IMemoryAllocator { int MaxSize { get; } bool TryAllocate(AllocationRequest request, [MaybeNullWhen(false)] out IAllocatedMemory allocated); bool TryAllocateInRange(PositionedAllocationRequest request, [MaybeNullWhen(false)] out IAllocatedMemory allocated); } public readonly record struct AllocationRequest { public int Size { get; init; } public int Alignment { get; init; } public bool Executable { get; init; } public AllocationRequest(int Size) { Executable = false; this.Size = Size; Alignment = 8; } [CompilerGenerated] public void Deconstruct(out int Size) { Size = this.Size; } } public readonly record struct PositionedAllocationRequest(nint Target, nint LowBound, nint HighBound, AllocationRequest Base); public interface IAllocatedMemory : IDisposable { bool IsExecutable { get; } nint BaseAddress { get; } int Size { get; } Span Memory { get; } } public interface INativeExceptionHelper { GetExceptionSlot GetExceptionSlot { get; } nint CreateNativeToManagedHelper(nint target, out IDisposable? handle); nint CreateManagedToNativeHelper(nint target, out IDisposable? handle); } public unsafe delegate nint* GetExceptionSlot(); public interface IRuntime { RuntimeKind Target { get; } RuntimeFeature Features { get; } Abi Abi { get; } event OnMethodCompiledCallback? OnMethodCompiled; MethodBase GetIdentifiable(MethodBase method); RuntimeMethodHandle GetMethodHandle(MethodBase method); bool RequiresGenericContext(MethodBase method); void DisableInlining(MethodBase method); IDisposable? PinMethodIfNeeded(MethodBase method); nint GetMethodEntryPoint(MethodBase method); void Compile(MethodBase method); } public delegate void OnMethodCompiledCallback(RuntimeMethodHandle methodHandle, MethodBase? method, nint codeStart, nint codeRw, ulong codeSize); public interface ISystem { OSKind Target { get; } SystemFeature Features { get; } Abi? DefaultAbi { get; } IMemoryAllocator MemoryAllocator { get; } INativeExceptionHelper? NativeExceptionHelper { get; } IEnumerable EnumerateLoadedModules(); IEnumerable EnumerateLoadedModuleFiles(); nint GetSizeOfReadableMemory(nint start, nint guess); void PatchData(PatchTargetKind targetKind, nint patchTarget, ReadOnlySpan data, Span backup); nint GetNativeJitHookConfig(int runtimeMajMin); } public enum PatchTargetKind { Executable, ReadOnly } public readonly record struct LoadedModule(ulong? BaseAddress, string? FileName, ulong? Size); public sealed class PlatformTriple { public record struct NativeDetour(SimpleNativeDetour Simple, nint AltEntry, IDisposable? AltHandle) { public bool HasAltEntry => AltEntry != IntPtr.Zero; } private static object lazyCurrentLock = new object(); private static PlatformTriple? lazyCurrent; private static readonly Func createCurrentFunc = CreateCurrent; private nint ThePreStub = IntPtr.Zero; public IArchitecture Architecture { get; } public ISystem System { get; } public IRuntime Runtime { get; } public static PlatformTriple Current => Helpers.GetOrInitWithLock(ref lazyCurrent, lazyCurrentLock, createCurrentFunc); public (ArchitectureKind Arch, OSKind OS, RuntimeKind Runtime) HostTriple => (Arch: Architecture.Target, OS: System.Target, Runtime: Runtime.Target); public FeatureFlags SupportedFeatures { get; } public Abi Abi { get; } [EditorBrowsable(EditorBrowsableState.Advanced)] public static IRuntime CreateCurrentRuntime(ISystem system, IArchitecture arch) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Expected I4, but got Unknown //IL_0070: Unknown result type (might be due to invalid IL or missing references) Helpers.ThrowIfArgumentNull(system, "system"); Helpers.ThrowIfArgumentNull(arch, "arch"); RuntimeKind runtime = PlatformDetection.Runtime; return (runtime - 1) switch { 0 => FxBaseRuntime.CreateForVersion(PlatformDetection.RuntimeVersion, system), 1 => CoreBaseRuntime.CreateForVersion(PlatformDetection.RuntimeVersion, system, arch), 2 => new MonoRuntime(system), _ => throw new PlatformNotSupportedException($"Runtime kind {runtime} not supported"), }; } [EditorBrowsable(EditorBrowsableState.Advanced)] public static IArchitecture CreateCurrentArchitecture(ISystem system) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Expected I4, but got Unknown //IL_0064: Unknown result type (might be due to invalid IL or missing references) Helpers.ThrowIfArgumentNull(system, "system"); ArchitectureKind architecture = PlatformDetection.Architecture; return (architecture - 2) switch { 0 => new x86Arch(system), 1 => new x86_64Arch(system), 2 => throw new NotImplementedException(), 3 => new Arm64Arch(system), _ => throw new PlatformNotSupportedException($"Architecture kind {architecture} not supported"), }; } [EditorBrowsable(EditorBrowsableState.Advanced)] public static ISystem CreateCurrentSystem() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Invalid comparison between Unknown and I4 //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Invalid comparison between Unknown and I4 //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Expected I4, but got Unknown //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Invalid comparison between Unknown and I4 //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Invalid comparison between Unknown and I4 //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Invalid comparison between Unknown and I4 //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Invalid comparison between Unknown and I4 OSKind oS = PlatformDetection.OS; if ((int)oS <= 17) { switch (oS - 1) { case 0: throw new NotImplementedException(); case 4: return new MacOSSystem(); case 1: goto IL_006c; case 2: case 3: goto IL_0074; } if ((int)oS == 9) { return new LinuxSystem(); } if ((int)oS == 17) { throw new NotImplementedException(); } } else { if ((int)oS == 34) { goto IL_006c; } if ((int)oS == 37) { throw new NotImplementedException(); } if ((int)oS == 41) { throw new NotImplementedException(); } } goto IL_0074; IL_006c: return new WindowsSystem(); IL_0074: throw new PlatformNotSupportedException($"OS kind {oS} not supported"); } private static PlatformTriple CreateCurrent() { ISystem system = CreateCurrentSystem(); IArchitecture architecture = CreateCurrentArchitecture(system); IRuntime runtime = CreateCurrentRuntime(system, architecture); return new PlatformTriple(architecture, system, runtime); } [EditorBrowsable(EditorBrowsableState.Advanced)] public static void SetPlatformTriple(PlatformTriple triple) { Helpers.ThrowIfArgumentNull(triple, "triple"); if (lazyCurrent != null) { ThrowTripleAlreadyExists(); } lock (lazyCurrentLock) { if (lazyCurrent != null) { ThrowTripleAlreadyExists(); } lazyCurrent = triple; } } [MethodImpl(MethodImplOptions.NoInlining)] private static void ThrowTripleAlreadyExists() { throw new InvalidOperationException("The platform triple has already been initialized; cannot set a new one"); } [EditorBrowsable(EditorBrowsableState.Advanced)] public PlatformTriple(IArchitecture architecture, ISystem system, IRuntime runtime) { Helpers.ThrowIfArgumentNull(architecture, "architecture"); Helpers.ThrowIfArgumentNull(system, "system"); Helpers.ThrowIfArgumentNull(runtime, "runtime"); Architecture = architecture; System = system; Runtime = runtime; SupportedFeatures = new FeatureFlags(Architecture.Features, System.Features, Runtime.Features); InitIfNeeded(Architecture); InitIfNeeded(System); InitIfNeeded(Runtime); Abi = Runtime.Abi; } private void InitIfNeeded(object obj) { (obj as IInitialize)?.Initialize(System); (obj as IInitialize)?.Initialize(Architecture); (obj as IInitialize)?.Initialize(Runtime); (obj as IInitialize)?.Initialize(this); (obj as IInitialize)?.Initialize(); } public void Compile(MethodBase method) { Helpers.ThrowIfArgumentNull(method, "method"); if (method.IsGenericMethodDefinition) { throw new ArgumentException("Cannot prepare generic method definition", "method"); } method = GetIdentifiable(method); if (SupportedFeatures.Has(RuntimeFeature.RequiresCustomMethodCompile)) { Runtime.Compile(method); return; } RuntimeMethodHandle methodHandle = Runtime.GetMethodHandle(method); if (method.IsGenericMethod) { Type[] genericArguments = method.GetGenericArguments(); RuntimeTypeHandle[] array = new RuntimeTypeHandle[genericArguments.Length]; for (int i = 0; i < genericArguments.Length; i++) { array[i] = genericArguments[i].TypeHandle; } RuntimeHelpers.PrepareMethod(methodHandle, array); } else { RuntimeHelpers.PrepareMethod(methodHandle); } } public MethodBase GetIdentifiable(MethodBase method) { //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_016d: Unknown result type (might be due to invalid IL or missing references) //IL_0172: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) Helpers.ThrowIfArgumentNull(method, "method"); if (SupportedFeatures.Has(RuntimeFeature.RequiresMethodIdentification)) { method = Runtime.GetIdentifiable(method); } if (method.ReflectedType != method.DeclaringType) { ParameterInfo[] parameters = method.GetParameters(); Type[] array = new Type[parameters.Length]; for (int i = 0; i < parameters.Length; i++) { array[i] = parameters[i].ParameterType; } bool flag2 = default(bool); if ((object)method.DeclaringType == null) { MethodInfo? method2 = method.Module.GetMethod(method.Name, (BindingFlags)(-1), null, method.CallingConvention, array, null); bool flag = (object)method2 != null; bool num = flag; AssertionInterpolatedStringHandler val = new AssertionInterpolatedStringHandler(16, 2, flag, ref flag2); if (flag2) { ((AssertionInterpolatedStringHandler)(ref val)).AppendLiteral("orig: "); ((AssertionInterpolatedStringHandler)(ref val)).AppendFormatted(method); ((AssertionInterpolatedStringHandler)(ref val)).AppendLiteral(", module: "); ((AssertionInterpolatedStringHandler)(ref val)).AppendFormatted(method.Module); } Helpers.Assert(num, ref val, "got is not null"); method = method2; } else if (method.IsConstructor) { ConstructorInfo? constructor = method.DeclaringType.GetConstructor((BindingFlags)(-1), null, method.CallingConvention, array, null); flag2 = (object)constructor != null; bool num2 = flag2; bool flag = default(bool); AssertionInterpolatedStringHandler val2 = new AssertionInterpolatedStringHandler(6, 1, flag2, ref flag); if (flag) { ((AssertionInterpolatedStringHandler)(ref val2)).AppendLiteral("orig: "); ((AssertionInterpolatedStringHandler)(ref val2)).AppendFormatted(method); } Helpers.Assert(num2, ref val2, "got is not null"); method = constructor; } else { MethodInfo? method3 = method.DeclaringType.GetMethod(method.Name, (BindingFlags)(-1), null, method.CallingConvention, array, null); bool flag = (object)method3 != null; bool num3 = flag; AssertionInterpolatedStringHandler val3 = new AssertionInterpolatedStringHandler(6, 1, flag, ref flag2); if (flag2) { ((AssertionInterpolatedStringHandler)(ref val3)).AppendLiteral("orig: "); ((AssertionInterpolatedStringHandler)(ref val3)).AppendFormatted(method); } Helpers.Assert(num3, ref val3, "got is not null"); method = method3; } } return method; } public IDisposable? PinMethodIfNeeded(MethodBase method) { if (SupportedFeatures.Has(RuntimeFeature.RequiresMethodPinning)) { return Runtime.PinMethodIfNeeded(method); } return null; } public bool TryDisableInlining(MethodBase method) { if (SupportedFeatures.Has(RuntimeFeature.DisableInlining)) { Runtime.DisableInlining(method); return true; } return false; } public SimpleNativeDetour CreateSimpleDetour(nint from, nint to, int detourMaxSize = -1, nint fromRw = 0) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) if (fromRw == 0) { fromRw = from; } bool flag = from != to; bool isEnabled = default(bool); AssertionInterpolatedStringHandler val = new AssertionInterpolatedStringHandler(48, 2, flag, ref isEnabled); if (isEnabled) { ((AssertionInterpolatedStringHandler)(ref val)).AppendLiteral("Cannot detour a method to itself! (from: "); ((AssertionInterpolatedStringHandler)(ref val)).AppendFormatted(from); ((AssertionInterpolatedStringHandler)(ref val)).AppendLiteral(", to: "); ((AssertionInterpolatedStringHandler)(ref val)).AppendFormatted(to); ((AssertionInterpolatedStringHandler)(ref val)).AppendLiteral(")"); } Helpers.Assert(flag, ref val, "from != to"); MMDbgLog.DebugLogTraceStringHandler message = new MMDbgLog.DebugLogTraceStringHandler(31, 2, out isEnabled); if (isEnabled) { message.AppendLiteral("Creating simple detour 0x"); message.AppendFormatted(from, "x16"); message.AppendLiteral(" => 0x"); message.AppendFormatted(to, "x16"); } MMDbgLog.Trace(ref message); NativeDetourInfo nativeDetourInfo = Architecture.ComputeDetourInfo(from, to, detourMaxSize); Span span = stackalloc byte[nativeDetourInfo.Size]; Architecture.GetDetourBytes(nativeDetourInfo, span, out IDisposable allocationHandle); byte[] array = new byte[nativeDetourInfo.Size]; System.PatchData(PatchTargetKind.Executable, fromRw, span, array); return new SimpleNativeDetour(this, nativeDetourInfo, array, allocationHandle); } public NativeDetour CreateNativeDetour(nint from, nint to, int detourMaxSize = -1, nint fromRw = 0) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) if (fromRw == 0) { fromRw = from; } bool flag = from != to; bool isEnabled = default(bool); AssertionInterpolatedStringHandler val = new AssertionInterpolatedStringHandler(48, 2, flag, ref isEnabled); if (isEnabled) { ((AssertionInterpolatedStringHandler)(ref val)).AppendLiteral("Cannot detour a method to itself! (from: "); ((AssertionInterpolatedStringHandler)(ref val)).AppendFormatted(from); ((AssertionInterpolatedStringHandler)(ref val)).AppendLiteral(", to: "); ((AssertionInterpolatedStringHandler)(ref val)).AppendFormatted(to); ((AssertionInterpolatedStringHandler)(ref val)).AppendLiteral(")"); } Helpers.Assert(flag, ref val, "from != to"); MMDbgLog.DebugLogTraceStringHandler message = new MMDbgLog.DebugLogTraceStringHandler(31, 2, out isEnabled); if (isEnabled) { message.AppendLiteral("Creating simple detour 0x"); message.AppendFormatted(from, "x16"); message.AppendLiteral(" => 0x"); message.AppendFormatted(to, "x16"); } MMDbgLog.Trace(ref message); NativeDetourInfo nativeDetourInfo = Architecture.ComputeDetourInfo(from, to, detourMaxSize); Span span = stackalloc byte[nativeDetourInfo.Size]; IDisposable allocationHandle; int detourBytes = Architecture.GetDetourBytes(nativeDetourInfo, span, out allocationHandle); nint altEntry = IntPtr.Zero; IDisposable handle = null; if (SupportedFeatures.Has(ArchitectureFeature.CreateAltEntryPoint)) { altEntry = Architecture.AltEntryFactory.CreateAlternateEntrypoint(from, detourBytes, out handle); } else { MMDbgLog.DebugLogWarningStringHandler message2 = new MMDbgLog.DebugLogWarningStringHandler(67, 2, out isEnabled); if (isEnabled) { message2.AppendLiteral("Cannot create alternate entry point for native detour (from: "); message2.AppendFormatted(from, "x16"); message2.AppendLiteral(", to: "); message2.AppendFormatted(to, "x16"); } MMDbgLog.Warning(ref message2); } byte[] array = new byte[nativeDetourInfo.Size]; System.PatchData(PatchTargetKind.Executable, fromRw, span, array); return new NativeDetour(new SimpleNativeDetour(this, nativeDetourInfo, array, allocationHandle), altEntry, handle); } public nint GetNativeMethodBody(MethodBase method) { if (SupportedFeatures.Has(RuntimeFeature.RequiresBodyThunkWalking)) { return GetNativeMethodBodyWalk(method, reloadPtr: true); } return GetNativeMethodBodyDirect(method); } private unsafe nint GetNativeMethodBodyWalk(MethodBase method, bool reloadPtr) { bool flag = false; bool flag2 = false; int value = 0; BytePatternCollection knownMethodThunks = Architecture.KnownMethodThunks; bool isEnabled; MMDbgLog.DebugLogTraceStringHandler message = new MMDbgLog.DebugLogTraceStringHandler(32, 1, out isEnabled); if (isEnabled) { message.AppendLiteral("Performing method body walk for "); message.AppendFormatted(method); } MMDbgLog.Trace(ref message); nint num = -1; FormatInterpolatedStringHandler val = default(FormatInterpolatedStringHandler); while (true) { nint num2 = Runtime.GetMethodEntryPoint(method); MMDbgLog.DebugLogTraceStringHandler message2 = new MMDbgLog.DebugLogTraceStringHandler(25, 1, out isEnabled); if (isEnabled) { message2.AppendLiteral("Starting entry point = 0x"); message2.AppendFormatted(num2, "x16"); } MMDbgLog.Trace(ref message2); while (true) { if (value++ > 20) { MMDbgLog.DebugLogErrorStringHandler message3 = new MMDbgLog.DebugLogErrorStringHandler(70, 4, out isEnabled); if (isEnabled) { message3.AppendLiteral("Could not get entry point for "); message3.AppendFormatted(method); message3.AppendLiteral("! (tried "); message3.AppendFormatted(value); message3.AppendLiteral(" times) entry: 0x"); message3.AppendFormatted(num2, "x16"); message3.AppendLiteral(" prevEntry: 0x"); message3.AppendFormatted(num, "x16"); } MMDbgLog.Error(ref message3); ((FormatInterpolatedStringHandler)(ref val))..ctor(47, 1); ((FormatInterpolatedStringHandler)(ref val)).AppendLiteral("Could not get entrypoint for "); ((FormatInterpolatedStringHandler)(ref val)).AppendFormatted(method); ((FormatInterpolatedStringHandler)(ref val)).AppendLiteral(" (stuck in a loop)"); throw new NotSupportedException(DebugFormatter.Format(ref val)); } if (flag2 || num != num2) { num = num2; nint sizeOfReadableMemory = System.GetSizeOfReadableMemory(num2, knownMethodThunks.MaxMinLength); if (sizeOfReadableMemory <= 0) { MMDbgLog.DebugLogWarningStringHandler message4 = new MMDbgLog.DebugLogWarningStringHandler(43, 2, out isEnabled); if (isEnabled) { message4.AppendLiteral("Got zero or negative readable length "); message4.AppendFormatted(sizeOfReadableMemory); message4.AppendLiteral(" at 0x"); message4.AppendFormatted(num2, "x16"); } MMDbgLog.Warning(ref message4); } ReadOnlySpan data = new ReadOnlySpan((void*)num2, Math.Min((int)sizeOfReadableMemory, knownMethodThunks.MaxMinLength)); if (knownMethodThunks.TryFindMatch(data, out ulong address, out BytePattern matchingPattern, out int offset, out int _)) { nint ptrGot = num2; flag2 = false; AddressMeaning addressMeaning = matchingPattern.AddressMeaning; MMDbgLog.DebugLogTraceStringHandler message5 = new MMDbgLog.DebugLogTraceStringHandler(46, 4, out isEnabled); if (isEnabled) { message5.AppendLiteral("Matched thunk with "); message5.AppendFormatted(addressMeaning); message5.AppendLiteral(" at 0x"); message5.AppendFormatted(num2, "x16"); message5.AppendLiteral(" (addr: 0x"); message5.AppendFormatted(address, "x8"); message5.AppendLiteral(", offset: "); message5.AppendFormatted(offset); message5.AppendLiteral(")"); } MMDbgLog.Trace(ref message5); if (addressMeaning.Kind.IsPrecodeFixup() && !flag) { nint num3 = addressMeaning.ProcessAddress(num2, offset, address); if (reloadPtr) { MMDbgLog.DebugLogTraceStringHandler message6 = new MMDbgLog.DebugLogTraceStringHandler(56, 1, out isEnabled); if (isEnabled) { message6.AppendLiteral("Method thunk reset; regenerating (PrecodeFixupThunk: 0x"); message6.AppendFormatted(num3, "X16"); message6.AppendLiteral(")"); } MMDbgLog.Trace(ref message6); Compile(method); flag2 = true; break; } num2 = num3; } else { num2 = addressMeaning.ProcessAddress(num2, offset, address); } MMDbgLog.DebugLogTraceStringHandler message7 = new MMDbgLog.DebugLogTraceStringHandler(23, 1, out isEnabled); if (isEnabled) { message7.AppendLiteral("Got next entry point 0x"); message7.AppendFormatted(num2, "x16"); } MMDbgLog.Trace(ref message7); num2 = NotThePreStub(ptrGot, num2, out var wasPreStub); if (wasPreStub && reloadPtr) { MMDbgLog.Trace("Matched ThePreStub"); Compile(method); break; } continue; } } return num2; } } } private nint GetNativeMethodBodyDirect(MethodBase method) { return Runtime.GetMethodEntryPoint(method); } private nint NotThePreStub(nint ptrGot, nint ptrParsed, out bool wasPreStub) { if (ThePreStub == IntPtr.Zero) { ThePreStub = -2; nint thePreStub = (from m in typeof(HttpWebRequest).Assembly.GetType("System.Net.Connection")?.GetMethods() group m by GetNativeMethodBodyWalk(m, reloadPtr: false)).First((IGrouping g) => g.Count() > 1).Key ?? (-1); ThePreStub = thePreStub; bool isEnabled; MMDbgLog.DebugLogTraceStringHandler message = new MMDbgLog.DebugLogTraceStringHandler(14, 1, out isEnabled); if (isEnabled) { message.AppendLiteral("ThePreStub: 0x"); message.AppendFormatted(ThePreStub, "X16"); } MMDbgLog.Trace(ref message); } wasPreStub = ptrParsed == ThePreStub; if (!wasPreStub) { return ptrParsed; } return ptrGot; } public MethodBase GetRealDetourTarget(MethodBase from, MethodBase to) { //IL_0257: Unknown result type (might be due to invalid IL or missing references) //IL_025e: Expected O, but got Unknown //IL_0266: Unknown result type (might be due to invalid IL or missing references) //IL_0270: Unknown result type (might be due to invalid IL or missing references) //IL_029c: Unknown result type (might be due to invalid IL or missing references) //IL_028a: Unknown result type (might be due to invalid IL or missing references) //IL_02af: Unknown result type (might be due to invalid IL or missing references) //IL_02ce: Unknown result type (might be due to invalid IL or missing references) //IL_0340: Unknown result type (might be due to invalid IL or missing references) //IL_02fa: Unknown result type (might be due to invalid IL or missing references) //IL_0332: Unknown result type (might be due to invalid IL or missing references) Helpers.ThrowIfArgumentNull(from, "from"); Helpers.ThrowIfArgumentNull(to, "to"); to = GetIdentifiable(to); if (!(from is MethodInfo methodInfo) || !(to is MethodInfo methodInfo2)) { return to; } bool flag = false; ReadOnlyMemory argumentOrder = Abi.ArgumentOrder; ReadOnlySpan span = argumentOrder.Span; for (int i = 0; i < span.Length; i++) { if (span[i] == SpecialArgumentKind.ReturnBuffer) { flag = true; break; } } Type returnType = methodInfo.ReturnType; bool flag2 = Abi.Classify(returnType, isReturn: true) == TypeClassification.ByReference; bool flag3 = !methodInfo.IsStatic; bool num = flag3 && methodInfo2.IsStatic && flag2 && flag; bool flag4 = HasGenericContext(Abi) && Runtime.RequiresGenericContext(methodInfo); if (!num && !flag4) { return to; } Type type = (flag2 ? returnType.MakeByRefType() : returnType); Type type2 = ((flag2 && !Abi.ReturnsReturnBuffer) ? typeof(void) : type); int num2 = -1; int num3 = -1; int num4 = -1; ParameterInfo[] parameters = from.GetParameters(); List list = new List(parameters.Length + 3); argumentOrder = Abi.ArgumentOrder; ReadOnlySpan span2 = argumentOrder.Span; for (int j = 0; j < span2.Length; j++) { switch (span2[j]) { case SpecialArgumentKind.ThisPointer: if (flag3) { num2 = list.Count; list.Add(Extensions.GetThisParamType(from)); } break; case SpecialArgumentKind.ReturnBuffer: if (flag2) { num3 = list.Count; list.Add(type); } break; case SpecialArgumentKind.GenericContext: if (flag4) { list.Add(typeof(nint)); } break; case SpecialArgumentKind.UserArguments: num4 = list.Count; list.AddRange(parameters.Select((ParameterInfo p) => p.ParameterType)); break; } } FormatInterpolatedStringHandler val = default(FormatInterpolatedStringHandler); ((FormatInterpolatedStringHandler)(ref val))..ctor(16, 2); ((FormatInterpolatedStringHandler)(ref val)).AppendLiteral("Glue:AbiFixup<"); ((FormatInterpolatedStringHandler)(ref val)).AppendFormatted(from); ((FormatInterpolatedStringHandler)(ref val)).AppendLiteral(","); ((FormatInterpolatedStringHandler)(ref val)).AppendFormatted(to); ((FormatInterpolatedStringHandler)(ref val)).AppendLiteral(">"); DynamicMethodDefinition val2 = new DynamicMethodDefinition(DebugFormatter.Format(ref val), type2, list.ToArray()); try { MethodDefinition definition = val2.Definition; definition.ImplAttributes = (MethodImplAttributes)(definition.ImplAttributes | 0x208); ILProcessor iLProcessor = val2.GetILProcessor(); if (flag2 && num3 >= 0) { iLProcessor.Emit(OpCodes.Ldarg, num3); } if (flag3) { iLProcessor.Emit(OpCodes.Ldarg, num2); } for (int num5 = 0; num5 < parameters.Length; num5++) { iLProcessor.Emit(OpCodes.Ldarg, num5 + num4); } iLProcessor.Emit(OpCodes.Call, ((MemberReference)iLProcessor.Body.Method).Module.ImportReference(to)); if (flag2 && num3 >= 0) { iLProcessor.Emit(OpCodes.Stobj, ((MemberReference)iLProcessor.Body.Method).Module.ImportReference(returnType)); } if (flag2 && Abi.ReturnsReturnBuffer) { iLProcessor.Emit(OpCodes.Ldarg, num3); } iLProcessor.Emit(OpCodes.Ret); return val2.Generate(); } finally { ((IDisposable)val2)?.Dispose(); } } private static bool HasGenericContext(Abi abi) { ReadOnlySpan span = abi.ArgumentOrder.Span; for (int i = 0; i < span.Length; i++) { if (span[i] == SpecialArgumentKind.GenericContext) { return true; } } return false; } } internal sealed class PlatformTripleDetourFactory : IDetourFactory { private abstract class DetourBase : ICoreDetourBase, IDisposable { protected abstract class DetourBoxBase { public SimpleNativeDetour? Detour; public readonly List OldDetours = new List(); protected readonly PlatformTriple Triple; protected readonly object Sync = new object(); private bool applyDetours; private bool isApplying; public bool IsApplied { get { return Volatile.Read(in applyDetours); } set { Volatile.Write(ref applyDetours, value); Thread.MemoryBarrier(); } } public bool IsApplying { get { return Volatile.Read(in isApplying); } set { Volatile.Write(ref isApplying, value); Thread.MemoryBarrier(); } } protected DetourBoxBase(PlatformTriple triple) { Triple = triple; applyDetours = false; isApplying = false; } public void ClearOldDetours() { foreach (SimpleNativeDetour oldDetour in OldDetours) { oldDetour.Dispose(); } OldDetours.Clear(); } } protected readonly PlatformTriple Triple; protected DetourBoxBase DetourBox; private bool disposedValue; public bool IsApplied => DetourBox.IsApplied; protected DetourBase(PlatformTriple triple) { Triple = triple; DetourBox = null; } protected TBox GetDetourBox() where TBox : DetourBoxBase { return Unsafe.As(DetourBox); } protected static void ReplaceDetourInLock(DetourBoxBase nativeDetour, SimpleNativeDetour? newDetour, out SimpleNativeDetour? oldDetour) { Thread.MemoryBarrier(); oldDetour = Interlocked.Exchange(ref nativeDetour.Detour, newDetour); if (oldDetour != null) { nativeDetour.OldDetours.Add(oldDetour); } } protected abstract SimpleNativeDetour CreateDetour(); public void Apply() { lock (DetourBox) { if (IsApplied) { throw new InvalidOperationException("Cannot apply a detour which is already applied"); } try { DetourBox.IsApplying = true; DetourBox.IsApplied = true; ReplaceDetourInLock(DetourBox, CreateDetour(), out SimpleNativeDetour _); } catch { DetourBox.IsApplied = false; throw; } finally { DetourBox.IsApplying = false; } } } protected abstract void BeforeUndo(); protected abstract void AfterUndo(); public void Undo() { lock (DetourBox) { if (!IsApplied) { throw new InvalidOperationException("Cannot undo a detour which is not applied"); } try { DetourBox.IsApplying = true; UndoCore(out SimpleNativeDetour _); DetourBox.ClearOldDetours(); } finally { DetourBox.IsApplying = false; } } } private void UndoCore(out SimpleNativeDetour? oldDetour) { BeforeUndo(); DetourBox.IsApplied = false; ReplaceDetourInLock(DetourBox, null, out oldDetour); AfterUndo(); } protected abstract void BeforeDispose(); private void Dispose(bool disposing) { if (!disposedValue) { BeforeDispose(); lock (DetourBox) { UndoCore(out SimpleNativeDetour _); DetourBox.ClearOldDetours(); } disposedValue = true; } } ~DetourBase() { Dispose(disposing: false); } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } } private sealed class Detour : DetourBase, ICoreDetour, ICoreDetourBase, IDisposable { private sealed class ManagedDetourBox : DetourBoxBase { private readonly MethodBase src; private readonly MethodBase target; public ManagedDetourBox(PlatformTriple triple, MethodBase src, MethodBase target) : base(triple) { this.src = src; this.target = target; Detour = null; } public void SubscribeCompileMethod() { AddRelatedDetour(src, this); } public void UnsubscribeCompileMethod() { RemoveRelatedDetour(src, this); } public void OnMethodCompiled(MethodBase method, nint codeStart, nint codeStartRw, ulong codeSize) { if (!base.IsApplied) { return; } method = Triple.GetIdentifiable(method); lock (Sync) { if (!base.IsApplied || base.IsApplying) { return; } bool isEnabled; MMDbgLog.DebugLogTraceStringHandler message = new MMDbgLog.DebugLogTraceStringHandler(43, 4, out isEnabled); if (isEnabled) { message.AppendLiteral("Updating detour from "); message.AppendFormatted(src); message.AppendLiteral(" to "); message.AppendFormatted(target); message.AppendLiteral(" (recompiled "); message.AppendFormatted(method); message.AppendLiteral(" to "); message.AppendFormatted(codeStart, "x16"); message.AppendLiteral(")"); } MMDbgLog.Trace(ref message); try { base.IsApplying = true; SimpleNativeDetour detour = Detour; nint to; nint num; nint fromRw; if (detour != null) { _ = detour.Source; to = detour.Destination; num = codeStart; fromRw = codeStartRw; } else { num = codeStart; fromRw = codeStartRw; to = Triple.Runtime.GetMethodHandle(target).GetFunctionPointer(); } SimpleNativeDetour newDetour = Triple.CreateSimpleDetour(num, to, (int)codeSize, fromRw); DetourBase.ReplaceDetourInLock(this, newDetour, out SimpleNativeDetour _); } finally { base.IsApplying = false; } } } } private sealed class RelatedDetourBag { public readonly MethodBase Method; public readonly List RelatedDetours = new List(); public bool IsValid = true; public RelatedDetourBag(MethodBase method) { Method = method; } } private readonly MethodBase realTarget; private static readonly object subLock = new object(); private static bool hasSubscribed; private static readonly ConcurrentDictionary relatedDetours = new ConcurrentDictionary(); private IDisposable? srcPin; private IDisposable? dstPin; private new ManagedDetourBox DetourBox => GetDetourBox(); public MethodBase Source { get; } public MethodBase Target { get; } public Detour(PlatformTriple triple, MethodBase src, MethodBase dst) : base(triple) { Source = triple.GetIdentifiable(src); Target = dst; realTarget = triple.GetRealDetourTarget(src, dst); base.DetourBox = new ManagedDetourBox(triple, Source, realTarget); if (triple.SupportedFeatures.Has(RuntimeFeature.CompileMethodHook)) { EnsureSubscribed(triple); DetourBox.SubscribeCompileMethod(); } } private static void EnsureSubscribed(PlatformTriple triple) { if (Volatile.Read(in hasSubscribed)) { return; } lock (subLock) { if (!Volatile.Read(in hasSubscribed)) { Volatile.Write(ref hasSubscribed, value: true); triple.Runtime.OnMethodCompiled += OnMethodCompiled; } } } private static void AddRelatedDetour(MethodBase m, ManagedDetourBox cmh) { while (true) { RelatedDetourBag orAdd = relatedDetours.GetOrAdd(m, (MethodBase method) => new RelatedDetourBag(method)); lock (orAdd) { if (!orAdd.IsValid) { continue; } orAdd.RelatedDetours.Add(cmh); if (orAdd.RelatedDetours.Count > 1) { bool isEnabled; MMDbgLog.DebugLogWarningStringHandler message = new MMDbgLog.DebugLogWarningStringHandler(115, 1, out isEnabled); if (isEnabled) { message.AppendLiteral("Multiple related detours for method "); message.AppendFormatted(m); message.AppendLiteral("! This means that the method has been detoured twice. Detour cleanup will fail."); } MMDbgLog.Warning(ref message); } break; } } } private static void RemoveRelatedDetour(MethodBase m, ManagedDetourBox cmh) { if (relatedDetours.TryGetValue(m, out RelatedDetourBag value)) { lock (value) { value.RelatedDetours.Remove(cmh); if (value.RelatedDetours.Count == 0) { value.IsValid = false; Helpers.Assert(relatedDetours.TryRemove(value.Method, out RelatedDetourBag _), (string)null, "relatedDetours.TryRemove(related.Method, out _)"); } return; } } bool isEnabled; MMDbgLog.DebugLogWarningStringHandler message = new MMDbgLog.DebugLogWarningStringHandler(79, 1, out isEnabled); if (isEnabled) { message.AppendLiteral("Attempted to remove a related detour from method "); message.AppendFormatted(m); message.AppendLiteral(" which has no RelatedDetourBag"); } MMDbgLog.Warning(ref message); } private static void OnMethodCompiled(RuntimeMethodHandle methodHandle, MethodBase? method, nint codeStart, nint codeStartRw, ulong codeSize) { if ((object)method == null) { return; } method = PlatformTriple.Current.GetIdentifiable(method); if (!relatedDetours.TryGetValue(method, out RelatedDetourBag value)) { return; } lock (value) { foreach (ManagedDetourBox relatedDetour in value.RelatedDetours) { relatedDetour.OnMethodCompiled(method, codeStart, codeStartRw, codeSize); } } } protected override SimpleNativeDetour CreateDetour() { bool isEnabled; MMDbgLog.DebugLogTraceStringHandler message = new MMDbgLog.DebugLogTraceStringHandler(33, 2, out isEnabled); if (isEnabled) { message.AppendLiteral("Applying managed detour from "); message.AppendFormatted(Source); message.AppendLiteral(" to "); message.AppendFormatted(realTarget); } MMDbgLog.Trace(ref message); srcPin = Triple.PinMethodIfNeeded(Source); dstPin = Triple.PinMethodIfNeeded(realTarget); Triple.Compile(Source); nint nativeMethodBody = Triple.GetNativeMethodBody(Source); Triple.Compile(realTarget); nint functionPointer = Triple.Runtime.GetMethodHandle(realTarget).GetFunctionPointer(); return Triple.CreateSimpleDetour(nativeMethodBody, functionPointer); } protected override void BeforeUndo() { bool isEnabled; MMDbgLog.DebugLogTraceStringHandler message = new MMDbgLog.DebugLogTraceStringHandler(32, 2, out isEnabled); if (isEnabled) { message.AppendLiteral("Undoing managed detour from "); message.AppendFormatted(Source); message.AppendLiteral(" to "); message.AppendFormatted(realTarget); } MMDbgLog.Trace(ref message); } protected override void AfterUndo() { Interlocked.Exchange(ref srcPin, null)?.Dispose(); Interlocked.Exchange(ref dstPin, null)?.Dispose(); } protected override void BeforeDispose() { if (Triple.SupportedFeatures.Has(RuntimeFeature.CompileMethodHook)) { DetourBox.UnsubscribeCompileMethod(); } } } private sealed class NativeDetour : DetourBase, ICoreNativeDetour, ICoreDetourBase, IDisposable { private sealed class NativeDetourBox : DetourBoxBase { public readonly nint From; public readonly nint To; public NativeDetourBox(PlatformTriple triple, nint from, nint to) : base(triple) { From = from; To = to; } } private IDisposable? origHandle; public nint Source => DetourBox.From; public nint Target => DetourBox.To; public bool HasOrigEntrypoint => OrigEntrypoint != IntPtr.Zero; public nint OrigEntrypoint { get; private set; } private new NativeDetourBox DetourBox => GetDetourBox(); public NativeDetour(PlatformTriple triple, nint from, nint to) : base(triple) { base.DetourBox = new NativeDetourBox(triple, from, to); } protected override SimpleNativeDetour CreateDetour() { bool isEnabled; MMDbgLog.DebugLogTraceStringHandler message = new MMDbgLog.DebugLogTraceStringHandler(32, 2, out isEnabled); if (isEnabled) { message.AppendLiteral("Applying native detour from "); message.AppendFormatted(Source, "x16"); message.AppendLiteral(" to "); message.AppendFormatted(Target, "x16"); } MMDbgLog.Trace(ref message); Triple.CreateNativeDetour(Source, Target).Deconstruct(out SimpleNativeDetour Simple, out nint AltEntry, out IDisposable AltHandle); SimpleNativeDetour result = Simple; nint origEntrypoint = AltEntry; IDisposable disposable = AltHandle; IDisposable? disposable2 = origHandle; AltHandle = disposable; disposable = disposable2; origHandle = AltHandle; OrigEntrypoint = origEntrypoint; return result; } protected override void BeforeUndo() { bool isEnabled; MMDbgLog.DebugLogTraceStringHandler message = new MMDbgLog.DebugLogTraceStringHandler(31, 2, out isEnabled); if (isEnabled) { message.AppendLiteral("Undoing native detour from "); message.AppendFormatted(Source, "x16"); message.AppendLiteral(" to "); message.AppendFormatted(Target, "x16"); } MMDbgLog.Trace(ref message); } protected override void AfterUndo() { OrigEntrypoint = IntPtr.Zero; origHandle?.Dispose(); origHandle = null; } protected override void BeforeDispose() { } } private readonly PlatformTriple triple; public bool SupportsNativeDetourOrigEntrypoint => Helpers.Has(triple.SupportedFeatures.Architecture, ArchitectureFeature.CreateAltEntryPoint); public PlatformTripleDetourFactory(PlatformTriple triple) { this.triple = triple; } public ICoreDetour CreateDetour(CreateDetourRequest request) { Helpers.ThrowIfArgumentNull(request.Source, "request.Source"); Helpers.ThrowIfArgumentNull(request.Target, "request.Target"); if (!triple.TryDisableInlining(request.Source)) { bool isEnabled; MMDbgLog.DebugLogWarningStringHandler message = new MMDbgLog.DebugLogWarningStringHandler(66, 1, out isEnabled); if (isEnabled) { message.AppendLiteral("Could not disable inlining of method "); message.AppendFormatted(request.Source); message.AppendLiteral("; detours may not be reliable"); } MMDbgLog.Warning(ref message); } Detour detour = new Detour(triple, request.Source, request.Target); if (request.ApplyByDefault) { detour.Apply(); } return detour; } public ICoreNativeDetour CreateNativeDetour(CreateNativeDetourRequest request) { NativeDetour nativeDetour = new NativeDetour(triple, request.Source, request.Target); if (request.ApplyByDefault) { nativeDetour.Apply(); } return nativeDetour; } } [Flags] public enum RuntimeFeature { None = 0, PreciseGC = 1, CompileMethodHook = 2, ILDetour = 4, GenericSharing = 8, ListGenericInstantiations = 0x40, DisableInlining = 0x10, Uninlining = 0x20, RequiresMethodPinning = 0x80, RequiresMethodIdentification = 0x100, RequiresBodyThunkWalking = 0x200, HasKnownABI = 0x400, RequiresCustomMethodCompile = 0x800 } public sealed class SimpleNativeDetour : IDisposable { private bool disposedValue; private readonly PlatformTriple triple; private NativeDetourInfo detourInfo; private Memory backup; private IDisposable? AllocHandle; public ReadOnlyMemory DetourBackup => backup; public nint Source => detourInfo.From; public nint Destination => detourInfo.To; internal SimpleNativeDetour(PlatformTriple triple, NativeDetourInfo detourInfo, Memory backup, IDisposable? allocHandle) { this.triple = triple; this.detourInfo = detourInfo; this.backup = backup; AllocHandle = allocHandle; } public void ChangeTarget(nint newTarget) { CheckDisposed(); bool isEnabled; MMDbgLog.DebugLogTraceStringHandler message = new MMDbgLog.DebugLogTraceStringHandler(47, 3, out isEnabled); if (isEnabled) { message.AppendLiteral("Retargeting simple detour 0x"); message.AppendFormatted(Source, "x16"); message.AppendLiteral(" => 0x"); message.AppendFormatted(Destination, "x16"); message.AppendLiteral(" to target 0x"); message.AppendFormatted(newTarget, "x16"); } MMDbgLog.Trace(ref message); NativeDetourInfo retarget = triple.Architecture.ComputeRetargetInfo(detourInfo, newTarget, detourInfo.Size); Span span = stackalloc byte[retarget.Size]; triple.Architecture.GetRetargetBytes(detourInfo, retarget, span, out IDisposable allocationHandle, out bool needsRepatch, out bool disposeOldAlloc); if (needsRepatch) { byte[] array = null; if (retarget.Size > backup.Length) { array = new byte[retarget.Size]; } triple.System.PatchData(PatchTargetKind.Executable, Source, span, array); if (array != null) { backup.Span.CopyTo(array); backup = array; } } detourInfo = retarget; IDisposable? allocHandle = AllocHandle; IDisposable allocHandle2 = allocationHandle; allocationHandle = allocHandle; AllocHandle = allocHandle2; if (disposeOldAlloc) { allocationHandle?.Dispose(); } } public void Undo() { CheckDisposed(); UndoCore(disposing: true); } private void CheckDisposed() { if (disposedValue) { throw new ObjectDisposedException("SimpleNativeDetour"); } } private void UndoCore(bool disposing) { bool isEnabled; MMDbgLog.DebugLogTraceStringHandler message = new MMDbgLog.DebugLogTraceStringHandler(30, 2, out isEnabled); if (isEnabled) { message.AppendLiteral("Undoing simple detour 0x"); message.AppendFormatted(Source, "x16"); message.AppendLiteral(" => 0x"); message.AppendFormatted(Destination, "x16"); } MMDbgLog.Trace(ref message); triple.System.PatchData(PatchTargetKind.Executable, Source, DetourBackup.Span, default(Span)); if (disposing) { Cleanup(); } disposedValue = true; } private void Cleanup() { AllocHandle?.Dispose(); } private void Dispose(bool disposing) { if (!disposedValue) { UndoCore(disposing); disposedValue = true; } } ~SimpleNativeDetour() { Dispose(disposing: false); } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } } [Flags] public enum SystemFeature { None = 0, RWXPages = 1, RXPages = 2, MayUseNativeJitHooks = 0x10 } } namespace MonoMod.Core.Platforms.Systems { internal sealed class LinuxSystem : ISystem, IInitialize { private sealed class MmapPagedMemoryAllocator : PagedMemoryAllocator { private sealed class SyscallNotImplementedException : Exception { } private static int PageProbePipeReadFD; private static int PageProbePipeWriteFD; private bool canTestPageAllocation = true; public MmapPagedMemoryAllocator(nint pageSize) : base(pageSize) { } unsafe static MmapPagedMemoryAllocator() { byte* num = stackalloc byte[8]; if (Unix.Pipe2((int*)num, Unix.PipeFlags.CloseOnExec) == -1) { throw new Win32Exception(Unix.Errno, "Failed to create pipe for page probes"); } PageProbePipeReadFD = *(int*)num; PageProbePipeWriteFD = ((int*)num)[1]; } public unsafe static bool PageAllocated(nint page) { byte b = default(byte); if (Unix.Mincore(page, 1u, &b) == -1) { int errno = Unix.Errno; return errno switch { 12 => false, 38 => throw new SyscallNotImplementedException(), _ => throw new NotImplementedException($"Got unimplemented errno for mincore(2); errno = {errno}"), }; } return true; } public unsafe static bool PageReadable(nint page) { if (Unix.Write(PageProbePipeWriteFD, page, 1) == -1) { int errno = Unix.Errno; if (errno == 14) { return false; } throw new NotImplementedException($"Got unimplemented errno for write(2); errno = {errno}"); } byte b = default(byte); if (Unix.Read(PageProbePipeReadFD, new IntPtr(&b), 1) == -1) { throw new Win32Exception("Failed to clean up page probe pipe after successful page probe"); } return true; } protected override bool TryAllocateNewPage(AllocationRequest request, [MaybeNullWhen(false)] out IAllocatedMemory allocated) { Unix.Protection protection = (request.Executable ? Unix.Protection.Execute : Unix.Protection.None); protection |= Unix.Protection.Read | Unix.Protection.Write; nint num = Unix.Mmap(IntPtr.Zero, (nuint)base.PageSize, protection, Unix.MmapFlags.Private | Unix.MmapFlags.Anonymous, -1, 0); long num2 = num; bool isEnabled = (((ulong)(num2 - -1) <= 1uL) ? true : false); if (isEnabled) { int errno = Unix.Errno; MMDbgLog.DebugLogErrorStringHandler message = new MMDbgLog.DebugLogErrorStringHandler(28, 2, out isEnabled); if (isEnabled) { message.AppendLiteral("Error creating allocation: "); message.AppendFormatted(errno); message.AppendLiteral(" "); message.AppendFormatted(new Win32Exception(errno).Message); } MMDbgLog.Error(ref message); allocated = null; return false; } Page page = new Page(this, num, (uint)base.PageSize, request.Executable); InsertAllocatedPage(page); if (!page.TryAllocate((uint)request.Size, (uint)request.Alignment, out PageAllocation alloc)) { RegisterForCleanup(page); allocated = null; return false; } allocated = alloc; return true; } protected override bool TryAllocateNewPage(PositionedAllocationRequest request, nint targetPage, nint lowPageBound, nint highPageBound, [MaybeNullWhen(false)] out IAllocatedMemory allocated) { if (!canTestPageAllocation) { allocated = null; return false; } Unix.Protection protection = (request.Base.Executable ? Unix.Protection.Execute : Unix.Protection.None); protection |= Unix.Protection.Read | Unix.Protection.Write; nint num = request.Base.Size / base.PageSize + 1; nint num2 = targetPage - base.PageSize; nint num3 = targetPage; nint num4 = -1; try { while (num2 >= lowPageBound || num3 <= highPageBound) { if (num3 <= highPageBound) { nint num5 = 0; while (true) { if (num5 < num) { if (PageAllocated(num3 + base.PageSize * num5)) { num3 += base.PageSize; goto IL_008e; } num5++; continue; } num4 = num3; break; } break; } goto IL_008e; IL_008e: if (num2 < lowPageBound) { continue; } nint num6 = 0; while (true) { if (num6 < num) { if (PageAllocated(num2 + base.PageSize * num6)) { num2 -= base.PageSize; goto IL_00c5; } num6++; continue; } num4 = num2; break; } break; IL_00c5:; } } catch (SyscallNotImplementedException) { canTestPageAllocation = false; allocated = null; return false; } if (num4 == -1) { allocated = null; return false; } nint num7 = Unix.Mmap(num4, (nuint)base.PageSize, protection, Unix.MmapFlags.Private | Unix.MmapFlags.Anonymous | Unix.MmapFlags.FixedNoReplace, -1, 0); long num8 = num7; if (((ulong)(num8 - -1) <= 1uL) ? true : false) { allocated = null; return false; } Page page = new Page(this, num7, (uint)base.PageSize, request.Base.Executable); InsertAllocatedPage(page); if (!page.TryAllocate((uint)request.Base.Size, (uint)request.Base.Alignment, out PageAllocation alloc)) { RegisterForCleanup(page); allocated = null; return false; } if (alloc.BaseAddress < request.LowBound || alloc.BaseAddress + alloc.Size >= request.HighBound) { alloc.Dispose(); allocated = null; return false; } allocated = alloc; return true; } protected override bool TryFreePage(Page page, [NotNullWhen(false)] out string? errorMsg) { if (Unix.Munmap(page.BaseAddr, page.Size) != 0) { errorMsg = new Win32Exception(Unix.Errno).Message; return false; } errorMsg = null; return true; } } private sealed class LinuxNativeLibDrop : PosixNativeLibraryDrop { public static readonly LinuxNativeLibDrop Instance = new LinuxNativeLibDrop(); protected override void CloseFileDescriptor(nint fd) { Unix.Close((int)fd); } protected unsafe override nint Mkstemp(Span template) { int num; fixed (byte* template2 = template) { num = Unix.MkSTemp(template2); } if (num == -1) { int errno = Unix.Errno; Win32Exception ex = new Win32Exception(errno); bool isEnabled; MMDbgLog.DebugLogErrorStringHandler message = new MMDbgLog.DebugLogErrorStringHandler(29, 2, out isEnabled); if (isEnabled) { message.AppendLiteral("Could not create temp file: "); message.AppendFormatted(errno); message.AppendLiteral(" "); message.AppendFormatted(ex); } MMDbgLog.Error(ref message); throw ex; } return num; } } private readonly Abi defaultAbi; private readonly nint PageSize; private readonly MmapPagedMemoryAllocator allocator; private IArchitecture? arch; private PosixExceptionHelper? lazyNativeExceptionHelper; public OSKind Target => (OSKind)9; public SystemFeature Features => SystemFeature.RWXPages | SystemFeature.RXPages; public Abi? DefaultAbi => defaultAbi; public IMemoryAllocator MemoryAllocator => allocator; public INativeExceptionHelper? NativeExceptionHelper => lazyNativeExceptionHelper ?? (lazyNativeExceptionHelper = CreateNativeExceptionHelper()); private static ReadOnlySpan NEHTempl => "/tmp/mm-exhelper.so.XXXXXX"u8; public IEnumerable EnumerateLoadedModules() { foreach (Linux.Procfs.Module item in Linux.Procfs.ParseMapsModules(Linux.Procfs.ProcPid.Self)) { yield return new LoadedModule(item.StartAddress, item.Path, item.Size); } } public IEnumerable EnumerateLoadedModuleFiles() { foreach (LoadedModule item in EnumerateLoadedModules()) { yield return item.FileName; } } public LinuxSystem() { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Invalid comparison between Unknown and I4 //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Invalid comparison between Unknown and I4 PageSize = (nint)Unix.Sysconf(Unix.SysconfName.PageSize); allocator = new MmapPagedMemoryAllocator(PageSize); ArchitectureKind architecture = PlatformDetection.Architecture; if ((int)architecture != 3) { if ((int)architecture != 5) { throw new NotImplementedException(); } defaultAbi = new Abi(new SpecialArgumentKind[2] { SpecialArgumentKind.ThisPointer, SpecialArgumentKind.UserArguments }, SystemVABI.ClassifyARM64, ReturnsReturnBuffer: false); } else { defaultAbi = new Abi(new SpecialArgumentKind[3] { SpecialArgumentKind.ReturnBuffer, SpecialArgumentKind.ThisPointer, SpecialArgumentKind.UserArguments }, SystemVABI.ClassifyAMD64, ReturnsReturnBuffer: true); } } public nint GetSizeOfReadableMemory(nint start, nint guess) { nint num = allocator.RoundDownToPageBoundary(start); if (!MmapPagedMemoryAllocator.PageReadable(num)) { return 0; } num += PageSize; nint num2 = num - start; while (num2 < guess) { if (!MmapPagedMemoryAllocator.PageReadable(num)) { return num2; } num2 += PageSize; num += PageSize; } return num2; } public unsafe void PatchData(PatchTargetKind patchKind, nint patchTarget, ReadOnlySpan data, Span backup) { if (patchKind == PatchTargetKind.Executable) { ProtectRWX(patchTarget, data.Length); } else { ProtectRW(patchTarget, data.Length); } Span destination = new Span((void*)patchTarget, data.Length); destination.TryCopyTo(backup); data.CopyTo(destination); } private void RoundToPageBoundary(ref nint addr, ref nint size) { nint num = allocator.RoundDownToPageBoundary(addr); size += addr - num; addr = num; } private void ProtectRW(nint addr, nint size) { RoundToPageBoundary(ref addr, ref size); if (Unix.Mprotect(addr, (nuint)size, Unix.Protection.Read | Unix.Protection.Write) != 0) { throw new Win32Exception(Unix.Errno); } } private void ProtectRWX(nint addr, nint size) { RoundToPageBoundary(ref addr, ref size); if (Unix.Mprotect(addr, (nuint)size, Unix.Protection.Read | Unix.Protection.Write | Unix.Protection.Execute) != 0) { throw new Win32Exception(Unix.Errno); } } void IInitialize.Initialize(IArchitecture value) { arch = value; } private PosixExceptionHelper CreateNativeExceptionHelper() { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Invalid comparison between Unknown and I4 //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Invalid comparison between Unknown and I4 Helpers.Assert(arch != null, (string)null, "arch is not null"); ArchitectureKind target = arch.Target; string text; if ((int)target != 3) { if ((int)target != 5) { throw new NotImplementedException("No exception helper for current arch"); } text = "exhelper_linux_arm64.so"; } else { text = "exhelper_linux_x86_64.so"; } string name = text; string filename; using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(name)) { Helpers.Assert(stream != null, (string)null, "embedded is not null"); filename = LinuxNativeLibDrop.Instance.DropLibrary(stream, NEHTempl); } return PosixExceptionHelper.CreateHelper(arch, filename); } public nint GetNativeJitHookConfig(int runtimeMajMin) { throw new NotImplementedException(); } } internal sealed class MacOSSystem : ISystem, IInitialize { private sealed class MacOsQueryingAllocator : QueryingMemoryPageAllocatorBase { public override uint PageSize { get; } public MacOsQueryingAllocator() { PageSize = (uint)OSX.GetPageSize(); } public unsafe override bool TryAllocatePage(nint size, bool executable, out nint allocated) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Invalid comparison between Unknown and I4 Helpers.Assert(size == PageSize, (string)null, "size == PageSize"); OSX.vm_prot_t vm_prot_t = (executable ? OSX.vm_prot_t.Execute : OSX.vm_prot_t.None); vm_prot_t |= OSX.vm_prot_t.Default; bool isEnabled; if ((int)PlatformDetection.Architecture == 5 && vm_prot_t == OSX.vm_prot_t.All) { MMDbgLog.Trace("RWX memory detected, doing mmap with MAP_JIT"); allocated = OSX.mmap(IntPtr.Zero, (ulong)size, OSX.map_prot.Read | OSX.map_prot.Write | OSX.map_prot.Execute, OSX.map_flags.Private | OSX.map_flags.JIT | OSX.map_flags.Anonymous, -1, 0L); if (allocated == -1) { int errno = OSX.Errno; Win32Exception value = new Win32Exception(errno); MMDbgLog.DebugLogErrorStringHandler message = new MMDbgLog.DebugLogErrorStringHandler(37, 2, out isEnabled); if (isEnabled) { message.AppendLiteral("Error creating allocation anywhere! "); message.AppendFormatted(errno); message.AppendLiteral(" "); message.AppendFormatted(value); } MMDbgLog.Error(ref message); allocated = 0; return false; } MMDbgLog.DebugLogTraceStringHandler message2 = new MMDbgLog.DebugLogTraceStringHandler(37, 2, out isEnabled); if (isEnabled) { message2.AppendLiteral("RWX memory allocated to 0x"); message2.AppendFormatted(allocated, "X16"); message2.AppendLiteral(" with size "); message2.AppendFormatted(size); } MMDbgLog.Trace(ref message2); return true; } ulong num = 0uL; OSX.kern_return_t kern_return_t = OSX.mach_vm_map(OSX.mach_task_self(), &num, (ulong)size, 0uL, OSX.vm_flags.Anywhere, 0, 0uL, true, vm_prot_t, vm_prot_t, OSX.vm_inherit_t.Copy); if (!kern_return_t) { MMDbgLog.DebugLogErrorStringHandler message3 = new MMDbgLog.DebugLogErrorStringHandler(41, 1, out isEnabled); if (isEnabled) { message3.AppendLiteral("Error creating allocation anywhere! kr = "); message3.AppendFormatted(kern_return_t.Value); } MMDbgLog.Error(ref message3); allocated = 0; return false; } allocated = (nint)num; return true; } public unsafe override bool TryAllocatePage(nint pageAddr, nint size, bool executable, out nint allocated) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Invalid comparison between Unknown and I4 Helpers.Assert(size == PageSize, (string)null, "size == PageSize"); OSX.vm_prot_t vm_prot_t = (executable ? OSX.vm_prot_t.Execute : OSX.vm_prot_t.None); vm_prot_t |= OSX.vm_prot_t.Default; bool isEnabled; if ((int)PlatformDetection.Architecture == 5 && vm_prot_t == OSX.vm_prot_t.All) { MMDbgLog.Trace("RWX memory detected, doing mmap with MAP_JIT"); allocated = OSX.mmap(pageAddr, (ulong)size, OSX.map_prot.Read | OSX.map_prot.Write | OSX.map_prot.Execute, OSX.map_flags.Private | OSX.map_flags.Fixed | OSX.map_flags.JIT | OSX.map_flags.Anonymous, -1, 0L); if (allocated == -1) { int errno = OSX.Errno; Win32Exception value = new Win32Exception(errno); MMDbgLog.DebugLogErrorStringHandler message = new MMDbgLog.DebugLogErrorStringHandler(37, 2, out isEnabled); if (isEnabled) { message.AppendLiteral("Error creating allocation anywhere! "); message.AppendFormatted(errno); message.AppendLiteral(" "); message.AppendFormatted(value); } MMDbgLog.Error(ref message); allocated = 0; return false; } MMDbgLog.DebugLogTraceStringHandler message2 = new MMDbgLog.DebugLogTraceStringHandler(45, 2, out isEnabled); if (isEnabled) { message2.AppendLiteral("RWX memory allocated to page at 0x"); message2.AppendFormatted(pageAddr, "X16"); message2.AppendLiteral(" with size "); message2.AppendFormatted(size); } MMDbgLog.Trace(ref message2); return true; } ulong num = (ulong)pageAddr; OSX.kern_return_t kern_return_t = OSX.mach_vm_map(OSX.mach_task_self(), &num, (ulong)size, 0uL, OSX.vm_flags.Fixed, 0, 0uL, true, vm_prot_t, vm_prot_t, OSX.vm_inherit_t.Copy); if (!kern_return_t) { MMDbgLog.DebugLogSpamStringHandler message3 = new MMDbgLog.DebugLogSpamStringHandler(38, 2, out isEnabled); if (isEnabled) { message3.AppendLiteral("Error creating allocation at 0x"); message3.AppendFormatted(num, "x16"); message3.AppendLiteral(": kr = "); message3.AppendFormatted(kern_return_t.Value); } MMDbgLog.Spam(ref message3); allocated = 0; return false; } allocated = (nint)num; return true; } public override bool TryFreePage(nint pageAddr, [NotNullWhen(false)] out string? errorMsg) { OSX.kern_return_t kern_return_t = OSX.mach_vm_deallocate(OSX.mach_task_self(), (ulong)pageAddr, PageSize); if (!kern_return_t) { errorMsg = $"Could not deallocate page: kr = {kern_return_t.Value}"; return false; } errorMsg = null; return true; } public override bool TryQueryPage(nint pageAddr, out bool isFree, out nint allocBase, out nint allocSize) { OSX.vm_prot_t prot; OSX.vm_prot_t maxProt; OSX.kern_return_t localRegionInfo = GetLocalRegionInfo(pageAddr, out allocBase, out allocSize, out prot, out maxProt); if ((bool)localRegionInfo) { if (allocBase > pageAddr) { allocSize = allocBase - pageAddr; allocBase = pageAddr; isFree = true; return true; } isFree = false; return true; } if (localRegionInfo == OSX.kern_return_t.InvalidAddress) { isFree = true; return true; } isFree = false; return false; } } private sealed class MacOSNativeLibDrop : PosixNativeLibraryDrop { public static readonly MacOSNativeLibDrop Instance = new MacOSNativeLibDrop(); protected override void CloseFileDescriptor(nint fd) { OSX.Close((int)fd); } protected unsafe override nint Mkstemp(Span template) { int num; fixed (byte* template2 = template) { num = OSX.MkSTemp(template2); } if (num == -1) { int errno = OSX.Errno; Win32Exception ex = new Win32Exception(errno); bool isEnabled; MMDbgLog.DebugLogErrorStringHandler message = new MMDbgLog.DebugLogErrorStringHandler(29, 2, out isEnabled); if (isEnabled) { message.AppendLiteral("Could not create temp file: "); message.AppendFormatted(errno); message.AppendLiteral(" "); message.AppendFormatted(ex); } MMDbgLog.Error(ref message); throw ex; } return num; } } private sealed class JitMemcpyHelper : PosixExceptionHelper { private readonly nint mmch_jit_memcpy; private readonly nint mmch_jit_hook_config; private JitMemcpyHelper(IArchitecture arch, nint getExPtr, nint m2n, nint n2m, nint memcpy, nint jitCfg) : base(arch, getExPtr, m2n, n2m) { mmch_jit_memcpy = memcpy; mmch_jit_hook_config = jitCfg; } public new static JitMemcpyHelper CreateHelper(IArchitecture arch, string filename, bool deleteAfterLoad = true) { nint num = DynDll.OpenLibrary(filename); nint export; nint export2; nint export3; nint export4; nint export5; try { if (deleteAfterLoad) { File.Delete(filename); } export = DynDll.GetExport((IntPtr)num, "eh_get_exception_ptr"); export2 = DynDll.GetExport((IntPtr)num, "eh_managed_to_native"); export3 = DynDll.GetExport((IntPtr)num, "eh_native_to_managed"); export4 = DynDll.GetExport((IntPtr)num, "mmch_jit_memcpy"); export5 = DynDll.GetExport((IntPtr)num, "mmch_jit_hook_config"); Helpers.Assert(export != IntPtr.Zero, (string)null, "eh_get_exception_ptr != IntPtr.Zero"); Helpers.Assert(export2 != IntPtr.Zero, (string)null, "eh_managed_to_native != IntPtr.Zero"); Helpers.Assert(export3 != IntPtr.Zero, (string)null, "eh_native_to_managed != IntPtr.Zero"); Helpers.Assert(export3 != IntPtr.Zero, (string)null, "eh_native_to_managed != IntPtr.Zero"); Helpers.Assert(export4 != IntPtr.Zero, (string)null, "mmch_jit_memcpy != IntPtr.Zero"); Helpers.Assert(export5 != IntPtr.Zero, (string)null, "mmch_jit_hook_config != IntPtr.Zero"); } catch { DynDll.CloseLibrary((IntPtr)num); throw; } return new JitMemcpyHelper(arch, export, export2, export3, export4, export5); } public unsafe void JitMemCpy(nint dst, nint src, ulong size) { ((delegate* unmanaged[Cdecl])mmch_jit_memcpy)(dst, src, size); } internal unsafe nint GetJitHookConfig(int runtimeMajMin) { return ((delegate* unmanaged[Cdecl])mmch_jit_hook_config)(runtimeMajMin); } } private IArchitecture? arch; private PosixExceptionHelper? lazyNativeExceptionHelper; public OSKind Target => (OSKind)5; public SystemFeature Features { get; } public Abi? DefaultAbi { get; } public uint PageSize { get; } = (uint)OSX.GetPageSize(); public IMemoryAllocator MemoryAllocator { get; } = new QueryingPagedMemoryAllocator(new MacOsQueryingAllocator()); public INativeExceptionHelper? NativeExceptionHelper => lazyNativeExceptionHelper ?? (lazyNativeExceptionHelper = CreateNativeExceptionHelper()); private static ReadOnlySpan NEHTempl => "/tmp/mm-exhelper.dylib.XXXXXX"u8; public MacOSSystem() { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Invalid comparison between Unknown and I4 //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Invalid comparison between Unknown and I4 ArchitectureKind architecture = PlatformDetection.Architecture; if ((int)architecture != 3) { if ((int)architecture != 5) { throw new NotImplementedException(); } Features = SystemFeature.RXPages | SystemFeature.MayUseNativeJitHooks; DefaultAbi = new Abi(new SpecialArgumentKind[2] { SpecialArgumentKind.ThisPointer, SpecialArgumentKind.UserArguments }, SystemVABI.ClassifyARM64, ReturnsReturnBuffer: false); } else { Features = SystemFeature.RWXPages | SystemFeature.RXPages; DefaultAbi = new Abi(new SpecialArgumentKind[3] { SpecialArgumentKind.ReturnBuffer, SpecialArgumentKind.ThisPointer, SpecialArgumentKind.UserArguments }, SystemVABI.ClassifyAMD64, ReturnsReturnBuffer: true); } } public unsafe IEnumerable EnumerateLoadedModules() { int count = OSX.task_dyld_info.Count; OSX.task_dyld_info task_dyld_info = default(OSX.task_dyld_info); if (!OSX.task_info(OSX.mach_task_self(), OSX.task_flavor_t.DyldInfo, &task_dyld_info, &count)) { return Array.Empty(); } ReadOnlySpan infoArray = task_dyld_info.all_image_infos->InfoArray; LoadedModule[] array = new LoadedModule[infoArray.Length]; for (int i = 0; i < array.Length; i++) { OSX.dyld_image_info dyld_image_info = infoArray[i]; array[i] = new LoadedModule((ulong)dyld_image_info.imageLoadAddress, dyld_image_info.imageFilePath.ToString(), null); } return array; } public IEnumerable EnumerateLoadedModuleFiles() { foreach (LoadedModule item in EnumerateLoadedModules()) { yield return item.FileName; } } public nint GetSizeOfReadableMemory(nint start, nint guess) { nint num = 0; do { if (!GetLocalRegionInfo(start, out var startAddr, out var outSize, out var prot, out var _)) { return num; } if (startAddr > start) { return num; } if ((prot & OSX.vm_prot_t.Read) == 0) { return num; } num += startAddr + outSize - start; start = startAddr + outSize; } while (num < guess); return num; } public unsafe void PatchData(PatchTargetKind targetKind, nint patchTarget, ReadOnlySpan data, Span backup) { int length = data.Length; bool isEnabled; bool flag; bool flag2; if (TryGetProtForMem(patchTarget, length, out var _, out var prot, out var crossesAllocBoundary, out var notAllocated)) { if (crossesAllocBoundary) { MMDbgLog.DebugLogWarningStringHandler message = new MMDbgLog.DebugLogWarningStringHandler(101, 2, out isEnabled); if (isEnabled) { message.AppendLiteral("Patch requested for memory which spans multiple memory allocations. Failures may result. (0x"); message.AppendFormatted(patchTarget, "x16"); message.AppendLiteral(" length "); message.AppendFormatted(length); message.AppendLiteral(")"); } MMDbgLog.Warning(ref message); } flag = Helpers.Has(prot, OSX.vm_prot_t.Write); flag2 = Helpers.Has(prot, OSX.vm_prot_t.Execute); } else { if (notAllocated) { MMDbgLog.DebugLogErrorStringHandler message2 = new MMDbgLog.DebugLogErrorStringHandler(68, 2, out isEnabled); if (isEnabled) { message2.AppendLiteral("Requested patch of region which was not fully allocated (0x"); message2.AppendFormatted(patchTarget, "x16"); message2.AppendLiteral(" length "); message2.AppendFormatted(length); message2.AppendLiteral(")"); } MMDbgLog.Error(ref message2); throw new InvalidOperationException("Cannot patch unallocated region"); } flag = false; flag2 = targetKind == PatchTargetKind.Executable; } if (!flag) { Helpers.Assert(!crossesAllocBoundary, (string)null, "!crossesBoundary"); MakePageWritable((nuint)patchTarget, (nuint)data.Length); prot = OSX.vm_prot_t.All; } Span destination = new Span((void*)patchTarget, data.Length); destination.TryCopyTo(backup); if (NativeExceptionHelper is JitMemcpyHelper jitMemcpyHelper && prot == OSX.vm_prot_t.All) { MMDbgLog.Trace("RWX memory detected, doing memcpy for MAP_JIT"); fixed (byte* src = data) { jitMemcpyHelper.JitMemCpy(patchTarget, (nint)src, (ulong)data.Length); MMDbgLog.DebugLogTraceStringHandler message3 = new MMDbgLog.DebugLogTraceStringHandler(20, 2, out isEnabled); if (isEnabled) { message3.AppendFormatted(data.Length); message3.AppendLiteral(" bytes written to 0x"); message3.AppendFormatted(patchTarget, "X16"); } MMDbgLog.Trace(ref message3); } } else { data.CopyTo(destination); } if (flag2) { OSX.sys_icache_invalidate((void*)patchTarget, (nuint)data.Length); } } private unsafe void MakePageWritable(nuint addrInPage, nuint bufferSize) { //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Invalid comparison between Unknown and I4 nuint num = addrInPage & (nuint)(~((nint)PageSize - (nint)1)); if (addrInPage + bufferSize > num + PageSize) { MMDbgLog.Warning("Crossed pages while performing page remap"); MakePageWritable(num, PageSize); MakePageWritable(num + PageSize, bufferSize - PageSize + (addrInPage - num)); return; } Helpers.Assert((bool)GetLocalRegionInfo((nint)num, out var _, out var _, out var prot, out var maxProt), (string)null, "GetLocalRegionInfo((nint)pageAddress, out _, out _, out var allocProt, out var allocMaxProt)"); if (Helpers.Has(prot, OSX.vm_prot_t.Write)) { return; } int num2 = OSX.mach_task_self(); bool isEnabled; if (Helpers.Has(maxProt, OSX.vm_prot_t.Write) || (int)PlatformDetection.Architecture != 5) { OSX.kern_return_t kern_return_t = OSX.mach_vm_protect(num2, num, PageSize, false, prot | OSX.vm_prot_t.Write); if ((bool)kern_return_t) { return; } MMDbgLog.DebugLogErrorStringHandler message = new MMDbgLog.DebugLogErrorStringHandler(60, 6, out isEnabled); if (isEnabled) { message.AppendLiteral("Could not vm_protect page 0x"); message.AppendFormatted(num, "x16"); message.AppendLiteral("+0x"); message.AppendFormatted(PageSize, "x"); message.AppendLiteral(" "); message.AppendLiteral("from "); message.AppendFormatted(OSX.P(prot)); message.AppendLiteral(" to "); message.AppendFormatted(OSX.P(prot | OSX.vm_prot_t.Write)); message.AppendLiteral(" (max prot "); message.AppendFormatted(OSX.P(maxProt)); message.AppendLiteral("): kr = "); message.AppendFormatted(kern_return_t.Value); } MMDbgLog.Error(ref message); MMDbgLog.Error("Trying copy/remap instead..."); } if (!Helpers.Has(prot, OSX.vm_prot_t.Read)) { if (!Helpers.Has(maxProt, OSX.vm_prot_t.Read)) { MMDbgLog.DebugLogErrorStringHandler message2 = new MMDbgLog.DebugLogErrorStringHandler(66, 3, out isEnabled); if (isEnabled) { message2.AppendLiteral("Requested 0x"); message2.AppendFormatted(num, "x16"); message2.AppendLiteral("+0x"); message2.AppendFormatted(PageSize, "x"); message2.AppendLiteral(" (max: "); message2.AppendFormatted(OSX.P(maxProt)); message2.AppendLiteral(") to be made writable, but its not readable!"); } MMDbgLog.Error(ref message2); throw new NotSupportedException("Cannot make page writable because its not readable"); } OSX.kern_return_t kern_return_t = OSX.mach_vm_protect(num2, num, PageSize, false, prot | OSX.vm_prot_t.Read); if (!kern_return_t) { MMDbgLog.DebugLogErrorStringHandler message3 = new MMDbgLog.DebugLogErrorStringHandler(60, 4, out isEnabled); if (isEnabled) { message3.AppendLiteral("vm_protect of 0x"); message3.AppendFormatted(num, "x16"); message3.AppendLiteral("+0x"); message3.AppendFormatted(PageSize, "x"); message3.AppendLiteral(" (max: "); message3.AppendFormatted(OSX.P(maxProt)); message3.AppendLiteral(") to become readable failed: kr = "); message3.AppendFormatted(kern_return_t.Value); } MMDbgLog.Error(ref message3); throw new NotSupportedException("Could not make page readable for remap"); } } MMDbgLog.DebugLogTraceStringHandler message4 = new MMDbgLog.DebugLogTraceStringHandler(41, 5, out isEnabled); if (isEnabled) { message4.AppendLiteral("Performing page remap on 0x"); message4.AppendFormatted(num, "x16"); message4.AppendLiteral("+0x"); message4.AppendFormatted(PageSize, "x"); message4.AppendLiteral(" from "); message4.AppendFormatted(OSX.P(prot)); message4.AppendLiteral("/"); message4.AppendFormatted(OSX.P(maxProt)); message4.AppendLiteral(" to "); message4.AppendFormatted(OSX.P(OSX.vm_prot_t.All)); } MMDbgLog.Trace(ref message4); nint num3 = OSX.mmap(IntPtr.Zero, PageSize, OSX.map_prot.Read | OSX.map_prot.Write | OSX.map_prot.Execute, OSX.map_flags.Private | OSX.map_flags.JIT | OSX.map_flags.Anonymous, -1, 0L); if (num3 == OSX.MAP_FAILED) { throw new Win32Exception(OSX.Errno); } try { if (NativeExceptionHelper is JitMemcpyHelper jitMemcpyHelper) { jitMemcpyHelper.JitMemCpy(num3, (nint)num, PageSize); } else { new Span((void*)num, (int)PageSize).CopyTo(new Span((void*)num3, (int)PageSize)); } ulong value = num; OSX.vm_prot_t vm_prot_t = default(OSX.vm_prot_t); OSX.vm_prot_t vm_prot_t2 = default(OSX.vm_prot_t); OSX.kern_return_t kern_return_t = OSX.mach_vm_remap(num2, &value, PageSize, 0uL, OSX.vm_flags.Overwrite, num2, (ulong)num3, true, &vm_prot_t, &vm_prot_t2, OSX.vm_inherit_t.Copy); if (!kern_return_t) { MMDbgLog.DebugLogErrorStringHandler message5 = new MMDbgLog.DebugLogErrorStringHandler(73, 9, out isEnabled); if (isEnabled) { message5.AppendLiteral("vm_remap() failed to map over target range: 0x"); message5.AppendFormatted(value, "x16"); message5.AppendLiteral("+0x"); message5.AppendFormatted(PageSize, "x"); message5.AppendLiteral(" ("); message5.AppendFormatted(OSX.P(prot)); message5.AppendLiteral("/"); message5.AppendFormatted(OSX.P(maxProt)); message5.AppendLiteral(")"); message5.AppendLiteral(" <- 0x"); message5.AppendFormatted(num3, "x16"); message5.AppendLiteral("+0x"); message5.AppendFormatted(PageSize, "x"); message5.AppendLiteral(" ("); message5.AppendFormatted(OSX.P(OSX.vm_prot_t.All)); message5.AppendLiteral("/"); message5.AppendFormatted(OSX.P(OSX.vm_prot_t.All)); message5.AppendLiteral("), kr = "); message5.AppendFormatted(kern_return_t.Value); } MMDbgLog.Error(ref message5); throw new NotSupportedException("vm_map() failed"); } } finally { OSX.kern_return_t kern_return_t = OSX.mach_vm_deallocate(num2, (ulong)num3, PageSize); if (!kern_return_t) { MMDbgLog.DebugLogErrorStringHandler message6 = new MMDbgLog.DebugLogErrorStringHandler(53, 3, out isEnabled); if (isEnabled) { message6.AppendLiteral("Could not deallocate created memory page 0x"); message6.AppendFormatted(num3, "x16"); message6.AppendLiteral("+0x"); message6.AppendFormatted(PageSize, "x"); message6.AppendLiteral("! kr = "); message6.AppendFormatted(kern_return_t.Value); } MMDbgLog.Error(ref message6); } } } private static bool TryGetProtForMem(nint addr, int length, out OSX.vm_prot_t maxProt, out OSX.vm_prot_t prot, out bool crossesAllocBoundary, out bool notAllocated) { maxProt = (OSX.vm_prot_t)(-1); prot = (OSX.vm_prot_t)(-1); crossesAllocBoundary = false; notAllocated = false; nint num = addr; while (addr < num + length) { nint startAddr; nint outSize; OSX.vm_prot_t prot2; OSX.vm_prot_t maxProt2; OSX.kern_return_t localRegionInfo = GetLocalRegionInfo(addr, out startAddr, out outSize, out prot2, out maxProt2); if ((bool)localRegionInfo) { if (startAddr > addr) { notAllocated = true; return false; } prot &= prot2; maxProt &= maxProt2; addr = startAddr + outSize; if (addr >= num + length) { break; } crossesAllocBoundary = true; continue; } if (localRegionInfo == OSX.kern_return_t.NoSpace) { notAllocated = true; return false; } return false; } return true; } private unsafe static OSX.kern_return_t GetLocalRegionInfo(nint origAddr, out nint startAddr, out nint outSize, out OSX.vm_prot_t prot, out OSX.vm_prot_t maxProt) { int num = int.MaxValue; int count = OSX.vm_region_submap_short_info_64.Count; ulong num2 = (ulong)origAddr; ulong num3 = default(ulong); OSX.vm_region_submap_short_info_64 vm_region_submap_short_info_ = default(OSX.vm_region_submap_short_info_64); OSX.kern_return_t kern_return_t = OSX.mach_vm_region_recurse(OSX.mach_task_self(), &num2, &num3, &num, &vm_region_submap_short_info_, &count); if (!kern_return_t) { startAddr = 0; outSize = 0; prot = OSX.vm_prot_t.None; maxProt = OSX.vm_prot_t.None; return kern_return_t; } Helpers.Assert(!vm_region_submap_short_info_.is_submap, (string)null, "!info.is_submap"); startAddr = (nint)num2; outSize = (nint)num3; prot = vm_region_submap_short_info_.protection; maxProt = vm_region_submap_short_info_.max_protection; return kern_return_t; } void IInitialize.Initialize(IArchitecture value) { arch = value; } public nint GetNativeJitHookConfig(int runtimeMajMin) { if (NativeExceptionHelper is JitMemcpyHelper jitMemcpyHelper) { return jitMemcpyHelper.GetJitHookConfig(runtimeMajMin); } return IntPtr.Zero; } private PosixExceptionHelper CreateNativeExceptionHelper() { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Invalid comparison between Unknown and I4 //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Invalid comparison between Unknown and I4 //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Invalid comparison between Unknown and I4 Helpers.Assert(arch != null, (string)null, "arch is not null"); ArchitectureKind target = arch.Target; string text; if ((int)target != 3) { if ((int)target != 5) { throw new NotImplementedException("No exception helper for current arch"); } text = "exhelper_macos_arm64.dylib"; } else { text = "exhelper_macos_x86_64.dylib"; } string name = text; string filename; using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(name)) { Helpers.Assert(stream != null, (string)null, "embedded is not null"); filename = MacOSNativeLibDrop.Instance.DropLibrary(stream, NEHTempl); } if ((int)arch.Target != 5) { return PosixExceptionHelper.CreateHelper(arch, filename); } return JitMemcpyHelper.CreateHelper(arch, filename); } } internal class PosixExceptionHelper : INativeExceptionHelper { private readonly IArchitecture arch; private readonly nint eh_get_exception_ptr; private readonly nint eh_managed_to_native; private readonly nint eh_native_to_managed; public unsafe nint NativeException { get { return *((delegate* unmanaged[Cdecl])eh_get_exception_ptr)(); } set { *((delegate* unmanaged[Cdecl])eh_get_exception_ptr)() = value; } } public unsafe GetExceptionSlot GetExceptionSlot => () => ((delegate* unmanaged[Cdecl])eh_get_exception_ptr)(); protected PosixExceptionHelper(IArchitecture arch, nint getExPtr, nint m2n, nint n2m) { this.arch = arch; eh_get_exception_ptr = getExPtr; eh_managed_to_native = m2n; eh_native_to_managed = n2m; } public static PosixExceptionHelper CreateHelper(IArchitecture arch, string filename, bool deleteAfterLoad = true) { nint num = DynDll.OpenLibrary(filename); nint export; nint export2; nint export3; try { if (deleteAfterLoad) { File.Delete(filename); } export = DynDll.GetExport((IntPtr)num, "eh_get_exception_ptr"); export2 = DynDll.GetExport((IntPtr)num, "eh_managed_to_native"); export3 = DynDll.GetExport((IntPtr)num, "eh_native_to_managed"); Helpers.Assert(export != IntPtr.Zero, (string)null, "eh_get_exception_ptr != IntPtr.Zero"); Helpers.Assert(export2 != IntPtr.Zero, (string)null, "eh_managed_to_native != IntPtr.Zero"); Helpers.Assert(export3 != IntPtr.Zero, (string)null, "eh_native_to_managed != IntPtr.Zero"); } catch { DynDll.CloseLibrary((IntPtr)num); throw; } return new PosixExceptionHelper(arch, export, export2, export3); } public nint CreateManagedToNativeHelper(nint target, out IDisposable? handle) { return ((IAllocatedMemory)(handle = arch.CreateSpecialEntryStub(eh_managed_to_native, target))).BaseAddress; } public nint CreateNativeToManagedHelper(nint target, out IDisposable? handle) { return ((IAllocatedMemory)(handle = arch.CreateSpecialEntryStub(eh_native_to_managed, target))).BaseAddress; } } internal abstract class PosixNativeLibraryDrop { protected abstract nint Mkstemp(Span template); protected abstract void CloseFileDescriptor(nint fd); public unsafe string DropLibrary(Stream sourceStream, ReadOnlySpan defaultTemplate) { //IL_0174: Unknown result type (might be due to invalid IL or missing references) //IL_017a: Invalid comparison between Unknown and I4 //IL_017c: Unknown result type (might be due to invalid IL or missing references) //IL_0182: Invalid comparison between Unknown and I4 object obj = default(object); byte[] array; int count; if (Switches.TryGetSwitchValue("HelperDropPath", ref obj) && obj is string path) { int num = defaultTemplate.LastIndexOf(47); Helpers.Assert(num >= 0, (string)null, "endOfDefaultTemplateDir >= 0"); ReadOnlySpan readOnlySpan = defaultTemplate.Slice(num); string fullPath = Path.GetFullPath(path); Directory.CreateDirectory(fullPath); int byteCount = Encoding.UTF8.GetByteCount(fullPath); array = ArrayPool.Shared.Rent(byteCount + readOnlySpan.Length + 1); array.AsSpan().Clear(); int num2; fixed (char* chars = fullPath.AsSpan()) { fixed (byte* bytes = array) { num2 = Encoding.UTF8.GetBytes(chars, fullPath.Length, bytes, array.Length); } } if (array[num2 - 1] == 47) { num2--; } readOnlySpan.CopyTo(array.AsSpan(num2)); array[num2 + readOnlySpan.Length] = 0; count = num2 + readOnlySpan.Length; } else { array = ArrayPool.Shared.Rent(defaultTemplate.Length + 1); array.AsSpan().Clear(); defaultTemplate.CopyTo(array); count = defaultTemplate.Length; } nint num3 = Mkstemp(array); string text = Encoding.UTF8.GetString(array, 0, count); ArrayPool.Shared.Return(array); if ((int)PlatformDetection.Runtime == 3 && (int)PlatformDetection.Corelib != 1) { CloseFileDescriptor(num3); using FileStream destination = new FileStream(text, FileMode.Create, FileAccess.Write); sourceStream.CopyTo(destination); } else { try { using FileStream destination2 = new FileStream(num3, FileAccess.Write); sourceStream.CopyTo(destination2); } finally { CloseFileDescriptor(num3); } } return text; } } internal static class SystemVABI { private static readonly ConditionalWeakTable> SysVIsMemoryCache = new ConditionalWeakTable>(); private static readonly StrongBox SBTrue = new StrongBox(value: true); private static readonly StrongBox SBFalse = new StrongBox(value: false); public static TypeClassification ClassifyAMD64(Type type, bool isReturn) { int managedSize = Extensions.GetManagedSize(type); if (managedSize > 16) { if (managedSize > 32) { if (!isReturn) { return TypeClassification.OnStack; } return TypeClassification.ByReference; } if (true) { if (!isReturn) { return TypeClassification.OnStack; } return TypeClassification.ByReference; } } return TypeClassification.InRegister; } public static TypeClassification ClassifyARM64(Type type, bool isReturn) { int managedSize = Extensions.GetManagedSize(type); if (managedSize > 16) { if (managedSize > 32) { if (!isReturn) { return TypeClassification.OnStack; } return TypeClassification.ByReference; } if (AnyFieldsNotFloat(type)) { if (!isReturn) { return TypeClassification.OnStack; } return TypeClassification.ByReference; } } return TypeClassification.InRegister; } private static bool AnyFieldsNotFloat(Type type) { return SysVIsMemoryCache.GetValue(type, delegate(Type type2) { FieldInfo[] fields = type2.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); for (int i = 0; i < fields.Length; i++) { Type fieldType = fields[i].FieldType; if ((object)fieldType != null && !fieldType.IsPrimitive && fieldType.IsValueType && AnyFieldsNotFloat(fieldType)) { return SBTrue; } TypeCode typeCode = Type.GetTypeCode(fieldType); if (typeCode != TypeCode.Single && typeCode != TypeCode.Double) { return SBTrue; } } return SBFalse; }).Value; } } internal sealed class WindowsSystem : ISystem, IControlFlowGuard { private sealed class PageAllocator : QueryingMemoryPageAllocatorBase { public override uint PageSize { get; } public unsafe PageAllocator() { Windows.SYSTEM_INFO sYSTEM_INFO = default(Windows.SYSTEM_INFO); Windows.GetSystemInfo(&sYSTEM_INFO); PageSize = sYSTEM_INFO.dwAllocationGranularity; } public unsafe override bool TryAllocatePage(nint size, bool executable, out nint allocated) { int flProtect = (executable ? 64 : 4); allocated = (nint)Windows.VirtualAlloc(null, (nuint)size, 12288u, (uint)flProtect); return allocated != IntPtr.Zero; } public unsafe override bool TryAllocatePage(nint pageAddr, nint size, bool executable, out nint allocated) { int flProtect = (executable ? 64 : 4); allocated = (nint)Windows.VirtualAlloc((void*)pageAddr, (nuint)size, 12288u, (uint)flProtect); return allocated != IntPtr.Zero; } public unsafe override bool TryFreePage(nint pageAddr, [NotNullWhen(false)] out string? errorMsg) { if (!Windows.VirtualFree((void*)pageAddr, 0u, 32768u)) { errorMsg = new Win32Exception((int)Windows.GetLastError()).Message; return false; } errorMsg = null; return true; } public unsafe override bool TryQueryPage(nint pageAddr, out bool isFree, out nint allocBase, out nint allocSize) { Windows.MEMORY_BASIC_INFORMATION mEMORY_BASIC_INFORMATION = default(Windows.MEMORY_BASIC_INFORMATION); if (Windows.VirtualQuery((void*)pageAddr, &mEMORY_BASIC_INFORMATION, (nuint)sizeof(Windows.MEMORY_BASIC_INFORMATION)) != 0) { isFree = mEMORY_BASIC_INFORMATION.State == 65536; allocBase = (nint)(isFree ? mEMORY_BASIC_INFORMATION.BaseAddress : mEMORY_BASIC_INFORMATION.AllocationBase); allocSize = pageAddr + (nint)mEMORY_BASIC_INFORMATION.RegionSize - allocBase; return true; } isFree = false; allocBase = IntPtr.Zero; allocSize = 0; return false; } } private static readonly MethodInfo? GetModulesInternalMethod = typeof(Process).GetMethod("GetModules_internal", BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[1] { typeof(nint) }, null); public OSKind Target => (OSKind)2; public SystemFeature Features => SystemFeature.RWXPages; public INativeExceptionHelper? NativeExceptionHelper => null; public Abi? DefaultAbi { get; } public IMemoryAllocator MemoryAllocator { get; } = new QueryingPagedMemoryAllocator(new PageAllocator()); bool IControlFlowGuard.IsSupported => Windows.HasSetProcessValidCallTargets; int IControlFlowGuard.TargetAlignmentRequirement => 16; private static TypeClassification ClassifyX64(Type type, bool isReturn) { int managedSize = Extensions.GetManagedSize(type); if (((uint)(managedSize - 1) <= 1u || managedSize == 4 || managedSize == 8) ? true : false) { return TypeClassification.InRegister; } return TypeClassification.ByReference; } private static TypeClassification ClassifyX86(Type type, bool isReturn) { if (!isReturn) { return TypeClassification.OnStack; } int managedSize = Extensions.GetManagedSize(type); if (((uint)(managedSize - 1) <= 1u || managedSize == 4) ? true : false) { return TypeClassification.InRegister; } return TypeClassification.ByReference; } public WindowsSystem() { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Invalid comparison between Unknown and I4 //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Invalid comparison between Unknown and I4 //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Invalid comparison between Unknown and I4 if ((int)PlatformDetection.Architecture == 3) { DefaultAbi = new Abi(new SpecialArgumentKind[3] { SpecialArgumentKind.ReturnBuffer, SpecialArgumentKind.ThisPointer, SpecialArgumentKind.UserArguments }, ClassifyX64, ReturnsReturnBuffer: true); } else if ((int)PlatformDetection.Architecture == 2) { DefaultAbi = new Abi(new SpecialArgumentKind[3] { SpecialArgumentKind.ThisPointer, SpecialArgumentKind.ReturnBuffer, SpecialArgumentKind.UserArguments }, ClassifyX86, ReturnsReturnBuffer: true); } else if ((int)PlatformDetection.Architecture == 5) { DefaultAbi = new Abi(new SpecialArgumentKind[2] { SpecialArgumentKind.ThisPointer, SpecialArgumentKind.UserArguments }, SystemVABI.ClassifyARM64, ReturnsReturnBuffer: false); } } public unsafe void PatchData(PatchTargetKind patchKind, nint patchTarget, ReadOnlySpan data, Span backup) { if (patchKind == PatchTargetKind.Executable) { ProtectRWX(patchTarget, (nuint)data.Length); } else { ProtectRW(patchTarget, (nuint)data.Length); } Span destination = new Span((void*)patchTarget, data.Length); destination.TryCopyTo(backup); data.CopyTo(destination); if (patchKind == PatchTargetKind.Executable) { FlushInstructionCache(patchTarget, (nuint)data.Length); } } private unsafe static void ProtectRW(nint addr, nuint size) { uint num = default(uint); if (!Windows.VirtualProtect((void*)addr, size, 4u, &num)) { throw LogAllSections(Windows.GetLastError(), addr, size, "ProtectRW"); } } private unsafe static void ProtectRWX(nint addr, nuint size) { uint num = default(uint); if (!Windows.VirtualProtect((void*)addr, size, 64u, &num)) { throw LogAllSections(Windows.GetLastError(), addr, size, "ProtectRWX"); } } private unsafe static void FlushInstructionCache(nint addr, nuint size) { if (!Windows.FlushInstructionCache(Windows.GetCurrentProcess(), (void*)addr, size)) { throw LogAllSections(Windows.GetLastError(), addr, size, "FlushInstructionCache"); } } public IEnumerable EnumerateLoadedModules() { Process currentProcess = Process.GetCurrentProcess(); IEnumerable enumerable = ((!(GetModulesInternalMethod == null)) ? ((object[])GetModulesInternalMethod.Invoke(currentProcess, new object[1] { currentProcess.Handle })).Cast() : ((IEnumerable)currentProcess.Modules).Cast()); foreach (ProcessModule item in enumerable) { yield return new LoadedModule((ulong)item.BaseAddress, item.FileName, (ulong)item.ModuleMemorySize); } } public IEnumerable EnumerateLoadedModuleFiles() { foreach (LoadedModule item in EnumerateLoadedModules()) { yield return item.FileName; } } public unsafe nint GetSizeOfReadableMemory(nint start, nint guess) { nint num = 0; Windows.MEMORY_BASIC_INFORMATION mEMORY_BASIC_INFORMATION = default(Windows.MEMORY_BASIC_INFORMATION); do { bool isEnabled; if (Windows.VirtualQuery((void*)start, &mEMORY_BASIC_INFORMATION, (nuint)sizeof(Windows.MEMORY_BASIC_INFORMATION)) == 0) { uint lastError = Windows.GetLastError(); MMDbgLog.DebugLogWarningStringHandler message = new MMDbgLog.DebugLogWarningStringHandler(22, 2, out isEnabled); if (isEnabled) { message.AppendLiteral("VirtualQuery failed: "); message.AppendFormatted(lastError); message.AppendLiteral(" "); message.AppendFormatted(new Win32Exception((int)lastError).Message); } MMDbgLog.Warning(ref message); return 0; } MMDbgLog.DebugLogSpamStringHandler message2 = new MMDbgLog.DebugLogSpamStringHandler(56, 4, out isEnabled); if (isEnabled) { message2.AppendLiteral("VirtualQuery(0x"); message2.AppendFormatted(start, "x16"); message2.AppendLiteral(") == { Protect = "); message2.AppendFormatted(mEMORY_BASIC_INFORMATION.Protect, "x"); message2.AppendLiteral(", BaseAddr = "); message2.AppendFormatted((nuint)mEMORY_BASIC_INFORMATION.BaseAddress, "x16"); message2.AppendLiteral(", Size = "); message2.AppendFormatted(mEMORY_BASIC_INFORMATION.RegionSize, "x4"); message2.AppendLiteral(" }"); } MMDbgLog.Spam(ref message2); if ((mEMORY_BASIC_INFORMATION.Protect & 0x66) == 0) { return num; } nint num2 = (nint)((byte*)mEMORY_BASIC_INFORMATION.BaseAddress + mEMORY_BASIC_INFORMATION.RegionSize); num += num2 - start; start = num2; } while (num < guess); return num; } private unsafe static Exception LogAllSections(uint error, nint src, nuint size, [CallerMemberName] string from = "") { Exception ex = new Win32Exception((int)error); if (!MMDbgLog.IsWritingLog) { return ex; } bool isEnabled; MMDbgLog.DebugLogErrorStringHandler message = new MMDbgLog.DebugLogErrorStringHandler(47, 3, out isEnabled); if (isEnabled) { message.AppendFormatted(from); message.AppendLiteral(" failed for 0x"); message.AppendFormatted(src, "X16"); message.AppendLiteral(" + "); message.AppendFormatted(size); message.AppendLiteral(" - logging all memory sections"); } MMDbgLog.Error(ref message); MMDbgLog.DebugLogErrorStringHandler message2 = new MMDbgLog.DebugLogErrorStringHandler(8, 1, out isEnabled); if (isEnabled) { message2.AppendLiteral("reason: "); message2.AppendFormatted(ex.Message); } MMDbgLog.Error(ref message2); try { nint num = 65536; int num2 = 0; Windows.MEMORY_BASIC_INFORMATION mEMORY_BASIC_INFORMATION = default(Windows.MEMORY_BASIC_INFORMATION); while (Windows.VirtualQuery((void*)num, &mEMORY_BASIC_INFORMATION, (nuint)sizeof(Windows.MEMORY_BASIC_INFORMATION)) != 0) { nuint num3 = (nuint)src + size; void* baseAddress = mEMORY_BASIC_INFORMATION.BaseAddress; nuint num4 = (nuint)((byte*)baseAddress + mEMORY_BASIC_INFORMATION.RegionSize); bool flag = (nuint)baseAddress <= num3 && (nuint)src <= num4; MMDbgLog.DebugLogTraceStringHandler message3 = new MMDbgLog.DebugLogTraceStringHandler(2, 2, out isEnabled); if (isEnabled) { message3.AppendFormatted(flag ? "*" : "-"); message3.AppendLiteral(" #"); message3.AppendFormatted(num2++); } MMDbgLog.Trace(ref message3); MMDbgLog.DebugLogTraceStringHandler message4 = new MMDbgLog.DebugLogTraceStringHandler(8, 1, out isEnabled); if (isEnabled) { message4.AppendLiteral("addr: 0x"); message4.AppendFormatted((nuint)mEMORY_BASIC_INFORMATION.BaseAddress, "X16"); } MMDbgLog.Trace(ref message4); MMDbgLog.DebugLogTraceStringHandler message5 = new MMDbgLog.DebugLogTraceStringHandler(8, 1, out isEnabled); if (isEnabled) { message5.AppendLiteral("size: 0x"); message5.AppendFormatted(mEMORY_BASIC_INFORMATION.RegionSize, "X16"); } MMDbgLog.Trace(ref message5); MMDbgLog.DebugLogTraceStringHandler message6 = new MMDbgLog.DebugLogTraceStringHandler(9, 1, out isEnabled); if (isEnabled) { message6.AppendLiteral("aaddr: 0x"); message6.AppendFormatted((nuint)mEMORY_BASIC_INFORMATION.AllocationBase, "X16"); } MMDbgLog.Trace(ref message6); MMDbgLog.DebugLogTraceStringHandler message7 = new MMDbgLog.DebugLogTraceStringHandler(7, 1, out isEnabled); if (isEnabled) { message7.AppendLiteral("state: "); message7.AppendFormatted(mEMORY_BASIC_INFORMATION.State); } MMDbgLog.Trace(ref message7); MMDbgLog.DebugLogTraceStringHandler message8 = new MMDbgLog.DebugLogTraceStringHandler(6, 1, out isEnabled); if (isEnabled) { message8.AppendLiteral("type: "); message8.AppendFormatted(mEMORY_BASIC_INFORMATION.Type); } MMDbgLog.Trace(ref message8); MMDbgLog.DebugLogTraceStringHandler message9 = new MMDbgLog.DebugLogTraceStringHandler(9, 1, out isEnabled); if (isEnabled) { message9.AppendLiteral("protect: "); message9.AppendFormatted(mEMORY_BASIC_INFORMATION.Protect); } MMDbgLog.Trace(ref message9); MMDbgLog.DebugLogTraceStringHandler message10 = new MMDbgLog.DebugLogTraceStringHandler(10, 1, out isEnabled); if (isEnabled) { message10.AppendLiteral("aprotect: "); message10.AppendFormatted(mEMORY_BASIC_INFORMATION.AllocationProtect); } MMDbgLog.Trace(ref message10); try { nint num5 = num; num = (nint)((long)mEMORY_BASIC_INFORMATION.BaseAddress + (long)mEMORY_BASIC_INFORMATION.RegionSize); if ((ulong)num <= (ulong)num5) { break; } } catch (OverflowException value) { MMDbgLog.DebugLogErrorStringHandler message11 = new MMDbgLog.DebugLogErrorStringHandler(9, 1, out isEnabled); if (isEnabled) { message11.AppendLiteral("overflow "); message11.AppendFormatted(value); } MMDbgLog.Error(ref message11); break; } } } catch { throw ex; } return ex; } unsafe void IControlFlowGuard.RegisterValidIndirectCallTargets(void* memoryRegionStart, nint memoryRegionLength, ReadOnlySpan validTargetsInMemoryRegion) { Windows.CFG_CALL_TARGET_INFO[] array = ArrayPool.Shared.Rent(validTargetsInMemoryRegion.Length); for (int i = 0; i < validTargetsInMemoryRegion.Length; i++) { nint offset = validTargetsInMemoryRegion[i]; array[i] = new Windows.CFG_CALL_TARGET_INFO { Offset = (nuint)offset, Flags = 9u }; } fixed (Windows.CFG_CALL_TARGET_INFO* offsetInformation = array) { Windows.TrySetProcessValidCallTargets(memoryRegionStart, (nuint)memoryRegionLength, (uint)validTargetsInMemoryRegion.Length, offsetInformation); } ArrayPool.Shared.Return(array); } public nint GetNativeJitHookConfig(int runtimeMajMin) { throw new NotImplementedException(); } } } namespace MonoMod.Core.Platforms.Runtimes { internal class Core100Runtime : Core90Runtime { private static readonly Guid JitVersionGuid = new Guid(2056043606u, 40473, 17185, 128, 185, 160, 210, 197, 120, 201, 69); protected override Guid ExpectedJitVersion => JitVersionGuid; protected override int VtableIndexICorJitInfoAllocMem => 160; protected override int ICorJitInfoFullVtableCount => 176; public Core100Runtime(ISystem system, IArchitecture arch) : base(system, arch) { } protected unsafe override void MakeAssemblySystemAssembly(Assembly assembly) { nint num = (nint)Core21Runtime.RuntimeAssemblyPtrField.GetValue(assembly); int num2 = IntPtr.Size + IntPtr.Size + IntPtr.Size; nint num3 = *(nint*)(num + num2); int num4 = IntPtr.Size + (FxCoreBaseRuntime.IsDebugClr ? (IntPtr.Size + 4 + 4 + 4 + IntPtr.Size + 4) : 0) + IntPtr.Size + 4 + ((IntPtr.Size == 8) ? 4 : 0) + IntPtr.Size + IntPtr.Size + IntPtr.Size + 4; if (FxCoreBaseRuntime.IsDebugClr && IntPtr.Size == 8) { num4 += 8; } *(sbyte*)(num3 + num4) = 1; } protected override MethodInfo MakeCreateRuntimeMethodInfoStub(Type methodHandleInternal) { //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Expected O, but got Unknown //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) ConstructorInfo constructorInfo = methodHandleInternal.GetConstructors((BindingFlags)(-1))[0]; Type type = typeof(RuntimeMethodHandle).Assembly.GetType("System.RuntimeMethodInfoStub"); ConstructorInfo constructor = type.GetConstructor(new Type[2] { methodHandleInternal, typeof(object) }); DynamicMethodDefinition val = new DynamicMethodDefinition("new RuntimeMethodInfoStub", type, new Type[2] { typeof(nint), typeof(object) }); try { ILProcessor iLProcessor = val.GetILProcessor(); iLProcessor.Emit(OpCodes.Ldarg_0); Extensions.Emit(iLProcessor, OpCodes.Newobj, (MethodBase)constructorInfo); iLProcessor.Emit(OpCodes.Ldarg_1); Extensions.Emit(iLProcessor, OpCodes.Newobj, (MethodBase)constructor); iLProcessor.Emit(OpCodes.Ret); return val.Generate(); } finally { ((IDisposable)val)?.Dispose(); } } protected override MethodInfo GetOrCreateGetTypeFromHandleUnsafe() { MethodInfo? method = typeof(RuntimeTypeHandle).GetMethod("GetRuntimeTypeFromHandleMaybeNull", (BindingFlags)(-1), null, new Type[1] { typeof(nint) }, null); Helpers.Assert((object)method != null, (string)null, "method is not null"); return method; } } internal class Core21Runtime : CoreBaseRuntime { private sealed class JitHookDelegateHolder { public readonly Core21Runtime Runtime; public readonly INativeExceptionHelper? NativeExceptionHelper; public readonly GetExceptionSlot? GetNativeExceptionSlot; public readonly JitHookHelpersHolder JitHookHelpers; public readonly CoreCLR.InvokeCompileMethodPtr InvokeCompileMethodPtr; public readonly nint CompileMethodPtr; [ThreadStatic] private static int hookEntrancy; public unsafe JitHookDelegateHolder(Core21Runtime runtime, CoreCLR.InvokeCompileMethodPtr icmp, nint compileMethod) { Runtime = runtime; NativeExceptionHelper = runtime.NativeExceptionHelper; JitHookHelpers = runtime.JitHookHelpers; InvokeCompileMethodPtr = icmp; CompileMethodPtr = compileMethod; CoreCLR.V21.CORINFO_METHOD_INFO cORINFO_METHOD_INFO = default(CoreCLR.V21.CORINFO_METHOD_INFO); byte* ptr = default(byte*); uint num = default(uint); icmp.InvokeCompileMethod(IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, &cORINFO_METHOD_INFO, 0u, &ptr, &num); MarshalEx.SetLastPInvokeError(MarshalEx.GetLastPInvokeError()); INativeExceptionHelper nativeExceptionHelper = NativeExceptionHelper; if (nativeExceptionHelper != null) { GetNativeExceptionSlot = nativeExceptionHelper.GetExceptionSlot; GetNativeExceptionSlot(); } _ = hookEntrancy; hookEntrancy = 0; } public unsafe CoreCLR.CorJitResult CompileMethodHook(nint jit, nint corJitInfo, CoreCLR.V21.CORINFO_METHOD_INFO* methodInfo, uint flags, byte** pNativeEntry, uint* pNativeSizeOfCode) { if (jit == IntPtr.Zero) { return CoreCLR.CorJitResult.CORJIT_OK; } *pNativeEntry = null; *pNativeSizeOfCode = 0u; int lastPInvokeError = MarshalEx.GetLastPInvokeError(); nint num = 0; GetExceptionSlot getNativeExceptionSlot = GetNativeExceptionSlot; nint* ptr = ((getNativeExceptionSlot != null) ? getNativeExceptionSlot() : null); hookEntrancy++; try { CoreCLR.CorJitResult result = InvokeCompileMethodPtr.InvokeCompileMethod(CompileMethodPtr, jit, corJitInfo, methodInfo, flags, pNativeEntry, pNativeSizeOfCode); if (ptr != null && (num = *ptr) != 0) { bool isEnabled; MMDbgLog.DebugLogWarningStringHandler message = new MMDbgLog.DebugLogWarningStringHandler(59, 1, out isEnabled); if (isEnabled) { message.AppendLiteral("Native exception caught in JIT by exception helper (ex: 0x"); message.AppendFormatted(num, "x16"); message.AppendLiteral(")"); } MMDbgLog.Warning(ref message); return result; } if (hookEntrancy == 1) { try { RuntimeTypeHandle[] array = null; RuntimeTypeHandle[] array2 = null; if (methodInfo->args.sigInst.classInst != null) { array = new RuntimeTypeHandle[methodInfo->args.sigInst.classInstCount]; for (int i = 0; i < array.Length; i++) { array[i] = JitHookHelpers.GetTypeFromNativeHandle(methodInfo->args.sigInst.classInst[i]).TypeHandle; } } if (methodInfo->args.sigInst.methInst != null) { array2 = new RuntimeTypeHandle[methodInfo->args.sigInst.methInstCount]; for (int j = 0; j < array2.Length; j++) { array2[j] = JitHookHelpers.GetTypeFromNativeHandle(methodInfo->args.sigInst.methInst[j]).TypeHandle; } } RuntimeTypeHandle typeHandle = JitHookHelpers.GetDeclaringTypeOfMethodHandle(methodInfo->ftn).TypeHandle; RuntimeMethodHandle methodHandle = JitHookHelpers.CreateHandleForHandlePointer(methodInfo->ftn); Runtime.OnMethodCompiledCore(typeHandle, methodHandle, array, array2, (nint)(*pNativeEntry), (nint)(*pNativeEntry), *pNativeSizeOfCode); } catch { } } return result; } finally { hookEntrancy--; if (ptr != null) { *ptr = num; } MarshalEx.SetLastPInvokeError(lastPInvokeError); } } } protected sealed class JitHookHelpersHolder { public delegate object MethodHandle_GetLoaderAllocatorD(nint methodHandle); public delegate object CreateRuntimeMethodInfoStubD(nint methodHandle, object loaderAllocator); public delegate RuntimeMethodHandle CreateRuntimeMethodHandleD(object runtimeMethodInfo); public delegate Type GetDeclaringTypeOfMethodHandleD(nint methodHandle); public delegate Type GetTypeFromNativeHandleD(nint handle); public readonly MethodHandle_GetLoaderAllocatorD MethodHandle_GetLoaderAllocator; public readonly CreateRuntimeMethodInfoStubD CreateRuntimeMethodInfoStub; public readonly CreateRuntimeMethodHandleD CreateRuntimeMethodHandle; public readonly GetDeclaringTypeOfMethodHandleD GetDeclaringTypeOfMethodHandle; public readonly GetTypeFromNativeHandleD GetTypeFromNativeHandle; public RuntimeMethodHandle CreateHandleForHandlePointer(nint handle) { return CreateRuntimeMethodHandle(CreateRuntimeMethodInfoStub(handle, MethodHandle_GetLoaderAllocator(handle))); } public JitHookHelpersHolder(Core21Runtime runtime) { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Expected O, but got Unknown //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Expected O, but got Unknown //IL_01cd: Unknown result type (might be due to invalid IL or missing references) //IL_01d4: Expected O, but got Unknown MethodInfo method = typeof(RuntimeMethodHandle).GetMethod("GetLoaderAllocator", BindingFlags.Static | BindingFlags.NonPublic); DynamicMethodDefinition val = new DynamicMethodDefinition("MethodHandle_GetLoaderAllocator", typeof(object), new Type[1] { typeof(nint) }); MethodInfo methodInfo; try { ILGenerator iLGenerator = val.GetILGenerator(); Type parameterType = method.GetParameters().First().ParameterType; iLGenerator.Emit(OpCodes.Ldarga_S, 0); iLGenerator.Emit(OpCodes.Ldobj, parameterType); iLGenerator.Emit(OpCodes.Call, method); iLGenerator.Emit(OpCodes.Ret); methodInfo = val.Generate(); } finally { ((IDisposable)val)?.Dispose(); } MethodHandle_GetLoaderAllocator = methodInfo.CreateDelegate(); MethodInfo orCreateGetTypeFromHandleUnsafe = runtime.GetOrCreateGetTypeFromHandleUnsafe(); GetTypeFromNativeHandle = orCreateGetTypeFromHandleUnsafe.CreateDelegate(); Type type = typeof(RuntimeMethodHandle).Assembly.GetType("System.RuntimeMethodHandleInternal"); MethodInfo method2 = typeof(RuntimeMethodHandle).GetMethod("GetDeclaringType", BindingFlags.Static | BindingFlags.NonPublic, null, new Type[1] { type }, null); DynamicMethodDefinition val2 = new DynamicMethodDefinition("GetDeclaringTypeOfMethodHandle", typeof(Type), new Type[1] { typeof(nint) }); MethodInfo methodInfo2; try { ILGenerator iLGenerator2 = val2.GetILGenerator(); iLGenerator2.Emit(OpCodes.Ldarga_S, 0); iLGenerator2.Emit(OpCodes.Ldobj, type); iLGenerator2.Emit(OpCodes.Call, method2); iLGenerator2.Emit(OpCodes.Ret); methodInfo2 = val2.Generate(); } finally { ((IDisposable)val2)?.Dispose(); } GetDeclaringTypeOfMethodHandle = methodInfo2.CreateDelegate(); CreateRuntimeMethodInfoStub = runtime.MakeCreateRuntimeMethodInfoStub(type).CreateDelegate(); ConstructorInfo con = typeof(RuntimeMethodHandle).GetConstructors(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).First(); DynamicMethodDefinition val3 = new DynamicMethodDefinition("new RuntimeMethodHandle", typeof(RuntimeMethodHandle), new Type[1] { typeof(object) }); MethodInfo methodInfo3; try { ILGenerator iLGenerator3 = val3.GetILGenerator(); iLGenerator3.Emit(OpCodes.Ldarg_0); iLGenerator3.Emit(OpCodes.Newobj, con); iLGenerator3.Emit(OpCodes.Ret); methodInfo3 = val3.Generate(); } finally { ((IDisposable)val3)?.Dispose(); } CreateRuntimeMethodHandle = methodInfo3.CreateDelegate(); } } private static readonly Func createJitHookHelpersFunc = CreateJitHookHelpers; private readonly object sync = new object(); private JitHookHelpersHolder? lazyJitHookHelpers; private static readonly Guid JitVersionGuid = new Guid(195102408u, 33184, 16511, 153, 161, 146, 132, 72, 193, 235, 98); private Delegate? ourCompileMethod; private IDisposable? n2mHookHelper; private IDisposable? m2nHookHelper; private protected static readonly FieldInfo RuntimeAssemblyPtrField = Type.GetType("System.Reflection.RuntimeAssembly").GetField("m_assembly", BindingFlags.Instance | BindingFlags.NonPublic); public override RuntimeFeature Features => base.Features | RuntimeFeature.CompileMethodHook; protected JitHookHelpersHolder JitHookHelpers => Helpers.GetOrInitWithLock(ref lazyJitHookHelpers, sync, createJitHookHelpersFunc, this); protected virtual Guid ExpectedJitVersion => JitVersionGuid; protected virtual int VtableIndexICorJitCompilerGetVersionGuid => 4; protected virtual int VtableIndexICorJitCompilerCompileMethod => 0; protected virtual CoreCLR.InvokeCompileMethodPtr InvokeCompileMethodPtr => CoreCLR.V21.InvokeCompileMethodPtr; public Core21Runtime(ISystem system) : base(system) { } private static JitHookHelpersHolder CreateJitHookHelpers(Core21Runtime self) { return new JitHookHelpersHolder(self); } protected virtual Delegate CastCompileHookToRealType(Delegate del) { return Extensions.CastDelegate(del); } protected unsafe static nint* GetVTableEntry(nint @object, int index) { return (nint*)((nint)(*(IntPtr*)@object) + (nint)index * (nint)sizeof(nint)); } protected unsafe static nint ReadObjectVTable(nint @object, int index) { return *GetVTableEntry(@object, index); } protected unsafe void CheckVersionGuid(nint jit) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) Guid guid = default(Guid); ((delegate* unmanaged[Thiscall])ReadObjectVTable(jit, VtableIndexICorJitCompilerGetVersionGuid))(jit, &guid); bool flag = guid == ExpectedJitVersion; bool flag2 = default(bool); AssertionInterpolatedStringHandler val = new AssertionInterpolatedStringHandler(66, 2, flag, ref flag2); if (flag2) { ((AssertionInterpolatedStringHandler)(ref val)).AppendLiteral("JIT version does not match expected JIT version! "); ((AssertionInterpolatedStringHandler)(ref val)).AppendLiteral("expected: "); ((AssertionInterpolatedStringHandler)(ref val)).AppendFormatted(ExpectedJitVersion); ((AssertionInterpolatedStringHandler)(ref val)).AppendLiteral(", got: "); ((AssertionInterpolatedStringHandler)(ref val)).AppendFormatted(guid); } Helpers.Assert(flag, ref val, "guid == ExpectedJitVersion"); } protected unsafe override void InstallManagedJitHook(nint jit) { CheckVersionGuid(jit); nint* vTableEntry = GetVTableEntry(jit, VtableIndexICorJitCompilerCompileMethod); nint compileMethod = EHManagedToNative(*vTableEntry, out m2nHookHelper); nint value = EHNativeToManaged(Marshal.GetFunctionPointerForDelegate(ourCompileMethod = CastCompileHookToRealType(CreateCompileMethodDelegate(compileMethod))), out n2mHookHelper); InvokeCompileMethodToPrepare(value); Span span = stackalloc byte[sizeof(nint)]; MemoryMarshal.Write(span, in value); base.System.PatchData(PatchTargetKind.ReadOnly, (nint)vTableEntry, span, default(Span)); } protected unsafe virtual void InvokeCompileMethodToPrepare(nint method) { CoreCLR.V21.CORINFO_METHOD_INFO cORINFO_METHOD_INFO = default(CoreCLR.V21.CORINFO_METHOD_INFO); byte* ptr = default(byte*); uint num = default(uint); InvokeCompileMethodPtr.InvokeCompileMethod(method, IntPtr.Zero, IntPtr.Zero, &cORINFO_METHOD_INFO, 0u, &ptr, &num); } protected virtual Delegate CreateCompileMethodDelegate(nint compileMethod) { return new <>f__AnonymousDelegate0(new JitHookDelegateHolder(this, InvokeCompileMethodPtr, compileMethod).CompileMethodHook); } protected virtual MethodInfo MakeCreateRuntimeMethodInfoStub(Type methodHandleInternal) { //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Expected O, but got Unknown Type[] array = new Type[2] { typeof(nint), typeof(object) }; Type type = typeof(RuntimeMethodHandle).Assembly.GetType("System.RuntimeMethodInfoStub"); ConstructorInfo constructor = type.GetConstructor(array); DynamicMethodDefinition val = new DynamicMethodDefinition("new RuntimeMethodInfoStub", type, array); try { ILGenerator iLGenerator = val.GetILGenerator(); iLGenerator.Emit(OpCodes.Ldarg_0); iLGenerator.Emit(OpCodes.Ldarg_1); iLGenerator.Emit(OpCodes.Newobj, constructor); iLGenerator.Emit(OpCodes.Ret); return val.Generate(); } finally { ((IDisposable)val)?.Dispose(); } } protected virtual MethodInfo GetOrCreateGetTypeFromHandleUnsafe() { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Expected O, but got Unknown //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Expected O, but got Unknown //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Expected O, but got Unknown //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Expected O, but got Unknown MethodInfo method = typeof(Type).GetMethod("GetTypeFromHandleUnsafe", (BindingFlags)(-1)); if ((object)method != null) { return method; } ModuleDefinition val = ModuleDefinition.CreateModule("MonoMod.Core.Platforms.Runtimes.Core21Runtime+Helpers", new ModuleParameters { Kind = (ModuleKind)0 }); Assembly assembly; try { TypeDefinition val2 = new TypeDefinition("System", "Type", (TypeAttributes)128) { BaseType = val.TypeSystem.Object }; val.Types.Add(val2); MethodDefinition val3 = new MethodDefinition("GetTypeFromHandleUnsafe", (MethodAttributes)22, val.ImportReference(typeof(Type))) { IsInternalCall = true }; ((MethodReference)val3).Parameters.Add(new ParameterDefinition(val.ImportReference(typeof(nint)))); val2.Methods.Add(val3); assembly = ReflectionHelper.Load(val); } finally { ((IDisposable)val)?.Dispose(); } MakeAssemblySystemAssembly(assembly); return assembly.GetType("System.Type").GetMethod("GetTypeFromHandleUnsafe", (BindingFlags)(-1)); } protected unsafe virtual void MakeAssemblySystemAssembly(Assembly assembly) { nint num = (nint)RuntimeAssemblyPtrField.GetValue(assembly); int num2 = IntPtr.Size + IntPtr.Size + IntPtr.Size + IntPtr.Size + IntPtr.Size + 4 + IntPtr.Size + IntPtr.Size + 4 + 4 + IntPtr.Size + IntPtr.Size + 4 + 4 + IntPtr.Size; if (IntPtr.Size == 8) { num2 += 4; } nint num3 = *(nint*)(num + num2); int num4 = IntPtr.Size + IntPtr.Size + IntPtr.Size + IntPtr.Size; nint num5 = *(nint*)(num3 + num4); int num6 = IntPtr.Size + (FxCoreBaseRuntime.IsDebugClr ? (IntPtr.Size + 4 + 4 + 4 + IntPtr.Size + 4) : 0) + IntPtr.Size + IntPtr.Size + 4 + 4 + IntPtr.Size + IntPtr.Size + IntPtr.Size + IntPtr.Size + 4; if (FxCoreBaseRuntime.IsDebugClr && IntPtr.Size == 8) { num6 += 8; } int* ptr = (int*)(num5 + num6); *ptr |= 1; } } internal class Core30Runtime : Core21Runtime { private static readonly Guid JitVersionGuid = new Guid(3590962897u, 30769, 18940, 189, 73, 182, 240, 84, 221, 77, 70); protected override Guid ExpectedJitVersion => JitVersionGuid; protected override CoreCLR.InvokeCompileMethodPtr InvokeCompileMethodPtr => CoreCLR.V21.InvokeCompileMethodPtr; public Core30Runtime(ISystem system) : base(system) { } protected override Delegate CastCompileHookToRealType(Delegate del) { return Extensions.CastDelegate(del); } } internal class Core31Runtime : Core30Runtime { protected override CoreCLR.InvokeCompileMethodPtr InvokeCompileMethodPtr => CoreCLR.V21.InvokeCompileMethodPtr; public Core31Runtime(ISystem system) : base(system) { } protected override Delegate CastCompileHookToRealType(Delegate del) { return Extensions.CastDelegate(del); } } internal class Core50Runtime : Core31Runtime { private static readonly Guid JitVersionGuid = new Guid(2783888292u, 16758, 17319, 140, 43, 160, 91, 85, 29, 79, 73); protected override Guid ExpectedJitVersion => JitVersionGuid; protected override int VtableIndexICorJitCompilerGetVersionGuid => 2; protected override CoreCLR.InvokeCompileMethodPtr InvokeCompileMethodPtr => CoreCLR.V21.InvokeCompileMethodPtr; public Core50Runtime(ISystem system) : base(system) { } protected override Delegate CastCompileHookToRealType(Delegate del) { return Extensions.CastDelegate(del); } } internal class Core60Runtime : Core50Runtime { [StructLayout(LayoutKind.Sequential, Pack = 1)] protected struct ICorJitInfoWrapper { public nint Vtbl; public unsafe nint** Wrapped; public const int HotCodeRW = 0; public const int ColdCodeRW = 1; private const int DataQWords = 4; private unsafe fixed ulong data[4]; public unsafe ref nint this[int index] => ref Unsafe.Add(ref Unsafe.As(ref data[0]), index); } private sealed class JitHookDelegateHolder { public readonly Core60Runtime Runtime; public readonly INativeExceptionHelper? NativeExceptionHelper; public readonly GetExceptionSlot? GetNativeExceptionSlot; public readonly JitHookHelpersHolder JitHookHelpers; public readonly CoreCLR.InvokeCompileMethodPtr InvokeCompileMethodPtr; public readonly nint CompileMethodPtr; public readonly ThreadLocal iCorJitInfoWrapper = new ThreadLocal(); public readonly ReadOnlyMemory iCorJitInfoWrapperAllocs; public readonly nint iCorJitInfoWrapperVtbl; [ThreadStatic] private static int hookEntrancy; public unsafe JitHookDelegateHolder(Core60Runtime runtime, CoreCLR.InvokeCompileMethodPtr icmp, nint compileMethod) { Runtime = runtime; NativeExceptionHelper = runtime.NativeExceptionHelper; JitHookHelpers = runtime.JitHookHelpers; InvokeCompileMethodPtr = icmp; CompileMethodPtr = compileMethod; iCorJitInfoWrapperVtbl = Marshal.AllocHGlobal(IntPtr.Size * runtime.ICorJitInfoFullVtableCount); iCorJitInfoWrapperAllocs = Runtime.arch.CreateNativeVtableProxyStubs(iCorJitInfoWrapperVtbl, runtime.ICorJitInfoFullVtableCount); Runtime.PatchWrapperVtable((nint*)iCorJitInfoWrapperVtbl); bool isEnabled; MMDbgLog.DebugLogTraceStringHandler message = new MMDbgLog.DebugLogTraceStringHandler(42, 1, out isEnabled); if (isEnabled) { message.AppendLiteral("Allocated ICorJitInfo wrapper vtable at 0x"); message.AppendFormatted(iCorJitInfoWrapperVtbl, "x16"); } MMDbgLog.Trace(ref message); CoreCLR.V21.CORINFO_METHOD_INFO cORINFO_METHOD_INFO = default(CoreCLR.V21.CORINFO_METHOD_INFO); byte* ptr = default(byte*); uint num = default(uint); icmp.InvokeCompileMethod(IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, &cORINFO_METHOD_INFO, 0u, &ptr, &num); MarshalEx.SetLastPInvokeError(MarshalEx.GetLastPInvokeError()); INativeExceptionHelper nativeExceptionHelper = NativeExceptionHelper; if (nativeExceptionHelper != null) { GetNativeExceptionSlot = nativeExceptionHelper.GetExceptionSlot; GetNativeExceptionSlot(); } _ = hookEntrancy; hookEntrancy = 0; } public unsafe CoreCLR.CorJitResult CompileMethodHook(nint jit, nint corJitInfo, CoreCLR.V21.CORINFO_METHOD_INFO* methodInfo, uint flags, byte** nativeEntry, uint* nativeSizeOfCode) { if (jit == IntPtr.Zero) { return CoreCLR.CorJitResult.CORJIT_OK; } *nativeEntry = null; *nativeSizeOfCode = 0u; int lastPInvokeError = MarshalEx.GetLastPInvokeError(); nint num = 0; GetExceptionSlot getNativeExceptionSlot = GetNativeExceptionSlot; nint* ptr = ((getNativeExceptionSlot != null) ? getNativeExceptionSlot() : null); hookEntrancy++; try { bool isEnabled; if (hookEntrancy == 1) { try { IAllocatedMemory allocatedMemory = iCorJitInfoWrapper.Value; if (allocatedMemory == null) { AllocationRequest allocationRequest = new AllocationRequest(sizeof(ICorJitInfoWrapper)); allocationRequest.Alignment = IntPtr.Size; allocationRequest.Executable = false; AllocationRequest request = allocationRequest; if (Runtime.System.MemoryAllocator.TryAllocate(request, out IAllocatedMemory allocated)) { allocatedMemory = (iCorJitInfoWrapper.Value = allocated); } } if (allocatedMemory != null) { ICorJitInfoWrapper* baseAddress = (ICorJitInfoWrapper*)allocatedMemory.BaseAddress; baseAddress->Vtbl = iCorJitInfoWrapperVtbl; baseAddress->Wrapped = (nint**)corJitInfo; (*baseAddress)[0] = IntPtr.Zero; (*baseAddress)[1] = IntPtr.Zero; corJitInfo = (nint)baseAddress; } } catch (Exception value) { try { MMDbgLog.DebugLogErrorStringHandler message = new MMDbgLog.DebugLogErrorStringHandler(48, 1, out isEnabled); if (isEnabled) { message.AppendLiteral("Error while setting up the ICorJitInfo wrapper: "); message.AppendFormatted(value); } MMDbgLog.Error(ref message); } catch { } } } CoreCLR.CorJitResult result = InvokeCompileMethodPtr.InvokeCompileMethod(CompileMethodPtr, jit, corJitInfo, methodInfo, flags, nativeEntry, nativeSizeOfCode); if (ptr != null && (num = *ptr) != 0) { MMDbgLog.DebugLogWarningStringHandler message2 = new MMDbgLog.DebugLogWarningStringHandler(59, 1, out isEnabled); if (isEnabled) { message2.AppendLiteral("Native exception caught in JIT by exception helper (ex: 0x"); message2.AppendFormatted(num, "x16"); message2.AppendLiteral(")"); } MMDbgLog.Warning(ref message2); return result; } if (hookEntrancy == 1) { try { IAllocatedMemory value2 = iCorJitInfoWrapper.Value; if (value2 == null) { return result; } nint rwEntry = (*(ICorJitInfoWrapper*)value2.BaseAddress)[0]; Runtime.CompileMethodHookPostCommon(methodInfo, nativeEntry, nativeSizeOfCode, rwEntry); } catch { } } return result; } finally { hookEntrancy--; if (ptr != null) { *ptr = num; } MarshalEx.SetLastPInvokeError(lastPInvokeError); } } } protected struct NativeJitHookConfig { public nint compileMethod; public nint compileMethodHook; public nint compileMethodHookPost; public nint allocMem; public nint allocMemHook; } private sealed class JitHookPostDelegateHolder { public readonly Core60Runtime Runtime; public readonly JitHookHelpersHolder JitHookHelpers; public static volatile bool patchedICorJitInfo; public static readonly object patchedICorJitInfoSyncRoot = new object(); public JitHookPostDelegateHolder(Core60Runtime runtime) { Runtime = runtime; JitHookHelpers = runtime.JitHookHelpers; } public unsafe CoreCLR.CorJitResult CompileMethodHookPost(nint jit, nint corJitInfo, CoreCLR.V21.CORINFO_METHOD_INFO* methodInfo, uint flags, byte** nativeEntry, uint* nativeSizeOfCode, CoreCLR.CorJitResult res, CoreCLR.V60.AllocMemArgs* pArgs) { if (jit == IntPtr.Zero) { return res; } try { if (!patchedICorJitInfo) { lock (patchedICorJitInfoSyncRoot) { if (!patchedICorJitInfo) { nint* vTableEntry = Core21Runtime.GetVTableEntry(corJitInfo, Runtime.VtableIndexICorJitInfoAllocMem); NativeJitHookConfig* nativeJitHookConfig = Runtime.GetNativeJitHookConfig(); nativeJitHookConfig->allocMem = *vTableEntry; nint value = nativeJitHookConfig->allocMemHook; Span span = stackalloc byte[sizeof(nint)]; MemoryMarshal.Write(span, in value); Runtime.System.PatchData(PatchTargetKind.ReadOnly, (nint)vTableEntry, span, default(Span)); patchedICorJitInfo = true; } } } Runtime.CompileMethodHookPostCommon(methodInfo, nativeEntry, nativeSizeOfCode, pArgs->hotCodeBlockRW); } catch { } return res; } } private sealed class AllocMemDelegateHolder { public readonly Core60Runtime Runtime; public readonly INativeExceptionHelper? NativeExceptionHelper; public readonly GetExceptionSlot? GetNativeExceptionSlot; public readonly CoreCLR.InvokeAllocMemPtr InvokeAllocMemPtr; public readonly int ICorJitInfoAllocMemIdx; public readonly ConcurrentDictionary AllocMemExceptionHelperCache = new ConcurrentDictionary(); public unsafe AllocMemDelegateHolder(Core60Runtime runtime, CoreCLR.InvokeAllocMemPtr iamp) { Runtime = runtime; NativeExceptionHelper = runtime.NativeExceptionHelper; GetNativeExceptionSlot = NativeExceptionHelper?.GetExceptionSlot; InvokeAllocMemPtr = iamp; ICorJitInfoAllocMemIdx = Runtime.VtableIndexICorJitInfoAllocMem; iamp.InvokeAllocMem(IntPtr.Zero, IntPtr.Zero, null); } private nint GetRealInvokePtr(nint ptr) { if (NativeExceptionHelper == null) { return ptr; } IDisposable handle; return AllocMemExceptionHelperCache.GetOrAdd(ptr, (nint p) => (M2N: Runtime.EHManagedToNative(p, out handle), handle)).M2N; } public unsafe void AllocMemHook(nint thisPtr, CoreCLR.V60.AllocMemArgs* args) { if (thisPtr != IntPtr.Zero) { nint** wrapped = ((ICorJitInfoWrapper*)thisPtr)->Wrapped; InvokeAllocMemPtr.InvokeAllocMem(GetRealInvokePtr((*wrapped)[ICorJitInfoAllocMemIdx]), (nint)wrapped, args); GetExceptionSlot getNativeExceptionSlot = GetNativeExceptionSlot; if (getNativeExceptionSlot == null || *getNativeExceptionSlot() == 0) { (*(ICorJitInfoWrapper*)thisPtr)[0] = args->hotCodeBlockRW; (*(ICorJitInfoWrapper*)thisPtr)[1] = args->coldCodeBlockRW; } } } } private readonly IArchitecture arch; private static readonly Guid JitVersionGuid = new Guid(1590910040u, 34171, 18653, 168, 24, 124, 1, 54, 220, 159, 115); private Delegate? ourCompileMethodHookPost; private Delegate? allocMemDelegate; private IDisposable? n2mAllocMemHelper; protected override Guid ExpectedJitVersion => JitVersionGuid; protected virtual int VtableIndexICorJitInfoAllocMem => 156; protected virtual int ICorJitInfoFullVtableCount => 173; protected virtual CoreCLR.InvokeAllocMemPtr InvokeAllocMemPtr => CoreCLR.V60.InvokeAllocMemPtr; protected override CoreCLR.InvokeCompileMethodPtr InvokeCompileMethodPtr => CoreCLR.V60.InvokeCompileMethodPtr; public Core60Runtime(ISystem system, IArchitecture arch) : base(system) { this.arch = arch; } protected override void InstallJitHook(nint jit) { if ((base.System.Features & SystemFeature.MayUseNativeJitHooks) == 0 || !InstallNativeJitHook(jit)) { InstallManagedJitHook(jit); } } protected unsafe virtual bool InstallNativeJitHook(nint jit) { NativeJitHookConfig* nativeJitHookConfig = GetNativeJitHookConfig(); if (nativeJitHookConfig == null) { return false; } CheckVersionGuid(jit); nint* vTableEntry = Core21Runtime.GetVTableEntry(jit, VtableIndexICorJitCompilerCompileMethod); nint functionPointerForDelegate = Marshal.GetFunctionPointerForDelegate(ourCompileMethodHookPost = CastCompileMethodHookPostToRealType(CreateCompileMethodHookPostDelegate())); CoreCLR.V21.CORINFO_METHOD_INFO cORINFO_METHOD_INFO = default(CoreCLR.V21.CORINFO_METHOD_INFO); byte* ptr = default(byte*); uint num = default(uint); CoreCLR.V60.AllocMemArgs allocMemArgs = default(CoreCLR.V60.AllocMemArgs); CoreCLR.V60.InvokeCompileMethodHookPostPtr.InvokeCompileMethodHookPost(functionPointerForDelegate, IntPtr.Zero, IntPtr.Zero, &cORINFO_METHOD_INFO, 0u, &ptr, &num, CoreCLR.CorJitResult.CORJIT_OK, &allocMemArgs); nativeJitHookConfig->compileMethod = *vTableEntry; nativeJitHookConfig->compileMethodHookPost = functionPointerForDelegate; nint value = nativeJitHookConfig->compileMethodHook; Span span = stackalloc byte[sizeof(nint)]; MemoryMarshal.Write(span, in value); base.System.PatchData(PatchTargetKind.ReadOnly, (nint)vTableEntry, span, default(Span)); CompileMethodPatchPrimer(); return true; } [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] private static void CompileMethodPatchPrimer() { } protected override Delegate CreateCompileMethodDelegate(nint compileMethod) { return new <>f__AnonymousDelegate0(new JitHookDelegateHolder(this, InvokeCompileMethodPtr, compileMethod).CompileMethodHook); } private unsafe void CompileMethodHookPostCommon(CoreCLR.V21.CORINFO_METHOD_INFO* methodInfo, byte** nativeEntry, uint* nativeSizeOfCode, nint rwEntry) { RuntimeTypeHandle[] array = null; RuntimeTypeHandle[] array2 = null; if (methodInfo->args.sigInst.classInst != null) { array = new RuntimeTypeHandle[methodInfo->args.sigInst.classInstCount]; for (int i = 0; i < array.Length; i++) { array[i] = base.JitHookHelpers.GetTypeFromNativeHandle(methodInfo->args.sigInst.classInst[i]).TypeHandle; } } if (methodInfo->args.sigInst.methInst != null) { array2 = new RuntimeTypeHandle[methodInfo->args.sigInst.methInstCount]; for (int j = 0; j < array2.Length; j++) { array2[j] = base.JitHookHelpers.GetTypeFromNativeHandle(methodInfo->args.sigInst.methInst[j]).TypeHandle; } } RuntimeTypeHandle typeHandle = base.JitHookHelpers.GetDeclaringTypeOfMethodHandle(methodInfo->ftn).TypeHandle; RuntimeMethodHandle methodHandle = base.JitHookHelpers.CreateHandleForHandlePointer(methodInfo->ftn); OnMethodCompiledCore(typeHandle, methodHandle, array, array2, (nint)(*nativeEntry), rwEntry, *nativeSizeOfCode); } protected unsafe virtual NativeJitHookConfig* GetNativeJitHookConfig() { return (NativeJitHookConfig*)base.System.GetNativeJitHookConfig(60); } protected virtual Delegate CreateCompileMethodHookPostDelegate() { return new <>f__AnonymousDelegate1(new JitHookPostDelegateHolder(this).CompileMethodHookPost); } protected virtual Delegate CastCompileMethodHookPostToRealType(Delegate del) { return Extensions.CastDelegate(del); } protected unsafe virtual void PatchWrapperVtable(nint* vtbl) { allocMemDelegate = CastAllocMemToRealType(CreateAllocMemDelegate()); nint num = EHNativeToManaged(Marshal.GetFunctionPointerForDelegate(allocMemDelegate), out n2mAllocMemHelper); InvokeAllocMemPtr.InvokeAllocMem(num, IntPtr.Zero, null); vtbl[VtableIndexICorJitInfoAllocMem] = num; } protected override Delegate CastCompileHookToRealType(Delegate del) { return Extensions.CastDelegate(del); } protected virtual Delegate CastAllocMemToRealType(Delegate del) { return Extensions.CastDelegate(del); } protected virtual Delegate CreateAllocMemDelegate() { return new <>f__AnonymousDelegate2(new AllocMemDelegateHolder(this, InvokeAllocMemPtr).AllocMemHook); } } internal class Core70Runtime : Core60Runtime { private static readonly Guid JitVersionGuid = new Guid(1810136669u, 43307, 19734, 146, 128, 246, 61, 246, 70, 173, 164); protected override Guid ExpectedJitVersion => JitVersionGuid; protected override int VtableIndexICorJitInfoAllocMem => 159; protected override int ICorJitInfoFullVtableCount => 175; public Core70Runtime(ISystem system, IArchitecture arch) : base(system, arch) { } } internal class Core80Runtime : Core70Runtime { private static readonly Guid JitVersionGuid = new Guid(1271838981u, 54608, 19037, 177, 235, 39, 111, byte.MaxValue, 104, 209, 131); protected override Guid ExpectedJitVersion => JitVersionGuid; protected override int VtableIndexICorJitInfoAllocMem => 154; protected override int ICorJitInfoFullVtableCount => 170; public Core80Runtime(ISystem system, IArchitecture arch) : base(system, arch) { } } internal class Core90Runtime : Core80Runtime { private static readonly Guid JitVersionGuid = new Guid(3592522360u, 39476, 19567, 141, 181, 7, 122, 6, 2, 47, 174); protected override Guid ExpectedJitVersion => JitVersionGuid; protected override int VtableIndexICorJitInfoAllocMem => 158; protected override int ICorJitInfoFullVtableCount => 174; public Core90Runtime(ISystem system, IArchitecture arch) : base(system, arch) { } } internal abstract class CoreBaseRuntime : FxCoreBaseRuntime, IInitialize { private nint? lazyJitObject; private INativeExceptionHelper? lazyNativeExceptionHelper; public override RuntimeKind Target => (RuntimeKind)2; protected ISystem System { get; } protected nint JitObject { get { nint valueOrDefault = lazyJitObject.GetValueOrDefault(); if (!lazyJitObject.HasValue) { valueOrDefault = GetJitObject(); lazyJitObject = valueOrDefault; return valueOrDefault; } return valueOrDefault; } } protected INativeExceptionHelper? NativeExceptionHelper => lazyNativeExceptionHelper ?? (lazyNativeExceptionHelper = System.NativeExceptionHelper); public static CoreBaseRuntime CreateForVersion(Version version, ISystem system, IArchitecture arch) { switch (version.Major) { case 2: case 4: return new Core21Runtime(system); case 3: return version.Minor switch { 0 => new Core30Runtime(system), 1 => new Core31Runtime(system), _ => throw new PlatformNotSupportedException($"Unknown .NET Core 3.x minor version {version.Minor}"), }; case 5: return new Core50Runtime(system); case 6: return new Core60Runtime(system, arch); case 7: return new Core70Runtime(system, arch); case 8: return new Core80Runtime(system, arch); case 9: return new Core90Runtime(system, arch); case 10: return new Core100Runtime(system, arch); default: throw new PlatformNotSupportedException($"CoreCLR version {version} is not supported"); } } protected CoreBaseRuntime(ISystem system) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Invalid comparison between Unknown and I4 //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Invalid comparison between Unknown and I4 System = system; Abi? defaultAbi = system.DefaultAbi; if (defaultAbi.HasValue) { Abi valueOrDefault = defaultAbi.GetValueOrDefault(); if ((int)PlatformDetection.Architecture == 3) { AbiCore = FxCoreBaseRuntime.AbiForCoreFx45X64(valueOrDefault); } else if ((int)PlatformDetection.Architecture == 5) { AbiCore = FxCoreBaseRuntime.AbiForCoreFx45ARM64(valueOrDefault); } } } void IInitialize.Initialize() { InstallJitHook(JitObject); } private static bool IsMaybeClrJitPath(string path) { return Path.GetFileName(path) == DynDll.MakeDllName("clrjit"); } protected virtual string GetClrJitPath() { string text = null; object obj = default(object); bool isEnabled; if (Switches.TryGetSwitchValue("JitPath", ref obj)) { string jitPath = obj as string; if (jitPath != null) { if (!IsMaybeClrJitPath(jitPath)) { MMDbgLog.DebugLogWarningStringHandler message = new MMDbgLog.DebugLogWarningStringHandler(77, 1, out isEnabled); if (isEnabled) { message.AppendLiteral("Provided value for MonoMod.JitPath switch '"); message.AppendFormatted(jitPath); message.AppendLiteral("' does not look like a ClrJIT path"); } MMDbgLog.Warning(ref message); } else { text = System.EnumerateLoadedModuleFiles().FirstOrDefault((string f) => f != null && f == jitPath); if (text == null) { MMDbgLog.DebugLogWarningStringHandler message2 = new MMDbgLog.DebugLogWarningStringHandler(82, 1, out isEnabled); if (isEnabled) { message2.AppendLiteral("Provided path for MonoMod.JitPath switch was not loaded in this process. jitPath: "); message2.AppendFormatted(jitPath); } MMDbgLog.Warning(ref message2); } } } } if (text == null) { text = System.EnumerateLoadedModuleFiles().FirstOrDefault((string f) => f != null && IsMaybeClrJitPath(f)); } if (text == null) { throw new PlatformNotSupportedException("Could not locate clrjit library"); } MMDbgLog.DebugLogTraceStringHandler message3 = new MMDbgLog.DebugLogTraceStringHandler(14, 1, out isEnabled); if (isEnabled) { message3.AppendLiteral("Got jit path: "); message3.AppendFormatted(text); } MMDbgLog.Trace(ref message3); return text; } private unsafe nint GetJitObject() { nint num = default(nint); if (!DynDll.TryOpenLibrary(GetClrJitPath(), ref num)) { throw new PlatformNotSupportedException("Could not open clrjit library"); } try { return ((delegate* unmanaged[Stdcall])DynDll.GetExport((IntPtr)num, "getJit"))(); } catch { DynDll.CloseLibrary((IntPtr)num); throw; } } protected virtual void InstallJitHook(nint jit) { InstallManagedJitHook(jit); } protected abstract void InstallManagedJitHook(nint jit); protected nint EHNativeToManaged(nint target, out IDisposable? handle) { if (NativeExceptionHelper != null) { return NativeExceptionHelper.CreateNativeToManagedHelper(target, out handle); } handle = null; return target; } protected nint EHManagedToNative(nint target, out IDisposable? handle) { if (NativeExceptionHelper != null) { return NativeExceptionHelper.CreateManagedToNativeHelper(target, out handle); } handle = null; return target; } } internal abstract class FxBaseRuntime : FxCoreBaseRuntime { public override RuntimeKind Target => (RuntimeKind)1; public static FxBaseRuntime CreateForVersion(Version version, ISystem system) { if (version.Major == 4) { return new FxCLR4Runtime(system); } if (version.Major == 2) { return new FxCLR2Runtime(system); } throw new PlatformNotSupportedException($"CLR version {version} is not suppoted."); } } internal sealed class FxCLR2Runtime : FxBaseRuntime { private readonly ISystem system; public FxCLR2Runtime(ISystem system) { this.system = system; Abi? abiCore = AbiCore; if (!abiCore.HasValue) { AbiCore = system.DefaultAbi; } } } internal sealed class FxCLR4Runtime : FxBaseRuntime { private ISystem system; public override RuntimeFeature Features => base.Features & ~RuntimeFeature.RequiresBodyThunkWalking; public FxCLR4Runtime(ISystem system) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Invalid comparison between Unknown and I4 //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Invalid comparison between Unknown and I4 this.system = system; Abi? defaultAbi = system.DefaultAbi; if (defaultAbi.HasValue) { Abi valueOrDefault = defaultAbi.GetValueOrDefault(); if ((int)PlatformDetection.Architecture == 3 && (PlatformDetection.RuntimeVersion.Revision >= 17379 || PlatformDetection.RuntimeVersion.Minor >= 5)) { AbiCore = FxCoreBaseRuntime.AbiForCoreFx45X64(valueOrDefault); } if ((int)PlatformDetection.Architecture == 5) { AbiCore = FxCoreBaseRuntime.AbiForCoreFx45ARM64(valueOrDefault); } } } private unsafe nint GetMethodBodyPtr(MethodBase method, RuntimeMethodHandle handle) { Fx.V48.MethodDesc* value = (Fx.V48.MethodDesc*)handle.Value; value = Fx.V48.MethodDesc.FindTightlyBoundWrappedMethodDesc(value); return (nint)value->GetNativeCode(); } public override nint GetMethodEntryPoint(MethodBase method) { method = GetIdentifiable(method); RuntimeMethodHandle methodHandle = GetMethodHandle(method); bool flag = false; nint methodBodyPtr; while (true) { Helpers.Assert(TryInvokeBclCompileMethod(methodHandle), (string)null, "TryInvokeBclCompileMethod(handle)"); methodHandle.GetFunctionPointer(); methodBodyPtr = GetMethodBodyPtr(method, methodHandle); if (methodBodyPtr != IntPtr.Zero) { break; } if (!flag) { Helpers.Assert(TryInvokeBclCompileMethod(methodHandle), (string)null, "TryInvokeBclCompileMethod(handle)"); flag = true; continue; } methodBodyPtr = methodHandle.GetFunctionPointer(); throw new InvalidOperationException($"Could not get entry point normally, GetFunctionPointer() = {methodBodyPtr:x16}"); } return methodBodyPtr; } } internal abstract class FxCoreBaseRuntime : IRuntime { protected Abi? AbiCore; private static readonly Type? RTDynamicMethod = typeof(DynamicMethod).GetNestedType("RTDynamicMethod", BindingFlags.NonPublic); private static readonly FieldInfo? RTDynamicMethod_m_owner = RTDynamicMethod?.GetField("m_owner", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly FieldInfo? _DynamicMethod_m_method = typeof(DynamicMethod).GetField("m_method", BindingFlags.Instance | BindingFlags.NonPublic); private static readonly MethodInfo? _DynamicMethod_GetMethodDescriptor = typeof(DynamicMethod).GetMethod("GetMethodDescriptor", BindingFlags.Instance | BindingFlags.NonPublic); private static readonly MethodInfo? _RuntimeMethodHandle_get_Value = typeof(RuntimeMethodHandle).GetMethod("get_Value", BindingFlags.Instance | BindingFlags.Public); private static readonly FieldInfo? _RuntimeMethodHandle_m_value = typeof(RuntimeMethodHandle).GetField("m_value", BindingFlags.Instance | BindingFlags.NonPublic); private static readonly MethodInfo? _IRuntimeMethodInfo_get_Value = typeof(RuntimeMethodHandle).Assembly.GetType("System.IRuntimeMethodInfo")?.GetMethod("get_Value"); private static readonly MethodInfo? _RuntimeHelpers__CompileMethod = typeof(RuntimeHelpers).GetMethod("_CompileMethod", BindingFlags.Static | BindingFlags.NonPublic) ?? typeof(RuntimeHelpers).GetMethod("CompileMethod", BindingFlags.Static | BindingFlags.NonPublic); private static readonly Type? RtH_CM_FirstArg; private static readonly bool _RuntimeHelpers__CompileMethod_TakesIntPtr; private static readonly bool _RuntimeHelpers__CompileMethod_TakesIRuntimeMethodInfo; private static readonly bool _RuntimeHelpers__CompileMethod_TakesRuntimeMethodHandleInternal; private Func? lazyGetDmHandleHelper; private Action? lazyBclCompileMethod; protected static readonly bool IsDebugClr; public abstract RuntimeKind Target { get; } public virtual RuntimeFeature Features => RuntimeFeature.PreciseGC | RuntimeFeature.GenericSharing | RuntimeFeature.DisableInlining | RuntimeFeature.RequiresMethodIdentification | RuntimeFeature.RequiresBodyThunkWalking | RuntimeFeature.HasKnownABI | RuntimeFeature.RequiresCustomMethodCompile; public Abi Abi => AbiCore ?? throw new PlatformNotSupportedException($"The runtime's Abi field is not set, and is unusable ({GetType()})"); private Func GetDMHandleHelper => lazyGetDmHandleHelper ?? (lazyGetDmHandleHelper = CreateGetDMHandleHelper()); private static bool CanCreateGetDMHandleHelper => (object)_DynamicMethod_GetMethodDescriptor != null; private Action BclCompileMethodHelper => lazyBclCompileMethod ?? (lazyBclCompileMethod = CreateBclCompileMethodHelper()); private static bool CanCreateBclCompileMethodHelper { get { if ((object)_RuntimeHelpers__CompileMethod != null) { if (!_RuntimeHelpers__CompileMethod_TakesIntPtr) { if ((object)_RuntimeMethodHandle_m_value != null) { if (!_RuntimeHelpers__CompileMethod_TakesIRuntimeMethodInfo) { if ((object)_IRuntimeMethodInfo_get_Value != null) { return _RuntimeHelpers__CompileMethod_TakesRuntimeMethodHandleInternal; } return false; } return true; } return false; } return true; } return false; } } public event OnMethodCompiledCallback? OnMethodCompiled; private static TypeClassification ClassifyRyuJitX86(Type type, bool isReturn) { while (!type.IsPrimitive || type.IsEnum) { FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (fields == null || fields.Length != 1) { break; } type = fields[0].FieldType; } TypeCode typeCode = Type.GetTypeCode(type); bool flag = ((typeCode == TypeCode.Boolean || (uint)(typeCode - 5) <= 5u) ? true : false); if (flag || type == typeof(nint) || type == typeof(nuint)) { return TypeClassification.InRegister; } flag = isReturn; if (flag) { bool flag2 = (uint)(typeCode - 11) <= 1u; flag = flag2; } if (flag) { return TypeClassification.InRegister; } flag = isReturn; if (flag) { bool flag2 = (uint)(typeCode - 13) <= 1u; flag = flag2; } if (flag) { return TypeClassification.InRegister; } if (isReturn) { int managedSize = Extensions.GetManagedSize(type); if (((uint)(managedSize - 1) <= 1u || managedSize == 4) ? true : false) { return TypeClassification.InRegister; } return TypeClassification.ByReference; } return TypeClassification.OnStack; } protected FxCoreBaseRuntime() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Invalid comparison between Unknown and I4 if ((int)PlatformDetection.Architecture == 2) { AbiCore = new Abi(new SpecialArgumentKind[4] { SpecialArgumentKind.ThisPointer, SpecialArgumentKind.ReturnBuffer, SpecialArgumentKind.UserArguments, SpecialArgumentKind.GenericContext }, ClassifyRyuJitX86, ReturnsReturnBuffer: true); } } protected static Abi AbiForCoreFx45X64(Abi baseAbi) { return baseAbi with { ArgumentOrder = new SpecialArgumentKind[4] { SpecialArgumentKind.ThisPointer, SpecialArgumentKind.ReturnBuffer, SpecialArgumentKind.GenericContext, SpecialArgumentKind.UserArguments } }; } protected static Abi AbiForCoreFx45ARM64(Abi baseAbi) { return baseAbi with { ArgumentOrder = new SpecialArgumentKind[3] { SpecialArgumentKind.ThisPointer, SpecialArgumentKind.GenericContext, SpecialArgumentKind.UserArguments } }; } public virtual MethodBase GetIdentifiable(MethodBase method) { if (RTDynamicMethod_m_owner != null && method.GetType() == RTDynamicMethod) { return (MethodBase)RTDynamicMethod_m_owner.GetValue(method); } return method; } public virtual RuntimeMethodHandle GetMethodHandle(MethodBase method) { if (method is DynamicMethod dynamicMethod) { if (TryGetDMHandle(dynamicMethod, out var handle) && TryInvokeBclCompileMethod(handle)) { return handle; } try { dynamicMethod.CreateDelegate(typeof(MulticastDelegate)); } catch { } if (TryGetDMHandle(dynamicMethod, out handle)) { return handle; } if (_DynamicMethod_m_method != null) { return (RuntimeMethodHandle)_DynamicMethod_m_method.GetValue(method); } } return method.MethodHandle; } public bool RequiresGenericContext(MethodBase method) { Type type = method.DeclaringType ?? typeof(object); if (!method.IsGenericMethod && !type.IsGenericType) { return false; } if ((method.IsGenericMethod ? method.GetGenericArguments() : Type.EmptyTypes).Any(IsGenericSharedType)) { return true; } if (!method.IsGenericMethod && !method.IsStatic && !type.IsValueType && (!type.IsInterface || method.IsAbstract)) { return false; } return (type.IsGenericType ? type.GetGenericArguments() : Type.EmptyTypes).Any(IsGenericSharedType); } private static bool IsGenericSharedType(Type type) { if (!type.IsPrimitive) { if (type.IsValueType) { if (type.IsGenericType) { return type.GetGenericArguments().Any(IsGenericSharedType); } return false; } return true; } return false; } private static Func CreateGetDMHandleHelper() { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Expected O, but got Unknown //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) Helpers.Assert(CanCreateGetDMHandleHelper, (string)null, "CanCreateGetDMHandleHelper"); DynamicMethodDefinition val = new DynamicMethodDefinition("get DynamicMethod RuntimeMethodHandle", typeof(RuntimeMethodHandle), new Type[1] { typeof(DynamicMethod) }); try { _ = val.Module; ILProcessor iLProcessor = val.GetILProcessor(); Helpers.Assert((object)_DynamicMethod_GetMethodDescriptor != null, (string)null, "_DynamicMethod_GetMethodDescriptor is not null"); iLProcessor.Emit(OpCodes.Ldarg_0); Extensions.Emit(iLProcessor, OpCodes.Call, (MethodBase)_DynamicMethod_GetMethodDescriptor); iLProcessor.Emit(OpCodes.Ret); return val.Generate().CreateDelegate>(); } finally { ((IDisposable)val)?.Dispose(); } } private static Action CreateBclCompileMethodHelper() { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Expected O, but got Unknown //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_0147: Unknown result type (might be due to invalid IL or missing references) Helpers.Assert(CanCreateBclCompileMethodHelper, (string)null, "CanCreateBclCompileMethodHelper"); DynamicMethodDefinition val = new DynamicMethodDefinition("invoke RuntimeHelpers.CompileMethod", (Type)null, new Type[1] { typeof(RuntimeMethodHandle) }); try { ModuleDefinition module = val.Module; ILProcessor iLProcessor = val.GetILProcessor(); iLProcessor.Emit(OpCodes.Ldarga_S, (byte)0); if (_RuntimeHelpers__CompileMethod_TakesIntPtr) { iLProcessor.Emit(OpCodes.Call, module.ImportReference((MethodBase)_RuntimeMethodHandle_get_Value)); iLProcessor.Emit(OpCodes.Call, module.ImportReference((MethodBase)_RuntimeHelpers__CompileMethod)); iLProcessor.Emit(OpCodes.Ret); return val.Generate().CreateDelegate>(); } Helpers.Assert((object)_RuntimeMethodHandle_m_value != null, (string)null, "_RuntimeMethodHandle_m_value is not null"); iLProcessor.Emit(OpCodes.Ldfld, module.ImportReference(_RuntimeMethodHandle_m_value)); if (_RuntimeHelpers__CompileMethod_TakesIRuntimeMethodInfo) { iLProcessor.Emit(OpCodes.Call, module.ImportReference((MethodBase)_RuntimeHelpers__CompileMethod)); iLProcessor.Emit(OpCodes.Ret); return val.Generate().CreateDelegate>(); } Helpers.Assert((object)_IRuntimeMethodInfo_get_Value != null, (string)null, "_IRuntimeMethodInfo_get_Value is not null"); iLProcessor.Emit(OpCodes.Callvirt, module.ImportReference((MethodBase)_IRuntimeMethodInfo_get_Value)); if (_RuntimeHelpers__CompileMethod_TakesRuntimeMethodHandleInternal) { iLProcessor.Emit(OpCodes.Call, module.ImportReference((MethodBase)_RuntimeHelpers__CompileMethod)); iLProcessor.Emit(OpCodes.Ret); return val.Generate().CreateDelegate>(); } Helpers.Assert(false, "Tried to generate BCL CompileMethod helper when it's not possible? (This should never happen if CanCreateBclCompileMethodHelper is correct)", "false"); throw new InvalidOperationException("UNREACHABLE"); } finally { ((IDisposable)val)?.Dispose(); } } private bool TryGetDMHandle(DynamicMethod dm, out RuntimeMethodHandle handle) { if (CanCreateGetDMHandleHelper) { handle = GetDMHandleHelper(dm); return true; } return TryGetDMHandleRefl(dm, out handle); } protected bool TryInvokeBclCompileMethod(RuntimeMethodHandle handle) { if (CanCreateBclCompileMethodHelper) { BclCompileMethodHelper(handle); return true; } return TryInvokeBclCompileMethodRefl(handle); } private static bool TryGetDMHandleRefl(DynamicMethod dm, out RuntimeMethodHandle handle) { handle = default(RuntimeMethodHandle); if ((object)_DynamicMethod_GetMethodDescriptor == null) { return false; } handle = (RuntimeMethodHandle)_DynamicMethod_GetMethodDescriptor.Invoke(dm, null); return true; } private static bool TryInvokeBclCompileMethodRefl(RuntimeMethodHandle handle) { if ((object)_RuntimeHelpers__CompileMethod == null) { return false; } if (_RuntimeHelpers__CompileMethod_TakesIntPtr) { _RuntimeHelpers__CompileMethod.Invoke(null, new object[1] { handle.Value }); return true; } if ((object)_RuntimeMethodHandle_m_value == null) { return false; } object value = _RuntimeMethodHandle_m_value.GetValue(handle); if (_RuntimeHelpers__CompileMethod_TakesIRuntimeMethodInfo) { _RuntimeHelpers__CompileMethod.Invoke(null, new object[1] { value }); return true; } if ((object)_IRuntimeMethodInfo_get_Value == null) { return false; } object obj = _IRuntimeMethodInfo_get_Value.Invoke(value, null); if (_RuntimeHelpers__CompileMethod_TakesRuntimeMethodHandleInternal) { _RuntimeHelpers__CompileMethod.Invoke(null, new object[1] { obj }); return true; } bool isEnabled; MMDbgLog.DebugLogErrorStringHandler message = new MMDbgLog.DebugLogErrorStringHandler(81, 1, out isEnabled); if (isEnabled) { message.AppendLiteral("Could not compile DynamicMethod using BCL reflection (_CompileMethod first arg: "); message.AppendFormatted(RtH_CM_FirstArg); message.AppendLiteral(")"); } MMDbgLog.Error(ref message); return false; } public virtual void Compile(MethodBase method) { RuntimeMethodHandle handle = GetMethodHandle(method); RuntimeHelpers.PrepareMethod(handle); Helpers.Assert(TryInvokeBclCompileMethod(handle), (string)null, "TryInvokeBclCompileMethod(handle)"); if (!method.IsVirtual) { return; } Type? declaringType = method.DeclaringType; if ((object)declaringType == null || !declaringType.IsValueType) { return; } if (TryGetCanonicalMethodHandle(ref handle)) { Helpers.Assert(TryInvokeBclCompileMethod(handle), (string)null, "TryInvokeBclCompileMethod(handle)"); return; } try { Extensions.CreateDelegate(method); } catch (Exception value) { bool isEnabled; MMDbgLog.DebugLogSpamStringHandler message = new MMDbgLog.DebugLogSpamStringHandler(91, 1, out isEnabled); if (isEnabled) { message.AppendLiteral("Caught exception while attempting to compile real entry point of virtual method on struct: "); message.AppendFormatted(value); } MMDbgLog.Spam(ref message); } } protected virtual bool TryGetCanonicalMethodHandle(ref RuntimeMethodHandle handle) { return false; } public virtual IDisposable? PinMethodIfNeeded(MethodBase method) { return null; } public unsafe virtual void DisableInlining(MethodBase method) { RuntimeMethodHandle methodHandle = GetMethodHandle(method); int num = (IsDebugClr ? (IntPtr.Size + IntPtr.Size + IntPtr.Size + IntPtr.Size + IntPtr.Size) : 0) + 2 + 1 + 1 + 2; ushort* ptr = (ushort*)(methodHandle.Value + num); *ptr |= 0x2000; } public virtual nint GetMethodEntryPoint(MethodBase method) { method = GetIdentifiable(method); if (method.IsVirtual) { Type? declaringType = method.DeclaringType; if ((object)declaringType != null && declaringType.IsValueType) { return Extensions.GetLdftnPointer(method); } } return GetMethodHandle(method).GetFunctionPointer(); } protected virtual void OnMethodCompiledCore(RuntimeTypeHandle declaringType, RuntimeMethodHandle methodHandle, ReadOnlyMemory? genericTypeArguments, ReadOnlyMemory? genericMethodArguments, nint methodBodyStart, nint methodBodyRw, ulong methodBodySize) { bool isEnabled; try { Type type = Type.GetTypeFromHandle(declaringType); if (genericTypeArguments.HasValue) { ReadOnlyMemory valueOrDefault = genericTypeArguments.GetValueOrDefault(); if (type.IsGenericTypeDefinition) { Type[] array = new Type[valueOrDefault.Length]; for (int i = 0; i < valueOrDefault.Length; i++) { array[i] = Type.GetTypeFromHandle(valueOrDefault.Span[i]); } type = type.MakeGenericType(array); } } MethodBase methodBase = MethodBase.GetMethodFromHandle(methodHandle, type.TypeHandle); if ((object)methodBase == null) { MethodInfo[] methods = type.GetMethods((BindingFlags)(-1)); foreach (MethodInfo methodInfo in methods) { if (methodInfo.MethodHandle.Value == methodHandle.Value) { methodBase = methodInfo; break; } } } MMDbgLog.DebugLogSpamStringHandler message = new MMDbgLog.DebugLogSpamStringHandler(28, 3, out isEnabled); if (isEnabled) { message.AppendLiteral("JIT compiled "); message.AppendFormatted(methodBase); message.AppendLiteral(" to 0x"); message.AppendFormatted(methodBodyStart, "x16"); message.AppendLiteral(" (rw: 0x"); message.AppendFormatted(methodBodyRw, "x16"); message.AppendLiteral(")"); } MMDbgLog.Spam(ref message); try { this.OnMethodCompiled?.Invoke(methodHandle, methodBase, methodBodyStart, methodBodyRw, methodBodySize); } catch (Exception value) { MMDbgLog.DebugLogErrorStringHandler message2 = new MMDbgLog.DebugLogErrorStringHandler(40, 1, out isEnabled); if (isEnabled) { message2.AppendLiteral("Error executing OnMethodCompiled event: "); message2.AppendFormatted(value); } MMDbgLog.Error(ref message2); } } catch (Exception value2) { MMDbgLog.DebugLogErrorStringHandler message3 = new MMDbgLog.DebugLogErrorStringHandler(31, 1, out isEnabled); if (isEnabled) { message3.AppendLiteral("Error in OnMethodCompiledCore: "); message3.AppendFormatted(value2); } MMDbgLog.Error(ref message3); } } static FxCoreBaseRuntime() { MethodInfo? runtimeHelpers__CompileMethod = _RuntimeHelpers__CompileMethod; RtH_CM_FirstArg = (((object)runtimeHelpers__CompileMethod != null) ? runtimeHelpers__CompileMethod.GetParameters()[0].ParameterType : null); _RuntimeHelpers__CompileMethod_TakesIntPtr = RtH_CM_FirstArg?.FullName == "System.IntPtr"; _RuntimeHelpers__CompileMethod_TakesIRuntimeMethodInfo = RtH_CM_FirstArg?.FullName == "System.IRuntimeMethodInfo"; _RuntimeHelpers__CompileMethod_TakesRuntimeMethodHandleInternal = RtH_CM_FirstArg?.FullName == "System.RuntimeMethodHandleInternal"; bool flag = default(bool); IsDebugClr = Switches.TryGetSwitchEnabled("DebugClr", ref flag) && flag; } } internal sealed class MonoRuntime : IRuntime { private sealed class PrivateMethodPin { private readonly MonoRuntime runtime; public MethodPinInfo Pin; public PrivateMethodPin(MonoRuntime runtime) { this.runtime = runtime; } public void UnpinOnce() { runtime.UnpinOnce(this); } } private sealed class PinHandle : IDisposable { private readonly PrivateMethodPin pin; private bool disposedValue; public PinHandle(PrivateMethodPin pin) { this.pin = pin; } private void Dispose(bool disposing) { if (!disposedValue) { pin.UnpinOnce(); disposedValue = true; } } ~PinHandle() { Dispose(disposing: false); } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } } private struct MethodPinInfo { public int Count; public MethodBase Method; public RuntimeMethodHandle Handle; public override string ToString() { return $"(MethodPinInfo: {Count}, {Method}, 0x{Handle.Value:X})"; } } private readonly ISystem system; private static readonly MethodInfo _DynamicMethod_CreateDynMethod = typeof(DynamicMethod).GetMethod("CreateDynMethod", BindingFlags.Instance | BindingFlags.NonPublic); private static readonly FieldInfo _DynamicMethod_mhandle = typeof(DynamicMethod).GetField("mhandle", BindingFlags.Instance | BindingFlags.NonPublic); private readonly ConcurrentDictionary pinnedMethods = new ConcurrentDictionary(); private readonly ConcurrentDictionary pinnedHandles = new ConcurrentDictionary(); public RuntimeKind Target => (RuntimeKind)3; public RuntimeFeature Features => RuntimeFeature.PreciseGC | RuntimeFeature.GenericSharing | RuntimeFeature.DisableInlining | RuntimeFeature.RequiresMethodPinning | RuntimeFeature.RequiresMethodIdentification | RuntimeFeature.RequiresCustomMethodCompile; public Abi Abi { get; } public event OnMethodCompiledCallback? OnMethodCompiled; private static TypeClassification LinuxAmd64Classifier(Type type, bool isReturn) { if (type.IsEnum) { type = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).First().FieldType; } switch (Type.GetTypeCode(type)) { case TypeCode.Empty: return TypeClassification.InRegister; case TypeCode.Object: case TypeCode.DBNull: case TypeCode.String: return TypeClassification.InRegister; case TypeCode.Boolean: case TypeCode.Char: case TypeCode.SByte: case TypeCode.Byte: case TypeCode.Int16: case TypeCode.UInt16: case TypeCode.Int32: case TypeCode.UInt32: case TypeCode.Int64: case TypeCode.UInt64: return TypeClassification.InRegister; case TypeCode.Single: case TypeCode.Double: return TypeClassification.InRegister; default: if (type.IsPointer) { return TypeClassification.InRegister; } if (type.IsByRef) { return TypeClassification.InRegister; } if (type == typeof(nint) || type == typeof(nuint)) { return TypeClassification.InRegister; } if (type == typeof(void)) { return TypeClassification.InRegister; } Helpers.Assert(type.IsValueType, (string)null, "type.IsValueType"); return ClassifyValueType(type, isReturn: true); } } private static TypeClassification ClassifyValueType(Type type, bool isReturn) { int managedSize = Extensions.GetManagedSize(type); bool flag = (!isReturn || managedSize != 8) && (isReturn || managedSize > 16); if (managedSize == 0) { return TypeClassification.InRegister; } if (flag) { if (!isReturn) { return TypeClassification.OnStack; } return TypeClassification.ByReference; } int num = ((managedSize <= 8) ? 1 : 2); int num2 = 1; int num3 = 1; if (isReturn && num != 1) { num2 = (num3 = 2); } if (num2 == 2 || num3 == 2) { num2 = 2; } return num2 switch { 1 => TypeClassification.InRegister, 2 => TypeClassification.OnStack, _ => throw new InvalidOperationException(), }; } private static IEnumerable NestedValutypeFields(Type type) { FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); FieldInfo[] array = fields; foreach (FieldInfo fieldInfo in array) { if (fieldInfo.FieldType.IsValueType) { foreach (FieldInfo item in NestedValutypeFields(fieldInfo.FieldType)) { yield return item; } } else { yield return fieldInfo; } } } public MonoRuntime(ISystem system) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Invalid comparison between Unknown and I4 //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Invalid comparison between Unknown and I4 //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Invalid comparison between Unknown and I4 //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Invalid comparison between Unknown and I4 //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Invalid comparison between Unknown and I4 //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Invalid comparison between Unknown and I4 this.system = system; Abi? defaultAbi = system.DefaultAbi; if (defaultAbi.HasValue) { Abi abi = defaultAbi.GetValueOrDefault(); OSKind kernel = OSKindExtensions.GetKernel(PlatformDetection.OS); if ((((int)kernel == 5 || (int)kernel == 9) ? true : false) && (int)PlatformDetection.Architecture == 3) { abi = abi with { Classifier = LinuxAmd64Classifier }; } kernel = PlatformDetection.OS; bool flag = (((int)kernel == 2 || (int)kernel == 34) ? true : false); if (flag) { ArchitectureKind architecture = PlatformDetection.Architecture; flag = architecture - 2 <= 1; } if (flag) { abi = abi with { ArgumentOrder = new SpecialArgumentKind[3] { SpecialArgumentKind.ThisPointer, SpecialArgumentKind.ReturnBuffer, SpecialArgumentKind.UserArguments } }; } Abi = abi; return; } throw new InvalidOperationException("Cannot use Mono system, because the underlying system doesn't provide a default ABI!"); } public unsafe void DisableInlining(MethodBase method) { ushort* ptr = (ushort*)((ulong)GetMethodHandle(method).Value + 2uL); *ptr |= 8; } public RuntimeMethodHandle GetMethodHandle(MethodBase method) { if (method is DynamicMethod) { _DynamicMethod_CreateDynMethod?.Invoke(method, ArrayEx.Empty()); if (_DynamicMethod_mhandle != null) { return (RuntimeMethodHandle)_DynamicMethod_mhandle.GetValue(method); } } return method.MethodHandle; } public bool RequiresGenericContext(MethodBase method) { return false; } public IDisposable? PinMethodIfNeeded(MethodBase method) { method = GetIdentifiable(method); PrivateMethodPin orAdd = pinnedMethods.GetOrAdd(method, delegate(MethodBase m) { PrivateMethodPin privateMethodPin = new PrivateMethodPin(this) { Pin = { Method = m } }; RuntimeMethodHandle key = (privateMethodPin.Pin.Handle = GetMethodHandle(m)); pinnedHandles[key] = privateMethodPin; DisableInlining(method); _ = method.DeclaringType?.IsGenericType ?? false; return privateMethodPin; }); Interlocked.Increment(ref orAdd.Pin.Count); return new PinHandle(orAdd); } private void UnpinOnce(PrivateMethodPin pin) { if (Interlocked.Decrement(ref pin.Pin.Count) <= 0) { pinnedMethods.TryRemove(pin.Pin.Method, out PrivateMethodPin value); pinnedHandles.TryRemove(pin.Pin.Handle, out value); } } public MethodBase GetIdentifiable(MethodBase method) { if (!pinnedHandles.TryGetValue(GetMethodHandle(method), out PrivateMethodPin value)) { return method; } return value.Pin.Method; } public nint GetMethodEntryPoint(MethodBase method) { if (pinnedMethods.TryGetValue(method, out PrivateMethodPin value)) { return value.Pin.Handle.GetFunctionPointer(); } return GetMethodHandle(method).GetFunctionPointer(); } public void Compile(MethodBase method) { GetMethodHandle(method).GetFunctionPointer(); } } } namespace MonoMod.Core.Platforms.Memory { public abstract class PagedMemoryAllocator : IMemoryAllocator { private sealed class FreeMem { public uint BaseOffset; public uint Size; public FreeMem? NextFree; } protected sealed class PageAllocation : IAllocatedMemory, IDisposable { private readonly Page owner; private readonly uint offset; private bool disposedValue; public bool IsExecutable => owner.IsExecutable; public nint BaseAddress => owner.BaseAddr + (nint)offset; public int Size { get; } public unsafe Span Memory => new Span((void*)BaseAddress, Size); public PageAllocation(Page page, uint offset, int size) { owner = page; this.offset = offset; Size = size; } private void Dispose(bool disposing) { if (!disposedValue) { owner.FreeMem(offset, (uint)Size); disposedValue = true; } } ~PageAllocation() { Dispose(disposing: false); } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } } protected sealed class Page { private readonly PagedMemoryAllocator owner; private readonly object sync = new object(); private FreeMem? freeList; public bool IsEmpty { get { FreeMem freeMem = freeList; if (freeMem != null && freeMem.BaseOffset == 0) { return freeMem.Size == Size; } return false; } } public nint BaseAddr { get; } public uint Size { get; } public bool IsExecutable { get; } public Page(PagedMemoryAllocator owner, nint baseAddr, uint size, bool isExecutable) { this.owner = owner; BaseAddr = baseAddr; Size = size; IsExecutable = isExecutable; freeList = new FreeMem { BaseOffset = 0u, Size = size, NextFree = null }; } public bool TryAllocate(uint size, uint align, [MaybeNullWhen(false)] out PageAllocation alloc) { lock (sync) { ref FreeMem nextFree = ref freeList; uint num = 0u; while (nextFree != null) { uint num2 = nextFree.BaseOffset % align; num2 = ((num2 != 0) ? (align - num2) : num2); if (nextFree.Size >= num2 + size) { num = num2; break; } nextFree = ref nextFree.NextFree; } if (nextFree == null) { alloc = null; return false; } uint offset = nextFree.BaseOffset + num; if (num == 0) { nextFree.BaseOffset += size; nextFree.Size -= size; } else { FreeMem freeMem = new FreeMem { BaseOffset = nextFree.BaseOffset, Size = num, NextFree = nextFree }; nextFree.BaseOffset += num + size; nextFree.Size -= num + size; nextFree = freeMem; } NormalizeFreeList(); alloc = new PageAllocation(this, offset, (int)size); return true; } } private void NormalizeFreeList() { ref FreeMem nextFree = ref freeList; while (nextFree != null) { if (nextFree.Size == 0) { nextFree = nextFree.NextFree; continue; } FreeMem nextFree2 = nextFree.NextFree; if (nextFree2 != null && nextFree2.BaseOffset == nextFree.BaseOffset + nextFree.Size) { nextFree.Size += nextFree2.Size; nextFree.NextFree = nextFree2.NextFree; } else { nextFree = ref nextFree.NextFree; } } } internal void FreeMem(uint offset, uint size) { lock (sync) { ref FreeMem nextFree = ref freeList; while (nextFree != null && nextFree.BaseOffset <= offset) { nextFree = ref nextFree.NextFree; } nextFree = new FreeMem { BaseOffset = offset, Size = size, NextFree = nextFree }; NormalizeFreeList(); if (IsEmpty) { owner.RegisterForCleanup(this); } } } } [StructLayout(LayoutKind.Sequential, Size = 1)] private readonly struct PageComparer : IComparer { public int Compare(Page? x, Page? y) { if (x == y) { return 0; } if (x == null) { return 1; } if (y == null) { return -1; } return ((long)x.BaseAddr).CompareTo(y.BaseAddr); } } private readonly struct PageAddrComparable : IComparable { private readonly nint addr; public PageAddrComparable(nint addr) { this.addr = addr; } public int CompareTo(Page? other) { if (other == null) { return 1; } return ((long)addr).CompareTo(other.BaseAddr); } } private readonly nint pageBaseMask; private readonly nint pageSize; private readonly bool pageSizeIsPow2; private Page?[] allocationList = new Page[16]; private int pageCount; private readonly ConcurrentBag pagesToClean = new ConcurrentBag(); private int registeredForCleanup; private readonly object sync = new object(); protected nint PageSize => pageSize; private ReadOnlySpan AllocList => allocationList.AsSpan().Slice(0, pageCount); public int MaxSize => (int)pageSize; protected PagedMemoryAllocator(nint pageSize) { this.pageSize = pageSize; pageSizeIsPow2 = BitOperationsEx.IsPow2((IntPtr)pageSize); pageBaseMask = ~(nint)0 << BitOperationsEx.TrailingZeroCount((IntPtr)pageSize); } public nint RoundDownToPageBoundary(nint addr) { if (pageSizeIsPow2) { return addr & pageBaseMask; } return addr - addr % pageSize; } protected void InsertAllocatedPage(Page page) { if (pageCount == allocationList.Length) { int newSize = (int)BitOperationsEx.RoundUpToPowerOf2((uint)(allocationList.Length + 1)); Array.Resize(ref allocationList, newSize); } Span span = allocationList.AsSpan(); int num = span.Slice(0, pageCount).BinarySearch(page, default(PageComparer)); if (num < 0) { num = ~num; if (num + 1 < span.Length) { span.Slice(num, pageCount - num).CopyTo(span.Slice(num + 1)); } span[num] = page; pageCount++; } } private void RemoveAllocatedPage(Page page) { Span span = allocationList.AsSpan(); int num = span.Slice(0, pageCount).BinarySearch(page, default(PageComparer)); if (num >= 0) { span.Slice(num + 1).CopyTo(span.Slice(num)); pageCount--; } } private int GetBoundIndex(nint ptr) { int num = AllocList.BinarySearch(new PageAddrComparable(ptr)); if (num < 0) { return ~num; } return num; } protected void RegisterForCleanup(Page page) { if (!Environment.HasShutdownStarted && !AppDomain.CurrentDomain.IsFinalizingForUnload()) { pagesToClean.Add(page); if (Interlocked.CompareExchange(ref registeredForCleanup, 1, 0) == 0) { System.Gen2GcCallback.Register(DoCleanup); } } } private bool DoCleanup() { if (Environment.HasShutdownStarted || AppDomain.CurrentDomain.IsFinalizingForUnload()) { return false; } Volatile.Write(ref registeredForCleanup, 0); Page result; while (pagesToClean.TryTake(out result)) { lock (sync) { if (!result.IsEmpty) { continue; } RemoveAllocatedPage(result); goto IL_005e; } IL_005e: if (!TryFreePage(result, out string errorMsg)) { bool isEnabled; MMDbgLog.DebugLogErrorStringHandler message = new MMDbgLog.DebugLogErrorStringHandler(27, 1, out isEnabled); if (isEnabled) { message.AppendLiteral("Could not deallocate page! "); message.AppendFormatted(errorMsg); } MMDbgLog.Error(ref message); } } return false; } protected abstract bool TryFreePage(Page page, [NotNullWhen(false)] out string? errorMsg); public bool TryAllocateInRange(PositionedAllocationRequest request, [MaybeNullWhen(false)] out IAllocatedMemory allocated) { if (request.Target < request.LowBound || request.Target > request.HighBound) { throw new ArgumentException("Target not between low and high", "request"); } if (request.Base.Size < 0) { throw new ArgumentException("Size is negative", "request"); } if (request.Base.Alignment <= 0) { throw new ArgumentException("Alignment is zero or negative", "request"); } if (request.Base.Size > pageSize) { throw new NotSupportedException("Single allocations cannot be larger than a page"); } nint num = RoundDownToPageBoundary(request.LowBound + pageSize - 1); nint num2 = RoundDownToPageBoundary(request.HighBound); nint num3 = RoundDownToPageBoundary(request.Target); nint target = request.Target; lock (sync) { int boundIndex = GetBoundIndex(num); int boundIndex2 = GetBoundIndex(num2); if (boundIndex != boundIndex2) { int boundIndex3 = GetBoundIndex(num3); int i = boundIndex3 - 1; int j = boundIndex3; while ((uint)j <= AllocList.Length && (uint)i < AllocList.Length && (i >= boundIndex || j < boundIndex2)) { for (; (uint)j < AllocList.Length && j < boundIndex2 && (i < boundIndex || target - AllocList[i].BaseAddr > AllocList[j].BaseAddr - target); j++) { if (TryAllocWithPage(AllocList[j], request, out allocated)) { return true; } } for (; (uint)i < AllocList.Length && i >= boundIndex && (j >= boundIndex2 || target - AllocList[i].BaseAddr < AllocList[j].BaseAddr - target); i++) { if (TryAllocWithPage(AllocList[i], request, out allocated)) { return true; } } } } return TryAllocateNewPage(request, num3, num, num2, out allocated); } } private static bool TryAllocWithPage(Page page, PositionedAllocationRequest request, [MaybeNullWhen(false)] out IAllocatedMemory allocated) { if (page.IsExecutable == request.Base.Executable && page.BaseAddr >= request.LowBound && page.BaseAddr < request.HighBound && page.TryAllocate((uint)request.Base.Size, (uint)request.Base.Alignment, out PageAllocation alloc)) { if (alloc.BaseAddress >= request.LowBound && alloc.BaseAddress < request.HighBound) { allocated = alloc; return true; } alloc.Dispose(); } allocated = null; return false; } public bool TryAllocate(AllocationRequest request, [MaybeNullWhen(false)] out IAllocatedMemory allocated) { if (request.Size < 0) { throw new ArgumentException("Size is negative", "request"); } if (request.Alignment <= 0) { throw new ArgumentException("Alignment is zero or negative", "request"); } if (request.Size > pageSize) { throw new NotSupportedException("Single allocations cannot be larger than a page"); } lock (sync) { ReadOnlySpan allocList = AllocList; for (int i = 0; i < allocList.Length; i++) { Page page = allocList[i]; if (page.IsExecutable == request.Executable && page.TryAllocate((uint)request.Size, (uint)request.Alignment, out PageAllocation alloc)) { allocated = alloc; return true; } } return TryAllocateNewPage(request, out allocated); } } protected abstract bool TryAllocateNewPage(AllocationRequest request, [MaybeNullWhen(false)] out IAllocatedMemory allocated); protected abstract bool TryAllocateNewPage(PositionedAllocationRequest request, nint targetPage, nint lowPageBound, nint highPageBound, [MaybeNullWhen(false)] out IAllocatedMemory allocated); } public abstract class QueryingMemoryPageAllocatorBase { public abstract uint PageSize { get; } public abstract bool TryQueryPage(nint pageAddr, out bool isFree, out nint allocBase, out nint allocSize); public abstract bool TryAllocatePage(nint size, bool executable, out nint allocated); public abstract bool TryAllocatePage(nint pageAddr, nint size, bool executable, out nint allocated); public abstract bool TryFreePage(nint pageAddr, [NotNullWhen(false)] out string? errorMsg); } public sealed class QueryingPagedMemoryAllocator : PagedMemoryAllocator { private readonly QueryingMemoryPageAllocatorBase pageAlloc; public QueryingPagedMemoryAllocator(QueryingMemoryPageAllocatorBase alloc) : base((nint)Helpers.ThrowIfNull(alloc, "alloc").PageSize) { pageAlloc = alloc; } protected override bool TryAllocateNewPage(AllocationRequest request, [MaybeNullWhen(false)] out IAllocatedMemory allocated) { if (!pageAlloc.TryAllocatePage(base.PageSize, request.Executable, out var allocated2)) { allocated = null; return false; } Page page = new Page(this, allocated2, (uint)base.PageSize, request.Executable); InsertAllocatedPage(page); if (!page.TryAllocate((uint)request.Size, (uint)request.Alignment, out PageAllocation alloc)) { RegisterForCleanup(page); allocated = null; return false; } allocated = alloc; return true; } protected override bool TryAllocateNewPage(PositionedAllocationRequest request, nint targetPage, nint lowPageBound, nint highPageBound, [MaybeNullWhen(false)] out IAllocatedMemory allocated) { nint target = request.Target; nint page = targetPage; nint page2 = targetPage + base.PageSize; while (page >= lowPageBound || page2 < highPageBound) { while (page2 < highPageBound && (page < lowPageBound || target - page > page2 - target)) { if (TryAllocNewPage(request, ref page2, goingUp: true, out allocated)) { return true; } } while (page >= lowPageBound && (page2 >= highPageBound || target - page < page2 - target)) { if (TryAllocNewPage(request, ref page, goingUp: false, out allocated)) { return true; } } } allocated = null; return false; } private bool TryAllocNewPage(PositionedAllocationRequest request, ref nint page, bool goingUp, [MaybeNullWhen(false)] out IAllocatedMemory allocated) { if (pageAlloc.TryQueryPage(page, out var isFree, out var allocBase, out var allocSize)) { if (isFree && pageAlloc.TryAllocatePage(page, base.PageSize, request.Base.Executable, out var allocated2)) { Page page2 = new Page(this, allocated2, (uint)base.PageSize, request.Base.Executable); InsertAllocatedPage(page2); if (page2.TryAllocate((uint)request.Base.Size, (uint)request.Base.Alignment, out PageAllocation alloc)) { allocated = alloc; return true; } RegisterForCleanup(page2); } if (goingUp) { page = allocBase + allocSize; } else { page = allocBase - base.PageSize; } allocated = null; return false; } if (goingUp) { page += base.PageSize; } else { page -= base.PageSize; } allocated = null; return false; } protected override bool TryFreePage(Page page, [NotNullWhen(false)] out string? errorMsg) { return pageAlloc.TryFreePage(page.BaseAddr, out errorMsg); } } } namespace MonoMod.Core.Platforms.Architectures { internal sealed class Arm64Arch : IArchitecture { private sealed class BranchRegisterKind : DetourKindBase { public static readonly BranchRegisterKind Instance = new BranchRegisterKind(); public override int Size => 16; public override int GetBytes(nint from, nint to, Span buffer, object? data, out IDisposable? allocHandle) { ((ReadOnlySpan)new byte[16] { 73, 0, 0, 88, 32, 1, 31, 214, 0, 0, 0, 0, 0, 0, 0, 0 }).CopyTo(buffer); Unsafe.WriteUnaligned(ref buffer[8], (ulong)to); allocHandle = null; bool isEnabled; MMDbgLog.DebugLogTraceStringHandler message = new MMDbgLog.DebugLogTraceStringHandler(29, 2, out isEnabled); if (isEnabled) { message.AppendLiteral("Detouring arm64 from 0x"); message.AppendFormatted(from, "X16"); message.AppendLiteral(" to 0x"); message.AppendFormatted(to, "X16"); } MMDbgLog.Trace(ref message); return Size; } public override bool TryGetRetargetInfo(NativeDetourInfo orig, nint to, int maxSize, out NativeDetourInfo retargetInfo) { retargetInfo = orig with { To = to }; return true; } public override int DoRetarget(NativeDetourInfo origInfo, nint to, Span buffer, object? data, out IDisposable? allocationHandle, out bool needsRepatch, out bool disposeOldAlloc) { needsRepatch = true; disposeOldAlloc = true; return GetBytes(origInfo.From, to, buffer, data, out allocationHandle); } } private BytePatternCollection? lazyKnownMethodThunks; private readonly ISystem System; public ArchitectureKind Target => (ArchitectureKind)5; public ArchitectureFeature Features => ArchitectureFeature.Immediate64; public BytePatternCollection KnownMethodThunks => Helpers.GetOrInit(ref lazyKnownMethodThunks, (Func)CreateKnownMethodThunks); public IAltEntryFactory AltEntryFactory => null; public Arm64Arch(ISystem system) { System = system; } public NativeDetourInfo ComputeDetourInfo(nint from, nint target, int maxSizeHint) { x86Shared.FixSizeHint(ref maxSizeHint); if (maxSizeHint < BranchRegisterKind.Instance.Size) { bool isEnabled; MMDbgLog.DebugLogWarningStringHandler message = new MMDbgLog.DebugLogWarningStringHandler(88, 1, out isEnabled); if (isEnabled) { message.AppendLiteral("Size too small for all known detour kinds! Defaulting to BranchRegister. provided size: "); message.AppendFormatted(maxSizeHint); } MMDbgLog.Warning(ref message); } return new NativeDetourInfo(from, target, BranchRegisterKind.Instance, null); } public int GetDetourBytes(NativeDetourInfo info, Span buffer, out IDisposable? allocationHandle) { return DetourKindBase.GetDetourBytes(info, buffer, out allocationHandle); } public NativeDetourInfo ComputeRetargetInfo(NativeDetourInfo detour, nint target, int maxSizeHint = -1) { x86Shared.FixSizeHint(ref maxSizeHint); if (DetourKindBase.TryFindRetargetInfo(detour, target, maxSizeHint, out var retargetInfo)) { return retargetInfo; } return ComputeDetourInfo(detour.From, target, maxSizeHint); } public int GetRetargetBytes(NativeDetourInfo original, NativeDetourInfo retarget, Span buffer, out IDisposable? allocationHandle, out bool needsRepatch, out bool disposeOldAlloc) { return DetourKindBase.DoRetarget(original, retarget, buffer, out allocationHandle, out needsRepatch, out disposeOldAlloc); } public ReadOnlyMemory CreateNativeVtableProxyStubs(nint vtableBase, int vtableSize) { return Shared.CreateVtableStubs(stubData: new byte[28] { 0, 4, 64, 249, 9, 0, 64, 249, 138, 0, 0, 24, 41, 1, 10, 139, 41, 1, 64, 249, 32, 1, 31, 214, 0, 0, 0, 0 }, system: System, vtableBase: vtableBase, vtableSize: vtableSize, indexOffs: 24, premulOffset: true); } public IAllocatedMemory CreateSpecialEntryStub(nint target, nint argument) { ReadOnlySpan readOnlySpan = new byte[32] { 137, 0, 0, 88, 170, 0, 0, 88, 64, 1, 31, 214, 31, 32, 3, 213, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; Span span = stackalloc byte[readOnlySpan.Length]; readOnlySpan.CopyTo(span); Unsafe.WriteUnaligned(ref span[16], argument); Unsafe.WriteUnaligned(ref span[24], target); return Shared.CreateSingleExecutableStub(System, span); } private static BytePatternCollection CreateKnownMethodThunks() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Invalid comparison between Unknown and I4 RuntimeKind runtime = PlatformDetection.Runtime; if (runtime - 1 <= 1) { List list = new List(); list.Add(new BytePattern(new AddressMeaning(AddressKind.Abs64), mustMatchAtStart: true, new byte[24] { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, new byte[24] { 137, 0, 0, 16, 42, 49, 64, 169, 64, 1, 31, 214, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2 })); list.Add(new BytePattern(new AddressMeaning(AddressKind.Abs64), mustMatchAtStart: true, new byte[24] { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, new byte[24] { 139, 0, 0, 16, 106, 49, 64, 169, 64, 1, 31, 214, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2 })); list.Add(new BytePattern(new AddressMeaning(AddressKind.Abs64), mustMatchAtStart: true, new byte[24] { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, new byte[24] { 12, 0, 0, 16, 107, 0, 0, 88, 96, 1, 31, 214, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2 })); list.Add(new BytePattern(new AddressMeaning(AddressKind.Abs64), mustMatchAtStart: true, new byte[32] { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, new byte[32] { 16, 0, 0, 145, 32, 0, 0, 145, 1, 2, 0, 145, 112, 0, 0, 88, 0, 2, 31, 214, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2 })); List list2 = list; ReadOnlySpan readOnlySpan = new int[5] { 4096, 8192, 16384, 32768, 65536 }; ReadOnlySpan readOnlySpan2 = new byte[20] { 11, 0, 2, 88, 96, 1, 31, 214, 12, 0, 2, 88, 43, 0, 2, 88, 96, 1, 31, 214 }; ReadOnlySpan readOnlySpan3 = new byte[24] { 11, 0, 2, 88, 96, 1, 31, 214, 191, 57, 3, 213, 12, 0, 2, 88, 43, 0, 2, 88, 96, 1, 31, 214 }; ReadOnlySpan readOnlySpan4 = new byte[36] { 9, 0, 2, 88, 42, 1, 64, 121, 74, 5, 0, 113, 42, 1, 0, 121, 96, 0, 0, 84, 169, 255, 1, 88, 32, 1, 31, 214, 170, 255, 1, 88, 64, 1, 31, 214 }; ReadOnlyMemory readOnlyMemory = new byte[36] { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }; ReadOnlySpan readOnlySpan5 = readOnlySpan; for (int i = 0; i < readOnlySpan5.Length; i++) { int num = readOnlySpan5[i]; byte[] array = readOnlySpan2.ToArray(); EncodeLdr64LiteralTo(array.AsSpan(0), num, 11); EncodeLdr64LiteralTo(array.AsSpan(8), -8 + num + 8, 12); EncodeLdr64LiteralTo(array.AsSpan(12), -12 + num + 16, 11); list2.Add(new BytePattern(new AddressMeaning(AddressKind.Rel64 | AddressKind.Indirect | AddressKind.ConstantAddr, 0, (uint)num), mustMatchAtStart: true, readOnlyMemory.Slice(0, array.Length), array)); list2.Add(new BytePattern(new AddressMeaning(AddressKind.PrecodeFixupThunkRel64 | AddressKind.Indirect | AddressKind.ConstantAddr, 0, (uint)(num + 16 - 8)), mustMatchAtStart: true, readOnlyMemory.Slice(0, array.Length - 8), array.AsMemory(8))); byte[] array2 = readOnlySpan3.ToArray(); EncodeLdr64LiteralTo(array2.AsSpan(0), num, 11); EncodeLdr64LiteralTo(array2.AsSpan(12), -12 + num + 8, 12); EncodeLdr64LiteralTo(array2.AsSpan(16), -16 + num + 16, 11); list2.Add(new BytePattern(new AddressMeaning(AddressKind.Rel64 | AddressKind.Indirect | AddressKind.ConstantAddr, 0, (uint)num), mustMatchAtStart: true, readOnlyMemory.Slice(0, array2.Length), array2)); list2.Add(new BytePattern(new AddressMeaning(AddressKind.PrecodeFixupThunkRel64 | AddressKind.Indirect | AddressKind.ConstantAddr, 0, (uint)(num + 16 - 8)), mustMatchAtStart: true, readOnlyMemory.Slice(0, array2.Length - 8), array2.AsMemory(8))); byte[] array3 = readOnlySpan4.ToArray(); EncodeLdr64LiteralTo(array3.AsSpan(0), num, 9); EncodeLdr64LiteralTo(array3.AsSpan(20), -20 + num + 8, 9); EncodeLdr64LiteralTo(array3.AsSpan(28), -28 + num + 16, 9); list2.Add(new BytePattern(new AddressMeaning(AddressKind.Rel64 | AddressKind.Indirect | AddressKind.ConstantAddr, 0, (uint)(num + 8)), mustMatchAtStart: true, readOnlyMemory.Slice(0, array3.Length), array3)); } return new BytePatternCollection(list2.ToArray()); } return new BytePatternCollection(); } private static void EncodeLdr64LiteralTo(Span dest, int offset, byte reg) { uint num = (uint)offset >> 2; num &= 0x7FFFF; uint num2 = 1476395008u; num2 |= num << 5; MemoryMarshal.Write(dest, num2 | reg); } } internal abstract class DetourKindBase : INativeDetourKind { public abstract int Size { get; } public abstract int GetBytes(nint from, nint to, Span buffer, object? data, out IDisposable? allocHandle); public static int GetDetourBytes(NativeDetourInfo info, Span buffer, out IDisposable? allocHandle) { Helpers.ThrowIfArgumentNull(info.InternalKind, "info.InternalKind"); if (buffer.Length < info.Size) { throw new ArgumentException("Buffer too short", "buffer"); } return ((DetourKindBase)info.InternalKind).GetBytes(info.From, info.To, buffer, info.InternalData, out allocHandle); } public abstract bool TryGetRetargetInfo(NativeDetourInfo orig, nint to, int maxSize, out NativeDetourInfo retargetInfo); public static bool TryFindRetargetInfo(NativeDetourInfo info, nint to, int maxSize, out NativeDetourInfo retargetInfo) { Helpers.ThrowIfArgumentNull(info.InternalKind, "info.InternalKind"); return ((DetourKindBase)info.InternalKind).TryGetRetargetInfo(info, to, maxSize, out retargetInfo); } public abstract int DoRetarget(NativeDetourInfo origInfo, nint to, Span buffer, object? data, out IDisposable? allocationHandle, out bool needsRepatch, out bool disposeOldAlloc); public static int DoRetarget(NativeDetourInfo orig, NativeDetourInfo info, Span buffer, out IDisposable? allocationHandle, out bool needsRepatch, out bool disposeOldAlloc) { Helpers.ThrowIfArgumentNull(info.InternalKind, "info.InternalKind"); if (buffer.Length < info.Size) { throw new ArgumentException("Buffer too short", "buffer"); } return ((DetourKindBase)info.InternalKind).DoRetarget(orig, info.To, buffer, info.InternalData, out allocationHandle, out needsRepatch, out disposeOldAlloc); } } internal static class Shared { public unsafe static IAllocatedMemory CreateSingleExecutableStub(ISystem system, ReadOnlySpan stubBytes) { IControlFlowGuard controlFlowGuard = system as IControlFlowGuard; if (controlFlowGuard != null && !controlFlowGuard.IsSupported) { controlFlowGuard = null; } Helpers.Assert(system.MemoryAllocator.TryAllocate(new AllocationRequest(stubBytes.Length) { Executable = true, Alignment = (controlFlowGuard?.TargetAlignmentRequirement ?? 1) }, out IAllocatedMemory allocated), (string)null, "system.MemoryAllocator.TryAllocate(new(stubBytes.Length)\n {\n Executable = true,\n Alignment = cfg is not null ? cfg.TargetAlignmentRequirement : 1, // if CFG is supported, use that alignment\n }, out var alloc)"); system.PatchData(PatchTargetKind.Executable, allocated.BaseAddress, stubBytes, default(Span)); if (controlFlowGuard != null) { IControlFlowGuard controlFlowGuard2 = controlFlowGuard; nint baseAddress = allocated.BaseAddress; nint memoryRegionLength = allocated.Size; nint reference = 0; controlFlowGuard2.RegisterValidIndirectCallTargets((void*)baseAddress, memoryRegionLength, new ReadOnlySpan(in reference)); } return allocated; } public unsafe static ReadOnlyMemory CreateVtableStubs(ISystem system, nint vtableBase, int vtableSize, ReadOnlySpan stubData, int indexOffs, bool premulOffset) { IControlFlowGuard controlFlowGuard = system as IControlFlowGuard; if (controlFlowGuard != null && !controlFlowGuard.IsSupported) { controlFlowGuard = null; } int num = stubData.Length; if (controlFlowGuard != null) { int targetAlignmentRequirement = controlFlowGuard.TargetAlignmentRequirement; num = ((num - 1) / targetAlignmentRequirement + 1) * targetAlignmentRequirement; } int maxSize = system.MemoryAllocator.MaxSize; int num2 = num * vtableSize; int num3 = num2 / maxSize; int num4 = maxSize / num; int num5 = num4 * num; int num6 = num2 % num5; IAllocatedMemory[] array = new IAllocatedMemory[num3 + ((num6 != 0) ? 1 : 0)]; byte[] array2 = ArrayPool.Shared.Rent(num5); nint[] array3 = ArrayPool.Shared.Rent(num4); Span span = array2.AsSpan().Slice(0, num5); for (int i = 0; i < num4; i++) { stubData.CopyTo(span.Slice(i * num)); } ref nint vtblBase = ref Unsafe.AsRef((void*)vtableBase); AllocationRequest allocationRequest = new AllocationRequest(num5); allocationRequest.Alignment = controlFlowGuard?.TargetAlignmentRequirement ?? IntPtr.Size; allocationRequest.Executable = true; AllocationRequest allocationRequest2 = allocationRequest; for (int j = 0; j < num3; j++) { Helpers.Assert(system.MemoryAllocator.TryAllocate(allocationRequest2, out IAllocatedMemory allocated), (string)null, "system.MemoryAllocator.TryAllocate(allocReq, out var alloc)"); array[j] = allocated; FillBufferIndicies(num, indexOffs, num4, j, span, premulOffset); FillVtbl(num, num4 * j, ref vtblBase, num4, allocated.BaseAddress, array3); system.PatchData(PatchTargetKind.Executable, allocated.BaseAddress, span, default(Span)); controlFlowGuard?.RegisterValidIndirectCallTargets((void*)allocated.BaseAddress, allocated.Size, array3.AsSpan(0, num4)); } if (num6 > 0) { allocationRequest2 = allocationRequest2 with { Size = num6 }; Helpers.Assert(system.MemoryAllocator.TryAllocate(allocationRequest2, out IAllocatedMemory allocated2), (string)null, "system.MemoryAllocator.TryAllocate(allocReq, out var alloc)"); array[^1] = allocated2; int num7 = num6 / num; FillBufferIndicies(num, indexOffs, num4, num3, span, premulOffset); FillVtbl(num, num4 * num3, ref vtblBase, num7, allocated2.BaseAddress, array3); system.PatchData(PatchTargetKind.Executable, allocated2.BaseAddress, span.Slice(0, num6), default(Span)); controlFlowGuard?.RegisterValidIndirectCallTargets((void*)allocated2.BaseAddress, allocated2.Size, array3.AsSpan(0, num7)); } ArrayPool.Shared.Return(array3); ArrayPool.Shared.Return(array2); return array; static void FillBufferIndicies(int stubSize, int num8, int numPerAlloc, int num10, Span mainAllocBuf, bool premul) { for (int k = 0; k < numPerAlloc; k++) { ref byte destination = ref mainAllocBuf[k * stubSize + num8]; uint num9 = (uint)(numPerAlloc * num10 + k); if (premul) { num9 *= (uint)IntPtr.Size; } Unsafe.WriteUnaligned(ref destination, num9); } } static void FillVtbl(int stubSize, int baseIndex, ref nint source, int numEntries, nint baseAddr, nint[] offsets) { for (int k = 0; k < numEntries; k++) { nint num8 = (offsets[k] = stubSize * k); Unsafe.Add(ref source, baseIndex + k) = baseAddr + num8; } } } } internal sealed class x86Arch : IArchitecture { private sealed class Abs32Kind : DetourKindBase { public static readonly Abs32Kind Instance = new Abs32Kind(); public override int Size => 6; public override int GetBytes(nint from, nint to, Span buffer, object? data, out IDisposable? allocHandle) { buffer[0] = 104; Unsafe.WriteUnaligned(ref buffer[1], Unsafe.As(ref to)); buffer[5] = 195; allocHandle = null; return Size; } public override bool TryGetRetargetInfo(NativeDetourInfo orig, nint to, int maxSize, out NativeDetourInfo retargetInfo) { retargetInfo = orig with { To = to }; return true; } public override int DoRetarget(NativeDetourInfo origInfo, nint to, Span buffer, object? data, out IDisposable? allocationHandle, out bool needsRepatch, out bool disposeOldAlloc) { needsRepatch = true; disposeOldAlloc = true; return GetBytes(origInfo.From, to, buffer, data, out allocationHandle); } } private BytePatternCollection? lazyKnownMethodThunks; private static readonly Func createKnownMethodThunksFunc = CreateKnownMethodThunks; private readonly ISystem system; private const int WinThisVtableThunkIndexOffs = 7; private const int SpecEntryStubArgOffs = 1; private const int SpecEntryStubTargetOffs = 6; public ArchitectureKind Target => (ArchitectureKind)2; public ArchitectureFeature Features => ArchitectureFeature.CreateAltEntryPoint; public BytePatternCollection KnownMethodThunks => Helpers.GetOrInit(ref lazyKnownMethodThunks, createKnownMethodThunksFunc); public IAltEntryFactory AltEntryFactory { get; } private static ReadOnlySpan WinThisVtableProxyThunk => new byte[12] { 139, 73, 4, 139, 1, 255, 160, 85, 85, 85, 85, 204 }; private static ReadOnlySpan SpecEntryStub => new byte[12] { 184, 0, 0, 0, 0, 185, 0, 0, 0, 0, 255, 225 }; private static BytePatternCollection CreateKnownMethodThunks() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Invalid comparison between Unknown and I4 RuntimeKind runtime = PlatformDetection.Runtime; if (runtime - 1 <= 1) { return new BytePatternCollection(new BytePattern(new AddressMeaning(AddressKind.Rel32, 16), 184, 65280, 65280, 65280, 65280, 144, 232, 65280, 65280, 65280, 65280, 233, 65282, 65282, 65282, 65282), new BytePattern(new AddressMeaning(AddressKind.Rel32, 5), true, 233, 65282, 65282, 65282, 65282, 95), new BytePattern(new AddressMeaning(AddressKind.PrecodeFixupThunkRel32, 5), true, 232, 65282, 65282, 65282, 65282, 94), new BytePattern(new AddressMeaning(AddressKind.PrecodeFixupThunkRel32, 5), true, 232, 65282, 65282, 65282, 65282, 204), new BytePattern(new AddressMeaning(AddressKind.Abs32 | AddressKind.Indirect), true, 255, 37, 65282, 65282, 65282, 65282, 161, 65280, 65280, 65280, 65280, 255, 37, 65280, 65280, 65280, 65280), new BytePattern(new AddressMeaning(AddressKind.PrecodeFixupThunkAbs32 | AddressKind.Indirect), true, 161, 65280, 65280, 65280, 65280, 255, 37, 65282, 65282, 65282, 65282), null); } return new BytePatternCollection(); } public NativeDetourInfo ComputeDetourInfo(nint from, nint to, int maxSizeHint = -1) { x86Shared.FixSizeHint(ref maxSizeHint); if (x86Shared.TryRel32Detour(from, to, maxSizeHint, out var info)) { return info; } if (maxSizeHint < Abs32Kind.Instance.Size) { bool isEnabled; MMDbgLog.DebugLogWarningStringHandler message = new MMDbgLog.DebugLogWarningStringHandler(79, 1, out isEnabled); if (isEnabled) { message.AppendLiteral("Size too small for all known detour kinds; defaulting to Abs32. provided size: "); message.AppendFormatted(maxSizeHint); } MMDbgLog.Warning(ref message); } return new NativeDetourInfo(from, to, Abs32Kind.Instance, null); } public int GetDetourBytes(NativeDetourInfo info, Span buffer, out IDisposable? allocationHandle) { return DetourKindBase.GetDetourBytes(info, buffer, out allocationHandle); } public NativeDetourInfo ComputeRetargetInfo(NativeDetourInfo detour, nint to, int maxSizeHint = -1) { x86Shared.FixSizeHint(ref maxSizeHint); if (DetourKindBase.TryFindRetargetInfo(detour, to, maxSizeHint, out var retargetInfo)) { return retargetInfo; } return ComputeDetourInfo(detour.From, to, maxSizeHint); } public int GetRetargetBytes(NativeDetourInfo original, NativeDetourInfo retarget, Span buffer, out IDisposable? allocationHandle, out bool needsRepatch, out bool disposeOldAlloc) { return DetourKindBase.DoRetarget(original, retarget, buffer, out allocationHandle, out needsRepatch, out disposeOldAlloc); } public x86Arch(ISystem system) { this.system = system; AltEntryFactory = new IcedAltEntryFactory(system, 32); } public ReadOnlyMemory CreateNativeVtableProxyStubs(nint vtableBase, int vtableSize) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) OSKind kernel = OSKindExtensions.GetKernel(PlatformDetection.OS); bool premulOffset = true; if (OSKindExtensions.Is(kernel, (OSKind)2)) { ReadOnlySpan winThisVtableProxyThunk = WinThisVtableProxyThunk; int indexOffs = 7; return Shared.CreateVtableStubs(system, vtableBase, vtableSize, winThisVtableProxyThunk, indexOffs, premulOffset); } throw new PlatformNotSupportedException(); } public IAllocatedMemory CreateSpecialEntryStub(nint target, nint argument) { Span span = stackalloc byte[SpecEntryStub.Length]; SpecEntryStub.CopyTo(span); Unsafe.WriteUnaligned(ref span[6], target); Unsafe.WriteUnaligned(ref span[1], argument); return Shared.CreateSingleExecutableStub(system, span); } } internal static class x86Shared { public sealed class Rel32Kind : DetourKindBase { public static readonly Rel32Kind Instance = new Rel32Kind(); public override int Size => 5; public override int GetBytes(nint from, nint to, Span buffer, object? data, out IDisposable? allocHandle) { buffer[0] = 233; Unsafe.WriteUnaligned(ref buffer[1], (int)(to - (from + 5))); allocHandle = null; return Size; } public override bool TryGetRetargetInfo(NativeDetourInfo orig, nint to, int maxSize, out NativeDetourInfo retargetInfo) { nint num = to - (orig.From + 5); if (Is32Bit(num) || Is32Bit(-num)) { retargetInfo = new NativeDetourInfo(orig.From, to, Instance, null); return true; } retargetInfo = default(NativeDetourInfo); return false; } public override int DoRetarget(NativeDetourInfo origInfo, nint to, Span buffer, object? data, out IDisposable? allocationHandle, out bool needsRepatch, out bool disposeOldAlloc) { needsRepatch = true; disposeOldAlloc = true; return GetBytes(origInfo.From, to, buffer, data, out allocationHandle); } } public static void FixSizeHint(ref int sizeHint) { if (sizeHint < 0) { sizeHint = int.MaxValue; } } public unsafe static bool TryRel32Detour(nint from, nint to, int sizeHint, out NativeDetourInfo info) { nint num = to - (from + 5); if (sizeHint >= Rel32Kind.Instance.Size && (Is32Bit(num) || Is32Bit(-num)) && *(byte*)(from + 5) != 95) { info = new NativeDetourInfo(from, to, Rel32Kind.Instance, null); return true; } info = default(NativeDetourInfo); return false; } public static bool Is32Bit(long to) { return (to & 0x7FFFFFFF) == to; } } internal sealed class x86_64Arch : IArchitecture { private sealed class Abs64Kind : DetourKindBase { public static readonly Abs64Kind Instance = new Abs64Kind(); public override int Size => 14; public override int GetBytes(nint from, nint to, Span buffer, object? data, out IDisposable? allocHandle) { buffer[0] = byte.MaxValue; buffer[1] = 37; Unsafe.WriteUnaligned(ref buffer[2], 0); Unsafe.WriteUnaligned(ref buffer[6], (long)to); allocHandle = null; return Size; } public override bool TryGetRetargetInfo(NativeDetourInfo orig, nint to, int maxSize, out NativeDetourInfo retargetInfo) { retargetInfo = orig with { To = to }; return true; } public override int DoRetarget(NativeDetourInfo origInfo, nint to, Span buffer, object? data, out IDisposable? allocationHandle, out bool needsRepatch, out bool disposeOldAlloc) { needsRepatch = true; disposeOldAlloc = true; return GetBytes(origInfo.From, to, buffer, data, out allocationHandle); } } private sealed class Rel32Ind64Kind : DetourKindBase { public static readonly Rel32Ind64Kind Instance = new Rel32Ind64Kind(); public override int Size => 6; public override int GetBytes(nint from, nint to, Span buffer, object? data, out IDisposable? allocHandle) { Helpers.ThrowIfArgumentNull(data, "data"); IAllocatedMemory allocatedMemory = (IAllocatedMemory)data; buffer[0] = byte.MaxValue; buffer[1] = 37; Unsafe.WriteUnaligned(ref buffer[2], (int)(allocatedMemory.BaseAddress - (from + 6))); Unsafe.WriteUnaligned(ref allocatedMemory.Memory[0], to); allocHandle = allocatedMemory; return Size; } public override bool TryGetRetargetInfo(NativeDetourInfo orig, nint to, int maxSize, out NativeDetourInfo retargetInfo) { retargetInfo = orig with { To = to }; return true; } public override int DoRetarget(NativeDetourInfo origInfo, nint to, Span buffer, object? data, out IDisposable? allocationHandle, out bool needsRepatch, out bool disposeOldAlloc) { if (origInfo.InternalKind == this) { needsRepatch = false; disposeOldAlloc = false; Helpers.ThrowIfArgumentNull(data, "data"); IAllocatedMemory allocatedMemory = (IAllocatedMemory)data; Unsafe.WriteUnaligned(ref allocatedMemory.Memory[0], to); allocationHandle = allocatedMemory; return 0; } needsRepatch = true; disposeOldAlloc = true; return GetBytes(origInfo.From, to, buffer, data, out allocationHandle); } } private BytePatternCollection? lazyKnownMethodThunks; private static readonly Func createKnownMethodThunksFunc = CreateKnownMethodThunks; private readonly ISystem system; private const int VtblProxyStubIdxOffs = 9; private const bool VtblProxyStubIdxPremul = true; private const int SpecEntryStubArgOffs = 2; private const int SpecEntryStubTargetOffs = 12; public ArchitectureKind Target => (ArchitectureKind)3; public ArchitectureFeature Features => ArchitectureFeature.Immediate64 | ArchitectureFeature.CreateAltEntryPoint; public BytePatternCollection KnownMethodThunks => Helpers.GetOrInit(ref lazyKnownMethodThunks, createKnownMethodThunksFunc); public IAltEntryFactory AltEntryFactory { get; } private static ReadOnlySpan VtblProxyStubWin => new byte[16] { 72, 139, 73, 8, 72, 139, 1, 255, 160, 85, 85, 85, 85, 204, 204, 204 }; private static ReadOnlySpan VtblProxyStubSysV => new byte[16] { 72, 139, 127, 8, 72, 139, 7, 255, 160, 85, 85, 85, 85, 204, 204, 204 }; private static ReadOnlySpan SpecEntryStub => new byte[23] { 72, 184, 0, 0, 0, 0, 0, 0, 0, 0, 73, 186, 0, 0, 0, 0, 0, 0, 0, 0, 65, 255, 226 }; private static BytePatternCollection CreateKnownMethodThunks() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Invalid comparison between Unknown and I4 RuntimeKind runtime = PlatformDetection.Runtime; if (runtime - 1 <= 1) { return new BytePatternCollection(new BytePattern(new AddressMeaning(AddressKind.Abs64), true, 72, 133, 201, 116, 65280, 72, 139, 1, 73, 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, 73, 59, 194, 116, 65280, 72, 184, 65282, 65282, 65282, 65282, 65282, 65282, 65282, 65282), new BytePattern(new AddressMeaning(AddressKind.Rel32, 5), true, 233, 65282, 65282, 65282, 65282, 95), new BytePattern(new AddressMeaning(AddressKind.Abs64), false, 72, 184, 65282, 65282, 65282, 65282, 65282, 65282, 65282, 65282, 255, 224), new BytePattern(new AddressMeaning(AddressKind.Rel32, 19), mustMatchAtStart: false, new byte[19] { 240, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 240, 255, 255, 0, 0, 0, 0 }, new byte[19] { 64, 184, 0, 0, 0, 0, 0, 0, 0, 0, 102, 255, 0, 15, 133, 2, 2, 2, 2 }), new BytePattern(new AddressMeaning(AddressKind.Abs64), mustMatchAtStart: false, new byte[27] { 240, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 240, 255, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255 }, new byte[27] { 64, 184, 0, 0, 0, 0, 0, 0, 0, 0, 102, 255, 0, 116, 0, 72, 184, 2, 2, 2, 2, 2, 2, 2, 2, 255, 224 }), new BytePattern(new AddressMeaning(AddressKind.PrecodeFixupThunkRel32, 5), true, 232, 65282, 65282, 65282, 65282, 94), new BytePattern(new AddressMeaning(AddressKind.PrecodeFixupThunkRel32, 5), true, 232, 65282, 65282, 65282, 65282, 204), new BytePattern(new AddressMeaning(AddressKind.Indirect, 6), mustMatchAtStart: true, new byte[19] { 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, new byte[19] { 255, 37, 2, 2, 2, 2, 76, 139, 21, 251, 15, 0, 0, 255, 37, 253, 15, 0, 0 }), new BytePattern(new AddressMeaning(AddressKind.PrecodeFixupThunkRel32 | AddressKind.Indirect, 13), mustMatchAtStart: true, new byte[13] { 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0 }, new byte[13] { 76, 139, 21, 251, 15, 0, 0, 255, 37, 2, 2, 2, 2 }), new BytePattern(new AddressMeaning(AddressKind.Indirect, 18), mustMatchAtStart: true, new byte[24] { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255 }, new byte[24] { 72, 139, 5, 249, 15, 0, 0, 102, 255, 8, 116, 6, 255, 37, 2, 2, 2, 2, 255, 37, 248, 15, 0, 0 }), new BytePattern(new AddressMeaning(AddressKind.Indirect, 6), mustMatchAtStart: true, new byte[19] { 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, new byte[19] { 255, 37, 2, 2, 2, 2, 76, 139, 21, 251, 63, 0, 0, 255, 37, 253, 63, 0, 0 }), new BytePattern(new AddressMeaning(AddressKind.PrecodeFixupThunkRel32 | AddressKind.Indirect, 13), mustMatchAtStart: true, new byte[13] { 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0 }, new byte[13] { 76, 139, 21, 251, 63, 0, 0, 255, 37, 2, 2, 2, 2 }), new BytePattern(new AddressMeaning(AddressKind.Indirect, 18), mustMatchAtStart: true, new byte[24] { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255 }, new byte[24] { 72, 139, 5, 249, 63, 0, 0, 102, 255, 8, 116, 6, 255, 37, 2, 2, 2, 2, 255, 37, 248, 63, 0, 0 }), null); } return new BytePatternCollection(); } public x86_64Arch(ISystem system) { this.system = system; AltEntryFactory = new IcedAltEntryFactory(system, 64); } public NativeDetourInfo ComputeDetourInfo(nint from, nint to, int sizeHint) { x86Shared.FixSizeHint(ref sizeHint); if (x86Shared.TryRel32Detour(from, to, sizeHint, out var info)) { return info; } nint num = from + 6; nint num2 = num + int.MinValue; if ((nuint)num2 > (nuint)num) { num2 = 0; } nint num3 = num + int.MaxValue; if ((nuint)num3 < (nuint)num) { num3 = -1; } PositionedAllocationRequest request = new PositionedAllocationRequest(num, num2, num3, new AllocationRequest(IntPtr.Size)); if (sizeHint >= Rel32Ind64Kind.Instance.Size && system.MemoryAllocator.TryAllocateInRange(request, out IAllocatedMemory allocated)) { return new NativeDetourInfo(from, to, Rel32Ind64Kind.Instance, allocated); } if (sizeHint < Abs64Kind.Instance.Size) { bool isEnabled; MMDbgLog.DebugLogWarningStringHandler message = new MMDbgLog.DebugLogWarningStringHandler(79, 1, out isEnabled); if (isEnabled) { message.AppendLiteral("Size too small for all known detour kinds; defaulting to Abs64. provided size: "); message.AppendFormatted(sizeHint); } MMDbgLog.Warning(ref message); } return new NativeDetourInfo(from, to, Abs64Kind.Instance, null); } public int GetDetourBytes(NativeDetourInfo info, Span buffer, out IDisposable? allocHandle) { return DetourKindBase.GetDetourBytes(info, buffer, out allocHandle); } public NativeDetourInfo ComputeRetargetInfo(NativeDetourInfo detour, nint to, int maxSizeHint = -1) { x86Shared.FixSizeHint(ref maxSizeHint); if (DetourKindBase.TryFindRetargetInfo(detour, to, maxSizeHint, out var retargetInfo)) { return retargetInfo; } return ComputeDetourInfo(detour.From, to, maxSizeHint); } public int GetRetargetBytes(NativeDetourInfo original, NativeDetourInfo retarget, Span buffer, out IDisposable? allocationHandle, out bool needsRepatch, out bool disposeOldAlloc) { return DetourKindBase.DoRetarget(original, retarget, buffer, out allocationHandle, out needsRepatch, out disposeOldAlloc); } public ReadOnlyMemory CreateNativeVtableProxyStubs(nint vtableBase, int vtableSize) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) ReadOnlySpan stubData; int indexOffs; bool premulOffset; if (OSKindExtensions.Is(PlatformDetection.OS, (OSKind)2)) { stubData = VtblProxyStubWin; indexOffs = 9; premulOffset = true; } else { stubData = VtblProxyStubSysV; indexOffs = 9; premulOffset = true; } return Shared.CreateVtableStubs(system, vtableBase, vtableSize, stubData, indexOffs, premulOffset); } public IAllocatedMemory CreateSpecialEntryStub(nint target, nint argument) { Span span = stackalloc byte[SpecEntryStub.Length]; SpecEntryStub.CopyTo(span); Unsafe.WriteUnaligned(ref span[12], target); Unsafe.WriteUnaligned(ref span[2], argument); return Shared.CreateSingleExecutableStub(system, span); } } } namespace MonoMod.Core.Platforms.Architectures.AltEntryFactories { internal sealed class IcedAltEntryFactory : IAltEntryFactory { private sealed class PtrCodeReader : CodeReader { public nint Base { get; } public int Position { get; private set; } public PtrCodeReader(nint basePtr) { Base = basePtr; Position = 0; } public unsafe override int ReadByte() { return *(byte*)(Base + Position++); } } private sealed class NullCodeWriter : CodeWriter { public override void WriteByte(byte value) { } } private sealed class BufferCodeWriter : CodeWriter, IDisposable { private readonly ArrayPool pool; private byte[]? buffer; private int pos; public ReadOnlyMemory Data => buffer.AsMemory().Slice(0, pos); public BufferCodeWriter() { pool = ArrayPool.Shared; } public override void WriteByte(byte value) { if (buffer == null) { buffer = pool.Rent(8); } if (buffer.Length <= pos) { byte[] destinationArray = pool.Rent(buffer.Length * 2); Array.Copy(buffer, destinationArray, buffer.Length); pool.Return(buffer); buffer = destinationArray; } buffer[pos++] = value; } public void Reset() { pos = 0; } public void Dispose() { if (buffer != null) { byte[] array = buffer; buffer = null; pool.Return(array); } } } private readonly ISystem system; private readonly IMemoryAllocator alloc; private readonly int bitness; public IcedAltEntryFactory(ISystem system, int bitness) { this.system = system; this.bitness = bitness; alloc = system.MemoryAllocator; } public unsafe nint CreateAlternateEntrypoint(nint entrypoint, int minLength, out IDisposable? handle) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Expected O, but got Unknown //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Invalid comparison between Unknown and I4 //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Invalid comparison between Unknown and I4 //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Expected I4, but got Unknown //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Invalid comparison between Unknown and I4 //IL_0304: Unknown result type (might be due to invalid IL or missing references) //IL_0309: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Expected I4, but got Unknown //IL_016f: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Invalid comparison between Unknown and I4 //IL_01af: Unknown result type (might be due to invalid IL or missing references) //IL_0154: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Unknown result type (might be due to invalid IL or missing references) //IL_0166: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_0142: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_0178: Unknown result type (might be due to invalid IL or missing references) //IL_023f: Unknown result type (might be due to invalid IL or missing references) //IL_0244: Unknown result type (might be due to invalid IL or missing references) //IL_0254: Unknown result type (might be due to invalid IL or missing references) //IL_0259: Unknown result type (might be due to invalid IL or missing references) //IL_025d: Unknown result type (might be due to invalid IL or missing references) //IL_0262: Unknown result type (might be due to invalid IL or missing references) //IL_01e8: Unknown result type (might be due to invalid IL or missing references) //IL_01ed: Unknown result type (might be due to invalid IL or missing references) //IL_022f: Unknown result type (might be due to invalid IL or missing references) //IL_0196: Unknown result type (might be due to invalid IL or missing references) //IL_03b0: Unknown result type (might be due to invalid IL or missing references) PtrCodeReader ptrCodeReader = new PtrCodeReader(entrypoint); Decoder val = Decoder.Create(bitness, (CodeReader)(object)ptrCodeReader, (ulong)entrypoint, (DecoderOptions)3); InstructionList val2 = new InstructionList(); while (ptrCodeReader.Position < minLength) { val.Decode(ref val2.AllocUninitializedElement()); } bool flag = false; Enumerator enumerator = val2.GetEnumerator(); try { while (((Enumerator)(ref enumerator)).MoveNext()) { if (((Instruction)(ref ((Enumerator)(ref enumerator)).Current)).IsIPRelativeMemoryOperand) { flag = true; break; } } } finally { ((IDisposable)(*(Enumerator*)(&enumerator))/*cast due to .constrained prefix*/).Dispose(); } Instruction val3 = val2[val2.Count - 1]; Encoder val4; Instruction val5; Code code2; if ((int)((Instruction)(ref val3)).Mnemonic == 59) { val4 = Encoder.Create(bitness, (CodeWriter)(object)new NullCodeWriter()); val5 = val3; Code code = ((Instruction)(ref val3)).Code; if ((int)code <= 313) { if ((int)code != 312) { if ((int)code != 313) { goto IL_017c; } code2 = (Code)698; } else { code2 = (Code)697; } } else { switch (code - 691) { case 0: goto IL_0113; case 1: goto IL_011f; case 2: goto IL_012b; } switch (code - 760) { case 3: break; case 4: goto IL_013d; case 5: goto IL_0146; case 0: goto IL_014f; case 1: goto IL_0158; case 2: goto IL_0161; default: goto IL_017c; } code2 = (Code)763; } goto IL_01ad; } Instruction val6 = Instruction.CreateBranch((Code)((bitness == 64) ? 696 : 695), val.IP); val2.Add(ref val6); goto IL_0312; IL_011f: code2 = (Code)695; goto IL_01ad; IL_017c: throw new InvalidOperationException($"Unrecognized call opcode {((Instruction)(ref val3)).Code}"); IL_012b: code2 = (Code)696; goto IL_01ad; IL_0146: code2 = (Code)765; goto IL_01ad; IL_0158: code2 = (Code)767; goto IL_01ad; IL_0312: int size = ptrCodeReader.Position + 5; using (BufferCodeWriter bufferCodeWriter = new BufferCodeWriter()) { IAllocatedMemory allocated; string text = default(string); BlockEncoderResult val7 = default(BlockEncoderResult); ReadOnlyMemory data; while (true) { bufferCodeWriter.Reset(); if (flag) { Helpers.Assert(alloc.TryAllocateInRange(new PositionedAllocationRequest(entrypoint, entrypoint + int.MinValue, entrypoint + int.MaxValue, new AllocationRequest(size) { Executable = true }), out allocated), (string)null, "alloc.TryAllocateInRange(\n new(entrypoint, (nint)entrypoint + int.MinValue, (nint)entrypoint + int.MaxValue,\n new(estTotalSize) { Executable = true }), out allocated)"); } else { Helpers.Assert(alloc.TryAllocate(new AllocationRequest(size) { Executable = true }, out allocated), (string)null, "alloc.TryAllocate(new(estTotalSize) { Executable = true }, out allocated)"); } nint baseAddress = allocated.BaseAddress; if (!BlockEncoder.TryEncode(bitness, new InstructionBlock((CodeWriter)(object)bufferCodeWriter, (IList)val2, (ulong)baseAddress), ref text, ref val7, (BlockEncoderOptions)0)) { allocated.Dispose(); bool isEnabled; MMDbgLog.DebugLogErrorStringHandler message = new MMDbgLog.DebugLogErrorStringHandler(44, 1, out isEnabled); if (isEnabled) { message.AppendLiteral("BlockEncoder failed to encode instructions: "); message.AppendFormatted(text); } MMDbgLog.Error(ref message); throw new InvalidOperationException("BlockEncoder failed to encode instructions: " + text); } data = bufferCodeWriter.Data; if (data.Length == allocated.Size) { break; } data = bufferCodeWriter.Data; size = data.Length; allocated.Dispose(); } ISystem obj = system; nint baseAddress2 = allocated.BaseAddress; data = bufferCodeWriter.Data; obj.PatchData(PatchTargetKind.Executable, baseAddress2, data.Span, default(Span)); handle = allocated; return allocated.BaseAddress; } IL_01ad: ((Instruction)(ref val5)).Code = code2; ((Instruction)(ref val5)).Length = (int)val4.Encode(ref val5, ((Instruction)(ref val5)).IP); ulong nextIP = ((Instruction)(ref val3)).NextIP; Instruction val8; bool flag2; Instruction val9; if (bitness == 32) { val8 = Instruction.Create((Code)142, (uint)nextIP); ((Instruction)(ref val8)).Length = (int)val4.Encode(ref val8, ((Instruction)(ref val5)).IP); ((Instruction)(ref val8)).IP = ((Instruction)(ref val5)).IP; ((Instruction)(ref val5)).IP = ((Instruction)(ref val5)).IP + (ulong)((Instruction)(ref val8)).Length; flag2 = false; val9 = default(Instruction); } else { flag2 = true; val9 = Instruction.CreateDeclareQword(nextIP); MemoryOperand val10 = new MemoryOperand((Register)70, (long)((Instruction)(ref val5)).NextIP); val8 = Instruction.Create((Code)771, ref val10); ((Instruction)(ref val8)).Length = (int)val4.Encode(ref val8, ((Instruction)(ref val5)).IP); ((Instruction)(ref val8)).IP = ((Instruction)(ref val5)).IP; ((Instruction)(ref val5)).IP = ((Instruction)(ref val5)).IP + (ulong)((Instruction)(ref val8)).Length; ((Instruction)(ref val9)).IP = ((Instruction)(ref val5)).NextIP; ((Instruction)(ref val8)).MemoryDisplacement64 = ((Instruction)(ref val9)).IP; } val2.RemoveAt(val2.Count - 1); val2.Add(ref val8); val2.Add(ref val5); if (flag2) { val2.Add(ref val9); } goto IL_0312; IL_0161: code2 = (Code)768; goto IL_01ad; IL_014f: code2 = (Code)766; goto IL_01ad; IL_013d: code2 = (Code)764; goto IL_01ad; IL_0113: code2 = (Code)694; goto IL_01ad; } } } namespace MonoMod.Core.Interop { internal static class CoreCLR { public enum CorJitResult { CORJIT_OK } public readonly struct InvokeCompileMethodPtr { private readonly nint methodPtr; public unsafe delegate* InvokeCompileMethod => (delegate*)methodPtr; public unsafe InvokeCompileMethodPtr(delegate* ptr) { methodPtr = (nint)ptr; } } public class V21 { public struct CORINFO_SIG_INST { public uint classInstCount; public unsafe nint* classInst; public uint methInstCount; public unsafe nint* methInst; } public struct CORINFO_SIG_INFO { public int callConv; public nint retTypeClass; public nint retTypeSigClass; public byte retType; public byte flags; public ushort numArgs; public CORINFO_SIG_INST sigInst; public nint args; public nint pSig; public uint sbSig; public nint scope; public uint token; } public struct CORINFO_METHOD_INFO { public nint ftn; public nint scope; public unsafe byte* ILCode; public uint ILCodeSize; public uint maxStack; public uint EHcount; public int options; public int regionKind; public CORINFO_SIG_INFO args; public CORINFO_SIG_INFO locals; } [UnmanagedFunctionPointer(CallingConvention.StdCall)] public unsafe delegate CorJitResult CompileMethodDelegate(nint thisPtr, nint corJitInfo, CORINFO_METHOD_INFO* methodInfo, uint flags, byte** nativeEntry, uint* nativeSizeOfCode); public unsafe static InvokeCompileMethodPtr InvokeCompileMethodPtr => new InvokeCompileMethodPtr((delegate*)(&InvokeCompileMethod)); public unsafe static CorJitResult InvokeCompileMethod(nint functionPtr, nint thisPtr, nint corJitInfo, CORINFO_METHOD_INFO* methodInfo, uint flags, byte** pNativeEntry, uint* pNativeSizeOfCode) { if (functionPtr == IntPtr.Zero) { *pNativeEntry = null; *pNativeSizeOfCode = 0u; return CorJitResult.CORJIT_OK; } return ((delegate* unmanaged[Stdcall])functionPtr)(thisPtr, corJitInfo, methodInfo, flags, pNativeEntry, pNativeSizeOfCode); } } public class V30 : V21 { } public class V31 : V30 { } public class V50 : V31 { } public class V100 : V90 { public new static class ICorJitInfoVtable { public const int AllocMemIndex = 160; public const int TotalVtableCount = 176; } } public readonly struct InvokeCompileMethodHookPostPtr { private readonly nint methodPtr; public unsafe delegate* InvokeCompileMethodHookPost => (delegate*)methodPtr; public unsafe InvokeCompileMethodHookPostPtr(delegate* ptr) { methodPtr = (nint)ptr; } } public readonly struct InvokeAllocMemPtr { private readonly nint methodPtr; public unsafe delegate* InvokeAllocMem => (delegate*)methodPtr; public unsafe InvokeAllocMemPtr(delegate* ptr) { methodPtr = (nint)ptr; } } public class V60 : V50 { public static class ICorJitInfoVtable { public const int AllocMemIndex = 156; public const int TotalVtableCount = 173; } public struct AllocMemArgs { public uint hotCodeSize; public uint coldCodeSize; public uint roDataSize; public uint xcptnsCount; public int flag; public nint hotCodeBlock; public nint hotCodeBlockRW; public nint coldCodeBlock; public nint coldCodeBlockRW; public nint roDataBlock; public nint roDataBlockRW; } [UnmanagedFunctionPointer(CallingConvention.ThisCall)] public unsafe delegate void AllocMemDelegate(nint thisPtr, AllocMemArgs* args); [UnmanagedFunctionPointer(CallingConvention.ThisCall)] public new unsafe delegate CorJitResult CompileMethodDelegate(nint thisPtr, nint corJitInfo, CORINFO_METHOD_INFO* methodInfo, uint flags, byte** nativeEntry, uint* nativeSizeOfCode); [UnmanagedFunctionPointer(CallingConvention.ThisCall)] public unsafe delegate CorJitResult CompileMethodHookPostDelegate(nint thisPtr, nint corJitInfo, CORINFO_METHOD_INFO* methodInfo, uint flags, byte** nativeEntry, uint* nativeSizeOfCode, CorJitResult res, AllocMemArgs* allocMemArgs); public enum MethodClassification { IL, FCall, NDirect, EEImpl, Array, Instantiated, ComInterop, Dynamic } [Flags] public enum MethodDescClassification : ushort { ClassificationMask = 7, HasNonVtableSlot = 8, MethodImpl = 0x10, HasNativeCodeSlot = 0x20, HasComPlusCallInfo = 0x40, Static = 0x80, Duplicate = 0x400, VerifiedState = 0x800, Verifiable = 0x1000, NotInline = 0x2000, Synchronized = 0x4000, RequiresFullSlotNumber = 0x8000 } public struct RelativePointer { private nint m_delta; public unsafe void* Value { get { nint delta = m_delta; if (delta != 0) { return Unsafe.AsPointer(ref Unsafe.AddByteOffset(ref this, delta)); } return null; } } public RelativePointer(nint delta) { m_delta = delta; } } public struct RelativeFixupPointer { private nint m_delta; public const nint FIXUP_POINTER_INDIRECTION = 1; public unsafe void* Value { get { nint delta = m_delta; if (delta == 0) { return null; } nint num = (nint)Unsafe.AsPointer(ref Unsafe.AddByteOffset(ref this, delta)); if ((num & 1) != 0) { num = *(nint*)(num - 1); } return (void*)num; } } } public struct MethodDesc { [Flags] public enum Flags3 : ushort { TokenRemainderMask = 0x3FFF, HasForwardedValuetypeParameter = 0x4000, ValueTypeParametersWalked = 0x4000, DoesNotHaveEquivalentValuetypeParameters = 0x8000 } [Flags] public enum Flags2 : byte { HasStableEntryPoint = 1, HasPrecode = 2, IsUnboxingStub = 4, IsJitIntrinsic = 0x10, IsEligibleForTieredCompilation = 0x20, RequiresCovariantReturnTypeChecking = 0x40 } public static readonly nuint Alignment = (nuint)((IntPtr.Size == 8) ? 8 : 4); public Flags3 m_wFlags3AndTokenRemainder; public byte m_chunkIndex; public Flags2 m_bFlags2; public const ushort PackedSlot_SlotMask = 1023; public const ushort PackedSlot_NameHashMask = 64512; public ushort m_wSlotNumber; public MethodDescClassification m_wFlags; private unsafe static readonly nuint[] s_ClassificationSizeTable = new nuint[8] { (nuint)sizeof(MethodDesc), (nuint)FCallMethodDescPtr.CurrentSize, (nuint)NDirectMethodDescPtr.CurrentSize, (nuint)EEImplMethodDescPtr.CurrentSize, (nuint)ArrayMethodDescPtr.CurrentSize, (nuint)sizeof(InstantiatedMethodDesc), (nuint)sizeof(ComPlusCallMethodDesc), (nuint)DynamicMethodDescPtr.CurrentSize }; public ushort SlotNumber { get { if (!Helpers.Has(m_wFlags, MethodDescClassification.RequiresFullSlotNumber)) { return (ushort)(m_wSlotNumber & 0x3FF); } return m_wSlotNumber; } } public MethodClassification Classification => (MethodClassification)(m_wFlags & MethodDescClassification.ClassificationMask); public unsafe MethodDescChunk* MethodDescChunk => (MethodDescChunk*)((byte*)Unsafe.AsPointer(ref this) - (nuint)((nint)sizeof(MethodDescChunk) + (nint)(m_chunkIndex * Alignment))); public unsafe MethodTable* MethodTable => MethodDescChunk->m_methodTable; public bool HasNonVtableSlot => Helpers.Has(m_wFlags, MethodDescClassification.HasNonVtableSlot); public bool HasStableEntryPoint => Helpers.Has(m_bFlags2, Flags2.HasStableEntryPoint); public bool HasPrecode => Helpers.Has(m_bFlags2, Flags2.HasPrecode); public bool HasNativeCodeSlot => Helpers.Has(m_wFlags, MethodDescClassification.HasNativeCodeSlot); public bool IsUnboxingStub => Helpers.Has(m_bFlags2, Flags2.IsUnboxingStub); public unsafe bool HasMethodInstantiation { get { if (TryAsInstantiated(out var md)) { return md->IMD_HasMethodInstantiation; } return false; } } public unsafe bool IsGenericMethodDefinition { get { if (TryAsInstantiated(out var md)) { return md->IMD_IsGenericMethodDefinition; } return false; } } public unsafe bool IsInstantiatingStub { get { if (!IsUnboxingStub && TryAsInstantiated(out var md)) { return md->IMD_IsWrapperStubWithInstantiations; } return false; } } public bool IsWrapperStub { get { if (!IsUnboxingStub) { return IsInstantiatingStub; } return true; } } public bool IsTightlyBoundToMethodTable { get { if (!HasNonVtableSlot) { return true; } if (HasMethodInstantiation) { return IsGenericMethodDefinition; } if (IsWrapperStub) { return false; } return true; } } public unsafe void* GetMethodEntryPoint() { if (HasNonVtableSlot) { nuint baseSize = GetBaseSize(); byte* ptr = (byte*)Unsafe.AsPointer(ref this) + baseSize; if (!Helpers.Has(MethodDescChunk->m_flagsAndTokenRange, V60.MethodDescChunk.Flags.IsZapped)) { return *(void**)ptr; } return new RelativePointer((nint)ptr).Value; } return MethodTable->GetSlot(SlotNumber); } public unsafe bool TryAsFCall(out FCallMethodDescPtr md) { if (Classification == MethodClassification.FCall) { md = new FCallMethodDescPtr(Unsafe.AsPointer(ref this), FCallMethodDescPtr.CurrentVtable); return true; } md = default(FCallMethodDescPtr); return false; } public unsafe bool TryAsNDirect(out NDirectMethodDescPtr md) { if (Classification == MethodClassification.NDirect) { md = new NDirectMethodDescPtr(Unsafe.AsPointer(ref this), NDirectMethodDescPtr.CurrentVtable); return true; } md = default(NDirectMethodDescPtr); return false; } public unsafe bool TryAsEEImpl(out EEImplMethodDescPtr md) { if (Classification == MethodClassification.EEImpl) { md = new EEImplMethodDescPtr(Unsafe.AsPointer(ref this), EEImplMethodDescPtr.CurrentVtable); return true; } md = default(EEImplMethodDescPtr); return false; } public unsafe bool TryAsArray(out ArrayMethodDescPtr md) { if (Classification == MethodClassification.Array) { md = new ArrayMethodDescPtr(Unsafe.AsPointer(ref this), ArrayMethodDescPtr.CurrentVtable); return true; } md = default(ArrayMethodDescPtr); return false; } public unsafe bool TryAsInstantiated(out InstantiatedMethodDesc* md) { if (Classification == MethodClassification.Instantiated) { md = (InstantiatedMethodDesc*)Unsafe.AsPointer(ref this); return true; } md = default(InstantiatedMethodDesc*); return false; } public unsafe bool TryAsComPlusCall(out ComPlusCallMethodDesc* md) { if (Classification == MethodClassification.ComInterop) { md = (ComPlusCallMethodDesc*)Unsafe.AsPointer(ref this); return true; } md = default(ComPlusCallMethodDesc*); return false; } public unsafe bool TryAsDynamic(out DynamicMethodDescPtr md) { if (Classification == MethodClassification.Dynamic) { md = new DynamicMethodDescPtr(Unsafe.AsPointer(ref this), DynamicMethodDescPtr.CurrentVtable); return true; } md = default(DynamicMethodDescPtr); return false; } public unsafe nuint SizeOf(bool includeNonVtable = true, bool includeMethodImpl = true, bool includeComPlus = true, bool includeNativeCode = true) { nuint num = (nuint)((nint)GetBaseSize() + (nint)((includeNonVtable && Helpers.Has(m_wFlags, MethodDescClassification.HasNonVtableSlot)) ? sizeof(void*) : 0) + ((includeMethodImpl && Helpers.Has(m_wFlags, MethodDescClassification.MethodImpl)) ? ((nint)sizeof(void*) * (nint)2) : 0) + ((includeComPlus && Helpers.Has(m_wFlags, MethodDescClassification.HasComPlusCallInfo)) ? sizeof(void*) : 0) + ((includeNativeCode && Helpers.Has(m_wFlags, MethodDescClassification.HasNativeCodeSlot)) ? sizeof(void*) : 0)); if (includeNativeCode && HasNativeCodeSlot) { num += (nuint)((((nuint)GetAddrOfNativeCodeSlot() & (nuint)1u) != 0) ? sizeof(void*) : 0); } return num; } public unsafe void* GetNativeCode() { if (HasNativeCodeSlot) { void* ptr = *(void**)((nuint)GetAddrOfNativeCodeSlot() & (nuint)(~(nint)1)); if (ptr != null) { return ptr; } } if (!HasStableEntryPoint || HasPrecode) { return null; } return GetStableEntryPoint(); } public unsafe void* GetStableEntryPoint() { return GetMethodEntryPoint(); } public unsafe static MethodDesc* FindTightlyBoundWrappedMethodDesc(MethodDesc* pMD) { if (pMD->IsUnboxingStub && pMD->TryAsInstantiated(out var md)) { pMD = md->IMD_GetWrappedMethodDesc(); } if (!pMD->IsTightlyBoundToMethodTable) { pMD = pMD->GetCanonicalMethodTable()->GetParallelMethodDesc(pMD); } if (pMD->IsUnboxingStub) { pMD = GetNextIntroducedMethod(pMD); } return pMD; } public unsafe static MethodDesc* GetNextIntroducedMethod(MethodDesc* pMD) { MethodDescChunk* methodDescChunk = pMD->MethodDescChunk; nuint num = (nuint)((byte*)pMD + pMD->SizeOf()); nuint num2 = (nuint)((byte*)methodDescChunk + methodDescChunk->SizeOf); if (num < num2) { return (MethodDesc*)num; } methodDescChunk = methodDescChunk->m_next; if (methodDescChunk != null) { return methodDescChunk->FirstMethodDesc; } return null; } public unsafe MethodTable* GetCanonicalMethodTable() { return MethodTable->GetCanonicalMethodTable(); } public unsafe void* GetAddrOfNativeCodeSlot() { nuint byteOffset = SizeOf(includeNonVtable: true, includeMethodImpl: true, includeComPlus: false, includeNativeCode: false); return Unsafe.AsPointer(ref Unsafe.AddByteOffset(ref this, byteOffset)); } public nuint GetBaseSize() { return GetBaseSize(Classification); } public static nuint GetBaseSize(MethodClassification classification) { return s_ClassificationSizeTable[(int)classification]; } } public struct MethodDescChunk { [Flags] public enum Flags : ushort { TokenRangeMask = 0x3FF, HasCompactEntrypoints = 0x4000, IsZapped = 0x8000 } public unsafe MethodTable* m_methodTable; public unsafe MethodDescChunk* m_next; public byte m_size; public byte m_count; public Flags m_flagsAndTokenRange; public unsafe MethodDesc* FirstMethodDesc => (MethodDesc*)((byte*)Unsafe.AsPointer(ref this) + sizeof(MethodDescChunk)); public uint Size => (uint)(m_size + 1); public uint Count => (uint)(m_count + 1); public unsafe nuint SizeOf => (nuint)sizeof(MethodDescChunk) + Size * MethodDesc.Alignment; } [FatInterface] public struct StoredSigMethodDescPtr { private unsafe readonly void* ptr_; private readonly nint[] vtbl_; public static nint[] CurrentVtable { get; } = (IntPtr.Size == 8) ? StoredSigMethodDesc_64.FatVtable_ : StoredSigMethodDesc_32.FatVtable_; public unsafe static int CurrentSize { get; } = (IntPtr.Size == 8) ? sizeof(StoredSigMethodDesc_64) : sizeof(StoredSigMethodDesc_32); public unsafe void* m_pSig { [FatInterfaceIgnore] get { return GetPSig(); } } public uint m_cSig { [FatInterfaceIgnore] get { return GetCSig(); } } private unsafe void* GetPSig() { return ((delegate*)vtbl_[0])(ptr_); } private unsafe uint GetCSig() { return ((delegate*)vtbl_[0])(ptr_); } public unsafe StoredSigMethodDescPtr(void* ptr, nint[] vtbl) { ptr_ = ptr; vtbl_ = vtbl; } } [FatInterfaceImpl(typeof(StoredSigMethodDescPtr))] public struct StoredSigMethodDesc_64 { public MethodDesc @base; public unsafe void* m_pSig; public uint m_cSig; public uint m_dwExtendedFlags; private static nint[]? fatVtable_; public unsafe static nint[] FatVtable_ { get { object obj = fatVtable_; if (obj == null) { obj = new nint[2] { (nint)(delegate*)(&S_GetPSig_), (nint)(delegate*)(&S_GetCSig_) }; fatVtable_ = (nint[]?)obj; } return (nint[])obj; unsafe static uint S_GetCSig_(void* ptr__) { return ((StoredSigMethodDesc_64*)ptr__)->GetCSig(); } unsafe static void* S_GetPSig_(void* ptr__) { return ((StoredSigMethodDesc_64*)ptr__)->GetPSig(); } } } private unsafe void* GetPSig() { return m_pSig; } private uint GetCSig() { return m_cSig; } } [FatInterfaceImpl(typeof(StoredSigMethodDescPtr))] public struct StoredSigMethodDesc_32 { public MethodDesc @base; public unsafe void* m_pSig; public uint m_cSig; private static nint[]? fatVtable_; public unsafe static nint[] FatVtable_ { get { object obj = fatVtable_; if (obj == null) { obj = new nint[2] { (nint)(delegate*)(&S_GetPSig_), (nint)(delegate*)(&S_GetCSig_) }; fatVtable_ = (nint[]?)obj; } return (nint[])obj; unsafe static uint S_GetCSig_(void* ptr__) { return ((StoredSigMethodDesc_32*)ptr__)->GetCSig(); } unsafe static void* S_GetPSig_(void* ptr__) { return ((StoredSigMethodDesc_32*)ptr__)->GetPSig(); } } } private unsafe void* GetPSig() { return m_pSig; } private uint GetCSig() { return m_cSig; } } [FatInterface] public struct FCallMethodDescPtr { private unsafe readonly void* ptr_; private readonly nint[] vtbl_; public static nint[] CurrentVtable { get; } = (IntPtr.Size == 8) ? FCallMethodDesc_64.FatVtable_ : FCallMethodDesc_32.FatVtable_; public unsafe static int CurrentSize { get; } = (IntPtr.Size == 8) ? sizeof(FCallMethodDesc_64) : sizeof(FCallMethodDesc_32); public uint m_dwECallID { [FatInterfaceIgnore] get { return GetECallID(); } } private unsafe uint GetECallID() { return ((delegate*)vtbl_[0])(ptr_); } public unsafe FCallMethodDescPtr(void* ptr, nint[] vtbl) { ptr_ = ptr; vtbl_ = vtbl; } } [FatInterfaceImpl(typeof(FCallMethodDescPtr))] public struct FCallMethodDesc_64 { public MethodDesc @base; public uint m_dwECallID; public uint m_padding; private static nint[]? fatVtable_; public unsafe static nint[] FatVtable_ { get { object obj = fatVtable_; if (obj == null) { obj = new nint[1] { (nint)(delegate*)(&S_GetECallID_) }; fatVtable_ = (nint[]?)obj; } return (nint[])obj; unsafe static uint S_GetECallID_(void* ptr__) { return ((FCallMethodDesc_64*)ptr__)->GetECallID(); } } } private uint GetECallID() { return m_dwECallID; } } [FatInterfaceImpl(typeof(FCallMethodDescPtr))] public struct FCallMethodDesc_32 { public MethodDesc @base; public uint m_dwECallID; private static nint[]? fatVtable_; public unsafe static nint[] FatVtable_ { get { object obj = fatVtable_; if (obj == null) { obj = new nint[1] { (nint)(delegate*)(&S_GetECallID_) }; fatVtable_ = (nint[]?)obj; } return (nint[])obj; unsafe static uint S_GetECallID_(void* ptr__) { return ((FCallMethodDesc_32*)ptr__)->GetECallID(); } } } private uint GetECallID() { return m_dwECallID; } } public struct DynamicResolver { } [Flags] public enum DynamicMethodDesc_ExtendedFlags { Attrs = 0xFFFF, ILStubAttrs = 0x17, MemberAccessMask = 7, ReverseStub = 8, Static = 0x10, CALLIStub = 0x20, DelegateStub = 0x40, StructMarshalStub = 0x80, Unbreakable = 0x100, SignatureNeedsResture = 0x400, StubNeedsCOMStarted = 0x800, MulticastStub = 0x1000, UnboxingILStub = 0x2000, WrapperDelegateStub = 0x4000, UnmanagedCallersOnlyStub = 0x8000, ILStub = 0x10000, LCGMethod = 0x20000, StackArgSize = 0xFFC0000 } [FatInterface] public struct DynamicMethodDescPtr { private unsafe readonly void* ptr_; private readonly nint[] vtbl_; public static nint[] CurrentVtable { get; } = (IntPtr.Size == 8) ? DynamicMethodDesc_64.FatVtable_ : DynamicMethodDesc_32.FatVtable_; public unsafe static int CurrentSize { get; } = (IntPtr.Size == 8) ? sizeof(DynamicMethodDesc_64) : sizeof(DynamicMethodDesc_32); public DynamicMethodDesc_ExtendedFlags Flags => GetFlags(); private unsafe DynamicMethodDesc_ExtendedFlags GetFlags() { return ((delegate*)vtbl_[0])(ptr_); } public unsafe DynamicMethodDescPtr(void* ptr, nint[] vtbl) { ptr_ = ptr; vtbl_ = vtbl; } } [FatInterfaceImpl(typeof(DynamicMethodDescPtr))] public struct DynamicMethodDesc_64 { public StoredSigMethodDesc_64 @base; public unsafe byte* m_pszMethodName; public unsafe DynamicResolver* m_pResolver; private static nint[]? fatVtable_; public DynamicMethodDesc_ExtendedFlags Flags => GetFlags(); public unsafe static nint[] FatVtable_ { get { object obj = fatVtable_; if (obj == null) { obj = new nint[1] { (nint)(delegate*)(&S_GetFlags_) }; fatVtable_ = (nint[]?)obj; } return (nint[])obj; unsafe static DynamicMethodDesc_ExtendedFlags S_GetFlags_(void* ptr__) { return ((DynamicMethodDesc_64*)ptr__)->GetFlags(); } } } private DynamicMethodDesc_ExtendedFlags GetFlags() { return (DynamicMethodDesc_ExtendedFlags)@base.m_dwExtendedFlags; } } [FatInterfaceImpl(typeof(DynamicMethodDescPtr))] public struct DynamicMethodDesc_32 { public StoredSigMethodDesc_32 @base; public unsafe byte* m_pszMethodName; public unsafe DynamicResolver* m_pResolver; public uint m_dwExtendedFlags; private static nint[]? fatVtable_; public DynamicMethodDesc_ExtendedFlags Flags => GetFlags(); public unsafe static nint[] FatVtable_ { get { object obj = fatVtable_; if (obj == null) { obj = new nint[1] { (nint)(delegate*)(&S_GetFlags_) }; fatVtable_ = (nint[]?)obj; } return (nint[])obj; unsafe static DynamicMethodDesc_ExtendedFlags S_GetFlags_(void* ptr__) { return ((DynamicMethodDesc_32*)ptr__)->GetFlags(); } } } private DynamicMethodDesc_ExtendedFlags GetFlags() { return (DynamicMethodDesc_ExtendedFlags)m_dwExtendedFlags; } } [FatInterface] public struct ArrayMethodDescPtr { private unsafe readonly void* ptr_; private readonly nint[] vtbl_; public static nint[] CurrentVtable { get; } = (IntPtr.Size == 8) ? ArrayMethodDesc_64.FatVtable_ : ArrayMethodDesc_32.FatVtable_; public unsafe static int CurrentSize { get; } = (IntPtr.Size == 8) ? sizeof(ArrayMethodDesc_64) : sizeof(ArrayMethodDesc_32); public unsafe ArrayMethodDescPtr(void* ptr, nint[] vtbl) { ptr_ = ptr; vtbl_ = vtbl; } } public enum ArrayFunc { Get, Set, Address, Ctor } [FatInterfaceImpl(typeof(ArrayMethodDescPtr))] public struct ArrayMethodDesc_64 { public StoredSigMethodDesc_64 @base; private static nint[]? fatVtable_; public static nint[] FatVtable_ => fatVtable_ ?? (fatVtable_ = new nint[0]); } [FatInterfaceImpl(typeof(ArrayMethodDescPtr))] public struct ArrayMethodDesc_32 { public StoredSigMethodDesc_32 @base; private static nint[]? fatVtable_; public static nint[] FatVtable_ => fatVtable_ ?? (fatVtable_ = new nint[0]); } public struct NDirectWriteableData { } [Flags] public enum NDirectMethodDesc_Flags : ushort { EarlyBound = 1, HasSuppressUnmanagedCodeAccess = 2, DefaultDllImportSearchPathIsCached = 4, IsMarshalingRequiredCached = 0x10, CachedMarshalingRequired = 0x20, NativeAnsi = 0x40, LastError = 0x80, NativeNoMangle = 0x100, VarArgs = 0x200, StdCall = 0x400, ThisCall = 0x800, IsQCall = 0x1000, DefaultDllImportSearchPathsStatus = 0x2000, NDirectPopulated = 0x8000 } [FatInterface] public struct NDirectMethodDescPtr { private unsafe readonly void* ptr_; private readonly nint[] vtbl_; public static nint[] CurrentVtable { get; } = ((int)PlatformDetection.Architecture == 2) ? NDirectMethodDesc_x86.FatVtable_ : NDirectMethodDesc_other.FatVtable_; public unsafe static int CurrentSize { get; } = ((int)PlatformDetection.Architecture == 2) ? sizeof(NDirectMethodDesc_x86) : sizeof(NDirectMethodDesc_other); public unsafe NDirectMethodDescPtr(void* ptr, nint[] vtbl) { ptr_ = ptr; vtbl_ = vtbl; } } [FatInterfaceImpl(typeof(NDirectMethodDescPtr))] public struct NDirectMethodDesc_other { public struct NDirect { public unsafe void* m_pNativeNDirectTarget; public unsafe byte* m_pszEntrypointName; public nuint union_pszLibName_dwECallID; public unsafe NDirectWriteableData* m_pWriteableData; public unsafe void* m_pImportThunkGlue; public uint m_DefaultDllImportSearchPathsAttributeValue; public NDirectMethodDesc_Flags m_wFlags; public unsafe MethodDesc* m_pStubMD; } public MethodDesc @base; private NDirect ndirect; private static nint[]? fatVtable_; public static nint[] FatVtable_ => fatVtable_ ?? (fatVtable_ = new nint[0]); } [FatInterfaceImpl(typeof(NDirectMethodDescPtr))] public struct NDirectMethodDesc_x86 { public struct NDirect { public unsafe void* m_pNativeNDirectTarget; public unsafe byte* m_pszEntrypointName; public nuint union_pszLibName_dwECallID; public unsafe NDirectWriteableData* m_pWriteableData; public unsafe void* m_pImportThunkGlue; public uint m_DefaultDllImportSearchPathsAttributeValue; public NDirectMethodDesc_Flags m_wFlags; public ushort m_cbStackArgumentSize; public unsafe MethodDesc* m_pStubMD; } public MethodDesc @base; private NDirect ndirect; private static nint[]? fatVtable_; public static nint[] FatVtable_ => fatVtable_ ?? (fatVtable_ = new nint[0]); } [FatInterface] public struct EEImplMethodDescPtr { private unsafe readonly void* ptr_; private readonly nint[] vtbl_; public static nint[] CurrentVtable { get; } = (IntPtr.Size == 8) ? EEImplMethodDesc_64.FatVtable_ : EEImplMethodDesc_32.FatVtable_; public unsafe static int CurrentSize { get; } = (IntPtr.Size == 8) ? sizeof(EEImplMethodDesc_64) : sizeof(EEImplMethodDesc_32); public unsafe EEImplMethodDescPtr(void* ptr, nint[] vtbl) { ptr_ = ptr; vtbl_ = vtbl; } } [FatInterfaceImpl(typeof(EEImplMethodDescPtr))] public struct EEImplMethodDesc_64 { public StoredSigMethodDesc_64 @base; private static nint[]? fatVtable_; public static nint[] FatVtable_ => fatVtable_ ?? (fatVtable_ = new nint[0]); } [FatInterfaceImpl(typeof(EEImplMethodDescPtr))] public struct EEImplMethodDesc_32 { public StoredSigMethodDesc_32 @base; private static nint[]? fatVtable_; public static nint[] FatVtable_ => fatVtable_ ?? (fatVtable_ = new nint[0]); } public struct ComPlusCallMethodDesc { public MethodDesc @base; public unsafe void* m_pComPlusCallInfo; } public struct InstantiatedMethodDesc { [Flags] public enum Flags : ushort { KindMask = 7, GenericMethodDefinition = 0, UnsharedMethodInstantiation = 1, SharedMethodInstantiation = 2, WrapperStubWithInstantiations = 3, EnCAddedMethod = 7, Unrestored = 8, HasComPlusCallInfo = 0x10 } public MethodDesc @base; public unsafe void* union_pDictLayout_pWrappedMethodDesc; public unsafe Dictionary* m_pPerInstInfo; public Flags m_wFlags2; public ushort m_wNumGenericArgs; public unsafe bool IMD_HasMethodInstantiation { get { if (!IMD_IsGenericMethodDefinition) { return m_pPerInstInfo != null; } return true; } } public bool IMD_IsGenericMethodDefinition => (m_wFlags2 & Flags.KindMask) == 0; public bool IMD_IsWrapperStubWithInstantiations => (m_wFlags2 & Flags.KindMask) == Flags.WrapperStubWithInstantiations; public unsafe MethodDesc* IMD_GetWrappedMethodDesc() { Helpers.Assert(IMD_IsWrapperStubWithInstantiations, (string)null, "IMD_IsWrapperStubWithInstantiations"); return (MethodDesc*)union_pDictLayout_pWrappedMethodDesc; } } public struct Dictionary { } public struct Module { } public struct MethodTableWriteableData { } public struct VTableIndir2_t { public unsafe void* pCode; public unsafe void* Value => pCode; } public struct VTableIndir_t { public unsafe VTableIndir2_t* Value; } private static class MultipurposeSlotHelpers { public unsafe static byte OffsetOfMp1() { MethodTable methodTable = default(MethodTable); return (byte)((byte*)(&methodTable.union_pPerInstInfo_ElementTypeHnd_pMultipurposeSlot1) - (byte*)(&methodTable)); } public unsafe static byte OffsetOfMp2() { MethodTable methodTable = default(MethodTable); return (byte)((byte*)(&methodTable.union_p_InterfaceMap_pMultipurposeSlot2) - (byte*)(&methodTable)); } public unsafe static byte RegularOffset(int index) { return (byte)(sizeof(MethodTable) + index * IntPtr.Size - 2 * IntPtr.Size); } } public struct MethodTable { [Flags] public enum Flags2 : ushort { MultipurposeSlotsMask = 0x1F, HasPerInstInfo = 1, HasInterfaceMap = 2, HasDispatchMapSlot = 4, HasNonVirtualSlots = 8, HasModuleOverride = 0x10, IsZapped = 0x20, IsPreRestored = 0x40, HasModuleDependencies = 0x80, IsIntrinsicType = 0x100, RequiresDispatchTokenFat = 0x200, HasCctor = 0x400, HasVirtualStaticMethods = 0x800, REquiresAlign8 = 0x1000, HasBoxedRegularStatics = 0x2000, HasSingleNonVirtualSlot = 0x4000, DependsOnEquivalentOrForwardedStructs = 0x8000 } public enum UnionLowBits { EEClass, Invalid, MethodTable, Indirection } public uint m_dwFlags; public uint m_BaseSize; public Flags2 m_wFlags2; public ushort m_wToken; public ushort m_wNumVirtuals; public ushort m_wNumInterfaces; private unsafe void* m_pParentMethodTable; public unsafe Module* m_pLoaderModule; public unsafe MethodTableWriteableData* m_pWriteableData; public unsafe void* union_pEEClass_pCanonMT; public unsafe void* union_pPerInstInfo_ElementTypeHnd_pMultipurposeSlot1; public unsafe void* union_p_InterfaceMap_pMultipurposeSlot2; public const int VTABLE_SLOTS_PER_CHUNK = 8; public const int VTABLE_SLOTS_PER_CHUNK_LOG2 = 3; private static readonly byte[] c_NonVirtualSlotsOffsets = GetNonVirtualSlotsOffsets(); public bool IsInterface => (m_dwFlags & 0xF0000) == 786432; public bool HasIndirectParent => (m_dwFlags & 0x800000) != 0; public bool HasSingleNonVirtualSlot => Helpers.Has(m_wFlags2, Flags2.HasSingleNonVirtualSlot); public unsafe MethodTable* GetCanonicalMethodTable() { nuint num = (nuint)union_pEEClass_pCanonMT; if ((num & 2) == 0) { return (MethodTable*)num; } if ((num & 1) != 0) { return *(MethodTable**)(num - 3); } return (MethodTable*)(num - 2); } public unsafe MethodDesc* GetParallelMethodDesc(MethodDesc* pDefMD) { return GetMethodDescForSlot(pDefMD->SlotNumber); } public unsafe MethodDesc* GetMethodDescForSlot(uint slotNumber) { if (IsInterface) { GetNumVirtuals(); } throw new NotImplementedException(); } public unsafe void* GetRestoredSlot(uint slotNumber) { MethodTable* ptr = (MethodTable*)Unsafe.AsPointer(ref this); void* slot; while (true) { ptr = ptr->GetCanonicalMethodTable(); slot = ptr->GetSlot(slotNumber); if (slot != null) { break; } ptr = ptr->GetParentMethodTable(); } return slot; } public unsafe MethodTable* GetParentMethodTable() { void* pParentMethodTable = m_pParentMethodTable; if (HasIndirectParent) { return *(MethodTable**)pParentMethodTable; } return (MethodTable*)pParentMethodTable; } public unsafe void* GetSlot(uint slotNumber) { nint slotPtrRaw = GetSlotPtrRaw(slotNumber); if (slotNumber < GetNumVirtuals()) { return ((VTableIndir2_t*)slotPtrRaw)->Value; } if ((m_wFlags2 & Flags2.IsZapped) != 0 && slotNumber >= GetNumVirtuals()) { return ((RelativePointer*)slotPtrRaw)->Value; } return *(void**)slotPtrRaw; } public unsafe nint GetSlotPtrRaw(uint slotNum) { if (slotNum < GetNumVirtuals()) { uint indexOfVtableIndirection = GetIndexOfVtableIndirection(slotNum); return (nint)(VTableIndir_t__GetValueMaybeNullAtPtr((nint)(GetVtableIndirections() + indexOfVtableIndirection)) + GetIndexAfterVtableIndirection(slotNum)); } if (HasSingleNonVirtualSlot) { return GetNonVirtualSlotsPtr(); } return (nint)(GetNonVirtualSlotsArray() + (slotNum - GetNumVirtuals())); } public ushort GetNumVirtuals() { return m_wNumVirtuals; } public static uint GetIndexOfVtableIndirection(uint slotNum) { return slotNum >> 3; } public unsafe VTableIndir_t* GetVtableIndirections() { return (VTableIndir_t*)((byte*)Unsafe.AsPointer(ref this) + sizeof(MethodTable)); } public unsafe static VTableIndir2_t* VTableIndir_t__GetValueMaybeNullAtPtr(nint @base) { return (VTableIndir2_t*)@base; } public static uint GetIndexAfterVtableIndirection(uint slotNum) { return slotNum & 7; } [MultipurposeSlotOffsetTable(3, typeof(MultipurposeSlotHelpers))] private static byte[] GetNonVirtualSlotsOffsets() { return new byte[8] { MultipurposeSlotHelpers.OffsetOfMp1(), MultipurposeSlotHelpers.OffsetOfMp2(), MultipurposeSlotHelpers.OffsetOfMp1(), MultipurposeSlotHelpers.RegularOffset(2), MultipurposeSlotHelpers.OffsetOfMp2(), MultipurposeSlotHelpers.RegularOffset(2), MultipurposeSlotHelpers.RegularOffset(2), MultipurposeSlotHelpers.RegularOffset(3) }; } public nint GetNonVirtualSlotsPtr() { return GetMultipurposeSlotPtr(Flags2.HasNonVirtualSlots, c_NonVirtualSlotsOffsets); } public unsafe nint GetMultipurposeSlotPtr(Flags2 flag, byte[] offsets) { nint num = offsets[(uint)(m_wFlags2 & (flag - 1))]; if (num >= sizeof(MethodTable)) { num += (nint)GetNumVTableIndirections() * (nint)sizeof(VTableIndir_t); } return (nint)((byte*)Unsafe.AsPointer(ref this) + num); } public unsafe void*** GetNonVirtualSlotsArray() { return (void***)GetNonVirtualSlotsPtr(); } public uint GetNumVTableIndirections() { return GetNumVtableIndirections(GetNumVirtuals()); } public static uint GetNumVtableIndirections(uint numVirtuals) { return numVirtuals + 7 >> 3; } } public unsafe static InvokeAllocMemPtr InvokeAllocMemPtr => new InvokeAllocMemPtr((delegate*)(&InvokeAllocMem)); public new unsafe static InvokeCompileMethodPtr InvokeCompileMethodPtr => new InvokeCompileMethodPtr((delegate*)(&InvokeCompileMethod)); public unsafe static InvokeCompileMethodHookPostPtr InvokeCompileMethodHookPostPtr => new InvokeCompileMethodHookPostPtr((delegate*)(&InvokeCompileMethodHookPost)); public unsafe static void InvokeAllocMem(nint functionPtr, nint thisPtr, AllocMemArgs* args) { if (functionPtr != IntPtr.Zero) { ((delegate* unmanaged[Thiscall])functionPtr)(thisPtr, args); } } public new unsafe static CorJitResult InvokeCompileMethod(nint functionPtr, nint thisPtr, nint corJitInfo, CORINFO_METHOD_INFO* methodInfo, uint flags, byte** nativeEntry, uint* nativeSizeOfCode) { if (functionPtr == IntPtr.Zero) { *nativeEntry = null; *nativeSizeOfCode = 0u; return CorJitResult.CORJIT_OK; } return ((delegate* unmanaged[Thiscall])functionPtr)(thisPtr, corJitInfo, methodInfo, flags, nativeEntry, nativeSizeOfCode); } public unsafe static CorJitResult InvokeCompileMethodHookPost(nint functionPtr, nint thisPtr, nint corJitInfo, CORINFO_METHOD_INFO* methodInfo, uint flags, byte** nativeEntry, uint* nativeSizeOfCode, CorJitResult res, AllocMemArgs* pArgs) { if (functionPtr == IntPtr.Zero) { return res; } return ((delegate* unmanaged[Thiscall])functionPtr)(thisPtr, corJitInfo, methodInfo, flags, nativeEntry, nativeSizeOfCode, res, pArgs); } } public class V70 : V60 { public new static class ICorJitInfoVtable { public const int AllocMemIndex = 159; public const int TotalVtableCount = 175; } } public class V80 : V70 { public new static class ICorJitInfoVtable { public const int AllocMemIndex = 154; public const int TotalVtableCount = 170; } } public class V90 : V80 { public new static class ICorJitInfoVtable { public const int AllocMemIndex = 158; public const int TotalVtableCount = 174; } } } internal static class Fx { public static class V48 { public enum MethodClassification { IL, FCall, NDirect, EEImpl, Array, Instantiated, ComInterop, Dynamic } public enum MethodDescClassification : ushort { ClassificationMask = 7, HasNonVtableSlot = 8, MethodImpl = 16, Static = 32, Intercepted = 64, RequiresLinktimeCheck = 128, RequiresInheritanceCheck = 256, ParentRequiresInheritanceCheck = 512, Duplicate = 1024, VerifiedState = 2048, Verifiable = 4096, NotInline = 8192, Synchronized = 16384, RequiresFullSlotNumber = 32768 } public struct MethodImpl { public unsafe uint* pdwSlots; public unsafe MethodDesc* pImplementedMD; } public struct MethodDesc { [Flags] public enum Flags3 : ushort { TokenRemainderMask = 0x3FFF, HasForwardedValuetypeParameter = 0x4000, ValueTypeParametersWalked = 0x4000, DoesNotHaveEquivalentValuetypeParameters = 0x8000 } [Flags] public enum Flags2 : byte { HasStableEntryPoint = 1, HasPrecode = 2, IsUnboxingStub = 4, HasNativeCodeSlot = 8, TransparencyMask = 0x30, TransparencyUnknown = 0, TransparencyTransparent = 0x10, TransparencyCritical = 0x20, TransparencyTreatAsSafe = 0x30, CASDemandsOnly = 0x40, HostProtectionLinkChecksOnly = 0x80 } public static readonly nuint Alignment = (nuint)((IntPtr.Size == 8) ? 8 : 4); public Flags3 m_wFlags3AndTokenRemainder; public byte m_chunkIndex; public Flags2 m_bFlags2; public const ushort PackedSlot_SlotMask = 1023; public const ushort PackedSlot_NameHashMask = 64512; public ushort m_wSlotNumber; public MethodDescClassification m_wFlags; private unsafe static readonly nuint[] s_ClassificationSizeTable = new nuint[8] { (nuint)sizeof(MethodDesc), (nuint)FCallMethodDescPtr.CurrentSize, (nuint)NDirectMethodDescPtr.CurrentSize, (nuint)EEImplMethodDescPtr.CurrentSize, (nuint)ArrayMethodDescPtr.CurrentSize, (nuint)sizeof(InstantiatedMethodDesc), (nuint)sizeof(ComPlusCallMethodDesc), (nuint)DynamicMethodDescPtr.CurrentSize }; public ushort SlotNumber { get { if (!Helpers.Has(m_wFlags, MethodDescClassification.RequiresFullSlotNumber)) { return (ushort)(m_wSlotNumber & 0x3FF); } return m_wSlotNumber; } } public MethodClassification Classification => (MethodClassification)(m_wFlags & MethodDescClassification.ClassificationMask); public unsafe MethodDescChunk* MethodDescChunk => (MethodDescChunk*)Unsafe.AsPointer(ref Unsafe.SubtractByteOffset(ref this, (nuint)sizeof(MethodDescChunk) + m_chunkIndex * Alignment)); public unsafe MethodTable* MethodTable => MethodDescChunk->MethodTable; public bool HasNonVtableSlot => Helpers.Has(m_wFlags, MethodDescClassification.HasNonVtableSlot); public bool HasStableEntryPoint => Helpers.Has(m_bFlags2, Flags2.HasStableEntryPoint); public bool HasPrecode => Helpers.Has(m_bFlags2, Flags2.HasPrecode); public bool HasNativeCodeSlot => Helpers.Has(m_bFlags2, Flags2.HasNativeCodeSlot); public bool IsUnboxingStub => Helpers.Has(m_bFlags2, Flags2.IsUnboxingStub); public unsafe bool HasMethodInstantiation { get { if (TryAsInstantiated(out var md)) { return md->IMD_HasMethodInstantiation; } return false; } } public unsafe bool IsGenericMethodDefinition { get { if (TryAsInstantiated(out var md)) { return md->IMD_IsGenericMethodDefinition; } return false; } } public unsafe bool IsInstantiatingStub { get { if (!IsUnboxingStub && TryAsInstantiated(out var md)) { return md->IMD_IsWrapperStubWithInstantiations; } return false; } } public unsafe bool IsGenericComPlusCall { get { if (TryAsInstantiated(out var md)) { return md->IMD_HasComPlusCallInfo; } return false; } } public bool IsWrapperStub { get { if (!IsUnboxingStub) { return IsInstantiatingStub; } return true; } } public bool IsTightlyBoundToMethodTable { get { if (!HasNonVtableSlot) { return true; } if (HasMethodInstantiation) { return IsGenericMethodDefinition; } if (IsWrapperStub) { return false; } return true; } } public unsafe void* GetMethodEntryPoint() { if (HasNonVtableSlot) { nuint baseSize = GetBaseSize(); return *(void**)((byte*)Unsafe.AsPointer(ref this) + baseSize); } return MethodTable->GetSlot(SlotNumber); } public unsafe bool TryAsInstantiated(out InstantiatedMethodDesc* md) { if (Classification == MethodClassification.Instantiated) { md = (InstantiatedMethodDesc*)Unsafe.AsPointer(ref this); return true; } md = default(InstantiatedMethodDesc*); return false; } public unsafe nuint SizeOf(bool includeNonVtable = true, bool includeMethodImpl = true, bool includeComPlus = true, bool includeNativeCode = true) { nuint num = (nuint)((nint)GetBaseSize() + (nint)((includeNonVtable && Helpers.Has(m_wFlags, MethodDescClassification.HasNonVtableSlot)) ? sizeof(void*) : 0) + ((includeMethodImpl && Helpers.Has(m_wFlags, MethodDescClassification.MethodImpl)) ? sizeof(MethodImpl) : 0)); if (includeNativeCode && HasNativeCodeSlot) { num += (uint)(((Unsafe.As(ref Unsafe.AddByteOffset(ref this, num)) & 1) != 0) ? (sizeof(void*) + sizeof(void*)) : sizeof(void*)); } if (includeComPlus && IsGenericComPlusCall) { num += (uint)ComPlusCallInfoPtr.CurrentSize; } return num; } public unsafe void* GetNativeCode() { if (HasNativeCodeSlot) { nuint num = (nuint)((RelativePointer*)GetAddrOfNativeCodeSlot())->Value & (nuint)(~(nint)1); if (num != 0) { return (void*)num; } } if (!HasStableEntryPoint || HasPrecode) { return null; } return GetStableEntryPoint(); } public unsafe void* GetStableEntryPoint() { return GetMethodEntryPoint(); } public unsafe static MethodDesc* FindTightlyBoundWrappedMethodDesc(MethodDesc* pMD) { if (pMD->IsUnboxingStub && pMD->TryAsInstantiated(out var md)) { pMD = md->IMD_GetWrappedMethodDesc(); } if (!pMD->IsTightlyBoundToMethodTable) { pMD = pMD->GetCanonicalMethodTable()->GetParallelMethodDesc(pMD); } if (pMD->IsUnboxingStub) { pMD = GetNextIntroducedMethod(pMD); } return pMD; } public unsafe static MethodDesc* GetNextIntroducedMethod(MethodDesc* pMD) { MethodDescChunk* methodDescChunk = pMD->MethodDescChunk; nuint num = (nuint)((byte*)pMD + pMD->SizeOf()); nuint num2 = (nuint)((byte*)methodDescChunk + methodDescChunk->SizeOf); if (num < num2) { return (MethodDesc*)num; } methodDescChunk = methodDescChunk->m_next; if (methodDescChunk != null) { return methodDescChunk->FirstMethodDesc; } return null; } public unsafe MethodTable* GetCanonicalMethodTable() { return MethodTable->GetCanonicalMethodTable(); } public unsafe void* GetAddrOfNativeCodeSlot() { nuint byteOffset = SizeOf(includeNonVtable: true, includeMethodImpl: true, includeComPlus: false, includeNativeCode: false); return Unsafe.AsPointer(ref Unsafe.AddByteOffset(ref this, byteOffset)); } public nuint GetBaseSize() { return GetBaseSize(Classification); } public static nuint GetBaseSize(MethodClassification classification) { return s_ClassificationSizeTable[(int)classification]; } } [FatInterface] public struct StoredSigMethodDescPtr { private unsafe readonly void* ptr_; private readonly nint[] vtbl_; public static nint[] CurrentVtable { get; } = (IntPtr.Size == 8) ? StoredSigMethodDesc_64.FatVtable_ : StoredSigMethodDesc_32.FatVtable_; public unsafe static int CurrentSize { get; } = (IntPtr.Size == 8) ? sizeof(StoredSigMethodDesc_64) : sizeof(StoredSigMethodDesc_32); public unsafe void* m_pSig { [FatInterfaceIgnore] get { return GetPSig(); } } public uint m_cSig { [FatInterfaceIgnore] get { return GetCSig(); } } private unsafe void* GetPSig() { return ((delegate*)vtbl_[0])(ptr_); } private unsafe uint GetCSig() { return ((delegate*)vtbl_[0])(ptr_); } public unsafe StoredSigMethodDescPtr(void* ptr, nint[] vtbl) { ptr_ = ptr; vtbl_ = vtbl; } } [FatInterfaceImpl(typeof(StoredSigMethodDescPtr))] public struct StoredSigMethodDesc_64 { public MethodDesc @base; public unsafe void* m_pSig; public uint m_cSig; public uint m_dwExtendedFlags; private static nint[]? fatVtable_; public unsafe static nint[] FatVtable_ { get { object obj = fatVtable_; if (obj == null) { obj = new nint[2] { (nint)(delegate*)(&S_GetPSig_), (nint)(delegate*)(&S_GetCSig_) }; fatVtable_ = (nint[]?)obj; } return (nint[])obj; unsafe static uint S_GetCSig_(void* ptr__) { return ((StoredSigMethodDesc_64*)ptr__)->GetCSig(); } unsafe static void* S_GetPSig_(void* ptr__) { return ((StoredSigMethodDesc_64*)ptr__)->GetPSig(); } } } private unsafe void* GetPSig() { return m_pSig; } private uint GetCSig() { return m_cSig; } } [FatInterfaceImpl(typeof(StoredSigMethodDescPtr))] public struct StoredSigMethodDesc_32 { public MethodDesc @base; public unsafe void* m_pSig; public uint m_cSig; private static nint[]? fatVtable_; public unsafe static nint[] FatVtable_ { get { object obj = fatVtable_; if (obj == null) { obj = new nint[2] { (nint)(delegate*)(&S_GetPSig_), (nint)(delegate*)(&S_GetCSig_) }; fatVtable_ = (nint[]?)obj; } return (nint[])obj; unsafe static uint S_GetCSig_(void* ptr__) { return ((StoredSigMethodDesc_32*)ptr__)->GetCSig(); } unsafe static void* S_GetPSig_(void* ptr__) { return ((StoredSigMethodDesc_32*)ptr__)->GetPSig(); } } } private unsafe void* GetPSig() { return m_pSig; } private uint GetCSig() { return m_cSig; } } [FatInterface] public struct FCallMethodDescPtr { private unsafe readonly void* ptr_; private readonly nint[] vtbl_; public static nint[] CurrentVtable { get; } = (IntPtr.Size == 8) ? FCallMethodDesc_64.FatVtable_ : FCallMethodDesc_32.FatVtable_; public unsafe static int CurrentSize { get; } = (IntPtr.Size == 8) ? sizeof(FCallMethodDesc_64) : sizeof(FCallMethodDesc_32); public uint m_dwECallID { [FatInterfaceIgnore] get { return GetECallID(); } } private unsafe uint GetECallID() { return ((delegate*)vtbl_[0])(ptr_); } public unsafe FCallMethodDescPtr(void* ptr, nint[] vtbl) { ptr_ = ptr; vtbl_ = vtbl; } } [FatInterfaceImpl(typeof(FCallMethodDescPtr))] public struct FCallMethodDesc_64 { public MethodDesc @base; public uint m_dwECallID; public uint m_padding; private static nint[]? fatVtable_; public unsafe static nint[] FatVtable_ { get { object obj = fatVtable_; if (obj == null) { obj = new nint[1] { (nint)(delegate*)(&S_GetECallID_) }; fatVtable_ = (nint[]?)obj; } return (nint[])obj; unsafe static uint S_GetECallID_(void* ptr__) { return ((FCallMethodDesc_64*)ptr__)->GetECallID(); } } } private uint GetECallID() { return m_dwECallID; } } [FatInterfaceImpl(typeof(FCallMethodDescPtr))] public struct FCallMethodDesc_32 { public MethodDesc @base; public uint m_dwECallID; private static nint[]? fatVtable_; public unsafe static nint[] FatVtable_ { get { object obj = fatVtable_; if (obj == null) { obj = new nint[1] { (nint)(delegate*)(&S_GetECallID_) }; fatVtable_ = (nint[]?)obj; } return (nint[])obj; unsafe static uint S_GetECallID_(void* ptr__) { return ((FCallMethodDesc_32*)ptr__)->GetECallID(); } } } private uint GetECallID() { return m_dwECallID; } } public struct DynamicResolver { } [Flags] public enum DynamicMethodDesc_ExtendedFlags : uint { Attrs = 0xFFFFu, ILStubAttrs = 0x17u, MemberAccessMask = 7u, ReverseStub = 8u, Static = 0x10u, CALLIStub = 0x20u, DelegateStub = 0x40u, CopyCtorArgs = 0x80u, Unbreakable = 0x100u, DelegateCOMStub = 0x200u, SignatureNeedsResture = 0x400u, StubNeedsCOMStarted = 0x800u, MulticastStub = 0x1000u, UnboxingILStub = 0x2000u, ILStub = 0x10000u, LCGMethod = 0x20000u, StackArgSize = 0xFFFC0000u } [FatInterface] public struct DynamicMethodDescPtr { private unsafe readonly void* ptr_; private readonly nint[] vtbl_; public static nint[] CurrentVtable { get; } = (IntPtr.Size == 8) ? DynamicMethodDesc_64.FatVtable_ : DynamicMethodDesc_32.FatVtable_; public unsafe static int CurrentSize { get; } = (IntPtr.Size == 8) ? sizeof(DynamicMethodDesc_64) : sizeof(DynamicMethodDesc_32); public DynamicMethodDesc_ExtendedFlags Flags => GetFlags(); private unsafe DynamicMethodDesc_ExtendedFlags GetFlags() { return ((delegate*)vtbl_[0])(ptr_); } public unsafe DynamicMethodDescPtr(void* ptr, nint[] vtbl) { ptr_ = ptr; vtbl_ = vtbl; } } [FatInterfaceImpl(typeof(DynamicMethodDescPtr))] public struct DynamicMethodDesc_64 { public StoredSigMethodDesc_64 @base; public unsafe byte* m_pszMethodName; public unsafe DynamicResolver* m_pResolver; private static nint[]? fatVtable_; public DynamicMethodDesc_ExtendedFlags Flags => GetFlags(); public unsafe static nint[] FatVtable_ { get { object obj = fatVtable_; if (obj == null) { obj = new nint[1] { (nint)(delegate*)(&S_GetFlags_) }; fatVtable_ = (nint[]?)obj; } return (nint[])obj; unsafe static DynamicMethodDesc_ExtendedFlags S_GetFlags_(void* ptr__) { return ((DynamicMethodDesc_64*)ptr__)->GetFlags(); } } } private DynamicMethodDesc_ExtendedFlags GetFlags() { return (DynamicMethodDesc_ExtendedFlags)@base.m_dwExtendedFlags; } } [FatInterfaceImpl(typeof(DynamicMethodDescPtr))] public struct DynamicMethodDesc_32 { public StoredSigMethodDesc_32 @base; public unsafe byte* m_pszMethodName; public unsafe DynamicResolver* m_pResolver; public uint m_dwExtendedFlags; private static nint[]? fatVtable_; public DynamicMethodDesc_ExtendedFlags Flags => GetFlags(); public unsafe static nint[] FatVtable_ { get { object obj = fatVtable_; if (obj == null) { obj = new nint[1] { (nint)(delegate*)(&S_GetFlags_) }; fatVtable_ = (nint[]?)obj; } return (nint[])obj; unsafe static DynamicMethodDesc_ExtendedFlags S_GetFlags_(void* ptr__) { return ((DynamicMethodDesc_32*)ptr__)->GetFlags(); } } } private DynamicMethodDesc_ExtendedFlags GetFlags() { return (DynamicMethodDesc_ExtendedFlags)m_dwExtendedFlags; } } [FatInterface] public struct ArrayMethodDescPtr { private unsafe readonly void* ptr_; private readonly nint[] vtbl_; public static nint[] CurrentVtable { get; } = (IntPtr.Size == 8) ? ArrayMethodDesc_64.FatVtable_ : ArrayMethodDesc_32.FatVtable_; public unsafe static int CurrentSize { get; } = (IntPtr.Size == 8) ? sizeof(ArrayMethodDesc_64) : sizeof(ArrayMethodDesc_32); public unsafe ArrayMethodDescPtr(void* ptr, nint[] vtbl) { ptr_ = ptr; vtbl_ = vtbl; } } public enum ArrayFunc { Get, Set, Address, Ctor } [FatInterfaceImpl(typeof(ArrayMethodDescPtr))] public struct ArrayMethodDesc_64 { public StoredSigMethodDesc_64 @base; private static nint[]? fatVtable_; public static nint[] FatVtable_ => fatVtable_ ?? (fatVtable_ = new nint[0]); } [FatInterfaceImpl(typeof(ArrayMethodDescPtr))] public struct ArrayMethodDesc_32 { public StoredSigMethodDesc_32 @base; private static nint[]? fatVtable_; public static nint[] FatVtable_ => fatVtable_ ?? (fatVtable_ = new nint[0]); } public struct NDirectWriteableData { } [Flags] public enum NDirectMethodDesc_Flags : ushort { EarlyBound = 1, HasSuppressUnmanagedCodeAccess = 2, DefaultDllImportSearchPathIsCached = 4, IsMarshalingRequiredCached = 0x10, CachedMarshalingRequired = 0x20, NativeAnsi = 0x40, LastError = 0x80, NativeNoMangle = 0x100, VarArgs = 0x200, StdCall = 0x400, ThisCall = 0x800, IsQCall = 0x1000, DefaultDllImportSearchPathsStatus = 0x2000, NDirectPopulated = 0x8000 } [FatInterface] public struct NDirectMethodDescPtr { private unsafe readonly void* ptr_; private readonly nint[] vtbl_; public static nint[] CurrentVtable { get; } = ((int)PlatformDetection.Architecture == 2) ? NDirectMethodDesc_x86.FatVtable_ : NDirectMethodDesc_other.FatVtable_; public unsafe static int CurrentSize { get; } = ((int)PlatformDetection.Architecture == 2) ? sizeof(NDirectMethodDesc_x86) : sizeof(NDirectMethodDesc_other); public unsafe NDirectMethodDescPtr(void* ptr, nint[] vtbl) { ptr_ = ptr; vtbl_ = vtbl; } } [FatInterfaceImpl(typeof(NDirectMethodDescPtr))] public struct NDirectMethodDesc_other { public struct NDirect { public unsafe void* m_pNativeNDirectTarget; public unsafe byte* m_pszEntrypointName; public nuint union_pszLibName_dwECallID; public unsafe NDirectWriteableData* m_pWriteableData; public unsafe void* m_pImportThunkGlue; public uint m_DefaultDllImportSearchPathsAttributeValue; public NDirectMethodDesc_Flags m_wFlags; public unsafe MethodDesc* m_pStubMD; } public MethodDesc @base; private NDirect ndirect; private static nint[]? fatVtable_; public static nint[] FatVtable_ => fatVtable_ ?? (fatVtable_ = new nint[0]); } [FatInterfaceImpl(typeof(NDirectMethodDescPtr))] public struct NDirectMethodDesc_x86 { public struct NDirect { public unsafe void* m_pNativeNDirectTarget; public unsafe byte* m_pszEntrypointName; public nuint union_pszLibName_dwECallID; public unsafe NDirectWriteableData* m_pWriteableData; public unsafe void* m_pImportThunkGlue; public uint m_DefaultDllImportSearchPathsAttributeValue; public NDirectMethodDesc_Flags m_wFlags; public ushort m_cbStackArgumentSize; public unsafe MethodDesc* m_pStubMD; } public MethodDesc @base; private NDirect ndirect; private static nint[]? fatVtable_; public static nint[] FatVtable_ => fatVtable_ ?? (fatVtable_ = new nint[0]); } [FatInterface] public struct EEImplMethodDescPtr { private unsafe readonly void* ptr_; private readonly nint[] vtbl_; public static nint[] CurrentVtable { get; } = (IntPtr.Size == 8) ? EEImplMethodDesc_64.FatVtable_ : EEImplMethodDesc_32.FatVtable_; public unsafe static int CurrentSize { get; } = (IntPtr.Size == 8) ? sizeof(EEImplMethodDesc_64) : sizeof(EEImplMethodDesc_32); public unsafe EEImplMethodDescPtr(void* ptr, nint[] vtbl) { ptr_ = ptr; vtbl_ = vtbl; } } [FatInterfaceImpl(typeof(EEImplMethodDescPtr))] public struct EEImplMethodDesc_64 { public StoredSigMethodDesc_64 @base; private static nint[]? fatVtable_; public static nint[] FatVtable_ => fatVtable_ ?? (fatVtable_ = new nint[0]); } [FatInterfaceImpl(typeof(EEImplMethodDescPtr))] public struct EEImplMethodDesc_32 { public StoredSigMethodDesc_32 @base; private static nint[]? fatVtable_; public static nint[] FatVtable_ => fatVtable_ ?? (fatVtable_ = new nint[0]); } [FatInterface] public struct ComPlusCallInfoPtr { private unsafe readonly void* ptr_; private readonly nint[] vtbl_; public unsafe static int CurrentSize { get; } = ((int)PlatformDetection.Architecture == 2) ? sizeof(ComPlusCallInfo_x86) : sizeof(ComPlusCallInfo_other); public unsafe ComPlusCallInfoPtr(void* ptr, nint[] vtbl) { ptr_ = ptr; vtbl_ = vtbl; } } public struct ComPlusCallInfo_x86 { public unsafe void* union_m_pILStub_pEventProviderMD; public unsafe MethodTable* m_pInterfaceMT; public byte m_flags; public ushort m_cachedComSlot; public ushort m_cbStackArgumentSize; public unsafe void* union_m_pRetThunk_pInterceptStub; private RelativePointer m_pStubMD; } public struct ComPlusCallInfo_other { public unsafe void* union_m_pILStub_pEventProviderMD; public unsafe MethodTable* m_pInterfaceMT; public byte m_flags; public ushort m_cachedComSlot; private RelativePointer m_pStubMD; } public struct ComPlusCallMethodDesc { public MethodDesc @base; public unsafe void* m_pComPlusCallInfo; } public struct InstantiatedMethodDesc { [Flags] public enum Flags : ushort { KindMask = 7, GenericMethodDefinition = 0, UnsharedMethodInstantiation = 1, SharedMethodInstantiation = 2, WrapperStubWithInstantiations = 3, EnCAddedMethod = 7, Unrestored = 8, HasComPlusCallInfo = 0x10 } public MethodDesc @base; public unsafe void* union_pDictLayout_pWrappedMethodDesc; public unsafe Dictionary* m_pPerInstInfo; public Flags m_wFlags2; public ushort m_wNumGenericArgs; public unsafe bool IMD_HasMethodInstantiation { get { if (!IMD_IsGenericMethodDefinition) { return m_pPerInstInfo != null; } return true; } } public bool IMD_IsGenericMethodDefinition => (m_wFlags2 & Flags.KindMask) == 0; public bool IMD_IsWrapperStubWithInstantiations => (m_wFlags2 & Flags.KindMask) == Flags.WrapperStubWithInstantiations; public bool IMD_HasComPlusCallInfo => Helpers.Has(m_wFlags2, Flags.HasComPlusCallInfo); public unsafe MethodDesc* IMD_GetWrappedMethodDesc() { Helpers.Assert(IMD_IsWrapperStubWithInstantiations, (string)null, "IMD_IsWrapperStubWithInstantiations"); return (MethodDesc*)union_pDictLayout_pWrappedMethodDesc; } } public struct RelativeFixupPointer { private readonly nint value; public unsafe void* Value { get { nint num = value; if (num == 0) { return null; } nint num2 = (nint)((byte*)Unsafe.AsPointer(ref this) + num); if ((num2 & 1) != 0) { num2 = *(nint*)(num2 - 1); } return (void*)num2; } } } public struct MethodDescChunk { [Flags] public enum Flags : ushort { TokenRangeMask = 0x3FF, HasCompactEntrypoints = 0x4000, IsZapped = 0x8000 } public RelativeFixupPointer m_methodTable; public unsafe MethodDescChunk* m_next; public byte m_size; public byte m_count; public Flags m_flagsAndTokenRange; public unsafe MethodTable* MethodTable => (MethodTable*)m_methodTable.Value; public unsafe MethodDesc* FirstMethodDesc => (MethodDesc*)((byte*)Unsafe.AsPointer(ref this) + sizeof(MethodDescChunk)); public uint Size => (uint)(m_size + 1); public uint Count => (uint)(m_count + 1); public unsafe nuint SizeOf => (nuint)sizeof(MethodDescChunk) + Size * MethodDesc.Alignment; } public struct Dictionary { } public struct Module { } public struct MethodTableWriteableData { } public struct RelativePointer { private nint m_delta; public unsafe void* Value { get { nint delta = m_delta; if (delta != 0) { return Unsafe.AsPointer(ref Unsafe.AddByteOffset(ref this, delta)); } return null; } } public RelativePointer(nint delta) { m_delta = delta; } } public struct MethodTable { [Flags] public enum WFlagsLow : uint { UNUSED_ComponentSize_1 = 1u, StaticsMask = 6u, StaticsMask_NonDynamic = 0u, StaticsMask_Dynamic = 2u, StaticsMask_Generics = 4u, StaticsMask_CrossModuleGenerics = 6u, StaticsMask_IfGenericsThenCrossModule = 2u, NotInPZM = 8u, GenericsMask = 0x30u, GenericsMask_NonGeneric = 0u, GenericsMask_GenericInst = 0x10u, GenericsMask_SharedInst = 0x20u, GenericsMask_TypicalInst = 0x30u, ContextStatic = 0x40u, HasRemotingVtsInfo = 0x80u, HasVariance = 0x100u, HasDefaultCtor = 0x200u, HasPreciseInitCctors = 0x400u, IsHFA_IsRegStructPassed = 0x800u, IsByRefLike = 0x1000u, UNUSED_ComponentSize_5 = 0x2000u, UNUSED_ComponentSize_6 = 0x4000u, UNUSED_ComponentSize_7 = 0x8000u, StringArrayValues = 0u } [Flags] public enum WFlagsHigh : uint { Category_Mask = 0xF0000u, Category_Class = 0u, Category_Unused_1 = 0x10000u, Category_MarshalByRef_Mask = 0xE0000u, Category_MarshalByRef = 0x20000u, Category_Contextful = 0x30000u, Category_ValueType = 0x40000u, Category_ValueType_Mask = 0xC0000u, Category_Nullable = 0x50000u, Category_PrimitiveValueType = 0x60000u, Category_TruePrimitive = 0x70000u, Category_Array = 0x80000u, Category_Array_Mask = 0xC0000u, Category_IfArrayThenSzArray = 0x20000u, Category_Interface = 0xC0000u, Category_Unused_2 = 0xD0000u, Category_TransparentProxy = 0xE0000u, Category_AsyncPin = 0xF0000u, Category_ElementTypeMask = 0xE0000u, HasFinalizer = 0x100000u, IfNotInterfaceThenMarshalable = 0x200000u, IfInterfaceThenHasGuidInfo = 0x200000u, ICastable = 0x400000u, HasIndirectParent = 0x800000u, ContainsPointers = 0x1000000u, HasTypeEquivalence = 0x2000000u, HasRCWPerTypeData = 0x4000000u, HasCriticalFinalizer = 0x8000000u, Collectible = 0x10000000u, ContainsGenericVariables = 0x20000000u, ComObject = 0x40000000u, HasComponentSize = 0x80000000u, NonTrivialInterfaceCast = 0x40480000u } [Flags] public enum Flags2 : ushort { MultipurposeSlotsMask = 0x1F, HasPerInstInfo = 1, HasInterfaceMap = 2, HasDispatchMapSlot = 4, HasNonVirtualSlots = 8, HasModuleOverride = 0x10, IsZapped = 0x20, IsPreRestored = 0x40, HasModuleDependencies = 0x80, NoSecurityProperties = 0x100, RequiresDispatchTokenFat = 0x200, HasCctor = 0x400, HasCCWTemplate = 0x800, RequiresAlign8 = 0x1000, HasBoxedRegularStatics = 0x2000, HasSingleNonVirtualSlot = 0x4000, DependsOnEquivalentOrForwardedStructs = 0x8000 } public enum UnionLowBits { EEClass, Invalid, MethodTable, Indirection } private static class MultipurposeSlotHelpers { public unsafe static byte OffsetOfMp1() { MethodTable methodTable = default(MethodTable); return (byte)((byte*)(&methodTable.union_pPerInstInfo_ElementTypeHnd_pMultipurposeSlot1) - (byte*)(&methodTable)); } public unsafe static byte OffsetOfMp2() { MethodTable methodTable = default(MethodTable); return (byte)((byte*)(&methodTable.union_p_InterfaceMap_pMultipurposeSlot2) - (byte*)(&methodTable)); } public unsafe static byte RegularOffset(int index) { return (byte)(sizeof(MethodTable) + index * IntPtr.Size - 2 * IntPtr.Size); } } private uint m_dwFlags; public uint m_BaseSize; public Flags2 m_wFlags2; public ushort m_wToken; public ushort m_wNumVirtuals; public ushort m_wNumInterfaces; private unsafe void* m_pParentMethodTable; public unsafe Module* m_pLoaderModule; public unsafe MethodTableWriteableData* m_pWriteableData; public unsafe void* union_pEEClass_pCanonMT; public unsafe void* union_pPerInstInfo_ElementTypeHnd_pMultipurposeSlot1; public unsafe void* union_p_InterfaceMap_pMultipurposeSlot2; public const int VTABLE_SLOTS_PER_CHUNK = 8; public const int VTABLE_SLOTS_PER_CHUNK_LOG2 = 3; private static readonly byte[] c_NonVirtualSlotsOffsets = GetNonVirtualSlotsOffsets(); public WFlagsHigh FlagsHigh => (WFlagsHigh)(m_dwFlags & 0xFFFF0000u); public WFlagsLow FlagsLow { get { if (!Helpers.Has(FlagsHigh, WFlagsHigh.HasComponentSize)) { return (WFlagsLow)(m_dwFlags & 0xFFFF); } return WFlagsLow.StaticsMask_NonDynamic; } } public int ComponentSize { get { if (!Helpers.Has(FlagsHigh, WFlagsHigh.HasComponentSize)) { return 0; } return (int)(m_dwFlags & 0xFFFF); } } public bool IsInterface => (m_dwFlags & 0xF0000) == 786432; public bool HasIndirectParent => Helpers.Has(FlagsHigh, WFlagsHigh.HasIndirectParent); public unsafe MethodTable* ParentMethodTable { get { void* pParentMethodTable = m_pParentMethodTable; if (HasIndirectParent) { return (MethodTable*)((MethodTable*)pParentMethodTable)->m_pParentMethodTable; } return (MethodTable*)pParentMethodTable; } } public bool HasSingleNonVirtualSlot => Helpers.Has(m_wFlags2, Flags2.HasSingleNonVirtualSlot); public unsafe MethodTable* GetCanonicalMethodTable() { nuint num = (nuint)union_pEEClass_pCanonMT; if ((num & 2) == 0) { return (MethodTable*)num; } if ((num & 1) != 0) { return *(MethodTable**)(num - 3); } return (MethodTable*)(num - 2); } public unsafe MethodDesc* GetParallelMethodDesc(MethodDesc* pDefMD) { return GetMethodDescForSlot(pDefMD->SlotNumber); } public unsafe MethodDesc* GetMethodDescForSlot(uint slotNumber) { if (IsInterface) { GetNumVirtuals(); } throw new NotImplementedException(); } public unsafe void* GetRestoredSlot(uint slotNumber) { MethodTable* ptr = (MethodTable*)Unsafe.AsPointer(ref this); void* slot; while (true) { ptr = ptr->GetCanonicalMethodTable(); slot = ptr->GetSlot(slotNumber); if (slot != null) { break; } ptr = ptr->ParentMethodTable; } return slot; } public unsafe void* GetSlot(uint slotNumber) { return *(void**)GetSlotPtrRaw(slotNumber); } public unsafe nint GetSlotPtrRaw(uint slotNum) { if (slotNum < GetNumVirtuals()) { uint indexOfVtableIndirection = GetIndexOfVtableIndirection(slotNum); void** ptr = (void**)GetVtableIndirections()[indexOfVtableIndirection]; return (nint)(ptr + GetIndexAfterVtableIndirection(slotNum)); } if (HasSingleNonVirtualSlot) { return GetNonVirtualSlotsPtr(); } return (nint)(GetNonVirtualSlotsArray() + (slotNum - GetNumVirtuals())); } public ushort GetNumVirtuals() { return m_wNumVirtuals; } public static uint GetIndexOfVtableIndirection(uint slotNum) { return slotNum >> 3; } public unsafe void** GetVtableIndirections() { return (void**)((byte*)Unsafe.AsPointer(ref this) + sizeof(MethodTable)); } public static uint GetIndexAfterVtableIndirection(uint slotNum) { return slotNum & 7; } [MultipurposeSlotOffsetTable(3, typeof(MultipurposeSlotHelpers))] private static byte[] GetNonVirtualSlotsOffsets() { return new byte[8] { MultipurposeSlotHelpers.OffsetOfMp1(), MultipurposeSlotHelpers.OffsetOfMp2(), MultipurposeSlotHelpers.OffsetOfMp1(), MultipurposeSlotHelpers.RegularOffset(2), MultipurposeSlotHelpers.OffsetOfMp2(), MultipurposeSlotHelpers.RegularOffset(2), MultipurposeSlotHelpers.RegularOffset(2), MultipurposeSlotHelpers.RegularOffset(3) }; } public nint GetNonVirtualSlotsPtr() { return GetMultipurposeSlotPtr(Flags2.HasNonVirtualSlots, c_NonVirtualSlotsOffsets); } public unsafe nint GetMultipurposeSlotPtr(Flags2 flag, byte[] offsets) { nint num = offsets[(uint)(m_wFlags2 & (flag - 1))]; if (num >= sizeof(MethodTable)) { num += (nint)GetNumVTableIndirections() * (nint)sizeof(void**); } return (nint)((byte*)Unsafe.AsPointer(ref this) + num); } public unsafe void** GetNonVirtualSlotsArray() { return (void**)((RelativePointer*)GetNonVirtualSlotsPtr())->Value; } public uint GetNumVTableIndirections() { return GetNumVtableIndirections(GetNumVirtuals()); } public static uint GetNumVtableIndirections(uint numVirtuals) { return numVirtuals + 7 >> 3; } } } } internal static class Linux { internal static class Procfs { internal enum ProcPid { Invalid = -1, Self } internal struct Module { public ulong StartAddress { get; } public ulong Size { get; internal set; } public string Path { get; } public Module(ulong startAddress, ulong size, string path) { StartAddress = startAddress; Size = size; Path = path; } } private const string RootPath = "/proc/"; private const string Self = "self"; private const string MapsFileName = "/maps"; private static string GetMapsFilePathForProcess(ProcPid pid) { if (pid != ProcPid.Self) { return $"{"/proc/"}{pid}{"/maps"}"; } return "/proc/self/maps"; } internal static IEnumerable? ParseMapsModules(ProcPid pid) { try { return ParseMapsModulesCore(File.ReadAllLines(GetMapsFilePathForProcess(pid))); } catch (IOException) { } catch (UnauthorizedAccessException) { } return null; } private static List ParseMapsModulesCore(IEnumerable lines) { Module module = default(Module); List modules = new List(); bool moduleHasReadAndExecFlags = false; foreach (string line in lines) { if (!TryParseMapsEntry(line, out (ulong, ulong, bool, string) parsedLine)) { CommitCurrentModule(); } else if (module.Size != 0 && module.Path == parsedLine.Item4 && module.StartAddress + module.Size == parsedLine.Item1) { module.Size += parsedLine.Item2; moduleHasReadAndExecFlags |= parsedLine.Item3; } else { CommitCurrentModule(); module = new Module(parsedLine.Item1, parsedLine.Item2, parsedLine.Item4); moduleHasReadAndExecFlags = parsedLine.Item3; } } CommitCurrentModule(); return modules; void CommitCurrentModule() { if (moduleHasReadAndExecFlags && module.Size != 0) { modules.Add(module); module = default(Module); } } } private static bool TryParseMapsEntry(string line, out (ulong StartAddress, ulong Size, bool HasReadAndExecFlags, string Path) parsedLine) { StringParser stringParser = new StringParser(line, ' ', skipEmpty: true); var (item, num) = stringParser.ParseRaw(TryParseAddressRange); if (num == 0) { parsedLine = default((ulong, ulong, bool, string)); return false; } bool item2 = stringParser.ParseRaw(HasReadAndExecFlags); stringParser.MoveNext(); stringParser.MoveNext(); stringParser.MoveNext(); if (!stringParser.MoveNext()) { parsedLine = default((ulong, ulong, bool, string)); return false; } string item3 = stringParser.ExtractCurrentToEnd(); parsedLine = (StartAddress: item, Size: num, HasReadAndExecFlags: item2, Path: item3); return true; static bool HasReadAndExecFlags(string s, ref int start, ref int end) { ReadOnlySpan span = s.AsSpan(start, end - start); if (span.IndexOf('r') > -1) { return span.IndexOf('x') > -1; } return false; } static (ulong Start, ulong Size) TryParseAddressRange(string s, ref int start, ref int end) { int num2 = s.IndexOf('-', start, end - start); if (num2 > 0 && ulong.TryParse(s.AsSpan(start, num2).ToString(), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var result) && ulong.TryParse(s.AsSpan(num2 + 1, end - (num2 + 1)).ToString(), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var result2)) { return (Start: result, Size: result2 - result); } return (Start: 0uL, Size: 0uL); } } } } internal static class OSX { [StructLayout(LayoutKind.Sequential, Pack = 4)] public struct vm_region_submap_short_info_64 { public vm_prot_t protection; public vm_prot_t max_protection; public vm_inherit_t inheritance; public ulong offset; public uint user_tag; public uint ref_count; public ushort shadow_depth; public byte external_pager; public ShareMode share_mode; public boolean_t is_submap; public vm_behavior_t behavior; public uint object_id; public ushort user_wired_count; public unsafe static int Count => sizeof(vm_region_submap_short_info_64) / 4; } public enum ShareMode : byte { COW = 1, Private, Empty, Shared, TrueShared, PrivateAliased, SharedAliased, LargePage } [StructLayout(LayoutKind.Sequential, Pack = 4)] public struct task_dyld_info { public ulong all_image_info_addr; public ulong all_image_info_size; public task_dyld_all_image_info_format all_image_info_format; public unsafe dyld_all_image_infos* all_image_infos => (dyld_all_image_infos*)all_image_info_addr; public unsafe static int Count => sizeof(task_dyld_info) / 4; } public struct dyld_all_image_infos { public uint version; public uint infoArrayCount; public unsafe dyld_image_info* infoArray; public unsafe ReadOnlySpan InfoArray => new ReadOnlySpan(infoArray, (int)infoArrayCount); } public struct dyld_image_info { public unsafe void* imageLoadAddress; public PCSTR imageFilePath; public nuint imageFileModDate; } public enum task_dyld_all_image_info_format { Bits32, Bits64 } public enum task_flavor_t : uint { DyldInfo = 17u } public enum vm_region_flavor_t { BasicInfo64 = 9 } [Flags] public enum vm_prot_t { None = 0, Read = 1, Write = 2, Execute = 4, Default = 3, All = 7, [Obsolete("Only used for memory_object_lock_request. Invalid otherwise.")] NoChange = 8, Copy = 0x10, WantsCopy = 0x10, [Obsolete("Invalid value. Indicates that other bits are to be applied as mask to actual bits.")] IsMask = 0x40, [Obsolete("Invalid value. Tells mprotect to not set Read. Used for execute-only.")] StripRead = 0x80, [Obsolete("Invalid value. Use only for mprotect.")] ExecuteOnly = 0x84 } public struct VmProtFmtProxy : IDebugFormattable { private readonly vm_prot_t value; public VmProtFmtProxy(vm_prot_t value) { this.value = value; } public bool TryFormatInto(Span span, out int wrote) { int num = 0; if (Helpers.Has(value, vm_prot_t.NoChange)) { if (span.Slice(num).Length < 1) { wrote = num; return false; } span[num++] = '~'; } if (span.Slice(num).Length < 3) { wrote = 0; return false; } span[num++] = (Helpers.Has(value, vm_prot_t.Read) ? 'r' : '-'); span[num++] = (Helpers.Has(value, vm_prot_t.Write) ? 'w' : '-'); span[num++] = (Helpers.Has(value, vm_prot_t.Execute) ? 'x' : '-'); if (Helpers.Has(value, vm_prot_t.StripRead)) { if (span.Slice(num).Length < 1) { wrote = num; return false; } span[num++] = '!'; } if (Helpers.Has(value, vm_prot_t.Copy)) { if (span.Slice(num).Length < 1) { wrote = num; return false; } span[num++] = 'c'; } if (Helpers.Has(value, vm_prot_t.IsMask)) { if (span.Slice(num).Length < " (mask)".Length) { wrote = num; return false; } " (mask)".AsSpan().CopyTo(span.Slice(num)); num += " (mask)".Length; } wrote = num; return true; } } [Flags] public enum vm_flags { Fixed = 0, Anywhere = 1, Purgable = 2, Chunk4GB = 4, RandomAddr = 8, NoCache = 0x10, Overwrite = 0x4000, SuperpageMask = 0x70000, SuperpageSizeAny = 0x10000, SuperpageWSize2MB = 0x20000, AliasMask = -16777216 } public enum vm_inherit_t : uint { Share = 0u, Copy = 1u, None = 2u, DonateCopy = 3u, Default = 1u, LastValid = 2u } public enum vm_behavior_t { Default, Random, Sequential, ReverseSequential, WillNeed, DontNeed, Free, ZeroWiredPages, Reusable, Reuse, CanReuse, PageOut } [DebuggerDisplay("{ToString(),nq}")] public struct boolean_t { private int value; public boolean_t(bool value) { this.value = (value ? 1 : 0); } public static implicit operator bool(boolean_t v) { return v.value != 0; } public static implicit operator boolean_t(bool v) { return new boolean_t(v); } public static bool operator true(boolean_t v) { return v; } public static bool operator false(boolean_t v) { return !v; } public override string ToString() { if (!this) { return "false"; } return "true"; } } [DebuggerDisplay("{Value}")] public struct kern_return_t : IEquatable { private int value; public static kern_return_t Success = new kern_return_t(0); public static kern_return_t InvalidAddress = new kern_return_t(1); public static kern_return_t ProtectionFailure = new kern_return_t(2); public static kern_return_t NoSpace = new kern_return_t(3); public static kern_return_t InvalidArgument = new kern_return_t(4); public static kern_return_t Failure = new kern_return_t(5); public int Value => value; public kern_return_t(int value) { this.value = value; } public static implicit operator bool(kern_return_t v) { return v.value == 0; } public static bool operator ==(kern_return_t x, kern_return_t y) { return x.value == y.value; } public static bool operator !=(kern_return_t x, kern_return_t y) { return x.value != y.value; } public override bool Equals(object? obj) { if (obj is kern_return_t other) { return Equals(other); } return false; } public bool Equals(kern_return_t other) { return value == other.value; } public override int GetHashCode() { return HashCode.Combine(value); } } [Flags] public enum map_prot { None = 0, Read = 1, Write = 2, Execute = 4 } [Flags] public enum map_flags { Private = 2, Fixed = 0x10, JIT = 0x800, Anonymous = 0x1000, Failed = 0x1001 } public const string LibSystem = "libSystem"; private unsafe static int* mach_task_self_; public static readonly nint MAP_FAILED; public unsafe static int Errno => *__error(); [DllImport("libSystem", EntryPoint = "getpagesize")] public static extern int GetPageSize(); [DllImport("libSystem")] public unsafe static extern void sys_icache_invalidate(void* start, nuint size); [DllImport("libSystem", CallingConvention = CallingConvention.Cdecl, EntryPoint = "mkstemp")] public unsafe static extern int MkSTemp(byte* template); [DllImport("libSystem", CallingConvention = CallingConvention.Cdecl, EntryPoint = "close")] public static extern int Close(int fd); [DllImport("libSystem", CallingConvention = CallingConvention.Cdecl)] public unsafe static extern int* __error(); static OSX() { MAP_FAILED = -1; _ = Errno; } [DllImport("libSystem")] public unsafe static extern kern_return_t mach_vm_region_recurse(int targetTask, [In][Out] ulong* address, [Out] ulong* size, [In][Out] int* nestingDepth, [Out] vm_region_submap_short_info_64* info, [In][Out] int* infoSize); [DllImport("libSystem")] public static extern kern_return_t mach_vm_protect(int targetTask, ulong address, ulong size, boolean_t setMax, vm_prot_t protection); [DllImport("libSystem")] public unsafe static extern kern_return_t mach_vm_allocate(int targetTask, [In][Out] ulong* address, ulong size, vm_flags flags); [DllImport("libSystem")] public unsafe static extern kern_return_t mach_vm_map(int targetTask, [In][Out] ulong* address, ulong size, ulong mask, vm_flags flags, int @object, ulong offset, boolean_t copy, vm_prot_t curProt, vm_prot_t maxProt, vm_inherit_t inheritance); [DllImport("libSystem")] public unsafe static extern kern_return_t mach_vm_remap(int targetTask, [In][Out] ulong* targetAddress, ulong size, ulong offset, vm_flags flags, int srcTask, ulong srcAddress, boolean_t copy, [Out] vm_prot_t* curProt, [Out] vm_prot_t* maxProt, vm_inherit_t inherit); [DllImport("libSystem")] public unsafe static extern kern_return_t mach_make_memory_entry_64(int targetTask, [In][Out] ulong* size, ulong offset, vm_prot_t permission, int* objectHandle, int parentHandle); [DllImport("libSystem")] public static extern kern_return_t mach_vm_deallocate(int targetTask, ulong address, ulong size); public unsafe static int mach_task_self() { int* ptr = mach_task_self_; if (ptr == null) { nint num = DynDll.OpenLibrary("libSystem"); try { ptr = (mach_task_self_ = (int*)DynDll.GetExport((IntPtr)num, "mach_task_self_")); } finally { DynDll.CloseLibrary((IntPtr)num); } } return *ptr; } [DllImport("libSystem")] public unsafe static extern kern_return_t task_info(int targetTask, task_flavor_t flavor, [Out] task_dyld_info* taskInfoOut, int* taskInfoCnt); public static VmProtFmtProxy P(vm_prot_t prot) { return new VmProtFmtProxy(prot); } [DllImport("libSystem")] public static extern nint mmap(nint address, ulong length, map_prot prot, map_flags flags, int fd, long offset); } [DebuggerDisplay("{DebuggerDisplay}")] internal readonly struct PCSTR : IEquatable { internal unsafe readonly byte* Value; internal unsafe int Length { get { byte* ptr = Value; if (ptr == null) { return 0; } for (; *ptr != 0; ptr++) { } return checked((int)(ptr - Value)); } } private string? DebuggerDisplay => ToString(); internal unsafe PCSTR(byte* value) { Value = value; } public unsafe static implicit operator byte*(PCSTR value) { return value.Value; } public unsafe static explicit operator PCSTR(byte* value) { return new PCSTR(value); } public unsafe bool Equals(PCSTR other) { return Value == other.Value; } public override bool Equals(object? obj) { if (obj is PCSTR other) { return Equals(other); } return false; } public unsafe override int GetHashCode() { return (int)Value; } public unsafe override string? ToString() { if (Value != null) { return new string((sbyte*)Value, 0, Length, Encoding.UTF8); } return null; } internal unsafe ReadOnlySpan AsSpan() { if (Value != null) { return new ReadOnlySpan(Value, Length); } return default(ReadOnlySpan); } } internal static class Unix { [Flags] public enum PipeFlags { CloseOnExec = 0x80000 } [Flags] public enum Protection { None = 0, Read = 1, Write = 2, Execute = 4 } [Flags] public enum MmapFlags { Shared = 1, Private = 2, SharedValidate = 3, Fixed = 0x10, Anonymous = 0x20, GrowsDown = 0x100, DenyWrite = 0x800, [Obsolete("Use Protection.Execute instead", true)] Executable = 0x1000, Locked = 0x2000, NoReserve = 0x4000, Populate = 0x8000, NonBlock = 0x10000, Stack = 0x20000, HugeTLB = 0x40000, Sync = 0x80000, FixedNoReplace = 0x100000 } public enum SysconfName { ArgMax, ChildMax, ClockTick, NGroupsMax, OpenMax, StreamMax, TZNameMax, JobControl, SavedIds, RealtimeSignals, PriorityScheduling, Timers, AsyncIO, PrioritizedIO, SynchronizedIO, FSync, MappedFiles, MemLock, MemLockRange, MemoryProtection, MessagePassing, Semaphores, SharedMemoryObjects, AIOListIOMax, AIOMax, AIOPrioDeltaMax, DelayTimerMax, MQOpenMax, MQPrioMax, Version, PageSize, RTSigMax, SemNSemsMax, SemValueMax, SigQueueMax, TimerMax } public const string LibC = "libc"; public unsafe static int Errno => *__errno_location(); [DllImport("libc", CallingConvention = CallingConvention.Cdecl, EntryPoint = "read")] public static extern nint Read(int fd, nint buf, nint count); [DllImport("libc", CallingConvention = CallingConvention.Cdecl, EntryPoint = "write")] public static extern nint Write(int fd, nint buf, nint count); [DllImport("libc", CallingConvention = CallingConvention.Cdecl, EntryPoint = "pipe2")] public unsafe static extern int Pipe2(int* pipefd, PipeFlags flags); [DllImport("libc", CallingConvention = CallingConvention.Cdecl, EntryPoint = "close")] public static extern int Close(int fd); [DllImport("libc", CallingConvention = CallingConvention.Cdecl, EntryPoint = "mmap")] public static extern nint Mmap(nint addr, nuint length, Protection prot, MmapFlags flags, int fd, int offset); [DllImport("libc", CallingConvention = CallingConvention.Cdecl, EntryPoint = "munmap")] public static extern int Munmap(nint addr, nuint length); [DllImport("libc", CallingConvention = CallingConvention.Cdecl, EntryPoint = "mprotect")] public static extern int Mprotect(nint addr, nuint len, Protection prot); [DllImport("libc", CallingConvention = CallingConvention.Cdecl, EntryPoint = "sysconf")] public static extern long Sysconf(SysconfName name); [DllImport("libc", CallingConvention = CallingConvention.Cdecl, EntryPoint = "mincore")] public unsafe static extern int Mincore(nint addr, nuint len, byte* vec); [DllImport("libc", CallingConvention = CallingConvention.Cdecl, EntryPoint = "mkstemp")] public unsafe static extern int MkSTemp(byte* template); [DllImport("libc", CallingConvention = CallingConvention.Cdecl)] public unsafe static extern int* __errno_location(); static Unix() { _ = Errno; } } internal static class Windows { [Conditional("NEVER")] [AttributeUsage(AttributeTargets.All)] private sealed class SetsLastSystemErrorAttribute : Attribute { } [Conditional("NEVER")] [AttributeUsage(AttributeTargets.All)] private sealed class NativeTypeNameAttribute : Attribute { public NativeTypeNameAttribute(string x) { } } public struct MEMORY_BASIC_INFORMATION { public unsafe void* BaseAddress; public unsafe void* AllocationBase; public uint AllocationProtect; public nuint RegionSize; public uint State; public uint Protect; public uint Type; } public struct SYSTEM_INFO { [StructLayout(LayoutKind.Explicit)] public struct _Anonymous_e__Union { public struct _Anonymous_e__Struct { public ushort wProcessorArchitecture; public ushort wReserved; } [FieldOffset(0)] public uint dwOemId; [FieldOffset(0)] public _Anonymous_e__Struct Anonymous; } public _Anonymous_e__Union Anonymous; public uint dwPageSize; public unsafe void* lpMinimumApplicationAddress; public unsafe void* lpMaximumApplicationAddress; public nuint dwActiveProcessorMask; public uint dwNumberOfProcessors; public uint dwProcessorType; public uint dwAllocationGranularity; public ushort wProcessorLevel; public ushort wProcessorRevision; [UnscopedRef] public ref uint dwOemId { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return ref Anonymous.dwOemId; } } [UnscopedRef] public ref ushort wProcessorArchitecture { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return ref Anonymous.Anonymous.wProcessorArchitecture; } } [UnscopedRef] public ref ushort wReserved { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return ref Anonymous.Anonymous.wReserved; } } } public readonly struct BOOL : IComparable, IComparable, IEquatable, IFormattable { public readonly int Value; public static BOOL FALSE => new BOOL(0); public static BOOL TRUE => new BOOL(1); public BOOL(int value) { Value = value; } public static bool operator ==(BOOL left, BOOL right) { return left.Value == right.Value; } public static bool operator !=(BOOL left, BOOL right) { return left.Value != right.Value; } public static bool operator <(BOOL left, BOOL right) { return left.Value < right.Value; } public static bool operator <=(BOOL left, BOOL right) { return left.Value <= right.Value; } public static bool operator >(BOOL left, BOOL right) { return left.Value > right.Value; } public static bool operator >=(BOOL left, BOOL right) { return left.Value >= right.Value; } public static implicit operator bool(BOOL value) { return value.Value != 0; } public static implicit operator BOOL(bool value) { return new BOOL(value ? 1 : 0); } public static bool operator false(BOOL value) { return value.Value == 0; } public static bool operator true(BOOL value) { return value.Value != 0; } public static implicit operator BOOL(byte value) { return new BOOL(value); } public static explicit operator byte(BOOL value) { return (byte)value.Value; } public static implicit operator BOOL(short value) { return new BOOL(value); } public static explicit operator short(BOOL value) { return (short)value.Value; } public static implicit operator BOOL(int value) { return new BOOL(value); } public static implicit operator int(BOOL value) { return value.Value; } public static explicit operator BOOL(long value) { return new BOOL((int)value); } public static implicit operator long(BOOL value) { return value.Value; } public static explicit operator BOOL(nint value) { return new BOOL((int)value); } public static implicit operator nint(BOOL value) { return value.Value; } public static implicit operator BOOL(sbyte value) { return new BOOL(value); } public static explicit operator sbyte(BOOL value) { return (sbyte)value.Value; } public static implicit operator BOOL(ushort value) { return new BOOL(value); } public static explicit operator ushort(BOOL value) { return (ushort)value.Value; } public static explicit operator BOOL(uint value) { return new BOOL((int)value); } public static explicit operator uint(BOOL value) { return (uint)value.Value; } public static explicit operator BOOL(ulong value) { return new BOOL((int)value); } public static explicit operator ulong(BOOL value) { return (ulong)value.Value; } public static explicit operator BOOL(nuint value) { return new BOOL((int)value); } public static explicit operator nuint(BOOL value) { return (nuint)value.Value; } public int CompareTo(object? obj) { if (obj is BOOL other) { return CompareTo(other); } if (obj != null) { throw new ArgumentException("obj is not an instance of BOOL."); } return 1; } public int CompareTo(BOOL other) { return Value.CompareTo(other.Value); } public override bool Equals(object? obj) { if (obj is BOOL other) { return Equals(other); } return false; } public bool Equals(BOOL other) { return Value.Equals(other.Value); } public override int GetHashCode() { return Value.GetHashCode(); } public override string ToString() { return Value.ToString((IFormatProvider?)null); } public string ToString(string? format, IFormatProvider? formatProvider) { return Value.ToString(format, formatProvider); } } public readonly struct HANDLE : IComparable, IComparable, IEquatable, IFormattable { public unsafe readonly void* Value; public unsafe static HANDLE INVALID_VALUE => new HANDLE((void*)(-1)); public static HANDLE NULL => new HANDLE(null); public unsafe HANDLE(void* value) { Value = value; } public unsafe static bool operator ==(HANDLE left, HANDLE right) { return left.Value == right.Value; } public unsafe static bool operator !=(HANDLE left, HANDLE right) { return left.Value != right.Value; } public unsafe static bool operator <(HANDLE left, HANDLE right) { return left.Value < right.Value; } public unsafe static bool operator <=(HANDLE left, HANDLE right) { return left.Value <= right.Value; } public unsafe static bool operator >(HANDLE left, HANDLE right) { return left.Value > right.Value; } public unsafe static bool operator >=(HANDLE left, HANDLE right) { return left.Value >= right.Value; } public unsafe static explicit operator HANDLE(void* value) { return new HANDLE(value); } public unsafe static implicit operator void*(HANDLE value) { return value.Value; } public unsafe static explicit operator HANDLE(byte value) { return new HANDLE((void*)value); } public unsafe static explicit operator byte(HANDLE value) { return (byte)value.Value; } public unsafe static explicit operator HANDLE(short value) { return new HANDLE((void*)value); } public unsafe static explicit operator short(HANDLE value) { return (short)value.Value; } public unsafe static explicit operator HANDLE(int value) { return new HANDLE((void*)value); } public unsafe static explicit operator int(HANDLE value) { return (int)value.Value; } public unsafe static explicit operator HANDLE(long value) { return new HANDLE((void*)value); } public unsafe static explicit operator long(HANDLE value) { return (long)value.Value; } public unsafe static explicit operator HANDLE(nint value) { return new HANDLE((void*)value); } public unsafe static implicit operator nint(HANDLE value) { return (nint)value.Value; } public unsafe static explicit operator HANDLE(sbyte value) { return new HANDLE((void*)value); } public unsafe static explicit operator sbyte(HANDLE value) { return (sbyte)value.Value; } public unsafe static explicit operator HANDLE(ushort value) { return new HANDLE((void*)value); } public unsafe static explicit operator ushort(HANDLE value) { return (ushort)value.Value; } public unsafe static explicit operator HANDLE(uint value) { return new HANDLE((void*)value); } public unsafe static explicit operator uint(HANDLE value) { return (uint)value.Value; } public unsafe static explicit operator HANDLE(ulong value) { return new HANDLE((void*)value); } public unsafe static explicit operator ulong(HANDLE value) { return (ulong)value.Value; } public unsafe static explicit operator HANDLE(nuint value) { return new HANDLE((void*)value); } public unsafe static implicit operator nuint(HANDLE value) { return (nuint)value.Value; } public int CompareTo(object? obj) { if (obj is HANDLE other) { return CompareTo(other); } if (obj != null) { throw new ArgumentException("obj is not an instance of HANDLE."); } return 1; } public unsafe int CompareTo(HANDLE other) { if (sizeof(nint) != 4) { return ((ulong)Value).CompareTo((ulong)other.Value); } return ((uint)Value).CompareTo((uint)other.Value); } public override bool Equals(object? obj) { if (obj is HANDLE other) { return Equals(other); } return false; } public unsafe bool Equals(HANDLE other) { nuint value = (nuint)Value; return ((UIntPtr)value).Equals((nuint)other.Value); } public unsafe override int GetHashCode() { nuint value = (nuint)Value; return ((UIntPtr)value).GetHashCode(); } public unsafe override string ToString() { if (sizeof(nuint) != 4) { return ((ulong)Value).ToString("X16", null); } return ((uint)Value).ToString("X8", null); } public unsafe string ToString(string? format, IFormatProvider? formatProvider) { if (sizeof(nint) != 4) { return ((ulong)Value).ToString(format, formatProvider); } return ((uint)Value).ToString(format, formatProvider); } } public struct CFG_CALL_TARGET_INFO { public nuint Offset; public nuint Flags; } public const int MEM_COMMIT = 4096; public const int MEM_RESERVE = 8192; public const int MEM_REPLACE_PLACEHOLDER = 16384; public const int MEM_RESERVE_PLACEHOLDER = 262144; public const int MEM_RESET = 524288; public const int MEM_TOP_DOWN = 1048576; public const int MEM_WRITE_WATCH = 2097152; public const int MEM_PHYSICAL = 4194304; public const int MEM_ROTATE = 8388608; public const int MEM_DIFFERENT_IMAGE_BASE_OK = 8388608; public const int MEM_RESET_UNDO = 16777216; public const int MEM_LARGE_PAGES = 536870912; public const uint MEM_4MB_PAGES = 2147483648u; public const int MEM_64K_PAGES = 541065216; public const int MEM_UNMAP_WITH_TRANSIENT_BOOST = 1; public const int MEM_COALESCE_PLACEHOLDERS = 1; public const int MEM_PRESERVE_PLACEHOLDER = 2; public const int MEM_DECOMMIT = 16384; public const int MEM_RELEASE = 32768; public const int MEM_FREE = 65536; public const int MEM_EXTENDED_PARAMETER_GRAPHICS = 1; public const int MEM_EXTENDED_PARAMETER_NONPAGED = 2; public const int MEM_EXTENDED_PARAMETER_ZERO_PAGES_OPTIONAL = 4; public const int MEM_EXTENDED_PARAMETER_NONPAGED_LARGE = 8; public const int MEM_EXTENDED_PARAMETER_NONPAGED_HUGE = 16; public const int MEM_EXTENDED_PARAMETER_SOFT_FAULT_PAGES = 32; public const int MEM_EXTENDED_PARAMETER_EC_CODE = 64; public const int MEM_EXTENDED_PARAMETER_IMAGE_NO_HPAT = 128; public const long MEM_EXTENDED_PARAMETER_NUMA_NODE_MANDATORY = long.MinValue; public const int MEM_EXTENDED_PARAMETER_TYPE_BITS = 8; public const ulong MEM_DEDICATED_ATTRIBUTE_NOT_SPECIFIED = ulong.MaxValue; public const int MEM_PRIVATE = 131072; public const int MEM_MAPPED = 262144; public const int MEM_IMAGE = 16777216; public const int PAGE_NOACCESS = 1; public const int PAGE_READONLY = 2; public const int PAGE_READWRITE = 4; public const int PAGE_WRITECOPY = 8; public const int PAGE_EXECUTE = 16; public const int PAGE_EXECUTE_READ = 32; public const int PAGE_EXECUTE_READWRITE = 64; public const int PAGE_EXECUTE_WRITECOPY = 128; public const int PAGE_GUARD = 256; public const int PAGE_NOCACHE = 512; public const int PAGE_WRITECOMBINE = 1024; public const int PAGE_GRAPHICS_NOACCESS = 2048; public const int PAGE_GRAPHICS_READONLY = 4096; public const int PAGE_GRAPHICS_READWRITE = 8192; public const int PAGE_GRAPHICS_EXECUTE = 16384; public const int PAGE_GRAPHICS_EXECUTE_READ = 32768; public const int PAGE_GRAPHICS_EXECUTE_READWRITE = 65536; public const int PAGE_GRAPHICS_COHERENT = 131072; public const int PAGE_GRAPHICS_NOCACHE = 262144; public const uint PAGE_ENCLAVE_THREAD_CONTROL = 2147483648u; public const uint PAGE_REVERT_TO_FILE_MAP = 2147483648u; public const int PAGE_TARGETS_NO_UPDATE = 1073741824; public const int PAGE_TARGETS_INVALID = 1073741824; public const int PAGE_ENCLAVE_UNVALIDATED = 536870912; public const int PAGE_ENCLAVE_MASK = 268435456; public const int PAGE_ENCLAVE_DECOMMIT = 268435456; public const int PAGE_ENCLAVE_SS_FIRST = 268435457; public const int PAGE_ENCLAVE_SS_REST = 268435458; public const int PROCESSOR_ARCHITECTURE_INTEL = 0; public const int PROCESSOR_ARCHITECTURE_MIPS = 1; public const int PROCESSOR_ARCHITECTURE_ALPHA = 2; public const int PROCESSOR_ARCHITECTURE_PPC = 3; public const int PROCESSOR_ARCHITECTURE_SHX = 4; public const int PROCESSOR_ARCHITECTURE_ARM = 5; public const int PROCESSOR_ARCHITECTURE_IA64 = 6; public const int PROCESSOR_ARCHITECTURE_ALPHA64 = 7; public const int PROCESSOR_ARCHITECTURE_MSIL = 8; public const int PROCESSOR_ARCHITECTURE_AMD64 = 9; public const int PROCESSOR_ARCHITECTURE_IA32_ON_WIN64 = 10; public const int PROCESSOR_ARCHITECTURE_NEUTRAL = 11; public const int PROCESSOR_ARCHITECTURE_ARM64 = 12; public const int PROCESSOR_ARCHITECTURE_ARM32_ON_WIN64 = 13; public const int PROCESSOR_ARCHITECTURE_IA32_ON_ARM64 = 14; public const int PROCESSOR_ARCHITECTURE_UNKNOWN = 65535; public const int CFG_CALL_TARGET_VALID = 1; public const int CFG_CALL_TARGET_PROCESSED = 2; public const int CFG_CALL_TARGET_CONVERT_EXPORT_SUPPRESSED_TO_VALID = 4; public const int CFG_CALL_TARGET_VALID_XFG = 8; public const int CFG_CALL_TARGET_CONVERT_XFG_TO_CFG = 16; private static bool? hasSetProcessValidCallTargets; public static bool HasSetProcessValidCallTargets { get { bool valueOrDefault = hasSetProcessValidCallTargets == true; if (!hasSetProcessValidCallTargets.HasValue) { valueOrDefault = DoGet(); hasSetProcessValidCallTargets = valueOrDefault; return valueOrDefault; } return valueOrDefault; [MethodImpl(MethodImplOptions.NoInlining)] static bool DoGet() { nint num = DynDll.OpenLibrary("kernelbase"); try { nint num2 = default(nint); return DynDll.TryGetExport((IntPtr)num, "SetProcessValidCallTargets", ref num2); } finally { DynDll.CloseLibrary((IntPtr)num); } } } } [DllImport("kernel32", ExactSpelling = true)] public unsafe static extern void* VirtualAlloc(void* lpAddress, nuint dwSize, uint flAllocationType, uint flProtect); [DllImport("kernel32", ExactSpelling = true)] public unsafe static extern BOOL VirtualProtect(void* lpAddress, nuint dwSize, uint flNewProtect, uint* lpflOldProtect); [DllImport("kernel32", ExactSpelling = true)] public unsafe static extern BOOL VirtualFree(void* lpAddress, nuint dwSize, uint dwFreeType); [DllImport("kernel32", ExactSpelling = true)] public unsafe static extern nuint VirtualQuery(void* lpAddress, MEMORY_BASIC_INFORMATION* lpBuffer, nuint dwLength); [DllImport("kernel32", ExactSpelling = true)] public unsafe static extern void GetSystemInfo(SYSTEM_INFO* lpSystemInfo); [DllImport("kernel32", ExactSpelling = true)] public static extern HANDLE GetCurrentProcess(); [DllImport("kernel32", ExactSpelling = true)] public unsafe static extern BOOL FlushInstructionCache(HANDLE hProcess, void* lpBaseAddress, nuint dwSize); [DllImport("kernel32", ExactSpelling = true)] public static extern uint GetLastError(); [DllImport("kernelbase", ExactSpelling = true)] private unsafe static extern BOOL SetProcessValidCallTargets(HANDLE hProcess, void* VirtualAddress, nuint RegionSize, uint NumberOfOffsets, CFG_CALL_TARGET_INFO* OffsetInformation); public unsafe static BOOL TrySetProcessValidCallTargets(void* VirtualAddress, nuint RegionSize, uint NumberOfOffsets, CFG_CALL_TARGET_INFO* OffsetInformation) { if (HasSetProcessValidCallTargets) { return SetProcessValidCallTargets(GetCurrentProcess(), VirtualAddress, RegionSize, NumberOfOffsets, OffsetInformation); } return false; } } } namespace MonoMod.Core.Interop.Attributes { [AttributeUsage(AttributeTargets.Struct, AllowMultiple = false)] internal sealed class FatInterfaceAttribute : Attribute { } [AttributeUsage(AttributeTargets.Struct, AllowMultiple = false)] internal sealed class FatInterfaceImplAttribute : Attribute { public Type FatInterface { get; } public FatInterfaceImplAttribute(Type fatInterface) { FatInterface = fatInterface; } } [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] internal sealed class FatInterfaceIgnoreAttribute : Attribute { } [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] internal sealed class MultipurposeSlotOffsetTableAttribute : Attribute { public int Bits { get; } public Type HelperType { get; } public MultipurposeSlotOffsetTableAttribute(int bits, Type helperType) { Bits = bits; HelperType = helperType; } } } namespace System.Runtime.CompilerServices { [ExcludeFromCodeCoverage] [DebuggerNonUserCode] internal static class IsExternalInit { } }