2001-06-21 Miguel de Icaza <miguel@ximian.com>
[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  *   Do byteswaps for big-endian systems on the various headers.
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
22 #define INVALID_ADDRESS 0xffffffff
23
24 /* FIXME: implement big endian versions */
25 #define le64_to_cpu(x) (x)
26 #define le32_to_cpu(x) (x)
27 #define le16_to_cpu(x) (x)
28 #define read32(x) le32_to_cpu (*((guint32 *) (x)))
29 #define read16(x) le16_to_cpu (*((guint16 *) (x)))
30 #define read64(x) le64_to_cpu (*((guint64 *) (x)))
31
32 static guint32
33 coff_map (dotnet_image_info_t *iinfo, guint32 addr)
34 {
35         const int top = iinfo->dn_section_count;
36         section_table_t *tables = iinfo->dn_section_tables;
37         int i;
38         
39         for (i = 0; i < top; i++){
40                 if ((addr >= tables->st_virtual_address) &&
41                     (addr < tables->st_virtual_address + tables->st_raw_data_size)){
42                         return addr - tables->st_virtual_address + tables->st_raw_data_ptr;
43                 }
44                 tables++;
45         }
46         return INVALID_ADDRESS;
47 }
48
49 static int
50 load_section_tables (MonoAssembly *assembly, dotnet_image_info_t *iinfo)
51 {
52         const int top = iinfo->dn_header.coff.coff_sections;
53         int i;
54
55         iinfo->dn_section_count = top;
56         iinfo->dn_section_tables = g_new (section_table_t, top);
57
58         for (i = 0; i < top; i++){
59                 section_table_t *t = &iinfo->dn_section_tables [i];
60                 
61                 if (fread (t, sizeof (section_table_t), 1, assembly->f) != 1)
62                         return FALSE;
63
64                 t->st_virtual_size = le32_to_cpu (t->st_virtual_size);
65                 t->st_virtual_address = le32_to_cpu (t->st_virtual_address);
66                 t->st_raw_data_size = le32_to_cpu (t->st_raw_data_size);
67                 t->st_raw_data_ptr = le32_to_cpu (t->st_raw_data_ptr);
68                 t->st_reloc_ptr = le32_to_cpu (t->st_reloc_ptr);
69                 t->st_lineno_ptr = le32_to_cpu (t->st_lineno_ptr);
70                 t->st_reloc_count = le16_to_cpu (t->st_reloc_count);
71                 t->st_line_count = le16_to_cpu (t->st_line_count);
72         }
73
74         return TRUE;
75 }
76
77 static gboolean
78 load_cli_header (MonoAssembly *assembly, dotnet_image_info_t *iinfo)
79 {
80         guint32 offset;
81         int n;
82         
83         offset = coff_map (iinfo, iinfo->dn_header.datadir.pe_cli_header.rva);
84         if (offset == INVALID_ADDRESS)
85                 return FALSE;
86
87         if (fseek (assembly->f, offset, 0) != 0)
88                 return FALSE;
89         
90         if ((n = fread (&iinfo->dn_cli_header, sizeof (cli_header_t), 1, assembly->f)) != 1)
91                 return FALSE;
92
93         /* Catch new uses of the fields that are supposed to be zero */
94
95         if ((iinfo->dn_cli_header.ch_eeinfo_table.rva != 0) ||
96             (iinfo->dn_cli_header.ch_helper_table.rva != 0) ||
97             (iinfo->dn_cli_header.ch_dynamic_info.rva != 0) ||
98             (iinfo->dn_cli_header.ch_delay_load_info.rva != 0) ||
99             (iinfo->dn_cli_header.ch_module_image.rva != 0) ||
100             (iinfo->dn_cli_header.ch_external_fixups.rva != 0) ||
101             (iinfo->dn_cli_header.ch_ridmap.rva != 0) ||
102             (iinfo->dn_cli_header.ch_debug_map.rva != 0) ||
103             (iinfo->dn_cli_header.ch_ip_map.rva != 0)){
104                 g_message ("Some fields in the CLI header which should have been zero are not zero");
105         }
106             
107         return TRUE;
108 }
109
110 static gboolean
111 load_metadata_ptrs (MonoAssembly *assembly, dotnet_image_info_t *iinfo)
112 {
113         metadata_t *metadata = &iinfo->dn_metadata;
114         guint32 offset, size;
115         guint16 streams;
116         int i;
117         char *ptr;
118         
119         offset = coff_map (iinfo, iinfo->dn_cli_header.ch_metadata.rva);
120         size = iinfo->dn_cli_header.ch_metadata.size;
121         
122         metadata->raw_metadata = raw_buffer_load (fileno (assembly->f), FALSE, offset, size);
123         if (metadata->raw_metadata == NULL)
124                 return FALSE;
125
126         ptr = metadata->raw_metadata;
127
128         if (strncmp (ptr, "BSJB", 4) == 0){
129                 guint32 version_string_len;
130
131                 ptr += 12;
132                 version_string_len = read32 (ptr);
133                 ptr += 4;
134                 ptr += version_string_len;
135                 if (((guint32) ptr) % 4)
136                         ptr += 4 - (((guint32) ptr) %4);
137         } else
138                 return FALSE;
139
140         /* skip over flags */
141         ptr += 2;
142         
143         streams = read16 (ptr);
144         ptr += 2;
145
146         for (i = 0; i < streams; i++){
147                 if (strncmp (ptr + 8, "#~", 3) == 0){
148                         metadata->heap_tables.sh_offset = read32 (ptr);
149                         metadata->heap_tables.sh_size = read32 (ptr + 4);
150                         ptr += 8 + 3;
151                 } else if (strncmp (ptr + 8, "#Strings", 9) == 0){
152                         metadata->heap_strings.sh_offset = read32 (ptr);
153                         metadata->heap_strings.sh_size = read32 (ptr + 4);
154                         ptr += 8 + 9;
155                 } else if (strncmp (ptr + 8, "#US", 4) == 0){
156                         metadata->heap_us.sh_offset = read32 (ptr);
157                         metadata->heap_us.sh_size = read32 (ptr + 4);
158                         ptr += 8 + 4;
159                 } else if (strncmp (ptr + 8, "#Blob", 6) == 0){
160                         metadata->heap_blob.sh_offset = read32 (ptr);
161                         metadata->heap_blob.sh_size = read32 (ptr + 4);
162                         ptr += 8 + 6;
163                 } else if (strncmp (ptr + 8, "#GUID", 6) == 0){
164                         metadata->heap_guid.sh_offset = read32 (ptr);
165                         metadata->heap_guid.sh_size = read32 (ptr + 4);
166                         ptr += 8 + 6;
167                 } else
168                         g_message ("Unknown heap type: %s\n", ptr + 8);
169                 if (((guint32)ptr) % 4){
170                         ptr += 4 - (((guint32)ptr) % 4);
171                 }
172         }
173         return TRUE;
174 }
175
176 /*
177  * Load representation of logical metadata tables, from the "#~" stream
178  */
179 static gboolean
180 load_tables (MonoAssembly *assembly, metadata_t *meta)
181 {
182         char *heap_tables = meta->raw_metadata + meta->heap_tables.sh_offset;
183         guint32 *rows;
184         guint64 valid_mask;
185         int valid = 0, table;
186         int heap_sizes;
187         
188         heap_sizes = heap_tables [6];
189         meta->idx_string_wide = ((heap_sizes & 0x01) == 1);
190         meta->idx_guid_wide   = ((heap_sizes & 0x02) == 2);
191         meta->idx_blob_wide   = ((heap_sizes & 0x04) == 4);
192         
193         valid_mask = read64 (heap_tables + 8);
194         rows = (guint32 *) (heap_tables + 24);
195         
196         for (table = 0; table < 64; table++){
197                 if ((valid_mask & ((guint64) 1 << table)) == 0){
198                         meta->tables [table].rows = 0;
199                         continue;
200                 }
201                 meta->tables [table].rows = read32 (rows);
202                 rows++;
203                 valid++;
204         }
205
206         meta->tables_base = (heap_tables + 24) + (4 * valid);
207
208         /* They must be the same */
209         g_assert (meta->tables_base == rows);
210
211         mono_metadata_compute_table_bases (meta);
212         return TRUE;
213 }
214
215 static gboolean
216 load_metadata (MonoAssembly *assembly, dotnet_image_info_t *iinfo)
217 {
218         if (!load_metadata_ptrs (assembly, iinfo))
219                 return FALSE;
220
221         return load_tables (assembly, &iinfo->dn_metadata);
222 }
223
224 MonoAssembly *
225 mono_assembly_open (const char *fname, enum MonoAssemblyOpenStatus *status)
226 {
227         dotnet_image_info_t *iinfo;
228         dotnet_header_t *header;
229         msdos_header_t msdos;
230         MonoAssembly *assembly;
231         int n;
232
233         assembly = g_new (MonoAssembly, 1);
234         assembly->f = fopen (fname, "r");
235         iinfo = g_new (dotnet_image_info_t, 1);
236         assembly->image_info = iinfo;
237
238         header = &iinfo->dn_header;
239                 
240         if (assembly->f == NULL){
241                 if (status)
242                         *status = MONO_ASSEMBLY_ERROR_ERRNO;
243                 mono_assembly_close (assembly);
244                 return NULL;
245         }
246
247         if (status)
248                 *status = MONO_ASSEMBLY_IMAGE_INVALID;
249         
250         if (fread (&msdos, sizeof (msdos), 1, assembly->f) != 1)
251                 goto invalid_image;
252         
253         if (!(msdos.msdos_header [0] == 0x4d && msdos.msdos_header [1] == 0x5a))
254                 goto invalid_image;
255         
256         if ((n = fread (header, sizeof (dotnet_header_t), 1, assembly->f)) != 1)
257                 goto invalid_image;
258
259         /*
260          * FIXME: byte swap all addresses here for header.
261          */
262         
263         if (!load_section_tables (assembly, iinfo))
264                 goto invalid_image;
265         
266         /* Load the CLI header */
267         if (!load_cli_header (assembly, iinfo))
268                 goto invalid_image;
269
270         if (!load_metadata (assembly, iinfo))
271                 goto invalid_image;
272         
273         if (status)
274                 *status = MONO_ASSEMBLY_OK;
275
276         return assembly;
277
278 invalid_image:
279         mono_assembly_close (assembly);
280                 return NULL;
281 }
282
283 void
284 mono_assembly_close (MonoAssembly *assembly)
285 {
286         g_return_if_fail (assembly != NULL);
287
288         if (assembly->f)
289                 fclose (assembly->f);
290
291         if (assembly->image_info){
292                 dotnet_image_info_t *ii = assembly->image_info;
293
294                 if (ii->dn_metadata.raw_metadata != NULL)
295                         raw_buffer_free (ii->dn_metadata.raw_metadata);
296         
297                 if (ii->dn_section_tables)
298                         g_free (ii->dn_section_tables);
299
300                 g_free (assembly->image_info);
301         }
302         
303         g_free (assembly);
304 }
305
306 const char *
307 mono_assembly_strerror (enum MonoAssemblyOpenStatus status)
308 {
309         switch (status){
310         case MONO_ASSEMBLY_OK:
311                 return "succes";
312         case MONO_ASSEMBLY_ERROR_ERRNO:
313                 return strerror (errno);
314         case MONO_ASSEMBLY_IMAGE_INVALID:
315                 return "File does not contain a valid CIL image";
316         }
317         return "Internal error";
318 }