Line data Source code
1 : #ifdef __PLUMED_HAS_ASMJIT 2 : // This file is part of AsmJit project <https://asmjit.com> 3 : // 4 : // See asmjit.h or LICENSE.md for license and copyright information 5 : // SPDX-License-Identifier: Zlib 6 : 7 : #ifndef ASMJIT_CORE_VIRTMEM_H_INCLUDED 8 : #define ASMJIT_CORE_VIRTMEM_H_INCLUDED 9 : 10 : #include "api-config.h" 11 : #ifndef ASMJIT_NO_JIT 12 : 13 : #include "globals.h" 14 : 15 : ASMJIT_BEGIN_NAMESPACE 16 : 17 : //! \addtogroup asmjit_virtual_memory 18 : //! \{ 19 : 20 : //! Virtual memory management. 21 : namespace VirtMem { 22 : 23 : //! Flushes instruction cache in the given region. 24 : //! 25 : //! Only useful on non-x86 architectures, however, it's a good practice to call it on any platform to make your 26 : //! code more portable. 27 : ASMJIT_API void flushInstructionCache(void* p, size_t size) noexcept; 28 : 29 : //! Virtual memory information. 30 : struct Info { 31 : //! Virtual memory page size. 32 : uint32_t pageSize; 33 : //! Virtual memory page granularity. 34 : uint32_t pageGranularity; 35 : }; 36 : 37 : //! Returns virtual memory information, see `VirtMem::Info` for more details. 38 : ASMJIT_API Info info() noexcept; 39 : 40 : //! Virtual memory access and mmap-specific flags. 41 : enum class MemoryFlags : uint32_t { 42 : //! No flags. 43 : kNone = 0, 44 : 45 : //! Memory is readable. 46 : kAccessRead = 0x00000001u, 47 : 48 : //! Memory is writable. 49 : kAccessWrite = 0x00000002u, 50 : 51 : //! Memory is executable. 52 : kAccessExecute = 0x00000004u, 53 : 54 : //! A combination of \ref MemoryFlags::kAccessRead and \ref MemoryFlags::kAccessWrite. 55 : kAccessReadWrite = kAccessRead | kAccessWrite, 56 : 57 : //! A combination of \ref MemoryFlags::kAccessRead, \ref MemoryFlags::kAccessWrite. 58 : kAccessRW = kAccessRead | kAccessWrite, 59 : 60 : //! A combination of \ref MemoryFlags::kAccessRead and \ref MemoryFlags::kAccessExecute. 61 : kAccessRX = kAccessRead | kAccessExecute, 62 : 63 : //! A combination of \ref MemoryFlags::kAccessRead, \ref MemoryFlags::kAccessWrite, and 64 : //! \ref MemoryFlags::kAccessExecute. 65 : kAccessRWX = kAccessRead | kAccessWrite | kAccessExecute, 66 : 67 : //! Use a `MAP_JIT` flag available on Apple platforms (introduced by Mojave), which allows JIT code to be executed 68 : //! in MAC bundles. This flag is not turned on by default, because when a process uses `fork()` the child process 69 : //! has no access to the pages mapped with `MAP_JIT`, which could break code that doesn't expect this behavior. 70 : //! 71 : //! \note This flag can only be used with \ref VirtMem::alloc(). 72 : kMMapEnableMapJit = 0x00000010u, 73 : 74 : //! Pass `PROT_MAX(PROT_READ)` to mmap() on platforms that support `PROT_MAX`. 75 : //! 76 : //! \note This flag can only be used with \ref VirtMem::alloc(). 77 : kMMapMaxAccessRead = 0x00000020u, 78 : //! Pass `PROT_MAX(PROT_WRITE)` to mmap() on platforms that support `PROT_MAX`. 79 : //! 80 : //! \note This flag can only be used with \ref VirtMem::alloc(). 81 : kMMapMaxAccessWrite = 0x00000040u, 82 : //! Pass `PROT_MAX(PROT_EXEC)` to mmap() on platforms that support `PROT_MAX`. 83 : //! 84 : //! \note This flag can only be used with \ref VirtMem::alloc(). 85 : kMMapMaxAccessExecute = 0x00000080u, 86 : 87 : //! A combination of \ref MemoryFlags::kMMapMaxAccessRead and \ref MemoryFlags::kMMapMaxAccessWrite. 88 : kMMapMaxAccessReadWrite = kMMapMaxAccessRead | kMMapMaxAccessWrite, 89 : 90 : //! A combination of \ref MemoryFlags::kMMapMaxAccessRead and \ref MemoryFlags::kMMapMaxAccessWrite. 91 : kMMapMaxAccessRW = kMMapMaxAccessRead | kMMapMaxAccessWrite, 92 : 93 : //! A combination of \ref MemoryFlags::kMMapMaxAccessRead and \ref MemoryFlags::kMMapMaxAccessExecute. 94 : kMMapMaxAccessRX = kMMapMaxAccessRead | kMMapMaxAccessExecute, 95 : 96 : //! A combination of \ref MemoryFlags::kMMapMaxAccessRead, \ref MemoryFlags::kMMapMaxAccessWrite, \ref 97 : //! MemoryFlags::kMMapMaxAccessExecute. 98 : kMMapMaxAccessRWX = kMMapMaxAccessRead | kMMapMaxAccessWrite | kMMapMaxAccessExecute, 99 : 100 : //! Not an access flag, only used by `allocDualMapping()` to override the default allocation strategy to always use 101 : //! a 'tmp' directory instead of "/dev/shm" (on POSIX platforms). Please note that this flag will be ignored if the 102 : //! operating system allows to allocate an executable memory by a different API than `open()` or `shm_open()`. For 103 : //! example on Linux `memfd_create()` is preferred and on BSDs `shm_open(SHM_ANON, ...)` is used if SHM_ANON is 104 : //! defined. 105 : //! 106 : //! \note This flag can only be used with \ref VirtMem::alloc(). 107 : kMappingPreferTmp = 0x80000000u 108 : }; 109 0 : ASMJIT_DEFINE_ENUM_FLAGS(MemoryFlags) 110 : 111 : //! Allocates virtual memory by either using `mmap()` (POSIX) or `VirtualAlloc()` (Windows). 112 : //! 113 : //! \note `size` should be aligned to page size, use \ref VirtMem::info() to obtain it. Invalid size will not be 114 : //! corrected by the implementation and the allocation would not succeed in such case. 115 : ASMJIT_API Error alloc(void** p, size_t size, MemoryFlags flags) noexcept; 116 : 117 : //! Releases virtual memory previously allocated by \ref VirtMem::alloc(). 118 : //! 119 : //! \note The size must be the same as used by \ref VirtMem::alloc(). If the size is not the same value the call 120 : //! will fail on any POSIX system, but pass on Windows, because it's implemented differently. 121 : ASMJIT_API Error release(void* p, size_t size) noexcept; 122 : 123 : //! A cross-platform wrapper around `mprotect()` (POSIX) and `VirtualProtect()` (Windows). 124 : ASMJIT_API Error protect(void* p, size_t size, MemoryFlags flags) noexcept; 125 : 126 : //! Dual memory mapping used to map an anonymous memory into two memory regions where one region is read-only, but 127 : //! executable, and the second region is read+write, but not executable. See \ref VirtMem::allocDualMapping() for 128 : //! more details. 129 : struct DualMapping { 130 : //! Pointer to data with 'Read+Execute' access (this memory is not writable). 131 : void* rx; 132 : //! Pointer to data with 'Read+Write' access (this memory is not executable). 133 : void* rw; 134 : }; 135 : 136 : //! Allocates virtual memory and creates two views of it where the first view has no write access. This is an addition 137 : //! to the API that should be used in cases in which the operating system either enforces W^X security policy or the 138 : //! application wants to use this policy by default to improve security and prevent an accidental (or purposed) 139 : //! self-modifying code. 140 : //! 141 : //! The memory returned in the `dm` are two independent mappings of the same shared memory region. You must use 142 : //! \ref VirtMem::releaseDualMapping() to release it when it's no longer needed. Never use `VirtMem::release()` to 143 : //! release the memory returned by `allocDualMapping()` as that would fail on Windows. 144 : //! 145 : //! \remarks Both pointers in `dm` would be set to `nullptr` if the function fails. 146 : ASMJIT_API Error allocDualMapping(DualMapping* dm, size_t size, MemoryFlags flags) noexcept; 147 : 148 : //! Releases virtual memory mapping previously allocated by \ref VirtMem::allocDualMapping(). 149 : //! 150 : //! \remarks Both pointers in `dm` would be set to `nullptr` if the function succeeds. 151 : ASMJIT_API Error releaseDualMapping(DualMapping* dm, size_t size) noexcept; 152 : 153 : //! Hardened runtime flags. 154 : enum class HardenedRuntimeFlags : uint32_t { 155 : //! No flags. 156 : kNone = 0, 157 : 158 : //! Hardened runtime is enabled - it's not possible to have "Write & Execute" memory protection. The runtime 159 : //! enforces W^X (either write or execute). 160 : //! 161 : //! \note If the runtime is hardened it means that an operating system specific protection is used. For example on 162 : //! MacOS platform it's possible to allocate memory with MAP_JIT flag and then use `pthread_jit_write_protect_np()` 163 : //! to temporarily swap access permissions for the current thread. Dual mapping is also a possibility on X86/X64 164 : //! architecture. 165 : kEnabled = 0x00000001u, 166 : 167 : //! Read+Write+Execute can only be allocated with MAP_JIT flag (Apple specific). 168 : kMapJit = 0x00000002u 169 : }; 170 : ASMJIT_DEFINE_ENUM_FLAGS(HardenedRuntimeFlags) 171 : 172 : //! Hardened runtime information. 173 : struct HardenedRuntimeInfo { 174 : //! Hardened runtime flags. 175 : HardenedRuntimeFlags flags; 176 : }; 177 : 178 : //! Returns runtime features provided by the OS. 179 : ASMJIT_API HardenedRuntimeInfo hardenedRuntimeInfo() noexcept; 180 : 181 : //! Values that can be used with `protectJitMemory()` function. 182 : enum class ProtectJitAccess : uint32_t { 183 : //! Protect JIT memory with Read+Write permissions. 184 : kReadWrite = 0, 185 : //! Protect JIT memory with Read+Execute permissions. 186 : kReadExecute = 1 187 : }; 188 : 189 : //! Protects access of memory mapped with MAP_JIT flag for the current thread. 190 : //! 191 : //! \note This feature is only available on Apple hardware (AArch64) at the moment and and uses a non-portable 192 : //! `pthread_jit_write_protect_np()` call when available. 193 : //! 194 : //! This function must be called before and after a memory mapped with MAP_JIT flag is modified. Example: 195 : //! 196 : //! ``` 197 : //! void* codePtr = ...; 198 : //! size_t codeSize = ...; 199 : //! 200 : //! VirtMem::protectJitMemory(VirtMem::ProtectJitAccess::kReadWrite); 201 : //! memcpy(codePtr, source, codeSize); 202 : //! VirtMem::protectJitMemory(VirtMem::ProtectJitAccess::kReadExecute); 203 : //! VirtMem::flushInstructionCache(codePtr, codeSize); 204 : //! ``` 205 : //! 206 : //! See \ref ProtectJitReadWriteScope, which makes it simpler than the code above. 207 : ASMJIT_API void protectJitMemory(ProtectJitAccess access) noexcept; 208 : 209 : //! JIT protection scope that prepares the given memory block to be written to in the current thread. 210 : //! 211 : //! It calls `VirtMem::protectJitMemory(VirtMem::ProtectJitAccess::kReadWrite)` at construction time and 212 : //! `VirtMem::protectJitMemory(VirtMem::ProtectJitAccess::kReadExecute)` combined with `flushInstructionCache()` 213 : //! in destructor. The purpose of this class is to make writing to JIT memory easier. 214 : class ProtectJitReadWriteScope { 215 : public: 216 : void* _rxPtr; 217 : size_t _size; 218 : 219 : //! Makes the given memory block RW protected. 220 : ASMJIT_FORCE_INLINE ProtectJitReadWriteScope(void* rxPtr, size_t size) noexcept 221 : : _rxPtr(rxPtr), 222 : _size(size) { 223 27492 : protectJitMemory(ProtectJitAccess::kReadWrite); 224 : } 225 : 226 : // Not copyable. 227 : ProtectJitReadWriteScope(const ProtectJitReadWriteScope& other) = delete; 228 : 229 : //! Makes the memory block RX protected again and flushes instruction cache. 230 : ASMJIT_FORCE_INLINE ~ProtectJitReadWriteScope() noexcept { 231 27492 : protectJitMemory(ProtectJitAccess::kReadExecute); 232 27492 : flushInstructionCache(_rxPtr, _size); 233 : } 234 : }; 235 : 236 : } // VirtMem 237 : 238 : //! \} 239 : 240 : ASMJIT_END_NAMESPACE 241 : 242 : #endif 243 : #endif // ASMJIT_CORE_VIRTMEM_H_INCLUDED 244 : 245 : #endif // __PLUMED_HAS_ASMJIT