Skip to content
Merged
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
58 changes: 58 additions & 0 deletions docs/design/datacontracts/FeatureFlags.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Contract FeatureFlags

This contract exposes whether optional runtime features are enabled in the target.

## APIs of contract

```csharp
public enum RuntimeFeature
{
COMInterop,
ComWrappers,
ObjCMarshal,
JavaMarshal,
OnStackReplacement,
PortableEntrypoints,
Webcil,
}

// Returns true if the target runtime has the given feature enabled.
// A feature is considered disabled when its global is absent from the
// target descriptor or present with a zero value.
bool IsEnabled(RuntimeFeature feature);
```

## Version 1

Global variables used:
| Global Name | Type | Purpose |
| --- | --- | --- |
| FeatureCOMInterop | uint8 | Present (nonzero) when COM interop is enabled |
| FeatureComWrappers | uint8 | Present (nonzero) when ComWrappers is enabled |
| FeatureObjCMarshal | uint8 | Present (nonzero) when Objective-C marshalling is enabled |
| FeatureJavaMarshal | uint8 | Present (nonzero) when Java marshalling is enabled |
| FeatureOnStackReplacement | uint8 | Present (nonzero) when on-stack replacement is enabled |
| FeaturePortableEntrypoints | uint8 | Present (nonzero) when portable entrypoints are enabled |
| FeatureWebcil | uint8 | Present (nonzero) when Webcil is enabled |

Feature flag globals are **only present in the descriptor when the feature is enabled**. A missing global and a zero-valued global are both treated as "disabled".

```csharp
bool IsEnabled(RuntimeFeature feature)
{
string? globalName = feature switch
{
RuntimeFeature.COMInterop => "FeatureCOMInterop",
RuntimeFeature.ComWrappers => "FeatureComWrappers",
RuntimeFeature.ObjCMarshal => "FeatureObjCMarshal",
RuntimeFeature.JavaMarshal => "FeatureJavaMarshal",
RuntimeFeature.OnStackReplacement => "FeatureOnStackReplacement",
RuntimeFeature.PortableEntrypoints => "FeaturePortableEntrypoints",
RuntimeFeature.Webcil => "FeatureWebcil",
_ => null,
};
return globalName is not null
&& TryReadGlobal<byte>(globalName, out byte? value)
&& value != 0;
}
```
8 changes: 8 additions & 0 deletions src/coreclr/nativeaot/Runtime/RuntimeInstance.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ class RuntimeInstance
friend class AsmOffsets;
friend class Thread;
friend void PopulateDebugHeaders();
friend struct ::cdac_data<RuntimeInstance>;

HANDLE m_hPalInstance; // this is the HANDLE passed into DllMain

Expand Down Expand Up @@ -109,6 +110,13 @@ class RuntimeInstance
};
typedef DPTR(RuntimeInstance) PTR_RuntimeInstance;

template<> struct cdac_data<RuntimeInstance>
{
static constexpr size_t OsModuleList = offsetof(RuntimeInstance, m_OsModuleList);
static constexpr size_t TypeManagerList = offsetof(RuntimeInstance, m_TypeManagerList);
static constexpr size_t ManagedCodeStartRange = offsetof(RuntimeInstance, m_pvManagedCodeStartRange);
static constexpr size_t ManagedCodeRangeSize = offsetof(RuntimeInstance, m_cbManagedCodeRange);
};

PTR_RuntimeInstance GetRuntimeInstance();

Expand Down
8 changes: 8 additions & 0 deletions src/coreclr/nativeaot/Runtime/TypeManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

class TypeManager
{
friend struct ::cdac_data<TypeManager>;

// NOTE: Part of this layout is a contract with the managed side in TypeManagerHandle.cs
HANDLE m_osModule;
ReadyToRunHeader * m_pHeader;
Expand All @@ -32,6 +34,12 @@ class TypeManager
};
};

