1 /**
2  * \file
3  * Support for the portable PDB symbol
4  * file format
5  *
6  *
7  * Author:
8  *	Mono Project (http://www.mono-project.com)
9  *
10  * Copyright 2015 Xamarin Inc (http://www.xamarin.com)
11  * Licensed under the MIT license. See LICENSE file in the project root for full license information.
12  */
13 
14 #include <config.h>
15 #include <stdlib.h>
16 #include <stdio.h>
17 #include <errno.h>
18 #include <string.h>
19 #include <mono/metadata/metadata.h>
20 #include <mono/metadata/tabledefs.h>
21 #include <mono/metadata/tokentype.h>
22 #include <mono/metadata/debug-helpers.h>
23 #include <mono/metadata/mono-debug.h>
24 #include <mono/metadata/debug-internals.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>
29 #include <mono/utils/bsearch.h>
30 #include <mono/utils/mono-logger-internals.h>
31 
32 #include "debug-mono-ppdb.h"
33 
34 struct _MonoPPDBFile {
35 	MonoImage *image;
36 	GHashTable *doc_hash;
37 	GHashTable *method_hash;
38 };
39 
40 /* IMAGE_DEBUG_DIRECTORY structure */
41 typedef struct
42 {
43 	gint32 characteristics;
44 	gint32 time_date_stamp;
45 	gint16 major_version;
46 	gint16 minor_version;
47 	gint32 type;
48 	gint32 size_of_data;
49 	gint32 address;
50 	gint32 pointer;
51 }  ImageDebugDirectory;
52 
53 typedef struct {
54 	gint32 signature;
55 	guint8 guid [16];
56 	gint32 age;
57 } CodeviewDebugDirectory;
58 
59 typedef struct {
60 	guint8 guid [20];
61 	guint32 entry_point;
62 	guint64 referenced_tables;
63 } PdbStreamHeader;
64 
65 static gboolean
get_pe_debug_guid(MonoImage * image,guint8 * out_guid,gint32 * out_age,gint32 * out_timestamp)66 get_pe_debug_guid (MonoImage *image, guint8 *out_guid, gint32 *out_age, gint32 *out_timestamp)
67 {
68 	MonoPEDirEntry *debug_dir_entry;
69 	ImageDebugDirectory *debug_dir;
70 
71 	debug_dir_entry = &((MonoCLIImageInfo*)image->image_info)->cli_header.datadir.pe_debug;
72 	if (!debug_dir_entry->size)
73 		return FALSE;
74 
75 	int offset = mono_cli_rva_image_map (image, debug_dir_entry->rva);
76 	debug_dir = (ImageDebugDirectory*)(image->raw_data + offset);
77 	if (debug_dir->type == 2 && debug_dir->major_version == 0x100 && debug_dir->minor_version == 0x504d) {
78 		/* This is a 'CODEVIEW' debug directory */
79 		CodeviewDebugDirectory *dir = (CodeviewDebugDirectory*)(image->raw_data + debug_dir->pointer);
80 
81 		if (dir->signature == 0x53445352) {
82 			memcpy (out_guid, dir->guid, 16);
83 			*out_age = dir->age;
84 			*out_timestamp = debug_dir->time_date_stamp;
85 			return TRUE;
86 		}
87 	}
88 	return FALSE;
89 }
90 
91 static void
doc_free(gpointer key)92 doc_free (gpointer key)
93 {
94 	MonoDebugSourceInfo *info = (MonoDebugSourceInfo *)key;
95 
96 	g_free (info->source_file);
97 	g_free (info);
98 }
99 
100 static MonoPPDBFile*
create_ppdb_file(MonoImage * ppdb_image)101 create_ppdb_file (MonoImage *ppdb_image)
102 {
103 	MonoPPDBFile *ppdb;
104 
105 	ppdb = g_new0 (MonoPPDBFile, 1);
106 	ppdb->image = ppdb_image;
107 	ppdb->doc_hash = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) doc_free);
108 	ppdb->method_hash = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) g_free);
109 	return ppdb;
110 }
111 
112 MonoPPDBFile*
mono_ppdb_load_file(MonoImage * image,const guint8 * raw_contents,int size)113 mono_ppdb_load_file (MonoImage *image, const guint8 *raw_contents, int size)
114 {
115 	MonoImage *ppdb_image = NULL;
116 	const char *filename;
117 	char *s, *ppdb_filename;
118 	MonoImageOpenStatus status;
119 	guint8 pe_guid [16];
120 	gint32 pe_age;
121 	gint32 pe_timestamp;
122 
123 	if (image->tables [MONO_TABLE_DOCUMENT].rows) {
124 		/* Embedded ppdb */
125 		mono_image_addref (image);
126 		return create_ppdb_file (image);
127 	}
128 
129 	if (!get_pe_debug_guid (image, pe_guid, &pe_age, &pe_timestamp)) {
130 		mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_ASSEMBLY, "Image '%s' has no debug directory.", image->name);
131 		return NULL;
132 	}
133 
134 	if (raw_contents) {
135 		if (size > 4 && strncmp ((char*)raw_contents, "BSJB", 4) == 0)
136 			ppdb_image = mono_image_open_from_data_internal ((char*)raw_contents, size, TRUE, &status, FALSE, TRUE, NULL);
137 	} else {
138 		/* ppdb files drop the .exe/.dll extension */
139 		filename = mono_image_get_filename (image);
140 		if (strlen (filename) > 4 && (!strcmp (filename + strlen (filename) - 4, ".exe") || !strcmp (filename + strlen (filename) - 4, ".dll"))) {
141 			s = g_strdup (filename);
142 			s [strlen (filename) - 4] = '\0';
143 			ppdb_filename = g_strdup_printf ("%s.pdb", s);
144 			g_free (s);
145 		} else {
146 			ppdb_filename = g_strdup_printf ("%s.pdb", filename);
147 		}
148 
149 		ppdb_image = mono_image_open_metadata_only (ppdb_filename, &status);
150 		if (!ppdb_image)
151 			g_free (ppdb_filename);
152 	}
153 	if (!ppdb_image)
154 		return NULL;
155 
156 	/*
157 	 * Check that the images match.
158 	 * The same id is stored in the Debug Directory of the PE file, and in the
159 	 * #Pdb stream in the ppdb file.
160 	 */
161 	PdbStreamHeader *pdb_stream = (PdbStreamHeader*)ppdb_image->heap_pdb.data;
162 
163 	g_assert (pdb_stream);
164 
165 	/* The pdb id is a concentation of the pe guid and the timestamp */
166 	if (memcmp (pe_guid, pdb_stream->guid, 16) != 0 || memcmp (&pe_timestamp, pdb_stream->guid + 16, 4) != 0) {
167 		g_warning ("Symbol file %s doesn't match image %s", ppdb_image->name,
168 				   image->name);
169 		mono_image_close (ppdb_image);
170 		return NULL;
171 	}
172 
173 	return create_ppdb_file (ppdb_image);
174 }
175 
176 void
mono_ppdb_close(MonoDebugHandle * handle)177 mono_ppdb_close (MonoDebugHandle *handle)
178 {
179 	MonoPPDBFile *ppdb = handle->ppdb;
180 
181 	mono_image_close (ppdb->image);
182 	g_hash_table_destroy (ppdb->doc_hash);
183 	g_hash_table_destroy (ppdb->method_hash);
184 	g_free (ppdb);
185 }
186 
187 MonoDebugMethodInfo *
mono_ppdb_lookup_method(MonoDebugHandle * handle,MonoMethod * method)188 mono_ppdb_lookup_method (MonoDebugHandle *handle, MonoMethod *method)
189 {
190 	MonoDebugMethodInfo *minfo;
191 	MonoPPDBFile *ppdb = handle->ppdb;
192 
193 	if (handle->image != mono_class_get_image (mono_method_get_class (method)))
194 		return NULL;
195 
196 	mono_debugger_lock ();
197 
198 	minfo = (MonoDebugMethodInfo *)g_hash_table_lookup (ppdb->method_hash, method);
199 	if (minfo) {
200 		mono_debugger_unlock ();
201 		return minfo;
202 	}
203 
204 	minfo = g_new0 (MonoDebugMethodInfo, 1);
205 	minfo->index = 0;
206 	minfo->method = method;
207 	minfo->handle = handle;
208 
209 	g_hash_table_insert (ppdb->method_hash, method, minfo);
210 
211 	mono_debugger_unlock ();
212 
213 	return minfo;
214 }
215 
216 static MonoDebugSourceInfo*
get_docinfo(MonoPPDBFile * ppdb,MonoImage * image,int docidx)217 get_docinfo (MonoPPDBFile *ppdb, MonoImage *image, int docidx)
218 {
219 	MonoTableInfo *tables = image->tables;
220 	guint32 cols [MONO_DOCUMENT_SIZE];
221 	const char *ptr;
222 	const char *start;
223 	const char *part_ptr;
224 	int size, part_size, partidx, nparts;
225 	char sep;
226 	GString *s;
227 	MonoDebugSourceInfo *res, *cached;
228 
229 	mono_debugger_lock ();
230 	cached = (MonoDebugSourceInfo *)g_hash_table_lookup (ppdb->doc_hash, GUINT_TO_POINTER (docidx));
231 	mono_debugger_unlock ();
232 	if (cached)
233 		return cached;
234 
235 	mono_metadata_decode_row (&tables [MONO_TABLE_DOCUMENT], docidx-1, cols, MONO_DOCUMENT_SIZE);
236 
237 	ptr = mono_metadata_blob_heap (image, cols [MONO_DOCUMENT_NAME]);
238 	size = mono_metadata_decode_blob_size (ptr, &ptr);
239 	start = ptr;
240 
241 	// FIXME: UTF8
242 	sep = ptr [0];
243 	ptr ++;
244 
245 	s = g_string_new ("");
246 
247 	nparts = 0;
248 	while (ptr < start + size) {
249 		partidx = mono_metadata_decode_value (ptr, &ptr);
250 		if (nparts)
251 			g_string_append_c (s, sep);
252 		if (partidx) {
253 			part_ptr = mono_metadata_blob_heap (image, partidx);
254 			part_size = mono_metadata_decode_blob_size (part_ptr, &part_ptr);
255 
256 			// FIXME: UTF8
257 			g_string_append_len (s, part_ptr, part_size);
258 		}
259 		nparts ++;
260 	}
261 
262 	res = g_new0 (MonoDebugSourceInfo, 1);
263 	res->source_file = g_string_free (s, FALSE);
264 	res->guid = NULL;
265 	res->hash = (guint8*)mono_metadata_blob_heap (image, cols [MONO_DOCUMENT_HASH]);
266 
267 	mono_debugger_lock ();
268 	cached = (MonoDebugSourceInfo *)g_hash_table_lookup (ppdb->doc_hash, GUINT_TO_POINTER (docidx));
269 	if (!cached) {
270 		g_hash_table_insert (ppdb->doc_hash, GUINT_TO_POINTER (docidx), res);
271 	} else {
272 		doc_free (res);
273 		res = cached;
274 	}
275 	mono_debugger_unlock ();
276 	return res;
277 }
278 
279 static char*
get_docname(MonoPPDBFile * ppdb,MonoImage * image,int docidx)280 get_docname (MonoPPDBFile *ppdb, MonoImage *image, int docidx)
281 {
282 	MonoDebugSourceInfo *info;
283 
284 	info = get_docinfo (ppdb, image, docidx);
285 	return g_strdup (info->source_file);
286 }
287 
288 /**
289  * mono_ppdb_lookup_location:
290  * \param minfo A \c MonoDebugMethodInfo which can be retrieved by mono_debug_lookup_method().
291  * \param offset IL offset within the corresponding method's CIL code.
292  *
293  * This function is similar to mono_debug_lookup_location(), but we
294  * already looked up the method and also already did the
295  * native address -> IL offset mapping.
296  */
297 MonoDebugSourceLocation *
mono_ppdb_lookup_location(MonoDebugMethodInfo * minfo,uint32_t offset)298 mono_ppdb_lookup_location (MonoDebugMethodInfo *minfo, uint32_t offset)
299 {
300 	MonoPPDBFile *ppdb = minfo->handle->ppdb;
301 	MonoImage *image = ppdb->image;
302 	MonoMethod *method = minfo->method;
303 	MonoTableInfo *tables = image->tables;
304 	guint32 cols [MONO_METHODBODY_SIZE];
305 	const char *ptr;
306 	const char *end;
307 	char *docname;
308 	int idx, size, docidx, iloffset, delta_il, delta_lines, delta_cols, start_line, start_col, adv_line, adv_col;
309 	gboolean first = TRUE, first_non_hidden = TRUE;
310 	MonoDebugSourceLocation *location;
311 
312 	if (!method->token)
313 		return NULL;
314 
315 	idx = mono_metadata_token_index (method->token);
316 
317 	mono_metadata_decode_row (&tables [MONO_TABLE_METHODBODY], idx-1, cols, MONO_METHODBODY_SIZE);
318 
319 	docidx = cols [MONO_METHODBODY_DOCUMENT];
320 
321 	if (!cols [MONO_METHODBODY_SEQ_POINTS])
322 		return NULL;
323 	ptr = mono_metadata_blob_heap (image, cols [MONO_METHODBODY_SEQ_POINTS]);
324 	size = mono_metadata_decode_blob_size (ptr, &ptr);
325 	end = ptr + size;
326 
327 	/* Header */
328 	/* LocalSignature */
329 	mono_metadata_decode_value (ptr, &ptr);
330 	if (docidx == 0)
331 		docidx = mono_metadata_decode_value (ptr, &ptr);
332 	docname = get_docname (ppdb, image, docidx);
333 
334 	iloffset = 0;
335 	start_line = 0;
336 	start_col = 0;
337 	while (ptr < end) {
338 		delta_il = mono_metadata_decode_value (ptr, &ptr);
339 		if (!first && delta_il == 0) {
340 			/* document-record */
341 			docidx = mono_metadata_decode_value (ptr, &ptr);
342 			docname = get_docname (ppdb, image, docidx);
343 			continue;
344 		}
345 		if (!first && iloffset + delta_il > offset)
346 			break;
347 		iloffset += delta_il;
348 		first = FALSE;
349 
350 		delta_lines = mono_metadata_decode_value (ptr, &ptr);
351 		if (delta_lines == 0)
352 			delta_cols = mono_metadata_decode_value (ptr, &ptr);
353 		else
354 			delta_cols = mono_metadata_decode_signed_value (ptr, &ptr);
355 		if (delta_lines == 0 && delta_cols == 0)
356 			/* hidden-sequence-point-record */
357 			continue;
358 		if (first_non_hidden) {
359 			start_line = mono_metadata_decode_value (ptr, &ptr);
360 			start_col = mono_metadata_decode_value (ptr, &ptr);
361 		} else {
362 			adv_line = mono_metadata_decode_signed_value (ptr, &ptr);
363 			adv_col = mono_metadata_decode_signed_value (ptr, &ptr);
364 			start_line += adv_line;
365 			start_col += adv_col;
366 		}
367 		first_non_hidden = FALSE;
368 	}
369 
370 	location = g_new0 (MonoDebugSourceLocation, 1);
371 	location->source_file = docname;
372 	location->row = start_line;
373 	location->il_offset = iloffset;
374 
375 	return location;
376 }
377 
378 void
mono_ppdb_get_seq_points(MonoDebugMethodInfo * minfo,char ** source_file,GPtrArray ** source_file_list,int ** source_files,MonoSymSeqPoint ** seq_points,int * n_seq_points)379 mono_ppdb_get_seq_points (MonoDebugMethodInfo *minfo, char **source_file, GPtrArray **source_file_list, int **source_files, MonoSymSeqPoint **seq_points, int *n_seq_points)
380 {
381 	MonoPPDBFile *ppdb = minfo->handle->ppdb;
382 	MonoImage *image = ppdb->image;
383 	MonoMethod *method = minfo->method;
384 	MonoTableInfo *tables = image->tables;
385 	guint32 cols [MONO_METHODBODY_SIZE];
386 	const char *ptr;
387 	const char *end;
388 	MonoDebugSourceInfo *docinfo;
389 	int i, method_idx, size, docidx, iloffset, delta_il, delta_lines, delta_cols, start_line, start_col, adv_line, adv_col;
390 	gboolean first = TRUE, first_non_hidden = TRUE;
391 	GArray *sps;
392 	MonoSymSeqPoint sp;
393 	GPtrArray *sfiles = NULL;
394 	GPtrArray *sindexes = NULL;
395 
396 	if (source_file)
397 		*source_file = NULL;
398 	if (source_file_list)
399 		*source_file_list = NULL;
400 	if (source_files)
401 		*source_files = NULL;
402 	if (seq_points)
403 		*seq_points = NULL;
404 	if (n_seq_points)
405 		*n_seq_points = 0;
406 
407 	if (source_file_list)
408 		*source_file_list = sfiles = g_ptr_array_new ();
409 	if (source_files)
410 		sindexes = g_ptr_array_new ();
411 
412 	if (!method->token)
413 		return;
414 
415 	method_idx = mono_metadata_token_index (method->token);
416 
417 	mono_metadata_decode_row (&tables [MONO_TABLE_METHODBODY], method_idx-1, cols, MONO_METHODBODY_SIZE);
418 
419 	docidx = cols [MONO_METHODBODY_DOCUMENT];
420 
421 	if (!cols [MONO_METHODBODY_SEQ_POINTS])
422 		return;
423 
424 	ptr = mono_metadata_blob_heap (image, cols [MONO_METHODBODY_SEQ_POINTS]);
425 	size = mono_metadata_decode_blob_size (ptr, &ptr);
426 	end = ptr + size;
427 
428 	sps = g_array_new (FALSE, TRUE, sizeof (MonoSymSeqPoint));
429 
430 	/* Header */
431 	/* LocalSignature */
432 	mono_metadata_decode_value (ptr, &ptr);
433 	if (docidx == 0)
434 		docidx = mono_metadata_decode_value (ptr, &ptr);
435 	docinfo = get_docinfo (ppdb, image, docidx);
436 
437 	if (sfiles)
438 		g_ptr_array_add (sfiles, docinfo);
439 
440 	if (source_file)
441 		*source_file = g_strdup (docinfo->source_file);
442 
443 	iloffset = 0;
444 	start_line = 0;
445 	start_col = 0;
446 	while (ptr < end) {
447 		delta_il = mono_metadata_decode_value (ptr, &ptr);
448 		if (!first && delta_il == 0) {
449 			/* subsequent-document-record */
450 			docidx = mono_metadata_decode_value (ptr, &ptr);
451 			docinfo = get_docinfo (ppdb, image, docidx);
452 			if (sfiles)
453 				g_ptr_array_add (sfiles, docinfo);
454 			continue;
455 		}
456 		iloffset += delta_il;
457 		first = FALSE;
458 
459 		delta_lines = mono_metadata_decode_value (ptr, &ptr);
460 		if (delta_lines == 0)
461 			delta_cols = mono_metadata_decode_value (ptr, &ptr);
462 		else
463 			delta_cols = mono_metadata_decode_signed_value (ptr, &ptr);
464 
465 		if (delta_lines == 0 && delta_cols == 0) {
466 			/* Hidden sequence point */
467 			continue;
468 		}
469 
470 		if (first_non_hidden) {
471 			start_line = mono_metadata_decode_value (ptr, &ptr);
472 			start_col = mono_metadata_decode_value (ptr, &ptr);
473 		} else {
474 			adv_line = mono_metadata_decode_signed_value (ptr, &ptr);
475 			adv_col = mono_metadata_decode_signed_value (ptr, &ptr);
476 			start_line += adv_line;
477 			start_col += adv_col;
478 		}
479 		first_non_hidden = FALSE;
480 
481 		memset (&sp, 0, sizeof (sp));
482 		sp.il_offset = iloffset;
483 		sp.line = start_line;
484 		sp.column = start_col;
485 		sp.end_line = start_line + delta_lines;
486 		sp.end_column = start_col + delta_cols;
487 
488 		g_array_append_val (sps, sp);
489 		if (source_files)
490 			g_ptr_array_add (sindexes, GUINT_TO_POINTER (sfiles->len - 1));
491 	}
492 
493 	if (n_seq_points) {
494 		*n_seq_points = sps->len;
495 		g_assert (seq_points);
496 		*seq_points = g_new (MonoSymSeqPoint, sps->len);
497 		memcpy (*seq_points, sps->data, sps->len * sizeof (MonoSymSeqPoint));
498 	}
499 
500 	if (source_files) {
501 		*source_files = g_new (int, sps->len);
502 		for (i = 0; i < sps->len; ++i)
503 			(*source_files)[i] = GPOINTER_TO_INT (g_ptr_array_index (sindexes, i));
504 		g_ptr_array_free (sindexes, TRUE);
505 	}
506 
507 	g_array_free (sps, TRUE);
508 }
509 
510 MonoDebugLocalsInfo*
mono_ppdb_lookup_locals(MonoDebugMethodInfo * minfo)511 mono_ppdb_lookup_locals (MonoDebugMethodInfo *minfo)
512 {
513 	MonoPPDBFile *ppdb = minfo->handle->ppdb;
514 	MonoImage *image = ppdb->image;
515 	MonoTableInfo *tables = image->tables;
516 	MonoMethod *method = minfo->method;
517 	guint32 cols [MONO_LOCALSCOPE_SIZE];
518 	guint32 locals_cols [MONO_LOCALVARIABLE_SIZE];
519 	int i, lindex, sindex, method_idx, start_scope_idx, scope_idx, locals_idx, locals_end_idx, nscopes;
520 	MonoDebugLocalsInfo *res;
521 	MonoMethodSignature *sig;
522 
523 	if (!method->token)
524 		return NULL;
525 
526 	sig = mono_method_signature (method);
527 	if (!sig)
528 		return NULL;
529 
530 	method_idx = mono_metadata_token_index (method->token);
531 
532 	start_scope_idx = mono_metadata_localscope_from_methoddef (image, method_idx);
533 
534 	if (!start_scope_idx)
535 		return NULL;
536 
537 	/* Compute number of locals and scopes */
538 	scope_idx = start_scope_idx;
539 	mono_metadata_decode_row (&tables [MONO_TABLE_LOCALSCOPE], scope_idx-1, cols, MONO_LOCALSCOPE_SIZE);
540 	locals_idx = cols [MONO_LOCALSCOPE_VARIABLELIST];
541 
542 	// https://github.com/dotnet/roslyn/blob/2ae8d5fed96ab3f1164031f9b4ac827f53289159/docs/specs/PortablePdb-Metadata.md#LocalScopeTable
543 	//
544 	// The variableList attribute in the pdb metadata table is a contiguous array that starts at a
545 	// given offset (locals_idx) above and
546 	//
547 	// """
548 	// continues to the smaller of:
549 	//
550 	// the last row of the LocalVariable table
551 	// the next run of LocalVariables, found by inspecting the VariableList of the next row in this LocalScope table.
552 	// """
553 	// this endpoint becomes locals_end_idx below
554 
555 	// March to the last scope that is in this method
556 	while (scope_idx <= tables [MONO_TABLE_LOCALSCOPE].rows) {
557 		mono_metadata_decode_row (&tables [MONO_TABLE_LOCALSCOPE], scope_idx-1, cols, MONO_LOCALSCOPE_SIZE);
558 		if (cols [MONO_LOCALSCOPE_METHOD] != method_idx)
559 			break;
560 		scope_idx ++;
561 	}
562 	// The number of scopes is the difference in the indices
563 	// for the first and last scopes
564 	nscopes = scope_idx - start_scope_idx;
565 
566 	// Ends with "the last row of the LocalVariable table"
567 	// this happens if the above loop marched one past the end
568 	// of the rows
569 	if (scope_idx > tables [MONO_TABLE_LOCALSCOPE].rows) {
570 		locals_end_idx = tables [MONO_TABLE_LOCALVARIABLE].rows + 1;
571 	} else {
572 		// Ends with "the next run of LocalVariables,
573 		// found by inspecting the VariableList of the next row in this LocalScope table."
574 		locals_end_idx = cols [MONO_LOCALSCOPE_VARIABLELIST];
575 	}
576 
577 	res = g_new0 (MonoDebugLocalsInfo, 1);
578 	res->num_blocks = nscopes;
579 	res->code_blocks = g_new0 (MonoDebugCodeBlock, res->num_blocks);
580 	res->num_locals = locals_end_idx - locals_idx;
581 	res->locals = g_new0 (MonoDebugLocalVar, res->num_locals);
582 
583 	lindex = 0;
584 	for (sindex = 0; sindex < nscopes; ++sindex) {
585 		scope_idx = start_scope_idx + sindex;
586 		mono_metadata_decode_row (&tables [MONO_TABLE_LOCALSCOPE], scope_idx-1, cols, MONO_LOCALSCOPE_SIZE);
587 
588 		locals_idx = cols [MONO_LOCALSCOPE_VARIABLELIST];
589 		if (scope_idx == tables [MONO_TABLE_LOCALSCOPE].rows) {
590 			locals_end_idx = tables [MONO_TABLE_LOCALVARIABLE].rows + 1;
591 		} else {
592 			locals_end_idx = mono_metadata_decode_row_col (&tables [MONO_TABLE_LOCALSCOPE], scope_idx-1 + 1, MONO_LOCALSCOPE_VARIABLELIST);
593 		}
594 
595 		res->code_blocks [sindex].start_offset = cols [MONO_LOCALSCOPE_STARTOFFSET];
596 		res->code_blocks [sindex].end_offset = cols [MONO_LOCALSCOPE_STARTOFFSET] + cols [MONO_LOCALSCOPE_LENGTH];
597 
598 		//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);
599 
600 		for (i = locals_idx; i < locals_end_idx; ++i) {
601 			mono_metadata_decode_row (&tables [MONO_TABLE_LOCALVARIABLE], i - 1, locals_cols, MONO_LOCALVARIABLE_SIZE);
602 
603 			res->locals [lindex].name = g_strdup (mono_metadata_string_heap (image, locals_cols [MONO_LOCALVARIABLE_NAME]));
604 			res->locals [lindex].index = locals_cols [MONO_LOCALVARIABLE_INDEX];
605 			res->locals [lindex].block = &res->code_blocks [sindex];
606 			lindex ++;
607 
608 			//printf ("\t %s %d\n", mono_metadata_string_heap (image, locals_cols [MONO_LOCALVARIABLE_NAME]), locals_cols [MONO_LOCALVARIABLE_INDEX]);
609 		}
610 	}
611 
612 	return res;
613 }
614 
615 /*
616 * We use this to pass context information to the row locator
617 */
618 typedef struct {
619 	int idx;			/* The index that we are trying to locate */
620 	int col_idx;		/* The index in the row where idx may be stored */
621 	MonoTableInfo *t;	/* pointer to the table */
622 	guint32 result;
623 } locator_t;
624 
625 static int
table_locator(const void * a,const void * b)626 table_locator (const void *a, const void *b)
627 {
628 	locator_t *loc = (locator_t *)a;
629 	const char *bb = (const char *)b;
630 	guint32 table_index = (bb - loc->t->base) / loc->t->row_size;
631 	guint32 col;
632 
633 	col = mono_metadata_decode_row_col(loc->t, table_index, loc->col_idx);
634 
635 	if (loc->idx == col) {
636 		loc->result = table_index;
637 		return 0;
638 	}
639 	if (loc->idx < col)
640 		return -1;
641 	else
642 		return 1;
643 }
644 
645 static gboolean
compare_guid(guint8 * guid1,guint8 * guid2)646 compare_guid (guint8* guid1, guint8* guid2) {
647 	for (int i = 0; i < 16; i++) {
648 		if (guid1 [i] != guid2 [i])
649 			return FALSE;
650 	}
651 	return TRUE;
652 }
653 
654 // for parent_type see HasCustomDebugInformation table at
655 // https://github.com/dotnet/corefx/blob/master/src/System.Reflection.Metadata/specs/PortablePdb-Metadata.md
656 static const char*
lookup_custom_debug_information(MonoImage * image,guint32 token,uint8_t parent_type,guint8 * guid)657 lookup_custom_debug_information (MonoImage* image, guint32 token, uint8_t parent_type, guint8* guid)
658 {
659 	MonoTableInfo *tables = image->tables;
660 	MonoTableInfo *table = &tables[MONO_TABLE_CUSTOMDEBUGINFORMATION];
661 	locator_t loc;
662 
663 	if (!table->base)
664 		return 0;
665 
666 	loc.idx = (mono_metadata_token_index (token) << 5) | parent_type;
667 	loc.col_idx = MONO_CUSTOMDEBUGINFORMATION_PARENT;
668 	loc.t = table;
669 
670 	if (!mono_binary_search (&loc, table->base, table->rows, table->row_size, table_locator))
671 		return NULL;
672 	// Great we found one of possibly many CustomDebugInformations of this entity they are distinguished by KIND guid
673 	// First try on this index found by binary search...(it's most likeley to be only one and binary search found the one we want)
674 	if (compare_guid (guid, (guint8*)mono_metadata_guid_heap (image, mono_metadata_decode_row_col (table, loc.result, MONO_CUSTOMDEBUGINFORMATION_KIND))))
675 		return mono_metadata_blob_heap (image, mono_metadata_decode_row_col (table, loc.result, MONO_CUSTOMDEBUGINFORMATION_VALUE));
676 
677 	// Move forward from binary found index, until parent token differs
678 	for (int i = loc.result + 1; i < table->rows; i++)
679 	{
680 		if (mono_metadata_decode_row_col (table, i, MONO_CUSTOMDEBUGINFORMATION_PARENT) != loc.idx)
681 			break;
682 		if (compare_guid (guid, (guint8*)mono_metadata_guid_heap (image, mono_metadata_decode_row_col (table, i, MONO_CUSTOMDEBUGINFORMATION_KIND))))
683 			return mono_metadata_blob_heap (image, mono_metadata_decode_row_col (table, i, MONO_CUSTOMDEBUGINFORMATION_VALUE));
684 	}
685 
686 	// Move backward from binary found index, until parent token differs
687 	for (int i = loc.result - 1; i >= 0; i--) {
688 		if (mono_metadata_decode_row_col (table, i, MONO_CUSTOMDEBUGINFORMATION_PARENT) != loc.idx)
689 			break;
690 		if (compare_guid (guid, (guint8*)mono_metadata_guid_heap (image, mono_metadata_decode_row_col (table, i, MONO_CUSTOMDEBUGINFORMATION_KIND))))
691 			return mono_metadata_blob_heap (image, mono_metadata_decode_row_col (table, i, MONO_CUSTOMDEBUGINFORMATION_VALUE));
692 	}
693 	return NULL;
694 }
695 
696 MonoDebugMethodAsyncInfo*
mono_ppdb_lookup_method_async_debug_info(MonoDebugMethodInfo * minfo)697 mono_ppdb_lookup_method_async_debug_info (MonoDebugMethodInfo *minfo)
698 {
699 	MonoMethod *method = minfo->method;
700 	MonoPPDBFile *ppdb = minfo->handle->ppdb;
701 	MonoImage *image = ppdb->image;
702 
703 	// Guid is taken from Roslyn source code:
704 	// https://github.com/dotnet/roslyn/blob/1ad4b58/src/Dependencies/CodeAnalysis.Metadata/PortableCustomDebugInfoKinds.cs#L9
705 	guint8 async_method_stepping_information_guid [16] = { 0xC5, 0x2A, 0xFD, 0x54, 0x25, 0xE9, 0x1A, 0x40, 0x9C, 0x2A, 0xF9, 0x4F, 0x17, 0x10, 0x72, 0xF8 };
706 	char const *blob = lookup_custom_debug_information (image, method->token, 0, async_method_stepping_information_guid);
707 	if (!blob)
708 		return NULL;
709 	int blob_len = mono_metadata_decode_blob_size (blob, &blob);
710 	MonoDebugMethodAsyncInfo* res = g_new0 (MonoDebugMethodAsyncInfo, 1);
711 	char const *pointer = blob;
712 
713 	// Format of this blob is taken from Roslyn source code:
714 	// https://github.com/dotnet/roslyn/blob/1ad4b58/src/Compilers/Core/Portable/PEWriter/MetadataWriter.PortablePdb.cs#L566
715 
716 	pointer += 4;//catch_handler_offset
717 	while (pointer - blob < blob_len) {
718 		res->num_awaits++;
719 		pointer += 8;//yield_offsets+resume_offsets
720 		mono_metadata_decode_value (pointer, &pointer);//move_next_method_token
721 	}
722 	g_assert(pointer - blob == blob_len); //Check that we used all blob data
723 	pointer = blob; //reset pointer after we figured num_awaits
724 
725 	res->yield_offsets = g_new (uint32_t, res->num_awaits);
726 	res->resume_offsets = g_new (uint32_t, res->num_awaits);
727 	res->move_next_method_token = g_new (uint32_t, res->num_awaits);
728 
729 	res->catch_handler_offset = read32 (pointer); pointer += 4;
730 	for (int i = 0; i < res->num_awaits; i++) {
731 		res->yield_offsets [i] = read32 (pointer); pointer += 4;
732 		res->resume_offsets [i] = read32 (pointer); pointer += 4;
733 		res->move_next_method_token [i] = mono_metadata_decode_value (pointer, &pointer);
734 	}
735 	return res;
736 }
737