2002-03-21 Martin Baulig <martin@gnome.org>
[mono.git] / mono / metadata / debug-symfile.c
1 #include <stdlib.h>
2 #include <string.h>
3 #include <mono/metadata/metadata.h>
4 #include <mono/metadata/rawbuffer.h>
5 #include <mono/metadata/debug-symfile.h>
6
7 #include <elf.h>
8 #include <fcntl.h>
9 #include <unistd.h>
10
11 #ifdef HAVE_ELF_H
12 #include <elf.h>
13 #endif
14
15 /* Keep in sync with Mono.CSharp.Debugger.MonoDwarfFileWriter */
16 #define MRT_none                        0x00
17 #define MRT_target_address_size         0x01
18 #define MRT_il_offset                   0x02
19 #define MRT_method_start_address        0x03
20 #define MRT_method_end_address          0x04
21
22 #define MRS_debug_info                  0x01
23 #define MRS_debug_abbrev                0x02
24 #define MRS_debug_line                  0x03
25 #define MRS_mono_reloc_table            0x04
26
27 #ifdef HAVE_ELF_H
28
29 static gboolean
30 get_sections_elf32 (MonoDebugSymbolFile *symfile, gboolean emit_warnings)
31 {
32         Elf32_Ehdr *header;
33         Elf32_Shdr *section, *strtab_section;
34         const char *strtab;
35         int i;
36
37         header = symfile->raw_contents;
38         if (header->e_version != EV_CURRENT) {
39                 if (emit_warnings)
40                         g_warning ("Symbol file %s has unknown ELF version %d",
41                                    symfile->file_name, header->e_version);
42                 return FALSE;
43         }
44
45         if (header->e_machine != EM_386) {
46                 if (emit_warnings)
47                         g_warning ("ELF file %s is for unknown architecture %d",
48                                    symfile->file_name, header->e_machine);
49                 return FALSE;
50         }
51
52         if (header->e_shentsize != sizeof (*section)) {
53                 if (emit_warnings)
54                         g_warning ("ELF file %s has unknown section header size "
55                                    "(expected %d, got %d)", symfile->file_name,
56                                    sizeof (*section), header->e_shentsize);
57                 return FALSE;
58         }
59
60         symfile->section_offsets = g_new0 (MonoDebugSymbolFileSection, MONO_DEBUG_SYMBOL_SECTION_MAX);
61
62         section = symfile->raw_contents + header->e_shoff;
63         strtab_section = section + header->e_shstrndx;
64         strtab = symfile->raw_contents + strtab_section->sh_offset;
65
66         for (i = 0; i < header->e_shnum; i++, section++) {
67                 const gchar *name = strtab + section->sh_name;
68
69                 if (!strcmp (name, ".debug_info")) {
70                         MonoDebugSymbolFileSection *sfs;
71
72                         sfs = &symfile->section_offsets [MONO_DEBUG_SYMBOL_SECTION_DEBUG_INFO];
73                         sfs->type = MONO_DEBUG_SYMBOL_SECTION_DEBUG_INFO;
74                         sfs->file_offset = section->sh_offset;
75                         sfs->size = section->sh_size;
76                 } else if (!strcmp (name, ".debug_line")) {
77                         MonoDebugSymbolFileSection *sfs;
78
79                         sfs = &symfile->section_offsets [MONO_DEBUG_SYMBOL_SECTION_DEBUG_LINE];
80                         sfs->type = MONO_DEBUG_SYMBOL_SECTION_DEBUG_LINE;
81                         sfs->file_offset = section->sh_offset;
82                         sfs->size = section->sh_size;
83                 } else if (!strcmp (name, ".debug_abbrev")) {
84                         MonoDebugSymbolFileSection *sfs;
85
86                         sfs = &symfile->section_offsets [MONO_DEBUG_SYMBOL_SECTION_DEBUG_ABBREV];
87                         sfs->type = MONO_DEBUG_SYMBOL_SECTION_DEBUG_ABBREV;
88                         sfs->file_offset = section->sh_offset;
89                         sfs->size = section->sh_size;
90                 } else if (!strcmp (name, ".mono_reloc_table")) {
91                         MonoDebugSymbolFileSection *sfs;
92
93                         sfs = &symfile->section_offsets [MONO_DEBUG_SYMBOL_SECTION_MONO_RELOC_TABLE];
94                         sfs->type = MONO_DEBUG_SYMBOL_SECTION_MONO_RELOC_TABLE;
95                         sfs->file_offset = section->sh_offset;
96                         sfs->size = section->sh_size;
97                 }
98         }
99
100         return TRUE;
101 }
102
103 #endif /* HAVE_ELF_H */
104
105 static gboolean
106 get_sections (MonoDebugSymbolFile *symfile, gboolean emit_warnings)
107 {
108 #ifdef HAVE_ELF_H
109         if (!strncmp (symfile->raw_contents, ELFMAG, strlen (ELFMAG)))
110                 return get_sections_elf32 (symfile, emit_warnings);
111 #endif
112
113         if (emit_warnings)
114                 g_warning ("Symbol file %s has unknown file format", symfile->file_name);
115
116         return FALSE;
117 }
118
119 MonoDebugSymbolFile *
120 mono_debug_open_symbol_file (MonoImage *image, const char *filename, gboolean emit_warnings)
121 {
122         MonoDebugSymbolFile *symfile;
123         off_t file_size;
124         void *ptr;
125         int fd;
126
127         fd = open (filename, O_RDWR);
128         if (fd == -1) {
129                 if (emit_warnings)
130                         g_warning ("Can't open symbol file: %s", filename);
131                 return NULL;
132         }
133
134         file_size = lseek (fd, 0, SEEK_END);
135         lseek (fd, 0, SEEK_SET);
136
137         if (file_size == (off_t) -1) {
138                 if (emit_warnings)
139                         g_warning ("Can't get size of symbol file: %s", filename);
140                 return NULL;
141         }
142
143         ptr = mono_raw_buffer_load (fd, 1, 0, file_size);
144         if (!ptr) {
145                 if (emit_warnings)
146                         g_warning ("Can't read symbol file: %s", filename);
147                 return NULL;
148         }
149
150         symfile = g_new0 (MonoDebugSymbolFile, 1);
151         symfile->fd = fd;
152         symfile->file_name = g_strdup (filename);
153         symfile->image = image;
154         symfile->raw_contents = ptr;
155
156         if (!get_sections (symfile, emit_warnings)) {
157                 mono_debug_close_symbol_file (symfile);
158                 return NULL;
159         }
160
161         return symfile;
162 }
163
164 void
165 mono_debug_close_symbol_file (MonoDebugSymbolFile *symfile)
166 {
167         if (!symfile)
168                 return;
169
170         if (symfile->raw_contents)
171                 mono_raw_buffer_free (symfile->raw_contents);
172         if (symfile->fd)
173                 close (symfile->fd);
174
175         g_free (symfile->file_name);
176         g_free (symfile->section_offsets);
177         g_free (symfile);
178 }
179
180 void
181 mono_debug_update_symbol_file (MonoDebugSymbolFile *symfile,
182                                MonoDebugMethodInfoFunc method_info_func,
183                                gpointer  user_data)
184 {
185         const char *reloc_ptr, *reloc_start, *reloc_end;
186         int version, already_relocated = 0;
187         long reloc_size;
188
189         if (!symfile->section_offsets [MONO_DEBUG_SYMBOL_SECTION_MONO_RELOC_TABLE].file_offset)
190                 return;
191
192         reloc_ptr = reloc_start = symfile->raw_contents +
193                 symfile->section_offsets [MONO_DEBUG_SYMBOL_SECTION_MONO_RELOC_TABLE].file_offset;
194
195         version = *((guint16 *) reloc_ptr)++;
196         if (version != MONO_DEBUG_SYMBOL_FILE_VERSION) {
197                 g_warning ("Symbol file %s has incorrect relocation table version "
198                            "(expected %d, got %d)", symfile->file_name,
199                            MONO_DEBUG_SYMBOL_FILE_VERSION, version);
200                 return;
201         }
202
203         already_relocated = *reloc_ptr;
204         *((char *) reloc_ptr)++ = 1;
205
206         reloc_size = *((guint32 *) reloc_ptr)++;
207         reloc_end = reloc_ptr + reloc_size;
208
209         while (reloc_ptr < reloc_end) {
210                 int type, size, section, offset;
211                 const char *tmp_ptr;
212                 void *base_ptr;
213
214                 type = *reloc_ptr++;
215                 size = * ((guint32 *) reloc_ptr)++;
216
217                 tmp_ptr = reloc_ptr;
218                 reloc_ptr += size;
219
220                 section = *tmp_ptr++;
221                 offset = *((guint32 *) tmp_ptr)++;
222
223                 if (section >= MONO_DEBUG_SYMBOL_SECTION_MAX) {
224                         g_warning ("Symbol file %s contains a relocation entry for unknown section %d",
225                                    symfile->file_name, section);
226                         continue;
227                 }
228
229                 if (!symfile->section_offsets [section].file_offset) {
230                         g_warning ("Symbol file %s contains a relocation entry for non-existing "
231                                    "section %d", symfile->file_name, section);
232                         continue;
233                 }
234
235                 base_ptr = symfile->raw_contents + symfile->section_offsets [section].file_offset;
236                 base_ptr += offset;
237
238                 switch (type) {
239                 case MRT_target_address_size:
240                         * (guint8 *) base_ptr = sizeof (void *);
241                         break;
242                 case MRT_method_start_address: {
243                         int token = *((guint32 *) tmp_ptr)++;
244                         MonoDebugMethodInfo *minfo;
245
246                         minfo = method_info_func (symfile, token, user_data);
247
248                         if (!minfo) {
249                                 * (void **) base_ptr = 0;
250                                 continue;
251                         }
252
253                         g_message ("Start of `%s' relocated to %p", minfo->method->name, minfo->code_start);
254
255                         * (void **) base_ptr = minfo->code_start;
256
257                         break;
258                 }
259                 case MRT_method_end_address: {
260                         int token = *((guint32 *) tmp_ptr)++;
261                         MonoDebugMethodInfo *minfo;
262
263                         minfo = method_info_func (symfile, token, user_data);
264
265                         if (!minfo) {
266                                 * (void **) base_ptr = 0;
267                                 continue;
268                         }
269
270                         * (void **) base_ptr = minfo->code_start + minfo->code_size;
271
272                         break;
273                 }
274                 case MRT_il_offset: {
275                         guint32 token = *((guint32 *) tmp_ptr)++;
276                         guint32 original = *((guint32 *) tmp_ptr)++;
277                         MonoDebugMethodInfo *minfo;
278                         guint32 address;
279                         int i;
280
281                         minfo = method_info_func (symfile, token, user_data);
282
283                         if (!minfo) {
284                                 * (void **) base_ptr = 0;
285                                 continue;
286                         }
287
288                         address = minfo->code_size;
289
290                         for (i = 0; i < minfo->num_il_offsets; i++) {
291                                 MonoDebugILOffsetInfo *il = &minfo->il_offsets [i];
292
293                                 if (il->offset >= original) {
294                                         address = il->address;
295                                         break;
296                                 }
297                         }
298
299                         g_message ("Relocating IL offset %d in `%s' to %d (%p)",
300                                    original, minfo->method->name, address,
301                                    minfo->code_start + address);
302
303                         * (void **) base_ptr = minfo->code_start + address;
304
305                         break;
306                 }
307                 default:
308                         g_warning ("Symbol file %s contains unknown relocation entry %d",
309                                    symfile->file_name, type);
310                         break;
311                 }
312         }
313 }