2001-06-27 Miguel de Icaza <miguel@ximian.com>
[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 "meta.h"
20 #include "util.h"
21 #include "dump.h"
22 #include "get.h"
23
24 FILE *output;
25
26 /* True if you want to get a dump of the header data */
27 gboolean dump_header_data_p = FALSE;
28
29 int dump_table = -1;
30
31 static void
32 dump_header_data (MonoAssembly *ass)
33 {
34         if (!dump_header_data_p)
35                 return;
36
37         fprintf (output,
38                  "// Ximian's CIL disassembler, version 1.0\n"
39                  "// Copyright (C) 2001 Ximian, Inc.\n\n");
40 }
41
42 static void
43 dis_directive_assembly (metadata_t *m)
44 {
45         metadata_tableinfo_t *t  = &m->tables [META_TABLE_ASSEMBLY];
46         guint32 cols [9];
47         
48         if (t->base == NULL)
49                 return;
50
51         expand (t, 0, cols, CSIZE (cols));
52         
53         fprintf (output,
54                  ".assembly %s\n"
55                  "{\n"
56                  "  .hash algorithm 0x%08x\n"
57                  "  .ver  %d.%d.%d.%d"
58                  "%s %s"
59                  "%s"
60                  "\n"
61                  "}\n",
62                  mono_metadata_string_heap (m, cols [7]),
63                  cols [0],
64                  cols [1], cols [2], cols [3], cols [4],
65                  cols [8] ? "\n  .locale" : "",
66                  cols [8] ? mono_metadata_string_heap (m, cols [8]) : "",
67                  cols [6] ? "\n  .publickey" : ""
68                 );
69 }
70
71 static void
72 dis_directive_assemblyref (metadata_t *m)
73 {
74         metadata_tableinfo_t *t = &m->tables [META_TABLE_ASSEMBLYREF];
75         guint32 cols [9];
76         int i;
77         
78         if (t->base == NULL)
79                 return;
80
81         for (i = 0; i < t->rows; i++){
82                 expand (t, i, cols, CSIZE (cols));
83
84                 fprintf (output,
85                          ".assembly extern %s\n"
86                          "{\n"
87                          "  .ver %d.%d.%d.%d\n"
88                          "}\n",
89                          mono_metadata_string_heap (m, cols [6]),
90                          cols [0], cols [1], cols [2], cols [3]
91                         );
92         }
93 }
94
95 static map_t visibility_map [] = {
96         { TYPE_ATTRIBUTE_NOT_PUBLIC,           "private " },
97         { TYPE_ATTRIBUTE_PUBLIC,               "public " },
98         { TYPE_ATTRIBUTE_NESTED_PUBLIC,        "nested-public " },
99         { TYPE_ATTRIBUTE_NESTED_PRIVATE,       "nested-private " },
100         { TYPE_ATTRIBUTE_NESTED_FAMILY,        "family " },
101         { TYPE_ATTRIBUTE_NESTED_ASSEMBLY,      "nested-assembly" },
102         { TYPE_ATTRIBUTE_NESTED_FAM_AND_ASSEM, "nested-fam-and-assembly" },
103         { TYPE_ATTRIBUTE_NESTED_FAM_OR_ASSEM,  "nested-fam-or-assembly" },
104         { 0, NULL }
105 };
106
107 static map_t layout_map [] = {
108         { TYPE_ATTRIBUTE_AUTO_LAYOUT,          "auto " },
109         { TYPE_ATTRIBUTE_SEQUENTIAL_LAYOUT,    "sequential " },
110         { TYPE_ATTRIBUTE_EXPLICIT_LAYOUT,      "explicit " },
111         { 0, NULL }
112 };
113
114 static map_t format_map [] = {
115         { TYPE_ATTRIBUTE_ANSI_CLASS,           "ansi " },
116         { TYPE_ATTRIBUTE_UNICODE_CLASS,        "unicode " },
117         { TYPE_ATTRIBUTE_AUTO_CLASS,           "auto " },
118         { 0, NULL }
119 };
120
121 static char *
122 typedef_flags (guint32 flags)
123 {
124         static char buffer [1024];
125         int visibility = flags & TYPE_ATTRIBUTE_VISIBILITY_MASK;
126         int layout = flags & TYPE_ATTRIBUTE_LAYOUT_MASK;
127         int format = flags & TYPE_ATTRIBUTE_STRING_FORMAT_MASK;
128         
129         buffer [0] = 0;
130
131         strcat (buffer, map (visibility, visibility_map));
132         strcat (buffer, map (layout, layout_map));
133         strcat (buffer, map (format, format_map));
134         
135         if (flags & TYPE_ATTRIBUTE_ABSTRACT)
136                 strcat (buffer, "abstract ");
137         if (flags & TYPE_ATTRIBUTE_SEALED)
138                 strcat (buffer, "sealed ");
139         if (flags & TYPE_ATTRIBUTE_SPECIAL_NAME)
140                 strcat (buffer, "special-name ");
141         if (flags & TYPE_ATTRIBUTE_IMPORT)
142                 strcat (buffer, "import ");
143         if (flags & TYPE_ATTRIBUTE_SERIALIZABLE)
144                 strcat (buffer, "serializable ");
145         if (flags & TYPE_ATTRIBUTE_BEFORE_FIELD_INIT)
146                 strcat (buffer, "beforefieldinit ");
147
148         return buffer;
149 }
150
151 /**
152  * dis_field_list:
153  * @m: metadata context
154  * @start: starting index into the Field Table.
155  * @end: ending index into Field table.
156  *
157  * This routine displays all the decoded fields from @start to @end
158  */
159 static void
160 dis_field_list (metadata_t *m, guint32 start, guint32 end)
161 {
162         metadata_tableinfo_t *t = &m->tables [META_TABLE_FIELD];
163         guint32 cols [3];
164         int i;
165
166         if (end > t->rows + 1){
167                 fprintf (output, "ERROR index out of range in fields");
168                 exit (1);
169         }
170                         
171         for (i = start; i < end; i++){
172                 char *sig, *flags;
173                 
174                 expand (t, i, cols, CSIZE (cols));
175                 sig = get_field_signature (m, cols [2]);
176                 flags = field_flags (cols [0]);
177                 
178                 if (cols [0] & FIELD_ATTRIBUTE_LITERAL){
179                         char *lit = decode_literal (m, cols [2]);
180                         
181                         fprintf (output, "    .field %s %s %s = ",
182                                  flags, sig,
183                                  mono_metadata_string_heap (m, cols [1]));
184                         fprintf (output, "%s\n", lit);
185                         g_free (lit);
186                 } else 
187                         fprintf (output, "    .field %s %s %s\n",
188                                  flags, sig,
189                                  mono_metadata_string_heap (m, cols [1]));
190                 g_free (flags);
191                 g_free (sig);
192         }
193 }
194
195 static map_t method_access_map [] = {
196         { METHOD_ATTRIBUTE_COMPILER_CONTROLLED, "compilercontrolled " },
197         { METHOD_ATTRIBUTE_PRIVATE,             "private" },
198         { METHOD_ATTRIBUTE_FAM_AND_ASSEM,       "famandassem" },
199         { METHOD_ATTRIBUTE_ASSEM,               "assembly " },
200         { METHOD_ATTRIBUTE_FAMILY,              "family " },
201         { METHOD_ATTRIBUTE_FAM_OR_ASSEM,        "famorassem " },
202         { METHOD_ATTRIBUTE_PUBLIC,              "public " },
203         { 0, NULL }
204 };
205
206 static map_t method_flags_map [] = {
207         { METHOD_ATTRIBUTE_STATIC,              "static " },
208         { METHOD_ATTRIBUTE_FINAL,               "final " },
209         { METHOD_ATTRIBUTE_VIRTUAL,             "virtual " },
210         { METHOD_ATTRIBUTE_HIDE_BY_SIG,         "hidebysig " },
211         { METHOD_ATTRIBUTE_VTABLE_LAYOUT_MASK,  "newslot " },
212         { METHOD_ATTRIBUTE_ABSTRACT,            "abstract " },
213         { METHOD_ATTRIBUTE_SPECIAL_NAME,        "specialname " },
214         { METHOD_ATTRIBUTE_RT_SPECIAL_NAME,     "rtspecialname " },
215         { METHOD_ATTRIBUTE_PINVOKE_IMPL,        "pinvokeimpl " }, 
216         { METHOD_ATTRIBUTE_UNMANAGED_EXPORT,    "export " },
217         { METHOD_ATTRIBUTE_HAS_SECURITY,        "hassecurity" },
218         { METHOD_ATTRIBUTE_REQUIRE_SEC_OBJECT,  "requiresecobj" },
219         { 0, NULL }
220 };
221
222 /**
223  * method_flags:
224  *
225  * Returns a stringified version of the Method's flags
226  */
227 static char *
228 method_flags (guint32 f)
229 {
230         GString *str = g_string_new ("");
231         int access = f & METHOD_ATTRIBUTE_MEMBER_ACCESS_MASK;
232         char *s;
233         
234         g_string_append (str, map (access, method_access_map));
235         g_string_append (str, flags (f, method_flags_map));
236
237         s = str->str;
238         g_string_free (str, FALSE);
239
240         return s;
241 }
242
243 static map_t method_impl_map [] = {
244         { METHOD_IMPL_ATTRIBUTE_IL,              "cil " },
245         { METHOD_IMPL_ATTRIBUTE_NATIVE,          "native " },
246         { METHOD_IMPL_ATTRIBUTE_OPTIL,           "optil " },
247         { METHOD_IMPL_ATTRIBUTE_RUNTIME,         "runtime " },
248         { 0, NULL }
249 };
250
251 static map_t managed_type_map [] = {
252         { METHOD_IMPL_ATTRIBUTE_UNMANAGED,       "unmanaged " },
253         { METHOD_IMPL_ATTRIBUTE_MANAGED,         "managed " },
254         { 0, NULL }
255 };
256
257 static map_t managed_impl_flags [] = {
258         { METHOD_IMPL_ATTRIBUTE_FORWARD_REF,     "fwdref " },
259         { METHOD_IMPL_ATTRIBUTE_PRESERVE_SIG,    "preservesig " },
260         { METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL,   "internalcall " },
261         { METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED,    "synchronized " },
262         { METHOD_IMPL_ATTRIBUTE_NOINLINING,      "noinline " },
263         { 0, NULL }
264 };
265
266 static char *
267 method_impl_flags (guint32 f)
268 {
269         GString *str = g_string_new ("");
270         char *s;
271         int code_type = f & METHOD_IMPL_ATTRIBUTE_CODE_TYPE_MASK;
272         int managed_type = f & METHOD_IMPL_ATTRIBUTE_MANAGED_MASK;
273
274         g_string_append (str, map (code_type, method_impl_map));
275         g_string_append (str, map (managed_type, managed_type_map));
276         g_string_append (str, flags (f, managed_impl_flags));
277         
278         s = str->str;
279         g_string_free (str, FALSE);
280         return s;
281 }
282
283 static void
284 dis_code (metadata_t *m, cli_image_info_t *ii, guint32 rva)
285 {
286         const char *ptr = cli_rva_map (ii, rva);
287
288         if (rva == 0)
289                 return;
290         
291         hex_dump (ptr, 0, 64);
292 }
293
294 typedef struct {
295         char  flags;
296         char *ret_type;
297         int   param_count;
298         char **param;
299 } MethodSignature;
300
301 /**
302  * parse_method_signature:
303  * @m: metadata context 
304  * @blob_signature: pointer to the signature in the Blob heap
305  *
306  * 22.2.1: MethodDefSig.  
307  *
308  * Returns the parsed information in the MethodSignature structure
309  * needs to be deallocated with free_method_signature().
310  */
311 static MethodSignature *
312 parse_method_signature (metadata_t *m, guint32 blob_signature)
313 {
314         GString *res = g_string_new ("");
315         const char *ptr = mono_metadata_blob_heap (m, blob_signature);
316         MethodSignature *ms = g_new0 (MethodSignature, 1);
317         char *s;
318         int i, len;
319
320         ptr = get_encoded_value (ptr, &len);
321         fprintf (output, "     // SIG: ");
322         hex_dump (ptr, 0, -len);
323         fprintf (output, "\n");
324         
325         ms->flags = *ptr++;
326
327         ptr = get_encoded_value (ptr, &ms->param_count);
328         ptr = get_ret_type (m, ptr, &ms->ret_type);
329         ms->param = g_new (char *, ms->param_count);
330         
331         for (i = 0; i < ms->param_count; i++)
332                 ptr = get_param (m, ptr, &(ms->param [i]));
333
334         s = res->str;
335         g_string_free (res, FALSE);
336         return ms;
337 }
338
339 static void
340 free_method_signature (MethodSignature *ms)
341 {
342         int i;
343         
344         for (i = 0; i < ms->param_count; i++)
345                 g_free (ms->param [i]);
346         g_free (ms->param);
347         g_free (ms->ret_type);
348         g_free (ms);
349 }
350
351 /**
352  * dis_field_list:
353  * @m: metadata context
354  * @start: starting index into the Method Table.
355  * @end: ending index into Method table.
356  *
357  * This routine displays the methods in the Method Table from @start to @end
358  */
359 static void
360 dis_method_list (metadata_t *m, cli_image_info_t *ii, guint32 start, guint32 end)
361 {
362         metadata_tableinfo_t *t = &m->tables [META_TABLE_METHOD];
363         metadata_tableinfo_t *p = &m->tables [META_TABLE_PARAM];
364         guint32 cols [6];
365         guint32 cols_next [6];
366         guint32 param_cols [3];
367         int i;
368
369         if (end > t->rows + 1){
370                 fprintf (output, "ERROR index out of range in methods");
371                 exit (1);
372         }
373
374         for (i = start; i < end; i++){
375                 MethodSignature *ms;
376                 char *flags, *impl_flags;
377                 
378                 expand (t, i, cols, CSIZE (cols));
379                 expand (t, i + 1, cols_next, CSIZE (cols_next));
380
381                 flags = method_flags (cols [2]);
382                 impl_flags = method_impl_flags (cols [1]);
383
384                 ms = parse_method_signature (m, cols [4]);
385                         
386                 fprintf (output,
387                          "    .method %s\n",
388                          flags);
389                 fprintf (output,
390                          "           %s %s",
391                          ms->ret_type,
392                          mono_metadata_string_heap (m, cols [3]));
393                 if (ms->param_count > 0){
394                         int i;
395
396                         fprintf (output, "(\n");
397                         for (i = 0; i < ms->param_count; i++){
398                                 char *pf;
399                                 
400                                 expand (p, i, param_cols, CSIZE (param_cols));
401                                 pf = param_flags (param_cols [0]);
402                                 fprintf (
403                                         output, "\t\t%s %s %s%s", pf, ms->param [i],
404                                         mono_metadata_string_heap (m, param_cols [2]),
405                                         (i+1 == ms->param_count) ? ")" : ",\n");
406
407                                 g_free (pf);
408                         }
409                                 
410                 }
411                 fprintf (output, " %s\n", impl_flags);
412                 g_free (flags);
413                 g_free (impl_flags);
414                 
415                 fprintf (output, "    {\n");
416                 fprintf (output, "        // Method begins at RVA 0x%x\n", cols [0]);
417                 dis_code (m, ii, cols [0]);
418                 fprintf (output, "    }\n\n");
419                 free_method_signature (ms);
420         }
421 }
422
423 /**
424  * dis_type:
425  * @m: metadata context
426  * @n: index of type to disassemble
427  *
428  * Disassembles the type whose index in the TypeDef table is @n.
429  */
430 static void
431 dis_type (metadata_t *m, cli_image_info_t *ii, int n)
432 {
433         metadata_tableinfo_t *t = &m->tables [META_TABLE_TYPEDEF];
434         guint32 cols [6];
435         guint32 cols_next [6];
436         const char *name;
437         
438         expand (t, n, cols, CSIZE (cols));
439         expand (t, n + 1, cols_next, CSIZE (cols_next));
440
441         fprintf (output, ".namespace %s\n{\n", mono_metadata_string_heap (m, cols [2]));
442         name = mono_metadata_string_heap (m, cols [1]);
443
444         if ((cols [0] & TYPE_ATTRIBUTE_CLASS_SEMANTIC_MASK) == TYPE_ATTRIBUTE_CLASS){
445                 char *base = get_typedef_or_ref (m, cols [3]);
446                 fprintf (output, "  .class %s%s\n", typedef_flags (cols [0]), name);
447                 fprintf (output, "  \textends %s\n", base);
448                 g_free (base);
449         } else
450                 fprintf (output, "  .class interface %s%s\n", typedef_flags (cols [0]), name);
451         
452         fprintf (output, "  {\n");
453
454         /*
455          * The value in the table is always valid, we know we have fields
456          * if the value stored is different than the next record.
457          */
458         if (cols [4] != cols_next [4])
459                 dis_field_list (m, cols [4] - 1, cols_next [4] - 1);
460         fprintf (output, "\n");
461         if (cols [4] != cols_next [5])
462                 dis_method_list (m, ii, cols [5] - 1, cols_next [5] - 1);
463
464         fprintf (output, "  }\n}\n\n");
465 }
466
467 /**
468  * dis_types:
469  * @m: metadata context
470  *
471  * disassembles all types in the @m context
472  */
473 static void
474 dis_types (metadata_t *m, cli_image_info_t *ii)
475 {
476         metadata_tableinfo_t *t = &m->tables [META_TABLE_TYPEDEF];
477         int i;
478
479         for (i = 0; i < t->rows; i++)
480                 dis_type (m, ii, i);
481 }
482
483 /**
484  * disassemble_file:
485  * @file: file containing CIL code.
486  *
487  * Disassembles the @file file.
488  */
489 static void
490 disassemble_file (const char *file)
491 {
492         enum MonoAssemblyOpenStatus status;
493         MonoAssembly *ass;
494         cli_image_info_t *ii;
495         metadata_t *m;
496
497
498         ass = mono_assembly_open (file, &status);
499         if (ass == NULL){
500                 fprintf (stderr, "Error while trying to process %s\n", file);
501                 
502         }
503
504         ii = ass->image_info;
505         m = &ii->cli_metadata;
506         
507         if (dump_table != -1){
508                 switch (dump_table){
509                 case META_TABLE_TYPEDEF:
510                         dump_table_typedef (m);
511                         break;
512                 case META_TABLE_TYPEREF:
513                         dump_table_typeref (m);
514                         break;
515                 case META_TABLE_ASSEMBLYREF:
516                         dump_table_assemblyref (m);
517                         break;
518                 case META_TABLE_PARAM:
519                         dump_table_param (m);
520                         break;
521                 default:
522                         g_error ("Internal error");
523                 }
524         } else {
525                 dump_header_data (ass);
526                 
527                 dis_directive_assemblyref (m);
528                 dis_directive_assembly (m);
529                 dis_types (m, ii);
530         }
531         
532         mono_assembly_close (ass);
533 }
534
535 static void
536 usage (void)
537 {
538         fprintf (stderr, "Usage is: monodis [--typeref][--typedef][--assemblyref] file ..\n");
539         exit (1);
540 }
541
542 int
543 main (int argc, char *argv [])
544 {
545         GList *input_files = NULL, *l;
546         int i;
547
548         output = stdout;
549         for (i = 1; i < argc; i++){
550                 if (argv [i][0] == '-'){
551                         if (argv [i][1] == 'h')
552                                 usage ();
553                         else if (argv [i][1] == 'd')
554                                 dump_header_data_p = TRUE;
555                         else if (strcmp (argv [i], "--help") == 0)
556                                 usage ();
557                         else if (strcmp (argv [i], "--typeref") == 0)
558                                 dump_table = META_TABLE_TYPEREF;
559                         else if (strcmp (argv [i], "--typedef") == 0)
560                                 dump_table = META_TABLE_TYPEDEF;
561                         else if (strcmp (argv [i], "--assemblyref") == 0)
562                                 dump_table = META_TABLE_ASSEMBLYREF;
563                         else if (strcmp (argv [i], "--param") == 0)
564                                 dump_table = META_TABLE_PARAM;
565                 } else
566                         input_files = g_list_append (input_files, argv [i]);
567         }
568
569         if (input_files == NULL)
570                 usage ();
571         
572         for (l = input_files; l; l = l->next)
573                 disassemble_file (l->data);
574
575         return 0;
576 }