template <typename TAlloc> inline void JITThunkEmitter<TAlloc>::FreeThunk(uintptr_t thunkAddress) { AutoCriticalSection autoCs(&this->cs); BVIndex thunkIndex = GetThunkIndexFromAddress(thunkAddress); if (thunkIndex >= this->freeThunks.Length() || this->freeThunks.TestAndSet(thunkIndex)) { Assert(UNREACHED); this->firstBitToCheck = 0; return; } if (thunkIndex < firstBitToCheck) { this->firstBitToCheck = thunkIndex; } if (CONFIG_FLAG(OOPCFGRegistration)) { #if ENABLE_OOP_NATIVE_CODEGEN if (JITManager::GetJITManager()->IsJITServer()) { HANDLE fileHandle = nullptr; PVOID baseAddress = nullptr; bool found = this->codeAllocator->GetFileInfo((PVOID)thunkAddress, &fileHandle, &baseAddress); AssertOrFailFast(found); this->threadContext->SetValidCallTargetFile((PVOID)thunkAddress, fileHandle, baseAddress, false); } else #endif { this->threadContext->SetValidCallTargetForCFG((PVOID)thunkAddress, false); } } uintptr_t pageStartAddress = GetThunkPageStart(thunkAddress); if (IsThunkPageEmpty(pageStartAddress)) { this->codeAllocator->Free((PVOID)pageStartAddress, AutoSystemInfo::PageSize, MEM_DECOMMIT); } else { char * localAddress = (char *)this->codeAllocator->AllocLocal((PVOID)thunkAddress, ThunkSize); if (localAddress == nullptr) { return; } UnprotectPage(localAddress); memset(localAddress, 0xCC, ThunkSize); ProtectPage(localAddress); this->codeAllocator->FreeLocal(localAddress); } FlushInstructionCache(this->processHandle, (PVOID)thunkAddress, ThunkSize); }
void EmitBufferManager<TAlloc, TPreReservedAlloc, SyncObject>::FreeAllocations(bool release) { #if PDATA_ENABLED && defined(_WIN32) DelayDeletingFunctionTable::Clear(); #endif AutoRealOrFakeCriticalSection<SyncObject> autoCs(&this->criticalSection); #if DBG_DUMP if (!release && PHASE_STATS1(Js::EmitterPhase)) { this->DumpAndResetStats(Js::Configuration::Global.flags.Filename); } #endif TEmitBufferAllocation * allocation = this->allocations; while (allocation != nullptr) { #ifdef ENABLE_DEBUG_CONFIG_OPTIONS if(CONFIG_FLAG(CheckEmitBufferPermissions)) { CheckBufferPermissions(allocation); } #endif if (release) { this->allocationHeap.Free(allocation->allocation); } else if ((scriptContext != nullptr) && allocation->recorded) { // In case of ThunkEmitter the script context would be null and we don't want to track that as code size. this->scriptContext->GetThreadContext()->SubCodeSize(allocation->bytesCommitted); allocation->recorded = false; } allocation = allocation->nextAllocation; } if (release) { this->allocations = nullptr; } else { this->allocationHeap.DecommitAll(); } }
template <typename TAlloc> inline uintptr_t JITThunkEmitter<TAlloc>::EnsureInitialized() { if (this->baseAddress != NULL) { return this->baseAddress; } // only take a lock if we need to initialize { AutoCriticalSection autoCs(&this->cs); // check again because we did the first one outside of lock if (this->baseAddress == NULL) { this->baseAddress = (uintptr_t)this->codeAllocator->AllocPages(nullptr, PageCount, MEM_RESERVE, PAGE_EXECUTE_READ, true); } } return this->baseAddress; }
bool EmitBufferManager<SyncObject>::FreeAllocation(void* address) { AutoRealOrFakeCriticalSection<SyncObject> autoCs(&this->criticalSection); EmitBufferAllocation* previous = nullptr; EmitBufferAllocation* allocation = allocations; while(allocation != nullptr) { if (address >= allocation->allocation->address && address < (allocation->allocation->address + allocation->bytesUsed)) { if (previous == nullptr) { this->allocations = allocation->nextAllocation; } else { previous->nextAllocation = allocation->nextAllocation; } if ((scriptContext != nullptr) && allocation->recorded) { this->scriptContext->GetThreadContext()->SubCodeSize(allocation->bytesCommitted); } VerboseHeapTrace(_u("Freeing 0x%p, allocation: 0x%p\n"), address, allocation->allocation->address); this->allocationHeap.Free(allocation->allocation); this->allocator->Free(allocation, sizeof(EmitBufferAllocation)); return true; } previous = allocation; allocation = allocation->nextAllocation; } return false; }
bool EmitBufferManager<TAlloc, TPreReservedAlloc, SyncObject>::FreeAllocation(void* address) { #if PDATA_ENABLED && defined(_WIN32) DelayDeletingFunctionTable::Clear(); #endif AutoRealOrFakeCriticalSection<SyncObject> autoCs(&this->criticalSection); #if _M_ARM address = (void*)((uintptr_t)address & ~0x1); // clear the thumb bit #endif TEmitBufferAllocation* previous = nullptr; TEmitBufferAllocation* allocation = allocations; while(allocation != nullptr) { if (address == allocation->allocation->address) { if (previous == nullptr) { this->allocations = allocation->nextAllocation; } else { previous->nextAllocation = allocation->nextAllocation; } if ((scriptContext != nullptr) && allocation->recorded) { this->scriptContext->GetThreadContext()->SubCodeSize(allocation->bytesCommitted); } #if defined(_CONTROL_FLOW_GUARD) && !defined(_M_ARM) if (allocation->allocation->thunkAddress) { if (JITManager::GetJITManager()->IsJITServer()) { ((ServerThreadContext*)this->threadContext)->GetJITThunkEmitter()->FreeThunk(allocation->allocation->thunkAddress); } else { ((ThreadContext*)this->threadContext)->GetJITThunkEmitter()->FreeThunk(allocation->allocation->thunkAddress); } } else #endif { if (!JITManager::GetJITManager()->IsJITServer() || CONFIG_FLAG(OOPCFGRegistration)) { void* callTarget = address; #if _M_ARM callTarget = (void*)((uintptr_t)callTarget | 0x1); // add the thumb bit back, so we CFG-unregister the actual call target #endif threadContext->SetValidCallTargetForCFG(callTarget, false); } } VerboseHeapTrace(_u("Freeing 0x%p, allocation: 0x%p\n"), address, allocation->allocation->address); this->allocationHeap.Free(allocation->allocation); this->allocator->Free(allocation, sizeof(TEmitBufferAllocation)); return true; } previous = allocation; allocation = allocation->nextAllocation; } return false; }
template <typename TAlloc> inline uintptr_t JITThunkEmitter<TAlloc>::CreateThunk(uintptr_t entryPoint) { AutoCriticalSection autoCs(&this->cs); if(EnsureInitialized() == NULL) { return NULL; } // find available thunk BVIndex thunkIndex = this->freeThunks.GetNextBit(this->firstBitToCheck); if (thunkIndex == BVInvalidIndex) { return NULL; } uintptr_t thunkAddress = GetThunkAddressFromIndex(thunkIndex); uintptr_t pageStartAddress = GetThunkPageStart(thunkAddress); char * localPageAddress = (char *)this->codeAllocator->AllocLocal((PVOID)pageStartAddress, AutoSystemInfo::PageSize); if (localPageAddress == nullptr) { return NULL; } if (IsThunkPageEmpty(pageStartAddress)) { if (this->codeAllocator->AllocPages((PVOID)pageStartAddress, 1, MEM_COMMIT, PAGE_EXECUTE_READ, true) == nullptr) { this->codeAllocator->FreeLocal(localPageAddress); return NULL; } UnprotectPage(localPageAddress); memset(localPageAddress, 0xCC, AutoSystemInfo::PageSize); } else { UnprotectPage(localPageAddress); } EncodeJmp(localPageAddress, thunkAddress, entryPoint); ProtectPage(localPageAddress); this->codeAllocator->FreeLocal(localPageAddress); if (CONFIG_FLAG(OOPCFGRegistration)) { #if ENABLE_OOP_NATIVE_CODEGEN if (JITManager::GetJITManager()->IsJITServer()) { HANDLE fileHandle = nullptr; PVOID baseAddress = nullptr; bool found = this->codeAllocator->GetFileInfo((PVOID)thunkAddress, &fileHandle, &baseAddress); AssertOrFailFast(found); this->threadContext->SetValidCallTargetFile((PVOID)thunkAddress, fileHandle, baseAddress, true); } else #endif { this->threadContext->SetValidCallTargetForCFG((PVOID)thunkAddress); } } this->firstBitToCheck = (thunkIndex + 1 < JITThunkEmitter<TAlloc>::TotalThunkCount) ? thunkIndex + 1 : 0; this->freeThunks.Clear(thunkIndex); if (!FlushInstructionCache(this->processHandle, (PVOID)thunkAddress, ThunkSize)) { return NULL; } return thunkAddress; }