Skip to content
Draft
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
43 changes: 42 additions & 1 deletion docs/design/datacontracts/Signature.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,26 @@ These tags are used in signatures generated internally by the runtime that are n

```csharp
TypeHandle DecodeFieldSignature(BlobHandle blobHandle, ModuleHandle moduleHandle, TypeHandle ctx);

// Returns the address of the first argument of a vararg call relative to the cookie pointer location.
TargetPointer GetVarArgArgsBase(TargetPointer vaSigCookieAddr);

// Returns the address and length of the raw vararg signature blob held by the cookie.
void GetVarArgSignature(TargetPointer vaSigCookieAddr, out TargetPointer signatureAddress, out uint signatureLength);
```

`vaSigCookieAddr` is the target address of the `VASigCookie*` slot pushed onto the stack by the
vararg call site (i.e. it is a pointer to a pointer to the cookie). `GetVarArgArgsBase` and
`GetVarArgSignature` throw if the address is null or the cookie pointer it references is null.

## Version 1

Data descriptors used:
| Data Descriptor Name | Field | Meaning |
| --- | --- | --- |
| _none_ | | |
| `VASigCookie` | `SizeOfArgs` | Total size in bytes of the pushed argument list. Used on x86 to locate the args base. |
| `VASigCookie` | `SignaturePointer` | Target address of the raw vararg signature blob. |
| `VASigCookie` | `SignatureLength` | Length in bytes of the raw vararg signature blob. |

Global variables used:
| Global Name | Type | Purpose |
Expand All @@ -37,6 +49,7 @@ Contracts used:
| RuntimeTypeSystem |
| Loader |
| EcmaMetadata |
| RuntimeInfo |

Constants:
| Constant Name | Meaning | Value |
Expand Down Expand Up @@ -69,3 +82,31 @@ TypeHandle ISignature.DecodeFieldSignature(BlobHandle blobHandle, ModuleHandle m
### Other consumers

`RuntimeSignatureDecoder` is shared infrastructure within the cDAC. Other contracts construct their own decoder and provider directly when they need to decode method or local signatures rather than going through this contract. For example, the [StackWalk](./StackWalk.md) contract uses `RuntimeSignatureDecoder<GcTypeKind, GcSignatureContext>` with a GC-specific provider to classify method parameters during signature-based GC reference scanning.

### Vararg call cookies

`GetVarArgArgsBase` and `GetVarArgSignature` decode a `VASigCookie*` slot pushed by a vararg call site and are used by the DAC/DBI `GetVarArgSig` API to recover the location of the first argument and the raw vararg signature blob for a vararg call frame.

```csharp
TargetPointer ISignature.GetVarArgArgsBase(TargetPointer vaSigCookieAddr)
{
TargetPointer vaSigCookie = _target.ReadPointer(vaSigCookieAddr);
VASigCookie cookie = _target.ProcessedData.GetOrAdd<VASigCookie>(vaSigCookie);

// On x86 the args are pushed below the cookie pointer (stack grows down on the args walk);
// on every other platform the first argument follows the cookie pointer in memory
// (stack grows up on the args walk).
if (RuntimeInfo.GetTargetArchitecture() == X86)
return vaSigCookieAddr + cookie.SizeOfArgs;
return vaSigCookieAddr + sizeof(TargetPointer);
}

void ISignature.GetVarArgSignature(TargetPointer vaSigCookieAddr, out TargetPointer signatureAddress, out uint signatureLength)
{
TargetPointer vaSigCookie = _target.ReadPointer(vaSigCookieAddr);
VASigCookie cookie = _target.ProcessedData.GetOrAdd<VASigCookie>(vaSigCookie);

signatureAddress = cookie.SignaturePointer;
signatureLength = cookie.SignatureLength;
}
Comment thread
rcj1 marked this conversation as resolved.
```
7 changes: 7 additions & 0 deletions src/coreclr/vm/ceeload.h
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,13 @@ struct VASigCookie
Instantiation methodInst;
};

