Skip to content

Instantly share code, notes, and snippets.

@gimelfarb
Created January 27, 2014 02:05
Show Gist options
  • Save gimelfarb/8642282 to your computer and use it in GitHub Desktop.
Save gimelfarb/8642282 to your computer and use it in GitHub Desktop.
Reading PDB symbol reference info (file, GUID, age) from a loaded Assembly
public static AssemblyDebugInfo ReadAssemblyDebugInfo(Assembly assembly)
{
// Parses PE headers structure from the module base address pointer
var modulePtr = Marshal.GetHINSTANCE(assembly.ManifestModule);
var peHdrs = PeHeaders.FromUnmanagedPtr(modulePtr);
// Depending on whether this is 32-bit or 64-bit module, the offsets are
// slightly different
uint debugOffset, debugSize;
if (peHdrs.Is32BitOptionalHeader && peHdrs.OptionalHeader32.NumberOfRvaAndSizes >= 7)
{
debugOffset = peHdrs.OptionalHeader32.Debug.VirtualAddress;
debugSize = peHdrs.OptionalHeader32.Debug.Size;
}
else if (!peHdrs.Is32BitOptionalHeader && peHdrs.OptionalHeader64.NumberOfRvaAndSizes >= 7)
{
debugOffset = peHdrs.OptionalHeader64.Debug.VirtualAddress;
debugSize = peHdrs.OptionalHeader64.Debug.Size;
}
else
{
// In case DEBUG information is not in the module
return null;
}
// Navigating to the DEBUG_DIRECTORY portion, which holds the debug information
var debugPtr = new IntPtr(modulePtr.ToInt64() + debugOffset);
var debugDirectory = (IMAGE_DEBUG_DIRECTORY)Marshal.PtrToStructure(debugPtr, typeof(IMAGE_DEBUG_DIRECTORY));
// Check that DEBUG information is there and that it is in CODEVIEW/RSDS format,
// which is what's used by .NET framework
if (debugDirectory.Type != IMAGE_DEBUG_TYPE_CODEVIEW) return null;
var rsdsPtr = new IntPtr(modulePtr.ToInt64() + debugDirectory.AddressOfRawData);
if (Marshal.ReadInt32(rsdsPtr) != RSDS_SIGNATURE) return null;
// Read the RSDS info, which includes the PDB GUID and its Age, together with
// PDB filepath
var rsdsInfo = (RSDS_DEBUG_FORMAT)Marshal.PtrToStructure(rsdsPtr, typeof(RSDS_DEBUG_FORMAT));
var pathPtr = new IntPtr(rsdsPtr.ToInt64() + Marshal.SizeOf(typeof(RSDS_DEBUG_FORMAT)));
var path = Marshal.PtrToStringAnsi(pathPtr);
return new AssemblyDebugInfo()
{
Guid = new Guid(rsdsInfo.Guid),
Age = rsdsInfo.Age,
Path = path
};
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
private struct IMAGE_DEBUG_DIRECTORY
{
public UInt32 Characteristics;
public UInt32 TimeDateStamp;
public UInt16 MajorVersion;
public UInt16 MinorVersion;
public UInt32 Type;
public UInt32 SizeOfData;
public UInt32 AddressOfRawData;
public UInt32 PointerToRawData;
}
private const UInt32 IMAGE_DEBUG_TYPE_CODEVIEW = 2;
private const UInt32 RSDS_SIGNATURE = 0x53445352;
// http://www.godevtool.com/Other/pdb.htm
[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)]
private struct RSDS_DEBUG_FORMAT
{
public UInt32 Signature; // RSDS
[MarshalAs(UnmanagedType.ByValArray, SizeConst=16)]
public Byte[] Guid;
public UInt32 Age;
}
private void Load(IntPtr memPtr, long index)
{
var startIndex = index;
dosHeader = FromMemoryPtr<IMAGE_DOS_HEADER>(memPtr, ref index);
index = startIndex + dosHeader.e_lfanew + 4;
fileHeader = FromMemoryPtr<IMAGE_FILE_HEADER>(memPtr, ref index);
// See the optiona header magic to determine 32-bit vs 64-bit
var optMagic = Marshal.ReadInt16(new IntPtr(memPtr.ToInt64() + index));
_is32bit = (optMagic != IMAGE_NT_OPTIONAL_HDR64_MAGIC);
if (_is32bit)
{
optionalHeader32 = FromMemoryPtr<IMAGE_OPTIONAL_HEADER32>(memPtr, ref index);
}
else
{
optionalHeader64 = FromMemoryPtr<IMAGE_OPTIONAL_HEADER64>(memPtr, ref index);
}
imageSectionHeaders = new IMAGE_SECTION_HEADER[fileHeader.NumberOfSections];
for (int headerNo = 0; headerNo < imageSectionHeaders.Length; ++headerNo)
{
imageSectionHeaders[headerNo] = FromMemoryPtr<IMAGE_SECTION_HEADER>(memPtr, ref index);
}
}
private static T FromMemoryPtr<T>(IntPtr memPtr, ref long index)
{
var obj = (T)Marshal.PtrToStructure(new IntPtr(memPtr.ToInt64() + index), typeof(T));
index += Marshal.SizeOf(typeof(T));
return obj;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment