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 }