From 11dca8844ba990964d60b07a550235e51a074b59 Mon Sep 17 00:00:00 2001 From: Milos Kotlar Date: Tue, 30 Jun 2026 11:12:23 +0200 Subject: [PATCH 01/10] [ios-clr] Throw descriptive error when a stripped IL body is needed at runtime Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/coreclr/dlls/mscorrc/mscorrc.rc | 1 + src/coreclr/dlls/mscorrc/resource.h | 1 + src/coreclr/vm/prestub.cpp | 10 ++++++++++ 3 files changed, 12 insertions(+) diff --git a/src/coreclr/dlls/mscorrc/mscorrc.rc b/src/coreclr/dlls/mscorrc/mscorrc.rc index 4db50443879a4d..ff1234962455a6 100644 --- a/src/coreclr/dlls/mscorrc/mscorrc.rc +++ b/src/coreclr/dlls/mscorrc/mscorrc.rc @@ -606,6 +606,7 @@ BEGIN BFA_INVALID_TOKEN_IN_MANIFESTRES "Invalid token saved in ManifestResource." BFA_EMPTY_ASSEMDEF_NAME "Empty assembly simple name." BFA_BAD_IL "Bad IL format." + BFA_STRIPPED_IL_BODY "A method body required at runtime was stripped from the ReadyToRun image and this runtime has no JIT to compile it." BFA_CLASSLOAD_VALUETYPEMISMATCH "Could not load type '%1' from assembly '%2' due to value type mismatch." BFA_METHODDECL_NOT_A_METHODDEF "Method declaration is not a methoddef." BFA_DUPLICATE_DELEGATE_METHOD "Duplicate definition for runtime implemented delegate method." diff --git a/src/coreclr/dlls/mscorrc/resource.h b/src/coreclr/dlls/mscorrc/resource.h index de761d74bd6315..aebab58f5d57c3 100644 --- a/src/coreclr/dlls/mscorrc/resource.h +++ b/src/coreclr/dlls/mscorrc/resource.h @@ -462,6 +462,7 @@ #define IDS_EE_INTEROP_STUB_CA_NO_ACCESS_TO_STUB_METHOD 0x2111 #endif +#define BFA_STRIPPED_IL_BODY 0x2112 #define BFA_REFERENCE_ASSEMBLY 0x2113 #define IDS_E_FIELDACCESS 0x2114 diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp index 6f34ba1de6e409..1c1e0960b881d0 100644 --- a/src/coreclr/vm/prestub.cpp +++ b/src/coreclr/vm/prestub.cpp @@ -703,6 +703,16 @@ namespace if (status == COR_ILMETHOD_DECODER::FORMAT_ERROR) COMPlusThrowHR(COR_E_BADIMAGEFORMAT, BFA_BAD_IL); + Module* pModule = pMD->GetModule(); + if (pModule->IsReadyToRun() + && pModule->GetReadyToRunInfo()->HasStrippedILBodies() + && pHeader->GetCodeSize() == 2 + && pHeader->Code[0] == 0x14 // ldnull + && pHeader->Code[1] == 0x7A) // throw + { + COMPlusThrowHR(COR_E_BADIMAGEFORMAT, BFA_STRIPPED_IL_BODY); + } + return pHeader; } From 1a4ff31067950f2b2204ee63a33bcce7c8fb1934 Mon Sep 17 00:00:00 2001 From: Milos Kotlar Date: Tue, 30 Jun 2026 11:35:45 +0200 Subject: [PATCH 02/10] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- src/coreclr/vm/prestub.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp index 1c1e0960b881d0..8e296e2bb59ca5 100644 --- a/src/coreclr/vm/prestub.cpp +++ b/src/coreclr/vm/prestub.cpp @@ -707,9 +707,8 @@ namespace if (pModule->IsReadyToRun() && pModule->GetReadyToRunInfo()->HasStrippedILBodies() && pHeader->GetCodeSize() == 2 - && pHeader->Code[0] == 0x14 // ldnull - && pHeader->Code[1] == 0x7A) // throw - { + && pHeader->Code[0] == CEE_LDNULL + && pHeader->Code[1] == CEE_THROW) COMPlusThrowHR(COR_E_BADIMAGEFORMAT, BFA_STRIPPED_IL_BODY); } From 2a9abf8d5a365f388842c6a8083e72dc91385b28 Mon Sep 17 00:00:00 2001 From: Milos Kotlar Date: Tue, 30 Jun 2026 12:17:27 +0200 Subject: [PATCH 03/10] Fix build: include openum.h for CEE_* opcodes and restore braces Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/coreclr/vm/prestub.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp index 8e296e2bb59ca5..fffb5db71b52dc 100644 --- a/src/coreclr/vm/prestub.cpp +++ b/src/coreclr/vm/prestub.cpp @@ -18,6 +18,7 @@ #include "comdelegate.h" #include "dbginterface.h" #include "stubgen.h" +#include "openum.h" #include "eventtrace.h" #include "array.h" #include "ecall.h" @@ -709,6 +710,7 @@ namespace && pHeader->GetCodeSize() == 2 && pHeader->Code[0] == CEE_LDNULL && pHeader->Code[1] == CEE_THROW) + { COMPlusThrowHR(COR_E_BADIMAGEFORMAT, BFA_STRIPPED_IL_BODY); } From 616b7b64ec68848920278c18bd028fa1abf6c7c7 Mon Sep 17 00:00:00 2001 From: Milos Kotlar Date: Tue, 30 Jun 2026 14:44:40 +0200 Subject: [PATCH 04/10] Use invalid IL sentinel for stripped method bodies Emit an intentionally invalid IL sentinel (illegal two-byte opcode 0xFE 0x24) for stripped method bodies instead of 'ldnull; throw', and match that exact sentinel in the runtime. The previous stub collided with a real 'throw null;' method body, which could trigger a false BadImageFormatException if such a method ever needed to be JITted. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../ReadyToRun/CopiedMethodILNode.cs | 10 +++++++--- src/coreclr/vm/prestub.cpp | 8 +++++--- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/CopiedMethodILNode.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/CopiedMethodILNode.cs index ab0d5c17ca6952..a089cd2be6b923 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/CopiedMethodILNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/CopiedMethodILNode.cs @@ -14,9 +14,13 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun { public class CopiedMethodILNode : ObjectNode, ISymbolDefinitionNode { - // Throws NullReferenceException if the stripped body is encountered. - // Tiny header (0x0A: 2 bytes code size) + ldnull (0x14) + throw (0x7A). - private static readonly byte[] s_minimalILBody = [0x0A, 0x14, 0x7A]; + // Sentinel body emitted for stripped IL methods. It is intentionally invalid IL + // so it can never collide with a real method body (e.g. a user 'throw null;' method, + // whose body is 'ldnull; throw'). The runtime detects this exact sentinel before the + // IL reaches the JIT and throws a descriptive BadImageFormatException + // (see GetAndVerifyMetadataILHeader / BFA_STRIPPED_IL_BODY in prestub.cpp). + // Tiny header (0x0A: 2 bytes code size) + the illegal two-byte opcode 0xFE 0x24 (CEE_UNUSED49). + private static readonly byte[] s_minimalILBody = [0x0A, 0xFE, 0x24]; EcmaMethod _method; diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp index fffb5db71b52dc..f4805ebb715c70 100644 --- a/src/coreclr/vm/prestub.cpp +++ b/src/coreclr/vm/prestub.cpp @@ -18,7 +18,6 @@ #include "comdelegate.h" #include "dbginterface.h" #include "stubgen.h" -#include "openum.h" #include "eventtrace.h" #include "array.h" #include "ecall.h" @@ -704,12 +703,15 @@ namespace if (status == COR_ILMETHOD_DECODER::FORMAT_ERROR) COMPlusThrowHR(COR_E_BADIMAGEFORMAT, BFA_BAD_IL); + // ILCompiler replaces stripped method bodies with an intentionally invalid IL sentinel + // (the illegal two-byte opcode 0xFE 0x24) so it can never collide with a real method body; + // see CopiedMethodILNode.s_minimalILBody. Detect it before the IL reaches the JIT. Module* pModule = pMD->GetModule(); if (pModule->IsReadyToRun() && pModule->GetReadyToRunInfo()->HasStrippedILBodies() && pHeader->GetCodeSize() == 2 - && pHeader->Code[0] == CEE_LDNULL - && pHeader->Code[1] == CEE_THROW) + && pHeader->Code[0] == 0xFE + && pHeader->Code[1] == 0x24) { COMPlusThrowHR(COR_E_BADIMAGEFORMAT, BFA_STRIPPED_IL_BODY); } From 91084e566e46740abea1e1aceff5b0942acb4f3c Mon Sep 17 00:00:00 2001 From: Milos Kotlar Date: Tue, 30 Jun 2026 14:50:03 +0200 Subject: [PATCH 05/10] Simplify stripped IL sentinel comments Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../DependencyAnalysis/ReadyToRun/CopiedMethodILNode.cs | 9 +++------ src/coreclr/vm/prestub.cpp | 4 +--- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/CopiedMethodILNode.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/CopiedMethodILNode.cs index a089cd2be6b923..5ffe983d6a6322 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/CopiedMethodILNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/CopiedMethodILNode.cs @@ -14,12 +14,9 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun { public class CopiedMethodILNode : ObjectNode, ISymbolDefinitionNode { - // Sentinel body emitted for stripped IL methods. It is intentionally invalid IL - // so it can never collide with a real method body (e.g. a user 'throw null;' method, - // whose body is 'ldnull; throw'). The runtime detects this exact sentinel before the - // IL reaches the JIT and throws a descriptive BadImageFormatException - // (see GetAndVerifyMetadataILHeader / BFA_STRIPPED_IL_BODY in prestub.cpp). - // Tiny header (0x0A: 2 bytes code size) + the illegal two-byte opcode 0xFE 0x24 (CEE_UNUSED49). + // Sentinel body for stripped IL methods: tiny header (0x0A) + the illegal two-byte + // opcode 0xFE 0x24. Invalid IL so it can never collide with a real method body; the + // runtime detects it in prestub.cpp (BFA_STRIPPED_IL_BODY) before it reaches the JIT. private static readonly byte[] s_minimalILBody = [0x0A, 0xFE, 0x24]; EcmaMethod _method; diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp index f4805ebb715c70..665e9c21a8d9c5 100644 --- a/src/coreclr/vm/prestub.cpp +++ b/src/coreclr/vm/prestub.cpp @@ -703,9 +703,7 @@ namespace if (status == COR_ILMETHOD_DECODER::FORMAT_ERROR) COMPlusThrowHR(COR_E_BADIMAGEFORMAT, BFA_BAD_IL); - // ILCompiler replaces stripped method bodies with an intentionally invalid IL sentinel - // (the illegal two-byte opcode 0xFE 0x24) so it can never collide with a real method body; - // see CopiedMethodILNode.s_minimalILBody. Detect it before the IL reaches the JIT. + // ILCompiler emits an invalid IL sentinel (0xFE 0x24) for stripped method bodies. Module* pModule = pMD->GetModule(); if (pModule->IsReadyToRun() && pModule->GetReadyToRunInfo()->HasStrippedILBodies() From 384d0b092b1e6b43bb30cb071791b3327decbf9a Mon Sep 17 00:00:00 2001 From: Milos Kotlar Date: Tue, 30 Jun 2026 14:52:23 +0200 Subject: [PATCH 06/10] Trim stripped IL sentinel comments Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../DependencyAnalysis/ReadyToRun/CopiedMethodILNode.cs | 3 +-- src/coreclr/vm/prestub.cpp | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/CopiedMethodILNode.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/CopiedMethodILNode.cs index 5ffe983d6a6322..5f7980fc3a90bf 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/CopiedMethodILNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/CopiedMethodILNode.cs @@ -15,8 +15,7 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun public class CopiedMethodILNode : ObjectNode, ISymbolDefinitionNode { // Sentinel body for stripped IL methods: tiny header (0x0A) + the illegal two-byte - // opcode 0xFE 0x24. Invalid IL so it can never collide with a real method body; the - // runtime detects it in prestub.cpp (BFA_STRIPPED_IL_BODY) before it reaches the JIT. + // opcode 0xFE 0x24. Invalid IL so it can never collide with a real method body. private static readonly byte[] s_minimalILBody = [0x0A, 0xFE, 0x24]; EcmaMethod _method; diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp index 665e9c21a8d9c5..e3cd53711861be 100644 --- a/src/coreclr/vm/prestub.cpp +++ b/src/coreclr/vm/prestub.cpp @@ -703,7 +703,6 @@ namespace if (status == COR_ILMETHOD_DECODER::FORMAT_ERROR) COMPlusThrowHR(COR_E_BADIMAGEFORMAT, BFA_BAD_IL); - // ILCompiler emits an invalid IL sentinel (0xFE 0x24) for stripped method bodies. Module* pModule = pMD->GetModule(); if (pModule->IsReadyToRun() && pModule->GetReadyToRunInfo()->HasStrippedILBodies() From b1319da5baf3c7e65d240c58aee1b6e65a1d88c9 Mon Sep 17 00:00:00 2001 From: Milos Kotlar Date: Thu, 2 Jul 2026 10:28:50 +0200 Subject: [PATCH 07/10] Fail fast instead of throw for stripped IL sentinel; address review feedback - prestub.cpp: EEPOLICY_HANDLE_FATAL_ERROR_WITH_MESSAGE instead of throw; named sentinel constants; comment covers JIT and interpreter - Remove now-unused BFA_STRIPPED_IL_BODY resource - CopiedMethodILNode: clarify sentinel comment Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/coreclr/dlls/mscorrc/mscorrc.rc | 1 - src/coreclr/dlls/mscorrc/resource.h | 1 - .../ReadyToRun/CopiedMethodILNode.cs | 6 +++-- src/coreclr/vm/prestub.cpp | 22 ++++++++++++++----- 4 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/coreclr/dlls/mscorrc/mscorrc.rc b/src/coreclr/dlls/mscorrc/mscorrc.rc index ff1234962455a6..4db50443879a4d 100644 --- a/src/coreclr/dlls/mscorrc/mscorrc.rc +++ b/src/coreclr/dlls/mscorrc/mscorrc.rc @@ -606,7 +606,6 @@ BEGIN BFA_INVALID_TOKEN_IN_MANIFESTRES "Invalid token saved in ManifestResource." BFA_EMPTY_ASSEMDEF_NAME "Empty assembly simple name." BFA_BAD_IL "Bad IL format." - BFA_STRIPPED_IL_BODY "A method body required at runtime was stripped from the ReadyToRun image and this runtime has no JIT to compile it." BFA_CLASSLOAD_VALUETYPEMISMATCH "Could not load type '%1' from assembly '%2' due to value type mismatch." BFA_METHODDECL_NOT_A_METHODDEF "Method declaration is not a methoddef." BFA_DUPLICATE_DELEGATE_METHOD "Duplicate definition for runtime implemented delegate method." diff --git a/src/coreclr/dlls/mscorrc/resource.h b/src/coreclr/dlls/mscorrc/resource.h index aebab58f5d57c3..de761d74bd6315 100644 --- a/src/coreclr/dlls/mscorrc/resource.h +++ b/src/coreclr/dlls/mscorrc/resource.h @@ -462,7 +462,6 @@ #define IDS_EE_INTEROP_STUB_CA_NO_ACCESS_TO_STUB_METHOD 0x2111 #endif -#define BFA_STRIPPED_IL_BODY 0x2112 #define BFA_REFERENCE_ASSEMBLY 0x2113 #define IDS_E_FIELDACCESS 0x2114 diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/CopiedMethodILNode.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/CopiedMethodILNode.cs index 5f7980fc3a90bf..c91658afdf89a9 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/CopiedMethodILNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/CopiedMethodILNode.cs @@ -14,8 +14,10 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun { public class CopiedMethodILNode : ObjectNode, ISymbolDefinitionNode { - // Sentinel body for stripped IL methods: tiny header (0x0A) + the illegal two-byte - // opcode 0xFE 0x24. Invalid IL so it can never collide with a real method body. + // Sentinel body for stripped IL methods: tiny header (0x0A, the tiny-header encoding for + // a 2-byte code size) followed by the two-byte opcode 0xFE 0x24, which is intentionally + // unassigned/invalid. Invalid IL so it can never collide with a real method body; the + // runtime detects it (in prestub.cpp) before it reaches the JIT or interpreter. private static readonly byte[] s_minimalILBody = [0x0A, 0xFE, 0x24]; EcmaMethod _method; diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp index e3cd53711861be..acb96201a80845 100644 --- a/src/coreclr/vm/prestub.cpp +++ b/src/coreclr/vm/prestub.cpp @@ -668,6 +668,14 @@ PCODE MethodDesc::JitCompileCode(PrepareCodeConfig* pConfig) namespace { + // Sentinel body emitted by crossgen2 --strip-il-bodies for methods whose IL was removed + // (see CopiedMethodILNode.s_minimalILBody in ILCompiler.ReadyToRun). The two-byte opcode + // 0xFE 0x24 is intentionally unassigned/invalid, so it can never collide with a real method + // body. Keep these values in sync with the crossgen2-side definition. + const DWORD STRIPPED_IL_SENTINEL_CODE_SIZE = 2; + const BYTE STRIPPED_IL_SENTINEL_BYTE0 = 0xFE; + const BYTE STRIPPED_IL_SENTINEL_BYTE1 = 0x24; + COR_ILMETHOD_DECODER* GetAndVerifyMetadataILHeader(MethodDesc* pMD, PrepareCodeConfig* pConfig, COR_ILMETHOD_DECODER* pDecoderMemory) { STANDARD_VM_CONTRACT; @@ -706,11 +714,15 @@ namespace Module* pModule = pMD->GetModule(); if (pModule->IsReadyToRun() && pModule->GetReadyToRunInfo()->HasStrippedILBodies() - && pHeader->GetCodeSize() == 2 - && pHeader->Code[0] == 0xFE - && pHeader->Code[1] == 0x24) - { - COMPlusThrowHR(COR_E_BADIMAGEFORMAT, BFA_STRIPPED_IL_BODY); + && pHeader->GetCodeSize() == STRIPPED_IL_SENTINEL_CODE_SIZE + && pHeader->Code[0] == STRIPPED_IL_SENTINEL_BYTE0 + && pHeader->Code[1] == STRIPPED_IL_SENTINEL_BYTE1) + { + // The IL body was stripped from the ReadyToRun image but is unexpectedly needed at + // runtime. Catch the sentinel here, before it reaches the JIT or interpreter, and + // fail fast: this is an unrecoverable image/configuration error, not a catchable one. + EEPOLICY_HANDLE_FATAL_ERROR_WITH_MESSAGE(COR_E_EXECUTIONENGINE, + W("A method body required at runtime was stripped from the ReadyToRun image (crossgen2 --strip-il-bodies).")); } return pHeader; From 3b1afd5c93547b27ae30c985958c05ab23c73ef5 Mon Sep 17 00:00:00 2001 From: Milos Kotlar Date: Thu, 2 Jul 2026 10:38:45 +0200 Subject: [PATCH 08/10] Drop sentinel constants and comments in prestub.cpp Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/coreclr/vm/prestub.cpp | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp index acb96201a80845..a10cbde440887c 100644 --- a/src/coreclr/vm/prestub.cpp +++ b/src/coreclr/vm/prestub.cpp @@ -668,14 +668,6 @@ PCODE MethodDesc::JitCompileCode(PrepareCodeConfig* pConfig) namespace { - // Sentinel body emitted by crossgen2 --strip-il-bodies for methods whose IL was removed - // (see CopiedMethodILNode.s_minimalILBody in ILCompiler.ReadyToRun). The two-byte opcode - // 0xFE 0x24 is intentionally unassigned/invalid, so it can never collide with a real method - // body. Keep these values in sync with the crossgen2-side definition. - const DWORD STRIPPED_IL_SENTINEL_CODE_SIZE = 2; - const BYTE STRIPPED_IL_SENTINEL_BYTE0 = 0xFE; - const BYTE STRIPPED_IL_SENTINEL_BYTE1 = 0x24; - COR_ILMETHOD_DECODER* GetAndVerifyMetadataILHeader(MethodDesc* pMD, PrepareCodeConfig* pConfig, COR_ILMETHOD_DECODER* pDecoderMemory) { STANDARD_VM_CONTRACT; @@ -714,13 +706,10 @@ namespace Module* pModule = pMD->GetModule(); if (pModule->IsReadyToRun() && pModule->GetReadyToRunInfo()->HasStrippedILBodies() - && pHeader->GetCodeSize() == STRIPPED_IL_SENTINEL_CODE_SIZE - && pHeader->Code[0] == STRIPPED_IL_SENTINEL_BYTE0 - && pHeader->Code[1] == STRIPPED_IL_SENTINEL_BYTE1) + && pHeader->GetCodeSize() == 2 + && pHeader->Code[0] == 0xFE + && pHeader->Code[1] == 0x24) { - // The IL body was stripped from the ReadyToRun image but is unexpectedly needed at - // runtime. Catch the sentinel here, before it reaches the JIT or interpreter, and - // fail fast: this is an unrecoverable image/configuration error, not a catchable one. EEPOLICY_HANDLE_FATAL_ERROR_WITH_MESSAGE(COR_E_EXECUTIONENGINE, W("A method body required at runtime was stripped from the ReadyToRun image (crossgen2 --strip-il-bodies).")); } From cfb042f50c6511306ae0c10994c9eb4db94eff7f Mon Sep 17 00:00:00 2001 From: Milos Kotlar Date: Thu, 2 Jul 2026 10:40:28 +0200 Subject: [PATCH 09/10] Simplify stripped IL sentinel comment and fail-fast message Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../DependencyAnalysis/ReadyToRun/CopiedMethodILNode.cs | 6 ++---- src/coreclr/vm/prestub.cpp | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/CopiedMethodILNode.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/CopiedMethodILNode.cs index c91658afdf89a9..787abe81061470 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/CopiedMethodILNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/CopiedMethodILNode.cs @@ -14,10 +14,8 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun { public class CopiedMethodILNode : ObjectNode, ISymbolDefinitionNode { - // Sentinel body for stripped IL methods: tiny header (0x0A, the tiny-header encoding for - // a 2-byte code size) followed by the two-byte opcode 0xFE 0x24, which is intentionally - // unassigned/invalid. Invalid IL so it can never collide with a real method body; the - // runtime detects it (in prestub.cpp) before it reaches the JIT or interpreter. + // Sentinel body for stripped IL methods: tiny header (0x0A) + invalid opcode 0xFE 0x24. + // Invalid IL so it can never collide with a real method body. private static readonly byte[] s_minimalILBody = [0x0A, 0xFE, 0x24]; EcmaMethod _method; diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp index a10cbde440887c..87369d9b0e21db 100644 --- a/src/coreclr/vm/prestub.cpp +++ b/src/coreclr/vm/prestub.cpp @@ -711,7 +711,7 @@ namespace && pHeader->Code[1] == 0x24) { EEPOLICY_HANDLE_FATAL_ERROR_WITH_MESSAGE(COR_E_EXECUTIONENGINE, - W("A method body required at runtime was stripped from the ReadyToRun image (crossgen2 --strip-il-bodies).")); + W("A method body required at runtime was stripped from the ReadyToRun image.")); } return pHeader; From e3c51f00d8ed237a9f090a405e86a396d87616fb Mon Sep 17 00:00:00 2001 From: Milos Kotlar Date: Thu, 2 Jul 2026 15:32:33 +0200 Subject: [PATCH 10/10] Update stripped-IL sentinel in strip-il-bodies test to match new 0xFE 0x24 stub PR #130025 changes the crossgen2 --strip-il-bodies stub from ldnull; throw (0x14 0x7A) to an invalid opcode (0xFE 0x24). Update the regression test added in PR #130075 to check for the new sentinel bytes. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../TestCasesRunner/R2RResultChecker.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RResultChecker.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RResultChecker.cs index 86c3112a2992ba..8a29c9de0bb128 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RResultChecker.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RResultChecker.cs @@ -1073,9 +1073,9 @@ public static bool MethodILIsStripped(string msilFilePath, string declaringType, if (!TryGetMethodIL(msilFilePath, declaringType, methodName, out byte[] il, out diagnostic)) return false; - bool stripped = il.AsSpan().SequenceEqual((ReadOnlySpan)[0x14, 0x7A]); + bool stripped = il.AsSpan().SequenceEqual((ReadOnlySpan)[0xFE, 0x24]); diagnostic = stripped - ? $"IL of '{declaringType}.{methodName}' is stripped (ldnull; throw)." + ? $"IL of '{declaringType}.{methodName}' is stripped (invalid opcode 0xFE 0x24)." : $"Expected IL of '{declaringType}.{methodName}' to be stripped, but it is present ({il.Length} bytes: {BitConverter.ToString(il)})."; return stripped; } @@ -1088,10 +1088,10 @@ public static bool MethodILIsPresent(string msilFilePath, string declaringType, if (!TryGetMethodIL(msilFilePath, declaringType, methodName, out byte[] il, out diagnostic)) return false; - bool present = !il.AsSpan().SequenceEqual((ReadOnlySpan)[0x14, 0x7A]); + bool present = !il.AsSpan().SequenceEqual((ReadOnlySpan)[0xFE, 0x24]); diagnostic = present ? $"IL of '{declaringType}.{methodName}' is present ({il.Length} bytes)." - : $"Expected IL of '{declaringType}.{methodName}' to be present, but it was stripped (ldnull; throw)."; + : $"Expected IL of '{declaringType}.{methodName}' to be present, but it was stripped (invalid opcode 0xFE 0x24)."; return present; } }