Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Is there a way to load debug symbols at runtime without any files #710

Closed
Serg046 opened this issue Dec 29, 2020 · 5 comments
Closed

Is there a way to load debug symbols at runtime without any files #710

Serg046 opened this issue Dec 29, 2020 · 5 comments

Comments

@Serg046
Copy link
Contributor

Serg046 commented Dec 29, 2020

This is what I am trying to do:

class Program
{
	static void Main(string[] args)
	{
		var asmDef = AssemblyDefinition.ReadAssembly(typeof(Test).Module.FullyQualifiedName,
			new ReaderParameters{ReadSymbols = true});
		asmDef.Name.Name += "Fake";
		var typeDef = asmDef.MainModule.GetType(typeof(Test).FullName, runtimeName: true).Resolve();
		var methodDef = typeDef.Methods.Single(m => m.Name == nameof(Test.Print));
		var consoleMethod = typeof(Console)
			.GetMethods()
			.Single(m => m.Name == nameof(Console.WriteLine) && m.GetParameters().Length == 1
			                                                 && m.GetParameters().Single().ParameterType ==
			                                                 typeof(string));
		methodDef.Body.Instructions.Insert(methodDef.Body.Instructions.Count - 1,
			Instruction.Create(OpCodes.Ldstr, "Injected"));
		methodDef.Body.Instructions.Insert(methodDef.Body.Instructions.Count - 1,
			Instruction.Create(OpCodes.Call, asmDef.MainModule.ImportReference(consoleMethod)));
		
		//This one works fine based on the pdb file
		var asm = WriteFileAssembly(asmDef);

		// But this one fails
		//var asm = WriteMemoryAssembly(asmDef);
		
		var type = asm.GetType(typeof(Test).FullName);
		type.InvokeMember(nameof(Test.Print), BindingFlags.InvokeMethod, null,
			Activator.CreateInstance(type), new object[0]);
	}

	private static Assembly WriteFileAssembly(AssemblyDefinition asmDef)
	{
		using var stream = File.Open("test.dll", FileMode.OpenOrCreate);
		asmDef.Write(stream, new WriterParameters {WriteSymbols = true});
		stream.Position = 0;
		// To avoid implicit load by the name
		File.Delete("renamed.pdb");
		File.Move("test.pdb", "renamed.pdb");
		// -----------------------------------
		var pdb = File.OpenRead("renamed.pdb");
		return AssemblyLoadContext.Default.LoadFromStream(stream, pdb);
	}

	private static Assembly WriteMemoryAssembly(AssemblyDefinition asmDef)
	{
		using var stream = new MemoryStream();
		using var symbolsStream = new MemoryStream();
		asmDef.Write(stream, new WriterParameters
		{
			SymbolStream = symbolsStream,
			SymbolWriterProvider = new SymbolsWriterProvider()
		});
		stream.Position = symbolsStream.Position = 0;
		return AssemblyLoadContext.Default.LoadFromStream(stream, symbolsStream);

		// This one doesn't work too with the same error
		// var pdb = File.OpenRead("renamed.pdb");
		// return AssemblyLoadContext.Default.LoadFromStream(stream, pdb);
	}

	private class SymbolsWriterProvider : ISymbolWriterProvider
	{
		public ISymbolWriter GetSymbolWriter(ModuleDefinition module, string fileName)
			=> throw new NotSupportedException();

		public ISymbolWriter GetSymbolWriter(ModuleDefinition module, Stream symbolStream)
			=> module.SymbolReader.GetWriterProvider().GetSymbolWriter(module, symbolStream);
	}
}

class Test
{
	public void Print()
	{
		Console.WriteLine("Test");
	}
}

It works fine when I generate a file but It doesn't when I try to load it in memory.
It fails with the following message: Module was built without symbols;
What is the right way to do it?

@jbevain
Copy link
Owner

jbevain commented Jan 6, 2021

Hey! That's a great question. I'm not really sure to be honest. Maybe when writing to a symbol stream instead of a file Cecil doesn't write the expected information in the debug header (after all, there's no path to write), and then the loader is confused.

@jbevain
Copy link
Owner

jbevain commented Jan 6, 2021

Maybe try to hardcode a file name in there:

https://github.com/jbevain/cecil/blob/master/Mono.Cecil.Cil/PortablePdb.cs#L304

Instead of writer.BaseStream.GetFileName (), as this will return an empty string on a non file based stream.

And see if then the symbols are loaded?

@jbevain
Copy link
Owner

jbevain commented Mar 15, 2021

@Serg046 Did you find something that worked in the end?

@Serg046
Copy link
Contributor Author

Serg046 commented Mar 17, 2021

I postponed the task where I need this behaviour that's why I didn't answer anything that time. But actually your idea works fine, thank you! What do you think about this #735?

@jbevain
Copy link
Owner

jbevain commented Mar 17, 2021

This is issue is related to #734 has well. Cecil needs to do a better job at handling filenames for non FileStream scenarios. But I like your fix in the meantime. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants