Skip to content

Adding GetILForModule cDAC API #118546

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
74 changes: 74 additions & 0 deletions docs/design/datacontracts/Loader.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ TargetPointer GetModule(ModuleHandle handle);
TargetPointer GetAssembly(ModuleHandle handle);
TargetPointer GetPEAssembly(ModuleHandle handle);
bool TryGetLoadedImageContents(ModuleHandle handle, out TargetPointer baseAddress, out uint size, out uint imageFlags);
TargetPointer ILoader.GetILAddr(TargetPointer peAssemblyPtr, int rva);
bool TryGetSymbolStream(ModuleHandle handle, out TargetPointer buffer, out uint size);
IEnumerable<TargetPointer> GetAvailableTypeParams(ModuleHandle handle);
IEnumerable<TargetPointer> GetInstantiatedMethods(ModuleHandle handle);
Expand Down Expand Up @@ -136,6 +137,7 @@ TargetPointer GetStubHeap(TargetPointer loaderAllocatorPointer);
| `ArrayListBlock` | `Next` | Next ArrayListBlock in chain |
| `ArrayListBlock` | `Size` | Size of data section in block |
| `ArrayListBlock` | `ArrayStart` | Start of data section in block |
| `SystemDomain` | `GlobalLoaderAllocator` | global LoaderAllocator |
| `EETypeHashTable` | `Buckets` | Pointer to hash table buckets |
| `EETypeHashTable` | `Count` | Count of elements in the hash table |
| `EETypeHashTable` | `VolatileEntryValue` | The data stored in the hash table entry |
Expand Down Expand Up @@ -170,6 +172,11 @@ private enum ModuleFlags_1 : uint
EditAndContinue = 0x00000008, // Edit and Continue is enabled for this module
ReflectionEmit = 0x00000040, // Reflection.Emit was used to create this module
}

