LAB-21Easy

ARM64 Register Trace

Trace a short AArch64 routine by hand and determine the final register state and how many times its loop executes.

15 min4 prerequisites6 tools

Disassembly

00:  mov x0, #10
01:  mov x1, #3
loop:
02:  add x0, x0, x1
03:  sub x1, x1, #1
04:  cmp x1, #0
05:  bne loop
06:  mov x2, x0
07:  ret 

Hints

Hint 1

AArch64 immediates use a # prefix (#10, #3) instead of x86's bare numbers — same idea, different syntax.

Hint 2

X0 accumulates X1 into itself each pass; X1 counts down from 3 to 0.

Hint 3

BNE branches while the result of the last CMP was not equal — that's the Z (zero) flag being clear.

Submit Findings

Prerequisites

  • arm1-arm64-architecture-basics
  • arm2-arm64-calling-convention-and-the-stack
  • AArch64 register names (X0-X30, W0-W30, SP, PC, NZCV)
  • Conditional branch flags (N, Z, C, V)

Methodology

Investigation Methodology

1. AArch64 Instruction Syntax

  • mov x0, #10 → X0 = 10 (immediate with # prefix)
  • add x0, x0, x1 → X0 = X0 + X1 (three-operand form)
  • sub x1, x1, #1 → X1 = X1 - 1
  • cmp x1, #0 → Compare X1 with 0, sets NZCV flags
  • bne loop → Branch if Z=0 (Not Equal)
  • ret → Return (BR X30)

2. Register Mapping (ARM64 → x86-64)

AArch64 x86-64 Role
X0-X7 RDI, RSI, RDX, RCX, R8, R9 Argument/Return registers
X8 — Indirect result
X9-X15 R10, R11, R12, R13, R14, R15 Caller-saved (volatile)
X16-X17 — Intra-procedure call scratch
X18 — Platform register (OS)
X19-X28 RBX, RBP, R12, R13, R14, R15 Callee-saved (non-volatile)
X29 (FP) RBP Frame pointer
X30 (LR) — Link register (return address)
SP RSP Stack pointer
PC RIP Program counter

3. NZCV Condition Flags

Flag Bit Set When
N (Negative) 31 Result MSB = 1 (negative)
Z (Zero) 30 Result = 0
C (Carry) 29 Unsigned overflow (carry out)
V (Overflow) 28 Signed overflow

Condition Codes:

  • EQ / NE : Z=1 / Z=0
  • CS / CC : C=1 / C=0 (unsigned >= / <)
  • MI / PL : N=1 / N=0 (negative / positive)
  • VS / VC : V=1 / V=0 (overflow)
  • HI : C=1 and Z=0 (unsigned >)
  • LS : C=0 or Z=1 (unsigned <=)
  • GE / LT : N=V / N≠V (signed >= / <)
  • GT / LE : Z=0 and N=V / Z=1 or N≠V (signed > / <=)

4. Trace Execution

Pass X0 X1 CMP Result Z BNE
1 10+3=13 3-1=2 2≠0 0 taken
2 13+2=15 2-1=1 1≠0 0 taken
3 15+1=16 1-1=0 0=0 1 NOT taken

5. Loop Analysis Pattern

  • Counter in X1 starts at N (3)
  • Decrements each iteration
  • Loop runs while counter ≠ 0 → N iterations
  • Accumulator X0 adds decreasing counter: N + (N-1) + ... + 1

6. Key Differences from x86

  • Explicit immediates: #10 not 10
  • Three-operand ALU: add dest, src1, src2 (dest ≠ src1 allowed)
  • Flags only from CMP/CCMP: ALU ops don't set flags by default
  • Condition suffixes: b.ne = branch if not equal
  • No ret with pop: Return address in X30 (LR), ret = br x30

Common Pitfalls

  • Confusing AArch64 `bne` (branch if Z=0) with x86 `jne` (same logic, different name)
  • Forgetting that ALU instructions (`add`, `sub`) DON'T set flags — only `cmp`/`ccmp` do
  • Misreading `mov x0, #10` as move from register 10 (it's immediate 10)
  • Assuming X0-X7 are callee-saved (they're volatile — caller-saved)
  • Not knowing that `ret` uses X30 (Link Register), not stack

Tools

  • Ghidra (AArch64 support)
  • Binary Ninja (ARM64)
  • IDA Pro (ARM64)
  • GDB (aarch64-linux-gnu-gdb)
  • ARM64 Assembly Reference Card
  • Built-in Assembly Visualizer

References

  • Lesson ARM1: ARM64 Architecture Basics
  • Lesson ARM2: ARM64 Calling Convention & Stack
  • ARM Architecture Reference Manual (ARMv8-A)
  • AArch64 Quick Reference Card
  • Procedure Call Standard for the ARM 64-bit Architecture (AAPCS64)

Extensions & Challenges

  • Translate the loop to x86-64 assembly — compare instruction count
  • Modify loop to sum 1..10 (change X1 initial value)
  • Research: How does AArch64 handle function calls (BL/BLR) and LR?
  • Analyze an Android ARM64 binary — find a loop, trace registers
  • Compare: AArch64 conditional execution vs x86 flag-based jumps