Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parse MethodDescs to obtain function ptrs on .NET Core 3.0+ #25

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions RuntimeDetour/Platforms/Native/DetourNativeX86Platform.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,9 @@ public NativeDetourData Create(IntPtr from, IntPtr to, byte? type) {
Method = from,
Target = to
};
detour.Size = DetourSizes[detour.Type = type ?? (byte) GetDetourType(from, to, ref detour.Extra)];
// Console.WriteLine($"{nameof(DetourNativeX86Platform)} create: {(DetourType) detour.Type} 0x{detour.Method.ToString("X16")} + 0x{detour.Size.ToString("X8")} -> 0x{detour.Target.ToString("X16")}");
detour.Type = type ?? (byte) GetDetourType(from, to, ref detour.Extra);
// MMDbgLog.Log($"{nameof(DetourNativeX86Platform)} create: {(DetourType) detour.Type} (override: {(type is byte ? ((DetourType) type.Value).ToString() : "<null>")}) 0x{detour.Method.ToString("X16")} -> 0x{detour.Target.ToString("X16")}");
detour.Size = DetourSizes[detour.Type];
return detour;
}

Expand Down
72 changes: 68 additions & 4 deletions RuntimeDetour/Platforms/Runtime/DetourRuntimeNETCore30Platform.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,84 @@ class DetourRuntimeNETCore30Platform : DetourRuntimeNETCorePlatform {
// The JitVersionGuid is the same for Core 3.0 and 3.1
public static readonly Guid JitVersionGuid = new Guid("d609bed1-7831-49fc-bd49-b6f054dd4d46");

public bool UseOldGetFunctionPointer = Environment.GetEnvironmentVariable("MONOMOD_RUNTIMEDETOUR_NETCORE30PLUS_OLDFTNPTR") == "1";

protected override unsafe void DisableInlining(MethodBase method, RuntimeMethodHandle handle) {
// https://github.com/dotnet/runtime/blob/89965be3ad2be404dc82bd9e688d5dd2a04bcb5f/src/coreclr/src/vm/method.hpp#L178
// mdcNotInline = 0x2000
// References to RuntimeMethodHandle (CORINFO_METHOD_HANDLE) pointing to MethodDesc
// can be traced as far back as https://ntcore.com/files/netint_injection.htm

const int offset =
const int m_bFlags2_offset =
2 // UINT16 m_wFlags3AndTokenRemainder
+ 1 // BYTE m_chunkIndex
;
byte* m_bFlags2 = ((byte*) handle.Value) + m_bFlags2_offset;
// DON'T SET enum_flag2_HasStableEntryPoint, unless you want the JIT to loop between ThePreStub and PrecodeFixupThunk.
// *m_bFlags2 &= unchecked((byte) ~0x20U); // enum_flag2_IsEligibleForTieredCompilation

const int m_wFlags_offset =
2 // UINT16 m_wFlags3AndTokenRemainder
+ 1 // BYTE m_chunkIndex
+ 1 // BYTE m_bFlags2
+ 2 // WORD m_wSlotNumber
;
ushort* m_wFlags = (ushort*) (((byte*) handle.Value) + m_wFlags_offset);
*m_wFlags |= 0x2000; // mdcNotInline
}

protected override unsafe IntPtr GetFunctionPointer(MethodBase method, RuntimeMethodHandle handle) {
// This probably applies to some older runtimes too, might even change on a per platform basis, is still fresh,
// might not work with AOT'd stuff and Wine, but at least it gets us past all the relevant stubs.
if (UseOldGetFunctionPointer)
return base.GetFunctionPointer(method, handle);

// TODO: RuntimeDetouring generics is pain.
for (Type type = method.DeclaringType; type != null; type = type.DeclaringType)
if (type.IsGenericType)
return base.GetFunctionPointer(method, handle);

if (method.IsVirtual && (method.DeclaringType?.IsValueType ?? false)) {
/* .NET has got TWO MethodDescs and thus TWO ENTRY POINTS for virtual struct methods (f.e. override ToString).
* More info: https://mattwarren.org/2017/08/02/A-look-at-the-internals-of-boxing-in-the-CLR/#unboxing-stub-creation
* See DetourRuntimeNETPlatform for a list of observations.
*/
bool interfaced = false;
foreach (Type intf in method.DeclaringType.GetInterfaces()) {
if (method.DeclaringType.GetInterfaceMap(intf).TargetMethods.Contains(method)) {
interfaced = true;
break;
}
}

if (!interfaced)
return method.GetLdftnPointer();

// TODO: Figure out how to best obtain the real location of interface method code.
return base.GetFunctionPointer(method, handle);
}

if (method.IsDynamicMethod()) {
// standard fields (8) | ???? (ptr) | ???? (8) | ???? (ptr) | ???? (ptr) | ???? (ptr) | real ptr
// Possibly related to how dynamic methods have both a mutable and a non-mutable counterpart?
return *(IntPtr*) ((long) handle.Value + 8 + IntPtr.Size + 8 + IntPtr.Size + IntPtr.Size + IntPtr.Size);
}

const int m_wFlags_offset =
2 // UINT16 m_wFlags3AndTokenRemainder
+ 1 // BYTE m_chunkIndex
+ 1 // BYTE m_bFlags2
+ 2 // WORD m_wSlotNumber
;
ushort* m_wFlags = (ushort*) (((byte*) handle.Value) + offset);
*m_wFlags |= 0x2000;
ushort* m_wFlags = (ushort*) (((byte*) handle.Value) + m_wFlags_offset);

// Check for mdcHasNonVtableSlot
if ((*m_wFlags & 0x0008) == 0x0008) {
// standard fields (8) | ???? (ptr) | real ptr
return *(IntPtr*) ((long) handle.Value + 8 + IntPtr.Size);
}

// standard fields (8) | real ptr
return *(IntPtr*) ((long) handle.Value + 8);
}

private IntPtr GetCompileMethod(IntPtr jit)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// These should be defined as part of your build process,
// but if you want to test them quickly...
// #define MONOMOD_RUNTIMEDETOUR_NET_SCAN_MANUAL
#define MONOMOD_RUNTIMEDETOUR_NET_SCAN_MANUAL
// #define MONOMOD_RUNTIMEDETOUR_NET_SCAN_AUTO
// Default to automatic only.
#if !MONOMOD_RUNTIMEDETOUR_NET_SCAN_MANUAL && !MONOMOD_RUNTIMEDETOUR_NET_SCAN_AUTO
Expand Down
7 changes: 4 additions & 3 deletions Shared/MMDbgLog.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,15 +56,16 @@ public static void Start() {
}

if (string.IsNullOrEmpty(path))
path = "mmdbglog.txt";
path = Path.GetFullPath($"{Path.GetFileNameWithoutExtension(path)}-{Tag}{Path.GetExtension(path)}");
path = Path.Combine(Environment.CurrentDirectory, "mmdbglog.txt");
path = Path.GetFullPath(path);
string dir = Path.GetDirectoryName(path);
path = Path.Combine(dir, $"{Path.GetFileNameWithoutExtension(path)}-{Tag}{Path.GetExtension(path)}");

try {
if (File.Exists(path))
File.Delete(path);
} catch { }
try {
string dir = Path.GetDirectoryName(path);
if (!Directory.Exists(dir))
Directory.CreateDirectory(dir);
Writer = new StreamWriter(new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write, FileShare.ReadWrite | FileShare.Delete), Encoding.UTF8);
Expand Down