diff --git a/src/installer/managed/Microsoft.NET.HostModel/AppHost/HostWriter.cs b/src/installer/managed/Microsoft.NET.HostModel/AppHost/HostWriter.cs
index b801cc4ee529d8..f9ce13d83b77da 100644
--- a/src/installer/managed/Microsoft.NET.HostModel/AppHost/HostWriter.cs
+++ b/src/installer/managed/Microsoft.NET.HostModel/AppHost/HostWriter.cs
@@ -32,13 +32,15 @@ public static partial class HostWriter
/// Specify whether to set the subsystem to GUI. Only valid for PE apphosts.
/// Path to the intermediate assembly, used for copying resources to PE apphosts.
/// Sign the app binary using codesign with an anonymous certificate.
+ /// Remove CET Shadow Stack compatibility flag if set
public static void CreateAppHost(
string appHostSourceFilePath,
string appHostDestinationFilePath,
string appBinaryFilePath,
bool windowsGraphicalUserInterface = false,
string assemblyToCopyResourcesFrom = null,
- bool enableMacOSCodeSign = false)
+ bool enableMacOSCodeSign = false,
+ bool disableCetCompat = false)
{
var bytesToWrite = Encoding.UTF8.GetBytes(appBinaryFilePath);
if (bytesToWrite.Length > 1024)
@@ -48,7 +50,7 @@ public static void CreateAppHost(
bool appHostIsPEImage = false;
- void RewriteAppHost(MemoryMappedViewAccessor accessor)
+ void RewriteAppHost(MemoryMappedFile mappedFile, MemoryMappedViewAccessor accessor)
{
// Re-write the destination apphost with the proper contents.
BinaryUtils.SearchAndReplace(accessor, AppBinaryPathPlaceholderSearchValue, bytesToWrite);
@@ -64,6 +66,11 @@ void RewriteAppHost(MemoryMappedViewAccessor accessor)
PEUtils.SetWindowsGraphicalUserInterfaceBit(accessor);
}
+
+ if (disableCetCompat && appHostIsPEImage)
+ {
+ PEUtils.RemoveCetCompatBit(mappedFile, accessor);
+ }
}
try
@@ -85,7 +92,7 @@ void RewriteAppHost(MemoryMappedViewAccessor accessor)
long sourceAppHostLength = appHostSourceStream.Length;
// Transform the host file in-memory.
- RewriteAppHost(memoryMappedViewAccessor);
+ RewriteAppHost(memoryMappedFile, memoryMappedViewAccessor);
// Save the transformed host.
using (FileStream fileStream = new FileStream(appHostDestinationFilePath, FileMode.Create))
diff --git a/src/installer/managed/Microsoft.NET.HostModel/AppHost/PEUtils.cs b/src/installer/managed/Microsoft.NET.HostModel/AppHost/PEUtils.cs
index 1bfc80fcfca492..6f4cca450a9390 100644
--- a/src/installer/managed/Microsoft.NET.HostModel/AppHost/PEUtils.cs
+++ b/src/installer/managed/Microsoft.NET.HostModel/AppHost/PEUtils.cs
@@ -41,6 +41,39 @@ public static bool IsPEImage(string filePath)
}
}
+ ///
+ /// Remove the CET Compat bit from extended DLL characteristics
+ ///
+ /// Memory-mapped PE file
+ ///
+ internal static void RemoveCetCompatBit(MemoryMappedFile file, MemoryMappedViewAccessor accessor)
+ {
+ using (PEReader reader = new PEReader(file.CreateViewStream(0, 0, MemoryMappedFileAccess.Read)))
+ {
+ // https://learn.microsoft.com/windows/win32/debug/pe-format#debug-type
+ const int IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS = 20;
+ foreach (DebugDirectoryEntry entry in reader.ReadDebugDirectory())
+ {
+ if ((int)entry.Type != IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS)
+ continue;
+
+ // Get the extended DLL characteristics from the debug directory entry data
+ ushort dllCharacteristics = AsLittleEndian(accessor.ReadUInt16(entry.DataPointer));
+
+ // Check for the CET compat bit
+ // https://learn.microsoft.com/windows/win32/debug/pe-format#extended-dll-characteristics
+ const ushort IMAGE_DLLCHARACTERISTICS_EX_CET_COMPAT = 0x1;
+ if ((dllCharacteristics & IMAGE_DLLCHARACTERISTICS_EX_CET_COMPAT) == 0)
+ break;
+
+ // Clear the CET compat bit
+ dllCharacteristics = (ushort)(dllCharacteristics & ~IMAGE_DLLCHARACTERISTICS_EX_CET_COMPAT);
+ accessor.Write(entry.DataPointer, AsLittleEndian(dllCharacteristics));
+ break;
+ }
+ }
+ }
+
///
/// This method will attempt to set the subsystem to GUI. The apphost file should be a windows PE file.
///
diff --git a/src/installer/tests/AppHost.Bundle.Tests/AppLaunch.cs b/src/installer/tests/AppHost.Bundle.Tests/AppLaunch.cs
index 62d632ac401668..a51cf310e79279 100644
--- a/src/installer/tests/AppHost.Bundle.Tests/AppLaunch.cs
+++ b/src/installer/tests/AppHost.Bundle.Tests/AppLaunch.cs
@@ -80,6 +80,28 @@ private void RunApp(bool selfContained)
}
}
+ [ConditionalTheory(typeof(Binaries.CetCompat), nameof(Binaries.CetCompat.IsSupported))]
+ [InlineData(true)]
+ [InlineData(false)]
+ public void DisableCetCompat(bool selfContained)
+ {
+ SingleFileTestApp app = selfContained
+ ? sharedTestState.SelfContainedApp.Copy()
+ : sharedTestState.FrameworkDependentApp.Copy();
+ app.CreateAppHost(disableCetCompat: true);
+
+ string singleFile = app.Bundle();
+ Command.Create(singleFile)
+ .CaptureStdErr()
+ .CaptureStdOut()
+ .DotNetRoot(TestContext.BuiltDotNet.BinPath, TestContext.BuildArchitecture)
+ .MultilevelLookup(false)
+ .Execute()
+ .Should().Pass()
+ .And.HaveStdOutContaining("Hello World")
+ .And.HaveStdOutContaining(TestContext.MicrosoftNETCoreAppVersion);
+ }
+
[Theory]
[InlineData(true)]
[InlineData(false)]
diff --git a/src/installer/tests/HostActivation.Tests/PortableAppActivation.cs b/src/installer/tests/HostActivation.Tests/FrameworkDependentAppLaunch.cs
similarity index 93%
rename from src/installer/tests/HostActivation.Tests/PortableAppActivation.cs
rename to src/installer/tests/HostActivation.Tests/FrameworkDependentAppLaunch.cs
index fa19efd53121ce..7ff4465194c7cc 100644
--- a/src/installer/tests/HostActivation.Tests/PortableAppActivation.cs
+++ b/src/installer/tests/HostActivation.Tests/FrameworkDependentAppLaunch.cs
@@ -10,11 +10,11 @@
namespace Microsoft.DotNet.CoreSetup.Test.HostActivation
{
- public class PortableAppActivation : IClassFixture
+ public class FrameworkDependentAppLaunch : IClassFixture
{
private readonly SharedTestState sharedTestState;
- public PortableAppActivation(SharedTestState fixture)
+ public FrameworkDependentAppLaunch(SharedTestState fixture)
{
sharedTestState = fixture;
}
@@ -90,9 +90,11 @@ public void Muxer_SpecificRuntimeConfig()
}
[Fact]
- public void AppHost_FrameworkDependent_Succeeds()
+ public void AppHost()
{
string appExe = sharedTestState.App.AppExe;
+ if (Binaries.CetCompat.IsSupported)
+ Assert.True(Binaries.CetCompat.IsMarkedCompatible(appExe));
// Get the framework location that was built
string builtDotnet = TestContext.BuiltDotNet.BinPath;
@@ -125,7 +127,7 @@ public void AppHost_FrameworkDependent_Succeeds()
[Theory]
[InlineData(true)]
[InlineData(false)]
- public void AppHost_FrameworkDependent_GlobalLocation_Succeeds(bool useRegisteredLocation)
+ public void AppHost_GlobalLocation(bool useRegisteredLocation)
{
string appExe = sharedTestState.App.AppExe;
@@ -171,6 +173,24 @@ public void AppHost_FrameworkDependent_GlobalLocation_Succeeds(bool useRegistere
}
}
+ [ConditionalFact(typeof(Binaries.CetCompat), nameof(Binaries.CetCompat.IsSupported))]
+ public void AppHost_DisableCetCompat()
+ {
+ TestApp app = sharedTestState.App.Copy();
+ app.CreateAppHost(disableCetCompat: true);
+ Assert.False(Binaries.CetCompat.IsMarkedCompatible(app.AppExe));
+
+ Command.Create(app.AppExe)
+ .CaptureStdErr()
+ .CaptureStdOut()
+ .DotNetRoot(TestContext.BuiltDotNet.BinPath, TestContext.BuildArchitecture)
+ .MultilevelLookup(false)
+ .Execute()
+ .Should().Pass()
+ .And.HaveStdOutContaining("Hello World")
+ .And.HaveStdOutContaining(TestContext.MicrosoftNETCoreAppVersion);
+ }
+
[Fact]
public void RuntimeConfig_FilePath_Breaks_MAX_PATH_Threshold()
{
@@ -264,7 +284,7 @@ public void MissingFrameworkInRuntimeConfig_Fails(bool useAppHost)
[Theory]
[InlineData(true)]
[InlineData(false)]
- public void AppHost_CLI_FrameworkDependent_MissingRuntimeFramework_ErrorReportedInStdErr(bool missingHostfxr)
+ public void AppHost_CLI_MissingRuntimeFramework_ErrorReportedInStdErr(bool missingHostfxr)
{
using (var invalidDotNet = TestArtifact.Create("cliErrors"))
{
@@ -306,7 +326,7 @@ public void AppHost_CLI_FrameworkDependent_MissingRuntimeFramework_ErrorReported
[Fact]
[PlatformSpecific(TestPlatforms.Windows)] // GUI app host is only supported on Windows.
- public void AppHost_GUI_FrameworkDependent_MissingRuntimeFramework_ErrorReportedInDialog()
+ public void AppHost_GUI_MissingRuntimeFramework_ErrorReportedInDialog()
{
TestApp app = sharedTestState.App.Copy();
app.CreateAppHost(isWindowsGui: true);
@@ -407,7 +427,7 @@ public void AppHost_GUI_NoCustomErrorWriter_FrameworkMissing_ErrorReportedInDial
[Fact]
[PlatformSpecific(TestPlatforms.Windows)] // GUI app host is only supported on Windows.
- public void AppHost_GUI_FrameworkDependent_DisabledGUIErrors_DialogNotShown()
+ public void AppHost_GUI_DisabledGUIErrors_DialogNotShown()
{
TestApp app = sharedTestState.App.Copy();
app.CreateAppHost(isWindowsGui: true);
diff --git a/src/installer/tests/HostActivation.Tests/StandaloneAppActivation.cs b/src/installer/tests/HostActivation.Tests/SelfContainedAppLaunch.cs
similarity index 86%
rename from src/installer/tests/HostActivation.Tests/StandaloneAppActivation.cs
rename to src/installer/tests/HostActivation.Tests/SelfContainedAppLaunch.cs
index 7390a305ad1595..b3e311a6cdd65d 100644
--- a/src/installer/tests/HostActivation.Tests/StandaloneAppActivation.cs
+++ b/src/installer/tests/HostActivation.Tests/SelfContainedAppLaunch.cs
@@ -12,11 +12,11 @@
namespace HostActivation.Tests
{
- public class StandaloneAppActivation : IClassFixture
+ public class SelfContainedAppLaunch : IClassFixture
{
private SharedTestState sharedTestState;
- public StandaloneAppActivation(StandaloneAppActivation.SharedTestState fixture)
+ public SelfContainedAppLaunch(SelfContainedAppLaunch.SharedTestState fixture)
{
sharedTestState = fixture;
}
@@ -25,6 +25,9 @@ public StandaloneAppActivation(StandaloneAppActivation.SharedTestState fixture)
public void Default()
{
string appExe = sharedTestState.App.AppExe;
+ if (Binaries.CetCompat.IsSupported)
+ Assert.True(Binaries.CetCompat.IsMarkedCompatible(appExe));
+
Command.Create(appExe)
.CaptureStdErr()
.CaptureStdOut()
@@ -43,6 +46,22 @@ public void Default()
}
}
+ [ConditionalFact(typeof(Binaries.CetCompat), nameof(Binaries.CetCompat.IsSupported))]
+ public void AppHost_DisableCetCompat()
+ {
+ TestApp app = sharedTestState.App.Copy();
+ app.CreateAppHost(disableCetCompat: true);
+ Assert.False(Binaries.CetCompat.IsMarkedCompatible(app.AppExe));
+
+ Command.Create(app.AppExe)
+ .CaptureStdErr()
+ .CaptureStdOut()
+ .Execute()
+ .Should().Pass()
+ .And.HaveStdOutContaining("Hello World")
+ .And.HaveStdOutContaining(TestContext.MicrosoftNETCoreAppVersion);
+ }
+
[Fact]
public void NoDepsJson_NoRuntimeConfig()
{
diff --git a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost/CreateAppHost.cs b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost/CreateAppHost.cs
index 901e899bf26c31..5ed29edf90a3b8 100644
--- a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost/CreateAppHost.cs
+++ b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost/CreateAppHost.cs
@@ -5,6 +5,8 @@
using System.Diagnostics;
using System.IO;
using System.Linq;
+using System.Reflection.Metadata;
+using System.Reflection.Metadata.Ecma335;
using System.Reflection.PortableExecutable;
using System.Runtime.CompilerServices;
using System.Text;
@@ -110,9 +112,9 @@ public void GUISubsystem_WindowsPEFile()
windowsGraphicalUserInterface: true);
BitConverter
- .ToUInt16(File.ReadAllBytes(destinationFilePath), SubsystemOffset)
- .Should()
- .Be((ushort)Subsystem.WindowsGui);
+ .ToUInt16(File.ReadAllBytes(destinationFilePath), SubsystemOffset)
+ .Should()
+ .Be((ushort)Subsystem.WindowsGui);
Assert.Equal((ushort)Subsystem.WindowsGui, PEUtils.GetWindowsGraphicalUserInterfaceBit(destinationFilePath));
}
@@ -304,6 +306,83 @@ public void CodeSigningFailuresThrow()
}
}
+ [Theory]
+ [InlineData(true)] // Bit is set in extended DLL characteristics
+ [InlineData(false)] // Bit is not set in extended DLL characteristics
+ [InlineData(null)] // No extended DLL characteristics
+ public void CetCompat(bool? cetCompatSet)
+ {
+ using (TestArtifact artifact = CreateTestDirectory())
+ {
+ // Create a PE image with with CET compatability enabled/disabled
+ BlobBuilder peBlob = Binaries.CetCompat.CreatePEImage(cetCompatSet);
+
+ // Add the placeholder - it just needs to exist somewhere in the image, as HostWriter.CreateAppHost requires it
+ peBlob.WriteBytes(AppBinaryPathPlaceholderSearchValue);
+
+ string source = Path.Combine(artifact.Location, "source.exe");
+ using (FileStream stream = new FileStream(source, FileMode.Create))
+ {
+ peBlob.WriteContentTo(stream);
+ }
+
+ bool originallyEnabled = cetCompatSet.HasValue ? cetCompatSet.Value : false;
+ Assert.Equal(originallyEnabled, Binaries.CetCompat.IsMarkedCompatible(source));
+
+ // Validate compatibility is disabled
+ string cetDisabled = Path.Combine(artifact.Location, "cetDisabled.exe");
+ HostWriter.CreateAppHost(
+ source,
+ cetDisabled,
+ "app",
+ disableCetCompat: true);
+ Assert.False(Binaries.CetCompat.IsMarkedCompatible(cetDisabled));
+
+ // Validate compatibility is not changed
+ string cetEnabled = Path.Combine(artifact.Location, "cetUnchanged.exe");
+ HostWriter.CreateAppHost(
+ source,
+ cetEnabled,
+ "app",
+ disableCetCompat: false);
+ Assert.Equal(originallyEnabled, Binaries.CetCompat.IsMarkedCompatible(cetEnabled));
+ }
+ }
+
+ [ConditionalFact(typeof(Binaries.CetCompat), nameof(Binaries.CetCompat.IsSupported))]
+ public void CetCompat_ProductHosts()
+ {
+ using (TestArtifact artifact = CreateTestDirectory())
+ {
+ string[] hosts = [Binaries.AppHost.FilePath, Binaries.SingleFileHost.FilePath];
+ foreach (string host in hosts)
+ {
+ // Hosts should be compatible with CET shadow stack by default
+ Assert.True(Binaries.CetCompat.IsMarkedCompatible(host));
+ string source = Path.Combine(artifact.Location, Path.GetFileName(host));
+ File.Copy(host, source);
+
+ // Validate compatibility is disabled
+ string cetDisabled = Path.Combine(artifact.Location, $"{Path.GetFileName(host)}_cetDisabled.exe");
+ HostWriter.CreateAppHost(
+ source,
+ cetDisabled,
+ "app",
+ disableCetCompat: true);
+ Assert.False(Binaries.CetCompat.IsMarkedCompatible(cetDisabled));
+
+ // Validate compatibility is not changed (remains enabled)
+ string cetEnabled = Path.Combine(artifact.Location, $"{Path.GetFileName(host)}_cetEnabled.exe");
+ HostWriter.CreateAppHost(
+ source,
+ cetEnabled,
+ "app",
+ disableCetCompat: false);
+ Assert.True(Binaries.CetCompat.IsMarkedCompatible(cetEnabled));
+ }
+ }
+ }
+
[Fact]
private void ResourceWithUnknownLanguage()
{
diff --git a/src/installer/tests/TestUtils/Binaries.cs b/src/installer/tests/TestUtils/Binaries.cs
index de2538c939c29c..d722c5aafcdc11 100644
--- a/src/installer/tests/TestUtils/Binaries.cs
+++ b/src/installer/tests/TestUtils/Binaries.cs
@@ -6,6 +6,8 @@
using System.IO;
using System.Linq;
using System.Reflection.Metadata;
+using System.Reflection.Metadata.Ecma335;
+using System.Reflection.PortableExecutable;
using System.Runtime.InteropServices;
using Microsoft.DotNet.Cli.Build;
@@ -108,5 +110,73 @@ static bool IsAssembly(string filePath)
}
}
}
+
+ public static class CetCompat
+ {
+ // We only support CET shadow stack compatibility for Windows x64 currently
+ public static bool IsSupported => OperatingSystem.IsWindows() && TestContext.BuildArchitecture == "x64";
+
+ // https://learn.microsoft.com/windows/win32/debug/pe-format#debug-type
+ private const int IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS = 20;
+
+ // https://learn.microsoft.com/windows/win32/debug/pe-format#extended-dll-characteristics
+ private const ushort IMAGE_DLLCHARACTERISTICS_EX_CET_COMPAT = 0x1;
+
+ ///
+ /// Determine if a PE image is marked with CET shadow stack compatibility
+ ///
+ /// Path to the image
+ /// True if image is marked compatible, false otherwise
+ public static bool IsMarkedCompatible(string filePath)
+ {
+ using (PEReader reader = new PEReader(new FileStream(filePath, FileMode.Open, FileAccess.Read)))
+ {
+ foreach (DebugDirectoryEntry entry in reader.ReadDebugDirectory())
+ {
+ if ((int)entry.Type != IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS)
+ continue;
+
+ // Get the extended DLL characteristics debug directory entry
+ PEMemoryBlock data = reader.GetSectionData(entry.DataRelativeVirtualAddress);
+ ushort dllCharacteristics = data.GetReader().ReadUInt16();
+
+ // Check for the CET compat bit
+ return (dllCharacteristics & IMAGE_DLLCHARACTERISTICS_EX_CET_COMPAT) != 0;
+ }
+
+ // Not marked compatible - no debug directory entry for extended DLL characteristics
+ return false;
+ }
+ }
+
+ ///
+ /// Create a PE image with with CET compatability enabled/disabled
+ ///
+ /// True to set CET compat bit, false to not set, null to omit extended DLL characteristics
+ /// PE image blob
+ public static BlobBuilder CreatePEImage(bool? setCetCompatBit)
+ {
+ // Create a PE image with with CET compatability enabled/disabled
+ DebugDirectoryBuilder debugDirectoryBuilder = new DebugDirectoryBuilder();
+ if (setCetCompatBit.HasValue)
+ {
+ debugDirectoryBuilder.AddEntry(
+ (DebugDirectoryEntryType)IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS,
+ version: 0,
+ stamp: 0,
+ setCetCompatBit.Value ? IMAGE_DLLCHARACTERISTICS_EX_CET_COMPAT : (ushort)0,
+ (BlobBuilder b, ushort data) => b.WriteUInt16(data));
+ }
+ ManagedPEBuilder peBuilder = new ManagedPEBuilder(
+ PEHeaderBuilder.CreateExecutableHeader(),
+ new MetadataRootBuilder(new MetadataBuilder()),
+ ilStream: new BlobBuilder(),
+ debugDirectoryBuilder: debugDirectoryBuilder);
+
+ BlobBuilder peBlob = new BlobBuilder();
+ peBuilder.Serialize(peBlob);
+ return peBlob;
+ }
+ }
}
}
diff --git a/src/installer/tests/TestUtils/SingleFileTestApp.cs b/src/installer/tests/TestUtils/SingleFileTestApp.cs
index bc65e2f6a89fed..756db09b6d942b 100644
--- a/src/installer/tests/TestUtils/SingleFileTestApp.cs
+++ b/src/installer/tests/TestUtils/SingleFileTestApp.cs
@@ -29,6 +29,14 @@ public SingleFileTestApp(string appName, bool selfContained, string location)
PopulateBuiltAppDirectory();
}
+ private SingleFileTestApp(SingleFileTestApp source)
+ : base(source)
+ {
+ AppName = source.AppName;
+ selfContained = source.selfContained;
+ builtApp = new TestApp(Path.Combine(Location, "builtApp"), AppName);
+ }
+
///
/// Create a framework-dependent single-file test app from pre-built output of .
///
@@ -73,6 +81,8 @@ public static IReadOnlyList GetRuntimeFilesToBundle()
return fileSpecs;
}
+ public SingleFileTestApp Copy() => new SingleFileTestApp(this);
+
public string Bundle(BundleOptions options = BundleOptions.None, Version? bundleVersion = null)
{
return Bundle(options, out _, bundleVersion);
@@ -132,6 +142,18 @@ public DirectoryInfo GetExtractionDir(string root, Manifest manifest)
return new DirectoryInfo(Path.Combine(root, Name, manifest.BundleID));
}
+ public void CreateAppHost(bool isWindowsGui = false, bool copyResources = true, bool disableCetCompat = false)
+ {
+ if (selfContained)
+ {
+ builtApp.CreateSingleFileHost(isWindowsGui, copyResources, disableCetCompat);
+ }
+ else
+ {
+ builtApp.CreateAppHost(isWindowsGui, copyResources, disableCetCompat);
+ }
+ }
+
private void PopulateBuiltAppDirectory()
{
// Copy the compiled app output - the app is expected to have been built as framework-dependent
@@ -182,14 +204,7 @@ private void PopulateBuiltAppDirectory()
builder.Build(builtApp);
// Create the apphost for the app
- if (selfContained)
- {
- builtApp.CreateSingleFileHost();
- }
- else
- {
- builtApp.CreateAppHost();
- }
+ CreateAppHost();
}
}
}
diff --git a/src/installer/tests/TestUtils/TestApp.cs b/src/installer/tests/TestUtils/TestApp.cs
index cca9ef4c3740f7..bf52801c7d27bd 100644
--- a/src/installer/tests/TestUtils/TestApp.cs
+++ b/src/installer/tests/TestUtils/TestApp.cs
@@ -77,13 +77,13 @@ public void PopulateFrameworkDependent(string fxName, string fxVersion, Action CreateAppHost(Binaries.AppHost.FilePath, isWindowsGui, copyResources);
+ public void CreateAppHost(bool isWindowsGui = false, bool copyResources = true, bool disableCetCompat = false)
+ => CreateAppHost(Binaries.AppHost.FilePath, isWindowsGui, copyResources, disableCetCompat);
- public void CreateSingleFileHost(bool isWindowsGui = false, bool copyResources = true)
- => CreateAppHost(Binaries.SingleFileHost.FilePath, isWindowsGui, copyResources);
+ public void CreateSingleFileHost(bool isWindowsGui = false, bool copyResources = true, bool disableCetCompat = false)
+ => CreateAppHost(Binaries.SingleFileHost.FilePath, isWindowsGui, copyResources, disableCetCompat);
- private void CreateAppHost(string hostSourcePath, bool isWindowsGui = false, bool copyResources = true)
+ private void CreateAppHost(string hostSourcePath, bool isWindowsGui, bool copyResources, bool disableCetCompat)
{
// Use the live-built apphost and HostModel to create the apphost to run
HostWriter.CreateAppHost(
@@ -91,7 +91,8 @@ private void CreateAppHost(string hostSourcePath, bool isWindowsGui = false, boo
AppExe,
Path.GetFileName(AppDll),
windowsGraphicalUserInterface: isWindowsGui,
- assemblyToCopyResourcesFrom: copyResources ? AppDll : null);
+ assemblyToCopyResourcesFrom: copyResources ? AppDll : null,
+ disableCetCompat: disableCetCompat);
}
public enum MockedComponent
diff --git a/src/native/corehost/apphost/standalone/CMakeLists.txt b/src/native/corehost/apphost/standalone/CMakeLists.txt
index 2630aed0deca3d..ddddffd5ce93e6 100644
--- a/src/native/corehost/apphost/standalone/CMakeLists.txt
+++ b/src/native/corehost/apphost/standalone/CMakeLists.txt
@@ -51,9 +51,14 @@ endif()
install_with_stripped_symbols(apphost TARGETS corehost)
-# Disable manifest generation into the file .exe on Windows
if(CLR_CMAKE_TARGET_WIN32)
+ # Disable manifest generation into the file .exe on Windows
target_link_options(apphost PRIVATE "/MANIFEST:NO")
+
+ # Enable CET-compatibility
+ if (CLR_CMAKE_HOST_ARCH_AMD64)
+ target_link_options(apphost PRIVATE "/CETCOMPAT")
+ endif()
endif()
if (CLR_CMAKE_TARGET_WIN32)
diff --git a/src/native/corehost/apphost/static/CMakeLists.txt b/src/native/corehost/apphost/static/CMakeLists.txt
index e431b87e33d0c6..b3d6766c03dca7 100644
--- a/src/native/corehost/apphost/static/CMakeLists.txt
+++ b/src/native/corehost/apphost/static/CMakeLists.txt
@@ -112,6 +112,11 @@ if(CLR_CMAKE_TARGET_WIN32)
# Disable manifest generation into the file .exe on Windows
add_linker_flag("/MANIFEST:NO")
+ # Enable CET-compatibility
+ if (CLR_CMAKE_HOST_ARCH_AMD64)
+ add_linker_flag("/CETCOMPAT")
+ endif()
+
# Incremental linking results in the linker inserting extra padding and routing function calls via thunks that can break the
# invariants (e.g. size of region between Jit_PatchedCodeLast-Jit_PatchCodeStart needs to fit in a page).
add_linker_flag("/INCREMENTAL:NO")