EmitBuffer.cpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659
  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. //----------------------------------------------------------------------------
  7. // EmitBufferManager::EmitBufferManager
  8. // Constructor
  9. //----------------------------------------------------------------------------
  10. template <typename TAlloc, typename TPreReservedAlloc, typename SyncObject>
  11. EmitBufferManager<TAlloc, TPreReservedAlloc, SyncObject>::EmitBufferManager(ArenaAllocator * allocator, CustomHeap::CodePageAllocators<TAlloc, TPreReservedAlloc> * codePageAllocators,
  12. Js::ScriptContext * scriptContext, ThreadContextInfo * threadContext, LPCWSTR name, HANDLE processHandle) :
  13. allocationHeap(allocator, codePageAllocators, processHandle),
  14. allocator(allocator),
  15. allocations(nullptr),
  16. scriptContext(scriptContext),
  17. threadContext(threadContext),
  18. processHandle(processHandle)
  19. {
  20. #if DBG_DUMP
  21. this->totalBytesCode = 0;
  22. this->totalBytesLoopBody = 0;
  23. this->totalBytesAlignment = 0;
  24. this->totalBytesCommitted = 0;
  25. this->totalBytesReserved = 0;
  26. this->name = name;
  27. #endif
  28. }
  29. //----------------------------------------------------------------------------
  30. // EmitBufferManager::~EmitBufferManager()
  31. // Free up all the VirtualAlloced memory
  32. //----------------------------------------------------------------------------
  33. template <typename TAlloc, typename TPreReservedAlloc, class SyncObject>
  34. EmitBufferManager<TAlloc, TPreReservedAlloc, SyncObject>::~EmitBufferManager()
  35. {
  36. Clear();
  37. }
  38. template <typename TAlloc, typename TPreReservedAlloc, class SyncObject>
  39. void
  40. EmitBufferManager<TAlloc, TPreReservedAlloc, SyncObject>::Decommit()
  41. {
  42. FreeAllocations(false);
  43. }
  44. template <typename TAlloc, typename TPreReservedAlloc, class SyncObject>
  45. void
  46. EmitBufferManager<TAlloc, TPreReservedAlloc, SyncObject>::Clear()
  47. {
  48. FreeAllocations(true);
  49. }
  50. template <typename TAlloc, typename TPreReservedAlloc, class SyncObject>
  51. void
  52. EmitBufferManager<TAlloc, TPreReservedAlloc, SyncObject>::FreeAllocations(bool release)
  53. {
  54. #if PDATA_ENABLED && defined(_WIN32)
  55. DelayDeletingFunctionTable::Clear();
  56. #endif
  57. AutoRealOrFakeCriticalSection<SyncObject> autoCs(&this->criticalSection);
  58. #if DBG_DUMP
  59. if (!release && PHASE_STATS1(Js::EmitterPhase))
  60. {
  61. this->DumpAndResetStats(Js::Configuration::Global.flags.Filename);
  62. }
  63. #endif
  64. TEmitBufferAllocation * allocation = this->allocations;
  65. while (allocation != nullptr)
  66. {
  67. #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
  68. if(CONFIG_FLAG(CheckEmitBufferPermissions))
  69. {
  70. CheckBufferPermissions(allocation);
  71. }
  72. #endif
  73. if (release)
  74. {
  75. this->allocationHeap.Free(allocation->allocation);
  76. }
  77. else if ((scriptContext != nullptr) && allocation->recorded)
  78. {
  79. // In case of ThunkEmitter the script context would be null and we don't want to track that as code size.
  80. this->scriptContext->GetThreadContext()->SubCodeSize(allocation->bytesCommitted);
  81. allocation->recorded = false;
  82. }
  83. allocation = allocation->nextAllocation;
  84. }
  85. if (release)
  86. {
  87. this->allocations = nullptr;
  88. }
  89. else
  90. {
  91. this->allocationHeap.DecommitAll();
  92. }
  93. }
  94. template <typename TAlloc, typename TPreReservedAlloc, class SyncObject>
  95. bool EmitBufferManager<TAlloc, TPreReservedAlloc, SyncObject>::IsInHeap(__in void* address)
  96. {
  97. AutoRealOrFakeCriticalSection<SyncObject> autocs(&this->criticalSection);
  98. return this->allocationHeap.IsInHeap(address);
  99. }
  100. template <typename TAlloc, typename TPreReservedAlloc>
  101. class AutoCustomHeapPointer
  102. {
  103. public:
  104. AutoCustomHeapPointer(CustomHeap::Heap<TAlloc, TPreReservedAlloc> * allocationHeap, CustomHeap::Allocation* heapAllocation) :
  105. _allocationHeap(allocationHeap),
  106. _heapAllocation(heapAllocation)
  107. {
  108. }
  109. ~AutoCustomHeapPointer()
  110. {
  111. if (_heapAllocation)
  112. {
  113. _allocationHeap->Free(_heapAllocation);
  114. }
  115. }
  116. CustomHeap::Allocation* Detach()
  117. {
  118. CustomHeap::Allocation* allocation = _heapAllocation;
  119. Assert(allocation != nullptr);
  120. _heapAllocation = nullptr;
  121. return allocation;
  122. }
  123. private:
  124. CustomHeap::Allocation* _heapAllocation;
  125. CustomHeap::Heap<TAlloc, TPreReservedAlloc>* _allocationHeap;
  126. };
  127. //----------------------------------------------------------------------------
  128. // EmitBufferManager::NewAllocation
  129. // Create a new allocation
  130. //----------------------------------------------------------------------------
  131. template <typename TAlloc, typename TPreReservedAlloc, class SyncObject>
  132. EmitBufferAllocation<TAlloc, TPreReservedAlloc> *
  133. EmitBufferManager<TAlloc, TPreReservedAlloc, SyncObject>::NewAllocation(size_t bytes, ushort pdataCount, ushort xdataSize, bool canAllocInPreReservedHeapPageSegment, bool isAnyJittedCode)
  134. {
  135. FAULTINJECT_MEMORY_THROW(_u("JIT"), bytes);
  136. Assert(this->criticalSection.IsLocked());
  137. bool isAllJITCodeInPreReservedRegion = true;
  138. CustomHeap::Allocation* heapAllocation = this->allocationHeap.Alloc(bytes, pdataCount, xdataSize, canAllocInPreReservedHeapPageSegment, isAnyJittedCode, &isAllJITCodeInPreReservedRegion);
  139. if (heapAllocation == nullptr)
  140. {
  141. if (!JITManager::GetJITManager()->IsJITServer())
  142. {
  143. // This is used in interpreter scenario, thus we need to try to recover memory, if possible.
  144. // Can't simply throw as in JIT scenario, for which throw is what we want in order to give more mem to interpreter.
  145. JsUtil::ExternalApi::RecoverUnusedMemory();
  146. heapAllocation = this->allocationHeap.Alloc(bytes, pdataCount, xdataSize, canAllocInPreReservedHeapPageSegment, isAnyJittedCode, &isAllJITCodeInPreReservedRegion);
  147. }
  148. }
  149. if (heapAllocation == nullptr)
  150. {
  151. Js::Throw::OutOfMemory();
  152. }
  153. #if DBG
  154. heapAllocation->isAllocationUsed = true;
  155. #endif
  156. AutoCustomHeapPointer<TAlloc, TPreReservedAlloc> allocatedMemory(&this->allocationHeap, heapAllocation);
  157. VerboseHeapTrace(_u("New allocation: 0x%p, size: %p\n"), heapAllocation->address, heapAllocation->size);
  158. TEmitBufferAllocation * allocation = AnewStruct(this->allocator, TEmitBufferAllocation);
  159. allocation->bytesCommitted = heapAllocation->size;
  160. allocation->allocation = allocatedMemory.Detach();
  161. allocation->bytesUsed = 0;
  162. allocation->nextAllocation = this->allocations;
  163. allocation->recorded = false;
  164. allocation->inPrereservedRegion = isAllJITCodeInPreReservedRegion;
  165. this->allocations = allocation;
  166. #if DBG_DUMP
  167. this->totalBytesCommitted += heapAllocation->size;
  168. #endif
  169. return allocation;
  170. }
  171. template <typename TAlloc, typename TPreReservedAlloc, class SyncObject>
  172. void
  173. EmitBufferManager<TAlloc, TPreReservedAlloc, SyncObject>::SetValidCallTarget(TEmitBufferAllocation* allocation, void* callTarget, bool isValid)
  174. {
  175. #if _M_ARM
  176. callTarget = (void*)((uintptr_t)callTarget | 0x1); // add the thumb bit back, so we CFG-unregister the actual call target
  177. #endif
  178. if (!JITManager::GetJITManager()->IsJITServer())
  179. {
  180. this->threadContext->SetValidCallTargetForCFG(callTarget, isValid);
  181. }
  182. #if ENABLE_OOP_NATIVE_CODEGEN
  183. else if (CONFIG_FLAG(OOPCFGRegistration))
  184. {
  185. void* segment = allocation->allocation->IsLargeAllocation()
  186. ? allocation->allocation->largeObjectAllocation.segment
  187. : allocation->allocation->page->segment;
  188. HANDLE fileHandle = nullptr;
  189. PVOID baseAddress = nullptr;
  190. bool found = false;
  191. if (this->allocationHeap.IsPreReservedSegment(segment))
  192. {
  193. found = ((SegmentBase<TPreReservedAlloc>*)segment)->GetAllocator()->GetVirtualAllocator()->GetFileInfo(callTarget, &fileHandle, &baseAddress);
  194. }
  195. else
  196. {
  197. found = ((SegmentBase<TAlloc>*)segment)->GetAllocator()->GetVirtualAllocator()->GetFileInfo(callTarget, &fileHandle, &baseAddress);
  198. }
  199. AssertOrFailFast(found);
  200. this->threadContext->SetValidCallTargetFile(callTarget, fileHandle, baseAddress, isValid);
  201. }
  202. #endif
  203. }
  204. template <typename TAlloc, typename TPreReservedAlloc, class SyncObject>
  205. bool
  206. EmitBufferManager<TAlloc, TPreReservedAlloc, SyncObject>::FreeAllocation(void* address)
  207. {
  208. #if PDATA_ENABLED && defined(_WIN32)
  209. DelayDeletingFunctionTable::Clear();
  210. #endif
  211. AutoRealOrFakeCriticalSection<SyncObject> autoCs(&this->criticalSection);
  212. #if _M_ARM
  213. address = (void*)((uintptr_t)address & ~0x1); // clear the thumb bit
  214. #endif
  215. TEmitBufferAllocation* previous = nullptr;
  216. TEmitBufferAllocation* allocation = allocations;
  217. while(allocation != nullptr)
  218. {
  219. if (address == allocation->allocation->address)
  220. {
  221. if (previous == nullptr)
  222. {
  223. this->allocations = allocation->nextAllocation;
  224. }
  225. else
  226. {
  227. previous->nextAllocation = allocation->nextAllocation;
  228. }
  229. if ((scriptContext != nullptr) && allocation->recorded)
  230. {
  231. this->scriptContext->GetThreadContext()->SubCodeSize(allocation->bytesCommitted);
  232. }
  233. #if defined(_CONTROL_FLOW_GUARD) && !defined(_M_ARM)
  234. if (allocation->allocation->thunkAddress)
  235. {
  236. if (JITManager::GetJITManager()->IsJITServer())
  237. {
  238. ((ServerThreadContext*)this->threadContext)->GetJITThunkEmitter()->FreeThunk(allocation->allocation->thunkAddress);
  239. }
  240. else
  241. {
  242. ((ThreadContext*)this->threadContext)->GetJITThunkEmitter()->FreeThunk(allocation->allocation->thunkAddress);
  243. }
  244. }
  245. else
  246. #endif
  247. {
  248. SetValidCallTarget(allocation, address, false);
  249. }
  250. VerboseHeapTrace(_u("Freeing 0x%p, allocation: 0x%p\n"), address, allocation->allocation->address);
  251. this->allocationHeap.Free(allocation->allocation);
  252. this->allocator->Free(allocation, sizeof(TEmitBufferAllocation));
  253. return true;
  254. }
  255. previous = allocation;
  256. allocation = allocation->nextAllocation;
  257. }
  258. return false;
  259. }
  260. //----------------------------------------------------------------------------
  261. // EmitBufferManager::FinalizeAllocation
  262. // Fill the rest of the buffer (length given by allocation->BytesFree()) with debugger breakpoints.
  263. //----------------------------------------------------------------------------
  264. template <typename TAlloc, typename TPreReservedAlloc, class SyncObject>
  265. bool EmitBufferManager<TAlloc, TPreReservedAlloc, SyncObject>::FinalizeAllocation(TEmitBufferAllocation *allocation, BYTE * dstBuffer)
  266. {
  267. Assert(this->criticalSection.IsLocked());
  268. DWORD bytes = allocation->BytesFree();
  269. if(bytes > 0)
  270. {
  271. BYTE* buffer = nullptr;
  272. this->GetBuffer(allocation, bytes, &buffer);
  273. if (!this->CommitBuffer(allocation, allocation->bytesCommitted, dstBuffer, 0, /*sourceBuffer=*/ nullptr, /*alignPad=*/ bytes))
  274. {
  275. return false;
  276. }
  277. #if DBG_DUMP
  278. this->totalBytesCode -= bytes;
  279. #endif
  280. }
  281. return true;
  282. }
  283. template <typename TAlloc, typename TPreReservedAlloc, class SyncObject>
  284. EmitBufferAllocation<TAlloc, TPreReservedAlloc>*
  285. EmitBufferManager<TAlloc, TPreReservedAlloc, SyncObject>::GetBuffer(TEmitBufferAllocation *allocation, __in size_t bytes, __deref_bcount(bytes) BYTE** ppBuffer)
  286. {
  287. Assert(this->criticalSection.IsLocked());
  288. Assert(allocation->BytesFree() >= bytes);
  289. // In case of ThunkEmitter the script context would be null and we don't want to track that as code size.
  290. if (scriptContext && !allocation->recorded)
  291. {
  292. this->scriptContext->GetThreadContext()->AddCodeSize(allocation->bytesCommitted);
  293. allocation->recorded = true;
  294. }
  295. // The codegen buffer is beyond the alignment section - hence, we pass this pointer.
  296. *ppBuffer = allocation->GetUnused();
  297. return allocation;
  298. }
  299. //----------------------------------------------------------------------------
  300. // EmitBufferManager::Allocate
  301. // Allocates an executable buffer with a certain alignment
  302. // NOTE: This buffer is not readable or writable. Use CommitBuffer
  303. // to modify this buffer one page at a time.
  304. //----------------------------------------------------------------------------
  305. template <typename TAlloc, typename TPreReservedAlloc, class SyncObject>
  306. EmitBufferAllocation<TAlloc, TPreReservedAlloc>*
  307. EmitBufferManager<TAlloc, TPreReservedAlloc, SyncObject>::AllocateBuffer(__in size_t bytes, __deref_bcount(bytes) BYTE** ppBuffer, ushort pdataCount /*=0*/, ushort xdataSize /*=0*/, bool canAllocInPreReservedHeapPageSegment /*=false*/,
  308. bool isAnyJittedCode /* = false*/)
  309. {
  310. AutoRealOrFakeCriticalSection<SyncObject> autoCs(&this->criticalSection);
  311. Assert(ppBuffer != nullptr);
  312. TEmitBufferAllocation * allocation = this->NewAllocation(bytes, pdataCount, xdataSize, canAllocInPreReservedHeapPageSegment, isAnyJittedCode);
  313. GetBuffer(allocation, bytes, ppBuffer);
  314. #if DBG
  315. MEMORY_BASIC_INFORMATION memBasicInfo;
  316. size_t resultBytes = VirtualQueryEx(this->processHandle, allocation->allocation->address, &memBasicInfo, sizeof(memBasicInfo));
  317. Assert(resultBytes == 0 || memBasicInfo.Protect == PAGE_EXECUTE_READ);
  318. #endif
  319. return allocation;
  320. }
  321. #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
  322. template <typename TAlloc, typename TPreReservedAlloc, class SyncObject>
  323. bool EmitBufferManager<TAlloc, TPreReservedAlloc, SyncObject>::CheckCommitFaultInjection()
  324. {
  325. if (Js::Configuration::Global.flags.ForceOOMOnEBCommit == 0)
  326. {
  327. return false;
  328. }
  329. commitCount++;
  330. if (Js::Configuration::Global.flags.ForceOOMOnEBCommit == -1)
  331. {
  332. Output::Print(_u("Commit count: %d\n"), commitCount);
  333. }
  334. else if (commitCount == Js::Configuration::Global.flags.ForceOOMOnEBCommit)
  335. {
  336. return true;
  337. }
  338. return false;
  339. }
  340. #endif
  341. #if DBG
  342. template <typename TAlloc, typename TPreReservedAlloc, class SyncObject>
  343. bool EmitBufferManager<TAlloc, TPreReservedAlloc, SyncObject>::IsBufferExecuteReadOnly(TEmitBufferAllocation * allocation)
  344. {
  345. AutoRealOrFakeCriticalSection<SyncObject> autoCs(&this->criticalSection);
  346. MEMORY_BASIC_INFORMATION memBasicInfo;
  347. size_t resultBytes = VirtualQuery(allocation->allocation->address, &memBasicInfo, sizeof(memBasicInfo));
  348. return resultBytes != 0 && memBasicInfo.Protect == PAGE_EXECUTE_READ;
  349. }
  350. #endif
  351. template <typename TAlloc, typename TPreReservedAlloc, class SyncObject>
  352. bool EmitBufferManager<TAlloc, TPreReservedAlloc, SyncObject>::ProtectBufferWithExecuteReadWriteForInterpreter(TEmitBufferAllocation* allocation)
  353. {
  354. Assert(this->criticalSection.IsLocked());
  355. Assert(allocation != nullptr);
  356. return (this->allocationHeap.ProtectAllocationWithExecuteReadWrite(allocation->allocation) == TRUE);
  357. }
  358. // Returns true if we successfully commit the buffer
  359. // Returns false if we OOM
  360. template <typename TAlloc, typename TPreReservedAlloc, class SyncObject>
  361. bool EmitBufferManager<TAlloc, TPreReservedAlloc, SyncObject>::CommitBufferForInterpreter(TEmitBufferAllocation* allocation, _In_reads_bytes_(bufferSize) BYTE* pBuffer, _In_ size_t bufferSize)
  362. {
  363. AutoRealOrFakeCriticalSection<SyncObject> autoCs(&this->criticalSection);
  364. Assert(allocation != nullptr);
  365. allocation->bytesUsed += bufferSize;
  366. #ifdef DEBUG
  367. this->totalBytesCode += bufferSize;
  368. #endif
  369. VerboseHeapTrace(_u("Setting execute permissions on 0x%p, allocation: 0x%p\n"), pBuffer, allocation->allocation->address);
  370. #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
  371. if (CheckCommitFaultInjection())
  372. {
  373. return false;
  374. }
  375. #endif
  376. if (!JITManager::GetJITManager()->IsJITServer() && !this->allocationHeap.ProtectAllocationWithExecuteReadOnly(allocation->allocation))
  377. {
  378. return false;
  379. }
  380. if (!FlushInstructionCache(this->processHandle, pBuffer, bufferSize))
  381. {
  382. return false;
  383. }
  384. return true;
  385. }
  386. //----------------------------------------------------------------------------
  387. // EmitBufferManager::CommitBuffer
  388. // Aligns the buffer with DEBUG instructions.
  389. // Copies contents of source buffer to the destination buffer - at max of one page at a time.
  390. // This ensures that only 1 page is writable at any point of time.
  391. // Commit a buffer from the last AllocateBuffer call that is filled.
  392. //
  393. // Skips over the initial allocation->GetBytesUsed() bytes of destBuffer. Then, fills in `alignPad` bytes with debug breakpoint instructions,
  394. // copies `bytes` bytes from sourceBuffer, and finally fills in the rest of destBuffer with debug breakpoint instructions.
  395. //----------------------------------------------------------------------------
  396. template <typename TAlloc, typename TPreReservedAlloc, class SyncObject>
  397. bool
  398. EmitBufferManager<TAlloc, TPreReservedAlloc, SyncObject>::CommitBuffer(TEmitBufferAllocation* allocation, __in const size_t destBufferBytes, __out_bcount(destBufferBytes) BYTE* destBuffer, __in size_t bytes, __in_bcount(bytes) const BYTE* sourceBuffer, __in DWORD alignPad)
  399. {
  400. AutoRealOrFakeCriticalSection<SyncObject> autoCs(&this->criticalSection);
  401. Assert(destBuffer != nullptr);
  402. Assert(allocation != nullptr);
  403. // The size of destBuffer is actually given by allocation->bytesCommitted, but due to a bug in some versions of PREFast, we can't refer to allocation->bytesCommitted in the
  404. // SAL annotation on destBuffer above. We've informed the PREFast maintainers, but we'll have to use destBufferBytes as a workaround until their fix makes it to Jenkins.
  405. Assert(destBufferBytes == allocation->bytesCommitted);
  406. // Must have at least enough room in destBuffer for the initial skipped bytes plus the bytes we're going to write.
  407. AnalysisAssert(allocation->bytesUsed + bytes + alignPad <= destBufferBytes);
  408. BYTE *currentDestBuffer = destBuffer + allocation->GetBytesUsed();
  409. char *bufferToFlush = allocation->allocation->address + allocation->GetBytesUsed();
  410. Assert(allocation->BytesFree() >= bytes + alignPad);
  411. size_t bytesLeft = bytes + alignPad;
  412. size_t sizeToFlush = bytesLeft;
  413. // Copy the contents and set the alignment pad
  414. while(bytesLeft != 0)
  415. {
  416. // currentDestBuffer must still point to somewhere in the interior of destBuffer.
  417. AnalysisAssert(destBuffer <= currentDestBuffer);
  418. AnalysisAssert(currentDestBuffer < destBuffer + destBufferBytes);
  419. DWORD spaceInCurrentPage = AutoSystemInfo::PageSize - ((size_t)currentDestBuffer & (AutoSystemInfo::PageSize - 1));
  420. size_t bytesToChange = bytesLeft > spaceInCurrentPage ? spaceInCurrentPage : bytesLeft;
  421. // Buffer and the bytes that are marked RWX - these will eventually be marked as 'EXCEUTE' only.
  422. BYTE* readWriteBuffer = currentDestBuffer;
  423. size_t readWriteBytes = bytesToChange;
  424. #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
  425. if (CheckCommitFaultInjection())
  426. {
  427. return false;
  428. }
  429. #endif
  430. if (!JITManager::GetJITManager()->IsJITServer() && !this->allocationHeap.ProtectAllocationWithExecuteReadWrite(allocation->allocation, (char*)readWriteBuffer))
  431. {
  432. return false;
  433. }
  434. // Pad with debug-breakpoint instructions up to alignBytes or the end of the current page, whichever is less.
  435. if (alignPad != 0)
  436. {
  437. DWORD alignBytes = alignPad < spaceInCurrentPage ? alignPad : spaceInCurrentPage;
  438. CustomHeap::FillDebugBreak(currentDestBuffer, alignBytes);
  439. alignPad -= alignBytes;
  440. currentDestBuffer += alignBytes;
  441. allocation->bytesUsed += alignBytes;
  442. bytesLeft -= alignBytes;
  443. bytesToChange -= alignBytes;
  444. #if DBG_DUMP
  445. this->totalBytesAlignment += alignBytes;
  446. #endif
  447. }
  448. // If there are bytes still left to be copied then we should do the copy, but only through the end of the current page.
  449. if(bytesToChange > 0)
  450. {
  451. AssertMsg(alignPad == 0, "If we are copying right now - we should be done with setting alignment.");
  452. const DWORD bufferBytesFree(allocation->BytesFree());
  453. // Use <= here instead of < to allow this memcopy to fill up the rest of destBuffer. If we do, then FinalizeAllocation,
  454. // called below, determines that no additional padding is necessary based on the values in `allocation'.
  455. AnalysisAssert(currentDestBuffer + bufferBytesFree <= destBuffer + destBufferBytes);
  456. memcpy_s(currentDestBuffer, bufferBytesFree, sourceBuffer, bytesToChange);
  457. currentDestBuffer += bytesToChange;
  458. sourceBuffer += bytesToChange;
  459. allocation->bytesUsed += bytesToChange;
  460. bytesLeft -= bytesToChange;
  461. }
  462. Assert(readWriteBuffer + readWriteBytes == currentDestBuffer);
  463. if (!JITManager::GetJITManager()->IsJITServer() && !this->allocationHeap.ProtectAllocationWithExecuteReadOnly(allocation->allocation, (char*)readWriteBuffer))
  464. {
  465. return false;
  466. }
  467. }
  468. if (!FlushInstructionCache(this->processHandle, bufferToFlush, sizeToFlush))
  469. {
  470. return false;
  471. }
  472. #if DBG_DUMP
  473. this->totalBytesCode += bytes;
  474. #endif
  475. //Finish the current EmitBufferAllocation by filling out the rest of destBuffer with debug breakpoint instructions.
  476. return FinalizeAllocation(allocation, destBuffer);
  477. }
  478. template <typename TAlloc, typename TPreReservedAlloc, class SyncObject>
  479. void
  480. EmitBufferManager<TAlloc, TPreReservedAlloc, SyncObject>::CompletePreviousAllocation(TEmitBufferAllocation* allocation)
  481. {
  482. AutoRealOrFakeCriticalSection<SyncObject> autoCs(&this->criticalSection);
  483. if (allocation != nullptr)
  484. {
  485. allocation->bytesUsed = allocation->bytesCommitted;
  486. }
  487. }
  488. #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
  489. template <typename TAlloc, typename TPreReservedAlloc, class SyncObject>
  490. void
  491. EmitBufferManager<TAlloc, TPreReservedAlloc, SyncObject>::CheckBufferPermissions(TEmitBufferAllocation *allocation)
  492. {
  493. AutoRealOrFakeCriticalSection<SyncObject> autoCs(&this->criticalSection);
  494. if(allocation->bytesCommitted == 0)
  495. return;
  496. MEMORY_BASIC_INFORMATION memInfo;
  497. BYTE *buffer = (BYTE*) allocation->allocation->address;
  498. SIZE_T size = allocation->bytesCommitted;
  499. while(1)
  500. {
  501. SIZE_T result = VirtualQuery(buffer, &memInfo, sizeof(memInfo));
  502. if(result == 0)
  503. {
  504. // VirtualQuery failed. This is not an expected condition, but it would be benign for the purposes of this check. Seems
  505. // to occur occasionally on process shutdown.
  506. break;
  507. }
  508. else if(memInfo.Protect == PAGE_EXECUTE_READWRITE)
  509. {
  510. Output::Print(_u("ERROR: Found PAGE_EXECUTE_READWRITE page!\n"));
  511. #ifdef DEBUG
  512. AssertMsg(FALSE, "Page was marked PAGE_EXECUTE_READWRITE");
  513. #else
  514. Fatal();
  515. #endif
  516. }
  517. // Figure out if we need to continue the query. The returned size might be larger than the size we requested,
  518. // for instance if more pages were allocated directly afterward, with the same permissions.
  519. if(memInfo.RegionSize >= size)
  520. {
  521. break;
  522. }
  523. // recalculate size for next iteration
  524. buffer += memInfo.RegionSize;
  525. size -= memInfo.RegionSize;
  526. if(size <= 0)
  527. {
  528. AssertMsg(FALSE, "Last VirtualQuery left us with unmatched regions");
  529. break;
  530. }
  531. }
  532. }
  533. #endif
  534. #if DBG_DUMP
  535. template <typename TAlloc, typename TPreReservedAlloc, class SyncObject>
  536. void
  537. EmitBufferManager<TAlloc, TPreReservedAlloc, SyncObject>::DumpAndResetStats(char16 const * filename)
  538. {
  539. if (this->totalBytesCommitted != 0)
  540. {
  541. size_t wasted = this->totalBytesCommitted - this->totalBytesCode - this->totalBytesAlignment;
  542. Output::Print(_u("Stats for %s: %s \n"), name, filename);
  543. Output::Print(_u(" Total code size : %10d (%6.2f%% of committed)\n"), this->totalBytesCode,
  544. (float)this->totalBytesCode * 100 / this->totalBytesCommitted);
  545. Output::Print(_u(" Total LoopBody code : %10d\n"), this->totalBytesLoopBody);
  546. Output::Print(_u(" Total alignment size : %10d (%6.2f%% of committed)\n"), this->totalBytesAlignment,
  547. (float)this->totalBytesAlignment * 100 / this->totalBytesCommitted);
  548. Output::Print(_u(" Total wasted size : %10d (%6.2f%% of committed)\n"), wasted,
  549. (float)wasted * 100 / this->totalBytesCommitted);
  550. Output::Print(_u(" Total committed size : %10d (%6.2f%% of reserved)\n"), this->totalBytesCommitted,
  551. (float)this->totalBytesCommitted * 100 / this->totalBytesReserved);
  552. Output::Print(_u(" Total reserved size : %10d\n"), this->totalBytesReserved);
  553. }
  554. this->totalBytesCode = 0;
  555. this->totalBytesLoopBody = 0;
  556. this->totalBytesAlignment = 0;
  557. this->totalBytesCommitted = 0;
  558. this->totalBytesReserved = 0;
  559. }
  560. #endif
  561. template class EmitBufferManager<VirtualAllocWrapper, PreReservedVirtualAllocWrapper, FakeCriticalSection>;
  562. template class EmitBufferManager<VirtualAllocWrapper, PreReservedVirtualAllocWrapper, CriticalSection>;
  563. #if ENABLE_OOP_NATIVE_CODEGEN
  564. template class EmitBufferManager<SectionAllocWrapper, PreReservedSectionAllocWrapper, FakeCriticalSection>;
  565. template class EmitBufferManager<SectionAllocWrapper, PreReservedSectionAllocWrapper, CriticalSection>;
  566. #endif