Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ public static class CorDbgHResults
public const int CORDBG_E_READVIRTUAL_FAILURE = unchecked((int)0x80131c49);
public const int ERROR_BUFFER_OVERFLOW = unchecked((int)0x8007006F); // HRESULT_FROM_WIN32(ERROR_BUFFER_OVERFLOW)
public const int CORDBG_E_CLASS_NOT_LOADED = unchecked((int)0x80131303);
public const int CORDBG_S_NOT_ALL_BITS_SET = unchecked((int)0x00131c13);
}
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,48 @@ public int GetCompilerFlags(ulong vmAssembly, Interop.BOOL* pfAllowJITOpts, Inte
}

public int SetCompilerFlags(ulong vmAssembly, Interop.BOOL fAllowJitOpts, Interop.BOOL fEnableEnC)
=> LegacyFallbackHelper.CanFallback() && _legacy is not null ? _legacy.SetCompilerFlags(vmAssembly, fAllowJitOpts, fEnableEnC) : HResults.E_NOTIMPL;
{
int hr = HResults.S_OK;
try
{
Contracts.ILoader loader = _target.Contracts.Loader;
Contracts.ModuleHandle handle = loader.GetModuleHandleFromAssemblyPtr(new TargetPointer(vmAssembly));

Contracts.DebuggerAssemblyControlFlags controlFlags = loader.GetDebuggerInfoBits(handle);
controlFlags &= ~(Contracts.DebuggerAssemblyControlFlags.DACF_ALLOW_JIT_OPTS | Contracts.DebuggerAssemblyControlFlags.DACF_ENC_ENABLED);
controlFlags &= Contracts.DebuggerAssemblyControlFlags.DACF_CONTROL_FLAGS_MASK;

if (fAllowJitOpts == Interop.BOOL.TRUE)
{
controlFlags |= Contracts.DebuggerAssemblyControlFlags.DACF_ALLOW_JIT_OPTS;
}

if (fEnableEnC == Interop.BOOL.TRUE)
{
controlFlags |= Contracts.DebuggerAssemblyControlFlags.DACF_ENC_ENABLED;
}
Comment thread
rcj1 marked this conversation as resolved.

loader.SetDebuggerInfoBits(handle, controlFlags);

// Check if EnC was requested but the module was not capable.
Copy link

Copilot AI Apr 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment says this is checking whether the module is “not capable”, but the condition is checking whether ModuleFlags.EditAndContinue ended up enabled after SetDebuggerInfoBits. That bit can remain unset for reasons other than capability (e.g., global EEConfig policy). Consider rewording the comment to reflect what’s actually being validated (whether EnC was successfully enabled).

Suggested change
// Check if EnC was requested but the module was not capable.
// Check if EnC was requested but was not successfully enabled.

Copilot uses AI. Check for mistakes.
if (fEnableEnC == Interop.BOOL.TRUE && (loader.GetFlags(handle) & Contracts.ModuleFlags.EditAndContinue) == 0)
{
hr = CorDbgHResults.CORDBG_S_NOT_ALL_BITS_SET;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: prefer Marshal.GetExceptionForHR

}
Comment thread
rcj1 marked this conversation as resolved.
}
catch (System.Exception ex)
{
hr = ex.HResult;
}
#if DEBUG
if (_legacy is not null)
{
int hrLocal = _legacy.SetCompilerFlags(vmAssembly, fAllowJitOpts, fEnableEnC);
Debug.ValidateHResult(hr, hrLocal);
}
#endif
return hr;
}

public int EnumerateAssembliesInAppDomain(ulong vmAppDomain, nint fpCallback, nint pUserData)
=> LegacyFallbackHelper.CanFallback() && _legacy is not null ? _legacy.EnumerateAssembliesInAppDomain(vmAppDomain, fpCallback, pUserData) : HResults.E_NOTIMPL;
Expand Down
120 changes: 119 additions & 1 deletion src/native/managed/cdac/tests/LoaderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ private static ILoader CreateLoaderContract(MockTarget.Architecture arch, Action
return target.Contracts.Loader;
}

