Start page1 page2 page3 page4 page5 page6 page7 page8 page9 page10
It is usual for functions to call other functions, even recursively, and then you need to take care of parameters and local variables, and return addresses — and to handle that we introduce the stack.
Programs running have received a piece of memory for stack space, and an initialized stackpointer, sp
.
We will see two strange things. First a drawing:
This means that the stack pointer points to a 16 bytes stack element. The x
-registers are 8 bytes,
so you can store two x
-registers at a time. And that's exactly what we a going to do.
At the start of any function we will push two important registers on the stack, and at the end we
will pop them off again — just before issuing the ret
-instruction.
The two registers are:
x30
(also called lr
for link register).
We will often need to call other functions, and then x30
will
be overwritten with a new value.x29
(also called fp
). We haven't seen
it yet, but it is used for local variables.So functions will mostly look like this, pushing the return address and the frame pointer on the stack:
func1: stp x29,x30,[sp, #-16]! /* push 2 registers */
...
ldp x29, x30, [sp], #16 /* pop 2 registers */
ret
The syntax for push and pop is strange. For now we will accept it with no explanation.
The library we made earlier worked without the stack, but let's introduce
it for the _phex
function. There is no need in the _exit
function.
In addition let's prove that the registers are restored by moving some arbitrary values into the two registers — and remove the two lines later on:
.text
.global _phex
.global _exit
_phex: stp x29,x30,[sp, #-16]!
movz x3,16
adr x2,message
again: ror x0,x0,60
and x1,x0,0xF
cmp x1,10
bge isAtoF
add x1,x1,48
b done
isAtoF: add x1,x1,55
done: strb w1,[x2]
add x2,x2,1
subs x3,x3,1
bne again
mov x0,1
adr x1,message
mov x2,17
mov x8,0x40
svc 0
movz x29,123 /* to be removed */
movz x30,456 /* to be removed */
ldp x29, x30, [sp], #16
ret
_exit: mov x0,0
mov x8, 93
svc 0
.data
message: .ascii "****************\n"
Everything should still work fine.
But actually the variable message
is local to phex. It doesn't need to
be known elsewhere, there is no need for it to exist unless we are in
_phex
, and if the function were recursive we would need more than one
variable, one for each called function.
This is where we will use the stack: allocate space for local variables, and use the frame pointer to access these variables.
Confused? You won't be after the next episode of ARM64 Assembly Programming.
Start page1 page2 page3 page4 page5 page6 page7 page8 page9 page10
2025-06-16