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