/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=8 sts=4 et sw=4 tw=99: * * Copyright (C) 2008 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef jit_ExecutableAllocator_h #define jit_ExecutableAllocator_h #include "mozilla/Maybe.h" #include "mozilla/XorShift128PlusRNG.h" #include <limits> #include <stddef.h> // for ptrdiff_t #include "jsalloc.h" #ifdef JS_CODEGEN_ARM #include "jit/arm/Architecture-arm.h" #endif #include "jit/arm/Simulator-arm.h" #include "jit/mips32/Simulator-mips32.h" #include "jit/mips64/Simulator-mips64.h" #include "jit/ProcessExecutableMemory.h" #include "js/GCAPI.h" #include "js/HashTable.h" #include "js/Vector.h" #ifdef JS_CPU_SPARC #ifdef __linux__ // bugzilla 502369 static void sync_instruction_memory(caddr_t v, u_int len) { caddr_t end = v + len; caddr_t p = v; while (p < end) { asm("flush %0" : : "r" (p)); p += 32; } } #else extern "C" void sync_instruction_memory(caddr_t v, u_int len); #endif #endif #if defined(__linux__) && \ (defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)) && \ (!defined(JS_SIMULATOR_MIPS32) && !defined(JS_SIMULATOR_MIPS64)) #include <sys/cachectl.h> #endif #if defined(JS_CODEGEN_ARM) && defined(XP_IOS) #include <libkern/OSCacheControl.h> #endif namespace JS { struct CodeSizes; } // namespace JS namespace js { namespace jit { enum CodeKind { ION_CODE = 0, BASELINE_CODE, REGEXP_CODE, OTHER_CODE }; class ExecutableAllocator; class JitRuntime; // These are reference-counted. A new one starts with a count of 1. class ExecutablePool { friend class ExecutableAllocator; private: struct Allocation { char* pages; size_t size; }; ExecutableAllocator* m_allocator; char* m_freePtr; char* m_end; Allocation m_allocation; // Reference count for automatic reclamation. unsigned m_refCount:31; // Flag that can be used by algorithms operating on pools. bool m_mark:1; // Number of bytes currently used for Method and Regexp JIT code. size_t m_ionCodeBytes; size_t m_baselineCodeBytes; size_t m_regexpCodeBytes; size_t m_otherCodeBytes; public: void release(bool willDestroy = false); void release(size_t n, CodeKind kind); void addRef(); ExecutablePool(ExecutableAllocator* allocator, Allocation a) : m_allocator(allocator), m_freePtr(a.pages), m_end(m_freePtr + a.size), m_allocation(a), m_refCount(1), m_mark(false), m_ionCodeBytes(0), m_baselineCodeBytes(0), m_regexpCodeBytes(0), m_otherCodeBytes(0) { } ~ExecutablePool(); void mark() { MOZ_ASSERT(!m_mark); m_mark = true; } void unmark() { MOZ_ASSERT(m_mark); m_mark = false; } bool isMarked() const { return m_mark; } private: ExecutablePool(const ExecutablePool&) = delete; void operator=(const ExecutablePool&) = delete; void* alloc(size_t n, CodeKind kind); size_t available() const; }; struct JitPoisonRange { jit::ExecutablePool* pool; void* start; size_t size; JitPoisonRange(jit::ExecutablePool* pool, void* start, size_t size) : pool(pool), start(start), size(size) {} }; typedef Vector<JitPoisonRange, 0, SystemAllocPolicy> JitPoisonRangeVector; class ExecutableAllocator { JSRuntime* rt_; public: explicit ExecutableAllocator(JSRuntime* rt); ~ExecutableAllocator(); void purge(); // alloc() returns a pointer to some memory, and also (by reference) a // pointer to reference-counted pool. The caller owns a reference to the // pool; i.e. alloc() increments the count before returning the object. void* alloc(size_t n, ExecutablePool** poolp, CodeKind type); void releasePoolPages(ExecutablePool* pool); void addSizeOfCode(JS::CodeSizes* sizes) const; private: static const size_t OVERSIZE_ALLOCATION = size_t(-1); static size_t roundUpAllocationSize(size_t request, size_t granularity); // On OOM, this will return an Allocation where pages is nullptr. ExecutablePool::Allocation systemAlloc(size_t n); static void systemRelease(const ExecutablePool::Allocation& alloc); ExecutablePool* createPool(size_t n); ExecutablePool* poolForSize(size_t n); static void reprotectPool(JSRuntime* rt, ExecutablePool* pool, ProtectionSetting protection); public: MOZ_MUST_USE static bool makeWritable(void* start, size_t size) { return ReprotectRegion(start, size, ProtectionSetting::Writable); } MOZ_MUST_USE static bool makeExecutable(void* start, size_t size) { return ReprotectRegion(start, size, ProtectionSetting::Executable); } void makeAllWritable() { reprotectAll(ProtectionSetting::Writable); } void makeAllExecutable() { reprotectAll(ProtectionSetting::Executable); } static void poisonCode(JSRuntime* rt, JitPoisonRangeVector& ranges); #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) || defined(JS_SIMULATOR_ARM64) static void cacheFlush(void*, size_t) { } #elif defined(JS_SIMULATOR_ARM) || defined(JS_SIMULATOR_MIPS32) || defined(JS_SIMULATOR_MIPS64) static void cacheFlush(void* code, size_t size) { js::jit::Simulator::FlushICache(code, size); } #elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64) static void cacheFlush(void* code, size_t size) { #if defined(_MIPS_ARCH_LOONGSON3A) // On Loongson3-CPUs, The cache flushed automatically // by hardware. Just need to execute an instruction hazard. uintptr_t tmp; asm volatile ( ".set push \n" ".set noreorder \n" "move %[tmp], $ra \n" "bal 1f \n" "daddiu $ra, 8 \n" "1: \n" "jr.hb $ra \n" "move $ra, %[tmp] \n" ".set pop\n" :[tmp]"=&r"(tmp) ); #elif defined(__GNUC__) intptr_t end = reinterpret_cast<intptr_t>(code) + size; __builtin___clear_cache(reinterpret_cast<char*>(code), reinterpret_cast<char*>(end)); #else _flush_cache(reinterpret_cast<char*>(code), size, BCACHE); #endif } #elif defined(JS_CODEGEN_ARM) && (defined(__FreeBSD__) || defined(__NetBSD__)) static void cacheFlush(void* code, size_t size) { __clear_cache(code, reinterpret_cast<char*>(code) + size); } #elif (defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64)) && defined(XP_IOS) static void cacheFlush(void* code, size_t size) { sys_icache_invalidate(code, size); } #elif defined(JS_CODEGEN_ARM) && (defined(__linux__) || defined(ANDROID)) && defined(__GNUC__) static void cacheFlush(void* code, size_t size) { void* end = (void*)(reinterpret_cast<char*>(code) + size); asm volatile ( "push {r7}\n" "mov r0, %0\n" "mov r1, %1\n" "mov r7, #0xf0000\n" "add r7, r7, #0x2\n" "mov r2, #0x0\n" "svc 0x0\n" "pop {r7}\n" : : "r" (code), "r" (end) : "r0", "r1", "r2"); if (ForceDoubleCacheFlush()) { void* start = (void*)((uintptr_t)code + 1); asm volatile ( "push {r7}\n" "mov r0, %0\n" "mov r1, %1\n" "mov r7, #0xf0000\n" "add r7, r7, #0x2\n" "mov r2, #0x0\n" "svc 0x0\n" "pop {r7}\n" : : "r" (start), "r" (end) : "r0", "r1", "r2"); } } #elif defined(JS_CODEGEN_ARM64) static void cacheFlush(void* code, size_t size) { __clear_cache(code, (void *)((size_t)code + size)); } #elif JS_CPU_SPARC static void cacheFlush(void* code, size_t size) { sync_instruction_memory((caddr_t)code, size); } #endif private: ExecutableAllocator(const ExecutableAllocator&) = delete; void operator=(const ExecutableAllocator&) = delete; void reprotectAll(ProtectionSetting); // These are strong references; they keep pools alive. static const size_t maxSmallPools = 4; typedef js::Vector<ExecutablePool*, maxSmallPools, js::SystemAllocPolicy> SmallExecPoolVector; SmallExecPoolVector m_smallPools; // All live pools are recorded here, just for stats purposes. These are // weak references; they don't keep pools alive. When a pool is destroyed // its reference is removed from m_pools. typedef js::HashSet<ExecutablePool*, js::DefaultHasher<ExecutablePool*>, js::SystemAllocPolicy> ExecPoolHashSet; ExecPoolHashSet m_pools; // All pools, just for stats purposes. }; } // namespace jit } // namespace js #endif /* jit_ExecutableAllocator_h */