JitUnwindWindows.cs 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. // https://github.com/MicrosoftDocs/cpp-docs/blob/master/docs/build/exception-handling-x64.md
  2. using ARMeilleure.CodeGen.Unwinding;
  3. using System;
  4. using System.Diagnostics;
  5. using System.Runtime.InteropServices;
  6. namespace ARMeilleure.Translation.Cache
  7. {
  8. static class JitUnwindWindows
  9. {
  10. private const int MaxUnwindCodesArraySize = 32; // Must be an even value.
  11. private struct RuntimeFunction
  12. {
  13. public uint BeginAddress;
  14. public uint EndAddress;
  15. public uint UnwindData;
  16. }
  17. private struct UnwindInfo
  18. {
  19. public byte VersionAndFlags;
  20. public byte SizeOfProlog;
  21. public byte CountOfUnwindCodes;
  22. public byte FrameRegister;
  23. public unsafe fixed ushort UnwindCodes[MaxUnwindCodesArraySize];
  24. }
  25. private enum UnwindOp
  26. {
  27. PushNonvol = 0,
  28. AllocLarge = 1,
  29. AllocSmall = 2,
  30. SetFpreg = 3,
  31. SaveNonvol = 4,
  32. SaveNonvolFar = 5,
  33. SaveXmm128 = 8,
  34. SaveXmm128Far = 9,
  35. PushMachframe = 10
  36. }
  37. private unsafe delegate RuntimeFunction* GetRuntimeFunctionCallback(ulong controlPc, IntPtr context);
  38. [DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
  39. private static unsafe extern bool RtlInstallFunctionTableCallback(
  40. ulong tableIdentifier,
  41. ulong baseAddress,
  42. uint length,
  43. GetRuntimeFunctionCallback callback,
  44. IntPtr context,
  45. string outOfProcessCallbackDll);
  46. private static GetRuntimeFunctionCallback _getRuntimeFunctionCallback;
  47. private static int _sizeOfRuntimeFunction;
  48. private unsafe static RuntimeFunction* _runtimeFunction;
  49. private unsafe static UnwindInfo* _unwindInfo;
  50. public static void InstallFunctionTableHandler(IntPtr codeCachePointer, uint codeCacheLength, IntPtr workBufferPtr)
  51. {
  52. ulong codeCachePtr = (ulong)codeCachePointer.ToInt64();
  53. _sizeOfRuntimeFunction = Marshal.SizeOf<RuntimeFunction>();
  54. bool result;
  55. unsafe
  56. {
  57. _runtimeFunction = (RuntimeFunction*)workBufferPtr;
  58. _unwindInfo = (UnwindInfo*)(workBufferPtr + _sizeOfRuntimeFunction);
  59. _getRuntimeFunctionCallback = new GetRuntimeFunctionCallback(FunctionTableHandler);
  60. result = RtlInstallFunctionTableCallback(
  61. codeCachePtr | 3,
  62. codeCachePtr,
  63. codeCacheLength,
  64. _getRuntimeFunctionCallback,
  65. codeCachePointer,
  66. null);
  67. }
  68. if (!result)
  69. {
  70. throw new InvalidOperationException("Failure installing function table callback.");
  71. }
  72. }
  73. private static unsafe RuntimeFunction* FunctionTableHandler(ulong controlPc, IntPtr context)
  74. {
  75. int offset = (int)((long)controlPc - context.ToInt64());
  76. if (!JitCache.TryFind(offset, out CacheEntry funcEntry))
  77. {
  78. return null; // Not found.
  79. }
  80. var unwindInfo = funcEntry.UnwindInfo;
  81. int codeIndex = 0;
  82. for (int index = unwindInfo.PushEntries.Length - 1; index >= 0; index--)
  83. {
  84. var entry = unwindInfo.PushEntries[index];
  85. switch (entry.PseudoOp)
  86. {
  87. case UnwindPseudoOp.SaveXmm128:
  88. {
  89. int stackOffset = entry.StackOffsetOrAllocSize;
  90. Debug.Assert(stackOffset % 16 == 0);
  91. if (stackOffset <= 0xFFFF0)
  92. {
  93. _unwindInfo->UnwindCodes[codeIndex++] = PackUnwindOp(UnwindOp.SaveXmm128, entry.PrologOffset, entry.RegIndex);
  94. _unwindInfo->UnwindCodes[codeIndex++] = (ushort)(stackOffset / 16);
  95. }
  96. else
  97. {
  98. _unwindInfo->UnwindCodes[codeIndex++] = PackUnwindOp(UnwindOp.SaveXmm128Far, entry.PrologOffset, entry.RegIndex);
  99. _unwindInfo->UnwindCodes[codeIndex++] = (ushort)(stackOffset >> 0);
  100. _unwindInfo->UnwindCodes[codeIndex++] = (ushort)(stackOffset >> 16);
  101. }
  102. break;
  103. }
  104. case UnwindPseudoOp.AllocStack:
  105. {
  106. int allocSize = entry.StackOffsetOrAllocSize;
  107. Debug.Assert(allocSize % 8 == 0);
  108. if (allocSize <= 128)
  109. {
  110. _unwindInfo->UnwindCodes[codeIndex++] = PackUnwindOp(UnwindOp.AllocSmall, entry.PrologOffset, (allocSize / 8) - 1);
  111. }
  112. else if (allocSize <= 0x7FFF8)
  113. {
  114. _unwindInfo->UnwindCodes[codeIndex++] = PackUnwindOp(UnwindOp.AllocLarge, entry.PrologOffset, 0);
  115. _unwindInfo->UnwindCodes[codeIndex++] = (ushort)(allocSize / 8);
  116. }
  117. else
  118. {
  119. _unwindInfo->UnwindCodes[codeIndex++] = PackUnwindOp(UnwindOp.AllocLarge, entry.PrologOffset, 1);
  120. _unwindInfo->UnwindCodes[codeIndex++] = (ushort)(allocSize >> 0);
  121. _unwindInfo->UnwindCodes[codeIndex++] = (ushort)(allocSize >> 16);
  122. }
  123. break;
  124. }
  125. case UnwindPseudoOp.PushReg:
  126. {
  127. _unwindInfo->UnwindCodes[codeIndex++] = PackUnwindOp(UnwindOp.PushNonvol, entry.PrologOffset, entry.RegIndex);
  128. break;
  129. }
  130. default: throw new NotImplementedException($"({nameof(entry.PseudoOp)} = {entry.PseudoOp})");
  131. }
  132. }
  133. Debug.Assert(codeIndex <= MaxUnwindCodesArraySize);
  134. _unwindInfo->VersionAndFlags = 1; // Flags: The function has no handler.
  135. _unwindInfo->SizeOfProlog = (byte)unwindInfo.PrologSize;
  136. _unwindInfo->CountOfUnwindCodes = (byte)codeIndex;
  137. _unwindInfo->FrameRegister = 0;
  138. _runtimeFunction->BeginAddress = (uint)funcEntry.Offset;
  139. _runtimeFunction->EndAddress = (uint)(funcEntry.Offset + funcEntry.Size);
  140. _runtimeFunction->UnwindData = (uint)_sizeOfRuntimeFunction;
  141. return _runtimeFunction;
  142. }
  143. private static ushort PackUnwindOp(UnwindOp op, int prologOffset, int opInfo)
  144. {
  145. return (ushort)(prologOffset | ((int)op << 8) | (opInfo << 12));
  146. }
  147. }
  148. }