1 | /* |
2 | * Copyright (C) 2008 Apple Inc. All rights reserved. |
3 | * |
4 | * Redistribution and use in source and binary forms, with or without |
5 | * modification, are permitted provided that the following conditions |
6 | * are met: |
7 | * 1. Redistributions of source code must retain the above copyright |
8 | * notice, this list of conditions and the following disclaimer. |
9 | * 2. Redistributions in binary form must reproduce the above copyright |
10 | * notice, this list of conditions and the following disclaimer in the |
11 | * documentation and/or other materials provided with the distribution. |
12 | * |
13 | * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY |
14 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
16 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR |
17 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
18 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
19 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
20 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
21 | * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
23 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
24 | */ |
25 | |
26 | #ifndef ExecutableAllocator_h |
27 | #define ExecutableAllocator_h |
28 | |
29 | #include <stddef.h> // for ptrdiff_t |
30 | #include <limits> |
31 | #include <wtf/Assertions.h> |
32 | #include <wtf/PassRefPtr.h> |
33 | #include <wtf/RefCounted.h> |
34 | #include <wtf/UnusedParam.h> |
35 | #include <wtf/Vector.h> |
36 | |
37 | #if OS(IPHONE_OS) |
38 | #include <libkern/OSCacheControl.h> |
39 | #include <sys/mman.h> |
40 | #endif |
41 | |
42 | #if OS(SYMBIAN) |
43 | #include <e32std.h> |
44 | #endif |
45 | |
46 | #if OS(WINCE) |
47 | // From pkfuncs.h (private header file from the Platform Builder) |
48 | #define CACHE_SYNC_ALL 0x07F |
49 | extern "C" __declspec(dllimport) void CacheRangeFlush(LPVOID pAddr, DWORD dwLength, DWORD dwFlags); |
50 | #endif |
51 | |
52 | #define JIT_ALLOCATOR_PAGE_SIZE (ExecutableAllocator::pageSize) |
53 | #define JIT_ALLOCATOR_LARGE_ALLOC_SIZE (ExecutableAllocator::pageSize * 4) |
54 | |
55 | #if ENABLE(ASSEMBLER_WX_EXCLUSIVE) |
56 | #define PROTECTION_FLAGS_RW (PROT_READ | PROT_WRITE) |
57 | #define PROTECTION_FLAGS_RX (PROT_READ | PROT_EXEC) |
58 | #define INITIAL_PROTECTION_FLAGS PROTECTION_FLAGS_RX |
59 | #else |
60 | #define INITIAL_PROTECTION_FLAGS (PROT_READ | PROT_WRITE | PROT_EXEC) |
61 | #endif |
62 | |
63 | namespace JSC { |
64 | |
65 | inline size_t roundUpAllocationSize(size_t request, size_t granularity) |
66 | { |
67 | if ((std::numeric_limits<size_t>::max() - granularity) <= request) |
68 | CRASH(); // Allocation is too large |
69 | |
70 | // Round up to next page boundary |
71 | size_t size = request + (granularity - 1); |
72 | size = size & ~(granularity - 1); |
73 | ASSERT(size >= request); |
74 | return size; |
75 | } |
76 | |
77 | } |
78 | |
79 | #if ENABLE(ASSEMBLER) |
80 | |
81 | namespace JSC { |
82 | |
83 | class ExecutablePool : public RefCounted<ExecutablePool> { |
84 | private: |
85 | struct Allocation { |
86 | char* pages; |
87 | size_t size; |
88 | #if OS(SYMBIAN) |
89 | RChunk* chunk; |
90 | #endif |
91 | }; |
92 | typedef Vector<Allocation, 2> AllocationList; |
93 | |
94 | public: |
95 | static PassRefPtr<ExecutablePool> create(size_t n) |
96 | { |
97 | return adoptRef(p: new ExecutablePool(n)); |
98 | } |
99 | |
100 | void* alloc(size_t n) |
101 | { |
102 | ASSERT(m_freePtr <= m_end); |
103 | |
104 | // Round 'n' up to a multiple of word size; if all allocations are of |
105 | // word sized quantities, then all subsequent allocations will be aligned. |
106 | n = roundUpAllocationSize(request: n, granularity: sizeof(void*)); |
107 | |
108 | if (static_cast<ptrdiff_t>(n) < (m_end - m_freePtr)) { |
109 | void* result = m_freePtr; |
110 | m_freePtr += n; |
111 | return result; |
112 | } |
113 | |
114 | // Insufficient space to allocate in the existing pool |
115 | // so we need allocate into a new pool |
116 | return poolAllocate(n); |
117 | } |
118 | |
119 | ~ExecutablePool() |
120 | { |
121 | AllocationList::const_iterator end = m_pools.end(); |
122 | for (AllocationList::const_iterator ptr = m_pools.begin(); ptr != end; ++ptr) |
123 | ExecutablePool::systemRelease(alloc: *ptr); |
124 | } |
125 | |
126 | size_t available() const { return (m_pools.size() > 1) ? 0 : m_end - m_freePtr; } |
127 | |
128 | private: |
129 | static Allocation systemAlloc(size_t n); |
130 | static void systemRelease(const Allocation& alloc); |
131 | |
132 | ExecutablePool(size_t n); |
133 | |
134 | void* poolAllocate(size_t n); |
135 | |
136 | char* m_freePtr; |
137 | char* m_end; |
138 | AllocationList m_pools; |
139 | }; |
140 | |
141 | class ExecutableAllocator { |
142 | enum ProtectionSeting { Writable, Executable }; |
143 | |
144 | public: |
145 | static size_t pageSize; |
146 | ExecutableAllocator() |
147 | { |
148 | if (!pageSize) |
149 | intializePageSize(); |
150 | m_smallAllocationPool = ExecutablePool::create(JIT_ALLOCATOR_LARGE_ALLOC_SIZE); |
151 | } |
152 | |
153 | PassRefPtr<ExecutablePool> poolForSize(size_t n) |
154 | { |
155 | // Try to fit in the existing small allocator |
156 | if (n < m_smallAllocationPool->available()) |
157 | return m_smallAllocationPool; |
158 | |
159 | // If the request is large, we just provide a unshared allocator |
160 | if (n > JIT_ALLOCATOR_LARGE_ALLOC_SIZE) |
161 | return ExecutablePool::create(n); |
162 | |
163 | // Create a new allocator |
164 | RefPtr<ExecutablePool> pool = ExecutablePool::create(JIT_ALLOCATOR_LARGE_ALLOC_SIZE); |
165 | |
166 | // If the new allocator will result in more free space than in |
167 | // the current small allocator, then we will use it instead |
168 | if ((pool->available() - n) > m_smallAllocationPool->available()) |
169 | m_smallAllocationPool = pool; |
170 | return pool.release(); |
171 | } |
172 | |
173 | #if ENABLE(ASSEMBLER_WX_EXCLUSIVE) |
174 | static void makeWritable(void* start, size_t size) |
175 | { |
176 | reprotectRegion(start, size, Writable); |
177 | } |
178 | |
179 | static void makeExecutable(void* start, size_t size) |
180 | { |
181 | reprotectRegion(start, size, Executable); |
182 | } |
183 | #else |
184 | static void makeWritable(void*, size_t) {} |
185 | static void makeExecutable(void*, size_t) {} |
186 | #endif |
187 | |
188 | |
189 | #if CPU(X86) || CPU(X86_64) |
190 | static void cacheFlush(void*, size_t) |
191 | { |
192 | } |
193 | #elif CPU(ARM_THUMB2) && OS(IPHONE_OS) |
194 | static void cacheFlush(void* code, size_t size) |
195 | { |
196 | sys_dcache_flush(code, size); |
197 | sys_icache_invalidate(code, size); |
198 | } |
199 | #elif CPU(ARM_THUMB2) && OS(LINUX) |
200 | static void cacheFlush(void* code, size_t size) |
201 | { |
202 | asm volatile ( |
203 | "push {r7}\n" |
204 | "mov r0, %0\n" |
205 | "mov r1, %1\n" |
206 | "movw r7, #0x2\n" |
207 | "movt r7, #0xf\n" |
208 | "movs r2, #0x0\n" |
209 | "svc 0x0\n" |
210 | "pop {r7}\n" |
211 | : |
212 | : "r" (code), "r" (reinterpret_cast<char*>(code) + size) |
213 | : "r0" , "r1" , "r2" ); |
214 | } |
215 | #elif OS(SYMBIAN) |
216 | static void cacheFlush(void* code, size_t size) |
217 | { |
218 | User::IMB_Range(code, static_cast<char*>(code) + size); |
219 | } |
220 | #elif CPU(ARM_TRADITIONAL) && OS(LINUX) |
221 | static void cacheFlush(void* code, size_t size) |
222 | { |
223 | asm volatile ( |
224 | "push {r7}\n" |
225 | "mov r0, %0\n" |
226 | "mov r1, %1\n" |
227 | "mov r7, #0xf0000\n" |
228 | "add r7, r7, #0x2\n" |
229 | "mov r2, #0x0\n" |
230 | "svc 0x0\n" |
231 | "pop {r7}\n" |
232 | : |
233 | : "r" (code), "r" (reinterpret_cast<char*>(code) + size) |
234 | : "r0" , "r1" , "r2" ); |
235 | } |
236 | #elif OS(WINCE) |
237 | static void cacheFlush(void* code, size_t size) |
238 | { |
239 | CacheRangeFlush(code, size, CACHE_SYNC_ALL); |
240 | } |
241 | #else |
242 | #error "The cacheFlush support is missing on this platform." |
243 | #endif |
244 | |
245 | private: |
246 | |
247 | #if ENABLE(ASSEMBLER_WX_EXCLUSIVE) |
248 | static void reprotectRegion(void*, size_t, ProtectionSeting); |
249 | #endif |
250 | |
251 | RefPtr<ExecutablePool> m_smallAllocationPool; |
252 | static void intializePageSize(); |
253 | }; |
254 | |
255 | inline ExecutablePool::ExecutablePool(size_t n) |
256 | { |
257 | size_t allocSize = roundUpAllocationSize(request: n, JIT_ALLOCATOR_PAGE_SIZE); |
258 | Allocation mem = systemAlloc(n: allocSize); |
259 | m_pools.append(val: mem); |
260 | m_freePtr = mem.pages; |
261 | if (!m_freePtr) |
262 | CRASH(); // Failed to allocate |
263 | m_end = m_freePtr + allocSize; |
264 | } |
265 | |
266 | inline void* ExecutablePool::poolAllocate(size_t n) |
267 | { |
268 | size_t allocSize = roundUpAllocationSize(request: n, JIT_ALLOCATOR_PAGE_SIZE); |
269 | |
270 | Allocation result = systemAlloc(n: allocSize); |
271 | if (!result.pages) |
272 | CRASH(); // Failed to allocate |
273 | |
274 | ASSERT(m_end >= m_freePtr); |
275 | if ((allocSize - n) > static_cast<size_t>(m_end - m_freePtr)) { |
276 | // Replace allocation pool |
277 | m_freePtr = result.pages + n; |
278 | m_end = result.pages + allocSize; |
279 | } |
280 | |
281 | m_pools.append(val: result); |
282 | return result.pages; |
283 | } |
284 | |
285 | } |
286 | |
287 | #endif // ENABLE(ASSEMBLER) |
288 | |
289 | #endif // !defined(ExecutableAllocator) |
290 | |