private enum PEImageFlags : uint
{
FLAG_MAPPED = 0x01, // the file is mapped/hydrated (vs. the raw disk layout)
};
```

### Method Implementations
Expand Down Expand Up @@ -333,6 +340,73 @@ bool TryGetLoadedImageContents(ModuleHandle handle, out TargetPointer baseAddres
return true;
}

TargetPointer ILoader.GetILAddr(TargetPointer peAssemblyPtr, int rva)
{
TargetPointer peImage = target.ReadPointer(peAssemblyPtr + /* PEAssembly::PEImage offset */);
if(peImage == TargetPointer.Null)
throw new InvalidOperationException("PEAssembly does not have a PEImage associated with it.");

TargetPointer peImageLayout = target.ReadPointer(peImage + /* PEImage::LoadedImageLayout offset */);
if(peImageLayout == TargetPointer.Null)
throw new InvalidOperationException("PEImage does not have a LoadedImageLayout associated with it.");

// Get base address and flags from PEImageLayout
TargetPointer baseAddress = target.ReadPointer(peImageLayout + /* PEImageLayout::Base offset */);
uint imageFlags = target.Read<uint>(peImageLayout + /* PEImageLayout::Flags offset */);

bool isMapped = (imageFlags & (uint)PEImageFlags.FLAG_MAPPED) != 0;

uint offset;
if (isMapped)
{
offset = (uint)rva;
}
else
{
// find NT headers using DOS header
uint dosHeaderLfanew = target.Read<uint>(baseAddress + /* ImageDosHeader::LfanewOffset */);
TargetPointer ntHeadersPtr = baseAddress + dosHeaderLfanew;

TargetPointer optionalHeaderPtr = ntHeadersPtr + /* ImageNTHeaders::OptionalHeaderOffset */;

// Get number of sections from file header
TargetPointer fileHeaderPtr = ntHeadersPtr + /* ImageNTHeaders::FileHeaderOffset */;
uint numberOfSections = target.Read<uint>(fileHeaderPtr + /* ImageFileHeader::NumberOfSectionsOffset */);

// Calculate first section address (after NT headers and optional header)
uint imageFileHeaderSize = target.Read<ushort>(fileHeaderPtr + /* ImageFileHeader::SizeOfOptionalHeaderOffset */);
TargetPointer firstSectionPtr = ntHeadersPtr + /* ImageNTHeaders::OptionalHeaderOffset */ + imageFileHeaderSize;

// Find the section containing this RVA
TargetPointer sectionPtr = TargetPointer.Null;
uint sectionHeaderSize = /* sizeof(ImageSectionHeader native struct) */;

for (uint i = 0; i < numberOfSections; i++)
{
TargetPointer currentSectionPtr = firstSectionPtr + (i * sectionHeaderSize);
uint virtualAddress = target.Read<uint>(currentSectionPtr + /* ImageSectionHeader::VirtualAddressOffset */);
uint sizeOfRawData = target.Read<uint>(currentSectionPtr + /* ImageSectionHeader::SizeOfRawDataOffset */);

if (rva >= VirtualAddress && rva < VirtualAddress + SizeOfRawData)
{
sectionPtr = currentSectionPtr;
}
}
if (sectionPtr == TargetPointer.Null)
{
throw new InvalidOperationException("Failed to read from image.");
}
else
{
// Convert RVA to file offset using section information
uint sectionVirtualAddress = target.Read<uint>(sectionPtr + /* ImageSectionHeader::VirtualAddressOffset */);
uint sectionPointerToRawData = target.Read<uint>(sectionPtr + /* ImageSectionHeader::PointerToRawDataOffset */);
offset = ((rva - sectionVirtualAddress) + sectionPointerToRawData);
}
}
return baseAddress + offset;
}

bool TryGetSymbolStream(ModuleHandle handle, out TargetPointer buffer, out uint size)
{
buffer = TargetPointer.Null;
Expand Down
4 changes: 4 additions & 0 deletions src/coreclr/vm/datadescriptor/datadescriptor.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#ifndef HOST_WINDOWS
#include "../pal/inc/pal.h"
#include "../pal/inc/rt/ntimage.h"
#endif // HOST_WINDOWS
#include "common.h"

#include <stdint.h>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ public interface ILoader : IContract
TargetPointer GetAssembly(ModuleHandle handle) => throw new NotImplementedException();
TargetPointer GetPEAssembly(ModuleHandle handle) => throw new NotImplementedException();
bool TryGetLoadedImageContents(ModuleHandle handle, out TargetPointer baseAddress, out uint size, out uint imageFlags) => throw new NotImplementedException();
TargetPointer GetILAddr(TargetPointer peAssemblyPtr, int rva) => throw new NotImplementedException();
bool TryGetSymbolStream(ModuleHandle handle, out TargetPointer buffer, out uint size) => throw new NotImplementedException();
IEnumerable<TargetPointer> GetAvailableTypeParams(ModuleHandle handle) => throw new NotImplementedException();
IEnumerable<TargetPointer> GetInstantiatedMethods(ModuleHandle handle) => throw new NotImplementedException();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,14 @@ public abstract class Target
/// <returns>Value read from the target</returns>
public abstract T Read<T>(ulong address) where T : unmanaged, IBinaryInteger<T>, IMinMaxValue<T>;

/// <summary>
/// Read a value from the target in little endianness
/// </summary>
/// <typeparam name="T">Type of value to read</typeparam>
/// <param name="address">Address to start reading from</param>
/// <returns>Value read from the target</returns>
public abstract T ReadLittleEndian<T>(ulong address) where T : unmanaged, IBinaryInteger<T>, IMinMaxValue<T>;

/// <summary>
/// Read a value from the target in target endianness
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ private enum ModuleFlags_1 : uint
BeingUnloaded = 0x100000,
}

private enum PEImageFlags : uint
{
FLAG_MAPPED = 0x01, // the file is mapped/hydrated (vs. the raw disk layout)
};
private readonly Target _target;

internal Loader_1(Target target)
Expand Down Expand Up @@ -196,6 +200,64 @@ bool ILoader.TryGetLoadedImageContents(ModuleHandle handle, out TargetPointer ba
return true;
}

private static bool IsMapped(Data.PEImageLayout peImageLayout)
{
return (peImageLayout.Flags & (uint)PEImageFlags.FLAG_MAPPED) != 0;
}

private TargetPointer FindNTHeaders(Data.PEImageLayout imageLayout)
{
Data.ImageDosHeader dosHeader = _target.ProcessedData.GetOrAdd<Data.ImageDosHeader>(imageLayout.Base);
return imageLayout.Base + (uint)dosHeader.Lfanew;
}

private TargetPointer RvaToSection(int rva, Data.PEImageLayout imageLayout)
{
TargetPointer ntHeadersPtr = FindNTHeaders(imageLayout);
Data.ImageNTHeaders ntHeaders = _target.ProcessedData.GetOrAdd<Data.ImageNTHeaders>(ntHeadersPtr);
int offset = Data.ImageNTHeaders.OptionalHeaderOffset;
TargetPointer section = ntHeadersPtr + (uint)offset + ntHeaders.FileHeader.SizeOfOptionalHeader;
TargetPointer sectionEnd = section + Data.ImageSectionHeader.Size * ntHeaders.FileHeader.NumberOfSections;
while (section < sectionEnd)
{
Data.ImageSectionHeader sectionHeader = _target.ProcessedData.GetOrAdd<Data.ImageSectionHeader>(section);
if (rva >= sectionHeader.VirtualAddress && rva < sectionHeader.VirtualAddress + sectionHeader.SizeOfRawData)
{
return section;
}
section += Data.ImageSectionHeader.Size;
}
return TargetPointer.Null;
}

private uint RvaToOffset(int rva, Data.PEImageLayout imageLayout)
{
TargetPointer section = RvaToSection(rva, imageLayout);
if (section == TargetPointer.Null)
throw new InvalidOperationException("Failed to read from image.");

Data.ImageSectionHeader sectionHeader = _target.ProcessedData.GetOrAdd<Data.ImageSectionHeader>(section);
uint offset = (uint)(rva - sectionHeader.VirtualAddress) + sectionHeader.PointerToRawData;
return offset;
}

TargetPointer ILoader.GetILAddr(TargetPointer peAssemblyPtr, int rva)
{
Data.PEAssembly assembly = _target.ProcessedData.GetOrAdd<Data.PEAssembly>(peAssemblyPtr);
if (assembly.PEImage == TargetPointer.Null)
throw new InvalidOperationException("PEAssembly does not have a PEImage associated with it.");
Data.PEImage peImage = _target.ProcessedData.GetOrAdd<Data.PEImage>(assembly.PEImage);
if (peImage.LoadedImageLayout == TargetPointer.Null)
throw new InvalidOperationException("PEImage does not have a LoadedImageLayout associated with it.");
Data.PEImageLayout peImageLayout = _target.ProcessedData.GetOrAdd<Data.PEImageLayout>(peImage.LoadedImageLayout);
uint offset;
if (IsMapped(peImageLayout))
offset = (uint)rva;
else
offset = RvaToOffset(rva, peImageLayout);
return peImageLayout.Base + offset;
}

bool ILoader.TryGetSymbolStream(ModuleHandle handle, out TargetPointer buffer, out uint size)
{
buffer = TargetPointer.Null;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace Microsoft.Diagnostics.DataContractReader.Data;

internal sealed class ImageDosHeader : IData<ImageDosHeader>
{
static ImageDosHeader IData<ImageDosHeader>.Create(Target target, TargetPointer address)
=> new ImageDosHeader(target, address);
private const int LfanewOffset = 60;

public ImageDosHeader(Target target, TargetPointer address)
{
Lfanew = target.ReadLittleEndian<int>(address + LfanewOffset);
}
public int Lfanew { get; init; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;

namespace Microsoft.Diagnostics.DataContractReader.Data;

internal sealed class ImageFileHeader : IData<ImageFileHeader>
{
static ImageFileHeader IData<ImageFileHeader>.Create(Target target, TargetPointer address) => new ImageFileHeader(target, address);
private const int NumberOfSectionsOffset = 2;
private const int SizeOfOptionalHeaderOffset = 16;
public ImageFileHeader(Target target, TargetPointer address)
{
NumberOfSections = target.ReadLittleEndian<ushort>(address + NumberOfSectionsOffset);
SizeOfOptionalHeader = target.ReadLittleEndian<ushort>(address + SizeOfOptionalHeaderOffset);
}
public ushort NumberOfSections { get; init; }
public ushort SizeOfOptionalHeader { get; init; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;

namespace Microsoft.Diagnostics.DataContractReader.Data;

internal sealed class ImageNTHeaders : IData<ImageNTHeaders>
{
static ImageNTHeaders IData<ImageNTHeaders>.Create(Target target, TargetPointer address) => new ImageNTHeaders(target, address);
public const int FileHeaderOffset = 4;
public const int OptionalHeaderOffset = 24;
public ImageNTHeaders(Target target, TargetPointer address)
{
FileHeader = target.ProcessedData.GetOrAdd<ImageFileHeader>(address + FileHeaderOffset);
OptionalHeader = target.ProcessedData.GetOrAdd<ImageOptionalHeader>(address + OptionalHeaderOffset);
}
public ImageFileHeader FileHeader { get; init; }
public ImageOptionalHeader OptionalHeader { get; init; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;

namespace Microsoft.Diagnostics.DataContractReader.Data;

internal sealed class ImageOptionalHeader : IData<ImageOptionalHeader>
{
static ImageOptionalHeader IData<ImageOptionalHeader>.Create(Target target, TargetPointer address) => new ImageOptionalHeader(target, address);
private const int SectionAlignmentOffset = 32;
public ImageOptionalHeader(Target target, TargetPointer address)
{
SectionAlignment = target.ReadLittleEndian<uint>(address + SectionAlignmentOffset);
}
public uint SectionAlignment { get; init; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;

namespace Microsoft.Diagnostics.DataContractReader.Data;

internal sealed class ImageSectionHeader : IData<ImageSectionHeader>
{
static ImageSectionHeader IData<ImageSectionHeader>.Create(Target target, TargetPointer address) => new ImageSectionHeader(target, address);
private const int VirtualSizeOffset = 8;
private const int VirtualAddressOffset = 12;
private const int SizeOfRawDataOffset = 16;
private const int PointerToRawDataOffset = 20;
public const uint Size = 40;
public ImageSectionHeader(Target target, TargetPointer address)
{
VirtualSize = target.ReadLittleEndian<uint>(address + VirtualSizeOffset);
VirtualAddress = target.ReadLittleEndian<uint>(address + VirtualAddressOffset);
SizeOfRawData = target.ReadLittleEndian<uint>(address + SizeOfRawDataOffset);
PointerToRawData = target.ReadLittleEndian<uint>(address + PointerToRawDataOffset);
}

public uint VirtualSize { get; init; }
public uint VirtualAddress { get; init; }
public uint SizeOfRawData { get; init; }
public uint PointerToRawData { get; init; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,20 @@ public override T Read<T>(ulong address)
return value;
}

/// <summary>
/// Read a value from the target in little endianness
/// </summary>
/// <typeparam name="T">Type of value to read</typeparam>
/// <param name="address">Address to start reading from</param>
/// <returns>Value read from the target</returns>
public override T ReadLittleEndian<T>(ulong address)
{
if (!TryRead(address, true, _dataTargetDelegates, out T value))
throw new InvalidOperationException($"Failed to read {typeof(T)} at 0x{address:x8}.");

return value;
}

/// <summary>
/// Read a value from the target in target endianness
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -663,7 +663,43 @@ int ISOSDacInterface.GetHillClimbingLogEntry(ClrDataAddress addr, void* data)
return hr;
}
int ISOSDacInterface.GetILForModule(ClrDataAddress moduleAddr, int rva, ClrDataAddress* il)
=> _legacyImpl is not null ? _legacyImpl.GetILForModule(moduleAddr, rva, il) : HResults.E_NOTIMPL;
{
int hr = HResults.S_OK;
if (moduleAddr == 0 || il == null)
{
hr = HResults.E_INVALIDARG;
}
else if (rva == 0)
*il = 0;
else
{
try
{
Contracts.ILoader loader = _target.Contracts.Loader;
TargetPointer module = moduleAddr.ToTargetPointer(_target);
Contracts.ModuleHandle moduleHandle = loader.GetModuleHandleFromModulePtr(module);
TargetPointer peAssemblyPtr = loader.GetPEAssembly(moduleHandle);
*il = loader.GetILAddr(peAssemblyPtr, rva).ToClrDataAddress(_target);
}
catch (System.Exception ex)
{
hr = ex.HResult;
}
}
#if DEBUG
if (_legacyImpl is not null)
{
ClrDataAddress ilLocal;
int hrLocal = _legacyImpl.GetILForModule(moduleAddr, rva, &ilLocal);
Debug.Assert(hrLocal == hr, $"cDAC: {hr:x}, DAC: {hrLocal:x}");
if (hr == HResults.S_OK)
{
Debug.Assert(*il == ilLocal, $"cDAC: {*il:x}, DAC: {ilLocal:x}");
}
}
#endif
return hr;
}
int ISOSDacInterface.GetJitHelperFunctionName(ClrDataAddress ip, uint count, byte* name, uint* pNeeded)
=> _legacyImpl is not null ? _legacyImpl.GetJitHelperFunctionName(ip, count, name, pNeeded) : HResults.E_NOTIMPL;
int ISOSDacInterface.GetJitManagerList(uint count, void* managers, uint* pNeeded)
Expand Down
Loading