• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *    Stack-less Just-In-Time compiler
3  *
4  *    Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without modification, are
7  * permitted provided that the following conditions are met:
8  *
9  *   1. Redistributions of source code must retain the above copyright notice, this list of
10  *      conditions and the following disclaimer.
11  *
12  *   2. Redistributions in binary form must reproduce the above copyright notice, this list
13  *      of conditions and the following disclaimer in the documentation and/or other materials
14  *      provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY
17  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
19  * SHALL THE COPYRIGHT HOLDER(S) OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
21  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
22  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
24  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26 
27 /*
28    This file contains a simple executable memory allocator
29 
30    It is assumed, that executable code blocks are usually medium (or sometimes
31    large) memory blocks, and the allocator is not too frequently called (less
32    optimized than other allocators). Thus, using it as a generic allocator is
33    not suggested.
34 
35    How does it work:
36      Memory is allocated in continuous memory areas called chunks by alloc_chunk()
37      Chunk format:
38      [ block ][ block ] ... [ block ][ block terminator ]
39 
40    All blocks and the block terminator is started with block_header. The block
41    header contains the size of the previous and the next block. These sizes
42    can also contain special values.
43      Block size:
44        0 - The block is a free_block, with a different size member.
45        1 - The block is a block terminator.
46        n - The block is used at the moment, and the value contains its size.
47      Previous block size:
48        0 - This is the first block of the memory chunk.
49        n - The size of the previous block.
50 
51    Using these size values we can go forward or backward on the block chain.
52    The unused blocks are stored in a chain list pointed by free_blocks. This
53    list is useful if we need to find a suitable memory area when the allocator
54    is called.
55 
56    When a block is freed, the new free block is connected to its adjacent free
57    blocks if possible.
58 
59      [ free block ][ used block ][ free block ]
60    and "used block" is freed, the three blocks are connected together:
61      [           one big free block           ]
62 */
63 
64 /* --------------------------------------------------------------------- */
65 /*  System (OS) functions                                                */
66 /* --------------------------------------------------------------------- */
67 
68 /* 64 KByte. */
69 #define CHUNK_SIZE	0x10000
70 
71 /*
72    alloc_chunk / free_chunk :
73      * allocate executable system memory chunks
74      * the size is always divisible by CHUNK_SIZE
75    SLJIT_ALLOCATOR_LOCK / SLJIT_ALLOCATOR_UNLOCK :
76      * provided as part of sljitUtils
77      * only the allocator requires this lock, sljit is fully thread safe
78        as it only uses local variables
79 */
80 
81 #ifdef _WIN32
82 #define SLJIT_UPDATE_WX_FLAGS(from, to, enable_exec)
83 
alloc_chunk(sljit_uw size)84 static SLJIT_INLINE void* alloc_chunk(sljit_uw size)
85 {
86 	return VirtualAlloc(NULL, size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
87 }
88 
free_chunk(void * chunk,sljit_uw size)89 static SLJIT_INLINE void free_chunk(void *chunk, sljit_uw size)
90 {
91 	SLJIT_UNUSED_ARG(size);
92 	VirtualFree(chunk, 0, MEM_RELEASE);
93 }
94 
95 #else /* POSIX */
96 
97 #if defined(__APPLE__) && defined(MAP_JIT)
98 /*
99    On macOS systems, returns MAP_JIT if it is defined _and_ we're running on a
100    version where it's OK to have more than one JIT block or where MAP_JIT is
101    required.
102    On non-macOS systems, returns MAP_JIT if it is defined.
103 */
104 #include <TargetConditionals.h>
105 #if TARGET_OS_OSX
106 #if defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86
107 #ifdef MAP_ANON
108 #include <sys/utsname.h>
109 #include <stdlib.h>
110 
111 #define SLJIT_MAP_JIT	(get_map_jit_flag())
112 
get_map_jit_flag()113 static SLJIT_INLINE int get_map_jit_flag()
114 {
115 	sljit_sw page_size;
116 	void *ptr;
117 	struct utsname name;
118 	static int map_jit_flag = -1;
119 
120 	if (map_jit_flag < 0) {
121 		map_jit_flag = 0;
122 		uname(&name);
123 
124 		/* Kernel version for 10.14.0 (Mojave) or later */
125 		if (atoi(name.release) >= 18) {
126 			page_size = get_page_alignment() + 1;
127 			/* Only use MAP_JIT if a hardened runtime is used */
128 			ptr = mmap(NULL, page_size, PROT_WRITE | PROT_EXEC,
129 					MAP_PRIVATE | MAP_ANON, -1, 0);
130 
131 			if (ptr != MAP_FAILED)
132 				munmap(ptr, page_size);
133 			else
134 				map_jit_flag = MAP_JIT;
135 		}
136 	}
137 	return map_jit_flag;
138 }
139 #endif /* MAP_ANON */
140 #else /* !SLJIT_CONFIG_X86 */
141 #if !(defined SLJIT_CONFIG_ARM && SLJIT_CONFIG_ARM)
142 #error Unsupported architecture
143 #endif /* SLJIT_CONFIG_ARM */
144 #include <pthread.h>
145 
146 #define SLJIT_MAP_JIT	(MAP_JIT)
147 #define SLJIT_UPDATE_WX_FLAGS(from, to, enable_exec) \
148                         apple_update_wx_flags(enable_exec)
149 
apple_update_wx_flags(sljit_s32 enable_exec)150 static SLJIT_INLINE void apple_update_wx_flags(sljit_s32 enable_exec)
151 {
152 	pthread_jit_write_protect_np(enable_exec);
153 }
154 #endif /* SLJIT_CONFIG_X86 */
155 #else /* !TARGET_OS_OSX */
156 #define SLJIT_MAP_JIT	(MAP_JIT)
157 #endif /* TARGET_OS_OSX */
158 #endif /* __APPLE__ && MAP_JIT */
159 #ifndef SLJIT_UPDATE_WX_FLAGS
160 #define SLJIT_UPDATE_WX_FLAGS(from, to, enable_exec)
161 #endif /* !SLJIT_UPDATE_WX_FLAGS */
162 #ifndef SLJIT_MAP_JIT
163 #define SLJIT_MAP_JIT	(0)
164 #endif /* !SLJIT_MAP_JIT */
165 
alloc_chunk(sljit_uw size)166 static SLJIT_INLINE void* alloc_chunk(sljit_uw size)
167 {
168 	void *retval;
169 	int prot = PROT_READ | PROT_WRITE | PROT_EXEC;
170 	int flags = MAP_PRIVATE;
171 	int fd = -1;
172 
173 #ifdef PROT_MAX
174 	prot |= PROT_MAX(prot);
175 #endif
176 
177 #ifdef MAP_ANON
178 	flags |= MAP_ANON | SLJIT_MAP_JIT;
179 #else /* !MAP_ANON */
180 	if (SLJIT_UNLIKELY((dev_zero < 0) && open_dev_zero()))
181 		return NULL;
182 
183 	fd = dev_zero;
184 #endif /* MAP_ANON */
185 
186 	retval = mmap(NULL, size, prot, flags, fd, 0);
187 	if (retval == MAP_FAILED)
188 		return NULL;
189 
190 	if (mprotect(retval, size, PROT_READ | PROT_WRITE | PROT_EXEC) < 0) {
191 		munmap(retval, size);
192 		return NULL;
193 	}
194 
195 	SLJIT_UPDATE_WX_FLAGS(retval, (uint8_t *)retval + size, 0);
196 
197 	return retval;
198 }
199 
free_chunk(void * chunk,sljit_uw size)200 static SLJIT_INLINE void free_chunk(void *chunk, sljit_uw size)
201 {
202 	munmap(chunk, size);
203 }
204 
205 #endif /* windows */
206 
207 /* --------------------------------------------------------------------- */
208 /*  Common functions                                                     */
209 /* --------------------------------------------------------------------- */
210 
211 #define CHUNK_MASK	(~(CHUNK_SIZE - 1))
212 
213 struct block_header {
214 	sljit_uw size;
215 	sljit_uw prev_size;
216 };
217 
218 struct free_block {
219 	struct block_header header;
220 	struct free_block *next;
221 	struct free_block *prev;
222 	sljit_uw size;
223 };
224 
225 #define AS_BLOCK_HEADER(base, offset) \
226 	((struct block_header*)(((sljit_u8*)base) + offset))
227 #define AS_FREE_BLOCK(base, offset) \
228 	((struct free_block*)(((sljit_u8*)base) + offset))
229 #define MEM_START(base)		((void*)(((sljit_u8*)base) + sizeof(struct block_header)))
230 #define ALIGN_SIZE(size)	(((size) + sizeof(struct block_header) + 7) & ~7)
231 
232 static struct free_block* free_blocks;
233 static sljit_uw allocated_size;
234 static sljit_uw total_size;
235 
sljit_insert_free_block(struct free_block * free_block,sljit_uw size)236 static SLJIT_INLINE void sljit_insert_free_block(struct free_block *free_block, sljit_uw size)
237 {
238 	free_block->header.size = 0;
239 	free_block->size = size;
240 
241 	free_block->next = free_blocks;
242 	free_block->prev = NULL;
243 	if (free_blocks)
244 		free_blocks->prev = free_block;
245 	free_blocks = free_block;
246 }
247 
sljit_remove_free_block(struct free_block * free_block)248 static SLJIT_INLINE void sljit_remove_free_block(struct free_block *free_block)
249 {
250 	if (free_block->next)
251 		free_block->next->prev = free_block->prev;
252 
253 	if (free_block->prev)
254 		free_block->prev->next = free_block->next;
255 	else {
256 		SLJIT_ASSERT(free_blocks == free_block);
257 		free_blocks = free_block->next;
258 	}
259 }
260 
sljit_malloc_exec(sljit_uw size)261 SLJIT_API_FUNC_ATTRIBUTE void* sljit_malloc_exec(sljit_uw size)
262 {
263 	struct block_header *header;
264 	struct block_header *next_header;
265 	struct free_block *free_block;
266 	sljit_uw chunk_size;
267 
268 	SLJIT_ALLOCATOR_LOCK();
269 	if (size < (64 - sizeof(struct block_header)))
270 		size = (64 - sizeof(struct block_header));
271 	size = ALIGN_SIZE(size);
272 
273 	free_block = free_blocks;
274 	while (free_block) {
275 		if (free_block->size >= size) {
276 			chunk_size = free_block->size;
277 			SLJIT_UPDATE_WX_FLAGS(NULL, NULL, 0);
278 			if (chunk_size > size + 64) {
279 				/* We just cut a block from the end of the free block. */
280 				chunk_size -= size;
281 				free_block->size = chunk_size;
282 				header = AS_BLOCK_HEADER(free_block, chunk_size);
283 				header->prev_size = chunk_size;
284 				AS_BLOCK_HEADER(header, size)->prev_size = size;
285 			}
286 			else {
287 				sljit_remove_free_block(free_block);
288 				header = (struct block_header*)free_block;
289 				size = chunk_size;
290 			}
291 			allocated_size += size;
292 			header->size = size;
293 			SLJIT_ALLOCATOR_UNLOCK();
294 			return MEM_START(header);
295 		}
296 		free_block = free_block->next;
297 	}
298 
299 	chunk_size = (size + sizeof(struct block_header) + CHUNK_SIZE - 1) & CHUNK_MASK;
300 	header = (struct block_header*)alloc_chunk(chunk_size);
301 	if (!header) {
302 		SLJIT_ALLOCATOR_UNLOCK();
303 		return NULL;
304 	}
305 
306 	chunk_size -= sizeof(struct block_header);
307 	total_size += chunk_size;
308 
309 	header->prev_size = 0;
310 	if (chunk_size > size + 64) {
311 		/* Cut the allocated space into a free and a used block. */
312 		allocated_size += size;
313 		header->size = size;
314 		chunk_size -= size;
315 
316 		free_block = AS_FREE_BLOCK(header, size);
317 		free_block->header.prev_size = size;
318 		sljit_insert_free_block(free_block, chunk_size);
319 		next_header = AS_BLOCK_HEADER(free_block, chunk_size);
320 	}
321 	else {
322 		/* All space belongs to this allocation. */
323 		allocated_size += chunk_size;
324 		header->size = chunk_size;
325 		next_header = AS_BLOCK_HEADER(header, chunk_size);
326 	}
327 	next_header->size = 1;
328 	next_header->prev_size = chunk_size;
329 	SLJIT_ALLOCATOR_UNLOCK();
330 	return MEM_START(header);
331 }
332 
sljit_free_exec(void * ptr)333 SLJIT_API_FUNC_ATTRIBUTE void sljit_free_exec(void* ptr)
334 {
335 	struct block_header *header;
336 	struct free_block* free_block;
337 
338 	SLJIT_ALLOCATOR_LOCK();
339 	header = AS_BLOCK_HEADER(ptr, -(sljit_sw)sizeof(struct block_header));
340 	allocated_size -= header->size;
341 
342 	/* Connecting free blocks together if possible. */
343 	SLJIT_UPDATE_WX_FLAGS(NULL, NULL, 0);
344 
345 	/* If header->prev_size == 0, free_block will equal to header.
346 	   In this case, free_block->header.size will be > 0. */
347 	free_block = AS_FREE_BLOCK(header, -(sljit_sw)header->prev_size);
348 	if (SLJIT_UNLIKELY(!free_block->header.size)) {
349 		free_block->size += header->size;
350 		header = AS_BLOCK_HEADER(free_block, free_block->size);
351 		header->prev_size = free_block->size;
352 	}
353 	else {
354 		free_block = (struct free_block*)header;
355 		sljit_insert_free_block(free_block, header->size);
356 	}
357 
358 	header = AS_BLOCK_HEADER(free_block, free_block->size);
359 	if (SLJIT_UNLIKELY(!header->size)) {
360 		free_block->size += ((struct free_block*)header)->size;
361 		sljit_remove_free_block((struct free_block*)header);
362 		header = AS_BLOCK_HEADER(free_block, free_block->size);
363 		header->prev_size = free_block->size;
364 	}
365 
366 	/* The whole chunk is free. */
367 	if (SLJIT_UNLIKELY(!free_block->header.prev_size && header->size == 1)) {
368 		/* If this block is freed, we still have (allocated_size / 2) free space. */
369 		if (total_size - free_block->size > (allocated_size * 3 / 2)) {
370 			total_size -= free_block->size;
371 			sljit_remove_free_block(free_block);
372 			free_chunk(free_block, free_block->size + sizeof(struct block_header));
373 		}
374 	}
375 
376 	SLJIT_UPDATE_WX_FLAGS(NULL, NULL, 1);
377 	SLJIT_ALLOCATOR_UNLOCK();
378 }
379 
sljit_free_unused_memory_exec(void)380 SLJIT_API_FUNC_ATTRIBUTE void sljit_free_unused_memory_exec(void)
381 {
382 	struct free_block* free_block;
383 	struct free_block* next_free_block;
384 
385 	SLJIT_ALLOCATOR_LOCK();
386 	SLJIT_UPDATE_WX_FLAGS(NULL, NULL, 0);
387 
388 	free_block = free_blocks;
389 	while (free_block) {
390 		next_free_block = free_block->next;
391 		if (!free_block->header.prev_size &&
392 				AS_BLOCK_HEADER(free_block, free_block->size)->size == 1) {
393 			total_size -= free_block->size;
394 			sljit_remove_free_block(free_block);
395 			free_chunk(free_block, free_block->size + sizeof(struct block_header));
396 		}
397 		free_block = next_free_block;
398 	}
399 
400 	SLJIT_ASSERT((total_size && free_blocks) || (!total_size && !free_blocks));
401 	SLJIT_UPDATE_WX_FLAGS(NULL, NULL, 1);
402 	SLJIT_ALLOCATOR_UNLOCK();
403 }
404