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