• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
roundUpAllocationSize(size_t request,size_t granularity)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:
create(size_t n)95     static PassRefPtr<ExecutablePool> create(size_t n)
96     {
97         return adoptRef(new ExecutablePool(n));
98     }
99 
alloc(size_t n)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(n, 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 
~ExecutablePool()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(*ptr);
124     }
125 
available()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;
ExecutableAllocator()146     ExecutableAllocator()
147     {
148         if (!pageSize)
149             intializePageSize();
150         m_smallAllocationPool = ExecutablePool::create(JIT_ALLOCATOR_LARGE_ALLOC_SIZE);
151     }
152 
poolForSize(size_t n)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)
makeWritable(void * start,size_t size)174     static void makeWritable(void* start, size_t size)
175     {
176         reprotectRegion(start, size, Writable);
177     }
178 
makeExecutable(void * start,size_t size)179     static void makeExecutable(void* start, size_t size)
180     {
181         reprotectRegion(start, size, Executable);
182     }
183 #else
makeWritable(void *,size_t)184     static void makeWritable(void*, size_t) {}
makeExecutable(void *,size_t)185     static void makeExecutable(void*, size_t) {}
186 #endif
187 
188 
189 #if CPU(X86) || CPU(X86_64)
cacheFlush(void *,size_t)190     static void cacheFlush(void*, size_t)
191     {
192     }
193 #elif CPU(ARM_THUMB2) && OS(IPHONE_OS)
cacheFlush(void * code,size_t size)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)
cacheFlush(void * code,size_t size)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)
cacheFlush(void * code,size_t size)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)
cacheFlush(void * code,size_t size)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)
cacheFlush(void * code,size_t size)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 
ExecutablePool(size_t n)255 inline ExecutablePool::ExecutablePool(size_t n)
256 {
257     size_t allocSize = roundUpAllocationSize(n, JIT_ALLOCATOR_PAGE_SIZE);
258     Allocation mem = systemAlloc(allocSize);
259     m_pools.append(mem);
260     m_freePtr = mem.pages;
261     if (!m_freePtr)
262         CRASH(); // Failed to allocate
263     m_end = m_freePtr + allocSize;
264 }
265 
poolAllocate(size_t n)266 inline void* ExecutablePool::poolAllocate(size_t n)
267 {
268     size_t allocSize = roundUpAllocationSize(n, JIT_ALLOCATOR_PAGE_SIZE);
269 
270     Allocation result = systemAlloc(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(result);
282     return result.pages;
283 }
284 
285 }
286 
287 #endif // ENABLE(ASSEMBLER)
288 
289 #endif // !defined(ExecutableAllocator)
290