mirror of
https://github.com/VSadov/Satori.git
synced 2025-06-08 03:27:04 +09:00
[cdac] Implement ISOSDacInterface::GetObjectData getting COM RCW/CCW (#105846)
- Add `SyncTableEntry`, `SyncBlock`, `InteropSyncBlockInfo` data descriptors, `SyncTableEntries` global - Get the sync block corresponding to a given object address in `Object` contract - Add `GetComData` to `Object` contract - Finish implementation of `ISOSDacInterface::GetObjectData` such that it populates the RCW/CCW - Add test helpers for mocking out sync blocks
This commit is contained in:
parent
a5635ff00c
commit
c5a89d4429
14 changed files with 360 additions and 14 deletions
|
@ -13,6 +13,9 @@ string GetStringValue(TargetPointer address);
|
|||
|
||||
// Get the pointer to the data corresponding to a managed array object. Error if address does not represent a array.
|
||||
TargetPointer GetArrayData(TargetPointer address, out uint count, out TargetPointer boundsStart, out TargetPointer lowerBounds);
|
||||
|
||||
// Get built-in COM data for the object if available. Returns false, if address does not represent a COM object using built-in COM
|
||||
bool GetBuiltInComData(TargetPointer address, out TargetPointer rcw, out TargetPointer ccw);
|
||||
```
|
||||
|
||||
## Version 1
|
||||
|
@ -21,9 +24,13 @@ Data descriptors used:
|
|||
| Data Descriptor Name | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| `Array` | `m_NumComponents` | Number of items in the array |
|
||||
| `InteropSyncBlockInfo` | `RCW` | Pointer to the RCW for the object (if it exists) |
|
||||
| `InteropSyncBlockInfo` | `CCW` | Pointer to the CCW for the object (if it exists) |
|
||||
| `Object` | `m_pMethTab` | Method table for the object |
|
||||
| `String` | `m_FirstChar` | First character of the string - `m_StringLength` can be used to read the full string (encoded in UTF-16) |
|
||||
| `String` | `m_StringLength` | Length of the string in characters (encoded in UTF-16) |
|
||||
| `SyncBlock` | `InteropInfo` | Optional `InteropSyncBlockInfo` for the sync block |
|
||||
| `SyncTableEntry` | `SyncBlock` | `SyncBlock` corresponding to the entry |
|
||||
|
||||
Global variables used:
|
||||
| Global Name | Type | Purpose |
|
||||
|
@ -32,6 +39,8 @@ Global variables used:
|
|||
| `ObjectHeaderSize` | uint32 | Size of the object header (sync block and alignment) |
|
||||
| `ObjectToMethodTableUnmask` | uint8 | Bits to clear for converting to a method table address |
|
||||
| `StringMethodTable` | TargetPointer | The method table for System.String |
|
||||
| `SyncTableEntries` | TargetPointer | The `SyncTableEntry` list |
|
||||
| `SyncBlockValueToObjectOffset` | uint16 | Offset from the sync block value (in the object header) to the object itself |
|
||||
|
||||
Contracts used:
|
||||
| Contract Name |
|
||||
|
@ -91,4 +100,29 @@ TargetPointer GetArrayData(TargetPointer address, out uint count, out TargetPoin
|
|||
ulong dataOffset = typeSystemContract.GetBaseSize(typeHandle) - _target.ReadGlobal<uint>("ObjectHeaderSize");
|
||||
return address + dataOffset;
|
||||
}
|
||||
|
||||
bool GetBuiltInComData(TargetPointer address, out TargetPointer rcw, out TargetPointer ccw);
|
||||
{
|
||||
uint syncBlockValue = target.Read<uint>(address - _target.ReadGlobal<ushort>("SyncBlockValueToObjectOffset"));
|
||||
|
||||
// Check if the sync block value represents a sync block index
|
||||
if ((syncBlockValue & (uint)(SyncBlockValue.Bits.IsHashCodeOrSyncBlockIndex | SyncBlockValue.Bits.IsHashCode)) != (uint)SyncBlockValue.Bits.IsHashCodeOrSyncBlockIndex)
|
||||
return false;
|
||||
|
||||
// Get the offset into the sync table entries
|
||||
uint index = syncBlockValue & SyncBlockValue.SyncBlockIndexMask;
|
||||
ulong offsetInSyncTableEntries = index * /* SyncTableEntry size */;
|
||||
|
||||
TargetPointer syncBlock = target.ReadPointer(_syncTableEntries + offsetInSyncTableEntries + /* SyncTableEntry::SyncBlock offset */);
|
||||
if (syncBlock == TargetPointer.Null)
|
||||
return false;
|
||||
|
||||
TargetPointer interopInfo = target.ReadPointer(syncBlock + /* SyncTableEntry::InteropInfo offset */);
|
||||
if (interopInfo == TargetPointer.Null)
|
||||
return false;
|
||||
|
||||
rcw = target.ReadPointer(interopInfo + /* InteropSyncBlockInfo::RCW offset */);
|
||||
ccw = target.ReadPointer(interopInfo + /* InteropSyncBlockInfo::CCW offset */);
|
||||
return rcw != TargetPointer.Null && ccw != TargetPointer.Null;
|
||||
}
|
||||
```
|
||||
|
|
|
@ -71,6 +71,7 @@ TADDR DACGetMethodTableFromObjectPointer(TADDR objAddr, ICorDebugDataTarget * ta
|
|||
ULONG32 returned = 0;
|
||||
TADDR Value = (TADDR)NULL;
|
||||
|
||||
// Every object has a pointer to its method table at offset 0
|
||||
HRESULT hr = target->ReadVirtual(objAddr, (PBYTE)&Value, sizeof(TADDR), &returned);
|
||||
|
||||
if ((hr != S_OK) || (returned != sizeof(TADDR)))
|
||||
|
@ -92,6 +93,8 @@ PTR_SyncBlock DACGetSyncBlockFromObjectPointer(TADDR objAddr, ICorDebugDataTarge
|
|||
ULONG32 returned = 0;
|
||||
DWORD Value = 0;
|
||||
|
||||
// Every object has an object header right before it. The sync block value (DWORD) is the last member
|
||||
// of the object header. Read the DWORD right before the object address to get the sync block value.
|
||||
HRESULT hr = target->ReadVirtual(objAddr - sizeof(DWORD), (PBYTE)&Value, sizeof(DWORD), &returned);
|
||||
|
||||
if ((hr != S_OK) || (returned != sizeof(DWORD)))
|
||||
|
|
|
@ -41,6 +41,8 @@
|
|||
// then the field layout can be specified as
|
||||
// CDAC_TYPE_FIELD(MyClassLayout, pointer, MyField, cdac_data<MyClass>::MyField)
|
||||
// There can be zero or more CDAC_TYPE_FIELD entries per type layout
|
||||
// For types mapping to managed objects, use exact managed type field names in the descriptor, as
|
||||
// field names often can't change due to binary serialization or implicit diagnostic contracts
|
||||
//
|
||||
// CDAC_TYPE_END(cdacTypeIdentifier) specifies the end of the type layout for cdacTypeIdentifier
|
||||
//
|
||||
|
@ -172,6 +174,8 @@ CDAC_TYPE_BEGIN(GCHandle)
|
|||
CDAC_TYPE_SIZE(sizeof(OBJECTHANDLE))
|
||||
CDAC_TYPE_END(GCHandle)
|
||||
|
||||
// Object
|
||||
|
||||
CDAC_TYPE_BEGIN(Object)
|
||||
CDAC_TYPE_INDETERMINATE(Object)
|
||||
CDAC_TYPE_FIELD(Object, /*pointer*/, m_pMethTab, cdac_data<Object>::m_pMethTab)
|
||||
|
@ -188,6 +192,24 @@ CDAC_TYPE_SIZE(sizeof(ArrayBase))
|
|||
CDAC_TYPE_FIELD(Array, /*pointer*/, m_NumComponents, cdac_data<ArrayBase>::m_NumComponents)
|
||||
CDAC_TYPE_END(Array)
|
||||
|
||||
CDAC_TYPE_BEGIN(InteropSyncBlockInfo)
|
||||
CDAC_TYPE_INDETERMINATE(InteropSyncBlockInfo)
|
||||
#ifdef FEATURE_COMINTEROP
|
||||
CDAC_TYPE_FIELD(InteropSyncBlockInfo, /*pointer*/, CCW, cdac_data<InteropSyncBlockInfo>::CCW)
|
||||
CDAC_TYPE_FIELD(InteropSyncBlockInfo, /*pointer*/, RCW, cdac_data<InteropSyncBlockInfo>::RCW)
|
||||
#endif // FEATURE_COMINTEROP
|
||||
CDAC_TYPE_END(InteropSyncBlockInfo)
|
||||
|
||||
CDAC_TYPE_BEGIN(SyncBlock)
|
||||
CDAC_TYPE_INDETERMINATE(SyncBlock)
|
||||
CDAC_TYPE_FIELD(SyncBlock, /*pointer*/, InteropInfo, cdac_data<SyncBlock>::InteropInfo)
|
||||
CDAC_TYPE_END(SyncBlock)
|
||||
|
||||
CDAC_TYPE_BEGIN(SyncTableEntry)
|
||||
CDAC_TYPE_SIZE(sizeof(SyncTableEntry))
|
||||
CDAC_TYPE_FIELD(SyncTableEntry, /*pointer*/, SyncBlock, offsetof(SyncTableEntry, m_SyncBlock))
|
||||
CDAC_TYPE_END(SyncTableEntry)
|
||||
|
||||
// Loader
|
||||
|
||||
CDAC_TYPE_BEGIN(Module)
|
||||
|
@ -310,12 +332,14 @@ CDAC_GLOBAL(ObjectToMethodTableUnmask, uint8, 1 | 1 << 1)
|
|||
CDAC_GLOBAL(SOSBreakingChangeVersion, uint8, SOS_BREAKING_CHANGE_VERSION)
|
||||
CDAC_GLOBAL(MethodDescAlignment, uint64, MethodDesc::ALIGNMENT)
|
||||
CDAC_GLOBAL(ObjectHeaderSize, uint64, OBJHEADER_SIZE)
|
||||
CDAC_GLOBAL(SyncBlockValueToObjectOffset, uint16, OBJHEADER_SIZE - cdac_data<ObjHeader>::SyncBlockValue)
|
||||
CDAC_GLOBAL_POINTER(ArrayBoundsZero, cdac_data<ArrayBase>::ArrayBoundsZero)
|
||||
CDAC_GLOBAL_POINTER(ExceptionMethodTable, &::g_pExceptionClass)
|
||||
CDAC_GLOBAL_POINTER(FreeObjectMethodTable, &::g_pFreeObjectMethodTable)
|
||||
CDAC_GLOBAL_POINTER(ObjectMethodTable, &::g_pObjectClass)
|
||||
CDAC_GLOBAL_POINTER(ObjectArrayMethodTable, &::g_pPredefinedArrayTypes[ELEMENT_TYPE_OBJECT])
|
||||
CDAC_GLOBAL_POINTER(StringMethodTable, &::g_pStringClass)
|
||||
CDAC_GLOBAL_POINTER(SyncTableEntries, &::g_pSyncTable)
|
||||
CDAC_GLOBAL_POINTER(MiniMetaDataBuffAddress, &::g_MiniMetaDataBuffAddress)
|
||||
CDAC_GLOBAL_POINTER(MiniMetaDataBuffMaxSize, &::g_MiniMetaDataBuffMaxSize)
|
||||
CDAC_GLOBALS_END()
|
||||
|
|
|
@ -969,6 +969,17 @@ private:
|
|||
// ObjectiveCMarshal.NativeAot.cs
|
||||
BYTE m_taggedAlloc[2 * sizeof(void*)];
|
||||
#endif // FEATURE_OBJCMARSHAL
|
||||
|
||||
template<typename T> friend struct ::cdac_data;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct cdac_data<InteropSyncBlockInfo>
|
||||
{
|
||||
#ifdef FEATURE_COMINTEROP
|
||||
static constexpr size_t CCW = offsetof(InteropSyncBlockInfo, m_pCCW);
|
||||
static constexpr size_t RCW = offsetof(InteropSyncBlockInfo, m_pRCW);
|
||||
#endif // FEATURE_COMINTEROP
|
||||
};
|
||||
|
||||
typedef DPTR(InteropSyncBlockInfo) PTR_InteropSyncBlockInfo;
|
||||
|
@ -1274,6 +1285,14 @@ class SyncBlock
|
|||
return m_Monitor.GetPtrForLockContract();
|
||||
}
|
||||
#endif // defined(ENABLE_CONTRACTS_IMPL)
|
||||
|
||||
template<typename T> friend struct ::cdac_data;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct cdac_data<SyncBlock>
|
||||
{
|
||||
static constexpr size_t InteropInfo = offsetof(SyncBlock, m_pInteropInfo);
|
||||
};
|
||||
|
||||
class SyncTableEntry
|
||||
|
@ -1654,8 +1673,15 @@ class ObjHeader
|
|||
void ReleaseSpinLock();
|
||||
|
||||
BOOL Validate (BOOL bVerifySyncBlkIndex = TRUE);
|
||||
|
||||
template<typename T> friend struct ::cdac_data;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct cdac_data<ObjHeader>
|
||||
{
|
||||
static constexpr size_t SyncBlockValue = offsetof(ObjHeader, m_SyncBlockValue);
|
||||
};
|
||||
|
||||
typedef DPTR(class ObjHeader) PTR_ObjHeader;
|
||||
|
||||
|
|
|
@ -30,6 +30,9 @@ internal static class Constants
|
|||
|
||||
internal const string MethodDescAlignment = nameof(MethodDescAlignment);
|
||||
internal const string ObjectHeaderSize = nameof(ObjectHeaderSize);
|
||||
internal const string SyncBlockValueToObjectOffset = nameof(SyncBlockValueToObjectOffset);
|
||||
|
||||
internal const string SyncTableEntries = nameof(SyncTableEntries);
|
||||
|
||||
internal const string ArrayBoundsZero = nameof(ArrayBoundsZero);
|
||||
}
|
||||
|
|
|
@ -14,9 +14,11 @@ internal interface IObject : IContract
|
|||
byte objectToMethodTableUnmask = target.ReadGlobal<byte>(Constants.Globals.ObjectToMethodTableUnmask);
|
||||
TargetPointer stringMethodTable = target.ReadPointer(
|
||||
target.ReadGlobalPointer(Constants.Globals.StringMethodTable));
|
||||
TargetPointer syncTableEntries = target.ReadPointer(
|
||||
target.ReadGlobalPointer(Constants.Globals.SyncTableEntries));
|
||||
return version switch
|
||||
{
|
||||
1 => new Object_1(target, methodTableOffset, objectToMethodTableUnmask, stringMethodTable),
|
||||
1 => new Object_1(target, methodTableOffset, objectToMethodTableUnmask, stringMethodTable, syncTableEntries),
|
||||
_ => default(Object),
|
||||
};
|
||||
}
|
||||
|
@ -25,6 +27,7 @@ internal interface IObject : IContract
|
|||
|
||||
public virtual string GetStringValue(TargetPointer address) => throw new NotImplementedException();
|
||||
public virtual TargetPointer GetArrayData(TargetPointer address, out uint count, out TargetPointer boundsStart, out TargetPointer lowerBounds) => throw new NotImplementedException();
|
||||
public virtual bool GetBuiltInComData(TargetPointer address, out TargetPointer rcw, out TargetPointer ccw) => throw new NotImplementedException();
|
||||
}
|
||||
|
||||
internal readonly struct Object : IObject
|
||||
|
|
|
@ -11,15 +11,32 @@ internal readonly struct Object_1 : IObject
|
|||
{
|
||||
private readonly Target _target;
|
||||
private readonly ulong _methodTableOffset;
|
||||
private readonly TargetPointer _stringMethodTable;
|
||||
private readonly byte _objectToMethodTableUnmask;
|
||||
private readonly TargetPointer _stringMethodTable;
|
||||
private readonly TargetPointer _syncTableEntries;
|
||||
|
||||
internal Object_1(Target target, ulong methodTableOffset, byte objectToMethodTableUnmask, TargetPointer stringMethodTable)
|
||||
private static class SyncBlockValue
|
||||
{
|
||||
[Flags]
|
||||
public enum Bits
|
||||
{
|
||||
// Value represents either the hash code or sync block index (bits 0-25)
|
||||
// - IsHashCodeOrSyncBlockIndex and IsHashCode are set: rest of the value is the hash code.
|
||||
// - IsHashCodeOrSyncBlockIndex set, IsHashCode not set: rest of the value is the sync block index
|
||||
IsHashCodeOrSyncBlockIndex = 0x08000000,
|
||||
IsHashCode = 0x04000000,
|
||||
}
|
||||
|
||||
public const uint SyncBlockIndexMask = (1 << 26) - 1;
|
||||
}
|
||||
|
||||
internal Object_1(Target target, ulong methodTableOffset, byte objectToMethodTableUnmask, TargetPointer stringMethodTable, TargetPointer syncTableEntries)
|
||||
{
|
||||
_target = target;
|
||||
_methodTableOffset = methodTableOffset;
|
||||
_stringMethodTable = stringMethodTable;
|
||||
_objectToMethodTableUnmask = objectToMethodTableUnmask;
|
||||
_syncTableEntries = syncTableEntries;
|
||||
}
|
||||
|
||||
public TargetPointer GetMethodTableAddress(TargetPointer address)
|
||||
|
@ -76,4 +93,37 @@ internal readonly struct Object_1 : IObject
|
|||
ulong dataOffset = typeSystemContract.GetBaseSize(typeHandle) - _target.ReadGlobal<uint>(Constants.Globals.ObjectHeaderSize);
|
||||
return address + dataOffset;
|
||||
}
|
||||
|
||||
public bool GetBuiltInComData(TargetPointer address, out TargetPointer rcw, out TargetPointer ccw)
|
||||
{
|
||||
rcw = TargetPointer.Null;
|
||||
ccw = TargetPointer.Null;
|
||||
|
||||
Data.SyncBlock? syncBlock = GetSyncBlock(address);
|
||||
if (syncBlock == null)
|
||||
return false;
|
||||
|
||||
Data.InteropSyncBlockInfo? interopInfo = syncBlock.InteropInfo;
|
||||
if (interopInfo == null)
|
||||
return false;
|
||||
|
||||
rcw = interopInfo.RCW;
|
||||
ccw = interopInfo.CCW;
|
||||
return rcw != TargetPointer.Null || ccw != TargetPointer.Null;
|
||||
}
|
||||
|
||||
private Data.SyncBlock? GetSyncBlock(TargetPointer address)
|
||||
{
|
||||
uint syncBlockValue = _target.Read<uint>(address - _target.ReadGlobal<ushort>(Constants.Globals.SyncBlockValueToObjectOffset));
|
||||
|
||||
// Check if the sync block value represents a sync block index
|
||||
if ((syncBlockValue & (uint)(SyncBlockValue.Bits.IsHashCodeOrSyncBlockIndex | SyncBlockValue.Bits.IsHashCode)) != (uint)SyncBlockValue.Bits.IsHashCodeOrSyncBlockIndex)
|
||||
return null;
|
||||
|
||||
// Get the offset into the sync table entries
|
||||
uint index = syncBlockValue & SyncBlockValue.SyncBlockIndexMask;
|
||||
ulong offsetInSyncTableEntries = index * (ulong)_target.GetTypeInfo(DataType.SyncTableEntry).Size!;
|
||||
Data.SyncTableEntry entry = _target.ProcessedData.GetOrAdd<Data.SyncTableEntry>(_syncTableEntries + offsetInSyncTableEntries);
|
||||
return entry.SyncBlock;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
// 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 InteropSyncBlockInfo : IData<InteropSyncBlockInfo>
|
||||
{
|
||||
static InteropSyncBlockInfo IData<InteropSyncBlockInfo>.Create(Target target, TargetPointer address)
|
||||
=> new InteropSyncBlockInfo(target, address);
|
||||
|
||||
public InteropSyncBlockInfo(Target target, TargetPointer address)
|
||||
{
|
||||
Target.TypeInfo type = target.GetTypeInfo(DataType.InteropSyncBlockInfo);
|
||||
|
||||
RCW = type.Fields.TryGetValue(nameof(RCW), out Target.FieldInfo rcwField)
|
||||
? target.ReadPointer(address + (ulong)rcwField.Offset)
|
||||
: TargetPointer.Null;
|
||||
CCW = type.Fields.TryGetValue(nameof(CCW), out Target.FieldInfo ccwField)
|
||||
? target.ReadPointer(address + (ulong)ccwField.Offset)
|
||||
: TargetPointer.Null;
|
||||
}
|
||||
|
||||
public TargetPointer RCW { get; init; }
|
||||
public TargetPointer CCW { get; init; }
|
||||
}
|
21
src/native/managed/cdacreader/src/Data/SyncBlock.cs
Normal file
21
src/native/managed/cdacreader/src/Data/SyncBlock.cs
Normal file
|
@ -0,0 +1,21 @@
|
|||
// 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 SyncBlock : IData<SyncBlock>
|
||||
{
|
||||
static SyncBlock IData<SyncBlock>.Create(Target target, TargetPointer address)
|
||||
=> new SyncBlock(target, address);
|
||||
|
||||
public SyncBlock(Target target, TargetPointer address)
|
||||
{
|
||||
Target.TypeInfo type = target.GetTypeInfo(DataType.SyncBlock);
|
||||
|
||||
TargetPointer interopInfoPointer = target.ReadPointer(address + (ulong)type.Fields[nameof(InteropInfo)].Offset);
|
||||
if (interopInfoPointer != TargetPointer.Null)
|
||||
InteropInfo = target.ProcessedData.GetOrAdd<InteropSyncBlockInfo>(interopInfoPointer);
|
||||
}
|
||||
|
||||
public InteropSyncBlockInfo? InteropInfo { get; init; }
|
||||
}
|
21
src/native/managed/cdacreader/src/Data/SyncTableEntry.cs
Normal file
21
src/native/managed/cdacreader/src/Data/SyncTableEntry.cs
Normal file
|
@ -0,0 +1,21 @@
|
|||
// 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 SyncTableEntry : IData<SyncTableEntry>
|
||||
{
|
||||
static SyncTableEntry IData<SyncTableEntry>.Create(Target target, TargetPointer address)
|
||||
=> new SyncTableEntry(target, address);
|
||||
|
||||
public SyncTableEntry(Target target, TargetPointer address)
|
||||
{
|
||||
Target.TypeInfo type = target.GetTypeInfo(DataType.SyncTableEntry);
|
||||
|
||||
TargetPointer syncBlockPointer = target.ReadPointer(address + (ulong)type.Fields[nameof(SyncBlock)].Offset);
|
||||
if (syncBlockPointer != TargetPointer.Null)
|
||||
SyncBlock = target.ProcessedData.GetOrAdd<SyncBlock>(syncBlockPointer);
|
||||
}
|
||||
|
||||
public SyncBlock? SyncBlock { get; init; }
|
||||
}
|
|
@ -42,4 +42,7 @@ public enum DataType
|
|||
MethodDesc,
|
||||
MethodDescChunk,
|
||||
Array,
|
||||
SyncBlock,
|
||||
SyncTableEntry,
|
||||
InteropSyncBlockInfo,
|
||||
}
|
||||
|
|
|
@ -374,9 +374,13 @@ internal sealed partial class SOSDacImpl : ISOSDacInterface, ISOSDacInterface2,
|
|||
data->ObjectType = DacpObjectType.OBJ_OTHER;
|
||||
}
|
||||
|
||||
// TODO: [cdac] Get RCW and CCW from interop info on sync block
|
||||
if (_target.ReadGlobal<byte>(Constants.Globals.FeatureCOMInterop) != 0)
|
||||
return HResults.E_NOTIMPL;
|
||||
// Populate COM data if this is a COM object
|
||||
if (_target.ReadGlobal<byte>(Constants.Globals.FeatureCOMInterop) != 0
|
||||
&& objectContract.GetBuiltInComData(objAddr, out TargetPointer rcw, out TargetPointer ccw))
|
||||
{
|
||||
data->RCW = rcw;
|
||||
data->CCW = ccw;
|
||||
}
|
||||
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
|
|
|
@ -66,6 +66,28 @@ public class MockDescriptors
|
|||
},
|
||||
};
|
||||
|
||||
private static readonly Target.TypeInfo SyncTableEntryInfo = new Target.TypeInfo()
|
||||
{
|
||||
Fields = {
|
||||
{ nameof(Data.SyncTableEntry.SyncBlock), new() { Offset = 0, Type = DataType.pointer} },
|
||||
},
|
||||
};
|
||||
|
||||
private static readonly Target.TypeInfo SyncBlockTypeInfo = new Target.TypeInfo()
|
||||
{
|
||||
Fields = {
|
||||
{ nameof(Data.SyncBlock.InteropInfo), new() { Offset = 0, Type = DataType.pointer} },
|
||||
},
|
||||
};
|
||||
|
||||
private static readonly Target.TypeInfo InteropSyncBlockTypeInfo = new Target.TypeInfo()
|
||||
{
|
||||
Fields = {
|
||||
{ nameof(Data.InteropSyncBlockInfo.RCW), new() { Offset = 0, Type = DataType.pointer} },
|
||||
{ nameof(Data.InteropSyncBlockInfo.CCW), new() { Offset = 0x8, Type = DataType.pointer} },
|
||||
},
|
||||
};
|
||||
|
||||
public static class RuntimeTypeSystem
|
||||
{
|
||||
internal const ulong TestFreeObjectMethodTableGlobalAddress = 0x00000000_7a0000a0;
|
||||
|
@ -144,30 +166,42 @@ public class MockDescriptors
|
|||
|
||||
public static class Object
|
||||
{
|
||||
const ulong TestStringMethodTableGlobalAddress = 0x00000000_100000a0;
|
||||
const ulong TestStringMethodTableAddress = 0x00000000_100000a8;
|
||||
private const ulong TestStringMethodTableGlobalAddress = 0x00000000_100000a0;
|
||||
private const ulong TestStringMethodTableAddress = 0x00000000_100000a8;
|
||||
internal const ulong TestArrayBoundsZeroGlobalAddress = 0x00000000_100000b0;
|
||||
|
||||
internal static Dictionary<DataType, Target.TypeInfo> Types(TargetTestHelpers helpers) => RuntimeTypeSystem.Types.Concat(
|
||||
new Dictionary<DataType, Target.TypeInfo>(){
|
||||
[DataType.Object] = ObjectTypeInfo,
|
||||
[DataType.String] = StringTypeInfo,
|
||||
[DataType.Array] = ArrayTypeInfo with { Size = helpers.ArrayBaseSize }
|
||||
}).ToDictionary();
|
||||
private const ulong TestSyncTableEntriesGlobalAddress = 0x00000000_100000c0;
|
||||
private const ulong TestSyncTableEntriesAddress = 0x00000000_f0000000;
|
||||
|
||||
internal const ulong TestObjectToMethodTableUnmask = 0x7;
|
||||
internal const ulong TestSyncBlockValueToObjectOffset = sizeof(uint);
|
||||
|
||||
internal static Dictionary<DataType, Target.TypeInfo> Types(TargetTestHelpers helpers) => RuntimeTypeSystem.Types.Concat(
|
||||
new Dictionary<DataType, Target.TypeInfo>()
|
||||
{
|
||||
[DataType.Object] = ObjectTypeInfo,
|
||||
[DataType.String] = StringTypeInfo,
|
||||
[DataType.Array] = ArrayTypeInfo with { Size = helpers.ArrayBaseSize },
|
||||
[DataType.SyncTableEntry] = SyncTableEntryInfo with { Size = (uint)helpers.SizeOfTypeInfo(SyncTableEntryInfo) },
|
||||
[DataType.SyncBlock] = SyncBlockTypeInfo,
|
||||
[DataType.InteropSyncBlockInfo] = InteropSyncBlockTypeInfo,
|
||||
}).ToDictionary();
|
||||
|
||||
internal static (string Name, ulong Value, string? Type)[] Globals(TargetTestHelpers helpers) => RuntimeTypeSystem.Globals.Concat(
|
||||
[
|
||||
(nameof(Constants.Globals.ObjectToMethodTableUnmask), TestObjectToMethodTableUnmask, "uint8"),
|
||||
(nameof(Constants.Globals.StringMethodTable), TestStringMethodTableGlobalAddress, null),
|
||||
(nameof(Constants.Globals.ArrayBoundsZero), TestArrayBoundsZeroGlobalAddress, null),
|
||||
(nameof(Constants.Globals.SyncTableEntries), TestSyncTableEntriesGlobalAddress, null),
|
||||
(nameof(Constants.Globals.ObjectHeaderSize), helpers.ObjHeaderSize, "uint32"),
|
||||
(nameof(Constants.Globals.SyncBlockValueToObjectOffset), TestSyncBlockValueToObjectOffset, "uint16"),
|
||||
]).ToArray();
|
||||
|
||||
internal static MockMemorySpace.Builder AddGlobalPointers(TargetTestHelpers targetTestHelpers, MockMemorySpace.Builder builder)
|
||||
{
|
||||
builder = RuntimeTypeSystem.AddGlobalPointers(targetTestHelpers, builder);
|
||||
builder = AddStringMethodTablePointer(targetTestHelpers, builder);
|
||||
builder = AddSyncTableEntriesPointer(targetTestHelpers, builder);
|
||||
return builder;
|
||||
}
|
||||
|
||||
|
@ -181,6 +215,13 @@ public class MockDescriptors
|
|||
]);
|
||||
}
|
||||
|
||||
private static MockMemorySpace.Builder AddSyncTableEntriesPointer(TargetTestHelpers targetTestHelpers, MockMemorySpace.Builder builder)
|
||||
{
|
||||
MockMemorySpace.HeapFragment fragment = new() { Name = "Address of Sync Table Entries", Address = TestSyncTableEntriesGlobalAddress, Data = new byte[targetTestHelpers.PointerSize] };
|
||||
targetTestHelpers.WritePointer(fragment.Data, TestSyncTableEntriesAddress);
|
||||
return builder.AddHeapFragment(fragment);
|
||||
}
|
||||
|
||||
internal static MockMemorySpace.Builder AddObject(TargetTestHelpers targetTestHelpers, MockMemorySpace.Builder builder, TargetPointer address, TargetPointer methodTable)
|
||||
{
|
||||
MockMemorySpace.HeapFragment fragment = new() { Name = $"Object : MT = '{methodTable}'", Address = address, Data = new byte[targetTestHelpers.SizeOfTypeInfo(ObjectTypeInfo)] };
|
||||
|
@ -189,6 +230,56 @@ public class MockDescriptors
|
|||
return builder.AddHeapFragment(fragment);
|
||||
}
|
||||
|
||||
internal static MockMemorySpace.Builder AddObjectWithSyncBlock(TargetTestHelpers targetTestHelpers, MockMemorySpace.Builder builder, TargetPointer address, TargetPointer methodTable, uint syncBlockIndex, TargetPointer rcw, TargetPointer ccw)
|
||||
{
|
||||
const uint IsSyncBlockIndexBits = 0x08000000;
|
||||
const uint SyncBlockIndexMask = (1 << 26) - 1;
|
||||
if ((syncBlockIndex & SyncBlockIndexMask) != syncBlockIndex)
|
||||
throw new ArgumentOutOfRangeException(nameof(syncBlockIndex), "Invalid sync block index");
|
||||
|
||||
builder = AddObject(targetTestHelpers, builder, address, methodTable);
|
||||
|
||||
// Add the sync table value before the object
|
||||
uint syncTableValue = IsSyncBlockIndexBits | syncBlockIndex;
|
||||
TargetPointer syncTableValueAddr = address - TestSyncBlockValueToObjectOffset;
|
||||
MockMemorySpace.HeapFragment fragment = new() { Name = $"Sync Table Value : index = {syncBlockIndex}", Address = syncTableValueAddr, Data = new byte[sizeof(uint)] };
|
||||
targetTestHelpers.Write(fragment.Data, syncTableValue);
|
||||
builder = builder.AddHeapFragment(fragment);
|
||||
|
||||
// Add the actual sync block and associated data
|
||||
return AddSyncBlock(targetTestHelpers, builder, syncBlockIndex, rcw, ccw);
|
||||
}
|
||||
|
||||
private static MockMemorySpace.Builder AddSyncBlock(TargetTestHelpers targetTestHelpers, MockMemorySpace.Builder builder, uint index, TargetPointer rcw, TargetPointer ccw)
|
||||
{
|
||||
// Tests write the sync blocks starting at TestSyncBlocksAddress
|
||||
const ulong TestSyncBlocksAddress = 0x00000000_e0000000;
|
||||
int syncBlockSize = targetTestHelpers.SizeOfTypeInfo(SyncBlockTypeInfo);
|
||||
int interopSyncBlockInfoSize = targetTestHelpers.SizeOfTypeInfo(InteropSyncBlockTypeInfo);
|
||||
ulong syncBlockAddr = TestSyncBlocksAddress + index * (ulong)(syncBlockSize + interopSyncBlockInfoSize);
|
||||
|
||||
// Add the sync table entry - pointing at the sync block
|
||||
uint syncTableEntrySize = (uint)targetTestHelpers.SizeOfTypeInfo(SyncTableEntryInfo);
|
||||
ulong syncTableEntryAddr = TestSyncTableEntriesAddress + index * syncTableEntrySize;
|
||||
MockMemorySpace.HeapFragment syncTableEntry = new() { Name = $"SyncTableEntries[{index}]", Address = syncTableEntryAddr, Data = new byte[syncTableEntrySize] };
|
||||
Span<byte> syncTableEntryData = syncTableEntry.Data;
|
||||
targetTestHelpers.WritePointer(syncTableEntryData.Slice(SyncTableEntryInfo.Fields[nameof(Data.SyncTableEntry.SyncBlock)].Offset), syncBlockAddr);
|
||||
|
||||
// Add the sync block - pointing at the interop sync block info
|
||||
ulong interopInfoAddr = syncBlockAddr + (ulong)syncBlockSize;
|
||||
MockMemorySpace.HeapFragment syncBlock = new() { Name = $"Sync Block", Address = syncBlockAddr, Data = new byte[syncBlockSize] };
|
||||
Span<byte> syncBlockData = syncBlock.Data;
|
||||
targetTestHelpers.WritePointer(syncBlockData.Slice(SyncBlockTypeInfo.Fields[nameof(Data.SyncBlock.InteropInfo)].Offset), interopInfoAddr);
|
||||
|
||||
// Add the interop sync block info
|
||||
MockMemorySpace.HeapFragment interopInfo = new() { Name = $"Interop Sync Block Info", Address = interopInfoAddr, Data = new byte[interopSyncBlockInfoSize] };
|
||||
Span<byte> interopInfoData = interopInfo.Data;
|
||||
targetTestHelpers.WritePointer(interopInfoData.Slice(InteropSyncBlockTypeInfo.Fields[nameof(Data.InteropSyncBlockInfo.RCW)].Offset), rcw);
|
||||
targetTestHelpers.WritePointer(interopInfoData.Slice(InteropSyncBlockTypeInfo.Fields[nameof(Data.InteropSyncBlockInfo.CCW)].Offset), ccw);
|
||||
|
||||
return builder.AddHeapFragments([syncTableEntry, syncBlock, interopInfo]);
|
||||
}
|
||||
|
||||
internal static MockMemorySpace.Builder AddStringObject(TargetTestHelpers targetTestHelpers, MockMemorySpace.Builder builder, TargetPointer address, string value)
|
||||
{
|
||||
int size = targetTestHelpers.SizeOfTypeInfo(ObjectTypeInfo) + targetTestHelpers.SizeOfTypeInfo(StringTypeInfo) + value.Length * sizeof(char);
|
||||
|
|
|
@ -159,4 +159,42 @@ public unsafe class ObjectTests
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[ClassData(typeof(MockTarget.StdArch))]
|
||||
public void ComData(MockTarget.Architecture arch)
|
||||
{
|
||||
const ulong TestComObjectAddress = 0x00000000_10000010;
|
||||
const ulong TestNonComObjectAddress = 0x00000000_10000020;
|
||||
|
||||
TargetPointer expectedRCW = 0xaaaa;
|
||||
TargetPointer expectedCCW = 0xbbbb;
|
||||
|
||||
TargetTestHelpers targetTestHelpers = new(arch);
|
||||
ObjectContractHelper(arch,
|
||||
(builder) =>
|
||||
{
|
||||
uint syncBlockIndex = 0;
|
||||
builder = MockObject.AddObjectWithSyncBlock(targetTestHelpers, builder, TestComObjectAddress, 0, syncBlockIndex++, expectedRCW, expectedCCW);
|
||||
builder = MockObject.AddObjectWithSyncBlock(targetTestHelpers, builder, TestNonComObjectAddress, 0, syncBlockIndex++, TargetPointer.Null, TargetPointer.Null);
|
||||
return builder;
|
||||
},
|
||||
(target) =>
|
||||
{
|
||||
Contracts.IObject contract = target.Contracts.Object;
|
||||
Assert.NotNull(contract);
|
||||
{
|
||||
bool res = contract.GetBuiltInComData(TestComObjectAddress, out TargetPointer rcw, out TargetPointer ccw);
|
||||
Assert.True(res);
|
||||
Assert.Equal(expectedRCW.Value, rcw.Value);
|
||||
Assert.Equal(expectedCCW.Value, ccw.Value);
|
||||
}
|
||||
{
|
||||
bool res = contract.GetBuiltInComData(TestNonComObjectAddress, out TargetPointer rcw, out TargetPointer ccw);
|
||||
Assert.False(res);
|
||||
Assert.Equal(TargetPointer.Null.Value, rcw.Value);
|
||||
Assert.Equal(TargetPointer.Null.Value, ccw.Value);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue