Browse Source

2002-03-19 Martin Baulig <[email protected]>

	This is an implementation of the System.Diagnostics.SymbolStore.SymbolWriter
	interface. It's still work in progress and not yet used anywhere.

	There is some preliminary documentation in the source files and some more
	docu in the README and README.relocation-table files.

	* IMonoSymbolWriter.cs: New file.
	* MonoDwarfFileWriter.cs: New file.
	* MonoSymbolDocumentWriter.cs: New file.
	* MonoSymbolWriter.cs: New file.

	* README, README.relocation-table: Documentation.

svn path=/trunk/mcs/; revision=3193
Martin Baulig 24 years ago
parent
commit
82b6dfc00a

+ 15 - 0
mcs/class/Mono.CSharp.Debugger/ChangeLog

@@ -0,0 +1,15 @@
+2002-03-19  Martin Baulig  <[email protected]>
+
+	This is an implementation of the System.Diagnostics.SymbolStore.SymbolWriter
+	interface. It's still work in progress and not yet used anywhere.
+
+	There is some preliminary documentation in the source files and some more
+	docu in the README and README.relocation-table files.
+
+	* IMonoSymbolWriter.cs: New file.
+	* MonoDwarfFileWriter.cs: New file.
+	* MonoSymbolDocumentWriter.cs: New file.
+	* MonoSymbolWriter.cs: New file.
+
+	* README, README.relocation-table: Documentation.
+

+ 38 - 0
mcs/class/Mono.CSharp.Debugger/IMonoSymbolWriter.cs

@@ -0,0 +1,38 @@
+//
+// System.Diagnostics.SymbolStore/IMonoSymbolWriter.cs
+//
+// Author:
+//   Martin Baulig ([email protected])
+//
+// This interface is derived from System.Diagnostics.SymbolStore.ISymbolWriter.
+//
+// (C) 2002 Ximian, Inc.  http://www.ximian.com
+//
+
+using System;
+using System.Reflection;
+using System.Reflection.Emit;
+using System.Diagnostics.SymbolStore;
+using System.Collections;
+using System.IO;
+	
+namespace Mono.CSharp.Debugger
+{
+	public interface IMonoSymbolWriter : ISymbolWriter
+	{
+		// The ISymbolWriter interface has an `IntPtr emitter' argument which
+		// seems to be a pointer an unmanaged interface containing the actual
+		// symbol writer. I was unable to find any documentation about how
+		// exactly this is used - but it seems to be in some proprietary,
+		// undocumented DLL.
+		//
+		// Since I want this interface to be usable on the Windows platform as
+		// well, I added this custom constructor. You should use this version
+		// of `Initialize' to make sure you're actually using this implementation.
+		void Initialize (string filename);
+
+		// This is some kind of a hack - there isn't any way yet to get the
+		// method name and source file back from a token.
+		void OpenMethod (SymbolToken token, string name, string source_file);
+	}
+}

+ 24 - 0
mcs/class/Mono.CSharp.Debugger/Mono.CSharp.Debugger.build

@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+
+<!-- NAnt build file for Mono.CSharp.Debugger.dll -->
+
+<project name="Mono.CSharp.Debugger" default="build">
+	<property name="debug" value="false"/>
+
+	<target name="build">
+		<csc target="library" output="../lib/Mono.CSharp.Debugger.dll" debug="${debug}">
+			<arg value="/nowarn:1595"/>
+			<arg value="/unsafe"/>
+			<sources>
+				<includes name="**/*.cs"/> 
+				<excludes name="Test/**"/>
+			</sources>
+			<references>
+				<includes name="../lib/corlib.dll"/>
+			</references>
+		</csc>
+	</target>
+	<target name="clean">
+		<delete file="../lib/Mono.CSharp.Debugger.dll" failonerror="false"/>
+	</target>
+</project>

+ 661 - 0
mcs/class/Mono.CSharp.Debugger/MonoDwarfFileWriter.cs

