2002-03-24 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
28 #define MRS_debug_info                  0x01
29 #define MRS_debug_abbrev                0x02
30 #define MRS_debug_line                  0x03
31 #define MRS_mono_reloc_table            0x04
32
33 #ifdef HAVE_ELF_H
34
35 static gboolean
36 get_sections_elf32 (MonoDebugSymbolFile *symfile, gboolean emit_warnings)
37 {
38         Elf32_Ehdr *header;
39         Elf32_Shdr *section, *strtab_section;
40         const char *strtab;
41         int i;
42
43         header = symfile->raw_contents;
44         if (header->e_version != EV_CURRENT) {
45                 if (emit_warnings)
46                         g_warning ("Symbol file %s has unknown ELF version %d",
47                                    symfile->file_name, header->e_version);
48                 return FALSE;
49         }
50
51         if (header->e_machine != EM_386) {
52                 if (emit_warnings)
53                         g_warning ("ELF file %s is for unknown architecture %d",
54                                    symfile->file_name, header->e_machine);
55                 return FALSE;
56         }
57
58         if (header->e_shentsize != sizeof (*section)) {
59                 if (emit_warnings)
60                         g_warning ("ELF file %s has unknown section header size "
61                                    "(expected %d, got %d)", symfile->file_name,
62                                    sizeof (*section), header->e_shentsize);
63                 return FALSE;
64         }
65
66         symfile->section_offsets = g_new0 (MonoDebugSymbolFileSection, MONO_DEBUG_SYMBOL_SECTION_MAX);
67
68         section = symfile->raw_contents + header->e_shoff;
69         strtab_section = section + header->e_shstrndx;
70         strtab = symfile->raw_contents + strtab_section->sh_offset;
71
72         for (i = 0; i < header->e_shnum; i++, section++) {
73                 const gchar *name = strtab + section->sh_name;
74
75                 if (!strcmp (name, ".debug_info")) {
76                         MonoDebugSymbolFileSection *sfs;
77
78                         sfs = &symfile->section_offsets [MONO_DEBUG_SYMBOL_SECTION_DEBUG_INFO];
79                         sfs->type = MONO_DEBUG_SYMBOL_SECTION_DEBUG_INFO;
80                         sfs->file_offset = section->sh_offset;
81                         sfs->size = section->sh_size;
82                 } else if (!strcmp (name, ".debug_line")) {
83                         MonoDebugSymbolFileSection *sfs;
84
85                         sfs = &symfile->section_offsets [MONO_DEBUG_SYMBOL_SECTION_DEBUG_LINE];
86                         sfs->type = MONO_DEBUG_SYMBOL_SECTION_DEBUG_LINE;
87                         sfs->file_offset = section->sh_offset;
88                         sfs->size = section->sh_size;
89                 } else if (!strcmp (name, ".debug_abbrev")) {
90                         MonoDebugSymbolFileSection *sfs;
91
92                         sfs = &symfile->section_offsets [MONO_DEBUG_SYMBOL_SECTION_DEBUG_ABBREV];
93                         sfs->type = MONO_DEBUG_SYMBOL_SECTION_DEBUG_ABBREV;
94                         sfs->file_offset = section->sh_offset;
95                         sfs->size = section->sh_size;
96                 } else if (!strcmp (name, ".mono_reloc_table")) {
97                         MonoDebugSymbolFileSection *sfs;
98
99                         sfs = &symfile->section_offsets [MONO_DEBUG_SYMBOL_SECTION_MONO_RELOC_TABLE];
100                         sfs->type = MONO_DEBUG_SYMBOL_SECTION_MONO_RELOC_TABLE;
101                         sfs->file_offset = section->sh_offset;
102                         sfs->size = section->sh_size;
103                 }
104         }
105
106         return TRUE;
107 }
108
109 #endif /* HAVE_ELF_H */
110
111 static gboolean
112 get_sections (MonoDebugSymbolFile *symfile, gboolean emit_warnings)
113 {
114 #ifdef HAVE_ELF_H
115         if (!strncmp (symfile->raw_contents, ELFMAG, strlen (ELFMAG)))
116                 return get_sections_elf32 (symfile, emit_warnings);
117 #endif
118
119         if (emit_warnings)
120                 g_warning ("Symbol file %s has unknown file format", symfile->file_name);
121
122         return FALSE;
123 }
124
125 MonoDebugSymbolFile *
126 mono_debug_open_symbol_file (MonoImage *image, const char *filename, gboolean emit_warnings)
127 {
128         MonoDebugSymbolFile *symfile;
129         off_t file_size;
130         void *ptr;
131         int fd;
132
133         fd = open (filename, O_RDWR);
134         if (fd == -1) {
135                 if (emit_warnings)
136                         g_warning ("Can't open symbol file: %s", filename);
137                 return NULL;
138         }
139
140         file_size = lseek (fd, 0, SEEK_END);
141         lseek (fd, 0, SEEK_SET);
142
143         if (file_size == (off_t) -1) {
144                 if (emit_warnings)
145                         g_warning ("Can't get size of symbol file: %s", filename);
146                 return NULL;
147         }
148
149         ptr = mono_raw_buffer_load (fd, 1, 0, file_size);
150         if (!ptr) {
151                 if (emit_warnings)
152                         g_warning ("Can't read symbol file: %s", filename);
153                 return NULL;
154         }
155
156         symfile = g_new0 (MonoDebugSymbolFile, 1);
157         symfile->fd = fd;
158         symfile->file_name = g_strdup (filename);
159         symfile->image = image;
160         symfile->raw_contents = ptr;
161         symfile->raw_contents_size = file_size;
162
163         if (!get_sections (symfile, emit_warnings)) {
164                 mono_debug_close_symbol_file (symfile);
165                 return NULL;
166         }
167
168         return symfile;
169 }
170
171 void
172 mono_debug_close_symbol_file (MonoDebugSymbolFile *symfile)
173 {
174         if (!symfile)
175                 return;
176
177         if (symfile->raw_contents)
178                 mono_raw_buffer_free (symfile->raw_contents);
179         if (symfile->fd)
180                 close (symfile->fd);
181
182         g_free (symfile->file_name);
183         g_free (symfile->section_offsets);
184         g_free (symfile);
185 }
186
187 void
188 mono_debug_update_symbol_file (MonoDebugSymbolFile *symfile,
189                                MonoDebugMethodInfoFunc method_info_func,
190                                gpointer  user_data)
191 {
192         const char *reloc_ptr, *reloc_start, *reloc_end;
193         int version, already_relocated = 0;
194         long reloc_size;
195
196         if (!symfile->section_offsets [MONO_DEBUG_SYMBOL_SECTION_MONO_RELOC_TABLE].file_offset)
197                 return;
198
199         reloc_ptr = reloc_start = symfile->raw_contents +
200                 symfile->section_offsets [MONO_DEBUG_SYMBOL_SECTION_MONO_RELOC_TABLE].file_offset;
201
202         version = *((guint16 *) reloc_ptr)++;
203         if (version != MONO_DEBUG_SYMBOL_FILE_VERSION) {
204                 g_warning ("Symbol file %s has incorrect relocation table version "
205                            "(expected %d, got %d)", symfile->file_name,
206                            MONO_DEBUG_SYMBOL_FILE_VERSION, version);
207                 return;
208         }
209
210         already_relocated = *reloc_ptr;
211         *((char *) reloc_ptr)++ = 1;
212
213         reloc_size = *((guint32 *) reloc_ptr)++;
214         reloc_end = reloc_ptr + reloc_size;
215
216         while (reloc_ptr < reloc_end) {
217                 int type, size, section, offset;
218                 const char *tmp_ptr;
219                 void *base_ptr;
220
221                 type = *reloc_ptr++;
222                 size = * ((guint32 *) reloc_ptr)++;
223
224                 tmp_ptr = reloc_ptr;
225                 reloc_ptr += size;
226
227                 section = *tmp_ptr++;
228                 offset = *((guint32 *) tmp_ptr)++;
229
230                 if (section >= MONO_DEBUG_SYMBOL_SECTION_MAX) {
231                         g_warning ("Symbol file %s contains a relocation entry for unknown section %d",
232                                    symfile->file_name, section);
233                         continue;
234                 }
235
236                 if (!symfile->section_offsets [section].file_offset) {
237                         g_warning ("Symbol file %s contains a relocation entry for non-existing "
238                                    "section %d", symfile->file_name, section);
239                         continue;
240                 }
241
242                 base_ptr = symfile->raw_contents + symfile->section_offsets [section].file_offset;
243                 base_ptr += offset;
244
245                 switch (type) {
246                 case MRT_target_address_size:
247                         * (guint8 *) base_ptr = sizeof (void *);
248                         break;
249                 case MRT_method_start_address: {
250                         int token = *((guint32 *) tmp_ptr)++;
251                         MonoDebugMethodInfo *minfo;
252
253                         minfo = method_info_func (symfile, token, user_data);
254
255                         if (!minfo) {
256                                 * (void **) base_ptr = 0;
257                                 continue;
258                         }
259
260 #if 0
261                         g_message ("Start of `%s' relocated to %p", minfo->method->name, minfo->code_start);
262 #endif
263
264                         * (void **) base_ptr = minfo->code_start;
265
266                         break;
267                 }
268                 case MRT_method_end_address: {
269                         int token = *((guint32 *) tmp_ptr)++;
270                         MonoDebugMethodInfo *minfo;
271
272                         minfo = method_info_func (symfile, token, user_data);
273
274                         if (!minfo) {
275                                 * (void **) base_ptr = 0;
276                                 continue;
277                         }
278
279                         * (void **) base_ptr = minfo->code_start + minfo->code_size;
280
281                         break;
282                 }
283                 case MRT_il_offset: {
284                         guint32 token = *((guint32 *) tmp_ptr)++;
285                         guint32 original = *((guint32 *) tmp_ptr)++;
286                         MonoDebugMethodInfo *minfo;
287                         guint32 address;
288                         int i;
289
290                         minfo = method_info_func (symfile, token, user_data);
291
292                         if (!minfo) {
293                                 * (void **) base_ptr = 0;
294                                 continue;
295                         }
296
297                         address = minfo->code_size;
298
299                         for (i = 0; i < minfo->num_il_offsets; i++) {
300                                 MonoDebugILOffsetInfo *il = &minfo->il_offsets [i];
301
302                                 if (il->offset >= original) {
303                                         address = il->address;
304                                         break;
305                                 }
306                         }
307
308 #if 0
309                         g_message ("Relocating IL offset %d in `%s' to %d (%p)",
310                                    original, minfo->method->name, address,
311                                    minfo->code_start + address);
312 #endif
313
314                         * (void **) base_ptr = minfo->code_start + address;
315
316                         break;
317                 }
318                 case MRT_local_variable: {
319                         guint32 token = *((guint32 *) tmp_ptr)++;
320                         guint32 original = *((guint32 *) tmp_ptr)++;
321                         MonoDebugMethodInfo *minfo;
322                         gint32 address;
323
324                         minfo = method_info_func (symfile, token, user_data);
325
326                         if (!minfo) {
327                                 * (void **) base_ptr = 0;
328                                 continue;
329                         }
330
331                         if (original > minfo->num_locals) {
332                                 g_warning ("Symbol file %s contains relocation entry for non-existing "
333                                            "local variable %d, but method %s only has %d local variables.",
334                                            symfile->file_name, original, minfo->method->name,
335                                            minfo->num_locals);
336                                 continue;
337                         }
338
339                         address = minfo->local_offsets [original];
340
341 #if 0
342                         g_message ("Relocating local variable %d (%s) to stack offset %d",
343                                    original, minfo->method->name, address);
344 #endif
345
346                         * (gint32 *) base_ptr = address;
347
348                         break;
349                 }
350                 case MRT_method_parameter: {
351                         guint32 token = *((guint32 *) tmp_ptr)++;
352                         guint32 original = *((guint32 *) tmp_ptr)++;
353                         MonoDebugMethodInfo *minfo;
354                         gint32 address;
355
356                         minfo = method_info_func (symfile, token, user_data);
357
358                         if (!minfo) {
359                                 * (void **) base_ptr = 0;
360                                 continue;
361                         }
362
363                         if (original > minfo->num_params) {
364                                 g_warning ("Symbol file %s contains relocation entry for non-existing "
365                                            "parameter %d, but method %s only has %d parameters.",
366                                            symfile->file_name, original, minfo->method->name,
367                                            minfo->num_params);
368                                 continue;
369                         }
370
371                         address = minfo->param_offsets [original];
372
373 #if 0
374                         g_message ("Relocating parameter %d (%s) to stack offset %d",
375                                    original, minfo->method->name, address);
376 #endif
377
378                         * (gint32 *) base_ptr = address;
379
380                         break;
381                 }
382                 case MRT_type_sizeof: {
383                         guint32 token = *((guint32 *) tmp_ptr)++;
384                         MonoClass *klass = mono_class_get (symfile->image, token);
385
386                         g_message ("Setting size of type %u to %d", token, klass->instance_size);
387
388                         * (gint8 *) base_ptr = klass->instance_size;
389
390                         break;
391                 }
392                 case MRT_type_field_offset: {
393                         guint32 token = *((guint32 *) tmp_ptr)++;
394                         guint32 original = *((guint32 *) tmp_ptr)++;
395                         MonoClass *klass = mono_class_get (symfile->image, token);
396                         guint32 offset;
397
398                         if (original > klass->field.count) {
399                                 g_warning ("Symbol file %s contains invalid field offset entry.",
400                                            symfile->file_name);
401                                 continue;
402                         }
403
404                         offset = klass->fields [original].offset;
405                         if (klass->byval_arg.type == MONO_TYPE_VALUETYPE)
406                                 offset -= sizeof (MonoObject);
407
408                         g_message ("Setting field %d of type %u to offset %d", original,
409                                    token, offset);
410
411                         * (guint32 *) base_ptr = offset;
412
413                         break;
414                 }
415                 default:
416                         g_warning ("Symbol file %s contains unknown relocation entry %d",
417                                    symfile->file_name, type);
418                         break;
419                 }
420         }
421
422         mono_raw_buffer_update (symfile->raw_contents, symfile->raw_contents_size);
423 }
424
425 MonoReflectionType *
426 ves_icall_Debugger_MonoSymbolWriter_get_local_type_from_sig (MonoReflectionAssembly *assembly,
427                                                              MonoArray *signature)
428 {
429         MonoDomain *domain; 
430         MonoImage *image;
431         MonoClass *klass;
432         MonoType *type;
433         const char *ptr;
434         int len = 0;
435
436         MONO_CHECK_ARG_NULL (assembly);
437         MONO_CHECK_ARG_NULL (signature);
438
439         domain = mono_domain_get();
440         image = assembly->assembly->image;
441
442         ptr = mono_array_addr (signature, char, 0);
443         g_assert (*ptr++ == 0x07);
444         len = mono_metadata_decode_value (ptr, &ptr);
445         g_assert (len == 1);
446
447         type = mono_metadata_parse_type (image, MONO_PARSE_LOCAL, 0, ptr, &ptr);
448
449         klass = mono_class_from_mono_type (type);
450
451         mono_class_init (klass);
452
453         return mono_type_get_object (domain, type);
454 }
455
456 MonoReflectionMethod *
457 ves_icall_Debugger_MonoSymbolWriter_method_from_token (MonoReflectionAssembly *assembly, guint32 token)
458 {
459         MonoDomain *domain; 
460         MonoImage *image;
461         MonoMethod *method;
462
463         MONO_CHECK_ARG_NULL (assembly);
464
465         domain = mono_domain_get();
466         image = assembly->assembly->image;
467
468         method = mono_get_method (image, token, NULL);
469
470         return mono_method_get_object (domain, method);
471 }
472
473 guint32
474 ves_icall_Debugger_DwarfFileWriter_get_type_token (MonoReflectionType *type)
475 {
476     MonoClass *klass = mono_class_from_mono_type (type->type);
477
478     mono_class_init (klass);
479
480     return klass->type_token;
481 }