-
Notifications
You must be signed in to change notification settings - Fork 0
/
dasm_mm.lua
145 lines (115 loc) · 3.97 KB
/
dasm_mm.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
--wrappers around mmap to support dynamic code exection.
--Written by Cosmin Apreutesei. Public Domain.
--Tested with Windows, Linux, BSD and OSX, x86 and x86-64.
local ffi = require'ffi'
local C = ffi.C
local function checkh(ptr) return assert(ptr ~= nil and ptr) end
local function checkz(ret) assert(ret == 0) end
local function checknz(ret) assert(ret ~= 0) end
local new, free, protect
--Using VirtualAlloc allows memory protection, but can only allocate memory in multiple-of-64K chunks.
local USE_VIRTUALALLOC = false
if ffi.os == 'Windows' then
if USE_VIRTUALALLOC then
ffi.cdef[[
void* VirtualAlloc(void* lpAddress, size_t dwSize, uint32_t flAllocationType, uint32_t flProtect);
int VirtualFree(void* lpAddress, size_t dwSize, uint32_t dwFreeType);
int VirtualProtect(void* lpAddress, size_t dwSize, uint32_t flNewProtect, uint32_t* lpflOldProtect);
]]
local PAGE_READWRITE = 0x04
local PAGE_EXECUTE_READ = 0x20
local MEM_COMMIT = 0x1000
local MEM_RESERVE = 0x2000
local MEM_RELEASE = 0x8000
function new(size)
return checkh(C.VirtualAlloc(nil, size, bit.bor(MEM_RESERVE, MEM_COMMIT), PAGE_READWRITE))
end
function protect(addr, size)
local oldprotect = ffi.new'uint32_t[1]' --because null not accepted
checknz(C.VirtualProtect(addr, size, PAGE_EXECUTE_READ, oldprotect))
end
function free(addr, size)
assert(size, 'size required') --on other platforms
checknz(C.VirtualFree(addr, 0, MEM_RELEASE))
end
else
local HEAP_NO_SERIALIZE = 0x00000001
local HEAP_ZERO_MEMORY = 0x00000008
local HEAP_CREATE_ENABLE_EXECUTE = 0x00040000
ffi.cdef[[
void* HeapCreate(uint32_t flOptions, size_t dwInitialSize, size_t dwMaximumSize);
void* HeapAlloc(void* hHeap, uint32_t dwFlags, size_t dwBytes);
int HeapFree(void* hHeap, uint32_t dwFlags, void* lpMem);
]]
local heap
function new(size)
heap = heap or checkh(C.HeapCreate(bit.bor(HEAP_NO_SERIALIZE, HEAP_CREATE_ENABLE_EXECUTE), 0, 0))
return checkh(C.HeapAlloc(heap, HEAP_ZERO_MEMORY, size))
end
function protect(addr, size) end
function free(addr, size)
assert(size, 'size required') --on other platforms
checknz(C.HeapFree(heap, HEAP_NO_SERIALIZE, addr))
end
end
elseif ffi.os == 'Linux' or ffi.os == 'BSD' or ffi.os == 'OSX' then
if ffi.os == 'OSX' then
ffi.cdef'typedef int64_t off_t;'
else
ffi.cdef'typedef long int off_t;'
end
ffi.cdef[[
void* mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
int munmap(void *addr, size_t length);
int mprotect(void *addr, size_t len, int prot);
]]
local PROT_READ = 1
local PROT_WRITE = 2
local PROT_EXEC = 4
local MAP_PRIVATE = 2
local MAP_ANON = ffi.os == 'Linux' and 0x20 or 0x1000
function new(size)
local ret = C.mmap(nil, size,
bit.bor(PROT_READ, PROT_WRITE),
bit.bor(MAP_PRIVATE, MAP_ANON), -1, 0)
if ffi.cast('intptr_t', ret) == ffi.cast('intptr_t', -1) then
error(string.format('mmap errno: %d', ffi.errno()))
end
return checkh(ret)
end
function protect(addr, size)
checkz(C.mprotect(addr, size, bit.bor(PROT_READ, PROT_EXEC)))
end
function free(addr, size)
checkz(C.munmap(addr, size))
end
end
local new = function(size) --override for hooking to gc
local addr = new(size)
ffi.gc(addr, function(addr)
free(addr, size)
ffi.gc(addr, nil)
end)
return addr
end
if not ... then
local function test(size)
local addr = new(size)
print(addr)
addr = ffi.cast('int32_t*', addr)
assert(addr[0] == 0)
addr[0] = 1234 --writable
assert(addr[0] == 1234)
protect(addr, size)
--addr[0] = 4321 --uncomment this to get a crash (r/o memory); TODO: test if executable
--addr = nil; collectgarbage() --enable this to fail the assertion below
return addr
end
local a1 = test(64*1024*1000) --64MB
local a2 = test(16) --16 bytes
assert(a1 ~= a2) --different pages
a1 = nil
a2 = nil
collectgarbage() --TODO: test if finalizer was called
end
return {new = new, free = free, protect = protect}