@@ -0,0 +1,661 @@
+//
+// System.Diagnostics.SymbolStore/MonoDwarfWriter.cs
+//
+// Author:
+//   Martin Baulig ([email protected])
+//
+// This is the default implementation of the System.Diagnostics.SymbolStore.ISymbolWriter
+// interface.
+//
+// (C) 2002 Ximian, Inc.  http://www.ximian.com
+//
+
+using System;
+using System.Reflection;
+using System.Reflection.Emit;
+using System.Diagnostics.SymbolStore;
+using System.IO;
+	
+namespace Mono.CSharp.Debugger
+{
+
+	public class DwarfFileWriter
+	{
+		protected const string producer_id = "Mono C# Compiler 0.01 03-18-2002";
+
+		protected CompileUnit[] compile_units = new CompileUnit [0];
+		protected StreamWriter writer = null;
+		protected string symbol_file = null;
+
+		//
+		// DwarfFileWriter public interface
+		//
+		public DwarfFileWriter (string symbol_file)
+		{
+			this.symbol_file = symbol_file;
+			this.writer = new StreamWriter (symbol_file);
+
+			Console.WriteLine ("WRITING DWARF FILE: " + symbol_file);
+		}
+
+		// Writes the final dwarf file.
+		public void Close ()
+		{
+			foreach (CompileUnit compile_unit in compile_units)
+				compile_unit.Emit ();
+
+			WriteAbbrevDeclarations ();
+			WriteRelocEntries ();
+
+			writer.Close ();
+		}
+
+		// Adds a new compile unit to this dwarf file
+		public void AddCompileUnit (CompileUnit compile_unit)
+		{
+			int i = compile_units.Length;
+			CompileUnit[] new_c = new CompileUnit [i + 1];
+			Array.Copy (compile_units, new_c, i);
+			new_c [i] = compile_unit;
+			compile_units = new_c;
+		}
+
+		// This string is written into the generated dwarf file to identify the
+		// producer and version number.
+		public string ProducerID {
+			get {
+				return producer_id;
+			}
+		}
+
+		//
+		// A compile unit refers to a single C# source file.
+		//
+		public class CompileUnit
+		{
+			protected DwarfFileWriter dw;
+			protected string source_file;
+			protected Die[] dies;
+
+			public CompileUnit (DwarfFileWriter dw, string source_file, Die[] dies)
+			{
+				this.dw = dw;
+				this.source_file = source_file;
+				this.dies = dies;
+
+				dw.AddCompileUnit (this);
+			}
+
+			//
+			// Construct a new compile unit for source file @source_file.
+			//
+			// This constructor automatically adds the newly created compile
+			// unit to the DwarfFileWriter's list of compile units.
+			//
+			public CompileUnit (DwarfFileWriter dw, string source_file)
+				: this (dw, source_file, new Die [0])
+			{ }
+
+			public string SourceFile {
+				get {
+					return source_file;
+				}
+			}
+
+			public string ProducerID {
+				get {
+					return dw.ProducerID;
+				}
+			}
+
+			public DwarfFileWriter DwarfFileWriter
+			{
+				get {
+					return dw;
+				}
+			}
+
+			// Add a new debugging information entry to this compile unit.
+			public void AddDie (Die die)
+			{
+				int i = dies.Length;
+				Die[] new_d = new Die [i + 1];
+				Array.Copy (dies, new_d, i);
+				new_d [i] = die;
+				dies = new_d;
+			}
+
+			// Write the whole compile unit to the dwarf file.
+			public void Emit ()
+			{
+				int start_index, end_index;
+
+				dw.WriteSectionStart (Section.DEBUG_INFO);
+
+				start_index = dw.WriteAnonLabel ();
+
+				end_index = dw.WriteSectionSize ();
+				dw.WriteUInt16 (2);
+				dw.WriteOffset ("debug_abbrev_b");
+				dw.AddRelocEntry (RelocEntryType.TARGET_ADDRESS_SIZE);
+				dw.WriteUInt8 (0);
+
+				if (dies != null)
+					foreach (Die die in dies)
+						die.Emit ();
+
+				dw.WriteAnonLabel (end_index);
+
+				dw.WriteSectionEnd ();
+			}
+		}
+
+		// DWARF tag from the DWARF 2 specification.
+		public enum DW_TAG {
+			TAG_compile_unit	= 0x11,
+			TAG_subprogram		= 0x2e
+		}
+
+		// DWARF attribute from the DWARF 2 specification.
+		public enum DW_AT {
+			AT_name			= 0x03,
+			AT_low_pc		= 0x11,
+			AT_high_pc		= 0x12,
+			AT_language		= 0x13,
+			AT_producer		= 0x25,
+			AT_external		= 0x3f
+		}
+
+		// DWARF form from the DWARF 2 specification.
+		public enum DW_FORM {
+			FORM_addr		= 0x01,
+			FORM_string		= 0x08,
+			FORM_data1		= 0x0b
+		}
+
+		public enum DW_LANG {
+			LANG_C_plus_plus	= 0x04,
+			LANG_C_sharp		= LANG_C_plus_plus
+		}
+
+		// Abstract base class for a "debugging information entry" (die).
+		public abstract class Die
+		{
+			protected DwarfFileWriter dw;
+			protected Die[] child_dies;
+			public readonly Die Parent;
+
+			protected readonly int abbrev_id;
+			protected readonly AbbrevDeclaration abbrev_decl;
+
+			//
+			// Create a new die If @parent is not null, add the newly
+			// created die to the parent's list of child dies.
+			//
+			// @abbrev_id is the abbreviation id for this die class.
+			// Derived classes should call the DwarfFileWriter's static
+			// RegisterAbbrevDeclaration function in their static constructor
+			// to get an abbrev id. Once you registered an abbrev entry, it'll
+			// be automatically written to the debug_abbrev section.
+			//
+			public Die (DwarfFileWriter dw, Die parent, int abbrev_id)
+			{
+				this.dw = dw;
+				this.Parent = parent;
+				this.abbrev_id = abbrev_id;
+				this.abbrev_decl = GetAbbrevDeclaration (abbrev_id);
+
+				if (parent != null)
+					parent.AddChildDie (this);
+			}
+
+			public Die (DwarfFileWriter dw, int abbrev_id)
+				: this (dw, null, abbrev_id)
+			{ }
+
+			public Die (Die parent, int abbrev_id)
+				: this (parent.dw, parent, abbrev_id)
+			{ }
+
+			protected void AddChildDie (Die die)
+			{
+				if (child_dies != null) {
+					int i = child_dies.Length;
+					Die[] new_d =
+						new Die [i + 1];
+					Array.Copy (child_dies, new_d, i);
+					new_d [i] = die;
+					child_dies = new_d;
+				} else {
+					child_dies = new Die [1];
+					child_dies [0] = die;
+				}
+			}
+
+			//
+			// Write this die and all its children to the dwarf file.
+			//
+			public virtual void Emit ()
+			{
+				dw.WriteULeb128 (abbrev_id);
+				DoEmit ();
+
+				if (abbrev_decl.HasChildren) {
+					if (child_dies != null)
+						foreach (Die child in child_dies)
+							child.Emit ();
+
+					dw.WriteUInt8 (0);
+				}
+			}
+
+			//
+			// Derived classes must implement this function to actually
+			// write themselves to the dwarf file.
+			//
+			// Note that the abbrev id has already been written in Emit() -
+			// if you don't like this, you must override Emit() as well.
+			//
+			public abstract void DoEmit ();
+		}
+
+		// DW_TAG_compile_unit
+		public class DieCompileUnit : Die
+		{
+			private static int my_abbrev_id;
+
+			static DieCompileUnit ()
+			{
+				AbbrevEntry[] entries = {
+					new AbbrevEntry (DW_AT.AT_name, DW_FORM.FORM_string),
+					new AbbrevEntry (DW_AT.AT_producer, DW_FORM.FORM_string),
+					new AbbrevEntry (DW_AT.AT_language, DW_FORM.FORM_data1)
+				};
+				AbbrevDeclaration decl = new AbbrevDeclaration (
+					DW_TAG.TAG_compile_unit, true, entries);
+
+				my_abbrev_id = RegisterAbbrevDeclaration (decl);
+			}
+
+			public readonly CompileUnit compile_unit;
+
+			//
+			// Create a new DW_TAG_compile_unit debugging information entry
+			// and add it to the @compile_unit.
+			//
+			public DieCompileUnit (CompileUnit compile_unit)
+				: base (compile_unit.DwarfFileWriter, my_abbrev_id)
+			{
+				this.compile_unit = compile_unit;
+				compile_unit.AddDie (this);
+			}
+
+			public override void DoEmit ()
+			{
+				dw.WriteString (compile_unit.SourceFile);
+				dw.WriteString (compile_unit.ProducerID);
+				dw.WriteUInt8 ((int) DW_LANG.LANG_C_sharp);
+			}
+		}
+
+		// DW_TAG_subprogram
+		public class DieSubProgram : Die
+		{
+			private static int my_abbrev_id_1;
+			private static int my_abbrev_id_2;
+
+			static DieSubProgram ()
+			{
+				// Method without return value
+				AbbrevEntry[] entries_1 = {
+					new AbbrevEntry (DW_AT.AT_name, DW_FORM.FORM_string),
+					new AbbrevEntry (DW_AT.AT_external, DW_FORM.FORM_data1),
+					new AbbrevEntry (DW_AT.AT_low_pc, DW_FORM.FORM_addr),
+					new AbbrevEntry (DW_AT.AT_high_pc, DW_FORM.FORM_addr)
+				};
+				// Method with return value
+				AbbrevEntry[] entries_2 = {
+					new AbbrevEntry (DW_AT.AT_name, DW_FORM.FORM_string),
+					new AbbrevEntry (DW_AT.AT_external, DW_FORM.FORM_data1),
+					new AbbrevEntry (DW_AT.AT_low_pc, DW_FORM.FORM_addr),
+					new AbbrevEntry (DW_AT.AT_high_pc, DW_FORM.FORM_addr)
+				};
+
+				AbbrevDeclaration decl_1 = new AbbrevDeclaration (
+					DW_TAG.TAG_subprogram, true, entries_1);
+				AbbrevDeclaration decl_2 = new AbbrevDeclaration (
+					DW_TAG.TAG_subprogram, true, entries_2);
+
+				my_abbrev_id_1 = RegisterAbbrevDeclaration (decl_1);
+				my_abbrev_id_2 = RegisterAbbrevDeclaration (decl_2);
+			}
+
+			protected string name;
+
+			//
+			// Create a new DW_TAG_subprogram debugging information entry
+			// for method @name (which has a void return value) and add it
+			// to the @parent_die
+			//
+			public DieSubProgram (DieCompileUnit parent_die, string name)
+				: base (parent_die, my_abbrev_id_1)
+			{
+				this.name = name;
+			}
+
+			public override void DoEmit ()
+			{
+				dw.WriteString (name);
+				dw.WriteFlag (true);
+				dw.AddRelocEntry (RelocEntryType.IL_OFFSET);
+				dw.WriteAddress (0);
+				dw.AddRelocEntry (RelocEntryType.IL_OFFSET);
+				dw.WriteAddress (0);
+			}
+		}
+
+		protected const int reloc_table_version = 1;
+
+		protected enum Section {
+			DEBUG_INFO,
+			DEBUG_ABBREV,
+			DEBUG_LINES,
+			MONO_RELOC_TABLE
+		}
+
+		public struct AbbrevEntry {
+			public AbbrevEntry (DW_AT attribute, DW_FORM form)
+			{
+				this._attribute = attribute;
+				this._form = form;
+			}
+
+			private DW_AT _attribute;
+			private DW_FORM _form;
+
+			public DW_AT Attribute {
+				get {
+					return _attribute;
+				}
+			}
+
+			public DW_FORM Form {
+				get {
+					return _form;
+				}
+			}
+		}
+
+		public struct AbbrevDeclaration {
+			public AbbrevDeclaration (DW_TAG tag, bool has_children, AbbrevEntry[] entries)
+			{
+				this._tag = tag;
+				this._has_children = has_children;
+				this._entries = entries;
+			}
+
+			private DW_TAG _tag;
+			private bool _has_children;
+			private AbbrevEntry[] _entries;
+
+			public DW_TAG Tag {
+				get {
+					return _tag;
+				}
+			}
+
+			public bool HasChildren {
+				get {
+					return _has_children;
+				}
+			}
+
+			public AbbrevEntry[] Entries {
+				get {
+					return _entries;
+				}
+			}
+		}
+	    
+		protected enum RelocEntryType {
+			NONE,
+			// Size of an address on the target machine
+			TARGET_ADDRESS_SIZE	= 0x01,
+			// Map an IL offset to a machine address
+			IL_OFFSET		= 0x02
+		}
+
+		protected struct RelocEntry {
+			public RelocEntry (RelocEntryType type, Section section, int index)
+			{
+				_type = type;
+				_section = section;
+				_index = index;
+			}
+
+			public RelocEntryType RelocType {
+				get {
+					return _type;
+				}
+			}
+
+			public Section Section {
+				get {
+					return _section;
+				}
+			}
+
+			public int Index {
+				get {
+					return _index;
+				}
+			}
+
+			private RelocEntryType _type;
+			private Section _section;
+			private int _index;
+		}
+
+		private int next_anon_label_idx = 0;
+		private Section current_section;
+		private RelocEntry[] reloc_entries = new RelocEntry [0];
+
+		private static AbbrevDeclaration[] abbrev_declarations = new AbbrevDeclaration [0];
+
+		protected string GetSectionName (Section section)
+		{
+			switch (section) {
+			case Section.DEBUG_INFO:
+				return "debug_info";
+			case Section.DEBUG_ABBREV:
+				return "debug_abbrev";
+			case Section.DEBUG_LINES:
+				return "debug_lines";
+			case Section.MONO_RELOC_TABLE:
+				return "mono_reloc_table";
+			default:
+				throw new ArgumentException ();
+			}
+		}
+
+		protected int WriteAnonLabel ()
+		{
+			int index = ++next_anon_label_idx;
+
+			WriteAnonLabel (index);
+
+			return index;
+		}
+
+		protected void AddRelocEntry (RelocEntry entry)
+		{
+			int i = reloc_entries.Length;
+			RelocEntry[] new_r = new RelocEntry [i + 1];
+			Array.Copy (reloc_entries, new_r, i);
+			new_r [i] = entry;
+			reloc_entries = new_r;
+		}
+
+		protected void AddRelocEntry (RelocEntryType type, Section section, int index)
+		{
+			AddRelocEntry (new RelocEntry (type, section, index));
+		}
+
+		protected void AddRelocEntry (RelocEntryType type)
+		{
+			AddRelocEntry (type, current_section, WriteAnonLabel ());
+		}
+
+
+		//
+		// Mono relocation table. See the README.relocation-table file in this
+		// directory for a detailed description of the file format.
+		//
+		protected void WriteRelocEntries ()
+		{
+			WriteSectionStart (Section.MONO_RELOC_TABLE);
+			WriteUInt16 (reloc_table_version);
+			WriteUInt8 (0);
+			int end_index = WriteSectionSize ();
+
+			foreach (RelocEntry entry in reloc_entries) {
+				WriteUInt8 ((int) entry.RelocType);
+				int tmp_index = WriteSectionSize ();
+
+				WriteUInt8 ((int) entry.Section);
+				WriteUInt16 (entry.Index);
+
+				WriteAnonLabel (tmp_index);
+			}
+
+			WriteAnonLabel (end_index);
+			WriteSectionEnd ();
+		}
+
+		//
+		// Registers a new abbreviation declaration.
+		//
+		// This function should be called by a static constructor in one of
+		// Die's subclasses.
+		//
+		protected static int RegisterAbbrevDeclaration (AbbrevDeclaration decl)
+		{
+			if (abbrev_declarations != null) {
+				int i = abbrev_declarations.Length;
+				AbbrevDeclaration[] new_a = new AbbrevDeclaration [i + 1];
+				Array.Copy (abbrev_declarations, new_a, i);
+				new_a [i] = decl;
+				abbrev_declarations = new_a;
+				return i + 1;
+			} else {
+				abbrev_declarations = new AbbrevDeclaration [1];
+				abbrev_declarations [0] = decl;
+				return 1;
+			}
+		}
+
+		protected static AbbrevDeclaration GetAbbrevDeclaration (int index)
+		{
+			return abbrev_declarations [index - 1];
+		}
+
+		protected void WriteAbbrevDeclarations ()
+		{
+			WriteSectionStart (Section.DEBUG_ABBREV);
+			WriteLabel ("debug_abbrev_b");
+
+			for (int index = 0; index < abbrev_declarations.Length; index++) {
+				AbbrevDeclaration decl = abbrev_declarations [index];
+
+				WriteULeb128 (index + 1);
+				WriteULeb128 ((int) decl.Tag);
+				WriteFlag (decl.HasChildren);
+
+				foreach (AbbrevEntry entry in decl.Entries)
+					WritePair ((int) entry.Attribute, (int) entry.Form);
+
+				WritePair (0, 0);
+			}
+
+			WriteSectionEnd ();
+		}
+
+
+		protected void WriteAnonLabel (int index)
+		{
+			writer.WriteLine (".L_" + index + ":");
+		}
+
+		protected void WriteLabel (string label)
+		{
+			writer.WriteLine (label + ":");
+		}
+
+		protected int WriteSectionSize ()
+		{
+			int start_index = ++next_anon_label_idx;
+			int end_index = ++next_anon_label_idx;
+
+			writer.WriteLine ("\t.long\t\t.L_" + end_index + " - .L_" + start_index);
+			WriteAnonLabel (start_index);
+
+			return end_index;
+		}
+
+		protected void WriteSectionStart (Section section)
+		{
+			writer.WriteLine ("\t.section\t." + GetSectionName (section));
+			current_section = section;
+		}
+
+		protected void WriteSectionEnd ()
+		{
+			writer.WriteLine ("\t.previous\n");
+		}
+
+		protected void WriteFlag (bool value)
+		{
+			writer.WriteLine ("\t.byte\t\t" + (value ? 1 : 0));
+		}
+
+		protected void WritePair (int key, int value)
+		{
+			writer.WriteLine ("\t.byte\t\t" + key + ", " + value);
+		}
+
+		protected void WriteUInt8 (int value)
+		{
+			writer.WriteLine ("\t.byte\t\t" + value);
+		}
+
+		protected void WriteUInt16 (int value)
+		{
+			writer.WriteLine ("\t.2byte\t\t" + value);
+		}
+
+		protected void WriteSLeb128 (int value)
+		{
+			writer.WriteLine ("\t.sleb128\t" + value);
+		}
+
+		protected void WriteULeb128 (int value)
+		{
+			writer.WriteLine ("\t.uleb128\t" + value);
+		}
+
+		protected void WriteOffset (string section)
+		{
+			writer.WriteLine ("\t.long\t\t" + section);
+		}
+
+		protected void WriteAddress (int value)
+		{
+			writer.WriteLine ("\t.long\t\t" + value);
+		}
+
+		protected void WriteString (string value)
+		{
+			writer.WriteLine ("\t.string\t\t\"" + value + "\"");
+		}
+	}
+}

