Skip to content

Commit

Permalink
Read contract descriptor from target
Browse files Browse the repository at this point in the history
  • Loading branch information
elinor-fung committed Apr 19, 2024
1 parent 9fdbbe7 commit 8db6283
Showing 1 changed file with 104 additions and 11 deletions.
115 changes: 104 additions & 11 deletions src/native/managed/cdacreader/src/Target.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,49 +16,142 @@ public struct TargetPointer

internal sealed unsafe class Target
{
private static readonly ReadOnlyMemory<byte> MagicLE = new byte[] { 0x44, 0x4e, 0x43, 0x43, 0x44, 0x41, 0x43, 0x00 }; // "DNCCDAC\0"
private static readonly ReadOnlyMemory<byte> MagicBE = new byte[] { 0x00, 0x43, 0x41, 0x44, 0x43, 0x43, 0x4e, 0x44 };

private readonly delegate* unmanaged<ulong, byte*, uint, void*, int> _readFromTarget;
private readonly void* _readContext;

private bool _isLittleEndian;
private int _pointerSize;

public Target(ulong _, delegate* unmanaged<ulong, byte*, uint, void*, int> readFromTarget, void* readContext)
private TargetPointer[] _pointerData = [];

public Target(ulong contractDescriptor, delegate* unmanaged<ulong, byte*, uint, void*, int> 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<byte> 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<byte> 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<byte> 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<byte> span = new ReadOnlySpan<byte>(buffer, _pointerSize);
if (ReadFromTarget(address, buffer, (uint)_pointerSize) < 0)
Span<byte> 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<byte> 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);
}

0 comments on commit 8db6283

Please sign in to comment.