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