+ 60 - 0
mcs/class/Mono.CSharp.Debugger/MonoSymbolDocumentWriter.cs

@@ -0,0 +1,60 @@
+//
+// System.Diagnostics.SymbolStore/MonoSymbolDocumentWriter.cs
+//
+// Author:
+//   Martin Baulig ([email protected])
+//
+// This is the default implementation of the
+// System.Diagnostics.SymbolStore.ISymbolDocumentWriter interface.
+//
+// (C) 2002 Ximian, Inc.  http://www.ximian.com
+//
+
+using System;
+using System.Reflection;
+using System.Reflection.Emit;
+using System.Diagnostics.SymbolStore;
+using System.IO;
+	
+namespace Mono.CSharp.Debugger
+{
+
+	public class MonoSymbolDocumentWriter : ISymbolDocumentWriter
+	{
+		protected string url;
+
+		//
+		// Constructor
+		//
+		public MonoSymbolDocumentWriter (string url)
+		{
+			this.url = url;
+		}
+
+		public string FileName {
+			get {
+				return url;
+			}
+		}
+
+		//
+		// Interface ISymbolDocumentWriter
+		//
+
+		//
+		// MonoSymbolWriter creates a DWARF 2 debugging file and DWARF operates
+		// on file names, but has no way to include a whole source file in the
+		// symbol file.
+		//
+
+		public void SetCheckSum (Guid algorithmId, byte[] checkSum)
+		{
+			throw new NotSupportedException ();
+		}
+
+		public void SetSource (byte[] source)
+		{
+			throw new NotSupportedException ();
+		}
+	}
+}

