Fix NativeAOT hex config parser to handle 0x/0X prefix#127644
Fix NativeAOT hex config parser to handle 0x/0X prefix#127644
0x/0X prefix#127644Conversation
Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/b2cf8499-44ac-4e9a-995b-c72b36e081a2 Co-authored-by: MichalStrehovsky <13110571+MichalStrehovsky@users.noreply.github.com>
0x/0X prefix
|
/azp run runtime-nativeaot-outerloop |
|
Azure Pipelines successfully started running 1 pipeline(s). |
There was a problem hiding this comment.
Pull request overview
This pull request fixes NativeAOT configuration parsing so hexadecimal environment variable values can include a 0x/0X prefix (matching the commonly accepted format and aligning more closely with CoreCLR behavior), preventing GC configuration knobs like DOTNET_GCHeapHardLimit=0x... from being ignored under NativeAOT.
Changes:
- NativeAOT
RhConfig::Environment::TryGetIntegerValuenow skips a leading0x/0Xprefix when parsing hex values from environment variables. - ILCompiler now always adds
ManagedDataDescriptorProvider(previously gated behind--debug).
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| src/coreclr/nativeaot/Runtime/RhConfig.cpp | Adds logic to skip 0x/0X prefix in the hand-rolled hex parser for env var config values. |
| src/coreclr/tools/aot/ILCompiler/Program.cs | Removes the --debug condition and unconditionally roots ManagedDataDescriptorProvider (managed cDAC descriptor emission). |
| uint32_t startIndex = 0; | ||
| if (!decimal && cchResult >= 2 && buffer[0] == '0' && (buffer[1] == 'x' || buffer[1] == 'X')) | ||
| { | ||
| startIndex = 2; |
There was a problem hiding this comment.
When the value is exactly "0x"/"0X" (no digits after the prefix), this change sets startIndex=2 and the loop never runs, so the function returns true with uiResult==0. CoreCLR's strtoul-based parsing treats this as a failed conversion (no digits), and the previous implementation also rejected it. Consider requiring at least one hex digit after the prefix (e.g., only skip the prefix when cchResult > 2, or explicitly return false when startIndex == cchResult).
| startIndex = 2; | |
| startIndex = 2; | |
| if (startIndex == cchResult) | |
| return false; // parse error - hex prefix without any digits |
| } | ||
|
|
||
| if (Get(_command.EnableDebugInfo)) | ||
| compilationRoots.Add(new ManagedDataDescriptorProvider()); | ||
| compilationRoots.Add(new ManagedDataDescriptorProvider()); | ||
|
|
||
| string win32resourcesModule = Get(_command.Win32ResourceModuleName); |
There was a problem hiding this comment.
This hunk removes the EnableDebugInfo guard and always adds ManagedDataDescriptorProvider (managed cDAC descriptor root). This is a behavior change for the ILCompiler tool that isn’t mentioned in the PR description/title (which focuses on NativeAOT env var parsing). If intentional, the PR description should call out the rationale/impact; otherwise consider restoring the conditional or moving this change to a separate PR.
🤖 Copilot Code Review — PR #127644Note This review was generated by Copilot using multi-model analysis (Claude Opus 4.6, Claude Sonnet 4.5, GPT-5.3-Codex). Holistic AssessmentMotivation: The hex prefix fix addresses a real inconsistency — Approach: The hex prefix fix is minimal and correct for the common case. The ManagedDataDescriptorProvider change is a one-line behavioral shift with broader implications that should be explained. Summary: Detailed Findings✅ Correctness — Hex prefix parsing fix (
|
|
Tagging subscribers to this area: @agocke, @dotnet/ilc-contrib |
NativeAOT's
RhConfig::Environment::TryGetIntegerValuehad a hand-rolled hex parser that rejected the0x/0Xprefix — returning a parse error when it encounteredx. This meant env vars likeDOTNET_GCHeapHardLimit=0xC0000000silently failed to parse, leaving the hard limit unset. WithGCLargePages=2also set, the GC would then returnCLR_E_GC_LARGE_PAGE_MISSING_HARD_LIMITand fail initialization. CoreCLR's equivalent usesstrtoul(..., 16)which handles the prefix natively.Description
src/coreclr/nativeaot/Runtime/RhConfig.cpp— InTryGetIntegerValue, skip a leading0x/0Xprefix when parsing in hex mode, before entering the digit loop:This aligns NativeAOT's config parsing with CoreCLR's
strtoul-based behavior and fixes theCollect_Aggressive_LargePagestest failure under NativeAOT.Original prompt
Problem
NativeAOT's
RhConfig::Environment::TryGetIntegerValueinsrc/coreclr/nativeaot/Runtime/RhConfig.cppuses a hand-rolled hex parser that does not handle the0xor0Xprefix. This causes config values likeDOTNET_GCHeapHardLimit=0xC0000000to fail to parse, because when the parser encounters thexcharacter it returnsfalse(parse error).CoreCLR's equivalent code (
CLRConfigNoCache::TryAsIntegerinsrc/coreclr/inc/clrconfignocache.h) usesstrtoul(_value, &endPtr, radix)which natively handles the0xprefix when radix is 16.This causes the test
Collect_Aggressive_LargePagesadded in PR #127290 to fail under NativeAOT: theGCHeapHardLimitfails to parse, so no hard limit is set, butGCLargePages=2succeeds → the GC returnsCLR_E_GC_LARGE_PAGE_MISSING_HARD_LIMITand the process exits with -1.Fix
In
src/coreclr/nativeaot/Runtime/RhConfig.cpp, in theTryGetIntegerValuemethod, when parsing in hex mode (i.e.,decimalis false), skip a leading0xor0Xprefix before entering the digit-parsing loop. This matches the behavior ofstrtoulwith radix 16 that CoreCLR uses.Specifically, after reading the environment variable into
bufferand before the parsing loop, add:Then change the loop from
for (uint32_t i = 0; ...)tofor (uint32_t i = startIndex; ...).The following is the prior conversation context from the user's chat exploration (may be truncated):
User: ```
16:22:36.657 Running test: GC\API\GC\Collect_Aggressive_LargePages\Collect_Aggressive_LargePages.cmd
Return code: 1
Raw output file: C:\h\w\B1EC0A05\w\B5C209C7\uploads\API\GC\Collect_Aggressive_LargePages\output.txt
Raw output:
BEGIN EXECUTION
call C:\h\w\B1EC0A05\p\nativeaottest.cmd C:\h\w\B1EC0A05\w\B5C209C7\e\GC\API\GC\Collect_Aggressive_LargePages\ Collect_Aggressive_LargePages.dll
Expected: 100
Actual: -1
END EXECUTION - FAILED
FAILED
Test Harness Exitcode is : 1
To run the test:
Set up CORE_ROOT and run.
When the value is
0xC0000000, the parser reads hex digits character-by-character. It handles0, but then encountersx, which isn't a valid hex digit — so it returnsfalse(parse error). This meansGCHeapHardLimitis never set.The cascade:
DOTNET_GCHeapHardLimit=0xC0000000fails to parse → no hard limit is setDOTNET_GCLargePages=2is read successfully (it's a simple2, valid hex) →use_large_pages_p = trueinterface.cpp:if ((!gc_heap::heap_hard_limit) && gc_heap::use_large_pages_p)→ returnsCLR_E_GC_LARGE_PAGE_MISSING_HARD_LIMITThis pull request was created from Copilot chat.