Mon Nov 12 12:41:32 CET 2001 Paolo Molaro <lupus@ximian.com>
[mono.git] / mono / metadata / reflection.c
1
2 /*
3  * reflection.c: Routines for creating an image at runtime.
4  * 
5  * Author:
6  *   Paolo Molaro (lupus@ximian.com)
7  *
8  * (C) 2001 Ximian, Inc.  http://www.ximian.com
9  *
10  */
11 #include <config.h>
12 #include "mono/metadata/reflection.h"
13 #include "mono/metadata/tabledefs.h"
14 #include "mono/metadata/tokentype.h"
15 #include <stdio.h>
16 #include <glib.h>
17 #include <errno.h>
18 #include <time.h>
19 #include <string.h>
20 #include "image.h"
21 #include "cil-coff.h"
22 #include "rawbuffer.h"
23 #include "mono-endian.h"
24 #include "private.h"
25
26 #define TEXT_OFFSET 512
27 #define CLI_H_SIZE 136
28 #define FILE_ALIGN 512
29
30 const unsigned char table_sizes [64] = {
31         MONO_MODULE_SIZE,
32         MONO_TYPEREF_SIZE,
33         MONO_TYPEDEF_SIZE,
34         0,
35         MONO_FIELD_SIZE,
36         0,
37         MONO_METHOD_SIZE,
38         0,
39         MONO_PARAM_SIZE,
40         MONO_INTERFACEIMPL_SIZE,
41         MONO_MEMBERREF_SIZE,    /* 0x0A */
42         MONO_CONSTANT_SIZE,
43         MONO_CUSTOM_ATTR_SIZE,
44         MONO_FIELD_MARSHAL_SIZE,
45         MONO_DECL_SECURITY_SIZE,
46         MONO_CLASS_LAYOUT_SIZE,
47         MONO_FIELD_LAYOUT_SIZE, /* 0x10 */
48         MONO_STAND_ALONE_SIGNATURE_SIZE,
49         MONO_EVENT_MAP_SIZE,
50         0,
51         MONO_EVENT_SIZE,
52         MONO_PROPERTY_MAP_SIZE,
53         0,
54         MONO_PROPERTY_SIZE,
55         MONO_METHOD_SEMA_SIZE,
56         MONO_MTHODIMPL_SIZE,
57         MONO_MODULEREF_SIZE,    /* 0x1A */
58         MONO_TYPESPEC_SIZE,
59         MONO_IMPLMAP_SIZE,      
60         MONO_FIELD_RVA_SIZE,
61         0,
62         0,
63         MONO_ASSEMBLY_SIZE,     /* 0x20 */
64         MONO_ASSEMBLY_PROCESSOR_SIZE,
65         MONO_ASSEMBLYOS_SIZE,
66         MONO_ASSEMBLYREF_SIZE,
67         MONO_ASSEMBLYREFPROC_SIZE,
68         MONO_ASSEMBLYREFOS_SIZE,
69         MONO_FILE_SIZE,
70         MONO_EXP_TYPE_SIZE,
71         MONO_MANIFEST_SIZE,
72         MONO_NESTED_CLASS_SIZE,
73         0       /* 0x2A */
74 };
75
76 static void
77 alloc_table (MonoDynamicTable *table, guint nrows)
78 {
79         table->rows = nrows;
80         g_assert (table->columns);
81         table->values = g_realloc (table->values, (1 + table->rows) * table->columns * sizeof (guint32));
82 }
83
84 static guint32
85 string_heap_insert (MonoStringHeap *sh, const char *str)
86 {
87         guint32 idx;
88         guint32 len;
89         gpointer oldkey, oldval;
90
91         if (g_hash_table_lookup_extended (sh->hash, str, &oldkey, &oldval))
92                 return GPOINTER_TO_UINT (oldval);
93
94         len = strlen (str) + 1;
95         idx = sh->index;
96         if (idx + len > sh->alloc_size) {
97                 sh->alloc_size += len + 4096;
98                 sh->data = g_realloc (sh->data, sh->alloc_size);
99         }
100         /*
101          * We strdup the string even if we already copy them in sh->data
102          * so that the string pointers in the hash remain valid even if
103          * we need to realloc sh->data. We may want to avoid that later.
104          */
105         g_hash_table_insert (sh->hash, g_strdup (str), GUINT_TO_POINTER (idx));
106         memcpy (sh->data + idx, str, len);
107         sh->index += len;
108         return idx;
109 }
110
111 static void
112 string_heap_init (MonoStringHeap *sh)
113 {
114         sh->index = 0;
115         sh->alloc_size = 4096;
116         sh->data = g_malloc (4096);
117         sh->hash = g_hash_table_new (g_str_hash, g_str_equal);
118         string_heap_insert (sh, "");
119 }
120
121 static void
122 string_heap_free (MonoStringHeap *sh)
123 {
124         g_free (sh->data);
125         g_hash_table_foreach (sh->hash, (GHFunc)g_free, NULL);
126         g_hash_table_destroy (sh->hash);
127 }
128
129 static guint32
130 mono_image_add_stream_data (MonoDynamicStream *stream, char *data, guint32 len)
131 {
132         guint32 idx;
133         if (stream->alloc_size < stream->index + len) {
134                 stream->alloc_size += len + 4096;
135                 stream->data = g_realloc (stream->data, stream->alloc_size);
136         }
137         memcpy (stream->data + stream->index, data, len);
138         idx = stream->index;
139         stream->index += len;
140         /* 
141          * align index? Not without adding an additional param that controls it since
142          * we may store a blob value in pieces.
143          */
144         return idx;
145 }
146
147 static void
148 encode_type (MonoType *type, char *p, char **endbuf)
149 {
150         switch (type->type){
151         case MONO_TYPE_VOID:
152         case MONO_TYPE_BOOLEAN:
153         case MONO_TYPE_CHAR:
154         case MONO_TYPE_I1:
155         case MONO_TYPE_U1:
156         case MONO_TYPE_I2:
157         case MONO_TYPE_U2:
158         case MONO_TYPE_I4:
159         case MONO_TYPE_U4:
160         case MONO_TYPE_I8:
161         case MONO_TYPE_U8:
162         case MONO_TYPE_R4:
163         case MONO_TYPE_R8:
164         case MONO_TYPE_I:
165         case MONO_TYPE_U:
166         case MONO_TYPE_STRING:
167         case MONO_TYPE_OBJECT:
168         case MONO_TYPE_TYPEDBYREF:
169                 mono_metadata_encode_value (type->type, p, endbuf);
170                 break;
171         case MONO_TYPE_SZARRAY:
172                 mono_metadata_encode_value (type->type, p, endbuf);
173                 encode_type (type->data.type, p, endbuf);
174                 break;
175         case MONO_TYPE_CLASS:
176                 mono_metadata_encode_value (type->type, p, endbuf);
177                 g_warning ("need to encode class %s", type->data.klass->name);
178                 break;
179         default:
180                 g_error ("need to encode type %x", type->type);
181         }
182 }
183
184 static guint32
185 method_encode_signature (MonoDynamicAssembly *assembly, MonoReflectionMethodBuilder *mb)
186 {
187         char *buf;
188         char *p;
189         int i;
190         guint32 nparams =  mb->parameters ? mono_array_length (mb->parameters): 0;
191         guint32 size = 10 + nparams * 10;
192         guint32 idx;
193         char blob_size [6];
194         char *b = blob_size;
195         
196         p = buf = g_malloc (size);
197         *p = 0;
198         if (!(mb->attrs & METHOD_ATTRIBUTE_STATIC))
199                 *p |= 0x20; /* hasthis */
200         /* 
201          * FIXME: set also call convention and explict_this if needed.
202          */
203         p++;
204         mono_metadata_encode_value (nparams, p, &p);
205         encode_type (mb->rtype->type, p, &p);
206         for (i = 0; i < nparams; ++i) {
207                 MonoReflectionType *pt = mono_array_get (mb->parameters, MonoReflectionType*, i);
208                 encode_type (pt->type, p, &p);
209         }
210         /* store length */
211         mono_metadata_encode_value (p-buf, b, &b);
212         idx = mono_image_add_stream_data (&assembly->blob, blob_size, b-blob_size);
213         mono_image_add_stream_data (&assembly->blob, buf, p-buf);
214         g_free (buf);
215         return idx;
216 }
217
218 static guint32
219 encode_locals (MonoDynamicAssembly *assembly, MonoReflectionILGen *ilgen)
220 {
221         MonoDynamicTable *table;
222         guint32 *values;
223         char *p;
224         guint32 idx, sig_idx;
225         guint nl = mono_array_length (ilgen->locals);
226         char *buf;
227         char blob_size [6];
228         char *b = blob_size;
229         int i;
230
231         p = buf = g_malloc (10 + nl * 10);
232         table = &assembly->tables [MONO_TABLE_STANDALONESIG];
233         idx = table->next_idx ++;
234         table->rows ++;
235         alloc_table (table, table->rows);
236         values = table->values + idx * MONO_STAND_ALONE_SIGNATURE_SIZE;
237
238         mono_metadata_encode_value (0x07, p, &p);
239         mono_metadata_encode_value (nl, p, &p);
240         for (i = 0; i < nl; ++i) {
241                 MonoReflectionLocalBuilder *lb = mono_array_get (ilgen->locals, MonoReflectionLocalBuilder*, i);
242                 encode_type (lb->type->type, p, &p);
243         }
244         mono_metadata_encode_value (p-buf, b, &b);
245         sig_idx = mono_image_add_stream_data (&assembly->blob, blob_size, b-blob_size);
246         mono_image_add_stream_data (&assembly->blob, buf, p-buf);
247         g_free (buf);
248
249         values [MONO_STAND_ALONE_SIGNATURE] = sig_idx;
250
251         return idx;
252 }
253
254 static guint32
255 method_encode_code (MonoDynamicAssembly *assembly, MonoReflectionMethodBuilder *mb)
256 {
257         /* we use only tiny formats now: need  to implement ILGenerator */
258         char flags = 0;
259         guint32 idx;
260         guint32 code_size;
261         gint32 max_stack;
262         gint32 num_locals = 0;
263         gint32 num_exception = 0;
264         gint maybe_small;
265         guint32 fat_flags;
266         char fat_header [12];
267         guint32 *intp;
268         guint16 *shortp;
269         guint32 local_sig = 0;
270         MonoArray *code;
271
272         if (mb->ilgen) {
273                 code = mb->ilgen->code;
274                 code_size = mb->ilgen->code_len;
275                 max_stack = mb->ilgen->max_stack;
276                 num_locals = mb->ilgen->locals ? mono_array_length (mb->ilgen->locals) : 0;
277         } else {
278                 code = mb->code;
279                 code_size = mono_array_length (code);
280                 max_stack = 8; /* we probably need to run a verifier on the code... */
281         }
282
283         /* check for exceptions, maxstack, locals */
284         maybe_small = (max_stack <= 8) && (!num_locals) && (!num_exception);
285         if (maybe_small) {
286                 if (code_size < 64 && !(code_size & 1)) {
287                         flags = (code_size << 2) | 0x2;
288                 } else if (code_size < 32 && (code_size & 1)) {
289                         flags = (code_size << 2) | 0x6; /* LAMESPEC: see metadata.c */
290                 } else {
291                         goto fat_header;
292                 }
293                 idx = mono_image_add_stream_data (&assembly->code, &flags, 1);
294                 mono_image_add_stream_data (&assembly->code, mono_array_addr (code, char, 0), code_size);
295                 return assembly->text_rva + idx + CLI_H_SIZE;
296         } 
297 fat_header:
298         if (num_locals)
299                 local_sig = encode_locals (assembly, mb->ilgen);
300         /* 
301          * FIXME: need to set also the header size in fat_flags.
302          * (and more sects and init locals flags)
303          */
304         fat_flags =  0x03;
305         shortp = (guint16*)(fat_header);
306         *shortp = fat_flags;
307         shortp = (guint16*)(fat_header + 2);
308         *shortp = max_stack;
309         intp = (guint32*)(fat_header + 4);
310         *intp = code_size;
311         intp = (guint32*)(fat_header + 8);
312         *intp = local_sig;
313         idx = mono_image_add_stream_data (&assembly->code, fat_header, 12);
314         mono_image_add_stream_data (&assembly->code, mono_array_addr (code, char, 0), code_size);
315         return assembly->text_rva + idx + CLI_H_SIZE;
316 }
317
318 static void
319 mono_image_get_method_info (MonoReflectionMethodBuilder *mb, MonoDynamicAssembly *assembly)
320 {
321         MonoDynamicTable *table;
322         guint32 *values;
323         char *name;
324
325         table = &assembly->tables [MONO_TABLE_METHOD];
326         mb->table_idx = table->next_idx ++;
327         values = table->values + mb->table_idx * MONO_METHOD_SIZE;
328         name = mono_string_to_utf8 (mb->name);
329         values [MONO_METHOD_NAME] = string_heap_insert (&assembly->sheap, name);
330         g_free (name);
331         values [MONO_METHOD_FLAGS] = mb->attrs;
332         values [MONO_METHOD_IMPLFLAGS] = 0;
333         values [MONO_METHOD_SIGNATURE] = method_encode_signature (assembly, mb);
334         values [MONO_METHOD_PARAMLIST] = 0; /* FIXME: add support later */
335         values [MONO_METHOD_RVA] = method_encode_code (assembly, mb);
336 }
337
338 static guint32
339 field_encode_signature (MonoDynamicAssembly *assembly, MonoReflectionFieldBuilder *fb)
340 {
341         char blob_size [64];
342         char *b = blob_size;
343         char *p;
344         char* buf;
345         guint32 idx;
346         
347         p = buf = g_malloc (64);
348         
349         mono_metadata_encode_value (0x06, p, &p);
350         /* encode custom attributes before the type */
351         encode_type (fb->type->type, p, &p);
352         g_assert (p-buf < 64);
353         mono_metadata_encode_value (p-buf, b, &b);
354         idx = mono_image_add_stream_data (&assembly->blob, blob_size, b-blob_size);
355         mono_image_add_stream_data (&assembly->blob, buf, p-buf);
356         g_free (buf);
357         return idx;
358 }
359
360 static void
361 mono_image_get_field_info (MonoReflectionFieldBuilder *fb, MonoDynamicAssembly *assembly)
362 {
363         MonoDynamicTable *table;
364         guint32 *values;
365         char *name;
366
367         table = &assembly->tables [MONO_TABLE_FIELD];
368         fb->table_idx = table->next_idx ++;
369         values = table->values + fb->table_idx * MONO_FIELD_SIZE;
370         name = mono_string_to_utf8 (fb->name);
371         values [MONO_FIELD_NAME] = string_heap_insert (&assembly->sheap, name);
372         g_free (name);
373         values [MONO_FIELD_FLAGS] = fb->attrs;
374         values [MONO_FIELD_SIGNATURE] = field_encode_signature (assembly, fb);
375 }
376
377 static guint32
378 property_encode_signature (MonoDynamicAssembly *assembly, MonoReflectionPropertyBuilder *fb)
379 {
380         char *buf, *p;
381         char blob_size [6];
382         char *b = blob_size;
383         guint32 nparams = 0;
384         MonoReflectionMethodBuilder *mb = fb->get_method;
385         guint32 idx, i;
386
387         if (mb && mb->parameters)
388                 nparams = mono_array_length (mb->parameters);
389         buf = p = g_malloc (24 + nparams * 10);
390         *p = 0x08;
391         p++;
392         mono_metadata_encode_value (nparams, p, &p);
393         if (mb) {
394                 encode_type (mb->rtype->type, p, &p);
395                 for (i = 0; i < nparams; ++i) {
396                         MonoReflectionType *pt = mono_array_get (mb->parameters, MonoReflectionType*, i);
397                         encode_type (pt->type, p, &p);
398                 }
399         } else {
400                 *p++ = 1; /* void: a property should probably not be allowed without a getter */
401         }
402         /* store length */
403         mono_metadata_encode_value (p-buf, b, &b);
404         idx = mono_image_add_stream_data (&assembly->blob, blob_size, b-blob_size);
405         mono_image_add_stream_data (&assembly->blob, buf, p-buf);
406         g_free (buf);
407         return idx;
408 }
409
410 static void
411 mono_image_get_property_info (MonoReflectionPropertyBuilder *pb, MonoDynamicAssembly *assembly)
412 {
413         MonoDynamicTable *table;
414         guint32 *values;
415         char *name;
416         guint num_methods = 0;
417         guint32 semaidx;
418
419         /* 
420          * we need to set things in the following tables:
421          * PROPERTYMAP (info already filled in _get_type_info ())
422          * PROPERTY    (rows already preallocated in _get_type_info ())
423          * METHOD      (method info already done with the generic method code)
424          * METHODSEMANTICS
425          */
426         table = &assembly->tables [MONO_TABLE_PROPERTY];
427         pb->table_idx = table->next_idx ++;
428         values = table->values + pb->table_idx * MONO_FIELD_SIZE;
429         name = mono_string_to_utf8 (pb->name);
430         values [MONO_PROPERTY_NAME] = string_heap_insert (&assembly->sheap, name);
431         g_free (name);
432         values [MONO_PROPERTY_FLAGS] = pb->attrs;
433         values [MONO_PROPERTY_TYPE] = property_encode_signature (assembly, pb);
434
435         /* FIXME: we still don't handle 'other' methods */
436         if (pb->get_method) num_methods ++;
437         if (pb->set_method) num_methods ++;
438
439         table = &assembly->tables [MONO_TABLE_METHODSEMANTICS];
440         table->rows += num_methods;
441         alloc_table (table, table->rows);
442
443         if (pb->get_method) {
444                 semaidx = table->next_idx ++;
445                 values = table->values + semaidx * MONO_METHOD_SEMA_SIZE;
446                 values [MONO_METHOD_SEMA_SEMANTICS] = METHOD_SEMANTIC_GETTER;
447                 values [MONO_METHOD_SEMA_METHOD] = pb->get_method->table_idx;
448                 values [MONO_METHOD_SEMA_ASSOCIATION] = (pb->table_idx << 1) | 1;
449         }
450         if (pb->set_method) {
451                 semaidx = table->next_idx ++;
452                 values = table->values + semaidx * MONO_METHOD_SEMA_SIZE;
453                 values [MONO_METHOD_SEMA_SEMANTICS] = METHOD_SEMANTIC_SETTER;
454                 values [MONO_METHOD_SEMA_METHOD] = pb->set_method->table_idx;
455                 values [MONO_METHOD_SEMA_ASSOCIATION] = (pb->table_idx << 1) | 0;
456         }
457 }
458
459 static guint32
460 mono_image_typedef_or_ref (MonoDynamicAssembly *assembly, MonoClass *klass)
461 {
462         MonoDynamicTable *table;
463         guint32 *values;
464         guint32 token;
465
466         if (!assembly->typeref)
467                 assembly->typeref = g_hash_table_new (g_direct_hash, g_direct_equal);
468         
469         token = GPOINTER_TO_UINT (g_hash_table_lookup (assembly->typeref, klass));
470         if (token)
471                 return token;
472         if (klass->image != mono_defaults.corlib)
473                 g_error ("multiple assemblyref not yet supported");
474
475         table = &assembly->tables [MONO_TABLE_TYPEREF];
476         alloc_table (table, table->rows + 1);
477         values = table->values + table->next_idx * MONO_TYPEREF_SIZE;
478         values [MONO_TYPEREF_SCOPE] = (1 << 2) | 2; /* first row in assemblyref LAMESPEC, see get.c */
479         values [MONO_TYPEREF_NAME] = string_heap_insert (&assembly->sheap, klass->name);
480         values [MONO_TYPEREF_NAMESPACE] = string_heap_insert (&assembly->sheap, klass->name_space);
481         token = 1 | (table->next_idx << 2); /* typeref */
482         g_hash_table_insert (assembly->typeref, klass, GUINT_TO_POINTER(token));
483         table->next_idx ++;
484         return token;
485 }
486
487 static void
488 mono_image_get_type_info (MonoReflectionTypeBuilder *tb, MonoDynamicAssembly *assembly)
489 {
490         MonoDynamicTable *table;
491         guint *values;
492         int i;
493         char *n;
494
495         table = &assembly->tables [MONO_TABLE_TYPEDEF];
496         tb->table_idx = table->next_idx ++;
497         values = table->values + tb->table_idx * MONO_TYPEDEF_SIZE;
498         values [MONO_TYPEDEF_FLAGS] = tb->attrs;
499         /* FIXME: use tb->base later */
500         values [MONO_TYPEDEF_EXTENDS] = mono_image_typedef_or_ref (assembly, mono_defaults.object_class);
501         n = mono_string_to_utf8 (tb->name);
502         values [MONO_TYPEDEF_NAME] = string_heap_insert (&assembly->sheap, n);
503         g_free (n);
504         n = mono_string_to_utf8 (tb->nspace);
505         values [MONO_TYPEDEF_NAMESPACE] = string_heap_insert (&assembly->sheap, n);
506         g_free (n);
507         values [MONO_TYPEDEF_FIELD_LIST] = assembly->tables [MONO_TABLE_FIELD].next_idx;
508         values [MONO_TYPEDEF_METHOD_LIST] = assembly->tables [MONO_TABLE_METHOD].next_idx;
509
510         /* handle methods */
511         if (tb->methods) {
512                 table = &assembly->tables [MONO_TABLE_METHOD];
513                 table->rows += mono_array_length (tb->methods);
514                 alloc_table (table, table->rows);
515                 for (i = 0; i < mono_array_length (tb->methods); ++i)
516                         mono_image_get_method_info (
517                                 mono_array_get (tb->methods, MonoReflectionMethodBuilder*, i), assembly);
518         }
519
520         /* handle fields */
521         if (tb->fields) {
522                 table = &assembly->tables [MONO_TABLE_FIELD];
523                 table->rows += mono_array_length (tb->fields);
524                 alloc_table (table, table->rows);
525                 for (i = 0; i < mono_array_length (tb->fields); ++i)
526                         mono_image_get_field_info (
527                                 mono_array_get (tb->fields, MonoReflectionFieldBuilder*, i), assembly);
528         }
529
530         /* Do the same with properties etc.. */
531         if (tb->properties && mono_array_length (tb->properties)) {
532                 table = &assembly->tables [MONO_TABLE_PROPERTY];
533                 table->rows += mono_array_length (tb->properties);
534                 alloc_table (table, table->rows);
535                 table = &assembly->tables [MONO_TABLE_PROPERTYMAP];
536                 table->rows ++;
537                 alloc_table (table, table->rows);
538                 values = table->values + table->rows * MONO_PROPERTY_MAP_SIZE;
539                 values [MONO_PROPERTY_MAP_PARENT] = tb->table_idx;
540                 values [MONO_PROPERTY_MAP_PROPERTY_LIST] = assembly->tables [MONO_TABLE_PROPERTY].next_idx;
541                 for (i = 0; i < mono_array_length (tb->properties); ++i)
542                         mono_image_get_property_info (
543                                 mono_array_get (tb->properties, MonoReflectionPropertyBuilder*, i), assembly);
544         }
545 }
546
547 static void
548 mono_image_fill_module_table (MonoReflectionModuleBuilder *mb, MonoDynamicAssembly *assembly)
549 {
550         MonoDynamicTable *table;
551         int i;
552         char *name;
553
554         table = &assembly->tables [MONO_TABLE_MODULE];
555         mb->table_idx = table->next_idx ++;
556         name = mono_string_to_utf8 (mb->module.name);
557         table->values [mb->table_idx * MONO_MODULE_SIZE + MONO_MODULE_NAME] = string_heap_insert (&assembly->sheap, name);
558         g_free (name);
559         /* need to set mvid? */
560
561         /*
562          * fill-in info in other tables as well.
563          */
564         table = &assembly->tables [MONO_TABLE_TYPEDEF];
565         table->rows += mono_array_length (mb->types);
566         alloc_table (table, table->rows);
567         for (i = 0; i < mono_array_length (mb->types); ++i)
568                 mono_image_get_type_info (mono_array_get (mb->types, MonoReflectionTypeBuilder*, i), assembly);
569 }
570
571 #define align_pointer(base,p)\
572         do {\
573                 guint32 __diff = (unsigned char*)(p)-(unsigned char*)(base);\
574                 if (__diff & 3)\
575                         (p) += 4 - (__diff & 3);\
576         } while (0)
577
578 static void
579 build_compressed_metadata (MonoDynamicAssembly *assembly)
580 {
581         int i;
582         guint64 valid_mask = 0;
583         guint32 heapt_size = 0;
584         guint32 meta_size = 256; /* allow for header and other stuff */
585         guint32 table_offset;
586         guint32 ntables = 0;
587         guint64 *int64val;
588         guint32 *int32val;
589         guint16 *int16val;
590         MonoImage *meta;
591         unsigned char *p;
592         char *version = "mono" VERSION;
593         
594         /* Compute table sizes */
595         meta = assembly->assembly.image = g_new0 (MonoImage, 1);
596         
597         /* Setup the info used by compute_sizes () */
598         meta->idx_blob_wide = assembly->blob.index >= 65536 ? 1 : 0;
599         meta->idx_guid_wide = assembly->guid.index >= 65536 ? 1 : 0;
600         meta->idx_string_wide = assembly->sheap.index >= 65536 ? 1 : 0;
601
602         meta_size += assembly->blob.index;
603         meta_size += assembly->guid.index;
604         meta_size += assembly->sheap.index;
605         meta_size += assembly->us.index;
606
607         for (i=0; i < 64; ++i)
608                 meta->tables [i].rows = assembly->tables [i].rows;
609         
610         for (i = 0; i < 64; i++){
611                 if (meta->tables [i].rows == 0)
612                         continue;
613                 valid_mask |= (guint64)1 << i;
614                 ntables ++;
615                 meta->tables [i].row_size = mono_metadata_compute_size (
616                         meta, i, &meta->tables [i].size_bitfield);
617                 heapt_size += meta->tables [i].row_size * meta->tables [i].rows;
618         }
619         heapt_size += 24; /* #~ header size */
620         heapt_size += ntables * 4;
621         meta_size += heapt_size;
622         meta->raw_metadata = g_malloc0 (meta_size);
623         p = meta->raw_metadata;
624         /* the metadata signature */
625         *p++ = 'B'; *p++ = 'S'; *p++ = 'J'; *p++ = 'B';
626         /* version numbers and 4 bytes reserved */
627         int16val = (guint16*)p;
628         *int16val++ = 1;
629         *int16val = 1;
630         p += 8;
631         /* version string */
632         int32val = (guint32*)p;
633         *int32val = strlen (version);
634         p += 4;
635         memcpy (p, version, *int32val);
636         p += *int32val;
637         align_pointer (meta->raw_metadata, p);
638         int16val = (guint16*)p;
639         *int16val++ = 0; /* flags must be 0 */
640         *int16val = 5; /* number of streams */
641         p += 4;
642
643         /*
644          * write the stream info.
645          */
646         table_offset = (p - (unsigned char*)meta->raw_metadata) + 5 * 8 + 40; /* room needed for stream headers */
647         
648         int32val = (guint32*)p;
649         *int32val++ = assembly->tstream.offset = table_offset;
650         *int32val = heapt_size;
651         table_offset += *int32val;
652         p += 8;
653         strcpy (p, "#~");
654         p += 3;
655         align_pointer (meta->raw_metadata, p);
656
657         int32val = (guint32*)p;
658         *int32val++ = assembly->sheap.offset = table_offset;
659         *int32val = assembly->sheap.index;
660         table_offset += *int32val;
661         p += 8;
662         strcpy (p, "#Strings");
663         p += 9;
664         align_pointer (meta->raw_metadata, p);
665
666         int32val = (guint32*)p;
667         *int32val++ = assembly->us.offset = table_offset;
668         *int32val = assembly->us.index;
669         table_offset += *int32val;
670         p += 8;
671         strcpy (p, "#US");
672         p += 4;
673         align_pointer (meta->raw_metadata, p);
674
675         int32val = (guint32*)p;
676         *int32val++ = assembly->blob.offset = table_offset;
677         *int32val = assembly->blob.index;
678         table_offset += *int32val;
679         p += 8;
680         strcpy (p, "#Blob");
681         p += 6;
682         align_pointer (meta->raw_metadata, p);
683
684         int32val = (guint32*)p;
685         *int32val++ = assembly->guid.offset = table_offset;
686         *int32val = assembly->guid.index;
687         table_offset += *int32val;
688         p += 8;
689         strcpy (p, "#GUID");
690         p += 6;
691         align_pointer (meta->raw_metadata, p);
692
693         /* 
694          * now copy the data, the table stream header and contents goes first.
695          */
696         g_assert ((p - (unsigned char*)meta->raw_metadata) < assembly->tstream.offset);
697         p = meta->raw_metadata + assembly->tstream.offset;
698         int32val = (guint32*)p;
699         *int32val = 0; /* reserved */
700         p += 4;
701         *p++ = 1; /* version */
702         *p++ = 0;
703         if (meta->idx_string_wide)
704                 *p |= 0x01;
705         if (meta->idx_guid_wide)
706                 *p |= 0x02;
707         if (meta->idx_blob_wide)
708                 *p |= 0x04;
709         ++p;
710         *p++ = 0; /* reserved */
711         int64val = (guint64*)p;
712         *int64val++ = valid_mask;
713         *int64val++ = 0; /* bitvector of sorted tables, set to 0 for now  */
714         p += 16;
715         int32val = (guint32*)p;
716         for (i = 0; i < 64; i++){
717                 if (meta->tables [i].rows == 0)
718                         continue;
719                 *int32val++ = meta->tables [i].rows;
720         }
721         p = (unsigned char*)int32val;
722         /* compress the tables */
723         for (i = 0; i < 64; i++){
724                 int row, col;
725                 guint32 *values;
726                 guint32 bitfield = meta->tables [i].size_bitfield;
727                 if (!meta->tables [i].rows)
728                         continue;
729                 if (assembly->tables [i].columns != mono_metadata_table_count (bitfield))
730                         g_error ("col count mismatch in %d: %d %d", i, assembly->tables [i].columns, mono_metadata_table_count (bitfield));
731                 meta->tables [i].base = p;
732                 for (row = 1; row <= meta->tables [i].rows; ++row) {
733                         values = assembly->tables [i].values + row * assembly->tables [i].columns;
734                         for (col = 0; col < assembly->tables [i].columns; ++col) {
735                                 switch (mono_metadata_table_size (bitfield, col)) {
736                                 case 1:
737                                         *p++ = values [col];
738                                         break;
739                                 case 2:
740                                         int16val = (guint16*)p;
741                                         *int16val = values [col];
742                                         p += 2;
743                                         break;
744                                 case 4:
745                                         int32val = (guint32*)p;
746                                         *int32val = values [col];
747                                         p += 4;
748                                         break;
749                                 default:
750                                         g_assert_not_reached ();
751                                 }
752                         }
753                 }
754                 g_assert ((p - (unsigned char*)meta->tables [i].base) == (meta->tables [i].rows * meta->tables [i].row_size));
755         }
756         
757         g_assert (assembly->guid.offset + assembly->guid.index < meta_size);
758         memcpy (meta->raw_metadata + assembly->sheap.offset, assembly->sheap.data, assembly->sheap.index);
759         memcpy (meta->raw_metadata + assembly->us.offset, assembly->us.data, assembly->us.index);
760         memcpy (meta->raw_metadata + assembly->blob.offset, assembly->blob.data, assembly->blob.index);
761         memcpy (meta->raw_metadata + assembly->guid.offset, assembly->guid.data, assembly->guid.index);
762
763         assembly->meta_size = assembly->guid.offset + assembly->guid.index;
764 }
765
766 static void
767 mono_image_build_metadata (MonoReflectionAssemblyBuilder *assemblyb)
768 {
769         MonoDynamicTable *table;
770         MonoDynamicAssembly *assembly = assemblyb->dynamic_assembly;
771         guint32 len;
772         guint32 *values;
773         char *name;
774         int i;
775         
776         /*
777          * FIXME: check if metadata was already built. 
778          */
779         string_heap_init (&assembly->sheap);
780         mono_image_add_stream_data (&assembly->us, "", 1);
781         mono_image_add_stream_data (&assembly->blob, "", 1);
782
783         assembly->text_rva =  0x00002000;
784
785         for (i=0; i < 64; ++i) {
786                 assembly->tables [i].next_idx = 1;
787                 assembly->tables [i].columns = table_sizes [i];
788         }
789         
790         table = &assembly->tables [MONO_TABLE_ASSEMBLY];
791         alloc_table (table, 1);
792         values = table->values + MONO_ASSEMBLY_SIZE;
793         values [MONO_ASSEMBLY_HASH_ALG] = 0x8004;
794         name = mono_string_to_utf8 (assemblyb->name);
795         values [MONO_ASSEMBLY_NAME] = string_heap_insert (&assembly->sheap, name);
796         g_free (name);
797         values [MONO_ASSEMBLY_CULTURE] = string_heap_insert (&assembly->sheap, "");
798
799         assembly->tables [MONO_TABLE_TYPEDEF].rows = 1; /* .<Module> */
800         assembly->tables [MONO_TABLE_TYPEDEF].next_idx++;
801
802         len = mono_array_length (assemblyb->modules);
803         table = &assembly->tables [MONO_TABLE_MODULE];
804         alloc_table (table, len);
805         for (i = 0; i < len; ++i)
806                 mono_image_fill_module_table (mono_array_get (assemblyb->modules, MonoReflectionModuleBuilder*, i), assembly);
807
808         table = &assembly->tables [MONO_TABLE_TYPEDEF];
809         /* 
810          * table->rows is already set above and in mono_image_fill_module_table.
811          */
812         alloc_table (table, table->rows);
813         /*
814          * Set the first entry.
815          */
816         values = table->values + table->columns;
817         values [MONO_TYPEDEF_FLAGS] = 0;
818         values [MONO_TYPEDEF_NAME] = string_heap_insert (&assembly->sheap, "<Module>") ;
819         values [MONO_TYPEDEF_NAMESPACE] = string_heap_insert (&assembly->sheap, "") ;
820         values [MONO_TYPEDEF_EXTENDS] = 0;
821         values [MONO_TYPEDEF_FIELD_LIST] = 1;
822         values [MONO_TYPEDEF_METHOD_LIST] = 1;
823
824         /* later include all the assemblies referenced */
825         table = &assembly->tables [MONO_TABLE_ASSEMBLYREF];
826         alloc_table (table, 1);
827         values = table->values + table->columns;
828         values [MONO_ASSEMBLYREF_NAME] = string_heap_insert (&assembly->sheap, "corlib");
829
830         build_compressed_metadata (assembly);
831 }
832
833 int
834 mono_image_get_header (MonoReflectionAssemblyBuilder *assemblyb, char *buffer, int maxsize)
835 {
836         MonoMSDOSHeader *msdos;
837         MonoDotNetHeader *header;
838         MonoSectionTable *section;
839         MonoCLIHeader *cli_header;
840         guint32 header_size =  TEXT_OFFSET + CLI_H_SIZE;
841         MonoDynamicAssembly *assembly;
842
843         static const unsigned char msheader[] = {
844                 0x4d, 0x5a, 0x90, 0x00, 0x03, 0x00, 0x00, 0x00,  0x04, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00,
845                 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
846                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
847                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00,
848                 0x0e, 0x1f, 0xba, 0x0e, 0x00, 0xb4, 0x09, 0xcd,  0x21, 0xb8, 0x01, 0x4c, 0xcd, 0x21, 0x54, 0x68,
849                 0x69, 0x73, 0x20, 0x70, 0x72, 0x6f, 0x67, 0x72,  0x61, 0x6d, 0x20, 0x63, 0x61, 0x6e, 0x6e, 0x6f,
850                 0x74, 0x20, 0x62, 0x65, 0x20, 0x72, 0x75, 0x6e,  0x20, 0x69, 0x6e, 0x20, 0x44, 0x4f, 0x53, 0x20,
851                 0x6d, 0x6f, 0x64, 0x65, 0x2e, 0x0d, 0x0d, 0x0a,  0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
852         };
853
854         if (maxsize < header_size)
855                 return -1;
856
857         assembly = assemblyb->dynamic_assembly = g_new0 (MonoDynamicAssembly, 1);
858         mono_image_build_metadata (assemblyb);
859
860         memset (buffer, 0, header_size);
861         memcpy (buffer, msheader, sizeof (MonoMSDOSHeader));
862
863         msdos = (MonoMSDOSHeader *)buffer;
864         header = (MonoDotNetHeader *)(buffer + sizeof (MonoMSDOSHeader));
865         section = (MonoSectionTable*) (buffer + sizeof (MonoMSDOSHeader) + sizeof (MonoDotNetHeader));
866
867         /* FIXME: ENDIAN problem: byteswap as needed */
868         msdos->pe_offset = sizeof (MonoMSDOSHeader);
869
870         header->pesig [0] = 'P';
871         header->pesig [1] = 'E';
872         header->pesig [2] = header->pesig [3] = 0;
873
874         header->coff.coff_machine = 0x14c;
875         header->coff.coff_sections = 1; /* only .text supported now */
876         header->coff.coff_time = time (NULL);
877         header->coff.coff_opt_header_size = sizeof (MonoDotNetHeader) - sizeof (MonoCOFFHeader) - 4;
878         /* it's an exe */
879         header->coff.coff_attributes = 0x010e;
880         /* it's a dll */
881         //header->coff.coff_attributes = 0x210e;
882         header->pe.pe_magic = 0x10B;
883         header->pe.pe_major = 6;
884         header->pe.pe_minor = 0;
885         /* need to set: pe_code_size pe_data_size pe_rva_entry_point pe_rva_code_base pe_rva_data_base */
886
887         header->nt.pe_image_base = 0x400000;
888         header->nt.pe_section_align = 8192;
889         header->nt.pe_file_alignment = FILE_ALIGN;
890         header->nt.pe_os_major = 4;
891         header->nt.pe_os_minor = 0;
892         header->nt.pe_subsys_major = 4;
893         /* need to set pe_image_size, pe_header_size */
894         header->nt.pe_subsys_required = 3; /* 3 -> cmdline app, 2 -> GUI app */
895         header->nt.pe_stack_reserve = 0x00100000;
896         header->nt.pe_stack_commit = 0x00001000;
897         header->nt.pe_heap_reserve = 0x00100000;
898         header->nt.pe_heap_commit = 0x00001000;
899         header->nt.pe_loader_flags = 1;
900         header->nt.pe_data_dir_count = 16;
901
902 #if 0
903         /* set: */
904         header->datadir.pe_import_table
905         pe_resource_table
906         pe_reloc_table
907         pe_iat  
908 #endif
909         header->datadir.pe_cli_header.size = CLI_H_SIZE;
910         header->datadir.pe_cli_header.rva = assembly->text_rva; /* we put it always at the beginning */
911
912         /* Write section tables */
913         strcpy (section->st_name, ".text");
914         section->st_virtual_size = 1024; /* FIXME */
915         section->st_virtual_address = assembly->text_rva;
916         section->st_raw_data_size = 1024; /* FIXME */
917         section->st_raw_data_ptr = TEXT_OFFSET;
918         section->st_flags = SECT_FLAGS_HAS_CODE | SECT_FLAGS_MEM_EXECUTE | SECT_FLAGS_MEM_READ;
919
920         /* 
921          * align: build_compressed_metadata () assumes metadata is aligned 
922          * see below:
923          * cli_header->ch_metadata.rva = assembly->text_rva + assembly->code.index + CLI_H_SIZE;
924          */
925         assembly->code.index += 3;
926         assembly->code.index &= ~3;
927
928         /*
929          * Write the MonoCLIHeader header 
930          */
931         cli_header = (MonoCLIHeader*)(buffer + TEXT_OFFSET);
932         cli_header->ch_size = CLI_H_SIZE;
933         cli_header->ch_runtime_major = 2;
934         cli_header->ch_flags = CLI_FLAGS_ILONLY;
935         if (assemblyb->entry_point) 
936                 cli_header->ch_entry_point = assemblyb->entry_point->table_idx | MONO_TOKEN_METHOD_DEF;
937         else
938                 cli_header->ch_entry_point = 0;
939         cli_header->ch_metadata.rva = assembly->text_rva + assembly->code.index + CLI_H_SIZE;
940         cli_header->ch_metadata.size = assembly->meta_size;
941         
942         return header_size;
943 }
944