internal MethodRedirection(MethodBase original, MethodBase replacement, bool start) { Original = original; Replacement = replacement; // Note: I'm making local copies of the following fields to avoid accessing fields multiple times. RuntimeMethodHandle originalHandle = original.GetRuntimeMethodHandle(); RuntimeMethodHandle replacementHandle = replacement.GetRuntimeMethodHandle(); const string JIT_ERROR = "The specified method hasn't been jitted yet, and thus cannot be used in a redirection."; // Fetch their respective start IntPtr originalStart = originalHandle.GetMethodStart(); IntPtr replacementStart = replacementHandle.GetMethodStart(); // Edge case: calling this on the same method if (originalStart == replacementStart) { throw new InvalidOperationException("Cannot redirect a method to itself."); } // Edge case: methods are too close to one another int difference = (int)Math.Abs(originalStart.ToInt64() - replacementStart.ToInt64()); int sizeOfPtr = IntPtr.Size; if (difference <= Helpers.PatchSize) { throw new InvalidOperationException("Unable to redirect methods whose bodies are too close to one another."); } // Make sure they're jitted if (!originalStart.HasBeenCompiled()) { if (!Helpers.TryPrepareMethod(original, originalHandle)) { throw new ArgumentException(JIT_ERROR, nameof(original)); } originalStart = originalHandle.GetMethodStart(); } if (!replacementStart.HasBeenCompiled()) { if (!Helpers.TryPrepareMethod(replacement, replacementHandle)) { throw new ArgumentException(JIT_ERROR, nameof(replacement)); } replacementStart = replacementHandle.GetMethodStart(); } // Copy local value to field originalMethodStart = originalStart; // In some cases, the memory might need to be readable / writable: // Make the memory region rw right away just in case. Helpers.AllowRW(originalStart); // Save bytes to change to redirect method byte[] replBytes = replacementBytes = Helpers.GetJmpBytes(replacementStart); byte[] origBytes = originalBytes = new byte[replBytes.Length]; Marshal.Copy(originalStart, origBytes, 0, origBytes.Length); if (start) { CopyToStart(replBytes, originalStart); isRedirecting = true; } // Save methods in static array to make sure they're not garbage collected PersistingMethods.Add(original); PersistingMethods.Add(replacement); }