提交 8da79efb 编写于 作者: T Tomáš Matoušek

Merge pull request #4785 from tmat/PdbPadding

PDB path padding
...@@ -6232,7 +6232,7 @@ class Program3 ...@@ -6232,7 +6232,7 @@ class Program3
using (var peFile = File.OpenRead(exe.Path)) using (var peFile = File.OpenRead(exe.Path))
{ {
PdbValidation.ValidateDebugDirectory(peFile, pdb.Path, isPortable: false); PdbValidation.ValidateDebugDirectory(peFile, pdb.Path, isPortable: false, isDeterministic: false);
} }
Assert.True(new FileInfo(exe.Path).Length < oldSize); Assert.True(new FileInfo(exe.Path).Length < oldSize);
...@@ -6243,7 +6243,7 @@ class Program3 ...@@ -6243,7 +6243,7 @@ class Program3
using (var peFile = File.OpenRead(exe.Path)) using (var peFile = File.OpenRead(exe.Path))
{ {
PdbValidation.ValidateDebugDirectory(peFile, pdb.Path, isPortable: false); PdbValidation.ValidateDebugDirectory(peFile, pdb.Path, isPortable: false, isDeterministic: false);
} }
} }
......
...@@ -1805,6 +1805,8 @@ private static EmitResult ToEmitResultAndFree(DiagnosticBag diagnostics, bool su ...@@ -1805,6 +1805,8 @@ private static EmitResult ToEmitResultAndFree(DiagnosticBag diagnostics, bool su
return new EmitResult(success, diagnostics.ToReadOnlyAndFree()); return new EmitResult(success, diagnostics.ToReadOnlyAndFree());
} }
internal bool IsEmitDeterministic => this.Feature("deterministic")?.Equals("true", StringComparison.OrdinalIgnoreCase) ?? false;
internal bool SerializeToPeStream( internal bool SerializeToPeStream(
CommonPEModuleBuilder moduleBeingBuilt, CommonPEModuleBuilder moduleBeingBuilt,
EmitStreamProvider peStreamProvider, EmitStreamProvider peStreamProvider,
...@@ -1825,7 +1827,7 @@ private static EmitResult ToEmitResultAndFree(DiagnosticBag diagnostics, bool su ...@@ -1825,7 +1827,7 @@ private static EmitResult ToEmitResultAndFree(DiagnosticBag diagnostics, bool su
Stream portablePdbTempStream = null; Stream portablePdbTempStream = null;
Stream peTempStream = null; Stream peTempStream = null;
bool deterministic = this.Feature("deterministic")?.Equals("true", StringComparison.OrdinalIgnoreCase) ?? false; bool deterministic = IsEmitDeterministic;
bool emitPortablePdb = moduleBeingBuilt.EmitOptions.DebugInformationFormat == DebugInformationFormat.PortablePdb; bool emitPortablePdb = moduleBeingBuilt.EmitOptions.DebugInformationFormat == DebugInformationFormat.PortablePdb;
string pdbPath = (pdbStreamProvider != null) ? (moduleBeingBuilt.EmitOptions.PdbFilePath ?? FileNameUtilities.ChangeExtension(SourceModule.Name, "pdb")) : null; string pdbPath = (pdbStreamProvider != null) ? (moduleBeingBuilt.EmitOptions.PdbFilePath ?? FileNameUtilities.ChangeExtension(SourceModule.Name, "pdb")) : null;
......
...@@ -27,6 +27,13 @@ internal sealed class PeWriter ...@@ -27,6 +27,13 @@ internal sealed class PeWriter
private const string ResourceSectionName = ".rsrc"; private const string ResourceSectionName = ".rsrc";
private const string RelocationSectionName = ".reloc"; private const string RelocationSectionName = ".reloc";
/// <summary>
/// Minimal size of PDB path in Debug Directory. We pad the path to this minimal size to
/// allow some tools to patch the path without the need to rewrite the entire image.
/// This is a workaround put in place until these tools are retired.
/// </summary>
private readonly int _minPdbPath;
/// <summary> /// <summary>
/// True if we should attempt to generate a deterministic output (no timestamps or random data). /// True if we should attempt to generate a deterministic output (no timestamps or random data).
/// </summary> /// </summary>
...@@ -53,6 +60,8 @@ internal sealed class PeWriter ...@@ -53,6 +60,8 @@ internal sealed class PeWriter
_pdbPathOpt = pdbPathOpt; _pdbPathOpt = pdbPathOpt;
_deterministic = deterministic; _deterministic = deterministic;
// The PDB padding workaround is only needed for legacy tools that don't use deterministic build.
_minPdbPath = deterministic ? 0 : 260;
_nativeResourcesOpt = nativeResourcesOpt; _nativeResourcesOpt = nativeResourcesOpt;
_nativeResourceSectionOpt = nativeResourceSectionOpt; _nativeResourceSectionOpt = nativeResourceSectionOpt;
_is32bit = !_properties.Requires64bits; _is32bit = !_properties.Requires64bits;
...@@ -457,8 +466,7 @@ private int ComputeSizeOfDebugDirectoryData() ...@@ -457,8 +466,7 @@ private int ComputeSizeOfDebugDirectoryData()
4 + // 4B signature "RSDS" 4 + // 4B signature "RSDS"
16 + // GUID 16 + // GUID
sizeof(uint) + // Age sizeof(uint) + // Age
Encoding.UTF8.GetByteCount(_pdbPathOpt) + Math.Max(BlobUtilities.GetUTF8ByteCount(_pdbPathOpt) + 1, _minPdbPath);
1; // Null terminator
} }
private int ComputeSizeOfDebugDirectory() private int ComputeSizeOfDebugDirectory()
...@@ -1380,9 +1388,13 @@ private void WriteDebugTable(Stream peStream, SectionHeader textSection, Content ...@@ -1380,9 +1388,13 @@ private void WriteDebugTable(Stream peStream, SectionHeader textSection, Content
writer.WriteUInt32(PdbWriter.Age); writer.WriteUInt32(PdbWriter.Age);
// UTF-8 encoded zero-terminated path to PDB // UTF-8 encoded zero-terminated path to PDB
int pathStart = writer.Position;
writer.WriteUTF8(_pdbPathOpt, allowUnpairedSurrogates: true); writer.WriteUTF8(_pdbPathOpt, allowUnpairedSurrogates: true);
writer.WriteByte(0); writer.WriteByte(0);
// padding:
writer.WriteBytes(0, Math.Max(0, _minPdbPath - (writer.Position - pathStart)));
writer.WriteContentTo(peStream); writer.WriteContentTo(peStream);
writer.Free(); writer.Free();
} }
......
...@@ -226,13 +226,13 @@ private static void RemoveNonPortablePdb(XElement expectedNativePdb) ...@@ -226,13 +226,13 @@ private static void RemoveNonPortablePdb(XElement expectedNativePdb)
actual = PdbToXmlConverter.ToXml(pdbbits, exebits, options, methodName: qualifiedMethodName); actual = PdbToXmlConverter.ToXml(pdbbits, exebits, options, methodName: qualifiedMethodName);
} }
ValidateDebugDirectory(exebits, compilation.AssemblyName + ".pdb", portable); ValidateDebugDirectory(exebits, compilation.AssemblyName + ".pdb", portable, compilation.IsEmitDeterministic);
} }
return actual; return actual;
} }
public static void ValidateDebugDirectory(Stream peStream, string pdbPath, bool isPortable) public static void ValidateDebugDirectory(Stream peStream, string pdbPath, bool isPortable, bool isDeterministic)
{ {
peStream.Seek(0, SeekOrigin.Begin); peStream.Seek(0, SeekOrigin.Begin);
PEReader peReader = new PEReader(peStream); PEReader peReader = new PEReader(peStream);
...@@ -281,11 +281,28 @@ public static void ValidateDebugDirectory(Stream peStream, string pdbPath, bool ...@@ -281,11 +281,28 @@ public static void ValidateDebugDirectory(Stream peStream, string pdbPath, bool
Assert.Equal(1u, reader.ReadUInt32()); Assert.Equal(1u, reader.ReadUInt32());
byte[] pathBlob = new byte[sizeOfData - 24 - 1]; byte[] pathBlob = new byte[sizeOfData - 24];
reader.Read(pathBlob, 0, pathBlob.Length); reader.Read(pathBlob, 0, pathBlob.Length);
var actualPath = Encoding.UTF8.GetString(pathBlob);
int terminator = Array.IndexOf(pathBlob, (byte)0);
Assert.True(terminator >= 0, "Path should be NUL terminated");
for (int i = terminator + 1; i < pathBlob.Length; i++)
{
Assert.Equal(0, pathBlob[i]);
}
if (isDeterministic)
{
Assert.Equal(pathBlob.Length - 1, terminator);
}
else
{
Assert.True(pathBlob.Length >= 260, "Path should be at least MAX_PATH long");
}
var actualPath = Encoding.UTF8.GetString(pathBlob, 0, terminator);
Assert.Equal(pdbPath, actualPath); Assert.Equal(pdbPath, actualPath);
Assert.Equal(0, reader.ReadByte());
} }
public static void VerifyMetadataEqualModuloMvid(Stream peStream1, Stream peStream2) public static void VerifyMetadataEqualModuloMvid(Stream peStream1, Stream peStream2)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册