template<> struct cdac_data<TypeManager>
{
static constexpr size_t OsModule = offsetof(TypeManager, m_osModule);
static constexpr size_t Header = offsetof(TypeManager, m_pHeader);
};

// TypeManagerHandle represents an AOT module in MRT based runtimes.
// These handles are a pointer to a TypeManager.
struct TypeManagerHandle
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "Pal.h"
#include "holder.h"
#include "RuntimeInstance.h"
#include "TypeManager.h"
#include "regdisplay.h"
#include "StackFrameIterator.h"
#include "thread.h"
Expand All @@ -23,6 +24,7 @@

GPTR_DECL(MethodTable, g_pFreeObjectEEType);
GPTR_DECL(StressLog, g_pStressLog);
GPTR_DECL(RuntimeInstance, g_pTheRuntimeInstance);

// ILC emits a ContractDescriptor named "DotNetManagedContractDescriptor" with
// managed type layouts. We take its address so datadescriptor.inc can reference
Expand Down
46 changes: 40 additions & 6 deletions src/coreclr/nativeaot/Runtime/datadescriptor/datadescriptor.inc
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ CDAC_TYPE_FIELD(StressLog, T_POINTER, Logs, offsetof(StressLog, logs))
CDAC_TYPE_FIELD(StressLog, T_UINT64, TickFrequency, offsetof(StressLog, tickFrequency))
CDAC_TYPE_FIELD(StressLog, T_UINT64, StartTimestamp, offsetof(StressLog, startTimeStamp))
CDAC_TYPE_FIELD(StressLog, T_UINT64, StartTime, offsetof(StressLog, startTime))
CDAC_TYPE_FIELD(StressLog, T_NUINT, ModuleOffset, offsetof(StressLog, moduleOffset))
CDAC_TYPE_END(StressLog)

CDAC_TYPE_BEGIN(ThreadStressLog)
Expand Down Expand Up @@ -138,6 +139,30 @@ CDAC_TYPE_FIELD(StressMsg, TYPE(StressMsgHeader), Header, 0)
CDAC_TYPE_FIELD(StressMsg, T_POINTER, Args, offsetof(StressMsg, args))
CDAC_TYPE_END(StressMsg)

// ========================
// Loader (RuntimeInstance / TypeManager)
// ========================

CDAC_TYPE_BEGIN(RuntimeInstance)
CDAC_TYPE_INDETERMINATE(RuntimeInstance)
CDAC_TYPE_FIELD(RuntimeInstance, T_POINTER, OsModuleList, cdac_data<RuntimeInstance>::OsModuleList)
CDAC_TYPE_FIELD(RuntimeInstance, T_POINTER, TypeManagerList, cdac_data<RuntimeInstance>::TypeManagerList)
CDAC_TYPE_FIELD(RuntimeInstance, T_POINTER, ManagedCodeStartRange, cdac_data<RuntimeInstance>::ManagedCodeStartRange)
CDAC_TYPE_FIELD(RuntimeInstance, T_UINT32, ManagedCodeRangeSize, cdac_data<RuntimeInstance>::ManagedCodeRangeSize)
CDAC_TYPE_END(RuntimeInstance)

CDAC_TYPE_BEGIN(TypeManagerEntry)
CDAC_TYPE_INDETERMINATE(TypeManagerEntry)
CDAC_TYPE_FIELD(TypeManagerEntry, T_POINTER, Next, offsetof(RuntimeInstance::TypeManagerEntry, m_pNext))
CDAC_TYPE_FIELD(TypeManagerEntry, T_POINTER, TypeManager, offsetof(RuntimeInstance::TypeManagerEntry, m_pTypeManager))
CDAC_TYPE_END(TypeManagerEntry)

CDAC_TYPE_BEGIN(TypeManager)
CDAC_TYPE_INDETERMINATE(TypeManager)
CDAC_TYPE_FIELD(TypeManager, T_POINTER, OsModule, cdac_data<TypeManager>::OsModule)
CDAC_TYPE_FIELD(TypeManager, T_POINTER, Header, cdac_data<TypeManager>::Header)
CDAC_TYPE_END(TypeManager)

CDAC_TYPES_END()

// ========================
Expand All @@ -148,30 +173,29 @@ CDAC_GLOBALS_BEGIN()

CDAC_GLOBAL_POINTER(ThreadStore, &ThreadStore::s_pThreadStore)

CDAC_GLOBAL_POINTER(RuntimeInstance, &g_pTheRuntimeInstance)

CDAC_GLOBAL_POINTER(FreeObjectMethodTable, &g_pFreeObjectEEType)

CDAC_GLOBAL_POINTER(GCLowestAddress, &g_lowest_address)
CDAC_GLOBAL_POINTER(GCHighestAddress, &g_highest_address)

// Thread state flag constants
CDAC_GLOBAL(ThreadStateFlagAttached, T_UINT32, 0x00000001)
CDAC_GLOBAL(ThreadStateFlagDetached, T_UINT32, 0x00000002)

// Object contract globals
#ifdef TARGET_64BIT
CDAC_GLOBAL(ObjectToMethodTableUnmask, T_UINT8, 7)
#else
CDAC_GLOBAL(ObjectToMethodTableUnmask, T_UINT8, 3)
#endif

// StressLog globals (no module table in NativeAOT)
CDAC_GLOBAL(StressLogEnabled, T_UINT8, 1)
CDAC_GLOBAL_POINTER(StressLog, &StressLog::theLog)
CDAC_GLOBAL(StressLogHasModuleTable, T_UINT8, 0)
CDAC_GLOBAL(StressLogChunkSize, T_UINT32, STRESSLOG_CHUNK_SIZE)
CDAC_GLOBAL(StressLogValidChunkSig, T_UINT32, 0xCFCFCFCF)
CDAC_GLOBAL(StressLogMaxMessageSize, T_UINT64, (uint64_t)StressMsg::maxMsgSize)

CDAC_GLOBAL(SOSBreakingChangeVersion, T_UINT8, 5)

#if defined(TARGET_BROWSER)
#ifdef Browser
#error Handle 'Browser' define
Expand Down Expand Up @@ -222,10 +246,20 @@ CDAC_GLOBAL(RecommendedReaderVersion, T_UINT32, CDAC_RECOMMENDED_READER_VERSION)

// Contracts: declare which contracts this runtime supports
CDAC_GLOBAL_CONTRACT(Thread, n1)
CDAC_GLOBAL_CONTRACT(Exception, c1)
CDAC_GLOBAL_CONTRACT(Exception, n1)
CDAC_GLOBAL_CONTRACT(Object, n1)
CDAC_GLOBAL_CONTRACT(RuntimeTypeSystem, n1)
CDAC_GLOBAL_CONTRACT(RuntimeInfo, c1)
CDAC_GLOBAL_CONTRACT(StressLog, c2)
CDAC_GLOBAL_CONTRACT(Loader, n1)
CDAC_GLOBAL_CONTRACT(ExecutionManager, n1)
CDAC_GLOBAL_CONTRACT(SyncBlock, n1)
CDAC_GLOBAL_CONTRACT(SymbolName, n1)
CDAC_GLOBAL_CONTRACT(CodeVersions, n1)
CDAC_GLOBAL_CONTRACT(ReJIT, n1)
CDAC_GLOBAL_CONTRACT(PrecodeStubs, n1)
CDAC_GLOBAL_CONTRACT(PlatformMetadata, n1)
CDAC_GLOBAL_CONTRACT(FeatureFlags, c1)

