Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.IO;
using System.Numerics;
using System.Reflection.Metadata;
using System.Reflection.PortableExecutable;
using System.Runtime.InteropServices;

namespace Microsoft.Diagnostics.DataContractReader.Contracts;
Expand All @@ -16,11 +17,26 @@ internal sealed class EcmaMetadata_1(Target target) : IEcmaMetadata

public TargetSpan GetReadOnlyMetadataAddress(ModuleHandle handle)
{
ILoader loader = target.Contracts.Loader;
Data.Module module = target.ProcessedData.GetOrAdd<Data.Module>(handle.Address);

TargetPointer baseAddress = module.GetLoadedMetadata(out ulong size);
if (!loader.TryGetLoadedImageContents(handle, out _, out _, out uint imageFlags))
{
throw new InvalidOperationException("Module is not loaded.");
}
bool isMapped = (imageFlags & 0x1) != 0; // FLAG_MAPPED = 0x1
PEStreamOptions isLoaded = isMapped ? PEStreamOptions.IsLoadedImage : PEStreamOptions.Default;

// Stream is backed by Target's read and doesn't have a definable size limit.
// For this use case we can use int.MaxValue to allow PEReader to read as much as it wants.
// As long as PEStreamOptions.PrefetchEntireImage is not set, PEReader will not read the entire stream.
TargetStream stream = new(target, module.Base, int.MaxValue);
using PEReader peReader = new PEReader(stream, PEStreamOptions.PrefetchMetadata | isLoaded);

int metadataStartOffset = peReader.PEHeaders.MetadataStartOffset;
int metadataSize = peReader.PEHeaders.MetadataSize;

return new TargetSpan(baseAddress, size);
return new TargetSpan(module.Base + (ulong)metadataStartOffset, (ulong)metadataSize);
}

public MetadataReader? GetMetadata(ModuleHandle handle)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,8 @@ internal sealed class Module : IData<Module>
static Module IData<Module>.Create(Target target, TargetPointer address)
=> new Module(target, address);

private readonly Target _target;

public Module(Target target, TargetPointer address)
{
_target = target;
Target.TypeInfo type = target.GetTypeInfo(DataType.Module);

Flags = target.Read<uint>(address + (ulong)type.Fields[nameof(Flags)].Offset);
Expand Down Expand Up @@ -55,54 +52,4 @@ public Module(Target target, TargetPointer address)
public TargetPointer TypeDefToMethodTableMap { get; init; }
public TargetPointer TypeRefToMethodTableMap { get; init; }
public TargetPointer MethodDefToILCodeVersioningStateMap { get; init; }

private TargetPointer _metadataStart = TargetPointer.Null;
private ulong _metadataSize;
public TargetPointer GetLoadedMetadata(out ulong size)
{
if (Base != TargetPointer.Null && _metadataStart == TargetPointer.Null && _metadataSize == 0)
{
int peSignatureOffset = _target.Read<int>(Base + PEFormat.DosStub.PESignatureOffset);
ulong headerOffset = Base + (ulong)peSignatureOffset;
ushort magic = _target.Read<ushort>(headerOffset + PEFormat.PEHeader.MagicOffset);
ulong clrHeaderOffset = magic == (ushort)PEMagic.PE32
? PEFormat.PEHeader.CLRRuntimeHeader32Offset
: PEFormat.PEHeader.CLRRuntimeHeader32PlusOffset;
int corHeaderRva = _target.Read<int>(headerOffset + clrHeaderOffset);

// Read RVA and size of the metadata
ulong metadataDirectoryAddress = Base + (ulong)corHeaderRva + PEFormat.CorHeader.MetadataOffset;
_metadataStart = Base + (ulong)_target.Read<int>(metadataDirectoryAddress);
_metadataSize = (ulong)_target.Read<int>(metadataDirectoryAddress + sizeof(int));
}

size = _metadataSize;
return _metadataStart;
}

// https://learn.microsoft.com/windows/win32/debug/pe-format
private static class PEFormat
{
private const int PESignatureSize = sizeof(int);
private const int CoffHeaderSize = 20;

public static class DosStub
{
public const int PESignatureOffset = 0x3c;
}

public static class PEHeader
{
private const ulong OptionalHeaderOffset = PESignatureSize + CoffHeaderSize;
public const ulong MagicOffset = OptionalHeaderOffset;
public const ulong CLRRuntimeHeader32Offset = OptionalHeaderOffset + 208;
public const ulong CLRRuntimeHeader32PlusOffset = OptionalHeaderOffset + 224;
}

// See ECMA-335 II.25.3.3 CLI Header
public static class CorHeader
{
public const ulong MetadataOffset = 8;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.IO;

namespace Microsoft.Diagnostics.DataContractReader;

internal class TargetStream(Target target, ulong startPosition, long size) : Stream
{
private readonly ulong _startPosition = startPosition;
private long _offset;
private readonly long _size = size;
private readonly Target _target = target;

private ulong GlobalPosition => _startPosition + (ulong)_offset;

public override bool CanRead => true;

public override bool CanSeek => true;

public override bool CanWrite => false;

public override long Length => _size;

public override long Position { get => _offset; set => _offset = value; }

public override unsafe int Read(byte[] buffer, int offset, int count)
{
Span<byte> span = buffer;
return Read(span.Slice(start: offset, length: count));
}
public override unsafe int Read(Span<byte> buffer)
{
_target.ReadBuffer(GlobalPosition, buffer);
_offset += buffer.Length;
return buffer.Length;
}
public override long Seek(long offset, SeekOrigin origin)
{
switch (origin)
{
case SeekOrigin.Begin:
_offset = offset;
break;
case SeekOrigin.Current:
_offset += offset;
break;
case SeekOrigin.End:
throw new NotSupportedException();
}
return _offset;
}

public override void Flush() => throw new NotImplementedException();
public override void SetLength(long value) => throw new NotImplementedException();
public override void Write(byte[] buffer, int offset, int count) => throw new NotImplementedException();
}
Loading