From a2cbb4f25700423f50e9f2321e6c640746576d57 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Thu, 16 Jul 2026 12:26:30 -0700 Subject: [PATCH] SuperPMI: fix spurious arm asmdiffs from out-of-range BL relocs The near-differ applies recorded relocations before comparing. For an arm32 direct BL (ARM32_THUMB_BRANCH24), applyRelocs substitutes a placeholder target when the code block lands more than +-16MB from the call target, so the two sides' immediates differ even though both call the same recorded target. Compare the recorded reloc kind and target instead. Also error out when base and diff JITs are the same module. --- .../tools/superpmi/superpmi/jitinstance.h | 4 ++++ .../tools/superpmi/superpmi/neardiffer.cpp | 22 +++++++++++++++++++ .../tools/superpmi/superpmi/superpmi.cpp | 12 ++++++++++ 3 files changed, 38 insertions(+) diff --git a/src/coreclr/tools/superpmi/superpmi/jitinstance.h b/src/coreclr/tools/superpmi/superpmi/jitinstance.h index b264894d368499..cbc6e5f5043ad5 100644 --- a/src/coreclr/tools/superpmi/superpmi/jitinstance.h +++ b/src/coreclr/tools/superpmi/superpmi/jitinstance.h @@ -52,6 +52,10 @@ class JitInstance ULONGLONG times[2]; ICorJitCompiler* pJitInstance; + // The loaded JIT module handle. Used to detect when the baseline and diff + // JITs resolve to the same loaded module (which shares global state). + HMODULE getModule() const { return hLib; } + // Allocate and initialize the jit provided static JitInstance* InitJit(char* nameOfJit, bool breakOnAssert, diff --git a/src/coreclr/tools/superpmi/superpmi/neardiffer.cpp b/src/coreclr/tools/superpmi/superpmi/neardiffer.cpp index 9bb7f913393056..e60b9d64454a81 100644 --- a/src/coreclr/tools/superpmi/superpmi/neardiffer.cpp +++ b/src/coreclr/tools/superpmi/superpmi/neardiffer.cpp @@ -562,6 +562,28 @@ bool NearDiffer::compareOffsets( return compareOffsetsWasm(payload, blockOffset, instrLen, offset1, offset2); } + // On arm32 a direct BL is fixed up with an ARM32_THUMB_BRANCH24 reloc. When the + // two code blocks are allocated more than +-16MB apart, applyRelocs substitutes a + // placeholder target for the out-of-range side, so the immediates differ even + // though both BLs target the same recorded address. Treat the sites as equal when + // both recorded the same reloc kind and target. + if (GetSpmiTargetArchitecture() == SPMI_TARGET_ARCHITECTURE_ARM) + { + const DiffData* data = (const DiffData*)payload; + + const Agnostic_RecordRelocation* reloc1 = + data->cr1->findRelocationInRange(data->originalBlock1, blockOffset, instrLen); + const Agnostic_RecordRelocation* reloc2 = + data->cr2->findRelocationInRange(data->originalBlock2, blockOffset, instrLen); + + if ((reloc1 != nullptr) && (reloc2 != nullptr) && (reloc1->fRelocType == reloc2->fRelocType) && + ((uint64_t)reloc1->target + (int32_t)reloc1->addlDelta == + (uint64_t)reloc2->target + (int32_t)reloc2->addlDelta)) + { + return true; + } + } + const DiffData* data = (const DiffData*)payload; size_t ip1 = data->originalBlock1 + blockOffset; size_t ip2 = data->originalBlock2 + blockOffset; diff --git a/src/coreclr/tools/superpmi/superpmi/superpmi.cpp b/src/coreclr/tools/superpmi/superpmi/superpmi.cpp index c509589330c9c9..b906f243703612 100644 --- a/src/coreclr/tools/superpmi/superpmi/superpmi.cpp +++ b/src/coreclr/tools/superpmi/superpmi/superpmi.cpp @@ -447,6 +447,18 @@ int __cdecl main(int argc, char* argv[]) // InitJit already printed a failure message return (int)SpmiResult::JitFailedToInit; } + + if (jit2->getModule() == jit->getModule()) + { + // The baseline and diff JITs resolved to the same loaded module. Because the JIT keeps + // global state (e.g. g_jitHost, JitConfig), sharing a single module between the two + // JitInstances corrupts that state and produces spurious diffs and intermittent crashes. + // Require the two JITs to be distinct files (copy one to a different path if needed). + LogError("The baseline JIT ('%s') and diff JIT ('%s') resolve to the same loaded module. " + "They must be distinct files; copy one JIT to a different path.", + o.nameOfJit, o.nameOfJit2); + return (int)SpmiResult::JitFailedToInit; + } } }