Weekend work
[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 #include <config.h>
10 #include <stdio.h>
11 #include <string.h>
12 #include <glib.h>
13 #include <mono/metadata/assembly.h>
14 #include <mono/metadata/cil-coff.h>
15 #include <mono/metadata/endian.h>
16 #include <mono/metadata/typeattr.h>
17 #include <mono/metadata/fieldattr.h>
18 #include <mono/metadata/eltype.h>
19 #include "util.h"
20
21 FILE *output;
22
23 /* True if you want to get a dump of the header data */
24 gboolean dump_header_data_p = FALSE;
25
26 static void
27 dump_header_data (MonoAssembly *ass)
28 {
29         if (!dump_header_data_p)
30                 return;
31
32         fprintf (output,
33                  "// Ximian's CIL disassembler, version 1.0\n"
34                  "// Copyright (C) 2001 Ximian, Inc.\n\n");
35 }
36
37 #define CSIZE(x) (sizeof (x) / 4)
38 static void
39 expand (metadata_tableinfo_t *t, int idx, guint32 *res, int res_size)
40 {
41         guint32 bitfield = t->size_bitfield;
42         int i, count = meta_table_count (bitfield);
43         char *data = t->base + idx * t->row_size;
44         
45         g_assert (res_size == count);
46         
47         for (i = 0; i < count; i++){
48                 int n = meta_table_size (bitfield, i);
49
50                 switch (n){
51                 case 1:
52                         res [i] = *data; break;
53                 case 2:
54                         res [i] = read16 (data); break;
55                         
56                 case 4:
57                         res [i] = read32 (data); break;
58                         
59                 default:
60                         g_assert_not_reached ();
61                 }
62                 data += n;
63         }
64 }
65
66 static void
67 dis_directive_assembly (metadata_t *m)
68 {
69         metadata_tableinfo_t *t  = &m->tables [META_TABLE_ASSEMBLY];
70         guint32 cols [9];
71         
72         if (t->base == NULL)
73                 return;
74
75         expand (t, 0, cols, CSIZE (cols));
76         
77         fprintf (output,
78                  ".assembly %s\n"
79                  "{\n"
80                  "  .hash algorithm 0x%08x\n"
81                  "  .ver  %d.%d.%d.%d"
82                  "%s %s"
83                  "%s"
84                  "\n"
85                  "}\n",
86                  mono_metadata_string_heap (m, cols [7]),
87                  cols [0],
88                  cols [1], cols [2], cols [3], cols [4],
89                  cols [8] ? "\n  .locale" : "",
90                  cols [8] ? mono_metadata_string_heap (m, cols [8]) : "",
91                  cols [6] ? "\n  .publickey" : ""
92                 );
93 }
94
95 static void
96 dis_directive_assemblyref (metadata_t *m)
97 {
98         metadata_tableinfo_t *t = &m->tables [META_TABLE_ASSEMBLYREF];
99         guint32 cols [9];
100         int i;
101         
102         if (t->base == NULL)
103                 return;
104
105         for (i = 0; i < t->rows; i++){
106                 expand (t, i, cols, CSIZE (cols));
107
108                 fprintf (output,
109                          ".assembly extern %s\n"
110                          "{\n"
111                          "  .ver %d.%d.%d.%d\n"
112                          "}\n",
113                          mono_metadata_string_heap (m, cols [6]),
114                          cols [0], cols [1], cols [2], cols [3]
115                         );
116         }
117 }
118
119 static map_t visibility_map [] = {
120         { TYPE_ATTRIBUTE_NOT_PUBLIC,           "not-public " },
121         { TYPE_ATTRIBUTE_PUBLIC,               "public " },
122         { TYPE_ATTRIBUTE_NESTED_PUBLIC,        "nested-public " },
123         { TYPE_ATTRIBUTE_NESTED_PRIVATE,       "nested-private " },
124         { TYPE_ATTRIBUTE_NESTED_FAMILY,        "family " },
125         { TYPE_ATTRIBUTE_NESTED_ASSEMBLY,      "nested-assembly" },
126         { TYPE_ATTRIBUTE_NESTED_FAM_AND_ASSEM, "nested-fam-and-assembly" },
127         { TYPE_ATTRIBUTE_NESTED_FAM_OR_ASSEM,  "nested-fam-or-assembly" },
128         { 0, NULL }
129 };
130
131 static map_t layout_map [] = {
132         { TYPE_ATTRIBUTE_AUTO_LAYOUT,          "auto " },
133         { TYPE_ATTRIBUTE_SEQUENTIAL_LAYOUT,    "sequential " },
134         { TYPE_ATTRIBUTE_EXPLICIT_LAYOUT,      "explicit " },
135         { 0, NULL }
136 };
137
138 static map_t format_map [] = {
139         { TYPE_ATTRIBUTE_ANSI_CLASS,           "ansi " },
140         { TYPE_ATTRIBUTE_UNICODE_CLASS,        "unicode " },
141         { TYPE_ATTRIBUTE_AUTO_CLASS,           "auto " },
142         { 0, NULL }
143 };
144
145 static char *
146 typedef_flags (guint32 flags)
147 {
148         static char buffer [1024];
149         int visibility = flags & TYPE_ATTRIBUTE_VISIBILITY_MASK;
150         int layout = flags & TYPE_ATTRIBUTE_LAYOUT_MASK;
151         int format = flags & TYPE_ATTRIBUTE_STRING_FORMAT_MASK;
152         
153         buffer [0] = 0;
154
155         strcat (buffer, map (visibility, visibility_map));
156         strcat (buffer, map (layout, layout_map));
157         strcat (buffer, map (format, format_map));
158         
159         if (flags & TYPE_ATTRIBUTE_ABSTRACT)
160                 strcat (buffer, "abstract ");
161         if (flags & TYPE_ATTRIBUTE_SEALED)
162                 strcat (buffer, "sealed ");
163         if (flags & TYPE_ATTRIBUTE_SPECIAL_NAME)
164                 strcat (buffer, "special-name ");
165         if (flags & TYPE_ATTRIBUTE_IMPORT)
166                 strcat (buffer, "import ");
167         if (flags & TYPE_ATTRIBUTE_SERIALIZABLE)
168                 strcat (buffer, "serializable ");
169         if (flags & TYPE_ATTRIBUTE_BEFORE_FIELD_INIT)
170                 strcat (buffer, "beforefieldinit ");
171
172         return buffer;
173 }
174
175 static map_t access_map [] = {
176         { FIELD_ATTRIBUTE_COMPILER_CONTROLLED, "compilercontrolled " },
177         { FIELD_ATTRIBUTE_PRIVATE,             "private " },
178         { FIELD_ATTRIBUTE_FAM_AND_ASSEM,       "famandassem " },
179         { FIELD_ATTRIBUTE_ASSEMBLY,            "assembly " },
180         { FIELD_ATTRIBUTE_FAMILY,              "family " },
181         { FIELD_ATTRIBUTE_FAM_OR_ASSEM,        "famorassem " },
182         { FIELD_ATTRIBUTE_PUBLIC,              "public " },
183         { 0, NULL }
184 };
185
186 static map_t field_flags_map [] = {
187         { FIELD_ATTRIBUTE_STATIC,              "static " },
188         { FIELD_ATTRIBUTE_INIT_ONLY,           "initonly " },
189         { FIELD_ATTRIBUTE_LITERAL,             "literal " },
190         { FIELD_ATTRIBUTE_NOT_SERIALIZED,      "notserialized " },
191         { FIELD_ATTRIBUTE_SPECIAL_NAME,        "specialname " },
192         { FIELD_ATTRIBUTE_PINVOKE_IMPL,        "FIXME:pinvokeimpl " },
193         { 0, NULL }
194 };
195
196 static map_t element_type_map [] = {
197         { ELEMENT_TYPE_END        , "end" },
198         { ELEMENT_TYPE_VOID       , "System.Void" },
199         { ELEMENT_TYPE_BOOLEAN    , "System.Bool" },
200         { ELEMENT_TYPE_CHAR       , "System.Char" }, 
201         { ELEMENT_TYPE_I1         , "System.SByte" },
202         { ELEMENT_TYPE_U1         , "System.Byte" }, 
203         { ELEMENT_TYPE_I2         , "System.Int16" },
204         { ELEMENT_TYPE_U2         , "System.UInt16" },
205         { ELEMENT_TYPE_I4         , "System.Int32" },
206         { ELEMENT_TYPE_U4         , "System.UInt32" },
207         { ELEMENT_TYPE_I8         , "System.Int64" },
208         { ELEMENT_TYPE_U8         , "System.UInt64" },
209         { ELEMENT_TYPE_R4         , "System.Single" },
210         { ELEMENT_TYPE_R8         , "System.Double" },
211         { ELEMENT_TYPE_STRING     , "System.String" },
212         { ELEMENT_TYPE_TYPEDBYREF , "TypedByRef" },
213         { ELEMENT_TYPE_I          , "System.Int32" },
214         { ELEMENT_TYPE_U          , "System.UPtr" },
215         { ELEMENT_TYPE_OBJECT     , "System.Object" },
216         { 0, NULL }
217 };
218
219 /**
220  * field_flags:
221  *
222  * Returns a stringified version of a Field's flags
223  */
224 static char *
225 field_flags (guint32 f)
226 {
227         static char buffer [1024];
228         int access = f & FIELD_ATTRIBUTE_FIELD_ACCESS_MASK;
229         
230         buffer [0] = 0;
231
232         strcat (buffer, map (access, access_map));
233         strcat (buffer, flags (f, field_flags_map));
234         return g_strdup (buffer);
235 }
236
237 /**
238  * get_encoded_value:
239  * @ptr: pointer to decode from
240  * @len: result value is stored here.
241  *
242  * This routine decompresses 32-bit values as specified in the "Blob and
243  * Signature" section (22.2)
244  *
245  * Returns: updated pointer location
246  */
247 static const char *
248 get_encoded_value (const char *_ptr, guint32 *len)
249 {
250         const unsigned char *ptr = (unsigned char *) _ptr;
251         unsigned char b = *ptr;
252         
253         if ((b & 0x80) == 0){
254                 *len = b;
255                 return ptr+1;
256         } else if ((b & 0x40) == 0){
257                 *len = ((b & 0x3f) << 8 | ptr [1]);
258                 return ptr + 2;
259         }
260         *len = ((b & 0x1f) << 24) |
261                 (ptr [1] << 16) |
262                 (ptr [2] << 8) |
263                 ptr [3];
264         
265         return ptr + 4;
266 }
267
268 /**
269  * get_custom_mod:
270  *
271  * Decodes a CustomMod (22.2.7)
272  *
273  * Returns: updated pointer location
274  */
275 static const char *
276 get_custom_mod (const char *ptr, char **return_value)
277 {
278         if ((*ptr == ELEMENT_TYPE_CMOD_OPT) ||
279             (*ptr == ELEMENT_TYPE_CMOD_REQD)){
280                 fprintf (stderr, "FIXME: still do not support CustomMods (22.2.7)");
281                 exit (1);
282         }
283         *return_value = NULL;
284         return ptr;
285 }
286
287 static char *
288 get_typedef (metadata_t *m, int idx)
289 {
290         guint32 cols [6];
291
292         expand (&m->tables [META_TABLE_TYPEDEF], idx - 1, cols, CSIZE (cols));
293
294         return g_strdup_printf (
295                 "%s.%s",
296                 mono_metadata_string_heap (m, cols [2]),
297                 mono_metadata_string_heap (m, cols [1]));
298 }
299
300 static char *
301 get_module (metadata_t *m, int idx)
302 {
303         guint32 cols [9];
304
305 /*      g_assert (idx <= m->tables [META_TABLE_MODULE].rows); */
306             
307 /*      return g_strdup_printf ("IDX=0x%x", idx); */
308         expand (&m->tables [META_TABLE_ASSEMBLYREF], 0, cols, CSIZE (cols));
309
310         return g_strdup (mono_metadata_string_heap (m, cols [6]));
311 }
312
313 static char *
314 get_typeref (metadata_t *m, int idx)
315 {
316         guint32 cols [3];
317         const char *s, *t;
318         char *x, *ret;
319         guint32 rs_idx, table;
320         
321         expand (&m->tables [META_TABLE_TYPEREF], idx - 1, cols, CSIZE (cols));
322
323         t = mono_metadata_string_heap (m, cols [1]);
324         s = mono_metadata_string_heap (m, cols [2]);
325         rs_idx = cols [0] >> 3;
326         table = cols [0] & 7;
327         printf ("------------ %d %d --------\n", rs_idx, table);
328                 
329         switch (table){
330         case 0: /* Module */
331                 x = get_module (m, rs_idx);
332                 ret = g_strdup_printf ("[%08x:%s] %s.%s", cols [0], x, s, t);
333                 g_free (x);
334                 break;
335
336         case 1: /* ModuleRef */
337                 ret = g_strdup_printf ("TypeRef: ModuleRef (%s.%s)", s, t);
338                 
339         case 3: /* AssemblyRef */
340                 ret = g_strdup_printf ("TypeRef: AssemblyRef (%s.%s)", s, t);
341                 
342         case 4: /* TypeRef */
343                 ret =  g_strdup_printf ("TypeRef: TYPEREF! (%s.%s)", s, t);
344
345         default:
346                 ret = g_strdup ("ERROR");
347         }
348
349         return ret;
350 }
351
352 static char *
353 typedef_or_ref (metadata_t *m, guint32 dor_token)
354 {
355         int table = dor_token & 0x03;
356         int idx = dor_token >> 2;
357         char *s, *temp = NULL;
358                 
359         switch (table){
360         case 0: /* TypeDef */
361                 temp = get_typedef (m, idx);
362                 s = g_strdup_printf ("%s", temp);
363                 break;
364                 
365         case 1: /* TypeRef */
366                 temp = get_typeref (m, idx);
367                 s = g_strdup_printf ("/* 0x%08x */ %s", dor_token, temp);
368                 break;
369                 
370         case 2: /* TypeSpec */
371                 s = g_strdup_printf ("TypeSpec: 0x%08x", dor_token);
372                 break;
373                 
374         }
375
376         if (temp)
377                 g_free (temp);
378
379         return s;
380 }
381
382 /** 
383  * get_encoded_typedef_or_ref:
384  * @m: metadata context 
385  * @ptr: location to decode from.
386  * @result: pointer to string where resulting decoded string is stored
387  *
388  * result will point to a g_malloc()ed string.
389  *
390  * Returns: the new ptr to continue decoding
391  */
392 static const char *
393 get_encoded_typedef_or_ref (metadata_t *m, const char *ptr, char **result)
394 {
395         guint32 token;
396         
397         ptr = get_encoded_value (ptr, &token);
398
399         *result = typedef_or_ref (m, token);
400
401         return ptr;
402 }
403
404 /**
405  * methoddefref_signature:
406  * @m: metadata context 
407  * @ptr: location to decode from.
408  * @result: pointer to string where resulting decoded string is stored
409  *
410  * This routine decodes into a string a MethodDef or a MethodRef.
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 methoddefref_signature (metadata_t *m, const char *ptr, char **result)
418 {
419         *result = g_strdup ("method-def-or-ref");
420         
421         return ptr;
422 }
423
424 /**
425  * get_type:
426  * @m: metadata context 
427  * @ptr: location to decode from.
428  * @result: pointer to string where resulting decoded string is stored
429  *
430  * This routine returs in @result the stringified type pointed by @ptr.
431  *
432  * Returns: the new ptr to continue decoding
433  */
434 static const char *
435 get_type (metadata_t *m, const char *ptr, char **result)
436 {
437         char c;
438         
439         c = *ptr++;
440         
441         switch (c){
442         case ELEMENT_TYPE_BOOLEAN:
443         case ELEMENT_TYPE_CHAR:
444         case ELEMENT_TYPE_I1:
445         case ELEMENT_TYPE_U1:
446         case ELEMENT_TYPE_I2:
447         case ELEMENT_TYPE_U2:
448         case ELEMENT_TYPE_I4:
449         case ELEMENT_TYPE_U4:
450         case ELEMENT_TYPE_I8:
451         case ELEMENT_TYPE_U8:
452         case ELEMENT_TYPE_R4:
453         case ELEMENT_TYPE_R8:
454         case ELEMENT_TYPE_I:
455         case ELEMENT_TYPE_STRING:
456         case ELEMENT_TYPE_OBJECT:
457                 *result = g_strdup (map (c, element_type_map));
458                 break;
459                 
460         case ELEMENT_TYPE_VALUETYPE:
461         case ELEMENT_TYPE_CLASS:
462                 ptr = get_encoded_typedef_or_ref (m, ptr, result);
463                 break;
464                 
465         case ELEMENT_TYPE_FNPTR:
466                 ptr = methoddefref_signature (m, ptr, result);
467                 break;
468                 
469         case ELEMENT_TYPE_ARRAY:
470                 *result = g_strdup ("ARRAY:TODO");
471                 break;
472                 
473         case ELEMENT_TYPE_SZARRAY:
474                 *result = g_strdup ("SZARRAY:TODO");
475         }
476         
477         return ptr;
478 }
479
480 /**
481  * 
482  * Returns a stringified representation of a FieldSig (22.2.4)
483  */
484 static char *
485 field_signature (metadata_t *m, guint32 blob_signature)
486 {
487         char *allocated_modifier_string, *allocated_type_string;
488         const char *ptr = mono_metadata_blob_heap (m, blob_signature);
489         const char *base;
490         int len;
491         static char buffer [8192];
492         
493         ptr = get_encoded_value (ptr, &len);
494         base = ptr;
495         /* FIELD is 0x06 */
496         g_assert (*ptr == 0x06);
497         hex_dump (ptr, 0, len);
498         ptr++; len--;
499         
500         ptr = get_custom_mod (ptr, &allocated_modifier_string);
501         ptr = get_type (m, ptr, &allocated_type_string);
502         
503         sprintf (buffer, "LEN=%d::::   ", len);
504         strcat (buffer, allocated_type_string);
505         
506         if (allocated_modifier_string)
507                 g_free (allocated_modifier_string);
508         if (allocated_type_string)
509                 g_free (allocated_modifier_string);
510         
511         return g_strdup (buffer);
512 }
513
514 /**
515  * decode_literal:
516  * @m: metadata context
517  * @token: token to decode
518  *
519  * decodes the literal indexed by @token.
520  */
521 static char *
522 decode_literal (metadata_t *m, guint32 token)
523 {
524         return g_strdup ("LITERAL_VALUE");
525 }
526
527 /**
528  * dis_field_list:
529  * @m: metadata context
530  * @start: starting index into the Field Table.
531  * @end: ending index into Field table.
532  *
533  * This routine displays all the decoded fields from @start to @end
534  */
535 static void
536 dis_field_list (metadata_t *m, guint32 start, guint32 end)
537 {
538         metadata_tableinfo_t *t = &m->tables [META_TABLE_FIELD];
539         guint32 cols [3];
540         int i;
541
542         if (end > t->rows + 1){
543                 fprintf (output, "ERROR index out of range in fields");
544                 exit (1);
545         }
546                         
547         for (i = start; i < end; i++){
548                 char *sig, *flags;
549                 
550                 expand (t, i, cols, CSIZE (cols));
551                 sig = field_signature (m, cols [2]);
552                 flags = field_flags (cols [0]);
553                 
554                 if (cols [0] & FIELD_ATTRIBUTE_LITERAL){
555                         char *lit = decode_literal (m, cols [2]);
556                         
557                         fprintf (output, "    .field %s %s %s = ",
558                                  flags, sig,
559                                  mono_metadata_string_heap (m, cols [1]));
560                         fprintf (output, "%s\n", lit);
561                         g_free (lit);
562                 } else 
563                         fprintf (output, "    .field %s %s %s\n",
564                                  flags, sig,
565                                  mono_metadata_string_heap (m, cols [1]));
566                 g_free (flags);
567                 g_free (sig);
568         }
569 }
570
571 /**
572  * dis_field_list:
573  * @m: metadata context
574  * @start: starting index into the Method Table.
575  * @end: ending index into Method table.
576  *
577  * This routine displays the methods in the Method Table from @start to @end
578  */
579 static void
580 dis_method_list (metadata_t *m, guint32 start, guint32 end)
581 {
582 }
583
584 /**
585  * dis_type:
586  * @m: metadata context
587  * @n: index of type to disassemble
588  *
589  * Disassembles the type whose index in the TypeDef table is @n.
590  */
591 static void
592 dis_type (metadata_t *m, int n)
593 {
594         metadata_tableinfo_t *t = &m->tables [META_TABLE_TYPEDEF];
595         guint32 cols [6];
596         guint32 cols_next [6];
597         const char *name;
598         char *tn;
599         
600         expand (t, n, cols, CSIZE (cols));
601         expand (t, n + 1, cols_next, CSIZE (cols_next));
602
603         name = mono_metadata_string_heap (m, cols [1]);
604
605         if ((cols [0] & TYPE_ATTRIBUTE_CLASS_SEMANTIC_MASK) == TYPE_ATTRIBUTE_CLASS)
606                 tn = "class";
607         else
608                 tn = "interface";
609         
610         fprintf (output, "  .%s %s%s\n", tn, typedef_flags (cols [0]), name);
611         fprintf (output, "  \textends %s\n", typedef_or_ref (m, cols [3]));
612         fprintf (output, "  {\n");
613
614         /*
615          * The value in the table is always valid, we know we have fields
616          * if the value stored is different than the next record.
617          */
618         if (cols [4] != cols_next [4])
619                 dis_field_list (m, cols [4] - 1, cols_next [4] - 1);
620         if (cols [4] != cols_next [5])
621                 dis_method_list (m, cols [5], cols_next [5]);
622
623         fprintf (output, "  }\n");
624 }
625
626 /**
627  * dis_types:
628  * @m: metadata context
629  *
630  * disassembles all types in the @m context
631  */
632 static void
633 dis_types (metadata_t *m)
634 {
635         metadata_tableinfo_t *t = &m->tables [META_TABLE_TYPEDEF];
636         int i;
637
638         for (i = 0; i < t->rows; i++)
639                 dis_type (m, i);
640 }
641
642 /**
643  * disassemble_file:
644  * @file: file containing CIL code.
645  *
646  * Disassembles the @file file.
647  */
648 static void
649 disassemble_file (const char *file)
650 {
651         enum MonoAssemblyOpenStatus status;
652         MonoAssembly *ass;
653         cli_image_info_t *ii;
654         metadata_t *m;
655
656
657         ass = mono_assembly_open (file, &status);
658         if (ass == NULL){
659                 fprintf (stderr, "Error while trying to process %s\n", file);
660                 
661         }
662
663         dump_header_data (ass);
664
665         ii = ass->image_info;
666         m = &ii->cli_metadata;
667         dis_directive_assemblyref (m);
668         dis_directive_assembly (m);
669         dis_types (m);
670         
671         mono_assembly_close (ass);
672 }
673
674 static void
675 usage (void)
676 {
677         fprintf (stderr, "Usage is: monodis file1 ..\n");
678         exit (1);
679 }
680
681 int
682 main (int argc, char *argv [])
683 {
684         GList *input_files = NULL, *l;
685         int i;
686
687         output = stdout;
688         for (i = 1; i < argc; i++){
689                 if (argv [i][0] == '-'){
690                         if (argv [i][1] == 'h')
691                                 usage ();
692                         else if (argv [i][1] == 'd')
693                                 dump_header_data_p = TRUE;
694                 } else
695                         input_files = g_list_append (input_files, argv [i]);
696         }
697
698         if (input_files == NULL)
699                 usage ();
700         
701         for (l = input_files; l; l = l->next)
702                 disassemble_file (l->data);
703
704         return 0;
705 }