Fix unknown type/methods in core dumps. (#2442)
authorMike McLaughlin <mikem@microsoft.com>
Thu, 29 Jul 2021 17:59:39 +0000 (10:59 -0700)
committerGitHub <noreply@github.com>
Thu, 29 Jul 2021 17:59:39 +0000 (10:59 -0700)
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.

src/Microsoft.Diagnostics.DebugServices.Implementation/ImageMappingMemoryService.cs
src/Microsoft.Diagnostics.DebugServices.Implementation/MetadataMappingMemoryService.cs
src/Microsoft.Diagnostics.DebugServices.Implementation/Module.cs
src/Microsoft.Diagnostics.DebugServices.Implementation/ModuleService.cs
src/Microsoft.Diagnostics.DebugServices.Implementation/ModuleServiceFromDataReader.cs
src/SOS/SOS.Extensions/ModuleServiceFromDebuggerServices.cs

index 44ceb9551274d7a99ee668ce8120abbf9cca8bb3..8ceeb7605696ef39a6fd4d8e49edf5970c82389c 100644 (file)
@@ -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<PEReader>();
                         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<ELFFile>()?.VirtualAddressReader;
-                        if (virtualAddressReader is null)
+                        else
                         {
-                            // Find or download the MachO image, if one.
-                            virtualAddressReader = module.Services.GetService<MachOFile>()?.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<ELFFile>()?.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<MachOFile>()?.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;
index 5b061c76ed0d7d9e50b04b545718ecfa3fb0c3f9..543f548b259d1b724c3b2adaa1c771a9655d0829 100644 (file)
@@ -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
 {
index 09f68ca5df9da189ded5228dd217f4b4b0d27f26..71f1a1cee0eca2d343bd342d0d383a4dcc4caab8 100644 (file)
@@ -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<byte> _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;
         }
index 65772fc80422a2f0a387b0fb19786f0e3729fa6b..1c576e214f20439cc78f4ae1fd33f466199efcdb 100644 (file)
@@ -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
         /// <param name="address">module base address</param>
         /// <param name="size">module size</param>
         /// <param name="pdbFileInfo">the pdb record or null</param>
-        /// <param name="versionData">the PE version or null</param>
         /// <param name="flags">module flags</param>
         /// <returns>PEImage instance or null</returns>
-        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
         /// <param name="address">module base address</param>
         /// <param name="size">module size</param>
         /// <param name="pdbFileInfo">the pdb record or null</param>
-        /// <param name="versionData">the PE version or null</param>
         /// <param name="flags">module flags</param>
+        /// 
         /// <returns>PEImage instance or null</returns>
-        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;
         }
index 1ee7ae131d4544901eac04b2f4eb437116016126..c2c6253b9aac14b21c8b72b0a22f11d580bbefb0 100644 (file)
@@ -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;
                 }
             }
 
index f3bc44177dc11c2ec220b14006bf171be54c93b9..3ff4cb5c1625ab69ac7b1d5a5298d2f523946fb6 100644 (file)
@@ -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;
                 }
             }