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}"); _sectionBuilder = new SectionBuilder(); _textSectionIndex = _sectionBuilder.AddSection(R2RPEBuilder.TextSectionName, SectionCharacteristics.ContainsCode | SectionCharacteristics.MemExecute | SectionCharacteristics.MemRead, 512); _rdataSectionIndex = _sectionBuilder.AddSection(".rdata", SectionCharacteristics.ContainsInitializedData | SectionCharacteristics.MemRead, 512); _dataSectionIndex = _sectionBuilder.AddSection(".data", SectionCharacteristics.ContainsInitializedData | SectionCharacteristics.MemWrite | SectionCharacteristics.MemRead, 512); 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(nodeContents, nodeIndex, name, node.Section, mapFile); } _sectionBuilder.SetReadyToRunHeaderTable(_nodeFactory.Header, _nodeFactory.Header.GetData(_nodeFactory).Data.Length); 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()); } using (var peStream = File.Create(_objectFilePath)) { _sectionBuilder.EmitR2R(targetMachine, _inputPeReader, UpdateDirectories, 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 static void EmitObject(string objectFilePath, IEnumerable <DependencyNode> nodes, NodeFactory factory, IObjectDumper dumper) { ObjectWriter objectWriter = new ObjectWriter(objectFilePath, factory); bool succeeded = false; try { ObjectNodeSection managedCodeSection; if (factory.Target.OperatingSystem == TargetOS.Windows) { managedCodeSection = MethodCodeNode.WindowsContentSection; // Emit sentinels for managed code section. ObjectNodeSection codeStartSection = factory.CompilationModuleGroup.IsSingleFileCompilation ? MethodCodeNode.StartSection : objectWriter.GetSharedSection(MethodCodeNode.StartSection, "__managedcode_a"); objectWriter.SetSection(codeStartSection); objectWriter.EmitSymbolDef(new Utf8StringBuilder().Append("__managedcode_a")); objectWriter.EmitIntValue(0, 1); ObjectNodeSection codeEndSection = factory.CompilationModuleGroup.IsSingleFileCompilation ? MethodCodeNode.EndSection : objectWriter.GetSharedSection(MethodCodeNode.EndSection, "__managedcode_z"); objectWriter.SetSection(codeEndSection); objectWriter.EmitSymbolDef(new Utf8StringBuilder().Append("__managedcode_z")); objectWriter.EmitIntValue(1, 1); } else { managedCodeSection = MethodCodeNode.UnixContentSection; // TODO 2916: managed code section has to be created here, switch is not necessary. objectWriter.SetSection(MethodCodeNode.UnixContentSection); objectWriter.SetSection(LsdaSection); } objectWriter.SetCodeSectionAttribute(managedCodeSection); // Build file info map. objectWriter.BuildFileInfoMap(nodes); var listOfOffsets = new List <int>(); foreach (DependencyNode depNode in nodes) { ObjectNode node = depNode as ObjectNode; if (node == null) { continue; } if (node.ShouldSkipEmittingObjectNode(factory)) { continue; } ObjectData nodeContents = node.GetData(factory); if (dumper != null) { dumper.DumpObjectNode(factory.NameMangler, node, nodeContents); } #if DEBUG foreach (ISymbolNode definedSymbol in nodeContents.DefinedSymbols) { try { _previouslyWrittenNodeNames.Add(definedSymbol.GetMangledName(factory.NameMangler), definedSymbol); } catch (ArgumentException) { ISymbolNode alreadyWrittenSymbol = _previouslyWrittenNodeNames[definedSymbol.GetMangledName(factory.NameMangler)]; Debug.Assert(false, "Duplicate node name emitted to file", $"Symbol {definedSymbol.GetMangledName(factory.NameMangler)} has already been written to the output object file {objectFilePath} with symbol {alreadyWrittenSymbol}"); } } #endif ObjectNodeSection section = node.Section; if (objectWriter.ShouldShareSymbol(node)) { section = objectWriter.GetSharedSection(section, ((ISymbolNode)node).GetMangledName(factory.NameMangler)); } // Ensure section and alignment for the node. objectWriter.SetSection(section); objectWriter.EmitAlignment(nodeContents.Alignment); objectWriter.ResetByteRunInterruptionOffsets(nodeContents.Relocs); // Build symbol definition map. objectWriter.BuildSymbolDefinitionMap(node, nodeContents.DefinedSymbols); // The DWARF CFI unwind is implemented for AMD64 only. if (!factory.Target.IsWindows && (factory.Target.Architecture == TargetArchitecture.X64)) { objectWriter.BuildCFIMap(factory, node); } // Build debug location map objectWriter.BuildDebugLocInfoMap(node); Relocation[] relocs = nodeContents.Relocs; int nextRelocOffset = -1; int nextRelocIndex = -1; if (relocs.Length > 0) { nextRelocOffset = relocs[0].Offset; nextRelocIndex = 0; } int i = 0; listOfOffsets.Clear(); listOfOffsets.AddRange(objectWriter._byteInterruptionOffsets); int offsetIndex = 0; while (i < nodeContents.Data.Length) { // Emit symbol definitions if necessary objectWriter.EmitSymbolDefinition(i); // Emit CFI codes for the given offset. objectWriter.EmitCFICodes(i); // Emit debug loc info if needed. objectWriter.EmitDebugLocInfo(i); if (i == nextRelocOffset) { Relocation reloc = relocs[nextRelocIndex]; long delta; unsafe { fixed(void *location = &nodeContents.Data[i]) { delta = Relocation.ReadValue(reloc.RelocType, location); } } int size = objectWriter.EmitSymbolReference(reloc.Target, (int)delta, reloc.RelocType); // Emit a copy of original Thumb2 instruction that came from RyuJIT if (reloc.RelocType == RelocType.IMAGE_REL_BASED_THUMB_MOV32 || reloc.RelocType == RelocType.IMAGE_REL_BASED_THUMB_BRANCH24) { unsafe { fixed(void *location = &nodeContents.Data[i]) { objectWriter.EmitBytes((IntPtr)location, size); } } } // Update nextRelocIndex/Offset if (++nextRelocIndex < relocs.Length) { nextRelocOffset = relocs[nextRelocIndex].Offset; } else { // This is the last reloc. Set the next reloc offset to -1 in case the last reloc has a zero size, // which means the reloc does not have vacant bytes corresponding to in the data buffer. E.g, // IMAGE_REL_THUMB_BRANCH24 is a kind of 24-bit reloc whose bits scatte over the instruction that // references it. We do not vacate extra bytes in the data buffer for this kind of reloc. nextRelocOffset = -1; } i += size; } else { while (offsetIndex < listOfOffsets.Count && listOfOffsets[offsetIndex] <= i) { offsetIndex++; } int nextOffset = offsetIndex == listOfOffsets.Count ? nodeContents.Data.Length : listOfOffsets[offsetIndex]; unsafe { // Todo: Use Span<T> instead once it's available to us in this repo fixed(byte *pContents = &nodeContents.Data[i]) { objectWriter.EmitBytes((IntPtr)(pContents), nextOffset - i); i += nextOffset - i; } } } } Debug.Assert(i == nodeContents.Data.Length); // It is possible to have a symbol just after all of the data. objectWriter.EmitSymbolDefinition(nodeContents.Data.Length); // Publish Windows unwind info. if (factory.Target.IsWindows) { objectWriter.PublishUnwindInfo(node); } // Emit the last CFI to close the frame. objectWriter.EmitCFICodes(nodeContents.Data.Length); if (objectWriter.HasFunctionDebugInfo()) { if (factory.Target.OperatingSystem == TargetOS.Windows) { // Build debug local var info. // It currently supports only Windows CodeView format. objectWriter.EmitDebugVarInfo(node); } objectWriter.EmitDebugFunctionInfo(nodeContents.Data.Length); } } objectWriter.EmitDebugModuleInfo(); succeeded = true; } finally { objectWriter.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}"); 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 ISymbolNode ObjectAndOffset(ObjectNode obj, int offset, string name) { return(_internalSymbols.GetOrAdd(new Tuple <ObjectNode, int, string>(obj, offset, name))); }
public ObjectAndOffsetSymbolNode(ObjectNode obj, int offset, string name) { _object = obj; _offset = offset; _name = name; }
public void PublishUnwindInfo(ObjectNode node) { INodeWithCodeInfo nodeWithCodeInfo = node as INodeWithCodeInfo; if (nodeWithCodeInfo == null) { return; } FrameInfo[] frameInfos = nodeWithCodeInfo.FrameInfos; if (frameInfos == null) { return; } byte[] gcInfo = nodeWithCodeInfo.GCInfo; ObjectData ehInfo = nodeWithCodeInfo.EHInfo; for (int i = 0; i < frameInfos.Length; i++) { FrameInfo frameInfo = frameInfos[i]; int start = frameInfo.StartOffset; int end = frameInfo.EndOffset; int len = frameInfo.BlobData.Length; byte[] blob = frameInfo.BlobData; _sb.Clear().Append(_nodeFactory.NameMangler.CompilationUnitPrefix).Append("_unwind").Append(i.ToStringInvariant()); ObjectNodeSection section = ObjectNodeSection.XDataSection; SwitchSection(_nativeObjectWriter, section.Name); byte[] blobSymbolName = _sb.Append(_currentNodeZeroTerminatedName).ToUtf8String().UnderlyingArray; EmitAlignment(4); EmitSymbolDef(blobSymbolName); FrameInfoFlags flags = frameInfo.Flags; if (ehInfo != null) { flags |= FrameInfoFlags.HasEHInfo; } EmitBlob(blob); EmitIntValue((byte)flags, 1); if (ehInfo != null) { EmitSymbolRef(_sb.Clear().Append(_nodeFactory.NameMangler.CompilationUnitPrefix).Append("_ehInfo").Append(_currentNodeZeroTerminatedName), RelocType.IMAGE_REL_BASED_ABSOLUTE); } if (gcInfo != null) { EmitBlob(gcInfo); gcInfo = null; } if (ehInfo != null) { // TODO: Place EHInfo into different section for better locality Debug.Assert(ehInfo.Alignment == 1); Debug.Assert(ehInfo.DefinedSymbols.Length == 0); EmitSymbolDef(_sb /* ehInfo */); EmitBlobWithRelocs(ehInfo.Data, ehInfo.Relocs); ehInfo = null; } // For window, just emit the frame blob (UNWIND_INFO) as a whole. EmitWinFrameInfo(start, end, len, blobSymbolName); EnsureCurrentSection(); } }
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 { } } } }
public void StartObjectNode(ObjectNode node) { Debug.Assert(_currentObjectNode == null); _currentObjectNode = node; Debug.Assert(_currentObjectData.Count == 0); }
public static void EmitObject(string objectFilePath, IEnumerable <DependencyNode> nodes, NodeFactory factory, WebAssemblyCodegenCompilation compilation, IObjectDumper dumper) { WebAssemblyObjectWriter objectWriter = new WebAssemblyObjectWriter(objectFilePath, factory, compilation); bool succeeded = false; try { objectWriter.EmitReadyToRunHeaderCallback(compilation.Module.Context); //ObjectNodeSection managedCodeSection = null; var listOfOffsets = new List <int>(); foreach (DependencyNode depNode in nodes) { ObjectNode node = depNode as ObjectNode; if (node == null) { continue; } if (node.ShouldSkipEmittingObjectNode(factory)) { continue; } if (node is ReadyToRunGenericHelperNode readyToRunGenericHelperNode) { objectWriter.GetCodeForReadyToRunGenericHelper(compilation, readyToRunGenericHelperNode, factory); continue; } objectWriter.StartObjectNode(node); ObjectData nodeContents = node.GetData(factory); if (dumper != null) { dumper.DumpObjectNode(factory.NameMangler, node, nodeContents); } #if DEBUG foreach (ISymbolNode definedSymbol in nodeContents.DefinedSymbols) { try { _previouslyWrittenNodeNames.Add(definedSymbol.GetMangledName(factory.NameMangler), definedSymbol); } catch (ArgumentException) { ISymbolNode alreadyWrittenSymbol = _previouslyWrittenNodeNames[definedSymbol.GetMangledName(factory.NameMangler)]; Debug.Fail("Duplicate node name emitted to file", $"Symbol {definedSymbol.GetMangledName(factory.NameMangler)} has already been written to the output object file {objectFilePath} with symbol {alreadyWrittenSymbol}"); } } #endif ObjectNodeSection section = node.Section; if (objectWriter.ShouldShareSymbol(node)) { section = objectWriter.GetSharedSection(section, ((ISymbolNode)node).GetMangledName(factory.NameMangler)); } // Ensure section and alignment for the node. objectWriter.SetSection(section); objectWriter.EmitAlignment(nodeContents.Alignment); objectWriter.ResetByteRunInterruptionOffsets(nodeContents.Relocs); // Build symbol definition map. objectWriter.BuildSymbolDefinitionMap(node, nodeContents.DefinedSymbols); Relocation[] relocs = nodeContents.Relocs; int nextRelocOffset = -1; int nextRelocIndex = -1; if (relocs.Length > 0) { nextRelocOffset = relocs[0].Offset; nextRelocIndex = 0; } int i = 0; listOfOffsets.Clear(); listOfOffsets.AddRange(objectWriter._byteInterruptionOffsets); int offsetIndex = 0; while (i < nodeContents.Data.Length) { // Emit symbol definitions if necessary objectWriter.EmitSymbolDefinition(i); if (i == nextRelocOffset) { Relocation reloc = relocs[nextRelocIndex]; long delta; unsafe { fixed(void *location = &nodeContents.Data[i]) { delta = Relocation.ReadValue(reloc.RelocType, location); } } ISymbolNode symbolToWrite = reloc.Target; var eeTypeNode = reloc.Target as EETypeNode; if (eeTypeNode != null) { if (eeTypeNode.ShouldSkipEmittingObjectNode(factory)) { symbolToWrite = factory.ConstructedTypeSymbol(eeTypeNode.Type); } } int size = objectWriter.EmitSymbolReference(symbolToWrite, (int)delta, reloc.RelocType); /* * WebAssembly has no thumb * // Emit a copy of original Thumb2 instruction that came from RyuJIT * if (reloc.RelocType == RelocType.IMAGE_REL_BASED_THUMB_MOV32 || * reloc.RelocType == RelocType.IMAGE_REL_BASED_THUMB_BRANCH24) * { * unsafe * { * fixed (void* location = &nodeContents.Data[i]) * { * objectWriter.EmitBytes((IntPtr)location, size); * } * } * }*/ // Update nextRelocIndex/Offset if (++nextRelocIndex < relocs.Length) { nextRelocOffset = relocs[nextRelocIndex].Offset; } else { // This is the last reloc. Set the next reloc offset to -1 in case the last reloc has a zero size, // which means the reloc does not have vacant bytes corresponding to in the data buffer. E.g, // IMAGE_REL_THUMB_BRANCH24 is a kind of 24-bit reloc whose bits scatte over the instruction that // references it. We do not vacate extra bytes in the data buffer for this kind of reloc. nextRelocOffset = -1; } i += size; } else { while (offsetIndex < listOfOffsets.Count && listOfOffsets[offsetIndex] <= i) { offsetIndex++; } int nextOffset = offsetIndex == listOfOffsets.Count ? nodeContents.Data.Length : listOfOffsets[offsetIndex]; unsafe { // Todo: Use Span<T> instead once it's available to us in this repo fixed(byte *pContents = &nodeContents.Data[i]) { objectWriter.EmitBytes((IntPtr)(pContents), nextOffset - i); i += nextOffset - i; } } } } Debug.Assert(i == nodeContents.Data.Length); // It is possible to have a symbol just after all of the data. objectWriter.EmitSymbolDefinition(nodeContents.Data.Length); objectWriter.DoneObjectNode(); } succeeded = true; } finally { objectWriter.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); // 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; 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 static void EmitObject(string objectFilePath, IEnumerable <DependencyNode> nodes, NodeFactory factory) { using (ObjectWriter objectWriter = new ObjectWriter(objectFilePath, factory)) { // TODO: Exception handling on Unix if (factory.Target.IsWindows) { objectWriter.CreateCustomSection(MethodCodeNode.ContentSection); // Emit sentinels for managed code section. ObjectNodeSection codeStartSection = factory.CompilationModuleGroup.IsSingleFileCompilation ? MethodCodeNode.StartSection : MethodCodeNode.StartSection.GetSharedSection("__managedcode_a"); objectWriter.SetSection(codeStartSection); objectWriter.EmitSymbolDef("__managedcode_a"); objectWriter.EmitIntValue(0, 1); ObjectNodeSection codeEndSection = factory.CompilationModuleGroup.IsSingleFileCompilation ? MethodCodeNode.EndSection : MethodCodeNode.EndSection.GetSharedSection("__managedcode_z"); objectWriter.SetSection(codeEndSection); objectWriter.EmitSymbolDef("__managedcode_z"); objectWriter.EmitIntValue(0, 1); } // Build file info map. objectWriter.BuildFileInfoMap(nodes); foreach (DependencyNode depNode in nodes) { ObjectNode node = depNode as ObjectNode; if (node == null) { continue; } if (node.ShouldSkipEmittingObjectNode(factory)) { continue; } #if DEBUG Debug.Assert(_previouslyWrittenNodeNames.Add(node.GetName()), "Duplicate node name emitted to file", "Node {0} has already been written to the output object file {1}", node.GetName(), objectFilePath); #endif ObjectNode.ObjectData nodeContents = node.GetData(factory); ObjectNodeSection section = node.Section; if (node.ShouldShareNodeAcrossModules(factory) && factory.Target.OperatingSystem == TargetOS.Windows) { Debug.Assert(node is ISymbolNode); section = section.GetSharedSection(((ISymbolNode)node).MangledName); } // Ensure section and alignment for the node. objectWriter.SetSection(section); objectWriter.EmitAlignment(nodeContents.Alignment); // Build symbol definition map. objectWriter.BuildSymbolDefinitionMap(node, nodeContents.DefinedSymbols); // Build CFI map (Unix) or publish unwind blob (Windows). objectWriter.BuildCFIMap(factory, node); // Build debug location map objectWriter.BuildDebugLocInfoMap(node); Relocation[] relocs = nodeContents.Relocs; int nextRelocOffset = -1; int nextRelocIndex = -1; if (relocs.Length > 0) { nextRelocOffset = relocs[0].Offset; nextRelocIndex = 0; } int i = 0; while (i < nodeContents.Data.Length) { // Emit symbol definitions if necessary objectWriter.EmitSymbolDefinition(i); // Emit CFI codes for the given offset. objectWriter.EmitCFICodes(i); // Emit debug loc info if needed. objectWriter.EmitDebugLocInfo(i); if (i == nextRelocOffset) { Relocation reloc = relocs[nextRelocIndex]; int size = objectWriter.EmitSymbolReference(reloc.Target, reloc.Delta, reloc.RelocType); // Update nextRelocIndex/Offset if (++nextRelocIndex < relocs.Length) { nextRelocOffset = relocs[nextRelocIndex].Offset; } i += size; } else { objectWriter.EmitIntValue(nodeContents.Data[i], 1); i++; } } // It is possible to have a symbol just after all of the data. objectWriter.EmitSymbolDefinition(nodeContents.Data.Length); // Emit the last CFI to close the frame. objectWriter.EmitCFICodes(nodeContents.Data.Length); if (objectWriter.HasFunctionDebugInfo()) { // Build debug local var info objectWriter.EmitDebugVarInfo(node); objectWriter.EmitDebugFunctionInfo(nodeContents.Data.Length); } } objectWriter.EmitDebugModuleInfo(); } }
public void BuildCFIMap(NodeFactory factory, ObjectNode node) { _offsetToCfis.Clear(); _offsetToCfiStart.Clear(); _offsetToCfiEnd.Clear(); _frameOpened = false; INodeWithCodeInfo nodeWithCodeInfo = node as INodeWithCodeInfo; if (nodeWithCodeInfo == null) { return; } FrameInfo[] frameInfos = nodeWithCodeInfo.FrameInfos; if (frameInfos == null) { return; } ObjectNode.ObjectData ehInfo = nodeWithCodeInfo.EHInfo; int i = 0; foreach (var frameInfo in frameInfos) { int start = frameInfo.StartOffset; int end = frameInfo.EndOffset; int len = frameInfo.BlobData.Length; byte[] blob = frameInfo.BlobData; if (_targetPlatform.OperatingSystem == TargetOS.Windows) { string blobSymbolName = "_unwind" + (i++).ToStringInvariant() + _currentNodeName; ObjectNodeSection section = ObjectNodeSection.XDataSection; if (node.ShouldShareNodeAcrossModules(factory) && factory.Target.OperatingSystem == TargetOS.Windows) { section = section.GetSharedSection(blobSymbolName); CreateCustomSection(section); } SwitchSection(_nativeObjectWriter, section.Name); EmitAlignment(4); EmitSymbolDef(blobSymbolName); if (ehInfo != null) { blob[blob.Length - 1] |= 0x04; // Flag to indicate that EHClauses follows } EmitBlob(blob); if (ehInfo != null) { Debug.Assert(ehInfo.Alignment == 1); Debug.Assert(ehInfo.DefinedSymbols.Length == 0); EmitBlobWithRelocs(ehInfo.Data, ehInfo.Relocs); ehInfo = null; } // TODO: Currently we get linker errors if we emit frame info for shared types. // This needs follow-up investigation. if (!node.ShouldShareNodeAcrossModules(factory)) { // For window, just emit the frame blob (UNWIND_INFO) as a whole. EmitWinFrameInfo(start, end, len, blobSymbolName); } EnsureCurrentSection(); } else { // For Unix, we build CFI blob map for each offset. Debug.Assert(len % CfiCodeSize == 0); // Record start/end of frames which shouldn't be overlapped. _offsetToCfiStart.Add(start); _offsetToCfiEnd.Add(end); for (int j = 0; j < len; j += CfiCodeSize) { // The first byte of CFI_CODE is offset from the range the frame covers. // Compute code offset from the root method. int codeOffset = blob[j] + start; List <byte[]> cfis; if (!_offsetToCfis.TryGetValue(codeOffset, out cfis)) { cfis = new List <byte[]>(); _offsetToCfis.Add(codeOffset, cfis); } byte[] cfi = new byte[CfiCodeSize]; Array.Copy(blob, j, cfi, 0, CfiCodeSize); cfis.Add(cfi); } } } }
public static void EmitObject(string objectFilePath, IEnumerable <DependencyNode> nodes, NodeFactory factory) { ObjectWriter objectWriter = new ObjectWriter(objectFilePath, factory); bool succeeded = false; try { if (factory.Target.OperatingSystem == TargetOS.Windows) { objectWriter.CreateCustomSection(MethodCodeNode.WindowsContentSection); // Emit sentinels for managed code section. ObjectNodeSection codeStartSection = factory.CompilationModuleGroup.IsSingleFileCompilation ? MethodCodeNode.StartSection : MethodCodeNode.StartSection.GetSharedSection("__managedcode_a"); objectWriter.SetSection(codeStartSection); objectWriter.EmitSymbolDef(new Utf8StringBuilder().Append("__managedcode_a")); objectWriter.EmitIntValue(0, 1); ObjectNodeSection codeEndSection = factory.CompilationModuleGroup.IsSingleFileCompilation ? MethodCodeNode.EndSection : MethodCodeNode.EndSection.GetSharedSection("__managedcode_z"); objectWriter.SetSection(codeEndSection); objectWriter.EmitSymbolDef(new Utf8StringBuilder().Append("__managedcode_z")); objectWriter.EmitIntValue(0, 1); } else { objectWriter.CreateCustomSection(MethodCodeNode.UnixContentSection); objectWriter.CreateCustomSection(LsdaSection); } // Build file info map. objectWriter.BuildFileInfoMap(nodes); foreach (DependencyNode depNode in nodes) { ObjectNode node = depNode as ObjectNode; if (node == null) { continue; } if (node.ShouldSkipEmittingObjectNode(factory)) { continue; } ObjectNode.ObjectData nodeContents = node.GetData(factory); #if DEBUG foreach (ISymbolNode definedSymbol in nodeContents.DefinedSymbols) { Debug.Assert(_previouslyWrittenNodeNames.Add(definedSymbol.GetMangledName()), "Duplicate node name emitted to file", $"Symbol {definedSymbol.GetMangledName()} has already been written to the output object file {objectFilePath}"); } #endif ObjectNodeSection section = node.Section; if (objectWriter.ShouldShareSymbol(node)) { section = section.GetSharedSection(((ISymbolNode)node).GetMangledName()); } // Ensure section and alignment for the node. objectWriter.SetSection(section); objectWriter.EmitAlignment(nodeContents.Alignment); // Build symbol definition map. objectWriter.BuildSymbolDefinitionMap(node, nodeContents.DefinedSymbols); // Build CFI map (Unix) or publish unwind blob (Windows). objectWriter.BuildCFIMap(factory, node); // Build debug location map objectWriter.BuildDebugLocInfoMap(node); Relocation[] relocs = nodeContents.Relocs; int nextRelocOffset = -1; int nextRelocIndex = -1; if (relocs.Length > 0) { nextRelocOffset = relocs[0].Offset; nextRelocIndex = 0; } int i = 0; while (i < nodeContents.Data.Length) { // Emit symbol definitions if necessary objectWriter.EmitSymbolDefinition(i); // Emit CFI codes for the given offset. objectWriter.EmitCFICodes(i); // Emit debug loc info if needed. objectWriter.EmitDebugLocInfo(i); if (i == nextRelocOffset) { Relocation reloc = relocs[nextRelocIndex]; long delta; unsafe { fixed(void *location = &nodeContents.Data[i]) { delta = Relocation.ReadValue(reloc.RelocType, location); } } int size = objectWriter.EmitSymbolReference(reloc.Target, (int)delta, reloc.RelocType); // Update nextRelocIndex/Offset if (++nextRelocIndex < relocs.Length) { nextRelocOffset = relocs[nextRelocIndex].Offset; } i += size; } else { objectWriter.EmitIntValue(nodeContents.Data[i], 1); i++; } } // It is possible to have a symbol just after all of the data. objectWriter.EmitSymbolDefinition(nodeContents.Data.Length); // Emit the last CFI to close the frame. objectWriter.EmitCFICodes(nodeContents.Data.Length); if (objectWriter.HasFunctionDebugInfo()) { // Build debug local var info objectWriter.EmitDebugVarInfo(node); objectWriter.EmitDebugFunctionInfo(nodeContents.Data.Length); } } objectWriter.EmitDebugModuleInfo(); succeeded = true; } finally { objectWriter.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; 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 (_outputInfoBuilder != null && 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 Add(ReadyToRunSectionType id, ObjectNode node, ISymbolNode startSymbol, ISymbolNode endSymbol = null) { _items.Add(new HeaderItem(id, node, startSymbol, endSymbol)); }
public static void EmitObject(string OutputPath, IEnumerable <DependencyNode> nodes, NodeFactory factory) { using (ObjectWriter objectWriter = new ObjectWriter(OutputPath)) { string currentSection = ""; // Build file info map. string[] debugFileInfos = objectWriter.BuildFileInfoMap(nodes); if (debugFileInfos != null) { objectWriter.EmitDebugFileInfo(debugFileInfos.Length, debugFileInfos); } foreach (DependencyNode depNode in nodes) { ObjectNode node = depNode as ObjectNode; if (node == null) { continue; } if (node.ShouldSkipEmittingObjectNode(factory)) { continue; } ObjectNode.ObjectData nodeContents = node.GetData(factory); if (currentSection != node.Section) { currentSection = node.Section; objectWriter.SwitchSection(currentSection); } objectWriter.EmitAlignment(nodeContents.Alignment); Relocation[] relocs = nodeContents.Relocs; int nextRelocOffset = -1; int nextRelocIndex = -1; if (relocs != null && relocs.Length > 0) { nextRelocOffset = relocs[0].Offset; nextRelocIndex = 0; } // Build symbol definition map. objectWriter.BuildSymbolDefinitionMap(nodeContents.DefinedSymbols); // Build debug location map objectWriter.BuildDebugLocInfoMap(node); for (int i = 0; i < nodeContents.Data.Length; i++) { // Emit symbol definitions if necessary objectWriter.EmitSymbolDefinition(i, factory); // Emit debug loc info if needed. objectWriter.EmitDebugLocInfo(i); if (i == nextRelocOffset) { Relocation reloc = relocs[nextRelocIndex]; ISymbolNode target = reloc.Target; string targetName = target.MangledName; int size = 0; bool isPCRelative = false; switch (reloc.RelocType) { case RelocType.IMAGE_REL_BASED_DIR64: size = 8; break; case RelocType.IMAGE_REL_BASED_REL32: size = 4; isPCRelative = true; break; default: throw new NotImplementedException(); } // Emit symbol reference objectWriter.EmitSymbolRef(targetName, size, isPCRelative, reloc.Delta); // Update nextRelocIndex/Offset if (++nextRelocIndex < relocs.Length) { nextRelocOffset = relocs[nextRelocIndex].Offset; } i += size - 1; continue; } objectWriter.EmitIntValue(nodeContents.Data[i], 1); } // It is possible to have a symbol just after all of the data. objectWriter.EmitSymbolDefinition(nodeContents.Data.Length, factory); // The first definition is the main node name string nodeName = objectWriter._offsetToDefSymbol[0][0].MangledName; // Emit frame info for object code. if (node is INodeWithFrameInfo) { FrameInfo[] frameInfos = ((INodeWithFrameInfo)node).FrameInfos; if (frameInfos != null) { foreach (var frameInfo in frameInfos) { objectWriter.EmitFrameInfo(nodeName, frameInfo.StartOffset, frameInfo.EndOffset, frameInfo.BlobData.Length, frameInfo.BlobData); } } } objectWriter.FlushDebugLocs(nodeName, nodeContents.Data.Length); objectWriter.SwitchSection(currentSection); } } }
public static void EmitObject(string objectFilePath, IEnumerable <DependencyNode> nodes, NodeFactory factory) { using (ObjectWriter objectWriter = new ObjectWriter(objectFilePath, factory)) { // Build file info map. string[] debugFileInfos = objectWriter.BuildFileInfoMap(nodes); if (debugFileInfos != null) { objectWriter.EmitDebugFileInfo(debugFileInfos.Length, debugFileInfos); } foreach (DependencyNode depNode in nodes) { ObjectNode node = depNode as ObjectNode; if (node == null) { continue; } if (node.ShouldSkipEmittingObjectNode(factory)) { continue; } #if DEBUG Debug.Assert(_previouslyWrittenNodeNames.Add(node.GetName()), "Duplicate node name emitted to file", "Node {0} has already been written to the output object file {1}", node.GetName(), objectFilePath); #endif ObjectNode.ObjectData nodeContents = node.GetData(factory); // Ensure section and alignment for the node. objectWriter.SetSection(node.Section); objectWriter.EmitAlignment(nodeContents.Alignment); Relocation[] relocs = nodeContents.Relocs; int nextRelocOffset = -1; int nextRelocIndex = -1; if (relocs != null && relocs.Length > 0) { nextRelocOffset = relocs[0].Offset; nextRelocIndex = 0; } // Build symbol definition map. objectWriter.BuildSymbolDefinitionMap(nodeContents.DefinedSymbols); // Build CFI map (Unix) or publish unwind blob (Windows). objectWriter.BuildCFIMap(node); // Build debug location map objectWriter.BuildDebugLocInfoMap(node); for (int i = 0; i < nodeContents.Data.Length; i++) { // Emit symbol definitions if necessary objectWriter.EmitSymbolDefinition(i); // Emit CFI codes for the given offset. objectWriter.EmitCFICodes(i); // Emit debug loc info if needed. objectWriter.EmitDebugLocInfo(i); if (i == nextRelocOffset) { Relocation reloc = relocs[nextRelocIndex]; ISymbolNode target = reloc.Target; string targetName = objectWriter.GetSymbolToEmitForTargetPlatform(target.MangledName); int size = 0; bool isPCRelative = false; switch (reloc.RelocType) { case RelocType.IMAGE_REL_BASED_DIR64: size = 8; break; case RelocType.IMAGE_REL_BASED_REL32: size = 4; isPCRelative = true; break; default: throw new NotImplementedException(); } // Emit symbol reference objectWriter.EmitSymbolRef(targetName, size, isPCRelative, reloc.Delta); // Update nextRelocIndex/Offset if (++nextRelocIndex < relocs.Length) { nextRelocOffset = relocs[nextRelocIndex].Offset; } i += size - 1; continue; } objectWriter.EmitIntValue(nodeContents.Data[i], 1); } // It is possible to have a symbol just after all of the data. objectWriter.EmitSymbolDefinition(nodeContents.Data.Length); // Emit the last CFI to close the frame. objectWriter.EmitCFICodes(nodeContents.Data.Length); objectWriter.FlushDebugLocs(nodeContents.Data.Length); } } }
public void BuildCFIMap(NodeFactory factory, ObjectNode node) { _offsetToCfis.Clear(); _offsetToCfiStart.Clear(); _offsetToCfiEnd.Clear(); _offsetToCfiLsdaBlobName.Clear(); _frameOpened = false; INodeWithCodeInfo nodeWithCodeInfo = node as INodeWithCodeInfo; if (nodeWithCodeInfo == null) { return; } FrameInfo[] frameInfos = nodeWithCodeInfo.FrameInfos; if (frameInfos == null) { return; } byte[] gcInfo = nodeWithCodeInfo.GCInfo; ObjectData ehInfo = nodeWithCodeInfo.EHInfo; for (int i = 0; i < frameInfos.Length; i++) { FrameInfo frameInfo = frameInfos[i]; int start = frameInfo.StartOffset; int end = frameInfo.EndOffset; int len = frameInfo.BlobData.Length; byte[] blob = frameInfo.BlobData; ObjectNodeSection lsdaSection = LsdaSection; if (ShouldShareSymbol(node)) { lsdaSection = lsdaSection.GetSharedSection(((ISymbolNode)node).GetMangledName(_nodeFactory.NameMangler)); } SwitchSection(_nativeObjectWriter, lsdaSection.Name, GetCustomSectionAttributes(lsdaSection), lsdaSection.ComdatName); _sb.Clear().Append("_lsda").Append(i.ToStringInvariant()).Append(_currentNodeZeroTerminatedName); byte[] blobSymbolName = _sb.ToUtf8String().UnderlyingArray; EmitSymbolDef(blobSymbolName); FrameInfoFlags flags = frameInfo.Flags; if (ehInfo != null) { flags |= FrameInfoFlags.HasEHInfo; } EmitIntValue((byte)flags, 1); if (i != 0) { EmitSymbolRef(_sb.Clear().Append("_lsda0").Append(_currentNodeZeroTerminatedName), RelocType.IMAGE_REL_BASED_RELPTR32); // emit relative offset from the main function EmitIntValue((ulong)(start - frameInfos[0].StartOffset), 4); } if (ehInfo != null) { EmitSymbolRef(_sb.Clear().Append("_ehInfo").Append(_currentNodeZeroTerminatedName), RelocType.IMAGE_REL_BASED_RELPTR32); } if (gcInfo != null) { EmitBlob(gcInfo); gcInfo = null; } if (ehInfo != null) { // TODO: Place EHInfo into different section for better locality Debug.Assert(ehInfo.Alignment == 1); Debug.Assert(ehInfo.DefinedSymbols.Length == 0); EmitSymbolDef(_sb /* ehInfo */); EmitBlobWithRelocs(ehInfo.Data, ehInfo.Relocs); ehInfo = null; } // For Unix, we build CFI blob map for each offset. Debug.Assert(len % CfiCodeSize == 0); // Record start/end of frames which shouldn't be overlapped. _offsetToCfiStart.Add(start); _offsetToCfiEnd.Add(end); _offsetToCfiLsdaBlobName.Add(start, blobSymbolName); for (int j = 0; j < len; j += CfiCodeSize) { // The first byte of CFI_CODE is offset from the range the frame covers. // Compute code offset from the root method. int codeOffset = blob[j] + start; List <byte[]> cfis; if (!_offsetToCfis.TryGetValue(codeOffset, out cfis)) { cfis = new List <byte[]>(); _offsetToCfis.Add(codeOffset, cfis); } byte[] cfi = new byte[CfiCodeSize]; Array.Copy(blob, j, cfi, 0, CfiCodeSize); cfis.Add(cfi); } } EnsureCurrentSection(); }
public void BuildCFIMap(NodeFactory factory, ObjectNode node) { _offsetToCfis.Clear(); _offsetToCfiStart.Clear(); _offsetToCfiEnd.Clear(); _offsetToCfiLsdaBlobName.Clear(); _frameOpened = false; INodeWithCodeInfo nodeWithCodeInfo = node as INodeWithCodeInfo; if (nodeWithCodeInfo == null) { return; } FrameInfo[] frameInfos = nodeWithCodeInfo.FrameInfos; if (frameInfos == null) { return; } byte[] gcInfo = nodeWithCodeInfo.GCInfo; ObjectNode.ObjectData ehInfo = nodeWithCodeInfo.EHInfo; string mainEhInfoSymbolName = null; for (int i = 0; i < frameInfos.Length; i++) { FrameInfo frameInfo = frameInfos[i]; int start = frameInfo.StartOffset; int end = frameInfo.EndOffset; int len = frameInfo.BlobData.Length; byte[] blob = frameInfo.BlobData; if (_targetPlatform.OperatingSystem == TargetOS.Windows) { string blobSymbolName = "_unwind" + i.ToStringInvariant() + _currentNodeName; ObjectNodeSection section = ObjectNodeSection.XDataSection; if (node.ShouldShareNodeAcrossModules(factory) && factory.Target.OperatingSystem == TargetOS.Windows) { section = section.GetSharedSection(blobSymbolName); CreateCustomSection(section); } SwitchSection(_nativeObjectWriter, section.Name); EmitAlignment(4); EmitSymbolDef(blobSymbolName); FrameInfoFlags flags = frameInfo.Flags; if (ehInfo != null) { flags |= FrameInfoFlags.HasEHInfo; } EmitBlob(blob); EmitIntValue((byte)flags, 1); if (ehInfo != null) { mainEhInfoSymbolName = "_ehInfo" + _currentNodeName; EmitSymbolRef(mainEhInfoSymbolName, RelocType.IMAGE_REL_BASED_ABSOLUTE); } if (gcInfo != null) { EmitBlob(gcInfo); gcInfo = null; } if (ehInfo != null) { // TODO: Place EHInfo into different section for better locality Debug.Assert(ehInfo.Alignment == 1); Debug.Assert(ehInfo.DefinedSymbols.Length == 0); EmitSymbolDef(mainEhInfoSymbolName); EmitBlobWithRelocs(ehInfo.Data, ehInfo.Relocs); ehInfo = null; } // TODO: Currently we get linker errors if we emit frame info for shared types. // This needs follow-up investigation. if (!node.ShouldShareNodeAcrossModules(factory)) { // For window, just emit the frame blob (UNWIND_INFO) as a whole. EmitWinFrameInfo(start, end, len, blobSymbolName); } } else { string blobSymbolName = "_lsda" + i.ToStringInvariant() + _currentNodeName; SwitchSection(_nativeObjectWriter, LsdaSection.Name); EmitSymbolDef(blobSymbolName); FrameInfoFlags flags = frameInfo.Flags; if (ehInfo != null) { flags |= FrameInfoFlags.HasEHInfo; } EmitIntValue((byte)flags, 1); if (i != 0) { EmitSymbolRef("_lsda0" + _currentNodeName, RelocType.IMAGE_REL_BASED_REL32, 4); // emit relative offset from the main function EmitIntValue((ulong)(start - frameInfos[0].StartOffset), 4); } if (ehInfo != null) { mainEhInfoSymbolName = "_ehInfo" + _currentNodeName; EmitSymbolRef(mainEhInfoSymbolName, RelocType.IMAGE_REL_BASED_REL32, 4); } if (gcInfo != null) { EmitBlob(gcInfo); gcInfo = null; } if (ehInfo != null) { // TODO: Place EHInfo into different section for better locality Debug.Assert(ehInfo.Alignment == 1); Debug.Assert(ehInfo.DefinedSymbols.Length == 0); EmitSymbolDef(mainEhInfoSymbolName); EmitBlobWithRelocs(ehInfo.Data, ehInfo.Relocs); ehInfo = null; } // For Unix, we build CFI blob map for each offset. Debug.Assert(len % CfiCodeSize == 0); // Record start/end of frames which shouldn't be overlapped. _offsetToCfiStart.Add(start); _offsetToCfiEnd.Add(end); _offsetToCfiLsdaBlobName.Add(start, blobSymbolName); for (int j = 0; j < len; j += CfiCodeSize) { // The first byte of CFI_CODE is offset from the range the frame covers. // Compute code offset from the root method. int codeOffset = blob[j] + start; List <byte[]> cfis; if (!_offsetToCfis.TryGetValue(codeOffset, out cfis)) { cfis = new List <byte[]>(); _offsetToCfis.Add(codeOffset, cfis); } byte[] cfi = new byte[CfiCodeSize]; Array.Copy(blob, j, cfi, 0, CfiCodeSize); cfis.Add(cfi); } } EnsureCurrentSection(); } }