private static (ILoader Contract, TestPlaceholderTarget Target) CreateLoaderContractWithTarget(
internal static (ILoader Contract, TestPlaceholderTarget Target) CreateLoaderContractWithTarget(
MockTarget.Architecture arch,
Action<MockLoaderBuilder, TestPlaceholderTarget.Builder> configure)
{
Expand Down Expand Up @@ -845,3 +845,121 @@ public void GetCompilerFlags(uint rawFlags, Interop.BOOL expectedAllowJITOpts, I
Assert.Equal(expectedEnableEnC, enableEnC);
}
}

public unsafe class SetCompilerFlagsTests
{
private const uint IsEditAndContinue = 0x00000008;
private const uint IsEncCapable = 0x00000200;
private const uint DebuggerAllowJitOptsPriv = 0x00000800;

private static (DacDbiImpl DacDbi, TestPlaceholderTarget Target) CreateDacDbiWithLoader(
MockTarget.Architecture arch,
Action<MockLoaderBuilder, TestPlaceholderTarget.Builder> configure)
{
var (_, target) = LoaderTests.CreateLoaderContractWithTarget(arch, configure);
var dacDbi = new DacDbiImpl(target, legacyObj: null);
return (dacDbi, target);
}

[Theory]
[ClassData(typeof(MockTarget.StdArch))]
public void SetCompilerFlags_BothFlagsSet_EncCapable(MockTarget.Architecture arch)
{
ulong assemblyAddr = 0;
TargetPointer moduleAddr = TargetPointer.Null;
int flagsOffset = 0;

var (dacDbi, target) = CreateDacDbiWithLoader(arch, (loader, builder) =>
{
var config = loader.AddEEConfig((uint)ClrModifiableAssemblies.Debug);
builder.AddGlobals((Constants.Globals.EEConfig, config.Address));
var module = loader.AddModule(flags: IsEncCapable);
assemblyAddr = module.Assembly;
moduleAddr = new TargetPointer(module.Address);
flagsOffset = loader.ModuleLayout.GetField(nameof(Data.Module.Flags)).Offset;
});

int hr = dacDbi.SetCompilerFlags(assemblyAddr, Interop.BOOL.TRUE, Interop.BOOL.TRUE);

Assert.Equal(System.HResults.S_OK, hr);
uint rawFlags = target.Read<uint>(moduleAddr + (ulong)flagsOffset);
Assert.NotEqual(0u, rawFlags & DebuggerAllowJitOptsPriv);
Assert.NotEqual(0u, rawFlags & IsEditAndContinue);
}

[Theory]
[ClassData(typeof(MockTarget.StdArch))]
public void SetCompilerFlags_BothFlagsUnset(MockTarget.Architecture arch)
{
ulong assemblyAddr = 0;
TargetPointer moduleAddr = TargetPointer.Null;
int flagsOffset = 0;

var (dacDbi, target) = CreateDacDbiWithLoader(arch, (loader, builder) =>
{
var config = loader.AddEEConfig((uint)ClrModifiableAssemblies.None);
builder.AddGlobals((Constants.Globals.EEConfig, config.Address));
var module = loader.AddModule();
assemblyAddr = module.Assembly;
moduleAddr = new TargetPointer(module.Address);
flagsOffset = loader.ModuleLayout.GetField(nameof(Data.Module.Flags)).Offset;
});

int hr = dacDbi.SetCompilerFlags(assemblyAddr, Interop.BOOL.FALSE, Interop.BOOL.FALSE);

Assert.Equal(System.HResults.S_OK, hr);
uint rawFlags = target.Read<uint>(moduleAddr + (ulong)flagsOffset);
Assert.Equal(0u, rawFlags & DebuggerAllowJitOptsPriv);
Assert.Equal(0u, rawFlags & IsEditAndContinue);
}

[Theory]
[ClassData(typeof(MockTarget.StdArch))]
public void SetCompilerFlags_EnCRequested_NotCapable(MockTarget.Architecture arch)
{
ulong assemblyAddr = 0;

var (dacDbi, _) = CreateDacDbiWithLoader(arch, (loader, builder) =>
{
var config = loader.AddEEConfig((uint)ClrModifiableAssemblies.None);
builder.AddGlobals((Constants.Globals.EEConfig, config.Address));
var module = loader.AddModule();
assemblyAddr = module.Assembly;
});

int hr = dacDbi.SetCompilerFlags(assemblyAddr, Interop.BOOL.TRUE, Interop.BOOL.TRUE);

Assert.Equal(CorDbgHResults.CORDBG_S_NOT_ALL_BITS_SET, hr);
}

[Theory]
[ClassData(typeof(MockTarget.StdArch))]
public void SetCompilerFlags_JitOptsToggling(MockTarget.Architecture arch)
{
ulong assemblyAddr = 0;
TargetPointer moduleAddr = TargetPointer.Null;
int flagsOffset = 0;

var (dacDbi, target) = CreateDacDbiWithLoader(arch, (loader, builder) =>
{
var config = loader.AddEEConfig((uint)ClrModifiableAssemblies.None);
builder.AddGlobals((Constants.Globals.EEConfig, config.Address));
var module = loader.AddModule();
assemblyAddr = module.Assembly;
moduleAddr = new TargetPointer(module.Address);
flagsOffset = loader.ModuleLayout.GetField(nameof(Data.Module.Flags)).Offset;
});

// Enable JIT opts
int hr = dacDbi.SetCompilerFlags(assemblyAddr, Interop.BOOL.TRUE, Interop.BOOL.FALSE);
Assert.Equal(System.HResults.S_OK, hr);
uint rawFlags = target.Read<uint>(moduleAddr + (ulong)flagsOffset);
Assert.NotEqual(0u, rawFlags & DebuggerAllowJitOptsPriv);

// Disable JIT opts
hr = dacDbi.SetCompilerFlags(assemblyAddr, Interop.BOOL.FALSE, Interop.BOOL.FALSE);
Assert.Equal(System.HResults.S_OK, hr);
rawFlags = target.Read<uint>(moduleAddr + (ulong)flagsOffset);
Assert.Equal(0u, rawFlags & DebuggerAllowJitOptsPriv);
}
}
Loading