Sunday, February 06, 2011

Getting the baked-in .pdb location from an assembly with Mono.Cecil 0.9.4

Because it isn't always as simple as

public static string GetPdbFileName (string assemblyFileName)
{
return Path.ChangeExtension (assemblyFileName, ".pdb");
}
view raw gistfile1.cs hosted with ❤ by GitHub

as is done in Mono.Cecil.Pdb.PdbHelper, if assemblies and symbols have been moved to separate locations during a build.

The tools are there -- we just need to get the debug data from the PE image if it's present, skip the first 24 bytes, and interpret the rest as a string. Alas, all the are annoyingly just slightly encapsulated from us. But never mind! Reflection gets us there without having to negotiate a patch or make a fork:

/// <summary>
/// Violate Cecil encapsulation to get the PDB path -- a candidate
/// for another method on ModuleDefinition
/// </summary>
/// <param name="assembly">The assembly to find the .pdb path for</param>
/// <returns>The path (null if the operation fails)</returns>
public static string GetPdbFromImage(AssemblyDefinition assembly)
{
var m = assembly.MainModule;
var imageField = typeof(ModuleDefinition).GetField("Image", BindingFlags.Instance | BindingFlags.NonPublic);
var image = imageField.GetValue(m);
var getDebugHeaderInfo = image.GetType().GetMethod("GetDebugHeader");
byte[] result = null;
var args = new object[] { result };
try
{
getDebugHeaderInfo.Invoke(image, args);
var SizeOfDebugInfo = 0x18;
result = args[0] as byte[];
if (null == result)
{
return null;
}
var byteValue = result.Skip(SizeOfDebugInfo).
TakeWhile(x => x != 0).ToArray();
if (byteValue.Length > 0)
{
// UTF-8 encoding works for an assembly named GetÞePdbLocation.
return Encoding.UTF8.GetString(byteValue);
}
}
catch (TargetInvocationException)
{}
return null;
}
view raw gistfile1.cs hosted with ❤ by GitHub

No comments :