// [AsmJit] // Complete x86/x64 JIT and Remote Assembler for C++. // // [License] // Zlib - See LICENSE.md file in the package. // [Export] #define ASMJIT_EXPORTS // [Dependencies] #include "../base/osutils.h" #include "../base/utils.h" #if ASMJIT_OS_POSIX # include # include # include # include #endif // ASMJIT_OS_POSIX #if ASMJIT_OS_MAC # include #endif // ASMJIT_OS_MAC #if ASMJIT_OS_WINDOWS # if defined(_MSC_VER) && _MSC_VER >= 1400 # include # else # define _InterlockedCompareExchange InterlockedCompareExchange # endif // _MSC_VER #endif // ASMJIT_OS_WINDOWS // [Api-Begin] #include "../asmjit_apibegin.h" namespace asmjit { // ============================================================================ // [asmjit::OSUtils - Virtual Memory] // ============================================================================ // Windows specific implementation using `VirtualAllocEx` and `VirtualFree`. #if ASMJIT_OS_WINDOWS static ASMJIT_NOINLINE const VMemInfo& OSUtils_GetVMemInfo() noexcept { static VMemInfo vmi; if (ASMJIT_UNLIKELY(!vmi.hCurrentProcess)) { SYSTEM_INFO info; ::GetSystemInfo(&info); vmi.pageSize = Utils::alignToPowerOf2(info.dwPageSize); vmi.pageGranularity = info.dwAllocationGranularity; vmi.hCurrentProcess = ::GetCurrentProcess(); } return vmi; }; VMemInfo OSUtils::getVirtualMemoryInfo() noexcept { return OSUtils_GetVMemInfo(); } void* OSUtils::allocVirtualMemory(size_t size, size_t* allocated, uint32_t flags) noexcept { return allocProcessMemory(static_cast(0), size, allocated, flags); } Error OSUtils::releaseVirtualMemory(void* p, size_t size) noexcept { return releaseProcessMemory(static_cast(0), p, size); } void* OSUtils::allocProcessMemory(HANDLE hProcess, size_t size, size_t* allocated, uint32_t flags) noexcept { if (size == 0) return nullptr; const VMemInfo& vmi = OSUtils_GetVMemInfo(); if (!hProcess) hProcess = vmi.hCurrentProcess; // VirtualAllocEx rounds the allocated size to a page size automatically, // but we need the `alignedSize` so we can store the real allocated size // into `allocated` output. size_t alignedSize = Utils::alignTo(size, vmi.pageSize); // Windows XP SP2 / Vista+ allow data-execution-prevention (DEP). DWORD protectFlags = 0; if (flags & kVMExecutable) protectFlags |= (flags & kVMWritable) ? PAGE_EXECUTE_READWRITE : PAGE_EXECUTE_READ; else protectFlags |= (flags & kVMWritable) ? PAGE_READWRITE : PAGE_READONLY; LPVOID mBase = ::VirtualAllocEx(hProcess, nullptr, alignedSize, MEM_COMMIT | MEM_RESERVE, protectFlags); if (ASMJIT_UNLIKELY(!mBase)) return nullptr; ASMJIT_ASSERT(Utils::isAligned(reinterpret_cast(mBase), vmi.pageSize)); if (allocated) *allocated = alignedSize; return mBase; } Error OSUtils::releaseProcessMemory(HANDLE hProcess, void* p, size_t size) noexcept { const VMemInfo& vmi = OSUtils_GetVMemInfo(); if (!hProcess) hProcess = vmi.hCurrentProcess; if (ASMJIT_UNLIKELY(!::VirtualFreeEx(hProcess, p, 0, MEM_RELEASE))) return DebugUtils::errored(kErrorInvalidState); return kErrorOk; } #endif // ASMJIT_OS_WINDOWS // Posix specific implementation using `mmap()` and `munmap()`. #if ASMJIT_OS_POSIX // Mac uses MAP_ANON instead of MAP_ANONYMOUS. #if !defined(MAP_ANONYMOUS) # define MAP_ANONYMOUS MAP_ANON #endif // MAP_ANONYMOUS static const VMemInfo& OSUtils_GetVMemInfo() noexcept { static VMemInfo vmi; if (ASMJIT_UNLIKELY(!vmi.pageSize)) { size_t pageSize = ::getpagesize(); vmi.pageSize = pageSize; vmi.pageGranularity = std::max(pageSize, 65536); } return vmi; }; VMemInfo OSUtils::getVirtualMemoryInfo() noexcept { return OSUtils_GetVMemInfo(); } void* OSUtils::allocVirtualMemory(size_t size, size_t* allocated, uint32_t flags) noexcept { const VMemInfo& vmi = OSUtils_GetVMemInfo(); size_t alignedSize = Utils::alignTo(size, vmi.pageSize); int protection = PROT_READ; if (flags & kVMWritable ) protection |= PROT_WRITE; if (flags & kVMExecutable) protection |= PROT_EXEC; void* mbase = ::mmap(nullptr, alignedSize, protection, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (ASMJIT_UNLIKELY(mbase == MAP_FAILED)) return nullptr; if (allocated) *allocated = alignedSize; return mbase; } Error OSUtils::releaseVirtualMemory(void* p, size_t size) noexcept { if (ASMJIT_UNLIKELY(::munmap(p, size) != 0)) return DebugUtils::errored(kErrorInvalidState); return kErrorOk; } #endif // ASMJIT_OS_POSIX // ============================================================================ // [asmjit::OSUtils - GetTickCount] // ============================================================================ #if ASMJIT_OS_WINDOWS static ASMJIT_INLINE uint32_t OSUtils_calcHiRes(const LARGE_INTEGER& now, double freq) noexcept { return static_cast( (int64_t)(double(now.QuadPart) / freq) & 0xFFFFFFFF); } uint32_t OSUtils::getTickCount() noexcept { static volatile uint32_t _hiResTicks; static volatile double _hiResFreq; do { uint32_t hiResOk = _hiResTicks; LARGE_INTEGER qpf, now; // If for whatever reason this fails, bail to `GetTickCount()`. if (!::QueryPerformanceCounter(&now)) break; // Expected - if we ran through this at least once `hiResTicks` will be // either 1 or 0xFFFFFFFF. If it's '1' then the Hi-Res counter is available // and `QueryPerformanceCounter()` can be used. if (hiResOk == 1) return OSUtils_calcHiRes(now, _hiResFreq); // Hi-Res counter is not available, bail to `GetTickCount()`. if (hiResOk != 0) break; // Detect availability of Hi-Res counter, if not available, bail to `GetTickCount()`. if (!::QueryPerformanceFrequency(&qpf)) { _InterlockedCompareExchange((LONG*)&_hiResTicks, 0xFFFFFFFF, 0); break; } double freq = double(qpf.QuadPart) / 1000.0; _hiResFreq = freq; _InterlockedCompareExchange((LONG*)&_hiResTicks, 1, 0); return OSUtils_calcHiRes(now, freq); } while (0); return ::GetTickCount(); } #elif ASMJIT_OS_MAC uint32_t OSUtils::getTickCount() noexcept { static mach_timebase_info_data_t _machTime; // See Apple's QA1398. if (ASMJIT_UNLIKELY(_machTime.denom == 0) || mach_timebase_info(&_machTime) != KERN_SUCCESS) return 0; // `mach_absolute_time()` returns nanoseconds, we want milliseconds. uint64_t t = mach_absolute_time() / 1000000; t = t * _machTime.numer / _machTime.denom; return static_cast(t & 0xFFFFFFFFU); } #elif defined(_POSIX_MONOTONIC_CLOCK) && _POSIX_MONOTONIC_CLOCK >= 0 uint32_t OSUtils::getTickCount() noexcept { struct timespec ts; if (ASMJIT_UNLIKELY(clock_gettime(CLOCK_MONOTONIC, &ts) != 0)) return 0; uint64_t t = (uint64_t(ts.tv_sec ) * 1000) + (uint64_t(ts.tv_nsec) / 1000000); return static_cast(t & 0xFFFFFFFFU); } #else #error "[asmjit] OSUtils::getTickCount() is not implemented for your target OS." uint32_t OSUtils::getTickCount() noexcept { return 0; } #endif } // asmjit namespace // [Api-End] #include "../asmjit_apiend.h"