1 /**
2  * \file
3  * MonoJitInfo functionality
4  *
5  * Author:
6  *	Dietmar Maurer (dietmar@ximian.com)
7  *	Patrik Torstensson
8  *
9  * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com)
10  * Copyright 2004-2009 Novell, Inc (http://www.novell.com)
11  * Copyright 2011-2012 Xamarin, Inc (http://www.xamarin.com)
12  * Licensed under the MIT license. See LICENSE file in the project root for full license information.
13  */
14 
15 #include <config.h>
16 #include <glib.h>
17 #include <string.h>
18 #include <sys/stat.h>
19 
20 #include <mono/metadata/gc-internals.h>
21 
22 #include <mono/utils/atomic.h>
23 #include <mono/utils/mono-compiler.h>
24 #include <mono/utils/mono-logger-internals.h>
25 #include <mono/utils/mono-membar.h>
26 #include <mono/utils/mono-counters.h>
27 #include <mono/utils/hazard-pointer.h>
28 #include <mono/utils/mono-tls.h>
29 #include <mono/utils/mono-mmap.h>
30 #include <mono/utils/mono-threads.h>
31 #include <mono/utils/unlocked.h>
32 #include <mono/metadata/object.h>
33 #include <mono/metadata/object-internals.h>
34 #include <mono/metadata/domain-internals.h>
35 #include <mono/metadata/class-internals.h>
36 #include <mono/metadata/assembly.h>
37 #include <mono/metadata/exception.h>
38 #include <mono/metadata/metadata-internals.h>
39 #include <mono/metadata/appdomain.h>
40 #include <mono/metadata/debug-internals.h>
41 #include <mono/metadata/mono-config.h>
42 #include <mono/metadata/threads-types.h>
43 #include <mono/metadata/runtime.h>
44 #include <metadata/threads.h>
45 #include <metadata/profiler-private.h>
46 #include <mono/metadata/coree.h>
47 
48 static MonoJitInfoFindInAot jit_info_find_in_aot_func = NULL;
49 
50 #define JIT_INFO_TABLE_FILL_RATIO_NOM		3
51 #define JIT_INFO_TABLE_FILL_RATIO_DENOM		4
52 #define JIT_INFO_TABLE_FILLED_NUM_ELEMENTS	(MONO_JIT_INFO_TABLE_CHUNK_SIZE * JIT_INFO_TABLE_FILL_RATIO_NOM / JIT_INFO_TABLE_FILL_RATIO_DENOM)
53 
54 #define JIT_INFO_TABLE_LOW_WATERMARK(n)		((n) / 2)
55 #define JIT_INFO_TABLE_HIGH_WATERMARK(n)	((n) * 5 / 6)
56 
57 #define JIT_INFO_TOMBSTONE_MARKER	((MonoMethod*)NULL)
58 #define IS_JIT_INFO_TOMBSTONE(ji)	((ji)->d.method == JIT_INFO_TOMBSTONE_MARKER)
59 
60 #define JIT_INFO_TABLE_HAZARD_INDEX		0
61 #define JIT_INFO_HAZARD_INDEX			1
62 
63 static int
jit_info_table_num_elements(MonoJitInfoTable * table)64 jit_info_table_num_elements (MonoJitInfoTable *table)
65 {
66 	return table->num_valid;
67 }
68 
69 static MonoJitInfoTableChunk*
jit_info_table_new_chunk(void)70 jit_info_table_new_chunk (void)
71 {
72 	MonoJitInfoTableChunk *chunk = g_new0 (MonoJitInfoTableChunk, 1);
73 	chunk->refcount = 1;
74 
75 	return chunk;
76 }
77 
78 MonoJitInfoTable *
mono_jit_info_table_new(MonoDomain * domain)79 mono_jit_info_table_new (MonoDomain *domain)
80 {
81 	MonoJitInfoTable *table = (MonoJitInfoTable *)g_malloc0 (MONO_SIZEOF_JIT_INFO_TABLE + sizeof (MonoJitInfoTableChunk*));
82 
83 	table->domain = domain;
84 	table->num_chunks = 1;
85 	table->chunks [0] = jit_info_table_new_chunk ();
86 	table->num_valid = 0;
87 
88 	return table;
89 }
90 
91 void
mono_jit_info_table_free(MonoJitInfoTable * table)92 mono_jit_info_table_free (MonoJitInfoTable *table)
93 {
94 	int i;
95 	int num_chunks = table->num_chunks;
96 	MonoDomain *domain = table->domain;
97 
98 	mono_domain_lock (domain);
99 
100 	table->domain->num_jit_info_tables--;
101 	if (table->domain->num_jit_info_tables <= 1) {
102 		GSList *list;
103 
104 		for (list = table->domain->jit_info_free_queue; list; list = list->next)
105 			g_free (list->data);
106 
107 		g_slist_free (table->domain->jit_info_free_queue);
108 		table->domain->jit_info_free_queue = NULL;
109 	}
110 
111 	/* At this point we assume that there are no other threads
112 	   still accessing the table, so we don't have to worry about
113 	   hazardous pointers. */
114 
115 	for (i = 0; i < num_chunks; ++i) {
116 		MonoJitInfoTableChunk *chunk = table->chunks [i];
117 		MonoJitInfo *tombstone;
118 
119 		if (--chunk->refcount > 0)
120 			continue;
121 
122 		for (tombstone = chunk->next_tombstone; tombstone; ) {
123 			MonoJitInfo *next = tombstone->n.next_tombstone;
124 			g_free (tombstone);
125 			tombstone = next;
126 		}
127 
128 		g_free (chunk);
129 	}
130 
131 	mono_domain_unlock (domain);
132 
133 	g_free (table);
134 }
135 
136 /* The jit_info_table is sorted in ascending order by the end
137  * addresses of the compiled methods.  The reason why we have to do
138  * this is that once we introduce tombstones, it becomes possible for
139  * code ranges to overlap, and if we sort by code start and insert at
140  * the back of the table, we cannot guarantee that we won't overlook
141  * an entry.
142  *
143  * There are actually two possible ways to do the sorting and
144  * inserting which work with our lock-free mechanism:
145  *
146  * 1. Sort by start address and insert at the front.  When looking for
147  * an entry, find the last one with a start address lower than the one
148  * you're looking for, then work your way to the front of the table.
149  *
150  * 2. Sort by end address and insert at the back.  When looking for an
151  * entry, find the first one with an end address higher than the one
152  * you're looking for, then work your way to the end of the table.
153  *
154  * We chose the latter out of convenience.
155  */
156 static int
jit_info_table_index(MonoJitInfoTable * table,gint8 * addr)157 jit_info_table_index (MonoJitInfoTable *table, gint8 *addr)
158 {
159 	int left = 0, right = table->num_chunks;
160 
161 	g_assert (left < right);
162 
163 	do {
164 		int pos = (left + right) / 2;
165 		MonoJitInfoTableChunk *chunk = table->chunks [pos];
166 
167 		if (addr < chunk->last_code_end)
168 			right = pos;
169 		else
170 			left = pos + 1;
171 	} while (left < right);
172 	g_assert (left == right);
173 
174 	if (left >= table->num_chunks)
175 		return table->num_chunks - 1;
176 	return left;
177 }
178 
179 static int
jit_info_table_chunk_index(MonoJitInfoTableChunk * chunk,MonoThreadHazardPointers * hp,gint8 * addr)180 jit_info_table_chunk_index (MonoJitInfoTableChunk *chunk, MonoThreadHazardPointers *hp, gint8 *addr)
181 {
182 	int left = 0, right = chunk->num_elements;
183 
184 	while (left < right) {
185 		int pos = (left + right) / 2;
186 		MonoJitInfo *ji = (MonoJitInfo *)mono_get_hazardous_pointer((gpointer volatile*)&chunk->data [pos], hp, JIT_INFO_HAZARD_INDEX);
187 		gint8 *code_end = (gint8*)ji->code_start + ji->code_size;
188 
189 		if (addr < code_end)
190 			right = pos;
191 		else
192 			left = pos + 1;
193 	}
194 	g_assert (left == right);
195 
196 	return left;
197 }
198 
199 static MonoJitInfo*
jit_info_table_find(MonoJitInfoTable * table,MonoThreadHazardPointers * hp,gint8 * addr)200 jit_info_table_find (MonoJitInfoTable *table, MonoThreadHazardPointers *hp, gint8 *addr)
201 {
202 	MonoJitInfo *ji;
203 	int chunk_pos, pos;
204 
205 	chunk_pos = jit_info_table_index (table, (gint8*)addr);
206 	g_assert (chunk_pos < table->num_chunks);
207 
208 	pos = jit_info_table_chunk_index (table->chunks [chunk_pos], hp, (gint8*)addr);
209 
210 	/* We now have a position that's very close to that of the
211 	   first element whose end address is higher than the one
212 	   we're looking for.  If we don't have the exact position,
213 	   then we have a position below that one, so we'll just
214 	   search upward until we find our element. */
215 	do {
216 		MonoJitInfoTableChunk *chunk = table->chunks [chunk_pos];
217 
218 		while (pos < chunk->num_elements) {
219 			ji = (MonoJitInfo *)mono_get_hazardous_pointer ((gpointer volatile*)&chunk->data [pos], hp, JIT_INFO_HAZARD_INDEX);
220 
221 			++pos;
222 
223 			if (IS_JIT_INFO_TOMBSTONE (ji)) {
224 				mono_hazard_pointer_clear (hp, JIT_INFO_HAZARD_INDEX);
225 				continue;
226 			}
227 			if ((gint8*)addr >= (gint8*)ji->code_start
228 					&& (gint8*)addr < (gint8*)ji->code_start + ji->code_size) {
229 				mono_hazard_pointer_clear (hp, JIT_INFO_HAZARD_INDEX);
230 				return ji;
231 			}
232 
233 			/* If we find a non-tombstone element which is already
234 			   beyond what we're looking for, we have to end the
235 			   search. */
236 			if ((gint8*)addr < (gint8*)ji->code_start)
237 				goto not_found;
238 		}
239 
240 		++chunk_pos;
241 		pos = 0;
242 	} while (chunk_pos < table->num_chunks);
243 
244  not_found:
245 	if (hp)
246 		mono_hazard_pointer_clear (hp, JIT_INFO_HAZARD_INDEX);
247 	return NULL;
248 }
249 
250 /*
251  * mono_jit_info_table_find_internal:
252  *
253  * If TRY_AOT is FALSE, avoid loading information for missing methods from AOT images, which is currently not async safe.
254  * In this case, only those AOT methods will be found whose jit info is already loaded.
255  * If ALLOW_TRAMPOLINES is TRUE, this can return a MonoJitInfo which represents a trampoline (ji->is_trampoline is true).
256  * ASYNC SAFETY: When called in an async context (mono_thread_info_is_async_context ()), this is async safe.
257  * In this case, the returned MonoJitInfo might not have metadata information, in particular,
258  * mono_jit_info_get_method () could fail.
259  */
260 MonoJitInfo*
mono_jit_info_table_find_internal(MonoDomain * domain,char * addr,gboolean try_aot,gboolean allow_trampolines)261 mono_jit_info_table_find_internal (MonoDomain *domain, char *addr, gboolean try_aot, gboolean allow_trampolines)
262 {
263 	MonoJitInfoTable *table;
264 	MonoJitInfo *ji, *module_ji;
265 	MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
266 
267 	UnlockedIncrement (&mono_stats.jit_info_table_lookup_count);
268 
269 	/* First we have to get the domain's jit_info_table.  This is
270 	   complicated by the fact that a writer might substitute a
271 	   new table and free the old one.  What the writer guarantees
272 	   us is that it looks at the hazard pointers after it has
273 	   changed the jit_info_table pointer.  So, if we guard the
274 	   table by a hazard pointer and make sure that the pointer is
275 	   still there after we've made it hazardous, we don't have to
276 	   worry about the writer freeing the table. */
277 	table = (MonoJitInfoTable *)mono_get_hazardous_pointer ((gpointer volatile*)&domain->jit_info_table, hp, JIT_INFO_TABLE_HAZARD_INDEX);
278 
279 	ji = jit_info_table_find (table, hp, (gint8*)addr);
280 	if (hp)
281 		mono_hazard_pointer_clear (hp, JIT_INFO_TABLE_HAZARD_INDEX);
282 	if (ji && ji->is_trampoline && !allow_trampolines)
283 		return NULL;
284 	if (ji)
285 		return ji;
286 
287 	/* Maybe its an AOT module */
288 	if (try_aot && mono_get_root_domain () && mono_get_root_domain ()->aot_modules) {
289 		table = (MonoJitInfoTable *)mono_get_hazardous_pointer ((gpointer volatile*)&mono_get_root_domain ()->aot_modules, hp, JIT_INFO_TABLE_HAZARD_INDEX);
290 		module_ji = jit_info_table_find (table, hp, (gint8*)addr);
291 		if (module_ji)
292 			ji = jit_info_find_in_aot_func (domain, module_ji->d.image, addr);
293 		if (hp)
294 			mono_hazard_pointer_clear (hp, JIT_INFO_TABLE_HAZARD_INDEX);
295 	}
296 
297 	if (ji && ji->is_trampoline && !allow_trampolines)
298 		return NULL;
299 
300 	return ji;
301 }
302 
303 /**
304  * mono_jit_info_table_find:
305  * \param domain Domain that you want to look up
306  * \param addr Points to an address with JITed code.
307  *
308  * Use this function to obtain a \c MonoJitInfo* object that can be used to get
309  * some statistics. You should provide both the \p domain on which you will be
310  * performing the probe, and an address. Since application domains can share code
311  * the same address can be in use by multiple domains at once.
312  *
313  * This does not return any results for trampolines.
314  *
315  * \returns NULL if the address does not belong to JITed code (it might be native
316  * code or a trampoline) or a valid pointer to a \c MonoJitInfo* .
317  */
318 MonoJitInfo*
mono_jit_info_table_find(MonoDomain * domain,char * addr)319 mono_jit_info_table_find (MonoDomain *domain, char *addr)
320 {
321 	return mono_jit_info_table_find_internal (domain, addr, TRUE, FALSE);
322 }
323 
324 static G_GNUC_UNUSED void
jit_info_table_check(MonoJitInfoTable * table)325 jit_info_table_check (MonoJitInfoTable *table)
326 {
327 	int i;
328 
329 	for (i = 0; i < table->num_chunks; ++i) {
330 		MonoJitInfoTableChunk *chunk = table->chunks [i];
331 		int j;
332 
333 		g_assert (chunk->refcount > 0 /* && chunk->refcount <= 8 */);
334 		if (chunk->refcount > 10)
335 			printf("warning: chunk refcount is %d\n", chunk->refcount);
336 		g_assert (chunk->num_elements <= MONO_JIT_INFO_TABLE_CHUNK_SIZE);
337 
338 		for (j = 0; j < chunk->num_elements; ++j) {
339 			MonoJitInfo *this_ji = chunk->data [j];
340 			MonoJitInfo *next;
341 
342 			g_assert ((gint8*)this_ji->code_start + this_ji->code_size <= chunk->last_code_end);
343 
344 			if (j < chunk->num_elements - 1)
345 				next = chunk->data [j + 1];
346 			else if (i < table->num_chunks - 1) {
347 				int k;
348 
349 				for (k = i + 1; k < table->num_chunks; ++k)
350 					if (table->chunks [k]->num_elements > 0)
351 						break;
352 
353 				if (k >= table->num_chunks)
354 					return;
355 
356 				g_assert (table->chunks [k]->num_elements > 0);
357 				next = table->chunks [k]->data [0];
358 			} else
359 				return;
360 
361 			g_assert ((gint8*)this_ji->code_start + this_ji->code_size <= (gint8*)next->code_start + next->code_size);
362 		}
363 	}
364 }
365 
366 static MonoJitInfoTable*
jit_info_table_realloc(MonoJitInfoTable * old)367 jit_info_table_realloc (MonoJitInfoTable *old)
368 {
369 	int i;
370 	int num_elements = jit_info_table_num_elements (old);
371 	int required_size;
372 	int num_chunks;
373 	int new_chunk, new_element;
374 	MonoJitInfoTable *result;
375 
376 	/* number of needed places for elements needed */
377 	required_size = (int)((long)num_elements * JIT_INFO_TABLE_FILL_RATIO_DENOM / JIT_INFO_TABLE_FILL_RATIO_NOM);
378 	num_chunks = (required_size + MONO_JIT_INFO_TABLE_CHUNK_SIZE - 1) / MONO_JIT_INFO_TABLE_CHUNK_SIZE;
379 	if (num_chunks == 0) {
380 		g_assert (num_elements == 0);
381 		return mono_jit_info_table_new (old->domain);
382 	}
383 	g_assert (num_chunks > 0);
384 
385 	result = (MonoJitInfoTable *)g_malloc (MONO_SIZEOF_JIT_INFO_TABLE + sizeof (MonoJitInfoTableChunk*) * num_chunks);
386 	result->domain = old->domain;
387 	result->num_chunks = num_chunks;
388 	result->num_valid = old->num_valid;
389 
390 	for (i = 0; i < num_chunks; ++i)
391 		result->chunks [i] = jit_info_table_new_chunk ();
392 
393 	new_chunk = 0;
394 	new_element = 0;
395 	for (i = 0; i < old->num_chunks; ++i) {
396 		MonoJitInfoTableChunk *chunk = old->chunks [i];
397 		int chunk_num_elements = chunk->num_elements;
398 		int j;
399 
400 		for (j = 0; j < chunk_num_elements; ++j) {
401 			if (!IS_JIT_INFO_TOMBSTONE (chunk->data [j])) {
402 				g_assert (new_chunk < num_chunks);
403 				result->chunks [new_chunk]->data [new_element] = chunk->data [j];
404 				if (++new_element >= JIT_INFO_TABLE_FILLED_NUM_ELEMENTS) {
405 					result->chunks [new_chunk]->num_elements = new_element;
406 					++new_chunk;
407 					new_element = 0;
408 				}
409 			}
410 		}
411 	}
412 
413 	if (new_chunk < num_chunks) {
414 		g_assert (new_chunk == num_chunks - 1);
415 		result->chunks [new_chunk]->num_elements = new_element;
416 		g_assert (result->chunks [new_chunk]->num_elements > 0);
417 	}
418 
419 	for (i = 0; i < num_chunks; ++i) {
420 		MonoJitInfoTableChunk *chunk = result->chunks [i];
421 		MonoJitInfo *ji = chunk->data [chunk->num_elements - 1];
422 
423 		result->chunks [i]->last_code_end = (gint8*)ji->code_start + ji->code_size;
424 	}
425 
426 	return result;
427 }
428 
429 static void
jit_info_table_split_chunk(MonoJitInfoTableChunk * chunk,MonoJitInfoTableChunk ** new1p,MonoJitInfoTableChunk ** new2p)430 jit_info_table_split_chunk (MonoJitInfoTableChunk *chunk, MonoJitInfoTableChunk **new1p, MonoJitInfoTableChunk **new2p)
431 {
432 	MonoJitInfoTableChunk *new1 = jit_info_table_new_chunk ();
433 	MonoJitInfoTableChunk *new2 = jit_info_table_new_chunk ();
434 
435 	g_assert (chunk->num_elements == MONO_JIT_INFO_TABLE_CHUNK_SIZE);
436 
437 	new1->num_elements = MONO_JIT_INFO_TABLE_CHUNK_SIZE / 2;
438 	new2->num_elements = MONO_JIT_INFO_TABLE_CHUNK_SIZE - new1->num_elements;
439 
440 	memcpy ((void*)new1->data, (void*)chunk->data, sizeof (MonoJitInfo*) * new1->num_elements);
441 	memcpy ((void*)new2->data, (void*)(chunk->data + new1->num_elements), sizeof (MonoJitInfo*) * new2->num_elements);
442 
443 	new1->last_code_end = (gint8*)new1->data [new1->num_elements - 1]->code_start
444 		+ new1->data [new1->num_elements - 1]->code_size;
445 	new2->last_code_end = (gint8*)new2->data [new2->num_elements - 1]->code_start
446 		+ new2->data [new2->num_elements - 1]->code_size;
447 
448 	*new1p = new1;
449 	*new2p = new2;
450 }
451 
452 static MonoJitInfoTable*
jit_info_table_copy_and_split_chunk(MonoJitInfoTable * table,MonoJitInfoTableChunk * chunk)453 jit_info_table_copy_and_split_chunk (MonoJitInfoTable *table, MonoJitInfoTableChunk *chunk)
454 {
455 	MonoJitInfoTable *new_table = (MonoJitInfoTable *)g_malloc (MONO_SIZEOF_JIT_INFO_TABLE
456 		+ sizeof (MonoJitInfoTableChunk*) * (table->num_chunks + 1));
457 	int i, j;
458 
459 	new_table->domain = table->domain;
460 	new_table->num_chunks = table->num_chunks + 1;
461 	new_table->num_valid = table->num_valid;
462 
463 	j = 0;
464 	for (i = 0; i < table->num_chunks; ++i) {
465 		if (table->chunks [i] == chunk) {
466 			jit_info_table_split_chunk (chunk, &new_table->chunks [j], &new_table->chunks [j + 1]);
467 			j += 2;
468 		} else {
469 			new_table->chunks [j] = table->chunks [i];
470 			++new_table->chunks [j]->refcount;
471 			++j;
472 		}
473 	}
474 
475 	g_assert (j == new_table->num_chunks);
476 
477 	return new_table;
478 }
479 
480 static MonoJitInfoTableChunk*
jit_info_table_purify_chunk(MonoJitInfoTableChunk * old)481 jit_info_table_purify_chunk (MonoJitInfoTableChunk *old)
482 {
483 	MonoJitInfoTableChunk *result = jit_info_table_new_chunk ();
484 	int i, j;
485 
486 	j = 0;
487 	for (i = 0; i < old->num_elements; ++i) {
488 		if (!IS_JIT_INFO_TOMBSTONE (old->data [i]))
489 			result->data [j++] = old->data [i];
490 	}
491 
492 	result->num_elements = j;
493 	if (result->num_elements > 0)
494 		result->last_code_end = (gint8*)result->data [j - 1]->code_start + result->data [j - 1]->code_size;
495 	else
496 		result->last_code_end = old->last_code_end;
497 
498 	return result;
499 }
500 
501 static MonoJitInfoTable*
jit_info_table_copy_and_purify_chunk(MonoJitInfoTable * table,MonoJitInfoTableChunk * chunk)502 jit_info_table_copy_and_purify_chunk (MonoJitInfoTable *table, MonoJitInfoTableChunk *chunk)
503 {
504 	MonoJitInfoTable *new_table = (MonoJitInfoTable *)g_malloc (MONO_SIZEOF_JIT_INFO_TABLE
505 		+ sizeof (MonoJitInfoTableChunk*) * table->num_chunks);
506 	int i, j;
507 
508 	new_table->domain = table->domain;
509 	new_table->num_chunks = table->num_chunks;
510 	new_table->num_valid = table->num_valid;
511 
512 	j = 0;
513 	for (i = 0; i < table->num_chunks; ++i) {
514 		if (table->chunks [i] == chunk)
515 			new_table->chunks [j++] = jit_info_table_purify_chunk (table->chunks [i]);
516 		else {
517 			new_table->chunks [j] = table->chunks [i];
518 			++new_table->chunks [j]->refcount;
519 			++j;
520 		}
521 	}
522 
523 	g_assert (j == new_table->num_chunks);
524 
525 	return new_table;
526 }
527 
528 /* As we add an element to the table the case can arise that the chunk
529  * to which we need to add is already full.  In that case we have to
530  * allocate a new table and do something about that chunk.  We have
531  * several strategies:
532  *
533  * If the number of elements in the table is below the low watermark
534  * or above the high watermark, we reallocate the whole table.
535  * Otherwise we only concern ourselves with the overflowing chunk:
536  *
537  * If there are no tombstones in the chunk then we split the chunk in
538  * two, each half full.
539  *
540  * If the chunk does contain tombstones, we just make a new copy of
541  * the chunk without the tombstones, which will have room for at least
542  * the one element we have to add.
543  */
544 static MonoJitInfoTable*
jit_info_table_chunk_overflow(MonoJitInfoTable * table,MonoJitInfoTableChunk * chunk)545 jit_info_table_chunk_overflow (MonoJitInfoTable *table, MonoJitInfoTableChunk *chunk)
546 {
547 	int num_elements = jit_info_table_num_elements (table);
548 	int i;
549 
550 	if (num_elements < JIT_INFO_TABLE_LOW_WATERMARK (table->num_chunks * MONO_JIT_INFO_TABLE_CHUNK_SIZE)
551 			|| num_elements > JIT_INFO_TABLE_HIGH_WATERMARK (table->num_chunks * MONO_JIT_INFO_TABLE_CHUNK_SIZE)) {
552 		//printf ("reallocing table\n");
553 		return jit_info_table_realloc (table);
554 	}
555 
556 	/* count the number of non-tombstone elements in the chunk */
557 	num_elements = 0;
558 	for (i = 0; i < chunk->num_elements; ++i) {
559 		if (!IS_JIT_INFO_TOMBSTONE (chunk->data [i]))
560 			++num_elements;
561 	}
562 
563 	if (num_elements == MONO_JIT_INFO_TABLE_CHUNK_SIZE) {
564 		//printf ("splitting chunk\n");
565 		return jit_info_table_copy_and_split_chunk (table, chunk);
566 	}
567 
568 	//printf ("purifying chunk\n");
569 	return jit_info_table_copy_and_purify_chunk (table, chunk);
570 }
571 
572 /* We add elements to the table by first making space for them by
573  * shifting the elements at the back to the right, one at a time.
574  * This results in duplicate entries during the process, but during
575  * all the time the table is in a sorted state.  Also, when an element
576  * is replaced by another one, the element that replaces it has an end
577  * address that is equal to or lower than that of the replaced
578  * element.  That property is necessary to guarantee that when
579  * searching for an element we end up at a position not higher than
580  * the one we're looking for (i.e. we either find the element directly
581  * or we end up to the left of it).
582  */
583 static void
jit_info_table_add(MonoDomain * domain,MonoJitInfoTable * volatile * table_ptr,MonoJitInfo * ji)584 jit_info_table_add (MonoDomain *domain, MonoJitInfoTable *volatile *table_ptr, MonoJitInfo *ji)
585 {
586 	MonoJitInfoTable *table;
587 	MonoJitInfoTableChunk *chunk;
588 	int chunk_pos, pos;
589 	int num_elements;
590 	int i;
591 
592 	table = *table_ptr;
593 
594  restart:
595 	chunk_pos = jit_info_table_index (table, (gint8*)ji->code_start + ji->code_size);
596 	g_assert (chunk_pos < table->num_chunks);
597 	chunk = table->chunks [chunk_pos];
598 
599 	if (chunk->num_elements >= MONO_JIT_INFO_TABLE_CHUNK_SIZE) {
600 		MonoJitInfoTable *new_table = jit_info_table_chunk_overflow (table, chunk);
601 
602 		/* Debugging code, should be removed. */
603 		//jit_info_table_check (new_table);
604 
605 		*table_ptr = new_table;
606 		mono_memory_barrier ();
607 		domain->num_jit_info_tables++;
608 		mono_thread_hazardous_try_free (table, (MonoHazardousFreeFunc)mono_jit_info_table_free);
609 		table = new_table;
610 
611 		goto restart;
612 	}
613 
614 	/* Debugging code, should be removed. */
615 	//jit_info_table_check (table);
616 
617 	num_elements = chunk->num_elements;
618 
619 	pos = jit_info_table_chunk_index (chunk, NULL, (gint8*)ji->code_start + ji->code_size);
620 
621 	/* First we need to size up the chunk by one, by copying the
622 	   last item, or inserting the first one, if the table is
623 	   empty. */
624 	if (num_elements > 0)
625 		chunk->data [num_elements] = chunk->data [num_elements - 1];
626 	else
627 		chunk->data [0] = ji;
628 	mono_memory_write_barrier ();
629 	chunk->num_elements = ++num_elements;
630 
631 	/* Shift the elements up one by one. */
632 	for (i = num_elements - 2; i >= pos; --i) {
633 		mono_memory_write_barrier ();
634 		chunk->data [i + 1] = chunk->data [i];
635 	}
636 
637 	/* Now we have room and can insert the new item. */
638 	mono_memory_write_barrier ();
639 	chunk->data [pos] = ji;
640 
641 	/* Set the high code end address chunk entry. */
642 	chunk->last_code_end = (gint8*)chunk->data [chunk->num_elements - 1]->code_start
643 		+ chunk->data [chunk->num_elements - 1]->code_size;
644 
645 	++table->num_valid;
646 
647 	/* Debugging code, should be removed. */
648 	//jit_info_table_check (table);
649 }
650 
651 void
mono_jit_info_table_add(MonoDomain * domain,MonoJitInfo * ji)652 mono_jit_info_table_add (MonoDomain *domain, MonoJitInfo *ji)
653 {
654 	g_assert (ji->d.method != NULL);
655 
656 	mono_domain_lock (domain);
657 
658 	UnlockedIncrement (&mono_stats.jit_info_table_insert_count);
659 
660 	jit_info_table_add (domain, &domain->jit_info_table, ji);
661 
662 	mono_domain_unlock (domain);
663 }
664 
665 static MonoJitInfo*
mono_jit_info_make_tombstone(MonoJitInfoTableChunk * chunk,MonoJitInfo * ji)666 mono_jit_info_make_tombstone (MonoJitInfoTableChunk *chunk, MonoJitInfo *ji)
667 {
668 	MonoJitInfo *tombstone = g_new0 (MonoJitInfo, 1);
669 
670 	tombstone->code_start = ji->code_start;
671 	tombstone->code_size = ji->code_size;
672 	tombstone->d.method = JIT_INFO_TOMBSTONE_MARKER;
673 	tombstone->n.next_tombstone = chunk->next_tombstone;
674 	chunk->next_tombstone = tombstone;
675 
676 	return tombstone;
677 }
678 
679 /*
680  * LOCKING: domain lock
681  */
682 static void
mono_jit_info_free_or_queue(MonoDomain * domain,MonoJitInfo * ji)683 mono_jit_info_free_or_queue (MonoDomain *domain, MonoJitInfo *ji)
684 {
685 	if (domain->num_jit_info_tables <= 1) {
686 		/* Can it actually happen that we only have one table
687 		   but ji is still hazardous? */
688 		mono_thread_hazardous_try_free (ji, g_free);
689 	} else {
690 		domain->jit_info_free_queue = g_slist_prepend (domain->jit_info_free_queue, ji);
691 	}
692 }
693 
694 static void
jit_info_table_remove(MonoJitInfoTable * table,MonoJitInfo * ji)695 jit_info_table_remove (MonoJitInfoTable *table, MonoJitInfo *ji)
696 {
697 	MonoJitInfoTableChunk *chunk;
698 	gpointer start = ji->code_start;
699 	int chunk_pos, pos;
700 
701 	chunk_pos = jit_info_table_index (table, (gint8 *)start);
702 	g_assert (chunk_pos < table->num_chunks);
703 
704 	pos = jit_info_table_chunk_index (table->chunks [chunk_pos], NULL, (gint8 *)start);
705 
706 	do {
707 		chunk = table->chunks [chunk_pos];
708 
709 		while (pos < chunk->num_elements) {
710 			if (chunk->data [pos] == ji)
711 				goto found;
712 
713 			g_assert (IS_JIT_INFO_TOMBSTONE (chunk->data [pos]));
714 			g_assert ((guint8*)chunk->data [pos]->code_start + chunk->data [pos]->code_size
715 				<= (guint8*)ji->code_start + ji->code_size);
716 
717 			++pos;
718 		}
719 
720 		++chunk_pos;
721 		pos = 0;
722 	} while (chunk_pos < table->num_chunks);
723 
724  found:
725 	g_assert (chunk->data [pos] == ji);
726 
727 	chunk->data [pos] = mono_jit_info_make_tombstone (chunk, ji);
728 	--table->num_valid;
729 
730 	/* Debugging code, should be removed. */
731 	//jit_info_table_check (table);
732 }
733 
734 void
mono_jit_info_table_remove(MonoDomain * domain,MonoJitInfo * ji)735 mono_jit_info_table_remove (MonoDomain *domain, MonoJitInfo *ji)
736 {
737 	MonoJitInfoTable *table;
738 
739 	mono_domain_lock (domain);
740 	table = domain->jit_info_table;
741 
742 	UnlockedIncrement (&mono_stats.jit_info_table_remove_count);
743 
744 	jit_info_table_remove (table, ji);
745 
746 	mono_jit_info_free_or_queue (domain, ji);
747 
748 	mono_domain_unlock (domain);
749 }
750 
751 void
mono_jit_info_add_aot_module(MonoImage * image,gpointer start,gpointer end)752 mono_jit_info_add_aot_module (MonoImage *image, gpointer start, gpointer end)
753 {
754 	MonoJitInfo *ji;
755 	MonoDomain *domain = mono_get_root_domain ();
756 
757 	g_assert (domain);
758 	mono_domain_lock (domain);
759 
760 	/*
761 	 * We reuse MonoJitInfoTable to store AOT module info,
762 	 * this gives us async-safe lookup.
763 	 */
764 	if (!domain->aot_modules) {
765 		domain->num_jit_info_tables ++;
766 		domain->aot_modules = mono_jit_info_table_new (domain);
767 	}
768 
769 	ji = g_new0 (MonoJitInfo, 1);
770 	ji->d.image = image;
771 	ji->code_start = start;
772 	ji->code_size = (guint8*)end - (guint8*)start;
773 	jit_info_table_add (domain, &domain->aot_modules, ji);
774 
775 	mono_domain_unlock (domain);
776 }
777 
778 void
mono_install_jit_info_find_in_aot(MonoJitInfoFindInAot func)779 mono_install_jit_info_find_in_aot (MonoJitInfoFindInAot func)
780 {
781 	jit_info_find_in_aot_func = func;
782 }
783 
784 int
mono_jit_info_size(MonoJitInfoFlags flags,int num_clauses,int num_holes)785 mono_jit_info_size (MonoJitInfoFlags flags, int num_clauses, int num_holes)
786 {
787 	int size = MONO_SIZEOF_JIT_INFO;
788 
789 	size += num_clauses * sizeof (MonoJitExceptionInfo);
790 	if (flags & JIT_INFO_HAS_GENERIC_JIT_INFO)
791 		size += sizeof (MonoGenericJitInfo);
792 	if (flags & JIT_INFO_HAS_TRY_BLOCK_HOLES)
793 		size += sizeof (MonoTryBlockHoleTableJitInfo) + num_holes * sizeof (MonoTryBlockHoleJitInfo);
794 	if (flags & JIT_INFO_HAS_ARCH_EH_INFO)
795 		size += sizeof (MonoArchEHJitInfo);
796 	if (flags & JIT_INFO_HAS_THUNK_INFO)
797 		size += sizeof (MonoThunkJitInfo);
798 	if (flags & JIT_INFO_HAS_UNWIND_INFO)
799 		size += sizeof (MonoUnwindJitInfo);
800 	return size;
801 }
802 
803 void
mono_jit_info_init(MonoJitInfo * ji,MonoMethod * method,guint8 * code,int code_size,MonoJitInfoFlags flags,int num_clauses,int num_holes)804 mono_jit_info_init (MonoJitInfo *ji, MonoMethod *method, guint8 *code, int code_size,
805 					MonoJitInfoFlags flags, int num_clauses, int num_holes)
806 {
807 	ji->d.method = method;
808 	ji->code_start = code;
809 	ji->code_size = code_size;
810 	ji->num_clauses = num_clauses;
811 	if (flags & JIT_INFO_HAS_GENERIC_JIT_INFO)
812 		ji->has_generic_jit_info = 1;
813 	if (flags & JIT_INFO_HAS_TRY_BLOCK_HOLES)
814 		ji->has_try_block_holes = 1;
815 	if (flags & JIT_INFO_HAS_ARCH_EH_INFO)
816 		ji->has_arch_eh_info = 1;
817 	if (flags & JIT_INFO_HAS_THUNK_INFO)
818 		ji->has_thunk_info = 1;
819 	if (flags & JIT_INFO_HAS_UNWIND_INFO)
820 		ji->has_unwind_info = 1;
821 }
822 
823 /**
824  * mono_jit_info_get_code_start:
825  * \param ji the JIT information handle
826  *
827  * Use this function to get the starting address for the method described by
828  * the \p ji object.  You can use this plus the \c mono_jit_info_get_code_size
829  * to determine the start and end of the native code.
830  *
831  * \returns Starting address with the native code.
832  */
833 gpointer
mono_jit_info_get_code_start(MonoJitInfo * ji)834 mono_jit_info_get_code_start (MonoJitInfo* ji)
835 {
836 	return ji->code_start;
837 }
838 
839 /**
840  * mono_jit_info_get_code_size:
841  * \param ji the JIT information handle
842  *
843  * Use this function to get the code size for the method described by
844  * the \p ji object. You can use this plus the \c mono_jit_info_get_code_start
845  * to determine the start and end of the native code.
846  *
847  * \returns Starting address with the native code.
848  */
849 int
mono_jit_info_get_code_size(MonoJitInfo * ji)850 mono_jit_info_get_code_size (MonoJitInfo* ji)
851 {
852 	return ji->code_size;
853 }
854 
855 /**
856  * mono_jit_info_get_method:
857  * \param ji the JIT information handle
858  *
859  * Use this function to get the \c MonoMethod* that backs
860  * the \p ji object.
861  *
862  * \returns The \c MonoMethod that represents the code tracked
863  * by \p ji.
864  */
865 MonoMethod*
mono_jit_info_get_method(MonoJitInfo * ji)866 mono_jit_info_get_method (MonoJitInfo* ji)
867 {
868 	g_assert (!ji->async);
869 	g_assert (!ji->is_trampoline);
870 	return ji->d.method;
871 }
872 
873 static gpointer
jit_info_key_extract(gpointer value)874 jit_info_key_extract (gpointer value)
875 {
876 	MonoJitInfo *info = (MonoJitInfo*)value;
877 
878 	return info->d.method;
879 }
880 
881 static gpointer*
jit_info_next_value(gpointer value)882 jit_info_next_value (gpointer value)
883 {
884 	MonoJitInfo *info = (MonoJitInfo*)value;
885 
886 	return (gpointer*)&info->n.next_jit_code_hash;
887 }
888 
889 void
mono_jit_code_hash_init(MonoInternalHashTable * jit_code_hash)890 mono_jit_code_hash_init (MonoInternalHashTable *jit_code_hash)
891 {
892 	mono_internal_hash_table_init (jit_code_hash,
893 				       mono_aligned_addr_hash,
894 				       jit_info_key_extract,
895 				       jit_info_next_value);
896 }
897 
898 MonoGenericJitInfo*
mono_jit_info_get_generic_jit_info(MonoJitInfo * ji)899 mono_jit_info_get_generic_jit_info (MonoJitInfo *ji)
900 {
901 	if (ji->has_generic_jit_info)
902 		return (MonoGenericJitInfo*)&ji->clauses [ji->num_clauses];
903 	else
904 		return NULL;
905 }
906 
907 /*
908  * mono_jit_info_get_generic_sharing_context:
909  * @ji: a jit info
910  *
911  * Returns the jit info's generic sharing context, or NULL if it
912  * doesn't have one.
913  */
914 MonoGenericSharingContext*
mono_jit_info_get_generic_sharing_context(MonoJitInfo * ji)915 mono_jit_info_get_generic_sharing_context (MonoJitInfo *ji)
916 {
917 	MonoGenericJitInfo *gi = mono_jit_info_get_generic_jit_info (ji);
918 
919 	if (gi)
920 		return gi->generic_sharing_context;
921 	else
922 		return NULL;
923 }
924 
925 /*
926  * mono_jit_info_set_generic_sharing_context:
927  * @ji: a jit info
928  * @gsctx: a generic sharing context
929  *
930  * Sets the jit info's generic sharing context.  The jit info must
931  * have memory allocated for the context.
932  */
933 void
mono_jit_info_set_generic_sharing_context(MonoJitInfo * ji,MonoGenericSharingContext * gsctx)934 mono_jit_info_set_generic_sharing_context (MonoJitInfo *ji, MonoGenericSharingContext *gsctx)
935 {
936 	MonoGenericJitInfo *gi = mono_jit_info_get_generic_jit_info (ji);
937 
938 	g_assert (gi);
939 
940 	gi->generic_sharing_context = gsctx;
941 }
942 
943 MonoTryBlockHoleTableJitInfo*
mono_jit_info_get_try_block_hole_table_info(MonoJitInfo * ji)944 mono_jit_info_get_try_block_hole_table_info (MonoJitInfo *ji)
945 {
946 	if (ji->has_try_block_holes) {
947 		char *ptr = (char*)&ji->clauses [ji->num_clauses];
948 		if (ji->has_generic_jit_info)
949 			ptr += sizeof (MonoGenericJitInfo);
950 		return (MonoTryBlockHoleTableJitInfo*)ptr;
951 	} else {
952 		return NULL;
953 	}
954 }
955 
956 static int
try_block_hole_table_size(MonoJitInfo * ji)957 try_block_hole_table_size (MonoJitInfo *ji)
958 {
959 	MonoTryBlockHoleTableJitInfo *table;
960 
961 	table = mono_jit_info_get_try_block_hole_table_info (ji);
962 	g_assert (table);
963 	return sizeof (MonoTryBlockHoleTableJitInfo) + table->num_holes * sizeof (MonoTryBlockHoleJitInfo);
964 }
965 
966 MonoArchEHJitInfo*
mono_jit_info_get_arch_eh_info(MonoJitInfo * ji)967 mono_jit_info_get_arch_eh_info (MonoJitInfo *ji)
968 {
969 	if (ji->has_arch_eh_info) {
970 		char *ptr = (char*)&ji->clauses [ji->num_clauses];
971 		if (ji->has_generic_jit_info)
972 			ptr += sizeof (MonoGenericJitInfo);
973 		if (ji->has_try_block_holes)
974 			ptr += try_block_hole_table_size (ji);
975 		return (MonoArchEHJitInfo*)ptr;
976 	} else {
977 		return NULL;
978 	}
979 }
980 
981 MonoThunkJitInfo*
mono_jit_info_get_thunk_info(MonoJitInfo * ji)982 mono_jit_info_get_thunk_info (MonoJitInfo *ji)
983 {
984 	if (ji->has_thunk_info) {
985 		char *ptr = (char*)&ji->clauses [ji->num_clauses];
986 		if (ji->has_generic_jit_info)
987 			ptr += sizeof (MonoGenericJitInfo);
988 		if (ji->has_try_block_holes)
989 			ptr += try_block_hole_table_size (ji);
990 		if (ji->has_arch_eh_info)
991 			ptr += sizeof (MonoArchEHJitInfo);
992 		return (MonoThunkJitInfo*)ptr;
993 	} else {
994 		return NULL;
995 	}
996 }
997 
998 MonoUnwindJitInfo*
mono_jit_info_get_unwind_info(MonoJitInfo * ji)999 mono_jit_info_get_unwind_info (MonoJitInfo *ji)
1000 {
1001 	if (ji->has_unwind_info) {
1002 		char *ptr = (char*)&ji->clauses [ji->num_clauses];
1003 		if (ji->has_generic_jit_info)
1004 			ptr += sizeof (MonoGenericJitInfo);
1005 		if (ji->has_try_block_holes)
1006 			ptr += try_block_hole_table_size (ji);
1007 		if (ji->has_arch_eh_info)
1008 			ptr += sizeof (MonoArchEHJitInfo);
1009 		if (ji->has_thunk_info)
1010 			ptr += sizeof (MonoThunkJitInfo);
1011 		return (MonoUnwindJitInfo*)ptr;
1012 	} else {
1013 		return NULL;
1014 	}
1015 }
1016