/// <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);
 }
Пример #4
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();
        }
Пример #5
0
        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());
        }
Пример #6
0
        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));
        }
Пример #7
0
        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));
Пример #9
0
        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());
        }
Пример #10
0
 /// <summary>
 /// Write string as UTF8 with null terminator.
 /// </summary>
 private static void WriteUtf8String(BlobBuilder cmw, string str)
 {
     cmw.WriteUTF8(str);
     cmw.WriteByte(0);
 }