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 :
32 : #include "jsalloc.h"
33 : #include "jsapi.h"
34 : #include "jsprvtd.h"
35 :
36 : #include "assembler/wtf/Assertions.h"
37 : #include "js/HashTable.h"
38 : #include "js/Vector.h"
39 :
40 : #if WTF_CPU_SPARC
41 : #ifdef linux // bugzilla 502369
42 : static void sync_instruction_memory(caddr_t v, u_int len)
43 : {
44 : caddr_t end = v + len;
45 : caddr_t p = v;
46 : while (p < end) {
47 : asm("flush %0" : : "r" (p));
48 : p += 32;
49 : }
50 : }
51 : #else
52 : extern "C" void sync_instruction_memory(caddr_t v, u_int len);
53 : #endif
54 : #endif
55 :
56 : #if WTF_OS_IOS
57 : #include <libkern/OSCacheControl.h>
58 : #include <sys/mman.h>
59 : #endif
60 :
61 : #if WTF_OS_SYMBIAN
62 : #include <e32std.h>
63 : #endif
64 :
65 : #if WTF_CPU_MIPS && WTF_OS_LINUX
66 : #include <sys/cachectl.h>
67 : #endif
68 :
69 : #if ENABLE_ASSEMBLER_WX_EXCLUSIVE
70 : #define PROTECTION_FLAGS_RW (PROT_READ | PROT_WRITE)
71 : #define PROTECTION_FLAGS_RX (PROT_READ | PROT_EXEC)
72 : #define INITIAL_PROTECTION_FLAGS PROTECTION_FLAGS_RX
73 : #else
74 : #define INITIAL_PROTECTION_FLAGS (PROT_READ | PROT_WRITE | PROT_EXEC)
75 : #endif
76 :
77 : #if ENABLE_ASSEMBLER
78 :
79 : //#define DEBUG_STRESS_JSC_ALLOCATOR
80 :
81 : namespace JSC {
82 :
83 : class ExecutableAllocator;
84 :
85 : enum CodeKind { METHOD_CODE, REGEXP_CODE };
86 :
87 : // These are reference-counted. A new one starts with a count of 1.
88 : class ExecutablePool {
89 :
90 : JS_DECLARE_ALLOCATION_FRIENDS_FOR_PRIVATE_CONSTRUCTOR;
91 : friend class ExecutableAllocator;
92 : private:
93 : struct Allocation {
94 : char* pages;
95 : size_t size;
96 : #if WTF_OS_SYMBIAN
97 : RChunk* chunk;
98 : #endif
99 : };
100 :
101 : ExecutableAllocator* m_allocator;
102 : char* m_freePtr;
103 : char* m_end;
104 : Allocation m_allocation;
105 :
106 : // Reference count for automatic reclamation.
107 : unsigned m_refCount;
108 :
109 : // Number of bytes currently used for Method and Regexp JIT code.
110 : size_t m_mjitCodeMethod;
111 : size_t m_mjitCodeRegexp;
112 :
113 : public:
114 : // Flag for downstream use, whether to try to release references to this pool.
115 : bool m_destroy;
116 :
117 : // GC number in which the m_destroy flag was most recently set. Used downstream to
118 : // remember whether m_destroy was computed for the currently active GC.
119 : size_t m_gcNumber;
120 :
121 521182 : void release(bool willDestroy = false)
122 : {
123 521182 : JS_ASSERT(m_refCount != 0);
124 : // XXX: disabled, see bug 654820.
125 : //JS_ASSERT_IF(willDestroy, m_refCount == 1);
126 521182 : if (--m_refCount == 0) {
127 19341 : js::UnwantedForeground::delete_(this);
128 : }
129 521182 : }
130 :
131 : private:
132 : // It should be impossible for us to roll over, because only small
133 : // pools have multiple holders, and they have one holder per chunk
134 : // of generated code, and they only hold 16KB or so of code.
135 501841 : void addRef()
136 : {
137 501841 : JS_ASSERT(m_refCount);
138 501841 : ++m_refCount;
139 501841 : }
140 :
141 19342 : ExecutablePool(ExecutableAllocator* allocator, Allocation a)
142 19342 : : m_allocator(allocator), m_freePtr(a.pages), m_end(m_freePtr + a.size), m_allocation(a),
143 38684 : m_refCount(1), m_mjitCodeMethod(0), m_mjitCodeRegexp(0), m_destroy(false), m_gcNumber(0)
144 19342 : { }
145 :
146 : ~ExecutablePool();
147 :
148 501980 : void* alloc(size_t n, CodeKind kind)
149 : {
150 501980 : JS_ASSERT(n <= available());
151 501980 : void *result = m_freePtr;
152 501980 : m_freePtr += n;
153 :
154 501980 : if ( kind == REGEXP_CODE )
155 55269 : m_mjitCodeRegexp += n;
156 : else
157 446711 : m_mjitCodeMethod += n;
158 :
159 501980 : return result;
160 : }
161 :
162 2201728 : size_t available() const {
163 2201728 : JS_ASSERT(m_end >= m_freePtr);
164 2201728 : return m_end - m_freePtr;
165 : }
166 : };
167 :
168 : enum AllocationBehavior
169 : {
170 : AllocationCanRandomize,
171 : AllocationDeterministic
172 : };
173 :
174 : class ExecutableAllocator {
175 : typedef void (*DestroyCallback)(void* addr, size_t size);
176 : enum ProtectionSetting { Writable, Executable };
177 : DestroyCallback destroyCallback;
178 :
179 : void initSeed();
180 :
181 : public:
182 14217 : explicit ExecutableAllocator(AllocationBehavior allocBehavior)
183 : : destroyCallback(NULL),
184 14217 : allocBehavior(allocBehavior)
185 : {
186 14217 : if (!pageSize) {
187 11298 : pageSize = determinePageSize();
188 : /*
189 : * On Windows, VirtualAlloc effectively allocates in 64K chunks.
190 : * (Technically, it allocates in page chunks, but the starting
191 : * address is always a multiple of 64K, so each allocation uses up
192 : * 64K of address space.) So a size less than that would be
193 : * pointless. But it turns out that 64KB is a reasonable size for
194 : * all platforms. (This assumes 4KB pages.)
195 : */
196 11298 : largeAllocSize = pageSize * 16;
197 : }
198 :
199 : #if WTF_OS_WINDOWS
200 : initSeed();
201 : #endif
202 :
203 14217 : JS_ASSERT(m_smallPools.empty());
204 14217 : }
205 :
206 14216 : ~ExecutableAllocator()
207 14216 : {
208 29690 : for (size_t i = 0; i < m_smallPools.length(); i++)
209 15474 : m_smallPools[i]->release(/* willDestroy = */true);
210 : // XXX: temporarily disabled because it fails; see bug 654820.
211 : //JS_ASSERT(m_pools.empty()); // if this asserts we have a pool leak
212 14216 : }
213 :
214 : // alloc() returns a pointer to some memory, and also (by reference) a
215 : // pointer to reference-counted pool. The caller owns a reference to the
216 : // pool; i.e. alloc() increments the count before returning the object.
217 501980 : void* alloc(size_t n, ExecutablePool** poolp, CodeKind type)
218 : {
219 : // Round 'n' up to a multiple of word size; if all allocations are of
220 : // word sized quantities, then all subsequent allocations will be
221 : // aligned.
222 501980 : n = roundUpAllocationSize(n, sizeof(void*));
223 501980 : if (n == OVERSIZE_ALLOCATION) {
224 0 : *poolp = NULL;
225 0 : return NULL;
226 : }
227 :
228 501980 : *poolp = poolForSize(n);
229 501980 : if (!*poolp)
230 0 : return NULL;
231 :
232 : // This alloc is infallible because poolForSize() just obtained
233 : // (found, or created if necessary) a pool that had enough space.
234 501980 : void *result = (*poolp)->alloc(n, type);
235 501980 : JS_ASSERT(result);
236 501980 : return result;
237 : }
238 :
239 19341 : void releasePoolPages(ExecutablePool *pool) {
240 19341 : JS_ASSERT(pool->m_allocation.pages);
241 19341 : if (destroyCallback)
242 9875 : destroyCallback(pool->m_allocation.pages, pool->m_allocation.size);
243 19341 : systemRelease(pool->m_allocation);
244 19341 : m_pools.remove(m_pools.lookup(pool)); // this asserts if |pool| is not in m_pools
245 19341 : }
246 :
247 : void sizeOfCode(size_t *method, size_t *regexp, size_t *unused) const;
248 :
249 306058 : void setDestroyCallback(DestroyCallback destroyCallback) {
250 306058 : this->destroyCallback = destroyCallback;
251 306058 : }
252 :
253 1763702 : void setRandomize(bool enabled) {
254 1763702 : allocBehavior = enabled ? AllocationCanRandomize : AllocationDeterministic;
255 1763702 : }
256 :
257 : private:
258 : static size_t pageSize;
259 : static size_t largeAllocSize;
260 : #if WTF_OS_WINDOWS
261 : static int64_t rngSeed;
262 : #endif
263 :
264 : static const size_t OVERSIZE_ALLOCATION = size_t(-1);
265 :
266 521322 : static size_t roundUpAllocationSize(size_t request, size_t granularity)
267 : {
268 : // Something included via windows.h defines a macro with this name,
269 : // which causes the function below to fail to compile.
270 : #ifdef _MSC_VER
271 : # undef max
272 : #endif
273 :
274 521322 : if ((std::numeric_limits<size_t>::max() - granularity) <= request)
275 0 : return OVERSIZE_ALLOCATION;
276 :
277 : // Round up to next page boundary
278 521322 : size_t size = request + (granularity - 1);
279 521322 : size = size & ~(granularity - 1);
280 521322 : JS_ASSERT(size >= request);
281 521322 : return size;
282 : }
283 :
284 : // On OOM, this will return an Allocation where pages is NULL.
285 : ExecutablePool::Allocation systemAlloc(size_t n);
286 : static void systemRelease(const ExecutablePool::Allocation& alloc);
287 : void *computeRandomAllocationAddress();
288 :
289 19342 : ExecutablePool* createPool(size_t n)
290 : {
291 19342 : size_t allocSize = roundUpAllocationSize(n, pageSize);
292 19342 : if (allocSize == OVERSIZE_ALLOCATION)
293 0 : return NULL;
294 :
295 19342 : if (!m_pools.initialized() && !m_pools.init())
296 0 : return NULL;
297 :
298 : #ifdef DEBUG_STRESS_JSC_ALLOCATOR
299 : ExecutablePool::Allocation a = systemAlloc(size_t(4294967291));
300 : #else
301 19342 : ExecutablePool::Allocation a = systemAlloc(allocSize);
302 : #endif
303 19342 : if (!a.pages)
304 0 : return NULL;
305 :
306 19342 : ExecutablePool *pool = js::OffTheBooks::new_<ExecutablePool>(this, a);
307 19342 : if (!pool) {
308 0 : systemRelease(a);
309 0 : return NULL;
310 : }
311 19342 : m_pools.put(pool);
312 19342 : return pool;
313 : }
314 :
315 : public:
316 501980 : ExecutablePool* poolForSize(size_t n)
317 : {
318 : #ifndef DEBUG_STRESS_JSC_ALLOCATOR
319 : // Try to fit in an existing small allocator. Use the pool with the
320 : // least available space that is big enough (best-fit). This is the
321 : // best strategy because (a) it maximizes the chance of the next
322 : // allocation fitting in a small pool, and (b) it minimizes the
323 : // potential waste when a small pool is next abandoned.
324 501980 : ExecutablePool *minPool = NULL;
325 1831544 : for (size_t i = 0; i < m_smallPools.length(); i++) {
326 1329564 : ExecutablePool *pool = m_smallPools[i];
327 1329564 : if (n <= pool->available() && (!minPool || pool->available() < minPool->available()))
328 547023 : minPool = pool;
329 : }
330 501980 : if (minPool) {
331 482638 : minPool->addRef();
332 482638 : return minPool;
333 : }
334 : #endif
335 :
336 : // If the request is large, we just provide a unshared allocator
337 19342 : if (n > largeAllocSize)
338 131 : return createPool(n);
339 :
340 : // Create a new allocator
341 19211 : ExecutablePool* pool = createPool(largeAllocSize);
342 19211 : if (!pool)
343 0 : return NULL;
344 : // At this point, local |pool| is the owner.
345 :
346 19211 : if (m_smallPools.length() < maxSmallPools) {
347 : // We haven't hit the maximum number of live pools; add the new pool.
348 15475 : m_smallPools.append(pool);
349 15475 : pool->addRef();
350 : } else {
351 : // Find the pool with the least space.
352 3736 : int iMin = 0;
353 14944 : for (size_t i = 1; i < m_smallPools.length(); i++)
354 22416 : if (m_smallPools[i]->available() <
355 11208 : m_smallPools[iMin]->available())
356 : {
357 3373 : iMin = i;
358 : }
359 :
360 : // If the new allocator will result in more free space than the small
361 : // pool with the least space, then we will use it instead
362 3736 : ExecutablePool *minPool = m_smallPools[iMin];
363 3736 : if ((pool->available() - n) > minPool->available()) {
364 3728 : minPool->release();
365 3728 : m_smallPools[iMin] = pool;
366 3728 : pool->addRef();
367 : }
368 : }
369 :
370 : // Pass ownership to the caller.
371 19211 : return pool;
372 : }
373 :
374 : #if ENABLE_ASSEMBLER_WX_EXCLUSIVE
375 : static void makeWritable(void* start, size_t size)
376 : {
377 : reprotectRegion(start, size, Writable);
378 : }
379 :
380 : static void makeExecutable(void* start, size_t size)
381 : {
382 : reprotectRegion(start, size, Executable);
383 : }
384 : #else
385 1230590 : static void makeWritable(void*, size_t) {}
386 1230590 : static void makeExecutable(void*, size_t) {}
387 : #endif
388 :
389 :
390 : #if WTF_CPU_X86 || WTF_CPU_X86_64
391 501980 : static void cacheFlush(void*, size_t)
392 : {
393 501980 : }
394 : #elif WTF_CPU_MIPS
395 : static void cacheFlush(void* code, size_t size)
396 : {
397 : #if WTF_COMPILER_GCC && (GCC_VERSION >= 40300)
398 : #if WTF_MIPS_ISA_REV(2) && (GCC_VERSION < 40403)
399 : int lineSize;
400 : asm("rdhwr %0, $1" : "=r" (lineSize));
401 : //
402 : // Modify "start" and "end" to avoid GCC 4.3.0-4.4.2 bug in
403 : // mips_expand_synci_loop that may execute synci one more time.
404 : // "start" points to the first byte of the cache line.
405 : // "end" points to the last byte of the line before the last cache line.
406 : // Because size is always a multiple of 4, this is safe to set
407 : // "end" to the last byte.
408 : //
409 : intptr_t start = reinterpret_cast<intptr_t>(code) & (-lineSize);
410 : intptr_t end = ((reinterpret_cast<intptr_t>(code) + size - 1) & (-lineSize)) - 1;
411 : __builtin___clear_cache(reinterpret_cast<char*>(start), reinterpret_cast<char*>(end));
412 : #else
413 : intptr_t end = reinterpret_cast<intptr_t>(code) + size;
414 : __builtin___clear_cache(reinterpret_cast<char*>(code), reinterpret_cast<char*>(end));
415 : #endif
416 : #else
417 : _flush_cache(reinterpret_cast<char*>(code), size, BCACHE);
418 : #endif
419 : }
420 : #elif WTF_CPU_ARM && WTF_OS_IOS
421 : static void cacheFlush(void* code, size_t size)
422 : {
423 : sys_dcache_flush(code, size);
424 : sys_icache_invalidate(code, size);
425 : }
426 : #elif WTF_CPU_ARM_THUMB2 && WTF_IOS
427 : static void cacheFlush(void* code, size_t size)
428 : {
429 : asm volatile (
430 : "push {r7}\n"
431 : "mov r0, %0\n"
432 : "mov r1, %1\n"
433 : "movw r7, #0x2\n"
434 : "movt r7, #0xf\n"
435 : "movs r2, #0x0\n"
436 : "svc 0x0\n"
437 : "pop {r7}\n"
438 : :
439 : : "r" (code), "r" (reinterpret_cast<char*>(code) + size)
440 : : "r0", "r1", "r2");
441 : }
442 : #elif WTF_OS_SYMBIAN
443 : static void cacheFlush(void* code, size_t size)
444 : {
445 : User::IMB_Range(code, static_cast<char*>(code) + size);
446 : }
447 : #elif WTF_CPU_ARM_TRADITIONAL && WTF_OS_LINUX && WTF_COMPILER_RVCT
448 : static __asm void cacheFlush(void* code, size_t size);
449 : #elif WTF_CPU_ARM_TRADITIONAL && (WTF_OS_LINUX || WTF_OS_ANDROID) && WTF_COMPILER_GCC
450 : static void cacheFlush(void* code, size_t size)
451 : {
452 : asm volatile (
453 : "push {r7}\n"
454 : "mov r0, %0\n"
455 : "mov r1, %1\n"
456 : "mov r7, #0xf0000\n"
457 : "add r7, r7, #0x2\n"
458 : "mov r2, #0x0\n"
459 : "svc 0x0\n"
460 : "pop {r7}\n"
461 : :
462 : : "r" (code), "r" (reinterpret_cast<char*>(code) + size)
463 : : "r0", "r1", "r2");
464 : }
465 : #elif WTF_CPU_SPARC
466 : static void cacheFlush(void* code, size_t size)
467 : {
468 : sync_instruction_memory((caddr_t)code, size);
469 : }
470 : #else
471 : #error "The cacheFlush support is missing on this platform."
472 : #endif
473 :
474 : private:
475 :
476 : #if ENABLE_ASSEMBLER_WX_EXCLUSIVE
477 : static void reprotectRegion(void*, size_t, ProtectionSetting);
478 : #endif
479 :
480 : // These are strong references; they keep pools alive.
481 : static const size_t maxSmallPools = 4;
482 : typedef js::Vector<ExecutablePool *, maxSmallPools, js::SystemAllocPolicy> SmallExecPoolVector;
483 : SmallExecPoolVector m_smallPools;
484 :
485 : // All live pools are recorded here, just for stats purposes. These are
486 : // weak references; they don't keep pools alive. When a pool is destroyed
487 : // its reference is removed from m_pools.
488 : typedef js::HashSet<ExecutablePool *, js::DefaultHasher<ExecutablePool *>, js::SystemAllocPolicy>
489 : ExecPoolHashSet;
490 : ExecPoolHashSet m_pools; // All pools, just for stats purposes.
491 : AllocationBehavior allocBehavior;
492 :
493 : static size_t determinePageSize();
494 : };
495 :
496 : }
497 :
498 : #endif // ENABLE(ASSEMBLER)
499 :
500 : #endif // !defined(ExecutableAllocator)
|