// Managed type sub-descriptor: ILC emits a ContractDescriptor with managed type layouts
// that the cDAC reader merges as a sub-descriptor. This provides field offsets for managed
Expand Down
15 changes: 1 addition & 14 deletions src/coreclr/vm/datadescriptor/datadescriptor.inc
Original file line number Diff line number Diff line change
Expand Up @@ -1590,33 +1590,21 @@ CDAC_GLOBAL(MethodDescTokenRemainderBitCount, T_UINT8, METHOD_TOKEN_REMAINDER_BI

#if FEATURE_COMINTEROP
CDAC_GLOBAL(FeatureCOMInterop, T_UINT8, 1)
#else
CDAC_GLOBAL(FeatureCOMInterop, T_UINT8, 0)
#endif
#if FEATURE_COMWRAPPERS
CDAC_GLOBAL(FeatureComWrappers, T_UINT8, 1)
#else
CDAC_GLOBAL(FeatureComWrappers, T_UINT8, 0)
#endif
#if FEATURE_OBJCMARSHAL
CDAC_GLOBAL(FeatureObjCMarshal, T_UINT8, 1)
#else
CDAC_GLOBAL(FeatureObjCMarshal, T_UINT8, 0)
#endif
#if FEATURE_JAVAMARSHAL
CDAC_GLOBAL(FeatureJavaMarshal, T_UINT8, 1)
#else
CDAC_GLOBAL(FeatureJavaMarshal, T_UINT8, 0)
#endif
#ifdef FEATURE_ON_STACK_REPLACEMENT
CDAC_GLOBAL(FeatureOnStackReplacement, T_UINT8, 1)
#else
CDAC_GLOBAL(FeatureOnStackReplacement, T_UINT8, 0)
#endif // FEATURE_ON_STACK_REPLACEMENT
#ifdef FEATURE_WEBCIL
CDAC_GLOBAL(FeatureWebcil, T_UINT8, 1)
#else
CDAC_GLOBAL(FeatureWebcil, T_UINT8, 0)
#endif
// See Object::GetGCSafeMethodTable
#ifdef TARGET_64BIT
Expand All @@ -1626,8 +1614,6 @@ CDAC_GLOBAL(ObjectToMethodTableUnmask, T_UINT8, 1 | 1 << 1)
#endif //TARGET_64BIT
#ifdef FEATURE_PORTABLE_ENTRYPOINTS
CDAC_GLOBAL(FeaturePortableEntrypoints, T_UINT8, 1)
#else
CDAC_GLOBAL(FeaturePortableEntrypoints, T_UINT8, 0)
#endif // FEATURE_PORTABLE_ENTRYPOINTS
CDAC_GLOBAL(SOSBreakingChangeVersion, T_UINT8, SOS_BREAKING_CHANGE_VERSION)
CDAC_GLOBAL(RecommendedReaderVersion, T_UINT32, CDAC_RECOMMENDED_READER_VERSION)
Expand Down Expand Up @@ -1754,6 +1740,7 @@ CDAC_GLOBAL_CONTRACT(Notifications, c1)
CDAC_GLOBAL_CONTRACT(ObjectiveCMarshal, c1)
#endif // FEATURE_OBJCMARSHAL
CDAC_GLOBAL_CONTRACT(Object, c1)
CDAC_GLOBAL_CONTRACT(FeatureFlags, c1)
CDAC_GLOBAL_CONTRACT(PlatformMetadata, c1)
CDAC_GLOBAL_CONTRACT(PrecodeStubs, c3)
#ifdef PROFILING_SUPPORTED
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ public abstract class ContractRegistry
/// </summary>
public virtual IPlatformMetadata PlatformMetadata => GetContract<IPlatformMetadata>();
/// <summary>
/// Gets an instance of the FeatureFlags contract for the target.
/// </summary>
public virtual IFeatureFlags FeatureFlags => GetContract<IFeatureFlags>();
/// <summary>
/// Gets an instance of the PrecodeStubs contract for the target.
/// </summary>
public virtual IPrecodeStubs PrecodeStubs => GetContract<IPrecodeStubs>();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// 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.Contracts;

/// <summary>
/// Feature flags that a runtime may or may not support.
/// </summary>
public enum RuntimeFeature
{
COMInterop,
ComWrappers,
ObjCMarshal,
JavaMarshal,
OnStackReplacement,
PortableEntrypoints,
Webcil,
}

public interface IFeatureFlags : IContract
{
Comment thread
max-charlamb marked this conversation as resolved.
static string IContract.Name { get; } = nameof(FeatureFlags);

/// <summary>
/// Returns <see langword="true"/> if the target runtime has the given feature enabled.
/// </summary>
bool IsEnabled(RuntimeFeature feature) => throw new NotImplementedException();
}

public readonly struct FeatureFlags : IFeatureFlags { }
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,9 @@ public readonly struct GCHeapData
public TargetPointer SavedSweepEphemeralSegment { get; init; } /* Only valid in segment GC builds */
public TargetPointer SavedSweepEphemeralStart { get; init; } /* Only valid in segment GC builds */

public TargetPointer InternalRootArray { get; init; }
public TargetNUInt InternalRootArrayIndex { get; init; }
public bool HeapAnalyzeSuccess { get; init; }
public TargetPointer? InternalRootArray { get; init; }
public TargetNUInt? InternalRootArrayIndex { get; init; }
public bool? HeapAnalyzeSuccess { get; init; }

public IReadOnlyList<TargetNUInt> InterestingData { get; init; }
public IReadOnlyList<TargetNUInt> CompactReasons { get; init; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ public override TargetPointer GetDebugInfo(RangeSection rangeSection, TargetCode
if (!GetRealCodeHeader(rangeSection, codeStart, out Data.RealCodeHeader? realCodeHeader))
return TargetPointer.Null;

bool featureOnStackReplacement = Target.ReadGlobal<byte>(Constants.Globals.FeatureOnStackReplacement) != 0;
bool featureOnStackReplacement = Target.Contracts.FeatureFlags.IsEnabled(RuntimeFeature.OnStackReplacement);
Data.EEJitManager eeJitManager = Target.ProcessedData.GetOrAdd<Data.EEJitManager>(rangeSection.Data.JitManager);
if (featureOnStackReplacement || eeJitManager.StoreRichDebugInfo)
hasFlagByte = true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ void IExecutionManager.GetMethodRegionInfo(CodeBlockHandle codeInfoHandle, out u

TargetPointer IExecutionManager.NonVirtualEntry2MethodDesc(TargetCodePointer entrypoint)
{
if (_target.ReadGlobal<byte>(Constants.Globals.FeaturePortableEntrypoints) != 0)
if (_target.Contracts.FeatureFlags.IsEnabled(RuntimeFeature.PortableEntrypoints))
{
Data.PortableEntryPoint portableEntryPoint = _target.ProcessedData.GetOrAdd<Data.PortableEntryPoint>(entrypoint.AsTargetPointer);
return portableEntryPoint.MethodDesc;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// 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.Contracts;

internal readonly struct FeatureFlags_1 : IFeatureFlags
{
private readonly Target _target;

public FeatureFlags_1(Target target)
{
_target = target;
}

bool IFeatureFlags.IsEnabled(RuntimeFeature feature)
{
string? globalName = feature switch
{
RuntimeFeature.COMInterop => Constants.Globals.FeatureCOMInterop,
RuntimeFeature.ComWrappers => Constants.Globals.FeatureComWrappers,
RuntimeFeature.ObjCMarshal => Constants.Globals.FeatureObjCMarshal,
RuntimeFeature.JavaMarshal => Constants.Globals.FeatureJavaMarshal,
RuntimeFeature.OnStackReplacement => Constants.Globals.FeatureOnStackReplacement,
RuntimeFeature.PortableEntrypoints => Constants.Globals.FeaturePortableEntrypoints,
RuntimeFeature.Webcil => Constants.Globals.FeatureWebcil,
_ => null,
};
return globalName is not null
&& _target.TryReadGlobal<byte>(globalName, out byte? value)
&& value != 0;
Comment thread
max-charlamb marked this conversation as resolved.
}
}
Loading
Loading