Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=8 sts=4 et sw=4 tw=99:
3 : *
4 : * Copyright (C) 2008 Apple Inc. All rights reserved.
5 : *
6 : * Redistribution and use in source and binary forms, with or without
7 : * modification, are permitted provided that the following conditions
8 : * are met:
9 : * 1. Redistributions of source code must retain the above copyright
10 : * notice, this list of conditions and the following disclaimer.
11 : * 2. Redistributions in binary form must reproduce the above copyright
12 : * notice, this list of conditions and the following disclaimer in the
13 : * documentation and/or other materials provided with the distribution.
14 : *
15 : * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
16 : * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 : * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18 : * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
19 : * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20 : * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 : * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22 : * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
23 : * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 : * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 : * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 : */
27 :
28 : #ifndef jit_ExecutableAllocator_h
29 : #define jit_ExecutableAllocator_h
30 :
31 : #include "mozilla/Maybe.h"
32 : #include "mozilla/XorShift128PlusRNG.h"
33 :
34 : #include <limits>
35 : #include <stddef.h> // for ptrdiff_t
36 :
37 : #include "jsalloc.h"
38 :
39 : #ifdef JS_CODEGEN_ARM
40 : #include "jit/arm/Architecture-arm.h"
41 : #endif
42 : #include "jit/arm/Simulator-arm.h"
43 : #if defined(JS_CODEGEN_ARM64)
44 : #include "jit/arm64/vixl/Cpu-vixl.h"
45 : #endif
46 : #include "jit/mips32/Simulator-mips32.h"
47 : #include "jit/mips64/Simulator-mips64.h"
48 : #include "jit/ProcessExecutableMemory.h"
49 : #include "js/GCAPI.h"
50 : #include "js/HashTable.h"
51 : #include "js/Vector.h"
52 :
53 : #if defined(__sparc__)
54 : #ifdef __linux__ // bugzilla 502369
55 : static void sync_instruction_memory(caddr_t v, u_int len)
56 : {
57 : caddr_t end = v + len;
58 : caddr_t p = v;
59 : while (p < end) {
60 : asm("flush %0" : : "r" (p));
61 : p += 32;
62 : }
63 : }
64 : #else
65 : extern "C" void sync_instruction_memory(caddr_t v, u_int len);
66 : #endif
67 : #endif
68 :
69 : #if defined(__linux__) && \
70 : (defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)) && \
71 : (!defined(JS_SIMULATOR_MIPS32) && !defined(JS_SIMULATOR_MIPS64))
72 : #include <sys/cachectl.h>
73 : #endif
74 :
75 : #if defined(JS_CODEGEN_ARM) && defined(XP_IOS)
76 : #include <libkern/OSCacheControl.h>
77 : #endif
78 :
79 : namespace JS {
80 : struct CodeSizes;
81 : } // namespace JS
82 :
83 : namespace js {
84 : namespace jit {
85 :
86 : enum CodeKind { ION_CODE = 0, BASELINE_CODE, REGEXP_CODE, OTHER_CODE };
87 :
88 : class ExecutableAllocator;
89 : class JitRuntime;
90 :
91 : // These are reference-counted. A new one starts with a count of 1.
92 : class ExecutablePool
93 : {
94 : friend class ExecutableAllocator;
95 :
96 : private:
97 : struct Allocation {
98 : char* pages;
99 : size_t size;
100 : };
101 :
102 : ExecutableAllocator* m_allocator;
103 : char* m_freePtr;
104 : char* m_end;
105 : Allocation m_allocation;
106 :
107 : // Reference count for automatic reclamation.
108 : unsigned m_refCount:31;
109 :
110 : // Flag that can be used by algorithms operating on pools.
111 : bool m_mark:1;
112 :
113 : // Number of bytes currently used for Method and Regexp JIT code.
114 : size_t m_ionCodeBytes;
115 : size_t m_baselineCodeBytes;
116 : size_t m_regexpCodeBytes;
117 : size_t m_otherCodeBytes;
118 :
119 : public:
120 : void release(bool willDestroy = false);
121 : void release(size_t n, CodeKind kind);
122 :
123 : void addRef();
124 :
125 81 : ExecutablePool(ExecutableAllocator* allocator, Allocation a)
126 243 : : m_allocator(allocator), m_freePtr(a.pages), m_end(m_freePtr + a.size), m_allocation(a),
127 : m_refCount(1), m_mark(false), m_ionCodeBytes(0), m_baselineCodeBytes(0),
128 243 : m_regexpCodeBytes(0), m_otherCodeBytes(0)
129 81 : { }
130 :
131 : ~ExecutablePool();
132 :
133 0 : void mark() {
134 0 : MOZ_ASSERT(!m_mark);
135 0 : m_mark = true;
136 0 : }
137 0 : void unmark() {
138 0 : MOZ_ASSERT(m_mark);
139 0 : m_mark = false;
140 0 : }
141 0 : bool isMarked() const {
142 0 : return m_mark;
143 : }
144 :
145 : private:
146 : ExecutablePool(const ExecutablePool&) = delete;
147 : void operator=(const ExecutablePool&) = delete;
148 :
149 : void* alloc(size_t n, CodeKind kind);
150 :
151 : size_t available() const;
152 : };
153 :
154 : struct JitPoisonRange
155 : {
156 : jit::ExecutablePool* pool;
157 : void* start;
158 : size_t size;
159 :
160 0 : JitPoisonRange(jit::ExecutablePool* pool, void* start, size_t size)
161 0 : : pool(pool), start(start), size(size)
162 0 : {}
163 : };
164 :
165 : typedef Vector<JitPoisonRange, 0, SystemAllocPolicy> JitPoisonRangeVector;
166 :
167 : class ExecutableAllocator
168 : {
169 : JSRuntime* rt_;
170 :
171 : public:
172 : explicit ExecutableAllocator(JSRuntime* rt);
173 : ~ExecutableAllocator();
174 :
175 : void purge();
176 :
177 : // alloc() returns a pointer to some memory, and also (by reference) a
178 : // pointer to reference-counted pool. The caller owns a reference to the
179 : // pool; i.e. alloc() increments the count before returning the object.
180 : void* alloc(JSContext* cx, size_t n, ExecutablePool** poolp, CodeKind type);
181 :
182 : void releasePoolPages(ExecutablePool* pool);
183 :
184 : void addSizeOfCode(JS::CodeSizes* sizes) const;
185 :
186 : private:
187 : static const size_t OVERSIZE_ALLOCATION = size_t(-1);
188 :
189 : static size_t roundUpAllocationSize(size_t request, size_t granularity);
190 :
191 : // On OOM, this will return an Allocation where pages is nullptr.
192 : ExecutablePool::Allocation systemAlloc(size_t n);
193 : static void systemRelease(const ExecutablePool::Allocation& alloc);
194 :
195 : ExecutablePool* createPool(size_t n);
196 : ExecutablePool* poolForSize(size_t n);
197 :
198 : static void reprotectPool(JSRuntime* rt, ExecutablePool* pool, ProtectionSetting protection);
199 :
200 : public:
201 : MOZ_MUST_USE
202 4499 : static bool makeWritable(void* start, size_t size)
203 : {
204 4499 : return ReprotectRegion(start, size, ProtectionSetting::Writable);
205 : }
206 :
207 : MOZ_MUST_USE
208 4499 : static bool makeExecutable(void* start, size_t size)
209 : {
210 4499 : return ReprotectRegion(start, size, ProtectionSetting::Executable);
211 : }
212 :
213 0 : void makeAllWritable() {
214 0 : reprotectAll(ProtectionSetting::Writable);
215 0 : }
216 0 : void makeAllExecutable() {
217 0 : reprotectAll(ProtectionSetting::Executable);
218 0 : }
219 :
220 : static void poisonCode(JSRuntime* rt, JitPoisonRangeVector& ranges);
221 :
222 : #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) || defined(JS_SIMULATOR_ARM64) || defined(JS_CODEGEN_NONE)
223 0 : static void cacheFlush(void*, size_t)
224 : {
225 0 : }
226 : #elif defined(JS_SIMULATOR_ARM) || defined(JS_SIMULATOR_MIPS32) || defined(JS_SIMULATOR_MIPS64)
227 : static void cacheFlush(void* code, size_t size)
228 : {
229 : js::jit::SimulatorProcess::FlushICache(code, size);
230 : }
231 : #elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
232 : static void cacheFlush(void* code, size_t size)
233 : {
234 : #if defined(_MIPS_ARCH_LOONGSON3A)
235 : // On Loongson3-CPUs, The cache flushed automatically
236 : // by hardware. Just need to execute an instruction hazard.
237 : uintptr_t tmp;
238 : asm volatile (
239 : ".set push \n"
240 : ".set noreorder \n"
241 : "move %[tmp], $ra \n"
242 : "bal 1f \n"
243 : "daddiu $ra, 8 \n"
244 : "1: \n"
245 : "jr.hb $ra \n"
246 : "move $ra, %[tmp] \n"
247 : ".set pop\n"
248 : :[tmp]"=&r"(tmp)
249 : );
250 : #elif defined(__GNUC__)
251 : intptr_t end = reinterpret_cast<intptr_t>(code) + size;
252 : __builtin___clear_cache(reinterpret_cast<char*>(code), reinterpret_cast<char*>(end));
253 : #else
254 : _flush_cache(reinterpret_cast<char*>(code), size, BCACHE);
255 : #endif
256 : }
257 : #elif defined(JS_CODEGEN_ARM) && (defined(__FreeBSD__) || defined(__NetBSD__))
258 : static void cacheFlush(void* code, size_t size)
259 : {
260 : __clear_cache(code, reinterpret_cast<char*>(code) + size);
261 : }
262 : #elif (defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64)) && defined(XP_IOS)
263 : static void cacheFlush(void* code, size_t size)
264 : {
265 : sys_icache_invalidate(code, size);
266 : }
267 : #elif defined(JS_CODEGEN_ARM) && (defined(__linux__) || defined(ANDROID)) && defined(__GNUC__)
268 : static void cacheFlush(void* code, size_t size)
269 : {
270 : void* end = (void*)(reinterpret_cast<char*>(code) + size);
271 : asm volatile (
272 : "push {r7}\n"
273 : "mov r0, %0\n"
274 : "mov r1, %1\n"
275 : "mov r7, #0xf0000\n"
276 : "add r7, r7, #0x2\n"
277 : "mov r2, #0x0\n"
278 : "svc 0x0\n"
279 : "pop {r7}\n"
280 : :
281 : : "r" (code), "r" (end)
282 : : "r0", "r1", "r2");
283 :
284 : if (ForceDoubleCacheFlush()) {
285 : void* start = (void*)((uintptr_t)code + 1);
286 : asm volatile (
287 : "push {r7}\n"
288 : "mov r0, %0\n"
289 : "mov r1, %1\n"
290 : "mov r7, #0xf0000\n"
291 : "add r7, r7, #0x2\n"
292 : "mov r2, #0x0\n"
293 : "svc 0x0\n"
294 : "pop {r7}\n"
295 : :
296 : : "r" (start), "r" (end)
297 : : "r0", "r1", "r2");
298 : }
299 : }
300 : #elif defined(JS_CODEGEN_ARM64)
301 : static void cacheFlush(void* code, size_t size)
302 : {
303 : vixl::CPU::EnsureIAndDCacheCoherency(code, size);
304 : }
305 : #elif defined(__sparc__)
306 : static void cacheFlush(void* code, size_t size)
307 : {
308 : sync_instruction_memory((caddr_t)code, size);
309 : }
310 : #endif
311 :
312 : private:
313 : ExecutableAllocator(const ExecutableAllocator&) = delete;
314 : void operator=(const ExecutableAllocator&) = delete;
315 :
316 : void reprotectAll(ProtectionSetting);
317 :
318 : // These are strong references; they keep pools alive.
319 : static const size_t maxSmallPools = 4;
320 : typedef js::Vector<ExecutablePool*, maxSmallPools, js::SystemAllocPolicy> SmallExecPoolVector;
321 : SmallExecPoolVector m_smallPools;
322 :
323 : // All live pools are recorded here, just for stats purposes. These are
324 : // weak references; they don't keep pools alive. When a pool is destroyed
325 : // its reference is removed from m_pools.
326 : typedef js::HashSet<ExecutablePool*, js::DefaultHasher<ExecutablePool*>, js::SystemAllocPolicy>
327 : ExecPoolHashSet;
328 : ExecPoolHashSet m_pools; // All pools, just for stats purposes.
329 : };
330 :
331 : } // namespace jit
332 : } // namespace js
333 :
334 : #endif /* jit_ExecutableAllocator_h */
|