Method header parsing
[mono.git] / mono / metadata / assembly.c
1 /*
2  * assembly.c: Routines for manipulating and assembly 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 "assembly.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_assembly_ensure_section_idx:
62  * @assembly: 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_assembly_ensure_section_idx (MonoAssembly *assembly, int section)
72 {
73         cli_image_info_t *iinfo = assembly->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 (assembly->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_assembly_ensure_section:
98  * @assembly: 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_assembly_ensure_section (MonoAssembly *assembly, const char *section)
108 {
109         cli_image_info_t *ii = assembly->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_assembly_ensure_section_idx (assembly, i);
117         }
118         return FALSE;
119 }
120
121 static int
122 load_section_tables (MonoAssembly *assembly, 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_new (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, assembly->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_assembly_ensure_section_idx (assembly, i))
149                         return FALSE;
150         
151         return TRUE;
152 }
153
154 static gboolean
155 load_cli_header (MonoAssembly *assembly, 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 (assembly->f, offset, 0) != 0)
165                 return FALSE;
166         
167         if ((n = fread (&iinfo->cli_cli_header, sizeof (cli_header_t), 1, assembly->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 (MonoAssembly *assembly, 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 (assembly->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 (MonoAssembly *assembly, 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                 meta->tables [table].rows = read32 (rows);
279                 rows++;
280                 valid++;
281         }
282
283         meta->tables_base = (heap_tables + 24) + (4 * valid);
284
285         /* They must be the same */
286         g_assert ((void *) meta->tables_base == (void *) rows);
287
288         mono_metadata_compute_table_bases (meta);
289         return TRUE;
290 }
291
292 static gboolean
293 load_metadata (MonoAssembly *assembly, cli_image_info_t *iinfo)
294 {
295         if (!load_metadata_ptrs (assembly, iinfo))
296                 return FALSE;
297
298         return load_tables (assembly, &iinfo->cli_metadata);
299 }
300
301 /**
302  * mono_assembly_open:
303  * @fname: filename that points to the module we want to open
304  * @status: An error condition is returned in this field
305  *
306  * Retuns: An open assembly of type %MonoAssembly or NULL on error.
307  * if NULL, then check the value of @status for details on the error
308  */
309 MonoAssembly *
310 mono_assembly_open (const char *fname, enum MonoAssemblyOpenStatus *status)
311 {
312         cli_image_info_t *iinfo;
313         dotnet_header_t *header;
314         msdos_header_t msdos;
315         MonoAssembly *assembly;
316         int n;
317
318         assembly = g_new (MonoAssembly, 1);
319         assembly->f = fopen (fname, "r");
320         iinfo = g_new (cli_image_info_t, 1);
321         assembly->image_info = iinfo;
322
323         header = &iinfo->cli_header;
324                 
325         if (assembly->f == NULL){
326                 if (status)
327                         *status = MONO_ASSEMBLY_ERROR_ERRNO;
328                 mono_assembly_close (assembly);
329                 return NULL;
330         }
331
332         if (status)
333                 *status = MONO_ASSEMBLY_IMAGE_INVALID;
334         
335         if (fread (&msdos, sizeof (msdos), 1, assembly->f) != 1)
336                 goto invalid_image;
337         
338         if (!(msdos.msdos_header [0] == 0x4d && msdos.msdos_header [1] == 0x5a))
339                 goto invalid_image;
340         
341         if ((n = fread (header, sizeof (dotnet_header_t), 1, assembly->f)) != 1)
342                 goto invalid_image;
343
344         /*
345          * FIXME: byte swap all addresses here for header.
346          */
347         
348         if (!load_section_tables (assembly, iinfo))
349                 goto invalid_image;
350         
351         /* Load the CLI header */
352         if (!load_cli_header (assembly, iinfo))
353                 goto invalid_image;
354
355         if (!load_metadata (assembly, iinfo))
356                 goto invalid_image;
357         
358         if (status)
359                 *status = MONO_ASSEMBLY_OK;
360
361         return assembly;
362
363 invalid_image:
364         mono_assembly_close (assembly);
365                 return NULL;
366 }
367
368 /**
369  * mono_assembly_close:
370  * @assembly: The image file we wish to close
371  *
372  * Closes an image file, deallocates all memory consumed and
373  * unmaps all possible sections of the file
374  */
375 void
376 mono_assembly_close (MonoAssembly *assembly)
377 {
378         g_return_if_fail (assembly != NULL);
379
380         if (assembly->f)
381                 fclose (assembly->f);
382
383         if (assembly->image_info){
384                 cli_image_info_t *ii = assembly->image_info;
385                 int i;
386
387                 if (ii->cli_metadata.raw_metadata != NULL)
388                         raw_buffer_free (ii->cli_metadata.raw_metadata);
389         
390                 for (i = 0; i < ii->cli_section_count; i++){
391                         if (!ii->cli_sections [i])
392                                 continue;
393                         raw_buffer_free (ii->cli_sections [i]);
394                 }
395                 if (ii->cli_section_tables)
396                         g_free (ii->cli_section_tables);
397                 if (ii->cli_sections)
398                         g_free (ii->cli_section_tables);
399                 g_free (assembly->image_info);
400         }
401         
402         g_free (assembly);
403 }
404
405 /** 
406  * mono_assembly_strerror:
407  * @status: an code indicating the result from a recent operation
408  *
409  * Returns: a string describing the error
410  */
411 const char *
412 mono_assembly_strerror (enum MonoAssemblyOpenStatus status)
413 {
414         switch (status){
415         case MONO_ASSEMBLY_OK:
416                 return "success";
417         case MONO_ASSEMBLY_ERROR_ERRNO:
418                 return strerror (errno);
419         case MONO_ASSEMBLY_IMAGE_INVALID:
420                 return "File does not contain a valid CIL image";
421         }
422         return "Internal error";
423 }
424