egametang/ET · error · ArgumentException

address offset of target and replace must less than (1 << 26

Error message

address offset of target and replace must less than (1 << 26) - 1) * 4

What it means

Thrown by CodePatcher_arm64_near's constructor. On ARM64, the B (unconditional branch) instruction encodes a 26-bit signed immediate multiplied by 4, giving a ±128 MiB reach ((2^26 - 1) * 4 bytes). If the absolute difference between target and replacement addresses reaches or exceeds this limit, the near branch cannot encode the jump and the constructor throws ArgumentException.

Source

Thrown at Packages/cn.etetet.hybridclr/Scripts/Editor/Share/3rds/UnityHook/CodePatcher.cs:265

    /// <summary>
    /// arm64 下 ±128MB 范围内的跳转
    /// </summary>
    public unsafe class CodePatcher_arm64_near : CodePatcher
    {
        private static readonly byte[] s_jmpCode = new byte[]    // 4 bytes
        {
            /*
             * from 0x14 to 0x17 is B opcode
             * offset bits is 26
             * https://developer.arm.com/documentation/ddi0596/2021-09/Base-Instructions/B--Branch-
             */
            0x00, 0x00, 0x00, 0x14,                         //  B $val   ; $val = (($dst - $src)/4) & 7FFFFFF
        };

        public CodePatcher_arm64_near(IntPtr target, IntPtr replace, IntPtr proxy) : base(target, replace, proxy, s_jmpCode.Length)
        {
            if (Math.Abs((long)target - (long)replace) >= ((1 << 26) - 1) * 4)
                throw new ArgumentException("address offset of target and replace must less than (1 << 26) - 1) * 4");

#if ENABLE_HOOK_DEBUG
            Debug.Log($"CodePatcher_arm64: {PrintAddrs()}");
#endif
        }

        protected override unsafe byte[] GenJmpCode(void* jmpFrom, void* jmpTo)
        {
            byte[] ret = new byte[s_jmpCode.Length];
            int val = (int)((long)jmpTo - (long)jmpFrom) / 4;

            fixed (void* p = &ret[0])
            {
                byte* ptr = (byte*)p;
                *ptr++ = (byte)val;
                *ptr++ = (byte)(val >> 8);
                *ptr++ = (byte)(val >> 16);

View on GitHub (pinned to 5cab01f7a8)

Solutions

  1. Use CodePatcher_arm64_far (20-byte ADR+LDR+BR sequence) for out-of-range offsets.
  2. Implement distance-based patcher selection: near when offset < ((1<<26)-1)*4, far otherwise.
  3. Investigate whether the replacement function can be allocated closer to the target (e.g. via code allocation near the original).
  4. Verify you are on ARM64 — if the issue is on ARM32, use the arm32 patchers instead.

Example fix

// before
var patcher = new CodePatcher_arm64_near(targetPtr, replacePtr, proxy);

// after — choose based on distance
long offset = Math.Abs((long)targetPtr - (long)replacePtr);
CodePatcher patcher = offset < ((1L << 26) - 1) * 4
    ? new CodePatcher_arm64_near(targetPtr, replacePtr, proxy)
    : new CodePatcher_arm64_far(targetPtr, replacePtr, proxy, 20);
Defensive patterns

Strategy: validation

Validate before calling

long offset = Math.Abs((long)targetPtr - (long)replacePtr);
long arm64NearLimit = ((1L << 26) - 1) * 4; // ~128 MiB
if (offset >= arm64NearLimit)
{
    Debug.LogWarning($"ARM64 near branch cannot reach offset {offset} (limit {arm64NearLimit}). Use CodePatcher_arm64_far.");
}

Type guard

static bool CanUseArm64Near(IntPtr target, IntPtr replace)
{
    return Math.Abs((long)target - (long)replace) < ((1L << 26) - 1) * 4;
}

Try / catch

try
{
    var patcher = new CodePatcher_arm64_near(targetPtr, replacePtr, proxy);
}
catch (ArgumentException ex) when (ex.Message.Contains("1 << 26"))
{
    // Offset exceeds ±128 MiB; fall back to far patcher
    var patcher = new CodePatcher_arm64_far(targetPtr, replacePtr, proxy, 20);
}

Prevention

When it happens

Trigger: Constructing a CodePatcher_arm64_near where Math.Abs((long)target - (long)replace) >= (67,108,863 * 4) = ~256 MiB. Occurs on ARM64 when ASLR, shared library loading, or JIT code allocation places the target and replacement functions more than ~128 MiB apart in the 64-bit address space.

Common situations: Hooking a method on ARM64 (modern Android, Apple Silicon) where the replacement lives in a separately loaded assembly or dynamically allocated code buffer far from the original; address space randomization (ASLR) on 64-bit placing libraries very far apart.

Related errors


AI-assisted analysis of egametang/ET@5cab01f7a8 (2026-08-13). Data as JSON: /api/errors/318b3beca51f3260. Report an issue: GitHub.