// 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;
using Microsoft.Diagnostics.Runtime.Utilities;
using Microsoft.FileFormats;
using Microsoft.FileFormats.ELF;
private readonly IDisposable _onChangeEvent;
private Flags _flags;
private PdbFileInfo _pdbFileInfo;
- private ImmutableArray<byte> _buildId;
+ protected ImmutableArray<byte> _buildId;
private PEImage _peImage;
public readonly ServiceProvider ServiceProvider;
}
}
- public ImmutableArray<byte> BuildId
+ public virtual ImmutableArray<byte> BuildId
{
get
{
if (_buildId.IsDefault)
{
- byte[] id = ModuleService.GetBuildId(ImageBase, ImageSize);
+ byte[] id = ModuleService.GetBuildId(ImageBase);
if (id != null)
{
_buildId = id.ToImmutableArray();
/// Returns the ELF module build id or the MachO module uuid
/// </summary>
/// <param name="address">module base address</param>
- /// <param name="size">module size</param>
/// <returns>build id or null</returns>
- internal byte[] GetBuildId(ulong address, ulong size)
+ internal byte[] GetBuildId(ulong address)
{
- Debug.Assert(size > 0);
- Stream stream = MemoryService.CreateMemoryStream(address, size);
+ Stream stream = MemoryService.CreateMemoryStream();
byte[] buildId = null;
try
{
if (Target.OperatingSystem == OSPlatform.Linux)
{
- var elfFile = new ELFFile(new StreamAddressSpace(stream), 0, true);
+ var elfFile = new ELFFile(new StreamAddressSpace(stream), address, true);
if (elfFile.IsValid())
{
buildId = elfFile.BuildID;
}
else if (Target.OperatingSystem == OSPlatform.OSX)
{
- var machOFile = new MachOFile(new StreamAddressSpace(stream), 0, true);
+ var machOFile = new MachOFile(new StreamAddressSpace(stream), address, true);
if (machOFile.IsValid())
{
buildId = machOFile.Uuid;
/// Get the version string from a Linux or MacOS image
/// </summary>
/// <param name="address">image base</param>
- /// <param name="size">image size</param>
/// <returns>version string or null</returns>
- protected string GetVersionString(ulong address, ulong size)
+ protected string GetVersionString(ulong address)
{
- Stream stream = MemoryService.CreateMemoryStream(address, size);
+ Stream stream = MemoryService.CreateMemoryStream();
try
{
if (Target.OperatingSystem == OSPlatform.Linux)
{
- var elfFile = new ELFFile(new StreamAddressSpace(stream), 0, true);
+ var elfFile = new ELFFile(new StreamAddressSpace(stream), address, true);
if (elfFile.IsValid())
{
foreach (ELFProgramHeader programHeader in elfFile.Segments.Select((segment) => segment.Header))
}
else if (Target.OperatingSystem == OSPlatform.OSX)
{
- var machOFile = new MachOFile(new StreamAddressSpace(stream), 0, true);
+ var machOFile = new MachOFile(new StreamAddressSpace(stream), address, true);
if (machOFile.IsValid())
{
foreach (MachSegmentLoadCommand loadCommand in machOFile.Segments.Select((segment) => segment.LoadCommand))
(loadCommand.InitProt & VmProtWrite) != 0 &&
loadCommand.SegName.ToString() != "__LINKEDIT")
{
- ulong loadAddress = loadCommand.VMAddress;
+ ulong loadAddress = loadCommand.VMAddress + machOFile.PreferredVMBaseAddress;
long loadSize = (long)loadCommand.VMSize;
- if (SearchVersionString(address + loadAddress, loadSize, out string productVersion))
+ if (SearchVersionString(loadAddress, loadSize, out string productVersion))
{
return productVersion;
}
using Microsoft.Diagnostics.Runtime;
using System;
using System.Collections.Generic;
+using System.Collections.Immutable;
using System.Diagnostics;
-using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
// This is what clrmd returns for non-PE modules that don't have a timestamp
private const uint InvalidTimeStamp = 0;
- private static readonly Microsoft.Diagnostics.Runtime.VersionInfo EmptyVersionInfo = new (0, 0, 0, 0);
+ private static readonly VersionInfo EmptyVersionInfo = new (0, 0, 0, 0);
private readonly ModuleServiceFromDataReader _moduleService;
private readonly IExportReader _exportReader;
private readonly ModuleInfo _moduleInfo;
public override uint? IndexTimeStamp => _moduleInfo.IndexTimeStamp == InvalidTimeStamp ? null : (uint)_moduleInfo.IndexTimeStamp;
+ public override ImmutableArray<byte> BuildId
+ {
+ get
+ {
+ if (_buildId.IsDefault)
+ {
+ ImmutableArray<byte> buildId = _moduleService._dataReader.GetBuildId(ImageBase);
+ // If the data reader can't get the build id, it returns a empty (instead of default) immutable array.
+ _buildId = buildId.IsEmpty ? base.BuildId : buildId;
+ }
+ return _buildId;
+ }
+ }
+
public override VersionData VersionData
{
get
{
if (_moduleService.Target.OperatingSystem != OSPlatform.Windows && !IsPEImage)
{
- _versionString = _moduleService.GetVersionString(ImageBase, ImageSize);
+ _versionString = _moduleService.GetVersionString(ImageBase);
}
}
return _versionString;
ServiceProvider.AddServiceFactory<IModuleService>(() => new ModuleServiceFromDataReader(this, _dataReader));
ServiceProvider.AddServiceFactory<IMemoryService>(() => {
IMemoryService memoryService = new MemoryServiceFromDataReader(_dataReader);
- if (IsDump && Host.HostType == HostType.DotnetDump)
+ if (IsDump)
{
memoryService = new ImageMappingMemoryService(this, memoryService);
+ // Any dump created for a MacOS target does not have managed assemblies in the module service so
+ // we need to use the metadata mapping memory service to make sure the metadata is available.
+ if (targetOS == OSPlatform.OSX)
+ {
+ memoryService = new MetadataMappingMemoryService(this, memoryService);
+ }
}
return memoryService;
});
return false;
}
+ /// <summary>
+ /// Create a stream for all of memory.
+ /// </summary>
+ /// <param name="memoryService">memory service instance</param>
+ /// <returns>Stream of all of memory</returns>
+ public static Stream CreateMemoryStream(this IMemoryService memoryService)
+ {
+ return new TargetStream(memoryService, 0, long.MaxValue);
+ }
+
/// <summary>
/// Create a stream for the address range.
/// </summary>
/// <param name="memoryService">memory service instance</param>
/// <param name="address">address to read</param>
/// <param name="size">size of stream</param>
- /// <returns></returns>
+ /// <returns>memory range Stream</returns>
public static Stream CreateMemoryStream(this IMemoryService memoryService, ulong address, ulong size)
{
+ Debug.Assert(address != 0);
+ Debug.Assert(size != 0);
return new TargetStream(memoryService, address, size);
}
public TargetStream(IMemoryService memoryService, ulong address, ulong size)
: base()
{
- Debug.Assert(address != 0);
- Debug.Assert(size != 0);
_memoryService = memoryService;
_address = address;
Length = (long)size;
// See the LICENSE file in the project root for more information.
using Microsoft.Diagnostics.DebugServices;
+using Microsoft.FileFormats;
+using Microsoft.FileFormats.ELF;
+using Microsoft.FileFormats.MachO;
+using System;
+using System.Diagnostics;
using System.Diagnostics.Tracing;
+using System.IO;
using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text.RegularExpressions;
namespace Microsoft.Diagnostics.ExtensionCommands
{
[Option(Name = "--verbose", Aliases = new string[] { "-v" }, Help = "Displays more details.")]
public bool Verbose { get; set; }
+ [Option(Name = "--segments", Aliases = new string[] { "-s" }, Help = "Displays the module segments.")]
+ public bool Segment { get; set; }
+
+
+ [Option(Name = "--name", Aliases = new string[] { "-n" }, Help = "RegEx filter on module name (path not included).")]
+ public string ModuleName { get; set; }
+
public IModuleService ModuleService { get; set; }
public override void Invoke()
{
+ Regex regex = ModuleName is not null ? new Regex(ModuleName, RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant) : null;
ulong totalSize = 0;
+
foreach (IModule module in ModuleService.EnumerateModules().OrderBy((m) => m.ModuleIndex))
{
totalSize += module.ImageSize;
- if (Verbose)
+ if (regex is null || regex.IsMatch(Path.GetFileName(module.FileName)))
{
- WriteLine("{0} {1}", module.ModuleIndex, module.FileName);
- WriteLine(" Address: {0:X16}", module.ImageBase);
- WriteLine(" ImageSize: {0:X8}", module.ImageSize);
- WriteLine(" IsPEImage: {0}", module.IsPEImage);
- WriteLine(" IsManaged: {0}", module.IsManaged);
- WriteLine(" IsFileLayout: {0}", module.IsFileLayout?.ToString() ?? "<unknown>");
- WriteLine(" IndexFileSize: {0}", module.IndexFileSize?.ToString("X8") ?? "<none>");
- WriteLine(" IndexTimeStamp: {0}", module.IndexTimeStamp?.ToString("X8") ?? "<none>");
- WriteLine(" Version: {0}", module.VersionData?.ToString() ?? "<none>");
- string versionString = module.VersionString;
- if (!string.IsNullOrEmpty(versionString)) {
- WriteLine(" {0}", versionString);
+ if (Verbose)
+ {
+ WriteLine("{0} {1}", module.ModuleIndex, module.FileName);
+ WriteLine(" Address: {0:X16}", module.ImageBase);
+ WriteLine(" ImageSize: {0:X8}", module.ImageSize);
+ WriteLine(" IsPEImage: {0}", module.IsPEImage);
+ WriteLine(" IsManaged: {0}", module.IsManaged);
+ WriteLine(" IsFileLayout: {0}", module.IsFileLayout?.ToString() ?? "<unknown>");
+ WriteLine(" IndexFileSize: {0}", module.IndexFileSize?.ToString("X8") ?? "<none>");
+ WriteLine(" IndexTimeStamp: {0}", module.IndexTimeStamp?.ToString("X8") ?? "<none>");
+ WriteLine(" Version: {0}", module.VersionData?.ToString() ?? "<none>");
+ string versionString = module.VersionString;
+ if (!string.IsNullOrEmpty(versionString))
+ {
+ WriteLine(" {0}", versionString);
+ }
+ WriteLine(" PdbInfo: {0}", module.PdbFileInfo?.ToString() ?? "<none>");
+ WriteLine(" BuildId: {0}", !module.BuildId.IsDefaultOrEmpty ? string.Concat(module.BuildId.Select((b) => b.ToString("x2"))) : "<none>");
+ }
+ else
+ {
+ WriteLine("{0:X16} {1:X8} {2}", module.ImageBase, module.ImageSize, module.FileName);
+ }
+ if (Segment)
+ {
+ DisplaySegments(module.ImageBase);
}
- WriteLine(" PdbInfo: {0}", module.PdbFileInfo?.ToString() ?? "<none>");
- WriteLine(" BuildId: {0}", !module.BuildId.IsDefaultOrEmpty ? string.Concat(module.BuildId.Select((b) => b.ToString("x2"))) : "<none>");
}
- else
+ }
+ WriteLine("Total image size: {0}", totalSize);
+ }
+
+ public ITarget Target { get; set; }
+
+ public IMemoryService MemoryService { get; set; }
+
+ void DisplaySegments(ulong address)
+ {
+ try
+ {
+ if (Target.OperatingSystem == OSPlatform.Linux)
+ {
+ Stream stream = MemoryService.CreateMemoryStream();
+ var elfFile = new ELFFile(new StreamAddressSpace(stream), address, true);
+ if (elfFile.IsValid())
+ {
+ foreach (ELFProgramHeader programHeader in elfFile.Segments.Select((segment) => segment.Header))
+ {
+ uint flags = MemoryService.PointerSize == 8 ? programHeader.Flags : programHeader.Flags32;
+ ulong loadAddress = programHeader.VirtualAddress;
+ ulong loadSize = programHeader.VirtualSize;
+ ulong fileOffset = programHeader.FileOffset;
+ string type = programHeader.Type.ToString();
+ WriteLine($" Segment: {loadAddress:X16} {loadSize:X16} {fileOffset:X16} {flags:x2} {type}");
+ }
+ }
+ }
+ else if (Target.OperatingSystem == OSPlatform.OSX)
{
- WriteLine("{0:X16} {1:X8} {2}", module.ImageBase, module.ImageSize, module.FileName);
+ Stream stream = MemoryService.CreateMemoryStream();
+ MachOFile machOFile = new(new StreamAddressSpace(stream), address, true);
+ if (machOFile.IsValid())
+ {
+ WriteLine(" LoadAddress: {0:X16}", machOFile.LoadAddress);
+ WriteLine(" LoadBias: {0:X16}", machOFile.PreferredVMBaseAddress);
+ for (int i = 0; i < machOFile.Segments.Length; i++)
+ {
+ MachSegment segment = machOFile.Segments[i];
+ ulong loadAddress = segment.LoadCommand.VMAddress;
+ ulong loadSize = segment.LoadCommand.VMSize;
+ ulong fileOffset = segment.LoadCommand.FileOffset;
+ uint prot = segment.LoadCommand.InitProt;
+ string name = segment.LoadCommand.SegName.ToString();
+ WriteLine($" Segment {i}: {loadAddress:X16} {loadSize:X16} {fileOffset:X16} {prot:x2} {name}");
+ }
+ }
}
}
- WriteLine("Total image size: {0}", totalSize);
+ catch (Exception ex) when (ex is InvalidVirtualAddressException || ex is BadInputFormatException)
+ {
+ Trace.TraceError($"Exception displaying module segments: {ex}");
+ }
}
}
}
{
if (_moduleService.Target.OperatingSystem != OSPlatform.Windows && !IsPEImage)
{
- _versionString = _moduleService.GetVersionString(ImageBase, ImageSize);
+ _versionString = _moduleService.GetVersionString(ImageBase);
}
}
}
{
try
{
- Stream stream = MemoryService.CreateMemoryStream(address, size);
KeyGenerator generator = null;
if (config == RuntimeConfiguration.UnixCore)
{
- var elfFile = new ELFFile(new StreamAddressSpace(stream), 0, true);
+ Stream stream = MemoryService.CreateMemoryStream();
+ var elfFile = new ELFFile(new StreamAddressSpace(stream), address, true);
generator = new ELFFileKeyGenerator(Tracer.Instance, elfFile, moduleFilePath);
}
else if (config == RuntimeConfiguration.OSXCore)
{
- var machOFile = new MachOFile(new StreamAddressSpace(stream), 0, true);
+ Stream stream = MemoryService.CreateMemoryStream();
+ var machOFile = new MachOFile(new StreamAddressSpace(stream), address, true);
generator = new MachOFileKeyGenerator(Tracer.Instance, machOFile, moduleFilePath);
}
else if (config == RuntimeConfiguration.WindowsCore || config == RuntimeConfiguration.WindowsDesktop)
{
+ Stream stream = MemoryService.CreateMemoryStream(address, size);
var peFile = new PEFile(new StreamAddressSpace(stream), true);
generator = new PEFileKeyGenerator(Tracer.Instance, peFile, moduleFilePath);
}
}
// Using the dotnet-dump analyze tool if the path exists in the config file.
- // TODO: dotnet-dump currently doesn't support macho core dumps that the MacOS createdump generates
- if (information.TestConfiguration.DotNetDumpPath() != null && OS.Kind != OSKind.OSX)
+ if (information.TestConfiguration.DotNetDumpPath() != null)
{
// Don't test dotnet-dump on triage dumps when running on desktop CLR.
if (information.TestConfiguration.IsNETCore || information.DumpType != SOSRunner.DumpType.Triage)
Assert.NotNull(parameters);
Assert.NotNull(parameters.ExceptionType);
Assert.NotNull(parameters.OSVersion);
- Assert.Equal(parameters.SystemManufacturer, "apple");
+ Assert.Equal("apple", (string)parameters.SystemManufacturer);
}
}
catch (Exception ex)
#include <string.h>
#include <string>
#include <dlfcn.h>
+#if defined(__APPLE__)
+#include <mach-o/loader.h>
+#endif
#include "sosplugin.h"
#include "arrayholder.h"
ULONG64
LLDBServices::GetModuleSize(
+ ULONG64 baseAddress,
/* const */ lldb::SBModule& module)
{
ULONG64 size = 0;
-
+#if defined(__APPLE__)
+ mach_header_64 header;
+ ULONG read;
+ HRESULT hr = ReadVirtual(baseAddress, &header, sizeof(mach_header_64), &read) == S_OK;
+ if (SUCCEEDED(hr))
+ {
+ // Since MachO segments are not contiguous the image size is just the headers/commands
+ size = sizeof(mach_header_64) + header.sizeofcmds;
+ }
+#else
// Find the first section with an valid base address
int numSections = module.GetNumSections();
for (int si = 0; si < numSections; si++)
size += section.GetByteSize();
}
}
+#endif
// For core dumps lldb doesn't return the section sizes when it
// doesn't have access to the actual module file, but SOS (like
// the SymbolReader code) still needs a non-zero module size.
path.append("/");
path.append(filename);
- int moduleSize = GetModuleSize(module);
+ int moduleSize = GetModuleSize(moduleAddress, module);
callback(&module, path.c_str(), moduleAddress, moduleSize);
}
{
return E_INVALIDARG;
}
+ ULONG64 moduleBase = GetModuleBase(target, module);
if (pBase)
{
- ULONG64 moduleBase = GetModuleBase(target, module);
if (moduleBase == UINT64_MAX)
{
return E_INVALIDARG;
}
if (pSize)
{
- *pSize = GetModuleSize(module);
+ *pSize = GetModuleSize(moduleBase, module);
}
if (pTimestamp)
{
ULONG m_cacheSize;
ULONG64 GetModuleBase(lldb::SBTarget& target, lldb::SBModule& module);
- ULONG64 GetModuleSize(lldb::SBModule& module);
+ ULONG64 GetModuleSize(ULONG64 baseAddress, lldb::SBModule& module);
ULONG64 GetExpression(lldb::SBFrame& frame, lldb::SBError& error, PCSTR exp);
void GetContextFromFrame(lldb::SBFrame& frame, DT_CONTEXT *dtcontext);
DWORD_PTR GetRegister(lldb::SBFrame& frame, const char *name);