Skip to content

fix(arm64): use x17 instead of x10 to avoid clobbering the 11th argument slot - #189

Merged
agiledragon merged 3 commits into
agiledragon:masterfrom
asd1192698984:fix-arm64-r10-clobber
Sep 9, 2026
Merged

agiledragon merged 3 commits into
agiledragon:masterfrom
asd1192698984:fix-arm64-r10-clobber

Conversation

@asd1192698984

Copy link
Copy Markdown
Contributor

fix(arm64): avoid clobbering R10 (11th argument slot) in the stub trampoline

What

Fix a SIGBUS on arm64 (darwin/arm64 & linux/arm64) caused by the stub trampoline clobbering register R10, which holds the 11th argument slot.

Why

In jmp_arm64.go, buildJmpDirective uses x10 as a temporary register:

LDR x10, [x26]
BR  x10

In Go's arm64 ABI, R0–R15 are the integer argument/result registers, so R10 is the 11th argument slot. Moreover, struct arguments are flattened field-by-field, so a signature like func Parse(in SomeBigStruct, out any) can easily put out's data word in R10.

When the target function is stubbed, LDR x10, [x26] overwrites the caller's 11th argument slot with the mock's own code address. If the mock dereferences or writes through that argument (e.g. *out.(*T) = ...), it writes into the mock's read-only text segment — SIGBUS on darwin/arm64 (W^X), SIGSEGV on linux/arm64.

Fix

Use x17 (the platform-reserved temp register REGRT2, never used to pass arguments) instead of x10:

- res = append(res, []byte{0x4A, 0x03, 0x40, 0xF9}...) // LDR x10, [x26]
- res = append(res, []byte{0x40, 0x01, 0x1F, 0xD6}...) // BR   x10
+ res = append(res, []byte{0x51, 0x03, 0x40, 0xF9}...) // LDR x17, [x26]
+ res = append(res, []byte{0x20, 0x02, 0x1F, 0xD6}...) // BR   x17

MOVZ/MOVK x26 is unchanged (x26 is callee-saved and not on the argument path).

Test

Added TestApplyFuncArm64R10: stubs a function whose signature puts an any argument's data word in R10, and the mock writes through it. On unpatched arm64 this writes to the mock's own text segment (SIGBUS); with the fix it passes.

Scope

  • Fixes darwin/arm64 and linux/arm64 (shared jmp_arm64.go).
  • amd64 / 386 / loong64 / riscv64 use their own jmp_*.go with non-argument temp registers — unaffected.

Related: #181

@agiledragon
agiledragon merged commit 6b4193c into agiledragon:master Sep 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants