提交 260090e0 编写于 作者: N Neal Gafter

Merge pull request #6168 from gafter/fix5940-stabilization

Add a PE file section for determinism
......@@ -2160,7 +2160,7 @@ public unsafe void PEHeaders1()
var corHeader = peHeaders.CorHeader;
Assert.Equal(PEMagic.PE32, peHeader.Magic);
Assert.Equal(0x00002362, peHeader.AddressOfEntryPoint);
Assert.Equal(0x0000237E, peHeader.AddressOfEntryPoint);
Assert.Equal(0x00002000, peHeader.BaseOfCode);
Assert.Equal(0x00004000, peHeader.BaseOfData);
Assert.Equal(0x00002000, peHeader.SizeOfHeaders);
......@@ -2199,15 +2199,15 @@ public unsafe void PEHeaders1()
Assert.Equal(0, peHeader.CopyrightTableDirectory.Size);
Assert.Equal(0x2008, peHeader.CorHeaderTableDirectory.RelativeVirtualAddress);
Assert.Equal(0x48, peHeader.CorHeaderTableDirectory.Size);
Assert.Equal(0, peHeader.DebugTableDirectory.RelativeVirtualAddress);
Assert.Equal(0, peHeader.DebugTableDirectory.Size);
Assert.Equal(0x2310, peHeader.DebugTableDirectory.RelativeVirtualAddress);
Assert.Equal(0x1C, peHeader.DebugTableDirectory.Size);
Assert.Equal(0, peHeader.ExceptionTableDirectory.RelativeVirtualAddress);
Assert.Equal(0, peHeader.ExceptionTableDirectory.Size);
Assert.Equal(0, peHeader.ExportTableDirectory.RelativeVirtualAddress);
Assert.Equal(0, peHeader.ExportTableDirectory.Size);
Assert.Equal(0x2000, peHeader.ImportAddressTableDirectory.RelativeVirtualAddress);
Assert.Equal(0x8, peHeader.ImportAddressTableDirectory.Size);
Assert.Equal(0x2310, peHeader.ImportTableDirectory.RelativeVirtualAddress);
Assert.Equal(0x232C, peHeader.ImportTableDirectory.RelativeVirtualAddress);
Assert.Equal(0x4f, peHeader.ImportTableDirectory.Size);
Assert.Equal(0, peHeader.LoadConfigTableDirectory.RelativeVirtualAddress);
Assert.Equal(0, peHeader.LoadConfigTableDirectory.Size);
......@@ -2225,30 +2225,30 @@ public unsafe void PEHeaders1()
peStream.Read(importAddressTableDirectoryBytes, 0, importAddressTableDirectoryBytes.Length);
AssertEx.Equal(new byte[]
{
0x44, 0x23, 0x00, 0x00,
0x60, 0x23, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00
}, importAddressTableDirectoryBytes);
int importTableDirectoryOffset;
Assert.True(peHeaders.TryGetDirectoryOffset(peHeader.ImportTableDirectory, out importTableDirectoryOffset));
Assert.Equal(0x2310, importTableDirectoryOffset);
Assert.Equal(0x232C, importTableDirectoryOffset);
var importTableDirectoryBytes = new byte[peHeader.ImportTableDirectory.Size];
peStream.Position = importTableDirectoryOffset;
peStream.Read(importTableDirectoryBytes, 0, importTableDirectoryBytes.Length);
AssertEx.Equal(new byte[]
{
0x38, 0x23, 0x00, 0x00, // RVA
0x54, 0x23, 0x00, 0x00, // RVA
0x00, 0x00, 0x00, 0x00, // 0
0x00, 0x00, 0x00, 0x00, // 0
0x52, 0x23, 0x00, 0x00, // name RVA
0x6E, 0x23, 0x00, 0x00, // name RVA
0x00, 0x20, 0x00, 0x00, // ImportAddressTableDirectory RVA
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x44, 0x23, 0x00, 0x00, // hint RVA
0x60, 0x23, 0x00, 0x00, // hint RVA
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, // hint
......@@ -2271,7 +2271,7 @@ public unsafe void PEHeaders1()
Assert.Equal(0, coffHeader.NumberOfSymbols);
Assert.Equal(0, coffHeader.PointerToSymbolTable);
Assert.Equal(0xe0, coffHeader.SizeOfOptionalHeader);
Assert.Equal(-1017800620, coffHeader.TimeDateStamp);
Assert.Equal(-609170495, coffHeader.TimeDateStamp);
Assert.Equal(0, corHeader.EntryPointTokenOrRelativeVirtualAddress);
Assert.Equal(CorFlags.ILOnly, corHeader.Flags);
......@@ -2305,7 +2305,7 @@ public unsafe void PEHeaders1()
Assert.Equal(SectionCharacteristics.ContainsCode | SectionCharacteristics.MemExecute | SectionCharacteristics.MemRead, sections[0].SectionCharacteristics);
Assert.Equal(0x2000, sections[0].SizeOfRawData);
Assert.Equal(0x2000, sections[0].VirtualAddress);
Assert.Equal(872, sections[0].VirtualSize);
Assert.Equal(900, sections[0].VirtualSize);
Assert.Equal(".reloc", sections[1].Name);
Assert.Equal(0, sections[1].NumberOfLineNumbers);
......@@ -2321,7 +2321,7 @@ public unsafe void PEHeaders1()
var relocBlock = peReader.GetSectionData(sections[1].VirtualAddress);
var relocBytes = new byte[sections[1].VirtualSize];
Marshal.Copy((IntPtr)relocBlock.Pointer, relocBytes, 0, relocBytes.Length);
Assert.Equal(new byte[] { 0, 0x20, 0, 0, 0x0c, 0, 0, 0, 0x64, 0x33, 0, 0 }, relocBytes);
AssertEx.Equal(new byte[] { 0, 0x20, 0, 0, 0x0c, 0, 0, 0, 0x80, 0x33, 0, 0 }, relocBytes);
}
[Fact]
......@@ -2386,8 +2386,8 @@ public void PEHeaders2()
Assert.Equal(0, peHeader.CopyrightTableDirectory.Size);
Assert.Equal(0x2000, peHeader.CorHeaderTableDirectory.RelativeVirtualAddress);
Assert.Equal(0x48, peHeader.CorHeaderTableDirectory.Size);
Assert.Equal(0, peHeader.DebugTableDirectory.RelativeVirtualAddress);
Assert.Equal(0, peHeader.DebugTableDirectory.Size);
Assert.Equal(0x2324, peHeader.DebugTableDirectory.RelativeVirtualAddress);
Assert.Equal(0x1C, peHeader.DebugTableDirectory.Size);
Assert.Equal(0, peHeader.ExceptionTableDirectory.RelativeVirtualAddress);
Assert.Equal(0, peHeader.ExceptionTableDirectory.Size);
Assert.Equal(0, peHeader.ExportTableDirectory.RelativeVirtualAddress);
......@@ -2409,7 +2409,7 @@ public void PEHeaders2()
Assert.Equal(0, coffHeader.NumberOfSymbols);
Assert.Equal(0, coffHeader.PointerToSymbolTable);
Assert.Equal(240, coffHeader.SizeOfOptionalHeader);
Assert.Equal(-1439607823, coffHeader.TimeDateStamp);
Assert.Equal(-862605524, coffHeader.TimeDateStamp);
Assert.Equal(0x06000001, corHeader.EntryPointTokenOrRelativeVirtualAddress);
Assert.Equal(CorFlags.ILOnly, corHeader.Flags);
......@@ -2443,7 +2443,7 @@ public void PEHeaders2()
Assert.Equal(SectionCharacteristics.ContainsCode | SectionCharacteristics.MemExecute | SectionCharacteristics.MemRead, sections[0].SectionCharacteristics);
Assert.Equal(0x400, sections[0].SizeOfRawData);
Assert.Equal(0x2000, sections[0].VirtualAddress);
Assert.Equal(804, sections[0].VirtualSize);
Assert.Equal(832, sections[0].VirtualSize);
}
}
}
......@@ -20,7 +20,7 @@ public ContentId(byte[] guid, byte[] stamp)
Stamp = stamp;
}
public bool IsDefault => Guid == null;
public bool IsDefault => Guid == null && Stamp == null;
internal static ContentId FromHash(ImmutableArray<byte> hashCode)
{
......
......@@ -202,7 +202,7 @@ private bool WritePeToStream(MetadataWriter mdWriter, Func<Stream> getPeStream,
DirectoryEntry importAddressTable = default(DirectoryEntry);
int entryPointAddress = 0;
if (EmitPdb)
if (EmitPdb || _deterministic)
{
debugDirectory = new DirectoryEntry(textSectionRva + ComputeOffsetToDebugTable(metadataSizes), ImageDebugDirectoryBaseSize);
}
......@@ -461,18 +461,30 @@ private int ComputeOffsetToMetadata(int ilStreamLength)
return OffsetToILStream + BitArithmeticUtilities.Align(ilStreamLength, 4);
}
private const int ImageDebugDirectoryBaseSize =
sizeof(uint) + // Characteristics
sizeof(uint) + // TimeDataStamp
sizeof(uint) + // Version
sizeof(uint) + // Type
sizeof(uint) + // SizeOfData
sizeof(uint) + // AddressOfRawData
sizeof(uint); // PointerToRawData
/// <summary>
/// The size of a single entry in the "Debug Directory (Image Only)"
/// </summary>
private const int ImageDebugDirectoryEntrySize =
sizeof(uint) + // Characteristics
sizeof(uint) + // TimeDataStamp
sizeof(uint) + // Version
sizeof(uint) + // Type
sizeof(uint) + // SizeOfData
sizeof(uint) + // AddressOfRawData
sizeof(uint); // PointerToRawData
/// <summary>
/// The size of our debug directory: one entry for debug information, and an optional second one indicating
/// that the timestamp is deterministic (i.e. not really a timestamp)
/// </summary>
private int ImageDebugDirectoryBaseSize =>
(_deterministic ? ImageDebugDirectoryEntrySize : 0) +
(EmitPdb ? ImageDebugDirectoryEntrySize : 0);
private int ComputeSizeOfDebugDirectoryData()
{
return
// The debug directory data is only needed if this.EmitPdb.
return (!EmitPdb) ? 0 :
4 + // 4B signature "RSDS"
16 + // GUID
sizeof(uint) + // Age
......@@ -481,7 +493,7 @@ private int ComputeSizeOfDebugDirectoryData()
private int ComputeSizeOfDebugDirectory()
{
return EmitPdb ? ImageDebugDirectoryBaseSize + ComputeSizeOfDebugDirectoryData() : 0;
return ImageDebugDirectoryBaseSize + ComputeSizeOfDebugDirectoryData();
}
private int ComputeSizeOfPeHeaders(int sectionCount)
......@@ -1211,7 +1223,7 @@ private static void WriteSectionHeader(SectionHeader sectionHeader, BlobBuilder
// strong name signature:
WriteSpaceForHash(peStream, metadataSizes.StrongNameSignatureSize);
if (EmitPdb)
if (EmitPdb || _deterministic)
{
WriteDebugTable(peStream, textSection, nativePdbContentId, portablePdbContentId, metadataSizes);
}
......@@ -1350,60 +1362,102 @@ private static void WriteSpaceForHash(Stream peStream, int strongNameSignatureSi
}
}
/// <summary>
/// Write one entry in the "Debug Directory (Image Only)"
/// See https://msdn.microsoft.com/en-us/windows/hardware/gg463119.aspx
/// section 5.1.1 (pages 71-72).
/// </summary>
private static void WriteDebugTableEntry(
PooledBlobBuilder writer,
byte[] stamp,
uint version, // major and minor version, combined
uint debugType,
uint sizeOfData,
uint addressOfRawData,
uint pointerToRawData
)
{
writer.WriteUInt32(0); // characteristics
Debug.Assert(stamp.Length == 4);
writer.WriteBytes(stamp);
writer.WriteUInt32(version);
writer.WriteUInt32(debugType);
writer.WriteUInt32(sizeOfData);
writer.WriteUInt32(addressOfRawData);
writer.WriteUInt32(pointerToRawData);
}
private readonly static byte[] zeroStamp = new byte[4]; // four bytes of zero
/// <summary>
/// Write the entire "Debug Directory (Image Only)" along with data that it points to.
/// </summary>
private void WriteDebugTable(Stream peStream, SectionHeader textSection, ContentId nativePdbContentId, ContentId portablePdbContentId, MetadataSizes metadataSizes)
{
Debug.Assert(nativePdbContentId.IsDefault ^ portablePdbContentId.IsDefault);
int tableSize = ImageDebugDirectoryBaseSize;
Debug.Assert(tableSize != 0);
Debug.Assert(nativePdbContentId.IsDefault || portablePdbContentId.IsDefault);
Debug.Assert(!EmitPdb || (nativePdbContentId.IsDefault ^ portablePdbContentId.IsDefault));
var writer = PooledBlobBuilder.GetInstance();
// characteristics:
writer.WriteUInt32(0);
// PDB stamp & version
if (portablePdbContentId.IsDefault)
int dataSize = ComputeSizeOfDebugDirectoryData();
if (this.EmitPdb)
{
writer.WriteBytes(nativePdbContentId.Stamp);
writer.WriteUInt32(0);
const int IMAGE_DEBUG_TYPE_CODEVIEW = 2; // from PE spec
uint dataOffset = (uint)(ComputeOffsetToDebugTable(metadataSizes) + tableSize);
WriteDebugTableEntry(writer,
stamp: nativePdbContentId.Stamp ?? portablePdbContentId.Stamp,
version: portablePdbContentId.IsDefault ? (uint)0 : ('P' << 24 | 'M' << 16 | 0x01 << 8 | 0x00),
debugType: IMAGE_DEBUG_TYPE_CODEVIEW,
sizeOfData: (uint)dataSize,
addressOfRawData: (uint)textSection.RelativeVirtualAddress + dataOffset, // RVA of the data
pointerToRawData: (uint)textSection.PointerToRawData + dataOffset); // position of the data in the PE stream
}
else
if (this._deterministic)
{
writer.WriteBytes(portablePdbContentId.Stamp);
writer.WriteUInt32('P' << 24 | 'M' << 16 | 0x01 << 8 | 0x00);
const int IMAGE_DEBUG_TYPE_NO_TIMESTAMP = 16; // from PE spec
WriteDebugTableEntry(writer,
stamp: zeroStamp,
version: 0,
debugType: IMAGE_DEBUG_TYPE_NO_TIMESTAMP,
sizeOfData: 0,
addressOfRawData: 0,
pointerToRawData: 0);
}
// type:
const int ImageDebugTypeCodeView = 2;
writer.WriteUInt32(ImageDebugTypeCodeView);
// size of data:
writer.WriteUInt32((uint)ComputeSizeOfDebugDirectoryData());
uint dataOffset = (uint)ComputeOffsetToDebugTable(metadataSizes) + ImageDebugDirectoryBaseSize;
// We should now have written all and precisely the data we said we'd write for the table entries.
Debug.Assert(writer.Count == tableSize);
// PointerToRawData (RVA of the data):
writer.WriteUInt32((uint)textSection.RelativeVirtualAddress + dataOffset);
// ====================
// The following is additional data beyond the debug directory at the offset `dataOffset`
// pointed to by the ImageDebugTypeCodeView entry.
// AddressOfRawData (position of the data in the PE stream):
writer.WriteUInt32((uint)textSection.PointerToRawData + dataOffset);
if (EmitPdb)
{
writer.WriteByte((byte)'R');
writer.WriteByte((byte)'S');
writer.WriteByte((byte)'D');
writer.WriteByte((byte)'S');
writer.WriteByte((byte)'R');
writer.WriteByte((byte)'S');
writer.WriteByte((byte)'D');
writer.WriteByte((byte)'S');
// PDB id:
writer.WriteBytes(nativePdbContentId.Guid ?? portablePdbContentId.Guid);
// PDB id:
writer.WriteBytes(nativePdbContentId.Guid ?? portablePdbContentId.Guid);
// age
writer.WriteUInt32(PdbWriter.Age);
// age
writer.WriteUInt32(PdbWriter.Age);
// UTF-8 encoded zero-terminated path to PDB
int pathStart = writer.Position;
writer.WriteUTF8(_pdbPathOpt, allowUnpairedSurrogates: true);
writer.WriteByte(0);
// UTF-8 encoded zero-terminated path to PDB
int pathStart = writer.Position;
writer.WriteUTF8(_pdbPathOpt, allowUnpairedSurrogates: true);
writer.WriteByte(0);
// padding:
writer.WriteBytes(0, Math.Max(0, _minPdbPath - (writer.Position - pathStart)));
}
// padding:
writer.WriteBytes(0, Math.Max(0, _minPdbPath - (writer.Position - pathStart)));
// We should now have written all and precisely the data we said we'd write for the table and its data.
Debug.Assert(writer.Count == tableSize + dataSize);
writer.WriteContentTo(peStream);
writer.Free();
......
......@@ -241,14 +241,18 @@ public static void ValidateDebugDirectory(Stream peStream, Stream portablePdbStr
int position;
Assert.True(peReader.PEHeaders.TryGetDirectoryOffset(debugDirectory, out position));
Assert.Equal(0x1c, debugDirectory.Size);
int entries = debugDirectory.Size / 0x1c;
Assert.Equal(0, debugDirectory.Size % 0x1c);
Assert.True(entries == 1 || entries == 2);
bool hasDebug = entries == 2;
byte[] buffer = new byte[debugDirectory.Size];
peStream.Read(buffer, 0, buffer.Length);
peStream.Read(buffer, 0, buffer.Length); // TODO: this is not guaranteed to read buffer.Length of data
peStream.Position = position;
var reader = new BinaryReader(peStream);
// first the IMAGE_DEBUG_TYPE_CODEVIEW entry
int characteristics = reader.ReadInt32();
Assert.Equal(0, characteristics);
......@@ -258,7 +262,7 @@ public static void ValidateDebugDirectory(Stream peStream, Stream portablePdbStr
Assert.Equal((portablePdbStreamOpt != null) ? 0x504d0100u : 0, version);
int type = reader.ReadInt32();
Assert.Equal(2, type);
Assert.Equal(2, type); // IMAGE_DEBUG_TYPE_CODEVIEW
int sizeOfData = reader.ReadInt32();
int rvaOfRawData = reader.ReadInt32();
......@@ -269,6 +273,27 @@ public static void ValidateDebugDirectory(Stream peStream, Stream portablePdbStr
int pointerToRawData = reader.ReadInt32();
Assert.Equal(pointerToRawData, sectionHeader.PointerToRawData + rvaOfRawData - sectionHeader.VirtualAddress);
// optionally a IMAGE_DEBUG_TYPE_NO_TIMESTAMP entry indicating that timestamps are deterministic
if (hasDebug)
{
int characteristics2 = reader.ReadInt32();
Assert.Equal(0, characteristics2);
byte[] stamp2 = reader.ReadBytes(sizeof(int));
int version2 = reader.ReadInt32();
Assert.Equal(0, version2);
int type2 = reader.ReadInt32();
Assert.Equal(16, type2); // IMAGE_DEBUG_TYPE_NO_TIMESTAMP
int sizeOfData2 = reader.ReadInt32();
int rvaOfRawData2 = reader.ReadInt32();
int pointerToRawData2 = reader.ReadInt32();
Assert.Equal(0, sizeOfData2 | rvaOfRawData2 | pointerToRawData2);
}
// Now verify the data pointed to by the IMAGE_DEBUG_TYPE_CODEVIEW entry
peStream.Position = pointerToRawData;
Assert.Equal((byte)'R', reader.ReadByte());
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册