Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qv4executableallocator.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant
4
6#include <QtQml/private/qv4functiontable_p.h>
7
8#include <wtf/StdLibExtras.h>
9#include <wtf/PageAllocation.h>
10
11using namespace QV4;
12
13void *ExecutableAllocator::Allocation::exceptionHandlerStart() const
14{
15 return reinterpret_cast<void*>(addr);
16}
17
18size_t ExecutableAllocator::Allocation::exceptionHandlerSize() const
19{
20 return QV4::exceptionHandlerSize();
21}
22
23void *ExecutableAllocator::Allocation::memoryStart() const
24{
25 return reinterpret_cast<void*>(addr);
26}
27
28void *ExecutableAllocator::Allocation::codeStart() const
29{
30 return reinterpret_cast<void*>(addr + exceptionHandlerSize());
31}
32
33void ExecutableAllocator::Allocation::deallocate(ExecutableAllocator *allocator)
34{
35 if (isValid())
36 allocator->free(this);
37 else
38 delete this;
39}
40
41ExecutableAllocator::Allocation *ExecutableAllocator::Allocation::split(size_t dividingSize)
42{
43 Allocation *remainder = new Allocation;
44 if (next)
45 next->prev = remainder;
46
47 remainder->next = next;
48 next = remainder;
49
50 remainder->prev = this;
51
52 remainder->size = size - dividingSize;
53 remainder->free = free;
54 remainder->addr = addr + dividingSize;
55 size = dividingSize;
56
57 return remainder;
58}
59
60bool ExecutableAllocator::Allocation::mergeNext(ExecutableAllocator *allocator)
61{
62 Q_ASSERT(free);
63 if (!next || !next->free)
64 return false;
65
66 allocator->freeAllocations.remove(size, this);
67 allocator->freeAllocations.remove(next->size, next);
68
69 size += next->size;
70 Allocation *newNext = next->next;
71 delete next;
72 next = newNext;
73 if (next)
74 next->prev = this;
75
76 allocator->freeAllocations.insert(size, this);
77 return true;
78}
79
80bool ExecutableAllocator::Allocation::mergePrevious(ExecutableAllocator *allocator)
81{
82 Q_ASSERT(free);
83 if (!prev || !prev->free)
84 return false;
85
86 allocator->freeAllocations.remove(size, this);
87 allocator->freeAllocations.remove(prev->size, prev);
88
89 prev->size += size;
90 if (next)
91 next->prev = prev;
92 prev->next = next;
93
94 allocator->freeAllocations.insert(prev->size, prev);
95
96 delete this;
97 return true;
98}
99
100ExecutableAllocator::ChunkOfPages::~ChunkOfPages()
101{
102 Allocation *alloc = firstAllocation;
103 while (alloc) {
104 Allocation *next = alloc->next;
105 if (alloc->isValid())
106 delete alloc;
107 alloc = next;
108 }
109 pages->deallocate();
110 delete pages;
111}
112
113bool ExecutableAllocator::ChunkOfPages::contains(Allocation *alloc) const
114{
115 Allocation *it = firstAllocation;
116 while (it) {
117 if (it == alloc)
118 return true;
119 it = it->next;
120 }
121 return false;
122}
123
124ExecutableAllocator::ExecutableAllocator()
125 = default;
126
127ExecutableAllocator::~ExecutableAllocator()
128{
129 for (ChunkOfPages *chunk : std::as_const(chunks)) {
130 for (Allocation *allocation = chunk->firstAllocation; allocation; allocation = allocation->next)
131 if (!allocation->free)
132 allocation->invalidate();
133 }
134
135 qDeleteAll(chunks);
136}
137
138ExecutableAllocator::Allocation *ExecutableAllocator::allocate(size_t size)
139{
140 QMutexLocker locker(&mutex);
141 Allocation *allocation = nullptr;
142
143 // Code is best aligned to 16-byte boundaries.
144 size = WTF::roundUpToMultipleOf(16, size + exceptionHandlerSize());
145
146 QMultiMap<size_t, Allocation*>::Iterator it = freeAllocations.lowerBound(size);
147 if (it != freeAllocations.end()) {
148 allocation = *it;
149 freeAllocations.erase(it);
150 }
151
152 if (!allocation) {
153 ChunkOfPages *chunk = new ChunkOfPages;
154 size_t allocSize = WTF::roundUpToMultipleOf(WTF::pageSize(), size);
155 chunk->pages = new WTF::PageAllocation(WTF::PageAllocation::allocate(allocSize, OSAllocator::JSJITCodePages));
156 chunks.insert(reinterpret_cast<quintptr>(chunk->pages->base()) - 1, chunk);
157 allocation = new Allocation;
158 allocation->addr = reinterpret_cast<quintptr>(chunk->pages->base());
159 allocation->size = allocSize;
160 allocation->free = true;
161 chunk->firstAllocation = allocation;
162 }
163
164 Q_ASSERT(allocation);
165 Q_ASSERT(allocation->free);
166
167 allocation->free = false;
168
169 if (allocation->size > size) {
170 Allocation *remainder = allocation->split(size);
171 remainder->free = true;
172 if (!remainder->mergeNext(this))
173 freeAllocations.insert(remainder->size, remainder);
174 }
175
176 return allocation;
177}
178
179void ExecutableAllocator::free(Allocation *allocation)
180{
181 QMutexLocker locker(&mutex);
182
183 Q_ASSERT(allocation);
184
185 allocation->free = true;
186
187 QMap<quintptr, ChunkOfPages*>::Iterator it = chunks.lowerBound(allocation->addr);
188 if (it != chunks.begin())
189 --it;
190 Q_ASSERT(it != chunks.end());
191 ChunkOfPages *chunk = *it;
192 Q_ASSERT(chunk->contains(allocation));
193
194 bool merged = allocation->mergeNext(this);
195 merged |= allocation->mergePrevious(this);
196 if (!merged)
197 freeAllocations.insert(allocation->size, allocation);
198
199 allocation = nullptr;
200
201 if (!chunk->firstAllocation->next) {
202 freeAllocations.remove(chunk->firstAllocation->size, chunk->firstAllocation);
203 chunks.erase(it);
204 delete chunk;
205 return;
206 }
207}
208
209ExecutableAllocator::ChunkOfPages *ExecutableAllocator::chunkForAllocation(Allocation *allocation) const
210{
211 QMutexLocker locker(&mutex);
212
213 QMap<quintptr, ChunkOfPages*>::ConstIterator it = chunks.lowerBound(allocation->addr);
214 if (it != chunks.begin())
215 --it;
216 if (it == chunks.end())
217 return nullptr;
218 return *it;
219}