--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#nullable enable
+
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Reflection.Metadata;
+using System.Security.Cryptography;
+
+namespace ILCompiler
+{
+ /// <summary>
+ /// Specifies a hash algorithms used for hashing source files.
+ /// </summary>
+ public enum SourceHashAlgorithm
+ {
+ /// <summary>
+ /// No algorithm specified.
+ /// </summary>
+ None = 0,
+
+ /// <summary>
+ /// Secure Hash Algorithm 1.
+ /// </summary>
+ Sha1 = 1,
+
+ /// <summary>
+ /// Secure Hash Algorithm 2 with a hash size of 256 bits.
+ /// </summary>
+ Sha256 = 2,
+ }
+
+ internal static class SourceHashAlgorithmUtils
+ {
+ public const SourceHashAlgorithm DefaultContentHashAlgorithm = SourceHashAlgorithm.Sha256;
+ }
+
+ internal abstract class CryptographicHashProvider
+ {
+ private ImmutableArray<byte> _lazySHA1Hash;
+ private ImmutableArray<byte> _lazySHA256Hash;
+ private ImmutableArray<byte> _lazySHA384Hash;
+ private ImmutableArray<byte> _lazySHA512Hash;
+ private ImmutableArray<byte> _lazyMD5Hash;
+
+ internal abstract ImmutableArray<byte> ComputeHash(HashAlgorithm algorithm);
+
+ internal ImmutableArray<byte> GetHash(AssemblyHashAlgorithm algorithmId)
+ {
+ using (HashAlgorithm? algorithm = TryGetAlgorithm(algorithmId))
+ {
+ // ERR_CryptoHashFailed has already been reported:
+ if (algorithm == null)
+ {
+ return ImmutableArray.Create<byte>();
+ }
+
+ switch (algorithmId)
+ {
+ case AssemblyHashAlgorithm.None:
+ case AssemblyHashAlgorithm.Sha1:
+ return GetHash(ref _lazySHA1Hash, algorithm);
+
+ case AssemblyHashAlgorithm.Sha256:
+ return GetHash(ref _lazySHA256Hash, algorithm);
+
+ case AssemblyHashAlgorithm.Sha384:
+ return GetHash(ref _lazySHA384Hash, algorithm);
+
+ case AssemblyHashAlgorithm.Sha512:
+ return GetHash(ref _lazySHA512Hash, algorithm);
+
+ case AssemblyHashAlgorithm.MD5:
+ return GetHash(ref _lazyMD5Hash, algorithm);
+
+ default:
+ throw new ArgumentException("algorithmId");
+ }
+ }
+ }
+
+ internal static int GetHashSize(SourceHashAlgorithm algorithmId)
+ {
+ switch (algorithmId)
+ {
+ case SourceHashAlgorithm.Sha1:
+ return 160 / 8;
+
+ case SourceHashAlgorithm.Sha256:
+ return 256 / 8;
+
+ default:
+ throw new ArgumentException("algorithmId");
+ }
+ }
+
+ internal static HashAlgorithm? TryGetAlgorithm(SourceHashAlgorithm algorithmId)
+ {
+ switch (algorithmId)
+ {
+ case SourceHashAlgorithm.Sha1:
+ return SHA1.Create();
+
+ case SourceHashAlgorithm.Sha256:
+ return SHA256.Create();
+
+ default:
+ return null;
+ }
+ }
+
+ internal static HashAlgorithmName GetAlgorithmName(SourceHashAlgorithm algorithmId)
+ {
+ switch (algorithmId)
+ {
+ case SourceHashAlgorithm.Sha1:
+ return HashAlgorithmName.SHA1;
+
+ case SourceHashAlgorithm.Sha256:
+ return HashAlgorithmName.SHA256;
+
+ default:
+ throw new ArgumentException("algorithmId");
+ }
+ }
+
+ internal static HashAlgorithm? TryGetAlgorithm(AssemblyHashAlgorithm algorithmId)
+ {
+ switch (algorithmId)
+ {
+ case AssemblyHashAlgorithm.None:
+ case AssemblyHashAlgorithm.Sha1:
+ return SHA1.Create();
+
+ case AssemblyHashAlgorithm.Sha256:
+ return SHA256.Create();
+
+ case AssemblyHashAlgorithm.Sha384:
+ return SHA384.Create();
+
+ case AssemblyHashAlgorithm.Sha512:
+ return SHA512.Create();
+
+ case AssemblyHashAlgorithm.MD5:
+ return MD5.Create();
+
+ default:
+ return null;
+ }
+ }
+
+ internal static bool IsSupportedAlgorithm(AssemblyHashAlgorithm algorithmId)
+ {
+ switch (algorithmId)
+ {
+ case AssemblyHashAlgorithm.None:
+ case AssemblyHashAlgorithm.Sha1:
+ case AssemblyHashAlgorithm.Sha256:
+ case AssemblyHashAlgorithm.Sha384:
+ case AssemblyHashAlgorithm.Sha512:
+ case AssemblyHashAlgorithm.MD5:
+ return true;
+
+ default:
+ return false;
+ }
+ }
+
+ private ImmutableArray<byte> GetHash(ref ImmutableArray<byte> lazyHash, HashAlgorithm algorithm)
+ {
+ if (lazyHash.IsDefault)
+ {
+ ImmutableInterlocked.InterlockedCompareExchange(ref lazyHash, ComputeHash(algorithm), default(ImmutableArray<byte>));
+ }
+
+ return lazyHash;
+ }
+
+ internal const int Sha1HashSize = 20;
+
+ internal static ImmutableArray<byte> ComputeSha1(Stream stream)
+ {
+ if (stream != null)
+ {
+ stream.Seek(0, SeekOrigin.Begin);
+ using (var hashProvider = SHA1.Create())
+ {
+ return ImmutableArray.Create(hashProvider.ComputeHash(stream));
+ }
+ }
+
+ return ImmutableArray<byte>.Empty;
+ }
+
+ internal static ImmutableArray<byte> ComputeSha1(ImmutableArray<byte> bytes)
+ {
+ return ComputeSha1(bytes.ToArray());
+ }
+
+ internal static ImmutableArray<byte> ComputeSha1(byte[] bytes)
+ {
+ using (var hashProvider = SHA1.Create())
+ {
+ return ImmutableArray.Create(hashProvider.ComputeHash(bytes));
+ }
+ }
+
+ internal static ImmutableArray<byte> ComputeHash(HashAlgorithmName algorithmName, IEnumerable<Blob> bytes)
+ {
+ using (var incrementalHash = IncrementalHash.CreateHash(algorithmName))
+ {
+ foreach (var blob in bytes)
+ {
+ incrementalHash.AppendData(blob.GetBytes());
+ }
+ return ImmutableArray.Create(incrementalHash.GetHashAndReset());
+ }
+ }
+
+ internal static ImmutableArray<byte> ComputeHash(HashAlgorithmName algorithmName, IEnumerable<ArraySegment<byte>> bytes)
+ {
+ using (var incrementalHash = IncrementalHash.CreateHash(algorithmName))
+ {
+ foreach (var segment in bytes)
+ {
+ incrementalHash.AppendData(segment);
+ }
+ return ImmutableArray.Create(incrementalHash.GetHashAndReset());
+ }
+ }
+
+ internal static ImmutableArray<byte> ComputeSourceHash(ImmutableArray<byte> bytes, SourceHashAlgorithm hashAlgorithm = SourceHashAlgorithmUtils.DefaultContentHashAlgorithm)
+ {
+ var algorithmName = GetAlgorithmName(hashAlgorithm);
+ using (var incrementalHash = IncrementalHash.CreateHash(algorithmName))
+ {
+ incrementalHash.AppendData(bytes.ToArray());
+ return ImmutableArray.Create(incrementalHash.GetHashAndReset());
+ }
+ }
+
+ internal static ImmutableArray<byte> ComputeSourceHash(IEnumerable<Blob> bytes, SourceHashAlgorithm hashAlgorithm = SourceHashAlgorithmUtils.DefaultContentHashAlgorithm)
+ {
+ return ComputeHash(GetAlgorithmName(hashAlgorithm), bytes);
+ }
+ }
+}
private EcmaModule _module;
private NativeDebugDirectoryEntryNode _nativeEntry;
+ private bool _insertDeterministicEntry;
public DebugDirectoryNode(EcmaModule sourceModule, string outputFileName)
{
_module = sourceModule;
+ _insertDeterministicEntry = sourceModule == null; // Mark module as deterministic if generating composite image
string pdbNameRoot = Path.GetFileNameWithoutExtension(outputFileName);
if (sourceModule != null)
{
public int Offset => 0;
- public int Size => (GetNumDebugDirectoryEntriesInModule() + 1) * ImageDebugDirectorySize;
+ public int Size => (GetNumDebugDirectoryEntriesInModule() + 1 + (_insertDeterministicEntry ? 1 : 0)) * ImageDebugDirectorySize;
public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb)
{
builder.EmitReloc(entry, RelocType.IMAGE_REL_FILE_ABSOLUTE);
}
+ // If generating a composite image, emit the deterministic marker
+ if (_insertDeterministicEntry)
+ {
+ builder.EmitUInt(0 /* Characteristics */);
+ builder.EmitUInt(0);
+ builder.EmitUShort(0);
+ builder.EmitUShort(0);
+ builder.EmitInt((int)DebugDirectoryEntryType.Reproducible);
+ builder.EmitInt(0);
+ builder.EmitUInt(0);
+ builder.EmitUInt(0);
+ }
+
// Second, copy existing entries from input module
- for(int i = 0; i < numEntries; i++)
+ for (int i = 0; i < numEntries; i++)
{
builder.EmitUInt(0 /* Characteristics */);
builder.EmitUInt(entries[i].Stamp);
ISymbolNode r2rHeaderExportSymbol,
string outputFileSimpleName,
Func<RuntimeFunctionsTableNode> getRuntimeFunctionsTable,
- int? customPESectionAlignment)
- : base(peHeaderBuilder, deterministicIdProvider: null)
+ int? customPESectionAlignment,
+ Func<IEnumerable<Blob>, BlobContentId> deterministicIdProvider)
+ : base(peHeaderBuilder, deterministicIdProvider: deterministicIdProvider)
{
_target = target;
_getRuntimeFunctionsTable = getRuntimeFunctionsTable;
/// </summary>
/// <param name="outputStream">Output stream for the final R2R PE file</param>
/// <param name="timeDateStamp">Timestamp to set in the PE header of the output R2R executable</param>
- public void Write(Stream outputStream, int timeDateStamp)
+ public void Write(Stream outputStream, int? timeDateStamp)
{
BlobBuilder outputPeFile = new BlobBuilder();
Serialize(outputPeFile);
ApplyMachineOSOverride(outputStream);
- SetPEHeaderTimeStamp(outputStream, timeDateStamp);
+ if (timeDateStamp.HasValue)
+ SetPEHeaderTimeStamp(outputStream, timeDateStamp.Value);
_written = true;
}