提交 1a6224cb 编写于 作者: T TomasMatousek

PEWriter metadata sizes fif.

The PEWriter used to calculate sizes of metadata tables, rows and columns 3 times during emit. Under certain circumstances the result of the first one was different from the other two which might have lead to writing invalid values to PE headers.
This change factors various sizes related to metadata tables to a separate class that is immutable, calculated once and then passed around.
 (changeset 1281677)
上级 50974f92
......@@ -96,14 +96,15 @@ internal static class EmitHelpers
definitionMap,
changes,
cancellationToken);
writer.WriteMetadataAndIL(metadataStream, ilStream);
Cci.MetadataSizes metadataSizes;
writer.WriteMetadataAndIL(metadataStream, ilStream, out metadataSizes);
writer.GetMethodTokens(updatedMethodTokens);
return new EmitDifferenceResult(
success: true,
diagnostics: diagnostics.ToReadOnlyAndFree(),
baseline: writer.GetDelta(baseline, compilation, encId));
baseline: writer.GetDelta(baseline, compilation, encId, metadataSizes));
}
catch (Cci.PdbWritingException e)
{
......
......@@ -378,6 +378,7 @@
<Compile Include="PEWriter\MemberRefComparer.cs" />
<Compile Include="PEWriter\Members.cs" />
<Compile Include="PEWriter\MemoryStream.cs" />
<Compile Include="PEWriter\MetadataSizes.cs" />
<Compile Include="PEWriter\MetadataVisitor.cs" />
<Compile Include="PEWriter\MethodSpecComparer.cs" />
<Compile Include="PEWriter\Miscellaneous.cs" />
......
......@@ -105,10 +105,11 @@ internal MethodLocals(ImmutableArray<ILocalDefinition> definitions, ImmutableArr
this.localMap = new Dictionary<IMethodDefinition, MethodLocals>();
}
private ImmutableArray<int> GetDeltaTableSizes()
private ImmutableArray<int> GetDeltaTableSizes(ImmutableArray<int> rowCounts)
{
var sizes = new int[MetadataTokens.TableCount];
this.GetTableSizes(sizes);
rowCounts.CopyTo(sizes);
sizes[(int)TableIndex.TypeRef] = this.typeRefIndex.Rows.Count;
sizes[(int)TableIndex.TypeDef] = this.typeDefs.GetAdded().Count;
......@@ -131,10 +132,7 @@ private ImmutableArray<int> GetDeltaTableSizes()
return ImmutableArray.Create(sizes);
}
internal EmitBaseline GetDelta(
EmitBaseline baseline,
Microsoft.CodeAnalysis.Compilation compilation,
Guid encId)
internal EmitBaseline GetDelta(EmitBaseline baseline, Compilation compilation, Guid encId, MetadataSizes metadataSizes)
{
Debug.Assert(this.unalignedStringStreamLength > 0); // OnSerializedMetadataTables should have been called.
......@@ -150,7 +148,7 @@ private ImmutableArray<int> GetDeltaTableSizes()
}
var previousTableSizes = this.previousGeneration.TableEntriesAdded;
var deltaTableSizes = this.GetDeltaTableSizes();
var deltaTableSizes = this.GetDeltaTableSizes(metadataSizes.RowCounts);
var tableSizes = new int[MetadataTokens.TableCount];
for (int i = 0; i < tableSizes.Length; i++)
......@@ -656,13 +654,13 @@ protected override void OnBeforeHeapsAligned()
this.unalignedStringStreamLength = this.stringWriter.BaseStream.Length;
}
protected override void PopulateEncLogTableRows(List<EncLogRow> table)
protected override void PopulateEncLogTableRows(List<EncLogRow> table, ImmutableArray<int> rowCounts)
{
// The EncLog table is a log of all the operations needed
// to update the previous metadata. That means all
// new references must be added to the EncLog.
var previousSizes = this.previousGeneration.TableSizes;
var deltaSizes = this.GetDeltaTableSizes();
var deltaSizes = this.GetDeltaTableSizes(rowCounts);
PopulateEncLogTableRows(table, TableIndex.AssemblyRef, previousSizes, deltaSizes);
PopulateEncLogTableRows(table, TableIndex.ModuleRef, previousSizes, deltaSizes);
......@@ -787,7 +785,7 @@ private static void PopulateEncLogTableRows(List<EncLogRow> table, uint tokenTyp
}
}
protected override void PopulateEncMapTableRows(List<EncMapRow> table)
protected override void PopulateEncMapTableRows(List<EncMapRow> table, ImmutableArray<int> rowCounts)
{
// The EncMap table maps from offset in each table in the delta
// metadata to token. As such, the EncMap is a concatenated
......@@ -795,7 +793,7 @@ protected override void PopulateEncMapTableRows(List<EncMapRow> table)
// and, within each table, sorted by row.
var tokens = ArrayBuilder<uint>.GetInstance();
var previousSizes = this.previousGeneration.TableSizes;
var deltaSizes = this.GetDeltaTableSizes();
var deltaSizes = this.GetDeltaTableSizes(rowCounts);
AddReferencedTokens(tokens, TableIndex.AssemblyRef, previousSizes, deltaSizes);
AddReferencedTokens(tokens, TableIndex.ModuleRef, previousSizes, deltaSizes);
......@@ -883,15 +881,14 @@ protected override void PopulateEncMapTableRows(List<EncMapRow> table)
TableIndex.GenericParamConstraint,
};
var tableSizes = new int[MetadataTokens.TableCount];
this.GetTableSizes(tableSizes);
for (uint i = 0; i < tableSizes.Length; i++)
for (int i = 0; i < rowCounts.Length; i++)
{
if (handledTables.Contains((TableIndex)i))
{
continue;
}
Debug.Assert(tableSizes[i] == 0);
Debug.Assert(rowCounts[i] == 0);
}
#endif
}
......
// Copyright (c) Microsoft Open Technologies, Inc. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Diagnostics;
namespace Roslyn.Utilities
{
internal static class BitArithmeticUtilities
......@@ -43,5 +45,32 @@ public static int CountBits(ulong v)
return (int)v;
}
}
internal static uint Align(uint position, uint alignment)
{
Debug.Assert(CountBits(alignment) == 1);
uint result = position & ~(alignment - 1);
if (result == position)
{
return result;
}
return result + alignment;
}
internal static int Align(int position, int alignment)
{
Debug.Assert(position >= 0 && alignment >= 0);
Debug.Assert(CountBits(alignment) == 1);
int result = position & ~(alignment - 1);
if (result == position)
{
return result;
}
return result + alignment;
}
}
}
\ No newline at end of file
// Copyright (c) Microsoft Open Technologies, Inc. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Cci = Microsoft.Cci;
using System.Reflection.PortableExecutable;
namespace Microsoft.Cci
{
internal class ClrHeader
internal sealed class CorHeader
{
internal ushort MajorRuntimeVersion;
internal ushort MinorRuntimeVersion;
internal DirectoryEntry MetaData;
internal uint Flags;
internal DirectoryEntry MetadataDirectory;
internal CorFlags Flags;
internal uint EntryPointToken;
internal DirectoryEntry Resources;
internal DirectoryEntry StrongNameSignature;
......
......@@ -293,7 +293,7 @@ private void SerializeNamespaceScopeMetadata(IMethodBody methodBody, ArrayBuilde
cmw.WriteByte(0); // kind: UsingInfo
cmw.Align(4);
cmw.WriteUint(streamLength = PeWriter.Aligned((uint)usingCounts.Count * 2 + 10, 4));
cmw.WriteUint(streamLength = BitArithmeticUtilities.Align((uint)usingCounts.Count * 2 + 10, 4));
cmw.WriteUshort((ushort)usingCounts.Count);
foreach (ushort uc in usingCounts)
{
......
......@@ -2,6 +2,7 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Threading;
using Microsoft.CodeAnalysis;
using EmitContext = Microsoft.CodeAnalysis.Emit.EmitContext;
......@@ -295,11 +296,11 @@ internal FullReferenceIndexer(PeWriter peWriter)
}
}
protected override void PopulateEncLogTableRows(List<EncLogRow> table)
protected override void PopulateEncLogTableRows(List<EncLogRow> table, ImmutableArray<int> rowCounts)
{
}
protected override void PopulateEncMapTableRows(List<EncMapRow> table)
protected override void PopulateEncMapTableRows(List<EncMapRow> table, ImmutableArray<int> rowCounts)
{
}
......
// Copyright (c) Microsoft Open Technologies, Inc. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Immutable;
using System.Diagnostics;
using System.Reflection.Metadata.Ecma335;
using Roslyn.Utilities;
namespace Microsoft.Cci
{
using BitArithmeticUtilities;
internal sealed class MetadataSizes
{
private const int StreamAlignment = 4;
private readonly bool isMinimalDelta;
public readonly byte BlobIndexSize;
public readonly byte StringIndexSize;
public readonly byte GuidIndexSize;
public readonly byte CustomAttributeTypeCodedIndexSize;
public readonly byte DeclSecurityCodedIndexSize;
public readonly byte EventDefIndexSize;
public readonly byte FieldDefIndexSize;
public readonly byte GenericParamIndexSize;
public readonly byte HasConstantCodedIndexSize;
public readonly byte HasCustomAttributeCodedIndexSize;
public readonly byte HasFieldMarshalCodedIndexSize;
public readonly byte HasSemanticsCodedIndexSize;
public readonly byte ImplementationCodedIndexSize;
public readonly byte MemberForwardedCodedIndexSize;
public readonly byte MemberRefParentCodedIndexSize;
public readonly byte MethodDefIndexSize;
public readonly byte MethodDefOrRefCodedIndexSize;
public readonly byte ModuleRefIndexSize;
public readonly byte ParameterIndexSize;
public readonly byte PropertyDefIndexSize;
public readonly byte ResolutionScopeCodedIndexSize;
public readonly byte TypeDefIndexSize;
public readonly byte TypeDefOrRefCodedIndexSize;
public readonly byte TypeOrMethodDefCodedIndexSize;
/// <summary>
/// Table row counts.
/// </summary>
public readonly ImmutableArray<int> RowCounts;
/// <summary>
/// Exact (unaligned) heap sizes.
/// </summary>
public readonly ImmutableArray<int> HeapSizes;
/// <summary>
/// Overall size of metadata stream storage (stream headers, streams: heaps + tables).
/// </summary>
public readonly int MetadataStreamStorageSize;
/// <summary>
/// The size of metadata stream (#- or #~). Aligned.
/// </summary>
public readonly int MetadataTableStreamSize;
public MetadataSizes(ImmutableArray<int> rowCounts, ImmutableArray<int> heapSizes, bool isMinimalDelta)
{
const byte large = 4;
const byte small = 2;
this.RowCounts = rowCounts;
this.HeapSizes = heapSizes;
this.isMinimalDelta = isMinimalDelta;
this.BlobIndexSize = (isMinimalDelta || heapSizes[(int)HeapIndex.Blob] > ushort.MaxValue) ? large : small;
this.StringIndexSize = (isMinimalDelta || heapSizes[(int)HeapIndex.String] > ushort.MaxValue) ? large : small;
this.GuidIndexSize = (isMinimalDelta || heapSizes[(int)HeapIndex.Guid] > ushort.MaxValue) ? large : small;
this.CustomAttributeTypeCodedIndexSize = this.GetIndexByteSize(3, TableIndex.MethodDef, TableIndex.MemberRef);
this.DeclSecurityCodedIndexSize = this.GetIndexByteSize(2, TableIndex.MethodDef, TableIndex.TypeDef);
this.EventDefIndexSize = this.GetIndexByteSize(0, TableIndex.Event);
this.FieldDefIndexSize = this.GetIndexByteSize(0, TableIndex.Field);
this.GenericParamIndexSize = this.GetIndexByteSize(0, TableIndex.GenericParam);
this.HasConstantCodedIndexSize = this.GetIndexByteSize(2, TableIndex.Field, TableIndex.Param, TableIndex.Property);
this.HasCustomAttributeCodedIndexSize = this.GetIndexByteSize(5,
TableIndex.MethodDef,
TableIndex.Field,
TableIndex.TypeRef,
TableIndex.TypeDef,
TableIndex.Param,
TableIndex.InterfaceImpl,
TableIndex.MemberRef,
TableIndex.Module,
TableIndex.DeclSecurity,
TableIndex.Property,
TableIndex.Event,
TableIndex.StandAloneSig,
TableIndex.ModuleRef,
TableIndex.TypeSpec,
TableIndex.Assembly,
TableIndex.AssemblyRef,
TableIndex.File,
TableIndex.ExportedType,
TableIndex.ManifestResource,
TableIndex.GenericParam,
TableIndex.GenericParamConstraint,
TableIndex.MethodSpec);
this.HasFieldMarshalCodedIndexSize = this.GetIndexByteSize(1, TableIndex.Field, TableIndex.Param);
this.HasSemanticsCodedIndexSize = this.GetIndexByteSize(1, TableIndex.Event, TableIndex.Property);
this.ImplementationCodedIndexSize = this.GetIndexByteSize(2, TableIndex.File, TableIndex.AssemblyRef, TableIndex.ExportedType);
this.MemberForwardedCodedIndexSize = this.GetIndexByteSize(1, TableIndex.Field, TableIndex.MethodDef);
this.MemberRefParentCodedIndexSize = this.GetIndexByteSize(3, TableIndex.TypeDef, TableIndex.TypeRef, TableIndex.ModuleRef, TableIndex.MethodDef, TableIndex.TypeSpec);
this.MethodDefIndexSize = this.GetIndexByteSize(0, TableIndex.MethodDef);
this.MethodDefOrRefCodedIndexSize = this.GetIndexByteSize(1, TableIndex.MethodDef, TableIndex.MemberRef);
this.ModuleRefIndexSize = this.GetIndexByteSize(0, TableIndex.ModuleRef);
this.ParameterIndexSize = this.GetIndexByteSize(0, TableIndex.Param);
this.PropertyDefIndexSize = this.GetIndexByteSize(0, TableIndex.Property);
this.ResolutionScopeCodedIndexSize = this.GetIndexByteSize(2, TableIndex.Module, TableIndex.ModuleRef, TableIndex.AssemblyRef, TableIndex.TypeRef);
this.TypeDefIndexSize = this.GetIndexByteSize(0, TableIndex.TypeDef);
this.TypeDefOrRefCodedIndexSize = this.GetIndexByteSize(2, TableIndex.TypeDef, TableIndex.TypeRef, TableIndex.TypeSpec);
this.TypeOrMethodDefCodedIndexSize = this.GetIndexByteSize(1, TableIndex.TypeDef, TableIndex.MethodDef);
int size = this.CalculateTableStreamHeaderSize();
size += GetTableSize(TableIndex.Module, 2 + 3 * this.GuidIndexSize + this.StringIndexSize);
size += GetTableSize(TableIndex.TypeRef, this.ResolutionScopeCodedIndexSize + this.StringIndexSize + this.StringIndexSize);
size += GetTableSize(TableIndex.TypeDef, 4 + this.StringIndexSize + this.StringIndexSize + this.TypeDefOrRefCodedIndexSize + this.FieldDefIndexSize + this.MethodDefIndexSize);
Debug.Assert(RowCounts[(int)TableIndex.FieldPtr] == 0);
size += GetTableSize(TableIndex.Field, 2 + this.StringIndexSize + this.BlobIndexSize);
Debug.Assert(RowCounts[(int)TableIndex.MethodPtr] == 0);
size += GetTableSize(TableIndex.MethodDef, 8 + this.StringIndexSize + this.BlobIndexSize + this.ParameterIndexSize);
Debug.Assert(RowCounts[(int)TableIndex.ParamPtr] == 0);
size += GetTableSize(TableIndex.Param, 4 + this.StringIndexSize);
size += GetTableSize(TableIndex.InterfaceImpl, this.TypeDefIndexSize + this.TypeDefOrRefCodedIndexSize);
size += GetTableSize(TableIndex.MemberRef, this.MemberRefParentCodedIndexSize + this.StringIndexSize + this.BlobIndexSize);
size += GetTableSize(TableIndex.Constant, 2 + this.HasConstantCodedIndexSize + this.BlobIndexSize);
size += GetTableSize(TableIndex.CustomAttribute, this.HasCustomAttributeCodedIndexSize + this.CustomAttributeTypeCodedIndexSize + this.BlobIndexSize);
size += GetTableSize(TableIndex.FieldMarshal, this.HasFieldMarshalCodedIndexSize + this.BlobIndexSize);
size += GetTableSize(TableIndex.DeclSecurity, 2 + this.DeclSecurityCodedIndexSize + this.BlobIndexSize);
size += GetTableSize(TableIndex.ClassLayout, 6 + this.TypeDefIndexSize);
size += GetTableSize(TableIndex.FieldLayout, 4 + this.FieldDefIndexSize);
size += GetTableSize(TableIndex.StandAloneSig, this.BlobIndexSize);
size += GetTableSize(TableIndex.EventMap, this.TypeDefIndexSize + this.EventDefIndexSize);
Debug.Assert(RowCounts[(int)TableIndex.EventPtr] == 0);
size += GetTableSize(TableIndex.Event, 2 + this.StringIndexSize + this.TypeDefOrRefCodedIndexSize);
size += GetTableSize(TableIndex.PropertyMap, this.TypeDefIndexSize + this.PropertyDefIndexSize);
Debug.Assert(RowCounts[(int)TableIndex.PropertyPtr] == 0);
size += GetTableSize(TableIndex.Property, 2 + this.StringIndexSize + this.BlobIndexSize);
size += GetTableSize(TableIndex.MethodSemantics, 2 + this.MethodDefIndexSize + this.HasSemanticsCodedIndexSize);
size += GetTableSize(TableIndex.MethodImpl, 0 + this.TypeDefIndexSize + this.MethodDefOrRefCodedIndexSize + this.MethodDefOrRefCodedIndexSize);
size += GetTableSize(TableIndex.ModuleRef, 0 + this.StringIndexSize);
size += GetTableSize(TableIndex.TypeSpec, 0 + this.BlobIndexSize);
size += GetTableSize(TableIndex.ImplMap, 2 + this.MemberForwardedCodedIndexSize + this.StringIndexSize + this.ModuleRefIndexSize);
size += GetTableSize(TableIndex.FieldRva, 4 + this.FieldDefIndexSize);
size += GetTableSize(TableIndex.EncLog, 8);
size += GetTableSize(TableIndex.EncMap, 4);
size += GetTableSize(TableIndex.Assembly, 16 + this.BlobIndexSize + this.StringIndexSize + this.StringIndexSize);
Debug.Assert(RowCounts[(int)TableIndex.AssemblyProcessor] == 0);
Debug.Assert(RowCounts[(int)TableIndex.AssemblyOS] == 0);
size += GetTableSize(TableIndex.AssemblyRef, 12 + this.BlobIndexSize + this.StringIndexSize + this.StringIndexSize + this.BlobIndexSize);
Debug.Assert(RowCounts[(int)TableIndex.AssemblyRefProcessor] == 0);
Debug.Assert(RowCounts[(int)TableIndex.AssemblyRefOS] == 0);
size += GetTableSize(TableIndex.File, 4 + this.StringIndexSize + this.BlobIndexSize);
size += GetTableSize(TableIndex.ExportedType, 8 + this.StringIndexSize + this.StringIndexSize + this.ImplementationCodedIndexSize);
size += GetTableSize(TableIndex.ManifestResource, 8 + this.StringIndexSize + this.ImplementationCodedIndexSize);
size += GetTableSize(TableIndex.NestedClass, this.TypeDefIndexSize + this.TypeDefIndexSize);
size += GetTableSize(TableIndex.GenericParam, 4 + this.TypeOrMethodDefCodedIndexSize + this.StringIndexSize);
size += GetTableSize(TableIndex.MethodSpec, this.MethodDefOrRefCodedIndexSize + this.BlobIndexSize);
size += GetTableSize(TableIndex.GenericParamConstraint, this.GenericParamIndexSize + this.TypeDefOrRefCodedIndexSize);
// +1 for terminating 0 byte
size = Align(size + 1, StreamAlignment);
this.MetadataTableStreamSize = size;
size += GetAlignedHeapSize(HeapIndex.String);
size += GetAlignedHeapSize(HeapIndex.UserString);
size += GetAlignedHeapSize(HeapIndex.Guid);
size += GetAlignedHeapSize(HeapIndex.Blob);
this.MetadataStreamStorageSize = size;
}
/// <summary>
/// Metadata header size.
/// Includes:
/// - metadata storage signature
/// - storage header
/// - stream headers
/// </summary>
public int MetadataHeaderSize
{
get
{
return this.isMinimalDelta ? 124 : 108;
}
}
/// <summary>
/// Total size of metadata (header and all streams).
/// </summary>
public int MetadataSize
{
get
{
return MetadataHeaderSize + MetadataStreamStorageSize;
}
}
public int GetAlignedHeapSize(HeapIndex index)
{
return Align(HeapSizes[(int)index], StreamAlignment);
}
private int GetTableSize(TableIndex index, int rowSize)
{
return RowCounts[(int)index] * rowSize;
}
internal int CalculateTableStreamHeaderSize()
{
int result = sizeof(int) + // Reserved
sizeof(short) + // Version (major, minor)
sizeof(byte) + // Heap index sizes
sizeof(byte) + // Bit width of RowId
sizeof(long) + // Valid table mask
sizeof(long); // Sorted table mask
foreach (int rowCount in RowCounts)
{
if (rowCount > 0)
{
// present table row count
result += sizeof(int);
}
}
return result;
}
private byte GetIndexByteSize(int discriminatingBits, params TableIndex[] tables)
{
const int BitsPerShort = 16;
return (byte)(isMinimalDelta || IndexDoesNotFit(BitsPerShort - discriminatingBits, tables) ? 4 : 2);
}
private bool IndexDoesNotFit(int numberOfBits, params TableIndex[] tables)
{
int maxIndex = (1 << numberOfBits) - 1;
foreach (TableIndex table in tables)
{
if (RowCounts[(int)table] > maxIndex)
{
return true;
}
}
return false;
}
}
}
......@@ -86,13 +86,14 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Emit
changes,
cancellationToken)
writer.WriteMetadataAndIL(metadataStream, ilStream)
Dim metadataSizes As Cci.MetadataSizes = Nothing
writer.WriteMetadataAndIL(metadataStream, ilStream, metadataSizes)
writer.GetMethodTokens(updatedMethodTokens)
Return New EmitDifferenceResult(
success:=True,
diagnostics:=diagnostics.ToReadOnlyAndFree(),
baseline:=writer.GetDelta(baseline, compilation, encId))
baseline:=writer.GetDelta(baseline, compilation, encId, metadataSizes))
Catch e As Cci.PdbWritingException
diagnostics.Add(ERRID.ERR_PDBWritingFailed, Location.None, e.Message)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册