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