diff --git a/MonoMod.Common.csproj b/MonoMod.Common.csproj index ca24e4d..afb34d1 100644 --- a/MonoMod.Common.csproj +++ b/MonoMod.Common.csproj @@ -11,6 +11,7 @@ net35;net452;netstandard2.0 $(TargetFrameworks);net5.0 + $(TargetFrameworks);net6.0 Library false diff --git a/RuntimeDetour/DetourHelper.cs b/RuntimeDetour/DetourHelper.cs index b831347..a58e7c3 100644 --- a/RuntimeDetour/DetourHelper.cs +++ b/RuntimeDetour/DetourHelper.cs @@ -63,8 +63,11 @@ public static IDetourNativePlatform Native { IDetourNativePlatform native; + if (PlatformHelper.Is(Platform.ARM)) { native = new DetourNativeARMPlatform(); + } else if (PlatformHelper.Is(Platform.LoongArch64)) { + native = new DetourNativeLoongArch64Platform(); } else { native = new DetourNativeX86Platform(); } diff --git a/RuntimeDetour/Platforms/Native/DetourNativeLoongArch64Platform.cs b/RuntimeDetour/Platforms/Native/DetourNativeLoongArch64Platform.cs new file mode 100644 index 0000000..5653ee2 --- /dev/null +++ b/RuntimeDetour/Platforms/Native/DetourNativeLoongArch64Platform.cs @@ -0,0 +1,86 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; + +namespace MonoMod.RuntimeDetour.Platforms { +#if !MONOMOD_INTERNAL + public +#endif + unsafe class DetourNativeLoongArch64Platform : IDetourNativePlatform { + public enum DetourType : byte { + LoongArch64, + } + + private static readonly uint[] DetourSizes = { + 4 + 4 + 8, + }; + + public void Apply(NativeDetourData detour) { + int offs = 0; + + // pcaddi $r21,3 + detour.Method.Write(ref offs, (byte) 0x75); + detour.Method.Write(ref offs, (byte) 0x00); + detour.Method.Write(ref offs, (byte) 0x00); + detour.Method.Write(ref offs, (byte) 0x18); + // ld.d $r21,$r21,0 + detour.Method.Write(ref offs, (byte) 0xb5); + detour.Method.Write(ref offs, (byte) 0x02); + detour.Method.Write(ref offs, (byte) 0xc0); + detour.Method.Write(ref offs, (byte) 0x28); + // jirl $r0, $r21, 0 + detour.Method.Write(ref offs, (byte) 0xA0); + detour.Method.Write(ref offs, (byte) 0x02); + detour.Method.Write(ref offs, (byte) 0x00); + detour.Method.Write(ref offs, (byte) 0x4C); + // + detour.Method.Write(ref offs, (ulong) detour.Target); + } + + public void Copy(IntPtr src, IntPtr dst, byte type) { + *(uint*) ((long) dst) = *(uint*) ((long) src); + *(uint*) ((long) dst + 4) = *(uint*) ((long) src + 4); + *(ulong*) ((long) dst + 8) = *(ulong*) ((long) src + 8); + } + + public NativeDetourData Create(IntPtr from, IntPtr to, byte? type = null) { + NativeDetourData detour = new NativeDetourData { + Method = (IntPtr) ((long) from & ~0x1), + Target = (IntPtr) ((long) to & ~0x1) + }; + detour.Size = DetourSizes[0]; + return detour; + } + + public void FlushICache(IntPtr src, uint size) { + } + + public void Free(NativeDetourData detour) { + // No extra data. + } + + public void MakeExecutable(IntPtr src, uint size) { + // no-op. + } + + public void MakeReadWriteExecutable(IntPtr src, uint size) { + // no-op. + } + + public void MakeWritable(IntPtr src, uint size) { + // no-op. + } + + public IntPtr MemAlloc(uint size) { + return Marshal.AllocHGlobal((int) size); + } + + public void MemFree(IntPtr ptr) { + Marshal.FreeHGlobal(ptr); + } + + private readonly byte[] _FlushCache64 = { 0x85, 0x94, 0x10, 0x00, 0x0c, 0xf0, 0xff, 0x02, 0x84, 0xb0, 0x14, 0x00, 0x85, 0x14, 0x00, 0x6c, 0x86, 0x00, 0x80, 0x03, 0x00, 0x00, 0x72, 0x38, 0xc6, 0x10, 0xc0, 0x02, 0xc5, 0xf8, 0xff, 0x6b, 0x00, 0x00, 0x72, 0x38, 0x85, 0x10, 0x00, 0x6c, 0x00, 0x00, 0x72, 0x38, 0x84, 0x10, 0xc0, 0x02, 0x85, 0xf8, 0xff, 0x6b, 0x00, 0x00, 0x72, 0x38, 0x20, 0x00, 0x00, 0x4c}; + } +} diff --git a/RuntimeDetour/Platforms/Runtime/DetourRuntimeNETPlatform.cs b/RuntimeDetour/Platforms/Runtime/DetourRuntimeNETPlatform.cs index 9b449a8..26ee645 100644 --- a/RuntimeDetour/Platforms/Runtime/DetourRuntimeNETPlatform.cs +++ b/RuntimeDetour/Platforms/Runtime/DetourRuntimeNETPlatform.cs @@ -223,6 +223,66 @@ IntPtr WalkPrecode(IntPtr curr) { } } } + } else if (PlatformHelper.Is(Platform.LoongArch64)) { + IntPtr WalkPrecode(IntPtr curr) { + long lptr = (long) curr; + + if ( + // StubPrecode + // https://github.com/dotnet/runtime/blob/7830fddeead7907f6dd45f814fc3b8d49cd4b082/src/coreclr/vm/arm64/cgencpu.h#L567-L572 + *(uint*) (lptr + 0x00) == 0x18000095 && //pcaddi $r21,4 + *(uint*) (lptr + 0x04) == 0x28c022ae && //ld.d $t2,$r21,8 + *(uint*) (lptr + 0x08) == 0x28c002b5 && //ld.d $r21,$r21,0 + *(uint*) (lptr + 0x0c) == 0x4c0002a0 //jirl $r0,$r21,0 + ) { + IntPtr next = *(IntPtr*) (lptr + 0x10); + return NotThePreStub(curr, next); + } else if ( + // NDirectImportPrecode + // https://github.com/dotnet/runtime/blob/7830fddeead7907f6dd45f814fc3b8d49cd4b082/src/coreclr/vm/arm64/cgencpu.h#L628-L633 + *(uint*) (lptr + 0x00) == 0x18000035 && //pcaddi $r21,1 + *(uint*) (lptr + 0x04) == 0x28c052ae && //ld.d $t2,$r21,20 + *(uint*) (lptr + 0x08) == 0x28c032b5 && //ld.d $r21,$r21,12 + *(uint*) (lptr + 0x0c) == 0x4c0002a0 //jirl $r0,$r21,0 + ) { + IntPtr next = *(IntPtr*) (lptr + 0x10); + return NotThePreStub(curr, next); + } else if ( + // FixupPrecode + // https://github.com/dotnet/runtime/blob/7830fddeead7907f6dd45f814fc3b8d49cd4b082/src/coreclr/vm/arm64/cgencpu.h#L666-L672 + *(uint*) (lptr + 0x00) == 0x18000075 && //pcaddi $r21,3 + *(uint*) (lptr + 0x04) == 0x02ffd2ae && //addi.d $t2,$r21,-12 + *(uint*) (lptr + 0x08) == 0x28c012b5 && //ld.d $r21,$r21,4 + *(uint*) (lptr + 0x0c) == 0x4c0002a0 //jirl $r0,$r21,0 + ) { + IntPtr next = *(IntPtr*) (lptr + 0x10); + return NotThePreStub(curr, next); + } else if ( + // ThisPtrRetBufPrecode + // https://github.com/dotnet/runtime/blob/4da6b9a8d55913c0ea560d63590d35dc942425be/src/coreclr/vm/arm64/stubs.cpp#L641-L647 + *(uint*) (lptr + 0x00) == 0x18000055 && //pcaddi r21,2 + *(uint*) (lptr + 0x04) == 0x28c042b5 && //ld.d r21,16(r21) + *(uint*) (lptr + 0x08) == 0x0380008f && //ori r15,a0,0x0 + *(uint*) (lptr + 0x0c) == 0x038000a4 && //ori a0,a1,0x0 + *(uint*) (lptr + 0x10) == 0x038001e5 && //ori a1,r15,0x0 + *(uint*) (lptr + 0x14) == 0x4c0002a0 //jirl r0,r21,0 + ) { + IntPtr next = *(IntPtr*) (lptr + 0x18); + return NotThePreStub(curr, next); + } + + return curr; + } + + int numIterations = 0; + + IntPtr nextPtr = WalkPrecode(ptr); + while (nextPtr != ptr && numIterations < 16) { + numIterations++; + ptr = nextPtr; + + nextPtr = WalkPrecode(ptr); + } } else if (IntPtr.Size == 4) { int iptr = (int) ptr; // x86 diff --git a/Utils/Platform.cs b/Utils/Platform.cs index 395430d..256ad75 100644 --- a/Utils/Platform.cs +++ b/Utils/Platform.cs @@ -35,6 +35,11 @@ public enum Platform : int { /// Wine = 1 << 17, + /// + /// On demand LoongArch64 platform bit. + /// + LoongArch64 = 1 << 18, + /// /// Unknown OS. /// diff --git a/Utils/PlatformHelper.cs b/Utils/PlatformHelper.cs index 4fb5ac8..de7d2e2 100644 --- a/Utils/PlatformHelper.cs +++ b/Utils/PlatformHelper.cs @@ -112,8 +112,12 @@ private static void DeterminePlatform() { _current |= (IntPtr.Size >= 8 ? Platform.Bits64 : 0); #if NETSTANDARD + /* Detect LoongArch64, Architecture.LoongArch64 may not be recognized by other platform. + * And I found RuntimeInformation.ProcessArchitecture.HasFlag(Architecture.Arm) returns True on LoongArch64. */ + if("LoongArch64" == RuntimeInformation.ProcessArchitecture.ToString()) + _current |= Platform.LoongArch64; // Detect ARM based on RuntimeInformation. - if (RuntimeInformation.ProcessArchitecture.HasFlag(Architecture.Arm) || + else if (RuntimeInformation.ProcessArchitecture.HasFlag(Architecture.Arm) || RuntimeInformation.OSArchitecture.HasFlag(Architecture.Arm)) _current |= Platform.ARM; #else