blob: 0de4f791cfb07f64bcfe7a2368c34f534a8547f4 [file] [log] [blame]
oliver@apple.comffa27602008-12-07 23:55:04 +00001/*
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
barraclough@apple.com43c4af92009-06-09 01:40:59 +000029#include <limits>
oliver@apple.comffa27602008-12-07 23:55:04 +000030#include <wtf/Assertions.h>
31#include <wtf/PassRefPtr.h>
32#include <wtf/RefCounted.h>
barraclough@apple.com43c4af92009-06-09 01:40:59 +000033#include <wtf/UnusedParam.h>
oliver@apple.comffa27602008-12-07 23:55:04 +000034#include <wtf/Vector.h>
35
barraclough@apple.com43c4af92009-06-09 01:40:59 +000036#if PLATFORM(IPHONE)
37#include <libkern/OSCacheControl.h>
38#include <sys/mman.h>
39#endif
oliver@apple.comffa27602008-12-07 23:55:04 +000040
barraclough@apple.comdac882c2008-12-23 07:08:59 +000041#define JIT_ALLOCATOR_PAGE_SIZE (ExecutableAllocator::pageSize)
oliver@apple.comffa27602008-12-07 23:55:04 +000042#define JIT_ALLOCATOR_LARGE_ALLOC_SIZE (ExecutableAllocator::pageSize * 4)
43
barraclough@apple.comd00f4a62009-06-02 01:20:35 +000044#if ENABLE(ASSEMBLER_WX_EXCLUSIVE)
45#define PROTECTION_FLAGS_RW (PROT_READ | PROT_WRITE)
46#define PROTECTION_FLAGS_RX (PROT_READ | PROT_EXEC)
47#define INITIAL_PROTECTION_FLAGS PROTECTION_FLAGS_RX
48#else
49#define INITIAL_PROTECTION_FLAGS (PROT_READ | PROT_WRITE | PROT_EXEC)
50#endif
51
oliver@apple.comffa27602008-12-07 23:55:04 +000052namespace JSC {
53
ggaren@apple.comcd169892009-03-20 22:56:27 +000054inline size_t roundUpAllocationSize(size_t request, size_t granularity)
55{
56 if ((std::numeric_limits<size_t>::max() - granularity) <= request)
57 CRASH(); // Allocation is too large
58
59 // Round up to next page boundary
60 size_t size = request + (granularity - 1);
61 size = size & ~(granularity - 1);
62 ASSERT(size >= request);
63 return size;
64}
65
hausmann@webkit.orga3a338f2009-05-07 07:46:40 +000066}
67
68#if ENABLE(ASSEMBLER)
69
70namespace JSC {
71
oliver@apple.comffa27602008-12-07 23:55:04 +000072class ExecutablePool : public RefCounted<ExecutablePool> {
73private:
74 struct Allocation {
75 char* pages;
76 size_t size;
77 };
78 typedef Vector<Allocation, 2> AllocationList;
79
80public:
81 static PassRefPtr<ExecutablePool> create(size_t n)
82 {
83 return adoptRef(new ExecutablePool(n));
84 }
85
86 void* alloc(size_t n)
87 {
barraclough@apple.comdac882c2008-12-23 07:08:59 +000088 ASSERT(m_freePtr <= m_end);
89
90 // Round 'n' up to a multiple of word size; if all allocations are of
91 // word sized quantities, then all subsequent allocations will be aligned.
92 n = roundUpAllocationSize(n, sizeof(void*));
93
94 if (static_cast<ptrdiff_t>(n) < (m_end - m_freePtr)) {
95 void* result = m_freePtr;
96 m_freePtr += n;
oliver@apple.comffa27602008-12-07 23:55:04 +000097 return result;
98 }
99
100 // Insufficient space to allocate in the existing pool
101 // so we need allocate into a new pool
102 return poolAllocate(n);
103 }
104
105 ~ExecutablePool()
106 {
107 AllocationList::const_iterator end = m_pools.end();
108 for (AllocationList::const_iterator ptr = m_pools.begin(); ptr != end; ++ptr)
109 ExecutablePool::systemRelease(*ptr);
110 }
111
112 size_t available() const { return (m_pools.size() > 1) ? 0 : m_end - m_freePtr; }
113
114private:
115 static Allocation systemAlloc(size_t n);
116 static void systemRelease(const Allocation& alloc);
117
barraclough@apple.comdac882c2008-12-23 07:08:59 +0000118 ExecutablePool(size_t n);
oliver@apple.comffa27602008-12-07 23:55:04 +0000119
barraclough@apple.comdac882c2008-12-23 07:08:59 +0000120 void* poolAllocate(size_t n);
oliver@apple.comffa27602008-12-07 23:55:04 +0000121
122 char* m_freePtr;
123 char* m_end;
124 AllocationList m_pools;
125};
126
127class ExecutableAllocator {
barraclough@apple.comd00f4a62009-06-02 01:20:35 +0000128 enum ProtectionSeting { Writable, Executable };
129
oliver@apple.comffa27602008-12-07 23:55:04 +0000130public:
131 static size_t pageSize;
132 ExecutableAllocator()
133 {
134 if (!pageSize)
135 intializePageSize();
136 m_smallAllocationPool = ExecutablePool::create(JIT_ALLOCATOR_LARGE_ALLOC_SIZE);
137 }
138
139 PassRefPtr<ExecutablePool> poolForSize(size_t n)
140 {
141 // Try to fit in the existing small allocator
142 if (n < m_smallAllocationPool->available())
143 return m_smallAllocationPool;
144
145 // If the request is large, we just provide a unshared allocator
146 if (n > JIT_ALLOCATOR_LARGE_ALLOC_SIZE)
147 return ExecutablePool::create(n);
148
149 // Create a new allocator
150 RefPtr<ExecutablePool> pool = ExecutablePool::create(JIT_ALLOCATOR_LARGE_ALLOC_SIZE);
151
152 // If the new allocator will result in more free space than in
153 // the current small allocator, then we will use it instead
154 if ((pool->available() - n) > m_smallAllocationPool->available())
155 m_smallAllocationPool = pool;
156 return pool.release();
157 }
158
barraclough@apple.com265c4332009-06-09 04:20:36 +0000159#if ENABLE(ASSEMBLER_WX_EXCLUSIVE) || !(PLATFORM(X86) || PLATFORM(X86_64))
barraclough@apple.com43c4af92009-06-09 01:40:59 +0000160 static void makeWritable(void* start, size_t size)
161 {
162 reprotectRegion(start, size, Writable);
163 }
barraclough@apple.comd00f4a62009-06-02 01:20:35 +0000164
barraclough@apple.com43c4af92009-06-09 01:40:59 +0000165 static void makeExecutable(void* start, size_t size)
166 {
167 reprotectRegion(start, size, Executable);
168 cacheFlush(start, size);
169 }
170
barraclough@apple.com265c4332009-06-09 04:20:36 +0000171 // If ASSEMBLER_WX_EXCLUSIVE protection is turned on, or on non-x86 platforms,
172 // we need to track start & size so we can makeExecutable/cacheFlush at the end.
barraclough@apple.comd00f4a62009-06-02 01:20:35 +0000173 class MakeWritable {
174 public:
175 MakeWritable(void* start, size_t size)
176 : m_start(start)
177 , m_size(size)
178 {
179 makeWritable(start, size);
180 }
181
182 ~MakeWritable()
183 {
184 makeExecutable(m_start, m_size);
185 }
186
187 private:
188 void* m_start;
189 size_t m_size;
190 };
barraclough@apple.com265c4332009-06-09 04:20:36 +0000191#else
192 static void makeWritable(void*, size_t) {}
193 static void makeExecutable(void*, size_t) {}
194
195 // On x86, without ASSEMBLER_WX_EXCLUSIVE, there is nothing to do here.
196 class MakeWritable { public: MakeWritable(void*, size_t) {} };
barraclough@apple.comd00f4a62009-06-02 01:20:35 +0000197#endif
198
oliver@apple.comffa27602008-12-07 23:55:04 +0000199private:
barraclough@apple.comd00f4a62009-06-02 01:20:35 +0000200
barraclough@apple.com265c4332009-06-09 04:20:36 +0000201#if ENABLE(ASSEMBLER_WX_EXCLUSIVE) || !(PLATFORM(X86) || PLATFORM(X86_64))
barraclough@apple.comd00f4a62009-06-02 01:20:35 +0000202#if ENABLE(ASSEMBLER_WX_EXCLUSIVE)
203 static void reprotectRegion(void*, size_t, ProtectionSeting);
barraclough@apple.com43c4af92009-06-09 01:40:59 +0000204#else
205 static void reprotectRegion(void*, size_t, ProtectionSeting) {}
barraclough@apple.comd00f4a62009-06-02 01:20:35 +0000206#endif
207
barraclough@apple.com43c4af92009-06-09 01:40:59 +0000208 static void cacheFlush(void* code, size_t size)
209 {
210#if PLATFORM(X86) || PLATFORM(X86_64)
211 UNUSED_PARAM(code);
212 UNUSED_PARAM(size);
barraclough@apple.comcf4899d2009-06-20 01:07:06 +0000213#elif PLATFORM_ARM_ARCH(7) && PLATFORM(IPHONE)
barraclough@apple.com43c4af92009-06-09 01:40:59 +0000214 sys_dcache_flush(code, size);
215 sys_icache_invalidate(code, size);
216#else
217#error "ExecutableAllocator::cacheFlush not implemented on this platform."
218#endif
219 }
barraclough@apple.com265c4332009-06-09 04:20:36 +0000220#endif
barraclough@apple.com43c4af92009-06-09 01:40:59 +0000221
oliver@apple.comffa27602008-12-07 23:55:04 +0000222 RefPtr<ExecutablePool> m_smallAllocationPool;
223 static void intializePageSize();
224};
225
barraclough@apple.comdac882c2008-12-23 07:08:59 +0000226inline ExecutablePool::ExecutablePool(size_t n)
oliver@apple.comffa27602008-12-07 23:55:04 +0000227{
barraclough@apple.comdac882c2008-12-23 07:08:59 +0000228 size_t allocSize = roundUpAllocationSize(n, JIT_ALLOCATOR_PAGE_SIZE);
229 Allocation mem = systemAlloc(allocSize);
230 m_pools.append(mem);
231 m_freePtr = mem.pages;
232 if (!m_freePtr)
233 CRASH(); // Failed to allocate
234 m_end = m_freePtr + allocSize;
235}
236
237inline void* ExecutablePool::poolAllocate(size_t n)
238{
239 size_t allocSize = roundUpAllocationSize(n, JIT_ALLOCATOR_PAGE_SIZE);
oliver@apple.comffa27602008-12-07 23:55:04 +0000240
barraclough@apple.comdac882c2008-12-23 07:08:59 +0000241 Allocation result = systemAlloc(allocSize);
242 if (!result.pages)
243 CRASH(); // Failed to allocate
244
245 ASSERT(m_end >= m_freePtr);
246 if ((allocSize - n) > static_cast<size_t>(m_end - m_freePtr)) {
247 // Replace allocation pool
248 m_freePtr = result.pages + n;
249 m_end = result.pages + allocSize;
250 }
251
252 m_pools.append(result);
253 return result.pages;
oliver@apple.comffa27602008-12-07 23:55:04 +0000254}
255
256}
257
258#endif // ENABLE(ASSEMBLER)
259
260#endif // !defined(ExecutableAllocator)