/// <summary> /// Capture the set of compilation options to allow a compilation /// to be reconstructed from the pdb /// </summary> private void EmbedCompilationOptions(CommonPEModuleBuilder module) { var builder = new BlobBuilder(); var compilerVersion = typeof(Compilation).Assembly.GetCustomAttribute <AssemblyInformationalVersionAttribute>().InformationalVersion; WriteValue(CompilationOptionNames.CompilerVersion, compilerVersion); WriteValue(CompilationOptionNames.Language, module.CommonCompilation.Options.Language); if (module.EmitOptions.FallbackSourceFileEncoding != null) { WriteValue(CompilationOptionNames.FallbackEncoding, module.EmitOptions.FallbackSourceFileEncoding.WebName); } if (module.EmitOptions.DefaultSourceFileEncoding != null) { WriteValue(CompilationOptionNames.DefaultEncoding, module.EmitOptions.DefaultSourceFileEncoding.WebName); } int portabilityPolicy = 0; if (module.CommonCompilation.Options.AssemblyIdentityComparer is DesktopAssemblyIdentityComparer identityComparer) { portabilityPolicy |= identityComparer.PortabilityPolicy.SuppressSilverlightLibraryAssembliesPortability ? 0b1 : 0; portabilityPolicy |= identityComparer.PortabilityPolicy.SuppressSilverlightPlatformAssembliesPortability ? 0b10 : 0; } if (portabilityPolicy != 0) { WriteValue(CompilationOptionNames.PortabilityPolicy, portabilityPolicy.ToString()); } var optimizationLevel = module.CommonCompilation.Options.OptimizationLevel; var debugPlusMode = module.CommonCompilation.Options.DebugPlusMode; if (optimizationLevel != OptimizationLevel.Debug || debugPlusMode) { WriteValue(CompilationOptionNames.Optimization, optimizationLevel.ToPdbSerializedString(debugPlusMode)); } var runtimeVersion = typeof(object).Assembly.GetCustomAttribute <AssemblyInformationalVersionAttribute>()?.InformationalVersion; WriteValue(CompilationOptionNames.RuntimeVersion, runtimeVersion); module.CommonCompilation.SerializePdbEmbeddedCompilationOptions(builder); _debugMetadataOpt.AddCustomDebugInformation( parent: EntityHandle.ModuleDefinition, kind: _debugMetadataOpt.GetOrAddGuid(PortableCustomDebugInfoKinds.CompilationOptions), value: _debugMetadataOpt.GetOrAddBlob(builder)); void WriteValue(string key, string value) { builder.WriteUTF8(key); builder.WriteByte(0); builder.WriteUTF8(value); builder.WriteByte(0); } }
/// <summary> /// Fills in stringIndexMap with data from stringIndex and write to stringWriter. /// Releases stringIndex as the stringTable is sealed after this point. /// </summary> private void SerializeStringHeap() { // Sort by suffix and remove stringIndex var sorted = new List <KeyValuePair <string, StringIdx> >(_strings); sorted.Sort(new SuffixSort()); _strings = null; _stringWriter = new BlobBuilder(1024); // Create VirtIdx to Idx map and add entry for empty string _stringIndexToResolvedOffsetMap = new int[sorted.Count + 1]; _stringIndexToResolvedOffsetMap[0] = 0; _stringWriter.WriteByte(0); // Find strings that can be folded string prev = string.Empty; foreach (KeyValuePair <string, StringIdx> entry in sorted) { int position = _stringHeapStartOffset + _stringWriter.Position; // It is important to use ordinal comparison otherwise we'll use the current culture! if (prev.EndsWith(entry.Key, StringComparison.Ordinal) && !BlobUtilities.IsLowSurrogateChar(entry.Key[0])) { // Map over the tail of prev string. Watch for null-terminator of prev string. _stringIndexToResolvedOffsetMap[entry.Value.MapIndex] = position - (BlobUtilities.GetUTF8ByteCount(entry.Key) + 1); } else { _stringIndexToResolvedOffsetMap[entry.Value.MapIndex] = position; _stringWriter.WriteUTF8(entry.Key, allowUnpairedSurrogates: false); _stringWriter.WriteByte(0); } prev = entry.Key; } }
/// <summary> /// Write string as UTF8 with null terminator. /// </summary> private static void WriteUtf8String(BlobBuilder builder, string str) { builder.WriteUTF8(str); builder.WriteByte(0); }
private void WriteDebugTable(Stream peStream, SectionHeader textSection, ContentId nativePdbContentId, ContentId portablePdbContentId, MetadataSizes metadataSizes) { Debug.Assert(nativePdbContentId.IsDefault ^ portablePdbContentId.IsDefault); var writer = new BlobBuilder(); // characteristics: writer.WriteUInt32(0); // PDB stamp & version if (portablePdbContentId.IsDefault) { writer.WriteBytes(nativePdbContentId.Stamp); writer.WriteUInt32(0); } else { writer.WriteBytes(portablePdbContentId.Stamp); writer.WriteUInt32('P' << 24 | 'M' << 16 | 0x00 << 8 | 0x01); } // type: const int ImageDebugTypeCodeView = 2; writer.WriteUInt32(ImageDebugTypeCodeView); // size of data: writer.WriteUInt32((uint)ComputeSizeOfDebugDirectoryData()); uint dataOffset = (uint)ComputeOffsetToDebugTable(metadataSizes) + ImageDebugDirectoryBaseSize; // PointerToRawData (RVA of the data): writer.WriteUInt32((uint)textSection.RelativeVirtualAddress + dataOffset); // AddressOfRawData (position of the data in the PE stream): writer.WriteUInt32((uint)textSection.PointerToRawData + dataOffset); writer.WriteByte((byte)'R'); writer.WriteByte((byte)'S'); writer.WriteByte((byte)'D'); writer.WriteByte((byte)'S'); // PDB id: writer.WriteBytes(nativePdbContentId.Guid ?? portablePdbContentId.Guid); // age writer.WriteUInt32(PdbWriter.Age); // UTF-8 encoded zero-terminated path to PDB writer.WriteUTF8(_pdbPathOpt); writer.WriteByte(0); writer.WriteTo(peStream); writer.Free(); }
public void WriteUTF8_ReplaceUnpairedSurrogates() { var writer = new BlobBuilder(4); writer.WriteUTF8("a", allowUnpairedSurrogates: false); writer.WriteUTF8("", allowUnpairedSurrogates: false); writer.WriteUTF8("bc", allowUnpairedSurrogates: false); writer.WriteUTF8("d", allowUnpairedSurrogates: false); writer.WriteUTF8("", allowUnpairedSurrogates: false); writer.WriteUTF8(Encoding.UTF8.GetString(new byte[] { 0x00, 0xC2, 0x80, 0xE1, 0x88, 0xB4 }), allowUnpairedSurrogates: false); writer.WriteUTF8("\0\ud800", allowUnpairedSurrogates: false); // hi surrogate writer.WriteUTF8("\0\udc00", allowUnpairedSurrogates: false); // lo surrogate writer.WriteUTF8("\0\ud800\udc00", allowUnpairedSurrogates: false); // pair writer.WriteUTF8("\0\udc00\ud800", allowUnpairedSurrogates: false); // lo + hi AssertEx.Equal(new byte[] { (byte)'a', (byte)'b', (byte)'c', (byte)'d', 0x00, 0xC2, 0x80, 0xE1, 0x88, 0xB4, 0x00, 0xEF, 0xBF, 0xBD, 0x00, 0xEF, 0xBF, 0xBD, 0x00, 0xF0, 0x90, 0x80, 0x80, 0x00, 0xEF, 0xBF, 0xBD, 0xEF, 0xBF, 0xBD }, writer.ToArray()); }
public unsafe void Write_Errors() { var builder = new BlobBuilder(16); Assert.Throws<ArgumentNullException>(() => builder.WriteUTF16((char[])null)); Assert.Throws<ArgumentNullException>(() => builder.WriteUTF16((string)null)); Assert.Throws<ArgumentNullException>(() => builder.WriteUTF8(null, allowUnpairedSurrogates: true)); Assert.Throws<ArgumentNullException>(() => builder.WriteUTF8(null, allowUnpairedSurrogates: true)); Assert.Throws<ArgumentNullException>(() => builder.TryWriteBytes((Stream)null, 0)); Assert.Throws<ArgumentNullException>(() => builder.WriteBytes(null)); Assert.Throws<ArgumentNullException>(() => builder.WriteBytes(null, 0, 0)); Assert.Throws<ArgumentNullException>(() => builder.WriteBytes((byte*)null, 0)); Assert.Throws<ArgumentNullException>(() => builder.WriteBytes(default(ImmutableArray<byte>))); Assert.Throws<ArgumentNullException>(() => builder.WriteBytes(default(ImmutableArray<byte>), 0, 0)); var bw = default(BlobWriter); Assert.Throws<ArgumentNullException>(() => builder.WriteContentTo(ref bw)); Assert.Throws<ArgumentNullException>(() => builder.WriteContentTo((Stream)null)); Assert.Throws<ArgumentNullException>(() => builder.WriteContentTo((BlobBuilder)null)); Assert.Throws<ArgumentOutOfRangeException>(() => builder.TryWriteBytes(new MemoryStream(), -1)); Assert.Throws<ArgumentOutOfRangeException>(() => builder.WriteBytes(0, -1)); Assert.Throws<ArgumentOutOfRangeException>(() => builder.WriteBytes(new byte[] { }, 1, 0)); Assert.Throws<ArgumentOutOfRangeException>(() => builder.WriteBytes(new byte[] { }, 0, 1)); Assert.Throws<ArgumentOutOfRangeException>(() => builder.WriteBytes(new byte[] { }, 0, -1)); Assert.Throws<ArgumentOutOfRangeException>(() => builder.WriteBytes(ImmutableArray<byte>.Empty, 1, 0)); Assert.Throws<ArgumentOutOfRangeException>(() => builder.WriteBytes(ImmutableArray<byte>.Empty, 0, 1)); Assert.Throws<ArgumentOutOfRangeException>(() => builder.WriteBytes(ImmutableArray<byte>.Empty, 1, -1)); }
public void Link() { var builder1 = new BlobBuilder(16); builder1.WriteByte(1); var builder2 = new BlobBuilder(16); builder2.WriteByte(2); var builder3 = new BlobBuilder(16); builder3.WriteByte(3); var builder4 = new BlobBuilder(16); builder4.WriteByte(4); var builder5 = new BlobBuilder(16); builder5.WriteByte(5); builder2.LinkPrefix(builder1); AssertEx.Equal(new byte[] { 1, 2 }, builder2.ToArray()); Assert.Throws<InvalidOperationException>(() => builder1.ToArray()); Assert.Throws<InvalidOperationException>(() => builder2.LinkPrefix(builder1)); Assert.Throws<InvalidOperationException>(() => builder1.WriteByte(0xff)); Assert.Throws<InvalidOperationException>(() => builder1.WriteBytes(1, 10)); Assert.Throws<InvalidOperationException>(() => builder1.WriteBytes(new byte[] { 1 })); Assert.Throws<InvalidOperationException>(() => builder1.ReserveBytes(1)); Assert.Throws<InvalidOperationException>(() => builder1.GetBlobs()); Assert.Throws<InvalidOperationException>(() => builder1.ContentEquals(builder1)); Assert.Throws<InvalidOperationException>(() => builder1.WriteUTF16("str")); Assert.Throws<InvalidOperationException>(() => builder1.WriteUTF8("str", allowUnpairedSurrogates: false)); builder2.LinkSuffix(builder3); AssertEx.Equal(new byte[] { 1, 2, 3 }, builder2.ToArray()); Assert.Throws<InvalidOperationException>(() => builder3.LinkPrefix(builder5)); builder2.LinkPrefix(builder4); AssertEx.Equal(new byte[] { 4, 1, 2, 3 }, builder2.ToArray()); Assert.Throws<InvalidOperationException>(() => builder4.LinkPrefix(builder5)); builder2.LinkSuffix(builder5); AssertEx.Equal(new byte[] { 4, 1, 2, 3, 5 }, builder2.ToArray()); }
/// <summary> /// Writes information about metadata references to the pdb so the same /// reference can be found on sourcelink to create the compilation again /// </summary> private void EmbedMetadataReferenceInformation(CommonPEModuleBuilder module) { var builder = new BlobBuilder(); // Order of information // File name (null terminated string): A.exe // Extern Alias (null terminated string): a1,a2,a3 // MetadataImageKind (byte) // EmbedInteropTypes (boolean) // COFF header Timestamp field (4 byte int) // COFF header SizeOfImage field (4 byte int) // MVID (Guid, 24 bytes) foreach (var metadataReference in module.CommonCompilation.ExternalReferences) { if (metadataReference is PortableExecutableReference portableReference && portableReference.FilePath is object) { var fileName = PathUtilities.GetFileName(portableReference.FilePath); var reference = module.CommonCompilation.GetAssemblyOrModuleSymbol(portableReference); var peReader = GetReader(reference); // Don't write before checking that we can get a peReader for the metadata reference if (peReader is null) { continue; } // Write file name first builder.WriteUTF8(fileName); // Make sure to add null terminator builder.WriteByte(0); // Extern alias if (portableReference.Properties.Aliases.Any()) { builder.WriteUTF8(string.Join(",", portableReference.Properties.Aliases)); } // Always null terminate the extern alias list builder.WriteByte(0); byte kindAndEmbedInteropTypes = (byte)(portableReference.Properties.EmbedInteropTypes ? 0b10 : 0b0); kindAndEmbedInteropTypes |= portableReference.Properties.Kind switch { MetadataImageKind.Assembly => 1, MetadataImageKind.Module => 0, _ => throw ExceptionUtilities.UnexpectedValue(portableReference.Properties.Kind) }; builder.WriteByte(kindAndEmbedInteropTypes); builder.WriteInt32(peReader.PEHeaders.CoffHeader.TimeDateStamp); builder.WriteInt32(peReader.PEHeaders.PEHeader.SizeOfImage); var metadataReader = peReader.GetMetadataReader(); var moduleDefinition = metadataReader.GetModuleDefinition(); builder.WriteGuid(metadataReader.GetGuid(moduleDefinition.Mvid)); } } _debugMetadataOpt.AddCustomDebugInformation( parent: EntityHandle.ModuleDefinition, kind: _debugMetadataOpt.GetOrAddGuid(PortableCustomDebugInfoKinds.CompilationMetadataReferences), value: _debugMetadataOpt.GetOrAddBlob(builder));
public void WriteUTF8() { var writer = new BlobBuilder(4); writer.WriteUTF8("a"); writer.WriteUTF8(""); writer.WriteUTF8("bc"); writer.WriteUTF8("d"); writer.WriteUTF8(""); writer.WriteUTF8(Encoding.UTF8.GetString(new byte[] { 0x00, 0xC2, 0x80, 0xE1, 0x88, 0xB4 })); writer.WriteUTF8("\0\ud800"); // hi surrogate writer.WriteUTF8("\0\udc00"); // lo surrogate writer.WriteUTF8("\0\ud800\udc00"); // pair writer.WriteUTF8("\0\udc00\ud800"); // lo + hi AssertEx.Equal(new byte[] { (byte)'a', (byte)'b', (byte)'c', (byte)'d', 0x00, 0xC2, 0x80, 0xE1, 0x88, 0xB4, 0x00, 0xED, 0xA0, 0x80, 0x00, 0xED, 0xB0, 0x80, 0x00, 0xF0, 0x90, 0x80, 0x80, 0x00, 0xED, 0xB0, 0x80, 0xED, 0xA0, 0x80 }, writer.ToArray()); }
/// <summary> /// Write string as UTF8 with null terminator. /// </summary> private static void WriteUtf8String(BlobBuilder cmw, string str) { cmw.WriteUTF8(str); cmw.WriteByte(0); }