aboutsummaryrefslogblamecommitdiffhomepage
path: root/src/arch/armv7.md
blob: f6c3a962caa053f1d62c249a6a83ace7b16a1623 (plain) (tree)





































































                                                                            

























                                                                                   























































































































































                                                                                    
# armv7a
keywords: arm, armv7, abi

- ISA type: `RISC`
- Endianness: `little`, `big`

## Registers
### General purpose registers
```markdown
bytes
[3:0]     alt     desc
---------------------------------------------
r0-r12            general purpose registers
r11       fp
r13       sp      stack pointer
r14       lr      link register
r15       pc      program counter
```

### Special registers
```markdown
bytes
[3:0]             desc
---------------------------------------------
cpsr              current program status register
```

### CPSR register
```markdown
cpsr
bits  desc
-----------------------------
 [31]  N negative flag
 [30]  Z zero flag
 [29]  C carry flag
 [28]  V overflow flag
 [27]  Q cummulative saturation (sticky)
  [9]  E load/store endianness
  [8]  A disable asynchronous aborts
  [7]  I disable IRQ
  [6]  F disable FIQ
  [5]  T indicate Thumb state
[4:0]  M process mode (USR, FIQ, IRQ, SVC, ABT, UND, SYS)
```

## Instructions cheatsheet
### Accessing system registers
Reading from system registers:
```armasm
mrs r0, cpsr      // move cpsr into r0
```

Writing to system registers:
```armasm
msr cpsr, r0      // move r0 into cpsr
```

### Control Flow
```armasm
b <lable>     // relative forward/back branch
bl <lable>    // relative forward/back branch & link return addr in r14 (LR)

// branch & exchange (can change between ARM & Thumb instruction set)
//   bit Rm[0] == 0 -> ARM
//   bit Rm[0] == 1 -> Thumb
bx <Rm>       // absolute branch to address in register Rm
blx <Rm>      // absolute branch to address in register Rm &
              // link return addr in r14 (LR)
```

### Load/Store
Different addressing modes.
```armasm
str r1, [r0]         // [r0]=r1
str r1, [r0, #4]     // [r0+4]=r1
str r1, [r0, #4]!    // r0+=4; [r0]=r1
str r1, [r0], 4      // [r0]=r1; r0+=4
```

Load/store multiple registers full-descending.
```armasm
stmfd r0!, {r1-r2, r5}    // r0-=4; [r0]=r5
                          // r0-=4; [r0]=r2
                          // r0-=4; [r0]=r1
ldmfd r0!, {r1-r2, r5}    // r1=[r0]; r0+=4
                          // r2=[r0]; r0+=4
                          // r5=[r0]; r0+=4
```
> `!` is optional but has the effect to update the base pointer register `r0` here.

Push/Pop
```armasm
push {r0-r2}    // effectively stmfd sp!, {r0-r2}
pop {r0-r2}     // effectively ldmfd sp!, {r0-r2}
```

## Procedure Call Standard ARM ([`aapcs32`][aapcs32])
### Passing arguments to functions
- integer/pointer arguments
  ```markdown
  reg     arg
  -----------
  r0        1
  ..       ..
  r3        4
  ```
- a double word (64bit) is passed in two consecutive registers (eg `r1+r2`)
- additional arguments are passed on the stack. Arguments are pushed
  `right-to-left (RTL)`, meaning next arguments are closer to current `sp`.
  ```markdown
  void take(..., int a5, int a6);
                     |       |   | ... |       Hi
                     |       +-->| a6  |       |
                     +---------->| a5  | <-SP  |
                                 +-----+       v
                                 | ... |       Lo
  ```

### Return values from functions
- integer/pointer return values
  ```markdown
  reg          size
  -----------------
  r0         32 bit
  r0+r1      64 bit
  ```

### Callee saved registers
- `r4` - `r11`
- `sp`

### Stack
- full descending
  - full: `sp` points to the last used location (valid item)
  - descending: stack grows downwards
