2002-03-26 Martin Baulig <martin@gnome.org>
[mono.git] / mono / metadata / debug-symfile.c
1 #include <config.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <mono/metadata/metadata.h>
5 #include <mono/metadata/rawbuffer.h>
6 #include <mono/metadata/appdomain.h>
7 #include <mono/metadata/exception.h>
8 #include <mono/metadata/debug-symfile.h>
9
10 #include <fcntl.h>
11 #include <unistd.h>
12
13 #ifdef HAVE_ELF_H
14 #include <elf.h>
15 #endif
16
17 /* Keep in sync with Mono.CSharp.Debugger.MonoDwarfFileWriter */
18 #define MRT_none                        0x00
19 #define MRT_target_address_size         0x01
20 #define MRT_il_offset                   0x02
21 #define MRT_method_start_address        0x03
22 #define MRT_method_end_address          0x04
23 #define MRT_local_variable              0x05
24 #define MRT_method_parameter            0x06
25 #define MRT_type_sizeof                 0x07
26 #define MRT_type_field_offset           0x08
27 #define MRT_mono_string_sizeof          0x09
28 #define MRT_mono_string_offset          0x0a
29 #define MRT_mono_array_sizeof           0x0b
30 #define MRT_mono_array_offset           0x0c
31 #define MRT_mono_array_bounds_sizeof    0x0d
32 #define MRT_mono_array_bounds_offset    0x0e
33
34 #define MRI_string_offset_length        0x00
35 #define MRI_string_offset_vector        0x01
36
37 #define MRI_array_offset_bounds         0x00
38 #define MRI_array_offset_max_length     0x01
39 #define MRI_array_offset_vector         0x02
40
41 #define MRI_array_bounds_offset_lower   0x00
42 #define MRI_array_bounds_offset_length  0x01
43
44 #define MRS_debug_info                  0x01
45 #define MRS_debug_abbrev                0x02
46 #define MRS_debug_line                  0x03
47 #define MRS_mono_reloc_table            0x04
48
49 #ifdef HAVE_ELF_H
50
51 static gboolean
52 get_sections_elf32 (MonoDebugSymbolFile *symfile, gboolean emit_warnings)
53 {
54         Elf32_Ehdr *header;
55         Elf32_Shdr *section, *strtab_section;
56         const char *strtab;
57         int i;
58
59         header = symfile->raw_contents;
60         if (header->e_version != EV_CURRENT) {
61                 if (emit_warnings)
62                         g_warning ("Symbol file %s has unknown ELF version %d",
63                                    symfile->file_name, header->e_version);
64                 return FALSE;
65         }
66
67         if (header->e_machine != EM_386) {
68                 if (emit_warnings)
69                         g_warning ("ELF file %s is for unknown architecture %d",
70                                    symfile->file_name, header->e_machine);
71                 return FALSE;
72         }
73
74         if (header->e_shentsize != sizeof (*section)) {
75                 if (emit_warnings)
76                         g_warning ("ELF file %s has unknown section header size "
77                                    "(expected %d, got %d)", symfile->file_name,
78                                    sizeof (*section), header->e_shentsize);
79                 return FALSE;
80         }
81
82         symfile->section_offsets = g_new0 (MonoDebugSymbolFileSection, MONO_DEBUG_SYMBOL_SECTION_MAX);
83
84         section = symfile->raw_contents + header->e_shoff;
85         strtab_section = section + header->e_shstrndx;
86         strtab = symfile->raw_contents + strtab_section->sh_offset;
87
88         for (i = 0; i < header->e_shnum; i++, section++) {
89                 const gchar *name = strtab + section->sh_name;
90
91                 if (!strcmp (name, ".debug_info")) {
92                         MonoDebugSymbolFileSection *sfs;
93
94                         sfs = &symfile->section_offsets [MONO_DEBUG_SYMBOL_SECTION_DEBUG_INFO];
95                         sfs->type = MONO_DEBUG_SYMBOL_SECTION_DEBUG_INFO;
96                         sfs->file_offset = section->sh_offset;
97                         sfs->size = section->sh_size;
98                 } else if (!strcmp (name, ".debug_line")) {
99                         MonoDebugSymbolFileSection *sfs;
100
101                         sfs = &symfile->section_offsets [MONO_DEBUG_SYMBOL_SECTION_DEBUG_LINE];
102                         sfs->type = MONO_DEBUG_SYMBOL_SECTION_DEBUG_LINE;
103                         sfs->file_offset = section->sh_offset;
104                         sfs->size = section->sh_size;
105                 } else if (!strcmp (name, ".debug_abbrev")) {
106                         MonoDebugSymbolFileSection *sfs;
107
108                         sfs = &symfile->section_offsets [MONO_DEBUG_SYMBOL_SECTION_DEBUG_ABBREV];
109                         sfs->type = MONO_DEBUG_SYMBOL_SECTION_DEBUG_ABBREV;
110                         sfs->file_offset = section->sh_offset;
111                         sfs->size = section->sh_size;
112                 } else if (!strcmp (name, ".mono_reloc_table")) {
113                         MonoDebugSymbolFileSection *sfs;
114
115                         sfs = &symfile->section_offsets [MONO_DEBUG_SYMBOL_SECTION_MONO_RELOC_TABLE];
116                         sfs->type = MONO_DEBUG_SYMBOL_SECTION_MONO_RELOC_TABLE;
117                         sfs->file_offset = section->sh_offset;
118                         sfs->size = section->sh_size;
119                 }
120         }
121
122         return TRUE;
123 }
124
125 #endif /* HAVE_ELF_H */
126
127 static gboolean
128 get_sections (MonoDebugSymbolFile *symfile, gboolean emit_warnings)
129 {
130 #ifdef HAVE_ELF_H
131         if (!strncmp (symfile->raw_contents, ELFMAG, strlen (ELFMAG)))
132                 return get_sections_elf32 (symfile, emit_warnings);
133 #endif
134
135         if (emit_warnings)
136                 g_warning ("Symbol file %s has unknown file format", symfile->file_name);
137
138         return FALSE;
139 }
140
141 static MonoClass *
142 mono_debug_class_get (MonoDebugSymbolFile *symfile, guint32 type_token)
143 {
144         MonoClass *klass;
145         MonoAssembly **ptr;
146
147         if ((klass = g_hash_table_lookup (symfile->image->class_cache, GUINT_TO_POINTER (type_token))))
148                 return klass;
149
150         for (ptr = symfile->image->references; ptr && *ptr; ptr++) {
151                 MonoImage *image = (*ptr)->image;
152
153                 if ((klass = g_hash_table_lookup (image->class_cache, GUINT_TO_POINTER (type_token))))
154                         return klass;
155         }
156
157         return NULL;
158 }
159
160 MonoDebugSymbolFile *
161 mono_debug_open_symbol_file (MonoImage *image, const char *filename, gboolean emit_warnings)
162 {
163         MonoDebugSymbolFile *symfile;
164         off_t file_size;
165         void *ptr;
166         int fd;
167
168         fd = open (filename, O_RDWR);
169         if (fd == -1) {
170                 if (emit_warnings)
171                         g_warning ("Can't open symbol file: %s", filename);
172                 return NULL;
173         }
174
175         file_size = lseek (fd, 0, SEEK_END);
176         lseek (fd, 0, SEEK_SET);
177
178         if (file_size == (off_t) -1) {
179                 if (emit_warnings)
180                         g_warning ("Can't get size of symbol file: %s", filename);
181                 return NULL;
182         }
183
184         ptr = mono_raw_buffer_load (fd, 1, 0, file_size);
185         if (!ptr) {
186                 if (emit_warnings)
187                         g_warning ("Can't read symbol file: %s", filename);
188                 return NULL;
189         }
190
191         symfile = g_new0 (MonoDebugSymbolFile, 1);
192         symfile->fd = fd;
193         symfile->file_name = g_strdup (filename);
194         symfile->image = image;
195         symfile->raw_contents = ptr;
196         symfile->raw_contents_size = file_size;
197
198         if (!get_sections (symfile, emit_warnings)) {
199                 mono_debug_close_symbol_file (symfile);
200                 return NULL;
201         }
202
203         return symfile;
204 }
205
206 void
207 mono_debug_close_symbol_file (MonoDebugSymbolFile *symfile)
208 {
209         if (!symfile)
210                 return;
211
212         if (symfile->raw_contents)
213                 mono_raw_buffer_free (symfile->raw_contents);
214         if (symfile->fd)
215                 close (symfile->fd);
216
217         g_free (symfile->file_name);
218         g_free (symfile->section_offsets);
219         g_free (symfile);
220 }
221
222 void
223 mono_debug_update_symbol_file (MonoDebugSymbolFile *symfile,
224                                MonoDebugMethodInfoFunc method_info_func,
225                                gpointer  user_data)
226 {
227         const char *reloc_ptr, *reloc_start, *reloc_end;
228         int version, already_relocated = 0;
229         long reloc_size;
230
231         if (!symfile->section_offsets [MONO_DEBUG_SYMBOL_SECTION_MONO_RELOC_TABLE].file_offset)
232                 return;
233
234         reloc_ptr = reloc_start = symfile->raw_contents +
235                 symfile->section_offsets [MONO_DEBUG_SYMBOL_SECTION_MONO_RELOC_TABLE].file_offset;
236
237         version = *((guint16 *) reloc_ptr)++;
238         if (version != MONO_DEBUG_SYMBOL_FILE_VERSION) {
239                 g_warning ("Symbol file %s has incorrect relocation table version "
240                            "(expected %d, got %d)", symfile->file_name,
241                            MONO_DEBUG_SYMBOL_FILE_VERSION, version);
242                 return;
243         }
244
245         already_relocated = *reloc_ptr;
246         *((char *) reloc_ptr)++ = 1;
247
248         reloc_size = *((guint32 *) reloc_ptr)++;
249         reloc_end = reloc_ptr + reloc_size;
250
251         while (reloc_ptr < reloc_end) {
252                 int type, size, section, offset;
253                 const char *tmp_ptr;
254                 void *base_ptr;
255
256                 type = *reloc_ptr++;
257                 size = * ((guint32 *) reloc_ptr)++;
258
259                 tmp_ptr = reloc_ptr;
260                 reloc_ptr += size;
261
262                 section = *tmp_ptr++;
263                 offset = *((guint32 *) tmp_ptr)++;
264
265                 if (section >= MONO_DEBUG_SYMBOL_SECTION_MAX) {
266                         g_warning ("Symbol file %s contains a relocation entry for unknown section %d",
267                                    symfile->file_name, section);
268                         continue;
269                 }
270
271                 if (!symfile->section_offsets [section].file_offset) {
272                         g_warning ("Symbol file %s contains a relocation entry for non-existing "
273                                    "section %d", symfile->file_name, section);
274                         continue;
275                 }
276
277                 base_ptr = symfile->raw_contents + symfile->section_offsets [section].file_offset;
278                 base_ptr += offset;
279
280                 switch (type) {
281                 case MRT_target_address_size:
282                         * (guint8 *) base_ptr = sizeof (void *);
283                         break;
284                 case MRT_method_start_address: {
285                         int token = *((guint32 *) tmp_ptr)++;
286                         MonoDebugMethodInfo *minfo;
287
288                         minfo = method_info_func (symfile, token, user_data);
289
290                         if (!minfo) {
291                                 * (void **) base_ptr = 0;
292                                 continue;
293                         }
294
295 #if 0
296                         g_message ("Start of `%s' relocated to %p", minfo->method->name, minfo->code_start);
297 #endif
298
299                         * (void **) base_ptr = minfo->code_start;
300
301                         break;
302                 }
303                 case MRT_method_end_address: {
304                         int token = *((guint32 *) tmp_ptr)++;
305                         MonoDebugMethodInfo *minfo;
306
307                         minfo = method_info_func (symfile, token, user_data);
308
309                         if (!minfo) {
310                                 * (void **) base_ptr = 0;
311                                 continue;
312                         }
313
314                         * (void **) base_ptr = minfo->code_start + minfo->code_size;
315
316                         break;
317                 }
318                 case MRT_il_offset: {
319                         guint32 token = *((guint32 *) tmp_ptr)++;
320                         guint32 original = *((guint32 *) tmp_ptr)++;
321                         MonoDebugMethodInfo *minfo;
322                         guint32 address;
323                         int i;
324
325                         minfo = method_info_func (symfile, token, user_data);
326
327                         if (!minfo) {
328                                 * (void **) base_ptr = 0;
329                                 continue;
330                         }
331
332                         address = minfo->code_size;
333
334                         for (i = 0; i < minfo->num_il_offsets; i++) {
335                                 MonoDebugILOffsetInfo *il = &minfo->il_offsets [i];
336
337                                 if (il->offset >= original) {
338                                         address = il->address;
339                                         break;
340                                 }
341                         }
342
343 #if 0
344                         g_message ("Relocating IL offset %d in `%s' to %d (%p)",
345                                    original, minfo->method->name, address,
346                                    minfo->code_start + address);
347 #endif
348
349                         * (void **) base_ptr = minfo->code_start + address;
350
351                         break;
352                 }
353                 case MRT_local_variable: {
354                         guint32 token = *((guint32 *) tmp_ptr)++;
355                         guint32 original = *((guint32 *) tmp_ptr)++;
356                         MonoDebugMethodInfo *minfo;
357                         gint32 address;
358
359                         minfo = method_info_func (symfile, token, user_data);
360
361                         if (!minfo) {
362                                 * (void **) base_ptr = 0;
363                                 continue;
364                         }
365
366                         if (original > minfo->num_locals) {
367                                 g_warning ("Symbol file %s contains relocation entry for non-existing "
368                                            "local variable %d, but method %s only has %d local variables.",
369                                            symfile->file_name, original, minfo->method->name,
370                                            minfo->num_locals);
371                                 continue;
372                         }
373
374                         address = minfo->local_offsets [original];
375
376 #if 0
377                         g_message ("Relocating local variable %d (%s) to stack offset %d",
378                                    original, minfo->method->name, address);
379 #endif
380
381                         * (gint32 *) base_ptr = address;
382
383                         break;
384                 }
385                 case MRT_method_parameter: {
386                         guint32 token = *((guint32 *) tmp_ptr)++;
387                         guint32 original = *((guint32 *) tmp_ptr)++;
388                         MonoDebugMethodInfo *minfo;
389                         gint32 address;
390
391                         minfo = method_info_func (symfile, token, user_data);
392
393                         if (!minfo) {
394                                 * (void **) base_ptr = 0;
395                                 continue;
396                         }
397
398                         if (minfo->method->signature->hasthis) {
399                                 if (original == 0) {
400                                         * (gint32 *) base_ptr = minfo->this_offset;
401                                         continue;
402                                 }
403
404                                 original--;
405                         }
406
407                         if (original > minfo->num_params) {
408                                 g_warning ("Symbol file %s contains relocation entry for non-existing "
409                                            "parameter %d, but method %s only has %d parameters.",
410                                            symfile->file_name, original, minfo->method->name,
411                                            minfo->num_params);
412                                 continue;
413                         }
414
415                         address = minfo->param_offsets [original];
416
417 #if 0
418                         g_message ("Relocating parameter %d (%s) to stack offset %d",
419                                    original, minfo->method->name, address);
420 #endif
421
422                         * (gint32 *) base_ptr = address;
423
424                         break;
425                 }
426                 case MRT_type_sizeof: {
427                         guint32 token = *((guint32 *) tmp_ptr)++;
428                         MonoClass *klass = mono_debug_class_get (symfile, token);
429
430                         if (!klass || !klass->inited)
431                                 continue;
432
433 #if 0
434                         g_message ("Setting size of type %u to %d", token, klass->instance_size);
435 #endif
436
437                         * (gint8 *) base_ptr = klass->instance_size;
438
439                         break;
440                 }
441                 case MRT_type_field_offset: {
442                         guint32 token = *((guint32 *) tmp_ptr)++;
443                         guint32 original = *((guint32 *) tmp_ptr)++;
444                         MonoClass *klass = mono_debug_class_get (symfile, token);
445                         guint32 offset;
446
447                         if (!klass || !klass->inited)
448                                 continue;
449
450                         if (original > klass->field.count) {
451                                 g_warning ("Symbol file %s contains invalid field offset entry.",
452                                            symfile->file_name);
453                                 continue;
454                         }
455
456                         offset = klass->fields [original].offset;
457                         if (klass->byval_arg.type == MONO_TYPE_VALUETYPE)
458                                 offset -= sizeof (MonoObject);
459
460 #if 0
461                         g_message ("Setting field %d of type %u to offset %d", original,
462                                    token, offset);
463 #endif
464
465                         * (guint32 *) base_ptr = offset;
466
467                         break;
468                 }
469                 case MRT_mono_string_sizeof:
470                         * (gint8 *) base_ptr = sizeof (MonoString);
471                         break;
472
473                 case MRT_mono_string_offset: {
474                         guint32 index = *((guint32 *) tmp_ptr)++;
475                         MonoString string;
476                         guint32 offset;
477
478                         switch (index) {
479                         case MRI_string_offset_length:
480                                 offset = (guchar *) &string.length - (guchar *) &string;
481                                 break;
482
483                         case MRI_string_offset_vector:
484                                 offset = (guchar *) &string.c_str - (guchar *) &string;
485                                 break;
486
487                         default:
488                                 g_warning ("Symbol file %s contains invalid string offset entry",
489                                            symfile->file_name);
490                                 continue;
491                         }
492
493                         * (guint32 *) base_ptr = offset;
494
495                         break;
496                 }
497                 case MRT_mono_array_sizeof:
498                         * (gint8 *) base_ptr = sizeof (MonoArray);
499                         break;
500
501                 case MRT_mono_array_offset: {
502                         guint32 index = *((guint32 *) tmp_ptr)++;
503                         MonoArray array;
504                         guint32 offset;
505
506                         switch (index) {
507                         case MRI_array_offset_bounds:
508                                 offset = (guchar *) &array.bounds - (guchar *) &array;
509                                 break;
510
511                         case MRI_array_offset_max_length:
512                                 offset = (guchar *) &array.max_length - (guchar *) &array;
513                                 break;
514
515                         case MRI_array_offset_vector:
516                                 offset = (guchar *) &array.vector - (guchar *) &array;
517                                 break;
518
519                         default:
520                                 g_warning ("Symbol file %s contains invalid array offset entry",
521                                            symfile->file_name);
522                                 continue;
523                         }
524
525                         * (guint32 *) base_ptr = offset;
526
527                         break;
528                 }
529
530                 case MRT_mono_array_bounds_sizeof:
531                         * (gint8 *) base_ptr = sizeof (MonoArrayBounds);
532                         break;
533
534                 case MRT_mono_array_bounds_offset: {
535                         guint32 index = *((guint32 *) tmp_ptr)++;
536                         MonoArrayBounds bounds;
537                         guint32 offset;
538
539                         switch (index) {
540                         case MRI_array_bounds_offset_lower:
541                                 offset = (guchar *) &bounds.lower_bound - (guchar *) &bounds;
542                                 break;
543
544                         case MRI_array_bounds_offset_length:
545                                 offset = (guchar *) &bounds.length - (guchar *) &bounds;
546                                 break;
547
548                         default:
549                                 g_warning ("Symbol file %s contains invalid array bounds offset entry",
550                                            symfile->file_name);
551                                 continue;
552                         }
553
554                         * (guint32 *) base_ptr = offset;
555
556                         break;
557                 }
558
559                 default:
560                         g_warning ("Symbol file %s contains unknown relocation entry %d",
561                                    symfile->file_name, type);
562                         break;
563                 }
564         }
565
566         mono_raw_buffer_update (symfile->raw_contents, symfile->raw_contents_size);
567 }
568
569 MonoReflectionType *
570 ves_icall_Debugger_MonoSymbolWriter_get_local_type_from_sig (MonoReflectionAssembly *assembly,
571                                                              MonoArray *signature)
572 {
573         MonoDomain *domain; 
574         MonoImage *image;
575         MonoClass *klass;
576         MonoType *type;
577         const char *ptr;
578         int len = 0;
579
580         MONO_CHECK_ARG_NULL (assembly);
581         MONO_CHECK_ARG_NULL (signature);
582
583         domain = mono_domain_get();
584         image = assembly->assembly->image;
585
586         ptr = mono_array_addr (signature, char, 0);
587         g_assert (*ptr++ == 0x07);
588         len = mono_metadata_decode_value (ptr, &ptr);
589         g_assert (len == 1);
590
591         type = mono_metadata_parse_type (image, MONO_PARSE_LOCAL, 0, ptr, &ptr);
592
593         klass = mono_class_from_mono_type (type);
594
595         mono_class_init (klass);
596
597         return mono_type_get_object (domain, type);
598 }
599
600 MonoReflectionMethod *
601 ves_icall_Debugger_MonoSymbolWriter_method_from_token (MonoReflectionAssembly *assembly, guint32 token)
602 {
603         MonoDomain *domain; 
604         MonoImage *image;
605         MonoMethod *method;
606
607         MONO_CHECK_ARG_NULL (assembly);
608
609         domain = mono_domain_get();
610         image = assembly->assembly->image;
611
612         method = mono_get_method (image, token, NULL);
613
614         return mono_method_get_object (domain, method);
615 }
616
617 guint32
618 ves_icall_Debugger_DwarfFileWriter_get_type_token (MonoReflectionType *type)
619 {
620         MonoClass *klass = mono_class_from_mono_type (type->type);
621
622         mono_class_init (klass);
623
624         return klass->type_token;
625 }