089b2c4d12b61d57191bff60274fe7793a339ad4
[mono.git] / mono / dis / main.c
1 /*
2  * dis.c: Sample disassembler
3  *
4  * Author:
5  *   Miguel de Icaza (miguel@ximian.com)
6  *
7  * (C) 2001 Ximian, Inc.
8  *
9  * TODO:
10  *   Investigate how interface inheritance works and how it should be dumped.
11  *   Structs are not being labeled as `valuetype' classes
12  *   Support CustomMods.
13  *
14  */
15 #include <config.h>
16 #include <stdio.h>
17 #include <string.h>
18 #include <glib.h>
19 #include <mono/metadata/assembly.h>
20 #include <mono/metadata/cil-coff.h>
21 #include <mono/metadata/endian.h>
22 #include <mono/metadata/typeattr.h>
23 #include <mono/metadata/fieldattr.h>
24 #include <mono/metadata/methodattr.h>
25 #include <mono/metadata/eltype.h>
26 #include <mono/metadata/blobsig.h>
27 #include <mono/metadata/paramattr.h>
28 #include "util.h"
29
30 FILE *output;
31
32 /* True if you want to get a dump of the header data */
33 gboolean dump_header_data_p = FALSE;
34
35 int dump_table = -1;
36
37 static void
38 dump_header_data (MonoAssembly *ass)
39 {
40         if (!dump_header_data_p)
41                 return;
42
43         fprintf (output,
44                  "// Ximian's CIL disassembler, version 1.0\n"
45                  "// Copyright (C) 2001 Ximian, Inc.\n\n");
46 }
47
48 #define CSIZE(x) (sizeof (x) / 4)
49 static void
50 expand (metadata_tableinfo_t *t, int idx, guint32 *res, int res_size)
51 {
52         guint32 bitfield = t->size_bitfield;
53         int i, count = meta_table_count (bitfield);
54         char *data = t->base + idx * t->row_size;
55         
56         g_assert (res_size == count);
57         
58         for (i = 0; i < count; i++){
59                 int n = meta_table_size (bitfield, i);
60
61                 switch (n){
62                 case 1:
63                         res [i] = *data; break;
64                 case 2:
65                         res [i] = read16 (data); break;
66                         
67                 case 4:
68                         res [i] = read32 (data); break;
69                         
70                 default:
71                         g_assert_not_reached ();
72                 }
73                 data += n;
74         }
75 }
76
77 static void
78 dis_directive_assembly (metadata_t *m)
79 {
80         metadata_tableinfo_t *t  = &m->tables [META_TABLE_ASSEMBLY];
81         guint32 cols [9];
82         
83         if (t->base == NULL)
84                 return;
85
86         expand (t, 0, cols, CSIZE (cols));
87         
88         fprintf (output,
89                  ".assembly %s\n"
90                  "{\n"
91                  "  .hash algorithm 0x%08x\n"
92                  "  .ver  %d.%d.%d.%d"
93                  "%s %s"
94                  "%s"
95                  "\n"
96                  "}\n",
97                  mono_metadata_string_heap (m, cols [7]),
98                  cols [0],
99                  cols [1], cols [2], cols [3], cols [4],
100                  cols [8] ? "\n  .locale" : "",
101                  cols [8] ? mono_metadata_string_heap (m, cols [8]) : "",
102                  cols [6] ? "\n  .publickey" : ""
103                 );
104 }
105
106 static void
107 dis_directive_assemblyref (metadata_t *m)
108 {
109         metadata_tableinfo_t *t = &m->tables [META_TABLE_ASSEMBLYREF];
110         guint32 cols [9];
111         int i;
112         
113         if (t->base == NULL)
114                 return;
115
116         for (i = 0; i < t->rows; i++){
117                 expand (t, i, cols, CSIZE (cols));
118
119                 fprintf (output,
120                          ".assembly extern %s\n"
121                          "{\n"
122                          "  .ver %d.%d.%d.%d\n"
123                          "}\n",
124                          mono_metadata_string_heap (m, cols [6]),
125                          cols [0], cols [1], cols [2], cols [3]
126                         );
127         }
128 }
129
130 static map_t visibility_map [] = {
131         { TYPE_ATTRIBUTE_NOT_PUBLIC,           "private " },
132         { TYPE_ATTRIBUTE_PUBLIC,               "public " },
133         { TYPE_ATTRIBUTE_NESTED_PUBLIC,        "nested-public " },
134         { TYPE_ATTRIBUTE_NESTED_PRIVATE,       "nested-private " },
135         { TYPE_ATTRIBUTE_NESTED_FAMILY,        "family " },
136         { TYPE_ATTRIBUTE_NESTED_ASSEMBLY,      "nested-assembly" },
137         { TYPE_ATTRIBUTE_NESTED_FAM_AND_ASSEM, "nested-fam-and-assembly" },
138         { TYPE_ATTRIBUTE_NESTED_FAM_OR_ASSEM,  "nested-fam-or-assembly" },
139         { 0, NULL }
140 };
141
142 static map_t layout_map [] = {
143         { TYPE_ATTRIBUTE_AUTO_LAYOUT,          "auto " },
144         { TYPE_ATTRIBUTE_SEQUENTIAL_LAYOUT,    "sequential " },
145         { TYPE_ATTRIBUTE_EXPLICIT_LAYOUT,      "explicit " },
146         { 0, NULL }
147 };
148
149 static map_t format_map [] = {
150         { TYPE_ATTRIBUTE_ANSI_CLASS,           "ansi " },
151         { TYPE_ATTRIBUTE_UNICODE_CLASS,        "unicode " },
152         { TYPE_ATTRIBUTE_AUTO_CLASS,           "auto " },
153         { 0, NULL }
154 };
155
156 static char *
157 typedef_flags (guint32 flags)
158 {
159         static char buffer [1024];
160         int visibility = flags & TYPE_ATTRIBUTE_VISIBILITY_MASK;
161         int layout = flags & TYPE_ATTRIBUTE_LAYOUT_MASK;
162         int format = flags & TYPE_ATTRIBUTE_STRING_FORMAT_MASK;
163         
164         buffer [0] = 0;
165
166         strcat (buffer, map (visibility, visibility_map));
167         strcat (buffer, map (layout, layout_map));
168         strcat (buffer, map (format, format_map));
169         
170         if (flags & TYPE_ATTRIBUTE_ABSTRACT)
171                 strcat (buffer, "abstract ");
172         if (flags & TYPE_ATTRIBUTE_SEALED)
173                 strcat (buffer, "sealed ");
174         if (flags & TYPE_ATTRIBUTE_SPECIAL_NAME)
175                 strcat (buffer, "special-name ");
176         if (flags & TYPE_ATTRIBUTE_IMPORT)
177                 strcat (buffer, "import ");
178         if (flags & TYPE_ATTRIBUTE_SERIALIZABLE)
179                 strcat (buffer, "serializable ");
180         if (flags & TYPE_ATTRIBUTE_BEFORE_FIELD_INIT)
181                 strcat (buffer, "beforefieldinit ");
182
183         return buffer;
184 }
185
186 static map_t field_access_map [] = {
187         { FIELD_ATTRIBUTE_COMPILER_CONTROLLED, "compilercontrolled " },
188         { FIELD_ATTRIBUTE_PRIVATE,             "private " },
189         { FIELD_ATTRIBUTE_FAM_AND_ASSEM,       "famandassem " },
190         { FIELD_ATTRIBUTE_ASSEMBLY,            "assembly " },
191         { FIELD_ATTRIBUTE_FAMILY,              "family " },
192         { FIELD_ATTRIBUTE_FAM_OR_ASSEM,        "famorassem " },
193         { FIELD_ATTRIBUTE_PUBLIC,              "public " },
194         { 0, NULL }
195 };
196
197 static map_t field_flags_map [] = {
198         { FIELD_ATTRIBUTE_STATIC,              "static " },
199         { FIELD_ATTRIBUTE_INIT_ONLY,           "initonly " },
200         { FIELD_ATTRIBUTE_LITERAL,             "literal " },
201         { FIELD_ATTRIBUTE_NOT_SERIALIZED,      "notserialized " },
202         { FIELD_ATTRIBUTE_SPECIAL_NAME,        "specialname " },
203         { FIELD_ATTRIBUTE_PINVOKE_IMPL,        "FIXME:pinvokeimpl " },
204         { 0, NULL }
205 };
206
207 static map_t element_type_map [] = {
208         { ELEMENT_TYPE_END        , "end" },
209         { ELEMENT_TYPE_VOID       , "void" },
210         { ELEMENT_TYPE_BOOLEAN    , "bool" },
211         { ELEMENT_TYPE_CHAR       , "char" }, 
212         { ELEMENT_TYPE_I1         , "sbyte" },
213         { ELEMENT_TYPE_U1         , "byte" }, 
214         { ELEMENT_TYPE_I2         , "int16" },
215         { ELEMENT_TYPE_U2         , "uint16" },
216         { ELEMENT_TYPE_I4         , "int32" },
217         { ELEMENT_TYPE_U4         , "uint32" },
218         { ELEMENT_TYPE_I8         , "int64" },
219         { ELEMENT_TYPE_U8         , "uint64" },
220         { ELEMENT_TYPE_R4         , "float32" },
221         { ELEMENT_TYPE_R8         , "float64" },
222         { ELEMENT_TYPE_STRING     , "string" },
223         { ELEMENT_TYPE_TYPEDBYREF , "TypedByRef" },
224         { ELEMENT_TYPE_I          , "native int" },
225         { ELEMENT_TYPE_U          , "native unsigned int" },
226         { ELEMENT_TYPE_OBJECT     , "object" },
227         { 0, NULL }
228 };
229
230 /**
231  * field_flags:
232  *
233  * Returns a stringified version of a Field's flags
234  */
235 static char *
236 field_flags (guint32 f)
237 {
238         static char buffer [1024];
239         int access = f & FIELD_ATTRIBUTE_FIELD_ACCESS_MASK;
240         
241         buffer [0] = 0;
242
243         strcat (buffer, map (access, field_access_map));
244         strcat (buffer, flags (f, field_flags_map));
245         return g_strdup (buffer);
246 }
247
248 /**
249  * get_encoded_value:
250  * @ptr: pointer to decode from
251  * @len: result value is stored here.
252  *
253  * This routine decompresses 32-bit values as specified in the "Blob and
254  * Signature" section (22.2)
255  *
256  * Returns: updated pointer location
257  */
258 static const char *
259 get_encoded_value (const char *_ptr, guint32 *len)
260 {
261         const unsigned char *ptr = (unsigned char *) _ptr;
262         unsigned char b = *ptr;
263         
264         if ((b & 0x80) == 0){
265                 *len = b;
266                 return ptr+1;
267         } else if ((b & 0x40) == 0){
268                 *len = ((b & 0x3f) << 8 | ptr [1]);
269                 return ptr + 2;
270         }
271         *len = ((b & 0x1f) << 24) |
272                 (ptr [1] << 16) |
273                 (ptr [2] << 8) |
274                 ptr [3];
275         
276         return ptr + 4;
277 }
278
279 static char *
280 get_typedef (metadata_t *m, int idx)
281 {
282         guint32 cols [6];
283
284         expand (&m->tables [META_TABLE_TYPEDEF], idx - 1, cols, CSIZE (cols));
285
286         return g_strdup_printf (
287                 "%s.%s",
288                 mono_metadata_string_heap (m, cols [2]),
289                 mono_metadata_string_heap (m, cols [1]));
290 }
291
292 static char *
293 get_module (metadata_t *m, int idx)
294 {
295         guint32 cols [5];
296         
297         /*
298          * There MUST BE only one module in the Module table
299          */
300         g_assert (idx == 1);
301             
302         expand (&m->tables [META_TABLE_MODULEREF], idx - 1, cols, CSIZE (cols));
303
304         return g_strdup (mono_metadata_string_heap (m, cols [6]));
305 }
306
307 static char *
308 get_assemblyref (metadata_t *m, int idx)
309 {
310         guint32 cols [9];
311         
312         expand (&m->tables [META_TABLE_ASSEMBLYREF], idx - 1, cols, CSIZE (cols));
313
314         return g_strdup (mono_metadata_string_heap (m, cols [6]));
315 }
316
317 static char *
318 get_typeref (metadata_t *m, int idx)
319 {
320         guint32 cols [3];
321         const char *s, *t;
322         char *x, *ret;
323         guint32 rs_idx, table;
324         
325         expand (&m->tables [META_TABLE_TYPEREF], idx - 1, cols, CSIZE (cols));
326
327         t = mono_metadata_string_heap (m, cols [1]);
328         s = mono_metadata_string_heap (m, cols [2]);
329
330         rs_idx = cols [0] >> 2;
331         /*
332          * Two bits in Beta2.
333          * ECMA spec claims 3 bits
334          */
335         table = cols [0] & 3;
336         
337         switch (table){
338         case 0: /* Module */
339                 x = get_module (m, rs_idx);
340                 ret = g_strdup_printf ("TODO:TypeRef-Module [%s] %s.%s", x, s, t);
341                 g_free (x);
342                 break;
343
344         case 1: /* ModuleRef */
345                 ret = g_strdup_printf ("TODO:TypeRef-ModuleRef (%s.%s)", s, t);
346                 break;
347                               
348         case 2: /*
349                  * AssemblyRef (ECMA docs claim it is 3, but it looks to
350                  * me like it is 2 (tokens are prefixed with 0x23)
351                  */
352                 x = get_assemblyref (m, rs_idx);
353                 ret = g_strdup_printf ("[%s] %s.%s", x, s, t);
354                 g_free (x);
355                 break;
356                 
357         case 4: /* TypeRef */
358                 ret =  g_strdup_printf ("TODO:TypeRef-TypeRef: TYPEREF! (%s.%s)", s, t);
359                 break;
360                 
361         default:
362                 ret = g_strdup_printf ("Unknown table in TypeRef %d", table);
363         }
364
365         return ret;
366 }
367
368 static char *
369 get_typedef_or_ref (metadata_t *m, guint32 dor_token)
370 {
371         char *temp = NULL, *s;
372         int table, idx;
373
374         /*
375          * low 2 bits contain encoding
376          */
377         table = dor_token & 0x03;
378         idx = dor_token >> 2;
379         
380         switch (table){
381         case 0: /* TypeDef */
382                 temp = get_typedef (m, idx);
383                 s = g_strdup_printf ("%s", temp);
384                 break;
385                 
386         case 1: /* TypeRef */
387                 temp = get_typeref (m, idx);
388                 s = g_strdup_printf ("%s", temp);
389                 break;
390                 
391         case 2: /* TypeSpec */
392                 s = g_strdup_printf ("TODO-TypeSpec: 0x%08x", idx);
393                 break;
394
395         default:
396                 g_error ("Unhandled encoding for typedef-or-ref coded index");
397
398         }
399         
400         if (temp)
401                 g_free (temp);
402
403         return s;
404 }
405
406 /** 
407  * get_encoded_typedef_or_ref:
408  * @m: metadata context 
409  * @ptr: location to decode from.
410  * @result: pointer to string where resulting decoded string is stored
411  *
412  * result will point to a g_malloc()ed string.
413  *
414  * Returns: the new ptr to continue decoding
415  */
416 static const char *
417 get_encoded_typedef_or_ref (metadata_t *m, const char *ptr, char **result)
418 {
419         guint32 token;
420         
421         ptr = get_encoded_value (ptr, &token);
422
423         *result = get_typedef_or_ref (m, token);
424
425         return ptr;
426 }
427
428 /**
429  * methoddefref_signature:
430  * @m: metadata context 
431  * @ptr: location to decode from.
432  * @result: pointer to string where resulting decoded string is stored
433  *
434  * This routine decodes into a string a MethodDef or a MethodRef.
435  *
436  * result will point to a g_malloc()ed string.
437  *
438  * Returns: the new ptr to continue decoding
439  */
440 static const char *
441 methoddefref_signature (metadata_t *m, const char *ptr, char **result)
442 {
443         *result = g_strdup ("method-def-or-ref");
444         
445         return ptr;
446 }
447
448 /**
449  * get_custom_mod:
450  *
451  * Decodes a CustomMod (22.2.7)
452  *
453  * Returns: updated pointer location
454  */
455 static const char *
456 get_custom_mod (metadata_t *m, const char *ptr, char **return_value)
457 {
458         char *s;
459         
460         if ((*ptr == ELEMENT_TYPE_CMOD_OPT) ||
461             (*ptr == ELEMENT_TYPE_CMOD_REQD)){
462                 ptr++;
463                 ptr = get_encoded_typedef_or_ref (m, ptr, &s);
464
465                 *return_value = g_strconcat ("CMOD ", s, NULL);
466                 g_free (s);
467         } else
468                 *return_value = NULL;
469         return ptr;
470 }
471
472
473 /**
474  * get_type:
475  * @m: metadata context 
476  * @ptr: location to decode from.
477  * @result: pointer to string where resulting decoded string is stored
478  *
479  * This routine returs in @result the stringified type pointed by @ptr.
480  * (22.2.12)
481  *
482  * Returns: the new ptr to continue decoding
483  */
484 static const char *
485 get_type (metadata_t *m, const char *ptr, char **result)
486 {
487         char c;
488         
489         c = *ptr++;
490         
491         switch (c){
492         case ELEMENT_TYPE_BOOLEAN:
493         case ELEMENT_TYPE_CHAR:
494         case ELEMENT_TYPE_I1:
495         case ELEMENT_TYPE_U1:
496         case ELEMENT_TYPE_I2:
497         case ELEMENT_TYPE_U2:
498         case ELEMENT_TYPE_I4:
499         case ELEMENT_TYPE_U4:
500         case ELEMENT_TYPE_I8:
501         case ELEMENT_TYPE_U8:
502         case ELEMENT_TYPE_R4:
503         case ELEMENT_TYPE_R8:
504         case ELEMENT_TYPE_I:
505         case ELEMENT_TYPE_STRING:
506         case ELEMENT_TYPE_OBJECT:
507                 *result = g_strdup (map (c, element_type_map));
508                 break;
509                 
510         case ELEMENT_TYPE_VALUETYPE:
511         case ELEMENT_TYPE_CLASS:
512                 ptr = get_encoded_typedef_or_ref (m, ptr, result);
513                 break;
514                 
515         case ELEMENT_TYPE_FNPTR:
516                 ptr = methoddefref_signature (m, ptr, result);
517                 break;
518                 
519         case ELEMENT_TYPE_SZARRAY: {
520                 char *child_type;
521                 
522                 ptr = get_type (m, ptr, &child_type);
523                 *result = g_strdup_printf ("%s[]", child_type);
524                 g_free (child_type);
525                 break;
526         }
527                 
528         case ELEMENT_TYPE_ARRAY:
529  
530                 *result = g_strdup ("ARRAY:TODO");
531         }
532         
533         return ptr;
534 }
535
536 /**
537  * 
538  * Returns a stringified representation of a FieldSig (22.2.4)
539  */
540 static char *
541 field_signature (metadata_t *m, guint32 blob_signature)
542 {
543         char *allocated_modifier_string, *allocated_type_string;
544         const char *ptr = mono_metadata_blob_heap (m, blob_signature);
545         const char *base;
546         char *res;
547         int len;
548         
549         ptr = get_encoded_value (ptr, &len);
550         base = ptr;
551         /* FIELD is 0x06 */
552         g_assert (*ptr == 0x06);
553 /*      hex_dump (ptr, 0, len); */
554         ptr++; len--;
555         
556         ptr = get_custom_mod (m, ptr, &allocated_modifier_string);
557         ptr = get_type (m, ptr, &allocated_type_string);
558
559         res = g_strdup_printf (
560                 "%s %s",
561                 allocated_modifier_string ? allocated_modifier_string : "",
562                 allocated_type_string);
563         
564         if (allocated_modifier_string)
565                 g_free (allocated_modifier_string);
566         if (allocated_type_string)
567                 g_free (allocated_modifier_string);
568         
569         return res;
570 }
571
572 /**
573  * decode_literal:
574  * @m: metadata context
575  * @token: token to decode
576  *
577  * decodes the literal indexed by @token.
578  */
579 static char *
580 decode_literal (metadata_t *m, guint32 token)
581 {
582         return g_strdup ("LITERAL_VALUE");
583 }
584
585 /**
586  * dis_field_list:
587  * @m: metadata context
588  * @start: starting index into the Field Table.
589  * @end: ending index into Field table.
590  *
591  * This routine displays all the decoded fields from @start to @end
592  */
593 static void
594 dis_field_list (metadata_t *m, guint32 start, guint32 end)
595 {
596         metadata_tableinfo_t *t = &m->tables [META_TABLE_FIELD];
597         guint32 cols [3];
598         int i;
599
600         if (end > t->rows + 1){
601                 fprintf (output, "ERROR index out of range in fields");
602                 exit (1);
603         }
604                         
605         for (i = start; i < end; i++){
606                 char *sig, *flags;
607                 
608                 expand (t, i, cols, CSIZE (cols));
609                 sig = field_signature (m, cols [2]);
610                 flags = field_flags (cols [0]);
611                 
612                 if (cols [0] & FIELD_ATTRIBUTE_LITERAL){
613                         char *lit = decode_literal (m, cols [2]);
614                         
615                         fprintf (output, "    .field %s %s %s = ",
616                                  flags, sig,
617                                  mono_metadata_string_heap (m, cols [1]));
618                         fprintf (output, "%s\n", lit);
619                         g_free (lit);
620                 } else 
621                         fprintf (output, "    .field %s %s %s\n",
622                                  flags, sig,
623                                  mono_metadata_string_heap (m, cols [1]));
624                 g_free (flags);
625                 g_free (sig);
626         }
627 }
628
629 static map_t method_access_map [] = {
630         { METHOD_ATTRIBUTE_COMPILER_CONTROLLED, "compilercontrolled " },
631         { METHOD_ATTRIBUTE_PRIVATE,             "private" },
632         { METHOD_ATTRIBUTE_FAM_AND_ASSEM,       "famandassem" },
633         { METHOD_ATTRIBUTE_ASSEM,               "assembly " },
634         { METHOD_ATTRIBUTE_FAMILY,              "family " },
635         { METHOD_ATTRIBUTE_FAM_OR_ASSEM,        "famorassem " },
636         { METHOD_ATTRIBUTE_PUBLIC,              "public " },
637         { 0, NULL }
638 };
639
640 static map_t method_flags_map [] = {
641         { METHOD_ATTRIBUTE_STATIC,              "static " },
642         { METHOD_ATTRIBUTE_FINAL,               "final " },
643         { METHOD_ATTRIBUTE_VIRTUAL,             "virtual " },
644         { METHOD_ATTRIBUTE_HIDE_BY_SIG,         "hidebysig " },
645         { METHOD_ATTRIBUTE_VTABLE_LAYOUT_MASK,  "newslot " },
646         { METHOD_ATTRIBUTE_ABSTRACT,            "abstract " },
647         { METHOD_ATTRIBUTE_SPECIAL_NAME,        "specialname " },
648         { METHOD_ATTRIBUTE_RT_SPECIAL_NAME,     "rtspecialname " },
649         { METHOD_ATTRIBUTE_PINVOKE_IMPL,        "pinvokeimpl " }, 
650         { METHOD_ATTRIBUTE_UNMANAGED_EXPORT,    "export " },
651         { METHOD_ATTRIBUTE_HAS_SECURITY,        "hassecurity" },
652         { METHOD_ATTRIBUTE_REQUIRE_SEC_OBJECT,  "requiresecobj" },
653         { 0, NULL }
654 };
655
656 /**
657  * method_flags:
658  *
659  * Returns a stringified version of the Method's flags
660  */
661 static char *
662 method_flags (guint32 f)
663 {
664         GString *str = g_string_new ("");
665         int access = f & METHOD_ATTRIBUTE_MEMBER_ACCESS_MASK;
666         char *s;
667         
668         g_string_append (str, map (access, method_access_map));
669         g_string_append (str, flags (f, method_flags_map));
670
671         s = str->str;
672         g_string_free (str, FALSE);
673
674         return s;
675 }
676
677 static map_t method_impl_map [] = {
678         { METHOD_IMPL_ATTRIBUTE_IL,              "cil " },
679         { METHOD_IMPL_ATTRIBUTE_NATIVE,          "native " },
680         { METHOD_IMPL_ATTRIBUTE_OPTIL,           "optil " },
681         { METHOD_IMPL_ATTRIBUTE_RUNTIME,         "runtime " },
682         { 0, NULL }
683 };
684
685 static map_t managed_type_map [] = {
686         { METHOD_IMPL_ATTRIBUTE_UNMANAGED,       "unmanaged " },
687         { METHOD_IMPL_ATTRIBUTE_MANAGED,         "managed " },
688         { 0, NULL }
689 };
690
691 static map_t managed_impl_flags [] = {
692         { METHOD_IMPL_ATTRIBUTE_FORWARD_REF,     "fwdref " },
693         { METHOD_IMPL_ATTRIBUTE_PRESERVE_SIG,    "preservesig " },
694         { METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL,   "internalcall " },
695         { METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED,    "synchronized " },
696         { METHOD_IMPL_ATTRIBUTE_NOINLINING,      "noinline " },
697         { 0, NULL }
698 };
699
700 static char *
701 method_impl_flags (guint32 f)
702 {
703         GString *str = g_string_new ("");
704         char *s;
705         int code_type = f & METHOD_IMPL_ATTRIBUTE_CODE_TYPE_MASK;
706         int managed_type = f & METHOD_IMPL_ATTRIBUTE_MANAGED_MASK;
707
708         g_string_append (str, map (code_type, method_impl_map));
709         g_string_append (str, map (managed_type, managed_type_map));
710         g_string_append (str, flags (f, managed_impl_flags));
711         
712         s = str->str;
713         g_string_free (str, FALSE);
714         return s;
715 }
716
717 /**
718  * get_ret_type:
719  * @m: metadata context 
720  * @ptr: location to decode from.
721  * @result: pointer to string where resulting decoded string is stored
722  *
723  * This routine returns in @result the stringified RetType (22.2.11)
724  *
725  * Returns: the new ptr to continue decoding.
726  */
727 static const char *
728 get_ret_type (metadata_t *m, const char *ptr, char **ret_type)
729 {
730         GString *str = g_string_new ("");
731         char *mod = NULL;
732         char *allocated_type_string;
733         
734         ptr = get_custom_mod (m, ptr, &mod);
735         if (mod){
736                 g_string_append (str, mod);
737                 g_string_append_c (str, ' ');
738                 g_free (mod);
739         }
740
741         if (*ptr == ELEMENT_TYPE_TYPEDBYREF){
742                 /* TODO: what does `typedbyref' mean? */
743                 g_string_append (str, "/* FIXME: What does this mean? */ typedbyref ");
744                 ptr++;
745         } else if (*ptr == ELEMENT_TYPE_VOID){
746                  g_string_append (str, "void");
747                  ptr++;
748         } else {
749                 if (*ptr == ELEMENT_TYPE_BYREF){
750                         g_string_append (str, "[out] ");
751                         ptr++;
752                 }
753
754                 ptr = get_type (m, ptr, &allocated_type_string);
755                 g_string_append (str, allocated_type_string);
756                 g_free (allocated_type_string);
757         }
758
759         *ret_type = str->str;
760         g_string_free (str, FALSE);
761
762         return ptr;
763 }
764
765 static const char *
766 get_param (metadata_t *m, const char *ptr, char **retval)
767 {
768         GString *str = g_string_new ("");
769         char *allocated_mod_string, *allocated_type_string;
770         
771         ptr = get_custom_mod (m, ptr, &allocated_mod_string);
772         if (allocated_mod_string){
773                 g_string_append (str, allocated_mod_string);
774                 g_string_append_c (str, ' ');
775                 g_free (allocated_mod_string);
776         }
777         
778         if (*ptr == ELEMENT_TYPE_TYPEDBYREF){
779                 g_string_append (str, "/*FIXME: what does typedbyref mean? */ typedbyref ");
780                 ptr++;
781         } else {
782                 if (*ptr == ELEMENT_TYPE_BYREF){
783                         g_string_append (str, "[out] ");
784                         ptr++;
785                 }
786                 ptr = get_type (m, ptr, &allocated_type_string);
787                 g_string_append (str, allocated_type_string);
788                 g_free (allocated_type_string);
789         }
790
791         *retval = str->str;
792         g_string_free (str, FALSE);
793         return ptr;
794 }
795
796 typedef struct {
797         char  flags;
798         char *ret_type;
799         int   param_count;
800         char **param;
801 } MethodSignature;
802
803 static MethodSignature *
804 parse_method_signature (metadata_t *m, guint32 blob_signature)
805 {
806         GString *res = g_string_new ("");
807         const char *ptr = mono_metadata_blob_heap (m, blob_signature);
808         MethodSignature *ms = g_new0 (MethodSignature, 1);
809         char *s;
810         int i, len;
811
812         ptr = get_encoded_value (ptr, &len);
813         fprintf (output, "     // SIG: ");
814         hex_dump (ptr, 0, -len);
815         fprintf (output, "\n");
816         
817         ms->flags = *ptr++;
818
819         ptr = get_encoded_value (ptr, &ms->param_count);
820         ptr = get_ret_type (m, ptr, &ms->ret_type);
821         ms->param = g_new (char *, ms->param_count);
822         
823         for (i = 0; i < ms->param_count; i++)
824                 ptr = get_param (m, ptr, &(ms->param [i]));
825
826         s = res->str;
827         g_string_free (res, FALSE);
828         return ms;
829 }
830
831 static void
832 free_method_signature (MethodSignature *ms)
833 {
834         int i;
835         
836         for (i = 0; i < ms->param_count; i++)
837                 g_free (ms->param [i]);
838         g_free (ms->param);
839         g_free (ms->ret_type);
840         g_free (ms);
841 }
842
843 static map_t param_map [] = {
844         { PARAM_ATTRIBUTE_IN,                "[in] " },
845         { PARAM_ATTRIBUTE_OUT,               "[out] " },
846         { PARAM_ATTRIBUTE_OPTIONAL,          "optional " },
847         { PARAM_ATTRIBUTE_HAS_DEFAULT,       "hasdefault " },
848         { PARAM_ATTRIBUTE_HAS_FIELD_MARSHAL, "fieldmarshal " },
849         { 0, NULL }
850 };
851
852 static char *
853 param_flags (guint32 f)
854 {
855         return g_strdup (map (f, param_map));
856 }
857
858 /**
859  * dis_field_list:
860  * @m: metadata context
861  * @start: starting index into the Method Table.
862  * @end: ending index into Method table.
863  *
864  * This routine displays the methods in the Method Table from @start to @end
865  */
866 static void
867 dis_method_list (metadata_t *m, guint32 start, guint32 end)
868 {
869         metadata_tableinfo_t *t = &m->tables [META_TABLE_METHOD];
870         metadata_tableinfo_t *p = &m->tables [META_TABLE_PARAM];
871         guint32 cols [6];
872         guint32 cols_next [6];
873         guint32 param_cols [3];
874         int i;
875
876         if (end > t->rows + 1){
877                 fprintf (output, "ERROR index out of range in methods");
878                 exit (1);
879         }
880
881         for (i = start; i < end; i++){
882                 MethodSignature *ms;
883                 char *flags, *impl_flags;
884                 
885                 expand (t, i, cols, CSIZE (cols));
886                 expand (t, i + 1, cols_next, CSIZE (cols_next));
887
888                 flags = method_flags (cols [2]);
889                 impl_flags = method_impl_flags (cols [1]);
890
891                 ms = parse_method_signature (m, cols [4]);
892                         
893                 fprintf (output,
894                          "    .method %s\n",
895                          flags);
896                 fprintf (output,
897                          "           %s %s",
898                          ms->ret_type,
899                          mono_metadata_string_heap (m, cols [3]));
900                 if (ms->param_count > 0){
901                         int i;
902
903                         fprintf (output, "(\n");
904                         for (i = 0; i < ms->param_count; i++){
905                                 char *pf;
906                                 
907                                 expand (p, i, param_cols, CSIZE (param_cols));
908                                 pf = param_flags (param_cols [0]);
909                                 fprintf (
910                                         output, "\t\t%s %s %s%s", pf, ms->param [i],
911                                         mono_metadata_string_heap (m, param_cols [2]),
912                                         (i+1 == ms->param_count) ? ")" : ",\n");
913
914                                 g_free (pf);
915                         }
916                                 
917                 }
918                 fprintf (output, " %s\n", impl_flags);
919                 g_free (flags);
920                 g_free (impl_flags);
921                 
922                 fprintf (output, "    {\n");
923                 fprintf (output, "        // Method begins at RVA 0x%x\n", cols [0]);
924                 fprintf (output, "        // Param: %d %d (%d)\n", cols [5], cols_next [5], ms->param_count);
925                 fprintf (output, "    }\n\n");
926                 free_method_signature (ms);
927         }
928 }
929
930 /**
931  * dis_type:
932  * @m: metadata context
933  * @n: index of type to disassemble
934  *
935  * Disassembles the type whose index in the TypeDef table is @n.
936  */
937 static void
938 dis_type (metadata_t *m, int n)
939 {
940         metadata_tableinfo_t *t = &m->tables [META_TABLE_TYPEDEF];
941         guint32 cols [6];
942         guint32 cols_next [6];
943         const char *name;
944         
945         expand (t, n, cols, CSIZE (cols));
946         expand (t, n + 1, cols_next, CSIZE (cols_next));
947
948         fprintf (output, ".namespace %s\n{\n", mono_metadata_string_heap (m, cols [2]));
949         name = mono_metadata_string_heap (m, cols [1]);
950
951         if ((cols [0] & TYPE_ATTRIBUTE_CLASS_SEMANTIC_MASK) == TYPE_ATTRIBUTE_CLASS){
952                 char *base = get_typedef_or_ref (m, cols [3]);
953                 fprintf (output, "  .class %s%s\n", typedef_flags (cols [0]), name);
954                 fprintf (output, "  \textends %s\n", base);
955                 g_free (base);
956         } else
957                 fprintf (output, "  .class interface %s%s\n", typedef_flags (cols [0]), name);
958         
959         fprintf (output, "  {\n");
960
961         /*
962          * The value in the table is always valid, we know we have fields
963          * if the value stored is different than the next record.
964          */
965         if (cols [4] != cols_next [4])
966                 dis_field_list (m, cols [4] - 1, cols_next [4] - 1);
967         fprintf (output, "\n");
968         if (cols [4] != cols_next [5])
969                 dis_method_list (m, cols [5] - 1, cols_next [5] - 1);
970
971         fprintf (output, "  }\n}\n\n");
972 }
973
974 static void
975 dump_table_typeref (metadata_t *m)
976 {
977         metadata_tableinfo_t *t = &m->tables [META_TABLE_TYPEREF];
978         int i;
979
980         fprintf (output, "Typeref Table\n");
981         
982         for (i = 1; i <= t->rows; i++){
983                 char *s = get_typeref (m, i);
984                 
985                 fprintf (output, "%d: %s\n", i, s);
986                 g_free (s);
987         }
988         fprintf (output, "\n");
989 }
990
991 static void
992 dump_table_typedef (metadata_t *m)
993 {
994         metadata_tableinfo_t *t = &m->tables [META_TABLE_TYPEDEF];
995         int i;
996
997         fprintf (output, "Typedef Table\n");
998         
999         for (i = 1; i <= t->rows; i++){
1000                 char *s = get_typedef (m, i);
1001                 
1002                 fprintf (output, "%d: %s\n", i, s);
1003                 g_free (s);
1004         }
1005         fprintf (output, "\n");
1006 }
1007
1008 static void
1009 dump_table_assemblyref (metadata_t *m)
1010 {
1011         metadata_tableinfo_t *t = &m->tables [META_TABLE_ASSEMBLYREF];
1012         int i;
1013
1014         fprintf (output, "AssemblyRef Table\n");
1015         
1016         for (i = 0; i < t->rows; i++){
1017                 guint32 cols [9];
1018
1019                 expand (t, i, cols, CSIZE (cols));
1020                 fprintf (output, "%d: %d.%d.%d.%d %s\n", i,
1021                          cols [0], cols [1], cols [2], cols [3],
1022                          mono_metadata_string_heap (m, cols [6]));
1023         }
1024         fprintf (output, "\n");
1025 }
1026
1027 static void
1028 dump_table_param (metadata_t *m)
1029 {
1030         metadata_tableinfo_t *t = &m->tables [META_TABLE_PARAM];
1031         int i;
1032
1033         fprintf (output, "Param Table\n");
1034         
1035         for (i = 0; i < t->rows; i++){
1036                 guint32 cols [3];
1037
1038                 expand (t, i, cols, CSIZE (cols));
1039                 fprintf (output, "%d: 0x%04x %d %s\n",
1040                          i,
1041                          cols [0], cols [1], 
1042                          mono_metadata_string_heap (m, cols [2]));
1043         }
1044         fprintf (output, "\n");
1045 }
1046
1047 /**
1048  * dis_types:
1049  * @m: metadata context
1050  *
1051  * disassembles all types in the @m context
1052  */
1053 static void
1054 dis_types (metadata_t *m)
1055 {
1056         metadata_tableinfo_t *t = &m->tables [META_TABLE_TYPEDEF];
1057         int i;
1058
1059         for (i = 0; i < t->rows; i++)
1060                 dis_type (m, i);
1061 }
1062
1063 /**
1064  * disassemble_file:
1065  * @file: file containing CIL code.
1066  *
1067  * Disassembles the @file file.
1068  */
1069 static void
1070 disassemble_file (const char *file)
1071 {
1072         enum MonoAssemblyOpenStatus status;
1073         MonoAssembly *ass;
1074         cli_image_info_t *ii;
1075         metadata_t *m;
1076
1077
1078         ass = mono_assembly_open (file, &status);
1079         if (ass == NULL){
1080                 fprintf (stderr, "Error while trying to process %s\n", file);
1081                 
1082         }
1083
1084         ii = ass->image_info;
1085         m = &ii->cli_metadata;
1086         
1087         if (dump_table != -1){
1088                 switch (dump_table){
1089                 case META_TABLE_TYPEDEF:
1090                         dump_table_typedef (m);
1091                         break;
1092                 case META_TABLE_TYPEREF:
1093                         dump_table_typeref (m);
1094                         break;
1095                 case META_TABLE_ASSEMBLYREF:
1096                         dump_table_assemblyref (m);
1097                         break;
1098                 case META_TABLE_PARAM:
1099                         dump_table_param (m);
1100                         break;
1101                 default:
1102                         g_error ("Internal error");
1103                 }
1104         } else {
1105                 dump_header_data (ass);
1106                 
1107                 dis_directive_assemblyref (m);
1108                 dis_directive_assembly (m);
1109                 dis_types (m);
1110         }
1111         
1112         mono_assembly_close (ass);
1113 }
1114
1115 static void
1116 usage (void)
1117 {
1118         fprintf (stderr, "Usage is: monodis [--typeref][--typedef][--assemblyref] file ..\n");
1119         exit (1);
1120 }
1121
1122 int
1123 main (int argc, char *argv [])
1124 {
1125         GList *input_files = NULL, *l;
1126         int i;
1127
1128         output = stdout;
1129         for (i = 1; i < argc; i++){
1130                 if (argv [i][0] == '-'){
1131                         if (argv [i][1] == 'h')
1132                                 usage ();
1133                         else if (argv [i][1] == 'd')
1134                                 dump_header_data_p = TRUE;
1135                         else if (strcmp (argv [i], "--help") == 0)
1136                                 usage ();
1137                         else if (strcmp (argv [i], "--typeref") == 0)
1138                                 dump_table = META_TABLE_TYPEREF;
1139                         else if (strcmp (argv [i], "--typedef") == 0)
1140                                 dump_table = META_TABLE_TYPEDEF;
1141                         else if (strcmp (argv [i], "--assemblyref") == 0)
1142                                 dump_table = META_TABLE_ASSEMBLYREF;
1143                         else if (strcmp (argv [i], "--param") == 0)
1144                                 dump_table = META_TABLE_PARAM;
1145                 } else
1146                         input_files = g_list_append (input_files, argv [i]);
1147         }
1148
1149         if (input_files == NULL)
1150                 usage ();
1151         
1152         for (l = input_files; l; l = l->next)
1153                 disassemble_file (l->data);
1154
1155         return 0;
1156 }