From: Mike McLaughlin Date: Thu, 29 Jul 2021 17:59:39 +0000 (-0700) Subject: Fix unknown type/methods in core dumps. (#2442) X-Git-Tag: submit/tizen/20220302.040122~21^2^2~186 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=30dc8118eac6d5a8aec58f8e6b1007ca9c6c31f0;p=platform%2Fcore%2Fdotnet%2Fdiagnostics.git Fix unknown type/methods in core dumps. (#2442) Fixes some of the issues in https://github.com/dotnet/diagnostics/issues/2375 The problem is that the image mapping memory service didn't convert the rva from a loaded layout calculated from the in-memory module to the file layout (the PEReader with the downloaded image). On Windows, images (native or managed) are always loaded layout so return false in IModule.IsFileLayout without calling GetPEInfo() to avoid the recursion that broken getting the info about coreclr.dll. It turns out that the heap dumps generated on Windows don't have the image in-memory. Don't get module version in GetPEInfo() to determine the layout. Cleanup. Skip relocations that span cache blocks. This happens very rarely and should not affect anything unless we get really really unlucky. --- diff --git a/src/Microsoft.Diagnostics.DebugServices.Implementation/ImageMappingMemoryService.cs b/src/Microsoft.Diagnostics.DebugServices.Implementation/ImageMappingMemoryService.cs index 44ceb9551..8ceeb7605 100644 --- a/src/Microsoft.Diagnostics.DebugServices.Implementation/ImageMappingMemoryService.cs +++ b/src/Microsoft.Diagnostics.DebugServices.Implementation/ImageMappingMemoryService.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using Microsoft.Diagnostics.Runtime.Utilities; using Microsoft.FileFormats; using Microsoft.FileFormats.ELF; using Microsoft.FileFormats.MachO; @@ -9,9 +10,9 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; +using System.Linq; using System.Reflection.Metadata; using System.Reflection.PortableExecutable; -using System.Runtime.InteropServices; namespace Microsoft.Diagnostics.DebugServices.Implementation { @@ -101,96 +102,111 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation IModule module = _moduleService.GetModuleFromAddress(address); if (module != null) { - Trace.TraceInformation("ReadMemory: address {0:X16} size {1:X8} found module {2}", address, bytesRequested, module.FileName); - - // Recursion can happen in the extreme case where the PE, ELF or MachO headers (in the module.Services.GetService<>() calls) + // Recursion can happen in the case where the PE, ELF or MachO headers (in the module.Services.GetService<>() calls) // used to get the timestamp/filesize or build id are not in the dump. - if (!_recursionProtection.Contains(module.ImageBase)) + if (!_recursionProtection.Contains(address)) { - _recursionProtection.Add(module.ImageBase); + _recursionProtection.Add(address); try { // We found a module that contains the memory requested. Now find or download the PE image. PEReader reader = module.Services.GetService(); if (reader is not null) { - // Read the memory from the PE image. int rva = (int)(address - module.ImageBase); Debug.Assert(rva >= 0); + Debug.Assert(!reader.IsLoadedImage); + Debug.Assert(reader.IsEntireImageAvailable); +#if TRACE_VERBOSE + Trace.TraceInformation($"ReadMemoryFromModule: address {address:X16} rva {rva:X8} bytesRequested {bytesRequested:X8} {module.FileName}"); +#endif + // Not reading anything in the PE's header + if (rva > reader.PEHeaders.PEHeader.SizeOfHeaders) + { + // This property can cause recursion because this PE being mapped here is read to determine the layout + if (!module.IsFileLayout.GetValueOrDefault(true)) + { + // If the PE image that we are mapping into has the "loaded" layout convert the rva to a flat/file based one. + for (int i = 0; i < reader.PEHeaders.SectionHeaders.Length; i++) + { + SectionHeader section = reader.PEHeaders.SectionHeaders[i]; + if (rva >= section.VirtualAddress && rva < (section.VirtualAddress + section.VirtualSize)) + { + rva = section.PointerToRawData + (rva - section.VirtualAddress); + break; + } + } + } + } + try { byte[] data = null; - int sizeOfHeaders = reader.PEHeaders.PEHeader.SizeOfHeaders; - if (rva < sizeOfHeaders) - { - // If the address isn't contained in one of the sections, assume that SOS is reading the PE headers directly. - Trace.TraceInformation("ReadMemory: rva {0:X8} size {1:X8} in PE Header", rva, bytesRequested); - data = reader.GetEntireImage().GetReader(rva, bytesRequested).ReadBytes(bytesRequested); - } - else + // Read the memory from the PE image found/downloaded above + PEMemoryBlock block = reader.GetEntireImage(); + if (rva < block.Length) { - PEMemoryBlock block = reader.GetSectionData(rva); - if (block.Length > 0) + int size = Math.Min(block.Length - rva, bytesRequested); + if ((rva + size) <= block.Length) { - int size = Math.Min(block.Length, bytesRequested); - data = block.GetReader().ReadBytes(size); + data = block.GetReader(rva, size).ReadBytes(size); ApplyRelocations(module, reader, rva, data); } else { - Trace.TraceError($"ReadMemory: FAILED rva {rva:X8}"); + Trace.TraceError($"ReadMemoryFromModule: FAILED address {address:X16} rva {rva:X8} {module.FileName}"); } } - + return data; } catch (Exception ex) when (ex is BadImageFormatException || ex is InvalidOperationException || ex is IOException) { - Trace.TraceError($"ReadMemory: exception {ex.Message}"); - return null; + Trace.TraceError($"ReadMemoryFromModule: exception: address {address:X16} {ex.Message} {module.FileName}"); } } - - // Find or download the ELF image, if one. - Reader virtualAddressReader = module.Services.GetService()?.VirtualAddressReader; - if (virtualAddressReader is null) + else { - // Find or download the MachO image, if one. - virtualAddressReader = module.Services.GetService()?.VirtualAddressReader; - } - if (virtualAddressReader is not null) - { - // Read the memory from the image. - ulong rva = address - module.ImageBase; - Debug.Assert(rva >= 0); - try + // Find or download the ELF image, if one. + Reader virtualAddressReader = module.Services.GetService()?.VirtualAddressReader; + if (virtualAddressReader is null) { - Trace.TraceInformation("ReadMemory: rva {0:X16} size {1:X8} in ELF or MachO file", rva, bytesRequested); - byte[] data = new byte[bytesRequested]; - uint read = virtualAddressReader.Read(rva, data, 0, (uint)bytesRequested); - if (read == 0) - { - Trace.TraceError($"ReadMemory: FAILED rva {rva:X8}"); - data = null; - } - return data; + // Find or download the MachO image, if one. + virtualAddressReader = module.Services.GetService()?.VirtualAddressReader; } - catch (Exception ex) when (ex is BadInputFormatException || ex is InvalidVirtualAddressException) + if (virtualAddressReader is not null) { - Trace.TraceError($"ReadMemory: ELF or MachO file exception {ex.Message}"); - return null; + // Read the memory from the image. + ulong rva = address - module.ImageBase; + Debug.Assert(rva >= 0); + try + { + Trace.TraceInformation($"ReadMemoryFromModule: address {address:X16} rva {rva:X16} size {bytesRequested:X8} in ELF or MachO file {module.FileName}"); + byte[] data = new byte[bytesRequested]; + uint read = virtualAddressReader.Read(rva, data, 0, (uint)bytesRequested); + if (read == 0) + { + Trace.TraceError($"ReadMemoryFromModule: FAILED address {address:X16} rva {rva:X16} {module.FileName}"); + data = null; + } + return data; + } + catch (Exception ex) when (ex is BadInputFormatException || ex is InvalidVirtualAddressException) + { + Trace.TraceError($"ReadMemoryFromModule: ELF or MachO file exception: address {address:X16} {ex.Message} {module.FileName}"); + } } } } finally { - _recursionProtection.Remove(module.ImageBase); + _recursionProtection.Remove(address); } } else { - Trace.TraceError("ReadMemory: recursion"); + Trace.TraceError($"ReadMemoryFromModule: recursion: address {address:X16} size {bytesRequested:X8} {module.FileName}"); } } return null; @@ -250,21 +266,25 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation break; case BaseRelocationType.ImageRelBasedHighLow: - { - uint value = BitConverter.ToUInt32(data, offset); - value += (uint)baseDelta; - byte[] source = BitConverter.GetBytes(value); - Array.Copy(source, 0, data, offset, source.Length); + if ((offset + sizeof(uint)) <= data.Length) + { + uint value = BitConverter.ToUInt32(data, offset); + value += (uint)baseDelta; + byte[] source = BitConverter.GetBytes(value); + Array.Copy(source, 0, data, offset, source.Length); + } break; - } + case BaseRelocationType.ImageRelBasedDir64: - { - ulong value = BitConverter.ToUInt64(data, offset); - value += baseDelta; - byte[] source = BitConverter.GetBytes(value); - Array.Copy(source, 0, data, offset, source.Length); + if ((offset + sizeof(ulong)) <= data.Length) + { + ulong value = BitConverter.ToUInt64(data, offset); + value += baseDelta; + byte[] source = BitConverter.GetBytes(value); + Array.Copy(source, 0, data, offset, source.Length); + } break; - } + default: Debug.Fail($"ApplyRelocations: invalid relocation type {type}"); break; diff --git a/src/Microsoft.Diagnostics.DebugServices.Implementation/MetadataMappingMemoryService.cs b/src/Microsoft.Diagnostics.DebugServices.Implementation/MetadataMappingMemoryService.cs index 5b061c76e..543f548b2 100644 --- a/src/Microsoft.Diagnostics.DebugServices.Implementation/MetadataMappingMemoryService.cs +++ b/src/Microsoft.Diagnostics.DebugServices.Implementation/MetadataMappingMemoryService.cs @@ -3,13 +3,12 @@ // See the LICENSE file in the project root for more information. using Microsoft.Diagnostics.Runtime; -using System.Linq; +using Microsoft.Diagnostics.Runtime.Utilities; using System; using System.Collections.Immutable; -using System.IO; -using Microsoft.Diagnostics.Runtime.Utilities; using System.Diagnostics; -using System.Diagnostics.Contracts; +using System.IO; +using System.Linq; namespace Microsoft.Diagnostics.DebugServices.Implementation { diff --git a/src/Microsoft.Diagnostics.DebugServices.Implementation/Module.cs b/src/Microsoft.Diagnostics.DebugServices.Implementation/Module.cs index 09f68ca5d..71f1a1cee 100644 --- a/src/Microsoft.Diagnostics.DebugServices.Implementation/Module.cs +++ b/src/Microsoft.Diagnostics.DebugServices.Implementation/Module.cs @@ -11,6 +11,7 @@ using System.Collections.Immutable; using System.Diagnostics; using System.Reflection.PortableExecutable; using System.Runtime.InteropServices; +using FileVersionInfo = Microsoft.Diagnostics.Runtime.Utilities.FileVersionInfo; namespace Microsoft.Diagnostics.DebugServices.Implementation { @@ -36,7 +37,6 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation private Flags _flags; private PdbFileInfo _pdbFileInfo; private ImmutableArray _buildId; - private VersionData _versionData; private PEImage _peImage; public readonly ServiceProvider ServiceProvider; @@ -105,16 +105,26 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation { get { - GetPEInfo(); - if ((_flags & Flags.IsFileLayout) != 0) + // For Windows targets we can assume that the file layout is always "loaded". The + // ImageMappingMemoryService depends on no recursion memory access for this property + // i.e. calling GetPEInfo(). + if (Target.OperatingSystem == OSPlatform.Windows) { - return true; + return false; } - if ((_flags & Flags.IsLoadedLayout) != 0) + else { - return false; + GetPEInfo(); + if ((_flags & Flags.IsFileLayout) != 0) + { + return true; + } + if ((_flags & Flags.IsLoadedLayout) != 0) + { + return false; + } + return null; } - return null; } } @@ -147,23 +157,28 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation } } - public virtual VersionData VersionData - { - get { return _versionData; } - set { _versionData = value; } - } + public abstract VersionData VersionData { get; } public abstract string VersionString { get; } #endregion - protected void GetVersionFromVersionString() + protected VersionData GetVersion() { - GetPEInfo(); + VersionData versionData = null; - // If we can't get the version from the PE, search for version string embedded in the module data - if (_versionData is null && !IsPEImage) + PEImage peImage = GetPEInfo(); + if (peImage != null) { + FileVersionInfo fileVersionInfo = peImage.GetFileVersionInfo(); + if (fileVersionInfo != null) + { + versionData = fileVersionInfo.VersionInfo.ToVersionData(); + } + } + else + { + // If we can't get the version from the PE, search for version string embedded in the module data string versionString = VersionString; if (versionString != null) { @@ -178,7 +193,7 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation try { Version version = System.Version.Parse(versionToParse); - _versionData = new VersionData(version.Major, version.Minor, version.Build, version.Revision); + versionData = new VersionData(version.Major, version.Minor, version.Build, version.Revision); } catch (ArgumentException ex) { @@ -187,12 +202,14 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation } } } + + return versionData; } protected PEImage GetPEInfo() { if (InitializeValue(Flags.InitializePEInfo)) { - _peImage = ModuleService.GetPEInfo(ImageBase, ImageSize, ref _pdbFileInfo, ref _versionData, ref _flags); + _peImage = ModuleService.GetPEInfo(ImageBase, ImageSize, ref _pdbFileInfo, ref _flags); } return _peImage; } diff --git a/src/Microsoft.Diagnostics.DebugServices.Implementation/ModuleService.cs b/src/Microsoft.Diagnostics.DebugServices.Implementation/ModuleService.cs index 65772fc80..1c576e214 100644 --- a/src/Microsoft.Diagnostics.DebugServices.Implementation/ModuleService.cs +++ b/src/Microsoft.Diagnostics.DebugServices.Implementation/ModuleService.cs @@ -16,7 +16,6 @@ using System.Linq; using System.Reflection.PortableExecutable; using System.Runtime.InteropServices; using System.Text; -using FileVersionInfo = Microsoft.Diagnostics.Runtime.Utilities.FileVersionInfo; namespace Microsoft.Diagnostics.DebugServices.Implementation { @@ -203,10 +202,9 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation /// module base address /// module size /// the pdb record or null - /// the PE version or null /// module flags /// PEImage instance or null - internal PEImage GetPEInfo(ulong address, ulong size, ref PdbFileInfo pdbFileInfo, ref VersionData versionData, ref Module.Flags flags) + internal PEImage GetPEInfo(ulong address, ulong size, ref PdbFileInfo pdbFileInfo, ref Module.Flags flags) { PEImage peImage = null; @@ -214,13 +212,13 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation if (Target.Host.HostType != HostType.Lldb) { // First try getting the PE info as load layout (native Windows DLLs and most managed PEs on Linux/MacOS). - peImage = GetPEInfo(isVirtual: true, address, size, ref pdbFileInfo, ref versionData, ref flags); + peImage = GetPEInfo(isVirtual: true, address: address, size: size, pdbFileInfo: ref pdbFileInfo, flags: ref flags); if (peImage == null) { if (Target.OperatingSystem != OSPlatform.Windows) { // Then try getting the PE info as file layout (some managed PEs on Linux/MacOS). - peImage = GetPEInfo(isVirtual: false, address, size, ref pdbFileInfo, ref versionData, ref flags); + peImage = GetPEInfo(isVirtual: false, address: address, size: size, pdbFileInfo: ref pdbFileInfo, flags: ref flags); } } } @@ -234,10 +232,10 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation /// module base address /// module size /// the pdb record or null - /// the PE version or null /// module flags + /// /// PEImage instance or null - private PEImage GetPEInfo(bool isVirtual, ulong address, ulong size, ref PdbFileInfo pdbFileInfo, ref VersionData versionData, ref Module.Flags flags) + private PEImage GetPEInfo(bool isVirtual, ulong address, ulong size, ref PdbFileInfo pdbFileInfo, ref Module.Flags flags) { Stream stream = MemoryService.CreateMemoryStream(address, size); try @@ -248,15 +246,7 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation { flags |= Module.Flags.IsPEImage; flags |= peImage.IsManaged ? Module.Flags.IsManaged : Module.Flags.None; - pdbFileInfo = peImage.DefaultPdb.ToPdbFileInfo(); - if (versionData is null) - { - FileVersionInfo fileVersionInfo = peImage.GetFileVersionInfo(); - if (fileVersionInfo != null) - { - versionData = fileVersionInfo.VersionInfo.ToVersionData(); - } - } + pdbFileInfo = peImage.DefaultPdb?.ToPdbFileInfo(); flags &= ~(Module.Flags.IsLoadedLayout | Module.Flags.IsFileLayout); flags |= isVirtual ? Module.Flags.IsLoadedLayout : Module.Flags.IsFileLayout; return peImage; @@ -268,7 +258,7 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation } catch (Exception ex) when (ex is BadImageFormatException || ex is EndOfStreamException || ex is IOException) { - Trace.TraceError($"GetPEInfo: loaded {address:X16} isVirtual {isVirtual} exception {ex.Message}"); + Trace.TraceError($"GetPEInfo: {address:X16} isVirtual {isVirtual} exception {ex.Message}"); } return null; } diff --git a/src/Microsoft.Diagnostics.DebugServices.Implementation/ModuleServiceFromDataReader.cs b/src/Microsoft.Diagnostics.DebugServices.Implementation/ModuleServiceFromDataReader.cs index 1ee7ae131..c2c6253b9 100644 --- a/src/Microsoft.Diagnostics.DebugServices.Implementation/ModuleServiceFromDataReader.cs +++ b/src/Microsoft.Diagnostics.DebugServices.Implementation/ModuleServiceFromDataReader.cs @@ -27,6 +27,7 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation private readonly IExportReader _exportReader; private readonly ModuleInfo _moduleInfo; private readonly ulong _imageSize; + private VersionData _versionData; private string _versionString; public ModuleFromDataReader(ModuleServiceFromDataReader moduleService, IExportReader exportReader, int moduleIndex, ModuleInfo moduleInfo, ulong imageSize) @@ -65,17 +66,17 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation { if (_moduleInfo.Version != EmptyVersionInfo) { - base.VersionData = _moduleInfo.Version.ToVersionData(); + _versionData = _moduleInfo.Version.ToVersionData(); } else { if (_moduleService.Target.OperatingSystem != OSPlatform.Windows) { - GetVersionFromVersionString(); + _versionData = GetVersion(); } } } - return base.VersionData; + return _versionData; } } diff --git a/src/SOS/SOS.Extensions/ModuleServiceFromDebuggerServices.cs b/src/SOS/SOS.Extensions/ModuleServiceFromDebuggerServices.cs index f3bc44177..3ff4cb5c1 100644 --- a/src/SOS/SOS.Extensions/ModuleServiceFromDebuggerServices.cs +++ b/src/SOS/SOS.Extensions/ModuleServiceFromDebuggerServices.cs @@ -4,13 +4,10 @@ using Microsoft.Diagnostics.DebugServices; using Microsoft.Diagnostics.DebugServices.Implementation; -using Microsoft.Diagnostics.Runtime; using Microsoft.Diagnostics.Runtime.Interop; using Microsoft.Diagnostics.Runtime.Utilities; -using System; using System.Collections.Generic; using System.Diagnostics; -using System.IO; using System.Runtime.InteropServices; namespace SOS.Extensions @@ -26,6 +23,7 @@ namespace SOS.Extensions private const uint InvalidTimeStamp = 0xFFFFFFFE; private readonly ModuleServiceFromDebuggerServices _moduleService; + private VersionData _versionData; private string _versionString; public ModuleFromDebuggerServices( @@ -77,17 +75,17 @@ namespace SOS.Extensions int minor = (int)fileInfo.dwFileVersionMS & 0xffff; int revision = (int)fileInfo.dwFileVersionLS >> 16; int patch = (int)fileInfo.dwFileVersionLS & 0xffff; - base.VersionData = new VersionData(major, minor, revision, patch); + _versionData = new VersionData(major, minor, revision, patch); } else { if (_moduleService.Target.OperatingSystem != OSPlatform.Windows) { - GetVersionFromVersionString(); + _versionData = GetVersion(); } } } - return base.VersionData; + return _versionData; } }