diff options
Diffstat (limited to 'tests/src/CoreMangLib/cti/system/reflection/emit/DynMethodJumpStubTests/DynMethodJumpStubTests.cs')
-rw-r--r-- | tests/src/CoreMangLib/cti/system/reflection/emit/DynMethodJumpStubTests/DynMethodJumpStubTests.cs | 136 |
1 files changed, 136 insertions, 0 deletions
diff --git a/tests/src/CoreMangLib/cti/system/reflection/emit/DynMethodJumpStubTests/DynMethodJumpStubTests.cs b/tests/src/CoreMangLib/cti/system/reflection/emit/DynMethodJumpStubTests/DynMethodJumpStubTests.cs new file mode 100644 index 0000000000..1dc717d884 --- /dev/null +++ b/tests/src/CoreMangLib/cti/system/reflection/emit/DynMethodJumpStubTests/DynMethodJumpStubTests.cs @@ -0,0 +1,136 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Reflection; +using System.Reflection.Emit; +using System.Runtime.InteropServices; +using System.Threading; + +public static class DynamicMethodJumpStubTests +{ + private static int Main() + { + DynamicMethodJumpStubTest(); + return 100; + } + + public static void DynamicMethodJumpStubTest() + { + if (!Environment.Is64BitProcess) + { + return; + } + + // Reserve memory around framework libraries. This is just a best attempt, it typically doesn't help since the + // precode allocator may have already committed pages it can allocate from, or it may commit reserved pages close to + // framework libraries. + ReserveMemoryAround(new Action(ExecutionContext.RestoreFlow).Method.MethodHandle); + + for (int i = 0; i < 64; ++i) + { + DynamicMethod dynamicMethod = CreateDynamicMethod("DynMethod" + i); + Action dynamicMethodDelegate = (Action)dynamicMethod.CreateDelegate(typeof(Action)); + + // Before compiling the dynamic method, reserve memory around its current entry point, which should be its + // precode. Then, when compiling the method, there would be a good chance that the code will be located far from + // the precode, forcing the use of a jump stub. + ReserveMemoryAround( + (RuntimeMethodHandle) + typeof(DynamicMethod).InvokeMember( + "GetMethodDescriptor", + BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.NonPublic, + null, + dynamicMethod, + null)); + + dynamicMethodDelegate(); + } + + // This test does not release reserved pages because they may have been committed by other components on the system + } + + private static DynamicMethod CreateDynamicMethod(string name) + { + var dynamicMethod = new DynamicMethod(name, null, null); + ILGenerator ilGenerator = dynamicMethod.GetILGenerator(); + ilGenerator.Emit(OpCodes.Ret); + return dynamicMethod; + } + + private const uint AllocationGranularity = (uint)64 << 10; + private const ulong ReserveRangeRadius = (ulong)4 << 30; // reserve 4 GB before and after the base address + + private static void ReserveMemoryAround(RuntimeMethodHandle methodHandle) + { + ulong baseAddress = (ulong)methodHandle.Value.ToInt64(); + + ulong low = baseAddress - ReserveRangeRadius; + if (low > baseAddress) + { + low = ulong.MinValue; + } + else + { + low &= ~((ulong)AllocationGranularity - 1); + } + + ulong high = baseAddress + ReserveRangeRadius; + if (high < baseAddress) + { + high = ulong.MaxValue; + } + + ulong address = low; + while (address <= high) + { + VirtualAlloc( + new UIntPtr(address), + new UIntPtr(AllocationGranularity), + AllocationType.RESERVE, + MemoryProtection.NOACCESS); + + if (address + AllocationGranularity < address) + { + break; + } + address += AllocationGranularity; + } + } + + [Flags] + private enum AllocationType : uint + { + COMMIT = 0x1000, + RESERVE = 0x2000, + RESET = 0x80000, + LARGE_PAGES = 0x20000000, + PHYSICAL = 0x400000, + TOP_DOWN = 0x100000, + WRITE_WATCH = 0x200000 + } + + [Flags] + private enum MemoryProtection : uint + { + EXECUTE = 0x10, + EXECUTE_READ = 0x20, + EXECUTE_READWRITE = 0x40, + EXECUTE_WRITECOPY = 0x80, + NOACCESS = 0x01, + READONLY = 0x02, + READWRITE = 0x04, + WRITECOPY = 0x08, + GUARD_Modifierflag = 0x100, + NOCACHE_Modifierflag = 0x200, + WRITECOMBINE_Modifierflag = 0x400 + } + + [DllImport("kernel32.dll", SetLastError = true)] + private static extern UIntPtr VirtualAlloc( + UIntPtr lpAddress, + UIntPtr dwSize, + AllocationType flAllocationType, + MemoryProtection flProtect); +} |