diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/ISymbolNode.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/ISymbolNode.cs index 742e0bd9505f6b..c44efbb394bd8f 100644 --- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/ISymbolNode.cs +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/ISymbolNode.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.IO; using ILCompiler.DependencyAnalysisFramework; using Internal.Text; @@ -97,7 +98,14 @@ public interface IChecksumNode : ISymbolNode { int ChecksumSize { get; } - void EmitChecksum(ReadOnlySpan outputBlob, Span checksumLocation); + /// + /// Computes the checksum over the complete output image. + /// is a seekable stream positioned at the start of the image and contains the image as it + /// is before any checksums are written, so the result must only depend on those original + /// bytes. The method may read the stream and leave its position changed, but must not + /// modify the stream contents; the caller writes the result. + /// + void EmitChecksum(Stream outputStream, Span checksumLocation); } /// diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/ObjectWriter.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/ObjectWriter.cs index 6229e6d43aaf45..c2cf6b7fdf4505 100644 --- a/src/coreclr/tools/Common/Compiler/ObjectWriter/ObjectWriter.cs +++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/ObjectWriter.cs @@ -668,15 +668,20 @@ private static string GetNodeTypeName(Type nodeType) private void EmitChecksums(Stream outputFileStream, List checksumRelocations) { - MemoryStream originalOutputStream = new(); - outputFileStream.Seek(0, SeekOrigin.Begin); - outputFileStream.CopyTo(originalOutputStream); - byte[] originalOutput = originalOutputStream.ToArray(); - EmitChecksumsForObject(outputFileStream, checksumRelocations, originalOutput); + // Defer writing the computed values until all checksums are computed so each one is + // calculated over the original image and not a value written by an earlier checksum. + List<(long Offset, byte[] Value)> pendingWrites = ComputeChecksums(outputFileStream, checksumRelocations); + + foreach ((long offset, byte[] value) in pendingWrites) + { + outputFileStream.Seek(offset, SeekOrigin.Begin); + outputFileStream.Write(value); + } } - private protected virtual void EmitChecksumsForObject(Stream outputFileStream, List checksumRelocations, ReadOnlySpan originalOutput) + private protected virtual List<(long Offset, byte[] Value)> ComputeChecksums(Stream outputFileStream, List checksumRelocations) { + List<(long Offset, byte[] Value)> pendingWrites = []; foreach (var block in checksumRelocations) { foreach (var reloc in block.ChecksumRelocations) @@ -684,13 +689,15 @@ private protected virtual void EmitChecksumsForObject(Stream outputFileStream, L IChecksumNode checksum = (IChecksumNode)reloc.Target; byte[] checksumValue = new byte[checksum.ChecksumSize]; - checksum.EmitChecksum(originalOutput, checksumValue); + outputFileStream.Seek(0, SeekOrigin.Begin); + checksum.EmitChecksum(outputFileStream, checksumValue); var checksumOffset = (long)_outputSectionLayout[block.SectionIndex].FilePosition + block.Offset + reloc.Offset; - outputFileStream.Seek(checksumOffset, SeekOrigin.Begin); - outputFileStream.Write(checksumValue); + pendingWrites.Add((checksumOffset, checksumValue)); } } + + return pendingWrites; } partial void HandleControlFlowForRelocation(ISymbolNode relocTarget, Utf8String relocSymbolName); diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/PEObjectWriter.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/PEObjectWriter.cs index ad67397800d1ea..ebbb1a33ebc115 100644 --- a/src/coreclr/tools/Common/Compiler/ObjectWriter/PEObjectWriter.cs +++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/PEObjectWriter.cs @@ -11,7 +11,6 @@ using System.Reflection.Metadata; using System.Reflection.PortableExecutable; using System.Security.Cryptography; -using System.Text; using ILCompiler.DependencyAnalysis; @@ -868,17 +867,25 @@ private void PopulateDataDirectoryForWellKnownSymbolIfPresent(OptionalHeaderData } } - private protected override void EmitChecksumsForObject(Stream outputFileStream, List checksumRelocations, ReadOnlySpan originalOutput) + private protected override List<(long Offset, byte[] Value)> ComputeChecksums(Stream outputFileStream, List checksumRelocations) { - base.EmitChecksumsForObject(outputFileStream, checksumRelocations, originalOutput); + List<(long Offset, byte[] Value)> pendingWrites = base.ComputeChecksums(outputFileStream, checksumRelocations); if (_coffTimestamp is null) { // If we were not provided a deterministic timestamp, generate one from a hash of the content. - outputFileStream.Seek(_coffHeaderOffset + CoffHeader.TimeDateStampOffset(bigObj: false), SeekOrigin.Begin); - using BinaryWriter writer = new(outputFileStream, Encoding.UTF8, leaveOpen: true); - writer.Write(BlobContentId.FromHash(SHA256.HashData(originalOutput)).Stamp); + outputFileStream.Seek(0, SeekOrigin.Begin); + Span hash = stackalloc byte[SHA256.HashSizeInBytes]; + SHA256.HashData(outputFileStream, hash); + + byte[] timestamp = new byte[sizeof(uint)]; + BinaryPrimitives.WriteUInt32LittleEndian(timestamp, BlobContentId.FromHash(hash.ToArray()).Stamp); + + long timestampOffset = _coffHeaderOffset + CoffHeader.TimeDateStampOffset(bigObj: false); + pendingWrites.Add((timestampOffset, timestamp)); } + + return pendingWrites; } private unsafe void ResolveRelocations(int sectionIndex, List symbolicRelocations, long imageBase, MemoryStream stream) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DebugDirectoryEntryNode.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DebugDirectoryEntryNode.cs index fcac97dee0ccef..bc31ff907f5fea 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DebugDirectoryEntryNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DebugDirectoryEntryNode.cs @@ -244,11 +244,13 @@ private class RSDSChecksumNode : DependencyNodeCore, IChecksumNode { public int ChecksumSize => 16; - public void EmitChecksum(ReadOnlySpan outputBlob, Span checksumLocation) + public void EmitChecksum(Stream outputStream, Span checksumLocation) { Debug.Assert(checksumLocation.Length == ChecksumSize); - // Take the first 16 bytes of the SHA256 hash as the RSDS checksum. - SHA256.HashData(outputBlob)[0..ChecksumSize].CopyTo(checksumLocation); + // Take the first 16 bytes of the SHA256 hash of the image as the RSDS checksum. + Span hash = stackalloc byte[SHA256.HashSizeInBytes]; + SHA256.HashData(outputStream, hash); + hash[0..ChecksumSize].CopyTo(checksumLocation); } public override bool InterestingForDynamicDependencyAnalysis => false;