// Copyright (C) Rolland Dudemaine All rights reserved. // Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "config.h" #include "OSAllocator.h" #include #include #include #include #include #define ASP_PAGESIZE 0x1000 namespace WTF { struct MRPair { mutable MemoryRegion pmr; mutable MemoryRegion vmr; mutable bool mapped; Address start; MRPair(Address _start = 0) : pmr(0), vmr(0), mapped(false), start(_start) {} bool operator<(const MRPair& rhs) const { return this->start < rhs.start; } }; class MRContainer { private: std::set mrset; LocalMutex iteratorGuard; public: MRContainer() { CheckSuccess(CreateLocalMutex(&iteratorGuard)); } const MRPair* getMRPair(Address start) { WaitForLocalMutex(iteratorGuard); auto pairIterator = mrset.find(MRPair(start)); const MRPair* result = ((pairIterator == mrset.end()) ? NULL : &(*pairIterator)); ReleaseLocalMutex(iteratorGuard); return result; } Error deleteMRPair(const MRPair* pair) { int erased = 0; WaitForLocalMutex(iteratorGuard); erased = mrset.erase(*pair); ReleaseLocalMutex(iteratorGuard); if(erased == 1) return Success; else return ArgumentError; /* An exception could be thrown in this case */ } Error insertMRPair(MRPair* pair) { WaitForLocalMutex(iteratorGuard); auto inserted = mrset.insert(*pair); ReleaseLocalMutex(iteratorGuard); if(inserted.second == true) return Success; else return Failure; /* An exception could be thrown in this case */ } ~MRContainer() { CheckSuccess(CloseLocalMutex(iteratorGuard)); } }; static MRContainer memoryRegionsContainer; Error setAttributes(MemoryRegion mr, bool writable, bool executable) { Value attributes = MEMORY_READ; if(writable) attributes |= MEMORY_WRITE; if(executable) attributes |= MEMORY_EXEC; return SetMemoryRegionAttributes(mr, attributes); } void OSAllocator::setMemoryAttributes(void* addr, size_t size, bool writable, bool executable) { Address addressIterator = Address(addr); for(int i=0; i<(size + ASP_PAGESIZE -1)/ASP_PAGESIZE; i++) { const MRPair* pair = memoryRegionsContainer.getMRPair(addressIterator); CheckSuccess(setAttributes(pair->vmr, writable, executable)); addressIterator += ASP_PAGESIZE; } } void* OSAllocator::reserveUncommitted(size_t bytes, Usage usage, bool writable, bool executable) { MemoryRegion VMR; Address virtualStart, length; CheckSuccess(AllocateAnyMemoryRegion(__ghs_VirtualMemoryRegionPool, bytes, &VMR)); CheckSuccess(GetMemoryRegionAddresses(VMR, &virtualStart, &length)); Address addressIterator = virtualStart; for(int i=0; i<(bytes + ASP_PAGESIZE -1)/ASP_PAGESIZE; i++) { MRPair pair; pair.start = addressIterator; CheckSuccess(SplitMemoryRegion(VMR, ASP_PAGESIZE, &pair.vmr)); CheckSuccess(setAttributes(pair.vmr, writable, executable)); memoryRegionsContainer.insertMRPair(&pair); addressIterator += ASP_PAGESIZE; } CheckSuccess(CloseMemoryRegion(VMR)); return (void*)virtualStart; } void* OSAllocator::reserveAndCommit(size_t bytes, Usage usage, bool writable, bool executable, bool includesGuardPages) { MemoryRegion VMR; Address virtualStart, length; CheckSuccess(AllocateAnyMemoryRegion(__ghs_VirtualMemoryRegionPool, bytes, &VMR)); CheckSuccess(GetMemoryRegionAddresses(VMR, &virtualStart, &length)); Address addressIterator = virtualStart; for(int i=0; i<(bytes + ASP_PAGESIZE -1)/ASP_PAGESIZE; i++) { MRPair pair; pair.start = addressIterator; CheckSuccess(SplitMemoryRegion(VMR, ASP_PAGESIZE, &pair.vmr)); CheckSuccess(setAttributes(pair.vmr, writable, executable)); /* Do not map the first and the last pages if guard pages are required */ if(!includesGuardPages || (i!=0 && i!= (bytes + ASP_PAGESIZE -1)/ASP_PAGESIZE -1)) { CheckSuccess(GetPageFromAddressSpaceFreeList(GetCurrentAddressSpace(), &pair.pmr)); CheckSuccess(MapMemoryRegion(pair.vmr, pair.pmr)); pair.mapped = true; } memoryRegionsContainer.insertMRPair(&pair); addressIterator += ASP_PAGESIZE; } CheckSuccess(CloseMemoryRegion(VMR)); return (void*)virtualStart; } void OSAllocator::commit(void* address, size_t bytes, bool writable, bool executable) { for(int i=0; i<(bytes + ASP_PAGESIZE -1)/ASP_PAGESIZE; i++) { const MRPair* pair = memoryRegionsContainer.getMRPair((Address)address); if(pair == NULL) return; CheckSuccess(setAttributes(pair->vmr, writable, executable)); CheckSuccess(GetPageFromAddressSpaceFreeList(GetCurrentAddressSpace(), &pair->pmr)); CheckSuccess(MapMemoryRegion(pair->vmr, pair->pmr)); pair->mapped = true; address = (char*)address + ASP_PAGESIZE; } } void OSAllocator::decommit(void* address, size_t bytes) { for(int i=0; i<(bytes + ASP_PAGESIZE -1)/ASP_PAGESIZE; i++) { const MRPair* pair = memoryRegionsContainer.getMRPair((Address)address); if(pair == NULL) return; if(pair->mapped == false) continue; CheckSuccess(UnmapMemoryRegion(pair->vmr)); CheckSuccess(PutPageOnAddressSpaceFreeList(GetCurrentAddressSpace(), pair->pmr)); pair->mapped = false; address = (char*)address + ASP_PAGESIZE; } } void OSAllocator::releaseDecommitted(void* address, size_t bytes) { for(int i=0; i<(bytes + ASP_PAGESIZE -1)/ASP_PAGESIZE; i++) { const MRPair* pair = memoryRegionsContainer.getMRPair((Address)address); if(pair == NULL) return; /* Check if the memory is still committed */ if(pair->mapped == true) { CheckSuccess(UnmapMemoryRegion(pair->vmr)); CheckSuccess(PutPageOnAddressSpaceFreeList(GetCurrentAddressSpace(), pair->pmr)); pair->mapped = false; } CheckSuccess(AddToMemoryPool(__ghs_VirtualMemoryRegionPool, pair->vmr)); address = (char*)address + ASP_PAGESIZE; memoryRegionsContainer.deleteMRPair(pair); } } bool OSAllocator::canAllocateExecutableMemory() { return true; } } // namespace WTF