Mon Apr 15 11:35:05 CEST 2002 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  *   
13  *   How are fields with literals mapped to constants?
14  */
15 #include <config.h>
16 #include <stdio.h>
17 #include <string.h>
18 #include <glib.h>
19 #include <stdlib.h>
20 #include "meta.h"
21 #include "util.h"
22 #include "dump.h"
23 #include "get.h"
24 #include "dis-cil.h"
25 #include <mono/metadata/loader.h>
26 #include <mono/metadata/assembly.h>
27 #include <mono/metadata/appdomain.h>
28
29 FILE *output;
30
31 /* True if you want to get a dump of the header data */
32 gboolean dump_header_data_p = FALSE;
33
34 gboolean substitute_with_mscorlib_p = FALSE;
35
36 int dump_table = -1;
37
38 static void
39 dump_header_data (MonoImage *img)
40 {
41         if (!dump_header_data_p)
42                 return;
43
44         fprintf (output,
45                  "// Ximian's CIL disassembler, version 1.0\n"
46                  "// Copyright (C) 2001 Ximian, Inc.\n\n");
47 }
48
49 static void
50 dis_directive_assembly (MonoImage *m)
51 {
52         MonoTableInfo *t  = &m->tables [MONO_TABLE_ASSEMBLY];
53         guint32 cols [MONO_ASSEMBLY_SIZE];
54         
55         if (t->base == NULL)
56                 return;
57
58         mono_metadata_decode_row (t, 0, cols, MONO_ASSEMBLY_SIZE);
59         
60         fprintf (output,
61                  ".assembly '%s'\n"
62                  "{\n"
63                  "  .hash algorithm 0x%08x\n"
64                  "  .ver  %d:%d:%d:%d"
65                  "%s %s"
66                  "%s"
67                  "\n"
68                  "}\n",
69                  mono_metadata_string_heap (m, cols [MONO_ASSEMBLY_NAME]),
70                  cols [MONO_ASSEMBLY_HASH_ALG],
71                  cols [MONO_ASSEMBLY_MAJOR_VERSION], cols [MONO_ASSEMBLY_MINOR_VERSION], 
72                  cols [MONO_ASSEMBLY_BUILD_NUMBER], cols [MONO_ASSEMBLY_REV_NUMBER],
73                  cols [MONO_ASSEMBLY_CULTURE] ? "\n  .locale" : "",
74                  cols [MONO_ASSEMBLY_CULTURE] ? mono_metadata_string_heap (m, cols [MONO_ASSEMBLY_CULTURE]) : "",
75                  cols [MONO_ASSEMBLY_PUBLIC_KEY] ? "\n  .publickey" : ""
76                 );
77 }
78
79 static void
80 dis_directive_assemblyref (MonoImage *m)
81 {
82         MonoTableInfo *t = &m->tables [MONO_TABLE_ASSEMBLYREF];
83         guint32 cols [MONO_ASSEMBLYREF_SIZE];
84         int i;
85         
86         if (t->base == NULL)
87                 return;
88
89         for (i = 0; i < t->rows; i++){
90                 mono_metadata_decode_row (t, i, cols, MONO_ASSEMBLYREF_SIZE);
91
92                 fprintf (output,
93                          ".assembly extern %s\n"
94                          "{\n"
95                          "  .ver %d:%d:%d:%d\n"
96                          "}\n",
97                          mono_metadata_string_heap (m, cols [MONO_ASSEMBLYREF_NAME]),
98                          cols [MONO_ASSEMBLYREF_MAJOR_VERSION], cols [MONO_ASSEMBLYREF_MINOR_VERSION], 
99                          cols [MONO_ASSEMBLYREF_BUILD_NUMBER], cols [MONO_ASSEMBLYREF_REV_NUMBER]
100                         );
101         }
102 }
103
104 static map_t visibility_map [] = {
105         { TYPE_ATTRIBUTE_NOT_PUBLIC,           "private " },
106         { TYPE_ATTRIBUTE_PUBLIC,               "public " },
107         { TYPE_ATTRIBUTE_NESTED_PUBLIC,        "nested-public " },
108         { TYPE_ATTRIBUTE_NESTED_PRIVATE,       "nested-private " },
109         { TYPE_ATTRIBUTE_NESTED_FAMILY,        "family " },
110         { TYPE_ATTRIBUTE_NESTED_ASSEMBLY,      "nested-assembly" },
111         { TYPE_ATTRIBUTE_NESTED_FAM_AND_ASSEM, "nested-fam-and-assembly" },
112         { TYPE_ATTRIBUTE_NESTED_FAM_OR_ASSEM,  "nested-fam-or-assembly" },
113         { 0, NULL }
114 };
115
116 static map_t layout_map [] = {
117         { TYPE_ATTRIBUTE_AUTO_LAYOUT,          "auto " },
118         { TYPE_ATTRIBUTE_SEQUENTIAL_LAYOUT,    "sequential " },
119         { TYPE_ATTRIBUTE_EXPLICIT_LAYOUT,      "explicit " },
120         { 0, NULL }
121 };
122
123 static map_t format_map [] = {
124         { TYPE_ATTRIBUTE_ANSI_CLASS,           "ansi " },
125         { TYPE_ATTRIBUTE_UNICODE_CLASS,        "unicode " },
126         { TYPE_ATTRIBUTE_AUTO_CLASS,           "auto " },
127         { 0, NULL }
128 };
129
130 static char *
131 typedef_flags (guint32 flags)
132 {
133         static char buffer [1024];
134         int visibility = flags & TYPE_ATTRIBUTE_VISIBILITY_MASK;
135         int layout = flags & TYPE_ATTRIBUTE_LAYOUT_MASK;
136         int format = flags & TYPE_ATTRIBUTE_STRING_FORMAT_MASK;
137         
138         buffer [0] = 0;
139
140         strcat (buffer, map (visibility, visibility_map));
141         strcat (buffer, map (layout, layout_map));
142         strcat (buffer, map (format, format_map));
143         
144         if (flags & TYPE_ATTRIBUTE_ABSTRACT)
145                 strcat (buffer, "abstract ");
146         if (flags & TYPE_ATTRIBUTE_SEALED)
147                 strcat (buffer, "sealed ");
148         if (flags & TYPE_ATTRIBUTE_SPECIAL_NAME)
149                 strcat (buffer, "special-name ");
150         if (flags & TYPE_ATTRIBUTE_IMPORT)
151                 strcat (buffer, "import ");
152         if (flags & TYPE_ATTRIBUTE_SERIALIZABLE)
153                 strcat (buffer, "serializable ");
154         if (flags & TYPE_ATTRIBUTE_BEFORE_FIELD_INIT)
155                 strcat (buffer, "beforefieldinit ");
156
157         return buffer;
158 }
159
160 /**
161  * dis_field_list:
162  * @m: metadata context
163  * @start: starting index into the Field Table.
164  * @end: ending index into Field table.
165  *
166  * This routine displays all the decoded fields from @start to @end
167  */
168 static void
169 dis_field_list (MonoImage *m, guint32 start, guint32 end)
170 {
171         MonoTableInfo *t = &m->tables [MONO_TABLE_FIELD];
172         guint32 cols [MONO_FIELD_SIZE];
173         int i;
174
175         if (end > t->rows + 1) {
176                 g_warning ("ERROR index out of range in fields");
177                 end = t->rows;
178         }
179                         
180         for (i = start; i < end; i++){
181                 char *sig, *flags;
182                 
183                 mono_metadata_decode_row (t, i, cols, MONO_FIELD_SIZE);
184                 sig = get_field_signature (m, cols [MONO_FIELD_SIGNATURE]);
185                 flags = field_flags (cols [MONO_FIELD_FLAGS]);
186                 
187                 if (cols [MONO_FIELD_FLAGS] & FIELD_ATTRIBUTE_LITERAL){
188                         char *lit;
189                         guint32 const_cols [MONO_CONSTANT_SIZE];
190                         guint32 crow;
191                         
192                         if ((crow = mono_metadata_get_constant_index (m, MONO_TOKEN_FIELD_DEF | (i+1)))) {
193                                 mono_metadata_decode_row (&m->tables [MONO_TABLE_CONSTANT], crow-1, const_cols, MONO_CONSTANT_SIZE);
194                                 lit = get_constant (m, const_cols [MONO_CONSTANT_TYPE], const_cols [MONO_CONSTANT_VALUE]);
195                         } else {
196                                 lit = g_strdup ("not found");
197                         }
198                         
199                         fprintf (output, "    .field %s %s %s = ",
200                                  flags, sig,
201                                  mono_metadata_string_heap (m, cols [MONO_FIELD_NAME]));
202                         fprintf (output, "%s\n", lit);
203                         g_free (lit);
204                 } else 
205                         fprintf (output, "    .field %s %s %s\n",
206                                  flags, sig,
207                                  mono_metadata_string_heap (m, cols [MONO_FIELD_NAME]));
208                 g_free (flags);
209                 g_free (sig);
210         }
211 }
212
213 static map_t method_access_map [] = {
214         { METHOD_ATTRIBUTE_COMPILER_CONTROLLED, "compilercontrolled " },
215         { METHOD_ATTRIBUTE_PRIVATE,             "private" },
216         { METHOD_ATTRIBUTE_FAM_AND_ASSEM,       "famandassem" },
217         { METHOD_ATTRIBUTE_ASSEM,               "assembly " },
218         { METHOD_ATTRIBUTE_FAMILY,              "family " },
219         { METHOD_ATTRIBUTE_FAM_OR_ASSEM,        "famorassem " },
220         { METHOD_ATTRIBUTE_PUBLIC,              "public " },
221         { 0, NULL }
222 };
223
224 static map_t method_flags_map [] = {
225         { METHOD_ATTRIBUTE_STATIC,              "static " },
226         { METHOD_ATTRIBUTE_FINAL,               "final " },
227         { METHOD_ATTRIBUTE_VIRTUAL,             "virtual " },
228         { METHOD_ATTRIBUTE_HIDE_BY_SIG,         "hidebysig " },
229         { METHOD_ATTRIBUTE_VTABLE_LAYOUT_MASK,  "newslot " },
230         { METHOD_ATTRIBUTE_ABSTRACT,            "abstract " },
231         { METHOD_ATTRIBUTE_SPECIAL_NAME,        "specialname " },
232         { METHOD_ATTRIBUTE_RT_SPECIAL_NAME,     "rtspecialname " },
233         { METHOD_ATTRIBUTE_UNMANAGED_EXPORT,    "export " },
234         { METHOD_ATTRIBUTE_HAS_SECURITY,        "hassecurity" },
235         { METHOD_ATTRIBUTE_REQUIRE_SEC_OBJECT,  "requiresecobj" },
236         { METHOD_ATTRIBUTE_PINVOKE_IMPL,        "pinvokeimpl " }, 
237         { 0, NULL }
238 };
239
240 /**
241  * method_flags:
242  *
243  * Returns a stringified version of the Method's flags
244  */
245 static char *
246 method_flags (guint32 f)
247 {
248         GString *str = g_string_new ("");
249         int access = f & METHOD_ATTRIBUTE_MEMBER_ACCESS_MASK;
250         char *s;
251         
252         g_string_append (str, map (access, method_access_map));
253         g_string_append (str, flags (f, method_flags_map));
254
255         s = str->str;
256         g_string_free (str, FALSE);
257
258         return s;
259 }
260
261 static map_t pinvoke_flags_map [] = {
262         { PINVOKE_ATTRIBUTE_NO_MANGLE ,            "nomangle " },
263         { PINVOKE_ATTRIBUTE_SUPPORTS_LAST_ERROR,   "lasterr " },
264         { 0, NULL }
265 };
266
267 static map_t pinvoke_call_conv_map [] = {
268         { PINVOKE_ATTRIBUTE_CALL_CONV_WINAPI,      "winapi " },
269         { PINVOKE_ATTRIBUTE_CALL_CONV_CDECL,       "cdecl " },
270         { PINVOKE_ATTRIBUTE_CALL_CONV_STDCALL,     "stdcall " },
271         { PINVOKE_ATTRIBUTE_CALL_CONV_THISCALL,    "thiscall " },
272         { PINVOKE_ATTRIBUTE_CALL_CONV_FASTCALL,    "fastcall " },
273         { 0, NULL }
274 };
275
276 static map_t pinvoke_char_set_map [] = {
277         { PINVOKE_ATTRIBUTE_CHAR_SET_NOT_SPEC,     "" },
278         { PINVOKE_ATTRIBUTE_CHAR_SET_ANSI,         "ansi " },
279         { PINVOKE_ATTRIBUTE_CHAR_SET_UNICODE ,     "unicode " },
280         { PINVOKE_ATTRIBUTE_CHAR_SET_AUTO,         "autochar " },
281         { 0, NULL }
282 };
283
284 /**
285  * pinvoke_flags:
286  *
287  * Returns a stringified version of the Method's pinvoke flags
288  */
289 static char *
290 pinvoke_flags (guint32 f)
291 {
292         GString *str = g_string_new ("");
293         int cset = f & PINVOKE_ATTRIBUTE_CHAR_SET_MASK;
294         int cconv = f & PINVOKE_ATTRIBUTE_CALL_CONV_MASK;
295         char *s;
296         
297         g_string_append (str, map (cset, pinvoke_char_set_map));
298         g_string_append (str, map (cconv, pinvoke_call_conv_map));
299         g_string_append (str, flags (f, pinvoke_flags_map));
300
301         s = g_strdup(str->str);
302         g_string_free (str, FALSE);
303
304         return s;
305 }
306
307 static map_t method_impl_map [] = {
308         { METHOD_IMPL_ATTRIBUTE_IL,              "cil " },
309         { METHOD_IMPL_ATTRIBUTE_NATIVE,          "native " },
310         { METHOD_IMPL_ATTRIBUTE_OPTIL,           "optil " },
311         { METHOD_IMPL_ATTRIBUTE_RUNTIME,         "runtime " },
312         { 0, NULL }
313 };
314
315 static map_t managed_type_map [] = {
316         { METHOD_IMPL_ATTRIBUTE_UNMANAGED,       "unmanaged " },
317         { METHOD_IMPL_ATTRIBUTE_MANAGED,         "managed " },
318         { 0, NULL }
319 };
320
321 static map_t managed_impl_flags [] = {
322         { METHOD_IMPL_ATTRIBUTE_FORWARD_REF,     "fwdref " },
323         { METHOD_IMPL_ATTRIBUTE_PRESERVE_SIG,    "preservesig " },
324         { METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL,   "internalcall " },
325         { METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED,    "synchronized " },
326         { METHOD_IMPL_ATTRIBUTE_NOINLINING,      "noinline " },
327         { 0, NULL }
328 };
329
330 static char *
331 method_impl_flags (guint32 f)
332 {
333         GString *str = g_string_new ("");
334         char *s;
335         int code_type = f & METHOD_IMPL_ATTRIBUTE_CODE_TYPE_MASK;
336         int managed_type = f & METHOD_IMPL_ATTRIBUTE_MANAGED_MASK;
337
338         g_string_append (str, map (code_type, method_impl_map));
339         g_string_append (str, map (managed_type, managed_type_map));
340         g_string_append (str, flags (f, managed_impl_flags));
341         
342         s = str->str;
343         g_string_free (str, FALSE);
344         return s;
345 }
346
347 static void
348 dis_locals (MonoImage *m, MonoMethodHeader *mh) 
349 {
350         int i;
351
352         fprintf(output, "\t.locals %s(\n", mh->init_locals ? "init " : "");
353         for (i=0; i < mh->num_locals; ++i) {
354                 char * desc;
355                 if (i)
356                         fprintf(output, ",\n");
357                 /* print also byref and pinned attributes */
358                 desc = dis_stringify_type (m, mh->locals[i]);
359                 fprintf(output, "\t\t%s\tV_%d", desc, i);
360                 g_free(desc);
361         }
362         fprintf(output, ")\n");
363 }
364
365 static void
366 dis_code (MonoImage *m, guint32 rva)
367 {
368         MonoMethodHeader *mh;
369         MonoCLIImageInfo *ii = m->image_info;
370         const char *ptr = mono_cli_rva_map (ii, rva);
371         const char *loc;
372
373         if (rva == 0)
374                 return;
375
376         mh = mono_metadata_parse_mh (m, ptr);
377         if (ii->cli_cli_header.ch_entry_point){
378                 loc = mono_metadata_locate_token (m, ii->cli_cli_header.ch_entry_point);
379                 if (rva == read32 (loc))
380                         fprintf (output, "\t.entrypoint\n");
381         }
382         
383         fprintf (output, "\t// Code size %d (0x%x)\n", mh->code_size, mh->code_size);
384         fprintf (output, "\t.maxstack %d\n", mh->max_stack);
385         if (mh->num_locals)
386                 dis_locals (m, mh);
387         dissasemble_cil (m, mh);
388         
389 /*
390   hex_dump (mh->code, 0, mh->code_size);
391   printf ("\nAfter the code\n");
392   hex_dump (mh->code + mh->code_size, 0, 64);
393 */
394         mono_metadata_free_mh (mh);
395 }
396
397 static char *
398 pinvoke_info (MonoImage *m, guint32 mindex)
399 {
400         MonoTableInfo *im = &m->tables [MONO_TABLE_IMPLMAP];
401         MonoTableInfo *mr = &m->tables [MONO_TABLE_MODULEREF];
402         guint32 im_cols [MONO_IMPLMAP_SIZE];
403         guint32 mr_cols [MONO_MODULEREF_SIZE];
404         const char *import, *scope;
405         char *flags;
406         int i;
407
408         for (i = 0; i < im->rows; i++) {
409
410                 mono_metadata_decode_row (im, i, im_cols, MONO_IMPLMAP_SIZE);
411
412                 if ((im_cols [MONO_IMPLMAP_MEMBER] >> 1) == mindex + 1) {
413
414                         flags = pinvoke_flags (im_cols [MONO_IMPLMAP_FLAGS]);
415
416                         import = mono_metadata_string_heap (m, im_cols [MONO_IMPLMAP_NAME]);
417
418                         mono_metadata_decode_row (mr, im_cols [MONO_IMPLMAP_SCOPE] - 1, 
419                                                   mr_cols, MONO_MODULEREF_SIZE);
420
421                         scope = mono_metadata_string_heap (m, mr_cols [MONO_MODULEREF_NAME]);
422                                 
423                         return g_strdup_printf ("(%s as %s %s)", scope, import,
424                                                 flags);
425                         g_free (flags);
426                 }
427         }
428
429         return NULL;
430 }
431
432 /**
433  * dis_method_list:
434  * @m: metadata context
435  * @start: starting index into the Method Table.
436  * @end: ending index into Method table.
437  *
438  * This routine displays the methods in the Method Table from @start to @end
439  */
440 static void
441 dis_method_list (MonoImage *m, guint32 start, guint32 end)
442 {
443         MonoTableInfo *t = &m->tables [MONO_TABLE_METHOD];
444         guint32 cols [MONO_METHOD_SIZE];
445         int i;
446
447         if (end > t->rows){
448                 fprintf (output, "ERROR index out of range in methods");
449                 /*exit (1);*/
450                 end = t->rows;
451         }
452
453         for (i = start; i < end; i++){
454                 MonoMethodSignature *ms;
455                 char *flags, *impl_flags;
456                 const char *sig;
457                 char *sig_str;
458                 
459                 mono_metadata_decode_row (t, i, cols, MONO_METHOD_SIZE);
460
461                 flags = method_flags (cols [MONO_METHOD_FLAGS]);
462                 impl_flags = method_impl_flags (cols [MONO_METHOD_IMPLFLAGS]);
463
464                 sig = mono_metadata_blob_heap (m, cols [MONO_METHOD_SIGNATURE]);
465                 mono_metadata_decode_blob_size (sig, &sig);
466                 ms = mono_metadata_parse_method_signature (m, 1, sig, &sig);
467                 sig_str = dis_stringify_method_signature (m, ms, i + 1);
468                         
469                 fprintf (output, "    // method line %d\n", i + 1);
470                 fprintf (output, "    .method %s", flags);
471
472                 if (cols [MONO_METHOD_FLAGS] & METHOD_ATTRIBUTE_PINVOKE_IMPL)
473                         fprintf (output, "%s", pinvoke_info (m, i));
474
475                 fprintf (output, "\n           %s", sig_str);
476                 fprintf (output, " %s\n", impl_flags);
477                 g_free (flags);
478                 g_free (impl_flags);
479                 
480                 fprintf (output, "    {\n");
481                 fprintf (output, "        // Method begins at RVA 0x%x\n", cols [MONO_METHOD_RVA]);
482                 dis_code (m, cols [MONO_METHOD_RVA]);
483                 fprintf (output, "    }\n\n");
484                 mono_metadata_free_method_signature (ms);
485                 g_free (sig_str);
486         }
487 }
488
489 typedef struct {
490         MonoTableInfo *t;
491         guint32 col_idx;
492         guint32 idx;
493         guint32 result;
494 } plocator_t;
495
496 static int
497 table_locator (const void *a, const void *b)
498 {
499         plocator_t *loc = (plocator_t *) a;
500         const char *bb = (const char *) b;
501         guint32 table_index = (bb - loc->t->base) / loc->t->row_size;
502         guint32 col;
503         
504         col = mono_metadata_decode_row_col (loc->t, table_index, loc->col_idx);
505
506         if (loc->idx == col) {
507                 loc->result = table_index;
508                 return 0;
509         }
510         if (loc->idx < col)
511                 return -1;
512         else 
513                 return 1;
514 }
515
516 static void
517 dis_property_methods (MonoImage *m, guint32 prop)
518 {
519         guint start, end;
520         MonoTableInfo *msemt = &m->tables [MONO_TABLE_METHODSEMANTICS];
521         guint32 cols [MONO_METHOD_SEMA_SIZE];
522         char *sig;
523         const char *type[] = {NULL, ".set", ".get", NULL, ".other"};
524
525         start = mono_metadata_methods_from_property (m, prop, &end);
526         while (start < end) {
527                 mono_metadata_decode_row (msemt, start, cols, MONO_METHOD_SEMA_SIZE);
528                 sig = dis_stringify_method_signature (m, NULL, cols [MONO_METHOD_SEMA_METHOD]);
529                 fprintf (output, "\t\t%s %s\n", type [cols [MONO_METHOD_SEMA_SEMANTICS]], sig);
530                 g_free (sig);
531                 ++start;
532         }
533 }
534
535 static char*
536 dis_property_signature (MonoImage *m, guint32 prop_idx)
537 {
538         MonoTableInfo *propt = &m->tables [MONO_TABLE_PROPERTY];
539         const char *ptr;
540         guint32 pcount, i;
541         guint32 cols [MONO_PROPERTY_SIZE];
542         MonoType *type;
543         MonoType *param;
544         char *blurb;
545         const char *name;
546         int prop_flags;
547         GString *res = g_string_new ("");
548
549         mono_metadata_decode_row (propt, prop_idx, cols, MONO_PROPERTY_SIZE);
550         name = mono_metadata_string_heap (m, cols [MONO_PROPERTY_NAME]);
551         prop_flags = cols [MONO_PROPERTY_FLAGS];
552         ptr = mono_metadata_blob_heap (m, cols [MONO_PROPERTY_TYPE]);
553         mono_metadata_decode_blob_size (ptr, &ptr);
554         /* ECMA claims 0x08 ... */
555         if (*ptr != 0x28 && *ptr != 0x08)
556                 g_warning("incorrect signature in propert blob: 0x%x", *ptr);
557         ptr++;
558         pcount = mono_metadata_decode_value (ptr, &ptr);
559         type = mono_metadata_parse_type (m, MONO_PARSE_TYPE, 0, ptr, &ptr);
560         blurb = dis_stringify_type (m, type);
561         if (prop_flags & 0x0200)
562                 g_string_append (res, "special ");
563         if (prop_flags & 0x0400)
564                 g_string_append (res, "runtime ");
565         if (prop_flags & 0x1000)
566                 g_string_append (res, "hasdefault ");
567         g_string_sprintfa (res, "%s %s (", blurb, name);
568         g_free (blurb);
569         mono_metadata_free_type (type);
570         for (i = 0; i < pcount; i++) {
571                 if (i)
572                         g_string_append (res, ", ");
573                 param = mono_metadata_parse_param (m, ptr, &ptr);
574                 blurb = dis_stringify_param (m, param);
575                 g_string_append (res, blurb);
576                 mono_metadata_free_type (param);
577                 g_free (blurb);
578         }
579         g_string_append_c (res, ')');
580         blurb = res->str;
581         g_string_free (res, FALSE);
582         return blurb;
583
584 }
585
586 static void
587 dis_property_list (MonoImage *m, guint32 typedef_row)
588 {
589         guint start, end, i;
590         start = mono_metadata_properties_from_typedef (m, typedef_row, &end);
591
592         for (i = start; i < end; ++i) {
593                 char *sig = dis_property_signature (m, i);
594                 fprintf (output, "\t.property %s\n\t{\n", sig);
595                 dis_property_methods (m, i);
596                 fprintf (output, "\t}\n");
597                 g_free (sig);
598         }
599 }
600
601 static char*
602 dis_event_signature (MonoImage *m, guint32 event_idx)
603 {
604         MonoTableInfo *et = &m->tables [MONO_TABLE_EVENT];
605         const char *name;
606         char *type, *res;
607         guint32 cols [MONO_EVENT_SIZE];
608         
609         mono_metadata_decode_row (et, event_idx, cols, MONO_EVENT_SIZE);
610         name = mono_metadata_string_heap (m, cols [MONO_EVENT_NAME]);
611         type = get_typedef_or_ref (m, cols [MONO_EVENT_TYPE]);
612
613         res = g_strdup_printf ("%s %s", type, name);
614         g_free (type);
615         return res;
616 }
617
618 static void
619 dis_event_methods (MonoImage *m, guint32 event)
620 {
621         guint start, end;
622         MonoTableInfo *msemt = &m->tables [MONO_TABLE_METHODSEMANTICS];
623         guint32 cols [MONO_METHOD_SEMA_SIZE];
624         char *sig;
625         const char *type;
626
627         start = mono_metadata_methods_from_event (m, event, &end);
628         while (start < end) {
629                 mono_metadata_decode_row (msemt, start, cols, MONO_METHOD_SEMA_SIZE);
630                 sig = dis_stringify_method_signature (m, NULL, cols [MONO_METHOD_SEMA_METHOD]);
631                 switch (cols [MONO_METHOD_SEMA_SEMANTICS]) {
632                 case METHOD_SEMANTIC_OTHER:
633                         type = ".other"; break;
634                 case METHOD_SEMANTIC_ADD_ON:
635                         type = ".addon"; break;
636                 case METHOD_SEMANTIC_REMOVE_ON:
637                         type = ".removeon"; break;
638                 case METHOD_SEMANTIC_FIRE:
639                         type = ".fire"; break;
640                 default:
641                         break;
642                 }
643                 fprintf (output, "\t\t%s %s\n", type, sig);
644                 g_free (sig);
645                 ++start;
646         }
647 }
648
649 static void
650 dis_event_list (MonoImage *m, guint32 typedef_row)
651 {
652         guint start, end, i;
653         start = mono_metadata_events_from_typedef (m, typedef_row, &end);
654
655         for (i = start; i < end; ++i) {
656                 char *sig = dis_event_signature (m, i);
657                 fprintf (output, "\t.event %s\n\t{\n", sig);
658                 dis_event_methods (m, i);
659                 fprintf (output, "\t}\n");
660                 g_free (sig);
661         }
662 }
663
664 static void
665 dis_interfaces (MonoImage *m, guint32 typedef_row)
666 {
667         plocator_t loc;
668         guint start;
669         guint32 cols [MONO_INTERFACEIMPL_SIZE];
670         char *intf;
671         MonoTableInfo *table = &m->tables [MONO_TABLE_INTERFACEIMPL];
672
673         if (!table->base)
674                 return;
675
676         loc.t = table;
677         loc.col_idx = MONO_INTERFACEIMPL_CLASS;
678         loc.idx = typedef_row;
679
680         if (!bsearch (&loc, table->base, table->rows, table->row_size, table_locator))
681                 return;
682
683         start = loc.result;
684         /*
685          * We may end up in the middle of the rows... 
686          */
687         while (start > 0) {
688                 if (loc.idx == mono_metadata_decode_row_col (table, start - 1, MONO_INTERFACEIMPL_CLASS))
689                         start--;
690                 else
691                         break;
692         }
693         while (start < table->rows) {
694                 mono_metadata_decode_row (table, start, cols, MONO_INTERFACEIMPL_SIZE);
695                 if (cols [MONO_INTERFACEIMPL_CLASS] != loc.idx)
696                         break;
697                 intf = get_typedef_or_ref (m, cols [MONO_INTERFACEIMPL_INTERFACE]);
698                 fprintf (output, "  \timplements %s\n", intf);
699                 g_free (intf);
700                 ++start;
701         }
702 }
703
704 /**
705  * dis_type:
706  * @m: metadata context
707  * @n: index of type to disassemble
708  *
709  * Disassembles the type whose index in the TypeDef table is @n.
710  */
711 static void
712 dis_type (MonoImage *m, int n)
713 {
714         MonoTableInfo *t = &m->tables [MONO_TABLE_TYPEDEF];
715         guint32 cols [MONO_TYPEDEF_SIZE];
716         guint32 cols_next [MONO_TYPEDEF_SIZE];
717         const char *name, *nspace;
718         guint32 packing_size, class_size;
719         gboolean next_is_valid, last;
720         
721         mono_metadata_decode_row (t, n, cols, MONO_TYPEDEF_SIZE);
722
723         if (t->rows > n + 1) {
724                 mono_metadata_decode_row (t, n + 1, cols_next, MONO_TYPEDEF_SIZE);
725                 next_is_valid = 1;
726         } else
727                 next_is_valid = 0;
728
729         nspace = mono_metadata_string_heap (m, cols [MONO_TYPEDEF_NAMESPACE]);
730         if (*nspace)
731                 fprintf (output, ".namespace %s\n{\n", nspace);
732         name = mono_metadata_string_heap (m, cols [MONO_TYPEDEF_NAME]);
733
734         if ((cols [MONO_TYPEDEF_FLAGS] & TYPE_ATTRIBUTE_CLASS_SEMANTIC_MASK) == TYPE_ATTRIBUTE_CLASS){
735                 char *base = get_typedef_or_ref (m, cols [MONO_TYPEDEF_EXTENDS]);
736                 fprintf (output, "  .class %s%s\n", typedef_flags (cols [MONO_TYPEDEF_FLAGS]), name);
737                 fprintf (output, "  \textends %s\n", base);
738                 g_free (base);
739         } else
740                 fprintf (output, "  .class interface %s%s\n", typedef_flags (cols [MONO_TYPEDEF_FLAGS]), name);
741         
742         dis_interfaces (m, n + 1);
743         fprintf (output, "  {\n");
744
745         if (mono_metadata_packing_from_typedef (m, n + 1, &packing_size, &class_size)) {
746                 fprintf (output, "    .pack %d\n", packing_size);
747                 fprintf (output, "    .size %d\n", class_size);
748         }
749         /*
750          * The value in the table is always valid, we know we have fields
751          * if the value stored is different than the next record.
752          */
753
754         if (next_is_valid)
755                 last = cols_next [MONO_TYPEDEF_FIELD_LIST] - 1;
756         else
757                 last = m->tables [MONO_TABLE_FIELD].rows;
758                         
759         if (cols [MONO_TYPEDEF_FIELD_LIST] && cols [MONO_TYPEDEF_FIELD_LIST] <= m->tables [MONO_TABLE_FIELD].rows)
760                 dis_field_list (m, cols [MONO_TYPEDEF_FIELD_LIST] - 1, last);
761         fprintf (output, "\n");
762
763         if (next_is_valid)
764                 last = cols_next [MONO_TYPEDEF_METHOD_LIST] - 1;
765         else
766                 last = m->tables [MONO_TABLE_METHOD].rows;
767         
768         if (cols [MONO_TYPEDEF_METHOD_LIST] && cols [MONO_TYPEDEF_METHOD_LIST] <= m->tables [MONO_TABLE_METHOD].rows)
769                 dis_method_list (m, cols [MONO_TYPEDEF_METHOD_LIST] - 1, last);
770
771         dis_property_list (m, n);
772         dis_event_list (m, n);
773
774         fprintf (output, "  }\n");
775         if (*nspace)
776                 fprintf (output, "}\n");
777         fprintf (output, "\n");
778 }
779
780 /**
781  * dis_types:
782  * @m: metadata context
783  *
784  * disassembles all types in the @m context
785  */
786 static void
787 dis_types (MonoImage *m)
788 {
789         MonoTableInfo *t = &m->tables [MONO_TABLE_TYPEDEF];
790         int i;
791
792         for (i = 1; i < t->rows; i++)
793                 dis_type (m, i);
794 }
795
796 struct {
797         const char *name;
798         int table;
799         void (*dumper) (MonoImage *m);
800 } table_list [] = {
801         { "--assembly",    MONO_TABLE_ASSEMBLY,    dump_table_assembly },
802         { "--assemblyref", MONO_TABLE_ASSEMBLYREF, dump_table_assemblyref },
803         { "--fields",      MONO_TABLE_FIELD,       dump_table_field },
804         { "--memberref",   MONO_TABLE_MEMBERREF,   dump_table_memberref },
805         { "--param",       MONO_TABLE_PARAM,       dump_table_param },
806         { "--typedef",     MONO_TABLE_TYPEDEF,     dump_table_typedef },
807         { "--typeref",     MONO_TABLE_TYPEREF,     dump_table_typeref },
808         { "--nested",      MONO_TABLE_NESTEDCLASS, dump_table_nestedclass },
809         { "--interface",   MONO_TABLE_INTERFACEIMPL,     dump_table_interfaceimpl },
810         { "--classlayout", MONO_TABLE_CLASSLAYOUT, dump_table_class_layout },
811         { "--constant",    MONO_TABLE_CONSTANT,    dump_table_constant },
812         { "--customattr",  MONO_TABLE_CUSTOMATTRIBUTE,    dump_table_customattr },
813         { "--property",    MONO_TABLE_PROPERTY,    dump_table_property },
814         { "--propertymap", MONO_TABLE_PROPERTYMAP, dump_table_property_map },
815         { "--event",       MONO_TABLE_EVENT,       dump_table_event },
816         { "--file",        MONO_TABLE_FILE,        dump_table_file },
817         { "--moduleref",   MONO_TABLE_MODULEREF,   dump_table_moduleref },
818         { "--module",      MONO_TABLE_MODULE,      dump_table_module },
819         { "--method",      MONO_TABLE_METHOD,      dump_table_method },
820         { "--methodsem",   MONO_TABLE_METHODSEMANTICS,      dump_table_methodsem },
821         { NULL, -1 }
822 };
823
824 /**
825  * disassemble_file:
826  * @file: file containing CIL code.
827  *
828  * Disassembles the @file file.
829  */
830 static void
831 disassemble_file (const char *file)
832 {
833         MonoAssembly *ass;
834         MonoImageOpenStatus status;
835         MonoImage *img;
836
837         ass = mono_assembly_open (file, NULL, &status);
838         if (ass == NULL){
839                 fprintf (stderr, "Error while trying to process %s\n", file);
840                 return;
841         }
842
843         img = ass->image;
844
845         if (dump_table != -1){
846                 (*table_list [dump_table].dumper) (img);
847         } else {
848                 dump_header_data (img);
849                 
850                 dis_directive_assemblyref (img);
851                 dis_directive_assembly (img);
852                 dis_types (img);
853         }
854         
855         mono_image_close (img);
856 }
857
858 static void
859 usage (void)
860 {
861         GString *args = g_string_new ("[--help] [--mscorlib] ");
862         int i;
863         
864         for (i = 0; table_list [i].name != NULL; i++){
865                 g_string_append (args, "[");
866                 g_string_append (args, table_list [i].name);
867                 g_string_append (args, "] ");
868                 if (((i-2) % 5) == 0)
869                         g_string_append_c (args, '\n');
870         }
871         fprintf (stderr,
872                  "Usage is: monodis %s file ..\n", args->str);
873         exit (1);
874 }
875
876 int
877 main (int argc, char *argv [])
878 {
879         GList *input_files = NULL, *l;
880         int i, j;
881
882         output = stdout;
883         for (i = 1; i < argc; i++){
884                 if (argv [i][0] == '-'){
885                         if (argv [i][1] == 'h')
886                                 usage ();
887                         else if (argv [i][1] == 'd')
888                                 dump_header_data_p = TRUE;
889                         else if (strcmp (argv [i], "--mscorlib") == 0) {
890                                 substitute_with_mscorlib_p = TRUE;
891                                 i++;
892                         }
893                         else if (strcmp (argv [i], "--help") == 0)
894                                 usage ();
895                         for (j = 0; table_list [j].name != NULL; j++) {
896                                 if (strcmp (argv [i], table_list [j].name) == 0)
897                                         dump_table = j;
898                         }
899                         if (dump_table < 0)
900                                 usage ();
901                 } else
902                         input_files = g_list_append (input_files, argv [i]);
903         }
904
905         if (input_files == NULL)
906                 usage ();
907         
908         mono_init (argv [0]);
909
910         for (l = input_files; l; l = l->next)
911                 disassemble_file (l->data);
912
913         return 0;
914 }