+ 296 - 0
mcs/class/Mono.CSharp.Debugger/MonoSymbolWriter.cs

@@ -0,0 +1,296 @@
+//
+// System.Diagnostics.SymbolStore/MonoSymbolWriter.cs
+//
+// Author:
+//   Martin Baulig ([email protected])
+//
+// This is the default implementation of the System.Diagnostics.SymbolStore.ISymbolWriter
+// interface.
+//
+// (C) 2002 Ximian, Inc.  http://www.ximian.com
+//
+
+using System;
+using System.Reflection;
+using System.Reflection.Emit;
+using System.Diagnostics.SymbolStore;
+using System.Collections;
+using System.IO;
+	
+namespace Mono.CSharp.Debugger
+{
+
+	public class MonoSymbolWriter : IMonoSymbolWriter
+	{
+		protected string output_filename = null;
+		protected Hashtable methods = null;
+		protected Hashtable sources = null;
+
+		protected class SourceInfo
+		{
+			private MethodInfo[] _methods;
+			public readonly string FileName;
+
+			public SourceInfo (string filename)
+			{
+				this.FileName = filename;
+
+				this._methods = new MethodInfo [0];
+			}
+
+			public MethodInfo[] GetMethods ()
+			{
+				return _methods;
+			}
+
+			public void AddMethod (MethodInfo method)
+			{
+				int i = _methods.Length;
+				MethodInfo[] new_m = new MethodInfo [i + 1];
+				Array.Copy (_methods, new_m, i);
+				new_m [i] = method;
+				_methods = new_m;
+			}
+		}
+
+		protected struct MethodInfo
+		{
+			public MethodInfo (string name, SourceInfo source_file) {
+				this.Name = name;
+				this.SourceFile = source_file;
+			}
+
+			public void SetSourceRange (int startLine, int startColumn,
+						    int endLine, int endColumn)
+			{
+			}
+
+			public readonly string Name;
+			public readonly SourceInfo SourceFile;
+		}
+
+		protected Object current_method = null;
+
+		//
+		// Interface IMonoSymbolWriter
+		//
+
+		public MonoSymbolWriter ()
+		{
+			methods = new Hashtable ();
+			sources = new Hashtable ();
+		}
+
+		public void Close () {
+			CreateDwarfFile (output_filename);
+		}
+
+		public void CloseNamespace () {
+		}
+
+		public void CloseScope (int endOffset) {
+		}
+
+		// Create and return a new IMonoSymbolDocumentWriter.
+		public ISymbolDocumentWriter DefineDocument (string url,
+							     Guid language,
+							     Guid languageVendor,
+							     Guid documentType)
+		{
+			return new MonoSymbolDocumentWriter (url);
+		}
+
+		public void DefineField (
+			SymbolToken parent,
+			string name,
+			FieldAttributes attributes,
+			byte[] signature,
+			SymAddressKind addrKind,
+			int addr1,
+			int addr2,
+			int addr3)
+		{
+		}
+
+		public void DefineGlobalVariable (
+			string name,
+			FieldAttributes attributes,
+			byte[] signature,
+			SymAddressKind addrKind,
+			int addr1,
+			int addr2,
+			int addr3)
+		{
+		}
+
+		public void DefineLocalVariable (string name,
+						 FieldAttributes attributes,
+						 byte[] signature,
+						 SymAddressKind addrKind,
+						 int addr1,
+						 int addr2,
+						 int addr3,
+						 int startOffset,
+						 int endOffset)
+		{
+			Console.WriteLine ("WRITE LOCAL: " + name + " " + addr1);
+		}
+
+		public void DefineParameter (
+			string name,
+			ParameterAttributes attributes,
+			int sequence,
+			SymAddressKind addrKind,
+			int addr1,
+			int addr2,
+			int addr3)
+		{
+		}
+
+		public void DefineSequencePoints (
+			ISymbolDocumentWriter document,
+			int[] offsets,
+			int[] lines,
+			int[] columns,
+			int[] endLines,
+			int[] endColumns)
+		{
+		}
+
+		public void Initialize (IntPtr emitter, string filename, bool fFullBuild)
+		{
+			throw new NotSupportedException ("Please use the 'Initialize (string filename)' "
+							 + "constructor and read the documentation in "
+							 + "Mono.CSharp.Debugger/IMonoSymbolWriter.cs");
+		}
+
+		// This is documented in IMonoSymbolWriter.cs
+		public void Initialize (string filename)
+		{
+			this.output_filename = filename;
+		}
+
+		public void OpenMethod (SymbolToken method)
+		{
+			// do nothing
+		}
+
+		// This is documented in IMonoSymbolWriter.cs
+		public void OpenMethod (SymbolToken symbol_token, string name, string source_file)
+		{
+			int token = symbol_token.GetToken ();
+			SourceInfo source_info;
+
+			if (methods.ContainsKey (token))
+				methods.Remove (token);
+
+			if (sources.ContainsKey (source_file))
+				source_info = (SourceInfo) sources [source_file];
+			else {
+				source_info = new SourceInfo (source_file);
+				sources.Add (source_file, source_info);
+			}
+
+			current_method = new MethodInfo (name, source_info);
+
+			source_info.AddMethod ((MethodInfo) current_method);
+
+			methods.Add (token, current_method);
+
+			OpenMethod (symbol_token);
+		}
+
+		public void SetMethodSourceRange (ISymbolDocumentWriter startDoc,
+						  int startLine, int startColumn,
+						  ISymbolDocumentWriter endDoc,
+						  int endLine, int endColumn)
+		{
+			if ((startDoc == null) || (endDoc == null))
+				throw new NullReferenceException ();
+
+			if (!(startDoc is MonoSymbolDocumentWriter) || !(endDoc is MonoSymbolDocumentWriter))
+				throw new NotSupportedException ("both startDoc and endDoc must be of type "
+								 + "MonoSymbolDocumentWriter");
+
+			if (!startDoc.Equals (endDoc))
+				throw new NotSupportedException ("startDoc and endDoc must be the same");
+
+			if (current_method != null)
+				((MethodInfo) current_method).SetSourceRange (startLine, startColumn,
+									      endLine, endColumn);
+		}
+
+		public void CloseMethod () {
+			current_method = null;
+		}
+
+		public void OpenNamespace (string name)
+		{
+		}
+
+		public int OpenScope (int startOffset)
+		{
+			throw new NotImplementedException ();
+		}
+
+		public void SetScopeRange (int scopeID, int startOffset, int endOffset)
+		{
+		}
+
+		public void SetSymAttribute (SymbolToken parent, string name, byte[] data)
+		{
+		}
+
+		public void SetUnderlyingWriter (IntPtr underlyingWriter)
+		{
+		}
+
+		public void SetUserEntryPoint (SymbolToken entryMethod)
+		{
+		}
+
+		public void UsingNamespace (string fullName)
+		{
+		}
+
+		//
+		// MonoSymbolWriter implementation
+		//
+		protected void WriteMethod (DwarfFileWriter.DieCompileUnit parent_die, MethodInfo method)
+		{
+			Console.WriteLine ("WRITING METHOD: " + method.Name);
+
+			DwarfFileWriter.DieSubProgram die;
+
+			die = new DwarfFileWriter.DieSubProgram (parent_die, method.Name);
+		}
+
+		protected void WriteSource (DwarfFileWriter writer, SourceInfo source)
+		{
+			Console.WriteLine ("WRITING SOURCE: " + source.FileName);
+
+			DwarfFileWriter.CompileUnit compile_unit = new DwarfFileWriter.CompileUnit (
+				writer, source.FileName);
+
+			DwarfFileWriter.DieCompileUnit die = new DwarfFileWriter.DieCompileUnit (compile_unit);
+
+			foreach (MethodInfo method in source.GetMethods ())
+				WriteMethod (die, method);
+		}
+
+		protected void CreateDwarfFile (string filename)
+		{
+			Console.WriteLine ("WRITING DWARF FILE: " + filename);
+
+			DwarfFileWriter writer = new DwarfFileWriter (filename);
+
+			foreach (SourceInfo source in sources.Values)
+				WriteSource (writer, source);
+
+			writer.Close ();
+
+			Console.WriteLine ("DONE WRITING DWARF FILE");
+
+		}
+	}
+}

