2006-09-27 Zoltan Varga <vargaz@gmail.com>
[mono.git] / mono / metadata / debug-helpers.c
1
2 #include <string.h>
3 #include "mono/metadata/tokentype.h"
4 #include "mono/metadata/opcodes.h"
5 #include "mono/metadata/class-internals.h"
6 #include "mono/metadata/mono-endian.h"
7 #include "mono/metadata/debug-helpers.h"
8 #include "mono/metadata/tabledefs.h"
9 #include "mono/metadata/appdomain.h"
10
11 struct MonoMethodDesc {
12         char *namespace;
13         char *klass;
14         char *name;
15         char *args;
16         guint num_args;
17         gboolean include_namespace;
18 };
19
20 static const char *wrapper_type_names [] = {
21         "none",
22         "delegate-invoke",
23         "delegate-begin-invoke",
24         "delegate-end-invoke",
25         "runtime-invoke",
26         "native-to-managed",
27         "managed-to-native",
28         "managed-to-managed",
29         "remoting-invoke",
30         "remoting-invoke-with-check",
31         "xdomain-invoke",
32         "xdomain-dispatch",
33         "ldfld",
34         "stfld",
35         "ldfld-remote",
36         "stfld-remote",
37         "synchronized",
38         "dynamic-method",
39         "isinst",
40         "cancast",
41         "proxy_isinst",
42         "stelemref",
43         "unbox",
44         "ldflda",
45         "write-barrier",
46         "unknown",
47         "cominterop-invoke",
48         "cominterop"
49 };
50
51 static void
52 append_class_name (GString *res, MonoClass *class, gboolean include_namespace)
53 {
54         if (!class) {
55                 g_string_append (res, "Unknown");
56                 return;
57         }
58         if (class->nested_in) {
59                 append_class_name (res, class->nested_in, include_namespace);
60                 g_string_append_c (res, '/');
61         }
62         if (include_namespace && *(class->name_space))
63                 g_string_sprintfa (res, "%s.", class->name_space);
64         g_string_sprintfa (res, "%s", class->name);
65 }
66
67 void
68 mono_type_get_desc (GString *res, MonoType *type, gboolean include_namespace) {
69         switch (type->type) {
70         case MONO_TYPE_VOID:
71                 g_string_append (res, "void"); break;
72         case MONO_TYPE_CHAR:
73                 g_string_append (res, "char"); break;
74         case MONO_TYPE_BOOLEAN:
75                 g_string_append (res, "bool"); break;
76         case MONO_TYPE_U1:
77                 g_string_append (res, "byte"); break;
78         case MONO_TYPE_I1:
79                 g_string_append (res, "sbyte"); break;
80         case MONO_TYPE_U2:
81                 g_string_append (res, "uint16"); break;
82         case MONO_TYPE_I2:
83                 g_string_append (res, "int16"); break;
84         case MONO_TYPE_U4:
85                 g_string_append (res, "uint"); break;
86         case MONO_TYPE_I4:
87                 g_string_append (res, "int"); break;
88         case MONO_TYPE_U8:
89                 g_string_append (res, "ulong"); break;
90         case MONO_TYPE_I8:
91                 g_string_append (res, "long"); break;
92         case MONO_TYPE_FNPTR: /* who cares for the exact signature? */
93                 g_string_append (res, "*()"); break;
94         case MONO_TYPE_U:
95                 g_string_append (res, "uintptr"); break;
96         case MONO_TYPE_I:
97                 g_string_append (res, "intptr"); break;
98         case MONO_TYPE_R4:
99                 g_string_append (res, "single"); break;
100         case MONO_TYPE_R8:
101                 g_string_append (res, "double"); break;
102         case MONO_TYPE_STRING:
103                 g_string_append (res, "string"); break;
104         case MONO_TYPE_OBJECT:
105                 g_string_append (res, "object"); break;
106         case MONO_TYPE_PTR:
107                 mono_type_get_desc (res, type->data.type, include_namespace);
108                 g_string_append_c (res, '*');
109                 break;
110         case MONO_TYPE_ARRAY:
111                 append_class_name (res, type->data.array->eklass, include_namespace);
112                 g_string_sprintfa (res, "[%d]", type->data.array->rank);
113                 break;
114         case MONO_TYPE_SZARRAY:
115                 mono_type_get_desc (res, &type->data.klass->byval_arg, include_namespace);
116                 g_string_append (res, "[]");
117                 break;
118         case MONO_TYPE_CLASS:
119         case MONO_TYPE_VALUETYPE:
120                 append_class_name (res, type->data.klass, include_namespace);
121                 break;
122         case MONO_TYPE_GENERICINST:
123                 mono_type_get_desc (res, &type->data.generic_class->container_class->byval_arg, include_namespace);
124                 break;
125         case MONO_TYPE_VAR:
126         case MONO_TYPE_MVAR:
127                 g_string_append (res, type->data.generic_param->name);
128                 break;
129         default:
130                 break;
131         }
132         if (type->byref)
133                 g_string_append_c (res, '&');
134 }
135
136 char*
137 mono_type_full_name (MonoType *type)
138 {
139         GString *str;
140         char *res;
141
142         str = g_string_new ("");
143         mono_type_get_desc (str, type, TRUE);
144
145         res = g_strdup (str->str);
146         g_string_free (str, TRUE);
147         return res;
148 }
149
150 char*
151 mono_signature_get_desc (MonoMethodSignature *sig, gboolean include_namespace)
152 {
153         int i;
154         char *result;
155         GString *res = g_string_new ("");
156
157         for (i = 0; i < sig->param_count; ++i) {
158                 if (i > 0)
159                         g_string_append_c (res, ',');
160                 mono_type_get_desc (res, sig->params [i], include_namespace);
161         }
162         result = res->str;
163         g_string_free (res, FALSE);
164         return result;
165 }
166
167 /**
168  * mono_method_desc_new:
169  * @name: the method name.
170  * @include_namespace: whether the name includes a namespace or not.
171  *
172  * Creates a method description for @name, which conforms to the following
173  * specification:
174  *
175  * [namespace.]classname:methodname[(args...)]
176  *
177  * in all the loaded assemblies.
178  *
179  * Returns: a parsed representation of the method description.
180  */
181 MonoMethodDesc*
182 mono_method_desc_new (const char *name, gboolean include_namespace)
183 {
184         MonoMethodDesc *result;
185         char *class_name, *class_nspace, *method_name, *use_args, *end;
186         int use_namespace;
187         
188         class_nspace = g_strdup (name);
189         use_args = strchr (class_nspace, '(');
190         if (use_args) {
191                 *use_args++ = 0;
192                 end = strchr (use_args, ')');
193                 if (!end) {
194                         g_free (class_nspace);
195                         return NULL;
196                 }
197                 *end = 0;
198         }
199         method_name = strrchr (class_nspace, ':');
200         if (!method_name) {
201                 g_free (class_nspace);
202                 return NULL;
203         }
204         *method_name++ = 0;
205         /* allow two :: to separate the method name */
206         if (*method_name == ':')
207                 method_name++;
208         class_name = strrchr (class_nspace, '.');
209         if (class_name) {
210                 *class_name++ = 0;
211                 use_namespace = 1;
212         } else {
213                 class_name = class_nspace;
214                 use_namespace = 0;
215         }
216         result = g_new0 (MonoMethodDesc, 1);
217         result->include_namespace = include_namespace;
218         result->name = method_name;
219         result->klass = class_name;
220         result->namespace = use_namespace? class_nspace: NULL;
221         result->args = use_args? use_args: NULL;
222         if (use_args) {
223                 end = use_args;
224                 if (*end)
225                         result->num_args = 1;
226                 while (*end) {
227                         if (*end == ',')
228                                 result->num_args++;
229                         ++end;
230                 }
231         }
232
233         return result;
234 }
235
236 MonoMethodDesc*
237 mono_method_desc_from_method (MonoMethod *method)
238 {
239         MonoMethodDesc *result;
240         
241         result = g_new0 (MonoMethodDesc, 1);
242         result->include_namespace = TRUE;
243         result->name = g_strdup (method->name);
244         result->klass = g_strdup (method->klass->name);
245         result->namespace = g_strdup (method->klass->name_space);
246
247         return result;
248 }
249
250 /**
251  * mono_method_desc_free:
252  * @desc: method description to be released
253  *
254  * Releases the MonoMethodDesc object @desc.
255  */
256 void
257 mono_method_desc_free (MonoMethodDesc *desc)
258 {
259         if (desc->namespace)
260                 g_free (desc->namespace);
261         else if (desc->klass)
262                 g_free (desc->klass);
263         g_free (desc);
264 }
265
266 /*
267  * namespace and class are supposed to match already if this function is used.
268  */
269 gboolean
270 mono_method_desc_match (MonoMethodDesc *desc, MonoMethod *method)
271 {
272         char *sig;
273         if (strcmp (desc->name, method->name))
274                 return FALSE;
275         if (!desc->args)
276                 return TRUE;
277         if (desc->num_args != mono_method_signature (method)->param_count)
278                 return FALSE;
279         sig = mono_signature_get_desc (mono_method_signature (method), desc->include_namespace);
280         if (strcmp (sig, desc->args)) {
281                 g_free (sig);
282                 return FALSE;
283         }
284         g_free (sig);
285         return TRUE;
286 }
287
288 gboolean
289 mono_method_desc_full_match (MonoMethodDesc *desc, MonoMethod *method)
290 {
291         if (strcmp (desc->klass, method->klass->name))
292                 return FALSE;
293         if (desc->namespace && strcmp (desc->namespace, method->klass->name_space))
294                 return FALSE;
295         return mono_method_desc_match (desc, method);
296 }
297
298 MonoMethod*
299 mono_method_desc_search_in_class (MonoMethodDesc *desc, MonoClass *klass)
300 {
301         MonoMethod* m;
302         gpointer iter = NULL;
303         
304         while ((m = mono_class_get_methods (klass, &iter)))
305                 if (mono_method_desc_match (desc, m))
306                         return m;
307         return NULL;
308 }
309
310 MonoMethod*
311 mono_method_desc_search_in_image (MonoMethodDesc *desc, MonoImage *image)
312 {
313         MonoClass *klass;
314         const MonoTableInfo *tdef;
315         const MonoTableInfo *methods;
316         MonoMethod *method;
317         int i;
318
319         if (desc->namespace && desc->klass) {
320                 klass = mono_class_from_name (image, desc->namespace, desc->klass);
321                 if (!klass)
322                         return NULL;
323                 return mono_method_desc_search_in_class (desc, klass);
324         }
325
326         tdef = mono_image_get_table_info (image, MONO_TABLE_TYPEDEF);
327         methods = mono_image_get_table_info (image, MONO_TABLE_METHOD);
328         for (i = 0; i < mono_table_info_get_rows (methods); ++i) {
329                 guint32 token = mono_metadata_decode_row_col (methods, i, MONO_METHOD_NAME);
330                 const char *n = mono_metadata_string_heap (image, token);
331
332                 if (strcmp (n, desc->name))
333                         continue;
334                 method = mono_get_method (image, MONO_TOKEN_METHOD_DEF | (i + 1), NULL);
335                 if (mono_method_desc_full_match (desc, method))
336                         return method;
337         }
338         return NULL;
339 }
340
341 static const unsigned char*
342 dis_one (GString *str, MonoDisHelper *dh, MonoMethod *method, const unsigned char *ip, const unsigned char *end)
343 {
344         MonoMethodHeader *header = mono_method_get_header (method);
345         const MonoOpcode *opcode;
346         guint32 label, token;
347         gint32 sval;
348         int i;
349         char *tmp;
350         const unsigned char* il_code = mono_method_header_get_code (header, NULL, NULL);
351
352         label = ip - il_code;
353         if (dh->indenter) {
354                 tmp = dh->indenter (dh, method, label);
355                 g_string_append (str, tmp);
356                 g_free (tmp);
357         }
358         if (dh->label_format)
359                 g_string_sprintfa (str, dh->label_format, label);
360         
361         i = mono_opcode_value (&ip, end);
362         ip++;
363         opcode = &mono_opcodes [i];
364         g_string_sprintfa (str, "%-10s", mono_opcode_name (i));
365
366         switch (opcode->argument) {
367         case MonoInlineNone:
368                 break;
369         case MonoInlineType:
370         case MonoInlineField:
371         case MonoInlineMethod:
372         case MonoInlineTok:
373         case MonoInlineSig:
374                 token = read32 (ip);
375                 if (dh->tokener) {
376                         tmp = dh->tokener (dh, method, token);
377                         g_string_append (str, tmp);
378                         g_free (tmp);
379                 } else {
380                         g_string_sprintfa (str, "0x%08x", token);
381                 }
382                 ip += 4;
383                 break;
384         case MonoInlineString:
385                 /* TODO */
386                 ip += 4;
387                 break;
388         case MonoInlineVar:
389                 g_string_sprintfa (str, "%d", read16 (ip));
390                 ip += 2;
391                 break;
392         case MonoShortInlineVar:
393                 g_string_sprintfa (str, "%d", (*ip));
394                 ip ++;
395                 break;
396         case MonoInlineBrTarget:
397                 sval = read32 (ip);
398                 ip += 4;
399                 if (dh->label_target)
400                         g_string_sprintfa (str, dh->label_target, ip + sval - il_code);
401                 else
402                         g_string_sprintfa (str, "%d", sval);
403                 break;
404         case MonoShortInlineBrTarget:
405                 sval = *(const signed char*)ip;
406                 ip ++;
407                 if (dh->label_target)
408                         g_string_sprintfa (str, dh->label_target, ip + sval - il_code);
409                 else
410                         g_string_sprintfa (str, "%d", sval);
411                 break;
412         case MonoInlineSwitch: {
413                 const unsigned char *end;
414                 sval = read32 (ip);
415                 ip += 4;
416                 end = ip + sval * 4;
417                 g_string_append_c (str, '(');
418                 for (i = 0; i < sval; ++i) {
419                         if (i > 0)
420                                 g_string_append (str, ", ");
421                         label = read32 (ip);
422                         if (dh->label_target)
423                                 g_string_sprintfa (str, dh->label_target, end + label - il_code);
424                         else
425                                 g_string_sprintfa (str, "%d", label);
426                         ip += 4;
427                 }
428                 g_string_append_c (str, ')');
429                 break;
430         }
431         case MonoInlineR: {
432                 double r;
433                 readr8 (ip, &r);
434                 g_string_sprintfa (str, "%g", r);
435                 ip += 8;
436                 break;
437         }
438         case MonoShortInlineR: {
439                 float r;
440                 readr4 (ip, &r);
441                 g_string_sprintfa (str, "%g", r);
442                 ip += 4;
443                 break;
444         }
445         case MonoInlineI:
446                 g_string_sprintfa (str, "%d", (gint32)read32 (ip));
447                 ip += 4;
448                 break;
449         case MonoShortInlineI:
450                 g_string_sprintfa (str, "%d", *(const signed char*)ip);
451                 ip ++;
452                 break;
453         case MonoInlineI8:
454                 ip += 8;
455                 break;
456         default:
457                 g_assert_not_reached ();
458         }
459         if (dh->newline)
460                 g_string_append (str, dh->newline);
461
462         return ip;
463 }
464
465 static MonoDisHelper
466 default_dh = {
467         "\n",
468         "IL_%04x: ", /* label_format */
469         "IL_%04x", /* label_target */
470         NULL, /* indenter */
471         NULL, /* tokener */
472         NULL  /* user data */
473 };
474
475 char*
476 mono_disasm_code_one (MonoDisHelper *dh, MonoMethod *method, const guchar *ip, const guchar **endp)
477 {
478         char *result;
479         GString *res = g_string_new ("");
480
481         if (!dh)
482                 dh = &default_dh;
483         /* set ip + 2 as the end: this is just a debugging method */
484         ip = dis_one (res, dh, method, ip, ip + 2);
485         if (endp)
486                 *endp = ip;
487         
488         result = res->str;
489         g_string_free (res, FALSE);
490         return result;
491 }
492
493 char*
494 mono_disasm_code (MonoDisHelper *dh, MonoMethod *method, const guchar *ip, const guchar* end)
495 {
496         char *result;
497         GString *res = g_string_new ("");
498
499         if (!dh)
500                 dh = &default_dh;
501         while (ip < end) {
502                 ip = dis_one (res, dh, method, ip, end);
503         }
504         
505         result = res->str;
506         g_string_free (res, FALSE);
507         return result;
508 }
509
510 static const char*
511 wrapper_type_to_str (guint32 wrapper_type)
512 {
513         g_assert (wrapper_type < sizeof (wrapper_type_names) / sizeof (char*));
514
515         return wrapper_type_names [wrapper_type];
516 }
517
518 char *
519 mono_method_full_name (MonoMethod *method, gboolean signature)
520 {
521         char *res;
522         char wrapper [64];
523         const char *nspace = method->klass->name_space;
524
525         if (signature) {
526                 char *tmpsig = mono_signature_get_desc (mono_method_signature (method), TRUE);
527
528                 if (method->wrapper_type != MONO_WRAPPER_NONE)
529                         sprintf (wrapper, "(wrapper %s) ", wrapper_type_to_str (method->wrapper_type));
530                 else
531                         strcpy (wrapper, "");
532                 res = g_strdup_printf ("%s%s%s%s:%s (%s)", wrapper, 
533                                                            nspace, *nspace ? "." : "",
534                                                            method->klass->name, method->name, tmpsig);
535                 g_free (tmpsig);
536         } else {
537
538                 res = g_strdup_printf ("%02d %s%s%s:%s", method->wrapper_type,
539                                                            nspace, *nspace ? "." : "",
540                                                            method->klass->name, method->name);
541         }
542
543         return res;
544 }
545
546 static const char*
547 print_name_space (MonoClass *klass)
548 {
549         if (klass->nested_in) {
550                 print_name_space (klass->nested_in);
551                 g_print (klass->nested_in->name);
552                 return "/";
553         }
554         if (klass->name_space [0]) {
555                 g_print (klass->name_space);
556                 return ".";
557         }
558         return "";
559 }
560
561 /**
562  * mono_object_describe:
563  *
564  * Prints to stdout a small description of the object @obj.
565  * For use in a debugger.
566  */
567 void
568 mono_object_describe (MonoObject *obj)
569 {
570         MonoClass* klass;
571         const char* sep;
572         if (!obj) {
573                 g_print ("(null)\n");
574                 return;
575         }
576         klass = mono_object_class (obj);
577         if (klass == mono_defaults.string_class) {
578                 char *utf8 = mono_string_to_utf8 ((MonoString*)obj);
579                 if (strlen (utf8) > 60) {
580                         utf8 [57] = '.';
581                         utf8 [58] = '.';
582                         utf8 [59] = '.';
583                         utf8 [60] = 0;
584                 }
585                 g_print ("String at %p, length: %d, '%s'\n", obj, mono_string_length ((MonoString*) obj), utf8);
586                 g_free (utf8);
587         } else if (klass->rank) {
588                 MonoArray *array = (MonoArray*)obj;
589                 sep = print_name_space (klass);
590                 g_print ("%s%s", sep, klass->name);
591                 g_print (" at %p, rank: %d, length: %d\n", obj, klass->rank, mono_array_length (array));
592         } else {
593                 sep = print_name_space (klass);
594                 g_print ("%s%s", sep, klass->name);
595                 g_print (" object at %p (klass: %p)\n", obj, klass);
596         }
597
598 }
599
600 static void
601 print_field_value (const char *field_ptr, MonoClassField *field, int type_offset)
602 {
603         MonoType *type;
604         g_print ("At %p (ofs: %2d) %s: ", field_ptr, field->offset + type_offset, field->name);
605         type = mono_type_get_underlying_type (field->type);
606
607         switch (type->type) {
608         case MONO_TYPE_I:
609         case MONO_TYPE_U:
610         case MONO_TYPE_PTR:
611         case MONO_TYPE_FNPTR:
612                 g_print ("%p\n", *(const void**)field_ptr);
613                 break;
614         case MONO_TYPE_STRING:
615         case MONO_TYPE_SZARRAY:
616         case MONO_TYPE_CLASS:
617         case MONO_TYPE_OBJECT:
618         case MONO_TYPE_ARRAY:
619                 mono_object_describe (*(MonoObject**)field_ptr);
620                 break;
621         case MONO_TYPE_GENERICINST:
622                 if (!mono_type_generic_inst_is_valuetype (type)) {
623                         mono_object_describe (*(MonoObject**)field_ptr);
624                         break;
625                 } else {
626                         /* fall through */
627                 }
628         case MONO_TYPE_VALUETYPE: {
629                 MonoClass *k = mono_class_from_mono_type (type);
630                 g_print ("%s ValueType (type: %p) at %p\n", k->name, k, field_ptr);
631                 break;
632         }
633         case MONO_TYPE_I1:
634                 g_print ("%d\n", *(gint8*)field_ptr);
635                 break;
636         case MONO_TYPE_U1:
637                 g_print ("%d\n", *(guint8*)field_ptr);
638                 break;
639         case MONO_TYPE_I2:
640                 g_print ("%d\n", *(gint16*)field_ptr);
641                 break;
642         case MONO_TYPE_U2:
643                 g_print ("%d\n", *(guint16*)field_ptr);
644                 break;
645         case MONO_TYPE_I4:
646                 g_print ("%d\n", *(gint32*)field_ptr);
647                 break;
648         case MONO_TYPE_U4:
649                 g_print ("%u\n", *(guint32*)field_ptr);
650                 break;
651         case MONO_TYPE_I8:
652                 g_print ("%lld\n", *(gint64*)field_ptr);
653                 break;
654         case MONO_TYPE_U8:
655                 g_print ("%llu\n", *(guint64*)field_ptr);
656                 break;
657         case MONO_TYPE_R4:
658                 g_print ("%f\n", *(gfloat*)field_ptr);
659                 break;
660         case MONO_TYPE_R8:
661                 g_print ("%f\n", *(gdouble*)field_ptr);
662                 break;
663         case MONO_TYPE_BOOLEAN:
664                 g_print ("%s (%d)\n", *(guint8*)field_ptr? "True": "False", *(guint8*)field_ptr);
665                 break;
666         case MONO_TYPE_CHAR:
667                 g_print ("'%c' (%d 0x%04x)\n", *(guint16*)field_ptr, *(guint16*)field_ptr, *(guint16*)field_ptr);
668                 break;
669         default:
670                 g_assert_not_reached ();
671                 break;
672         }
673 }
674
675 static void
676 objval_describe (MonoClass *class, const char *addr)
677 {
678         MonoClassField *field;
679         MonoClass *p;
680         const char *field_ptr;
681         int type_offset = 0;
682         if (class->valuetype)
683                 type_offset = -sizeof (MonoObject);
684
685         for (p = class; p != NULL; p = p->parent) {
686                 gpointer iter = NULL;
687                 int printed_header = FALSE;
688                 while ((field = mono_class_get_fields (p, &iter))) {
689                         if (field->type->attrs & (FIELD_ATTRIBUTE_STATIC | FIELD_ATTRIBUTE_HAS_FIELD_RVA))
690                                 continue;
691
692                         if (p != class && !printed_header) {
693                                 const char *sep;
694                                 g_print ("In class ");
695                                 sep = print_name_space (p);
696                                 g_print ("%s%s:\n", sep, p->name);
697                                 printed_header = TRUE;
698                         }
699                         field_ptr = (const char*)addr + field->offset + type_offset;
700
701                         print_field_value (field_ptr, field, type_offset);
702                 }
703         }
704 }
705
706 /**
707  * mono_object_describe_fields:
708  *
709  * Prints to stdout a small description of each field of the object @obj.
710  * For use in a debugger.
711  */
712 void
713 mono_object_describe_fields (MonoObject *obj)
714 {
715         MonoClass *class = mono_object_class (obj);
716         objval_describe (class, (char*)obj);
717 }
718
719 /**
720  * mono_value_describe_fields:
721  *
722  * Prints to stdout a small description of each field of the value type
723  * stored at @addr of type @klass.
724  * For use in a debugger.
725  */
726 void
727 mono_value_describe_fields (MonoClass* klass, const char* addr)
728 {
729         objval_describe (klass, addr);
730 }
731
732 /**
733  * mono_class_describe_statics:
734  *
735  * Prints to stdout a small description of each static field of the type @klass
736  * in the current application domain.
737  * For use in a debugger.
738  */
739 void
740 mono_class_describe_statics (MonoClass* klass)
741 {
742         MonoClassField *field;
743         MonoClass *p;
744         const char *field_ptr;
745         const char *addr = mono_class_vtable (mono_domain_get (), klass)->data;
746         if (!addr)
747                 return;
748
749         for (p = klass; p != NULL; p = p->parent) {
750                 gpointer iter = NULL;
751                 while ((field = mono_class_get_fields (p, &iter))) {
752                         if (field->type->attrs & FIELD_ATTRIBUTE_LITERAL)
753                                 continue;
754                         if (!(field->type->attrs & (FIELD_ATTRIBUTE_STATIC | FIELD_ATTRIBUTE_HAS_FIELD_RVA)))
755                                 continue;
756
757                         field_ptr = (const char*)addr + field->offset;
758
759                         print_field_value (field_ptr, field, 0);
760                 }
761         }
762 }
763