template<>
struct cdac_data<VASigCookie>
{
static constexpr size_t SignaturePointer = offsetof(VASigCookie, signature) + offsetof(Signature, m_pSig);
static constexpr size_t SignatureLength = offsetof(VASigCookie, signature) + offsetof(Signature, m_cbSig);
};

//
// VASigCookies are allocated in VASigCookieBlocks to amortize
// allocation cost and allow proper bookkeeping.
Expand Down
7 changes: 7 additions & 0 deletions src/coreclr/vm/datadescriptor/datadescriptor.inc
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,13 @@ CDAC_TYPE_FIELD(ArrayListBlock, T_UINT32, Size, cdac_data<ArrayListBase>::Size)
CDAC_TYPE_FIELD(ArrayListBlock, T_POINTER, ArrayStart, cdac_data<ArrayListBase>::ArrayStart)
CDAC_TYPE_END(ArrayListBlock)

CDAC_TYPE_BEGIN(VASigCookie)
CDAC_TYPE_INDETERMINATE(VASigCookie)
CDAC_TYPE_FIELD(VASigCookie, T_UINT32, SizeOfArgs, offsetof(VASigCookie, sizeOfArgs))
CDAC_TYPE_FIELD(VASigCookie, T_POINTER, SignaturePointer, cdac_data<VASigCookie>::SignaturePointer)
CDAC_TYPE_FIELD(VASigCookie, T_UINT32, SignatureLength, cdac_data<VASigCookie>::SignatureLength)
CDAC_TYPE_END(VASigCookie)

// RuntimeTypeSystem

CDAC_TYPE_BEGIN(MethodTable)
Expand Down
2 changes: 2 additions & 0 deletions src/coreclr/vm/siginfo.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,8 @@ class Signature
DWORD GetRawSigLen() const;

private:
friend struct ::cdac_data<VASigCookie>;

PCCOR_SIGNATURE m_pSig;
DWORD m_cbSig;
}; // class Signature
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ public interface ISignature : IContract
{
static string IContract.Name { get; } = nameof(Signature);
TypeHandle DecodeFieldSignature(BlobHandle blobHandle, ModuleHandle moduleHandle, TypeHandle ctx) => throw new NotImplementedException();
TargetPointer GetVarArgArgsBase(TargetPointer vaSigCookieAddr) => throw new NotImplementedException();
void GetVarArgSignature(TargetPointer vaSigCookieAddr, out TargetPointer signatureAddress, out uint signatureLength) => throw new NotImplementedException();
}

public readonly struct Signature : ISignature
Comment thread
rcj1 marked this conversation as resolved.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ public enum DataType
ComInterfaceEntry,
InternalComInterfaceDispatch,
AuxiliarySymbolInfo,
VASigCookie,
CodeRangeMapRangeList,

/* GC Data Types */
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
// 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.Collections.Generic;
using System.Diagnostics;
using System.Reflection.Metadata;
using Microsoft.Diagnostics.DataContractReader.SignatureHelpers;

Expand Down Expand Up @@ -49,4 +51,43 @@ TypeHandle ISignature.DecodeFieldSignature(BlobHandle blobHandle, ModuleHandle m
RuntimeSignatureDecoder<TypeHandle, TypeHandle> decoder = new(provider, _target, mdReader, ctx);
return decoder.DecodeFieldSignature(ref blobReader);
}

TargetPointer ISignature.GetVarArgArgsBase(TargetPointer vaSigCookieAddr)
{
if (vaSigCookieAddr == TargetPointer.Null)
throw new ArgumentException("VASigCookie address must be non-null.", nameof(vaSigCookieAddr));
// Compute the address of the first argument. On x86 the args are pushed below the cookie
// pointer (stack grows down on the args walk), so the first argument lies at
// vaSigCookieAddr + sizeOfArgs.
// On all other platforms the first argument follows the cookie pointer in memory
// (stack grows up on the args walk), so its address is at
// vaSigCookieAddr + sizeof(VASigCookie*).
if (_target.Contracts.RuntimeInfo.GetTargetArchitecture() == RuntimeInfoArchitecture.X86)
{
Data.VASigCookie cookie = GetCookie(vaSigCookieAddr);
return new TargetPointer(vaSigCookieAddr.Value + cookie.SizeOfArgs);
}

return new TargetPointer(vaSigCookieAddr.Value + (ulong)_target.PointerSize);
}
Comment on lines +55 to +72

void ISignature.GetVarArgSignature(TargetPointer vaSigCookieAddr, out TargetPointer signatureAddress, out uint signatureLength)
{
if (vaSigCookieAddr == TargetPointer.Null)
throw new ArgumentException("VASigCookie address must be non-null.", nameof(vaSigCookieAddr));
Data.VASigCookie cookie = GetCookie(vaSigCookieAddr);

signatureAddress = cookie.SignaturePointer;
signatureLength = cookie.SignatureLength;
Debug.Assert(signatureAddress != TargetPointer.Null || signatureLength == 0,
"VASigCookie has a non-zero signature length but a null signature pointer.");
Comment on lines +80 to +83
}
private Data.VASigCookie GetCookie(TargetPointer vaSigCookieAddr)
{
TargetPointer vaSigCookie = _target.ReadPointer(vaSigCookieAddr);
if (vaSigCookie == TargetPointer.Null)
throw new InvalidOperationException("VASigCookie pointer is null.");

return _target.ProcessedData.GetOrAdd<Data.VASigCookie>(vaSigCookie);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// 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 VASigCookie : IData<VASigCookie>
{
static VASigCookie IData<VASigCookie>.Create(Target target, TargetPointer address)
=> new VASigCookie(target, address);

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

SizeOfArgs = target.ReadField<uint>(address, type, nameof(SizeOfArgs));
SignaturePointer = target.ReadPointerField(address, type, nameof(SignaturePointer));
SignatureLength = target.ReadField<uint>(address, type, nameof(SignatureLength));
}

public uint SizeOfArgs { get; init; }
public TargetPointer SignaturePointer { get; init; }
public uint SignatureLength { get; init; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -1167,7 +1167,40 @@ public int IsDiagnosticsHiddenOrLCGMethod(ulong vmMethodDesc, int* pRetVal)
}

public int GetVarArgSig(ulong VASigCookieAddr, ulong* pArgBase, DacDbiTargetBuffer* pRetVal)
=> LegacyFallbackHelper.CanFallback() && _legacy is not null ? _legacy.GetVarArgSig(VASigCookieAddr, pArgBase, pRetVal) : HResults.E_NOTIMPL;
{
*pArgBase = 0;
*pRetVal = default;
int hr = HResults.S_OK;
try
{
Contracts.ISignature signature = _target.Contracts.Signature;
TargetPointer argBase = signature.GetVarArgArgsBase(new TargetPointer(VASigCookieAddr));
signature.GetVarArgSignature(new TargetPointer(VASigCookieAddr), out TargetPointer sigAddr, out uint sigLen);

*pArgBase = argBase.Value;
*pRetVal = new DacDbiTargetBuffer { pAddress = sigAddr.Value, cbSize = sigLen };
}
catch (System.Exception ex)
{
hr = ex.HResult;
}
#if DEBUG
if (_legacy is not null)
{
ulong argBaseLocal;
DacDbiTargetBuffer retValLocal = default;
int hrLocal = _legacy.GetVarArgSig(VASigCookieAddr, &argBaseLocal, &retValLocal);
Debug.ValidateHResult(hr, hrLocal);
if (hr == HResults.S_OK)
{
Debug.Assert(*pArgBase == argBaseLocal, $"cDAC argBase: 0x{*pArgBase:X}, DAC argBase: 0x{argBaseLocal:X}");
Debug.Assert(pRetVal->pAddress == retValLocal.pAddress, $"cDAC sigAddr: 0x{pRetVal->pAddress:X}, DAC sigAddr: 0x{retValLocal.pAddress:X}");
Debug.Assert(pRetVal->cbSize == retValLocal.cbSize, $"cDAC sigLen: {pRetVal->cbSize}, DAC sigLen: {retValLocal.cbSize}");
}
}
#endif
return hr;
}

public int RequiresAlign8(ulong thExact, Interop.BOOL* pResult)
{
Expand Down
Loading
Loading