// Copyright 2016 The SwiftShader Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "ExecutableMemory.hpp" #include "Debug.hpp" #if defined(_WIN32) #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #include <windows.h> #include <intrin.h> #elif defined(__Fuchsia__) #include <unistd.h> #include <zircon/process.h> #include <zircon/syscalls.h> #else #include <errno.h> #include <sys/mman.h> #include <stdlib.h> #include <unistd.h> #endif #include <memory.h> #undef allocate #undef deallocate #if (defined(__i386__) || defined(_M_IX86) || defined(__x86_64__) || defined (_M_X64)) && !defined(__x86__) #define __x86__ #endif namespace rr { namespace { struct Allocation { // size_t bytes; unsigned char *block; }; void *allocateRaw(size_t bytes, size_t alignment) { ASSERT((alignment & (alignment - 1)) == 0); // Power of 2 alignment. #if defined(LINUX_ENABLE_NAMED_MMAP) void *allocation; int result = posix_memalign(&allocation, alignment, bytes); if(result != 0) { errno = result; allocation = nullptr; } return allocation; #else unsigned char *block = new unsigned char[bytes + sizeof(Allocation) + alignment]; unsigned char *aligned = nullptr; if(block) { aligned = (unsigned char*)((uintptr_t)(block + sizeof(Allocation) + alignment - 1) & -(intptr_t)alignment); Allocation *allocation = (Allocation*)(aligned - sizeof(Allocation)); // allocation->bytes = bytes; allocation->block = block; } return aligned; #endif } #if defined(LINUX_ENABLE_NAMED_MMAP) // Create a file descriptor for anonymous memory with the given // name. Returns -1 on failure. // TODO: remove once libc wrapper exists. int memfd_create(const char* name, unsigned int flags) { #if __aarch64__ #define __NR_memfd_create 279 #elif __arm__ #define __NR_memfd_create 279 #elif __powerpc64__ #define __NR_memfd_create 360 #elif __i386__ #define __NR_memfd_create 356 #elif __x86_64__ #define __NR_memfd_create 319 #endif /* __NR_memfd_create__ */ #ifdef __NR_memfd_create // In the event of no system call this returns -1 with errno set // as ENOSYS. return syscall(__NR_memfd_create, name, flags); #else return -1; #endif } // Returns a file descriptor for use with an anonymous mmap, if // memfd_create fails, -1 is returned. Note, the mappings should be // MAP_PRIVATE so that underlying pages aren't shared. int anonymousFd() { static int fd = memfd_create("SwiftShader JIT", 0); return fd; } // Ensure there is enough space in the "anonymous" fd for length. void ensureAnonFileSize(int anonFd, size_t length) { static size_t fileSize = 0; if(length > fileSize) { ftruncate(anonFd, length); fileSize = length; } } #endif // defined(LINUX_ENABLE_NAMED_MMAP) } // anonymous namespace size_t memoryPageSize() { static int pageSize = 0; if(pageSize == 0) { #if defined(_WIN32) SYSTEM_INFO systemInfo; GetSystemInfo(&systemInfo); pageSize = systemInfo.dwPageSize; #else pageSize = sysconf(_SC_PAGESIZE); #endif } return pageSize; } void *allocate(size_t bytes, size_t alignment) { void *memory = allocateRaw(bytes, alignment); if(memory) { memset(memory, 0, bytes); } return memory; } void deallocate(void *memory) { #if defined(LINUX_ENABLE_NAMED_MMAP) free(memory); #else if(memory) { unsigned char *aligned = (unsigned char*)memory; Allocation *allocation = (Allocation*)(aligned - sizeof(Allocation)); delete[] allocation->block; } #endif } // Rounds |x| up to a multiple of |m|, where |m| is a power of 2. inline uintptr_t roundUp(uintptr_t x, uintptr_t m) { ASSERT(m > 0 && (m & (m - 1)) == 0); // |m| must be a power of 2. return (x + m - 1) & ~(m - 1); } void *allocateExecutable(size_t bytes) { size_t pageSize = memoryPageSize(); size_t length = roundUp(bytes, pageSize); void *mapping; #if defined(LINUX_ENABLE_NAMED_MMAP) // Try to name the memory region for the executable code, // to aid profilers. int anonFd = anonymousFd(); if(anonFd == -1) { mapping = mmap(nullptr, length, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); } else { ensureAnonFileSize(anonFd, length); mapping = mmap(nullptr, length, PROT_READ | PROT_WRITE, MAP_PRIVATE, anonFd, 0); } if(mapping == MAP_FAILED) { mapping = nullptr; } #elif defined(__Fuchsia__) zx_handle_t vmo; if (zx_vmo_create(length, ZX_VMO_NON_RESIZABLE, &vmo) != ZX_OK) { return nullptr; } if (zx_vmo_replace_as_executable(vmo, ZX_HANDLE_INVALID, &vmo) != ZX_OK) { return nullptr; } zx_vaddr_t reservation; zx_status_t status = zx_vmar_map( zx_vmar_root_self(), ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, 0, vmo, 0, length, &reservation); zx_handle_close(vmo); if (status != ZX_OK) { return nullptr; } zx_vaddr_t alignedReservation = roundUp(reservation, pageSize); mapping = reinterpret_cast<void*>(alignedReservation); // Unmap extra memory reserved before the block. if (alignedReservation != reservation) { size_t prefix_size = alignedReservation - reservation; status = zx_vmar_unmap(zx_vmar_root_self(), reservation, prefix_size); ASSERT(status == ZX_OK); length -= prefix_size; } // Unmap extra memory at the end. if (length > bytes) { status = zx_vmar_unmap( zx_vmar_root_self(), alignedReservation + bytes, length - bytes); ASSERT(status == ZX_OK); } #else mapping = allocate(length, pageSize); #endif return mapping; } void markExecutable(void *memory, size_t bytes) { #if defined(_WIN32) unsigned long oldProtection; VirtualProtect(memory, bytes, PAGE_EXECUTE_READ, &oldProtection); #elif defined(__Fuchsia__) zx_status_t status = zx_vmar_protect( zx_vmar_root_self(), ZX_VM_PERM_READ | ZX_VM_PERM_EXECUTE, reinterpret_cast<zx_vaddr_t>(memory), bytes); ASSERT(status != ZX_OK); #else mprotect(memory, bytes, PROT_READ | PROT_EXEC); #endif } void deallocateExecutable(void *memory, size_t bytes) { #if defined(_WIN32) unsigned long oldProtection; VirtualProtect(memory, bytes, PAGE_READWRITE, &oldProtection); deallocate(memory); #elif defined(LINUX_ENABLE_NAMED_MMAP) size_t pageSize = memoryPageSize(); size_t length = (bytes + pageSize - 1) & ~(pageSize - 1); munmap(memory, length); #elif defined(__Fuchsia__) zx_vmar_unmap(zx_vmar_root_self(), reinterpret_cast<zx_vaddr_t>(memory), bytes); #else mprotect(memory, bytes, PROT_READ | PROT_WRITE); deallocate(memory); #endif } }