| File: Compiler\ObjectWriter\CodeView\CodeViewTypesBuilder.cs | Web Access |
| Project: src\runtime\src\coreclr\tools\aot\ILCompiler.Compiler\ILCompiler.Compiler.csproj (ILCompiler.Compiler) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.Buffers; using System.Buffers.Binary; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Text; using ILCompiler.DependencyAnalysis; using Internal.JitInterface; using Internal.Text; using Internal.TypeSystem; using Internal.TypeSystem.TypesDebugInfo; using static ILCompiler.ObjectWriter.CodeViewNative; using static ILCompiler.ObjectWriter.CodeViewNative.CodeViewPointer; using static ILCompiler.ObjectWriter.CodeViewNative.CodeViewPropertyFlags; using static ILCompiler.ObjectWriter.CodeViewNative.CodeViewType; using static ILCompiler.ObjectWriter.CodeViewNative.LeafRecordType; namespace ILCompiler.ObjectWriter { /// <summary>Builder for the CodeView .debug$T section.<summary> /// <remarks> /// The .debug$T section in CodeView contains type (enum, struct, class) /// descriptions. The section is composed of records that are prefixed /// with their type and length. Each record is assigned a type index /// representing its position in the stream. The type indexes start at /// 0x1000 and increase by one for each record. Elementary types, such /// as uint, short, or float, have preassigned indexes below 0x1000, /// represented by the <see cref="CodeViewType" /> enumeration. /// /// The maximum record size is limited to sizeof(ushort) due to the /// layout of the record header. List records (eg. field list) can /// be split into multiple records that are chained together. /// </remarks> internal sealed class CodeViewTypesBuilder : ITypesDebugInfoWriter { private readonly NameMangler _nameMangler; private readonly SectionWriter _sectionWriter; private readonly int _targetPointerSize; private readonly uint _classVTableTypeIndex; private readonly uint _vfuncTabTypeIndex; private readonly List<(Utf8String, uint)> _userDefinedTypes = new(); private uint _nextTypeIndex = 0x1000; public IList<(Utf8String, uint)> UserDefinedTypes => _userDefinedTypes; public CodeViewTypesBuilder(NameMangler nameMangler, int targetPointerSize, SectionWriter sectionWriter) { _nameMangler = nameMangler; _sectionWriter = sectionWriter; _targetPointerSize = targetPointerSize; // Write CodeView version header Span<byte> versionBuffer = stackalloc byte[sizeof(uint)]; BinaryPrimitives.WriteUInt32LittleEndian(versionBuffer, 4); _sectionWriter.Write(versionBuffer); // We pretend that the MethodTable pointer in System.Object is VTable shape. // We use the same "Vtable" for all types because the vtable shape debug // record is not expressive enough to capture our vtable shape (where the // vtable slots don't start at the beginning of the vtable). using (LeafRecordWriter record = StartLeafRecord(LF_VTSHAPE)) { record.Write((ushort)0); // Number of entries in vfunctable } _classVTableTypeIndex = _nextTypeIndex++; using (LeafRecordWriter record = StartLeafRecord(LF_POINTER)) { record.Write(_classVTableTypeIndex); record.Write((uint)((targetPointerSize == 8 ? CV_PTR_64 : CV_PTR_NEAR32) | CV_PTR_MODE_LVREF)); } _vfuncTabTypeIndex = _nextTypeIndex++; } public uint GetPrimitiveTypeIndex(TypeDesc type) { Debug.Assert(type.IsPrimitive, "it is not a primitive type"); return GetPrimitiveTypeIndex(type.Category); } private uint GetPrimitiveTypeIndex(TypeFlags typeFlags) { // CodeView uses predefined codes for simple types return (uint)(typeFlags switch { TypeFlags.Boolean => T_BOOL08, TypeFlags.Char => T_WCHAR, TypeFlags.SByte => T_INT1, TypeFlags.Byte => T_UINT1, TypeFlags.Int16 => T_INT2, TypeFlags.UInt16 => T_UINT2, TypeFlags.Int32 => T_INT4, TypeFlags.UInt32 => T_UINT4, TypeFlags.Int64 => T_INT8, TypeFlags.UInt64 => T_UINT8, TypeFlags.IntPtr => _targetPointerSize == 8 ? T_INT8 : T_INT4, TypeFlags.UIntPtr => _targetPointerSize == 8 ? T_UINT8 : T_UINT4, TypeFlags.Single => T_REAL32, TypeFlags.Double => T_REAL64, _ => T_NOTYPE, }); } public uint GetPointerTypeIndex(PointerTypeDescriptor pointerDescriptor) { using (LeafRecordWriter record = StartLeafRecord(LF_POINTER)) { record.Write(pointerDescriptor.ElementType); record.Write((uint)( (pointerDescriptor.Is64Bit == 1 ? CV_PTR_64 : CV_PTR_NEAR32) | (pointerDescriptor.IsReference == 1 ? CV_PTR_MODE_LVREF : CV_PTR_MODE_PTR) | (pointerDescriptor.IsConst == 1 ? CV_PTR_IS_CONST : 0))); } return _nextTypeIndex++; } public uint GetArrayTypeIndex( ClassTypeDescriptor classDescriptor, ArrayTypeDescriptor arrayDescriptor) { uint memberCount = 0; uint offset = 0; using (LeafRecordWriter arrayRecord = StartLeafRecord(LF_ARRAY)) { arrayRecord.Write(arrayDescriptor.ElementType); arrayRecord.Write(T_INT4); arrayRecord.Write(arrayDescriptor.Size); arrayRecord.Write(""); } uint arrayRecordTypeIndex = _nextTypeIndex++; using (LeafRecordWriter fieldListRecord = StartLeafRecord(LF_FIELDLIST)) { if (classDescriptor.BaseClassId != 0) { fieldListRecord.StartListEntry(LF_BCLASS); fieldListRecord.Write((ushort)0); // Attributes fieldListRecord.Write(classDescriptor.BaseClassId); fieldListRecord.WriteEncodedInteger(0); // Offset fieldListRecord.EndListEntry(); memberCount++; offset += (uint)_targetPointerSize; } fieldListRecord.StartListEntry(LF_MEMBER); fieldListRecord.Write((ushort)CodeViewMemberAccess.CV_public); fieldListRecord.Write(T_INT4); fieldListRecord.WriteEncodedInteger(offset); fieldListRecord.Write("count"); fieldListRecord.EndListEntry(); memberCount++; offset += (uint)_targetPointerSize; if (arrayDescriptor.IsMultiDimensional == 1) { for (uint i = 0; i < arrayDescriptor.Rank; ++i) { fieldListRecord.StartListEntry(LF_MEMBER); fieldListRecord.Write((ushort)CodeViewMemberAccess.CV_public); fieldListRecord.Write(T_INT4); fieldListRecord.WriteEncodedInteger(offset); fieldListRecord.Write($"length{i}"); fieldListRecord.EndListEntry(); memberCount++; offset += 4; } for (uint i = 0; i < arrayDescriptor.Rank; ++i) { fieldListRecord.StartListEntry(LF_MEMBER); fieldListRecord.Write((ushort)CodeViewMemberAccess.CV_public); fieldListRecord.Write(T_INT4); fieldListRecord.WriteEncodedInteger(offset); fieldListRecord.Write($"bounds{i}"); fieldListRecord.EndListEntry(); memberCount++; offset += 4; } } fieldListRecord.StartListEntry(LF_MEMBER); fieldListRecord.Write((ushort)CodeViewMemberAccess.CV_public); fieldListRecord.Write(arrayRecordTypeIndex); fieldListRecord.WriteEncodedInteger(offset); fieldListRecord.Write("values"); fieldListRecord.EndListEntry(); memberCount++; } uint fieldListTypeIndex = _nextTypeIndex++; Debug.Assert(classDescriptor.IsStruct == 0); using (LeafRecordWriter record = StartLeafRecord(LF_CLASS)) { Debug.Assert(memberCount <= ushort.MaxValue); Debug.Assert(arrayDescriptor.Size <= ushort.MaxValue); record.Write((ushort)memberCount); // Number of elements in class record.Write((ushort)0); // Class options (CodeViewPropertyFlags) record.Write(fieldListTypeIndex); // Field descriptor index record.Write((uint)0); // Derived-from descriptor index record.Write((uint)0); // Vtshape descriptor index record.Write((ushort)arrayDescriptor.Size); // Size record.Write(classDescriptor.Name); } uint typeIndex = _nextTypeIndex++; _userDefinedTypes.Add((classDescriptor.Name, typeIndex)); return typeIndex; } public uint GetEnumTypeIndex( EnumTypeDescriptor typeDescriptor, EnumRecordTypeDescriptor[] typeRecords) { using (LeafRecordWriter fieldListRecord = StartLeafRecord(LF_FIELDLIST)) { foreach (EnumRecordTypeDescriptor record in typeRecords) { fieldListRecord.StartListEntry(LF_ENUMERATE); fieldListRecord.Write((ushort)CodeViewMemberAccess.CV_public); fieldListRecord.WriteEncodedInteger(record.Value); fieldListRecord.Write(record.Name); fieldListRecord.EndListEntry(); } } uint fieldListTypeIndex = _nextTypeIndex++; using (LeafRecordWriter record = StartLeafRecord(LF_ENUM)) { Debug.Assert(typeRecords.Length <= ushort.MaxValue); record.Write((ushort)typeRecords.Length); // Number of elements in class record.Write((ushort)0); // Class options (CodeViewPropertyFlags) record.Write(typeDescriptor.ElementType); record.Write(fieldListTypeIndex); record.Write(typeDescriptor.Name); } uint typeIndex = _nextTypeIndex++; _userDefinedTypes.Add((typeDescriptor.Name, typeIndex)); return typeIndex; } public uint GetClassTypeIndex(ClassTypeDescriptor classDescriptor) { using (LeafRecordWriter record = StartLeafRecord(classDescriptor.IsStruct == 1 ? LF_STRUCTURE : LF_CLASS)) { record.Write((ushort)0); // Number of elements in class record.Write((ushort)CV_PROP_FORWARD_REFERENCE); // Class options (CodeViewPropertyFlags) record.Write((uint)0); // Field descriptor index record.Write((uint)0); // Derived-from descriptor index record.Write((uint)0); // Vtshape descriptor index record.Write((ushort)0); // Size record.Write(classDescriptor.Name); } return _nextTypeIndex++; } public uint GetCompleteClassTypeIndex( ClassTypeDescriptor classTypeDescriptor, ClassFieldsTypeDescriptor classFieldsTypeDescriptor, DataFieldDescriptor[] fields, StaticDataFieldDescriptor[] statics) { uint memberCount = 0; using (LeafRecordWriter fieldListRecord = StartLeafRecord(LF_FIELDLIST)) { if (classTypeDescriptor.BaseClassId != 0) { fieldListRecord.StartListEntry(LF_BCLASS); fieldListRecord.Write((ushort)0); // Attributes fieldListRecord.Write(classTypeDescriptor.BaseClassId); fieldListRecord.WriteEncodedInteger(0); // Offset fieldListRecord.EndListEntry(); memberCount++; } else if (classTypeDescriptor.IsStruct == 0) { fieldListRecord.StartListEntry(LF_VFUNCTAB); fieldListRecord.Write((ushort)0); // Padding fieldListRecord.Write(_vfuncTabTypeIndex); fieldListRecord.EndListEntry(); memberCount++; } foreach (DataFieldDescriptor desc in fields) { if (desc.Offset == 0xFFFFFFFF) { fieldListRecord.StartListEntry(LF_STMEMBER); fieldListRecord.Write((ushort)CodeViewMemberAccess.CV_public); fieldListRecord.Write(desc.FieldTypeIndex); fieldListRecord.Write(desc.Name); fieldListRecord.EndListEntry(); } else { fieldListRecord.StartListEntry(LF_MEMBER); fieldListRecord.Write((ushort)CodeViewMemberAccess.CV_public); fieldListRecord.Write(desc.FieldTypeIndex); fieldListRecord.WriteEncodedInteger(desc.Offset); fieldListRecord.Write(desc.Name); fieldListRecord.EndListEntry(); } memberCount++; } } uint fieldListTypeIndex = _nextTypeIndex++; using (LeafRecordWriter record = StartLeafRecord(classTypeDescriptor.IsStruct == 1 ? LF_STRUCTURE : LF_CLASS)) { Debug.Assert(memberCount <= ushort.MaxValue); record.Write((ushort)memberCount); // Number of elements in class record.Write((ushort)0); // Class options (CodeViewPropertyFlags) record.Write(fieldListTypeIndex); // Field descriptor index record.Write((uint)0); // Derived-from descriptor index record.Write((uint)0); // Vtshape descriptor index record.WriteEncodedInteger(classFieldsTypeDescriptor.Size); // Size record.Write(classTypeDescriptor.Name); } uint typeIndex = _nextTypeIndex++; _userDefinedTypes.Add((classTypeDescriptor.Name, typeIndex)); return typeIndex; } public uint GetMemberFunctionTypeIndex(MemberFunctionTypeDescriptor memberDescriptor, uint[] argumentTypes) { using (LeafRecordWriter fieldListRecord = StartLeafRecord(LF_ARGLIST)) { fieldListRecord.Write((uint)argumentTypes.Length); foreach (uint argumentType in argumentTypes) { fieldListRecord.Write(argumentType); } } uint argumentListTypeIndex = _nextTypeIndex++; using (LeafRecordWriter record = StartLeafRecord(LF_MFUNCTION)) { Debug.Assert(memberDescriptor.NumberOfArguments <= ushort.MaxValue); record.Write(memberDescriptor.ReturnType); record.Write(memberDescriptor.ContainingClass); record.Write(memberDescriptor.TypeIndexOfThisPointer); record.Write((byte)memberDescriptor.CallingConvention); // TODO: Evaluate if we should mark constructors record.Write((byte)0); // Function options (CV_funcattr_t) record.Write((ushort)memberDescriptor.NumberOfArguments); record.Write(argumentListTypeIndex); record.Write((uint)memberDescriptor.ThisAdjust); } return _nextTypeIndex++; } public uint GetMemberFunctionId(MemberFunctionIdTypeDescriptor memberIdDescriptor) { using (LeafRecordWriter record = StartLeafRecord(LF_MFUNC_ID)) { record.Write(memberIdDescriptor.ParentClass); record.Write(memberIdDescriptor.MemberFunction); record.Write(memberIdDescriptor.Name); } return _nextTypeIndex++; } public Utf8String GetMangledName(TypeDesc type) { return _nameMangler.GetMangledTypeName(type); } private LeafRecordWriter StartLeafRecord(LeafRecordType leafRecordType) { return new LeafRecordWriter(this, leafRecordType); } private ref struct LeafRecordWriter { private CodeViewTypesBuilder _debugTypesBuilder; private LeafRecordType _leafRecordType; private ArrayBufferWriter<byte> _bufferWriter; private int _lastListMemberStart; public LeafRecordWriter(CodeViewTypesBuilder debugTypesBuilder, LeafRecordType leafRecordType) { _debugTypesBuilder = debugTypesBuilder; _leafRecordType = leafRecordType; _bufferWriter = new(); Write((ushort)_leafRecordType); } public void Dispose() { int length = sizeof(ushort) + _bufferWriter.WrittenCount; int padding = ((length + 3) & ~3) - length; Debug.Assert(length <= ushort.MaxValue); Span<byte> lengthBuffer = stackalloc byte[sizeof(ushort)]; BinaryPrimitives.WriteUInt16LittleEndian(lengthBuffer, (ushort)(length + padding - sizeof(ushort))); _debugTypesBuilder._sectionWriter.Write(lengthBuffer); _debugTypesBuilder._sectionWriter.Write(_bufferWriter.WrittenSpan); _debugTypesBuilder._sectionWriter.WritePadding(padding); _bufferWriter.Clear(); } public void Write(byte value) { _bufferWriter.GetSpan(1)[0] = value; _bufferWriter.Advance(1); } public void Write(ushort value) { BinaryPrimitives.WriteUInt16LittleEndian(_bufferWriter.GetSpan(sizeof(ushort)), value); _bufferWriter.Advance(sizeof(ushort)); } public void Write(uint value) { BinaryPrimitives.WriteUInt32LittleEndian(_bufferWriter.GetSpan(sizeof(uint)), value); _bufferWriter.Advance(sizeof(uint)); } public void Write(ulong value) { BinaryPrimitives.WriteUInt64LittleEndian(_bufferWriter.GetSpan(sizeof(ulong)), value); _bufferWriter.Advance(sizeof(ulong)); } public void Write(Utf8String value) { int byteCount = value.Length + 1; value.AsSpan().CopyTo(_bufferWriter.GetSpan(byteCount)); _bufferWriter.Advance(byteCount); } public void Write(string value) { int byteCount = Encoding.UTF8.GetByteCount(value) + 1; Encoding.UTF8.GetBytes(value, _bufferWriter.GetSpan(byteCount)); _bufferWriter.Advance(byteCount); } public void WriteEncodedInteger(ulong value) { if (value < (ushort)LF_NUMERIC) { Write((ushort)value); } else if (value <= ushort.MaxValue) { Write((ushort)LF_USHORT); Write((ushort)value); } else if (value <= uint.MaxValue) { Write((ushort)LF_ULONG); Write((uint)value); } else { Write((ushort)LF_UQUADWORD); Write(value); } } public void Write(CodeViewType value) => Write((uint)value); private void WritePadding() { int paddingLength = ((_bufferWriter.WrittenCount - 2 + 3) & ~3) - (_bufferWriter.WrittenCount - 2); Span<byte> padding = _bufferWriter.GetSpan(paddingLength); for (int i = 0; i < paddingLength; i++) { padding[i] = (byte)(LF_PAD0 + paddingLength - i); } _bufferWriter.Advance(paddingLength); } public void StartListEntry(LeafRecordType recordType) { Debug.Assert(_leafRecordType == LF_FIELDLIST || _leafRecordType == LF_METHODLIST); _lastListMemberStart = _bufferWriter.WrittenCount; Write((ushort)recordType); } public void EndListEntry() { Debug.Assert(_leafRecordType == LF_FIELDLIST || _leafRecordType == LF_METHODLIST); Debug.Assert(_lastListMemberStart > 0); WritePadding(); // If the current list record overflows the maximum list length then emit a // LF_FIELDLIST/LF_METHODLIST leaf record now and start a new one. The new one // has LF_INDEX as the first element to chain the lists together. if (_bufferWriter.WrittenCount > short.MaxValue - sizeof(ushort)) { // At least one record was already written in the current list. Debug.Assert(_lastListMemberStart > sizeof(ushort)); // Flush the current record up to _lastListMemberStart and write LF_INDEX to reference it. int length = sizeof(ushort) + _lastListMemberStart; int padding = ((length + 3) & ~3) - length; _debugTypesBuilder._sectionWriter.WriteLittleEndian<ushort>((ushort)(length + padding - sizeof(ushort))); _debugTypesBuilder._sectionWriter.Write(_bufferWriter.WrittenSpan.Slice(0, _lastListMemberStart)); _debugTypesBuilder._sectionWriter.WritePadding(padding); byte[] overflow = _bufferWriter.WrittenSpan.Slice(_lastListMemberStart).ToArray(); _bufferWriter.Clear(); Write((ushort)_leafRecordType); Write((ushort)LF_INDEX); Write((ushort)0); // Padding Write((uint)_debugTypesBuilder._nextTypeIndex++); overflow.CopyTo(_bufferWriter.GetSpan(overflow.Length)); _bufferWriter.Advance(overflow.Length); _lastListMemberStart = _bufferWriter.WrittenCount; } } } } }