diff options
author | jfrijters <jfrijters> | 2015-02-17 16:48:31 +0300 |
---|---|---|
committer | Marek Safar <marek.safar@gmail.com> | 2015-02-18 14:49:17 +0300 |
commit | cd4bed9dd6540c380177c5b9c72f4d020f1b138f (patch) | |
tree | f7cf4cc627a964282591ab470fe8c75233a3725b /reflect | |
parent | 9830ce248ed867f7037c72152265966e31d43235 (diff) |
Added UniverseOptions.DeterministicOutput to enable deterministic output (i.e. setting the PE file header time stamp to zero and computing the module version id based on the contents, instead of using a random guid).
Diffstat (limited to 'reflect')
-rw-r--r-- | reflect/Emit/ModuleBuilder.cs | 45 | ||||
-rw-r--r-- | reflect/Impl/MdbWriter.cs | 5 | ||||
-rw-r--r-- | reflect/Impl/PdbWriter.cs | 5 | ||||
-rw-r--r-- | reflect/Impl/SymbolSupport.cs | 2 | ||||
-rw-r--r-- | reflect/Universe.cs | 13 | ||||
-rw-r--r-- | reflect/Writer/ModuleWriter.cs | 36 | ||||
-rw-r--r-- | reflect/Writer/TextSection.cs | 4 |
7 files changed, 99 insertions, 11 deletions
diff --git a/reflect/Emit/ModuleBuilder.cs b/reflect/Emit/ModuleBuilder.cs index 646c3553..b415262d 100644 --- a/reflect/Emit/ModuleBuilder.cs +++ b/reflect/Emit/ModuleBuilder.cs @@ -41,8 +41,8 @@ namespace IKVM.Reflection.Emit public sealed class ModuleBuilder : Module, ITypeOwner { private static readonly bool usePublicKeyAssemblyReference = false; - private Guid mvid = Guid.NewGuid(); - private DateTime timestamp = DateTime.UtcNow; + private Guid mvid; + private uint timestamp; private long imageBaseAddress = 0x00400000; private long stackReserve = -1; private int fileAlignment = 0x200; @@ -251,6 +251,15 @@ namespace IKVM.Reflection.Emit if (emitSymbolInfo) { symbolWriter = SymbolSupport.CreateSymbolWriterFor(this); + if (universe.Deterministic && !symbolWriter.IsDeterministic) + { + throw new NotSupportedException(); + } + } + if (!universe.Deterministic) + { + __PEHeaderTimeDateStamp = DateTime.UtcNow; + mvid = Guid.NewGuid(); } // <Module> must be the first record in the TypeDef table moduleType = new TypeBuilder(this, null, "<Module>"); @@ -1122,7 +1131,7 @@ namespace IKVM.Reflection.Emit } } - internal void WriteMetadata(MetadataWriter mw) + internal void WriteMetadata(MetadataWriter mw, out int guidHeapOffset) { mw.Write(0x424A5342); // Signature ("BSJB") mw.Write((ushort)1); // MajorVersion @@ -1175,6 +1184,7 @@ namespace IKVM.Reflection.Emit Tables.Write(mw); Strings.Write(mw); UserStrings.Write(mw); + guidHeapOffset = mw.Position; Guids.Write(mw); if (!Blobs.IsEmpty) { @@ -1396,26 +1406,49 @@ namespace IKVM.Reflection.Emit get { return fileName; } } + internal Guid GetModuleVersionIdOrEmpty() + { + return mvid; + } + public override Guid ModuleVersionId { - get { return mvid; } + get + { + if (mvid == Guid.Empty && universe.Deterministic) + { + // if a deterministic GUID is used, it can't be queried before the assembly has been written + throw new InvalidOperationException(); + } + return mvid; + } } public void __SetModuleVersionId(Guid guid) { + if (guid == Guid.Empty && universe.Deterministic) + { + // if you want to use Guid.Empty, don't set UniverseOptions.DeterministicOutput + throw new ArgumentOutOfRangeException(); + } mvid = guid; } + internal uint GetTimeDateStamp() + { + return timestamp; + } + public DateTime __PEHeaderTimeDateStamp { - get { return timestamp; } + get { return new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds(timestamp); } set { if (value < new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc) || value > new DateTime(2106, 2, 7, 6, 28, 15, DateTimeKind.Utc)) { throw new ArgumentOutOfRangeException(); } - timestamp = value; + timestamp = (uint)(value - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds; } } diff --git a/reflect/Impl/MdbWriter.cs b/reflect/Impl/MdbWriter.cs index 226137b9..4d1e2cd7 100644 --- a/reflect/Impl/MdbWriter.cs +++ b/reflect/Impl/MdbWriter.cs @@ -228,6 +228,11 @@ namespace IKVM.Reflection.Impl { throw new InvalidOperationException(); } + + public bool IsDeterministic + { + get { return true; } + } } } #endif // MONO diff --git a/reflect/Impl/PdbWriter.cs b/reflect/Impl/PdbWriter.cs index b8976bbf..4d8fc1d8 100644 --- a/reflect/Impl/PdbWriter.cs +++ b/reflect/Impl/PdbWriter.cs @@ -1184,5 +1184,10 @@ namespace IKVM.Reflection.Impl { throw new NotImplementedException(); } + + public bool IsDeterministic + { + get { return false; } + } } } diff --git a/reflect/Impl/SymbolSupport.cs b/reflect/Impl/SymbolSupport.cs index c573b2b5..7b4e8906 100644 --- a/reflect/Impl/SymbolSupport.cs +++ b/reflect/Impl/SymbolSupport.cs @@ -55,6 +55,7 @@ namespace IKVM.Reflection.Impl void RemapToken(int oldToken, int newToken); void DefineLocalVariable2(string name, FieldAttributes attributes, int signature, int addrKind, int addr1, int addr2, int addr3, int startOffset, int endOffset); void OpenMethod(SymbolToken symbolToken, MethodBase mb); + bool IsDeterministic { get; } } #else interface ISymbolWriterImpl : ISymbolWriter @@ -63,6 +64,7 @@ namespace IKVM.Reflection.Impl void RemapToken(int oldToken, int newToken); void DefineLocalVariable2(string name, FieldAttributes attributes, int signature, SymAddressKind addrKind, int addr1, int addr2, int addr3, int startOffset, int endOffset); void OpenMethod(SymbolToken symbolToken, MethodBase mb); + bool IsDeterministic { get; } } #endif diff --git a/reflect/Universe.cs b/reflect/Universe.cs index 30ed1204..82fad202 100644 --- a/reflect/Universe.cs +++ b/reflect/Universe.cs @@ -115,6 +115,12 @@ namespace IKVM.Reflection * - Module.__ReadDataFromRVA() * - MethodBase.GetMethodBody() * - FieldInfo.__GetDataFromRVA() + * + * DeterministicOutput + * The generated output file will depend only on the input. In other words, + * the PE file header time stamp will be set to zero and the module version + * id will be based on a SHA1 of the contents, instead of a random guid. + * This option can not be used in combination with PDB file generation. */ [Flags] @@ -129,6 +135,8 @@ namespace IKVM.Reflection ResolveMissingMembers = 32, DisableWindowsRuntimeProjection = 64, DecodeVersionInfoAttributeBlobs = 128, + DeterministicOutput = 256, + SupressReferenceTypeIdentityConversion = 1 << 20 } @@ -1250,5 +1258,10 @@ namespace IKVM.Reflection { get { return (options & UniverseOptions.SupressReferenceTypeIdentityConversion) != 0; } } + + internal bool Deterministic + { + get { return (options & UniverseOptions.DeterministicOutput) != 0; } + } } } diff --git a/reflect/Writer/ModuleWriter.cs b/reflect/Writer/ModuleWriter.cs index ba3bf85a..7ef9c6d6 100644 --- a/reflect/Writer/ModuleWriter.cs +++ b/reflect/Writer/ModuleWriter.cs @@ -81,7 +81,8 @@ namespace IKVM.Reflection.Writer moduleBuilder.ApplyUnmanagedExports(imageFileMachine); moduleBuilder.FixupMethodBodyTokens(); - moduleBuilder.ModuleTable.Add(0, moduleBuilder.Strings.Add(moduleBuilder.moduleName), moduleBuilder.Guids.Add(moduleBuilder.ModuleVersionId), 0, 0); + int moduleVersionIdIndex = moduleBuilder.Guids.Add(moduleBuilder.GetModuleVersionIdOrEmpty()); + moduleBuilder.ModuleTable.Add(0, moduleBuilder.Strings.Add(moduleBuilder.moduleName), moduleVersionIdIndex, 0, 0); if (moduleBuilder.UserStrings.IsEmpty) { @@ -213,7 +214,7 @@ namespace IKVM.Reflection.Writer } // Set the PE File timestamp - writer.Headers.FileHeader.TimeDateStamp = (uint)(moduleBuilder.__PEHeaderTimeDateStamp - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds; + writer.Headers.FileHeader.TimeDateStamp = moduleBuilder.GetTimeDateStamp(); // we need to start by computing the number of sections, because code.PointerToRawData depends on that writer.Headers.FileHeader.NumberOfSections = 2; @@ -312,7 +313,8 @@ namespace IKVM.Reflection.Writer } stream.Seek(text.PointerToRawData, SeekOrigin.Begin); - code.Write(mw, sdata.VirtualAddress); + int guidHeapOffset; + code.Write(mw, sdata.VirtualAddress, out guidHeapOffset); if (sdata.SizeOfRawData != 0) { @@ -335,6 +337,15 @@ namespace IKVM.Reflection.Writer // file alignment stream.SetLength(reloc.PointerToRawData + reloc.SizeOfRawData); + // if we don't have a guid, generate one based on the contents of the assembly + if (moduleBuilder.universe.Deterministic && moduleBuilder.GetModuleVersionIdOrEmpty() == Guid.Empty) + { + Guid guid = GenerateModuleVersionId(stream); + stream.Position = guidHeapOffset + (moduleVersionIdIndex - 1) * 16; + stream.Write(guid.ToByteArray(), 0, 16); + moduleBuilder.__SetModuleVersionId(guid); + } + // do the strong naming if (keyPair != null) { @@ -429,5 +440,24 @@ namespace IKVM.Reflection.Writer length -= read; } } + + private static Guid GenerateModuleVersionId(Stream stream) + { + SHA1Managed hash = new SHA1Managed(); + using (CryptoStream cs = new CryptoStream(Stream.Null, hash, CryptoStreamMode.Write)) + { + stream.Seek(0, SeekOrigin.Begin); + byte[] buf = new byte[8192]; + HashChunk(stream, cs, buf, (int)stream.Length); + } + byte[] bytes = new byte[16]; + Buffer.BlockCopy(hash.Hash, 0, bytes, 0, bytes.Length); + // set GUID type to "version 4" (random) + bytes[7] &= 0x0F; + bytes[7] |= 0x40; + bytes[8] &= 0x3F; + bytes[8] |= 0x80; + return new Guid(bytes); + } } } diff --git a/reflect/Writer/TextSection.cs b/reflect/Writer/TextSection.cs index 4657d30e..fceedcf6 100644 --- a/reflect/Writer/TextSection.cs +++ b/reflect/Writer/TextSection.cs @@ -275,7 +275,7 @@ namespace IKVM.Reflection.Writer } } - internal void Write(MetadataWriter mw, uint sdataRVA) + internal void Write(MetadataWriter mw, uint sdataRVA, out int guidHeapOffset) { // Now that we're ready to start writing, we need to do some fix ups moduleBuilder.TypeRef.Fixup(moduleBuilder); @@ -356,7 +356,7 @@ namespace IKVM.Reflection.Writer // Metadata AssertRVA(mw, MetadataRVA); - moduleBuilder.WriteMetadata(mw); + moduleBuilder.WriteMetadata(mw, out guidHeapOffset); // alignment padding for (int i = (int)(VTableFixupsRVA - (MetadataRVA + MetadataLength)); i > 0; i--) |