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