private ValueTuple <Dictionary <string, AssemblyReferenceHandle>, string[]> BuildAssemblyRefMap() { var assemblyRefsByName = new Dictionary <string, AssemblyReferenceHandle>(StringComparer.OrdinalIgnoreCase); var assemblyNames = new string[Reader.AssemblyReferences.Count]; foreach (var handle in Reader.AssemblyReferences) { var displayName = MetadataHelpers.GetAssemblyDisplayName(Reader, Reader.GetAssemblyReference(handle)); assemblyNames[MetadataTokens.GetRowNumber(handle)] = displayName; assemblyRefsByName.Add(displayName, handle); } return(ValueTuple.Create(assemblyRefsByName, assemblyNames)); }
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); } }