2002-04-20 Gonzalo Paniagua Javier <gonzalo@ximian.com>
[mono.git] / mono / metadata / image.c
1 /*
2  * image.c: Routines for manipulating an image stored in an
3  * extended PE/COFF file.
4  * 
5  * Author:
6  *   Miguel de Icaza (miguel@ximian.com)
7  *
8  * (C) 2001 Ximian, Inc.  http://www.ximian.com
9  *
10  * TODO:
11  *   Implement big-endian versions of the reading routines.
12  */
13 #include <config.h>
14 #include <stdio.h>
15 #include <glib.h>
16 #include <errno.h>
17 #include <time.h>
18 #include <string.h>
19 #include "image.h"
20 #include "cil-coff.h"
21 #include "rawbuffer.h"
22 #include "mono-endian.h"
23 #include "private.h"
24 #include "tabledefs.h"
25
26 #define INVALID_ADDRESS 0xffffffff
27
28 /*
29  * Keeps track of the various assemblies loaded
30  */
31 static GHashTable *loaded_images_hash;
32
33 guint32
34 mono_cli_rva_image_map (MonoCLIImageInfo *iinfo, guint32 addr)
35 {
36         const int top = iinfo->cli_section_count;
37         MonoSectionTable *tables = iinfo->cli_section_tables;
38         int i;
39         
40         for (i = 0; i < top; i++){
41                 if ((addr >= tables->st_virtual_address) &&
42                     (addr < tables->st_virtual_address + tables->st_raw_data_size)){
43                         return addr - tables->st_virtual_address + tables->st_raw_data_ptr;
44                 }
45                 tables++;
46         }
47         return INVALID_ADDRESS;
48 }
49
50 char *
51 mono_cli_rva_map (MonoCLIImageInfo *iinfo, guint32 addr)
52 {
53         const int top = iinfo->cli_section_count;
54         MonoSectionTable *tables = iinfo->cli_section_tables;
55         int i;
56         
57         for (i = 0; i < top; i++){
58                 if ((addr >= tables->st_virtual_address) &&
59                     (addr < tables->st_virtual_address + tables->st_raw_data_size)){
60                         return (char*)iinfo->cli_sections [i] +
61                                 (addr - tables->st_virtual_address);
62                 }
63                 tables++;
64         }
65         return NULL;
66 }
67
68 /**
69  * mono_image_ensure_section_idx:
70  * @image: The image we are operating on
71  * @section: section number that we will load/map into memory
72  *
73  * This routine makes sure that we have an in-memory copy of
74  * an image section (.text, .rsrc, .data).
75  *
76  * Returns: TRUE on success
77  */
78 int
79 mono_image_ensure_section_idx (MonoImage *image, int section)
80 {
81         MonoCLIImageInfo *iinfo = image->image_info;
82         MonoSectionTable *sect;
83         gboolean writable;
84         
85         g_return_val_if_fail (section < iinfo->cli_section_count, FALSE);
86
87         if (iinfo->cli_sections [section] != NULL)
88                 return TRUE;
89
90         sect = &iinfo->cli_section_tables [section];
91         
92         writable = sect->st_flags & SECT_FLAGS_MEM_WRITE;
93
94         iinfo->cli_sections [section] = mono_raw_buffer_load (
95                 fileno (image->f), writable,
96                 sect->st_raw_data_ptr, sect->st_raw_data_size);
97
98         if (iinfo->cli_sections [section] == NULL)
99                 return FALSE;
100
101         return TRUE;
102 }
103
104 /**
105  * mono_image_ensure_section:
106  * @image: The image we are operating on
107  * @section: section name that we will load/map into memory
108  *
109  * This routine makes sure that we have an in-memory copy of
110  * an image section (.text, .rsrc, .data).
111  *
112  * Returns: TRUE on success
113  */
114 int
115 mono_image_ensure_section (MonoImage *image, const char *section)
116 {
117         MonoCLIImageInfo *ii = image->image_info;
118         int i;
119         
120         for (i = 0; i < ii->cli_section_count; i++){
121                 if (strncmp (ii->cli_section_tables [i].st_name, section, 8) != 0)
122                         continue;
123                 
124                 return mono_image_ensure_section_idx (image, i);
125         }
126         return FALSE;
127 }
128
129 static int
130 load_section_tables (MonoImage *image, MonoCLIImageInfo *iinfo)
131 {
132         const int top = iinfo->cli_header.coff.coff_sections;
133         int i;
134
135         iinfo->cli_section_count = top;
136         iinfo->cli_section_tables = g_new0 (MonoSectionTable, top);
137         iinfo->cli_sections = g_new0 (void *, top);
138         
139         for (i = 0; i < top; i++){
140                 MonoSectionTable *t = &iinfo->cli_section_tables [i];
141                 
142                 if (fread (t, sizeof (MonoSectionTable), 1, image->f) != 1)
143                         return FALSE;
144
145 #if G_BYTE_ORDER != G_LITTLE_ENDIAN
146                 t->st_virtual_size = GUINT32_FROM_LE (t->st_virtual_size);
147                 t->st_virtual_address = GUINT32_FROM_LE (t->st_virtual_address);
148                 t->st_raw_data_size = GUINT32_FROM_LE (t->st_raw_data_size);
149                 t->st_raw_data_ptr = GUINT32_FROM_LE (t->st_raw_data_ptr);
150                 t->st_reloc_ptr = GUINT32_FROM_LE (t->st_reloc_ptr);
151                 t->st_lineno_ptr = GUINT32_FROM_LE (t->st_lineno_ptr);
152                 t->st_reloc_count = GUINT16_FROM_LE (t->st_reloc_count);
153                 t->st_line_count = GUINT16_FROM_LE (t->st_line_count);
154                 t->st_flags = GUINT32_FROM_LE (t->st_flags);
155 #endif
156                 /* consistency checks here */
157         }
158
159         for (i = 0; i < top; i++)
160                 if (!mono_image_ensure_section_idx (image, i))
161                         return FALSE;
162         
163         return TRUE;
164 }
165
166 static gboolean
167 load_cli_header (MonoImage *image, MonoCLIImageInfo *iinfo)
168 {
169         guint32 offset;
170         int n;
171         
172         offset = mono_cli_rva_image_map (iinfo, iinfo->cli_header.datadir.pe_cli_header.rva);
173         if (offset == INVALID_ADDRESS)
174                 return FALSE;
175
176         if (fseek (image->f, offset, SEEK_SET) != 0)
177                 return FALSE;
178         
179         if ((n = fread (&iinfo->cli_cli_header, sizeof (MonoCLIHeader), 1, image->f)) != 1)
180                 return FALSE;
181
182 #if G_BYTE_ORDER != G_LITTLE_ENDIAN
183 #define SWAP32(x) (x) = GUINT32_FROM_LE ((x))
184 #define SWAP16(x) (x) = GUINT16_FROM_LE ((x))
185 #define SWAPPDE(x) do { (x).rva = GUINT32_FROM_LE ((x).rva); (x).size = GUINT32_FROM_LE ((x).size);} while (0)
186         SWAP32 (iinfo->cli_cli_header.ch_size);
187         SWAP32 (iinfo->cli_cli_header.ch_flags);
188         SWAP32 (iinfo->cli_cli_header.ch_entry_point);
189         SWAP16 (iinfo->cli_cli_header.ch_runtime_major);
190         SWAP16 (iinfo->cli_cli_header.ch_runtime_minor);
191         SWAPPDE (iinfo->cli_cli_header.ch_metadata);
192         SWAPPDE (iinfo->cli_cli_header.ch_resources);
193         SWAPPDE (iinfo->cli_cli_header.ch_strong_name);
194         SWAPPDE (iinfo->cli_cli_header.ch_code_manager_table);
195         SWAPPDE (iinfo->cli_cli_header.ch_vtable_fixups);
196         SWAPPDE (iinfo->cli_cli_header.ch_export_address_table_jumps);
197         SWAPPDE (iinfo->cli_cli_header.ch_eeinfo_table);
198         SWAPPDE (iinfo->cli_cli_header.ch_helper_table);
199         SWAPPDE (iinfo->cli_cli_header.ch_dynamic_info);
200         SWAPPDE (iinfo->cli_cli_header.ch_delay_load_info);
201         SWAPPDE (iinfo->cli_cli_header.ch_module_image);
202         SWAPPDE (iinfo->cli_cli_header.ch_external_fixups);
203         SWAPPDE (iinfo->cli_cli_header.ch_ridmap);
204         SWAPPDE (iinfo->cli_cli_header.ch_debug_map);
205         SWAPPDE (iinfo->cli_cli_header.ch_ip_map);
206 #undef SWAP32
207 #undef SWAP16
208 #undef SWAPPDE
209 #endif
210         /* Catch new uses of the fields that are supposed to be zero */
211
212         if ((iinfo->cli_cli_header.ch_eeinfo_table.rva != 0) ||
213             (iinfo->cli_cli_header.ch_helper_table.rva != 0) ||
214             (iinfo->cli_cli_header.ch_dynamic_info.rva != 0) ||
215             (iinfo->cli_cli_header.ch_delay_load_info.rva != 0) ||
216             (iinfo->cli_cli_header.ch_module_image.rva != 0) ||
217             (iinfo->cli_cli_header.ch_external_fixups.rva != 0) ||
218             (iinfo->cli_cli_header.ch_ridmap.rva != 0) ||
219             (iinfo->cli_cli_header.ch_debug_map.rva != 0) ||
220             (iinfo->cli_cli_header.ch_ip_map.rva != 0)){
221
222                 /*
223                  * No need to scare people who are testing this, I am just
224                  * labelling this as a LAMESPEC
225                  */
226                 /* g_warning ("Some fields in the CLI header which should have been zero are not zero"); */
227
228         }
229             
230         return TRUE;
231 }
232
233 static gboolean
234 load_metadata_ptrs (MonoImage *image, MonoCLIImageInfo *iinfo)
235 {
236         guint32 offset, size;
237         guint16 streams;
238         int i;
239         char *ptr;
240         
241         offset = mono_cli_rva_image_map (iinfo, iinfo->cli_cli_header.ch_metadata.rva);
242         size = iinfo->cli_cli_header.ch_metadata.size;
243         
244         image->raw_metadata = mono_raw_buffer_load (fileno (image->f), FALSE, offset, size);
245         if (image->raw_metadata == NULL)
246                 return FALSE;
247
248         ptr = image->raw_metadata;
249
250         if (strncmp (ptr, "BSJB", 4) == 0){
251                 guint32 version_string_len;
252
253                 ptr += 12;
254                 version_string_len = read32 (ptr);
255                 ptr += 4;
256                 ptr += version_string_len;
257                 if (((guint32) ptr) % 4)
258                         ptr += 4 - (((guint32) ptr) %4);
259         } else
260                 return FALSE;
261
262         /* skip over flags */
263         ptr += 2;
264         
265         streams = read16 (ptr);
266         ptr += 2;
267
268         for (i = 0; i < streams; i++){
269                 if (strncmp (ptr + 8, "#~", 3) == 0){
270                         image->heap_tables.data = image->raw_metadata + read32 (ptr);
271                         image->heap_tables.size = read32 (ptr + 4);
272                         ptr += 8 + 3;
273                 } else if (strncmp (ptr + 8, "#Strings", 9) == 0){
274                         image->heap_strings.data = image->raw_metadata + read32 (ptr);
275                         image->heap_strings.size = read32 (ptr + 4);
276                         ptr += 8 + 9;
277                 } else if (strncmp (ptr + 8, "#US", 4) == 0){
278                         image->heap_us.data = image->raw_metadata + read32 (ptr);
279                         image->heap_us.size = read32 (ptr + 4);
280                         ptr += 8 + 4;
281                 } else if (strncmp (ptr + 8, "#Blob", 6) == 0){
282                         image->heap_blob.data = image->raw_metadata + read32 (ptr);
283                         image->heap_blob.size = read32 (ptr + 4);
284                         ptr += 8 + 6;
285                 } else if (strncmp (ptr + 8, "#GUID", 6) == 0){
286                         image->heap_guid.data = image->raw_metadata + read32 (ptr);
287                         image->heap_guid.size = read32 (ptr + 4);
288                         ptr += 8 + 6;
289                 } else {
290                         g_message ("Unknown heap type: %s\n", ptr + 8);
291                         ptr += 8 + strlen (ptr) + 1;
292                 }
293                 if (((guint32)ptr) % 4){
294                         ptr += 4 - (((guint32)ptr) % 4);
295                 }
296         }
297         return TRUE;
298 }
299
300 /*
301  * Load representation of logical metadata tables, from the "#~" stream
302  */
303 static gboolean
304 load_tables (MonoImage *image)
305 {
306         const char *heap_tables = image->heap_tables.data;
307         const guint32 *rows;
308         guint64 valid_mask;
309         int valid = 0, table;
310         int heap_sizes;
311         
312         heap_sizes = heap_tables [6];
313         image->idx_string_wide = ((heap_sizes & 0x01) == 1);
314         image->idx_guid_wide   = ((heap_sizes & 0x02) == 2);
315         image->idx_blob_wide   = ((heap_sizes & 0x04) == 4);
316         
317         valid_mask = read64 (heap_tables + 8);
318         rows = (const guint32 *) (heap_tables + 24);
319         
320         for (table = 0; table < 64; table++){
321                 if ((valid_mask & ((guint64) 1 << table)) == 0){
322                         image->tables [table].rows = 0;
323                         continue;
324                 }
325                 if (table > 0x2b) {
326                         g_warning("bits in valid must be zero above 0x2b (II - 23.1.6)");
327                 }
328                 image->tables [table].rows = read32 (rows);
329                 rows++;
330                 valid++;
331         }
332
333         image->tables_base = (heap_tables + 24) + (4 * valid);
334
335         /* They must be the same */
336         g_assert ((const void *) image->tables_base == (const void *) rows);
337
338         mono_metadata_compute_table_bases (image);
339         return TRUE;
340 }
341
342 static gboolean
343 load_metadata (MonoImage *image, MonoCLIImageInfo *iinfo)
344 {
345         if (!load_metadata_ptrs (image, iinfo))
346                 return FALSE;
347
348         return load_tables (image);
349 }
350
351 static void
352 load_class_names (MonoImage *image)
353 {
354         MonoTableInfo  *t = &image->tables [MONO_TABLE_TYPEDEF];
355         guint32 cols [MONO_TYPEDEF_SIZE];
356         const char *name;
357         const char *nspace;
358         GHashTable *nspace_table;
359         GHashTable *name_cache = image->name_cache;
360         guint32 i, visib;
361
362         for (i = 1; i <= t->rows; ++i) {
363                 mono_metadata_decode_row (t, i - 1, cols, MONO_TYPEDEF_SIZE);
364                 /* nested types are accessed from the nesting name */
365                 visib = cols [MONO_TYPEDEF_FLAGS] & TYPE_ATTRIBUTE_VISIBILITY_MASK;
366                 if (visib > TYPE_ATTRIBUTE_PUBLIC && visib < TYPE_ATTRIBUTE_NESTED_ASSEMBLY)
367                         continue;
368                 name = mono_metadata_string_heap (image, cols [MONO_TYPEDEF_NAME]);
369                 nspace = mono_metadata_string_heap (image, cols [MONO_TYPEDEF_NAMESPACE]);
370                 if (!(nspace_table = g_hash_table_lookup (name_cache, nspace))) {
371                         nspace_table = g_hash_table_new (g_str_hash, g_str_equal);
372                         g_hash_table_insert (name_cache, (char *)nspace, (char *)nspace_table);
373                 }
374                 g_hash_table_insert (nspace_table, (char *) name, GUINT_TO_POINTER (i));
375         }
376 }
377
378 static MonoImage *
379 do_mono_image_open (const char *fname, MonoImageOpenStatus *status)
380 {
381         MonoCLIImageInfo *iinfo;
382         MonoDotNetHeader *header;
383         MonoMSDOSHeader msdos;
384         MonoImage *image;
385         int n;
386
387         image = g_new0 (MonoImage, 1);
388         image->f = fopen (fname, "rb");
389         image->name = g_strdup (fname);
390         iinfo = g_new0 (MonoCLIImageInfo, 1);
391         image->image_info = iinfo;
392
393         image->method_cache = g_hash_table_new (g_direct_hash, g_direct_equal);
394         image->class_cache = g_hash_table_new (g_direct_hash, g_direct_equal);
395         image->name_cache = g_hash_table_new (g_str_hash, g_str_equal);
396         image->array_cache = g_hash_table_new (g_direct_hash, g_direct_equal);
397
398         header = &iinfo->cli_header;
399                 
400         if (image->f == NULL){
401                 if (status)
402                         *status = MONO_IMAGE_ERROR_ERRNO;
403                 mono_image_close (image);
404                 return NULL;
405         }
406
407         if (status)
408                 *status = MONO_IMAGE_IMAGE_INVALID;
409         
410         if (fread (&msdos, sizeof (msdos), 1, image->f) != 1)
411                 goto invalid_image;
412         
413         if (!(msdos.msdos_header [0] == 'M' && msdos.msdos_header [1] == 'Z'))
414                 goto invalid_image;
415         
416         msdos.pe_offset = GUINT32_FROM_LE (msdos.pe_offset);
417
418         if (msdos.pe_offset != sizeof (msdos))
419                 fseek (image->f, msdos.pe_offset, SEEK_SET);
420         
421         if ((n = fread (header, sizeof (MonoDotNetHeader), 1, image->f)) != 1)
422                 goto invalid_image;
423
424 #if G_BYTE_ORDER != G_LITTLE_ENDIAN
425 #define SWAP32(x) (x) = GUINT32_FROM_LE ((x))
426 #define SWAP16(x) (x) = GUINT16_FROM_LE ((x))
427 #define SWAPPDE(x) do { (x).rva = GUINT32_FROM_LE ((x).rva); (x).size = GUINT32_FROM_LE ((x).size);} while (0)
428         SWAP32 (header->coff.coff_time);
429         SWAP32 (header->coff.coff_symptr);
430         SWAP32 (header->coff.coff_symcount);
431         SWAP16 (header->coff.coff_machine);
432         SWAP16 (header->coff.coff_sections);
433         SWAP16 (header->coff.coff_opt_header_size);
434         SWAP16 (header->coff.coff_attributes);
435         /* MonoPEHeader */
436         SWAP32 (header->pe.pe_code_size);
437         SWAP32 (header->pe.pe_data_size);
438         SWAP32 (header->pe.pe_uninit_data_size);
439         SWAP32 (header->pe.pe_rva_entry_point);
440         SWAP32 (header->pe.pe_rva_code_base);
441         SWAP32 (header->pe.pe_rva_data_base);
442         SWAP16 (header->pe.pe_magic);
443
444         /* MonoPEHeaderNT: not used yet */
445         SWAP32  (header->nt.pe_image_base);     /* must be 0x400000 */
446         SWAP32  (header->nt.pe_section_align);       /* must be 8192 */
447         SWAP32  (header->nt.pe_file_alignment);      /* must be 512 or 4096 */
448         SWAP16  (header->nt.pe_os_major);            /* must be 4 */
449         SWAP16  (header->nt.pe_os_minor);            /* must be 0 */
450         SWAP16  (header->nt.pe_user_major);
451         SWAP16  (header->nt.pe_user_minor);
452         SWAP16  (header->nt.pe_subsys_major);
453         SWAP16  (header->nt.pe_subsys_minor);
454         SWAP32  (header->nt.pe_reserved_1);
455         SWAP32  (header->nt.pe_image_size);
456         SWAP32  (header->nt.pe_header_size);
457         SWAP32  (header->nt.pe_checksum);
458         SWAP16  (header->nt.pe_subsys_required);
459         SWAP16  (header->nt.pe_dll_flags);
460         SWAP32  (header->nt.pe_stack_reserve);
461         SWAP32  (header->nt.pe_stack_commit);
462         SWAP32  (header->nt.pe_heap_reserve);
463         SWAP32  (header->nt.pe_heap_commit);
464         SWAP32  (header->nt.pe_loader_flags);
465         SWAP32  (header->nt.pe_data_dir_count);
466
467         /* MonoDotNetHeader: mostly unused */
468         SWAPPDE (header->datadir.pe_export_table);
469         SWAPPDE (header->datadir.pe_import_table);
470         SWAPPDE (header->datadir.pe_resource_table);
471         SWAPPDE (header->datadir.pe_exception_table);
472         SWAPPDE (header->datadir.pe_certificate_table);
473         SWAPPDE (header->datadir.pe_reloc_table);
474         SWAPPDE (header->datadir.pe_debug);
475         SWAPPDE (header->datadir.pe_copyright);
476         SWAPPDE (header->datadir.pe_global_ptr);
477         SWAPPDE (header->datadir.pe_tls_table);
478         SWAPPDE (header->datadir.pe_load_config_table);
479         SWAPPDE (header->datadir.pe_bound_import);
480         SWAPPDE (header->datadir.pe_iat);
481         SWAPPDE (header->datadir.pe_delay_import_desc);
482         SWAPPDE (header->datadir.pe_cli_header);
483         SWAPPDE (header->datadir.pe_reserved);
484
485 #undef SWAP32
486 #undef SWAP16
487 #undef SWAPPDE
488 #endif
489
490         if (header->coff.coff_machine != 0x14c)
491                 goto invalid_image;
492
493         if (header->coff.coff_opt_header_size != (sizeof (MonoDotNetHeader) - sizeof (MonoCOFFHeader) - 4))
494                 goto invalid_image;
495
496         if (header->pesig[0] != 'P' || header->pesig[1] != 'E' || header->pe.pe_magic != 0x10B)
497                 goto invalid_image;
498
499         if (header->pe.pe_major != 6 || header->pe.pe_minor != 0)
500                 goto invalid_image;
501
502         /*
503          * FIXME: byte swap all addresses here for header.
504          */
505         
506         if (!load_section_tables (image, iinfo))
507                 goto invalid_image;
508         
509         /* Load the CLI header */
510         if (!load_cli_header (image, iinfo))
511                 goto invalid_image;
512
513         if (!load_metadata (image, iinfo))
514                 goto invalid_image;
515
516         load_class_names (image);
517
518         image->assembly_name = mono_metadata_string_heap (image, 
519                         mono_metadata_decode_row_col (&image->tables [MONO_TABLE_ASSEMBLY],
520                                         0, MONO_ASSEMBLY_NAME));
521
522         if (status)
523                 *status = MONO_IMAGE_OK;
524
525         return image;
526
527 invalid_image:
528         mono_image_close (image);
529                 return NULL;
530 }
531
532 MonoImage *
533 mono_image_loaded (const char *name) {
534         if (loaded_images_hash)
535                 return g_hash_table_lookup (loaded_images_hash, name);
536         return NULL;
537 }
538
539 /**
540  * mono_image_open:
541  * @fname: filename that points to the module we want to open
542  * @status: An error condition is returned in this field
543  *
544  * Retuns: An open image of type %MonoImage or NULL on error.
545  * if NULL, then check the value of @status for details on the error
546  */
547 MonoImage *
548 mono_image_open (const char *fname, MonoImageOpenStatus *status)
549 {
550         MonoImage *image;
551         
552         g_return_val_if_fail (fname != NULL, NULL);
553
554         if (loaded_images_hash){
555                 image = g_hash_table_lookup (loaded_images_hash, fname);
556                 if (image){
557                         image->ref_count++;
558                         return image;
559                 }
560         }
561
562         image = do_mono_image_open (fname, status);
563         if (image == NULL)
564                 return NULL;
565
566         if (!loaded_images_hash)
567                 loaded_images_hash = g_hash_table_new (g_str_hash, g_str_equal);
568         g_hash_table_insert (loaded_images_hash, image->name, image);
569         g_hash_table_insert (loaded_images_hash, (char *) image->assembly_name, image);
570         
571         return image;
572 }
573
574 static void
575 free_hash_table(gpointer key, gpointer val, gpointer user_data)
576 {
577         g_hash_table_destroy ((GHashTable*)val);
578 }
579
580 /**
581  * mono_image_close:
582  * @image: The image file we wish to close
583  *
584  * Closes an image file, deallocates all memory consumed and
585  * unmaps all possible sections of the file
586  */
587 void
588 mono_image_close (MonoImage *image)
589 {
590         g_return_if_fail (image != NULL);
591
592         if (--image->ref_count)
593                 return;
594
595         g_hash_table_remove (loaded_images_hash, image->name);
596         
597         if (image->f)
598                 fclose (image->f);
599
600         g_free (image->name);
601
602         g_hash_table_destroy (image->method_cache);
603         g_hash_table_destroy (image->class_cache);
604         g_hash_table_destroy (image->array_cache);
605         g_hash_table_foreach (image->name_cache, free_hash_table, NULL);
606         g_hash_table_destroy (image->name_cache);
607         
608         if (image->raw_metadata != NULL)
609                 mono_raw_buffer_free (image->raw_metadata);
610         
611         if (image->image_info){
612                 MonoCLIImageInfo *ii = image->image_info;
613                 int i;
614
615                 for (i = 0; i < ii->cli_section_count; i++){
616                         if (!ii->cli_sections [i])
617                                 continue;
618                         mono_raw_buffer_free (ii->cli_sections [i]);
619                 }
620                 if (ii->cli_section_tables)
621                         g_free (ii->cli_section_tables);
622                 if (ii->cli_sections)
623                         g_free (ii->cli_sections);
624                 g_free (image->image_info);
625         }
626         
627         g_free (image);
628 }
629
630 /** 
631  * mono_image_strerror:
632  * @status: an code indicating the result from a recent operation
633  *
634  * Returns: a string describing the error
635  */
636 const char *
637 mono_image_strerror (MonoImageOpenStatus status)
638 {
639         switch (status){
640         case MONO_IMAGE_OK:
641                 return "success";
642         case MONO_IMAGE_ERROR_ERRNO:
643                 return strerror (errno);
644         case MONO_IMAGE_IMAGE_INVALID:
645                 return "File does not contain a valid CIL image";
646         case MONO_IMAGE_MISSING_ASSEMBLYREF:
647                 return "An assembly was referenced, but could not be found";
648         }
649         return "Internal error";
650 }
651