- `sp` must be 4byte aligned (word boundary) at all time
- `sp` must be 8byte aligned on public interface interfaces

### Frame chain
- not strictly required by each platform
- linked list of stack-frames
- each frame links to the frame of its caller by a `frame record`
  - a frame record is described as a `(FP,LR)` pair (2x32bit)
- `r11 (FP)` must point to the frame record of the current stack-frame
  ```markdown
        +------+                   Hi
        |   0  |     frame0        |
     +->|   0  |                   |
     |  |  ... |                   |
     |  +------+                   |
     |  |  LR  |     frame1        |
     +--|  FP  |<-+                |
        | ...  |  |                |
        +------+  |                |
        |  LR  |  |  current       |
  r11 ->|  FP  |--+  frame         v
        | ...  |                   Lo
  ```
- end of the frame chain is indicated by following frame record `(0,-)`
- location of the frame record in the stack frame is not specified
- `r11` is not updated before the new frame record is fully constructed

### Function prologue & epilogue
- prologue
  ```armasm
  push {fp, lr}
  mov fp, sp              // FP points to frame record
  ```
- epilogue
  ```armasm
  pop {fp, pc}            // pop LR directly into PC
  ```

## ASM skeleton
Small assembler skeleton, ready to use with following properties:
- use raw Linux syscalls (`man 2 syscall` for ABI)
- no `C runtime (crt)`
- gnu assembler [`gas`][gas_doc]
```armasm
// file: greet.S

#include <asm/unistd.h>      // syscall NRs

    .arch armv7-a

    .section .text, "ax"
    .balign 4

    // Emit `arm` instructions, same as `.arm` directive.
    .code 32
    .global _start
_start:
    // Branch with link and exchange instruction set.
    blx _do_greet

    mov r0, #0               // exit code
    mov r7, #__NR_exit       // exit(2) syscall
    swi 0x0

    // Emit `thumb` instructions, same as `.thumb` directive.
    .code 16
    .thumb_func
_do_greet:
    mov r0, #2               // fd
    ldr r1, =greeting        // buf
    ldr r2, =greeting_len    // &len
    ldr r2, [r2]             // len
    mov r7, #__NR_write      // write(2) syscall
    swi 0x0

    // Branch and exchange instruction set.
    bx lr

    .balign 8                // align data on 8byte boundary
    .section .rodata, "a"
greeting:
    .asciz "Hi ASM-World!\n"
greeting_len:
    .int .-greeting
```
> man gcc: `file.S` assembler code that must be preprocessed.

To cross-compile and run:
```bash
> arm-linux-gnueabi-gcc -o greet greet.S -nostartfiles -nostdlib  \
    -Wl,--dynamic-linker=/usr/arm-linux-gnueabi/lib/ld-linux.so.3 \
  && qemu-arm ./greet
Hi ASM-World!
```
> Cross-compiling on `Ubuntu 20.04 (x86_64)`, paths might differ on other
> distributions. Explicitly specifying the dynamic linker should not be
> required when compiling natively on arm.

## References
- [Procedure Call Standard ARM][aapcs32]
- [ARMv7-A Programmer's Guide][armv7a_prog_guide]
- [ARMv7-A Architecture Reference Manual][armv7a_arm]
- [GNU Assembler][gas_doc]
- [GNU Assembler Directives][gas_directives]
- [GNU Assembler `ARM` dependent features][gas_arm]


[aapcs32]: https://github.com/ARM-software/abi-aa/blob/master/aapcs32/aapcs32.rst
[armv7a_prog_guide]: https://developer.arm.com/documentation/den0013/latest
[armv7a_arm]: https://developer.arm.com/documentation/ddi0406/latest
[gas_doc]: https://sourceware.org/binutils/docs/as
[gas_directives]: https://sourceware.org/binutils/docs/as/Pseudo-Ops.html#Pseudo-Ops
[gas_arm]: https://sourceware.org/binutils/docs/as/ARM_002dDependent.html