Skip to content

Commit

Permalink
Fix unknown type/methods in core dumps. (#2442)
Browse files Browse the repository at this point in the history
Fixes some of the issues in #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.
  • Loading branch information
mikem8361 authored Jul 29, 2021
1 parent fb75a91 commit 30dc811
Show file tree
Hide file tree
Showing 6 changed files with 135 additions and 110 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,17 @@
// 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;
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
{
Expand Down Expand Up @@ -101,96 +102,111 @@ private byte[] ReadMemoryFromModule(ulong address, int bytesRequested)
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;
Expand Down Expand Up @@ -250,21 +266,25 @@ private void ApplyRelocations(IModule module, PEReader reader, int dataVA, byte[
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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down
53 changes: 35 additions & 18 deletions src/Microsoft.Diagnostics.DebugServices.Implementation/Module.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using System.Diagnostics;
using System.Reflection.PortableExecutable;
using System.Runtime.InteropServices;
using FileVersionInfo = Microsoft.Diagnostics.Runtime.Utilities.FileVersionInfo;

namespace Microsoft.Diagnostics.DebugServices.Implementation
{
Expand All @@ -36,7 +37,6 @@ public enum Flags : byte
private Flags _flags;
private PdbFileInfo _pdbFileInfo;
private ImmutableArray<byte> _buildId;
private VersionData _versionData;
private PEImage _peImage;

public readonly ServiceProvider ServiceProvider;
Expand Down Expand Up @@ -105,16 +105,26 @@ public bool? IsFileLayout
{
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;
}
}

Expand Down Expand Up @@ -147,23 +157,28 @@ public ImmutableArray<byte> BuildId
}
}

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)
{
Expand All @@ -178,7 +193,7 @@ protected void GetVersionFromVersionString()
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)
{
Expand All @@ -187,12 +202,14 @@ protected void GetVersionFromVersionString()
}
}
}

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;
}
Expand Down
Loading

0 comments on commit 30dc811

Please sign in to comment.