{"record":{"id":"4147fb2dda6ab85d","repo":"egametang/ET","slug":"address-offset-of-target-and-replace-must-less-tha","errorCode":null,"errorMessage":"address offset of target and replace must less than ((1 << 25) - 1)","messagePattern":"address offset of target and replace must less than \\(\\(1 << 25\\) - 1\\)","errorType":"exception","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"Packages/cn.etetet.hybridclr/Scripts/Editor/Share/3rds/UnityHook/CodePatcher.cs","lineNumber":191,"sourceCode":"                ptr += 8;\n                *ptr++ = 0x50;\n                *ptr++ = 0xC3;\n            }\n            return ret;\n        }\n    }\n\n    public unsafe class CodePatcher_arm32_near : CodePatcher\n    {\n        private static readonly byte[] s_jmpCode = new byte[]    // 4 bytes\n        {\n            0x00, 0x00, 0x00, 0xEA,                         // B $val   ; $val = (($dst - $src) / 4 - 2) & 0x1FFFFFF\n        };\n\n        public CodePatcher_arm32_near(IntPtr target, IntPtr replace, IntPtr proxy) : base(target, replace, proxy, s_jmpCode.Length)\n        {\n            if (Math.Abs((long)target - (long)replace) >= ((1 << 25) - 1))\n                throw new ArgumentException(\"address offset of target and replace must less than ((1 << 25) - 1)\");\n\n#if ENABLE_HOOK_DEBUG\n            Debug.Log($\"CodePatcher_arm32_near: {PrintAddrs()}\");\n#endif\n        }\n\n        protected override unsafe byte[] GenJmpCode(void* jmpFrom, void* jmpTo)\n        {\n            byte[] ret = new byte[s_jmpCode.Length];\n            int val = ((int)jmpTo - (int)jmpFrom) / 4 - 2;\n\n            fixed (void* p = &ret[0])\n            {\n                byte* ptr = (byte*)p;\n                *ptr++ = (byte)val;\n                *ptr++ = (byte)(val >> 8);\n                *ptr++ = (byte)(val >> 16);\n                *ptr++ = 0xEA;","sourceCodeStart":173,"sourceCodeEnd":209,"githubUrl":"https://github.com/egametang/ET/blob/5cab01f7a8bee5f49f4781eebe9e2b1c6d7ebe0f/Packages/cn.etetet.hybridclr/Scripts/Editor/Share/3rds/UnityHook/CodePatcher.cs#L173-L209","documentation":"Thrown by CodePatcher_arm32_near's constructor. On ARM32, a near branch (B instruction) encodes a 24-bit signed offset multiplied by 4, giving a ±32 MiB reach (2^25 - 1 bytes). If the absolute difference between the target function address and the replacement function address reaches or exceeds this limit, the single-instruction branch cannot reach and the constructor rejects the patch with ArgumentException.","triggerScenarios":"Constructing a CodePatcher_arm32_near where Math.Abs((long)target - (long)replace) >= 33,554,431 bytes. This occurs when ASLR or memory layout places the target method and its replacement in memory regions more than ~32 MiB apart — common when the replacement is in a separately loaded assembly or a JIT-allocated code region far from the original.","commonSituations":"Hooking a method on ARM32 (older Android devices, 32-bit iOS) where the hot-reload replacement function is loaded far from the original; using the near patcher unconditionally instead of letting the framework choose near vs. far based on actual distance; address space fragmentation on 32-bit systems.","solutions":["Use the far branch variant CodePatcher_arm32_far instead, which uses an 8-byte LDR PC sequence with no distance limit.","If you control patcher selection, compute the offset first and choose near vs. far dynamically.","On 32-bit ARM, consider whether the hook is necessary at all — prefer higher-level interception if address layout is uncontrollable.","Check if the target runtime is actually 32-bit ARM; if it is 64-bit, use the arm64 patchers."],"exampleFix":"// before\nvar patcher = new CodePatcher_arm32_near(targetPtr, replacePtr, proxyPtr);\n\n// after — choose patcher based on distance\nlong offset = Math.Abs((long)targetPtr - (long)replacePtr);\nCodePatcher patcher = offset < ((1 << 25) - 1)\n    ? new CodePatcher_arm32_near(targetPtr, replacePtr, proxyPtr)\n    : new CodePatcher_arm32_far(targetPtr, replacePtr, proxyPtr);","handlingStrategy":"validation","validationCode":"long offset = Math.Abs((long)targetPtr - (long)replacePtr);\nconst long ARM32_NEAR_LIMIT = (1 << 25) - 1; // ~32 MiB\nif (offset >= ARM32_NEAR_LIMIT)\n{\n    // Use far patcher instead, or abort with a clear message\n    Debug.LogWarning($\"ARM32 near branch cannot reach offset {offset}. Use CodePatcher_arm32_far.\");\n}","typeGuard":"static bool CanUseArm32Near(IntPtr target, IntPtr replace)\n{\n    return Math.Abs((long)target - (long)replace) < ((1 << 25) - 1);\n}","tryCatchPattern":"try\n{\n    var patcher = new CodePatcher_arm32_near(targetPtr, replacePtr, proxyPtr);\n}\ncatch (ArgumentException ex) when (ex.Message.Contains(\"must less than ((1 << 25)\"))\n{\n    // Fall back to far patcher\n    var patcher = new CodePatcher_arm32_far(targetPtr, replacePtr, proxyPtr);\n}","preventionTips":["Always compute the address offset before selecting a patcher class.","Implement a factory method that chooses near vs. far based on actual distance.","On ARM32, prefer far patcher as a safe default if you cannot predict address layout.","Test hooks on real ARM32 devices, not just emulators, to catch distance issues."],"tags":["unityhook","arm32","code-patching","address-range","method-hook"],"backgroundTag":null,"analyzedSha":"5cab01f7a8bee5f49f4781eebe9e2b1c6d7ebe0f","analyzedAt":"2026-08-13T21:10:40.377Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}