Fix roslyn install with AOT disabled.
[mono-project.git] / mono / metadata / debug-mono-ppdb.c
blob8357099a551aa9bec2f6c5dcb147724731e1ee5f
1 /*
2 * debug-mono-ppdb.c: Support for the portable PDB symbol
3 * file format
6 * Author:
7 * Mono Project (http://www.mono-project.com)
9 * Copyright 2015 Xamarin Inc (http://www.xamarin.com)
10 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
13 #include <config.h>
14 #include <stdlib.h>
15 #include <stdio.h>
16 #include <errno.h>
17 #include <string.h>
18 #include <mono/metadata/metadata.h>
19 #include <mono/metadata/tabledefs.h>
20 #include <mono/metadata/tokentype.h>
21 #include <mono/metadata/debug-helpers.h>
22 #include <mono/metadata/mono-debug.h>
23 #include <mono/metadata/debug-mono-symfile.h>
24 #include <mono/metadata/mono-debug-debugger.h>
25 #include <mono/metadata/mono-endian.h>
26 #include <mono/metadata/metadata-internals.h>
27 #include <mono/metadata/class-internals.h>
28 #include <mono/metadata/cil-coff.h>
30 #include "debug-mono-ppdb.h"
32 struct _MonoPPDBFile {
33 MonoImage *image;
34 GHashTable *doc_hash;
35 GHashTable *method_hash;
38 /* IMAGE_DEBUG_DIRECTORY structure */
39 typedef struct
41 gint32 characteristics;
42 gint32 time_date_stamp;
43 gint16 major_version;
44 gint16 minor_version;
45 gint32 type;
46 gint32 size_of_data;
47 gint32 address;
48 gint32 pointer;
49 } ImageDebugDirectory;
51 typedef struct {
52 gint32 signature;
53 guint8 guid [16];
54 gint32 age;
55 } CodeviewDebugDirectory;
57 typedef struct {
58 guint8 guid [20];
59 guint32 entry_point;
60 guint64 referenced_tables;
61 } PdbStreamHeader;
63 static gboolean
64 get_pe_debug_guid (MonoImage *image, guint8 *out_guid, gint32 *out_age, gint32 *out_timestamp)
66 MonoPEDirEntry *debug_dir_entry;
67 ImageDebugDirectory *debug_dir;
69 debug_dir_entry = &((MonoCLIImageInfo*)image->image_info)->cli_header.datadir.pe_debug;
70 if (!debug_dir_entry->size)
71 return FALSE;
73 int offset = mono_cli_rva_image_map (image, debug_dir_entry->rva);
74 debug_dir = (ImageDebugDirectory*)(image->raw_data + offset);
75 if (debug_dir->type == 2 && debug_dir->major_version == 0x100 && debug_dir->minor_version == 0x504d) {
76 /* This is a 'CODEVIEW' debug directory */
77 CodeviewDebugDirectory *dir = (CodeviewDebugDirectory*)(image->raw_data + debug_dir->pointer);
79 if (dir->signature == 0x53445352) {
80 memcpy (out_guid, dir->guid, 16);
81 *out_age = dir->age;
82 *out_timestamp = debug_dir->time_date_stamp;
83 return TRUE;
86 return FALSE;
89 static void
90 doc_free (gpointer key)
92 MonoDebugSourceInfo *info = (MonoDebugSourceInfo *)key;
94 g_free (info->source_file);
95 g_free (info);
98 MonoPPDBFile*
99 mono_ppdb_load_file (MonoImage *image, const guint8 *raw_contents, int size)
101 MonoImage *ppdb_image = NULL;
102 const char *filename;
103 char *s, *ppdb_filename;
104 MonoImageOpenStatus status;
105 guint8 pe_guid [16];
106 gint32 pe_age;
107 gint32 pe_timestamp;
108 MonoPPDBFile *ppdb;
110 if (!get_pe_debug_guid (image, pe_guid, &pe_age, &pe_timestamp))
111 return NULL;
113 if (raw_contents) {
114 if (size > 4 && strncmp ((char*)raw_contents, "BSJB", 4) == 0)
115 ppdb_image = mono_image_open_from_data_internal ((char*)raw_contents, size, TRUE, &status, FALSE, TRUE, NULL);
116 } else {
117 /* ppdb files drop the .exe/.dll extension */
118 filename = mono_image_get_filename (image);
119 if (strlen (filename) > 4 && (!strcmp (filename + strlen (filename) - 4, ".exe") || !strcmp (filename + strlen (filename) - 4, ".dll"))) {
120 s = g_strdup (filename);
121 s [strlen (filename) - 4] = '\0';
122 ppdb_filename = g_strdup_printf ("%s.pdb", s);
123 g_free (s);
124 } else {
125 ppdb_filename = g_strdup_printf ("%s.pdb", filename);
128 ppdb_image = mono_image_open_metadata_only (ppdb_filename, &status);
129 if (!ppdb_image)
130 g_free (ppdb_filename);
132 if (!ppdb_image)
133 return NULL;
136 * Check that the images match.
137 * The same id is stored in the Debug Directory of the PE file, and in the
138 * #Pdb stream in the ppdb file.
140 PdbStreamHeader *pdb_stream = (PdbStreamHeader*)ppdb_image->heap_pdb.data;
142 g_assert (pdb_stream);
144 /* The pdb id is a concentation of the pe guid and the timestamp */
145 if (memcmp (pe_guid, pdb_stream->guid, 16) != 0 || memcmp (&pe_timestamp, pdb_stream->guid + 16, 4) != 0) {
146 g_warning ("Symbol file %s doesn't match image %s", ppdb_image->name,
147 image->name);
148 mono_image_close (ppdb_image);
149 return NULL;
152 ppdb = g_new0 (MonoPPDBFile, 1);
153 ppdb->image = ppdb_image;
154 ppdb->doc_hash = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) doc_free);
155 ppdb->method_hash = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) g_free);
157 return ppdb;
160 void
161 mono_ppdb_close (MonoDebugHandle *handle)
163 MonoPPDBFile *ppdb = handle->ppdb;
165 mono_image_close (ppdb->image);
166 g_hash_table_destroy (ppdb->doc_hash);
167 g_hash_table_destroy (ppdb->method_hash);
168 g_free (ppdb);
171 MonoDebugMethodInfo *
172 mono_ppdb_lookup_method (MonoDebugHandle *handle, MonoMethod *method)
174 MonoDebugMethodInfo *minfo;
175 MonoPPDBFile *ppdb = handle->ppdb;
177 if (handle->image != mono_class_get_image (mono_method_get_class (method)))
178 return NULL;
180 mono_debugger_lock ();
182 minfo = (MonoDebugMethodInfo *)g_hash_table_lookup (ppdb->method_hash, method);
183 if (minfo) {
184 mono_debugger_unlock ();
185 return minfo;
188 minfo = g_new0 (MonoDebugMethodInfo, 1);
189 minfo->index = 0;
190 minfo->method = method;
191 minfo->handle = handle;
193 g_hash_table_insert (ppdb->method_hash, method, minfo);
195 mono_debugger_unlock ();
197 return minfo;
200 static MonoDebugSourceInfo*
201 get_docinfo (MonoPPDBFile *ppdb, MonoImage *image, int docidx)
203 MonoTableInfo *tables = image->tables;
204 guint32 cols [MONO_DOCUMENT_SIZE];
205 const char *ptr;
206 const char *start;
207 const char *part_ptr;
208 int size, part_size, partidx, nparts;
209 char sep;
210 GString *s;
211 MonoDebugSourceInfo *res, *cached;
213 mono_debugger_lock ();
214 cached = (MonoDebugSourceInfo *)g_hash_table_lookup (ppdb->doc_hash, GUINT_TO_POINTER (docidx));
215 mono_debugger_unlock ();
216 if (cached)
217 return cached;
219 mono_metadata_decode_row (&tables [MONO_TABLE_DOCUMENT], docidx-1, cols, MONO_DOCUMENT_SIZE);
221 ptr = mono_metadata_blob_heap (image, cols [MONO_DOCUMENT_NAME]);
222 size = mono_metadata_decode_blob_size (ptr, &ptr);
223 start = ptr;
225 // FIXME: UTF8
226 sep = ptr [0];
227 ptr ++;
229 s = g_string_new ("");
231 nparts = 0;
232 while (ptr < start + size) {
233 partidx = mono_metadata_decode_value (ptr, &ptr);
234 if (nparts)
235 g_string_append_c (s, sep);
236 if (partidx) {
237 part_ptr = mono_metadata_blob_heap (image, partidx);
238 part_size = mono_metadata_decode_blob_size (part_ptr, &part_ptr);
240 // FIXME: UTF8
241 g_string_append_len (s, part_ptr, part_size);
243 nparts ++;
246 res = g_new0 (MonoDebugSourceInfo, 1);
247 res->source_file = g_string_free (s, FALSE);
248 res->guid = NULL;
249 res->hash = (guint8*)mono_metadata_blob_heap (image, cols [MONO_DOCUMENT_HASH]);
251 mono_debugger_lock ();
252 cached = (MonoDebugSourceInfo *)g_hash_table_lookup (ppdb->doc_hash, GUINT_TO_POINTER (docidx));
253 if (!cached) {
254 g_hash_table_insert (ppdb->doc_hash, GUINT_TO_POINTER (docidx), res);
255 } else {
256 doc_free (res);
257 res = cached;
259 mono_debugger_unlock ();
260 return res;
263 static char*
264 get_docname (MonoPPDBFile *ppdb, MonoImage *image, int docidx)
266 MonoDebugSourceInfo *info;
268 info = get_docinfo (ppdb, image, docidx);
269 return g_strdup (info->source_file);
273 * mono_ppdb_lookup_location:
274 * @minfo: A `MonoDebugMethodInfo' which can be retrieved by
275 * mono_debug_lookup_method().
276 * @offset: IL offset within the corresponding method's CIL code.
278 * This function is similar to mono_debug_lookup_location(), but we
279 * already looked up the method and also already did the
280 * `native address -> IL offset' mapping.
282 MonoDebugSourceLocation *
283 mono_ppdb_lookup_location (MonoDebugMethodInfo *minfo, uint32_t offset)
285 MonoPPDBFile *ppdb = minfo->handle->ppdb;
286 MonoImage *image = ppdb->image;
287 MonoMethod *method = minfo->method;
288 MonoTableInfo *tables = image->tables;
289 guint32 cols [MONO_METHODBODY_SIZE];
290 const char *ptr;
291 const char *end;
292 char *docname;
293 int idx, size, docidx, iloffset, delta_il, delta_lines, delta_cols, start_line, start_col, adv_line, adv_col;
294 gboolean first = TRUE, first_non_hidden = TRUE;
295 MonoDebugSourceLocation *location;
297 if (!method->token)
298 return NULL;
300 idx = mono_metadata_token_index (method->token);
302 mono_metadata_decode_row (&tables [MONO_TABLE_METHODBODY], idx-1, cols, MONO_METHODBODY_SIZE);
304 docidx = cols [MONO_METHODBODY_DOCUMENT];
306 if (!cols [MONO_METHODBODY_SEQ_POINTS])
307 return NULL;
308 ptr = mono_metadata_blob_heap (image, cols [MONO_METHODBODY_SEQ_POINTS]);
309 size = mono_metadata_decode_blob_size (ptr, &ptr);
310 end = ptr + size;
312 /* Header */
313 /* LocalSignature */
314 mono_metadata_decode_value (ptr, &ptr);
315 if (docidx == 0)
316 docidx = mono_metadata_decode_value (ptr, &ptr);
317 docname = get_docname (ppdb, image, docidx);
319 iloffset = 0;
320 start_line = 0;
321 start_col = 0;
322 while (ptr < end) {
323 delta_il = mono_metadata_decode_value (ptr, &ptr);
324 if (!first && delta_il == 0) {
325 /* document-record */
326 docidx = mono_metadata_decode_value (ptr, &ptr);
327 docname = get_docname (ppdb, image, docidx);
328 continue;
330 if (!first && iloffset + delta_il > offset)
331 break;
332 iloffset += delta_il;
333 first = FALSE;
335 delta_lines = mono_metadata_decode_value (ptr, &ptr);
336 if (delta_lines == 0)
337 delta_cols = mono_metadata_decode_value (ptr, &ptr);
338 else
339 delta_cols = mono_metadata_decode_signed_value (ptr, &ptr);
340 if (delta_lines == 0 && delta_cols == 0)
341 /* hidden-sequence-point-record */
342 continue;
343 if (first_non_hidden) {
344 start_line = mono_metadata_decode_value (ptr, &ptr);
345 start_col = mono_metadata_decode_value (ptr, &ptr);
346 } else {
347 adv_line = mono_metadata_decode_signed_value (ptr, &ptr);
348 adv_col = mono_metadata_decode_signed_value (ptr, &ptr);
349 start_line += adv_line;
350 start_col += adv_col;
352 first_non_hidden = FALSE;
355 location = g_new0 (MonoDebugSourceLocation, 1);
356 location->source_file = docname;
357 location->row = start_line;
358 location->il_offset = iloffset;
360 return location;
363 void
364 mono_ppdb_get_seq_points (MonoDebugMethodInfo *minfo, char **source_file, GPtrArray **source_file_list, int **source_files, MonoSymSeqPoint **seq_points, int *n_seq_points)
366 MonoPPDBFile *ppdb = minfo->handle->ppdb;
367 MonoImage *image = ppdb->image;
368 MonoMethod *method = minfo->method;
369 MonoTableInfo *tables = image->tables;
370 guint32 cols [MONO_METHODBODY_SIZE];
371 const char *ptr;
372 const char *end;
373 MonoDebugSourceInfo *docinfo;
374 int i, method_idx, size, docidx, iloffset, delta_il, delta_lines, delta_cols, start_line, start_col, adv_line, adv_col;
375 gboolean first = TRUE, first_non_hidden = TRUE;
376 GArray *sps;
377 MonoSymSeqPoint sp;
378 GPtrArray *sfiles = NULL;
379 GPtrArray *sindexes = NULL;
381 if (source_file)
382 *source_file = NULL;
383 if (source_file_list)
384 *source_file_list = NULL;
385 if (source_files)
386 *source_files = NULL;
387 if (seq_points)
388 *seq_points = NULL;
389 if (n_seq_points)
390 *n_seq_points = 0;
392 if (source_file_list)
393 *source_file_list = sfiles = g_ptr_array_new ();
394 if (source_files)
395 sindexes = g_ptr_array_new ();
397 if (!method->token)
398 return;
400 method_idx = mono_metadata_token_index (method->token);
402 mono_metadata_decode_row (&tables [MONO_TABLE_METHODBODY], method_idx-1, cols, MONO_METHODBODY_SIZE);
404 docidx = cols [MONO_METHODBODY_DOCUMENT];
406 if (!cols [MONO_METHODBODY_SEQ_POINTS])
407 return;
409 ptr = mono_metadata_blob_heap (image, cols [MONO_METHODBODY_SEQ_POINTS]);
410 size = mono_metadata_decode_blob_size (ptr, &ptr);
411 end = ptr + size;
413 sps = g_array_new (FALSE, TRUE, sizeof (MonoSymSeqPoint));
415 /* Header */
416 /* LocalSignature */
417 mono_metadata_decode_value (ptr, &ptr);
418 if (docidx == 0)
419 docidx = mono_metadata_decode_value (ptr, &ptr);
420 docinfo = get_docinfo (ppdb, image, docidx);
422 if (sfiles)
423 g_ptr_array_add (sfiles, docinfo);
425 iloffset = 0;
426 start_line = 0;
427 start_col = 0;
428 while (ptr < end) {
429 delta_il = mono_metadata_decode_value (ptr, &ptr);
430 if (!first && delta_il == 0) {
431 /* subsequent-document-record */
432 docidx = mono_metadata_decode_value (ptr, &ptr);
433 docinfo = get_docinfo (ppdb, image, docidx);
434 if (sfiles)
435 g_ptr_array_add (sfiles, docinfo);
436 continue;
438 iloffset += delta_il;
439 first = FALSE;
441 delta_lines = mono_metadata_decode_value (ptr, &ptr);
442 if (delta_lines == 0)
443 delta_cols = mono_metadata_decode_value (ptr, &ptr);
444 else
445 delta_cols = mono_metadata_decode_signed_value (ptr, &ptr);
447 if (delta_lines == 0 && delta_cols == 0) {
448 /* Hidden sequence point */
449 continue;
452 if (first_non_hidden) {
453 start_line = mono_metadata_decode_value (ptr, &ptr);
454 start_col = mono_metadata_decode_value (ptr, &ptr);
455 } else {
456 adv_line = mono_metadata_decode_signed_value (ptr, &ptr);
457 adv_col = mono_metadata_decode_signed_value (ptr, &ptr);
458 start_line += adv_line;
459 start_col += adv_col;
461 first_non_hidden = FALSE;
463 memset (&sp, 0, sizeof (sp));
464 sp.il_offset = iloffset;
465 sp.line = start_line;
466 sp.column = start_col;
467 sp.end_line = start_line + delta_lines;
468 sp.end_column = start_col + delta_cols;
470 g_array_append_val (sps, sp);
471 if (source_files)
472 g_ptr_array_add (sindexes, GUINT_TO_POINTER (sfiles->len - 1));
475 if (n_seq_points) {
476 *n_seq_points = sps->len;
477 g_assert (seq_points);
478 *seq_points = g_new (MonoSymSeqPoint, sps->len);
479 memcpy (*seq_points, sps->data, sps->len * sizeof (MonoSymSeqPoint));
482 if (source_file)
483 *source_file = g_strdup (((MonoDebugSourceInfo*)g_ptr_array_index (sfiles, 0))->source_file);
484 if (source_files) {
485 *source_files = g_new (int, sps->len);
486 for (i = 0; i < sps->len; ++i)
487 (*source_files)[i] = GPOINTER_TO_INT (g_ptr_array_index (sindexes, i));
488 g_ptr_array_free (sindexes, TRUE);
491 g_array_free (sps, TRUE);
494 MonoDebugLocalsInfo*
495 mono_ppdb_lookup_locals (MonoDebugMethodInfo *minfo)
497 MonoPPDBFile *ppdb = minfo->handle->ppdb;
498 MonoImage *image = ppdb->image;
499 MonoTableInfo *tables = image->tables;
500 MonoMethod *method = minfo->method;
501 guint32 cols [MONO_LOCALSCOPE_SIZE];
502 guint32 locals_cols [MONO_LOCALVARIABLE_SIZE];
503 int i, lindex, sindex, method_idx, start_scope_idx, scope_idx, locals_idx, locals_end_idx, nscopes;
504 MonoDebugLocalsInfo *res;
505 MonoMethodSignature *sig;
507 if (!method->token)
508 return NULL;
510 sig = mono_method_signature (method);
511 if (!sig)
512 return NULL;
514 method_idx = mono_metadata_token_index (method->token);
516 start_scope_idx = mono_metadata_localscope_from_methoddef (image, method_idx);
518 if (!start_scope_idx)
519 return NULL;
521 /* Compute number of locals and scopes */
522 scope_idx = start_scope_idx;
523 mono_metadata_decode_row (&tables [MONO_TABLE_LOCALSCOPE], scope_idx-1, cols, MONO_LOCALSCOPE_SIZE);
524 locals_idx = cols [MONO_LOCALSCOPE_VARIABLELIST];
526 // https://github.com/dotnet/roslyn/blob/2ae8d5fed96ab3f1164031f9b4ac827f53289159/docs/specs/PortablePdb-Metadata.md#LocalScopeTable
528 // The variableList attribute in the pdb metadata table is a contiguous array that starts at a
529 // given offset (locals_idx) above and
531 // """
532 // continues to the smaller of:
534 // the last row of the LocalVariable table
535 // the next run of LocalVariables, found by inspecting the VariableList of the next row in this LocalScope table.
536 // """
537 // this endpoint becomes locals_end_idx below
539 // March to the last scope that is in this method
540 while (scope_idx <= tables [MONO_TABLE_LOCALSCOPE].rows) {
541 mono_metadata_decode_row (&tables [MONO_TABLE_LOCALSCOPE], scope_idx-1, cols, MONO_LOCALSCOPE_SIZE);
542 if (cols [MONO_LOCALSCOPE_METHOD] != method_idx)
543 break;
544 scope_idx ++;
546 // The number of scopes is the difference in the indices
547 // for the first and last scopes
548 nscopes = scope_idx - start_scope_idx;
550 // Ends with "the last row of the LocalVariable table"
551 // this happens if the above loop marched one past the end
552 // of the rows
553 if (scope_idx > tables [MONO_TABLE_LOCALSCOPE].rows) {
554 locals_end_idx = tables [MONO_TABLE_LOCALVARIABLE].rows + 1;
555 } else {
556 // Ends with "the next run of LocalVariables,
557 // found by inspecting the VariableList of the next row in this LocalScope table."
558 locals_end_idx = cols [MONO_LOCALSCOPE_VARIABLELIST];
561 res = g_new0 (MonoDebugLocalsInfo, 1);
562 res->num_blocks = nscopes;
563 res->code_blocks = g_new0 (MonoDebugCodeBlock, res->num_blocks);
564 res->num_locals = locals_end_idx - locals_idx;
565 res->locals = g_new0 (MonoDebugLocalVar, res->num_locals);
567 lindex = 0;
568 for (sindex = 0; sindex < nscopes; ++sindex) {
569 scope_idx = start_scope_idx + sindex;
570 mono_metadata_decode_row (&tables [MONO_TABLE_LOCALSCOPE], scope_idx-1, cols, MONO_LOCALSCOPE_SIZE);
572 locals_idx = cols [MONO_LOCALSCOPE_VARIABLELIST];
573 if (scope_idx == tables [MONO_TABLE_LOCALSCOPE].rows) {
574 locals_end_idx = tables [MONO_TABLE_LOCALVARIABLE].rows + 1;
575 } else {
576 locals_end_idx = mono_metadata_decode_row_col (&tables [MONO_TABLE_LOCALSCOPE], scope_idx-1 + 1, MONO_LOCALSCOPE_VARIABLELIST);
579 res->code_blocks [sindex].start_offset = cols [MONO_LOCALSCOPE_STARTOFFSET];
580 res->code_blocks [sindex].end_offset = cols [MONO_LOCALSCOPE_STARTOFFSET] + cols [MONO_LOCALSCOPE_LENGTH];
582 //printf ("Scope: %s %d %d %d-%d\n", mono_method_full_name (method, 1), cols [MONO_LOCALSCOPE_STARTOFFSET], cols [MONO_LOCALSCOPE_LENGTH], locals_idx, locals_end_idx);
584 for (i = locals_idx; i < locals_end_idx; ++i) {
585 mono_metadata_decode_row (&tables [MONO_TABLE_LOCALVARIABLE], i - 1, locals_cols, MONO_LOCALVARIABLE_SIZE);
587 res->locals [lindex].name = g_strdup (mono_metadata_string_heap (image, locals_cols [MONO_LOCALVARIABLE_NAME]));
588 res->locals [lindex].index = locals_cols [MONO_LOCALVARIABLE_INDEX];
589 res->locals [lindex].block = &res->code_blocks [sindex];
590 lindex ++;
592 //printf ("\t %s %d\n", mono_metadata_string_heap (image, locals_cols [MONO_LOCALVARIABLE_NAME]), locals_cols [MONO_LOCALVARIABLE_INDEX]);
596 return res;