private unsafe static void ConvertConstantSignature(BlobBuilder builder, MetadataModel metadataModel, byte[] signature, object value) { fixed(byte *sigPtr = signature) { var sigReader = new BlobReader(sigPtr, signature.Length); // copy custom modifiers over: byte rawTypeCode; while (true) { rawTypeCode = sigReader.ReadByte(); if (rawTypeCode != (int)SignatureTypeCode.OptionalModifier && rawTypeCode != (int)SignatureTypeCode.RequiredModifier) { break; } builder.WriteByte(rawTypeCode); builder.WriteCompressedInteger(sigReader.ReadCompressedInteger()); } switch ((SignatureTypeCode)rawTypeCode) { case (SignatureTypeCode)SignatureTypeKind.Class: case (SignatureTypeCode)SignatureTypeKind.ValueType: int typeRefDefSpec = sigReader.ReadCompressedInteger(); if (value is decimal) { // GeneralConstant: VALUETYPE TypeDefOrRefOrSpecEncoded <decimal> builder.WriteByte((byte)SignatureTypeKind.ValueType); builder.WriteCompressedInteger(typeRefDefSpec); builder.WriteDecimal((decimal)value); } else if (value is double d) { // GeneralConstant: VALUETYPE TypeDefOrRefOrSpecEncoded <date-time> builder.WriteByte((byte)SignatureTypeKind.ValueType); builder.WriteCompressedInteger(typeRefDefSpec); builder.WriteDateTime(new DateTime(BitConverter.DoubleToInt64Bits(d))); } else if (value is 0 && rawTypeCode == (byte)SignatureTypeKind.Class) { // GeneralConstant: CLASS TypeDefOrRefOrSpecEncoded builder.WriteByte(rawTypeCode); builder.WriteCompressedInteger(typeRefDefSpec); } else { // EnumConstant ::= EnumTypeCode EnumValue EnumType // EnumTypeCode ::= BOOLEAN | CHAR | I1 | U1 | I2 | U2 | I4 | U4 | I8 | U8 // EnumType ::= TypeDefOrRefOrSpecEncoded var enumTypeCode = AssemblyDisplayNameBuilder.GetConstantTypeCode(value); builder.WriteByte((byte)enumTypeCode); builder.WriteConstant(value); builder.WriteCompressedInteger(typeRefDefSpec); } break;
private static BlobHandle SerializeModuleImportScope( MetadataBuilder metadataBuilder, IEnumerable <ImportInfo> csExternAliasImports, IEnumerable <ImportInfo> vbProjectLevelImports, string vbDefaultNamespace, MetadataModel metadataModel) { // module-level import scope: var builder = new BlobBuilder(); var encoder = new ImportDefinitionEncoder(metadataBuilder, builder); if (vbDefaultNamespace != null) { SerializeModuleDefaultNamespace(metadataBuilder, vbDefaultNamespace); } foreach (var import in csExternAliasImports) { SerializeImport(encoder, import, metadataModel); } foreach (var import in vbProjectLevelImports) { SerializeImport(encoder, import, metadataModel); } return(metadataBuilder.GetOrAddBlob(builder)); }
private unsafe static BlobHandle SerializeConstantSignature(MetadataBuilder metadataBuilder, MetadataModel metadataModel, byte[] signature, object value) { var builder = new BlobBuilder(); ConvertConstantSignature(builder, metadataModel, signature, value); return(metadataBuilder.GetOrAddBlob(builder)); }
private static void SerializeScope( MetadataBuilder metadataBuilder, MetadataModel metadataModel, MethodDefinitionHandle methodHandle, ImportScopeHandle importScopeHandle, ISymUnmanagedScope symScope, Dictionary <int, DynamicLocalInfo> dynamicSlots, Dictionary <string, DynamicLocalInfo> dynamicNames, bool vbSemantics, ref LocalVariableHandle lastLocalVariableHandle, ref LocalConstantHandle lastLocalConstantHandle) { // VB Windows PDB encode the range as end-inclusive, // all Portable PDBs use end-exclusive encoding. int start = symScope.GetStartOffset(); int end = symScope.GetEndOffset() + (vbSemantics ? 1 : 0); metadataBuilder.AddLocalScope( method: methodHandle, importScope: importScopeHandle, variableList: NextHandle(lastLocalVariableHandle), constantList: NextHandle(lastLocalConstantHandle), startOffset: start, length: end - start); foreach (var symLocal in symScope.GetLocals()) { int slot = symLocal.GetSlot(); string name = symLocal.GetName(); lastLocalVariableHandle = metadataBuilder.AddLocalVariable( attributes: (LocalVariableAttributes)symLocal.GetAttributes(), index: slot, name: metadataBuilder.GetOrAddString(name)); DynamicLocalInfo dynamicInfo; if (slot > 0 && dynamicSlots.TryGetValue(slot, out dynamicInfo) || slot == 0 && dynamicNames.TryGetValue(name, out dynamicInfo)) { metadataBuilder.AddCustomDebugInformation( parent: lastLocalVariableHandle, kind: metadataBuilder.GetOrAddGuid(PortableCustomDebugInfoKinds.DynamicLocalVariables), value: SerializeDynamicLocalBlob(metadataBuilder, dynamicInfo)); } } foreach (var symConstant in symScope.GetConstants()) { string name = symConstant.GetName(); object value = symConstant.GetValue(); lastLocalConstantHandle = metadataBuilder.AddLocalConstant( name: metadataBuilder.GetOrAddString(name), signature: SerializeConstantSignature(metadataBuilder, metadataModel, symConstant.GetSignature(), value)); DynamicLocalInfo dynamicInfo; if (dynamicNames.TryGetValue(name, out dynamicInfo)) { metadataBuilder.AddCustomDebugInformation( parent: lastLocalConstantHandle, kind: metadataBuilder.GetOrAddGuid(PortableCustomDebugInfoKinds.DynamicLocalVariables), value: SerializeDynamicLocalBlob(metadataBuilder, dynamicInfo)); } } int previousChildScopeEnd = start; foreach (ISymUnmanagedScope child in symScope.GetChildren()) { int childScopeStart = child.GetStartOffset(); int childScopeEnd = child.GetEndOffset(); // scopes are properly nested: if (childScopeStart < previousChildScopeEnd || childScopeEnd > end) { // TODO: loc/warning throw new BadImageFormatException($"Invalid scope IL offset range: [{childScopeStart}, {childScopeEnd}), method 0x{MetadataTokens.GetToken(methodHandle):x}."); } previousChildScopeEnd = childScopeEnd; SerializeScope(metadataBuilder, metadataModel, methodHandle, importScopeHandle, child, dynamicSlots, dynamicNames, vbSemantics, ref lastLocalVariableHandle, ref lastLocalConstantHandle); } }
private static void SerializeImport(ImportDefinitionEncoder encoder, ImportInfo import, MetadataModel metadataModel) { var assemblyRef = default(AssemblyReferenceHandle); EntityHandle type; switch (import.Kind) { case ImportTargetKind.Assembly: // alias: assembly alias // target: assembly name for module-level extern alias definition, or null for file level extern alias import if (import.Target == null) { // TODO: skip if the alias isn't defined in an ancestor scope? encoder.ImportAssemblyReferenceAlias(import.Alias); break; } if (!metadataModel.TryResolveAssemblyReference(import.Target, out assemblyRef)) { // no type from the assembly is used, the AssemblyRef is not present in the metadata break; } encoder.AliasAssemblyReference(assemblyRef, import.Alias); break; case ImportTargetKind.Namespace: if (import.ExternAlias != null && !metadataModel.TryResolveAssemblyReference(import.ExternAlias, out assemblyRef)) { // no type from the assembly is used, the AssemblyRef is not present in the metadata break; } encoder.Namespace(import.Target, import.Alias, assemblyRef); break; case ImportTargetKind.Type: if (!metadataModel.TryResolveType(import.Target, out type)) { // the type is not used in the source, the metadata is missing a TypeRef. break; } encoder.Type(type, import.Alias); break; case ImportTargetKind.NamespaceOrType: if (metadataModel.TryResolveType(import.Target, out type)) { encoder.Type(type, import.Alias); } else { encoder.Namespace(import.Target, import.Alias); } break; case ImportTargetKind.XmlNamespace: encoder.XmlNamespace(import.Alias, import.Target); break; case ImportTargetKind.DefaultNamespace: // alraedy handled throw ExceptionUtilities.Unreachable; case ImportTargetKind.CurrentNamespace: case ImportTargetKind.MethodToken: case ImportTargetKind.Defunct: break; } }
private static BlobHandle SerializeImportsBlob(MetadataBuilder metadataBuilder, ImmutableArray <ImportInfo> imports, MetadataModel metadataModel) { var builder = new BlobBuilder(); var encoder = new ImportDefinitionEncoder(metadataBuilder, builder); foreach (var import in imports) { SerializeImport(encoder, import, metadataModel); } return(metadataBuilder.GetOrAddBlob(builder)); }
public static void Convert(Stream peStream, Stream sourcePdbStream, Stream targetPdbStream) { var metadataBuilder = new MetadataBuilder(); ImmutableArray <int> typeSystemRowCounts; var debugEntryPointToken = default(MethodDefinitionHandle); var pdbId = default(BlobContentId); try { using (var peReader = new PEReader(peStream)) { pdbId = ReadPdbId(peReader); var symReader = SymReaderFactory.CreateWindowsPdbReader(sourcePdbStream, peReader); var metadataReader = peReader.GetMetadataReader(); var metadataModel = new MetadataModel(metadataReader); typeSystemRowCounts = metadataModel.GetRowCounts(); debugEntryPointToken = ReadEntryPointHandle(symReader); // documents: var documentIndex = new Dictionary <string, DocumentHandle>(StringComparer.Ordinal); var documents = symReader.GetDocuments(); metadataBuilder.SetCapacity(TableIndex.Document, documents.Length); bool vbSemantics = false; foreach (var document in documents) { string name = document.GetName(); Guid language = document.GetLanguage(); // TODO: // won't work for IL-merged assmemblies vbSemantics |= language == SymReaderHelpers.VisualBasicLanguageGuid; var rid = metadataBuilder.AddDocument( name: metadataBuilder.GetOrAddDocumentName(name), hashAlgorithm: metadataBuilder.GetOrAddGuid(document.GetHashAlgorithm()), hash: metadataBuilder.GetOrAddBlob(document.GetChecksum()), language: metadataBuilder.GetOrAddGuid(language)); documentIndex.Add(name, rid); } var lastLocalVariableHandle = default(LocalVariableHandle); var lastLocalConstantHandle = default(LocalConstantHandle); var importStringsByMethod = new Dictionary <int, ImmutableArray <string> >(); var importScopesByMethod = new Dictionary <int, ImportScopeHandle>(); // Maps import scope content to import scope handles var importScopeIndex = new Dictionary <ImportScopeInfo, ImportScopeHandle>(); var importScopes = new List <ImportScopeInfo>(); // reserve slot for module import scope: importScopes.Add(default(ImportScopeInfo)); var externAliasImports = new List <ImportInfo>(); var externAliasStringSet = new HashSet <string>(StringComparer.Ordinal); string vbDefaultNamespace = null; var vbProjectLevelImports = new List <ImportInfo>(); // first pass: foreach (var methodHandle in metadataReader.MethodDefinitions) { int methodToken = MetadataTokens.GetToken(methodHandle); ImmutableArray <ImmutableArray <ImportInfo> > importGroups; if (vbSemantics) { var importStrings = CustomDebugInfoReader.GetVisualBasicImportStrings( methodToken, symReader, getMethodImportStrings: (token, sr) => GetImportStrings(token, importStringsByMethod, sr)); if (importStrings.IsEmpty) { // no debug info continue; } var vbFileLevelImports = ArrayBuilder <ImportInfo> .GetInstance(); foreach (var importString in importStrings) { if (TryParseImportString(importString, out var import, vbSemantics: true)) { if (import.Kind == ImportTargetKind.DefaultNamespace) { vbDefaultNamespace = import.Target; } else if (import.Scope == VBImportScopeKind.Project) { vbProjectLevelImports.Add(import); } else { vbFileLevelImports.Add(import); } } } importGroups = ImmutableArray.Create(vbFileLevelImports.ToImmutableAndFree()); } else { var importStringGroups = CustomDebugInfoReader.GetCSharpGroupedImportStrings( methodToken, symReader, getMethodCustomDebugInfo: (token, sr) => sr.GetCustomDebugInfo(token, methodVersion: 1), getMethodImportStrings: (token, sr) => GetImportStrings(token, importStringsByMethod, sr), externAliasStrings: out var localExternAliasStrings); if (importStringGroups.IsDefault) { // no debug info continue; } if (!localExternAliasStrings.IsDefault) { foreach (var externAlias in localExternAliasStrings) { if (externAliasStringSet.Add(externAlias) && TryParseImportString(externAlias, out var import, vbSemantics: false)) { externAliasImports.Add(import); } } } importGroups = ImmutableArray.CreateRange(importStringGroups.Select(g => ParseImportStrings(g, vbSemantics: false))); } var importScopeHandle = DefineImportScope(importGroups, importScopeIndex, importScopes); importScopesByMethod.Add(methodToken, importScopeHandle); } // import scopes: metadataBuilder.AddImportScope( parentScope: default(ImportScopeHandle), imports: SerializeModuleImportScope(metadataBuilder, externAliasImports, vbProjectLevelImports, vbDefaultNamespace, metadataModel)); for (int i = 1; i < importScopes.Count; i++) { metadataBuilder.AddImportScope( parentScope: importScopes[i].Parent, imports: SerializeImportsBlob(metadataBuilder, importScopes[i].Imports, metadataModel)); } var dynamicNames = new Dictionary <string, DynamicLocalInfo>(); var dynamicSlots = new Dictionary <int, DynamicLocalInfo>(); // methods: metadataBuilder.SetCapacity(TableIndex.MethodDebugInformation, metadataReader.MethodDefinitions.Count); foreach (var methodHandle in metadataReader.MethodDefinitions) { var methodDef = metadataReader.GetMethodDefinition(methodHandle); int methodToken = MetadataTokens.GetToken(methodHandle); var symMethod = symReader.GetMethod(methodToken); if (symMethod == null) { metadataBuilder.AddMethodDebugInformation(default(DocumentHandle), sequencePoints: default(BlobHandle)); continue; } // method debug info: int localSignatureRowId; if (methodDef.RelativeVirtualAddress != 0) { var methodBody = peReader.GetMethodBody(methodDef.RelativeVirtualAddress); localSignatureRowId = methodBody.LocalSignature.IsNil ? 0 : MetadataTokens.GetRowNumber(methodBody.LocalSignature); } else { localSignatureRowId = 0; } var symSequencePoints = symMethod.GetSequencePoints().ToImmutableArray(); DocumentHandle singleDocumentHandle; BlobHandle sequencePointsBlob = SerializeSequencePoints(metadataBuilder, localSignatureRowId, symSequencePoints, documentIndex, out singleDocumentHandle); metadataBuilder.AddMethodDebugInformation( document: singleDocumentHandle, sequencePoints: sequencePointsBlob); // state machine and async info: var symAsyncMethod = symMethod.AsAsyncMethod(); if (symAsyncMethod != null) { var kickoffToken = MetadataTokens.Handle(symAsyncMethod.GetKickoffMethod()); metadataBuilder.AddStateMachineMethod( moveNextMethod: methodHandle, kickoffMethod: (MethodDefinitionHandle)kickoffToken); metadataBuilder.AddCustomDebugInformation( parent: methodHandle, kind: metadataBuilder.GetOrAddGuid(PortableCustomDebugInfoKinds.AsyncMethodSteppingInformationBlob), value: SerializeAsyncMethodSteppingInfo(metadataBuilder, symAsyncMethod, MetadataTokens.GetRowNumber(methodHandle))); } // custom debug information: var dynamicLocals = default(ImmutableArray <DynamicLocalInfo>); byte[] customDebugInfoBytes = symReader.GetCustomDebugInfo(methodToken, methodVersion: 1); if (customDebugInfoBytes != null) { foreach (var record in CustomDebugInfoReader.GetCustomDebugInfoRecords(customDebugInfoBytes)) { switch (record.Kind) { case CustomDebugInfoKind.DynamicLocals: dynamicLocals = CustomDebugInfoReader.DecodeDynamicLocalsRecord(record.Data); break; case CustomDebugInfoKind.StateMachineHoistedLocalScopes: metadataBuilder.AddCustomDebugInformation( parent: methodHandle, kind: metadataBuilder.GetOrAddGuid(PortableCustomDebugInfoKinds.EncLocalSlotMap), value: SerializeStateMachineHoistedLocalsBlob(metadataBuilder, CustomDebugInfoReader.DecodeStateMachineHoistedLocalScopesRecord(record.Data))); break; case CustomDebugInfoKind.EditAndContinueLocalSlotMap: metadataBuilder.AddCustomDebugInformation( parent: methodHandle, kind: metadataBuilder.GetOrAddGuid(PortableCustomDebugInfoKinds.EncLocalSlotMap), value: metadataBuilder.GetOrAddBlob(record.Data)); break; case CustomDebugInfoKind.EditAndContinueLambdaMap: metadataBuilder.AddCustomDebugInformation( parent: methodHandle, kind: metadataBuilder.GetOrAddGuid(PortableCustomDebugInfoKinds.EncLambdaAndClosureMap), value: metadataBuilder.GetOrAddBlob(record.Data)); break; } } } var rootScope = symMethod.GetRootScope(); if (rootScope.GetNamespaces().Length == 0 || rootScope.GetLocals().Length == 0 || rootScope.GetConstants().Length == 0) { dynamicNames.Clear(); dynamicSlots.Clear(); foreach (var dynamicLocal in dynamicLocals) { if (dynamicLocal.SlotId == 0) { // All dynamic constants have slot id == 0, // but a variable can also have slot id == 0 if (!dynamicNames.ContainsKey(dynamicLocal.LocalName)) { dynamicNames.Add(dynamicLocal.LocalName, dynamicLocal); } else { // TODO: warning } } else if (!dynamicSlots.ContainsKey(dynamicLocal.SlotId)) { dynamicSlots.Add(dynamicLocal.SlotId, dynamicLocal); } else { // TODO: warning } } foreach (ISymUnmanagedScope scope in rootScope.GetChildren()) { SerializeScope( metadataBuilder, metadataModel, methodHandle, importScopesByMethod[methodToken], scope, dynamicSlots, dynamicNames, vbSemantics, ref lastLocalVariableHandle, ref lastLocalConstantHandle); } } else { // TODO: warning: // "Root scope must be empty (method 0x{0:x8})", MetadataTokens.GetToken(methodHandle)) } } } } catch (COMException e) { // TODO: loc throw new BadImageFormatException("Invalid PDB format: " + e.Message, e); } var serializer = new PortablePdbBuilder(metadataBuilder, typeSystemRowCounts, debugEntryPointToken, idProvider: _ => pdbId); BlobBuilder blobBuilder = new BlobBuilder(); serializer.Serialize(blobBuilder); blobBuilder.WriteContentTo(targetPdbStream); }
public unsafe static void Convert(BlobBuilder builder, MetadataModel metadataModel, byte[] signature, object value) { fixed(byte *sigPtr = signature) { var sigReader = new BlobReader(sigPtr, signature.Length); // copy custom modifiers over: byte rawTypeCode; while (true) { rawTypeCode = sigReader.ReadByte(); if (rawTypeCode != (int)SignatureTypeCode.OptionalModifier && rawTypeCode != (int)SignatureTypeCode.RequiredModifier) { break; } builder.WriteByte(rawTypeCode); builder.WriteCompressedInteger(sigReader.ReadCompressedInteger()); } switch ((SignatureTypeCode)rawTypeCode) { case (SignatureTypeCode)SignatureTypeKind.Class: case (SignatureTypeCode)SignatureTypeKind.ValueType: int typeRefDefSpec = sigReader.ReadCompressedInteger(); if (value is decimal) { // GeneralConstant: VALUETYPE TypeDefOrRefOrSpecEncoded <decimal> builder.WriteByte((byte)SignatureTypeKind.ValueType); builder.WriteCompressedInteger(typeRefDefSpec); builder.WriteDecimal((decimal)value); } else if (value is DateTime) { // GeneralConstant: VALUETYPE TypeDefOrRefOrSpecEncoded <date-time> builder.WriteByte((byte)SignatureTypeKind.ValueType); builder.WriteCompressedInteger(typeRefDefSpec); builder.WriteDateTime((DateTime)value); } else if (value == null) { // GeneralConstant: CLASS TypeDefOrRefOrSpecEncoded builder.WriteByte(rawTypeCode); builder.WriteCompressedInteger(typeRefDefSpec); } else { // EnumConstant ::= EnumTypeCode EnumValue EnumType // EnumTypeCode ::= BOOLEAN | CHAR | I1 | U1 | I2 | U2 | I4 | U4 | I8 | U8 // EnumType ::= TypeDefOrRefOrSpecEncoded var enumTypeCode = MetadataHelpers.GetConstantTypeCode(value); builder.WriteByte((byte)enumTypeCode); builder.WriteConstant(value); builder.WriteCompressedInteger(typeRefDefSpec); } break; case SignatureTypeCode.Object: // null: Debug.Assert(value == null); builder.WriteByte((byte)SignatureTypeCode.Object); break; case SignatureTypeCode.Boolean: case SignatureTypeCode.Char: case SignatureTypeCode.SByte: case SignatureTypeCode.Byte: case SignatureTypeCode.Int16: case SignatureTypeCode.UInt16: case SignatureTypeCode.Int32: case SignatureTypeCode.UInt32: case SignatureTypeCode.Int64: case SignatureTypeCode.UInt64: case SignatureTypeCode.Single: case SignatureTypeCode.Double: case SignatureTypeCode.String: // PrimitiveConstant builder.WriteByte(rawTypeCode); builder.WriteConstant(value); break; case SignatureTypeCode.SZArray: case SignatureTypeCode.Array: case SignatureTypeCode.GenericTypeInstance: Debug.Assert(value == null); // Find an existing TypeSpec in metadata. // If there isn't one we can't represent the constant type in the Portable PDB, use Object. // +1/-1 for the type code we already read. var spec = new byte[sigReader.RemainingBytes + 1]; Buffer.BlockCopy(signature, sigReader.Offset - 1, spec, 0, spec.Length); TypeSpecificationHandle typeSpec; if (metadataModel.TryResolveTypeSpecification(spec, out typeSpec)) { builder.WriteCompressedInteger(CodedIndex.TypeDefOrRefOrSpec(typeSpec)); } else { // TODO: warning - can't translate const type builder.WriteByte((byte)SignatureTypeCode.Object); } break; case SignatureTypeCode.GenericMethodParameter: case SignatureTypeCode.GenericTypeParameter: case SignatureTypeCode.FunctionPointer: case SignatureTypeCode.Pointer: // generic parameters, pointers are not valid types for constants: throw new BadImageFormatException(); } } }
internal void Convert(PEReader peReader, MetadataReader pdbReader, PdbWriter <TDocumentWriter> pdbWriter, PdbConversionOptions options) { if (!SymReaderHelpers.TryReadPdbId(peReader, out var pePdbId, out int peAge)) { throw new InvalidDataException(ConverterResources.SpecifiedPEFileHasNoAssociatedPdb); } if (!new BlobContentId(pdbReader.DebugMetadataHeader.Id).Equals(pePdbId)) { throw new InvalidDataException(ConverterResources.PdbNotMatchingDebugDirectory); } string vbDefaultNamespace = MetadataUtilities.GetVisualBasicDefaultNamespace(pdbReader); bool vbSemantics = vbDefaultNamespace != null; string vbDefaultNamespaceImportString = string.IsNullOrEmpty(vbDefaultNamespace) ? null : "*" + vbDefaultNamespace; var metadataReader = peReader.GetMetadataReader(); var metadataModel = new MetadataModel(metadataReader, vbSemantics); var documentWriters = new ArrayBuilder <TDocumentWriter>(pdbReader.Documents.Count); var documentNames = new ArrayBuilder <string>(pdbReader.Documents.Count); var symSequencePointBuilder = new SequencePointsBuilder(capacity: 64); var declaredExternAliases = new HashSet <string>(); var importStringsBuilder = new List <string>(); var importGroups = new List <int>(); var cdiBuilder = new BlobBuilder(); var dynamicLocals = new List <(string LocalName, byte[] Flags, int Count, int SlotIndex)>(); var tupleLocals = new List <(string LocalName, int SlotIndex, int ScopeStart, int ScopeEnd, ImmutableArray <string> Names)>(); var openScopeEndOffsets = new Stack <int>(); // state for calculating import string forwarding: var lastImportScopeHandle = default(ImportScopeHandle); var vbLastImportScopeNamespace = default(string); var lastImportScopeMethodDefHandle = default(MethodDefinitionHandle); var importStringsMap = new Dictionary <ImmutableArray <string>, MethodDefinitionHandle>(SequenceComparer <string> .Instance); var aliasedAssemblyRefs = GetAliasedAssemblyRefs(pdbReader); foreach (var documentHandle in pdbReader.Documents) { var document = pdbReader.GetDocument(documentHandle); var languageGuid = pdbReader.GetGuid(document.Language); var name = pdbReader.GetString(document.Name); documentNames.Add(name); documentWriters.Add(pdbWriter.DefineDocument( name: name, language: languageGuid, type: s_documentTypeText, vendor: GetLanguageVendorGuid(languageGuid), algorithmId: pdbReader.GetGuid(document.HashAlgorithm), checksum: pdbReader.GetBlobBytes(document.Hash))); } var localScopeEnumerator = pdbReader.LocalScopes.GetEnumerator(); LocalScope?currentLocalScope = NextLocalScope(); LocalScope?NextLocalScope() => localScopeEnumerator.MoveNext() ? pdbReader.GetLocalScope(localScopeEnumerator.Current) : default(LocalScope?); // Handle of the method that is gonna contain list of AssemblyRef aliases. // Other methods will forward to it. var methodDefHandleWithAssemblyRefAliases = default(MethodDefinitionHandle); foreach (var methodDebugInfoHandle in pdbReader.MethodDebugInformation) { var methodDebugInfo = pdbReader.GetMethodDebugInformation(methodDebugInfoHandle); var methodDefHandle = methodDebugInfoHandle.ToDefinitionHandle(); int methodToken = MetadataTokens.GetToken(methodDefHandle); var methodDef = metadataReader.GetMethodDefinition(methodDefHandle); #if DEBUG var declaringTypeDef = metadataReader.GetTypeDefinition(methodDef.GetDeclaringType()); var typeName = metadataReader.GetString(declaringTypeDef.Name); var methodName = metadataReader.GetString(methodDef.Name); #endif bool methodOpened = false; var methodBodyOpt = (methodDef.RelativeVirtualAddress != 0 && (methodDef.ImplAttributes & MethodImplAttributes.CodeTypeMask) == MethodImplAttributes.Managed) ? peReader.GetMethodBody(methodDef.RelativeVirtualAddress) : null; var vbCurrentMethodNamespace = vbSemantics ? GetMethodNamespace(metadataReader, methodDef) : null; var moveNextHandle = metadataModel.FindStateMachineMoveNextMethod(methodDefHandle, vbSemantics); bool isKickOffMethod = !moveNextHandle.IsNil; var forwardImportScopesToMethodDef = default(MethodDefinitionHandle); Debug.Assert(dynamicLocals.Count == 0); Debug.Assert(tupleLocals.Count == 0); Debug.Assert(openScopeEndOffsets.Count == 0); void LazyOpenMethod() { if (!methodOpened) { #if DEBUG Debug.WriteLine($"Open Method '{typeName}::{methodName}' {methodToken:X8}"); #endif pdbWriter.OpenMethod(methodToken); methodOpened = true; } } void CloseOpenScopes(int currentScopeStartOffset) { // close all open scopes that end before this scope starts: while (openScopeEndOffsets.Count > 0 && currentScopeStartOffset >= openScopeEndOffsets.Peek()) { int scopeEnd = openScopeEndOffsets.Pop(); Debug.WriteLine($"Close Scope [.., {scopeEnd})"); // Note that the root scope end is not end-inclusive in VB: pdbWriter.CloseScope(AdjustEndScopeOffset(scopeEnd, isEndInclusive: vbSemantics && openScopeEndOffsets.Count > 0)); } } bool isFirstMethodScope = true; while (currentLocalScope.HasValue && currentLocalScope.Value.Method == methodDefHandle) { // kickoff methods don't have any scopes emitted to Windows PDBs if (methodBodyOpt == null) { ReportDiagnostic(PdbDiagnosticId.MethodAssociatedWithLocalScopeHasNoBody, MetadataTokens.GetToken(localScopeEnumerator.Current)); } else if (!isKickOffMethod) { LazyOpenMethod(); var localScope = currentLocalScope.Value; CloseOpenScopes(localScope.StartOffset); Debug.WriteLine($"Open Scope [{localScope.StartOffset}, {localScope.EndOffset})"); pdbWriter.OpenScope(localScope.StartOffset); openScopeEndOffsets.Push(localScope.EndOffset); if (isFirstMethodScope) { if (lastImportScopeHandle == localScope.ImportScope && vbLastImportScopeNamespace == vbCurrentMethodNamespace) { // forward to a method that has the same imports: forwardImportScopesToMethodDef = lastImportScopeMethodDefHandle; } else { Debug.Assert(importStringsBuilder.Count == 0); Debug.Assert(declaredExternAliases.Count == 0); Debug.Assert(importGroups.Count == 0); AddImportStrings(importStringsBuilder, importGroups, declaredExternAliases, pdbReader, metadataModel, localScope.ImportScope, aliasedAssemblyRefs, vbDefaultNamespaceImportString, vbCurrentMethodNamespace, vbSemantics); var importStrings = importStringsBuilder.ToImmutableArray(); importStringsBuilder.Clear(); if (importStringsMap.TryGetValue(importStrings, out forwardImportScopesToMethodDef)) { // forward to a method that has the same imports: lastImportScopeMethodDefHandle = forwardImportScopesToMethodDef; } else { // attach import strings to the current method: WriteImports(pdbWriter, importStrings); lastImportScopeMethodDefHandle = methodDefHandle; importStringsMap[importStrings] = methodDefHandle; } lastImportScopeHandle = localScope.ImportScope; vbLastImportScopeNamespace = vbCurrentMethodNamespace; } if (vbSemantics && !forwardImportScopesToMethodDef.IsNil) { pdbWriter.UsingNamespace("@" + MetadataTokens.GetToken(forwardImportScopesToMethodDef)); } // This is the method that's gonna have AssemblyRef aliases attached: if (methodDefHandleWithAssemblyRefAliases.IsNil) { foreach (var(assemblyRefHandle, alias) in aliasedAssemblyRefs) { var assemblyRef = metadataReader.GetAssemblyReference(assemblyRefHandle); pdbWriter.UsingNamespace("Z" + alias + " " + AssemblyDisplayNameBuilder.GetAssemblyDisplayName(metadataReader, assemblyRef)); } } } foreach (var localVariableHandle in localScope.GetLocalVariables()) { var variable = pdbReader.GetLocalVariable(localVariableHandle); string name = pdbReader.GetString(variable.Name); if (name.Length > MaxEntityNameLength) { ReportDiagnostic(PdbDiagnosticId.LocalConstantNameTooLong, MetadataTokens.GetToken(localVariableHandle)); continue; } if (methodBodyOpt.LocalSignature.IsNil) { ReportDiagnostic(PdbDiagnosticId.MethodContainingLocalVariablesHasNoLocalSignature, methodToken); continue; } // TODO: translate hoisted variable scopes to dummy VB hoisted state machine locals (https://github.com/dotnet/roslyn/issues/8473) pdbWriter.DefineLocalVariable(variable.Index, name, variable.Attributes, MetadataTokens.GetToken(methodBodyOpt.LocalSignature)); var dynamicFlags = MetadataUtilities.ReadDynamicCustomDebugInformation(pdbReader, localVariableHandle); if (TryGetDynamicLocal(name, variable.Index, dynamicFlags, out var dynamicLocal)) { dynamicLocals.Add(dynamicLocal); } var tupleElementNames = MetadataUtilities.ReadTupleCustomDebugInformation(pdbReader, localVariableHandle); if (!tupleElementNames.IsDefaultOrEmpty) { tupleLocals.Add((name, SlotIndex: variable.Index, ScopeStart: 0, ScopeEnd: 0, Names: tupleElementNames)); } } foreach (var localConstantHandle in localScope.GetLocalConstants()) { var constant = pdbReader.GetLocalConstant(localConstantHandle); string name = pdbReader.GetString(constant.Name); if (name.Length > MaxEntityNameLength) { ReportDiagnostic(PdbDiagnosticId.LocalConstantNameTooLong, MetadataTokens.GetToken(localConstantHandle)); continue; } var(value, signature) = PortableConstantSignature.GetConstantValueAndSignature(pdbReader, localConstantHandle, metadataReader.GetQualifiedTypeName); if (!metadataModel.TryGetStandaloneSignatureHandle(signature, out var constantSignatureHandle)) { // Signature will be unspecified. At least we store the name and the value. constantSignatureHandle = default(StandaloneSignatureHandle); } pdbWriter.DefineLocalConstant(name, value, MetadataTokens.GetToken(constantSignatureHandle)); var dynamicFlags = MetadataUtilities.ReadDynamicCustomDebugInformation(pdbReader, localConstantHandle); if (TryGetDynamicLocal(name, 0, dynamicFlags, out var dynamicLocal)) { dynamicLocals.Add(dynamicLocal); } var tupleElementNames = MetadataUtilities.ReadTupleCustomDebugInformation(pdbReader, localConstantHandle); if (!tupleElementNames.IsDefaultOrEmpty) { // Note that the end offset of tuple locals is always end-exclusive, regardless of whether the PDB uses VB semantics or not. tupleLocals.Add((name, SlotIndex: -1, ScopeStart: localScope.StartOffset, ScopeEnd: localScope.EndOffset, Names: tupleElementNames)); } } } currentLocalScope = NextLocalScope(); isFirstMethodScope = false; } bool hasAnyScopes = !isFirstMethodScope; CloseOpenScopes(int.MaxValue); if (openScopeEndOffsets.Count > 0) { ReportDiagnostic(PdbDiagnosticId.LocalScopeRangesNestingIsInvalid, methodToken); openScopeEndOffsets.Clear(); } if (!methodDebugInfo.SequencePointsBlob.IsNil) { LazyOpenMethod(); WriteSequencePoints(pdbWriter, documentWriters, symSequencePointBuilder, methodDebugInfo.GetSequencePoints(), methodToken); } // async method data: var asyncData = MetadataUtilities.ReadAsyncMethodData(pdbReader, methodDebugInfoHandle); if (!asyncData.IsNone) { LazyOpenMethod(); pdbWriter.SetAsyncInfo( moveNextMethodToken: methodToken, kickoffMethodToken: MetadataTokens.GetToken(asyncData.KickoffMethod), catchHandlerOffset: asyncData.CatchHandlerOffset, yieldOffsets: asyncData.YieldOffsets.ToArray(), resumeOffsets: asyncData.ResumeOffsets.ToArray()); } // custom debug information: var cdiEncoder = new CustomDebugInfoEncoder(cdiBuilder); if (isKickOffMethod) { cdiEncoder.AddStateMachineTypeName(GetIteratorTypeName(metadataReader, moveNextHandle)); } else { if (!vbSemantics && hasAnyScopes) { if (forwardImportScopesToMethodDef.IsNil) { // record the number of import strings in each scope: cdiEncoder.AddUsingGroups(importGroups); if (!methodDefHandleWithAssemblyRefAliases.IsNil) { // forward assembly ref aliases to the first method: cdiEncoder.AddForwardModuleInfo(methodDefHandleWithAssemblyRefAliases); } } else { // forward all imports to another method: cdiEncoder.AddForwardMethodInfo(forwardImportScopesToMethodDef); } } var hoistedLocalScopes = GetStateMachineHoistedLocalScopes(pdbReader, methodDefHandle); if (!hoistedLocalScopes.IsDefault) { cdiEncoder.AddStateMachineHoistedLocalScopes(hoistedLocalScopes); } if (dynamicLocals.Count > 0) { cdiEncoder.AddDynamicLocals(dynamicLocals); dynamicLocals.Clear(); } if (tupleLocals.Count > 0) { cdiEncoder.AddTupleElementNames(tupleLocals); tupleLocals.Clear(); } } importGroups.Clear(); // the following blobs map 1:1 CopyCustomDebugInfoRecord(ref cdiEncoder, pdbReader, methodDefHandle, PortableCustomDebugInfoKinds.EncLocalSlotMap, CustomDebugInfoKind.EditAndContinueLocalSlotMap); CopyCustomDebugInfoRecord(ref cdiEncoder, pdbReader, methodDefHandle, PortableCustomDebugInfoKinds.EncLambdaAndClosureMap, CustomDebugInfoKind.EditAndContinueLambdaMap); if (cdiEncoder.RecordCount > 0) { LazyOpenMethod(); pdbWriter.DefineCustomMetadata(cdiEncoder.ToArray()); } cdiBuilder.Clear(); if (methodOpened && aliasedAssemblyRefs.Length > 0 && !isKickOffMethod && methodDefHandleWithAssemblyRefAliases.IsNil) { methodDefHandleWithAssemblyRefAliases = methodDefHandle; } if (methodOpened) { Debug.WriteLine($"Close Method {methodToken:X8}"); pdbWriter.CloseMethod(); } } if (!pdbReader.DebugMetadataHeader.EntryPoint.IsNil) { pdbWriter.SetEntryPoint(MetadataTokens.GetToken(pdbReader.DebugMetadataHeader.EntryPoint)); } var sourceLinkHandle = pdbReader.GetCustomDebugInformation(EntityHandle.ModuleDefinition, PortableCustomDebugInfoKinds.SourceLink); if (!sourceLinkHandle.IsNil) { if ((options & PdbConversionOptions.SuppressSourceLinkConversion) == 0) { ConvertSourceServerData(pdbReader.GetStringUTF8(sourceLinkHandle), pdbWriter, documentNames); } else { pdbWriter.SetSourceLinkData(pdbReader.GetBlobBytes(sourceLinkHandle)); } } SymReaderHelpers.GetWindowsPdbSignature(pdbReader.DebugMetadataHeader.Id, out var guid, out var stamp, out var age); pdbWriter.UpdateSignature(guid, stamp, age); }
public SerializedTypeNameSignatureDecoder(MetadataModel model, bool useAssemblyQualification, char nestedNameSeparator) { _model = model; _useAssemblyQualification = useAssemblyQualification; _nestedNameSeparator = nestedNameSeparator; }
public static void Convert(Stream peStream, Stream sourcePdbStream, Stream targetPdbStream) { using (var peReader = new PEReader(peStream)) using (var pdbReaderProvider = MetadataReaderProvider.FromPortablePdbStream(sourcePdbStream)) using (var pdbWriter = new PdbWriter(peReader.GetMetadataReader())) { var metadataReader = peReader.GetMetadataReader(); var metadataModel = new MetadataModel(metadataReader); var pdbReader = pdbReaderProvider.GetMetadataReader(); var documentWriters = new ArrayBuilder <ISymUnmanagedDocumentWriter>(pdbReader.Documents.Count); var symSequencePointBuilder = new SequencePointsBuilder(capacity: 64); var declaredExternAliases = new HashSet <string>(); var importStringsBuilder = new List <string>(); var importCountsPerScope = new List <int>(); var cdiBuilder = new BlobBuilder(); var dynamicLocals = new List <(string LocalName, byte[] Flags, int Count, int SlotIndex)>(); // state for calculating import string forwarding: var lastImportScopeHandle = default(ImportScopeHandle); var lastImportScopeMethodDefHandle = default(MethodDefinitionHandle); var importStringsMap = new Dictionary <ImmutableArray <string>, MethodDefinitionHandle>(); var aliasedAssemblyRefs = GetAliasedAssemblyRefs(pdbReader); var kickOffMethodToMoveNextMethodMap = GetStateMachineMethodMap(pdbReader); string vbDefaultNamespace = MetadataUtilities.GetVisualBasicDefaultNamespace(metadataReader); bool vbSemantics = vbDefaultNamespace != null; string vbDefaultNamespaceImportString = vbSemantics ? "*" + vbDefaultNamespace : null; foreach (var documentHandle in pdbReader.Documents) { var document = pdbReader.GetDocument(documentHandle); var languageGuid = pdbReader.GetGuid(document.Language); documentWriters.Add(pdbWriter.DefineDocument( name: pdbReader.GetString(document.Name), language: languageGuid, type: s_documentTypeText, vendor: GetLanguageVendorGuid(languageGuid), algorithmId: pdbReader.GetGuid(document.HashAlgorithm), checksum: pdbReader.GetBlobBytes(document.Hash))); } var localScopeEnumerator = pdbReader.LocalScopes.GetEnumerator(); LocalScope currentLocalScope = NextLocalScope(); LocalScope NextLocalScope() => localScopeEnumerator.MoveNext() ? pdbReader.GetLocalScope(localScopeEnumerator.Current) : default(LocalScope); var firstMethodDefHandle = default(MethodDefinitionHandle); foreach (var methodDebugInfoHandle in pdbReader.MethodDebugInformation) { var methodDebugInfo = pdbReader.GetMethodDebugInformation(methodDebugInfoHandle); var methodDefHandle = methodDebugInfoHandle.ToDefinitionHandle(); int methodToken = MetadataTokens.GetToken(methodDefHandle); var methodDef = metadataReader.GetMethodDefinition(methodDefHandle); // methods without debug info: if (methodDebugInfo.Document.IsNil && methodDebugInfo.SequencePointsBlob.IsNil) { continue; } // methods without method body don't currently have any debug information: if (methodDef.RelativeVirtualAddress == 0) { continue; } var methodBody = peReader.GetMethodBody(methodDef.RelativeVirtualAddress); pdbWriter.OpenMethod(methodToken); var forwardImportScopesToMethodDef = default(MethodDefinitionHandle); Debug.Assert(dynamicLocals.Count == 0); bool isFirstMethodScope = true; while (currentLocalScope.Method == methodDefHandle) { if (isFirstMethodScope) { if (lastImportScopeHandle == currentLocalScope.ImportScope) { // forward to a method that has the same imports: forwardImportScopesToMethodDef = lastImportScopeMethodDefHandle; } else { Debug.Assert(importStringsBuilder.Count == 0); Debug.Assert(declaredExternAliases.Count == 0); Debug.Assert(importCountsPerScope.Count == 0); AddImportStrings(importStringsBuilder, importCountsPerScope, declaredExternAliases, pdbReader, metadataModel, currentLocalScope.ImportScope, aliasedAssemblyRefs, vbDefaultNamespaceImportString); var importStrings = importStringsBuilder.ToImmutableArray(); importStringsBuilder.Clear(); if (importStringsMap.TryGetValue(importStrings, out forwardImportScopesToMethodDef)) { // forward to a method that has the same imports: lastImportScopeMethodDefHandle = forwardImportScopesToMethodDef; } else { // attach import strings to the current method: WriteImports(pdbWriter, importStrings); lastImportScopeMethodDefHandle = methodDefHandle; } lastImportScopeHandle = currentLocalScope.ImportScope; } if (vbSemantics && !forwardImportScopesToMethodDef.IsNil) { pdbWriter.UsingNamespace("@" + MetadataTokens.GetToken(forwardImportScopesToMethodDef)); } } else { pdbWriter.OpenScope(currentLocalScope.StartOffset); } foreach (var localConstantHandle in currentLocalScope.GetLocalConstants()) { var constant = pdbReader.GetLocalConstant(localConstantHandle); string name = pdbReader.GetString(constant.Name); if (name.Length > MaxEntityNameLength) { // TODO: report warning continue; } var(value, signature) = PortableConstantSignature.GetConstantValueAndSignature(pdbReader, localConstantHandle, pdbWriter.MetadataImport); if (!metadataModel.TryGetStandaloneSignatureHandle(signature, out var constantSignatureHandle)) { // TODO: report warning // TODO: // Currently the EEs require signature to match exactly the type of the value. // We could relax that and use the type of the value regardless of the signature for primitive types. // Then we could use any signature here. continue; } pdbWriter.DefineLocalConstant(name, value, MetadataTokens.GetToken(constantSignatureHandle)); var dynamicFlags = MetadataUtilities.ReadDynamicCustomDebugInformation(pdbReader, localConstantHandle); if (TryGetDynamicLocal(name, 0, dynamicFlags, out var dynamicLocal)) { dynamicLocals.Add(dynamicLocal); } } foreach (var localVariableHandle in currentLocalScope.GetLocalVariables()) { var variable = pdbReader.GetLocalVariable(localVariableHandle); string name = pdbReader.GetString(variable.Name); if (name.Length > MaxEntityNameLength) { // TODO: report warning continue; } int localSignatureToken = methodBody.LocalSignature.IsNil ? 0 : MetadataTokens.GetToken(methodBody.LocalSignature); pdbWriter.DefineLocalVariable(variable.Index, name, variable.Attributes, localSignatureToken); var dynamicFlags = MetadataUtilities.ReadDynamicCustomDebugInformation(pdbReader, localVariableHandle); if (TryGetDynamicLocal(name, variable.Index, dynamicFlags, out var dynamicLocal)) { dynamicLocals.Add(dynamicLocal); } } if (!isFirstMethodScope) { pdbWriter.CloseScope(currentLocalScope.EndOffset - (vbSemantics ? 1 : 0)); } currentLocalScope = NextLocalScope(); isFirstMethodScope = false; } WriteSequencePoints(pdbWriter, documentWriters, symSequencePointBuilder, methodDebugInfo.GetSequencePoints()); // async method data: var asyncData = MetadataUtilities.ReadAsyncMethodData(pdbReader, methodDebugInfoHandle); if (!asyncData.IsNone) { pdbWriter.SetAsyncInfo( moveNextMethodToken: methodToken, kickoffMethodToken: MetadataTokens.GetToken(asyncData.KickoffMethod), catchHandlerOffset: asyncData.CatchHandlerOffset, yieldOffsets: asyncData.YieldOffsets, resumeOffsets: asyncData.ResumeOffsets); } // custom debug information: var cdiEncoder = new CustomDebugInfoEncoder(cdiBuilder); if (kickOffMethodToMoveNextMethodMap.TryGetValue(methodDefHandle, out var moveNextHandle)) { cdiEncoder.AddReferenceToIteratorClass(GetIteratorTypeName(metadataReader, moveNextHandle)); } else { if (!vbSemantics) { if (forwardImportScopesToMethodDef.IsNil) { // record the number of import strings in each scope: cdiEncoder.AddUsingInfo(importCountsPerScope); if (!firstMethodDefHandle.IsNil) { // forward assembly ref aliases to the first method: cdiEncoder.AddReferenceToMethodWithModuleInfo(firstMethodDefHandle); } } else { // forward all imports to another method: cdiEncoder.AddReferenceToPreviousMethodWithUsingInfo(forwardImportScopesToMethodDef); } } var hoistedLocalScopes = GetStateMachineHoistedLocalScopes(pdbReader, methodDefHandle); if (!hoistedLocalScopes.IsDefault) { cdiEncoder.AddStateMachineLocalScopes(hoistedLocalScopes); } } if (dynamicLocals.Count > 0) { cdiEncoder.AddDynamicLocals(dynamicLocals); dynamicLocals.Clear(); } // the following blobs map 1:1 CopyCustomDebugInfoRecord(ref cdiEncoder, pdbReader, methodDefHandle, PortableCustomDebugInfoKinds.TupleElementNames, CustomDebugInfoKind.TupleElementNames); CopyCustomDebugInfoRecord(ref cdiEncoder, pdbReader, methodDefHandle, PortableCustomDebugInfoKinds.EncLocalSlotMap, CustomDebugInfoKind.EditAndContinueLocalSlotMap); CopyCustomDebugInfoRecord(ref cdiEncoder, pdbReader, methodDefHandle, PortableCustomDebugInfoKinds.EncLambdaAndClosureMap, CustomDebugInfoKind.EditAndContinueLambdaMap); cdiBuilder.Clear(); if (firstMethodDefHandle.IsNil) { firstMethodDefHandle = methodDefHandle; foreach (var(assemblyRefHandle, alias) in aliasedAssemblyRefs) { var assemblyRef = metadataReader.GetAssemblyReference(assemblyRefHandle); pdbWriter.UsingNamespace("Z" + alias + " " + MetadataHelpers.GetAssemblyDisplayName(metadataReader, assemblyRef)); } } pdbWriter.CloseMethod(methodBody.GetILReader().Length); } pdbWriter.WriteTo(targetPdbStream); } }