/// <summary> /// Parses a PEFile from a given stream. If it is valid, a new PEFile object is /// constructed and returned. Otherwise, null is returned. /// </summary> public static PEFile TryLoad(Stream stream, bool virt) { PEBuffer headerBuff = new PEBuffer(stream); PEHeader hdr = PEHeader.FromBuffer(headerBuff, virt); if (hdr == null) return null; PEFile pefile = new PEFile(); pefile.Init(stream, "stream", virt, headerBuff, hdr); return pefile; }
public override bool IsMatchingPdb(string pdbPath) { if (m_peFile == null) m_peFile = new PEFile(new ReadVirtualStream(m_runtime.DataReader, (long)m_imageBase, (long)m_size), true); string pdbName; Guid pdbGuid; int rev; if (!m_peFile.GetPdbSignature(out pdbName, out pdbGuid, out rev)) throw new ClrDiagnosticsException("Failed to get PDB signature from module.", ClrDiagnosticsException.HR.DataRequestError); IDiaDataSource source = DiaLoader.GetDiaSourceObject(); IDiaSession session; source.loadDataFromPdb(pdbPath); source.openSession(out session); return pdbGuid == session.globalScope.guid; }
/// <summary> /// Helper function to load a DLL and then lookup exported functions. For this we use CLRMD and specifically the PEHeader class /// </summary> /// <param name="DLLPath"></param> /// <returns></returns> public unsafe Dictionary <int, ExportedSymbol> GetExports(string DLLPath) { PEHeader Header = new Microsoft.Diagnostics.Runtime.Utilities.PEFile(DLLPath).Header; var dir = Header.ExportDirectory; var offset = Header.RvaToFileOffset(dir.VirtualAddress); // this is the placeholder for the final mapping of ordinal # to address map Dictionary <int, ExportedSymbol> exports = null; using (var mmf = MemoryMappedFile.CreateFromFile(new FileStream(DLLPath, FileMode.Open, FileAccess.Read, FileShare.Read), null, 0, MemoryMappedFileAccess.Read, null, HandleInheritability.None, false)) { using (var _accessor = mmf.CreateViewAccessor(0, 0, MemoryMappedFileAccess.Read)) { IMAGE_EXPORT_DIRECTORY exportDirectory; _accessor.Read(offset, out exportDirectory); var count = exportDirectory.NumberOfFunctions; exports = new Dictionary <int, ExportedSymbol>(count); var namesOffset = exportDirectory.AddressOfNames != 0 ? Header.RvaToFileOffset(exportDirectory.AddressOfNames) : 0; var ordinalOffset = exportDirectory.AddressOfOrdinals != 0 ? Header.RvaToFileOffset(exportDirectory.AddressOfOrdinals) : 0; var functionsOffset = Header.RvaToFileOffset(exportDirectory.AddressOfFunctions); var ordinalBase = (int)exportDirectory.Base; var name = new sbyte[64]; fixed(sbyte *p = name) { for (uint i = 0; i < count; i++) { // read function address var address = _accessor.ReadUInt32(functionsOffset + i * 4); exports.Add((int)(ordinalBase + i), new ExportedSymbol { Name = string.Format("Ordinal{0}", ordinalBase + i), Address = address }); } } } } return(exports); }
public void PdbGuidAgeTest() { int pdbAge; Guid pdbSignature; PdbReader.GetPdbProperties(TestTargets.NestedException.Pdb, out pdbSignature, out pdbAge); // Ensure we get the same answer a different way. using (PdbReader pdbReader = new PdbReader(TestTargets.NestedException.Pdb)) { Assert.AreEqual(pdbAge, pdbReader.Age); Assert.AreEqual(pdbSignature, pdbReader.Signature); } // Ensure the PEFile has the same signature/age. using (PEFile peFile = new PEFile(TestTargets.NestedException.Executable)) { Assert.AreEqual(peFile.PdbInfo.Guid, pdbSignature); Assert.AreEqual(peFile.PdbInfo.Revision, pdbAge); } }
internal ResourceNode(string name, int nodeFileOffset, PEFile file, bool isLeaf, bool isTop = false) { _file = file; _nodeFileOffset = nodeFileOffset; _isTop = isTop; IsLeaf = isLeaf; Name = name; if (isLeaf) { var buff = _file.AllocBuff(); IMAGE_RESOURCE_DATA_ENTRY* dataDescr = (IMAGE_RESOURCE_DATA_ENTRY*)buff.Fetch(nodeFileOffset, sizeof(IMAGE_RESOURCE_DATA_ENTRY)); _dataLen = dataDescr->Size; _dataFileOffset = file.Header.RvaToFileOffset(dataDescr->RvaToData); var data = FetchData(0, _dataLen, buff); _file.FreeBuff(buff); } }
internal static bool TryGetIndexProperties(Stream stream, bool virt, out int timestamp, out int filesize) { try { using (PEFile pefile = new PEFile(stream, virt)) { var header = pefile.Header; timestamp = header.TimeDateStampSec; filesize = (int)header.SizeOfImage; return true; } } catch { timestamp = 0; filesize = 0; return false; } }
/// <summary> /// This API looks up an executable file, by its build-timestamp and size (on a symbol server), 'fileName' should be /// a simple name (no directory), and you need the buildTimeStamp and sizeOfImage that are found in the PE header. /// /// Returns null if it cannot find anything. /// </summary> public string FindExecutableFilePath(string fileName, int buildTimeStamp, int sizeOfImage, ISymbolNotification notification) { Debug.Assert(notification != null); string exeIndexPath = null; foreach (SymPathElement element in SymbolPath.Elements) { if (element.IsSymServer) { if (exeIndexPath == null) exeIndexPath = fileName + @"\" + buildTimeStamp.ToString("x") + sizeOfImage.ToString("x") + @"\" + fileName; string cache = element.Cache; if (cache == null) cache = SymbolPath.DefaultSymbolCache; string targetPath = GetFileFromServer(element.Target, exeIndexPath, cache, notification); if (targetPath != null) return targetPath; } else { string filePath = Path.Combine(element.Target, fileName); _log.WriteLine("Probing file {0}", filePath); if (File.Exists(filePath)) { using (PEFile file = new PEFile(filePath)) { // TODO: This is incorrect. //if ((file.Header.TimeDateStampSec == buildTimeStamp) && (file.Header.SizeOfImage == sizeOfImage)) { notification.FoundSymbolOnPath(filePath); return filePath; } //m_log.WriteLine("Found file {0} but file timstamp:size {1}:{2} != desired {3}:{4}, rejecting.", // filePath, file.Header.TimeDateStampSec, file.Header.SizeOfImage, buildTimeStamp, sizeOfImage); } } notification.ProbeFailed(filePath); } } return null; }
public string FindSymbolFilePath(string pdbSimpleName, Guid pdbIndexGuid, int pdbIndexAge, ISymbolNotification notification) { string pdbIndexPath = null; foreach (SymPathElement element in SymbolPath.Elements) { if (element.IsSymServer) { pdbSimpleName = Path.GetFileName(pdbSimpleName); if (pdbIndexPath == null) pdbIndexPath = pdbSimpleName + @"\" + pdbIndexGuid.ToString().Replace("-", "") + pdbIndexAge.ToString("x") + @"\" + pdbSimpleName; string cache = element.Cache; if (cache == null) cache = SymbolPath.DefaultSymbolCache; string targetPath = GetFileFromServer(element.Target, pdbIndexPath, cache, notification); if (targetPath != null) return targetPath; } else { string filePath = Path.Combine(element.Target, pdbSimpleName); _log.WriteLine("Probing file {0}", filePath); if (File.Exists(filePath)) { using (PEFile file = new PEFile(filePath)) { IDiaDataSource source = DiaLoader.GetDiaSourceObject(); IDiaSession session; source.loadDataFromPdb(filePath); source.openSession(out session); if (pdbIndexGuid == session.globalScope.guid) { notification.FoundSymbolOnPath(filePath); return filePath; } _log.WriteLine("Found file {0} but guid {1} != desired {2}, rejecting.", filePath, session.globalScope.guid, pdbIndexGuid); } } notification.ProbeFailed(filePath); } } return null; }
public override string TryDownloadPdb(ISymbolNotification notification) { var dataTarget = m_runtime.DataTarget; if (notification == null) notification = dataTarget.DefaultSymbolNotification ?? new NullSymbolNotification(); if (m_peFile == null) m_peFile = new PEFile(new ReadVirtualStream(m_runtime.DataReader, (long)m_imageBase, (long)m_size), true); string pdbName; Guid pdbGuid; int rev; if (!m_peFile.GetPdbSignature(out pdbName, out pdbGuid, out rev)) throw new ClrDiagnosticsException("Failed to get PDB signature from module.", ClrDiagnosticsException.HR.DataRequestError); if (File.Exists(pdbName)) return pdbName; var reader = dataTarget.SymbolReader; return reader.FindSymbolFilePath(pdbName, pdbGuid, rev, notification); }
private string FindImage(string image, uint imageTimestamp, uint imageSize) { // Test file on disk. if (File.Exists(image)) { try { using (PEFile file = new PEFile(image)) { var header = file.Header; if (header.TimeDateStampSec == (int)imageTimestamp && header.SizeOfImage == imageSize) return image; } } catch { // Ignore any exceptions when trying to determine image and file timestamp. Debug.Assert(false); } } try { image = Path.GetFileName(image); } catch (ArgumentException) { return null; } // Try symbol server instead. return m_dataTarget.TryDownloadFile(image, (int)imageTimestamp, (int)imageSize, null); }
SymbolModule FindPdbForModule(ModuleInfo module) { if (module == null) return null; string pdbName; Guid pdbGuid; int rev; using (PEFile pefile = new PEFile(new ReadVirtualStream(m_dataReader, (long)module.ImageBase, (long)module.FileSize), true)) if (!pefile.GetPdbSignature(out pdbName, out pdbGuid, out rev)) return null; if (!File.Exists(pdbName)) { ISymbolNotification notification = DefaultSymbolNotification ?? new NullSymbolNotification(); pdbName = Path.GetFileName(pdbName); pdbName = SymbolReader.FindSymbolFilePath(pdbName, pdbGuid, rev, notification); if (string.IsNullOrEmpty(pdbName) || !File.Exists(pdbName)) return null; } if (pdbName == null) { m_symbols[module] = null; return null; } SymbolModule symbols = null; try { symbols = new SymbolModule(SymbolReader, pdbName); m_symbols[module] = symbols; } catch { m_symbols[module] = null; return null; } return symbols; }
// These routines find a PDB based on something (either an DLL or a pdb 'signature') /// <summary> /// Finds the symbol file for 'exeFilePath' that exists on the current machine (we open /// it to find the needed info). Uses the SymbolReader.SymbolPath (including Symbol servers) to /// look up the PDB, and will download the PDB to the local cache if necessary. It will also /// generate NGEN pdbs unless SymbolReaderFlags.NoNGenPDB is set. /// /// returns null if the pdb can't be found. /// </summary> public string FindSymbolFilePathForModule(string dllFilePath) { Debug.Assert(!IsDisposed); try { dllFilePath = BypassSystem32FileRedirection(dllFilePath); if (File.Exists(dllFilePath)) { using (var peFile = new PEFile(dllFilePath)) { string pdbName; Guid pdbGuid; int pdbAge; if (peFile.GetPdbSignature(out pdbName, out pdbGuid, out pdbAge, true)) { string fileVersionString = null; var fileVersion = peFile.GetFileVersionInfo(); if (fileVersion != null) fileVersionString = fileVersion.FileVersion; // TODO FIX NOW should this be here? m_log.WriteLine("Exe {0} has pdb {1} GUID {2} age {3}", dllFilePath, pdbName, pdbGuid, pdbAge); var ret = FindSymbolFilePath(pdbName, pdbGuid, pdbAge, dllFilePath, fileVersionString); if (ret == null && (Flags & SymbolReaderFlags.NoNGenPDB) == 0 && (dllFilePath.EndsWith(".ni.dll", StringComparison.OrdinalIgnoreCase) || dllFilePath.EndsWith(".ni.exe", StringComparison.OrdinalIgnoreCase))) { m_log.WriteLine("Could not find PDB for NGEN image, Trying to genereate it."); ret = GenerateNGenPdbForModule(Path.GetFullPath(dllFilePath)); } m_log.WriteLine("FindSymbolFilePathForModule returns {0}", ret ?? "NULL"); return ret; } else m_log.WriteLine("File does not have a codeview debug signature."); } } else m_log.WriteLine("File does not exist."); } catch (Exception e) { m_log.WriteLine("Failure opening PE file: {0}", e.Message); } m_log.WriteLine("[Failed to find PDB file for {0}]", dllFilePath); return null; }
/// <summary> /// Given a full filename path to an NGEN image, insure that there is an NGEN image for it /// in the symbol cache. If one already exists, this method simply returns that. If not /// it is generated and placed in the symbol cache. When generating the PDB this routine /// attempt to resolve line numbers, which DOES require looking up the PDB for the IL image. /// Thus routine may do network accesses (to download IL PDBs). /// /// Note that FindSymbolFilePathForModule calls this, so normally you don't need to call /// this method directly. /// </summary> public string GenerateNGenPdbForModule(string ngenImageFullPath) { SymbolReader symReader = this; var log = symReader.m_log; if (!File.Exists(ngenImageFullPath)) { log.WriteLine("Warning, NGEN image does not exist: {0}", ngenImageFullPath); return null; } // When V4.5 shipped, NGEN CreatePdb did not support looking up the IL pdb using symbol servers. // We work around by explicitly fetching the IL PDB and pointing NGEN CreatePdb at that. string ilPdbName = null; Guid ilPdbGuid = Guid.Empty; int ilPdbAge = 0; string pdbName; Guid pdbGuid; int pdbAge; using (var peFile = new PEFile(ngenImageFullPath)) { if (!peFile.GetPdbSignature(out pdbName, out pdbGuid, out pdbAge, true)) { log.WriteLine("Could not get PDB signature for {0}", ngenImageFullPath); return null; } // Also get the IL pdb information peFile.GetPdbSignature(out ilPdbName, out ilPdbGuid, out ilPdbAge, false); } // Fast path, the file already exists. pdbName = Path.GetFileName(pdbName); var relPath = pdbName + "\\" + pdbGuid.ToString("N") + pdbAge.ToString() + "\\" + pdbName; var pdbPath = Path.Combine(symReader.SymbolCacheDirectory, relPath); if (File.Exists(pdbPath)) return pdbPath; var clrDir = GetClrDirectoryForNGenImage(ngenImageFullPath, log); if (clrDir == null) return null; // See if this is a V4.5 CLR, if so we can do line numbers too.l var lineNumberArg = ""; var ngenexe = Path.Combine(clrDir, "ngen.exe"); log.WriteLine("Checking for V4.5 for NGEN image {0}", ngenexe); if (!File.Exists(ngenexe)) return null; var isV4_5Runtime = false; Match m; using (var peFile = new PEFile(ngenexe)) { var fileVersionInfo = peFile.GetFileVersionInfo(); if (fileVersionInfo != null) { var clrFileVersion = fileVersionInfo.FileVersion; m = Regex.Match(clrFileVersion, @"^[\d.]+\.(\d+) "); // Fetch the build number (last number) if (m.Success) { // Is this a V4.5 runtime? var buildNumber = int.Parse(m.Groups[1].Value); log.WriteLine("Got NGEN.exe Build number: {0}", buildNumber); if (buildNumber > 16000) { if (ilPdbName != null) { var ilPdbPath = symReader.FindSymbolFilePath(ilPdbName, ilPdbGuid, ilPdbAge); if (ilPdbPath != null) lineNumberArg = "/lines " + Command.Quote(Path.GetDirectoryName(ilPdbPath)); else log.WriteLine("Could not find IL PDB {0} Guid {1} Age {2}.", ilPdbName, ilPdbGuid, ilPdbAge); } else log.WriteLine("NGEN image did not have IL PDB information, giving up on line number info."); isV4_5Runtime = true; } } } } var options = new CommandOptions(); options.AddEnvironmentVariable("COMPLUS_NGenEnableCreatePdb", "1"); // NGenLocalWorker is needed for V4.0 runtims but interferes on V4.5 runtimes. if (!isV4_5Runtime) options.AddEnvironmentVariable("COMPLUS_NGenLocalWorker", "1"); options.AddEnvironmentVariable("_NT_SYMBOL_PATH", symReader.SymbolPath.ToString()); var newPath = "%PATH%;" + clrDir; options.AddEnvironmentVariable("PATH", newPath); options.AddOutputStream(log); options.AddNoThrow(); // For Metro Auto-NGEN images we need to use a location where the app can write the PDB file var outputDirectory = symReader.SymbolCacheDirectory; var outputPdbPath = pdbPath; // Find the tempDir where we can write. string tempDir = null; m = Regex.Match(ngenImageFullPath, @"(.*)\\Microsoft\\CLR_v(\d+)\.\d+(_(\d\d))?\\NativeImages", RegexOptions.IgnoreCase); if (m.Success) { tempDir = Path.Combine(m.Groups[1].Value, @"Temp\NGenPdb"); DirectoryUtilities.Clean(tempDir); Directory.CreateDirectory(tempDir); outputDirectory = tempDir; outputPdbPath = Path.Combine(tempDir, relPath); log.WriteLine("Updating NGEN createPdb output file to {0}", outputPdbPath); // TODO FIX NOW REMOVE (for debugging) } try { for (; ; ) // Loop for retrying without /lines { // TODO FIX NOW: there is a and ugly problem with persistance of suboptimial PDB files // This is made pretty bad because the not finding the IL pdbs is enough to make it fail. // TODO we need to figure out a convention show we know that we have fallen back to no-lines // and we should regenerate it if we ultimately get the PDB information var cmdLine = string.Format(@"{0}\ngen.exe createpdb {1} {2} {3}", clrDir, Command.Quote(ngenImageFullPath), Command.Quote(outputDirectory), lineNumberArg); // TODO FIX NOW REMOVE after V4.5 is out a while log.WriteLine("set COMPLUS_NGenEnableCreatePdb=1"); if (!isV4_5Runtime) log.WriteLine("set COMPLUS_NGenLocalWorker=1"); log.WriteLine("set PATH=" + newPath); log.WriteLine("set _NT_SYMBOL_PATH={0}", symReader.SymbolPath); log.WriteLine("*** NGEN CREATEPDB cmdline: {0}\r\n", cmdLine); options.AddOutputStream(log); var cmd = Command.Run(cmdLine, options); log.WriteLine("*** NGEN CREATEPDB returns: {0}", cmd.ExitCode); if (cmd.ExitCode != 0) { // ngen might make a bad PDB, so if it returns failure delete it. if (File.Exists(outputPdbPath)) File.Delete(outputPdbPath); // We may have failed because we could not get the PDB. if (lineNumberArg.Length != 0) { log.WriteLine("Ngen failed to generate pdb for {0}, trying again without /lines", ngenImageFullPath); lineNumberArg = ""; continue; } } if (cmd.ExitCode != 0 || !File.Exists(outputPdbPath)) { log.WriteLine("ngen failed to generate pdb for {0} at expected location {1}", ngenImageFullPath, outputPdbPath); return null; } // Copy the file to where we want the PDB to live. if (outputPdbPath != pdbPath) { Directory.CreateDirectory(Path.GetDirectoryName(pdbPath)); // Make sure the destination directory exists. File.Copy(outputPdbPath, pdbPath); } return pdbPath; } } finally { // Insure we have cleaned up any temporary files. if (tempDir != null) DirectoryUtilities.Clean(tempDir); } }