+ 43 - 0
mcs/class/Mono.CSharp.Debugger/README

@@ -0,0 +1,43 @@
+This is an implementation of the System.Diagnostics.SymbolStore.ISymbolWriter
+interface which writes a dwarf debugging information file.
+
+Unfortunately there are several major problems with this interface and I'm
+unsure how to solve them:
+
+1.) The interface contains a constructor method `Initialize' which has an
+    'IntPtr' emitter argument which seems to be a pointer to the actual
+    symbol writer which resides in a proprietary, undocumented DLL (I spent
+    almost 3 hours browsing the ".NET Framework SDK Documentation" and
+    msdn.microsoft.com - without success.
+
+    A short test showed me that mscorlib doesn't like passing zero, this
+    won't give you the system's default implementation.
+
+    To solve this problem, I created a derived interface IMonoSymbolWriter
+    which contains an additional constructor which only takes the name of
+    the symbol file as argument.
+
+        void Initialize (string filename);
+
+2.) You seem to get an instance of a class implementing this interface by
+    creating a new instance of System.Reflection.Emit.ModuleBuilder (with the
+    `bool createSymbolFile' argument) and then calling GetSymWriter() on
+    the returned object.
+
+    So far so good, but how does this method find out which symbol writer
+    to use ?
+
+3.) According to the documentation, some of the methods of
+    System.Reflection.Emit.ILGenerator and System.Reflection.Emit.LocalBuilder
+    seem to use the symbol writer to emit symbol debugging information.
+
+    But again, how do these objects get the symbol writer ?
+
+Currently, there are two ways to use this assembly:
+
+a.) Fix the problems outlined above and dynamically load this assembly
+    (Mono.CSharp.Debugger.dll) when a new symbol writer is created.
+
+b.) Reference this assembly in your application and manually create the
+    symbol writer using the constructor.
+

