2002-03-25 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 (original > minfo->num_params) {
399                                 g_warning ("Symbol file %s contains relocation entry for non-existing "
400                                            "parameter %d, but method %s only has %d parameters.",
401                                            symfile->file_name, original, minfo->method->name,
402                                            minfo->num_params);
403                                 continue;
404                         }
405
406                         address = minfo->param_offsets [original];
407
408 #if 0
409                         g_message ("Relocating parameter %d (%s) to stack offset %d",
410                                    original, minfo->method->name, address);
411 #endif
412
413                         * (gint32 *) base_ptr = address;
414
415                         break;
416                 }
417                 case MRT_type_sizeof: {
418                         guint32 token = *((guint32 *) tmp_ptr)++;
419                         MonoClass *klass = mono_debug_class_get (symfile, token);
420
421                         if (!klass || !klass->inited)
422                                 continue;
423
424                         g_message ("Setting size of type %u to %d", token, klass->instance_size);
425
426                         * (gint8 *) base_ptr = klass->instance_size;
427
428                         break;
429                 }
430                 case MRT_type_field_offset: {
431                         guint32 token = *((guint32 *) tmp_ptr)++;
432                         guint32 original = *((guint32 *) tmp_ptr)++;
433                         MonoClass *klass = mono_debug_class_get (symfile, token);
434                         guint32 offset;
435
436                         if (!klass || !klass->inited)
437                                 continue;
438
439                         if (original > klass->field.count) {
440                                 g_warning ("Symbol file %s contains invalid field offset entry.",
441                                            symfile->file_name);
442                                 continue;
443                         }
444
445                         offset = klass->fields [original].offset;
446                         if (klass->byval_arg.type == MONO_TYPE_VALUETYPE)
447                                 offset -= sizeof (MonoObject);
448
449                         g_message ("Setting field %d of type %u to offset %d", original,
450                                    token, offset);
451
452                         * (guint32 *) base_ptr = offset;
453
454                         break;
455                 }
456                 case MRT_mono_string_sizeof:
457                         * (gint8 *) base_ptr = sizeof (MonoString);
458                         break;
459
460                 case MRT_mono_string_offset: {
461                         guint32 index = *((guint32 *) tmp_ptr)++;
462                         MonoString string;
463                         guint32 offset;
464
465                         switch (index) {
466                         case MRI_string_offset_length:
467                                 offset = (guchar *) &string.length - (guchar *) &string;
468                                 break;
469
470                         case MRI_string_offset_vector:
471                                 offset = (guchar *) &string.c_str - (guchar *) &string;
472                                 break;
473
474                         default:
475                                 g_warning ("Symbol file %s contains invalid string offset entry",
476                                            symfile->file_name);
477                                 continue;
478                         }
479
480                         * (guint32 *) base_ptr = offset;
481
482                         break;
483                 }
484                 case MRT_mono_array_sizeof:
485                         * (gint8 *) base_ptr = sizeof (MonoArray);
486                         break;
487
488                 case MRT_mono_array_offset: {
489                         guint32 index = *((guint32 *) tmp_ptr)++;
490                         MonoArray array;
491                         guint32 offset;
492
493                         switch (index) {
494                         case MRI_array_offset_bounds:
495                                 offset = (guchar *) &array.bounds - (guchar *) &array;
496                                 break;
497
498                         case MRI_array_offset_max_length:
499                                 offset = (guchar *) &array.max_length - (guchar *) &array;
500                                 break;
501
502                         case MRI_array_offset_vector:
503                                 offset = (guchar *) &array.vector - (guchar *) &array;
504                                 break;
505
506                         default:
507                                 g_warning ("Symbol file %s contains invalid array offset entry",
508                                            symfile->file_name);
509                                 continue;
510                         }
511
512                         * (guint32 *) base_ptr = offset;
513
514                         break;
515                 }
516
517                 case MRT_mono_array_bounds_sizeof:
518                         * (gint8 *) base_ptr = sizeof (MonoArrayBounds);
519                         break;
520
521                 case MRT_mono_array_bounds_offset: {
522                         guint32 index = *((guint32 *) tmp_ptr)++;
523                         MonoArrayBounds bounds;
524                         guint32 offset;
525
526                         switch (index) {
527                         case MRI_array_bounds_offset_lower:
528                                 offset = (guchar *) &bounds.lower_bound - (guchar *) &bounds;
529                                 break;
530
531                         case MRI_array_bounds_offset_length:
532                                 offset = (guchar *) &bounds.length - (guchar *) &bounds;
533                                 break;
534
535                         default:
536                                 g_warning ("Symbol file %s contains invalid array bounds offset entry",
537                                            symfile->file_name);
538                                 continue;
539                         }
540
541                         * (guint32 *) base_ptr = offset;
542
543                         break;
544                 }
545
546                 default:
547                         g_warning ("Symbol file %s contains unknown relocation entry %d",
548                                    symfile->file_name, type);
549                         break;
550                 }
551         }
552
553         mono_raw_buffer_update (symfile->raw_contents, symfile->raw_contents_size);
554 }
555
556 MonoReflectionType *
557 ves_icall_Debugger_MonoSymbolWriter_get_local_type_from_sig (MonoReflectionAssembly *assembly,
558                                                              MonoArray *signature)
559 {
560         MonoDomain *domain; 
561         MonoImage *image;
562         MonoClass *klass;
563         MonoType *type;
564         const char *ptr;
565         int len = 0;
566
567         MONO_CHECK_ARG_NULL (assembly);
568         MONO_CHECK_ARG_NULL (signature);
569
570         domain = mono_domain_get();
571         image = assembly->assembly->image;
572
573         ptr = mono_array_addr (signature, char, 0);
574         g_assert (*ptr++ == 0x07);
575         len = mono_metadata_decode_value (ptr, &ptr);
576         g_assert (len == 1);
577
578         type = mono_metadata_parse_type (image, MONO_PARSE_LOCAL, 0, ptr, &ptr);
579
580         klass = mono_class_from_mono_type (type);
581
582         mono_class_init (klass);
583
584         return mono_type_get_object (domain, type);
585 }
586
587 MonoReflectionMethod *
588 ves_icall_Debugger_MonoSymbolWriter_method_from_token (MonoReflectionAssembly *assembly, guint32 token)
589 {
590         MonoDomain *domain; 
591         MonoImage *image;
592         MonoMethod *method;
593
594         MONO_CHECK_ARG_NULL (assembly);
595
596         domain = mono_domain_get();
597         image = assembly->assembly->image;
598
599         method = mono_get_method (image, token, NULL);
600
601         return mono_method_get_object (domain, method);
602 }
603
604 guint32
605 ves_icall_Debugger_DwarfFileWriter_get_type_token (MonoReflectionType *type)
606 {
607         MonoClass *klass = mono_class_from_mono_type (type->type);
608
609         mono_class_init (klass);
610
611         return klass->type_token;
612 }