diff --git a/eng/SignCheckExclusionsFile.txt b/eng/SignCheckExclusionsFile.txt
new file mode 100644
index 00000000000000..f41753338155eb
--- /dev/null
+++ b/eng/SignCheckExclusionsFile.txt
@@ -0,0 +1,15 @@
+;; Exclusions for SignCheck. Corresponds to info in Signing.props.
+;; Format: https://github.com/dotnet/arcade/blob/397316e195639450b6c76bfeb9823b40bee72d6d/src/SignCheck/Microsoft.SignCheck/Verification/Exclusion.cs#L23-L35
+;;
+;; This issue tracks a way to implement exclusions via Signing.props and avoid this extra file: https://github.com/dotnet/arcade/issues/2888
+
+;; The apphost and comhost are template files, modified by the SDK to produce the executable for FDE
+;; and SCD apps. If they are signed, the file that the SDK produces has an invalid signature and
+;; can't be signed again. More info at https://github.com/dotnet/core-setup/pull/7549.
+*apphost.exe;;Template, DO-NOT-SIGN, https://github.com/dotnet/core-setup/pull/7549
+*singlefilehost.exe;;Template, DO-NOT-SIGN, https://github.com/dotnet/core-setup/pull/7549
+*comhost.dll;;Template, DO-NOT-SIGN, https://github.com/dotnet/core-setup/pull/7549
+*apphosttemplateapphostexe.exe;;Template, DO-NOT-SIGN, https://github.com/dotnet/core-setup/pull/7549
+*comhosttemplatecomhostdll.dll;;Template, DO-NOT-SIGN, https://github.com/dotnet/core-setup/pull/7549
+*staticapphosttemplateapphostexe.exe;;Template, DO-NOT-SIGN, https://github.com/dotnet/core-setup/pull/7549
+*dotnet.js;;Workaround, https://github.com/dotnet/core-eng/issues/9933
\ No newline at end of file
diff --git a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj
index c12d6c854ac0c0..b089e0b436671b 100644
--- a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj
+++ b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj
@@ -238,7 +238,6 @@
-
@@ -290,8 +289,6 @@
-
-
diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/LowLevelLifoSemaphore.Unix.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/LowLevelLifoSemaphore.Unix.cs
deleted file mode 100644
index 25fc6ff09ad2a1..00000000000000
--- a/src/coreclr/System.Private.CoreLib/src/System/Threading/LowLevelLifoSemaphore.Unix.cs
+++ /dev/null
@@ -1,51 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.Diagnostics;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-using Microsoft.Win32.SafeHandles;
-
-namespace System.Threading
-{
- ///
- /// A LIFO semaphore implemented using the PAL's semaphore with uninterruptible waits.
- ///
- internal sealed partial class LowLevelLifoSemaphore : IDisposable
- {
- private Semaphore? _semaphore;
-
- private void Create(int maximumSignalCount)
- {
- Debug.Assert(maximumSignalCount > 0);
- _semaphore = new Semaphore(0, maximumSignalCount);
- }
-
- public bool WaitCore(int timeoutMs)
- {
- Debug.Assert(_semaphore != null);
- Debug.Assert(timeoutMs >= -1);
-
- int waitResult = WaitNative(_semaphore!.SafeWaitHandle, timeoutMs);
- Debug.Assert(waitResult == WaitHandle.WaitSuccess || waitResult == WaitHandle.WaitTimeout);
- return waitResult == WaitHandle.WaitSuccess;
- }
-
- [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "WaitHandle_WaitOnePrioritized")]
- private static partial int WaitNative(SafeWaitHandle handle, int timeoutMs);
-
- private void ReleaseCore(int count)
- {
- Debug.Assert(_semaphore != null);
- Debug.Assert(count > 0);
-
- _semaphore!.Release(count);
- }
-
- public void Dispose()
- {
- Debug.Assert(_semaphore != null);
- _semaphore!.Dispose();
- }
- }
-}
diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/Mutex.CoreCLR.Unix.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/Mutex.CoreCLR.Unix.cs
deleted file mode 100644
index 08b0748b412c8c..00000000000000
--- a/src/coreclr/System.Private.CoreLib/src/System/Threading/Mutex.CoreCLR.Unix.cs
+++ /dev/null
@@ -1,178 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.IO;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-using System.Text;
-using Microsoft.Win32.SafeHandles;
-
-namespace System.Threading
-{
- ///
- /// Synchronization primitive that can also be used for interprocess synchronization
- ///
- public sealed partial class Mutex : WaitHandle
- {
- private unsafe void CreateMutexCore(bool initiallyOwned)
- {
- SafeWaitHandle handle =
- CreateMutex(
- initiallyOwned,
- name: null,
- currentUserOnly: false,
- systemCallErrors: null,
- systemCallErrorsBufferSize: 0);
- if (handle.IsInvalid)
- {
- int errorCode = Marshal.GetLastPInvokeError();
- handle.SetHandleAsInvalid();
- throw Win32Marshal.GetExceptionForWin32Error(errorCode);
- }
-
- SafeWaitHandle = handle;
- }
-
- private void CreateMutexCore(
- bool initiallyOwned,
- string? name,
- NamedWaitHandleOptionsInternal options,
- out bool createdNew)
- {
- bool currentUserOnly = false;
- if (!string.IsNullOrEmpty(name) && options.WasSpecified)
- {
- name = options.GetNameWithSessionPrefix(name);
- currentUserOnly = options.CurrentUserOnly;
- }
-
- SafeWaitHandle mutexHandle =
- CreateMutexCore(initiallyOwned, name, currentUserOnly, out int errorCode, out string? errorDetails);
- if (mutexHandle.IsInvalid)
- {
- mutexHandle.SetHandleAsInvalid();
- if (errorCode == Interop.Errors.ERROR_FILENAME_EXCED_RANGE)
- // On Unix, length validation is done by CoreCLR's PAL after converting to utf-8
- throw new ArgumentException(SR.Argument_WaitHandleNameTooLong, nameof(name));
- if (errorCode == Interop.Errors.ERROR_INVALID_HANDLE)
- throw new WaitHandleCannotBeOpenedException(SR.Format(SR.Threading_WaitHandleCannotBeOpenedException_InvalidHandle, name));
-
- throw Win32Marshal.GetExceptionForWin32Error(errorCode, name, errorDetails);
- }
-
- createdNew = errorCode != Interop.Errors.ERROR_ALREADY_EXISTS;
- SafeWaitHandle = mutexHandle;
- }
-
- private static OpenExistingResult OpenExistingWorker(
- string name,
- NamedWaitHandleOptionsInternal options,
- out Mutex? result)
- {
- ArgumentException.ThrowIfNullOrEmpty(name);
-
- bool currentUserOnly = false;
- if (options.WasSpecified)
- {
- name = options.GetNameWithSessionPrefix(name);
- currentUserOnly = options.CurrentUserOnly;
- }
-
- result = null;
- // To allow users to view & edit the ACL's, call OpenMutex
- // with parameters to allow us to view & edit the ACL. This will
- // fail if we don't have permission to view or edit the ACL's.
- // If that happens, ask for less permissions.
- SafeWaitHandle myHandle = OpenMutexCore(name, currentUserOnly, out int errorCode, out string? errorDetails);
-
- if (myHandle.IsInvalid)
- {
- myHandle.Dispose();
-
- if (errorCode == Interop.Errors.ERROR_FILENAME_EXCED_RANGE)
- {
- // On Unix, length validation is done by CoreCLR's PAL after converting to utf-8
- throw new ArgumentException(SR.Argument_WaitHandleNameTooLong, nameof(name));
- }
- if (Interop.Errors.ERROR_FILE_NOT_FOUND == errorCode || Interop.Errors.ERROR_INVALID_NAME == errorCode)
- return OpenExistingResult.NameNotFound;
- if (Interop.Errors.ERROR_PATH_NOT_FOUND == errorCode)
- return OpenExistingResult.PathNotFound;
- if (Interop.Errors.ERROR_INVALID_HANDLE == errorCode)
- return OpenExistingResult.NameInvalid;
-
- throw Win32Marshal.GetExceptionForWin32Error(errorCode, name, errorDetails);
- }
-
- result = new Mutex(myHandle);
- return OpenExistingResult.Success;
- }
-
- // Note: To call ReleaseMutex, you must have an ACL granting you
- // MUTEX_MODIFY_STATE rights (0x0001). The other interesting value
- // in a Mutex's ACL is MUTEX_ALL_ACCESS (0x1F0001).
- public void ReleaseMutex()
- {
- if (!Interop.Kernel32.ReleaseMutex(SafeWaitHandle))
- {
- throw new ApplicationException(SR.Arg_SynchronizationLockException);
- }
- }
-
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- // Unix-specific implementation
-
- private const int SystemCallErrorsBufferSize = 256;
-
- private static unsafe SafeWaitHandle CreateMutexCore(
- bool initialOwner,
- string? name,
- bool currentUserOnly,
- out int errorCode,
- out string? errorDetails)
- {
- byte* systemCallErrors = stackalloc byte[SystemCallErrorsBufferSize];
- SafeWaitHandle mutexHandle =
- CreateMutex(initialOwner, name, currentUserOnly, systemCallErrors, SystemCallErrorsBufferSize);
-
- // Get the error code even if the handle is valid, as it could be ERROR_ALREADY_EXISTS, indicating that the mutex
- // already exists and was opened
- errorCode = Marshal.GetLastPInvokeError();
-
- errorDetails = mutexHandle.IsInvalid ? GetErrorDetails(systemCallErrors) : null;
- return mutexHandle;
- }
-
- private static unsafe SafeWaitHandle OpenMutexCore(string name, bool currentUserOnly, out int errorCode, out string? errorDetails)
- {
- byte* systemCallErrors = stackalloc byte[SystemCallErrorsBufferSize];
- SafeWaitHandle mutexHandle = OpenMutex(name, currentUserOnly, systemCallErrors, SystemCallErrorsBufferSize);
- errorCode = mutexHandle.IsInvalid ? Marshal.GetLastPInvokeError() : Interop.Errors.ERROR_SUCCESS;
- errorDetails = mutexHandle.IsInvalid ? GetErrorDetails(systemCallErrors) : null;
- return mutexHandle;
- }
-
- private static unsafe string? GetErrorDetails(byte* systemCallErrors)
- {
- int systemCallErrorsLength =
- new ReadOnlySpan(systemCallErrors, SystemCallErrorsBufferSize).IndexOf((byte)'\0');
- if (systemCallErrorsLength > 0)
- {
- try
- {
- return
- SR.Format(SR.Unix_SystemCallErrors, Encoding.UTF8.GetString(systemCallErrors, systemCallErrorsLength));
- }
- catch { } // avoid hiding the original error due to an error here
- }
-
- return null;
- }
-
- [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "PAL_CreateMutexW", SetLastError = true, StringMarshalling = StringMarshalling.Utf16)]
- private static unsafe partial SafeWaitHandle CreateMutex([MarshalAs(UnmanagedType.Bool)] bool initialOwner, string? name, [MarshalAs(UnmanagedType.Bool)] bool currentUserOnly, byte* systemCallErrors, uint systemCallErrorsBufferSize);
-
- [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "PAL_OpenMutexW", SetLastError = true, StringMarshalling = StringMarshalling.Utf16)]
- private static unsafe partial SafeWaitHandle OpenMutex(string name, [MarshalAs(UnmanagedType.Bool)] bool currentUserOnly, byte* systemCallErrors, uint systemCallErrorsBufferSize);
- }
-}
diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs
index 99569377896a14..0a0eed3748dbef 100644
--- a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs
+++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs
@@ -33,6 +33,10 @@ public sealed partial class Thread
private string? _name;
private StartHelper? _startHelper;
+#if TARGET_UNIX || TARGET_BROWSER || TARGET_WASI
+ internal WaitSubsystem.ThreadWaitInfo? _waitInfo;
+#endif
+
/*=========================================================================
** The base implementation of Thread is all native. The following fields
** should never be used in the C# code. They are here to define the proper
@@ -108,14 +112,6 @@ private void StartCallback()
startHelper.Run();
}
- ///
- /// Suspends the current thread for timeout milliseconds. If timeout == 0,
- /// forces the thread to give up the remainder of its timeslice. If timeout
- /// == Timeout.Infinite, no timeout will occur.
- ///
- [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_Sleep")]
- private static partial void SleepInternal(int millisecondsTimeout);
-
// Max iterations to be done in SpinWait without switching GC modes.
private const int SpinWaitCoopThreshold = 1024;
@@ -298,6 +294,28 @@ public ThreadState ThreadState
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_GetThreadState")]
private static partial int GetThreadState(ThreadHandle t);
+ internal void SetWaitSleepJoinState()
+ {
+ // This method is called when the thread is about to enter a wait, sleep, or join state.
+ // It sets the state in the native layer to indicate that the thread is waiting.
+ SetWaitSleepJoinState(GetNativeHandle());
+ GC.KeepAlive(this);
+ }
+
+ internal void ClearWaitSleepJoinState()
+ {
+ // This method is called when the thread is no longer in a wait, sleep, or join state.
+ // It clears the state in the native layer to indicate that the thread is no longer waiting.
+ ClearWaitSleepJoinState(GetNativeHandle());
+ GC.KeepAlive(this);
+ }
+
+ [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_SetWaitSleepJoinState")]
+ private static partial void SetWaitSleepJoinState(ThreadHandle t);
+
+ [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_ClearWaitSleepJoinState")]
+ private static partial void ClearWaitSleepJoinState(ThreadHandle t);
+
///
/// An unstarted thread can be marked to indicate that it will host a
/// single-threaded or multi-threaded apartment.
@@ -347,6 +365,9 @@ private bool SetApartmentStateUnchecked(ApartmentState state, bool throwOnError)
return true;
}
+ internal static bool ReentrantWaitsEnabled =>
+ CurrentThread.GetApartmentState() == ApartmentState.STA;
+
#else // FEATURE_COMINTEROP_APARTMENT_SUPPORT
public ApartmentState GetApartmentState() => ApartmentState.Unknown;
@@ -364,6 +385,8 @@ private static bool SetApartmentStateUnchecked(ApartmentState state, bool throwO
return true;
}
+
+ internal const bool ReentrantWaitsEnabled = false;
#endif // FEATURE_COMINTEROP_APARTMENT_SUPPORT
#if FEATURE_COMINTEROP
@@ -513,6 +536,45 @@ private static void PollGC()
static void PollGCWorker() => PollGCInternal();
}
+#if TARGET_UNIX || TARGET_BROWSER || TARGET_WASI
+ internal WaitSubsystem.ThreadWaitInfo WaitInfo
+ {
+ get
+ {
+ return Volatile.Read(ref _waitInfo) ?? AllocateWaitInfo();
+
+ WaitSubsystem.ThreadWaitInfo AllocateWaitInfo()
+ {
+ Interlocked.CompareExchange(ref _waitInfo, new WaitSubsystem.ThreadWaitInfo(this), null!);
+ return _waitInfo;
+ }
+ }
+ }
+#endif
+
+#pragma warning disable CA1822 // Member 'OnThreadExiting' does not access instance data and can be marked as static
+ private void OnThreadExiting()
+#pragma warning restore CA1822 // Member 'OnThreadExiting' does not access instance data and can be marked as static
+ {
+#if TARGET_UNIX || TARGET_BROWSER || TARGET_WASI
+ // Inform the wait subsystem that the thread is exiting. For instance, this would abandon any mutexes locked by
+ // the thread.
+ _waitInfo?.OnThreadExiting();
+#endif
+ }
+
+ [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_ReentrantWaitAny")]
+ internal static unsafe partial int ReentrantWaitAny([MarshalAs(UnmanagedType.Bool)] bool alertable, int timeout, int count, IntPtr* handles);
+
+ internal static void CheckForPendingInterrupt()
+ {
+ CheckForPendingInterrupt(CurrentThread.GetNativeHandle());
+ GC.KeepAlive(CurrentThread);
+ }
+
+ [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_CheckForPendingInterrupt")]
+ private static partial void CheckForPendingInterrupt(ThreadHandle t);
+
[StructLayout(LayoutKind.Sequential)]
private struct NativeThreadClass
{
@@ -521,12 +583,12 @@ private struct NativeThreadClass
private enum NativeThreadState
{
- None = 0,
- TS_AbortRequested = 0x00000001, // Abort the thread
- TS_DebugSuspendPending = 0x00000008, // Is the debugger suspending threads?
- TS_GCOnTransitions = 0x00000010, // Force a GC on stub transitions (GCStress only)
+ None = 0,
+ TS_AbortRequested = 0x00000001, // Abort the thread
+ TS_DebugSuspendPending = 0x00000008, // Is the debugger suspending threads?
+ TS_GCOnTransitions = 0x00000010, // Force a GC on stub transitions (GCStress only)
- // We require (and assert) that the following bits are less than 0x100.
+ // We require (and assert) that the following bits are less than 0x100.
TS_CatchAtSafePoint = (TS_AbortRequested | TS_DebugSuspendPending | TS_GCOnTransitions),
};
}
diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/WaitHandle.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/WaitHandle.CoreCLR.cs
deleted file mode 100644
index a63096dc84941f..00000000000000
--- a/src/coreclr/System.Private.CoreLib/src/System/Threading/WaitHandle.CoreCLR.cs
+++ /dev/null
@@ -1,35 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-
-namespace System.Threading
-{
- public abstract partial class WaitHandle
- {
- [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "WaitHandle_WaitOneCore")]
- private static partial int WaitOneCore(IntPtr waitHandle, int millisecondsTimeout, [MarshalAs(UnmanagedType.Bool)] bool useTrivialWaits);
-
- private static int WaitMultipleIgnoringSyncContextCore(ReadOnlySpan waitHandles, bool waitAll, int millisecondsTimeout)
- => WaitMultipleIgnoringSyncContext(waitHandles, waitHandles.Length, waitAll, millisecondsTimeout);
-
- [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "WaitHandle_WaitMultipleIgnoringSyncContext")]
- private static partial int WaitMultipleIgnoringSyncContext(ReadOnlySpan waitHandles, int numHandles, [MarshalAs(UnmanagedType.Bool)] bool waitAll, int millisecondsTimeout);
-
- private static int SignalAndWaitCore(IntPtr waitHandleToSignal, IntPtr waitHandleToWaitOn, int millisecondsTimeout)
- {
- int ret = SignalAndWait(waitHandleToSignal, waitHandleToWaitOn, millisecondsTimeout);
-
- if (ret == Interop.Errors.ERROR_TOO_MANY_POSTS)
- {
- throw new InvalidOperationException(SR.Threading_WaitHandleTooManyPosts);
- }
-
- return ret;
- }
-
- [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "WaitHandle_SignalAndWait")]
- private static partial int SignalAndWait(IntPtr waitHandleToSignal, IntPtr waitHandleToWaitOn, int millisecondsTimeout);
- }
-}
diff --git a/src/coreclr/debug/runtimeinfo/.editorconfig b/src/coreclr/debug/runtimeinfo/.editorconfig
new file mode 100644
index 00000000000000..ce2ce7b8d1ec54
--- /dev/null
+++ b/src/coreclr/debug/runtimeinfo/.editorconfig
@@ -0,0 +1,2 @@
+[contracts.jsonc]
+indent_size = 2
diff --git a/src/coreclr/debug/runtimeinfo/configure.h.in b/src/coreclr/debug/runtimeinfo/configure.h.in
new file mode 100644
index 00000000000000..efe0ede365c644
--- /dev/null
+++ b/src/coreclr/debug/runtimeinfo/configure.h.in
@@ -0,0 +1,6 @@
+#ifndef RUNTIME_INFO_CONFIGURE_H_INCLUDED
+#define RUNTIME_INFO_CONFIGURE_H_INCLUDED
+
+#define RID_STRING @CLR_DOTNET_RID@
+
+#endif // RUNTIME_INFO_CONFIGURE_H_INCLUDED
diff --git a/src/coreclr/debug/runtimeinfo/contract-descriptor.c.in b/src/coreclr/debug/runtimeinfo/contract-descriptor.c.in
new file mode 100644
index 00000000000000..c1f0edd7a66f9c
--- /dev/null
+++ b/src/coreclr/debug/runtimeinfo/contract-descriptor.c.in
@@ -0,0 +1,34 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#include
+
+#ifdef _MSC_VER
+#define DLLEXPORT __declspec(dllexport)
+#else
+#define DLLEXPORT __attribute__((visibility("default")))
+#endif
+
+struct DotNetRuntimeContractDescriptor
+{
+ uint64_t magic;
+ uint32_t flags;
+ const uint32_t descriptor_size;
+ const char *descriptor;
+ const uint32_t pointer_data_count;
+ uint32_t pad0;
+ const uintptr_t *pointer_data;
+};
+
+extern const uintptr_t contractDescriptorPointerData[];
+
+DLLEXPORT struct DotNetRuntimeContractDescriptor DotNetRuntimeContractDescriptor;
+
+DLLEXPORT struct DotNetRuntimeContractDescriptor DotNetRuntimeContractDescriptor = {
+ .magic = 0x0043414443434e44ull, // "DNCCDAC\0"
+ .flags = %%platformFlags%%,
+ .descriptor_size = %%jsonDescriptorSize%%,
+ .descriptor = "%%jsonDescriptor%%",
+ .pointer_data_count = %%pointerDataCount%%,
+ .pointer_data = &contractDescriptorPointerData[0],
+};
diff --git a/src/coreclr/debug/runtimeinfo/contractdescriptorstub.c b/src/coreclr/debug/runtimeinfo/contractdescriptorstub.c
new file mode 100644
index 00000000000000..59421a6692d2a7
--- /dev/null
+++ b/src/coreclr/debug/runtimeinfo/contractdescriptorstub.c
@@ -0,0 +1,39 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#include
+
+#ifdef _MSC_VER
+#define DLLEXPORT __declspec(dllexport)
+#else
+#define DLLEXPORT __attribute__((visibility("default")))
+#endif
+
+struct DotNetRuntimeContractDescriptor
+{
+ uint64_t magic;
+ uint32_t flags;
+ const uint32_t descriptor_size;
+ const char *descriptor;
+ const uint32_t pointer_data_count;
+ uint32_t pad0;
+ const uintptr_t *pointer_data;
+};
+
+extern const uintptr_t contractDescriptorPointerData[];
+
+// just the placeholder pointer
+const uintptr_t contractDescriptorPointerData[] = { (uintptr_t)0 };
+
+DLLEXPORT struct DotNetRuntimeContractDescriptor DotNetRuntimeContractDescriptor;
+
+#define STUB_DESCRIPTOR "{\"version\":0,\"baseline\":\"empty\",\"contracts\":{},\"types\":{},\"globals\":{}}"
+
+DLLEXPORT struct DotNetRuntimeContractDescriptor DotNetRuntimeContractDescriptor = {
+ .magic = 0x0043414443434e44ull, // "DNCCDAC\0"
+ .flags = 0x1u & (sizeof(void*) == 4 ? 0x02u : 0x00u),
+ .descriptor_size = sizeof(STUB_DESCRIPTOR),
+ .descriptor = STUB_DESCRIPTOR,
+ .pointer_data_count = 1,
+ .pointer_data = &contractDescriptorPointerData[0],
+};
diff --git a/src/coreclr/debug/runtimeinfo/contractpointerdata.cpp b/src/coreclr/debug/runtimeinfo/contractpointerdata.cpp
new file mode 100644
index 00000000000000..1848b1fb69d898
--- /dev/null
+++ b/src/coreclr/debug/runtimeinfo/contractpointerdata.cpp
@@ -0,0 +1,24 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#include "common.h"
+
+#include
+#include
+
+#include "cdacplatformmetadata.hpp"
+#include "threads.h"
+#include "vars.hpp"
+
+extern "C"
+{
+// without an extern declaration, clang does not emit this global into the object file
+extern const uintptr_t contractDescriptorPointerData[];
+
+const uintptr_t contractDescriptorPointerData[] = {
+ (uintptr_t)0, // placeholder
+#define CDAC_GLOBAL_POINTER(name,value) (uintptr_t)(value),
+#include "datadescriptor.inc"
+};
+
+}
diff --git a/src/coreclr/debug/runtimeinfo/contracts.jsonc b/src/coreclr/debug/runtimeinfo/contracts.jsonc
new file mode 100644
index 00000000000000..81b28cd2e1fe92
--- /dev/null
+++ b/src/coreclr/debug/runtimeinfo/contracts.jsonc
@@ -0,0 +1,27 @@
+//algorithmic contracts for coreclr
+// The format of this file is: JSON with comments
+// {
+// "CONTRACT NAME": VERSION,
+// ...
+// }
+// CONTRACT NAME is an arbitrary string, VERSION is an integer
+//
+// cdac-build-tool can take multiple "-c contract_file" arguments
+// so to conditionally include contracts, put additional contracts in a separate file
+{
+ "CodeVersions": 1,
+ "DacStreams": 1,
+ "EcmaMetadata" : 1,
+ "Exception": 1,
+ "ExecutionManager": 2,
+ "Loader": 1,
+ "Object": 1,
+ "PlatformMetadata": 1,
+ "PrecodeStubs": 3,
+ "ReJIT": 1,
+ "RuntimeInfo": 1,
+ "RuntimeTypeSystem": 1,
+ "StackWalk": 1,
+ "StressLog": 2,
+ "Thread": 1
+}
diff --git a/src/coreclr/debug/runtimeinfo/datadescriptor.cpp b/src/coreclr/debug/runtimeinfo/datadescriptor.cpp
new file mode 100644
index 00000000000000..7400c2582c83ad
--- /dev/null
+++ b/src/coreclr/debug/runtimeinfo/datadescriptor.cpp
@@ -0,0 +1,341 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#include "common.h"
+
+#include
+#include
+
+#include
+#include "cdacplatformmetadata.hpp"
+#include "methodtable.h"
+#include "threads.h"
+#include "exinfo.h"
+
+#include "configure.h"
+
+#include "../debug/ee/debugger.h"
+
+#ifdef HAVE_GCCOVER
+#include "gccover.h"
+#endif // HAVE_GCCOVER
+
+// begin blob definition
+
+extern "C"
+{
+
+struct TypeSpec
+{
+ uint32_t Name;
+ uint32_t Fields;
+ uint16_t Size; // note: C++ fragile no designated initializers - Size must come after Name and Fields
+};
+
+struct FieldSpec
+{
+ uint32_t Name;
+ uint32_t TypeName;
+ uint16_t FieldOffset;
+};
+
+struct GlobalLiteralSpec
+{
+ uint32_t Name;
+ uint32_t TypeName;
+ uint64_t Value;
+};
+
+struct GlobalPointerSpec
+{
+ uint32_t Name;
+ uint32_t PointerDataIndex;
+};
+
+struct GlobalStringSpec
+{
+ uint32_t Name;
+ uint32_t StringValue;
+};
+
+#define CONCAT(token1,token2) token1 ## token2
+#define CONCAT4(token1, token2, token3, token4) token1 ## token2 ## token3 ## token4
+
+#define MAKE_TYPELEN_NAME(tyname) CONCAT(cdac_string_pool_typename__, tyname)
+#define MAKE_FIELDLEN_NAME(tyname,membername) CONCAT4(cdac_string_pool_membername__, tyname, __, membername)
+#define MAKE_FIELDTYPELEN_NAME(tyname,membername) CONCAT4(cdac_string_pool_membertypename__, tyname, __, membername)
+#define MAKE_GLOBALLEN_NAME(globalname) CONCAT(cdac_string_pool_globalname__, globalname)
+#define MAKE_GLOBALTYPELEN_NAME(globalname) CONCAT(cdac_string_pool_globaltypename__, globalname)
+#define MAKE_GLOBALVALUELEN_NAME(globalname) CONCAT(cdac_string_pool_globalvalue__, globalname)
+
+// used to stringify the result of a macros expansion
+#define STRINGIFY(x) #x
+
+// define a struct where the size of each field is the length of some string. we will use offsetof to get
+// the offset of each struct element, which will be equal to the offset of the beginning of that string in the
+// string pool.
+struct CDacStringPoolSizes
+{
+ char cdac_string_pool_nil; // make the first real string start at offset 1
+#define DECL_LEN(membername,len) char membername[(len)];
+#define CDAC_BASELINE(name) DECL_LEN(cdac_string_pool_baseline_, (sizeof(name)))
+#define CDAC_TYPE_BEGIN(name) DECL_LEN(MAKE_TYPELEN_NAME(name), sizeof(#name))
+#define CDAC_TYPE_FIELD(tyname,membertyname,membername,offset) DECL_LEN(MAKE_FIELDLEN_NAME(tyname,membername), sizeof(#membername)) \
+ DECL_LEN(MAKE_FIELDTYPELEN_NAME(tyname,membername), sizeof(#membertyname))
+#define CDAC_GLOBAL_STRING(name, stringval) DECL_LEN(MAKE_GLOBALLEN_NAME(name), sizeof(#name)) \
+ DECL_LEN(MAKE_GLOBALVALUELEN_NAME(name), sizeof(STRINGIFY(stringval)))
+#define CDAC_GLOBAL_POINTER(name,value) DECL_LEN(MAKE_GLOBALLEN_NAME(name), sizeof(#name))
+#define CDAC_GLOBAL(name,tyname,value) DECL_LEN(MAKE_GLOBALLEN_NAME(name), sizeof(#name)) \
+ DECL_LEN(MAKE_GLOBALTYPELEN_NAME(name), sizeof(#tyname))
+#include "datadescriptor.inc"
+ char cdac_string_pool_trailing_nil;
+#undef DECL_LEN
+};
+
+#define GET_TYPE_NAME(name) offsetof(struct CDacStringPoolSizes, MAKE_TYPELEN_NAME(name))
+#define GET_FIELD_NAME(tyname,membername) offsetof(struct CDacStringPoolSizes, MAKE_FIELDLEN_NAME(tyname,membername))
+#define GET_FIELDTYPE_NAME(tyname,membername) offsetof(struct CDacStringPoolSizes, MAKE_FIELDTYPELEN_NAME(tyname,membername))
+#define GET_GLOBAL_NAME(globalname) offsetof(struct CDacStringPoolSizes, MAKE_GLOBALLEN_NAME(globalname))
+#define GET_GLOBALTYPE_NAME(globalname) offsetof(struct CDacStringPoolSizes, MAKE_GLOBALTYPELEN_NAME(globalname))
+#define GET_GLOBALSTRING_VALUE(globalname) offsetof(struct CDacStringPoolSizes, MAKE_GLOBALVALUELEN_NAME(globalname))
+
+// count the types
+enum
+{
+ CDacBlobTypesCount =
+#define CDAC_TYPES_BEGIN() 0
+#define CDAC_TYPE_BEGIN(name) + 1
+#include "datadescriptor.inc"
+};
+
+// count the field pool size.
+// there's 1 placeholder element at the start, and 1 endmarker after each type
+enum
+{
+ CDacBlobFieldsPoolCount =
+#define CDAC_TYPES_BEGIN() 1
+#define CDAC_TYPE_FIELD(tyname,membertyname,membername,offset) + 1
+#define CDAC_TYPE_END(name) + 1
+#include "datadescriptor.inc"
+};
+
+// count the literal globals
+enum
+{
+ CDacBlobGlobalLiteralsCount =
+#define CDAC_GLOBALS_BEGIN() 0
+#define CDAC_GLOBAL(name,tyname,value) + 1
+#include "datadescriptor.inc"
+};
+
+// count the aux vector globals
+enum
+{
+ CDacBlobGlobalPointersCount =
+#define CDAC_GLOBALS_BEGIN() 0
+#define CDAC_GLOBAL_POINTER(name,value) + 1
+#include "datadescriptor.inc"
+};
+
+// count the global strings
+enum
+{
+ CDacBlobGlobalStringsCount =
+#define CDAC_GLOBALS_BEGIN() 0
+#define CDAC_GLOBAL_STRING(name,value) + 1
+#include "datadescriptor.inc"
+};
+
+
+#define MAKE_TYPEFIELDS_TYNAME(tyname) CONCAT(CDacFieldsPoolTypeStart__, tyname)
+
+// index of each run of fields.
+// we make a struct containing one 1-byte field for each field in the run, and then take the offset of the
+// struct to get the index of the run of fields.
+// this looks like
+//
+// struct CDacFieldsPoolSizes {
+// char cdac_fields_pool_start_placeholder__;
+// struct CDacFieldsPoolTypeStart__MethodTable {
+// char cdac_fields_pool_member__MethodTable__GCHandle;
+// char cdac_fields_pool_member__MethodTable_endmarker;
+// } CDacFieldsPoolTypeStart__MethodTable;
+// ...
+// };
+//
+// so that offsetof(struct CDacFieldsPoolSizes, CDacFieldsPoolTypeStart__MethodTable) will give the offset of the
+// method table field descriptors in the run of fields
+struct CDacFieldsPoolSizes
+{
+#define DECL_LEN(membername) char membername;
+#define CDAC_TYPES_BEGIN() DECL_LEN(cdac_fields_pool_start_placeholder__)
+#define CDAC_TYPE_BEGIN(name) struct MAKE_TYPEFIELDS_TYNAME(name) {
+#define CDAC_TYPE_FIELD(tyname,membertyname,membername,offset) DECL_LEN(CONCAT4(cdac_fields_pool_member__, tyname, __, membername))
+#define CDAC_TYPE_END(name) DECL_LEN(CONCAT4(cdac_fields_pool_member__, tyname, _, endmarker)) \
+ } MAKE_TYPEFIELDS_TYNAME(name);
+#include "datadescriptor.inc"
+#undef DECL_LEN
+};
+
+#define GET_TYPE_FIELDS(tyname) offsetof(struct CDacFieldsPoolSizes, MAKE_TYPEFIELDS_TYNAME(tyname))
+
+// index of each global pointer
+//
+// struct CDacGlobalPointerIndex
+// {
+// char placeholder;
+// char firstGlobalPointerName;
+// char secondGlobalPointerName;
+// ...
+//}
+//
+// offsetof (CDACGlobalPointerIndex, NAME) returns the index of the global
+struct CDacGlobalPointerIndex
+{
+#define DECL_LEN(membername) char membername;
+#define CDAC_GLOBALS_BEGIN() DECL_LEN(cdac_global_pointer_index_start_placeholder__)
+#define CDAC_GLOBAL_POINTER(name,value) DECL_LEN(CONCAT(cdac_global_pointer_index__, name))
+#include "datadescriptor.inc"
+#undef DECL_LEN
+};
+
+#define GET_GLOBAL_POINTER_INDEX(name) offsetof(struct CDacGlobalPointerIndex, CONCAT(cdac_global_pointer_index__, name))
+
+struct BinaryBlobDataDescriptor
+{
+ // The blob begins with a directory that gives the relative offsets of the `Baseline`, `Types`,
+ // `FieldsPool`, `GlobalLiteralValues`, `GlobalPointerValues` and `Names` fields of the blob.
+ // The number of elements of each of the arrays is next. This is followed by the sizes of the
+ // spec structs. Since `BinaryBlobDataDescriptor` is created via macros, we want to embed the
+ // `offsetof` and `sizeof` of the components of the blob into the blob itself without having to
+ // account for any padding that the C/C++ compiler may introduce to enforce alignment.
+ // Additionally the `Directory` tries to follow a common C/C++ alignment rule (we don't want
+ // padding introduced in the directory itself): N-byte members are aligned to start on N-byte
+ // boundaries.
+ struct Directory {
+ uint32_t FlagsAndBaselineStart;
+ uint32_t TypesStart;
+
+ uint32_t FieldsPoolStart;
+ uint32_t GlobalLiteralValuesStart;
+
+ uint32_t GlobalPointersStart;
+ uint32_t GlobalStringValuesStart;
+ uint32_t NamesPoolStart;
+
+ uint32_t TypeCount;
+ uint32_t FieldsPoolCount;
+
+ uint32_t GlobalLiteralValuesCount;
+ uint32_t GlobalPointerValuesCount;
+ uint32_t GlobalStringValuesCount;
+
+ uint32_t NamesPoolCount;
+
+ uint8_t TypeSpecSize;
+ uint8_t FieldSpecSize;
+ uint8_t GlobalLiteralSpecSize;
+ uint8_t GlobalPointerSpecSize;
+ uint8_t GlobalStringSpecSize;
+ } Directory;
+ uint32_t PlatformFlags;
+ uint32_t BaselineName;
+ struct TypeSpec Types[CDacBlobTypesCount];
+ struct FieldSpec FieldsPool[CDacBlobFieldsPoolCount];
+ struct GlobalLiteralSpec GlobalLiteralValues[CDacBlobGlobalLiteralsCount];
+ struct GlobalPointerSpec GlobalPointerValues[CDacBlobGlobalPointersCount];
+ struct GlobalStringSpec GlobalStringValues[CDacBlobGlobalStringsCount];
+ uint8_t NamesPool[sizeof(struct CDacStringPoolSizes)];
+ uint8_t EndMagic[4];
+};
+
+struct MagicAndBlob {
+ uint64_t magic;
+ struct BinaryBlobDataDescriptor Blob;
+};
+
+// we only support 32-bit and 64-bit right now
+static_assert(sizeof(void*) == 4 || sizeof(void*) == 8);
+
+// C-style designated initializers are a C++20 feature. Have to use plain old aggregate initialization instead.
+
+DLLEXPORT
+struct MagicAndBlob BlobDataDescriptor = {
+ /*.magic = */ 0x00424F4C42434144ull,// "DACBLOB",
+ /*.Blob =*/ {
+ /*.Directory =*/ {
+ /* .FlagsAndBaselineStart = */ offsetof(struct BinaryBlobDataDescriptor, PlatformFlags),
+ /* .TypesStart = */ offsetof(struct BinaryBlobDataDescriptor, Types),
+ /* .FieldsPoolStart = */ offsetof(struct BinaryBlobDataDescriptor, FieldsPool),
+ /* .GlobalLiteralValuesStart = */ offsetof(struct BinaryBlobDataDescriptor, GlobalLiteralValues),
+ /* .GlobalPointersStart = */ offsetof(struct BinaryBlobDataDescriptor, GlobalPointerValues),
+ /* .GlobalStringValuesStart = */ offsetof(struct BinaryBlobDataDescriptor, GlobalStringValues),
+ /* .NamesPoolStart = */ offsetof(struct BinaryBlobDataDescriptor, NamesPool),
+ /* .TypeCount = */ CDacBlobTypesCount,
+ /* .FieldsPoolCount = */ CDacBlobFieldsPoolCount,
+ /* .GlobalLiteralValuesCount = */ CDacBlobGlobalLiteralsCount,
+ /* .GlobalPointerValuesCount = */ CDacBlobGlobalPointersCount,
+ /* .GlobalStringValuesCount = */ CDacBlobGlobalStringsCount,
+ /* .NamesPoolCount = */ sizeof(struct CDacStringPoolSizes),
+ /* .TypeSpecSize = */ sizeof(struct TypeSpec),
+ /* .FieldSpecSize = */ sizeof(struct FieldSpec),
+ /* .GlobalLiteralSpecSize = */ sizeof(struct GlobalLiteralSpec),
+ /* .GlobalPointerSpecSize = */ sizeof(struct GlobalPointerSpec),
+ /* .GlobalStringSpecSize = */ sizeof(struct GlobalStringSpec)
+ },
+ /* .PlatformFlags = */ (sizeof(void*) == 4 ? 0x02 : 0) | 0x01,
+ /* .BaselineName = */ offsetof(struct CDacStringPoolSizes, cdac_string_pool_baseline_),
+
+ /* .Types = */ {
+#define CDAC_TYPE_BEGIN(name) { \
+ /* .Name = */ GET_TYPE_NAME(name), \
+ /* .Fields = */ GET_TYPE_FIELDS(name),
+#define CDAC_TYPE_INDETERMINATE(name) /*.Size = */ 0,
+#define CDAC_TYPE_SIZE(size) /* .Size = */ size,
+#define CDAC_TYPE_END(name) },
+#include "datadescriptor.inc"
+ },
+
+ /* .FieldsPool = */ {
+#define CDAC_TYPES_BEGIN() {0,},
+#define CDAC_TYPE_FIELD(tyname,membertyname,membername,offset) { \
+ /* .Name = */ GET_FIELD_NAME(tyname,membername), \
+ /* .TypeName = */ GET_FIELDTYPE_NAME(tyname,membername), \
+ /* .FieldOffset = */ offset, \
+},
+#define CDAC_TYPE_END(name) { 0, },
+#include "datadescriptor.inc"
+ },
+
+ /* .GlobalLiteralValues = */ {
+#define CDAC_GLOBAL(name,tyname,value) { /*.Name = */ GET_GLOBAL_NAME(name), /* .TypeName = */ GET_GLOBALTYPE_NAME(name), /* .Value = */ value },
+#include "datadescriptor.inc"
+ },
+
+ /* .GlobalPointerValues = */ {
+#define CDAC_GLOBAL_POINTER(name,value) { /* .Name = */ GET_GLOBAL_NAME(name), /* .PointerDataIndex = */ GET_GLOBAL_POINTER_INDEX(name) },
+#include "datadescriptor.inc"
+ },
+
+ /* .GlobalStringValues = */ {
+#define CDAC_GLOBAL_STRING(name,value) { /* .Name = */ GET_GLOBAL_NAME(name), /* .Value = */ GET_GLOBALSTRING_VALUE(name) },
+#include "datadescriptor.inc"
+ },
+
+ /* .NamesPool = */ ("\0" // starts with a nul
+#define CDAC_BASELINE(name) name "\0"
+#define CDAC_TYPE_BEGIN(name) #name "\0"
+#define CDAC_TYPE_FIELD(tyname,membertyname,membername,offset) #membername "\0" #membertyname "\0"
+#define CDAC_GLOBAL_STRING(name,value) #name "\0" STRINGIFY(value) "\0"
+#define CDAC_GLOBAL_POINTER(name,value) #name "\0"
+#define CDAC_GLOBAL(name,tyname,value) #name "\0" #tyname "\0"
+#include "datadescriptor.inc"
+ ),
+
+ /* .EndMagic = */ { 0x01, 0x02, 0x03, 0x04 },
+ }
+};
+
+// end blob definition
+
+} // extern "C"
diff --git a/src/coreclr/debug/runtimeinfo/datadescriptor.inc b/src/coreclr/debug/runtimeinfo/datadescriptor.inc
new file mode 100644
index 00000000000000..f28496e5c9b7d3
--- /dev/null
+++ b/src/coreclr/debug/runtimeinfo/datadescriptor.inc
@@ -0,0 +1,1027 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+//
+// No include guards. This file is included multiple times.
+
+// The format is:
+// CDAC_BASELINE("string") baseline data contract that the runtime should follow. "empty" is reasonable
+// CDAC_TYPES_BEGIN()
+// ... ...
+// CDAC_TYPES_END()
+// CDAC_GLOBALS_BEGIN()
+// ... ...
+// CDAC_GLOBALS_END()
+//
+// In the format is:
+// CDAC_TYPE_BEGIN(cdacTypeIdentifier) // defined a new data descriptor named cdacIdentifier
+//
+// CDAC_TYPE_SIZE(k) -or- CDAC_TYPE_INDETERMINATE(cdacTypeIdentifier) specifies that the type has
+// size k (bytes - usually sizeof(SomeNativeType)) or specify that the type's size is not provided
+// It is important that CDAC_TYPE_SIZE or CDAC_TYPE_INDETERMINATE immediately follows
+// CDAC_TYPE_BEGIN
+//
+// CDAC_TYPE_FIELD(cdacTypeIdentifier, cdacFieldTypeIdentifier, cdacFieldName, k) specifies the
+// field of "cdacTypeIdentifier" that has name cdacFieldName and has the type
+// "cdacFieldtypeIdentifier" located at offset k in the type layout. k is usually
+// offsetof(SomeClass, m_FieldName) if the field is public
+//
+// if the field is private, the convention is that SomeClass declares a friend struct
+// cdac_data and provides a specialization of cdac_data with a public constexpr
+// size_t member that holds the offset:
+//
+// class MyClass {
+// private:
+// void* m_myField;
+// friend template cdac_data;
+// };
+// template<> struct cdac_data {
+// static constexpr size_t MyField = offsetof(MyClass, m_myField);
+// };
+//
+// then the field layout can be specified as
+// CDAC_TYPE_FIELD(MyClassLayout, pointer, MyField, cdac_data::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
+//
+// In the format is:
+//
+// CDAC_GLOBAL(cdacGlobalName, cdacTypeIdentifier, value)
+// or
+// CDAC_GLOBAL_POINTER(cdacGlobalName, cdacTypeIdentifier, address)
+//
+// Zero or more globals can be defined
+//
+// if a global is given with CDAC_GLOBAL(), `value` should be a constexpr uint64_t (or convertible
+// to uint64_t) for example, it can be a literal constant or a preprocessor definition
+//
+// if a global is a CDAC_GLOBAL_POINTER(), address should be a constexpr pointer or a constexpr
+// uintptr_t
+//
+//
+//
+// This file is compiled using the target architecture. Preprocessor defines for the target
+// platform will be available. It is ok to use `#ifdef`.
+
+#ifndef CDAC_BASELINE
+#define CDAC_BASELINE(identifier)
+#endif
+#ifndef CDAC_TYPES_BEGIN
+#define CDAC_TYPES_BEGIN()
+#endif
+#ifndef CDAC_TYPE_BEGIN
+#define CDAC_TYPE_BEGIN(tyname)
+#endif
+#ifndef CDAC_TYPE_SIZE
+#define CDAC_TYPE_SIZE(k)
+#endif
+#ifndef CDAC_TYPE_INDETERMINATE
+#define CDAC_TYPE_INDETERMINATE(tyname)
+#endif
+#ifndef CDAC_TYPE_FIELD
+#define CDAC_TYPE_FIELD(tyname,fieldtyname,fieldname,off)
+#endif
+#ifndef CDAC_TYPE_END
+#define CDAC_TYPE_END(tyname)
+#endif
+#ifndef CDAC_TYPES_END
+#define CDAC_TYPES_END()
+#endif
+#ifndef CDAC_GLOBALS_BEGIN
+#define CDAC_GLOBALS_BEGIN()
+#endif
+#ifndef CDAC_GLOBAL
+#define CDAC_GLOBAL(globalname,tyname,val)
+#endif
+#ifndef CDAC_GLOBAL_POINTER
+#define CDAC_GLOBAL_POINTER(globalname,addr)
+#endif
+#ifndef CDAC_GLOBAL_STRING
+#define CDAC_GLOBAL_STRING(globalname,stringval)
+#endif
+#ifndef CDAC_GLOBALS_END
+#define CDAC_GLOBALS_END()
+#endif
+
+CDAC_BASELINE("empty")
+CDAC_TYPES_BEGIN()
+
+CDAC_TYPE_BEGIN(Thread)
+CDAC_TYPE_INDETERMINATE(Thread)
+CDAC_TYPE_FIELD(Thread, /*uint32*/, Id, cdac_data::Id)
+CDAC_TYPE_FIELD(Thread, /*nuint*/, OSId, cdac_data::OSId)
+CDAC_TYPE_FIELD(Thread, /*uint32*/, State, cdac_data::State)
+CDAC_TYPE_FIELD(Thread, /*uint32*/, PreemptiveGCDisabled, cdac_data::PreemptiveGCDisabled)
+CDAC_TYPE_FIELD(Thread, /*pointer*/, RuntimeThreadLocals, cdac_data::RuntimeThreadLocals)
+CDAC_TYPE_FIELD(Thread, /*pointer*/, Frame, cdac_data::Frame)
+CDAC_TYPE_FIELD(Thread, /*pointer*/, ExceptionTracker, cdac_data::ExceptionTracker)
+CDAC_TYPE_FIELD(Thread, GCHandle, GCHandle, cdac_data::ExposedObject)
+CDAC_TYPE_FIELD(Thread, GCHandle, LastThrownObject, cdac_data::LastThrownObject)
+CDAC_TYPE_FIELD(Thread, pointer, LinkNext, cdac_data::Link)
+#ifndef TARGET_UNIX
+CDAC_TYPE_FIELD(Thread, /*pointer*/, TEB, cdac_data::TEB)
+#endif
+CDAC_TYPE_END(Thread)
+
+CDAC_TYPE_BEGIN(ThreadStore)
+CDAC_TYPE_INDETERMINATE(ThreadStore)
+CDAC_TYPE_FIELD(ThreadStore, /*SLink*/, FirstThreadLink, cdac_data::FirstThreadLink)
+CDAC_TYPE_FIELD(ThreadStore, /*int32*/, ThreadCount, cdac_data::ThreadCount)
+CDAC_TYPE_FIELD(ThreadStore, /*int32*/, UnstartedCount, cdac_data::UnstartedCount)
+CDAC_TYPE_FIELD(ThreadStore, /*int32*/, BackgroundCount, cdac_data::BackgroundCount)
+CDAC_TYPE_FIELD(ThreadStore, /*int32*/, PendingCount, cdac_data::PendingCount)
+CDAC_TYPE_FIELD(ThreadStore, /*int32*/, DeadCount, cdac_data::DeadCount)
+CDAC_TYPE_END(ThreadStore)
+
+CDAC_TYPE_BEGIN(RuntimeThreadLocals)
+CDAC_TYPE_INDETERMINATE(RuntimeThreadLocals)
+CDAC_TYPE_FIELD(RuntimeThreadLocals, /*EEAllocContext*/, AllocContext, offsetof(RuntimeThreadLocals, alloc_context))
+CDAC_TYPE_END(RuntimeThreadLocals)
+
+CDAC_TYPE_BEGIN(EEAllocContext)
+CDAC_TYPE_INDETERMINATE(EEAllocContext)
+CDAC_TYPE_FIELD(EEAllocContext, /*GCAllocContext*/, GCAllocationContext, offsetof(ee_alloc_context, m_GCAllocContext))
+CDAC_TYPE_END(EEAllocContext)
+
+CDAC_TYPE_BEGIN(GCAllocContext)
+CDAC_TYPE_INDETERMINATE(GCAllocContext)
+CDAC_TYPE_FIELD(GCAllocContext, /*pointer*/, Pointer, offsetof(gc_alloc_context, alloc_ptr))
+CDAC_TYPE_FIELD(GCAllocContext, /*pointer*/, Limit, offsetof(gc_alloc_context, alloc_limit))
+CDAC_TYPE_END(GCAllocContext)
+
+// Exception
+
+// Use exact managed type field names for the descriptor as field names often can't change due to binary serialization or implicit diagnostic contracts
+CDAC_TYPE_BEGIN(Exception)
+CDAC_TYPE_INDETERMINATE(Exception)
+CDAC_TYPE_FIELD(Exception, /*pointer*/, _message, cdac_data::_message)
+CDAC_TYPE_FIELD(Exception, /*pointer*/, _innerException, cdac_data::_innerException)
+CDAC_TYPE_FIELD(Exception, /*pointer*/, _stackTrace, cdac_data::_stackTrace)
+CDAC_TYPE_FIELD(Exception, /*pointer*/, _watsonBuckets, cdac_data::_watsonBuckets)
+CDAC_TYPE_FIELD(Exception, /*pointer*/, _stackTraceString, cdac_data::_stackTraceString)
+CDAC_TYPE_FIELD(Exception, /*pointer*/, _remoteStackTraceString, cdac_data::_remoteStackTraceString)
+CDAC_TYPE_FIELD(Exception, /*int32*/, _HResult, cdac_data::_HResult)
+CDAC_TYPE_FIELD(Exception, /*int32*/, _xcode, cdac_data::_xcode)
+CDAC_TYPE_END(Exception)
+
+CDAC_TYPE_BEGIN(ExceptionInfo)
+CDAC_TYPE_INDETERMINATE(ExceptionInfo)
+CDAC_TYPE_FIELD(ExceptionInfo, /*pointer*/, ThrownObject, offsetof(ExInfo, m_hThrowable))
+CDAC_TYPE_FIELD(PreviousNestedInfo, /*pointer*/, PreviousNestedInfo, offsetof(ExInfo, m_pPrevNestedInfo))
+CDAC_TYPE_END(ExceptionInfo)
+
+
+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