1 /** 2 Copyright: Copyright (c) 2017-2019 Andrey Penechko. 3 License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). 4 Authors: Andrey Penechko. 5 */ 6 module vox.utils.mem; 7 8 import vox.utils : PAGE_SIZE, format; 9 10 enum MemType 11 { 12 RW, 13 RWX 14 } 15 16 ubyte[] alloc_executable_memory(size_t bytes) 17 { 18 return allocate(bytes, cast(void*)0x4000_0000UL, MemType.RWX); 19 } 20 21 void free_executable_memory(ubyte[] bytes) 22 { 23 deallocate(bytes); 24 } 25 26 version(Posix) 27 { 28 import core.stdc.errno : errno; 29 ubyte[] allocate(size_t bytes, void* location, bool is_executable) 30 { 31 import core.sys.posix.sys.mman : mmap, MAP_ANON, PROT_READ, 32 PROT_WRITE, PROT_EXEC, MAP_PRIVATE, MAP_FAILED; 33 if (!bytes) return null; 34 35 int protection = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0); 36 37 auto p = mmap(location, bytes, protection, MAP_PRIVATE | MAP_ANON, -1, 0); 38 if (p is MAP_FAILED) return null; 39 return cast(ubyte[])p[0 .. bytes]; 40 } 41 42 void deallocate(ubyte[] b) 43 { 44 import core.sys.posix.sys.mman : munmap; 45 if (b.ptr is null) return; 46 int res = munmap(b.ptr, b.length); 47 assert(res == 0, format("munmap(%X, %s) failed, %s", b.ptr, b.length, errno)); 48 } 49 50 void markAsRW(void* addr, size_t numPages) 51 { 52 import core.sys.posix.sys.mman : mprotect, PROT_READ, PROT_WRITE; 53 if (numPages == 0) return; 54 int res = mprotect(addr, numPages*PAGE_SIZE, PROT_READ | PROT_WRITE); 55 assert(res == 0, format("mprotect(%X, %s, PROT_READ | PROT_WRITE) failed, %s", addr, numPages*PAGE_SIZE, errno)); 56 } 57 58 void markAsRO(void* addr, size_t numPages) 59 { 60 import core.sys.posix.sys.mman : mprotect, PROT_READ; 61 if (numPages == 0) return; 62 int res = mprotect(addr, numPages*PAGE_SIZE, PROT_READ); 63 assert(res == 0, format("mprotect(%X, %s, PROT_READ) failed, %s", addr, numPages*PAGE_SIZE, errno)); 64 } 65 66 void markAsExecutable(void* addr, size_t numPages) 67 { 68 import core.sys.posix.sys.mman : mprotect, PROT_READ, PROT_EXEC; 69 if (numPages == 0) return; 70 int res = mprotect(addr, numPages*PAGE_SIZE, PROT_READ | PROT_EXEC); 71 assert(res == 0, format("mprotect(%X, %s, PROT_EXEC) failed, %s", addr, numPages*PAGE_SIZE, errno)); 72 } 73 } 74 else version(Windows) 75 { 76 import vox.utils.windows : 77 FlushInstructionCache, GetLastError, GetCurrentProcess, 78 VirtualAlloc, VirtualFree, VirtualProtect, 79 MEM_COMMIT, PAGE_READWRITE, PAGE_READONLY, MEM_RELEASE, PAGE_EXECUTE_READWRITE, MEM_RESERVE, PAGE_EXECUTE; 80 81 ubyte[] allocate(size_t bytes, void* location, MemType memoryType) 82 { 83 if (!bytes) return null; 84 85 int protection; 86 87 final switch(memoryType) 88 { 89 case MemType.RW: protection = PAGE_READWRITE; break; 90 case MemType.RWX: protection = PAGE_EXECUTE_READWRITE; break; 91 } 92 93 auto p = VirtualAlloc(location, bytes, MEM_COMMIT | MEM_RESERVE, protection); 94 95 if (p == null) 96 { 97 import std.stdio; 98 import std.windows.syserror; 99 int errCode = GetLastError(); 100 writefln("allocate(%s:bytes, %s:location, %s:memoryType", bytes, location, memoryType); 101 writeln(sysErrorString(errCode)); 102 assert(false, "VirtualAlloc alloc failed"); 103 } 104 105 return cast(ubyte[])p[0 .. bytes]; 106 } 107 108 void deallocate(ubyte[] b) 109 { 110 if (b.ptr is null) return; 111 VirtualFree(b.ptr, 0, MEM_RELEASE); 112 } 113 114 void markAsRW(void* addr, size_t numPages) 115 { 116 if (numPages == 0) return; 117 uint val; 118 VirtualProtect(addr, numPages*PAGE_SIZE, PAGE_READWRITE, &val); 119 } 120 121 void markAsRO(void* addr, size_t numPages) 122 { 123 if (numPages == 0) return; 124 uint val; 125 VirtualProtect(addr, numPages*PAGE_SIZE, PAGE_READONLY, &val); 126 } 127 128 void markAsExecutable(void* addr, size_t numPages) 129 { 130 if (numPages == 0) return; 131 uint val; 132 int res = VirtualProtect(addr, numPages*PAGE_SIZE, PAGE_EXECUTE, &val); 133 assert(res != 0, format("VirtualProtect(%X, %s, PAGE_EXECUTE, %s) failed", addr, numPages*PAGE_SIZE, val)); 134 FlushInstructionCache(GetCurrentProcess(), addr, numPages*PAGE_SIZE); 135 } 136 137 void testAdresses() 138 { 139 import std.stdio; 140 import std.windows.syserror : sysErrorString; 141 import vox.utils.windows : VirtualAlloc, VirtualFree, GetLastError, MEM_COMMIT, MEM_RESERVE, PAGE_READWRITE; 142 size_t successful; 143 size_t failed; 144 size_t bytes = PAGE_SIZE * 1024; 145 foreach(ulong loc; 0..16 * 16) 146 { 147 void* location = cast(void*)(loc*64*1024*1024); 148 auto p = VirtualAlloc(location, bytes, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); 149 150 if (p == null) 151 { 152 int errCode = GetLastError(); 153 writefln("Fail loc %s err '%s'", location, sysErrorString(errCode)); 154 ++failed; 155 } 156 else 157 { 158 ++successful; 159 VirtualFree(p, 0, MEM_RELEASE); 160 writefln("Success loc %s ptr %s", location, p); 161 } 162 } 163 164 writefln("s %s", successful); 165 writefln("f %s", failed); 166 } 167 }