1 /**
2  * \file
3  * Images created at runtime.
4  *
5  *
6  * Author:
7  *   Paolo Molaro (lupus@ximian.com)
8  *
9  * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com)
10  * Copyright 2004-2009 Novell, Inc (http://www.novell.com)
11  * Copyright 2011 Rodrigo Kumpera
12  * Copyright 2016 Microsoft
13  *
14  * Licensed under the MIT license. See LICENSE file in the project root for full license information.
15  */
16 
17 #include <config.h>
18 #include <glib.h>
19 #include "mono/metadata/object.h"
20 #include "mono/metadata/dynamic-image-internals.h"
21 #include "mono/metadata/dynamic-stream-internals.h"
22 #include "mono/metadata/gc-internals.h"
23 #include "mono/metadata/metadata-internals.h"
24 #include "mono/metadata/profiler-private.h"
25 #include "mono/metadata/reflection-internals.h"
26 #include "mono/metadata/sre-internals.h"
27 #include "mono/utils/checked-build.h"
28 #include "mono/utils/mono-error-internals.h"
29 #include "mono/utils/mono-os-mutex.h"
30 
31 const unsigned char table_sizes [MONO_TABLE_NUM] = {
32 	MONO_MODULE_SIZE,
33 	MONO_TYPEREF_SIZE,
34 	MONO_TYPEDEF_SIZE,
35 	0,
36 	MONO_FIELD_SIZE,
37 	0,
38 	MONO_METHOD_SIZE,
39 	0,
40 	MONO_PARAM_SIZE,
41 	MONO_INTERFACEIMPL_SIZE,
42 	MONO_MEMBERREF_SIZE,	/* 0x0A */
43 	MONO_CONSTANT_SIZE,
44 	MONO_CUSTOM_ATTR_SIZE,
45 	MONO_FIELD_MARSHAL_SIZE,
46 	MONO_DECL_SECURITY_SIZE,
47 	MONO_CLASS_LAYOUT_SIZE,
48 	MONO_FIELD_LAYOUT_SIZE,	/* 0x10 */
49 	MONO_STAND_ALONE_SIGNATURE_SIZE,
50 	MONO_EVENT_MAP_SIZE,
51 	0,
52 	MONO_EVENT_SIZE,
53 	MONO_PROPERTY_MAP_SIZE,
54 	0,
55 	MONO_PROPERTY_SIZE,
56 	MONO_METHOD_SEMA_SIZE,
57 	MONO_METHODIMPL_SIZE,
58 	MONO_MODULEREF_SIZE,	/* 0x1A */
59 	MONO_TYPESPEC_SIZE,
60 	MONO_IMPLMAP_SIZE,
61 	MONO_FIELD_RVA_SIZE,
62 	0,
63 	0,
64 	MONO_ASSEMBLY_SIZE,	/* 0x20 */
65 	MONO_ASSEMBLY_PROCESSOR_SIZE,
66 	MONO_ASSEMBLYOS_SIZE,
67 	MONO_ASSEMBLYREF_SIZE,
68 	MONO_ASSEMBLYREFPROC_SIZE,
69 	MONO_ASSEMBLYREFOS_SIZE,
70 	MONO_FILE_SIZE,
71 	MONO_EXP_TYPE_SIZE,
72 	MONO_MANIFEST_SIZE,
73 	MONO_NESTED_CLASS_SIZE,
74 
75 	MONO_GENERICPARAM_SIZE,	/* 0x2A */
76 	MONO_METHODSPEC_SIZE,
77 	MONO_GENPARCONSTRAINT_SIZE
78 
79 };
80 
81 // The dynamic images list is only needed to support the mempool reference tracking feature in checked-build.
82 static GPtrArray *dynamic_images;
83 static mono_mutex_t dynamic_images_mutex;
84 
85 static inline void
dynamic_images_lock(void)86 dynamic_images_lock (void)
87 {
88 	mono_os_mutex_lock (&dynamic_images_mutex);
89 }
90 
91 static inline void
dynamic_images_unlock(void)92 dynamic_images_unlock (void)
93 {
94 	mono_os_mutex_unlock (&dynamic_images_mutex);
95 }
96 
97 void
mono_dynamic_images_init(void)98 mono_dynamic_images_init (void)
99 {
100 	mono_os_mutex_init (&dynamic_images_mutex);
101 }
102 
103 #ifndef DISABLE_REFLECTION_EMIT
104 static void
string_heap_init(MonoDynamicStream * sh)105 string_heap_init (MonoDynamicStream *sh)
106 {
107 	mono_dynstream_init (sh);
108 }
109 #endif
110 
111 #ifndef DISABLE_REFLECTION_EMIT
112 static int
mono_blob_entry_hash(const char * str)113 mono_blob_entry_hash (const char* str)
114 {
115 	MONO_REQ_GC_NEUTRAL_MODE;
116 
117 	guint len, h;
118 	const char *end;
119 	len = mono_metadata_decode_blob_size (str, &str);
120 	if (len > 0) {
121 		end = str + len;
122 		h = *str;
123 		for (str += 1; str < end; str++)
124 			h = (h << 5) - h + *str;
125 		return h;
126 	} else {
127 		return 0;
128 	}
129 }
130 
131 static gboolean
mono_blob_entry_equal(const char * str1,const char * str2)132 mono_blob_entry_equal (const char *str1, const char *str2) {
133 	MONO_REQ_GC_NEUTRAL_MODE;
134 
135 	int len, len2;
136 	const char *end1;
137 	const char *end2;
138 	len = mono_metadata_decode_blob_size (str1, &end1);
139 	len2 = mono_metadata_decode_blob_size (str2, &end2);
140 	if (len != len2)
141 		return 0;
142 	return memcmp (end1, end2, len) == 0;
143 }
144 #endif
145 
146 
147 /**
148  * mono_find_dynamic_image_owner:
149  *
150  * Find the dynamic image, if any, which a given pointer is located in the memory of.
151  */
152 MonoImage *
mono_find_dynamic_image_owner(void * ptr)153 mono_find_dynamic_image_owner (void *ptr)
154 {
155 	MonoImage *owner = NULL;
156 	int i;
157 
158 	dynamic_images_lock ();
159 
160 	if (dynamic_images)
161 	{
162 		for (i = 0; !owner && i < dynamic_images->len; ++i) {
163 			MonoImage *image = (MonoImage *)g_ptr_array_index (dynamic_images, i);
164 			if (mono_mempool_contains_addr (image->mempool, ptr))
165 				owner = image;
166 		}
167 	}
168 
169 	dynamic_images_unlock ();
170 
171 	return owner;
172 }
173 
174 static inline void
dynamic_image_lock(MonoDynamicImage * image)175 dynamic_image_lock (MonoDynamicImage *image)
176 {
177 	MONO_ENTER_GC_SAFE;
178 	mono_image_lock ((MonoImage*)image);
179 	MONO_EXIT_GC_SAFE;
180 }
181 
182 static inline void
dynamic_image_unlock(MonoDynamicImage * image)183 dynamic_image_unlock (MonoDynamicImage *image)
184 {
185 	mono_image_unlock ((MonoImage*)image);
186 }
187 
188 #ifndef DISABLE_REFLECTION_EMIT
189 /*
190  * mono_dynamic_image_register_token:
191  *
192  *   Register the TOKEN->OBJ mapping in the mapping table in ASSEMBLY. This is required for
193  * the Module.ResolveXXXToken () methods to work.
194  */
195 void
mono_dynamic_image_register_token(MonoDynamicImage * assembly,guint32 token,MonoObjectHandle obj,int how_collide)196 mono_dynamic_image_register_token (MonoDynamicImage *assembly, guint32 token, MonoObjectHandle obj, int how_collide)
197 {
198 	MONO_REQ_GC_UNSAFE_MODE;
199 
200 	g_assert (!MONO_HANDLE_IS_NULL (obj));
201 	g_assert (strcmp (mono_handle_class (obj)->name, "EnumBuilder"));
202 	dynamic_image_lock (assembly);
203 	MonoObject *prev = (MonoObject *)mono_g_hash_table_lookup (assembly->tokens, GUINT_TO_POINTER (token));
204 	if (prev) {
205 		switch (how_collide) {
206 		case MONO_DYN_IMAGE_TOK_NEW:
207 			g_warning ("%s: Unexpected previous object when called with MONO_DYN_IMAGE_TOK_NEW", __func__);
208 			break;
209 		case MONO_DYN_IMAGE_TOK_SAME_OK:
210 			if (prev != MONO_HANDLE_RAW (obj)) {
211 				g_warning ("%s: condition `prev == MONO_HANDLE_RAW (obj)' not met", __func__);
212 			}
213 			break;
214 		case MONO_DYN_IMAGE_TOK_REPLACE:
215 			break;
216 		default:
217 			g_assert_not_reached ();
218 		}
219 	}
220 	mono_g_hash_table_insert (assembly->tokens, GUINT_TO_POINTER (token), MONO_HANDLE_RAW (obj));
221 	dynamic_image_unlock (assembly);
222 }
223 #else
224 void
mono_dynamic_image_register_token(MonoDynamicImage * assembly,guint32 token,MonoObjectHandle obj,int how_collide)225 mono_dynamic_image_register_token (MonoDynamicImage *assembly, guint32 token, MonoObjectHandle obj, int how_collide)
226 {
227 }
228 #endif
229 
230 static MonoObject*
lookup_dyn_token(MonoDynamicImage * assembly,guint32 token)231 lookup_dyn_token (MonoDynamicImage *assembly, guint32 token)
232 {
233 	MONO_REQ_GC_UNSAFE_MODE;
234 
235 	MonoObject *obj;
236 
237 	dynamic_image_lock (assembly);
238 	obj = (MonoObject *)mono_g_hash_table_lookup (assembly->tokens, GUINT_TO_POINTER (token));
239 	dynamic_image_unlock (assembly);
240 
241 	return obj;
242 }
243 
244 #ifndef DISABLE_REFLECTION_EMIT
245 MonoObjectHandle
mono_dynamic_image_get_registered_token(MonoDynamicImage * dynimage,guint32 token,MonoError * error)246 mono_dynamic_image_get_registered_token (MonoDynamicImage *dynimage, guint32 token, MonoError *error)
247 {
248 	error_init (error);
249 	return MONO_HANDLE_NEW (MonoObject, lookup_dyn_token (dynimage, token));
250 }
251 #else /* DISABLE_REFLECTION_EMIT */
252 MonoObjectHandle
mono_dynamic_image_get_registered_token(MonoDynamicImage * dynimage,guint32 token,MonoError * error)253 mono_dynamic_image_get_registered_token (MonoDynamicImage *dynimage, guint32 token, MonoError *error)
254 {
255 	g_assert_not_reached ();
256 	return NULL_HANDLE;
257 }
258 #endif
259 
260 /**
261  *
262  * mono_dynamic_image_is_valid_token:
263  *
264  * Returns TRUE if token is valid in the given image.
265  *
266  */
267 gboolean
mono_dynamic_image_is_valid_token(MonoDynamicImage * image,guint32 token)268 mono_dynamic_image_is_valid_token (MonoDynamicImage *image, guint32 token)
269 {
270 	return lookup_dyn_token (image, token) != NULL;
271 }
272 
273 #ifndef DISABLE_REFLECTION_EMIT
274 
275 #endif /* DISABLE_REFLECTION_EMIT */
276 
277 #ifndef DISABLE_REFLECTION_EMIT
278 /**
279  * mono_reflection_lookup_dynamic_token:
280  *
281  * Finish the Builder object pointed to by TOKEN and return the corresponding
282  * runtime structure. If HANDLE_CLASS is not NULL, it is set to the class required by
283  * mono_ldtoken. If valid_token is TRUE, assert if it is not found in the token->object
284  * mapping table.
285  *
286  * LOCKING: Take the loader lock
287  */
288 gpointer
mono_reflection_lookup_dynamic_token(MonoImage * image,guint32 token,gboolean valid_token,MonoClass ** handle_class,MonoGenericContext * context,MonoError * error)289 mono_reflection_lookup_dynamic_token (MonoImage *image, guint32 token, gboolean valid_token, MonoClass **handle_class, MonoGenericContext *context, MonoError *error)
290 {
291 	MonoDynamicImage *assembly = (MonoDynamicImage*)image;
292 	MonoObject *obj;
293 	MonoClass *klass;
294 
295 	error_init (error);
296 
297 	obj = lookup_dyn_token (assembly, token);
298 	if (!obj) {
299 		if (valid_token)
300 			g_error ("Could not find required dynamic token 0x%08x", token);
301 		else {
302 			mono_error_set_execution_engine (error, "Could not find dynamic token 0x%08x", token);
303 			return NULL;
304 		}
305 	}
306 
307 	if (!handle_class)
308 		handle_class = &klass;
309 	gpointer result = mono_reflection_resolve_object (image, obj, handle_class, context, error);
310 	return result;
311 }
312 #else /* DISABLE_REFLECTION_EMIT */
313 gpointer
mono_reflection_lookup_dynamic_token(MonoImage * image,guint32 token,gboolean valid_token,MonoClass ** handle_class,MonoGenericContext * context,MonoError * error)314 mono_reflection_lookup_dynamic_token (MonoImage *image, guint32 token, gboolean valid_token, MonoClass **handle_class, MonoGenericContext *context, MonoError *error)
315 {
316 	error_init (error);
317 	return NULL;
318 }
319 #endif /* DISABLE_REFLECTION_EMIT */
320 
321 #ifndef DISABLE_REFLECTION_EMIT
322 MonoDynamicImage*
mono_dynamic_image_create(MonoDynamicAssembly * assembly,char * assembly_name,char * module_name)323 mono_dynamic_image_create (MonoDynamicAssembly *assembly, char *assembly_name, char *module_name)
324 {
325 	static const guchar entrycode [16] = {0xff, 0x25, 0};
326 	MonoDynamicImage *image;
327 	int i;
328 
329 	const char *version;
330 
331 	if (!strcmp (mono_get_runtime_info ()->framework_version, "2.1"))
332 		version = "v2.0.50727"; /* HACK: SL 2 enforces the .net 2 metadata version */
333 	else
334 		version = mono_get_runtime_info ()->runtime_version;
335 
336 	image = g_new0 (MonoDynamicImage, 1);
337 
338 	MONO_PROFILER_RAISE (image_loading, (&image->image));
339 
340 	/*g_print ("created image %p\n", image);*/
341 	/* keep in sync with image.c */
342 	image->image.name = assembly_name;
343 	image->image.assembly_name = image->image.name; /* they may be different */
344 	image->image.module_name = module_name;
345 	image->image.version = g_strdup (version);
346 	image->image.md_version_major = 1;
347 	image->image.md_version_minor = 1;
348 	image->image.dynamic = TRUE;
349 
350 	image->image.references = g_new0 (MonoAssembly*, 1);
351 	image->image.references [0] = NULL;
352 
353 	mono_image_init (&image->image);
354 
355 	image->token_fixups = mono_g_hash_table_new_type ((GHashFunc)mono_object_hash, NULL, MONO_HASH_KEY_GC, MONO_ROOT_SOURCE_REFLECTION, "dynamic module token fixups table");
356 	image->method_to_table_idx = g_hash_table_new (NULL, NULL);
357 	image->field_to_table_idx = g_hash_table_new (NULL, NULL);
358 	image->method_aux_hash = g_hash_table_new (NULL, NULL);
359 	image->vararg_aux_hash = g_hash_table_new (NULL, NULL);
360 	image->handleref = g_hash_table_new (NULL, NULL);
361 	image->tokens = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_VALUE_GC, MONO_ROOT_SOURCE_REFLECTION, "dynamic module tokens table");
362 	image->generic_def_objects = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_VALUE_GC, MONO_ROOT_SOURCE_REFLECTION, "dynamic module generic definitions table");
363 	image->typespec = g_hash_table_new ((GHashFunc)mono_metadata_type_hash, (GCompareFunc)mono_metadata_type_equal);
364 	image->typeref = g_hash_table_new ((GHashFunc)mono_metadata_type_hash, (GCompareFunc)mono_metadata_type_equal);
365 	image->blob_cache = g_hash_table_new ((GHashFunc)mono_blob_entry_hash, (GCompareFunc)mono_blob_entry_equal);
366 	image->gen_params = g_ptr_array_new ();
367 	image->remapped_tokens = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_VALUE_GC, MONO_ROOT_SOURCE_REFLECTION, "dynamic module remapped tokens table");
368 
369 	/*g_print ("string heap create for image %p (%s)\n", image, module_name);*/
370 	string_heap_init (&image->sheap);
371 	mono_dynstream_add_data (&image->us, "", 1);
372 	mono_dynamic_image_add_to_blob_cached (image, (char*) "", 1, NULL, 0);
373 	/* import tables... */
374 	mono_dynstream_add_data (&image->code, (char*)entrycode, sizeof (entrycode));
375 	image->iat_offset = mono_dynstream_add_zero (&image->code, 8); /* two IAT entries */
376 	image->idt_offset = mono_dynstream_add_zero (&image->code, 2 * sizeof (MonoIDT)); /* two IDT entries */
377 	image->imp_names_offset = mono_dynstream_add_zero (&image->code, 2); /* flags for name entry */
378 	mono_dynstream_add_data (&image->code, "_CorExeMain", 12);
379 	mono_dynstream_add_data (&image->code, "mscoree.dll", 12);
380 	image->ilt_offset = mono_dynstream_add_zero (&image->code, 8); /* two ILT entries */
381 	mono_dynstream_data_align (&image->code);
382 
383 	image->cli_header_offset = mono_dynstream_add_zero (&image->code, sizeof (MonoCLIHeader));
384 
385 	for (i=0; i < MONO_TABLE_NUM; ++i) {
386 		image->tables [i].next_idx = 1;
387 		image->tables [i].columns = table_sizes [i];
388 	}
389 
390 	image->image.assembly = (MonoAssembly*)assembly;
391 	image->run = assembly->run;
392 	image->save = assembly->save;
393 	image->pe_kind = 0x1; /* ILOnly */
394 	image->machine = 0x14c; /* I386 */
395 
396 	MONO_PROFILER_RAISE (image_loaded, (&image->image));
397 
398 	dynamic_images_lock ();
399 
400 	if (!dynamic_images)
401 		dynamic_images = g_ptr_array_new ();
402 
403 	g_ptr_array_add (dynamic_images, image);
404 
405 	dynamic_images_unlock ();
406 
407 	return image;
408 }
409 #else /* DISABLE_REFLECTION_EMIT */
410 MonoDynamicImage*
mono_dynamic_image_create(MonoDynamicAssembly * assembly,char * assembly_name,char * module_name)411 mono_dynamic_image_create (MonoDynamicAssembly *assembly, char *assembly_name, char *module_name)
412 {
413 	g_assert_not_reached ();
414 	return NULL;
415 }
416 #endif /* DISABLE_REFLECTION_EMIT */
417 
418 guint32
mono_dynamic_image_add_to_blob_cached(MonoDynamicImage * assembly,char * b1,int s1,char * b2,int s2)419 mono_dynamic_image_add_to_blob_cached (MonoDynamicImage *assembly, char *b1, int s1, char *b2, int s2)
420 {
421 	MONO_REQ_GC_NEUTRAL_MODE;
422 
423 	guint32 idx;
424 	char *copy;
425 	gpointer oldkey, oldval;
426 
427 	copy = (char *)g_malloc (s1+s2);
428 	memcpy (copy, b1, s1);
429 	memcpy (copy + s1, b2, s2);
430 	if (g_hash_table_lookup_extended (assembly->blob_cache, copy, &oldkey, &oldval)) {
431 		g_free (copy);
432 		idx = GPOINTER_TO_UINT (oldval);
433 	} else {
434 		idx = mono_dynstream_add_data (&assembly->blob, b1, s1);
435 		mono_dynstream_add_data (&assembly->blob, b2, s2);
436 		g_hash_table_insert (assembly->blob_cache, copy, GUINT_TO_POINTER (idx));
437 	}
438 	return idx;
439 }
440 
441 void
mono_dynimage_alloc_table(MonoDynamicTable * table,guint nrows)442 mono_dynimage_alloc_table (MonoDynamicTable *table, guint nrows)
443 {
444 	MONO_REQ_GC_NEUTRAL_MODE;
445 
446 	table->rows = nrows;
447 	g_assert (table->columns);
448 	if (nrows + 1 >= table->alloc_rows) {
449 		while (nrows + 1 >= table->alloc_rows) {
450 			if (table->alloc_rows == 0)
451 				table->alloc_rows = 16;
452 			else
453 				table->alloc_rows *= 2;
454 		}
455 
456 		table->values = (guint32 *)g_renew (guint32, table->values, (table->alloc_rows) * table->columns);
457 	}
458 }
459 
460 
461 static void
free_blob_cache_entry(gpointer key,gpointer val,gpointer user_data)462 free_blob_cache_entry (gpointer key, gpointer val, gpointer user_data)
463 {
464 	g_free (key);
465 }
466 
467 static void
release_hashtable(MonoGHashTable ** hash)468 release_hashtable (MonoGHashTable **hash)
469 {
470 	if (*hash) {
471 		mono_g_hash_table_destroy (*hash);
472 		*hash = NULL;
473 	}
474 }
475 
476 void
mono_dynamic_image_release_gc_roots(MonoDynamicImage * image)477 mono_dynamic_image_release_gc_roots (MonoDynamicImage *image)
478 {
479 	release_hashtable (&image->token_fixups);
480 	release_hashtable (&image->tokens);
481 	release_hashtable (&image->remapped_tokens);
482 	release_hashtable (&image->generic_def_objects);
483 }
484 
485 // Free dynamic image pass one: Free resources but not image itself
486 void
mono_dynamic_image_free(MonoDynamicImage * image)487 mono_dynamic_image_free (MonoDynamicImage *image)
488 {
489 	MonoDynamicImage *di = image;
490 	GList *list;
491 	int i;
492 
493 	if (di->typespec)
494 		g_hash_table_destroy (di->typespec);
495 	if (di->typeref)
496 		g_hash_table_destroy (di->typeref);
497 	if (di->handleref)
498 		g_hash_table_destroy (di->handleref);
499 	if (di->tokens)
500 		mono_g_hash_table_destroy (di->tokens);
501 	if (di->remapped_tokens)
502 		mono_g_hash_table_destroy (di->remapped_tokens);
503 	if (di->generic_def_objects)
504 		mono_g_hash_table_destroy (di->generic_def_objects);
505 	if (di->blob_cache) {
506 		g_hash_table_foreach (di->blob_cache, free_blob_cache_entry, NULL);
507 		g_hash_table_destroy (di->blob_cache);
508 	}
509 	if (di->standalonesig_cache)
510 		g_hash_table_destroy (di->standalonesig_cache);
511 	for (list = di->array_methods; list; list = list->next) {
512 		ArrayMethod *am = (ArrayMethod *)list->data;
513 		mono_sre_array_method_free (am);
514 	}
515 	g_list_free (di->array_methods);
516 	if (di->gen_params) {
517 		for (i = 0; i < di->gen_params->len; i++) {
518 			GenericParamTableEntry *entry = (GenericParamTableEntry *)g_ptr_array_index (di->gen_params, i);
519 			mono_sre_generic_param_table_entry_free (entry);
520 		}
521 	 	g_ptr_array_free (di->gen_params, TRUE);
522 	}
523 	if (di->token_fixups)
524 		mono_g_hash_table_destroy (di->token_fixups);
525 	if (di->method_to_table_idx)
526 		g_hash_table_destroy (di->method_to_table_idx);
527 	if (di->field_to_table_idx)
528 		g_hash_table_destroy (di->field_to_table_idx);
529 	if (di->method_aux_hash)
530 		g_hash_table_destroy (di->method_aux_hash);
531 	if (di->vararg_aux_hash)
532 		g_hash_table_destroy (di->vararg_aux_hash);
533 	g_free (di->strong_name);
534 	g_free (di->win32_res);
535 	if (di->public_key)
536 		g_free (di->public_key);
537 
538 	/*g_print ("string heap destroy for image %p\n", di);*/
539 	mono_dynamic_stream_reset (&di->sheap);
540 	mono_dynamic_stream_reset (&di->code);
541 	mono_dynamic_stream_reset (&di->resources);
542 	mono_dynamic_stream_reset (&di->us);
543 	mono_dynamic_stream_reset (&di->blob);
544 	mono_dynamic_stream_reset (&di->tstream);
545 	mono_dynamic_stream_reset (&di->guid);
546 	for (i = 0; i < MONO_TABLE_NUM; ++i) {
547 		g_free (di->tables [i].values);
548 	}
549 
550 	dynamic_images_lock ();
551 
552 	if (dynamic_images)
553 		g_ptr_array_remove (dynamic_images, di);
554 
555 	dynamic_images_unlock ();
556 }
557 
558 // Free dynamic image pass two: Free image itself (might never get called in some debug modes)
559 void
mono_dynamic_image_free_image(MonoDynamicImage * image)560 mono_dynamic_image_free_image (MonoDynamicImage *image)
561 {
562 	g_free (image);
563 }
564