+ 86 - 0
mcs/class/Mono.CSharp.Debugger/README.relocation-table

@@ -0,0 +1,86 @@
+This is an implementation of the System.Diagnostics.SymbolStore.ISymbolWriter
+interface which writes a symbol file in the dwarf format.
+
+The output is an assembler input file containing dwarf data with an
+additional ELF section called ".mono_reloc_table". This section is
+read by the JIT engine to map IL offsets and such to machine
+addresses.
+
+The mono relocation table contains of
+
+1. A 2-byte unsigned integer containing a version number, currently 1.
+
+2. A 1-byte unsigned integer containing a flag specifying whether the
+   object file containing this section has already been relocated.
+
+   The symbol writer sets this flag to zero, you must change it to 1
+   if you write the relocated file back to disk.
+
+3. A 4-byte unsigned integer containing the total size of the
+   relocation type, but not including the size field itself.
+
+4. One or more relocation entries.
+
+Each entry in the relocation table has the following form:
+
+1. A 1-byte unsigned integer specifying the type of this entry.
+
+2. A 4-byte unsigned integer containing the size of this entry, but
+   not including the size field itself.
+
+3. A 1-byte unsigned integer specifying the ELF section of this entry.
+
+4. A 2-byte unsigned offset from the beginning of the ELF section to
+   the location which needs to be relocated. This is called the target.
+
+5. Optional additional data depending on the type of the entry.
+
+The following entry types are currently defined:
+
+a) TARGET_ADDRESS_SIZE - 0x01
+
+   The target is a 1-byte unsigned integer containing the size in
+   bytes of an address on the target machine.
+
+b) IL_OFFSET - 0x02
+
+   The target is an integer of appropriate size to hold an address on
+   the target machine (the assembler ensures that the object has the
+   correct size) and contains an IL offset from the beginning of the
+   current method which needs to be replaced with the corresponding
+   machine address.
+
+To use the generated object file, the JIT must:
+
+* Check whether the file contains a ".mono_reloc_table" section.
+  If not, the file cannot be used (you need to change the address size
+  field in each compilation unit header).
+
+* Check whether the flag field of the relocation table (the 3rd byte
+  of the relocation table) is zero. If it is 1, the file has already
+  been relocated. In this case, it's best to reject the file.
+
+* Set the flag field to 1.
+
+* Process all relocation entries.
+
+* Write the modified file back to disk.
+
+To do the relocation, the JIT can assume that:
+
+1.) All the relevant ELF sections are physically contiguous
+    (a requirement from the DWARF 2 specification).
+
+2.) All relocation targets holding addresses are suitable of holding
+    an address on the target machine.
+
+    This means that you can do something like
+
+	* (void **) ptr = map_to_machine (* (void **) ptr)
+
+3.) There are no other changes needed in the object file - so you can
+    just write it back to disk once you're done with the relocation.
+
+
+Last changed March 19th, 2002
+Martin Baulig <[email protected]>