Sat Jun 1 13:46:54 CEST 2002 Paolo Molaro <lupus@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, sorted_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         sorted_mask = read64 (heap_tables + 16);
319         rows = (const guint32 *) (heap_tables + 24);
320         
321         for (table = 0; table < 64; table++){
322                 if ((valid_mask & ((guint64) 1 << table)) == 0){
323                         image->tables [table].rows = 0;
324                         continue;
325                 }
326                 if (table > 0x2b) {
327                         g_warning("bits in valid must be zero above 0x2b (II - 23.1.6)");
328                 }
329                 /*if ((sorted_mask & ((guint64) 1 << table)) == 0){
330                         g_print ("table %s (0x%02x) is sorted\n", mono_meta_table_name (table), table);
331                 }*/
332                 image->tables [table].rows = read32 (rows);
333                 rows++;
334                 valid++;
335         }
336
337         image->tables_base = (heap_tables + 24) + (4 * valid);
338
339         /* They must be the same */
340         g_assert ((const void *) image->tables_base == (const void *) rows);
341
342         mono_metadata_compute_table_bases (image);
343         return TRUE;
344 }
345
346 static gboolean
347 load_metadata (MonoImage *image, MonoCLIImageInfo *iinfo)
348 {
349         if (!load_metadata_ptrs (image, iinfo))
350                 return FALSE;
351
352         return load_tables (image);
353 }
354
355 static void
356 load_class_names (MonoImage *image)
357 {
358         MonoTableInfo  *t = &image->tables [MONO_TABLE_TYPEDEF];
359         guint32 cols [MONO_TYPEDEF_SIZE];
360         const char *name;
361         const char *nspace;
362         GHashTable *nspace_table;
363         GHashTable *name_cache = image->name_cache;
364         guint32 i, visib;
365
366         for (i = 1; i <= t->rows; ++i) {
367                 mono_metadata_decode_row (t, i - 1, cols, MONO_TYPEDEF_SIZE);
368                 /* nested types are accessed from the nesting name */
369                 visib = cols [MONO_TYPEDEF_FLAGS] & TYPE_ATTRIBUTE_VISIBILITY_MASK;
370                 if (visib > TYPE_ATTRIBUTE_PUBLIC && visib < TYPE_ATTRIBUTE_NESTED_ASSEMBLY)
371                         continue;
372                 name = mono_metadata_string_heap (image, cols [MONO_TYPEDEF_NAME]);
373                 nspace = mono_metadata_string_heap (image, cols [MONO_TYPEDEF_NAMESPACE]);
374                 if (!(nspace_table = g_hash_table_lookup (name_cache, nspace))) {
375                         nspace_table = g_hash_table_new (g_str_hash, g_str_equal);
376                         g_hash_table_insert (name_cache, (char *)nspace, (char *)nspace_table);
377                 }
378                 g_hash_table_insert (nspace_table, (char *) name, GUINT_TO_POINTER (i));
379         }
380 }
381
382 static MonoImage *
383 do_mono_image_open (const char *fname, MonoImageOpenStatus *status)
384 {
385         MonoCLIImageInfo *iinfo;
386         MonoDotNetHeader *header;
387         MonoMSDOSHeader msdos;
388         MonoImage *image;
389         int n;
390
391         image = g_new0 (MonoImage, 1);
392         image->f = fopen (fname, "rb");
393         image->name = g_strdup (fname);
394         iinfo = g_new0 (MonoCLIImageInfo, 1);
395         image->image_info = iinfo;
396
397         image->method_cache = g_hash_table_new (g_direct_hash, g_direct_equal);
398         image->class_cache = g_hash_table_new (g_direct_hash, g_direct_equal);
399         image->name_cache = g_hash_table_new (g_str_hash, g_str_equal);
400         image->array_cache = g_hash_table_new ((GHashFunc)mono_metadata_type_hash, (GCompareFunc)mono_metadata_type_equal);
401
402         header = &iinfo->cli_header;
403                 
404         if (image->f == NULL){
405                 if (status)
406                         *status = MONO_IMAGE_ERROR_ERRNO;
407                 mono_image_close (image);
408                 return NULL;
409         }
410
411         if (status)
412                 *status = MONO_IMAGE_IMAGE_INVALID;
413         
414         if (fread (&msdos, sizeof (msdos), 1, image->f) != 1)
415                 goto invalid_image;
416         
417         if (!(msdos.msdos_header [0] == 'M' && msdos.msdos_header [1] == 'Z'))
418                 goto invalid_image;
419         
420         msdos.pe_offset = GUINT32_FROM_LE (msdos.pe_offset);
421
422         if (msdos.pe_offset != sizeof (msdos))
423                 fseek (image->f, msdos.pe_offset, SEEK_SET);
424         
425         if ((n = fread (header, sizeof (MonoDotNetHeader), 1, image->f)) != 1)
426                 goto invalid_image;
427
428 #if G_BYTE_ORDER != G_LITTLE_ENDIAN
429 #define SWAP32(x) (x) = GUINT32_FROM_LE ((x))
430 #define SWAP16(x) (x) = GUINT16_FROM_LE ((x))
431 #define SWAPPDE(x) do { (x).rva = GUINT32_FROM_LE ((x).rva); (x).size = GUINT32_FROM_LE ((x).size);} while (0)
432         SWAP32 (header->coff.coff_time);
433         SWAP32 (header->coff.coff_symptr);
434         SWAP32 (header->coff.coff_symcount);
435         SWAP16 (header->coff.coff_machine);
436         SWAP16 (header->coff.coff_sections);
437         SWAP16 (header->coff.coff_opt_header_size);
438         SWAP16 (header->coff.coff_attributes);
439         /* MonoPEHeader */
440         SWAP32 (header->pe.pe_code_size);
441         SWAP32 (header->pe.pe_data_size);
442         SWAP32 (header->pe.pe_uninit_data_size);
443         SWAP32 (header->pe.pe_rva_entry_point);
444         SWAP32 (header->pe.pe_rva_code_base);
445         SWAP32 (header->pe.pe_rva_data_base);
446         SWAP16 (header->pe.pe_magic);
447
448         /* MonoPEHeaderNT: not used yet */
449         SWAP32  (header->nt.pe_image_base);     /* must be 0x400000 */
450         SWAP32  (header->nt.pe_section_align);       /* must be 8192 */
451         SWAP32  (header->nt.pe_file_alignment);      /* must be 512 or 4096 */
452         SWAP16  (header->nt.pe_os_major);            /* must be 4 */
453         SWAP16  (header->nt.pe_os_minor);            /* must be 0 */
454         SWAP16  (header->nt.pe_user_major);
455         SWAP16  (header->nt.pe_user_minor);
456         SWAP16  (header->nt.pe_subsys_major);
457         SWAP16  (header->nt.pe_subsys_minor);
458         SWAP32  (header->nt.pe_reserved_1);
459         SWAP32  (header->nt.pe_image_size);
460         SWAP32  (header->nt.pe_header_size);
461         SWAP32  (header->nt.pe_checksum);
462         SWAP16  (header->nt.pe_subsys_required);
463         SWAP16  (header->nt.pe_dll_flags);
464         SWAP32  (header->nt.pe_stack_reserve);
465         SWAP32  (header->nt.pe_stack_commit);
466         SWAP32  (header->nt.pe_heap_reserve);
467         SWAP32  (header->nt.pe_heap_commit);
468         SWAP32  (header->nt.pe_loader_flags);
469         SWAP32  (header->nt.pe_data_dir_count);
470
471         /* MonoDotNetHeader: mostly unused */
472         SWAPPDE (header->datadir.pe_export_table);
473         SWAPPDE (header->datadir.pe_import_table);
474         SWAPPDE (header->datadir.pe_resource_table);
475         SWAPPDE (header->datadir.pe_exception_table);
476         SWAPPDE (header->datadir.pe_certificate_table);
477         SWAPPDE (header->datadir.pe_reloc_table);
478         SWAPPDE (header->datadir.pe_debug);
479         SWAPPDE (header->datadir.pe_copyright);
480         SWAPPDE (header->datadir.pe_global_ptr);
481         SWAPPDE (header->datadir.pe_tls_table);
482         SWAPPDE (header->datadir.pe_load_config_table);
483         SWAPPDE (header->datadir.pe_bound_import);
484         SWAPPDE (header->datadir.pe_iat);
485         SWAPPDE (header->datadir.pe_delay_import_desc);
486         SWAPPDE (header->datadir.pe_cli_header);
487         SWAPPDE (header->datadir.pe_reserved);
488
489 #undef SWAP32
490 #undef SWAP16
491 #undef SWAPPDE
492 #endif
493
494         if (header->coff.coff_machine != 0x14c)
495                 goto invalid_image;
496
497         if (header->coff.coff_opt_header_size != (sizeof (MonoDotNetHeader) - sizeof (MonoCOFFHeader) - 4))
498                 goto invalid_image;
499
500         if (header->pesig[0] != 'P' || header->pesig[1] != 'E' || header->pe.pe_magic != 0x10B)
501                 goto invalid_image;
502
503         if (header->pe.pe_major != 6 || header->pe.pe_minor != 0)
504                 goto invalid_image;
505
506         /*
507          * FIXME: byte swap all addresses here for header.
508          */
509         
510         if (!load_section_tables (image, iinfo))
511                 goto invalid_image;
512         
513         /* Load the CLI header */
514         if (!load_cli_header (image, iinfo))
515                 goto invalid_image;
516
517         if (!load_metadata (image, iinfo))
518                 goto invalid_image;
519
520         load_class_names (image);
521
522         /* modules don't have an assembly table row */
523         if (image->tables [MONO_TABLE_ASSEMBLY].rows)
524                 image->assembly_name = mono_metadata_string_heap (image, 
525                         mono_metadata_decode_row_col (&image->tables [MONO_TABLE_ASSEMBLY],
526                                         0, MONO_ASSEMBLY_NAME));
527
528         if (status)
529                 *status = MONO_IMAGE_OK;
530
531         return image;
532
533 invalid_image:
534         mono_image_close (image);
535                 return NULL;
536 }
537
538 MonoImage *
539 mono_image_loaded (const char *name) {
540         if (loaded_images_hash)
541                 return g_hash_table_lookup (loaded_images_hash, name);
542         return NULL;
543 }
544
545 /**
546  * mono_image_open:
547  * @fname: filename that points to the module we want to open
548  * @status: An error condition is returned in this field
549  *
550  * Retuns: An open image of type %MonoImage or NULL on error.
551  * if NULL, then check the value of @status for details on the error
552  */
553 MonoImage *
554 mono_image_open (const char *fname, MonoImageOpenStatus *status)
555 {
556         MonoImage *image;
557         
558         g_return_val_if_fail (fname != NULL, NULL);
559
560         if (loaded_images_hash){
561                 image = g_hash_table_lookup (loaded_images_hash, fname);
562                 if (image){
563                         image->ref_count++;
564                         return image;
565                 }
566         }
567
568         image = do_mono_image_open (fname, status);
569         if (image == NULL)
570                 return NULL;
571
572         if (!loaded_images_hash)
573                 loaded_images_hash = g_hash_table_new (g_str_hash, g_str_equal);
574         g_hash_table_insert (loaded_images_hash, image->name, image);
575         if (image->assembly_name)
576                 g_hash_table_insert (loaded_images_hash, (char *) image->assembly_name, image);
577         
578         return image;
579 }
580
581 static void
582 free_hash_table(gpointer key, gpointer val, gpointer user_data)
583 {
584         g_hash_table_destroy ((GHashTable*)val);
585 }
586
587 /**
588  * mono_image_close:
589  * @image: The image file we wish to close
590  *
591  * Closes an image file, deallocates all memory consumed and
592  * unmaps all possible sections of the file
593  */
594 void
595 mono_image_close (MonoImage *image)
596 {
597         g_return_if_fail (image != NULL);
598
599         if (--image->ref_count)
600                 return;
601
602         g_hash_table_remove (loaded_images_hash, image->name);
603         
604         if (image->f)
605                 fclose (image->f);
606
607         g_free (image->name);
608
609         g_hash_table_destroy (image->method_cache);
610         g_hash_table_destroy (image->class_cache);
611         g_hash_table_destroy (image->array_cache);
612         g_hash_table_foreach (image->name_cache, free_hash_table, NULL);
613         g_hash_table_destroy (image->name_cache);
614         
615         if (image->raw_metadata != NULL)
616                 mono_raw_buffer_free (image->raw_metadata);
617         
618         if (image->image_info){
619                 MonoCLIImageInfo *ii = image->image_info;
620                 int i;
621
622                 for (i = 0; i < ii->cli_section_count; i++){
623                         if (!ii->cli_sections [i])
624                                 continue;
625                         mono_raw_buffer_free (ii->cli_sections [i]);
626                 }
627                 if (ii->cli_section_tables)
628                         g_free (ii->cli_section_tables);
629                 if (ii->cli_sections)
630                         g_free (ii->cli_sections);
631                 g_free (image->image_info);
632         }
633         
634         g_free (image);
635 }
636
637 /** 
638  * mono_image_strerror:
639  * @status: an code indicating the result from a recent operation
640  *
641  * Returns: a string describing the error
642  */
643 const char *
644 mono_image_strerror (MonoImageOpenStatus status)
645 {
646         switch (status){
647         case MONO_IMAGE_OK:
648                 return "success";
649         case MONO_IMAGE_ERROR_ERRNO:
650                 return strerror (errno);
651         case MONO_IMAGE_IMAGE_INVALID:
652                 return "File does not contain a valid CIL image";
653         case MONO_IMAGE_MISSING_ASSEMBLYREF:
654                 return "An assembly was referenced, but could not be found";
655         }
656         return "Internal error";
657 }
658