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; 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 { } } } }