2 * debug-mono-ppdb.c: Support for the portable PDB symbol
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.
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 {
35 GHashTable *method_hash;
38 /* IMAGE_DEBUG_DIRECTORY structure */
41 gint32 characteristics;
42 gint32 time_date_stamp;
49 } ImageDebugDirectory;
55 } CodeviewDebugDirectory;
60 guint64 referenced_tables;
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)
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);
82 *out_timestamp = debug_dir->time_date_stamp;
90 doc_free (gpointer key)
92 MonoDebugSourceInfo *info = (MonoDebugSourceInfo *)key;
94 g_free (info->source_file);
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;
111 if (size > 4 && strncmp ((char*)raw_contents, "BSJB", 4) == 0)
112 ppdb_image = mono_image_open_from_data_internal ((char*)raw_contents, size, TRUE, &status, FALSE, TRUE, NULL);
114 /* ppdb files drop the .exe/.dll extension */
115 filename = mono_image_get_filename (image);
116 if (strlen (filename) > 4 && (!strcmp (filename + strlen (filename) - 4, ".exe") || !strcmp (filename + strlen (filename) - 4, ".dll"))) {
117 s = g_strdup (filename);
118 s [strlen (filename) - 4] = '\0';
119 ppdb_filename = g_strdup_printf ("%s.pdb", s);
122 ppdb_filename = g_strdup_printf ("%s.pdb", filename);
125 ppdb_image = mono_image_open_metadata_only (ppdb_filename, &status);
127 g_free (ppdb_filename);
133 * Check that the images match.
134 * The same id is stored in the Debug Directory of the PE file, and in the
135 * #Pdb stream in the ppdb file.
137 if (get_pe_debug_guid (image, pe_guid, &pe_age, &pe_timestamp)) {
138 PdbStreamHeader *pdb_stream = (PdbStreamHeader*)ppdb_image->heap_pdb.data;
140 g_assert (pdb_stream);
142 /* The pdb id is a concentation of the pe guid and the timestamp */
143 if (memcmp (pe_guid, pdb_stream->guid, 16) != 0 || memcmp (&pe_timestamp, pdb_stream->guid + 16, 4) != 0) {
144 g_warning ("Symbol file %s doesn't match image %s", ppdb_image->name,
146 mono_image_close (ppdb_image);
151 ppdb = g_new0 (MonoPPDBFile, 1);
152 ppdb->image = ppdb_image;
153 ppdb->doc_hash = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) doc_free);
154 ppdb->method_hash = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) g_free);
160 mono_ppdb_close (MonoDebugHandle *handle)
162 MonoPPDBFile *ppdb = handle->ppdb;
164 mono_image_close (ppdb->image);
165 g_hash_table_destroy (ppdb->doc_hash);
166 g_hash_table_destroy (ppdb->method_hash);
170 MonoDebugMethodInfo *
171 mono_ppdb_lookup_method (MonoDebugHandle *handle, MonoMethod *method)
173 MonoDebugMethodInfo *minfo;
174 MonoPPDBFile *ppdb = handle->ppdb;
176 if (handle->image != mono_class_get_image (mono_method_get_class (method)))
179 mono_debugger_lock ();
181 minfo = (MonoDebugMethodInfo *)g_hash_table_lookup (ppdb->method_hash, method);
183 mono_debugger_unlock ();
187 minfo = g_new0 (MonoDebugMethodInfo, 1);
189 minfo->method = method;
190 minfo->handle = handle;
192 g_hash_table_insert (ppdb->method_hash, method, minfo);
194 mono_debugger_unlock ();
199 static MonoDebugSourceInfo*
200 get_docinfo (MonoPPDBFile *ppdb, MonoImage *image, int docidx)
202 MonoTableInfo *tables = image->tables;
203 guint32 cols [MONO_DOCUMENT_SIZE];
206 const char *part_ptr;
207 int size, part_size, partidx, nparts;
210 MonoDebugSourceInfo *res, *cached;
212 mono_debugger_lock ();
213 cached = (MonoDebugSourceInfo *)g_hash_table_lookup (ppdb->doc_hash, GUINT_TO_POINTER (docidx));
214 mono_debugger_unlock ();
218 mono_metadata_decode_row (&tables [MONO_TABLE_DOCUMENT], docidx-1, cols, MONO_DOCUMENT_SIZE);
220 ptr = mono_metadata_blob_heap (image, cols [MONO_DOCUMENT_NAME]);
221 size = mono_metadata_decode_blob_size (ptr, &ptr);
228 s = g_string_new ("");
231 while (ptr < start + size) {
232 partidx = mono_metadata_decode_value (ptr, &ptr);
234 g_string_append_c (s, sep);
236 part_ptr = mono_metadata_blob_heap (image, partidx);
237 part_size = mono_metadata_decode_blob_size (part_ptr, &part_ptr);
240 g_string_append_len (s, part_ptr, part_size);
245 res = g_new0 (MonoDebugSourceInfo, 1);
246 res->source_file = g_string_free (s, FALSE);
248 res->hash = (guint8*)mono_metadata_blob_heap (image, cols [MONO_DOCUMENT_HASH]);
250 mono_debugger_lock ();
251 cached = (MonoDebugSourceInfo *)g_hash_table_lookup (ppdb->doc_hash, GUINT_TO_POINTER (docidx));
253 g_hash_table_insert (ppdb->doc_hash, GUINT_TO_POINTER (docidx), res);
258 mono_debugger_unlock ();
263 get_docname (MonoPPDBFile *ppdb, MonoImage *image, int docidx)
265 MonoDebugSourceInfo *info;
267 info = get_docinfo (ppdb, image, docidx);
268 return g_strdup (info->source_file);
272 * mono_ppdb_lookup_location:
273 * @minfo: A `MonoDebugMethodInfo' which can be retrieved by
274 * mono_debug_lookup_method().
275 * @offset: IL offset within the corresponding method's CIL code.
277 * This function is similar to mono_debug_lookup_location(), but we
278 * already looked up the method and also already did the
279 * `native address -> IL offset' mapping.
281 MonoDebugSourceLocation *
282 mono_ppdb_lookup_location (MonoDebugMethodInfo *minfo, uint32_t offset)
284 MonoPPDBFile *ppdb = minfo->handle->ppdb;
285 MonoImage *image = ppdb->image;
286 MonoMethod *method = minfo->method;
287 MonoTableInfo *tables = image->tables;
288 guint32 cols [MONO_METHODBODY_SIZE];
292 int idx, size, docidx, iloffset, delta_il, delta_lines, delta_cols, start_line, start_col, adv_line, adv_col;
293 gboolean first = TRUE, first_non_hidden = TRUE;
294 MonoDebugSourceLocation *location;
299 idx = mono_metadata_token_index (method->token);
301 mono_metadata_decode_row (&tables [MONO_TABLE_METHODBODY], idx-1, cols, MONO_METHODBODY_SIZE);
303 docidx = cols [MONO_METHODBODY_DOCUMENT];
305 if (!cols [MONO_METHODBODY_SEQ_POINTS])
307 ptr = mono_metadata_blob_heap (image, cols [MONO_METHODBODY_SEQ_POINTS]);
308 size = mono_metadata_decode_blob_size (ptr, &ptr);
313 mono_metadata_decode_value (ptr, &ptr);
315 docidx = mono_metadata_decode_value (ptr, &ptr);
316 docname = get_docname (ppdb, image, docidx);
322 delta_il = mono_metadata_decode_value (ptr, &ptr);
323 if (!first && delta_il == 0) {
324 /* document-record */
325 docidx = mono_metadata_decode_value (ptr, &ptr);
326 docname = get_docname (ppdb, image, docidx);
329 if (!first && iloffset + delta_il > offset)
331 iloffset += delta_il;
334 delta_lines = mono_metadata_decode_value (ptr, &ptr);
335 if (delta_lines == 0)
336 delta_cols = mono_metadata_decode_value (ptr, &ptr);
338 delta_cols = mono_metadata_decode_signed_value (ptr, &ptr);
339 if (delta_lines == 0 && delta_cols == 0)
340 /* hidden-sequence-point-record */
342 if (first_non_hidden) {
343 start_line = mono_metadata_decode_value (ptr, &ptr);
344 start_col = mono_metadata_decode_value (ptr, &ptr);
346 adv_line = mono_metadata_decode_signed_value (ptr, &ptr);
347 adv_col = mono_metadata_decode_signed_value (ptr, &ptr);
348 start_line += adv_line;
349 start_col += adv_col;
351 first_non_hidden = FALSE;
354 location = g_new0 (MonoDebugSourceLocation, 1);
355 location->source_file = docname;
356 location->row = start_line;
357 location->il_offset = iloffset;
363 mono_ppdb_get_seq_points (MonoDebugMethodInfo *minfo, char **source_file, GPtrArray **source_file_list, int **source_files, MonoSymSeqPoint **seq_points, int *n_seq_points)
365 MonoPPDBFile *ppdb = minfo->handle->ppdb;
366 MonoImage *image = ppdb->image;
367 MonoMethod *method = minfo->method;
368 MonoTableInfo *tables = image->tables;
369 guint32 cols [MONO_METHODBODY_SIZE];
372 MonoDebugSourceInfo *docinfo;
373 int i, method_idx, size, docidx, iloffset, delta_il, delta_lines, delta_cols, start_line, start_col, adv_line, adv_col;
374 gboolean first = TRUE, first_non_hidden = TRUE;
377 GPtrArray *sfiles = NULL;
378 GPtrArray *sindexes = NULL;
382 if (source_file_list)
383 *source_file_list = NULL;
385 *source_files = NULL;
391 if (source_file_list)
392 *source_file_list = sfiles = g_ptr_array_new ();
394 sindexes = g_ptr_array_new ();
399 method_idx = mono_metadata_token_index (method->token);
401 mono_metadata_decode_row (&tables [MONO_TABLE_METHODBODY], method_idx-1, cols, MONO_METHODBODY_SIZE);
403 docidx = cols [MONO_METHODBODY_DOCUMENT];
405 if (!cols [MONO_METHODBODY_SEQ_POINTS])
408 ptr = mono_metadata_blob_heap (image, cols [MONO_METHODBODY_SEQ_POINTS]);
409 size = mono_metadata_decode_blob_size (ptr, &ptr);
412 sps = g_array_new (FALSE, TRUE, sizeof (MonoSymSeqPoint));
416 mono_metadata_decode_value (ptr, &ptr);
418 docidx = mono_metadata_decode_value (ptr, &ptr);
419 docinfo = get_docinfo (ppdb, image, docidx);
422 g_ptr_array_add (sfiles, docinfo);
428 delta_il = mono_metadata_decode_value (ptr, &ptr);
429 if (!first && delta_il == 0) {
430 /* subsequent-document-record */
431 docidx = mono_metadata_decode_value (ptr, &ptr);
432 docinfo = get_docinfo (ppdb, image, docidx);
434 g_ptr_array_add (sfiles, docinfo);
437 iloffset += delta_il;
440 delta_lines = mono_metadata_decode_value (ptr, &ptr);
441 if (delta_lines == 0)
442 delta_cols = mono_metadata_decode_value (ptr, &ptr);
444 delta_cols = mono_metadata_decode_signed_value (ptr, &ptr);
446 if (delta_lines == 0 && delta_cols == 0) {
447 /* Hidden sequence point */
451 if (first_non_hidden) {
452 start_line = mono_metadata_decode_value (ptr, &ptr);
453 start_col = mono_metadata_decode_value (ptr, &ptr);
455 adv_line = mono_metadata_decode_signed_value (ptr, &ptr);
456 adv_col = mono_metadata_decode_signed_value (ptr, &ptr);
457 start_line += adv_line;
458 start_col += adv_col;
460 first_non_hidden = FALSE;
462 memset (&sp, 0, sizeof (sp));
463 sp.il_offset = iloffset;
464 sp.line = start_line;
465 sp.column = start_col;
466 sp.end_line = start_line + delta_lines;
467 sp.end_column = start_col + delta_cols;
469 g_array_append_val (sps, sp);
471 g_ptr_array_add (sindexes, GUINT_TO_POINTER (sfiles->len - 1));
475 *n_seq_points = sps->len;
476 g_assert (seq_points);
477 *seq_points = g_new (MonoSymSeqPoint, sps->len);
478 memcpy (*seq_points, sps->data, sps->len * sizeof (MonoSymSeqPoint));
482 *source_file = g_strdup (((MonoDebugSourceInfo*)g_ptr_array_index (sfiles, 0))->source_file);
484 *source_files = g_new (int, sps->len);
485 for (i = 0; i < sps->len; ++i)
486 (*source_files)[i] = GPOINTER_TO_INT (g_ptr_array_index (sindexes, i));
487 g_ptr_array_free (sindexes, TRUE);
490 g_array_free (sps, TRUE);
494 mono_ppdb_lookup_locals (MonoDebugMethodInfo *minfo)
496 MonoPPDBFile *ppdb = minfo->handle->ppdb;
497 MonoImage *image = ppdb->image;
498 MonoTableInfo *tables = image->tables;
499 MonoMethod *method = minfo->method;
500 guint32 cols [MONO_LOCALSCOPE_SIZE];
501 guint32 locals_cols [MONO_LOCALVARIABLE_SIZE];
502 int i, lindex, sindex, method_idx, start_scope_idx, scope_idx, locals_idx, locals_end_idx, nscopes;
503 MonoDebugLocalsInfo *res;
504 MonoMethodSignature *sig;
509 sig = mono_method_signature (method);
513 method_idx = mono_metadata_token_index (method->token);
515 start_scope_idx = mono_metadata_localscope_from_methoddef (image, method_idx);
517 if (!start_scope_idx)
520 /* Compute number of locals and scopes */
521 scope_idx = start_scope_idx;
522 mono_metadata_decode_row (&tables [MONO_TABLE_LOCALSCOPE], scope_idx-1, cols, MONO_LOCALSCOPE_SIZE);
523 locals_idx = cols [MONO_LOCALSCOPE_VARIABLELIST];
525 // https://github.com/dotnet/roslyn/blob/2ae8d5fed96ab3f1164031f9b4ac827f53289159/docs/specs/PortablePdb-Metadata.md#LocalScopeTable
527 // The variableList attribute in the pdb metadata table is a contiguous array that starts at a
528 // given offset (locals_idx) above and
531 // continues to the smaller of:
533 // the last row of the LocalVariable table
534 // the next run of LocalVariables, found by inspecting the VariableList of the next row in this LocalScope table.
536 // this endpoint becomes locals_end_idx below
538 // March to the last scope that is in this method
539 while (scope_idx <= tables [MONO_TABLE_LOCALSCOPE].rows) {
540 mono_metadata_decode_row (&tables [MONO_TABLE_LOCALSCOPE], scope_idx-1, cols, MONO_LOCALSCOPE_SIZE);
541 if (cols [MONO_LOCALSCOPE_METHOD] != method_idx)
545 // The number of scopes is the difference in the indices
546 // for the first and last scopes
547 nscopes = scope_idx - start_scope_idx;
549 // Ends with "the last row of the LocalVariable table"
550 // this happens if the above loop marched one past the end
552 if (scope_idx > tables [MONO_TABLE_LOCALSCOPE].rows) {
553 locals_end_idx = tables [MONO_TABLE_LOCALVARIABLE].rows + 1;
555 // Ends with "the next run of LocalVariables,
556 // found by inspecting the VariableList of the next row in this LocalScope table."
557 locals_end_idx = cols [MONO_LOCALSCOPE_VARIABLELIST];
560 res = g_new0 (MonoDebugLocalsInfo, 1);
561 res->num_blocks = nscopes;
562 res->code_blocks = g_new0 (MonoDebugCodeBlock, res->num_blocks);
563 res->num_locals = locals_end_idx - locals_idx;
564 res->locals = g_new0 (MonoDebugLocalVar, res->num_locals);
567 for (sindex = 0; sindex < nscopes; ++sindex) {
568 scope_idx = start_scope_idx + sindex;
569 mono_metadata_decode_row (&tables [MONO_TABLE_LOCALSCOPE], scope_idx-1, cols, MONO_LOCALSCOPE_SIZE);
571 locals_idx = cols [MONO_LOCALSCOPE_VARIABLELIST];
572 if (scope_idx == tables [MONO_TABLE_LOCALSCOPE].rows) {
573 locals_end_idx = tables [MONO_TABLE_LOCALVARIABLE].rows + 1;
575 locals_end_idx = mono_metadata_decode_row_col (&tables [MONO_TABLE_LOCALSCOPE], scope_idx-1 + 1, MONO_LOCALSCOPE_VARIABLELIST);
578 res->code_blocks [sindex].start_offset = cols [MONO_LOCALSCOPE_STARTOFFSET];
579 res->code_blocks [sindex].end_offset = cols [MONO_LOCALSCOPE_STARTOFFSET] + cols [MONO_LOCALSCOPE_LENGTH];
581 //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);
583 for (i = locals_idx; i < locals_end_idx; ++i) {
584 mono_metadata_decode_row (&tables [MONO_TABLE_LOCALVARIABLE], i - 1, locals_cols, MONO_LOCALVARIABLE_SIZE);
586 res->locals [lindex].name = g_strdup (mono_metadata_string_heap (image, locals_cols [MONO_LOCALVARIABLE_NAME]));
587 res->locals [lindex].index = locals_cols [MONO_LOCALVARIABLE_INDEX];
588 res->locals [lindex].block = &res->code_blocks [sindex];
591 //printf ("\t %s %d\n", mono_metadata_string_heap (image, locals_cols [MONO_LOCALVARIABLE_NAME]), locals_cols [MONO_LOCALVARIABLE_INDEX]);