diff --git a/docs/design/libraries/ComInterfaceGenerator/Compatibility.md b/docs/design/libraries/ComInterfaceGenerator/Compatibility.md
new file mode 100644
index 0000000000000..8fbc707bc58f1
--- /dev/null
+++ b/docs/design/libraries/ComInterfaceGenerator/Compatibility.md
@@ -0,0 +1,27 @@
+# Semantic Compatibility
+
+Documentation on compatibility guidance and the current state. The version headings act as a rolling delta between the previous version.
+
+## .NET 8
+
+### Interface base types
+
+IUnknown-derived interfaces are supported. IDispatch-based interfaces are disallowed. The default is IUnknown-derived (in comparison to the built-in support's default of IDispatch-derived).
+
+### Marshalling rules
+
+The marshalling rules are identical to LibraryImportGenerator's support.
+
+### Interface inheritance
+
+Interface inheritance is supported for up to one COM-based interface type. Unlike the built-in COM interop system, base interface methods do **NOT** need to be redefined. The source generator discovers the members from the base interface and generates the derived interface members at appropriate offsets.
+
+The generator also generates shadow members in the derived interface for each base interface member. The shadow members have default implementations that call the base interface member, but the emitted code for the "COM Object Wrapper" implementation will override the shadow members with a call to the underlying COM interface member on the current interface. This shadow member support helps reduce `QueryInterface` overhead in interface inheritance scenarios.
+
+### Interop with `ComImport`
+
+Source-generated COM will provide limited opt-in interop with `ComImport`-based COM interop. In particular, the following scenarios are supported:
+
+- Casting a "Com Object Wrapper" created using `StrategyBasedComWrappers` to a `ComImport`-based interface type.
+
+This support is achieved through some internal interfaces and reflection-emit to shim a `DynamicInterfaceCastableImplementation` of a `ComImport` interface to use the built-in runtime interop marshalling support. The core of this experience is implemented by the `System.Runtime.InteropServices.Marshalling.ComImportInteropInterfaceDetailsStrategy` class.
diff --git a/eng/testing/linker/project.csproj.template b/eng/testing/linker/project.csproj.template
index ab498732e2ebf..f322932390520 100644
--- a/eng/testing/linker/project.csproj.template
+++ b/eng/testing/linker/project.csproj.template
@@ -9,6 +9,7 @@
{PublishAot}{AppHostSourcePath}{SingleFileHostSourcePath}
+ true{MonoAOTCompilerDir}
diff --git a/src/libraries/System.Runtime.InteropServices/src/ILLink/ILLink.Substitutions.xml b/src/libraries/System.Runtime.InteropServices/src/ILLink/ILLink.Substitutions.xml
new file mode 100644
index 0000000000000..958b0e0280bb1
--- /dev/null
+++ b/src/libraries/System.Runtime.InteropServices/src/ILLink/ILLink.Substitutions.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/src/libraries/System.Runtime.InteropServices/src/System.Runtime.InteropServices.csproj b/src/libraries/System.Runtime.InteropServices/src/System.Runtime.InteropServices.csproj
index adc431932e547..393febd4cdcb7 100644
--- a/src/libraries/System.Runtime.InteropServices/src/System.Runtime.InteropServices.csproj
+++ b/src/libraries/System.Runtime.InteropServices/src/System.Runtime.InteropServices.csproj
@@ -32,6 +32,7 @@
+
@@ -72,6 +73,10 @@
+
+
+
+
diff --git a/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/Marshalling/ComImportInteropInterfaceDetailsStrategy.cs b/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/Marshalling/ComImportInteropInterfaceDetailsStrategy.cs
new file mode 100644
index 0000000000000..e4579ac5f58de
--- /dev/null
+++ b/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/Marshalling/ComImportInteropInterfaceDetailsStrategy.cs
@@ -0,0 +1,198 @@
+// 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.CodeAnalysis;
+using System.Reflection;
+using System.Reflection.Emit;
+using System.Runtime.CompilerServices;
+
+namespace System.Runtime.InteropServices.Marshalling
+{
+ ///
+ /// An interface details strategy that enables discovering both interfaces defined with source-generated COM (i.e. and ) and built-in COM (i.e. ).
+ ///
+ ///
+ /// This strategy is meant for intermediary adoption scenarios and is not compatible with trimming or NativeAOT by design. Since built-in COM is not trim friendly or AOT-compatible, these restrictions are okay.
+ /// This strategy only supports "COM Object Wrapper" scenarios, so casting a COM object wrapper to a -attributed type. It does not support exposing a -attributed type as an additional interface on a managed object wrapper.
+ /// The strategy provides -based implementations of -attributed interfaces by dynamically generating an interface using that has the following shape:
+ ///
+ /// [assembly:IgnoresAccessChecksTo("AssemblyContainingIComInterface")]
+ /// [assembly:IgnoresAccessChecksTo("AssemblyContainingRetType")]
+ /// [assembly:IgnoresAccessChecksTo("AssemblyContainingArgType1")]
+ /// [assembly:IgnoresAccessChecksTo("AssemblyContainingArgType2")]
+ /// // One attribute per containing assembly of each type used in each method signature of the interface.
+ ///
+ /// namespace System.Runtime.CompilerServices
+ /// {
+ /// [AssemblyUsage(AttributeTargets.Assembly, AllowMultiple = true)]
+ /// internal class IgnoresAccessChecksToAttribute : Attribute
+ /// {
+ /// public IgnoresAccessChecksToAttribute(string assemblyName) { }
+ /// }
+ /// }
+ ///
+ /// [DynamicInterfaceCastableImplementation]
+ /// interface InterfaceForwarder : IComInterface
+ /// {
+ /// RetType IComInterface.Method1(ArgType1 arg1, ArgType2 arg2, ...)
+ /// {
+ /// return ((IComInterface)((IComImportAdapter)this).GetRuntimeCallableWrapper())(arg1, arg2, ...);
+ /// }
+ /// }
+ ///
+ ///
+ /// This mechanism allows source-generated COM interop to allow using built-in COM interfaces with runtime-defined marshalling behavior with minimal work on the source-generated COM interop side.
+ /// Additionally, by scoping the majority of the logic to this class, we make this logic more easily trimmable.
+ ///
+ /// We emit the IgnoresAccessChecksToAttribute to enable casting to internal types, which is a very common scenario (most types are internal).
+ ///
+ [RequiresDynamicCode("Enabling interop between source-generated and built-in COM is not supported when trimming is enabled.")]
+ [RequiresUnreferencedCode("Enabling interop between source-generated and built-in COM requires dynamic code generation.")]
+ internal sealed class ComImportInteropInterfaceDetailsStrategy : IIUnknownInterfaceDetailsStrategy
+ {
+ public static readonly IIUnknownInterfaceDetailsStrategy Instance = new ComImportInteropInterfaceDetailsStrategy();
+
+ private readonly ConditionalWeakTable _forwarderInterfaceCache = new();
+
+ // TODO: Support exposing ComImport interfaces through StrategyBasedComWrappers?
+ public IComExposedDetails? GetComExposedTypeDetails(RuntimeTypeHandle type) => DefaultIUnknownInterfaceDetailsStrategy.Instance.GetComExposedTypeDetails(type);
+
+ public IIUnknownDerivedDetails? GetIUnknownDerivedDetails(RuntimeTypeHandle type)
+ {
+ Type runtimeType = Type.GetTypeFromHandle(type)!;
+ if (!runtimeType.IsImport)
+ {
+ return DefaultIUnknownInterfaceDetailsStrategy.Instance.GetIUnknownDerivedDetails(type);
+ }
+
+ Type implementationType = _forwarderInterfaceCache.GetValue(runtimeType, runtimeType =>
+ {
+ AssemblyBuilder assembly = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("ComImportForwarder"), runtimeType.IsCollectible ? AssemblyBuilderAccess.RunAndCollect : AssemblyBuilderAccess.Run);
+ ModuleBuilder module = assembly.DefineDynamicModule("ComImportForwarder");
+
+ ConstructorInfo ignoresAccessChecksToAttributeConstructor = GetIgnoresAccessChecksToAttributeConstructor(module);
+
+ assembly.SetCustomAttribute(new CustomAttributeBuilder(ignoresAccessChecksToAttributeConstructor, new object[] { typeof(IComImportAdapter).Assembly.GetName().Name! }));
+
+ TypeBuilder implementation = module.DefineType("InterfaceForwarder", TypeAttributes.Interface | TypeAttributes.Abstract, parent: null, interfaces: runtimeType.GetInterfaces());
+ implementation.AddInterfaceImplementation(runtimeType);
+ implementation.SetCustomAttribute(new CustomAttributeBuilder(typeof(DynamicInterfaceCastableImplementationAttribute).GetConstructor(Array.Empty())!, Array.Empty