JITThunkEmitter.cpp 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. //-------------------------------------------------------------------------------------------------------
  2. // Copyright (C) Microsoft. All rights reserved.
  3. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  4. //-------------------------------------------------------------------------------------------------------
  5. #include "Backend.h"
  6. #if defined(ENABLE_NATIVE_CODEGEN) && defined(_CONTROL_FLOW_GUARD) && (_M_IX86 || _M_X64)
  7. template class JITThunkEmitter<VirtualAllocWrapper>;
  8. #if ENABLE_OOP_NATIVE_CODEGEN
  9. template class JITThunkEmitter<SectionAllocWrapper>;
  10. #endif
  11. template <typename TAlloc>
  12. const BYTE JITThunkEmitter<TAlloc>::DirectJmp[] = {
  13. 0xE9, 0x00, 0x00, 0x00, 0x00, // JMP <relativeAddress>.32
  14. 0xCC, 0xCC, 0xCC,
  15. 0xCC, 0xCC, 0xCC, 0xCC,
  16. 0xCC, 0xCC, 0xCC, 0xCC
  17. };
  18. #if _M_X64
  19. template <typename TAlloc>
  20. const BYTE JITThunkEmitter<TAlloc>::IndirectJmp[] = {
  21. 0x48, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // MOV RAX, <relativeAddress>.64
  22. 0x48, 0xFF, 0xE0, // JMP RAX
  23. 0xCC, 0xCC, 0xCC
  24. };
  25. #endif
  26. template <typename TAlloc>
  27. JITThunkEmitter<TAlloc>::JITThunkEmitter(ThreadContextInfo * threadContext, TAlloc * codeAllocator, HANDLE processHandle) :
  28. processHandle(processHandle),
  29. codeAllocator(codeAllocator),
  30. threadContext(threadContext),
  31. baseAddress(NULL),
  32. firstBitToCheck(0)
  33. {
  34. freeThunks.SetAll();
  35. }
  36. template <typename TAlloc>
  37. JITThunkEmitter<TAlloc>::~JITThunkEmitter()
  38. {
  39. if (baseAddress != NULL)
  40. {
  41. this->codeAllocator->Free((PVOID)baseAddress, TotalThunkSize, MEM_RELEASE);
  42. }
  43. }
  44. template <typename TAlloc> inline
  45. uintptr_t
  46. JITThunkEmitter<TAlloc>::CreateThunk(uintptr_t entryPoint)
  47. {
  48. AutoCriticalSection autoCs(&this->cs);
  49. if(EnsureInitialized() == NULL)
  50. {
  51. return NULL;
  52. }
  53. // find available thunk
  54. BVIndex thunkIndex = this->freeThunks.GetNextBit(this->firstBitToCheck);
  55. if (thunkIndex == BVInvalidIndex)
  56. {
  57. return NULL;
  58. }
  59. uintptr_t thunkAddress = GetThunkAddressFromIndex(thunkIndex);
  60. uintptr_t pageStartAddress = GetThunkPageStart(thunkAddress);
  61. char * localPageAddress = (char *)this->codeAllocator->AllocLocal((PVOID)pageStartAddress, AutoSystemInfo::PageSize);
  62. if (localPageAddress == nullptr)
  63. {
  64. return NULL;
  65. }
  66. if (IsThunkPageEmpty(pageStartAddress))
  67. {
  68. if (this->codeAllocator->Alloc((PVOID)pageStartAddress, AutoSystemInfo::PageSize, MEM_COMMIT, PAGE_EXECUTE, true) == nullptr)
  69. {
  70. this->codeAllocator->FreeLocal(localPageAddress);
  71. return NULL;
  72. }
  73. UnprotectPage(localPageAddress);
  74. memset(localPageAddress, 0xCC, AutoSystemInfo::PageSize);
  75. }
  76. else
  77. {
  78. UnprotectPage(localPageAddress);
  79. }
  80. EncodeJmp(localPageAddress, thunkAddress, entryPoint);
  81. ProtectPage(localPageAddress);
  82. this->codeAllocator->FreeLocal(localPageAddress);
  83. if (CONFIG_FLAG(OOPCFGRegistration))
  84. {
  85. this->threadContext->SetValidCallTargetForCFG((PVOID)thunkAddress);
  86. }
  87. this->firstBitToCheck = (thunkIndex + 1 < JITThunkEmitter<TAlloc>::TotalThunkCount) ? thunkIndex + 1 : 0;
  88. this->freeThunks.Clear(thunkIndex);
  89. FlushInstructionCache(this->processHandle, (PVOID)thunkAddress, ThunkSize);
  90. return thunkAddress;
  91. }
  92. template <typename TAlloc> inline
  93. void
  94. JITThunkEmitter<TAlloc>::FreeThunk(uintptr_t thunkAddress)
  95. {
  96. AutoCriticalSection autoCs(&this->cs);
  97. BVIndex thunkIndex = GetThunkIndexFromAddress(thunkAddress);
  98. if (thunkIndex >= this->freeThunks.Length() || this->freeThunks.TestAndSet(thunkIndex))
  99. {
  100. Assert(UNREACHED);
  101. this->firstBitToCheck = 0;
  102. return;
  103. }
  104. if (thunkIndex < firstBitToCheck)
  105. {
  106. this->firstBitToCheck = thunkIndex;
  107. }
  108. if (CONFIG_FLAG(OOPCFGRegistration))
  109. {
  110. this->threadContext->SetValidCallTargetForCFG((PVOID)thunkAddress, false);
  111. }
  112. uintptr_t pageStartAddress = GetThunkPageStart(thunkAddress);
  113. if (IsThunkPageEmpty(pageStartAddress))
  114. {
  115. this->codeAllocator->Free((PVOID)pageStartAddress, AutoSystemInfo::PageSize, MEM_DECOMMIT);
  116. }
  117. else
  118. {
  119. char * localAddress = (char *)this->codeAllocator->AllocLocal((PVOID)thunkAddress, ThunkSize);
  120. if (localAddress == nullptr)
  121. {
  122. return;
  123. }
  124. UnprotectPage(localAddress);
  125. memset(localAddress, 0xCC, ThunkSize);
  126. ProtectPage(localAddress);
  127. this->codeAllocator->FreeLocal(localAddress);
  128. }
  129. FlushInstructionCache(this->processHandle, (PVOID)thunkAddress, ThunkSize);
  130. }
  131. template <typename TAlloc> inline
  132. uintptr_t
  133. JITThunkEmitter<TAlloc>::EnsureInitialized()
  134. {
  135. if (this->baseAddress != NULL)
  136. {
  137. return this->baseAddress;
  138. }
  139. // only take a lock if we need to initialize
  140. {
  141. AutoCriticalSection autoCs(&this->cs);
  142. // check again because we did the first one outside of lock
  143. if (this->baseAddress == NULL)
  144. {
  145. this->baseAddress = (uintptr_t)this->codeAllocator->Alloc(nullptr, TotalThunkSize, MEM_RESERVE, PAGE_EXECUTE, true);
  146. }
  147. }
  148. return this->baseAddress;
  149. }
  150. template <typename TAlloc> inline
  151. bool
  152. JITThunkEmitter<TAlloc>::IsInThunk(uintptr_t address) const
  153. {
  154. return IsInThunk(this->baseAddress, address);
  155. }
  156. /* static */
  157. template <typename TAlloc> inline
  158. bool
  159. JITThunkEmitter<TAlloc>::IsInThunk(uintptr_t thunkBaseAddress, uintptr_t address)
  160. {
  161. bool isInThunk = address >= thunkBaseAddress && address < thunkBaseAddress + TotalThunkSize;
  162. Assert(!isInThunk || address % ThunkSize == 0);
  163. return isInThunk;
  164. }
  165. /* static */
  166. template <typename TAlloc> inline
  167. void
  168. JITThunkEmitter<TAlloc>::EncodeJmp(char * localPageAddress, uintptr_t thunkAddress, uintptr_t targetAddress)
  169. {
  170. char * localAddress = localPageAddress + thunkAddress % AutoSystemInfo::PageSize;
  171. ptrdiff_t relativeAddress = targetAddress - thunkAddress - DirectJmpIPAdjustment;
  172. #if _M_X64
  173. if (relativeAddress > INT_MAX || relativeAddress < INT_MIN)
  174. {
  175. memcpy_s(localAddress, ThunkSize, IndirectJmp, ThunkSize);
  176. uintptr_t * jmpTarget = (uintptr_t*)(localAddress + IndirectJmpTargetOffset);
  177. *jmpTarget = targetAddress;
  178. }
  179. else
  180. #endif
  181. {
  182. memcpy_s(localAddress, ThunkSize, DirectJmp, ThunkSize);
  183. uintptr_t * jmpTarget = (uintptr_t*)(localAddress + DirectJmpTargetOffset);
  184. *jmpTarget = relativeAddress;
  185. }
  186. }
  187. template <typename TAlloc> inline
  188. bool
  189. JITThunkEmitter<TAlloc>::IsThunkPageEmpty(uintptr_t address) const
  190. {
  191. Assert(address == GetThunkPageStart(address));
  192. BVIndex pageStartIndex = GetThunkIndexFromAddress(address);
  193. Assert(pageStartIndex != BVInvalidIndex);
  194. BVStatic<ThunksPerPage> * pageBV = this->freeThunks.GetRange<ThunksPerPage>(pageStartIndex);
  195. return pageBV->IsAllSet();
  196. }
  197. template <> inline
  198. void
  199. JITThunkEmitter<VirtualAllocWrapper>::ProtectPage(void * address)
  200. {
  201. #if defined(ENABLE_JIT_CLAMP)
  202. AutoEnableDynamicCodeGen enableCodeGen(true);
  203. #endif
  204. DWORD oldProtect;
  205. BOOL result = VirtualProtectEx(this->processHandle, address, ThunkSize, PAGE_EXECUTE, &oldProtect);
  206. AssertOrFailFast(result && oldProtect == PAGE_EXECUTE_READWRITE);
  207. }
  208. template <> inline
  209. void
  210. JITThunkEmitter<VirtualAllocWrapper>::UnprotectPage(void * address)
  211. {
  212. #if defined(ENABLE_JIT_CLAMP)
  213. AutoEnableDynamicCodeGen enableCodeGen(true);
  214. #endif
  215. DWORD oldProtect;
  216. BOOL result = VirtualProtectEx(this->processHandle, address, ThunkSize, PAGE_EXECUTE_READWRITE, &oldProtect);
  217. AssertOrFailFast(result && oldProtect == PAGE_EXECUTE);
  218. }
  219. #if ENABLE_OOP_NATIVE_CODEGEN
  220. template <> inline
  221. void
  222. JITThunkEmitter<SectionAllocWrapper>::ProtectPage(void * address)
  223. {
  224. }
  225. template <> inline
  226. void
  227. JITThunkEmitter<SectionAllocWrapper>::UnprotectPage(void * address)
  228. {
  229. }
  230. #endif
  231. template <typename TAlloc> inline
  232. uintptr_t
  233. JITThunkEmitter<TAlloc>::GetThunkAddressFromIndex(BVIndex index) const
  234. {
  235. return this->baseAddress + index * ThunkSize;
  236. }
  237. template <typename TAlloc> inline
  238. BVIndex
  239. JITThunkEmitter<TAlloc>::GetThunkIndexFromAddress(uintptr_t thunkAddress) const
  240. {
  241. uintptr_t thunkIndex = (thunkAddress - this->baseAddress) / ThunkSize;
  242. #if TARGET_64
  243. if (thunkIndex > BVInvalidIndex)
  244. {
  245. thunkIndex = BVInvalidIndex;
  246. }
  247. #endif
  248. return (BVIndex)thunkIndex;
  249. }
  250. /* static */
  251. template <typename TAlloc> inline
  252. uintptr_t
  253. JITThunkEmitter<TAlloc>::GetThunkPageStart(uintptr_t address)
  254. {
  255. return address - address % AutoSystemInfo::PageSize;
  256. }
  257. #endif