Skip to content

Fix add, sub, adds, subs and cmp encoding in the arm64 assembler ##arch - #27134

Merged
trufae merged 2 commits into
radareorg:masterfrom
HuzaifaChaudary:arm64-subs-and-w-sub
Nov 8, 2026
Merged

trufae merged 2 commits into
radareorg:masterfrom
HuzaifaChaudary:arm64-subs-and-w-sub

Conversation

@HuzaifaChaudary

@HuzaifaChaudary HuzaifaChaudary commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor
  • Mark this if you consider it ready to merge
  • I've added tests (optional)
  • I wrote some lines in the book (optional)

Description

add , sub and cmp went through arithmetic () and cmp () , which build the byte swapped word by hand and miss several cases , so these came out as other instructions :

input before ( decoded with llvm-mc ) after
subs x0, x1, x28 sub x0, x1, x28 20001ceb
sub w0, w1, w17 sub x0, x1, x17 2000114b
add x0, x0, 0x1000 add x0, x0, #0, lsl #12 00044091
add x0, x0, w28, uxtw add x0, x0, x28 00403c8b
adds x0, x0, x28, lsl 3 adds x0, x0, x28 000c1cab
cmp x0, 0xfff cmp x1, #63 1ffc3ff1

subs matched the sub prefix and lost its S bit , sub was always 64-bit , immediates above 0xfff ran into the shift bit , and extends and shift amounts were dropped .

addsub () now encodes the three forms from the manual in normal bit order and swaps once at the end , like stp does in #27132 :

  • immediate : 12 bits , lsl 12 written or implied by a value like 0x1000 , and a negative value turns add into sub and back , as llvm does
  • shifted register : lsl , lsr , asr with the amount checked against the register size
  • extended register : uxtb .. sxtx with an amount up to 4 , and used for lsl or no shift when sp is an operand , so add x0, sp, x1 is right too
  • register 31 is checked to be sp or xzr where each is allowed , so add xzr, x0, 1 and adds sp, x0, 1 are refused

add , adds , sub , subs and cmp ( as subs with xzr ) use it , adds () is removed .

found by assembling the same text with rasm2 -a arm.nz and llvm-mc . over the same 3915 line list , the encodings equal to llvm went from 2451 to 3116 and the wrong ones from 762 to 149 , 68 of those being the ldp / stp of #27132 . nothing that was right before changed , and nothing llvm refuses is accepted now . the 17 new lines in db/asm/arm_64 fail before the change except 3 that already worked , db/asm and db/tools/rasm2 have no failures .

Copilot AI balanced review requested due to automatic review settings October 22, 2026 21:03

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

trufae commented Oct 26, 2026

Copy link
Copy Markdown
Collaborator

Please rebase on master and resolve the conflicts first.

The shared encoder makes sense. I checked the PR head (5c262eb) with a small driver linked to r_util: all 17 new assembly cases match, versus 3 on the base. But there are two range checks to fix before merging:

  • imm = -imm overflows for 0x8000000000000000. UBSan catches it. add x0, x1, 0x8000000000000000 is then accepted as sub x0, x1, 0 (200000d1). Keep the magnitude unsigned, negate with unsigned arithmetic, then check the range. This also affects subs and cmp.
  • The extended-register path copies the parser's ut64 shift_amount into an int before checking it. add x0, x1, w2, uxtw 0xffffffff becomes a negative amount, passes amount <= 4, and triggers a left shift of a negative value. It emits 20fcffff. Keep amount as ut64 until after the 0..4 check. Please add rejection tests for these inputs, alongside the valid boundary of 4.

One nearby followup:

  • The shifted-register path still accepts SP as Rm. add x0, x1, sp emits add x0, x1, xzr; cmp x0, sp has the same problem. This already happens on the base. A small !(rm->reg_type & ARM_SP) check in that path would close the gap. Cover WSP too, and keep XZR/WZR working.

No obvious performance blocker here. The encoding work and the small bounded cmp operand move are constant time. These fixes should fit in the current helper without another abstraction.

@HuzaifaChaudary

Copy link
Copy Markdown
Contributor Author

rebased on master . the only conflict was in test/db/asm/arm_64 , both sides had added cases at the same place , so both are kept .

fixed in 7a60487 :

  • the immediate stays a ut64 and is negated as 0 - imm , so 0x8000000000000000 stays too big and is rejected for add , sub , subs and cmp .
  • the extend amount stays ut64 until the <= 4 check , so uxtw 0xffffffff is rejected . uxtw 4 is in the asm tests as the valid boundary , and uxtw 5 is rejected .
  • the shifted register path rejects sp and wsp as rm now , so add x0, x1, sp , cmp x0, sp and cmp w0, wsp fail . xzr and wzr still work , and there are asm tests for both .

the rejected inputs are one test in db/cmd/cmd_pa that uses pa , so nothing forks . it fails on the old head and passes with the fix . db/asm and cmd_pa have no failures . the new encodings match llvm-mc .

@trufae
trufae merged commit 2d77001 into radareorg:master Nov 8, 2026
50 checks passed
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.

3 participants