| /* |
| * Stack-less Just-In-Time compiler |
| * |
| * Copyright Zoltan Herczeg (hzmester@freemail.hu). 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 THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``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 THE COPYRIGHT HOLDER(S) 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. |
| */ |
| |
| /* |
| This file contains a simple W^X executable memory allocator for POSIX |
| like systems and Windows |
| |
| In *NIX, MAP_ANON is required (that is considered a feature) so make |
| sure to set the right availability macros for your system or the code |
| will fail to build. |
| |
| If your system doesn't support mapping of anonymous pages (ex: IRIX) it |
| is also likely that it doesn't need this allocator and should be using |
| the standard one instead. |
| |
| It allocates a separate map for each code block and may waste a lot of |
| memory, because whatever was requested, will be rounded up to the page |
| size (minimum 4KB, but could be even bigger). |
| |
| It changes the page permissions (RW <-> RX) as needed and therefore, if you |
| will be updating the code after it has been generated, need to make sure to |
| block any concurrent execution, or could result in a SIGBUS, that could |
| even manifest itself at a different address than the one that was being |
| modified. |
| |
| Only use if you are unable to use the regular allocator because of security |
| restrictions and adding exceptions to your application or the system are |
| not possible. |
| */ |
| |
| #define SLJIT_UPDATE_WX_FLAGS(from, to, enable_exec) \ |
| sljit_update_wx_flags((from), (to), (enable_exec)) |
| |
| #ifndef _WIN32 |
| #include <sys/types.h> |
| #include <sys/mman.h> |
| |
| #ifdef __NetBSD__ |
| #if defined(PROT_MPROTECT) |
| #define check_se_protected(ptr, size) (0) |
| #define SLJIT_PROT_WX PROT_MPROTECT(PROT_EXEC) |
| #else /* !PROT_MPROTECT */ |
| #ifdef _NETBSD_SOURCE |
| #include <sys/param.h> |
| #else /* !_NETBSD_SOURCE */ |
| typedef unsigned int u_int; |
| #define devmajor_t sljit_s32 |
| #endif /* _NETBSD_SOURCE */ |
| #include <sys/sysctl.h> |
| #include <unistd.h> |
| |
| #define check_se_protected(ptr, size) netbsd_se_protected() |
| |
| static SLJIT_INLINE int netbsd_se_protected(void) |
| { |
| int mib[3]; |
| int paxflags; |
| size_t len = sizeof(paxflags); |
| |
| mib[0] = CTL_PROC; |
| mib[1] = getpid(); |
| mib[2] = PROC_PID_PAXFLAGS; |
| |
| if (SLJIT_UNLIKELY(sysctl(mib, 3, &paxflags, &len, NULL, 0) < 0)) |
| return -1; |
| |
| return (paxflags & CTL_PROC_PAXFLAGS_MPROTECT) ? -1 : 0; |
| } |
| #endif /* PROT_MPROTECT */ |
| #else /* POSIX */ |
| #define check_se_protected(ptr, size) generic_se_protected(ptr, size) |
| |
| static SLJIT_INLINE int generic_se_protected(void *ptr, sljit_uw size) |
| { |
| if (SLJIT_LIKELY(!mprotect(ptr, size, PROT_EXEC))) |
| return mprotect(ptr, size, PROT_READ | PROT_WRITE); |
| |
| return -1; |
| } |
| #endif /* NetBSD */ |
| |
| #if defined SLJIT_SINGLE_THREADED && SLJIT_SINGLE_THREADED |
| #define SLJIT_SE_LOCK() |
| #define SLJIT_SE_UNLOCK() |
| #else /* !SLJIT_SINGLE_THREADED */ |
| #include <pthread.h> |
| #define SLJIT_SE_LOCK() pthread_mutex_lock(&se_lock) |
| #define SLJIT_SE_UNLOCK() pthread_mutex_unlock(&se_lock) |
| #endif /* SLJIT_SINGLE_THREADED */ |
| |
| #ifndef SLJIT_PROT_WX |
| #define SLJIT_PROT_WX 0 |
| #endif /* !SLJIT_PROT_WX */ |
| |
| SLJIT_API_FUNC_ATTRIBUTE void* sljit_malloc_exec(sljit_uw size) |
| { |
| #if !(defined SLJIT_SINGLE_THREADED && SLJIT_SINGLE_THREADED) |
| static pthread_mutex_t se_lock = PTHREAD_MUTEX_INITIALIZER; |
| #endif |
| static int se_protected = !SLJIT_PROT_WX; |
| int prot = PROT_READ | PROT_WRITE | SLJIT_PROT_WX; |
| sljit_uw* ptr; |
| |
| if (SLJIT_UNLIKELY(se_protected < 0)) |
| return NULL; |
| |
| #ifdef PROT_MAX |
| prot |= PROT_MAX(PROT_READ | PROT_WRITE | PROT_EXEC); |
| #endif |
| |
| size += sizeof(sljit_uw); |
| ptr = (sljit_uw*)mmap(NULL, size, prot, MAP_PRIVATE | MAP_ANON, -1, 0); |
| |
| if (ptr == MAP_FAILED) |
| return NULL; |
| |
| if (SLJIT_UNLIKELY(se_protected > 0)) { |
| SLJIT_SE_LOCK(); |
| se_protected = check_se_protected(ptr, size); |
| SLJIT_SE_UNLOCK(); |
| if (SLJIT_UNLIKELY(se_protected < 0)) { |
| munmap((void *)ptr, size); |
| return NULL; |
| } |
| } |
| |
| *ptr++ = size; |
| return ptr; |
| } |
| |
| #undef SLJIT_PROT_WX |
| #undef SLJIT_SE_UNLOCK |
| #undef SLJIT_SE_LOCK |
| |
| SLJIT_API_FUNC_ATTRIBUTE void sljit_free_exec(void* ptr) |
| { |
| sljit_uw *start_ptr = ((sljit_uw*)ptr) - 1; |
| munmap((void*)start_ptr, *start_ptr); |
| } |
| |
| static void sljit_update_wx_flags(void *from, void *to, sljit_s32 enable_exec) |
| { |
| sljit_uw page_mask = (sljit_uw)get_page_alignment(); |
| sljit_uw start = (sljit_uw)from; |
| sljit_uw end = (sljit_uw)to; |
| int prot = PROT_READ | (enable_exec ? PROT_EXEC : PROT_WRITE); |
| |
| SLJIT_ASSERT(start < end); |
| |
| start &= ~page_mask; |
| end = (end + page_mask) & ~page_mask; |
| |
| mprotect((void*)start, end - start, prot); |
| } |
| |
| #else /* windows */ |
| |
| SLJIT_API_FUNC_ATTRIBUTE void* sljit_malloc_exec(sljit_uw size) |
| { |
| sljit_uw *ptr; |
| |
| size += sizeof(sljit_uw); |
| ptr = (sljit_uw*)VirtualAlloc(NULL, size, |
| MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); |
| |
| if (!ptr) |
| return NULL; |
| |
| *ptr++ = size; |
| |
| return ptr; |
| } |
| |
| SLJIT_API_FUNC_ATTRIBUTE void sljit_free_exec(void* ptr) |
| { |
| sljit_uw start = (sljit_uw)ptr - sizeof(sljit_uw); |
| #if defined(SLJIT_DEBUG) && SLJIT_DEBUG |
| sljit_uw page_mask = (sljit_uw)get_page_alignment(); |
| |
| SLJIT_ASSERT(!(start & page_mask)); |
| #endif |
| VirtualFree((void*)start, 0, MEM_RELEASE); |
| } |
| |
| static void sljit_update_wx_flags(void *from, void *to, sljit_s32 enable_exec) |
| { |
| DWORD oldprot; |
| sljit_uw page_mask = (sljit_uw)get_page_alignment(); |
| sljit_uw start = (sljit_uw)from; |
| sljit_uw end = (sljit_uw)to; |
| DWORD prot = enable_exec ? PAGE_EXECUTE : PAGE_READWRITE; |
| |
| SLJIT_ASSERT(start < end); |
| |
| start &= ~page_mask; |
| end = (end + page_mask) & ~page_mask; |
| |
| VirtualProtect((void*)start, end - start, prot, &oldprot); |
| } |
| |
| #endif /* !windows */ |
| |
| SLJIT_API_FUNC_ATTRIBUTE void sljit_free_unused_memory_exec(void) |
| { |
| /* This allocator does not keep unused memory for future allocations. */ |
| } |