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(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(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 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 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(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 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