2002-07-15 Dietmar Maurer <dietmar@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->ref_count = 1;
393         image->f = fopen (fname, "rb");
394         image->name = g_strdup (fname);
395         iinfo = g_new0 (MonoCLIImageInfo, 1);
396         image->image_info = iinfo;
397
398         image->method_cache = g_hash_table_new (g_direct_hash, g_direct_equal);
399         image->class_cache = g_hash_table_new (g_direct_hash, g_direct_equal);
400         image->name_cache = g_hash_table_new (g_str_hash, g_str_equal);
401         image->array_cache = g_hash_table_new ((GHashFunc)mono_metadata_type_hash, 
402                                                (GCompareFunc)mono_metadata_type_equal);
403
404         image->delegate_begin_invoke_cache = 
405                 g_hash_table_new ((GHashFunc)mono_signature_hash, 
406                                   (GCompareFunc)mono_metadata_signature_equal);
407         image->delegate_end_invoke_cache = 
408                 g_hash_table_new ((GHashFunc)mono_signature_hash, 
409                                   (GCompareFunc)mono_metadata_signature_equal);
410         image->delegate_invoke_cache = 
411                 g_hash_table_new ((GHashFunc)mono_signature_hash, 
412                                   (GCompareFunc)mono_metadata_signature_equal);
413
414         image->runtime_invoke_cache = g_hash_table_new (g_direct_hash, g_direct_equal);
415         image->managed_wrapper_cache = g_hash_table_new (g_direct_hash, g_direct_equal);
416         image->native_wrapper_cache = g_hash_table_new (g_direct_hash, g_direct_equal);
417         image->remoting_invoke_cache = g_hash_table_new (g_direct_hash, g_direct_equal);
418
419         header = &iinfo->cli_header;
420                 
421         if (image->f == NULL){
422                 if (status)
423                         *status = MONO_IMAGE_ERROR_ERRNO;
424                 mono_image_close (image);
425                 return NULL;
426         }
427
428         if (status)
429                 *status = MONO_IMAGE_IMAGE_INVALID;
430         
431         if (fread (&msdos, sizeof (msdos), 1, image->f) != 1)
432                 goto invalid_image;
433         
434         if (!(msdos.msdos_sig [0] == 'M' && msdos.msdos_sig [1] == 'Z'))
435                 goto invalid_image;
436         
437         msdos.pe_offset = GUINT32_FROM_LE (msdos.pe_offset);
438
439         if (msdos.pe_offset != sizeof (msdos))
440                 fseek (image->f, msdos.pe_offset, SEEK_SET);
441         
442         if ((n = fread (header, sizeof (MonoDotNetHeader), 1, image->f)) != 1)
443                 goto invalid_image;
444
445 #if G_BYTE_ORDER != G_LITTLE_ENDIAN
446 #define SWAP32(x) (x) = GUINT32_FROM_LE ((x))
447 #define SWAP16(x) (x) = GUINT16_FROM_LE ((x))
448 #define SWAPPDE(x) do { (x).rva = GUINT32_FROM_LE ((x).rva); (x).size = GUINT32_FROM_LE ((x).size);} while (0)
449         SWAP32 (header->coff.coff_time);
450         SWAP32 (header->coff.coff_symptr);
451         SWAP32 (header->coff.coff_symcount);
452         SWAP16 (header->coff.coff_machine);
453         SWAP16 (header->coff.coff_sections);
454         SWAP16 (header->coff.coff_opt_header_size);
455         SWAP16 (header->coff.coff_attributes);
456         /* MonoPEHeader */
457         SWAP32 (header->pe.pe_code_size);
458         SWAP32 (header->pe.pe_data_size);
459         SWAP32 (header->pe.pe_uninit_data_size);
460         SWAP32 (header->pe.pe_rva_entry_point);
461         SWAP32 (header->pe.pe_rva_code_base);
462         SWAP32 (header->pe.pe_rva_data_base);
463         SWAP16 (header->pe.pe_magic);
464
465         /* MonoPEHeaderNT: not used yet */
466         SWAP32  (header->nt.pe_image_base);     /* must be 0x400000 */
467         SWAP32  (header->nt.pe_section_align);       /* must be 8192 */
468         SWAP32  (header->nt.pe_file_alignment);      /* must be 512 or 4096 */
469         SWAP16  (header->nt.pe_os_major);            /* must be 4 */
470         SWAP16  (header->nt.pe_os_minor);            /* must be 0 */
471         SWAP16  (header->nt.pe_user_major);
472         SWAP16  (header->nt.pe_user_minor);
473         SWAP16  (header->nt.pe_subsys_major);
474         SWAP16  (header->nt.pe_subsys_minor);
475         SWAP32  (header->nt.pe_reserved_1);
476         SWAP32  (header->nt.pe_image_size);
477         SWAP32  (header->nt.pe_header_size);
478         SWAP32  (header->nt.pe_checksum);
479         SWAP16  (header->nt.pe_subsys_required);
480         SWAP16  (header->nt.pe_dll_flags);
481         SWAP32  (header->nt.pe_stack_reserve);
482         SWAP32  (header->nt.pe_stack_commit);
483         SWAP32  (header->nt.pe_heap_reserve);
484         SWAP32  (header->nt.pe_heap_commit);
485         SWAP32  (header->nt.pe_loader_flags);
486         SWAP32  (header->nt.pe_data_dir_count);
487
488         /* MonoDotNetHeader: mostly unused */
489         SWAPPDE (header->datadir.pe_export_table);
490         SWAPPDE (header->datadir.pe_import_table);
491         SWAPPDE (header->datadir.pe_resource_table);
492         SWAPPDE (header->datadir.pe_exception_table);
493         SWAPPDE (header->datadir.pe_certificate_table);
494         SWAPPDE (header->datadir.pe_reloc_table);
495         SWAPPDE (header->datadir.pe_debug);
496         SWAPPDE (header->datadir.pe_copyright);
497         SWAPPDE (header->datadir.pe_global_ptr);
498         SWAPPDE (header->datadir.pe_tls_table);
499         SWAPPDE (header->datadir.pe_load_config_table);
500         SWAPPDE (header->datadir.pe_bound_import);
501         SWAPPDE (header->datadir.pe_iat);
502         SWAPPDE (header->datadir.pe_delay_import_desc);
503         SWAPPDE (header->datadir.pe_cli_header);
504         SWAPPDE (header->datadir.pe_reserved);
505
506 #undef SWAP32
507 #undef SWAP16
508 #undef SWAPPDE
509 #endif
510
511         if (header->coff.coff_machine != 0x14c)
512                 goto invalid_image;
513
514         if (header->coff.coff_opt_header_size != (sizeof (MonoDotNetHeader) - sizeof (MonoCOFFHeader) - 4))
515                 goto invalid_image;
516
517         if (header->pesig[0] != 'P' || header->pesig[1] != 'E' || header->pe.pe_magic != 0x10B)
518                 goto invalid_image;
519
520         if (header->pe.pe_major != 6 || header->pe.pe_minor != 0)
521                 goto invalid_image;
522
523         /*
524          * FIXME: byte swap all addresses here for header.
525          */
526         
527         if (!load_section_tables (image, iinfo))
528                 goto invalid_image;
529         
530         /* Load the CLI header */
531         if (!load_cli_header (image, iinfo))
532                 goto invalid_image;
533
534         if (!load_metadata (image, iinfo))
535                 goto invalid_image;
536
537         load_class_names (image);
538
539         /* modules don't have an assembly table row */
540         if (image->tables [MONO_TABLE_ASSEMBLY].rows)
541                 image->assembly_name = mono_metadata_string_heap (image, 
542                         mono_metadata_decode_row_col (&image->tables [MONO_TABLE_ASSEMBLY],
543                                         0, MONO_ASSEMBLY_NAME));
544
545         image->module_name = mono_metadata_string_heap (image, 
546                         mono_metadata_decode_row_col (&image->tables [MONO_TABLE_MODULE],
547                                         0, MONO_MODULE_NAME));
548
549         if (status)
550                 *status = MONO_IMAGE_OK;
551
552         image->ref_count=1;
553
554         return image;
555
556 invalid_image:
557         mono_image_close (image);
558                 return NULL;
559 }
560
561 MonoImage *
562 mono_image_loaded (const char *name) {
563         if (strcmp (name, "mscorlib") == 0)
564                 name = "corlib";
565         if (loaded_images_hash)
566                 return g_hash_table_lookup (loaded_images_hash, name);
567         return NULL;
568 }
569
570 /**
571  * mono_image_open:
572  * @fname: filename that points to the module we want to open
573  * @status: An error condition is returned in this field
574  *
575  * Retuns: An open image of type %MonoImage or NULL on error.
576  * if NULL, then check the value of @status for details on the error
577  */
578 MonoImage *
579 mono_image_open (const char *fname, MonoImageOpenStatus *status)
580 {
581         MonoImage *image;
582         
583         g_return_val_if_fail (fname != NULL, NULL);
584
585         if (loaded_images_hash){
586                 image = g_hash_table_lookup (loaded_images_hash, fname);
587                 if (image){
588                         image->ref_count++;
589                         return image;
590                 }
591         }
592
593         image = do_mono_image_open (fname, status);
594         if (image == NULL)
595                 return NULL;
596
597         if (!loaded_images_hash)
598                 loaded_images_hash = g_hash_table_new (g_str_hash, g_str_equal);
599         g_hash_table_insert (loaded_images_hash, image->name, image);
600         if (image->assembly_name)
601                 g_hash_table_insert (loaded_images_hash, (char *) image->assembly_name, image);
602         
603         return image;
604 }
605
606 static void
607 free_hash_table(gpointer key, gpointer val, gpointer user_data)
608 {
609         g_hash_table_destroy ((GHashTable*)val);
610 }
611
612 /**
613  * mono_image_close:
614  * @image: The image file we wish to close
615  *
616  * Closes an image file, deallocates all memory consumed and
617  * unmaps all possible sections of the file
618  */
619 void
620 mono_image_close (MonoImage *image)
621 {
622         g_return_if_fail (image != NULL);
623
624         if (--image->ref_count)
625                 return;
626
627         if (!loaded_images_hash)
628                 loaded_images_hash = g_hash_table_new (g_str_hash, g_str_equal);
629         g_hash_table_remove (loaded_images_hash, image->name);
630         
631         if (image->f)
632                 fclose (image->f);
633
634         g_free (image->name);
635
636         g_hash_table_destroy (image->method_cache);
637         g_hash_table_destroy (image->class_cache);
638         g_hash_table_destroy (image->array_cache);
639         g_hash_table_foreach (image->name_cache, free_hash_table, NULL);
640         g_hash_table_destroy (image->name_cache);
641         
642         if (image->raw_metadata != NULL)
643                 mono_raw_buffer_free (image->raw_metadata);
644         
645         if (image->image_info){
646                 MonoCLIImageInfo *ii = image->image_info;
647                 int i;
648
649                 for (i = 0; i < ii->cli_section_count; i++){
650                         if (!ii->cli_sections [i])
651                                 continue;
652                         mono_raw_buffer_free (ii->cli_sections [i]);
653                 }
654                 if (ii->cli_section_tables)
655                         g_free (ii->cli_section_tables);
656                 if (ii->cli_sections)
657                         g_free (ii->cli_sections);
658                 g_free (image->image_info);
659         }
660         
661         g_free (image);
662 }
663
664 /** 
665  * mono_image_strerror:
666  * @status: an code indicating the result from a recent operation
667  *
668  * Returns: a string describing the error
669  */
670 const char *
671 mono_image_strerror (MonoImageOpenStatus status)
672 {
673         switch (status){
674         case MONO_IMAGE_OK:
675                 return "success";
676         case MONO_IMAGE_ERROR_ERRNO:
677                 return strerror (errno);
678         case MONO_IMAGE_IMAGE_INVALID:
679                 return "File does not contain a valid CIL image";
680         case MONO_IMAGE_MISSING_ASSEMBLYREF:
681                 return "An assembly was referenced, but could not be found";
682         }
683         return "Internal error";
684 }
685
686 static gpointer
687 mono_image_walk_resource_tree (MonoCLIImageInfo *info, guint32 res_id,
688                                guint32 lang_id, gunichar2 *name,
689                                MonoPEResourceDirEntry *entry,
690                                MonoPEResourceDir *root, guint32 level)
691 {
692         gboolean is_string=entry->name_is_string;
693
694         /* Level 0 holds a directory entry for each type of resource
695          * (identified by ID or name).
696          *
697          * Level 1 holds a directory entry for each named resource
698          * item, and each "anonymous" item of a particular type of
699          * resource.
700          *
701          * Level 2 holds a directory entry for each language pointing to
702          * the actual data.
703          */
704
705         if(level==0) {
706                 if((is_string==FALSE && entry->name_offset!=res_id) ||
707                    (is_string==TRUE)) {
708                         return(NULL);
709                 }
710         } else if (level==1) {
711 #if 0
712                 if(name!=NULL &&
713                    is_string==TRUE && name!=lookup (entry->name_offset)) {
714                         return(NULL);
715                 }
716 #endif
717         } else if (level==2) {
718                 if((is_string==FALSE && entry->name_offset!=lang_id) ||
719                    (is_string==TRUE)) {
720                         return(NULL);
721                 }
722         } else {
723                 g_assert_not_reached ();
724         }
725
726         if(entry->is_dir==TRUE) {
727                 MonoPEResourceDir *res_dir=(MonoPEResourceDir *)(((char *)root)+entry->dir_offset);
728                 MonoPEResourceDirEntry *sub_entries=(MonoPEResourceDirEntry *)(res_dir+1);
729                 guint32 entries, i;
730                 
731                 entries=res_dir->res_named_entries + res_dir->res_id_entries;
732
733                 for(i=0; i<entries; i++) {
734                         MonoPEResourceDirEntry *sub_entry=&sub_entries[i];
735                         gpointer ret;
736                         
737                         ret=mono_image_walk_resource_tree (info, res_id,
738                                                            lang_id, name,
739                                                            sub_entry, root,
740                                                            level+1);
741                         if(ret!=NULL) {
742                                 return(ret);
743                         }
744                 }
745
746                 return(NULL);
747         } else {
748                 MonoPEResourceDataEntry *data_entry=(MonoPEResourceDataEntry *)((char *)(root)+entry->dir_offset);
749                 
750                 return(data_entry);
751         }
752 }
753
754 gpointer
755 mono_image_lookup_resource (MonoImage *image, guint32 res_id, guint32 lang_id, gunichar2 *name)
756 {
757         MonoCLIImageInfo *info;
758         MonoDotNetHeader *header;
759         MonoPEDatadir *datadir;
760         MonoPEDirEntry *rsrc;
761         MonoPEResourceDir *resource_dir;
762         MonoPEResourceDirEntry *res_entries;
763         guint32 entries, i;
764
765         if(image==NULL) {
766                 return(NULL);
767         }
768
769         info=image->image_info;
770         if(info==NULL) {
771                 return(NULL);
772         }
773
774         header=&info->cli_header;
775         if(header==NULL) {
776                 return(NULL);
777         }
778
779         datadir=&header->datadir;
780         if(datadir==NULL) {
781                 return(NULL);
782         }
783
784         rsrc=&datadir->pe_resource_table;
785         if(rsrc==NULL) {
786                 return(NULL);
787         }
788
789         resource_dir=(MonoPEResourceDir *)mono_cli_rva_map (info, rsrc->rva);
790         if(resource_dir==NULL) {
791                 return(NULL);
792         }
793         
794         entries=resource_dir->res_named_entries + resource_dir->res_id_entries;
795         res_entries=(MonoPEResourceDirEntry *)(resource_dir+1);
796         
797         for(i=0; i<entries; i++) {
798                 MonoPEResourceDirEntry *entry=&res_entries[i];
799                 gpointer ret;
800                 
801                 ret=mono_image_walk_resource_tree (info, res_id, lang_id,
802                                                    name, entry, resource_dir,
803                                                    0);
804                 if(ret!=NULL) {
805                         return(ret);
806                 }
807         }
808
809         return(NULL);
810 }