From d190ffcbd9a68ce8be9746799ad4691cf0146e85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Mon, 30 Mar 2026 13:29:20 +0900 Subject: [PATCH 1/3] Stop RyuJIT from injecting thumb bit under ILC, remove ObjectWriter compensation ILC defines ARM32 method symbols with the thumb bit (+1) set per AAELF convention. RyuJIT was also adding +1 to movw/movt label addresses, causing double-counting that ObjectWriter had to compensate for with maskThumbBitOut/maskThumbBitIn logic. Guard the thumb bit injection in emitarm.cpp so it only applies for ReadyToRun and runtime JIT (where symbols don't carry the thumb bit), not for ILC. Replace the maskThumbBit compensation in ObjectWriter with a simpler approach: for BRANCH24 only, strip the thumb bit from the symbol value since the encoding can't represent it. For MOV32, the symbol value (with thumb bit) flows through directly. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/coreclr/jit/emitarm.cpp | 9 +++++++- .../Compiler/ObjectWriter/ObjectWriter.cs | 23 ++++++++----------- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/src/coreclr/jit/emitarm.cpp b/src/coreclr/jit/emitarm.cpp index 1f5698a0afb01e..2ec083ef220ab4 100644 --- a/src/coreclr/jit/emitarm.cpp +++ b/src/coreclr/jit/emitarm.cpp @@ -5334,7 +5334,14 @@ BYTE* emitter::emitOutputLJ(insGroup* ig, BYTE* dst, instrDesc* i) else { assert(ins == INS_movw || ins == INS_movt); - distVal = (ssize_t)emitOffsetToPtr(dstOffs) + 1; // Or in thumb bit + distVal = (ssize_t)emitOffsetToPtr(dstOffs); + + // ILC defines method symbols with the thumb bit already set, so don't add it here. + // For ReadyToRun and non-relocatable code (runtime JIT), we set it ourselves. + if (!m_compiler->opts.compReloc || m_compiler->IsReadyToRun()) + { + distVal += 1; + } } if (dstOffs <= srcOffs) diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/ObjectWriter.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/ObjectWriter.cs index 1736908a2250c4..a0ecf092f4dfd2 100644 --- a/src/coreclr/tools/Common/Compiler/ObjectWriter/ObjectWriter.cs +++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/ObjectWriter.cs @@ -189,19 +189,17 @@ or IMAGE_REL_BASED_THUMB_BRANCH24 or IMAGE_REL_BASED_THUMB_MOV32_PCREL && // Resolve the relocation to already defined symbol and write it into data fixed (byte* pData = data) { - // RyuJIT generates the Thumb bit in the addend and we also get it from - // the symbol value. The AAELF ABI specification defines the R_ARM_THM_JUMP24 - // and R_ARM_THM_MOVW_PREL_NC relocations using the formula ((S + A) | T) – P. - // The thumb bit is thus supposed to be only added once. - // For R_ARM_THM_JUMP24 the thumb bit cannot be encoded, so mask it out. - // - // R2R doesn't use add the thumb bit to the symbol value, so we don't need to do this here. #if !READYTORUN - long maskThumbBitOut = relocType is IMAGE_REL_BASED_THUMB_BRANCH24 or IMAGE_REL_BASED_THUMB_MOV32_PCREL ? 1 : 0; - long maskThumbBitIn = relocType is IMAGE_REL_BASED_THUMB_MOV32_PCREL ? 1 : 0; + // ILC defines method symbols with the thumb bit (+1) set per the AAELF ABI + // convention. For BRANCH24, the encoding cannot represent the thumb bit + // (per AAELF formula ((S + A) | T) – P), so strip it from the symbol value. + // For MOV32, the thumb bit can be encoded, so use the symbol value as-is. + // R2R doesn't add the thumb bit to the symbol value. + long symbolValue = relocType is IMAGE_REL_BASED_THUMB_BRANCH24 + ? definedSymbol.Value & ~1L + : definedSymbol.Value; #else - long maskThumbBitOut = 0; - long maskThumbBitIn = 0; + long symbolValue = definedSymbol.Value; #endif long adjustedAddend = addend; @@ -213,9 +211,8 @@ or IMAGE_REL_BASED_THUMB_BRANCH24 or IMAGE_REL_BASED_THUMB_MOV32_PCREL && _ => 0 }; - adjustedAddend += definedSymbol.Value & ~maskThumbBitOut; + adjustedAddend += symbolValue; adjustedAddend += Relocation.ReadValue(relocType, (void*)pData); - adjustedAddend |= definedSymbol.Value & maskThumbBitIn; adjustedAddend -= offset; if (relocType is IMAGE_REL_BASED_THUMB_BRANCH24 && !Relocation.FitsInThumb2BlRel24((int)adjustedAddend)) From cf8592490da8294f20baddfb46bb40ae33463fae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Tue, 31 Mar 2026 09:49:22 +0900 Subject: [PATCH 2/3] Refine comments on thumb bit handling in ObjectWriter Clarified comments regarding method symbols and thumb bit handling in relocation. --- .../tools/Common/Compiler/ObjectWriter/ObjectWriter.cs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/ObjectWriter.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/ObjectWriter.cs index a0ecf092f4dfd2..cbc3cf3537f7cf 100644 --- a/src/coreclr/tools/Common/Compiler/ObjectWriter/ObjectWriter.cs +++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/ObjectWriter.cs @@ -189,18 +189,13 @@ or IMAGE_REL_BASED_THUMB_BRANCH24 or IMAGE_REL_BASED_THUMB_MOV32_PCREL && // Resolve the relocation to already defined symbol and write it into data fixed (byte* pData = data) { -#if !READYTORUN - // ILC defines method symbols with the thumb bit (+1) set per the AAELF ABI + // Method symbols should be defined with the thumb bit (+1) set per the AAELF ABI // convention. For BRANCH24, the encoding cannot represent the thumb bit // (per AAELF formula ((S + A) | T) – P), so strip it from the symbol value. - // For MOV32, the thumb bit can be encoded, so use the symbol value as-is. - // R2R doesn't add the thumb bit to the symbol value. + // NOTE: R2R doesn't currently add the thumb bit to the symbol value, so this is a NOP. long symbolValue = relocType is IMAGE_REL_BASED_THUMB_BRANCH24 ? definedSymbol.Value & ~1L : definedSymbol.Value; -#else - long symbolValue = definedSymbol.Value; -#endif long adjustedAddend = addend; adjustedAddend -= relocType switch From bbcf2fd59c66b65cc03544ecaebec924fc2c2ab8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Tue, 31 Mar 2026 09:49:38 +0900 Subject: [PATCH 3/3] Update src/coreclr/jit/emitarm.cpp Co-authored-by: Jan Kotas --- src/coreclr/jit/emitarm.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/jit/emitarm.cpp b/src/coreclr/jit/emitarm.cpp index 2ec083ef220ab4..cfb5de23666cac 100644 --- a/src/coreclr/jit/emitarm.cpp +++ b/src/coreclr/jit/emitarm.cpp @@ -5338,7 +5338,7 @@ BYTE* emitter::emitOutputLJ(insGroup* ig, BYTE* dst, instrDesc* i) // ILC defines method symbols with the thumb bit already set, so don't add it here. // For ReadyToRun and non-relocatable code (runtime JIT), we set it ourselves. - if (!m_compiler->opts.compReloc || m_compiler->IsReadyToRun()) + if (!m_compiler->IsNativeAot()) { distVal += 1; }