Flushage
[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 <string.h>
18 #include "image.h"
19 #include "cil-coff.h"
20 #include "rawbuffer.h"
21 #include "endian.h"
22
23 #define INVALID_ADDRESS 0xffffffff
24
25 guint32
26 cli_rva_image_map (cli_image_info_t *iinfo, guint32 addr)
27 {
28         const int top = iinfo->cli_section_count;
29         section_table_t *tables = iinfo->cli_section_tables;
30         int i;
31         
32         for (i = 0; i < top; i++){
33                 if ((addr >= tables->st_virtual_address) &&
34                     (addr < tables->st_virtual_address + tables->st_raw_data_size)){
35                         return addr - tables->st_virtual_address + tables->st_raw_data_ptr;
36                 }
37                 tables++;
38         }
39         return INVALID_ADDRESS;
40 }
41
42 char *
43 cli_rva_map (cli_image_info_t *iinfo, guint32 addr)
44 {
45         const int top = iinfo->cli_section_count;
46         section_table_t *tables = iinfo->cli_section_tables;
47         int i;
48         
49         for (i = 0; i < top; i++){
50                 if ((addr >= tables->st_virtual_address) &&
51                     (addr < tables->st_virtual_address + tables->st_raw_data_size)){
52                         return iinfo->cli_sections [i] +
53                                 (addr - tables->st_virtual_address);
54                 }
55                 tables++;
56         }
57         return NULL;
58 }
59
60 /**
61  * mono_image_ensure_section_idx:
62  * @image: The image we are operating on
63  * @section: section number that we will load/map into memory
64  *
65  * This routine makes sure that we have an in-memory copy of
66  * an image section (.text, .rsrc, .data).
67  *
68  * Returns: TRUE on success
69  */
70 int
71 mono_image_ensure_section_idx (MonoImage *image, int section)
72 {
73         cli_image_info_t *iinfo = image->image_info;
74         section_table_t *sect;
75         gboolean writable;
76         
77         g_return_val_if_fail (section < iinfo->cli_section_count, FALSE);
78
79         if (iinfo->cli_sections [section] != NULL)
80                 return TRUE;
81
82         sect = &iinfo->cli_section_tables [section];
83         
84         writable = sect->st_flags & SECT_FLAGS_MEM_WRITE;
85
86         iinfo->cli_sections [section] = raw_buffer_load (
87                 fileno (image->f), writable,
88                 sect->st_raw_data_ptr, sect->st_raw_data_size);
89
90         if (iinfo->cli_sections [section] == NULL)
91                 return FALSE;
92
93         return TRUE;
94 }
95
96 /**
97  * mono_image_ensure_section:
98  * @image: The image we are operating on
99  * @section: section name that we will load/map into memory
100  *
101  * This routine makes sure that we have an in-memory copy of
102  * an image section (.text, .rsrc, .data).
103  *
104  * Returns: TRUE on success
105  */
106 int
107 mono_image_ensure_section (MonoImage *image, const char *section)
108 {
109         cli_image_info_t *ii = image->image_info;
110         int i;
111         
112         for (i = 0; i < ii->cli_section_count; i++){
113                 if (strncmp (ii->cli_section_tables [i].st_name, section, 8) != 0)
114                         continue;
115                 
116                 return mono_image_ensure_section_idx (image, i);
117         }
118         return FALSE;
119 }
120
121 static int
122 load_section_tables (MonoImage *image, cli_image_info_t *iinfo)
123 {
124         const int top = iinfo->cli_header.coff.coff_sections;
125         int i;
126
127         iinfo->cli_section_count = top;
128         iinfo->cli_section_tables = g_new0 (section_table_t, top);
129         iinfo->cli_sections = g_new0 (void *, top);
130         
131         for (i = 0; i < top; i++){
132                 section_table_t *t = &iinfo->cli_section_tables [i];
133                 
134                 if (fread (t, sizeof (section_table_t), 1, image->f) != 1)
135                         return FALSE;
136
137                 t->st_virtual_size = le32_to_cpu (t->st_virtual_size);
138                 t->st_virtual_address = le32_to_cpu (t->st_virtual_address);
139                 t->st_raw_data_size = le32_to_cpu (t->st_raw_data_size);
140                 t->st_raw_data_ptr = le32_to_cpu (t->st_raw_data_ptr);
141                 t->st_reloc_ptr = le32_to_cpu (t->st_reloc_ptr);
142                 t->st_lineno_ptr = le32_to_cpu (t->st_lineno_ptr);
143                 t->st_reloc_count = le16_to_cpu (t->st_reloc_count);
144                 t->st_line_count = le16_to_cpu (t->st_line_count);
145         }
146
147         for (i = 0; i < top; i++)
148                 if (!mono_image_ensure_section_idx (image, i))
149                         return FALSE;
150         
151         return TRUE;
152 }
153
154 static gboolean
155 load_cli_header (MonoImage *image, cli_image_info_t *iinfo)
156 {
157         guint32 offset;
158         int n;
159         
160         offset = cli_rva_image_map (iinfo, iinfo->cli_header.datadir.pe_cli_header.rva);
161         if (offset == INVALID_ADDRESS)
162                 return FALSE;
163
164         if (fseek (image->f, offset, 0) != 0)
165                 return FALSE;
166         
167         if ((n = fread (&iinfo->cli_cli_header, sizeof (cli_header_t), 1, image->f)) != 1)
168                 return FALSE;
169
170         /* Catch new uses of the fields that are supposed to be zero */
171
172         if ((iinfo->cli_cli_header.ch_eeinfo_table.rva != 0) ||
173             (iinfo->cli_cli_header.ch_helper_table.rva != 0) ||
174             (iinfo->cli_cli_header.ch_dynamic_info.rva != 0) ||
175             (iinfo->cli_cli_header.ch_delay_load_info.rva != 0) ||
176             (iinfo->cli_cli_header.ch_module_image.rva != 0) ||
177             (iinfo->cli_cli_header.ch_external_fixups.rva != 0) ||
178             (iinfo->cli_cli_header.ch_ridmap.rva != 0) ||
179             (iinfo->cli_cli_header.ch_debug_map.rva != 0) ||
180             (iinfo->cli_cli_header.ch_ip_map.rva != 0)){
181                 g_message ("Some fields in the CLI header which should have been zero are not zero");
182         }
183             
184         return TRUE;
185 }
186
187 static gboolean
188 load_metadata_ptrs (MonoImage *image, cli_image_info_t *iinfo)
189 {
190         metadata_t *metadata = &iinfo->cli_metadata;
191         guint32 offset, size;
192         guint16 streams;
193         int i;
194         char *ptr;
195         
196         offset = cli_rva_image_map (iinfo, iinfo->cli_cli_header.ch_metadata.rva);
197         size = iinfo->cli_cli_header.ch_metadata.size;
198         
199         metadata->raw_metadata = raw_buffer_load (fileno (image->f), FALSE, offset, size);
200         if (metadata->raw_metadata == NULL)
201                 return FALSE;
202
203         ptr = metadata->raw_metadata;
204
205         if (strncmp (ptr, "BSJB", 4) == 0){
206                 guint32 version_string_len;
207
208                 ptr += 12;
209                 version_string_len = read32 (ptr);
210                 ptr += 4;
211                 ptr += version_string_len;
212                 if (((guint32) ptr) % 4)
213                         ptr += 4 - (((guint32) ptr) %4);
214         } else
215                 return FALSE;
216
217         /* skip over flags */
218         ptr += 2;
219         
220         streams = read16 (ptr);
221         ptr += 2;
222
223         for (i = 0; i < streams; i++){
224                 if (strncmp (ptr + 8, "#~", 3) == 0){
225                         metadata->heap_tables.sh_offset = read32 (ptr);
226                         metadata->heap_tables.sh_size = read32 (ptr + 4);
227                         ptr += 8 + 3;
228                 } else if (strncmp (ptr + 8, "#Strings", 9) == 0){
229                         metadata->heap_strings.sh_offset = read32 (ptr);
230                         metadata->heap_strings.sh_size = read32 (ptr + 4);
231                         ptr += 8 + 9;
232                 } else if (strncmp (ptr + 8, "#US", 4) == 0){
233                         metadata->heap_us.sh_offset = read32 (ptr);
234                         metadata->heap_us.sh_size = read32 (ptr + 4);
235                         ptr += 8 + 4;
236                 } else if (strncmp (ptr + 8, "#Blob", 6) == 0){
237                         metadata->heap_blob.sh_offset = read32 (ptr);
238                         metadata->heap_blob.sh_size = read32 (ptr + 4);
239                         ptr += 8 + 6;
240                 } else if (strncmp (ptr + 8, "#GUID", 6) == 0){
241                         metadata->heap_guid.sh_offset = read32 (ptr);
242                         metadata->heap_guid.sh_size = read32 (ptr + 4);
243                         ptr += 8 + 6;
244                 } else
245                         g_message ("Unknown heap type: %s\n", ptr + 8);
246                 if (((guint32)ptr) % 4){
247                         ptr += 4 - (((guint32)ptr) % 4);
248                 }
249         }
250         return TRUE;
251 }
252
253 /*
254  * Load representation of logical metadata tables, from the "#~" stream
255  */
256 static gboolean
257 load_tables (MonoImage *image, metadata_t *meta)
258 {
259         char *heap_tables = meta->raw_metadata + meta->heap_tables.sh_offset;
260         guint32 *rows;
261         guint64 valid_mask;
262         int valid = 0, table;
263         int heap_sizes;
264         
265         heap_sizes = heap_tables [6];
266         meta->idx_string_wide = ((heap_sizes & 0x01) == 1);
267         meta->idx_guid_wide   = ((heap_sizes & 0x02) == 2);
268         meta->idx_blob_wide   = ((heap_sizes & 0x04) == 4);
269         
270         valid_mask = read64 (heap_tables + 8);
271         rows = (guint32 *) (heap_tables + 24);
272         
273         for (table = 0; table < 64; table++){
274                 if ((valid_mask & ((guint64) 1 << table)) == 0){
275                         meta->tables [table].rows = 0;
276                         continue;
277                 }
278                 if (table > 0x2b) {
279                         g_warning("bits in valid must be zero above 0x2b (II - 23.1.6)");
280                 }
281                 meta->tables [table].rows = read32 (rows);
282                 rows++;
283                 valid++;
284         }
285
286         meta->tables_base = (heap_tables + 24) + (4 * valid);
287
288         /* They must be the same */
289         g_assert ((void *) meta->tables_base == (void *) rows);
290
291         mono_metadata_compute_table_bases (meta);
292         return TRUE;
293 }
294
295 static gboolean
296 load_metadata (MonoImage *image, cli_image_info_t *iinfo)
297 {
298         if (!load_metadata_ptrs (image, iinfo))
299                 return FALSE;
300
301         return load_tables (image, &iinfo->cli_metadata);
302 }
303
304 /**
305  * mono_image_open:
306  * @fname: filename that points to the module we want to open
307  * @status: An error condition is returned in this field
308  *
309  * Retuns: An open image of type %MonoImage or NULL on error.
310  * if NULL, then check the value of @status for details on the error
311  */
312 MonoImage *
313 mono_image_open (const char *fname, enum MonoImageOpenStatus *status)
314 {
315         cli_image_info_t *iinfo;
316         dotnet_header_t *header;
317         msdos_header_t msdos;
318         MonoImage *image;
319         int n;
320
321         image = g_new0 (MonoImage, 1);
322         image->f = fopen (fname, "r");
323         image->name = g_strdup (fname);
324         iinfo = g_new0 (cli_image_info_t, 1);
325         image->image_info = iinfo;
326
327         header = &iinfo->cli_header;
328                 
329         if (image->f == NULL){
330                 if (status)
331                         *status = MONO_IMAGE_ERROR_ERRNO;
332                 mono_image_close (image);
333                 return NULL;
334         }
335
336         if (status)
337                 *status = MONO_IMAGE_IMAGE_INVALID;
338         
339         if (fread (&msdos, sizeof (msdos), 1, image->f) != 1)
340                 goto invalid_image;
341         
342         if (!(msdos.msdos_header [0] == 0x4d && msdos.msdos_header [1] == 0x5a))
343                 goto invalid_image;
344         
345         if ((n = fread (header, sizeof (dotnet_header_t), 1, image->f)) != 1)
346                 goto invalid_image;
347
348         /*
349          * FIXME: byte swap all addresses here for header.
350          */
351         
352         if (!load_section_tables (image, iinfo))
353                 goto invalid_image;
354         
355         /* Load the CLI header */
356         if (!load_cli_header (image, iinfo))
357                 goto invalid_image;
358
359         if (!load_metadata (image, iinfo))
360                 goto invalid_image;
361         
362         if (status)
363                 *status = MONO_IMAGE_OK;
364
365         return image;
366
367 invalid_image:
368         mono_image_close (image);
369                 return NULL;
370 }
371
372 /**
373  * mono_image_close:
374  * @image: The image file we wish to close
375  *
376  * Closes an image file, deallocates all memory consumed and
377  * unmaps all possible sections of the file
378  */
379 void
380 mono_image_close (MonoImage *image)
381 {
382         g_return_if_fail (image != NULL);
383
384         if (image->f)
385                 fclose (image->f);
386
387         g_free (image->name);
388         
389         if (image->image_info){
390                 cli_image_info_t *ii = image->image_info;
391                 int i;
392
393                 if (ii->cli_metadata.raw_metadata != NULL)
394                         raw_buffer_free (ii->cli_metadata.raw_metadata);
395         
396                 for (i = 0; i < ii->cli_section_count; i++){
397                         if (!ii->cli_sections [i])
398                                 continue;
399                         raw_buffer_free (ii->cli_sections [i]);
400                 }
401                 if (ii->cli_section_tables)
402                         g_free (ii->cli_section_tables);
403                 if (ii->cli_sections)
404                         g_free (ii->cli_sections);
405                 g_free (image->image_info);
406         }
407         
408         g_free (image);
409 }
410
411 /** 
412  * mono_image_strerror:
413  * @status: an code indicating the result from a recent operation
414  *
415  * Returns: a string describing the error
416  */
417 const char *
418 mono_image_strerror (enum MonoImageOpenStatus status)
419 {
420         switch (status){
421         case MONO_IMAGE_OK:
422                 return "success";
423         case MONO_IMAGE_ERROR_ERRNO:
424                 return strerror (errno);
425         case MONO_IMAGE_IMAGE_INVALID:
426                 return "File does not contain a valid CIL image";
427         }
428         return "Internal error";
429 }
430