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")