diff --git a/Mono.Cecil.PE/ImageWriter.cs b/Mono.Cecil.PE/ImageWriter.cs index bf7ed9136..b33efe303 100644 --- a/Mono.Cecil.PE/ImageWriter.cs +++ b/Mono.Cecil.PE/ImageWriter.cs @@ -814,14 +814,6 @@ public uint GetHeaderSize () return pe_header_size + SizeOfOptionalHeader () + (sections * section_header_size); } - public void PatchMvid (Guid guid) - { - uint offset = GetRVAFileOffset (text, text_map.GetRVA (TextSegment.GuidHeap)); - BaseStream.Seek (offset, SeekOrigin.Begin); - var arr = guid.ToByteArray (); - BaseStream.Write (arr, 0, arr.Length); - } - void PatchWin32Resources (ByteBuffer resources) { PatchResourceDirectoryTable (resources); diff --git a/Mono.Cecil.csproj b/Mono.Cecil.csproj index 3129b8fee..d3745519a 100644 --- a/Mono.Cecil.csproj +++ b/Mono.Cecil.csproj @@ -1,7 +1,6 @@  netstandard2.0;net40 - true diff --git a/Mono.Cecil/AssemblyWriter.cs b/Mono.Cecil/AssemblyWriter.cs index 691b5fa39..3ce36bbc2 100644 --- a/Mono.Cecil/AssemblyWriter.cs +++ b/Mono.Cecil/AssemblyWriter.cs @@ -109,6 +109,7 @@ static void Write (ModuleDefinition module, Disposable stream, WriterPar if (parameters.DeterministicMvid) module.Mvid = Guid.Empty; + var metadata = new MetadataBuilder (module, fq_name, timestamp, symbol_writer_provider); try { module.metadata_builder = metadata; @@ -117,62 +118,21 @@ static void Write (ModuleDefinition module, Disposable stream, WriterPar metadata.SetSymbolWriter (symbol_writer); BuildMetadata (module, metadata); + if (parameters.DeterministicMvid) + metadata.ComputeDeterministicMvid (); + var writer = ImageWriter.CreateWriter (module, metadata, stream); stream.value.SetLength (0); writer.WriteImage (); if (parameters.StrongNameKeyPair != null) CryptoService.StrongName (stream.value, writer, parameters.StrongNameKeyPair); - if (parameters.DeterministicMvid) { - module.Mvid = ComputeGuid (stream.value); - writer.PatchMvid (module.Mvid); - } } } finally { module.metadata_builder = null; } } - static void CopyStreamChunk (Stream stream, Stream dest_stream, byte [] buffer, int length) - { - while (length > 0) { - int read = stream.Read (buffer, 0, System.Math.Min (buffer.Length, length)); - dest_stream.Write (buffer, 0, read); - length -= read; - } - } - - static byte [] ComputeHash (Stream stream) - { - const int buffer_size = 8192; - - var sha1 = new SHA1Managed (); - - stream.Seek (0, SeekOrigin.Begin); - var buffer = new byte [buffer_size]; - - using (var crypto_stream = new CryptoStream (Stream.Null, sha1, CryptoStreamMode.Write)) - CopyStreamChunk (stream, crypto_stream, buffer, (int) stream.Length); - return sha1.Hash; - } - - static unsafe Guid ComputeGuid (Stream stream) - { - byte[] hashCode = ComputeHash (stream); - - // From corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/BlobContentId.cs - Guid guid = default(Guid); - byte* guidPtr = (byte*)&guid; - for (var i = 0; i < 16; i++) { - guidPtr[i] = hashCode[i]; - } - // modify the guid data so it decodes to the form of a "random" guid ala rfc4122 - guidPtr[7] = (byte)((guidPtr[7] & 0x0f) | (4 << 4)); - guidPtr[8] = (byte)((guidPtr[8] & 0x3f) | (2 << 6)); - - return guid; - } - static void BuildMetadata (ModuleDefinition module, MetadataBuilder metadata) { if (!module.HasImage) { @@ -2684,6 +2644,25 @@ void AddSequencePoints (MethodDebugInformation info) method_debug_information_table.rows [rid - 1].Col2 = GetBlobIndex (signature); } + + public void ComputeDeterministicMvid () + { + var guid = CryptoService.ComputeGuid (CryptoService.ComputeHash ( + data, + resources, + string_heap, + user_string_heap, + blob_heap, + table_heap, + code)); + + var position = guid_heap.position; + guid_heap.position = 0; + guid_heap.WriteBytes (guid.ToByteArray ()); + guid_heap.position = position; + + module.Mvid = guid; + } } sealed class SignatureWriter : ByteBuffer { diff --git a/Mono.Security.Cryptography/CryptoService.cs b/Mono.Security.Cryptography/CryptoService.cs index 284d9bb80..7a4198b5c 100644 --- a/Mono.Security.Cryptography/CryptoService.cs +++ b/Mono.Security.Cryptography/CryptoService.cs @@ -102,20 +102,48 @@ public static byte [] ComputeHash (string file) if (!File.Exists (file)) return Empty.Array; + using (var stream = new FileStream (file, FileMode.Open, FileAccess.Read, FileShare.Read)) + return ComputeHash (stream); + } + + public static byte [] ComputeHash (Stream stream) + { const int buffer_size = 8192; var sha1 = new SHA1Managed (); + var buffer = new byte [buffer_size]; - using (var stream = new FileStream (file, FileMode.Open, FileAccess.Read, FileShare.Read)) { + using (var crypto_stream = new CryptoStream (Stream.Null, sha1, CryptoStreamMode.Write)) + CopyStreamChunk (stream, crypto_stream, buffer, (int) stream.Length); - var buffer = new byte [buffer_size]; + return sha1.Hash; + } - using (var crypto_stream = new CryptoStream (Stream.Null, sha1, CryptoStreamMode.Write)) - CopyStreamChunk (stream, crypto_stream, buffer, (int) stream.Length); + public static byte [] ComputeHash (params ByteBuffer [] buffers) + { + var sha1 = new SHA1Managed (); + + using (var crypto_stream = new CryptoStream (Stream.Null, sha1, CryptoStreamMode.Write)) { + for (int i = 0; i < buffers.Length; i++) { + crypto_stream.Write (buffers [0].buffer, 0, buffers [0].length); + } } return sha1.Hash; } + + public static Guid ComputeGuid (byte [] hash) + { + // From corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/BlobContentId.cs + var guid = new byte [16]; + Buffer.BlockCopy (hash, 0, guid, 0, 16); + + // modify the guid data so it decodes to the form of a "random" guid ala rfc4122 + guid [7] = (byte) ((guid [7] & 0x0f) | (4 << 4)); + guid [8] = (byte) ((guid [8] & 0x3f) | (2 << 6)); + + return new Guid (guid); + } } static partial class Mixin { diff --git a/Test/Mono.Cecil.Tests/PortablePdbTests.cs b/Test/Mono.Cecil.Tests/PortablePdbTests.cs index 826387110..6e1efbc90 100644 --- a/Test/Mono.Cecil.Tests/PortablePdbTests.cs +++ b/Test/Mono.Cecil.Tests/PortablePdbTests.cs @@ -669,5 +669,19 @@ public void UseCustomSymbolWriterToChangeDebugHeaderPdbPath () Assert.AreEqual (Path.GetFileName (debug_header_pdb_path), pdb_path); } } + + [Test] + public void WriteAndReadAgainModuleWithDeterministicMvid () + { + const string resource = "mylib.dll"; + string destination = Path.GetTempFileName (); + + using (var module = GetResourceModule (resource, new ReaderParameters { SymbolReaderProvider = new PortablePdbReaderProvider () })) { + module.Write (destination, new WriterParameters { DeterministicMvid = true, SymbolWriterProvider = new SymbolWriterProvider () }); + } + + using (var module = ModuleDefinition.ReadModule (destination, new ReaderParameters { SymbolReaderProvider = new PortablePdbReaderProvider () })) { + } + } } }