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#include <stddef.h> // for ptrdiff_t
29#include <limits>
30#include <wtf/Assertions.h>
31#include <wtf/PageAllocation.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(IOS)
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 CPU(MIPS) && OS(LINUX)
47#include <sys/cachectl.h>
48#endif
49
50#if CPU(SH4) && OS(LINUX)
51#include <asm/cachectl.h>
52#include <asm/unistd.h>
53#include <sys/syscall.h>
54#include <unistd.h>
55#endif
56
57#if OS(WINCE)
58// From pkfuncs.h (private header file from the Platform Builder)
59#define CACHE_SYNC_ALL 0x07F
60extern "C" __declspec(dllimport) void CacheRangeFlush(LPVOID pAddr, DWORD dwLength, DWORD dwFlags);
61#endif
62
63#if PLATFORM(BREWMP)
64#include <AEEIMemCache1.h>
65#include <AEEMemCache1.bid>
66#include <wtf/brew/RefPtrBrew.h>
67#endif
68
69#define JIT_ALLOCATOR_PAGE_SIZE (ExecutableAllocator::pageSize)
70#define JIT_ALLOCATOR_LARGE_ALLOC_SIZE (ExecutableAllocator::pageSize * 4)
71
72#if ENABLE(ASSEMBLER_WX_EXCLUSIVE)
73#define PROTECTION_FLAGS_RW (PROT_READ | PROT_WRITE)
74#define PROTECTION_FLAGS_RX (PROT_READ | PROT_EXEC)
75#define EXECUTABLE_POOL_WRITABLE false
76#else
77#define EXECUTABLE_POOL_WRITABLE true
78#endif
79
80namespace JSC {
81
82inline size_t roundUpAllocationSize(size_t request, size_t granularity)
83{
84    if ((std::numeric_limits<size_t>::max() - granularity) <= request)
85        CRASH(); // Allocation is too large
86
87    // Round up to next page boundary
88    size_t size = request + (granularity - 1);
89    size = size & ~(granularity - 1);
90    ASSERT(size >= request);
91    return size;
92}
93
94}
95
96#if ENABLE(JIT) && ENABLE(ASSEMBLER)
97
98namespace JSC {
99
100class ExecutablePool : public RefCounted<ExecutablePool> {
101public:
102#if ENABLE(EXECUTABLE_ALLOCATOR_DEMAND)
103    typedef PageAllocation Allocation;
104#else
105    class Allocation {
106    public:
107        Allocation(void* base, size_t size)
108            : m_base(base)
109            , m_size(size)
110        {
111        }
112        void* base() { return m_base; }
113        size_t size() { return m_size; }
114        bool operator!() const { return !m_base; }
115
116    private:
117        void* m_base;
118        size_t m_size;
119    };
120#endif
121    typedef Vector<Allocation, 2> AllocationList;
122
123    static PassRefPtr<ExecutablePool> create(size_t n)
124    {
125        return adoptRef(new ExecutablePool(n));
126    }
127
128    void* alloc(size_t n)
129    {
130        ASSERT(m_freePtr <= m_end);
131
132        // Round 'n' up to a multiple of word size; if all allocations are of
133        // word sized quantities, then all subsequent allocations will be aligned.
134        n = roundUpAllocationSize(n, sizeof(void*));
135
136        if (static_cast<ptrdiff_t>(n) < (m_end - m_freePtr)) {
137            void* result = m_freePtr;
138            m_freePtr += n;
139            return result;
140        }
141
142        // Insufficient space to allocate in the existing pool
143        // so we need allocate into a new pool
144        return poolAllocate(n);
145    }
146
147    void tryShrink(void* allocation, size_t oldSize, size_t newSize)
148    {
149        if (static_cast<char*>(allocation) + oldSize != m_freePtr)
150            return;
151        m_freePtr = static_cast<char*>(allocation) + roundUpAllocationSize(newSize, sizeof(void*));
152    }
153
154    ~ExecutablePool()
155    {
156        AllocationList::iterator end = m_pools.end();
157        for (AllocationList::iterator ptr = m_pools.begin(); ptr != end; ++ptr)
158            ExecutablePool::systemRelease(*ptr);
159    }
160
161    size_t available() const { return (m_pools.size() > 1) ? 0 : m_end - m_freePtr; }
162
163private:
164    static Allocation systemAlloc(size_t n);
165    static void systemRelease(Allocation& alloc);
166
167    ExecutablePool(size_t n);
168
169    void* poolAllocate(size_t n);
170
171    char* m_freePtr;
172    char* m_end;
173    AllocationList m_pools;
174};
175
176class ExecutableAllocator {
177    enum ProtectionSetting { Writable, Executable };
178
179public:
180    static size_t pageSize;
181    ExecutableAllocator()
182    {
183        if (!pageSize)
184            intializePageSize();
185        if (isValid())
186            m_smallAllocationPool = ExecutablePool::create(JIT_ALLOCATOR_LARGE_ALLOC_SIZE);
187#if !ENABLE(INTERPRETER)
188        else
189            CRASH();
190#endif
191    }
192
193    bool isValid() const;
194
195    static bool underMemoryPressure();
196
197    PassRefPtr<ExecutablePool> poolForSize(size_t n)
198    {
199        // Try to fit in the existing small allocator
200        ASSERT(m_smallAllocationPool);
201        if (n < m_smallAllocationPool->available())
202            return m_smallAllocationPool;
203
204        // If the request is large, we just provide a unshared allocator
205        if (n > JIT_ALLOCATOR_LARGE_ALLOC_SIZE)
206            return ExecutablePool::create(n);
207
208        // Create a new allocator
209        RefPtr<ExecutablePool> pool = ExecutablePool::create(JIT_ALLOCATOR_LARGE_ALLOC_SIZE);
210
211        // If the new allocator will result in more free space than in
212        // the current small allocator, then we will use it instead
213        if ((pool->available() - n) > m_smallAllocationPool->available())
214            m_smallAllocationPool = pool;
215        return pool.release();
216    }
217
218#if ENABLE(ASSEMBLER_WX_EXCLUSIVE)
219    static void makeWritable(void* start, size_t size)
220    {
221        reprotectRegion(start, size, Writable);
222    }
223
224    static void makeExecutable(void* start, size_t size)
225    {
226        reprotectRegion(start, size, Executable);
227    }
228#else
229    static void makeWritable(void*, size_t) {}
230    static void makeExecutable(void*, size_t) {}
231#endif
232
233
234#if CPU(X86) || CPU(X86_64)
235    static void cacheFlush(void*, size_t)
236    {
237    }
238#elif CPU(MIPS)
239    static void cacheFlush(void* code, size_t size)
240    {
241#if GCC_VERSION_AT_LEAST(4, 3, 0)
242#if WTF_MIPS_ISA_REV(2) && !GCC_VERSION_AT_LEAST(4, 4, 3)
243        int lineSize;
244        asm("rdhwr %0, $1" : "=r" (lineSize));
245        //
246        // Modify "start" and "end" to avoid GCC 4.3.0-4.4.2 bug in
247        // mips_expand_synci_loop that may execute synci one more time.
248        // "start" points to the fisrt byte of the cache line.
249        // "end" points to the last byte of the line before the last cache line.
250        // Because size is always a multiple of 4, this is safe to set
251        // "end" to the last byte.
252        //
253        intptr_t start = reinterpret_cast<intptr_t>(code) & (-lineSize);
254        intptr_t end = ((reinterpret_cast<intptr_t>(code) + size - 1) & (-lineSize)) - 1;
255        __builtin___clear_cache(reinterpret_cast<char*>(start), reinterpret_cast<char*>(end));
256#else
257        intptr_t end = reinterpret_cast<intptr_t>(code) + size;
258        __builtin___clear_cache(reinterpret_cast<char*>(code), reinterpret_cast<char*>(end));
259#endif
260#else
261        _flush_cache(reinterpret_cast<char*>(code), size, BCACHE);
262#endif
263    }
264#elif CPU(ARM_THUMB2) && OS(IOS)
265    static void cacheFlush(void* code, size_t size)
266    {
267        sys_cache_control(kCacheFunctionPrepareForExecution, code, size);
268    }
269#elif CPU(ARM_THUMB2) && OS(LINUX)
270    static void cacheFlush(void* code, size_t size)
271    {
272        asm volatile (
273            "push    {r7}\n"
274            "mov     r0, %0\n"
275            "mov     r1, %1\n"
276            "movw    r7, #0x2\n"
277            "movt    r7, #0xf\n"
278            "movs    r2, #0x0\n"
279            "svc     0x0\n"
280            "pop     {r7}\n"
281            :
282            : "r" (code), "r" (reinterpret_cast<char*>(code) + size)
283            : "r0", "r1", "r2");
284    }
285#elif OS(SYMBIAN)
286    static void cacheFlush(void* code, size_t size)
287    {
288        User::IMB_Range(code, static_cast<char*>(code) + size);
289    }
290#elif CPU(ARM_TRADITIONAL) && OS(LINUX) && COMPILER(RVCT)
291    static __asm void cacheFlush(void* code, size_t size);
292#elif CPU(ARM_TRADITIONAL) && OS(LINUX) && COMPILER(GCC)
293    static void cacheFlush(void* code, size_t size)
294    {
295        asm volatile (
296            "push    {r7}\n"
297            "mov     r0, %0\n"
298            "mov     r1, %1\n"
299            "mov     r7, #0xf0000\n"
300            "add     r7, r7, #0x2\n"
301            "mov     r2, #0x0\n"
302            "svc     0x0\n"
303            "pop     {r7}\n"
304            :
305            : "r" (code), "r" (reinterpret_cast<char*>(code) + size)
306            : "r0", "r1", "r2");
307    }
308#elif OS(WINCE)
309    static void cacheFlush(void* code, size_t size)
310    {
311        CacheRangeFlush(code, size, CACHE_SYNC_ALL);
312    }
313#elif PLATFORM(BREWMP)
314    static void cacheFlush(void* code, size_t size)
315    {
316        RefPtr<IMemCache1> memCache = createRefPtrInstance<IMemCache1>(AEECLSID_MemCache1);
317        IMemCache1_ClearCache(memCache.get(), reinterpret_cast<uint32>(code), size, MEMSPACE_CACHE_FLUSH, MEMSPACE_DATACACHE);
318        IMemCache1_ClearCache(memCache.get(), reinterpret_cast<uint32>(code), size, MEMSPACE_CACHE_INVALIDATE, MEMSPACE_INSTCACHE);
319    }
320#elif CPU(SH4) && OS(LINUX)
321    static void cacheFlush(void* code, size_t size)
322    {
323#ifdef CACHEFLUSH_D_L2
324        syscall(__NR_cacheflush, reinterpret_cast<unsigned>(code), size, CACHEFLUSH_D_WB | CACHEFLUSH_I | CACHEFLUSH_D_L2);
325#else
326        syscall(__NR_cacheflush, reinterpret_cast<unsigned>(code), size, CACHEFLUSH_D_WB | CACHEFLUSH_I);
327#endif
328    }
329#else
330    #error "The cacheFlush support is missing on this platform."
331#endif
332    static size_t committedByteCount();
333
334private:
335
336#if ENABLE(ASSEMBLER_WX_EXCLUSIVE)
337    static void reprotectRegion(void*, size_t, ProtectionSetting);
338#endif
339
340    RefPtr<ExecutablePool> m_smallAllocationPool;
341    static void intializePageSize();
342};
343
344inline ExecutablePool::ExecutablePool(size_t n)
345{
346    size_t allocSize = roundUpAllocationSize(n, JIT_ALLOCATOR_PAGE_SIZE);
347    Allocation mem = systemAlloc(allocSize);
348    m_pools.append(mem);
349    m_freePtr = static_cast<char*>(mem.base());
350    if (!m_freePtr)
351        CRASH(); // Failed to allocate
352    m_end = m_freePtr + allocSize;
353}
354
355inline void* ExecutablePool::poolAllocate(size_t n)
356{
357    size_t allocSize = roundUpAllocationSize(n, JIT_ALLOCATOR_PAGE_SIZE);
358
359    Allocation result = systemAlloc(allocSize);
360    if (!result.base())
361        CRASH(); // Failed to allocate
362
363    ASSERT(m_end >= m_freePtr);
364    if ((allocSize - n) > static_cast<size_t>(m_end - m_freePtr)) {
365        // Replace allocation pool
366        m_freePtr = static_cast<char*>(result.base()) + n;
367        m_end = static_cast<char*>(result.base()) + allocSize;
368    }
369
370    m_pools.append(result);
371    return result.base();
372}
373
374}
375
376#endif // ENABLE(JIT) && ENABLE(ASSEMBLER)
377
378#endif // !defined(ExecutableAllocator)
379