From 8db62837e46d355bb5d0890ba09e9b2dc48a1f90 Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Tue, 16 Apr 2024 22:08:30 -0700 Subject: [PATCH] Read contract descriptor from target --- src/native/managed/cdacreader/src/Target.cs | 115 ++++++++++++++++++-- 1 file changed, 104 insertions(+), 11 deletions(-) diff --git a/src/native/managed/cdacreader/src/Target.cs b/src/native/managed/cdacreader/src/Target.cs index 898cab774bfb5..3b4a88c8ce5ac 100644 --- a/src/native/managed/cdacreader/src/Target.cs +++ b/src/native/managed/cdacreader/src/Target.cs @@ -16,49 +16,142 @@ public struct TargetPointer internal sealed unsafe class Target { + private static readonly ReadOnlyMemory MagicLE = new byte[] { 0x44, 0x4e, 0x43, 0x43, 0x44, 0x41, 0x43, 0x00 }; // "DNCCDAC\0" + private static readonly ReadOnlyMemory MagicBE = new byte[] { 0x00, 0x43, 0x41, 0x44, 0x43, 0x43, 0x4e, 0x44 }; + private readonly delegate* unmanaged _readFromTarget; private readonly void* _readContext; private bool _isLittleEndian; private int _pointerSize; - public Target(ulong _, delegate* unmanaged readFromTarget, void* readContext) + private TargetPointer[] _pointerData = []; + + public Target(ulong contractDescriptor, delegate* unmanaged readFromTarget, void* readContext) { _readFromTarget = readFromTarget; _readContext = readContext; - // TODO: [cdac] Populate from descriptor - _isLittleEndian = BitConverter.IsLittleEndian; - _pointerSize = IntPtr.Size; + ReadContractDescriptor(contractDescriptor); + } + + // See docs/design/datacontracts/contract-descriptor.md + private void ReadContractDescriptor(ulong address) + { + // Magic - uint64_t + Span buffer = stackalloc byte[sizeof(ulong)]; + if (ReadFromTarget(address, buffer) < 0) + throw new InvalidOperationException("Failed to read magic."); + + address += sizeof(ulong); + _isLittleEndian = buffer.SequenceEqual(MagicLE.Span); + if (!_isLittleEndian && !buffer.SequenceEqual(MagicBE.Span)) + throw new InvalidOperationException("Invalid magic."); + + // Flags - uint32_t + uint flags = ReadUInt32(address); + address += sizeof(uint); + + // Bit 1 represents the pointer size. 0 = 64-bit, 1 = 32-bit. + _pointerSize = (int)(flags & 0x2) == 0 ? sizeof(ulong) : sizeof(uint); + + // Descriptor size - uint32_t + uint descriptorSize = ReadUInt32(address); + address += sizeof(uint); + + // Descriptor - char* + TargetPointer descriptor = ReadPointer(address); + address += (uint)_pointerSize; + + // Pointer data count - uint32_t + uint pointerDataCount = ReadUInt32(address); + address += sizeof(uint); + + // Padding + address += sizeof(uint); + + // Pointer data - uintptr_t* + TargetPointer pointerData = ReadPointer(address); + + // Read descriptor + // TODO: [cdac] Pass to JSON parser + Span descriptorBuffer = stackalloc byte[(int)descriptorSize]; + if (ReadFromTarget(descriptor.Value, descriptorBuffer) < 0) + throw new InvalidOperationException("Failed to read descriptor."); + + // Read pointer data + _pointerData = new TargetPointer[pointerDataCount]; + for (int i = 0; i < pointerDataCount; i++) + { + _pointerData[i] = ReadPointer(pointerData.Value + (uint)(i * _pointerSize)); + } + } + + public uint ReadUInt32(ulong address) + { + if (!TryReadUInt32(address, out uint value)) + throw new InvalidOperationException($"Failed to read uint32 at 0x{address:x8}."); + + return value; + } + + public bool TryReadUInt32(ulong address, out uint value) + { + value = 0; + + Span buffer = stackalloc byte[sizeof(uint)]; + if (ReadFromTarget(address, buffer) < 0) + return false; + + value = _isLittleEndian + ? BinaryPrimitives.ReadUInt32LittleEndian(buffer) + : BinaryPrimitives.ReadUInt32BigEndian(buffer); + + return true; + } + + public TargetPointer ReadPointer(ulong address) + { + if (!TryReadPointer(address, out TargetPointer pointer)) + throw new InvalidOperationException($"Failed to read pointer at 0x{address:x8}."); + + return pointer; } public bool TryReadPointer(ulong address, out TargetPointer pointer) { pointer = TargetPointer.Null; - byte* buffer = stackalloc byte[_pointerSize]; - ReadOnlySpan span = new ReadOnlySpan(buffer, _pointerSize); - if (ReadFromTarget(address, buffer, (uint)_pointerSize) < 0) + Span buffer = stackalloc byte[_pointerSize]; + if (ReadFromTarget(address, buffer) < 0) return false; if (_pointerSize == sizeof(uint)) { pointer = new TargetPointer( _isLittleEndian - ? BinaryPrimitives.ReadUInt32LittleEndian(span) - : BinaryPrimitives.ReadUInt32BigEndian(span)); + ? BinaryPrimitives.ReadUInt32LittleEndian(buffer) + : BinaryPrimitives.ReadUInt32BigEndian(buffer)); } else if (_pointerSize == sizeof(ulong)) { pointer = new TargetPointer( _isLittleEndian - ? BinaryPrimitives.ReadUInt64LittleEndian(span) - : BinaryPrimitives.ReadUInt64BigEndian(span)); + ? BinaryPrimitives.ReadUInt64LittleEndian(buffer) + : BinaryPrimitives.ReadUInt64BigEndian(buffer)); } return true; } + private int ReadFromTarget(ulong address, Span buffer) + { + fixed (byte* bufferPtr = buffer) + { + return _readFromTarget(address, bufferPtr, (uint)buffer.Length, _readContext); + } + } + private int ReadFromTarget(ulong address, byte* buffer, uint bytesToRead) => _readFromTarget(address, buffer, bytesToRead, _readContext); }