/// <summary> /// Emit a single ObjectData into the proper section of the output R2R PE executable. /// </summary> /// <param name="r2rPeBuilder">R2R PE builder to output object data to</param> /// <param name="data">ObjectData blob to emit</param> /// <param name="nodeIndex">Logical index of the emitted node for diagnostic purposes</param> /// <param name="name">Textual representation of the ObjecData blob in the map file</param> /// <param name="section">Section to emit the blob into</param> private void EmitObjectData(R2RPEBuilder r2rPeBuilder, ObjectData data, int nodeIndex, string name, ObjectNodeSection section) { #if DEBUG for (int symbolIndex = 0; symbolIndex < data.DefinedSymbols.Length; symbolIndex++) { ISymbolNode definedSymbol = data.DefinedSymbols[symbolIndex]; NodeInfo alreadyWrittenSymbol; string symbolName = definedSymbol.GetMangledName(_nodeFactory.NameMangler); if (_previouslyWrittenNodeNames.TryGetValue(symbolName, out alreadyWrittenSymbol)) { Console.WriteLine($@"Duplicate symbol - 1st occurrence: [{alreadyWrittenSymbol.NodeIndex}:{alreadyWrittenSymbol.SymbolIndex}], {alreadyWrittenSymbol.Node.GetMangledName(_nodeFactory.NameMangler)}"); Console.WriteLine($@"Duplicate symbol - 2nd occurrence: [{nodeIndex}:{symbolIndex}], {definedSymbol.GetMangledName(_nodeFactory.NameMangler)}"); Debug.Fail("Duplicate node name emitted to file", $"Symbol {definedSymbol.GetMangledName(_nodeFactory.NameMangler)} has already been written to the output object file {_objectFilePath} with symbol {alreadyWrittenSymbol}"); } _previouslyWrittenNodeNames.Add(symbolName, new NodeInfo(definedSymbol, nodeIndex, symbolIndex)); } #endif r2rPeBuilder.AddObjectData(data, section, name, _outputInfoBuilder); }
public void EmitPortableExecutable() { bool succeeded = false; try { Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); PEHeaderBuilder headerBuilder; int? timeDateStamp; ISymbolNode r2rHeaderExportSymbol; Func <IEnumerable <Blob>, BlobContentId> peIdProvider = null; if (_nodeFactory.CompilationModuleGroup.IsCompositeBuildMode && _componentModule == null) { headerBuilder = PEHeaderProvider.Create( imageCharacteristics: Characteristics.ExecutableImage | Characteristics.Dll, dllCharacteristics: default(DllCharacteristics), Subsystem.Unknown, _nodeFactory.Target); peIdProvider = new Func <IEnumerable <Blob>, BlobContentId>(content => BlobContentId.FromHash(CryptographicHashProvider.ComputeSourceHash(content))); timeDateStamp = null; r2rHeaderExportSymbol = _nodeFactory.Header; } else { PEReader inputPeReader = (_componentModule != null ? _componentModule.PEReader : _nodeFactory.CompilationModuleGroup.CompilationModuleSet.First().PEReader); headerBuilder = PEHeaderProvider.Copy(inputPeReader.PEHeaders, _nodeFactory.Target); timeDateStamp = inputPeReader.PEHeaders.CoffHeader.TimeDateStamp; r2rHeaderExportSymbol = null; } Func <RuntimeFunctionsTableNode> getRuntimeFunctionsTable = null; if (_componentModule == null) { getRuntimeFunctionsTable = GetRuntimeFunctionsTable; } R2RPEBuilder r2rPeBuilder = new R2RPEBuilder( _nodeFactory.Target, headerBuilder, r2rHeaderExportSymbol, Path.GetFileName(_objectFilePath), getRuntimeFunctionsTable, _customPESectionAlignment, peIdProvider); NativeDebugDirectoryEntryNode nativeDebugDirectoryEntryNode = null; ISymbolDefinitionNode firstImportThunk = null; ISymbolDefinitionNode lastImportThunk = null; ObjectNode lastWrittenObjectNode = null; int nodeIndex = -1; foreach (var depNode in _nodes) { ++nodeIndex; ObjectNode node = depNode as ObjectNode; if (node == null) { continue; } if (node.ShouldSkipEmittingObjectNode(_nodeFactory)) { continue; } ObjectData nodeContents = node.GetData(_nodeFactory); if (node is NativeDebugDirectoryEntryNode nddeNode) { // There should be only one NativeDebugDirectoryEntry. Debug.Assert(nativeDebugDirectoryEntryNode == null); nativeDebugDirectoryEntryNode = nddeNode; } if (node is ImportThunk importThunkNode) { Debug.Assert(firstImportThunk == null || lastWrittenObjectNode is ImportThunk, "All the import thunks must be in single contiguous run"); if (firstImportThunk == null) { firstImportThunk = importThunkNode; } lastImportThunk = importThunkNode; } string name = null; if (_mapFileBuilder != null) { name = depNode.GetType().ToString(); int firstGeneric = name.IndexOf('['); if (firstGeneric < 0) { firstGeneric = name.Length; } int lastDot = name.LastIndexOf('.', firstGeneric - 1, firstGeneric); if (lastDot > 0) { name = name.Substring(lastDot + 1); } } EmitObjectData(r2rPeBuilder, nodeContents, nodeIndex, name, node.Section); lastWrittenObjectNode = node; if ((_generatePdbFile || _generatePerfMapFile) && node is MethodWithGCInfo methodNode) { _outputInfoBuilder.AddMethod(methodNode, nodeContents.DefinedSymbols[0]); } } r2rPeBuilder.SetCorHeader(_nodeFactory.CopiedCorHeaderNode, _nodeFactory.CopiedCorHeaderNode.Size); r2rPeBuilder.SetDebugDirectory(_nodeFactory.DebugDirectoryNode, _nodeFactory.DebugDirectoryNode.Size); if (firstImportThunk != null) { r2rPeBuilder.AddSymbolForRange(_nodeFactory.DelayLoadMethodCallThunks, firstImportThunk, lastImportThunk); } if (_nodeFactory.Win32ResourcesNode != null) { Debug.Assert(_nodeFactory.Win32ResourcesNode.Size != 0); r2rPeBuilder.SetWin32Resources(_nodeFactory.Win32ResourcesNode, _nodeFactory.Win32ResourcesNode.Size); } using (var peStream = File.Create(_objectFilePath)) { r2rPeBuilder.Write(peStream, timeDateStamp); if (_mapFileBuilder != null) { _mapFileBuilder.SetFileSize(peStream.Length); } // Compute MD5 hash of the output image and store that in the native DebugDirectory entry using (var md5Hash = MD5.Create()) { peStream.Seek(0, SeekOrigin.Begin); byte[] hash = md5Hash.ComputeHash(peStream); byte[] rsdsEntry = nativeDebugDirectoryEntryNode.GenerateRSDSEntryData(hash); int offsetToUpdate = r2rPeBuilder.GetSymbolFilePosition(nativeDebugDirectoryEntryNode); peStream.Seek(offsetToUpdate, SeekOrigin.Begin); peStream.Write(rsdsEntry); } } if (_outputInfoBuilder != null) { r2rPeBuilder.AddSections(_outputInfoBuilder); if (_generateMapFile) { string mapFileName = Path.ChangeExtension(_objectFilePath, ".map"); _mapFileBuilder.SaveMap(mapFileName); } if (_generateMapCsvFile) { string nodeStatsCsvFileName = Path.ChangeExtension(_objectFilePath, ".nodestats.csv"); string mapCsvFileName = Path.ChangeExtension(_objectFilePath, ".map.csv"); _mapFileBuilder.SaveCsv(nodeStatsCsvFileName, mapCsvFileName); } if (_generatePdbFile) { string path = _pdbPath; if (string.IsNullOrEmpty(path)) { path = Path.GetDirectoryName(_objectFilePath); } _symbolFileBuilder.SavePdb(path, _objectFilePath); } if (_generatePerfMapFile) { string path = _perfMapPath; if (string.IsNullOrEmpty(path)) { path = Path.GetDirectoryName(_objectFilePath); } _symbolFileBuilder.SavePerfMap(path, _objectFilePath); } if (_profileFileBuilder != null) { string path = Path.ChangeExtension(_objectFilePath, ".profile"); _profileFileBuilder.SaveProfile(path); } } succeeded = true; } finally { if (!succeeded) { // If there was an exception while generating the OBJ file, make sure we don't leave the unfinished // object file around. try { File.Delete(_objectFilePath); } catch { } } } }
public void EmitPortableExecutable() { bool succeeded = false; FileStream mapFileStream = null; TextWriter mapFile = null; try { string mapFileName = Path.ChangeExtension(_objectFilePath, ".map"); mapFileStream = new FileStream(mapFileName, FileMode.Create, FileAccess.Write); mapFile = new StreamWriter(mapFileStream); Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); mapFile.WriteLine($@"R2R object emission started: {DateTime.Now}"); R2RPEBuilder r2rPeBuilder = new R2RPEBuilder( _nodeFactory.Target, _inputPeReader, GetRuntimeFunctionsTable); int nodeIndex = -1; foreach (var depNode in _nodes) { ++nodeIndex; ObjectNode node = depNode as ObjectNode; if (node == null) { continue; } if (node.ShouldSkipEmittingObjectNode(_nodeFactory)) { continue; } ObjectData nodeContents = node.GetData(_nodeFactory); string name = null; if (mapFile != null) { name = depNode.GetType().ToString(); int firstGeneric = name.IndexOf('['); if (firstGeneric < 0) { firstGeneric = name.Length; } int lastDot = name.LastIndexOf('.', firstGeneric - 1, firstGeneric); if (lastDot > 0) { name = name.Substring(lastDot + 1); } } EmitObjectData(r2rPeBuilder, nodeContents, nodeIndex, name, node.Section, mapFile); } r2rPeBuilder.SetCorHeader(_nodeFactory.CopiedCorHeaderNode, _nodeFactory.CopiedCorHeaderNode.Size); if (_nodeFactory.Win32ResourcesNode != null) { Debug.Assert(_nodeFactory.Win32ResourcesNode.Size != 0); r2rPeBuilder.SetWin32Resources(_nodeFactory.Win32ResourcesNode, _nodeFactory.Win32ResourcesNode.Size); } using (var peStream = File.Create(_objectFilePath)) { r2rPeBuilder.Write(peStream); } mapFile.WriteLine($@"R2R object emission finished: {DateTime.Now}, {stopwatch.ElapsedMilliseconds} msecs"); mapFile.Flush(); mapFileStream.Flush(); succeeded = true; } finally { if (mapFile != null) { mapFile.Dispose(); } if (mapFileStream != null) { mapFileStream.Dispose(); } if (!succeeded) { // If there was an exception while generating the OBJ file, make sure we don't leave the unfinished // object file around. try { File.Delete(_objectFilePath); } catch { } } } }
public void EmitPortableExecutable() { bool succeeded = false; FileStream mapFileStream = null; TextWriter mapFile = null; try { string mapFileName = Path.ChangeExtension(_objectFilePath, ".map"); mapFileStream = new FileStream(mapFileName, FileMode.Create, FileAccess.Write); mapFile = new StreamWriter(mapFileStream); Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); mapFile.WriteLine($@"R2R object emission started: {DateTime.Now}"); Machine targetMachine; switch (_nodeFactory.Target.Architecture) { case Internal.TypeSystem.TargetArchitecture.X64: targetMachine = Machine.Amd64; break; case Internal.TypeSystem.TargetArchitecture.X86: targetMachine = Machine.I386; break; default: throw new NotImplementedException(_nodeFactory.Target.Architecture.ToString()); } R2RPEBuilder r2rPeBuilder = new R2RPEBuilder( targetMachine, _inputPeReader, _nodeFactory.SectionStartNode, GetRuntimeFunctionsTable); int nodeIndex = -1; foreach (var depNode in _nodes) { ++nodeIndex; ObjectNode node = depNode as ObjectNode; if (node == null) { continue; } if (node.ShouldSkipEmittingObjectNode(_nodeFactory)) { continue; } ObjectData nodeContents = node.GetData(_nodeFactory); string name = null; if (mapFile != null) { name = depNode.GetType().ToString(); int firstGeneric = name.IndexOf('['); if (firstGeneric < 0) { firstGeneric = name.Length; } int lastDot = name.LastIndexOf('.', firstGeneric - 1, firstGeneric); if (lastDot > 0) { name = name.Substring(lastDot + 1); } } EmitObjectData(r2rPeBuilder, nodeContents, nodeIndex, name, node.Section, mapFile); } r2rPeBuilder.SetHeaderTable(_nodeFactory.Header, _nodeFactory.Header.GetData(_nodeFactory).Data.Length); using (var peStream = File.Create(_objectFilePath)) { r2rPeBuilder.Write(peStream); } mapFile.WriteLine($@"R2R object emission finished: {DateTime.Now}, {stopwatch.ElapsedMilliseconds} msecs"); mapFile.Flush(); mapFileStream.Flush(); succeeded = true; } finally { if (mapFile != null) { mapFile.Dispose(); } if (mapFileStream != null) { mapFileStream.Dispose(); } if (!succeeded) { // If there was an exception while generating the OBJ file, make sure we don't leave the unfinished // object file around. try { File.Delete(_objectFilePath); } catch { } } } }
public void EmitPortableExecutable() { bool succeeded = false; try { Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); PEHeaderBuilder headerBuilder; int timeDateStamp; ISymbolNode r2rHeaderExportSymbol; if (_nodeFactory.CompilationModuleGroup.IsCompositeBuildMode && _componentModule == null) { headerBuilder = PEHeaderProvider.Create( imageCharacteristics: Characteristics.ExecutableImage | Characteristics.Dll, dllCharacteristics: default(DllCharacteristics), Subsystem.Unknown, _nodeFactory.Target); // TODO: generate a non-zero timestamp: https://github.com/dotnet/runtime/issues/32507 timeDateStamp = 0; r2rHeaderExportSymbol = _nodeFactory.Header; } else { PEReader inputPeReader = (_componentModule != null ? _componentModule.PEReader : _nodeFactory.CompilationModuleGroup.CompilationModuleSet.First().PEReader); headerBuilder = PEHeaderProvider.Copy(inputPeReader.PEHeaders, _nodeFactory.Target); timeDateStamp = inputPeReader.PEHeaders.CoffHeader.TimeDateStamp; r2rHeaderExportSymbol = null; } Func <RuntimeFunctionsTableNode> getRuntimeFunctionsTable = null; if (_componentModule == null) { getRuntimeFunctionsTable = GetRuntimeFunctionsTable; } R2RPEBuilder r2rPeBuilder = new R2RPEBuilder( _nodeFactory.Target, headerBuilder, r2rHeaderExportSymbol, Path.GetFileName(_objectFilePath), getRuntimeFunctionsTable); NativeDebugDirectoryEntryNode nativeDebugDirectoryEntryNode = null; int nodeIndex = -1; foreach (var depNode in _nodes) { ++nodeIndex; ObjectNode node = depNode as ObjectNode; if (node == null) { continue; } if (node.ShouldSkipEmittingObjectNode(_nodeFactory)) { continue; } ObjectData nodeContents = node.GetData(_nodeFactory); if (node is NativeDebugDirectoryEntryNode nddeNode) { // There should be only one NativeDebugDirectoryEntry. // This assert will need to be revisited when we implement the composite R2R format, where we'll need to figure // out how native symbols will be emitted, and verify that the DiaSymReader library is able to consume them. Debug.Assert(nativeDebugDirectoryEntryNode == null); nativeDebugDirectoryEntryNode = nddeNode; } string name = null; if (_mapFileBuilder != null) { name = depNode.GetType().ToString(); int firstGeneric = name.IndexOf('['); if (firstGeneric < 0) { firstGeneric = name.Length; } int lastDot = name.LastIndexOf('.', firstGeneric - 1, firstGeneric); if (lastDot > 0) { name = name.Substring(lastDot + 1); } } EmitObjectData(r2rPeBuilder, nodeContents, nodeIndex, name, node.Section, _mapFileBuilder); } if (!_nodeFactory.CompilationModuleGroup.IsCompositeBuildMode || _componentModule != null) { r2rPeBuilder.SetCorHeader(_nodeFactory.CopiedCorHeaderNode, _nodeFactory.CopiedCorHeaderNode.Size); r2rPeBuilder.SetDebugDirectory(_nodeFactory.DebugDirectoryNode, _nodeFactory.DebugDirectoryNode.Size); } if (_nodeFactory.Win32ResourcesNode != null) { Debug.Assert(_nodeFactory.Win32ResourcesNode.Size != 0); r2rPeBuilder.SetWin32Resources(_nodeFactory.Win32ResourcesNode, _nodeFactory.Win32ResourcesNode.Size); } using (var peStream = File.Create(_objectFilePath)) { r2rPeBuilder.Write(peStream, timeDateStamp); if (_mapFileBuilder != null) { _mapFileBuilder.SetFileSize(peStream.Length); } // Compute MD5 hash of the output image and store that in the native DebugDirectory entry using (var md5Hash = MD5.Create()) { peStream.Seek(0, SeekOrigin.Begin); byte[] hash = md5Hash.ComputeHash(peStream); byte[] rsdsEntry = nativeDebugDirectoryEntryNode.GenerateRSDSEntryData(hash); int offsetToUpdate = r2rPeBuilder.GetSymbolFilePosition(nativeDebugDirectoryEntryNode); peStream.Seek(offsetToUpdate, SeekOrigin.Begin); peStream.Write(rsdsEntry); } } if (_mapFileBuilder != null) { r2rPeBuilder.AddSections(_mapFileBuilder); string mapFileName = Path.ChangeExtension(_objectFilePath, ".map"); _mapFileBuilder.Save(mapFileName); } succeeded = true; } finally { if (!succeeded) { // If there was an exception while generating the OBJ file, make sure we don't leave the unfinished // object file around. try { File.Delete(_objectFilePath); } catch { } } } }
public void EmitPortableExecutable() { bool succeeded = false; FileStream mapFileStream = null; TextWriter mapFile = null; try { string mapFileName = Path.ChangeExtension(_objectFilePath, ".map"); mapFileStream = new FileStream(mapFileName, FileMode.Create, FileAccess.Write); mapFile = new StreamWriter(mapFileStream); Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); mapFile.WriteLine($@"R2R object emission started: {DateTime.Now}"); Machine targetMachine; switch (_nodeFactory.Target.Architecture) { case Internal.TypeSystem.TargetArchitecture.X64: targetMachine = Machine.Amd64; break; case Internal.TypeSystem.TargetArchitecture.X86: targetMachine = Machine.I386; break; default: throw new NotImplementedException(_nodeFactory.Target.Architecture.ToString()); } R2RPEBuilder r2rPeBuilder = new R2RPEBuilder( targetMachine, _inputPeReader, _nodeFactory.SectionStartNode, GetRuntimeFunctionsTable); int nodeIndex = -1; foreach (var depNode in _nodes) { ++nodeIndex; ObjectNode node = depNode as ObjectNode; if (node == null) { continue; } if (node.ShouldSkipEmittingObjectNode(_nodeFactory)) { continue; } ObjectData nodeContents = node.GetData(_nodeFactory); string name = null; if (mapFile != null) { name = depNode.GetType().ToString(); int firstGeneric = name.IndexOf('['); if (firstGeneric < 0) { firstGeneric = name.Length; } int lastDot = name.LastIndexOf('.', firstGeneric - 1, firstGeneric); if (lastDot > 0) { name = name.Substring(lastDot + 1); } } EmitObjectData(r2rPeBuilder, nodeContents, nodeIndex, name, node.Section, mapFile); } r2rPeBuilder.SetHeaderTable(_nodeFactory.Header, _nodeFactory.Header.GetData(_nodeFactory).Data.Length); using (var peStream = File.Create(_objectFilePath)) { r2rPeBuilder.Write(peStream); // TODO: System.Reflection.Metadata doesn't currently support // OS machine overrides. We cannot directly pass the xor-ed // target machine to PEHeaderBuilder because it may incorrectly // detect 32-bitness and emit wrong OptionalHeader.Magic. const int DosHeaderSize = 0x80; const int PESignatureSize = sizeof(uint); byte[] patchedTargetMachine = BitConverter.GetBytes( (ushort)unchecked ((ushort)targetMachine ^ (ushort)GetMachineOSOverride())); Debug.Assert(patchedTargetMachine.Length == sizeof(ushort)); peStream.Seek(DosHeaderSize + PESignatureSize, SeekOrigin.Begin); peStream.Write(patchedTargetMachine, 0, patchedTargetMachine.Length); } mapFile.WriteLine($@"R2R object emission finished: {DateTime.Now}, {stopwatch.ElapsedMilliseconds} msecs"); mapFile.Flush(); mapFileStream.Flush(); succeeded = true; } finally { if (mapFile != null) { mapFile.Dispose(); } if (mapFileStream != null) { mapFileStream.Dispose(); } if (!succeeded) { // If there was an exception while generating the OBJ file, make sure we don't leave the unfinished // object file around. try { File.Delete(_objectFilePath); } catch { } } } }
public void EmitPortableExecutable() { bool succeeded = false; FileStream mapFileStream = null; TextWriter mapFile = null; try { if (_generateMapFile) { string mapFileName = Path.ChangeExtension(_objectFilePath, ".map"); mapFileStream = new FileStream(mapFileName, FileMode.Create, FileAccess.Write); mapFile = new StreamWriter(mapFileStream); } Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); if (mapFile != null) { mapFile.WriteLine($@"R2R object emission started: {DateTime.Now}"); } R2RPEBuilder r2rPeBuilder = new R2RPEBuilder( _nodeFactory.Target, _inputPeReader, GetRuntimeFunctionsTable); NativeDebugDirectoryEntryNode nativeDebugDirectoryEntryNode = null; int nodeIndex = -1; foreach (var depNode in _nodes) { ++nodeIndex; ObjectNode node = depNode as ObjectNode; if (node == null) { continue; } if (node.ShouldSkipEmittingObjectNode(_nodeFactory)) { continue; } ObjectData nodeContents = node.GetData(_nodeFactory); if (node is NativeDebugDirectoryEntryNode nddeNode) { // There should be only one NativeDebugDirectoryEntry. // This assert will need to be revisited when we implement the composite R2R format, where we'll need to figure // out how native symbols will be emitted, and verify that the DiaSymReader library is able to consume them. Debug.Assert(nativeDebugDirectoryEntryNode == null); nativeDebugDirectoryEntryNode = nddeNode; } string name = null; if (mapFile != null) { name = depNode.GetType().ToString(); int firstGeneric = name.IndexOf('['); if (firstGeneric < 0) { firstGeneric = name.Length; } int lastDot = name.LastIndexOf('.', firstGeneric - 1, firstGeneric); if (lastDot > 0) { name = name.Substring(lastDot + 1); } } EmitObjectData(r2rPeBuilder, nodeContents, nodeIndex, name, node.Section, mapFile); } r2rPeBuilder.SetCorHeader(_nodeFactory.CopiedCorHeaderNode, _nodeFactory.CopiedCorHeaderNode.Size); r2rPeBuilder.SetDebugDirectory(_nodeFactory.DebugDirectoryNode, _nodeFactory.DebugDirectoryNode.Size); if (_nodeFactory.Win32ResourcesNode != null) { Debug.Assert(_nodeFactory.Win32ResourcesNode.Size != 0); r2rPeBuilder.SetWin32Resources(_nodeFactory.Win32ResourcesNode, _nodeFactory.Win32ResourcesNode.Size); } using (var peStream = File.Create(_objectFilePath)) { r2rPeBuilder.Write(peStream); // Compute MD5 hash of the output image and store that in the native DebugDirectory entry using (var md5Hash = MD5.Create()) { peStream.Seek(0, SeekOrigin.Begin); byte[] hash = md5Hash.ComputeHash(peStream); byte[] rsdsEntry = nativeDebugDirectoryEntryNode.GenerateRSDSEntryData(hash); int offsetToUpdate = r2rPeBuilder.GetSymbolFilePosition(nativeDebugDirectoryEntryNode); peStream.Seek(offsetToUpdate, SeekOrigin.Begin); peStream.Write(rsdsEntry); } } if (mapFile != null) { mapFile.WriteLine($@"R2R object emission finished: {DateTime.Now}, {stopwatch.ElapsedMilliseconds} msecs"); mapFile.Flush(); mapFileStream.Flush(); } succeeded = true; } finally { if (mapFile != null) { mapFile.Dispose(); } if (mapFileStream != null) { mapFileStream.Dispose(); } if (!succeeded) { // If there was an exception while generating the OBJ file, make sure we don't leave the unfinished // object file around. try { File.Delete(_objectFilePath); } catch { } } } }