aboutsummaryrefslogtreecommitdiff
path: root/client/asmjit/core/virtmem.cpp
diff options
context:
space:
mode:
authorauth12 <[email protected]>2020-07-21 13:07:42 -0700
committerauth12 <[email protected]>2020-07-21 13:07:42 -0700
commitf09669dd5846d95b063712571ccb7519910a0d6e (patch)
tree902f5ad201651f2d96ccf619e90b76cfa06a7b9b /client/asmjit/core/virtmem.cpp
parentSyscalls. (diff)
downloadloader-f09669dd5846d95b063712571ccb7519910a0d6e.tar.xz
loader-f09669dd5846d95b063712571ccb7519910a0d6e.zip
Added game selection.
Started process wrapper. Removed asmjit.
Diffstat (limited to 'client/asmjit/core/virtmem.cpp')
-rw-r--r--client/asmjit/core/virtmem.cpp589
1 files changed, 0 insertions, 589 deletions
diff --git a/client/asmjit/core/virtmem.cpp b/client/asmjit/core/virtmem.cpp
deleted file mode 100644
index 0606748..0000000
--- a/client/asmjit/core/virtmem.cpp
+++ /dev/null
@@ -1,589 +0,0 @@
-// AsmJit - Machine code generation for C++
-//
-// * Official AsmJit Home Page: https://asmjit.com
-// * Official Github Repository: https://github.com/asmjit/asmjit
-//
-// Copyright (c) 2008-2020 The AsmJit Authors
-//
-// This software is provided 'as-is', without any express or implied
-// warranty. In no event will the authors be held liable for any damages
-// arising from the use of this software.
-//
-// Permission is granted to anyone to use this software for any purpose,
-// including commercial applications, and to alter it and redistribute it
-// freely, subject to the following restrictions:
-//
-// 1. The origin of this software must not be misrepresented; you must not
-// claim that you wrote the original software. If you use this software
-// in a product, an acknowledgment in the product documentation would be
-// appreciated but is not required.
-// 2. Altered source versions must be plainly marked as such, and must not be
-// misrepresented as being the original software.
-// 3. This notice may not be removed or altered from any source distribution.
-
-#include "../core/api-build_p.h"
-#ifndef ASMJIT_NO_JIT
-
-#include "../core/osutils.h"
-#include "../core/string.h"
-#include "../core/support.h"
-#include "../core/virtmem.h"
-
-#if !defined(_WIN32)
- #include <errno.h>
- #include <fcntl.h>
- #include <sys/mman.h>
- #include <sys/stat.h>
- #include <sys/types.h>
- #include <unistd.h>
-
- // Linux has a `memfd_create` syscall that we would like to use, if available.
- #if defined(__linux__)
- #include <sys/syscall.h>
- #endif
-
- // Apple recently introduced MAP_JIT flag, which we want to use.
- #if defined(__APPLE__)
- #include <TargetConditionals.h>
- #if TARGET_OS_OSX
- #include <sys/utsname.h>
- #endif
- // Older SDK doesn't define `MAP_JIT`.
- #ifndef MAP_JIT
- #define MAP_JIT 0x800
- #endif
- #endif
-
- // BSD/OSX: `MAP_ANONYMOUS` is not defined, `MAP_ANON` is.
- #if !defined(MAP_ANONYMOUS)
- #define MAP_ANONYMOUS MAP_ANON
- #endif
-#endif
-
-#include <atomic>
-
-#if defined(__APPLE__)
- #define ASMJIT_VM_SHM_DETECT 0
-#else
- #define ASMJIT_VM_SHM_DETECT 1
-#endif
-
-ASMJIT_BEGIN_NAMESPACE
-
-// ============================================================================
-// [asmjit::VirtMem - Utilities]
-// ============================================================================
-
-static const uint32_t VirtMem_dualMappingFilter[2] = {
- VirtMem::kAccessWrite,
- VirtMem::kAccessExecute
-};
-
-// ============================================================================
-// [asmjit::VirtMem - Virtual Memory [Windows]]
-// ============================================================================
-
-#if defined(_WIN32)
-struct ScopedHandle {
- inline ScopedHandle() noexcept
- : value(nullptr) {}
-
- inline ~ScopedHandle() noexcept {
- if (value != nullptr)
- ::CloseHandle(value);
- }
-
- HANDLE value;
-};
-
-static void VirtMem_getInfo(VirtMem::Info& vmInfo) noexcept {
- SYSTEM_INFO systemInfo;
-
- ::GetSystemInfo(&systemInfo);
- vmInfo.pageSize = Support::alignUpPowerOf2<uint32_t>(systemInfo.dwPageSize);
- vmInfo.pageGranularity = systemInfo.dwAllocationGranularity;
-}
-
-// Windows specific implementation that uses `VirtualAlloc` and `VirtualFree`.
-static DWORD VirtMem_accessToWinProtectFlags(uint32_t flags) noexcept {
- DWORD protectFlags;
-
- // READ|WRITE|EXECUTE.
- if (flags & VirtMem::kAccessExecute)
- protectFlags = (flags & VirtMem::kAccessWrite) ? PAGE_EXECUTE_READWRITE : PAGE_EXECUTE_READ;
- else if (flags & VirtMem::kAccessReadWrite)
- protectFlags = (flags & VirtMem::kAccessWrite) ? PAGE_READWRITE : PAGE_READONLY;
- else
- protectFlags = PAGE_NOACCESS;
-
- // Any other flags to consider?
- return protectFlags;
-}
-
-static DWORD VirtMem_accessToWinDesiredAccess(uint32_t flags) noexcept {
- DWORD access = (flags & VirtMem::kAccessWrite) ? FILE_MAP_WRITE : FILE_MAP_READ;
- if (flags & VirtMem::kAccessExecute)
- access |= FILE_MAP_EXECUTE;
- return access;
-}
-
-Error VirtMem::alloc(void** p, size_t size, uint32_t flags) noexcept {
- *p = nullptr;
- if (size == 0)
- return DebugUtils::errored(kErrorInvalidArgument);
-
- DWORD protectFlags = VirtMem_accessToWinProtectFlags(flags);
- void* result = ::VirtualAlloc(nullptr, size, MEM_COMMIT | MEM_RESERVE, protectFlags);
-
- if (!result)
- return DebugUtils::errored(kErrorOutOfMemory);
-
- *p = result;
- return kErrorOk;
-}
-
-Error VirtMem::release(void* p, size_t size) noexcept {
- DebugUtils::unused(size);
- if (ASMJIT_UNLIKELY(!::VirtualFree(p, 0, MEM_RELEASE)))
- return DebugUtils::errored(kErrorInvalidArgument);
- return kErrorOk;
-}
-
-Error VirtMem::protect(void* p, size_t size, uint32_t flags) noexcept {
- DWORD protectFlags = VirtMem_accessToWinProtectFlags(flags);
- DWORD oldFlags;
-
- if (::VirtualProtect(p, size, protectFlags, &oldFlags))
- return kErrorOk;
-
- return DebugUtils::errored(kErrorInvalidArgument);
-}
-
-Error VirtMem::allocDualMapping(DualMapping* dm, size_t size, uint32_t flags) noexcept {
- dm->ro = nullptr;
- dm->rw = nullptr;
-
- if (size == 0)
- return DebugUtils::errored(kErrorInvalidArgument);
-
- ScopedHandle handle;
- handle.value = ::CreateFileMappingW(
- INVALID_HANDLE_VALUE,
- nullptr,
- PAGE_EXECUTE_READWRITE,
- (DWORD)(uint64_t(size) >> 32),
- (DWORD)(size & 0xFFFFFFFFu),
- nullptr);
-
- if (ASMJIT_UNLIKELY(!handle.value))
- return DebugUtils::errored(kErrorOutOfMemory);
-
- void* ptr[2];
- for (uint32_t i = 0; i < 2; i++) {
- DWORD desiredAccess = VirtMem_accessToWinDesiredAccess(flags & ~VirtMem_dualMappingFilter[i]);
- ptr[i] = ::MapViewOfFile(handle.value, desiredAccess, 0, 0, size);
-
- if (ptr[i] == nullptr) {
- if (i == 0)
- ::UnmapViewOfFile(ptr[0]);
- return DebugUtils::errored(kErrorOutOfMemory);
- }
- }
-
- dm->ro = ptr[0];
- dm->rw = ptr[1];
- return kErrorOk;
-}
-
-Error VirtMem::releaseDualMapping(DualMapping* dm, size_t size) noexcept {
- DebugUtils::unused(size);
- bool failed = false;
-
- if (!::UnmapViewOfFile(dm->ro))
- failed = true;
-
- if (dm->ro != dm->rw && !UnmapViewOfFile(dm->rw))
- failed = true;
-
- if (failed)
- return DebugUtils::errored(kErrorInvalidArgument);
-
- dm->ro = nullptr;
- dm->rw = nullptr;
- return kErrorOk;
-}
-#endif
-
-// ============================================================================
-// [asmjit::VirtMem - Virtual Memory [Posix]]
-// ============================================================================
-
-#if !defined(_WIN32)
-struct ScopedFD {
- inline ScopedFD() noexcept
- : value(-1) {}
-
- inline ~ScopedFD() noexcept {
- if (value != -1)
- close(value);
- }
-
- int value;
-};
-
-static void VirtMem_getInfo(VirtMem::Info& vmInfo) noexcept {
- uint32_t pageSize = uint32_t(::getpagesize());
-
- vmInfo.pageSize = pageSize;
- vmInfo.pageGranularity = Support::max<uint32_t>(pageSize, 65536);
-}
-
-// Some operating systems don't allow /dev/shm to be executable. On Linux this
-// happens when /dev/shm is mounted with 'noexec', which is enforced by systemd.
-// Other operating systems like OSX also restrict executable permissions regarding
-// /dev/shm, so we use a runtime detection before trying to allocate the requested
-// memory by the user. Sometimes we don't need the detection as we know it would
-// always result in 'kShmStrategyTmpDir'.
-enum ShmStrategy : uint32_t {
- kShmStrategyUnknown = 0,
- kShmStrategyDevShm = 1,
- kShmStrategyTmpDir = 2
-};
-
-// Posix specific implementation that uses `mmap()` and `munmap()`.
-static int VirtMem_accessToPosixProtection(uint32_t flags) noexcept {
- int protection = 0;
- if (flags & VirtMem::kAccessRead ) protection |= PROT_READ;
- if (flags & VirtMem::kAccessWrite ) protection |= PROT_READ | PROT_WRITE;
- if (flags & VirtMem::kAccessExecute) protection |= PROT_READ | PROT_EXEC;
- return protection;
-}
-
-// Translates libc errors specific to VirtualMemory mapping to `asmjit::Error`.
-static Error VirtMem_makeErrorFromErrno(int e) noexcept {
- switch (e) {
- case EACCES:
- case EAGAIN:
- case ENODEV:
- case EPERM:
- return kErrorInvalidState;
-
- case EFBIG:
- case ENOMEM:
- case EOVERFLOW:
- return kErrorOutOfMemory;
-
- case EMFILE:
- case ENFILE:
- return kErrorTooManyHandles;
-
- default:
- return kErrorInvalidArgument;
- }
-}
-
-#if defined(__APPLE__)
-// Detects whether the current process is hardened, which means that pages that
-// have WRITE and EXECUTABLE flags cannot be allocated without MAP_JIT flag.
-static ASMJIT_INLINE bool VirtMem_isHardened() noexcept {
- static volatile uint32_t globalHardenedFlag;
-
- enum HardenedFlag : uint32_t {
- kHardenedFlagUnknown = 0,
- kHardenedFlagDisabled = 1,
- kHardenedFlagEnabled = 2
- };
-
- uint32_t flag = globalHardenedFlag;
- if (flag == kHardenedFlagUnknown) {
- VirtMem::Info memInfo;
- VirtMem_getInfo(memInfo);
-
- void* ptr = mmap(nullptr, memInfo.pageSize, PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
- if (ptr == MAP_FAILED) {
- flag = kHardenedFlagEnabled;
- }
- else {
- flag = kHardenedFlagDisabled;
- munmap(ptr, memInfo.pageSize);
- }
- globalHardenedFlag = flag;
- }
-
- return flag == kHardenedFlagEnabled;
-}
-
-// MAP_JIT flag required to run unsigned JIT code is only supported by kernel
-// version 10.14+ (Mojave) and IOS.
-static ASMJIT_INLINE bool VirtMem_hasMapJitSupport() noexcept {
-#if TARGET_OS_OSX
- static volatile int globalVersion;
-
- int ver = globalVersion;
- if (!ver) {
- struct utsname osname;
- uname(&osname);
- ver = atoi(osname.release);
- globalVersion = ver;
- }
- return ver >= 18;
-#else
- // Assume it's available.
- return true;
-#endif
-}
-
-static ASMJIT_INLINE int VirtMem_appleSpecificMMapFlags(uint32_t flags) {
- // Always use MAP_JIT flag if user asked for it (could be used for testing
- // on non-hardened processes) and detect whether it must be used when the
- // process is actually hardened (in that case it doesn't make sense to rely
- // on user `flags`).
- bool useMapJit = ((flags & VirtMem::kMMapEnableMapJit) != 0) || VirtMem_isHardened();
- if (useMapJit)
- return VirtMem_hasMapJitSupport() ? int(MAP_JIT) : 0;
- else
- return 0;
-}
-#else
-static ASMJIT_INLINE int VirtMem_appleSpecificMMapFlags(uint32_t flags) {
- DebugUtils::unused(flags);
- return 0;
-}
-#endif
-
-static const char* VirtMem_getTmpDir() noexcept {
- const char* tmpDir = getenv("TMPDIR");
- return tmpDir ? tmpDir : "/tmp";
-}
-
-static Error VirtMem_openAnonymousMemory(int* fd, bool preferTmpOverDevShm) noexcept {
-#if defined(SYS_memfd_create)
- // Linux specific 'memfd_create' - if the syscall returns `ENOSYS` it means
- // it's not available and we will never call it again (would be pointless).
-
- // Zero initialized, if ever changed to '1' that would mean the syscall is not
- // available and we must use `shm_open()` and `shm_unlink()`.
- static volatile uint32_t memfd_create_not_supported;
-
- if (!memfd_create_not_supported) {
- *fd = (int)syscall(SYS_memfd_create, "vmem", 0);
- if (ASMJIT_LIKELY(*fd >= 0))
- return kErrorOk;
-
- int e = errno;
- if (e == ENOSYS)
- memfd_create_not_supported = 1;
- else
- return DebugUtils::errored(VirtMem_makeErrorFromErrno(e));
- }
-#endif
-
-#if defined(SHM_ANON)
- // Originally FreeBSD extension, apparently works in other BSDs too.
- DebugUtils::unused(preferTmpOverDevShm);
- *fd = shm_open(SHM_ANON, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
-
- if (ASMJIT_LIKELY(*fd >= 0))
- return kErrorOk;
- else
- return DebugUtils::errored(VirtMem_makeErrorFromErrno(errno));
-#else
- // POSIX API. We have to generate somehow a unique name. This is nothing
- // cryptographic, just using a bit from the stack address to always have
- // a different base for different threads (as threads have their own stack)
- // and retries for avoiding collisions. We use `shm_open()` with flags that
- // require creation of the file so we never open an existing shared memory.
- static std::atomic<uint32_t> internalCounter;
-
- StringTmp<128> uniqueName;
- const char* kShmFormat = "/shm-id-%08llX";
-
- uint32_t kRetryCount = 100;
- uint64_t bits = ((uintptr_t)(void*)&uniqueName) & 0x55555555u;
-
- for (uint32_t i = 0; i < kRetryCount; i++) {
- bits -= uint64_t(OSUtils::getTickCount()) * 773703683;
- bits = ((bits >> 14) ^ (bits << 6)) + uint64_t(++internalCounter) * 10619863;
-
- if (!ASMJIT_VM_SHM_DETECT || preferTmpOverDevShm) {
- uniqueName.assign(VirtMem_getTmpDir());
- uniqueName.appendFormat(kShmFormat, (unsigned long long)bits);
- *fd = open(uniqueName.data(), O_RDWR | O_CREAT | O_EXCL, 0);
- if (ASMJIT_LIKELY(*fd >= 0)) {
- unlink(uniqueName.data());
- return kErrorOk;
- }
- }
- else {
- uniqueName.assignFormat(kShmFormat, (unsigned long long)bits);
- *fd = shm_open(uniqueName.data(), O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
- if (ASMJIT_LIKELY(*fd >= 0)) {
- shm_unlink(uniqueName.data());
- return kErrorOk;
- }
- }
-
- int e = errno;
- if (e == EEXIST)
- continue;
- else
- return DebugUtils::errored(VirtMem_makeErrorFromErrno(e));
- }
- return kErrorOk;
-#endif
-}
-
-#if ASMJIT_VM_SHM_DETECT
-static Error VirtMem_detectShmStrategy(uint32_t* strategyOut) noexcept {
- ScopedFD fd;
- VirtMem::Info vmInfo = VirtMem::info();
-
- ASMJIT_PROPAGATE(VirtMem_openAnonymousMemory(&fd.value, false));
- if (ftruncate(fd.value, off_t(vmInfo.pageSize)) != 0)
- return DebugUtils::errored(VirtMem_makeErrorFromErrno(errno));
-
- void* ptr = mmap(nullptr, vmInfo.pageSize, PROT_READ | PROT_EXEC, MAP_SHARED, fd.value, 0);
- if (ptr == MAP_FAILED) {
- int e = errno;
- if (e == EINVAL) {
- *strategyOut = kShmStrategyTmpDir;
- return kErrorOk;
- }
- return DebugUtils::errored(VirtMem_makeErrorFromErrno(e));
- }
- else {
- munmap(ptr, vmInfo.pageSize);
- *strategyOut = kShmStrategyDevShm;
- return kErrorOk;
- }
-}
-#endif
-
-#if ASMJIT_VM_SHM_DETECT
-static Error VirtMem_getShmStrategy(uint32_t* strategyOut) noexcept {
- // Initially don't assume anything. It has to be tested whether
- // '/dev/shm' was mounted with 'noexec' flag or not.
- static volatile uint32_t globalShmStrategy = kShmStrategyUnknown;
-
- uint32_t strategy = globalShmStrategy;
- if (strategy == kShmStrategyUnknown) {
- ASMJIT_PROPAGATE(VirtMem_detectShmStrategy(&strategy));
- globalShmStrategy = strategy;
- }
-
- *strategyOut = strategy;
- return kErrorOk;
-}
-#else
-static Error VirtMem_getShmStrategy(uint32_t* strategyOut) noexcept {
- *strategyOut = kShmStrategyTmpDir;
- return kErrorOk;
-}
-#endif
-
-Error VirtMem::alloc(void** p, size_t size, uint32_t flags) noexcept {
- *p = nullptr;
-
- if (size == 0)
- return DebugUtils::errored(kErrorInvalidArgument);
-
- int protection = VirtMem_accessToPosixProtection(flags);
- int mmFlags = MAP_PRIVATE | MAP_ANONYMOUS | VirtMem_appleSpecificMMapFlags(flags);
- void* ptr = mmap(nullptr, size, protection, mmFlags, -1, 0);
-
- if (ptr == MAP_FAILED)
- return DebugUtils::errored(kErrorOutOfMemory);
-
- *p = ptr;
- return kErrorOk;
-}
-
-Error VirtMem::release(void* p, size_t size) noexcept {
- if (ASMJIT_UNLIKELY(munmap(p, size) != 0))
- return DebugUtils::errored(kErrorInvalidArgument);
-
- return kErrorOk;
-}
-
-
-Error VirtMem::protect(void* p, size_t size, uint32_t flags) noexcept {
- int protection = VirtMem_accessToPosixProtection(flags);
- if (mprotect(p, size, protection) == 0)
- return kErrorOk;
-
- return DebugUtils::errored(kErrorInvalidArgument);
-}
-
-Error VirtMem::allocDualMapping(DualMapping* dm, size_t size, uint32_t flags) noexcept {
- dm->ro = nullptr;
- dm->rw = nullptr;
-
- if (off_t(size) <= 0)
- return DebugUtils::errored(size == 0 ? kErrorInvalidArgument : kErrorTooLarge);
-
- bool preferTmpOverDevShm = (flags & kMappingPreferTmp) != 0;
- if (!preferTmpOverDevShm) {
- uint32_t strategy;
- ASMJIT_PROPAGATE(VirtMem_getShmStrategy(&strategy));
- preferTmpOverDevShm = (strategy == kShmStrategyTmpDir);
- }
-
- // ScopedFD will automatically close the file descriptor in its destructor.
- ScopedFD fd;
- ASMJIT_PROPAGATE(VirtMem_openAnonymousMemory(&fd.value, preferTmpOverDevShm));
- if (ftruncate(fd.value, off_t(size)) != 0)
- return DebugUtils::errored(VirtMem_makeErrorFromErrno(errno));
-
- void* ptr[2];
- for (uint32_t i = 0; i < 2; i++) {
- ptr[i] = mmap(nullptr, size, VirtMem_accessToPosixProtection(flags & ~VirtMem_dualMappingFilter[i]), MAP_SHARED, fd.value, 0);
- if (ptr[i] == MAP_FAILED) {
- // Get the error now before `munmap` has a chance to clobber it.
- int e = errno;
- if (i == 1)
- munmap(ptr[0], size);
- return DebugUtils::errored(VirtMem_makeErrorFromErrno(e));
- }
- }
-
- dm->ro = ptr[0];
- dm->rw = ptr[1];
- return kErrorOk;
-}
-
-Error VirtMem::releaseDualMapping(DualMapping* dm, size_t size) noexcept {
- Error err = release(dm->ro, size);
- if (dm->ro != dm->rw)
- err |= release(dm->rw, size);
-
- if (err)
- return DebugUtils::errored(kErrorInvalidArgument);
-
- dm->ro = nullptr;
- dm->rw = nullptr;
- return kErrorOk;
-}
-#endif
-
-// ============================================================================
-// [asmjit::VirtMem - Virtual Memory [Memory Info]]
-// ============================================================================
-
-VirtMem::Info VirtMem::info() noexcept {
- static VirtMem::Info vmInfo;
- static std::atomic<uint32_t> vmInfoInitialized;
-
- if (!vmInfoInitialized.load()) {
- VirtMem::Info localMemInfo;
- VirtMem_getInfo(localMemInfo);
-
- vmInfo = localMemInfo;
- vmInfoInitialized.store(1u);
- }
-
- return vmInfo;
-}
-
-ASMJIT_END_NAMESPACE
-
-#endif