From 29b9338d1afdfcd73e66445ae4d5699c8f904cca Mon Sep 17 00:00:00 2001 From: mdh1418 Date: Tue, 23 Jun 2026 14:11:17 -0400 Subject: [PATCH 1/3] Handle PE symbol file casing Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../SymbolManifestGenerator.cs | 20 ++++++++-- .../KeyGenerators/PEFileKeyGenerator.cs | 26 +++++++------ .../PEFileKeyGenerationTests.cs | 37 +++++++++++++++++++ 3 files changed, 69 insertions(+), 14 deletions(-) diff --git a/src/Microsoft.SymbolManifestGenerator/SymbolManifestGenerator.cs b/src/Microsoft.SymbolManifestGenerator/SymbolManifestGenerator.cs index e70b927d39..df1264a98c 100644 --- a/src/Microsoft.SymbolManifestGenerator/SymbolManifestGenerator.cs +++ b/src/Microsoft.SymbolManifestGenerator/SymbolManifestGenerator.cs @@ -111,9 +111,23 @@ public static bool GenerateManifest(ITracer tracer, DirectoryInfo dir, string ma if (specialFilesRequireAdjacentRuntime) { - FileInfo correlatedFile = new(Path.Combine(runtimeModule.DirectoryName, correlatedFileName)); - return (FoundMultipleCandidates: false, - File: correlatedFile.Exists ? correlatedFile : default); + FileInfo[] adjacentCorrelatedFiles = allFiles + .Where(file => + file.DirectoryName.Equals(runtimeModule.DirectoryName, StringComparison.Ordinal) && + file.Name.Equals(correlatedFileName, StringComparison.OrdinalIgnoreCase)) + .ToArray(); + + if (adjacentCorrelatedFiles.Length > 1) + { + tracer.Error($"Multiple adjacent files '${correlatedFileName}' found for runtime '{runtimeModule.FullName}': {string.Join(", ", adjacentCorrelatedFiles)}."); + } + + return adjacentCorrelatedFiles.Length switch + { + 0 => (FoundMultipleCandidates: false, default), + 1 => (FoundMultipleCandidates: false, adjacentCorrelatedFiles[0]), + _ => (FoundMultipleCandidates: true, default) + }; } FileInfo[] correlatedFiles = allFiles.Where(file => file.Name.Equals(correlatedFileName, StringComparison.OrdinalIgnoreCase)).ToArray(); diff --git a/src/Microsoft.SymbolStore/KeyGenerators/PEFileKeyGenerator.cs b/src/Microsoft.SymbolStore/KeyGenerators/PEFileKeyGenerator.cs index d18650b888..cf92ca88ba 100644 --- a/src/Microsoft.SymbolStore/KeyGenerators/PEFileKeyGenerator.cs +++ b/src/Microsoft.SymbolStore/KeyGenerators/PEFileKeyGenerator.cs @@ -51,7 +51,10 @@ public override IEnumerable GetKeys(KeyTypeFlags flags) { yield return GetKey(_path, _peFile.Timestamp, _peFile.SizeOfImage); } - if ((flags & KeyTypeFlags.RuntimeKeys) != 0 && (GetFileName(_path) == CoreClrFileName || GetFileName(_path) == ClrFileName)) + string runtimeFileName = GetFileName(_path); + if ((flags & KeyTypeFlags.RuntimeKeys) != 0 && + (runtimeFileName.Equals(CoreClrFileName, StringComparison.OrdinalIgnoreCase) || + runtimeFileName.Equals(ClrFileName, StringComparison.OrdinalIgnoreCase))) { yield return GetKey(_path, _peFile.Timestamp, _peFile.SizeOfImage); } @@ -107,10 +110,10 @@ public override IEnumerable GetKeys(KeyTypeFlags flags) if ((flags & KeyTypeFlags.ClrKeys) != 0) { string coreclrId = BuildId(_peFile.Timestamp, _peFile.SizeOfImage); - string[] sosFiles = GetSOSFiles(GetFileName(_path)).ToArray(); + string[] sosFiles = GetSOSFiles(runtimeFileName).ToArray(); if (sosFiles.Length == 0) { - Tracer.Verbose("PEFile `{0}`: no SOS special files generated for runtime file {1}. No SOS keys will be produced.", _path, GetFileName(_path)); + Tracer.Verbose("PEFile `{0}`: no SOS special files generated for runtime file {1}. No SOS keys will be produced.", _path, runtimeFileName); } foreach (string specialFileName in sosFiles) { @@ -122,10 +125,10 @@ public override IEnumerable GetKeys(KeyTypeFlags flags) if ((flags & (KeyTypeFlags.ClrKeys | KeyTypeFlags.DacDbiKeys)) != 0) { string coreclrId = BuildId(_peFile.Timestamp, _peFile.SizeOfImage); - string[] dacFiles = GetDACFiles(GetFileName(_path)).ToArray(); + string[] dacFiles = GetDACFiles(runtimeFileName).ToArray(); if (dacFiles.Length == 0) { - Tracer.Verbose("PEFile `{0}`: no DAC/DBI special files generated for runtime file {1}. No DAC/DBI keys will be produced.", _path, GetFileName(_path)); + Tracer.Verbose("PEFile `{0}`: no DAC/DBI special files generated for runtime file {1}. No DAC/DBI keys will be produced.", _path, runtimeFileName); } foreach (string specialFileName in dacFiles) { @@ -155,7 +158,7 @@ public override IEnumerable GetKeys(KeyTypeFlags flags) private IEnumerable GetSOSFiles(string runtimeFileName) { - if (runtimeFileName == ClrFileName) + if (runtimeFileName.Equals(ClrFileName, StringComparison.OrdinalIgnoreCase)) { return GetFilesLongNameVariants(SosFileName); } @@ -165,14 +168,14 @@ private IEnumerable GetSOSFiles(string runtimeFileName) private IEnumerable GetDACFiles(string runtimeFileName) { - if (runtimeFileName == CoreClrFileName) + if (runtimeFileName.Equals(CoreClrFileName, StringComparison.OrdinalIgnoreCase)) { string[] coreClrDACFiles = new string[] { CoreClrDACFileName, DbiFileName }; IEnumerable longNameDACFiles = GetFilesLongNameVariants(CoreClrDACFileName); return coreClrDACFiles.Concat(longNameDACFiles); } - if (runtimeFileName == ClrFileName) + if (runtimeFileName.Equals(ClrFileName, StringComparison.OrdinalIgnoreCase)) { string[] clrDACFiles = new string[] { ClrDACFileName, DbiFileName }; IEnumerable longNameDACFiles = GetFilesLongNameVariants(ClrDACFileName); @@ -184,7 +187,7 @@ private IEnumerable GetDACFiles(string runtimeFileName) private IEnumerable GetFilesLongNameVariants(string fileWithLongNameVariant) { - if (!s_knownFilesWithLongNameVariant.Contains(fileWithLongNameVariant)) + if (!s_knownFilesWithLongNameVariant.Contains(fileWithLongNameVariant, StringComparer.OrdinalIgnoreCase)) { Tracer.Warning("{0} is not a recognized file with a long name variant", fileWithLongNameVariant); return Enumerable.Empty(); @@ -251,8 +254,9 @@ public static SymbolStoreKey GetKey(string path, uint timestamp, uint sizeOfImag // The clr special file flag can not be based on the GetSpecialFiles() list because // that is only valid when "path" is the coreclr.dll. string fileName = GetFileName(path); - bool clrSpecialFile = s_knownRuntimeSpecialFiles.Contains(fileName) || - (s_knownFilesWithLongNameVariant.Any((file) => fileName.StartsWith(Path.GetFileNameWithoutExtension(file).ToLowerInvariant() + "_")) && Path.GetExtension(fileName) == ".dll"); + bool clrSpecialFile = s_knownRuntimeSpecialFiles.Contains(fileName, StringComparer.OrdinalIgnoreCase) || + (s_knownFilesWithLongNameVariant.Any((file) => fileName.StartsWith(Path.GetFileNameWithoutExtension(file) + "_", StringComparison.OrdinalIgnoreCase)) + && Path.GetExtension(fileName).Equals(".dll", StringComparison.OrdinalIgnoreCase)); string id = BuildId(timestamp, sizeOfImage); return BuildKey(path, id, clrSpecialFile); diff --git a/src/tests/Microsoft.SymbolStore.UnitTests/PEFileKeyGenerationTests.cs b/src/tests/Microsoft.SymbolStore.UnitTests/PEFileKeyGenerationTests.cs index 2d6e3044c6..ac8b4eb646 100644 --- a/src/tests/Microsoft.SymbolStore.UnitTests/PEFileKeyGenerationTests.cs +++ b/src/tests/Microsoft.SymbolStore.UnitTests/PEFileKeyGenerationTests.cs @@ -135,5 +135,42 @@ public void PEFileGenerateRuntimeKeys(MockPEFile mockPEFile) Assert.Empty(runtimeKeys); } } + + [Fact] + public void PEFileGenerateRuntimeKeysCaseInsensitiveRuntimeName() + { + using var mockFileStream = new FileStream("TestBinaries/mockclr_amd64.dll", FileMode.Open, FileAccess.Read); + var mockSymbolStoreFile = new SymbolStoreFile(mockFileStream, "CLR.DLL"); + var generator = new PEFileKeyGenerator(_tracer, mockSymbolStoreFile); + + var runtimeKeys = generator.GetKeys(KeyTypeFlags.RuntimeKeys); + Assert.Single(runtimeKeys); + Assert.Equal("clr.dll/4D4F434B434c52/clr.dll", runtimeKeys.Single().Index); + } + + [Fact] + public void PEFileGenerateClrKeysCaseInsensitiveRuntimeName() + { + using var mockFileStream = new FileStream("TestBinaries/mockclr_amd64.dll", FileMode.Open, FileAccess.Read); + var mockSymbolStoreFile = new SymbolStoreFile(mockFileStream, "CLR.DLL"); + var generator = new PEFileKeyGenerator(_tracer, mockSymbolStoreFile); + + var clrKeys = generator.GetKeys(KeyTypeFlags.ClrKeys).ToDictionary((key) => key.Index); + Assert.Contains("sos_amd64_amd64_1.2.3.45.dll/4D4F434B434c52/sos_amd64_amd64_1.2.3.45.dll", clrKeys.Keys); + Assert.Contains("mscordacwks.dll/4D4F434B434c52/mscordacwks.dll", clrKeys.Keys); + Assert.Contains("mscordbi.dll/4D4F434B434c52/mscordbi.dll", clrKeys.Keys); + } + + [Fact] + public void PEFileGenerateIdentityKeysCaseInsensitiveSpecialName() + { + using var mockFileStream = new FileStream("TestBinaries/mockdac.dll", FileMode.Open, FileAccess.Read); + var mockSymbolStoreFile = new SymbolStoreFile(mockFileStream, "MSCORDACWKS.DLL"); + var generator = new PEFileKeyGenerator(_tracer, mockSymbolStoreFile); + + SymbolStoreKey identityKey = generator.GetKeys(KeyTypeFlags.IdentityKey).Single(); + Assert.Equal("mscordacwks.dll/4D4F434B444143/mscordacwks.dll", identityKey.Index); + Assert.True(identityKey.IsClrSpecialFile); + } } } From 83dc3c6b499ba4a328f82ac7363f388c17076039 Mon Sep 17 00:00:00 2001 From: mdh1418 Date: Tue, 23 Jun 2026 14:29:51 -0400 Subject: [PATCH 2/3] Optimize adjacent runtime lookup Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../SymbolManifestGenerator.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/Microsoft.SymbolManifestGenerator/SymbolManifestGenerator.cs b/src/Microsoft.SymbolManifestGenerator/SymbolManifestGenerator.cs index df1264a98c..3f6df97cd5 100644 --- a/src/Microsoft.SymbolManifestGenerator/SymbolManifestGenerator.cs +++ b/src/Microsoft.SymbolManifestGenerator/SymbolManifestGenerator.cs @@ -111,11 +111,10 @@ public static bool GenerateManifest(ITracer tracer, DirectoryInfo dir, string ma if (specialFilesRequireAdjacentRuntime) { - FileInfo[] adjacentCorrelatedFiles = allFiles - .Where(file => - file.DirectoryName.Equals(runtimeModule.DirectoryName, StringComparison.Ordinal) && - file.Name.Equals(correlatedFileName, StringComparison.OrdinalIgnoreCase)) - .ToArray(); + FileInfo[] adjacentCorrelatedFiles = runtimeModule.Directory?.GetFiles() + .Where(file => file.Name.Equals(correlatedFileName, StringComparison.OrdinalIgnoreCase)) + .ToArray() + ?? Array.Empty(); if (adjacentCorrelatedFiles.Length > 1) { From a9771252361c5ac4ec42173796c1e143d7202988 Mon Sep 17 00:00:00 2001 From: mdh1418 Date: Tue, 23 Jun 2026 14:31:16 -0400 Subject: [PATCH 3/3] Fix correlated file log formatting Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../SymbolManifestGenerator.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.SymbolManifestGenerator/SymbolManifestGenerator.cs b/src/Microsoft.SymbolManifestGenerator/SymbolManifestGenerator.cs index 3f6df97cd5..3ced3766b8 100644 --- a/src/Microsoft.SymbolManifestGenerator/SymbolManifestGenerator.cs +++ b/src/Microsoft.SymbolManifestGenerator/SymbolManifestGenerator.cs @@ -118,7 +118,7 @@ public static bool GenerateManifest(ITracer tracer, DirectoryInfo dir, string ma if (adjacentCorrelatedFiles.Length > 1) { - tracer.Error($"Multiple adjacent files '${correlatedFileName}' found for runtime '{runtimeModule.FullName}': {string.Join(", ", adjacentCorrelatedFiles)}."); + tracer.Error($"Multiple adjacent files '{correlatedFileName}' found for runtime '{runtimeModule.FullName}': {string.Join(", ", adjacentCorrelatedFiles)}."); } return adjacentCorrelatedFiles.Length switch @@ -133,7 +133,7 @@ public static bool GenerateManifest(ITracer tracer, DirectoryInfo dir, string ma if (correlatedFiles.Length > 1) { - tracer.Error($"Multiple files '${correlatedFileName}' found for runtime '{runtimeModule.FullName}': {string.Join(", ", correlatedFiles)}."); + tracer.Error($"Multiple files '{correlatedFileName}' found for runtime '{runtimeModule.FullName}': {string.Join(", ", correlatedFiles)}."); } return correlatedFiles.Length switch